/*
Copyright (C) 1991, Marcus J. Ranum. All rights reserved.
*/
#ifndef lint
static char RCSid[] = "$Header: /home/mjr/hacks/umud/RCS/run.c,v 1.12 91/10/22 11:29:28 mjr Exp $";
#endif
/* configure all options BEFORE including system stuff. */
#include "config.h"
#include <stdio.h>
#include <ctype.h>
#include "mud.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;
#ifdef COMMAND_STATS
static long int hotwired = 0;
static long int atsyscmd = 0;
static long int syscmd = 0;
static long int exitstaken = 0;
static long int whomac = 0;
static long int aswhomac = 0;
static long int roommac = 0;
static long int usemac = 0;
static long int invmac = 0;
cmd__cmdstats(ac,av,who,aswho)
int ac;
char *av[];
char *who;
char *aswho;
{
char buf[32];
say(who,"Hotwired (i.e. : and \") ",itoa(hotwired,buf),"\n",(char *)0);
say(who,"@<system macro/command> ",itoa(atsyscmd,buf),"\n",(char *)0);
say(who,"<system macro/command> ",itoa(syscmd,buf),"\n",(char *)0);
say(who,"exits taken ",itoa(exitstaken,buf),"\n",(char *)0);
say(who,"macros on who ",itoa(whomac,buf),"\n",(char *)0);
say(who,"macros on aswho ",itoa(aswhomac,buf),"\n",(char *)0);
say(who,"macros on rooms ",itoa(roommac,buf),"\n",(char *)0);
say(who,"macros on used objects ",itoa(usemac,buf),"\n",(char *)0);
say(who,"macros on inventory ",itoa(invmac,buf),"\n",(char *)0);
}
#endif
/*
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. */
*(xp - 1) = '\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 ;)
*/
static 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));
}
/*
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;
char *listp;
char *here;
/* PHASE #1 - check for hard wired call to system function */
if(av[0][0] == '@') {
if((c = cmdlookup(av[0] + 1)) != (Cmd *)0) {
#ifdef COMMAND_STATS
atsyscmd++;
#endif
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);
}
return((*c->func)(ac,av,who,aswho));
}
/* call system macro if one */
if(!nowhereman && (macp = symlook(av[0] + 1)) != (char *)0){
#ifdef COMMAND_STATS
atsyscmd++;
#endif
return(run(who,who,macp,ac,av,0));
}
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){
#ifdef COMMAND_STATS
exitstaken++;
#endif
return(player_go(ac,av,who,here,ob));
}
/* PHASE #3 - look for macro attached to caller */
macp = ut_getatt(who,0,typ_cmd,av[0],(char *)0);
if(macp != (char *)0){
#ifdef COMMAND_STATS
whomac++;
#endif
return(run(who,who,macp,ac,av,0));
}
/* PHASE #3a - look for macro attached to aswho -- for luck */
macp = ut_getatt(aswho,0,typ_cmd,av[0],(char *)0);
if(macp != (char *)0){
#ifdef COMMAND_STATS
aswhomac++;
#endif
return(run(who,aswho,macp,ac,av,0));
}
/* PHASE #4 - look for macro attached to room */
macp = ut_getatt(here,0,typ_cmd,av[0],(char *)0);
if(macp != (char *)0){
#ifdef COMMAND_STATS
roommac++;
#endif
return(run(who,here,macp,ac,av,0));
}
/* PHASE #5 - look for macro attached to object in use */
macp = ut_getatt(who,0,typ_cmd,var_using,av[0],(char *)0);
if(macp != (char *)0) {
char *up;
up = ut_getatt(who,0,typ_obj,var_using,(char *)0);
if(up != (char *)0){
#ifdef COMMAND_STATS
usemac++;
#endif
return(run(who,up,macp,ac,av,0));
}
}
#ifdef SEARCH_INVENTORY
/* PHASE #5a - look for a macro on anything the player is carrying */
listp = ut_getatt(who,0,typ_list,var_cont,(char *)0);
listp = lstnext(listp,ob);
while(listp != (char *)0){
macp = ut_getatt(ob,0,typ_cmd,av[0],(char *)0);
if(macp != (char *)0){
#ifdef COMMAND_STATS
invmac++;
#endif
return(run(who,ob,macp,ac,av,0));
}
listp = lstnext(listp,ob);
}
#endif /* SEARCH_INVENTORY */
/* 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) {
#ifdef COMMAND_STATS
syscmd++;
#endif
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);
}
return((*c->func)(ac,av,who,aswho));
}
/* PHASE #7 call system macro if one */
if(!nowhereman && (macp = symlook(av[0])) != (char *)0){
#ifdef COMMAND_STATS
syscmd++;
#endif
return(run(who,who,macp,ac,av,0));
}
/* 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) {
actor = who;
recursion_depth = 0;
} else {
if(++recursion_depth > 20) {
say(who,"Too many recursions.\n",(char *)0);
return(1);
}
}
/* we set 'bp' as the remaining text pointer here */
if((bp = s) == (char *)0 || *bp == '\0')
return(0);
/* 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;
#ifdef COMMAND_STATS
hotwired++;
#endif
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;
#ifdef COMMAND_STATS
hotwired++;
#endif
return(runargv(who,aswho,ac,av));
}
while(bp != (char *)0 && *bp != '\0') {
if(real_call)
actor = 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(-1);
case -2:
say(who,"too many tokens in command.\n",(char *)0);
return(-1);
}
continue;
}
uers += runargv(who,aswho,ac,av);
}
return(uers);
}
/* return the run_level of the run */
int
run_level()
{
return(recursion_depth);
}
/* return the actor of the run */
char *
run_actor()
{
return(actor);
}
/*
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,"wizard","wizard"));
}
logf("uknown command: ",av[0],"\n",(char *)0);
return(1);
}