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

#ifndef	lint
static	char	RCSid[] = "$Header: /home/mjr/hacks/umud/RCS/run.c,v 1.8 92/05/09 18:53:35 mjr Exp $";
#endif

/* configure all options BEFORE including system stuff. */
#include	"config.h"


#include	<stdio.h>
#include	<ctype.h>

#include	"mud.h"
#include	"sym.h"
#include	"cmd.h"
#include	"match.h"
#include	"vars.h"

/*
routines to run commands, expand variables, and tokenize. if you modify
this to search different locations for macros, you'll probably want to
look at runargv(), rather than all the string-thrashing code.

if you are adding a new variable or variable type, you will probably
want to look *CAREFULLY* at vlookup.
*/


static	int	nowhereman;		/* player is nowhere */
static	char	*actor;			/* real player who started all this */
static	int	recursion_depth;


/*
copy 's' into the expansion buffer. return nonzero if it don't fit.
*/
static	int
pastein(s,bp,lp)
char	*s;
char	**bp;
int	*lp;
{
	while(s && *s != '\0') {
		if(--(*lp) <= 0)
			return(1);
		**bp = *s++, (*bp)++;
	}
	return(0);
}




/*
	vlookup is responsible for taking a string and returning
another string that somehow has something to do with the first.
a buffer is provided, in case string space is needed.
*/
static	char	*
vlookup(nam,who,aswho,ac,av,buf,bsiz)
char	*nam;
char	*who;
char	*aswho;
int	ac;
char	*av[];
char	*buf;
int	bsiz;
{
	if(nam == (char *)0 || *nam == '\0')
		return((char *)0);


	/* locale */
	if(!strcmp(nam,"here"))
		return(ut_loc(aswho));


	/* actor */
	if(!strcmp(nam,"actor"))
		return(run_actor());


	/* me */
	if(!strcmp(nam,"me"))
		return(aswho);


	/* self */
	if(!strcmp(nam,"self"))
		return(aswho);


	/* subv */
	if(!strcmp(nam,"subv")) {
		char	*sp;

		if((sp = ut_getatt(who,0,typ_str,nam,(char *)0)) == (char *)0)
			return("it");
		return(sp);
	}

	/* Subv */
	if(!strcmp(nam,"Subv")) {
		char	*sp;

		if((sp = ut_getatt(who,0,typ_str,nam,(char *)0)) == (char *)0)
			return("It");
		return(sp);
	}


	/* objv */
	if(!strcmp(nam,"objv")) {
		char	*sp;

		if((sp = ut_getatt(who,0,typ_str,nam,(char *)0)) == (char *)0)
			return("it");
		return(sp);
	}

	/* Objv */
	if(!strcmp(nam,"Objv")) {
		char	*sp;

		if((sp = ut_getatt(who,0,typ_str,nam,(char *)0)) == (char *)0)
			return("It");
		return(sp);
	}


	/* posv */
	if(!strcmp(nam,"posv")) {
		char	*sp;

		if((sp = ut_getatt(who,0,typ_str,nam,(char *)0)) == (char *)0)
			return("its");
		return(sp);
	}

	/* Posv */
	if(!strcmp(nam,"Posv")) {
		char	*sp;

		if((sp = ut_getatt(who,0,typ_str,nam,(char *)0)) == (char *)0)
			return("Its");
		return(sp);
	}


	/* check for $# - param count */
	if(!strcmp(nam,"#"))
		return(itoa(ac,buf));


	/* check for $1 - $ac - individual params */
	if(isdigit(*nam)) {
		int	anum;

		if((anum = atoi(nam)) >= 0 && anum < ac)
			return(av[anum]);
		return((char *)0);
	}


	/* check for $* and $+# - concatenated params */
	if(*nam == '*' || *nam == '+') {
		char	*xp = buf;
		int	xs = bsiz;
		char	*yp;
		int	lo = 0;

		/* if $+#, then figure out the low bound */
		if(*nam != '*') {
			if((lo = atoi(nam + 1)) < 0 || lo >= ac)
				return((char *)0);
		}

		/* now concatenate args */
		for(; lo < ac; lo++) {
			for(yp = av[lo]; *yp != '\0';) {
				if(--xs <= 0)
					return((char *)0);
				*xp++ = *yp++;
			}
			if(--xs <= 0)
				return((char *)0);
			*xp++ = ' ';
		}

		/* ok. */
		if(xp > buf)
			*(xp - 1) = '\0';
		else
			*buf = '\0';
		return(buf);
	}


	/* and last but not least, we get to the hairy stuff. */
	if(*nam == '@') {
		char	ob[MAXOID];

		nam++;

		if(matchlocal(who,nam,ut_loc(who),MTCH_UNIQ|MTCH_NONLOC|MTCH_QUIET,ob)
				&& matchlocal(who,nam,ut_loc(who),MTCH_FRST|MTCH_QUIET,ob))
			return((char *)0);
		(void)strcpy(buf,ob);
		return(buf);
	}


	if(*nam == '!') {
		char	ob[MAXOID];
		int	avp;

		nam++;

		if(!isdigit(*nam))
			return((char *)0);
		if((avp = atoi(nam)) < 0 || avp >= ac)
			return((char *)0);

		if(matchlocal(who,av[avp],ut_loc(who),MTCH_UNIQ|MTCH_NONLOC|MTCH_QUIET,ob)
				&& matchlocal(who,av[avp],ut_loc(who),MTCH_FRST|MTCH_QUIET,ob))
			return((char *)0);

		(void)strcpy(buf,ob);
		return(buf);
	}

	return((char *)0);
}




