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/
%{

#include	"config.h"
#include	"mud.h"
#include	"cmd.h"
#include	"sbuf.h"
#include	"vars.h"
#include	"u.h"


/* input read from a line */
static char *yyin = (char *) 0;
static char *p_who;

static int yylex(void);
static void yyerror(char *s);

static Nod *node(int t, Nod *r, Nod *l);
static Nod *compiled;         /* root of compiled program */
static Nod *tmpsyms;          /* root of scope symbol table */
static Nod *ins_stmt(Nod *nl, Nod *n);
static int lookup(char *s);
static int follow(int expect, int ifyes, int ifno);
static Nod *eval_gettmp(char *nam, Nod *tab, char *who, char *aswho);

static int bltcmdreturned = -1;       /* kludge */
static Nod bltcmdretnod;

static int returning = 0;

%}

%union {
  Nod *nval;
  char *cval;
  int ival;
} 

%token NUM STR IDENT IF IN FOR FORARG ELSE VNULL
%token ARGLIST CALL CALLCNT CALLIDENT EVAL STMT TASGN TEVAL
%token NEGATE VOMIT OID BLTIN BLTINCMD RETURN SUID

%right ASGN
%left OR
%left AND
%left GT GTE LT LTE EQ NE
%left ADD SUB
%left MUL DIV
%right UNARY
%left NOT

%type < nval > asgn statement statements condition
%type < nval > refnum element_head element expr arglist
%type < cval > STR IDENT 
%type < ival > NUM 
  
%% 

program: 
	statements {
	  compiled = $1;
	}

	| error {
	  say (p_who, "program run aborted\n", (char *) 0);
	  compiled = NNULL;
	}
	;

statements:
        /* nothing */
	{
	  $$ = (Nod *) 0;
	}

	| statements statement ';' {
	  $$ = ins_stmt ($1, node (STMT, NNULL, $2));
	}
	;

asgn:
	'$' IDENT ASGN expr {
	  $$ = node (TASGN, $4, NNULL);
	  $$->nv.cv = $2;
	}

	| element ASGN expr {
	  $$ = node (ASGN, $3, $1);
	  $$->nv.cv = (char *) 0;
	}

	| '(' IDENT ')' element ASGN expr {
	  $$ = node (ASGN, $6, $4);
	  $$->nv.cv = $2;
	}
	;

statement:                     /* NOTHING */
	{
	  $$ = node (VNULL, NNULL, NNULL);
	  $$->nv.iv = UERR_NONE;
	}

	| expr | IF condition statement ELSE statement {
	  $$ = node (IF, $2, node (ELSE, $3, $5));
	}

	| IF condition statement {
	  $$ = node (IF, $2, node (ELSE, $3, NNULL));
	}

	| FOR '$' IDENT IN condition statement {
	  $$ = node (FOR, $5, $6);
	  $$->nv.cv = $3;
	}

	| FORARG '$' IDENT statement {
	  $$ = node (FORARG, $4, NNULL);
	  $$->nv.cv = $3;
	}

	| RETURN expr {
	  $$ = node (RETURN, $2, NNULL);
	}

	| RETURN {
	  $$ = node (RETURN, node (VNULL, NNULL, NNULL), NNULL);
	}

	| '{' statements '}' {
	  $$ = $2;
	}

	| refnum ':' '{' statements '}' {
	  $$ = node (SUID, $1, $4);
	}
	;

condition:
	'(' expr ')' {
	  $$ = $2;
	}
	;

refnum:
	'#' NUM {
	  char obuf[MAXOID];

	  $$ = node (OID, NNULL, NNULL);
	  $$->nv.cv = tmpstr (itoa ($2, obuf, 10));
	}

	| '#' NUM '@' IDENT {
	  char obuf[MAXOID];
	  int l;

	  /* must assemble parsed object-id - icky, but it works */
	  $$ = node (OID, NNULL, NNULL);
	  (void) itoa ($2, obuf, 10);
	  if ((l = (strlen (obuf) + strlen ($4) + 2)) > MAXOID) {
	    $$ = node (VNULL, NNULL, NNULL);
	    $$->nv.iv = UERR_BADOID;
	  } else {
	    $$ = node (OID, NNULL, NNULL);
	    $$->nv.cv = (char *) tmpalloc (l);
	    sprintf ($$->nv.cv, "%s@%s", obuf, $4);
	  }
	}

	| '#' IDENT {
	  $$ = node (OID, NNULL, NNULL);
	  $$->nv.cv = $2;
	}

	| '#' STR {
	  $$ = node (OID, NNULL, NNULL);
	  $$->nv.cv = $2;
	}
	;

element_head:
	refnum | '$' IDENT {
	  $$ = node (TEVAL, NNULL, NNULL);
	  $$->nv.cv = $2;
	}

	| '$' NUM {
	  $$ = node (CALLIDENT, NNULL, NNULL);
	  $$->nv.iv = $2;
	}
	;

element:
	element_head '.' IDENT {
	  $$ = node (IDENT, $1, node (STR, NNULL, NNULL));
	  $$->l->nv.cv = $3;
	}

	| element '.' IDENT {
	  $$ = node (IDENT, $1, node (STR, NNULL, NNULL));
	  $$->l->nv.cv = $3;
	}

	| element_head '.' '(' expr ')' {
	  $$ = node (IDENT, $1, $4);
	}

	| element '.' '(' expr ')' {
	  $$ = node (IDENT, $1, $4);
	}
	;

