/
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/RCS/bool.c,v 1.3 91/09/19 12:56:24 mjr Exp $";
#endif

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


#include	<ctype.h>

#include	"mud.h"
#include	"match.h"
#include	"vars.h"
#include	"sbuf.h"

/*
Zen recursive descent boolean evaluation. Code by Molly, 1991.
Some of this code is deliberately weird. live with it.
*/


/* test if x is a token char */
#define	ISTOK(x) (x =='!'||x =='('||x ==')'||x == '&'||x=='|')


/* WARNING! this is initted statically and deinnited in the main evaluator. */
static	Sbuf	sb;
static	char	*bp;
static	int	lasttok = '$';

static	char	*whop;
static	char	*wherep;
static	int	checkmode = 0;
static	int	b_expr();


static	int
evaluate_truth(s,withflg)
char	*s;
int	withflg;
{
	char	*lp;

	if(whop == (char *)0)
		return(0);

	if(!strcmp(s,whop))
		return(1);

	/* check the stuff the guy is carrying */
	if(ut_listchk(whop,var_cont,s))
		return(1);

	/* check the stuff the guy is using */
	if((lp = ut_getatt(whop,0,typ_obj,var_using,(char *)0)) != (char *)0) {
		if(!strcmp(s,lp))
			return(1);
	}

	/* if with flag is set, then check if the thing is in the room or
	is another player in the room */
	if(withflg && wherep != (char *)0) {
		if(ut_listchk(wherep,var_ply,s)
		|| ut_listchk(wherep,var_cont,s))
			return(1);
	}

	return(0);
}

static	int
nexttok()
{
	int	quot = 0;

	while(isspace(*bp))
		bp++;

	if(ISTOK(*bp)) {
		quot = *bp++;
		return(quot);
	}

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

	sbuf_reset(&sb);
	while(*bp != '\0') {
		/* enter quote */
		if(!quot && (*bp == '\'' || *bp == '\"')) {
			quot = *bp++;
			continue;
		}

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

		/* done word */
		if(!quot && (isspace(*bp) || ISTOK(*bp))) {
			sbuf_put('\0',&sb);
			break;
		}

		/* handle escapes */
		if(*bp == '\\') {
			bp++;
			if(*bp != '\0') {
				sbuf_put(*bp,&sb);
				bp++;
			}
			continue;
		}

		/* default - just copy */
		sbuf_put(*bp,&sb);
		bp++;
	}
	if(sbuf_len(&sb) >= 0) { /* sbuf_len() return char count - 1 */
		sbuf_put('\0',&sb);
		if(!strcmp(sbuf_buf(&sb),"with"))
			return('+');
		return('^');
	}
	return('$');
}




static	int
b_subexpr()
{
	int	x;

	switch(lasttok = nexttok()) {
	case 'T':
		return(1);


	case 'F':
		return(0);


	case '^':
		if(checkmode)
			return(1);
		return(evaluate_truth(sbuf_buf(&sb),0));


	case '+':
		lasttok = nexttok();
		if(lasttok != '^') {
			if(checkmode && whop != (char *)0)
				say(whop,"missing WITH item\n",(char *)0);
			return(-1);
		}
		if(checkmode)
			return(1);
		return(evaluate_truth(sbuf_buf(&sb),1));


	case '!':
		if((x = b_subexpr()) == -1) {
			if(checkmode && whop != (char *)0)
				say(whop,"missing subexpression after '!'\n",(char *)0);
			return(-1);
		}
		return(!x);


	case '(':
		x = b_expr();
		if(lasttok != ')') {
			if(checkmode && whop != (char *)0)
				say(whop,"missing right parenthesis\n",(char *)0);
			return(-1);
		}
		return(x);


	case '$':
		return('$');


	default:
		if(checkmode && whop != (char *)0) {
			say(whop,"syntax error",(char *)0);
			if(bp != (char *)0 && *bp != '\0')
				say(whop," near \"",bp,"\"",(char *)0);
			say(whop,"\n",(char *)0);
		}
		return(-1);
	}
}




