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