/* Copyright 1989, 1990 by James Aspnes, David Applegate, and Bennet Yee */ /* See the file COPYING for distribution information */ /* Command interpreter */ #include <ctype.h> #include "config.h" #include "db.h" #include "bytecode.h" #include "globals.h" /* for before and after handlers */ void do_action(datum actor, datum thing, datum verb) { datum action; if((action = lookup_action(thing, verb)) != NOTHING) { /* got it */ PUSH_GLOBALS { you = actor; me = thing; text = NOTHING; run_action(action); } POP_GLOBALS; } } /* runs action verb on obj with you = actor */ /* mt and t hold string versions of mtext and text */ /* returns nonzero if successful */ static int try_action(datum actor, datum verb, datum obj, const char *mt, const char *t) { datum action; datum location; if((action = lookup_action(obj, verb)) != NOTHING) { /* got it */ location = safe_get(actor, location); do_action(actor, location, BEFORE_ACTION); do_action(actor, actor, BEFORE_ACTION); PUSH_GLOBALS { you = actor; me = obj; mtext = intern(mt); text = intern(t); run_action(action); } POP_GLOBALS; do_action(actor, actor, AFTER_ACTION); do_action(actor, location, AFTER_ACTION); return 1; } else { return 0; } } static datum special_match(datum actor, datum location, const char *string) { datum x; if(!strcmp(string, ME_STRING)) { return actor; } else if(!strcmp(string, HERE_STRING)) { return location; } else if(*string == NUMERIC_NAME_TOKEN && (x = atol(string+1)) != NOTHING && controls(actor, x)) { return x; } else { return NOTHING; } } static void parse_program(datum actor, const char *command) { byte *code; if(!flag_set(actor, F_PROGRAMMER)) { notify(actor, "You are not permitted to run programs."); return; } else if((code = compile(command)) == 0) { notify(actor, compile_error); } else { PUSH_GLOBALS { me = you = actor; mtext = text = NOTHING; run_code(code); free((void *) code); } POP_GLOBALS; } return; } /* tries performing a match on vtext, itext, and otext */ /* actor sets you */ /* location is actor's location */ /* actor_contents and location_contents are the contents sets */ /* vtext is the verb */ /* otext is the object being acted on */ /* itext is the unparsed text */ /* returns nonzero if successful */ static int try_match(datum actor, datum location, set actor_contents, set loc_contents, const char *vtext, const char *otext, const char *itext) { datum verb; /* interned vtext */ datum objname; /* interned otext */ datum obj; /* matched object */ if((verb = intern_soft(vtext)) != NOTHING) { if((obj = special_match(actor, location, otext)) != NOTHING) { if(try_action(actor, verb, obj, otext, itext)) return 1; } else if((objname = intern_soft(otext)) != NOTHING) { SET_FOREACH_MATCH(loc_contents, objname, obj) { if(try_action(actor, verb, obj, otext, itext)) return 1; } END_SET_FOREACH; SET_FOREACH_MATCH(actor_contents, objname, obj) { if(try_action(actor, verb, obj, otext, itext)) return 1; } END_SET_FOREACH; } } /* we lost */ return 0; } /*** parse_command ***/ /* Parses a command line and sends out the appropriate methods. */ /* Tries the following templates, in order: */ /* Verb (on room) => verb handler on room */ /* Verb (on actor) => verb handler on actor */ /* Object => _invoke handler on matched object */ /* Verb Object => verb handler on object */ /* Verb Object1 Prep Object2 => verb<prep on obj1 or verb>prep on obj2 or verb^prep on room or verb^prep on actor */ /* Verb text => verb handler on room */ /* Verb text => verb handler on actor */ /* ??? => _default on room */ /* ??? => _default on actor */ /* when matching an object, the room's contents are always checked first */ void parse_command(datum actor, const char *safe_command) { char cbuf[MAX_STRLEN+1]; /* mutable copy of safe_command */ datum verb; /* interned verb */ char vbuf[MAX_STRLEN+1]; /* buffer for compound verb */ char *vp; /* pointer to left/right location in vbuf */ int c; /* saved character */ char *command; /* start of command */ char *args; /* start of verb arguments */ char *obj1end; /* end of first obj */ char *pstart; /* start of preposition */ char *pend; /* end of preposition */ char *obj2start; /* start of second object */ datum obj; /* matched object */ datum location; /* location of actor */ set loc_contents; /* contents lists */ set actor_contents; extern set get_contents(datum); /* verify that actor exists and can run commands */ if(!flag_set(actor, F_PLAYER)) return; /* eat leading whitespace */ while(*safe_command && isspace(*safe_command)) safe_command++; /* look for special command */ if(*safe_command == RUN_CODE_COMMAND) { parse_program(actor, safe_command+1); return; } /* get actor's location */ /* if actor isn't anywhere, lose */ if((location = safe_get(actor, location)) == NOTHING) goto parse_failed; /* check for illegal character */ /* this excludes calling _before, _after, _tick, etc. */ if(!isalnum(*safe_command)) { goto parse_failed; } /* everything ok, do the parse */ strip_whitespace(safe_command, cbuf); command = cbuf; /* check for room command or actor command */ if((verb = intern_soft(command)) != NOTHING) { if(try_action(actor, verb, location, 0, 0)) return; if(try_action(actor, verb, actor, 0, 0)) return; } /* get location and actor contents lists */ loc_contents = get_contents(location); actor_contents = get_contents(actor); /* rebuild the name lists -- this forces aliases to be interned */ set_build_name_list(loc_contents); set_build_name_list(actor_contents); /* check for object invocation */ /* reintern verb in case it's there now but wasn't before list builds */ if((verb = intern_soft(command)) != NOTHING) { SET_FOREACH_MATCH(loc_contents, verb, obj) { if(try_action(actor, INVOKE_ACTION, obj, command, 0)) return; } END_SET_FOREACH; SET_FOREACH_MATCH(actor_contents, verb, obj) { if(try_action(actor, INVOKE_ACTION, obj, command, 0)) return; } END_SET_FOREACH; } /* must be V O or V O P O */ /* try V O first */ /* get the verb word */ for(args = command; *args && !isspace(*args); args++); if(*args != '\0') *args++ = '\0'; /* check for no objects */ if(!*args) goto parse_failed; /* try V O */ if(try_match(actor, location, actor_contents, loc_contents, command, args, 0)) return; /* try V O P O */ /* don't try to read this cruft! */ /* copy V into vbuf */ strcpy(vbuf, command); vp = vbuf + strlen(vbuf); /* walk obj1end across args */ obj1end = args; for(;;) { /* add a word to obj1 */ while(*obj1end && !isspace(*obj1end)) obj1end++; if(*obj1end == '\0') break; /* no room for P O2 */ /* find start of prep */ for(pstart = obj1end; *pstart && isspace(*pstart); pstart++); if(*pstart == '\0') break; /* no prep */ /* find end of prep */ for(pend = pstart; *pend && !isspace(*pend); pend++); if(*pend == '\0') break; /* no room for O2 */ /* find start of obj2 */ for(obj2start = pend; *obj2start && isspace(*obj2start); obj2start++); if(*obj2start == '\0') break; /* no obj2 */ /* else all the pointers work */ /* mark obj1end */ c = *obj1end; *obj1end = '\0'; /* grab the verb */ strncpy(vp+1, pstart, pend - pstart); vp[1 + pend - pstart] = '\0'; /* try verb>prep */ vp[0] = RIGHT_ACTION; if(try_match(actor, location, actor_contents, loc_contents, vbuf, obj2start, args)) return; /* try verb<prep */ vp[0] = LEFT_ACTION; if(try_match(actor, location, actor_contents, loc_contents, vbuf, args, obj2start)) return; /* try verb^prep --- double-text action */ vp[0] = DOUBLE_ACTION; if((verb = intern_soft(vbuf)) != NOTHING) { if(try_action(actor, verb, location, args, obj2start)) return; if(try_action(actor, verb, actor, args, obj2start)) return; } /* didn't work this time, undo '\0' at obj1end and move obj1end to start of next word */ *obj1end = c; obj1end = pstart; } /* try V text */ if((verb = intern_soft(command)) != NOTHING) { if(try_action(actor, verb, location, 0, args)) return; if(try_action(actor, verb, actor, 0, args)) return; } parse_failed: /* can't do it */ /* try defaults */ if(try_action(actor, DEFAULT_ACTION, location, 0, safe_command)) return; if(try_action(actor, DEFAULT_ACTION, actor, 0, safe_command)) return; /* we lose completely */ notify(actor, DEFAULT_HUH_MESSAGE); return; }