tinymush-3.1p1/game/backups/
tinymush-3.1p1/game/bin/
tinymush-3.1p1/game/data/
tinymush-3.1p1/game/modules/
tinymush-3.1p1/game/modules/old/
tinymush-3.1p1/src/modules/comsys/
tinymush-3.1p1/src/modules/hello/
tinymush-3.1p1/src/modules/mail/
tinymush-3.1p1/src/tools/
/* boolexp.c */
/* $Id: boolexp.c,v 1.30 2004/02/23 04:35:14 rmg Exp $ */

#include "copyright.h"
#include "autoconf.h"
#include "config.h"

#include "alloc.h"	/* required by mudconf */
#include "flags.h"	/* required by mudconf */
#include "htab.h"	/* required by mudconf */
#include "mudconf.h"	/* required by code */

#include "db.h"		/* required by externs */
#include "externs.h"	/* required by code */

#include "match.h"	/* required by code */
#include "attrs.h"	/* required by code */
#include "powers.h"	/* required by code */
#include "ansi.h"	/* required by code */

static int parsing_internal = 0;

/* ---------------------------------------------------------------------------
 * check_attr: indicate if attribute ATTR on player passes key when checked by
 * the object lockobj
 */

static int check_attr(player, lockobj, attr, key)
dbref player, lockobj;
ATTR *attr;
char *key;
{
	char *buff;
	dbref aowner;
	int aflags, alen, checkit;

	buff = atr_pget(player, attr->number, &aowner, &aflags, &alen);
	checkit = 0;

	if (attr->number == A_LCONTROL) {
		/* We can see control locks... else we'd break zones */
		checkit = 1;
	} else if (See_attr(lockobj, player, attr, aowner, aflags)) {
		checkit = 1;
	} else if (attr->number == A_NAME) {
		checkit = 1;
	}
	if (checkit && (!wild_match(key, buff))) {
		checkit = 0;
	}
	free_lbuf(buff);
	return checkit;
}

static dbref lock_originator = NOTHING;	/* grotesque hack */