static	int
b_expr()
{
	int	ret = b_subexpr();
	int	x;

	for(;;) {
		switch(lasttok = nexttok()) {
		case	'&':
			if((x = b_subexpr()) == -1) {
				if(checkmode && whop != (char *)0)
					say(whop,"missing AND clause\n",(char *)0);
				return(-1);
			}

			if(x == '$') {
				if(checkmode && whop != (char *)0)
					say(whop,"missing subexpression\n",(char *)0);
				return(-1);
			}

			ret = (ret && x);
			break;

		case	'|':
			if((x = b_subexpr()) == -1) {
				if(checkmode && whop != (char *)0)
					say(whop,"missing OR clause\n",(char *)0);
				return(-1);
			}

			if(x == '$') {
				if(checkmode && whop != (char *)0)
					say(whop,"missing subexpression\n",(char *)0);
				return(-1);
			}

			ret = (ret || x);
			break;

		default:
			return(ret);
		}
	}
}



int
bool_eval(who,where,s,check)
char	*who;
char	*where;
char	*s;
int	check;
{
	int	ret;

	bp = s;
	whop = who;
	wherep = where;
	checkmode = check;

	sbuf_initstatic(&sb);
	ret = b_expr();
	if(ret < 0 && checkmode && whop != (char *)0)
		say(whop,"invalid boolean expression.\n",(char *)0);
	sbuf_freestatic(&sb);
	whop = (char *)0;
	wherep = (char *)0;
	checkmode = 0;
	return(ret);
}



/*
check a lock.
*/
int
bool_locked(who,what,where,lockvar,deflt)
char	*who;
char	*what;
char	*where;
char	*lockvar;
int	deflt;
{
	char	*lockp;

	lockp = ut_getatt(what,0,typ_bool,lockvar,(char *)0);
	if(lockp == (char *)0)
		return(deflt);
	return(bool_eval(who,where,lockp,0) == 0);
}



/*
check a lock's syntax.
*/
int
bool_syntax(who,exp)
char	*who;
char	*exp;
{
	int	ret;

	ret = bool_eval(who,(char *)0,exp,1);
	if(ret == -1)
		return(1);
	return(0);
}



/*
Match a string against the environment, and paste the object
ID in to the specified Sbuf.
*/
static int
bool_pasteID(who,what,s)
char	*who;
char	*what;
Sbuf	*s;
{
	char	objid[MAXOID];
	char	*where;

	where = ut_loc(who);
	if(!matchlocal(who,what,where,
		MTCH_UNIQ | MTCH_NONLOC | MTCH_MEOK,objid)){
			sbuf_strcat(objid,s);
			sbuf_unput(s); /* lose trailing \0 */
			return(0);
	}
	return(-1);
}




/*
Re-write a lock, matching junk against the environment and generating
object IDs.
*/
bool_rewrite(who,s,out)
char	*who;
char	*s;
Sbuf	*out; /* pre-initted */
{
	char	quot = '\0';
	char	ch;
	char	*thing = "";
	char	*p;

	while(*s){

		/* End a quoted thing */
		if(quot && *s == quot){
			*s = '\0';

			if(bool_pasteID(who,thing,out)){
				*s = quot;
				return(-1);
			}

			*s++ = quot;
			quot = '\0';
			continue;
		}


		/* Make the rest of these clauses if(!quot && <stuff> */
		if(quot) {
			s++;
			continue;
		}

		/* Skip white-boy space. Marcus-like */
		if(isspace(*s)) {
			s++;
			continue;
		}

		/* Copy TOK type things blindly */
		if(ISTOK(*s) ||
			((*s == 'T' || *s == 'F') &&
			(ISTOK(*(s+1)) || isspace(*(s+1)) || *(s+1) == '\0'))){
			sbuf_put(*s++,out);
			continue;
		}

		/* Start quoted thing */
		if(*s == '\'' || *s == '\"'){
			quot = *s++;
			thing = s;
			continue;
		}

		if(!strncmp("with ",s,5)){
			s += 5;
			sbuf_strcat("with ",out);
			sbuf_unput(out); /* lose trailing \0 */
			continue;
		}

		/* Grub out an un-quoted thing name */
		thing = s;
		p = (char *)0;
		while(!ISTOK(*s) && *s){
			if(!isspace(*s) && isspace(s[1]))
				p = s + 1;
			s++;
		}

		if(!p)
			p = s;

		ch = *p;
		*p = '\0';

		if(bool_pasteID(who,thing,out)){
			*s = ch;
			return(-1);
		}
		*p = ch;
	}

	/* OK. Fell off end of string. Check for unclosed quoted thing */
	if(quot){
		if(bool_pasteID(who,thing,out))
			return(-1);
	}
	sbuf_put('\0',out);
	return(0);
		
}