/*
	iteratively look up the components of a $-variable and when
we reach a final result, paste it in.
*/
int
vresolve(nam,who,aswho,ac,av,bp,lp)
char	*nam;
char	*who;
char	*aswho;
int	ac;
char	*av[];
char	**bp;
int	*lp;
{
	Obj	*op;
	int	wiz;
	char	*e = nam;
	char	*lkp;
	char	*nxt;
	char	buf[MUDBUF];

	if(nam == (char *)0 || *nam == '\0')
		return(0);

	if((e = index(e,'.')) != (char *)0)
		*e++ = '\0';

	/* resolve first part */
	if((lkp = vlookup(nam,who,aswho,ac,av,buf,sizeof(buf))) == (char *)0)
		return(0);

	/* happy ending? (usual case) */
	if(e == (char *)0) {
		return(pastein(lkp,bp,lp));
	}

	/* sigh. */
	wiz = ut_flagged(aswho,var_wiz);
	
	/* gets tricky. we have '.'s in there. iterate our ass off */
	while(1) {
		char	*ap;
		char	*atp;

		if((nxt = e) == (char *)0  || *nxt == '\0')
			break;

		if((atp = var_namatch(nxt)) == (char *)0) {
			atp = nxt;
		}

		if((e = index(e,'.')) != (char *)0)
			*e++ = '\0';
		if((op = cache_get(lkp)) == (Obj *)0)
			return(0);
		if((ap = objattr(op,atp,(int *)0)) == (char *)0)
			return(0);
		if(e != (char *)0 && *e != '\0' && !attistype(ap,typ_obj))
			return(0);
		if((ap = attdata(ap)) == (char *)0)
			return(0);

		/* final insult: can the guy even see it ? */
		if(!wiz && !var_ispublic(atp,who,aswho,lkp) && !ut_isobjown(aswho,lkp))
			return(0);

		lkp = ap;
	}
	return(pastein(lkp,bp,lp));
}




