Revision 238 (by dpavlin, 2004/03/08 17:46:16) tagging openisis 0.9.0
/*
	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 */