/
umud/DOC/
umud/DOC/examples/
umud/DOC/internals/
umud/DOC/wizard/
umud/MISC/
umud/MISC/dbchk/
umud/RWHO/rwhod/
/*
	Copyright (C) 1991, Marcus J. Ranum. All rights reserved.
*/

#ifndef	lint
static	char	RCSid[] = "$Header: /usr/users/mjr/hacks/umud/DB/RCS/expr.c,v 1.2 91/08/07 00:48:16 mjr Exp $";
#endif

/* configure all options BEFORE including system stuff. */
#include	"config.h"


#include	<stdio.h>
#include	<ctype.h>

#include	"mud.h"


#define	ISPUN(x) (x=='#'||x=='['||x==']'||x =='!'||x =='('||x ==')'||x=='&'||x=='|'||x=='=')


typedef	struct	_b_exp	{
	int	typ;
	char	*str;
	struct	_b_exp	*rhs;
	struct	_b_exp	*lhs;
} b_exp;



static	b_exp	*build_expr();
static	char	*tokp;
static	char	*bufp;
static	int	lasttok;
static	char	*curnam;
static	Obj	*curobj;
b_exp	*compiled;



static	b_exp	*
new_b_exp()
{
	b_exp	*ret;

	ret = (b_exp *)malloc(sizeof(b_exp));
	if(ret == (b_exp *)0)
		fatal("malloc new expression node: ",(char *)-1,"\n",(char *)0);
	ret->typ = '$';
	ret->str = (char *)0;
	ret->rhs = ret->lhs = (b_exp *)0;
	return(ret);
}




static	int
nexttok()
{
	int	quot = 0;
	int	ghoti = 0;
	char	tbuf[512];
	char	*top;

	while(isspace(*bufp))
		bufp++;

	top = tbuf;

	if(ISPUN(*bufp)) {
		quot = *bufp++;
		return(quot);
	}

	if((*bufp == 'T' || *bufp == 'F') &&
		(isspace(*(bufp + 1)) || *(bufp + 1) == '\0')) {
		quot = *bufp++;
		bufp++;
		return(quot);
	}

	while(*bufp != '\0') {
		ghoti = 1;
		if(!quot && (*bufp == '\'' || *bufp == '\"')) {
			quot = *bufp++;
			continue;
		}

		/* done quote */
		if(quot && *bufp == quot) {
			quot = 0;
			bufp++;
			continue;
		}

		/* done word */
		if(!quot && (isspace(*bufp) || ISPUN(*bufp)))
			break;

		/* handle escapes */
		if(*bufp == '\\') {
			bufp++;
			if(*bufp != '\0') {
				*top++ = *bufp++;
			}
			continue;
		}

		/* default - just copy */
		*top++ = *bufp++;
	}

	if(top - tbuf > sizeof(tbuf) - 2)
		fatal("string buffer overrun in expression\n",(char *)0);

	if(ghoti) {
		*top = '\0';
		tokp = (char *)malloc(top - tbuf + 1);
		if(tokp == (char *)0)
			fatal("cannot allocate string: ",(char *)-1,"\n",(char *)0);
		strcpy(tokp,tbuf);
		return('W');
	}
	return('$');
}




static	b_exp	*
build_subexpr()
{
	b_exp	*ret;

	switch(lasttok = nexttok()) {

	case 'W':
		ret = new_b_exp();
		ret->typ = 'W';
		ret->str = tokp;
		return(ret);


	case '!':
		ret = new_b_exp();
		if((ret->rhs = build_subexpr()) == (b_exp *)0)
			return((b_exp *)0);
		ret->typ = '!';
		return(ret);


	case '(':
		ret = build_expr();
		if((lasttok = nexttok()) != ')') {
			fprintf(stderr,"missing right parenthesis\n");
			return((b_exp *)0);
		}
		return(ret);


	case '[':
		lasttok = nexttok();
		if(lasttok != 'W') {
			fprintf(stderr,"missing regular expression\n");
			return((b_exp *)0);
		}
		if((lasttok = nexttok()) != ']') {
			fprintf(stderr,"missing regular expression terminator \n");
			return((b_exp *)0);
		}
		ret = new_b_exp();
		ret->typ = 'R';
		ret->str = tokp;
		return(ret);


	case '#':
		lasttok = nexttok();
		if(lasttok != 'W') {
			fprintf(stderr,"missing object ID\n");
			return((b_exp *)0);
		}
		ret = new_b_exp();
		ret->typ = 'O';
		ret->str = tokp;
		return(ret);


	case '$':
	case 'T':
	case 'F':
		ret = new_b_exp();
		ret->typ = lasttok;
		return(ret);


	default:
		fprintf(stderr,"syntax error");
		if(lasttok == 'W') {
			fprintf(stderr," near \"%s\"",tokp);
		} else { 
			if(bufp != (char *)0 && *bufp != '\0')
				fprintf(stderr," near \"%c %s\"",lasttok,bufp);
		}
		fprintf(stderr,"\n");
		return((b_exp *)0);
	}
}