expr:
	NUM {
	  $$ = node (NUM, NNULL, NNULL);
	  $$->nv.iv = $1;
	}

	| STR {
	  $$ = node (STR, NNULL, NNULL);
	  $$->nv.cv = $1;
	}

	| VNULL {
	  $$ = node (VNULL, NNULL, NNULL);
	  $$->nv.iv = UERR_USER;
	}

	| refnum | '$' '#' {
	  $$ = node (CALLCNT, NNULL, NNULL);
	}

	| '$' NUM {
	  $$ = node (CALLIDENT, NNULL, NNULL);
	  $$->nv.iv = $2;
	}

	| '$' IDENT {
	  $$ = node (TEVAL, NNULL, NNULL);
	  $$->nv.cv = $2;
	}

	| asgn | element '(' arglist ')' {
	  $$ = node (CALL, $1, $3);
	
	}

	| IDENT '(' arglist ')' {
	  $$ = node (BLTIN, NNULL, $3);
	  $$->nv.cv = $1;
	}

	| '@' IDENT '(' arglist ')' {
	  $$ = node (BLTINCMD, NNULL, $4);
	  $$->nv.cv = $2;
	}

	| element {
	  $$ = node (EVAL, $1, NNULL);
	}

	| '(' expr ')' {
	  $$ = $2;
	}

	| expr ADD expr {
	  $$ = node (ADD, $3, $1);
	}

	| expr SUB expr {
	  $$ = node (SUB, $3, $1);
	}

	| expr MUL expr {
	  $$ = node (MUL, $3, $1);
	}

	| expr DIV expr {
	  $$ = node (DIV, $3, $1);
	}

	| SUB expr %prec UNARY {
	  $$ = node (NEGATE, NNULL, $2);
	}

	| expr EQ expr {
	  $$ = node (EQ, $3, $1);
	}

	| expr NE expr {
	  $$ = node (NE, $3, $1);
	}

	| expr AND expr {
	  $$ = node (AND, $3, $1);
	}

	| expr OR expr {
	  $$ = node (OR, $3, $1);
	}

	| expr LT expr {
	  $$ = node (LT, $3, $1);
	}

	| expr LTE expr {
	  $$ = node (LTE, $3, $1);
	}

	| expr GT expr {
	  $$ = node (GT, $3, $1);
	}

	| expr GTE expr {
	  $$ = node (GTE, $3, $1);
	}

	| NOT expr %prec UNARY {
	  $$ = node (NOT, NNULL, $2);
	}
	;

arglist:
        /* nothing */
	{
	  $$ = (Nod *) 0;
	}
	
	| expr {
	  $$ = node (ARGLIST, NNULL, $1);
	}
	
	| arglist ',' expr {
	  $$ = ins_stmt ($1, node (ARGLIST, NNULL, $3));
	}
	;

%%

int eval (Nod * n, char *w, char *aw, int ac, char *av[]);
extern int eval_castbool (Nod * n);


/* append an expression to a list. needed because of how we parse */
static Nod *ins_stmt (nl, n)
Nod *nl;
Nod *n;
{
  Nod *np = nl;

  while (np != NNULL && np->r != NNULL)
    np = np->r;

  if (np == NNULL)
    return (n);
  np->r = n;
  n->r = NNULL;
  return (nl);
}



static int lookup (s)
char *s;
{
  int start = 0;
  int ret;

  static struct kwordz {
    char *kw;
    int rval;
  } keyz[] = {
    {
    "NULL", VNULL}, {
    "else", ELSE}, {
    "foreach", FOR}, {
    "foreacharg", FORARG}, {
    "if", IF}, {
    "in", IN}, {
    "return", RETURN}, {
    0, 0}
  };

  int end = (sizeof (keyz) / sizeof (struct kwordz)) - 2;
  int p = end / 2;

  while (start <= end) {
    ret = strcmp (s, keyz[p].kw);
    if (ret == 0)
      return (keyz[p].rval);
    if (ret > 0)
      start = p + 1;
    else
      end = p - 1;

    p = start + ((end - start) / 2);
  }
  return (-1);
}





static int follow (expect, ifyes, ifno)
int expect;
int ifyes;
int ifno;
{
  if (*yyin == expect) {
    yyin++;
    return (ifyes);
  }
  return (ifno);
}




void parser_setinput (char *s)
{
  yyin = s;
}



int parser_compile (char *who)
{
  p_who = who;
  compiled = NNULL;
  yyparse ();
  p_who = (char *) 0;
  if (compiled != NNULL)
    return (1);
  return (0);
}



int parser_run (char *who, char *aswho, int ac, char *av[])
{
  int rv;
  Nod *savret;

  returning = 0;
  if (add_run_level () > MAXRECURSIONS) {
    say (who, "Too many recursions. Program ends.\n", (char *) 0);
    return (0);
  }
  tmpsyms = NNULL;
  p_who = who;
  savret = compiled;
  rv = eval (compiled, who, aswho, ac, av);

  /* if returned buffer is empty, adopt whatever was left in root Nod */
  if (!returning)
    eval_adopt (savret, &bltcmdretnod, 0);
  p_who = (char *) 0;
  return (rv);
}