/*
tokenize a command line into an argv. this is complex, but *CAN'T*
use static buffers since it can get called in recursion. this is
egregiously more complicated than I'd like it to be, but all this
string fiddling requires some hefty error checking. at least Saber-C
likes it, even if I don't  ;)
*/
int
enargv(bp,av,avsiz,buf,bsiz,retp,who,aswho,cac,cav)
char	*bp;
char	**av;
int	avsiz;
char	buf[];
int	bsiz;
char	**retp;
char	*who;
char	*aswho;
int	cac;
char	*cav[];
{
	char	*op;
	int	ac = 0;
	int	quot = 0;

	/* clean slate */
	av[0] = op = buf;
	*op = '\0';
	av[1] = (char *)0;

	/* preskip white/junk */
	while(*bp != '\0' && (isspace(*bp) || !isprint(*bp) || *bp == ';'))
		bp++;

	/* this is basically a BIG case statement */
	while(*bp != '\0') {


		/* drop nonprint */
		if(!isprint(*bp)) {
			bp++;
			continue;
		}


		/* set a quotation mark if on is needed */
		if(!quot && (*bp == '\"' || *bp == '\'')) {
			quot = *bp++;
			continue;
		}


		/* accept a word OR a virtual newline */
		if((isspace(*bp) || *bp == ';') && !quot) {
			if(bsiz-- <= 0) {
				if(retp != (char **)0)
					*retp = (char *)0;
				return(-1);
			}
			*op++ = '\0';

			if(++ac >= avsiz) {
				if(retp != (char **)0)
					*retp = (char *)0;
				return(-2);
			}
			av[ac] = op;
			av[ac + 1] = (char *)0;
			*op = '\0';

			while(isspace(*bp))	/* eat whitespace first! */
				bp++;

			/* if semic (virtual line break) return now */
			if(*bp == ';') {
				if(retp != (char **)0)
					*retp = ++bp;
				return(ac);
			}

			if(*bp == '\0') {
				if(retp != (char **)0)
					*retp = bp;
				return(ac);
			}

			continue;
		}


		/* end a quote */
		if(quot && *bp == quot) {
			quot = 0;
			bp++;
			continue;
		}


		/* check escapes - do *NOT* permit escaped newlines!!!! */
		if(*bp == '\\') {
			*op++ = *(++bp);

			if(*bp == '\0')
				break;
			bp++;
			continue;
		}


		/* handle '=' to tokenize to end-of-line hack */
		if(*bp == '=') {
			bp++;

			while(isspace(*bp) && *bp != '\0')
				bp++;

			/* if we are currently in mid-arg, terminate */
			if(av[ac] != (char *)0 && av[ac] != op) {
				*op++ = '\0';
				if(++ac >= avsiz) {
					if(retp != (char **)0)
						*retp = (char *)0;
					return(-2);
				}
				av[ac] = op;
			}

			while(*bp != '\0') {
				if(--bsiz < 0) {
					if(retp != (char **)0)
						*retp = (char *)0;
					return(-1);
				}
				*op++ = *bp++;
			}
			*op++ = '\0';

			if(++ac >= avsiz) {
				if(retp != (char **)0)
					*retp = (char *)0;
				return(-2);
			}
			av[ac] = (char *)0;
			if(retp != (char **)0)
				*retp = bp;
			return(ac);
		}


		/* expand $vars */
		if(quot != '\'' && *bp == '$') {
			char	vb[MAXVLEN];
			int	vl = 0;

			/* transform "$$" -> "$" */
			if(*(++bp) == '$') {
				if(--bsiz < 0) {
					if(retp != (char **)0)
						*retp = (char *)0;
					return(-1);
				}
				*op++ = *bp++;
				continue;
			}

			/* handle ${var} format */
			if(*bp == '{') {
			        bp++;
				while(*bp != '\0' && *bp != '}' && vl < sizeof(vb) - 2)
					vb[vl++] = *bp++;
				vb[vl] = '\0';
				if (*bp == '}')
					bp++;
				if(vresolve(vb,who,aswho,cac,cav,&op,&bsiz)) {
					if(retp != (char **)0)
						*retp = (char *)0;
					return(-1);
				}
				continue;
			}


			/* default format: word or suchlike junk */
			while(*bp != '\0' && vl < sizeof(vb) - 2 &&
				(isalpha(*bp) || isdigit(*bp) ||
				*bp == '.' || *bp == '_' || *bp == '+' ||
				*bp == '*' || *bp == '#'))
					vb[vl++] = *bp++;
			vb[vl] = '\0';
			if(vresolve(vb,who,aswho,cac,cav,&op,&bsiz)) {
				if(retp != (char **)0)
					*retp = (char *)0;
				return(-1);
			}
			continue;
		}


		/* default: just copy character */
		if(--bsiz < 0) {
			if(retp != (char **)0)
				*retp = (char *)0;
			return(-1);
		}
		*op++ = *bp++;
	}


	/* partially finished word at null */
	if(av[ac] != (char *)0 && av[ac][0] != '\0') {
		*op = '\0';

		if(ac++ >= avsiz) {
			if(retp != (char **)0)
				*retp = (char *)0;
			return(-2);
		}
		av[ac] = (char *)0;
	}

	if(retp != (char **)0)
		*retp = bp;
	return(ac);
}