int eval_boolexp(player, thing, from, b)
dbref player, thing, from;
BOOLEXP *b;
{
	dbref aowner, obj, source;
	int aflags, alen, c, checkit;
	char *key, *buff, *buff2, *bp, *str;
	ATTR *a;
	GDATA *preserve;

	if (b == TRUE_BOOLEXP)
		return 1;

	switch (b->type) {
	case BOOLEXP_AND:
		return (eval_boolexp(player, thing, from, b->sub1) &&
			eval_boolexp(player, thing, from, b->sub2));
	case BOOLEXP_OR:
		return (eval_boolexp(player, thing, from, b->sub1) ||
			eval_boolexp(player, thing, from, b->sub2));
	case BOOLEXP_NOT:
		return !eval_boolexp(player, thing, from, b->sub1);
	case BOOLEXP_INDIR:
		/*
		 * BOOLEXP_INDIR (i.e. @) is a unary operation which is replaced at
		 * evaluation time by the lock of the object whose number is the
		 * argument of the operation.
		 */

		mudstate.lock_nest_lev++;
		if (mudstate.lock_nest_lev >= mudconf.lock_nest_lim) {
			STARTLOG(LOG_BUGS, "BUG", "LOCK")
				log_name_and_loc(player);
			log_printf(": Lock exceeded recursion limit.");
			ENDLOG
				notify(player, "Sorry, broken lock!");
			mudstate.lock_nest_lev--;
			return (0);
		}
		if ((b->sub1->type != BOOLEXP_CONST) || (b->sub1->thing < 0)) {
			STARTLOG(LOG_BUGS, "BUG", "LOCK")
				log_name_and_loc(player);
			log_printf(": Lock had bad indirection (%c, type %d)",
				   INDIR_TOKEN, b->sub1->type);
			ENDLOG
				notify(player, "Sorry, broken lock!");
			mudstate.lock_nest_lev--;
			return (0);
		}
		key = atr_get(b->sub1->thing, A_LOCK, &aowner, &aflags, &alen);
		lock_originator = thing;
		c = eval_boolexp_atr(player, b->sub1->thing, from, key);
		lock_originator = NOTHING;
		free_lbuf(key);
		mudstate.lock_nest_lev--;
		return (c);
	case BOOLEXP_CONST:
		return (b->thing == player ||
			member(b->thing, Contents(player)));
	case BOOLEXP_ATR:
		a = atr_num(b->thing);
		if (!a)
			return 0;	/*
					 * no such attribute 
					 */

		/*
		 * First check the object itself, then its contents 
		 */

		if (check_attr(player, from, a, (char *)b->sub1))
			return 1;
		DOLIST(obj, Contents(player)) {
			if (check_attr(obj, from, a, (char *)b->sub1))
				return 1;
		}
		return 0;
	case BOOLEXP_EVAL:
		a = atr_num(b->thing);
		if (!a)
			return 0;	/*
					 * no such attribute 
					 */
		source = from;
		buff = atr_pget(from, a->number, &aowner, &aflags, &alen);
		if (!*buff) {
			free_lbuf(buff);
			buff = atr_pget(thing, a->number, &aowner,
					&aflags, &alen);
			source = thing;
		}
		checkit = 0;
		
		if ((a->number == A_NAME) || (a->number == A_LCONTROL)) {
			checkit = 1;
		} else if (Read_attr(source, source, a, aowner, aflags)) {
			checkit = 1;
		}
		if (checkit) {
		        preserve = save_global_regs("eval_boolexp_save");
			buff2 = bp = alloc_lbuf("eval_boolexp");
			str = buff;
			exec(buff2, &bp, source, 
			     ((lock_originator == NOTHING) ?
			      player : lock_originator),
			     player, EV_FCHECK | EV_EVAL | EV_TOP,
			     &str, (char **)NULL, 0);
 			restore_global_regs("eval_boolexp_save", preserve);
			checkit = !string_compare(buff2, (char *)b->sub1);
			free_lbuf(buff2);
		}
		free_lbuf(buff);
		return checkit;
	case BOOLEXP_IS:

		/*
		 * If an object check, do that 
		 */

		if (b->sub1->type == BOOLEXP_CONST)
			return (b->sub1->thing == player);

		/*
		 * Nope, do an attribute check 
		 */

		a = atr_num(b->sub1->thing);
		if (!a)
			return 0;
		return (check_attr(player, from, a, (char *)(b->sub1)->sub1));
	case BOOLEXP_CARRY:

		/*
		 * If an object check, do that 
		 */

		if (b->sub1->type == BOOLEXP_CONST)
			return (member(b->sub1->thing, Contents(player)));

		/*
		 * Nope, do an attribute check 
		 */

		a = atr_num(b->sub1->thing);
		if (!a)
			return 0;
		DOLIST(obj, Contents(player)) {
			if (check_attr(obj, from, a, (char *)(b->sub1)->sub1))
				return 1;
		}
		return 0;
	case BOOLEXP_OWNER:
		return (Owner(b->sub1->thing) == Owner(player));
	default:
	    fprintf(mainlog_fp, "ABORT! boolexp.c, unknown boolexp type in eval_boolexp().\n");
	    abort();		/* bad type */
	    return 0;		/* NOTREACHED */
	}
}

int eval_boolexp_atr(player, thing, from, key)
dbref player, thing, from;
char *key;
{
	BOOLEXP *b;
	int ret_value;

	b = parse_boolexp(player, key, 1);
	if (b == NULL) {
		ret_value = 1;
	} else {
		ret_value = eval_boolexp(player, thing, from, b);
		free_boolexp(b);
	}
	return (ret_value);
}

/* If the parser returns TRUE_BOOLEXP, you lose TRUE_BOOLEXP cannot be typed
 * in by the user; use @unlock instead
 */

static char *parsebuf, *parsestore;
static dbref parse_player;

static void NDECL(skip_whitespace)
{
	while (*parsebuf && isspace(*parsebuf))
		parsebuf++;
}

static BOOLEXP *NDECL(parse_boolexp_E);		/* defined below */

static BOOLEXP *test_atr(s)
char *s;
{
	ATTR *attrib;
	BOOLEXP *b;
	char *buff, *s1;
	int anum, locktype;

	buff = alloc_lbuf("test_atr");
	StringCopy(buff, s);
	for (s = buff; *s && (*s != ':') && (*s != '/'); s++) ;
	if (!*s) {
		free_lbuf(buff);
		return ((BOOLEXP *) NULL);
	}
	if (*s == '/')
		locktype = BOOLEXP_EVAL;
	else
		locktype = BOOLEXP_ATR;

	*s++ = '\0';
	/*
	 * see if left side is valid attribute.  Access to attr is checked on 
	 * eval.  Also allow numeric references to attributes.  It can't hurt
	 * us, and lets us import stuff that stores attr locks by number
	 * instead of by name. 
	 */
	if (!(attrib = atr_str(buff))) {

		/*
		 * Only #1 can lock on numbers 
		 */
		if (!God(parse_player)) {
			free_lbuf(buff);
			return ((BOOLEXP *) NULL);
		}
		s1 = buff;
		for (s1 = buff; isdigit(*s1); s1++) ;
		if (*s1) {
			free_lbuf(buff);
			return ((BOOLEXP *) NULL);
		}
		anum = atoi(buff);
		if (anum <= 0) {
			free_lbuf(buff);
			return ((BOOLEXP *) NULL);
		}
	} else {
		anum = attrib->number;
	}

	/*
	 * made it now make the parse tree node 
	 */
	b = alloc_bool("test_atr");
	b->type = locktype;
	b->thing = (dbref) anum;
	b->sub1 = (BOOLEXP *) XSTRDUP(s, "test_atr.sub1");
	free_lbuf(buff);
	return (b);
}