/* simple lexical analyser that reads input from a string (yyin) */
static int yylex ()
{
  char lexbuf[MUDBUF];
  char *p = lexbuf;

  if (yyin == (char *) 0)
    return (0);

  /* handle whitespace */
  while (isspace (*yyin))
    yyin++;

  if (*yyin == '\0') {
    yyin = (char *) 0;
    return (0);
  }

  /* handle NUM */
  if (isdigit (*yyin)) {
    int num = *yyin - '0';

    yyin++;
    while (isdigit (*yyin)) {
      num = (num * 10) + (*yyin - '0');
      yyin++;
    }

    yylval.ival = num;
    return (NUM);
  }


  /* handle keywords or idents/builtins */
  if (isalpha (*yyin) || *yyin == '_') {
    int cnt = 0;
    int rv;

    *p++ = *yyin++;
    while (*yyin != '\0' && (isalnum (*yyin) || *yyin == '_')) {
      if (++cnt + 1 >= 20) {
        say (p_who, "identifier too long\n", (char *) 0);
        return (VOMIT);
      }
      *p++ = *yyin++;
    }
    *p = '\0';

    if ((rv = lookup (lexbuf)) != -1)
      return (rv);

    yylval.cval = tmpstr (lexbuf);
    return (IDENT);
  }


  /* handle quoted strings */
  if (*yyin == '"' || *yyin == '\'') {
    int cnt = 0;
    int quot = *yyin++;

    /* match quoted strings */
    while (*yyin != '\0' && *yyin != quot) {
      if (!isascii (*yyin)) {
        yyin++;
        continue;
      }

      if (++cnt + 1 >= (int) sizeof (lexbuf)) {
        say (p_who, "string too long\n", (char *) 0);
        return (VOMIT);
      }

      if (*yyin == '\\') {
        yyin++;
        switch (*yyin) {
        case '\0':
          say (p_who, "EOF in string\n", (char *) 0);
          return (VOMIT);

        case 't':
          *p++ = '\t';
          break;
#ifdef	U_NEWLINES
        case 'n':
          *p++ = '\n';
          break;
#endif
        default:
          *p++ = *yyin;
          break;
        }
      } else {
        *p++ = *yyin;
      }
      yyin++;
    }

    if (*yyin == '\0') {
      say (p_who, "EOF in string\n", (char *) 0);
      return (VOMIT);
    }
    if (*yyin == quot)
      yyin++;

    *p = '\0';
    yylval.cval = tmpstr (lexbuf);
    return (STR);
  }


  yyin++;
  switch (*(yyin - 1)) {
  case '=':
    return (follow ('=', EQ, ASGN));
  case '>':
    return (follow ('=', GTE, GT));
  case '<':
    return (follow ('=', LTE, LT));
  case '!':
    return (follow ('=', NE, NOT));
  case '&':
    return (follow ('&', AND, '&'));
  case '|':
    return (follow ('|', OR, '|'));
  case '/':
    return (DIV);
  case '*':
    return (MUL);
  case '-':
    return (SUB);
  case '+':
    return (ADD);
  }
  return (*(yyin - 1));
}



static void yyerror (char *s)
{
  say (p_who, s, "\n", (char *) 0);
}




static Nod *node (int t, Nod * r, Nod * l)
{
  Nod *ret;

  if ((ret = (Nod *) tmpalloc (sizeof (Nod))) == (Nod *) 0) {
    WIN32CLEANUP
    exit (1);
  }
  ret->l = l;
  ret->r = r;
  ret->nv.t = t;
  ret->nv.cv = (char *) 0;
  return (ret);
}




static Nod *eval_gettmp (nam, tab, who, aswho)
char *nam;
Nod *tab;
char *who;
char *aswho;
{
  static Nod fak;
  while (tab != NNULL) {
    if (!strcmp (nam, tab->nv.cv))
      return (tab);
    tab = tab->r;
  }

  /* didn't find it? overload the usual suspects */
  if (!strcmp (nam, "here")) {
    fak.rv.cv = ut_loc (aswho);
    fak.rv.t = OID;
    return (&fak);
  }
  if (!strcmp (nam, "actor")) {
    fak.rv.cv = run_actor ();
    fak.rv.t = OID;
    return (&fak);
  }
  if (!strcmp (nam, "me") || !strcmp (nam, "self")) {
    fak.rv.cv = aswho;
    fak.rv.t = OID;
    return (&fak);
  }
  if (!strcmp (nam, "subv") || !strcmp (nam, "objv")) {
    if ((fak.rv.cv =
        ut_getatt (who, 0, typ_str, nam, (char *) 0)) == (char *) 0)
      fak.rv.cv = "it";
    fak.rv.t = STR;
    return (&fak);
  }
  if (!strcmp (nam, "Subv") || !strcmp (nam, "Objv")) {
    if ((fak.rv.cv =
        ut_getatt (who, 0, typ_str, nam, (char *) 0)) == (char *) 0)
      fak.rv.cv = "It";
    fak.rv.t = STR;
    return (&fak);
  }
  if (!strcmp (nam, "posv")) {
    if ((fak.rv.cv =
        ut_getatt (who, 0, typ_str, nam, (char *) 0)) == (char *) 0)
      fak.rv.cv = "its";
    fak.rv.t = STR;
    return (&fak);
  }
  if (!strcmp (nam, "Posv")) {
    if ((fak.rv.cv =
        ut_getatt (who, 0, typ_str, nam, (char *) 0)) == (char *) 0)
      fak.rv.cv = "Its";
    fak.rv.t = STR;
    return (&fak);
  }
  return (NNULL);
}



/*
kludge to permit passing back values from normal cmds.
if this is passed back in a U call, we set the returned
value in the node appropriately.
*/
void eval_cmd_returnint (int val)
{
  bltcmdretnod.rv.t = bltcmdreturned = NUM;
  bltcmdretnod.rv.iv = val;
}



