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