/*
Copyright (C) 1991, Marcus J. Ranum. All rights reserved.
*/
#ifndef lint
static char RCSid[] = "$Header: /home/mjr/hacks/umud/DB/RCS/expr.c,v 1.1 92/02/09 22:59:30 mjr Exp $";
#endif
/* configure all options BEFORE including system stuff. */
#include "config.h"
#include <stdio.h>
#include <ctype.h>
#include "mud.h"
#define ISPUN(x) (x=='#'||x=='['||x==']'||x =='!'||x =='('||x ==')'||x=='&'||x=='|'||x=='=')
typedef struct _b_exp {
int typ;
char *str;
struct _b_exp *rhs;
struct _b_exp *lhs;
} b_exp;
static b_exp *build_expr();
static char *tokp;
static char *bufp;
static int lasttok;
static char *curnam;
static Obj *curobj;
b_exp *compiled;
static b_exp *
new_b_exp()
{
b_exp *ret;
ret = (b_exp *)malloc(sizeof(b_exp));
if(ret == (b_exp *)0)
fatal("malloc new expression node: ",(char *)-1,"\n",(char *)0);
ret->typ = '$';
ret->str = (char *)0;
ret->rhs = ret->lhs = (b_exp *)0;
return(ret);
}
static int
nexttok()
{
int quot = 0;
int ghoti = 0;
char tbuf[512];
char *top;
while(isspace(*bufp))
bufp++;
top = tbuf;
if(ISPUN(*bufp)) {
quot = *bufp++;
return(quot);
}
if((*bufp == 'T' || *bufp == 'F') &&
(isspace(*(bufp + 1)) || *(bufp + 1) == '\0')) {
quot = *bufp++;
bufp++;
return(quot);
}
while(*bufp != '\0') {
ghoti = 1;
if(!quot && (*bufp == '\'' || *bufp == '\"')) {
quot = *bufp++;
continue;
}
/* done quote */
if(quot && *bufp == quot) {
quot = 0;
bufp++;
continue;
}
/* done word */
if(!quot && (isspace(*bufp) || ISPUN(*bufp)))
break;
/* handle escapes */
if(*bufp == '\\') {
bufp++;
if(*bufp != '\0') {
*top++ = *bufp++;
}
continue;
}
/* default - just copy */
*top++ = *bufp++;
}
if(top - tbuf > sizeof(tbuf) - 2)
fatal("string buffer overrun in expression\n",(char *)0);
if(ghoti) {
*top = '\0';
tokp = (char *)malloc(top - tbuf + 1);
if(tokp == (char *)0)
fatal("cannot allocate string: ",(char *)-1,"\n",(char *)0);
strcpy(tokp,tbuf);
return('W');
}
return('$');
}
static b_exp *
build_subexpr()
{
b_exp *ret;
switch(lasttok = nexttok()) {
case 'W':
ret = new_b_exp();
ret->typ = 'W';
ret->str = tokp;
return(ret);
case '!':
ret = new_b_exp();
if((ret->rhs = build_subexpr()) == (b_exp *)0)
return((b_exp *)0);
ret->typ = '!';
return(ret);
case '(':
ret = build_expr();
if((lasttok = nexttok()) != ')') {
fprintf(stderr,"missing right parenthesis\n");
return((b_exp *)0);
}
return(ret);
case '[':
lasttok = nexttok();
if(lasttok != 'W') {
fprintf(stderr,"missing regular expression\n");
return((b_exp *)0);
}
if((lasttok = nexttok()) != ']') {
fprintf(stderr,"missing regular expression terminator \n");
return((b_exp *)0);
}
ret = new_b_exp();
ret->typ = 'R';
ret->str = tokp;
return(ret);
case '#':
lasttok = nexttok();
if(lasttok != 'W') {
fprintf(stderr,"missing object ID\n");
return((b_exp *)0);
}
ret = new_b_exp();
ret->typ = 'O';
ret->str = tokp;
return(ret);
case '$':
case 'T':
case 'F':
ret = new_b_exp();
ret->typ = lasttok;
return(ret);
default:
fprintf(stderr,"syntax error");
if(lasttok == 'W') {
fprintf(stderr," near \"%s\"",tokp);
} else {
if(bufp != (char *)0 && *bufp != '\0')
fprintf(stderr," near \"%c %s\"",lasttok,bufp);
}
fprintf(stderr,"\n");
return((b_exp *)0);
}
}
static b_exp *
build_expr()
{
b_exp *ret;
b_exp *lhs;
int x;
if((lhs = build_subexpr()) == (b_exp *)0)
return((b_exp *)0);
switch(lasttok = nexttok()) {
case '&':
ret = new_b_exp();
if((ret->rhs = build_subexpr()) == (b_exp *)0)
return((b_exp *)0);
if(ret->rhs->typ == '$') {
fprintf(stderr,"missing AND clause\n");
return((b_exp *)0);
}
ret->typ = '&';
ret->lhs = lhs;
return(ret);
case '=':
ret = new_b_exp();
if((ret->rhs = build_subexpr()) == (b_exp *)0)
return((b_exp *)0);
if(lhs->typ != 'W') {
fprintf(stderr,"missing attribute for comparison\n");
return((b_exp *)0);
}
if(ret->rhs->typ != 'W' && ret->rhs->typ != 'R') {
fprintf(stderr,"missing string for comparison\n");
return((b_exp *)0);
}
ret->typ = '=';
ret->lhs = lhs;
return(ret);
case '|':
ret = new_b_exp();
if((ret->rhs = build_subexpr()) == (b_exp *)0)
return((b_exp *)0);
if(ret->rhs->typ == '$') {
fprintf(stderr,"missing OR clause\n");
return((b_exp *)0);
}
ret->typ = '|';
ret->lhs = lhs;
return(ret);
default:
return(lhs);
}
}
static int
eval_subexpr(ex)
b_exp *ex;
{
switch(ex->typ) {
case 'W':
fprintf(stderr,"meaningless evaluation");
if(ex->str != (char *)0)
fprintf(stderr,": \"%s\"",ex->str);
fprintf(stderr,"\n");
return(1);
case 'O':
if(!strcmp(ex->str,curnam))
return(1);
return(0);
case 'T':
return(1);
case 'F':
return(0);
default:
return(-1);
}
}
static int
eval_truthof(s1,s2,re)
char *s1;
char *s2;
int re;
{
#ifdef REGEX
extern char *regcmp();
extern char *regex();
#else
extern char *re_comp();
#endif
char *xp;
char *vp;
char *ap;
char *tp;
int tl = 0;
/* look for a '.' in the attribute name */
for(xp = s1; *xp != '\0' && *xp != '.'; xp++)
;
if(*xp == '.') {
tl = xp - s1;
ap = ++xp;
tp = s1;
} else {
ap = s1;
tp = (char *)0;
}
/* feh */
if(*ap == '\0' || (vp = objattr(curobj,ap,0)) == (char *)0)
return(0);
/* specified type ? must match */
if(tp != (char *)0 && (strncmp(vp,tp,tl) || vp[tl] != ' '))
return(0);
/* this eats - already done in objattr really */
xp = vp;
/* skip to end of type */
while(*xp != ' ' && *xp != '\0')
xp++;
if(*xp == '\0')
return(0);
xp++;
/* skip to end of attribute name */
while(*xp != '=' && *xp != '\0')
xp++;
if(*xp == '\0')
return(0);
xp++;
/* if not a regular expression, this is simplicty */
if(!re) {
if(!strcmp(xp,s2))
return(1);
return(0);
}
#ifdef REGEX
if((vp = regcmp(s2, (char *)0)) == (char *)0) {
fprintf(stderr,"bad regular expression\n");
exit(1);
}
tp = regex(vp,xp);
free(vp);
return(tp != (char *)0);
#else
if((vp = re_comp(s2)) != (char *)0) {
fprintf(stderr,"bad regular expression: %s\n",vp);
exit(1);
}
return(re_exec(xp));
#endif
}
static int
eval_expr(ex)
b_exp *ex;
{
int rh;
int lh;
switch(ex->typ) {
case '|':
rh = eval_expr(ex->rhs);
lh = eval_expr(ex->lhs);
return(lh || rh);
case '&':
rh = eval_expr(ex->rhs);
lh = eval_expr(ex->lhs);
return(rh == lh);
case '=':
if(ex->lhs->typ != 'W' || (ex->rhs->typ != 'W' && ex->rhs->typ != 'R'))
return(0);
return(eval_truthof(ex->lhs->str,ex->rhs->str,ex->rhs->typ == 'R' ? 1 : 0));
default:
return(eval_subexpr(ex));
}
}
setexpr(s)
char *s;
{
bufp = s;
if((compiled = build_expr()) == (b_exp *)0)
return(1);
return(0);
}
checkexpr(nam,op)
char *nam;
Obj *op;
{
int ret;
curnam = nam;
curobj = op;
ret = eval_expr(compiled);
curnam = (char *)0;
curobj = (Obj *)0;
return(ret);
}