/
umud/DOC/
umud/DOC/U/
umud/DOC/U/U-examples/
umud/DOC/internals/
umud/DOC/wizard/
umud/MISC/
umud/MISC/dbchk/
umud/RWHO/rwhod/
%{
#include	<stdio.h>
#include	<ctype.h>

/* hack */
#define	DONT_REDEFINE_MALLOC_DAMMIT

#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	Nod	*node();
static	Nod	*compiled;		/* root of compiled program */
static	Nod	*tmpsyms;		/* root of scope symbol table */
static	Nod	*ins_stmt();


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));
	}
	| '#' NUM '@' IDENT
	{
		char	obuf[MAXOID];
		int	l;

		/* must assemble parsed object-id - icky, but it works */
		$$ = node(OID,NNULL,NNULL);
		(void)itoa($2,obuf);
		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));
	}
	;
%%


/* 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(s)
char	*s;
{
	yyin = s;
}



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



int
parser_run(who,aswho,ac,av)
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 >= 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	int
yyerror(s)
char	*s;
{
	say(p_who,s,"\n",(char *)0);
}




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

	if((ret = (Nod *)tmpalloc(sizeof(Nod))) == (Nod *)0) {
		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(val)
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(val)
int	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(val)
int	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 */
eval(n,w,aw,ac,av)
Nod	*n;			/* root node */
char	*w;			/* who */
char	*aw;			/* aswho */
int	ac;			/* initial arg count */
char	*av[];			/* arg vector */
{
	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,0);
		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);
		}
		/* 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->nv.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->nv.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->nv.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);
	}
}