/* 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;
}