/*
 * L -> (E); L -> object identifier 
 */

static BOOLEXP *NDECL(parse_boolexp_L)
{
	BOOLEXP *b;
	char *p, *buf;
	MSTATE mstate;


	buf = NULL;
	skip_whitespace();

	switch (*parsebuf) {
	case '(':
		parsebuf++;
		b = parse_boolexp_E();
		skip_whitespace();
		if (b == TRUE_BOOLEXP || *parsebuf++ != ')') {
			free_boolexp(b);
			return TRUE_BOOLEXP;
		}
		break;
	default:

		/* must have hit an object ref.  Load the name into our 
		 * buffer */

		buf = alloc_lbuf("parse_boolexp_L");
		p = buf;
		while (*parsebuf && (*parsebuf != AND_TOKEN) &&
		       (*parsebuf != OR_TOKEN) && (*parsebuf != ')')) {
			*p++ = *parsebuf++;
		}

		/* strip trailing whitespace */

		*p-- = '\0';
		while (isspace(*p))
			*p-- = '\0';

		/* check for an attribute */

		if ((b = test_atr(buf)) != NULL) {
			free_lbuf(buf);
			return (b);
		}
		b = alloc_bool("parse_boolexp_L");
		b->type = BOOLEXP_CONST;

		/* Do the match.
		 * If we are parsing a boolexp that was a stored lock then
		 * we know that object refs are all dbrefs, so we skip the
		 * expensive match code.
		 */


		if (!mudstate.standalone) {
			if (parsing_internal) {
				if (buf[0] != '#') {
					free_lbuf(buf);
					free_bool(b);
					return TRUE_BOOLEXP;
				}
				b->thing = atoi(&buf[1]);
				if (!Good_obj(b->thing)) {
					free_lbuf(buf);
					free_bool(b);
					return TRUE_BOOLEXP;
				}
			} else {
				save_match_state(&mstate);
				init_match(parse_player, buf, TYPE_THING);
				match_everything(MAT_EXIT_PARENTS);
				b->thing = match_result();
				restore_match_state(&mstate);
			}

			if (b->thing == NOTHING) {
				notify(parse_player,
				       tprintf("I don't see %s here.", buf));
				free_lbuf(buf);
				free_bool(b);
				return TRUE_BOOLEXP;
			}
			if (b->thing == AMBIGUOUS) {
				notify(parse_player,
				       tprintf("I don't know which %s you mean!",
					       buf));
				free_lbuf(buf);
				free_bool(b);
				return TRUE_BOOLEXP;
			}
		} else {
			if (buf[0] != '#') {
				free_lbuf(buf);
				free_bool(b);
				return TRUE_BOOLEXP;
			}
			b->thing = atoi(&buf[1]);
			if (b->thing < 0) {
				free_lbuf(buf);
				free_bool(b);
				return TRUE_BOOLEXP;
			}
		}
		free_lbuf(buf);
	}
	return b;
}

/*
 * F -> !F; F -> @L; F -> =L; F -> +L; F -> $L 
 */
/*
 * The argument L must be type BOOLEXP_CONST 
 */

