/* command.c */
#include "copyright.h"
#include "config.h"
#include <stdio.h>
#include <ctype.h>
#include "teeny.h"
#include "match.h"
#include "strlist.h"
/*
* The command parser for TeenyMUD. Command names are stored in a table,
* along with the corresponding routine to execute and the number of
* arguments (0, 1, or 2) required by the command.
*
* All command functions must be of type voidfunc.
*/
struct commands {
char *name;
voidfunc (*function) ();
int nargs;
};
typedef struct commands CMDS;
/* Forward declarations for the table. Most are extern. */
extern voidfunc do_drop();
extern voidfunc do_examine();
extern voidfunc do_enter();
extern voidfunc do_go();
extern voidfunc do_gripe();
extern voidfunc do_help();
extern voidfunc do_home();
extern voidfunc do_inventory();
extern voidfunc do_kill();
extern voidfunc do_leave();
extern voidfunc do_look();
extern voidfunc do_news();
extern voidfunc do_page();
extern voidfunc do_pose();
extern voidfunc do_say();
extern voidfunc do_take();
extern voidfunc do_whisper();
extern voidfunc do_attach();
extern voidfunc do_boot();
extern voidfunc do_children();
extern voidfunc do_chownall();
extern voidfunc do_chown();
extern voidfunc do_copy();
extern voidfunc do_create();
extern voidfunc do_dig();
extern voidfunc do_doing();
extern voidfunc do_dump();
extern voidfunc do_edit();
extern voidfunc do_entrances();
extern voidfunc do_force();
extern voidfunc do_find();
extern voidfunc do_fixlists();
extern voidfunc do_fixrooms();
extern voidfunc do_lock();
extern voidfunc do_link();
extern voidfunc do_newpassword();
extern voidfunc do_open();
extern voidfunc do_owned();
extern voidfunc do_password();
extern voidfunc do_pcreate();
extern voidfunc do_poll();
extern voidfunc do_purge();
extern voidfunc do_quota();
extern voidfunc do_recycle();
#ifdef PDX
extern voidfunc do_restrict();
#endif
extern voidfunc do_set();
extern voidfunc do_shutdown();
extern voidfunc do_stats();
extern voidfunc do_teleport();
extern voidfunc do_textdump();
extern voidfunc do_toad();
extern voidfunc do_unlink();
extern voidfunc do_unlock();
extern voidfunc do_version();
extern voidfunc do_wall();
extern voidfunc do_wizzwall();
/*
* The tables themselves. Regular and 'At' commands are kept in seperate
* tables for speed. Note that a function _always_ gets the 'player'
* variable passed to it, even when it's in the table as having no arguments.
*/
static CMDS cmds[] = {
/* drop */
{"drop", do_drop, 1},
{"dro", do_drop, 1},
{"dr", do_drop, 1},
{"d", do_drop, 1},
{"enter", do_enter, 1},
{"ente", do_enter, 1},
{"ent", do_enter, 1},
{"en", do_enter, 1},
/* examine */
{"examine", do_examine, 1},
{"examin", do_examine, 1},
{"exami", do_examine, 1},
{"exam", do_examine, 1},
{"exa", do_examine, 1},
{"ex", do_examine, 1},
{"e", do_examine, 1},
/* get */
{"get", do_take, 1},
{"ge", do_take, 1},
/* go */
{"go", do_go, 1},
/* gripe */
{"gripe", do_gripe, 1},
{"grip", do_gripe, 1},
{"gri", do_gripe, 1},
{"gr", do_gripe, 1},
/* help */
#ifdef HELPSYSTEM
{"help", do_help, 1},
#else
{"help", do_help, 0},
#endif /* HELPSYSTEM */
/* home */
{"home", do_home, 0},
/* inventory */
{"inventory", do_inventory, 0},
{"inventor", do_inventory, 0},
{"invento", do_inventory, 0},
{"invent", do_inventory, 0},
{"inven", do_inventory, 0},
{"inve", do_inventory, 0},
{"inv", do_inventory, 0},
{"in", do_inventory, 0},
{"i", do_inventory, 0},
/* kill */
{"kill", do_kill, 1},
{"kil", do_kill, 1},
{"ki", do_kill, 1},
{"k", do_kill, 1},
/* leave */
{"leave", do_leave, 0},
{"leav", do_leave, 0},
{"lea", do_leave, 0},
{"le", do_leave, 0},
/* look */
{"look", do_look, 1},
{"loo", do_look, 1},
{"lo", do_look, 1},
{"l", do_look, 1},
/* move */
{"move", do_go, 1},
{"mov", do_go, 1},
{"mo", do_go, 1},
{"m", do_go, 1},
/* news */
#ifdef NEWSSYSTEM
{"news", do_news, 1},
#else
{"news", do_news, 0},
#endif /* NEWSSYSTEM */
/* page */
{"page", do_page, 2},
{"pag", do_page, 2},
{"pa", do_page, 2},
/* pose */
{"pose", do_pose, 1},
{"pos", do_pose, 1},
{"po", do_pose, 1},
/* read */
{"read", do_look, 1},
{"rea", do_look, 1},
{"re", do_look, 1},
/* say */
{"say", do_say, 1},
{"sa", do_say, 1},
/* take */
{"take", do_take, 1},
{"tak", do_take, 1},
{"ta", do_take, 1},
/* throw */
{"throw", do_drop, 1},
{"thro", do_drop, 1},
{"thr", do_drop, 1},
{"th", do_drop, 1},
/* whisper */
{"whisper", do_whisper, 2},
{"whispe", do_whisper, 2},
{"whisp", do_whisper, 2},
{"whis", do_whisper, 2},
{"whi", do_whisper, 2},
{"wh", do_whisper, 2},
{"w", do_whisper, 2},
{NULL, NULL, 0}
};
static CMDS atcmds[] = {
/* @attach */
{"attach", do_attach, 2},
{"attac", do_attach, 2},
{"atta", do_attach, 2},
{"att", do_attach, 2},
{"at", do_attach, 2},
/* @boot */
{"boot", do_boot, 1},
/* @children */
{"children", do_children, 2},
{"childre", do_children, 2},
{"childr", do_children, 2},
{"child", do_children, 2},
/* @chownall */
{"chownall", do_chownall, 2},
/* @chown */
{"chown", do_chown, 2},
{"chow", do_chown, 2},
{"cho", do_chown, 2},
{"ch", do_chown, 2},
/* @copy */
{"copy", do_copy, 2},
{"cop", do_copy, 2},
{"co", do_copy, 2},
/* @create */
{"create", do_create, 1},
{"creat", do_create, 1},
{"crea", do_create, 1},
{"cre", do_create, 1},
{"cr", do_create, 1},
/* @dig */
{"dig", do_dig, 2},
{"di", do_dig, 2},
/* @doing */
{"doing", do_doing, 2},
{"doin", do_doing, 2},
{"doi", do_doing, 2},
{"do", do_doing, 2},
/* @dump */
{"dump", do_dump, 0},
/* @edit */
{"edit", do_edit, 2},
{"edi", do_edit, 2},
{"ed", do_edit, 2},
/* @entrances */
{"entrances", do_entrances, 1},
{"entrance", do_entrances, 1},
{"entranc", do_entrances, 1},
{"entran", do_entrances, 1},
{"entra", do_entrances, 1},
{"entr", do_entrances, 1},
{"ent", do_entrances, 1},
/* @force */
{"force", do_force, 2},
{"forc", do_force, 2},
{"for", do_force, 2},
{"fo", do_force, 2},
/* @find */
{"find", do_find, 2},
{"fin", do_find, 2},
{"fi", do_find, 2},
/* @fix-lists */
{"fix-lists", do_fixlists, 0},
/* @fix-rooms */
{"fix-rooms", do_fixrooms, 0},
/* @gripe */
{"gripe", do_gripe, 1},
{"grip", do_gripe, 1},
{"gri", do_gripe, 1},
{"gr", do_gripe, 1},
/* @lock */
{"lock", do_lock, 2},
{"loc", do_lock, 2},
{"lo", do_lock, 2},
/* @link */
{"link", do_link, 2},
{"lin", do_link, 2},
{"li", do_link, 2},
/* @newpassword */
{"newpassword", do_newpassword, 2},
/* @open */
{"open", do_open, 2},
{"ope", do_open, 2},
{"op", do_open, 2},
/* @owned */
{"owned", do_owned, 2},
{"owne", do_owned, 2},
{"own", do_owned, 2},
{"ow", do_owned, 2},
/* @password */
{"password", do_password, 2},
/* @pcreate */
{"pcreate", do_pcreate, 2},
{"pcreat", do_pcreate, 2},
{"pcrea", do_pcreate, 2},
{"pcre", do_pcreate, 2},
{"pcr", do_pcreate, 2},
{"pc", do_pcreate, 2},
/* @poll */
{"poll", do_poll, 1},
/* @purge */
{"purge", do_purge, 1},
/* @quota */
{"quota", do_quota, 2},
/* @recycle */
{"recycle", do_recycle, 1},
{"recycl", do_recycle, 1},
{"recyc", do_recycle, 1},
{"recy", do_recycle, 1},
{"rec", do_recycle, 1},
#ifdef PDX
{"restrict", do_restrict, 1},
#endif
/* @set */
{"set", do_set, 2},
/* @shutdown */
{"shutdown", do_shutdown, 0},
{"shutdow", do_shutdown, 0},
{"shutdo", do_shutdown, 0},
{"shutd", do_shutdown, 0},
{"shut", do_shutdown, 0},
{"shu", do_shutdown, 0},
{"sh", do_shutdown, 0},
/* @stats */
{"stats", do_stats, 1},
{"stat", do_stats, 1},
{"sta", do_stats, 1},
{"st", do_stats, 1},
/* @teleport */
{"teleport", do_teleport, 2},
{"telepor", do_teleport, 2},
{"telepo", do_teleport, 2},
{"telep", do_teleport, 2},
{"tele", do_teleport, 2},
{"tel", do_teleport, 2},
{"te", do_teleport, 2},
/* @textdump */
{"textdump", do_textdump, 1},
/* @toad */
{"toad", do_toad, 2},
/* @unlink */
{"unlink", do_unlink, 1},
{"unlin", do_unlink, 1},
{"unli", do_unlink, 1},
/* @unlock */
{"unlock", do_unlock, 1},
{"unloc", do_unlock, 1},
{"unlo", do_unlock, 1},
/* @version */
{"version", do_version, 0},
/* @wall */
{"wall", do_wall, 1},
/* @wizzwall */
{"wizzwall", do_wizzwall, 1},
{"wizwall", do_wizzwall, 1},
{"wizz", do_wizzwall, 1},
{"wiz", do_wizzwall, 1},
{NULL, NULL, 0}
};
static void handle_at_cmd();
static int handle_exit_cmd();
static int cmdcmp();
/* A macro to instantly parse the command, given suitably set up pointers */
#define Parse {if(first != NULL) first_end[1] = '\0';}
void handle_cmd(player, cmd)
int player;
char *cmd;
{
char *first; /* First char of arg 1 */
char *first_end; /* Terminate arg 1 at first_end[1] */
char *second; /* Same deal */
char *p;
CMDS *fp;
lock_cache(); /* Lock up the cache so things stay in memory */
#ifdef LOGCOMMANDS
{
char *x;
if (get_str_elt(player, NAME, &x) != -1) {
log_command("COMMAND [%s(#%d)]: %s\n", x, player, cmd);
} else
log_command("COMMAND [???(#%d)]: %s\n", player, cmd);
}
#endif /* LOGCOMMANDS */
if (cmd[0] == '\"') {
do_say(player, cmd + 1);
if (mudstat() != DUMPING)
unlock_cache();
return;
}
if (cmd[0] == ':') {
do_pose(player, cmd + 1);
if (mudstat() != DUMPING)
unlock_cache();
return;
}
if (cmd[0] != '@') {
if (handle_exit_cmd(player, cmd)) {
if (mudstat() != DUMPING)
unlock_cache();
cache_trim();
return;
}
}
/* Smash the command up, prepare to parse out args if required */
first = second = first_end = NULL;
p = cmd;
while (!isspace(*p) && *p)
p++; /* Skip over the command */
first = p;
while (isspace(*p) && *p)
p++; /* Skip to arg 1 */
*first = '\0';
if (!(*p)) { /* There is no arg 1 */
first = NULL;
goto parse;
}
first = p;
while ((*p != '=') && *p)
p++; /* Skip to end of arg 1 */
first_end = p - 1; /* Not the terminator, last char */
if (!(*p))
goto parse;
/* Back first_end up. We don't like trailing whitespace */
while (isspace(*first_end))
first_end--;
p++; /* Skip the '=' */
while (isspace(*p) && *p)
p++; /* Skip to arg 2 */
if (!(*p))
goto parse;
second = p;
while (*p)
p++; /* Skip to end of arg 2 */
p--;
while (isspace(*p))
p--; /* Back up over trailing whitespace. */
p[1] = '\0';
/* Go to it */
parse:
if ((cmd[0] == '@') && !isguest(player)) {
handle_at_cmd(cmd + 1, player, first, first_end, second);
return;
}
for (fp = cmds; fp->name && !cmdcmp(fp, cmd); fp++);
if (fp->name) {
switch (fp->nargs) {
case 0:
fp->function(player);
break;
case 1:
fp->function(player, first);
break;
case 2:
Parse;
fp->function(player, first, second);
break;
default:
log_error("handle_cmd: function %s has a bad argument number\n",
fp->name);
do_huh(player);
}
if (mudstat() != DUMPING)
unlock_cache();
cache_trim();
} else {
do_huh(player);
if (mudstat() != DUMPING)
unlock_cache();
cache_trim();
}
}
static void handle_at_cmd(cmd, player, first, first_end, second)
char *cmd;
int player;
char *first, *first_end, *second;
{
StringList *strs;
CMDS *fp;
for (fp = atcmds; fp->name && !cmdcmp(fp, cmd); fp++);
if (fp->name) {
switch (fp->nargs) {
case 0:
fp->function(player);
break;
case 1:
fp->function(player, first);
break;
case 2:
Parse;
fp->function(player, first, second);
break;
default:
log_error("handle_at_cmd: function %s has a bad argument number\n",
fp->name);
do_huh(player);
}
if (mudstat() != DUMPING)
unlock_cache();
cache_trim();
} else {
for (strs = Strings; strs->name && cmd[1] && cmd[1] != ' ' &&
!stringprefix(cmd, (strs->calias) ? strs->calias : strs->name) &&
!stringprefix(cmd, strs->name); strs++);
if (strs->name && !(strs->flag & STR_NCMD)) {
Parse;
do_set_string(player, first, second, strs->code);
} else {
if (!handle_exit_cmd(player, cmd))
do_huh(player);
}
if (mudstat() != DUMPING)
unlock_cache();
cache_trim();
}
}
static int handle_exit_cmd(player, cmd)
int player;
char *cmd;
{
struct match *exlist;
int here, list, count;
/* Is this an exit? If so, cope with it. */
if (get_int_elt(player, LOC, &here) == -1) {
log_error("handle_exit_cmd: bad location ref on player %d\n", player);
return 0;
}
if (get_int_elt(here, EXITS, &list) == -1) {
log_error("handle_exit_cmd: bad exit list on object %d\n", here);
return 0;
}
if ((exlist = match_exits(cmd, list, &count, MAT_INTERNAL)) != NULL) {
/* Ok. We have a list of exits. Cope with 'em. */
do_go_attempt(player, here, exlist);
return 1;
} else {
int contents, parent;
/* check for EXTERNAL exits on player */
if (get_int_elt(player, EXITS, &list) != -1) {
if ((exlist = match_exits(cmd, list, &count, MAT_EXTERNAL)) != NULL) {
do_go_attempt(player, here, exlist);
return 1;
}
}
/* check for EXTERNAL exits on contents */
if (get_int_elt(here, CONTENTS, &contents) == -1) {
log_error("handle_exit_cmd: bad contents list on object %d\n", here);
return 0;
}
while (contents != -1) {
if (!isplayer(contents)) {
if (get_int_elt(contents, EXITS, &list) == -1) {
log_error("handle_exit_cmd: bad exits list on object %d\n", contents);
return 0;
}
if ((exlist = match_exits(cmd, list, &count, MAT_EXTERNAL)) != NULL) {
do_go_attempt(player, here, exlist);
return 1;
}
}
if (get_int_elt(contents, NEXT, &contents) == 1) {
log_error("handle_exit_cmd: bad next elt on object %d\n", contents);
return 0;
}
}
/* check parents */
if (isroom(here)) {
parent = here;
do {
if (get_int_elt(parent, LOC, &parent) == -1) {
log_error("handle_exit_cmd: bad parent ref on #%d\n", parent);
return 0;
}
if (get_int_elt(parent, EXITS, &list) == -1) {
log_error("handle_exit_cmd: bad exit ref on #%d\n", parent);
return 0;
}
if ((exlist = match_exits(cmd, list, &count, MAT_INTERNAL)) != NULL) {
do_go_attempt(player, here, exlist);
return 1;
}
} while (parent != ROOT_PARENT);
} else {
if (get_int_elt(ROOT_PARENT, EXITS, &list) == -1) {
log_error("handle_exit_cmd: bad exits list on ROOT!\n");
return 0;
}
if ((exlist = match_exits(cmd, list, &count, MAT_INTERNAL)) != NULL) {
do_go_attempt(player, here, exlist);
return 1;
}
}
}
return 0;
}
#undef Parse
static int cmdcmp(fp, cmd)
register CMDS *fp;
register char *cmd;
{
return (!strncasecmp(fp->name, cmd, strlen(fp->name)) &&
((!cmd[strlen(fp->name)]) || isspace(cmd[strlen(fp->name)])));
}