/* boolexp.c */
#include "copyright.h"
#ifdef WANT_ANSI
#ifdef __STDC__
#include <stddef.h>
#include <stdlib.h>
#endif /* __STDC__ */
#endif /* WANT_ANSI */
#include "db.h"
#include "match.h"
#include "mudconf.h"
#include "externs.h"
#include "interface.h"
#ifndef STANDALONE
/* ---------------------------------------------------------------------------
* check_attr: indicate if attribute ATTR on player passes key when checked by
* the object lockobj
*/
int check_attr (dbref player, dbref lockobj, ATTR *attr, char *key)
{
char *buff;
dbref aowner;
int aflags, checkit;
buff = atr_pget(player, attr->number, &aowner, &aflags);
checkit = 0;
if (See_attr(lockobj, player, attr, aowner)) {
checkit = 1;
#ifdef ATR_NAME
} else if (attr->number == A_NAME) {
checkit = 1;
#endif
}
if (checkit && (!wild_match(key, buff, (char **)NULL, 0))) {
checkit = 0;
}
free_lbuf(buff);
return checkit;
}
int eval_boolexp(dbref player, dbref thing, struct boolexp * b)
{
dbref aowner, obj;
int aflags, c;
char *key, *buff;
ATTR *a;
if (b == TRUE_BOOLEXP) return 1;
switch (b->type) {
case BOOLEXP_AND:
return (eval_boolexp(player, thing, b->sub1) &&
eval_boolexp(player, thing, b->sub2));
case BOOLEXP_OR:
return (eval_boolexp(player, thing, b->sub1) ||
eval_boolexp(player, thing, b->sub2));
case BOOLEXP_NOT:
return !eval_boolexp(player, thing, 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) {
#ifndef STANDALONE
STARTLOG(LOG_BUGS,"BUG","LOCK")
log_name_and_loc(player);
log_text((char *)": Lock exceeded recursion limit.");
ENDLOG
notify(player, "Sorry, broken lock!");
#else
fprintf(stderr, "Lock exceeded recursion limit.\n");
#endif
mudstate.lock_nest_lev--;
return (0);
}
if ((b->sub1->type != BOOLEXP_CONST) || (b->sub1->thing < 0)) {
#ifndef STANDALONE
STARTLOG(LOG_BUGS,"BUG","LOCK")
log_name_and_loc(player);
buff = alloc_mbuf("eval_boolexp.LOG.indir");
sprintf(buff,
": Lock had bad indirection (%c, type %d)",
INDIR_TOKEN, b->sub1->type);
log_text(buff);
free_mbuf(buff);
ENDLOG
notify(player, "Sorry, broken lock!");
#else
fprintf(stderr, "Broken lock.\n");
#endif
mudstate.lock_nest_lev--;
return (0);
}
key = atr_get(b->sub1->thing, A_LOCK, &aowner, &aflags);
c = eval_boolexp_atr(player, thing, key);
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, thing, a, (char *)b->sub1)) return 1;
DOLIST(obj, Contents(player)) {
if (check_attr(obj, thing, a, (char *)b->sub1))
return 1;
}
return 0;
case BOOLEXP_IS:
return (b->sub1->thing == player);
case BOOLEXP_CARRY:
return (member(b->sub1->thing, Contents(player)));
case BOOLEXP_OWNER:
return (Owner(b->sub1->thing) == Owner(player));
default:
abort(); /* bad type */
return 0;
}
}
int eval_boolexp_atr(dbref player, dbref thing, char *key)
{
struct boolexp *b;
int ret_value;
b = parse_boolexp(player, key);
if (b == NULL)
ret_value = 1;
else
ret_value = eval_boolexp(player, thing, b);
free_boolexp(b);
return (ret_value);
}
#endif
/* If the parser returns TRUE_BOOLEXP, you lose */
/* TRUE_BOOLEXP cannot be typed in by the user; use @unlock instead */
static const char *parsebuf;
static char parsestore[LBUF_SIZE];
static dbref parse_player;
static void skip_whitespace()
{
while (*parsebuf && isspace(*parsebuf))
parsebuf++;
}
static struct boolexp *parse_boolexp_E(); /* defined below */
static struct boolexp *test_atr(char *s)
{
ATTR *attrib;
struct boolexp *b;
char *buff, *s1;
int anum;
buff = alloc_lbuf("test_atr");
strcpy(buff, s);
for (s = buff; *s && (*s != ':'); s++) ;
if (!*s) {
free_lbuf(buff);
return((struct boolexp *)NULL);
}
*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)) return((struct boolexp *)NULL);
s1 = buff;
for (s1=buff; isdigit(*s1); s1++) ;
if (*s1) {
free_lbuf(buff);
return((struct boolexp *)NULL);
}
anum = atoi(buff);
} else {
anum = attrib->number;
}
/* made it now make the parse tree node */
b = alloc_bool("test_str");
b->type = BOOLEXP_ATR;
b->thing = (dbref)anum;
b->sub1 = (struct boolexp *)strsave(s);
free_lbuf(buff);
return (b);
}
/* L -> (E); L -> object identifier */
static struct boolexp *
parse_boolexp_L()
{
struct boolexp *b;
char *p, *buf;
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 */
#ifndef STANDALONE
init_match(parse_player, buf, TYPE_THING);
match_neighbor();
match_possession();
match_me();
match_absolute();
match_player();
b->thing = match_result();
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 /* STANDALONE ... had better be #<num> or we're hosed */
if (buf[0] != '#') {
free_lbuf(buf);
free_bool(b);
return TRUE_BOOLEXP;
}
b->thing = atol(&buf[1]);
if (b->thing < 0) {
free_lbuf(buf);
free_bool(b);
return TRUE_BOOLEXP;
}
#endif
free_lbuf(buf);
}
return b;
}
/* F -> !F; F -> @L; F -> =L; F -> +L; F -> $L */
/* The argument to ` must be type BOOLEXP_CONST */
static struct boolexp *
parse_boolexp_F()
{
struct 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);
/*NOTREACHED*/
break;
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);
/*NOTREACHED*/
break;
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) {
free_boolexp(b2);
return (TRUE_BOOLEXP);
} else
return (b2);
/*NOTREACHED*/
break;
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) {
free_boolexp(b2);
return (TRUE_BOOLEXP);
} else
return (b2);
/*NOTREACHED*/
break;
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);
/*NOTREACHED*/
break;
default:
return (parse_boolexp_L());
}
}
/* T -> F; T -> F & T */
static struct boolexp *
parse_boolexp_T()
{
struct boolexp *b;
struct boolexp *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 struct boolexp *
parse_boolexp_E()
{
struct boolexp *b;
struct boolexp *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;
}
struct boolexp *
parse_boolexp(dbref player, const char *buf)
{
strcpy(parsestore, buf);
parsebuf = parsestore;
parse_player = player;
if ((buf == NULL) || (*buf == '\0'))
return (TRUE_BOOLEXP);
return parse_boolexp_E();
}