/*
Copyright (C) 1991, Marcus J. Ranum. All rights reserved.
*/
#ifndef lint
static char RCSid[] = "$Header: /usr/users/mjr/hacks/umud/RCS/bool.c,v 1.3 91/09/19 12:56:24 mjr Exp $";
#endif
/* configure all options BEFORE including system stuff. */
#include "config.h"
#include <ctype.h>
#include "mud.h"
#include "match.h"
#include "vars.h"
#include "sbuf.h"
/*
Zen recursive descent boolean evaluation. Code by Molly, 1991.
Some of this code is deliberately weird. live with it.
*/
/* test if x is a token char */
#define ISTOK(x) (x =='!'||x =='('||x ==')'||x == '&'||x=='|')
/* WARNING! this is initted statically and deinnited in the main evaluator. */
static Sbuf sb;
static char *bp;
static int lasttok = '$';
static char *whop;
static char *wherep;
static int checkmode = 0;
static int b_expr();
static int
evaluate_truth(s,withflg)
char *s;
int withflg;
{
char *lp;
if(whop == (char *)0)
return(0);
if(!strcmp(s,whop))
return(1);
/* check the stuff the guy is carrying */
if(ut_listchk(whop,var_cont,s))
return(1);
/* check the stuff the guy is using */
if((lp = ut_getatt(whop,0,typ_obj,var_using,(char *)0)) != (char *)0) {
if(!strcmp(s,lp))
return(1);
}
/* if with flag is set, then check if the thing is in the room or
is another player in the room */
if(withflg && wherep != (char *)0) {
if(ut_listchk(wherep,var_ply,s)
|| ut_listchk(wherep,var_cont,s))
return(1);
}
return(0);
}
static int
nexttok()
{
int quot = 0;
while(isspace(*bp))
bp++;
if(ISTOK(*bp)) {
quot = *bp++;
return(quot);
}
if((*bp == 'T' || *bp == 'F') && (ISTOK(*(bp+1)) || isspace(*(bp+1)) || *(bp+1) == '\0')) {
quot = *bp++;
return(quot);
}
sbuf_reset(&sb);
while(*bp != '\0') {
/* enter quote */
if(!quot && (*bp == '\'' || *bp == '\"')) {
quot = *bp++;
continue;
}
/* done quote */
if(quot && *bp == quot) {
quot = 0;
bp++;
continue;
}
/* done word */
if(!quot && (isspace(*bp) || ISTOK(*bp))) {
sbuf_put('\0',&sb);
break;
}
/* handle escapes */
if(*bp == '\\') {
bp++;
if(*bp != '\0') {
sbuf_put(*bp,&sb);
bp++;
}
continue;
}
/* default - just copy */
sbuf_put(*bp,&sb);
bp++;
}
if(sbuf_len(&sb) >= 0) { /* sbuf_len() return char count - 1 */
sbuf_put('\0',&sb);
if(!strcmp(sbuf_buf(&sb),"with"))
return('+');
return('^');
}
return('$');
}
static int
b_subexpr()
{
int x;
switch(lasttok = nexttok()) {
case 'T':
return(1);
case 'F':
return(0);
case '^':
if(checkmode)
return(1);
return(evaluate_truth(sbuf_buf(&sb),0));
case '+':
lasttok = nexttok();
if(lasttok != '^') {
if(checkmode && whop != (char *)0)
say(whop,"missing WITH item\n",(char *)0);
return(-1);
}
if(checkmode)
return(1);
return(evaluate_truth(sbuf_buf(&sb),1));
case '!':
if((x = b_subexpr()) == -1) {
if(checkmode && whop != (char *)0)
say(whop,"missing subexpression after '!'\n",(char *)0);
return(-1);
}
return(!x);
case '(':
x = b_expr();
if(lasttok != ')') {
if(checkmode && whop != (char *)0)
say(whop,"missing right parenthesis\n",(char *)0);
return(-1);
}
return(x);
case '$':
return('$');
default:
if(checkmode && whop != (char *)0) {
say(whop,"syntax error",(char *)0);
if(bp != (char *)0 && *bp != '\0')
say(whop," near \"",bp,"\"",(char *)0);
say(whop,"\n",(char *)0);
}
return(-1);
}
}
static int
b_expr()
{
int ret = b_subexpr();
int x;
for(;;) {
switch(lasttok = nexttok()) {
case '&':
if((x = b_subexpr()) == -1) {
if(checkmode && whop != (char *)0)
say(whop,"missing AND clause\n",(char *)0);
return(-1);
}
if(x == '$') {
if(checkmode && whop != (char *)0)
say(whop,"missing subexpression\n",(char *)0);
return(-1);
}
ret = (ret && x);
break;
case '|':
if((x = b_subexpr()) == -1) {
if(checkmode && whop != (char *)0)
say(whop,"missing OR clause\n",(char *)0);
return(-1);
}
if(x == '$') {
if(checkmode && whop != (char *)0)
say(whop,"missing subexpression\n",(char *)0);
return(-1);
}
ret = (ret || x);
break;
default:
return(ret);
}
}
}
int
bool_eval(who,where,s,check)
char *who;
char *where;
char *s;
int check;
{
int ret;
bp = s;
whop = who;
wherep = where;
checkmode = check;
sbuf_initstatic(&sb);
ret = b_expr();
if(ret < 0 && checkmode && whop != (char *)0)
say(whop,"invalid boolean expression.\n",(char *)0);
sbuf_freestatic(&sb);
whop = (char *)0;
wherep = (char *)0;
checkmode = 0;
return(ret);
}
/*
check a lock.
*/
int
bool_locked(who,what,where,lockvar,deflt)
char *who;
char *what;
char *where;
char *lockvar;
int deflt;
{
char *lockp;
lockp = ut_getatt(what,0,typ_bool,lockvar,(char *)0);
if(lockp == (char *)0)
return(deflt);
return(bool_eval(who,where,lockp,0) == 0);
}
/*
check a lock's syntax.
*/
int
bool_syntax(who,exp)
char *who;
char *exp;
{
int ret;
ret = bool_eval(who,(char *)0,exp,1);
if(ret == -1)
return(1);
return(0);
}
/*
Match a string against the environment, and paste the object
ID in to the specified Sbuf.
*/
static int
bool_pasteID(who,what,s)
char *who;
char *what;
Sbuf *s;
{
char objid[MAXOID];
char *where;
where = ut_loc(who);
if(!matchlocal(who,what,where,
MTCH_UNIQ | MTCH_NONLOC | MTCH_MEOK,objid)){
sbuf_strcat(objid,s);
sbuf_unput(s); /* lose trailing \0 */
return(0);
}
return(-1);
}
/*
Re-write a lock, matching junk against the environment and generating
object IDs.
*/
bool_rewrite(who,s,out)
char *who;
char *s;
Sbuf *out; /* pre-initted */
{
char quot = '\0';
char ch;
char *thing = "";
char *p;
while(*s){
/* End a quoted thing */
if(quot && *s == quot){
*s = '\0';
if(bool_pasteID(who,thing,out)){
*s = quot;
return(-1);
}
*s++ = quot;
quot = '\0';
continue;
}
/* Make the rest of these clauses if(!quot && <stuff> */
if(quot) {
s++;
continue;
}
/* Skip white-boy space. Marcus-like */
if(isspace(*s)) {
s++;
continue;
}
/* Copy TOK type things blindly */
if(ISTOK(*s) ||
((*s == 'T' || *s == 'F') &&
(ISTOK(*(s+1)) || isspace(*(s+1)) || *(s+1) == '\0'))){
sbuf_put(*s++,out);
continue;
}
/* Start quoted thing */
if(*s == '\'' || *s == '\"'){
quot = *s++;
thing = s;
continue;
}
if(!strncmp("with ",s,5)){
s += 5;
sbuf_strcat("with ",out);
sbuf_unput(out); /* lose trailing \0 */
continue;
}
/* Grub out an un-quoted thing name */
thing = s;
p = (char *)0;
while(!ISTOK(*s) && *s){
if(!isspace(*s) && isspace(s[1]))
p = s + 1;
s++;
}
if(!p)
p = s;
ch = *p;
*p = '\0';
if(bool_pasteID(who,thing,out)){
*s = ch;
return(-1);
}
*p = ch;
}
/* OK. Fell off end of string. Check for unclosed quoted thing */
if(quot){
if(bool_pasteID(who,thing,out))
return(-1);
}
sbuf_put('\0',out);
return(0);
}