run_tokenize(bp,av,avsiz,tobuf,tobsiz)
char	*bp;
char	**av;
int	avsiz;
char	*tobuf;
int	tobsiz;
{
	return(enargv(bp,av,avsiz,tobuf,tobsiz,(char **)0,"","",0,(char **)0));
}


static	int
run_execute(who,aswho,macp,ac,av,rv)
char	*who;
char	*aswho;
char	*macp;
int	ac;
char	*av[];
int	*rv;
{
	if(attistype(macp,typ_cmd)) {
		*rv = run(who,aswho,attdata(macp),ac,av,0);
		return(1);
	}
	if(attistype(macp,typ_u)) {
		parser_setinput(attdata(macp));
		if(parser_compile(who))
			(void)parser_run(who,aswho,ac,av);
		*rv = UERR_NONE;
		return(1);
	}
	return(0);
}



/*
run an argv'd argc'd command line. here's where to insert MUD-local
command-line processing that needs to take place before commands run
*/
static	int
runargv(who,aswho,ac,av)
char	*who;
char	*aswho;
int	ac;
char	*av[];
{
	Cmd	*c;
	char	ob[MAXOID];
	char	*macp;
	int	mtyp;
	char	*here;

	/* PHASE #1 - check for hard wired call to system function */
	if(av[0][0] == '@') {

		if((c = cmdlookup(av[0] + 1)) != (Cmd *)0) {
			if(ac < c->argc ||
				(c->argc != ac && (c->flgs & CM_FIXARG))) {
				say(who,"usage: ",c->usage,"\n",(char *)0);
				return(1);
			}

			if((c->flgs & CM_PRIV) && !ut_flagged(aswho,var_wiz)) {
				say(who,"permission denied.\n",(char *)0);
				return(1);
			}


			if(nowhereman && !(c->flgs & CM_NOWHERE) && !ut_flagged(aswho,var_wiz)) {
				say(who,"You are nowhere. Go away.\n",(char *)0);
				return(1);
			}

#ifdef	PLAYERONLY
			if((c->flgs & CM_NOPLY) && !ut_flagged(who,var_isplay)
				&& !ut_flagged(aswho,var_wiz))
				return(1);
#endif

			return((*c->func)(ac,av,who,aswho));
		}

		/* call system macro if one */
		if(!nowhereman && (macp = symlook(av[0] + 1,&mtyp)) != (char *)0){
			char	*whop = aswho;

			/* handle system setuid macros - DANGER */
			if(mtyp & SFLG_SUID)
				if((whop = symowner(av[0] + 1)) == (char *)0)
					whop = aswho;

			if(mtyp & SFLG_CMD)
				return(run(who,whop,macp,ac,av,0));
			if(mtyp & SFLG_UCMD) {
				parser_setinput(macp);
				if(parser_compile(who))
					(void)parser_run(who,whop,ac,av);
				return(UERR_NONE);
			}
		}

		say(who,"\"",av[0],"\": unknown command.\n",(char *)0);
		return(1);
	}

	/* folks who are nowhere can't do this */
	if(nowhereman)
		goto nowherejump;

	/* PHASE #2 - look for exit in room */
	here = ut_loc(who);
	if(matchargvexit(here,av,ac,ob) != 0){
		return(player_go(ac,av,who,here,ob));
	}


	/* PHASE #3 - look for macro attached to caller */
	macp = ut_getatt(who,1,(char *)0,av[0],(char *)0);
	if(macp != (char *)0 && run_execute(who,who,macp,ac,av,&mtyp))
		return(mtyp);


	/* PHASE #3a - look for macro attached to aswho -- for luck */
	macp = ut_getatt(aswho,1,(char *)0,av[0],(char *)0);
	if(macp != (char *)0 && run_execute(who,aswho,macp,ac,av,&mtyp))
		return(mtyp);


	/* PHASE #4 - look for macro attached to room */
	macp = ut_getatt(here,1,(char *)0,av[0],(char *)0);
	if(macp != (char *)0 && run_execute(who,here,macp,ac,av,&mtyp))
		return(mtyp);


#ifdef	COMBAT
	/* PHASE #4.5 - look for macro attached to object in use */
	macp = ut_getatt(who,0,typ_obj,var_weapon,(char *)0);
	if(macp != (char *)0) {
		char	*up;

		up = ut_getatt(macp,1,(char *)0,av[0],(char *)0);
		if(up != (char *)0 && run_execute(who,macp,up,ac,av,&mtyp))
			return(mtyp);
	}
#endif


	/* PHASE #5 - look for macro attached to object in use */
	macp = ut_getatt(who,0,typ_obj,var_using,(char *)0);
	if(macp != (char *)0) {
		char	*up;

		up = ut_getatt(macp,1,(char *)0,av[0],(char *)0);
		if(up != (char *)0 && run_execute(who,macp,up,ac,av,&mtyp))
			return(mtyp);
	}

#ifdef SEARCH_INVENTORY
	/* PHASE #5a - look for a macro on anything the player is carrying */
	macp = ut_getatt(who,0,typ_list,var_cont,(char *)0);
	macp = lstnext(macp,ob,sizeof(ob));
	while(macp != (char *)0){
		char	*listp;
		listp = ut_getatt(ob,1,(char *)0,av[0],(char *)0);
		if(listp != (char *)0 && run_execute(who,ob,listp,ac,av,&mtyp))
			return(mtyp);
		macp = lstnext(macp,ob,sizeof(ob));
	}
#endif /* SEARCH_INVENTORY */

#ifdef SEARCH_ROOM
	/* PHASE #5b - look for a macro on anything in the room (ugh!) */
	macp = ut_getatt(here,0,typ_list,var_cont,(char *)0);
	macp = lstnext(macp,ob,sizeof(ob));
	while(macp != (char *)0){
		char	*listp;
		listp = ut_getatt(ob,1,(char *)0,av[0],(char *)0);
		if(listp != (char *)0 && run_execute(who,ob,listp,ac,av,&mtyp))
			return(mtyp);
		macp = lstnext(macp,ob,sizeof(ob));
	}
#endif /* SEARCH_ROOM */
	
/* skip stuff for folks who are nowhere */
nowherejump:

	/* PHASE #6 - check for non-hardwired call to system functions */
	if((c = cmdlookup(av[0])) != (Cmd *)0) {
		if(ac < c->argc || (c->argc != ac && (c->flgs & CM_FIXARG))) {
			say(who,"usage: ",c->usage,"\n",(char *)0);
			return(1);
		}

		if((c->flgs & CM_PRIV) && !ut_flagged(aswho,var_wiz)) {
			say(who,"permission denied.\n",(char *)0);
			return(1);
		}

		if(nowhereman && !(c->flgs & CM_NOWHERE) && !ut_flagged(aswho,var_wiz)) {
			say(who,"You are nowhere. Go away.\n",(char *)0);
			return(1);
		}

#ifdef	PLAYERONLY
		if((c->flgs & CM_NOPLY) && !ut_flagged(who,var_isplay)
			&& !ut_flagged(aswho,var_wiz))
			return(1);
#endif

		return((*c->func)(ac,av,who,aswho));
	}

	/* PHASE #7 call system macro if one */
	if(!nowhereman && (macp = symlook(av[0],&mtyp)) != (char *)0){
		char	*whop = who;

		/* handle system setuid macros - DANGER */
		if(mtyp & SFLG_SUID)
			if((whop = symowner(av[0])) == (char *)0)
				whop = aswho;

		if(mtyp & SFLG_CMD)
			return(run(who,whop,macp,ac,av,0));
		if(mtyp & SFLG_UCMD) {
			parser_setinput(macp);
			if(parser_compile(who))
				(void)parser_run(who,whop,ac,av);
			return(UERR_NONE);
		}
	}


	/* PHASE #8 - barf. */
	say(who,"Huh? What is \"",av[0],"\"?\n",(char *)0);
	return(1);
}




