/* boolexp.c */
#include "copyright.h"
#include "config.h"
#include <stdio.h>
#ifdef STRING_H
#include <string.h>
#else
#include <strings.h>
#endif /* STRING_H */
#include <ctype.h>
#include "teeny.h"
/*
* Routines for parsing, evaluating and displaying boolean expressions.
*/
/*
* Opcodes for our little Boolean expression evaluator.
*/
#define STOP -1
#define AND -2
#define OR -3
#define NOT -4
#define OPEN -5
#define CLOSE -6
#define BADREF -7
#define MALE_LOCK -100
#define FEMALE_LOCK -101
#define NEUTER_LOCK -102
#define WIZARD_LOCK -200
#define ROBOT_LOCK -201
static char *get_tok();
static char *to_infix();
void parse_lock();
static int stack[64]; /* Expressions can't be too big. */
static int work[256];
static char toocomplex[] = "Lock is too complex\r\n";
static char badlock[] = "I don't understand that lock.\r\n";
/*
* We use these structs, an array of them, to build parse trees from our
* internal RPN locks so we can write them out in infix.
*/
struct tree {
int dat;
short left, right; /* Array offsets */
};
/*
* Parses a boolean expression, stuffs it into a hunk of memory, and returns
* a pointer to said hunk. Returns NULL if it can't cope.
*/
int *
bool_parse(player, str)
int player;
char *str;
{
int wp, sp;
int tok, *thelock;
sp = 0;
wp = 1;
if (str == NULL) {
notify_player(player, badlock);
return (NULL);
}
do {
str = get_tok(player, str, &tok);
if (tok == BADREF) {
notify_player(player, badlock);
return (NULL);
}
switch (tok) {
case OPEN:
if (sp < 63) {
stack[sp++] = OPEN;
} else {
notify_player(player, toocomplex);
return (NULL);
}
break;
case CLOSE:
while (sp > 0 && stack[sp - 1] != OPEN && wp < 255) {
work[wp++] = stack[--sp];
}
if (sp == 0 || stack[sp - 1] != OPEN) {
notify_player(player, "Too complex, or unbalanced parens.\r\n");
return (NULL);
}
sp--;
break;
case AND:
if (sp > 0 && stack[sp - 1] == OR) {
notify_player(player, badlock);
return (NULL);
}
while (sp > 0 && stack[sp - 1] == NOT && wp < 255) {
work[wp++] = stack[--sp];
}
if (wp == 255) {
notify_player(player, toocomplex);
return (NULL);
}
if (sp < 63) {
stack[sp++] = AND;
} else {
notify_player(player, toocomplex);
return (NULL);
}
break;
case OR:
if (sp > 0 && (stack[sp - 1] == AND || stack[sp - 1] == NOT)) {
notify_player(player, badlock);
return (NULL);
}
while (sp > 0 && (stack[sp - 1] == NOT ||
stack[sp - 1] == AND) && wp < 255) {
work[wp++] = stack[--sp];
}
if (wp == 255) {
notify_player(player, toocomplex);
return (NULL);
}
if (sp < 63) {
stack[sp++] = OR;
} else {
notify_player(player, toocomplex);
return (NULL);
}
break;
case NOT:
if (sp < 63) {
stack[sp++] = NOT;
} else {
notify_player(player, toocomplex);
return (NULL);
}
break;
case STOP:
while (sp > 0 && wp < 255) {
if (stack[sp - 1] != AND && stack[sp - 1] != OR
&& stack[sp - 1] != NOT) {
notify_player(player, badlock);
return (NULL);
}
work[wp++] = stack[--sp];
}
if (wp == 255) {
notify_player(player, toocomplex);
return (NULL);
}
work[wp++] = STOP;
break;
default:
work[wp++] = tok;
break;
}
} while (tok != STOP);
/* Stow it away somewhere. */
thelock = (int *) ty_malloc(sizeof(int) * wp, "bool_parse");
thelock[0] = --wp;
while (wp > 0) {
thelock[wp] = work[wp];
wp--;
}
return (thelock);
}
/*
* Grabs the next token out a string, and returns a pointer to the next thing
* in the string.
*/
static char *
get_tok(player, p, tok)
char *p;
int *tok;
{
char *q, ch;
int obj;
while (*p && isspace(*p))
p++;
switch (*p) {
case '&':
*tok = AND;
break;
case '|':
*tok = OR;
break;
case '!':
*tok = NOT;
break;
case '(':
*tok = OPEN;
break;
case ')':
*tok = CLOSE;
break;
case '\0':
*tok = STOP;
break;
default:
/* Snarf out an object name */
q = p;
while (*p != '&' && *p != '|' && *p != '!' && *p != ')' && *p) {
p++;
}
p--;
while (isspace(*p))
p--;
ch = p[1];
p[1] = '\0';
if (!strcasecmp(q, "sex:male")) {
obj = MALE_LOCK;
} else if (!strcasecmp(q, "sex:female")) {
obj = FEMALE_LOCK;
} else if (!strcasecmp(q, "sex:it") || !strcasecmp(q, "sex:neuter")) {
obj = NEUTER_LOCK;
} else if (!strcasecmp(q, "flag:wizard")) {
obj = WIZARD_LOCK;
} else if (!strcasecmp(q, "flag:robot")) {
obj = ROBOT_LOCK;
} else
obj = resolve_object(player, q, 1); /* Allow *<playername> */
p[1] = ch;
if (!exists_object(obj) && obj > -2) {
*tok = BADREF;
} else {
*tok = obj;
}
}
/* p points at the last char of the token. */
p++;
return (p);
}
/*
* Writes a boolean expression into a sized buffer.
*/
int bool_display(player, exp, buff, buffsiz)
int player;
int *exp;
char *buff;
int buffsiz;
{
struct tree trees[64];
int tp;
char *p;
int sp;
int count;
if (exp == NULL) {
strcpy(buff, "*UNLOCKED*");
return (1);
}
tp = sp = 0;
count = *exp++;
while (*exp != STOP && count) {
switch (*exp) {
case AND:
case OR:
if (sp < 2 || tp > 63) {
warning("bool_display", "lock bad or too big");
return (-1);
}
trees[tp].dat = *exp;
trees[tp].left = (short) (stack[--sp]);
trees[tp].right = (short) (stack[--sp]);
stack[sp++] = tp++;
break;
case NOT:
if (sp < 1 || tp > 63) {
warning("bool_display", "lock bad or too big");
return (-1);
}
trees[tp].dat = NOT;
trees[tp].right = (short) (stack[--sp]);
stack[sp++] = tp++;
break;
default:
if (sp > 255 || tp > 63) {
warning("bool_display", "lock bad or too big");
return (-1);
}
trees[tp].dat = *exp;
if (sp < 63) {
stack[sp++] = tp++;
} else {
warning("to_infix", "Internal lock too complex!");
return (-1);
}
break;
}
exp++;
}
/* OK. We've built this parse tree thing. Now traverse the tree. */
if (sp != 1) {
warning("bool_display", "bad internal boolean expression");
return (-1);
}
p = to_infix(player, trees, tp - 1, buff, buffsiz);
*p = '\0';
return (0);
}
/*
* Convert a tree to infix. Recursive as hell.
*/
static char *
to_infix(player, trees, t, b, s)
int player;
struct tree *trees;
int t;
char *b;
int s;
{
char *p;
if (s < 5) {
return (b);
}
switch (trees[t].dat) {
case AND:
*b++ = '(';
p = to_infix(player, trees, trees[t].left, b, s);
if (s - (p - b) < 6) {
return (p);
}
*p++ = ' ';
*p++ = '&';
*p++ = ' ';
p = to_infix(player, trees, trees[t].right, p, s - (p - b));
*p++ = ')';
break;
case OR:
*b++ = '(';
p = to_infix(player, trees, trees[t].left, b, s);
if (s - (p - b) < 5) {
return (p);
}
*p++ = ' ';
*p++ = '|';
*p++ = ' ';
p = to_infix(player, trees, trees[t].right, p, s - (p - b));
*p++ = ')';
break;
case NOT:
if (s < 4) {
return (p);
}
*b++ = '!';
*b++ = '(';
p = to_infix(player, trees, trees[t].right, b, s - 2);
*p++ = ')';
break;
case MALE_LOCK:
if (s < 8) {
return (b);
}
strcpy(b, "sex:male");
p = b + 8;
break;
case FEMALE_LOCK:
if (s < 10) {
return (b);
}
strcpy(b, "sex:female");
p = b + 10;
break;
case NEUTER_LOCK:
if (s < 6) {
return (b);
}
strcpy(b, "sex:it");
p = b + 6;
break;
case WIZARD_LOCK:
if (s < 11) {
return (b);
}
strcpy(b, "flag:WIZARD");
p = b + 11;
break;
case ROBOT_LOCK:
if (s < 13) {
return (b);
}
strcpy(b, "flag:ROBOT");
p = b + 13;
break;
default:
if (s < 9) {
return (b);
} else {
char *z;
z = stuff_name(player, trees[t].dat);
if (strlen(z) > (s - 1))
z[s - 2] = 0; /* just trunicate it */
(void) strcpy(b, z);
p = b + strlen(z);
}
break;
}
return (p);
}
/*
* Returns 1 if thing is locked against player, 0 if not.
*/
int islocked(player, thing, code)
int player;
int thing;
int code;
{
int *exp;
int sp, op1, op2;
if ((code != LOCK) && (code != ELOCK)) {
warning("islocked", "bad code");
return (0);
}
if (get_lock_elt(thing, code, &exp) == -1) {
warning("islocked", "bad lock reference");
return (0);
}
if (exp == NULL) { /* No lock */
return (0);
}
/* Run down the expression until you hit a STOP */
exp++; /* Skip the count field */
sp = 0;
while (*exp != STOP) {
if (*exp < 0) {
switch (*exp) {
case NOT:
op1 = stack[--sp];
stack[sp++] = !op1;
break;
case AND:
op1 = stack[--sp];
op2 = stack[--sp];
stack[sp++] = op1 && op2;
break;
case OR:
op1 = stack[--sp];
op2 = stack[--sp];
stack[sp++] = op1 || op2;
break;
case MALE_LOCK:
stack[sp++] = (check_player_gender(player, MALE_LOCK)
? TRUE : FALSE);
break;
case FEMALE_LOCK:
stack[sp++] = (check_player_gender(player, FEMALE_LOCK)
? TRUE : FALSE);
break;
case NEUTER_LOCK:
stack[sp++] = (check_player_gender(player, NEUTER_LOCK)
? TRUE : FALSE);
break;
case WIZARD_LOCK:
stack[sp++] = (iswiz(player) ? TRUE : FALSE);
break;
case ROBOT_LOCK:
stack[sp++] = (isrobot(player) ? TRUE : FALSE);
break;
}
} else {
/* See if the player has this object */
if (sp < 63) {
stack[sp++] = (player_has(player, *exp) ? TRUE : FALSE);
} else {
warning("islocked", "Internal lock too complex!");
return (1);
}
}
exp++;
}
if (sp != 1) {
warning("islocked", "bad internal boolean expression");
return (0);
}
return (!stack[0]);
}
/*
* Basic tester, sees if a player is or has a specific object.
*/
int player_has(player, obj)
int player;
int obj;
{
int current;
if (player == obj) {
return (1);
}
/* Search the list */
if (get_int_elt(player, CONTENTS, ¤t) == -1) {
warning("player_has", "bad contents ref on player");
return (0);
}
while (current != -1) {
if (current == obj) {
return (1);
}
if (get_int_elt(current, NEXT, ¤t) == -1) {
warning("player_has", "bad NEXT ref");
return (0);
}
}
/* Didn't have it, I guess... */
return (0);
}
static int check_player_gender(player, code)
int player;
int code;
{
int flags;
if (get_int_elt(player, FLAGS, &flags) == -1) {
warning("check_player_gender", "bad flags ref on player");
return (0);
}
switch (code) {
case MALE_LOCK:
return ((flags & GENDER_MALE) ? 1 : 0);
break;
case FEMALE_LOCK:
return ((flags & GENDER_FEMALE) ? 1 : 0);
break;
case NEUTER_LOCK:
return ((flags & GENDER_NEUTER) ? 1 : 0);
break;
default:
warning("check_player_gender", "bad code");
return (0);
}
return (0);
}
/* @unlock, @lock moved here from buildcmds.c */
void parse_lock(str, obj, attr)
char *str, **obj;
int *attr;
{
char *p;
*attr = LOCK;
*obj = str;
for (p = str; *p && *p != '/'; p++);
if (*p == '/')
*p++ = 0; /* term obj */
else /* obj is already terminated */
return;
/* parse attr */
if (stringprefix(p, "ELOCK")) {
*attr = ELOCK;
return;
} else if (stringprefix(p, "LOCK")) {
*attr = LOCK;
return;
} else
*attr = -1;
}
voidfunc do_unlock(player, arg)
int player;
char *arg;
{
int attr, obj;
char *objname;
if (!arg || !*arg) {
notify_player(player, "Unlock what?\r\n");
return;
}
parse_lock(arg, &objname, &attr);
if (attr == -1) {
notify_player(player, "No such attribute.\r\n");
return;
}
if ((obj = resolve_object(player, objname, 0)) == -1)
if ((obj = resolve_exit(player, objname)) == -1) {
notify_player(player, "I don't see that here.\r\n");
return;
}
if (obj == -2) {
notify_player(player, "I can't tell which one you mean.\r\n");
return;
}
if (!controls(player, obj)) {
notify_player(player, "You can't unlock that!\r\n");
return;
}
if (set_lock_elt(obj, attr, (int *) NULL) == -1) {
notify_bad(player);
return;
}
notify_player(player, "Unlocked.\r\n");
}
voidfunc do_lock(player, argone, argtwo)
int player;
char *argone;
char *argtwo;
{
int obj, *exp, attr;
char *objname;
if (!argone || !*argone) {
notify_player(player, "Lock what to what?\r\n");
return;
}
parse_lock(argone, &objname, &attr);
if (attr == -1) {
notify_player(player, "No such attribute.\r\n");
return;
}
if ((obj = resolve_object(player, objname, 0)) == -1) {
if ((obj = resolve_exit(player, objname)) == -1) {
notify_player(player, "I don't see that here.\r\n");
return;
}
}
if (obj == -2) {
notify_player(player, "I don't know which one you mean.\r\n");
return;
}
if (!controls(player, obj)) {
notify_player(player, "You can't do that!\r\n");
return;
}
/* OK. We can mess with the lock on this thing. */
exp = bool_parse(player, argtwo);
if (exp == NULL)
return; /* bool_parse() already told the player off. */
if (set_lock_elt(obj, attr, exp) == -1) {
notify_bad(player);
return;
}
notify_player(player, "Locked.\r\n");
}