untermud/DOC/
untermud/DOC/U/
untermud/DOC/U/U-examples/
untermud/DOC/internals/
untermud/DOC/wizard/
untermud/MISC/
untermud/MISC/dbchk/
untermud/RWHO/
untermud/RWHO/rwhod/
/*
    Copyright (C) 1991, Marcus J. Ranum. All rights reserved.
*/

/* configure all options BEFORE including system stuff. */
#include    "config.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(void);
static int nexttok(void);
static b_exp *build_subexpr(void);
static b_exp *build_expr(void);
static int eval_subexpr(b_exp *ex);
static int eval_truthof(char *s1, char *s2, int re);
static int eval_expr(b_exp *ex);

static b_exp *new_b_exp (void)
{
  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 (void)
{
  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 > (int) 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 (void)
{
  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 (void)
{
  b_exp *ret;
  b_exp *lhs;

  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));
  }
}



int setexpr (char *s)
{
  bufp = s;
  if ((compiled = build_expr ()) == (b_exp *) 0)
    return (1);
  return (0);
}



int checkexpr (char *nam, Obj * op)
{
  int ret;

  curnam = nam;
  curobj = op;

  ret = eval_expr (compiled);
  curnam = (char *) 0;
  curobj = (Obj *) 0;
  return (ret);
}