/*
get a line of commands from 'who' and repeatedly break it into parameters,
or simply detect hotwired commands and call them.
the eb and ab, etc must not be static buffers, as this can be called
recursively.
*/
run(who,aswho,s,argc,argv,real_call)
char	*who;
char	*aswho;
char	*s;
int	argc;
char	*argv[];
int	real_call;
{
	char	*bp;
	char	*bp2;
	char	ab[MUDBUF * 2];		/* buffer to tokenize into */
	char	*av[MAXARG];		/* token vector */
	int	ac;
	int	uers = 0;

	/* some sanity checking and whatnot */
	if(real_call) {
		run_setactor(who);
		recursion_depth = 0;
	} else {
		if(++recursion_depth > MAXRECURSIONS) {
			say(who,"Too many recursions.\n",(char *)0);
			return(UERR_FATAL);
		}
	}

	/* we set 'bp' as the remaining text pointer here */
	if((bp = s) == (char *)0 || *bp == '\0')
		return(UERR_NONE);

	/* Preskip whitespace, obviously */
	while(isspace(*bp))
		bp++;


	/*
	check for hotwired commands, and if there are any, fake up an
	argc, argv, and just run that.
	hotwire say */
	if(*bp == '\"' && !nowhereman) {
		ac = 2;
		av[0] = "say";
		av[1] = bp + 1;
		av[2] = (char *)0;
		bp = (char *)0;
		return(runargv(who,aswho,ac,av));
	}

	if(*bp == ':' && !nowhereman) {
		ac = 2;
		av[0] = "do";
		av[1] = bp + 1;
		av[2] = (char *)0;
		bp = (char *)0;
		return(runargv(who,aswho,ac,av));
	}

	if(*bp == '!' && !nowhereman) {
		run_setactor(who);
		parser_setinput(bp + 1);
		if(parser_compile(who))
			(void)parser_run(who,aswho,ac,av);
		return(UERR_NONE);
	}

	while(bp != (char *)0 && *bp != '\0') {
		if(real_call)
			run_setactor(who);

		/* tokenize the line and deal with it */
		ac = enargv(bp,av,MAXARG,ab,sizeof(ab),&bp2,who,aswho,argc,argv);
		bp = bp2;
		if(ac == 0)
			continue;

		if(ac < 0) {
			switch(ac) {
			case	-1:
				say(who,"command input too large.\n",(char *)0);
				return(UERR_FATAL);

			case 	-2:
				say(who,"too many tokens in command.\n",(char *)0);
				return(UERR_FATAL);
			}
			continue;
		}

		uers = runargv(who,aswho,ac,av);
	}
	return(uers);
}





/* return the run_level of the run */
int
run_level()
{
	return(recursion_depth);
}





/* add one to the run level */
int
add_run_level()
{
	return(++recursion_depth);
}





/* return the actor of the run */
char	*
run_actor()
{
	return(actor);
}



void
run_setactor(s)
char	*s;
{
	actor = s;
}




/* run a command line at boot-time.  */
int
run_boot(l)
char	*l;
{
	Cmd	*c;
	char	*av[MAXARG];
	char	ab[BUFSIZ];
	int	ac;

	ac = run_tokenize(l,av,MAXARG,ab,sizeof(ab));
	if(ac == 0)
		return(0);
	if(ac < 0) {
		logf("tokenization error in boot command.\n",(char *)0);
		return(1);
	}

	if((c = cmdlookup(av[0])) != (Cmd *)0) {
		if(ac < c->argc ||
			(c->argc != ac && (c->flgs & CM_FIXARG))) {
			logf("usage: ",c->usage,"\n",(char *)0);
			return(1);
		}
		return((*c->func)(ac,av,PRIVUSER,PRIVUSER));
	}

	logf("uknown command: ",av[0],"\n",(char *)0);
	return(1);
}