static BOOLEXP *NDECL(parse_boolexp_F)
{
	BOOLEXP *b2;

	skip_whitespace();
	switch (*parsebuf) {
	case NOT_TOKEN:
		parsebuf++;
		b2 = alloc_bool("parse_boolexp_F.not");
		b2->type = BOOLEXP_NOT;
		if ((b2->sub1 = parse_boolexp_F()) == TRUE_BOOLEXP) {
			free_boolexp(b2);
			return (TRUE_BOOLEXP);
		} else
			return (b2);
	case INDIR_TOKEN:
		parsebuf++;
		b2 = alloc_bool("parse_boolexp_F.indir");
		b2->type = BOOLEXP_INDIR;
		b2->sub1 = parse_boolexp_L();
		if ((b2->sub1) == TRUE_BOOLEXP) {
			free_boolexp(b2);
			return (TRUE_BOOLEXP);
		} else if ((b2->sub1->type) != BOOLEXP_CONST) {
			free_boolexp(b2);
			return (TRUE_BOOLEXP);
		} else
			return (b2);
	case IS_TOKEN:
		parsebuf++;
		b2 = alloc_bool("parse_boolexp_F.is");
		b2->type = BOOLEXP_IS;
		b2->sub1 = parse_boolexp_L();
		if ((b2->sub1) == TRUE_BOOLEXP) {
			free_boolexp(b2);
			return (TRUE_BOOLEXP);
		} else if (((b2->sub1->type) != BOOLEXP_CONST) &&
			   ((b2->sub1->type) != BOOLEXP_ATR)) {
			free_boolexp(b2);
			return (TRUE_BOOLEXP);
		} else
			return (b2);
	case CARRY_TOKEN:
		parsebuf++;
		b2 = alloc_bool("parse_boolexp_F.carry");
		b2->type = BOOLEXP_CARRY;
		b2->sub1 = parse_boolexp_L();
		if ((b2->sub1) == TRUE_BOOLEXP) {
			free_boolexp(b2);
			return (TRUE_BOOLEXP);
		} else if (((b2->sub1->type) != BOOLEXP_CONST) &&
			   ((b2->sub1->type) != BOOLEXP_ATR)) {
			free_boolexp(b2);
			return (TRUE_BOOLEXP);
		} else
			return (b2);
	case OWNER_TOKEN:
		parsebuf++;
		b2 = alloc_bool("parse_boolexp_F.owner");
		b2->type = BOOLEXP_OWNER;
		b2->sub1 = parse_boolexp_L();
		if ((b2->sub1) == TRUE_BOOLEXP) {
			free_boolexp(b2);
			return (TRUE_BOOLEXP);
		} else if ((b2->sub1->type) != BOOLEXP_CONST) {
			free_boolexp(b2);
			return (TRUE_BOOLEXP);
		} else
			return (b2);
	default:
		return (parse_boolexp_L());
	}
}

/*
 * T -> F; T -> F & T 
 */

static BOOLEXP *NDECL(parse_boolexp_T)
{
	BOOLEXP *b, *b2;

	if ((b = parse_boolexp_F()) != TRUE_BOOLEXP) {
		skip_whitespace();
		if (*parsebuf == AND_TOKEN) {
			parsebuf++;

			b2 = alloc_bool("parse_boolexp_T");
			b2->type = BOOLEXP_AND;
			b2->sub1 = b;
			if ((b2->sub2 = parse_boolexp_T()) == TRUE_BOOLEXP) {
				free_boolexp(b2);
				return TRUE_BOOLEXP;
			}
			b = b2;
		}
	}
	return b;
}

/*
 * E -> T; E -> T | E 
 */

static BOOLEXP *NDECL(parse_boolexp_E)
{
	BOOLEXP *b, *b2;

	if ((b = parse_boolexp_T()) != TRUE_BOOLEXP) {
		skip_whitespace();
		if (*parsebuf == OR_TOKEN) {
			parsebuf++;

			b2 = alloc_bool("parse_boolexp_E");
			b2->type = BOOLEXP_OR;
			b2->sub1 = b;
			if ((b2->sub2 = parse_boolexp_E()) == TRUE_BOOLEXP) {
				free_boolexp(b2);
				return TRUE_BOOLEXP;
			}
			b = b2;
		}
	}
	return b;
}

BOOLEXP *parse_boolexp(player, buf, internal)
dbref player;
const char *buf;
int internal;
{
	char *p;
	int num_opens = 0;
	BOOLEXP *ret;
	
	if (!internal) {
	    /* Don't allow funky characters in locks.
	     * Don't allow unbalanced parentheses.
	     */
	    for (p = (char *) buf; *p; p++) {
		if ((*p == '\t') || (*p == '\r') || (*p == '\n') ||
		    (*p == ESC_CHAR)) {
		    return (TRUE_BOOLEXP);
		}
		if (*p == '(') {
		    num_opens++;
		} else if (*p == ')') {
		    if (num_opens > 0) {
			num_opens--;
		    } else {
			return (TRUE_BOOLEXP);
		    }
		}
	    }
	    if (num_opens != 0)
		return (TRUE_BOOLEXP);
	}

	if ((buf == NULL) || (*buf == '\0'))
		return (TRUE_BOOLEXP);

	parsestore = parsebuf = alloc_lbuf("parse_boolexp");
	StringCopy(parsebuf, buf);
	parse_player = player;
	if (!mudstate.standalone)
		parsing_internal = internal;
	ret = parse_boolexp_E();
	free_lbuf(parsestore);
	return ret;
}