dbm/
misc/
old-docs/
/* 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)])));
}