/*
kludge to permit passing back values from normal cmds.
here we pass back a string
*/
void eval_cmd_returnstr (char *val)
{
  bltcmdretnod.rv.t = bltcmdreturned = STR;
  bltcmdretnod.rv.cv = tmpstr (val);
}



/*
kludge to permit passing back values from normal cmds.
here we pass back an OBJ
*/
void eval_cmd_returnoid (char *val)
{
  bltcmdretnod.rv.t = bltcmdreturned = OID;
  bltcmdretnod.rv.cv = tmpstr (val);
}



int eval_cmd_returnedtrue ()
{
  if (bltcmdreturned == -1)
    return (0);
  return (eval_castbool (&bltcmdretnod));
}




/* 
  recursively evaluate nodes, placing returned values in .rv 
   Nod*	n - root node 
   char* w - who 
   char* aw - aswho 
   int ac - initial arg count 
   char* av[] - arg vector 
 */
int eval (Nod * n, char *w, char *aw, int ac, char *av[])
{
  if (n == NNULL)
    return (1);

  switch (n->nv.t) {

    /* evaluate statements (a linked list), and throw results away */
  case STMT:
    while (1) {
      if (eval (n->l, w, aw, ac, av) != 0)
        return (1);
      if (n->r == NNULL || returning)
        break;
      n = n->r;
    }
    if (returning)
      eval_adopt (&bltcmdretnod, n, 0);
    else {
      n->rv.t = VNULL;
      n->rv.iv = UERR_NONE;
    }
    return (0);




    /* call a set of commands as someone else */
  case SUID:
    if (eval (n->r, w, aw, ac, av) != 0)
      return (1);
    if (n->r->rv.t != OID) {
      n->rv.t = VNULL;
      n->rv.iv = UERR_BADOID;
      return (0);
    }
    if (ut_flagged (aw, var_wiz) || ut_isobjown (aw, n->r->rv.cv)) {
      char *sava;
      sava = run_actor ();
      run_setactor (n->r->rv.cv);
      if (eval (n->l, n->r->rv.cv, n->r->rv.cv, ac, av) != 0)
        return (1);
      run_setactor (sava);
      eval_adopt (n->l, n, 0);
    }
    n->rv.t = VNULL;
    n->rv.iv = UERR_PERM;
    return (0);




    /* function or macro call. */
  case CALL:
    /* stack a new temporary variable context and restore after call */
    /*
       we must needs duplicate a lot of the functionality of EVAL here
       but can't do it exactly because we need to get the type of the
       thing we're calling (be it cmd or U or whatever)
     */
    if (n->r == NNULL || n->r->r == NNULL) {
      n->rv.t = VNULL;
      n->rv.iv = UERR_BADOID;
      return (0);
    }
    /* if this is not the case, we have a scrambled parse tree */
    if (n->r->nv.t != IDENT) {
      n->rv.t = VNULL;
      n->rv.iv = UERR_FATAL;
      return (1);
    }
    if (eval (n->r->l, w, aw, ac, av) != 0)
      return (1);
    if (eval (n->r->r, w, aw, ac, av) != 0)
      return (1);
    if (n->r->r->rv.t != OID) {
      n->rv.t = VNULL;
      n->rv.iv = n->r->r->rv.iv;
      return (0);
    }
    /* now get the attribute of the object in question */
    if ((n->rv.cv = var_namatch (n->r->l->rv.cv)) == (char *) 0)
      n->rv.cv = n->r->l->rv.cv;

    n->rv.cv =
      ut_getatt (n->r->r->rv.cv, 1, (char *) 0, n->rv.cv, (char *) 0);
    if (n->rv.cv == (char *) 0) {
      n->rv.t = VNULL;
      n->rv.iv = UERR_NOATTR;
      return (0);
    }

    /* permissions for the small minded - check IF it exists (faster) */
    if (!ut_flagged (aw, var_wiz) &&
      !var_ispublic (n->rv.cv, w, aw, n->r->r->rv.cv) &&
      !ut_isobjown (aw, n->r->r->rv.cv)) {
      n->rv.t = VNULL;
      n->rv.iv = UERR_PERM;
      return (0);
    }

    returning = 0;
    /* is it a cmd? if so just call through */
    if (attistype (n->rv.cv, typ_cmd)) {
      char *nav[MAXARG];
      int nac = 0;
      Nod *argp;

      /* setup argvec from arglist */
      nav[nac++] = n->r->l->rv.cv;
      for (argp = n->l; argp != NNULL; argp = argp->r) {
        if (eval (argp->l, w, aw, ac, av) != 0)
          return (1);
        nav[nac++] = eval_caststr (argp->l);
        if (nac >= MAXARG - 1) {
          n->rv.t = VNULL;
          n->rv.iv = UERR_ARGCNT;
          return (0);
        }
      }
      nav[nac] = (char *) 0;
      n->rv.t = NUM;
      n->rv.iv = run (w, aw, attdata (n->rv.cv), nac, nav, 0);
      returning = 0;
      return (0);
    }

    /* is it a U program? if so compile and call */
    if (attistype (n->rv.cv, typ_u)) {
      char *nav[MAXARG];
      int nac = 0;
      Nod *argp;
      Nod *symsav;              /* pointer to old symbol table */

      parser_setinput (attdata (n->rv.cv));
      if (!parser_compile (w)) {
        n->rv.t = VNULL;
        n->rv.iv = UERR_SYNTAX;
        return (0);
      }

      /* build argvec */
      nav[nac++] = n->r->l->rv.cv;
      for (argp = n->l; argp != NNULL; argp = argp->r) {
        if (eval (argp->l, w, aw, ac, av) != 0)
          return (1);
        nav[nac++] = eval_caststr (argp->l);
        if (nac >= MAXARG - 1) {
          n->rv.t = VNULL;
          n->rv.iv = UERR_ARGCNT;
          return (0);
        }
      }
      nav[nac] = (char *) 0;
      n->rv.t = NUM;
      symsav = tmpsyms;
      tmpsyms = NNULL;          /* new sym table, local scope */
      n->rv.iv = parser_run (w, aw, nac, nav);
      tmpsyms = symsav;         /* pop back old sym table */
      if (returning)
        eval_adopt (&bltcmdretnod, n, 0);
      returning = 0;
      return (0);
    }
    /* feh */
    n->rv.t = VNULL;
    n->rv.iv = UERR_NOFUNC;
    return (0);




    /* builtin op - call through to builtin function */
  case BLTIN:
    if (n->nv.cv[0] != '\0') {
      Bltin *bp;

      if ((bp = u_bltlookup (n->nv.cv)) != (Bltin *) 0) {
        Nod *argp;
        Nod *avec[MAXARG];
        int acnt = 0;
        int rv;

        for (argp = n->l; argp != NNULL; argp = argp->r) {
          if (eval (argp->l, w, aw, ac, av) != 0)
            return (1);
          avec[acnt++] = argp->l;
          if (acnt >= MAXARG - 1) {
            n->rv.t = VNULL;
            n->rv.iv = UERR_ARGCNT;
            return (0);
          }
        }
        /* function does not take varargs. therefore, check */
        if (bp->flgs & BLT_FIXARG && acnt != bp->argc) {
          n->rv.t = VNULL;
          n->rv.iv = UERR_ARGCNT;
          return (1);
        }
        /* wizard only ? */
        if (bp->flgs & BLT_WIZONLY && !ut_flagged (aw, var_wiz)) {
          n->rv.t = VNULL;
          n->rv.iv = UERR_PERM;
          return (1);
        }
        /*
           call through function. function sets its own
           return value in process (presumably). we set
           return value to zero, so functions can return
           it as a success signal, if need be.
         */
        n->rv.t = NUM;
        n->rv.iv = 0;
        if ((rv = (*bp->func) (w, aw, acnt, avec, n)) != UERR_NONE) {
          n->rv.t = VNULL;
          n->rv.iv = rv;
          return (0);
        }
        return (0);
      }
    }
    n->rv.t = VNULL;
    n->rv.iv = UERR_NOFUNC;
    return (0);




    /* builtin command - call to normal U-macro builtin */
  case BLTINCMD:
    if (n->nv.cv[0] != '\0') {
      Cmd *cp;

      if ((cp = u_cmdlookup (n->nv.cv)) != (Cmd *) 0) {
        Nod *argp;
        char *nav[MAXARG];
        int nac = 0;

        nav[nac++] = n->nv.cv;
        for (argp = n->l; argp != NNULL; argp = argp->r) {
          if (eval (argp->l, w, aw, ac, av) != 0)
            return (1);
          nav[nac++] = eval_caststr (argp->l);
          if (nac >= MAXARG - 1) {
            n->rv.t = VNULL;
            n->rv.iv = UERR_ARGCNT;
            return (0);
          }
        }
        nav[nac] = (char *) 0;
        if (cp->flgs & CM_FIXARG && nac != cp->argc) {
          n->rv.t = VNULL;
          n->rv.iv = UERR_ARGCNT;
          return (0);
        }
        /* wizard only ? */
        if (cp->flgs & CM_PRIV && !ut_flagged (aw, var_wiz)) {
          n->rv.t = VNULL;
          n->rv.iv = UERR_PERM;
          return (0);
        }
#ifdef	PLAYERONLY
        if (cp->flgs & CM_NOPLY && !ut_flagged (aw, var_wiz) &&
          !ut_flagged (w, var_isplay)) {
          n->rv.t = VNULL;
          n->rv.iv = UERR_PERM;
          return (0);
        }
#endif
        /*
           call through function. function sets its own
           return value in process (presumably). we set
           return value to zero, so functions can return
           it as a success signal, if need be.
         */
        bltcmdreturned = -1;

        /*
           if the function returns nonzero, it failed
           we assume, so we assign the return value to
           VNULL and trap the return code as an error
           number and HOPE the guy who wrote the cmd
           got it right.
         */
        n->rv.t = NUM;
        if ((n->rv.iv = (*cp->func) (nac, nav, w, aw)) != UERR_NONE)
          n->rv.t = VNULL;
        else
          n->rv.iv = 0;

        /* cmd tried to explicitly pass back value.. */
        if (bltcmdreturned != -1)
          eval_adopt (&bltcmdretnod, n, 0);
        return (0);
      }
    }
    n->rv.t = VNULL;
    n->rv.iv = UERR_NOFUNC;
    return (0);




    /* parameter count - easy */
  case CALLCNT:
    n->rv.t = NUM;
    n->rv.iv = ac;
    return (0);




    /* parameter by number */
  case CALLIDENT:
    if (n->nv.iv < 0 || n->nv.iv >= ac) {
      n->rv.iv = UERR_NOATTR;
      n->rv.t = VNULL;
      return (0);
    }
    n->rv.t = STR;
    n->rv.cv = av[n->nv.iv];
    return (0);




    /*
       conditional operator
       IF nodes are built with an ELSE node on the left.
       we need to look down the branches of the ELSE node.
       $$ = node(IF,$2,node(ELSE,$3,$5));
     */
  case IF:
    if (n->l == NNULL || n->l->nv.t != ELSE)
      return (1);
    if (n->r != NNULL && eval (n->r, w, aw, ac, av) != 0)
      return (1);
    if (n->r != NNULL && eval_castbool (n->r)) {
      if (n->l->r != NNULL && eval (n->l->r, w, aw, ac, av) != 0)
        return (1);
    } else {
      if (n->l->l != NNULL && eval (n->l->l, w, aw, ac, av) != 0)
        return (1);
    }
    return (0);




    /* iterate a list. note that the list condition gets
       evaluated exactly once. */
  case FOR:
    if (eval (n->r, w, aw, ac, av))
      return (1);
    if (n->r->rv.t != STR) {
      n->rv.t = VNULL;
      n->rv.iv = UERR_BADLST;
      return (0);
    }
    if (n->r->rv.cv[0] != '\0') {
      char *lp;
      Sbuf sb;
      Nod *tn;

      lp = n->r->rv.cv;
      sbuf_initstatic (&sb);
      while ((lp = lstnextsbuf (lp, &sb)) != (char *) 0) {
        if ((tn = eval_gettmp (n->nv.cv, tmpsyms, w, aw)) == (Nod *) 0)
          tmpsyms = tn = node (0, tmpsyms, NNULL);
        tn->nv.cv = n->nv.cv;
        tn->rv.t = STR;
        tn->rv.cv = sbuf_buf (&sb);
        if (eval (n->l, w, aw, ac, av))
          return (1);
      }
      sbuf_freestatic (&sb);
    }
    n->rv.t = VNULL;
    n->rv.iv = UERR_NONE;
    return (0);




    /* iterate all our paramters */
  case FORARG:
    if (n->nv.cv[0] != '\0') {
      Nod *tn;
      int c;

      for (c = 1; c < ac; c++) {
        if ((tn = eval_gettmp (n->nv.cv, tmpsyms, w, aw)) == (Nod *) 0)
          tmpsyms = tn = node (0, tmpsyms, NNULL);
        tn->nv.cv = n->nv.cv;
        tn->rv.t = STR;
        tn->rv.cv = av[c];
        if (eval (n->r, w, aw, ac, av))
          return (1);
      }
    }
    n->rv.t = VNULL;
    n->rv.iv = UERR_NONE;
    return (0);




    /* return from call */
  case RETURN:
    if (eval (n->r, w, aw, ac, av) != 0)
      return (1);
    eval_adopt (n->r, n, 0);
    eval_adopt (n, &bltcmdretnod, 0);
    bltcmdreturned = n->rv.t;
    returning = 1;
    return (0);




    /*
       assignment operation to NON temporary. unfortunately a lot
       of this logic is duplicated from vars.c, but it's all subtly
       different.
     */
  case ASGN:
    if (eval (n->r, w, aw, ac, av) != 0)
      return (1);
    /* l contains coding of where to store */
    if (n->l->nv.t != IDENT) {
      n->rv.t = VNULL;
      n->rv.iv = UERR_FATAL;
      return (1);
    }
    /* eval attr to be assigned to */
    if (eval (n->l->r, w, aw, ac, av) || eval (n->l->l, w, aw, ac, av))
      return (1);
    if (n->l->r->rv.t != OID) {
      n->rv.t = VNULL;
      n->rv.iv = n->l->r->rv.iv;
      return (0);
    }
    /* actually perform the assignment and modify the database */
    if (n->r->rv.t == VNULL) {
      /* assignment to NULL unsets value */
      n->rv.iv =
        var_unset_internal (w, aw, n->l->r->rv.cv, n->l->l->rv.cv, 0);
    } else {
      char nbuf[MAXOID];
      char *tp = typ_str;
      char *dp = n->r->rv.cv;

      if (n->r->rv.t == NUM) {
        tp = typ_int;
        dp = itoa (n->r->rv.iv, nbuf, 10);
      }
      /* cast overrides all */
      if (n->nv.cv != (char *) 0) {
        /* only wizards can give unknown types */
        if (!fndtyp (n->nv.cv) && !ut_flagged (aw, var_wiz)) {
          n->rv.t = VNULL;
          n->rv.iv = UERR_TYPE;
          return (0);
        }
        tp = n->nv.cv;
      }
      n->rv.iv =
        var_set_internal (w, aw, n->l->r->rv.cv, tp, n->l->l->rv.cv, dp, 0);
    }
    if (n->rv.iv != UERR_NONE) {
      n->rv.t = VNULL;
      return (0);
    }
    eval_adopt (n->r, n, 0);
    return (0);




    /* assignment operation to temporary */
  case TASGN:
    if (n->r == NNULL) {
      n->rv.t = VNULL;
      n->rv.iv = UERR_FATAL;
      return (1);
    }

    if (eval (n->r, w, aw, ac, av) == 0) {
      Nod *tn;
      if ((tn = eval_gettmp (n->nv.cv, tmpsyms, w, aw)) == (Nod *) 0) {
        tmpsyms = tn = node (0, tmpsyms, NNULL);
        eval_adopt (n->r, tn, 0);
        tn->nv.cv = n->nv.cv;   /* set name */
      } else {
        eval_adopt (n->r, tn, 0);       /* reset existing */
      }
      eval_adopt (tn, n, 0);
    } else {
      n->rv.t = VNULL;
      n->rv.iv = UERR_BADOID;
    }
    return (0);




    /* look up a temporary */
  case TEVAL:
    if (n->nv.cv != (char *) 0 && n->nv.cv[0] != '\0') {
      Nod *tn;
      if ((tn = eval_gettmp (n->nv.cv, tmpsyms, w, aw)) != (Nod *) 0) {
        eval_adopt (tn, n, 0);
        return (0);
      }
    }
    n->rv.t = VNULL;
    n->rv.iv = UERR_NOATTR;
    return (0);




    /* look up an element value and return the contents */
  case EVAL:
    /* both elemental nodes should be ready to eval */
    if (n->r == NNULL || n->r->r == NNULL) {
      n->rv.t = VNULL;
      n->rv.iv = UERR_BADOID;
      return (0);
    }
    /* if this is not the case, we have a scrambled parse tree */
    if (n->r->nv.t != IDENT) {
      n->rv.t = VNULL;
      n->rv.iv = UERR_FATAL;
      return (1);
    }
    if (eval (n->r->r, w, aw, ac, av) || eval (n->r->l, w, aw, ac, av))
      return (1);
    if (n->r->r->rv.t != OID && n->r->r->rv.t != STR) {
      n->rv.t = VNULL;
      n->rv.iv = n->r->r->rv.iv;
      return (0);
    }
    /* now get the attribute of the object in question */
    n->rv.cv =
      ut_getatt (n->r->r->rv.cv, 1, (char *) 0, n->r->l->rv.cv, (char *) 0);
    if (n->rv.cv == (char *) 0) {
      n->rv.t = VNULL;
      n->rv.iv = UERR_NOATTR;
      return (0);
    }

    /* permissions for the small minded - check IF it exists (faster) */
    if (!ut_flagged (aw, var_wiz) &&
      !var_ispublic (n->r->l->rv.cv, w, aw, n->r->r->rv.cv) &&
      !ut_isobjown (aw, n->r->r->rv.cv)) {
      n->rv.t = VNULL;
      n->rv.iv = UERR_PERM;
      return (0);
    }

    /* *ATTEMPT* to give it SOME kind of type */
    if (attistype (n->rv.cv, typ_int)) {
      n->rv.t = NUM;
      n->rv.iv = atoi (attdata (n->rv.cv));
    } else if (attistype (n->rv.cv, typ_flag)) {
      n->rv.t = NUM;
      n->rv.iv = 1;
    } else {
      n->rv.t = STR;
      n->rv.cv = attdata (n->rv.cv);
    }
    return (0);




    /* chained evaluation. grammar ensures this had *better* be an OID */
  case IDENT:
    if (eval (n->r, w, aw, ac, av) || eval (n->l, w, aw, ac, av))
      return (1);
    if (n->l->rv.t != STR && n->l->rv.t != OID) {
      n->rv.t = VNULL;
      n->rv.iv = UERR_BADOID;
      return (0);
    }
    if ((n->r->rv.t != STR && n->r->rv.t != OID) || n->r->rv.cv == (char *) 0) {
      n->rv.t = VNULL;
      n->rv.iv = UERR_BADOID;
      return (0);
    }
    n->rv.cv = ut_getatt (n->r->rv.cv, 0, typ_obj, n->l->rv.cv, (char *) 0);
    if (n->rv.cv == (char *) 0) {
      n->rv.t = VNULL;
      n->rv.iv = UERR_NOATTR;
      return (0);
    }
    if (!ut_flagged (aw, var_wiz) &&
      !var_ispublic (n->l->rv.cv, w, aw, n->r->rv.cv) &&
      !ut_isobjown (aw, n->r->rv.cv)) {
      n->rv.t = VNULL;
      n->rv.iv = UERR_PERM;
      return (0);
    }
    n->rv.t = OID;
    return (0);




    /* basic sanity check on an object specifier */
  case OID:
    if (!ut_isgoodid (n->nv.cv)) {
      n->rv.t = VNULL;
      n->rv.iv = UERR_BADOID;
    }
    n->rv.t = OID;
    n->rv.cv = n->nv.cv;
    return (0);




    /* addition operation. string addition is overloaded */
  case ADD:
    if (n->l == NNULL || n->r == NNULL ||
      eval (n->r, w, aw, ac, av) || eval (n->l, w, aw, ac, av))
      return (1);
    if (n->l->rv.t == NUM && n->r->rv.t == NUM) {
      n->rv.t = NUM;
      n->rv.iv = n->l->rv.iv + n->r->rv.iv;
      return (0);
    }

    /* overload cast promote to string */
    if (n->l->rv.t == STR) {
      int l1 = strlen (n->l->rv.cv);
      int l2;
      char *x;
      x = eval_caststr (n->r);
      l2 = strlen (x);
      n->rv.t = STR;
      n->rv.cv = tmpalloc ((unsigned) (l1 + l2 + 1));
      bcopy (n->l->rv.cv, n->rv.cv, l1);
      bcopy (x, n->rv.cv + l1, l2);
      *(n->rv.cv + l1 + l2) = '\0';
      return (0);
    }
    n->rv.t = VNULL;
    n->rv.iv = UERR_TYPE;
    return (0);




    /* subtraction operation. numeric only */
  case SUB:
    if (n->l == NNULL || n->r == NNULL ||
      eval (n->r, w, aw, ac, av) || eval (n->l, w, aw, ac, av))
      return (1);
    if (n->l->rv.t == NUM && n->r->rv.t == NUM) {
      n->rv.t = NUM;
      n->rv.iv = n->l->rv.iv - n->r->rv.iv;
      return (0);
    }
    n->rv.t = VNULL;
    n->rv.iv = UERR_TYPE;
    return (0);




    /* multiply - used for numerics only */
  case MUL:
    if (n->l == NNULL || n->r == NNULL ||
      eval (n->r, w, aw, ac, av) || eval (n->l, w, aw, ac, av))
      return (1);
    if (n->l->rv.t == NUM && n->r->rv.t == NUM) {
      n->rv.t = NUM;
      n->rv.iv = n->l->rv.iv * n->r->rv.iv;
      return (0);
    }
    n->rv.t = VNULL;
    n->rv.iv = UERR_TYPE;
    return (0);




    /* division with check for div by zero - numerics only */
  case DIV:
    if (n->l == NNULL || n->r == NNULL ||
      eval (n->r, w, aw, ac, av) || eval (n->l, w, aw, ac, av))
      return (1);
    if (n->l->rv.t == NUM && n->r->rv.t == NUM) {
      n->rv.t = NUM;
      if (n->r->rv.iv == 0) {
        n->rv.t = VNULL;
        n->rv.iv = UERR_ZDIV;
        return (0);
      }
      n->rv.iv = n->l->rv.iv / n->r->rv.iv;
      return (0);
    }
    n->rv.t = VNULL;
    n->rv.iv = UERR_TYPE;
    return (0);




    /* equality */
  case EQ:
  case NE:
    if (n->l == NNULL || n->r == NNULL ||
      eval (n->r, w, aw, ac, av) || eval (n->l, w, aw, ac, av))
      return (1);
    n->rv.t = NUM;
    if (n->l->rv.t == NUM && n->r->rv.t == NUM) {
      if (n->nv.t == EQ)
        n->rv.iv = (n->l->rv.iv == n->r->rv.iv);
      else
        n->rv.iv = (n->l->rv.iv != n->r->rv.iv);
      return (0);
    }
    if ((n->l->rv.t == STR || n->l->rv.t == OID) &&
      (n->r->rv.t == STR || n->r->rv.t == OID)) {
      int xx = strcmp (n->r->rv.cv, n->l->rv.cv);
      if (n->nv.t == EQ)
        n->rv.iv = xx == 0 ? 1 : 0;
      else
        n->rv.iv = xx != 0 ? 1 : 0;
      return (0);
    }
    if (n->l->rv.t == VNULL && n->r->rv.t == VNULL) {
      if (n->nv.t == EQ)
        n->rv.iv = 1;
      else
        n->rv.iv = 0;
      return (0);
    }
    n->rv.t = VNULL;
    n->rv.iv = UERR_TYPE;
    return (0);




    /* grouping operations */
  case AND:
  case OR:
    n->rv.t = NUM;
    if (n->l == NNULL || n->r == NNULL ||
      eval (n->r, w, aw, ac, av) || eval (n->l, w, aw, ac, av))
      return (1);
    if (n->nv.t == AND)
      n->rv.iv = (eval_castbool (n->l) && eval_castbool (n->r));
    else
      n->rv.iv = (eval_castbool (n->l) || eval_castbool (n->r));
    return (0);




    /* ordering operation. string ordering is overloaded. */
  case LT:
  case LTE:
  case GT:
  case GTE:
    if (n->l == NNULL || n->r == NNULL ||
      eval (n->r, w, aw, ac, av) || eval (n->l, w, aw, ac, av))
      return (1);
    n->rv.t = NUM;
    if (n->l->rv.t == NUM && n->r->rv.t == NUM) {
      if (n->nv.t == LTE)
        n->rv.iv = (n->l->rv.iv <= n->r->rv.iv);
      else if (n->nv.t == LT)
        n->rv.iv = (n->l->rv.iv < n->r->rv.iv);
      else if (n->nv.t == GT)
        n->rv.iv = (n->l->rv.iv > n->r->rv.iv);
      else
        n->rv.iv = (n->l->rv.iv >= n->r->rv.iv);
      return (0);
    }
    if (n->l->rv.t == STR && n->r->rv.t == STR) {
      int x = strcmp (n->l->rv.cv, n->r->rv.cv);
      if (n->nv.t == LTE)
        n->rv.iv = (x <= 0);
      else if (n->nv.t == LT)
        n->rv.iv = (x < 0);
      else if (n->nv.t == GT)
        n->rv.iv = (x > 0);
      else
        n->rv.iv = (x >= 0);
      return (0);
    }
    n->rv.t = VNULL;
    n->rv.iv = UERR_TYPE;
    return (0);




    /* negate numeric value */
  case NEGATE:
    if (n->l == NNULL || eval (n->l, w, aw, ac, av))
      return (1);
    n->rv.t = NUM;
    if (n->l->rv.t == NUM) {
      n->rv.t = NUM;
      n->rv.iv = -(n->l->rv.iv);
      return (0);
    }
    n->rv.t = VNULL;
    n->rv.iv = UERR_TYPE;
    return (0);




    /* invert truth sense */
  case NOT:
    if (n->l == NNULL || eval (n->l, w, aw, ac, av))
      return (1);
    n->rv.t = NUM;
    n->rv.iv = !(eval_castbool (n->l));
    return (0);




    /* pose a NULL */
  case VNULL:
    n->rv.t = n->nv.t;
    n->rv.iv = n->nv.iv;
    return (0);




    /* numeric literal */
  case NUM:
    n->rv.t = n->nv.t;
    n->rv.iv = n->nv.iv;
    return (0);




    /* string literal */
  case STR:
    n->rv.t = n->nv.t;
    n->rv.cv = n->nv.cv;
    return (0);




    /* error */
  default:
    n->rv.t = VNULL;
    n->rv.iv = UERR_FATAL;
    return (1);
  }
}