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