/*
openisis - an open implementation of the CDS/ISIS database
Version 0.8.x (patchlevel see file Version)
Copyright (C) 2001-2003 by Erik Grziwotz, erik@openisis.org
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public
License as published by the Free Software Foundation; either
version 2.1 of the License, or (at your option) any later version.
This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public
License along with this library; if not, write to the Free Software
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
see README for more information
EOH */
/*
$Id: lqry.c,v 1.7 2003/04/08 00:20:52 kripke Exp $
implementation of record cooking.
*/
#include <string.h> /* memset et al */
#include "ldb.h"
/* ************************************************************
private types
*/
/* ************************************************************
private data
*/
static unsigned char op[256];
/* ************************************************************
private functions
*/
static int lqry_scan ( OpenIsisSet *set, int db,
const char *key, int mode, int skip )
{
(void)set;(void)db;(void)key;(void)mode;(void)skip;
log_msg( LOG_ERROR, "scan mode not implemented yet, sorry!" );
return -1;
} /* lqry_scan */
/* ************************************************************
package functions
*/
/* ************************************************************
public functions
*/
int dQuery ( Set *set, int db,
const char *key, int mode, int skip )
{
LdbPost p;
int ret = 0;
memset( &p, 0, sizeof(p) );
p.skp = skip;
p.tag = mode >> 16;
mode &= 0xff;
if ( OPENISIS_QRY_SIMPLE > mode ) {
char *tmp = (char*)key;
if ( OPENISIS_QRY_SCANE <= mode )
return lqry_scan( set, db, key, mode, skip );
switch ( (int)mode ) {
case OPENISIS_QRY_KEYEQ: break;
case OPENISIS_QRY_KEYAT:
{
size_t l = strlen(key);
if ( !l || '$' != key[l-1] )
break;
tmp = mAlloc( l );
if ( ! tmp )
return -ERR_NOMEM;
memcpy( tmp, key, l-1 );
tmp[l-1] = 0;
}
case OPENISIS_QRY_KEYPF:
p.mode = LDB_PFX;
}
ret = ldb_search( db, tmp, &p, 0 );
if ( tmp != key )
mFree( tmp );
/* } else if ( OPENISIS_QRY_SIMPLE < mode ) { */
} else { /* handling query expression (simple) */
size_t klen = strlen(key);
unsigned char *buf, *uc, *term, *end, utmp;
if ( ! op['.'] ) /* operators */
op['.'] = op['$'] = op['('] = op['*'] = op['+'] = op['^'] = op['/'] = 1;
buf = (unsigned char*)mAlloc( klen+1 );
if ( ! buf )
return -ERR_NOMEM;
/* startover: */
memcpy( buf, key, klen+1 ); /* reload, might have frobed the buf on \ */
uc = buf;
p.mode = LDB_OR;
nextterm:
while ( *uc && ' ' >= *uc ) uc++; /* skip white */
if ( !*uc ) goto done;
/* take as term, whatever it is ... */
term = uc;
if ( '"' == *term ) { /* anything up to next unescaped " */
int bs = 0; /* count of backslashes seen so far */
term++;
while ( *++uc && '"' != *uc ) {
if ( '\\' == *uc ) {
if ( ! *++uc ) break; /* trailing \ */
bs++;
}
if ( bs ) uc[-bs] = *uc;
}
end = uc++ - bs;
if ( '$' == end[-1] ) {
p.mode |= LDB_PFX;
end--;
}
} else {
while ( *++uc && !op[*uc]
&& (' '<*uc || (uc==term+3 && !memcmp("ANY",term,3)))
); /* find white or op */
end = uc;
if ( '$' == *uc ) {
p.mode |= LDB_PFX;
uc++;
}
}
/* now end is on 1st char after term, possibly on a $ or ".
uc is on next pos to scan. */
/* check for tag */
p.tag = 0;
if ( '/' == *uc ) {
int parenths = '(' == *++uc;
if ( parenths ) uc++; /* accept tag w o w/o () */
/* eat arbitrary large numbers :)) */
while ( '0'<=*uc && *uc<='9' ) p.tag = 10*p.tag + *uc++ - '0';
if ( parenths ) while ( *uc && ')'!=*uc++ ); /* mv behind ) */
}
/* do it */
utmp = *end; *end = 0; /* terminate term */
if ( !(p.mode & (LDB_AND|LDB_NOT)) )
p.near = -1; /* collect all pos */
else if ( !p.fil ) /* nothing to AND */
goto operator;
if ( 0 > (ret = ldb_search( db, (const char*)term, &p, 0 )) )
goto done;
sMsg( LOG_INFO, "src %d '%.30s'@%d %hd(%hd): fill %d cut %d",
p.skp, term, p.tag, p.mode, p.near, p.fil, p.cut );
operator:
*end = utmp; /* restore */
/* look for next operator */
p.mode = LDB_AND;
p.near = 0;
while ( *uc && ' ' >= *uc ) uc++; /* skip white */
if ( !*uc ) goto done;
switch ( *uc ) {
case '*': uc++; break;
case '^': p.mode = LDB_NOT; uc++; break;
case '+': p.mode = LDB_OR; uc++; break;
case '.': while ( '.' == *uc++ ) p.near++; break;
case '$': while ( '$' == *uc++ ) p.near--; break;
case '(':
if ( ! uc[1] ) goto done;
if ( ')' != uc[2] ) break;
if ( 'F' == uc[1] ) p.near = LDB_NEAR_F;
if ( 'G' == uc[1] ) p.near = LDB_NEAR_G;
if ( '0'<uc[1] && uc[1]<='9' ) p.near = uc[1]-'0';
uc += 3;
break;
case 'O':
if ( 'R' == uc[1] && ' ' >= uc[2] ) { p.mode = LDB_OR; uc += 3; }
break;
case 'A':
if ( 'N' == uc[1] && 'D' == uc[2] && ' ' >= uc[3] ) uc += 4;
break;
case 'N':
if ( 'O' == uc[1] && 'T' == uc[2] && ' ' >= uc[3] ) {
p.mode = LDB_NOT; uc += 4;
}
break;
}
goto nextterm;
done:
/* somewhat screws the idea of limiting costs ...
if ( !p.fil && p.cut > p.skp ) {
sMsg( LOG_INFO, "OUCH! full cut at %d for '%.50s' >= %d",
p.cut, key, p.skp );
p.skp = p.cut;
p.cut = 0;
goto startover;
}
*/
mFree( buf );
}
return 0 > ret ? ret : ldb_p2s( set, &p );
} /* openIsisQuery */