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