static	b_exp	*
build_expr()
{
	b_exp	*ret;
	b_exp	*lhs;
	int	x;

	if((lhs = build_subexpr()) == (b_exp *)0)
		return((b_exp *)0);

	switch(lasttok = nexttok()) {

	case	'&':
		ret = new_b_exp();
		if((ret->rhs = build_subexpr()) == (b_exp *)0)
			return((b_exp *)0);
		if(ret->rhs->typ == '$') {
			fprintf(stderr,"missing AND clause\n");
			return((b_exp *)0);
		}
		ret->typ = '&';
		ret->lhs = lhs;
		return(ret);


	case	'=':
		ret = new_b_exp();
		if((ret->rhs = build_subexpr()) == (b_exp *)0)
			return((b_exp *)0);
		if(lhs->typ != 'W') {
			fprintf(stderr,"missing attribute for comparison\n");
			return((b_exp *)0);
		}
		if(ret->rhs->typ != 'W' && ret->rhs->typ != 'R') {
			fprintf(stderr,"missing string for comparison\n");
			return((b_exp *)0);
		}
		ret->typ = '=';
		ret->lhs = lhs;
		return(ret);


	case	'|':
		ret = new_b_exp();
		if((ret->rhs = build_subexpr()) == (b_exp *)0)
			return((b_exp *)0);
		if(ret->rhs->typ == '$') {
			fprintf(stderr,"missing OR clause\n");
			return((b_exp *)0);
		}
		ret->typ = '|';
		ret->lhs = lhs;
		return(ret);


	default:
		return(lhs);
	}
}




static	int
eval_subexpr(ex)
b_exp	*ex;
{
	switch(ex->typ) {
	case 'W':
		fprintf(stderr,"meaningless evaluation");
		if(ex->str != (char *)0)
			fprintf(stderr,": \"%s\"",ex->str);
		fprintf(stderr,"\n");
		return(1);

	case 'O':
		if(!strcmp(ex->str,curnam))
			return(1);
		return(0);

	case 'T':
		return(1);

	case 'F':
		return(0);

	default:
		return(-1);
	}
}




static	int
eval_truthof(s1,s2,re)
char	*s1;
char	*s2;
int	re;
{
#ifdef	REGEX
	extern	char	*regcmp();
	extern	char	*regex();
#else
	extern	char	*re_comp();
#endif
	char	*xp;
	char	*vp;
	char	*ap;
	char	*tp;
	int	tl = 0;

	/* look for a '.' in the attribute name */
	for(xp = s1; *xp != '\0' && *xp != '.'; xp++)
		;

	if(*xp == '.') {
		tl = xp - s1;
		ap = ++xp;
		tp = s1;
	} else {
		ap = s1;
		tp = (char *)0;
	}

	/* feh */
	if(*ap ==  '\0' || (vp = objattr(curobj,ap,0)) == (char *)0)
		return(0);

	/* specified type ? must match */
	if(tp != (char *)0 && (strncmp(vp,tp,tl) || vp[tl] != ' '))
		return(0);

	/* this eats - already done in objattr really */
	xp = vp;
	/* skip to end of type */
	while(*xp != ' ' && *xp != '\0')
		xp++;
	if(*xp == '\0')
		return(0);
	xp++;

	/* skip to end of attribute name */
	while(*xp != '=' && *xp != '\0')
		xp++;
	if(*xp == '\0')
		return(0);
	xp++;


	/* if not a regular expression, this is simplicty */
	if(!re) {
		if(!strcmp(xp,s2))
			return(1);
		return(0);
	}

#ifdef	REGEX
	if((vp = regcmp(s2, (char *)0)) == (char *)0) {
		fprintf(stderr,"bad regular expression\n");
		exit(1);
	}

	tp = regex(vp,xp);
	free(vp);
	return(tp != (char *)0);
#else
	if((vp = re_comp(s2)) != (char *)0) {
		fprintf(stderr,"bad regular expression: %s\n",vp);
		exit(1);
	}

	return(re_exec(xp));
#endif
}




static	int
eval_expr(ex)
b_exp	*ex;
{
	int	rh;
	int	lh;

	switch(ex->typ) {
	case '|':
		rh = eval_expr(ex->rhs);
		lh = eval_expr(ex->lhs);
		return(lh || rh);

	case '&':
		rh = eval_expr(ex->rhs);
		lh = eval_expr(ex->lhs);
		return(rh == lh);

	case '=':
		if(ex->lhs->typ != 'W' || (ex->rhs->typ != 'W' && ex->rhs->typ != 'R'))
			return(0);
		return(eval_truthof(ex->lhs->str,ex->rhs->str,ex->rhs->typ == 'R' ? 1 : 0));

	default:
		return(eval_subexpr(ex));
	}
}



setexpr(s)
char	*s;
{
	bufp = s;
	if((compiled = build_expr()) == (b_exp *)0)
		return(1);
	return(0);
}



checkexpr(nam,op)
char	*nam;
Obj	*op;
{
	int	ret;

	curnam = nam;
	curobj = op;

	ret = eval_expr(compiled);
	curnam = (char *)0;
	curobj = (Obj *)0;
	return(ret);
}