/* 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
#ifdef GENDER_BOOL_LOCK
#define MALE_LOCK -100
#define FEMALE_LOCK -101
#define NEUTER_LOCK -102
#endif /* GENDER_BOOL_LOCK */
static char *get_tok();
static char *to_infix();
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;
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';
#ifdef GENDER_BOOL_LOCK
if (!strcmp(q, "sex:male"))
{
obj = MALE_LOCK;
} else
if (!strcmp(q, "sex:female"))
{
obj = FEMALE_LOCK;
} else
if (!strcmp(q, "sex:it") || !strcmp(q, "sex:neuter"))
{
obj = NEUTER_LOCK;
} else
#endif /* GENDER_BOOL_LOCK */
obj = resolve_object(player, q, 1); /* Allow *<playername> */
p[1] = ch;
if (obj == -1)
{
*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 (0);
}
tp = sp = 0;
count = *exp++;
while (*exp != STOP && count)
{
switch (*exp)
{
case AND:
case OR:
if (sp < 2 || tp > 63)
{
log_error("bool_display, player was #%d\n", player);
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, ret;
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;
#ifdef GENDER_BOOL_LOCK
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;
#endif /* GENDER_BOOL_LOCK */
default:
if (s < 9)
{
return (b);
}
ret = stuff_name(player, trees[t].dat, b, s - 1);
if (ret == -1)
{
strcpy(b, "<unknown>");
p = b + 9;
} else
{
p = b + ret;
}
break;
}
return (p);
}
/*
* Returns 1 if thing is locked against player, 0 if not.
*/
int
islocked(player, thing)
int player;
int thing;
{
int *exp;
int sp, op1, op2;
int ret;
if (ret = isflaglocked(player, thing))
return (ret);
if (get_lock_elt(thing, LOCK, &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;
#ifdef GENDER_BOOL_LOCK
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;
#endif /* GENDER_BOOL_LOCK */
}
} 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)
{
log_error("islocked: player = %d, thing = %d\n", player, thing);
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);
}
#ifdef GENDER_BOOL_LOCK
static int
check_player_gender(player, code)
int player;
int code;
{
char *gender;
if (get_str_elt(player, GENDER, &gender) == -1)
{
warning("check_player_gender", "bad gender ref on player");
return (0);
}
if (gender == NULL)
return (0);
switch (code)
{
case MALE_LOCK:
if (*gender == 'm' || *gender == 'M')
{
return (1);
} else
{
return (0);
}
break;
case FEMALE_LOCK:
if (*gender == 'f' || *gender == 'F')
{
return (1);
} else
{
return (0);
}
break;
case NEUTER_LOCK:
if (*gender == 'i' || *gender == 'I' || *gender == 'n' ||
*gender == 'N')
{
return (1);
} else
{
return (0);
}
break;
default:
warning("check_player_gender", "bad code");
return (0);
}
return (0);
}
#endif /* GENDER_BOOL_LOCK */
int
isflaglocked(player, thing)
int player;
int thing;
{
if (ishaven(thing) && ishaven(player))
{
return 1;
} else
{
if (isexit(thing) && istemple(thing) && istemple(player))
{
return 1;
} else
{
if (iswiz(thing) && !iswiz(player))
{
return 1;
} else
{
return 0;
}
}
}
}