/* Copyright 1989, 1990 by James Aspnes, David Applegate, and Bennet Yee */ /* See the file COPYING for distribution information */ /* Command interpreter */ #include "os.h" #include "config.h" #include "db.h" #include "bytecode.h" #include "globals.h" #include "externs.h" #include "interface.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; }