/
teeny/db/
teeny/dbm/
teeny/docs/
teeny/includes/
teeny/misc/
teeny/news/
teeny/text/
/* command.c */

#include "copyright.h"
#include "config.h"

#include <stdio.h>
#include <ctype.h>

#include "teeny.h"
#include "match.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_finger();
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_look();
extern voidfunc do_news();
extern voidfunc do_page();
extern voidfunc do_poll();
extern voidfunc do_pose();
extern voidfunc do_say();
extern voidfunc do_take();
extern voidfunc do_whisper();
extern voidfunc do_boot();
extern voidfunc do_chownall();
extern voidfunc do_chown();
extern voidfunc do_create();
extern voidfunc do_dig();
extern voidfunc do_dump();
voidfunc do_descstr();
#ifdef DROP_FIELDS
voidfunc do_dropstr();
#endif /*DROP_FIELDS*/
#ifdef EDIT
extern voidfunc do_edit();
#endif /*EDIT*/
voidfunc do_efind();
voidfunc do_failstr();
extern voidfunc do_force();
extern voidfunc do_find();
voidfunc do_genderstr();
extern voidfunc do_lock();
extern voidfunc do_link();
voidfunc do_namestr();
extern voidfunc do_newpassword();
voidfunc do_oarrive();
#ifdef DROP_FIELDS
voidfunc do_odropstr();
#endif /*DROP_FIELDS*/
voidfunc do_ofailstr();
voidfunc do_oleave();
extern voidfunc do_open();
voidfunc do_osucstr();
extern voidfunc do_owned();
extern voidfunc do_password();
extern voidfunc do_pcreate();
extern voidfunc do_purge();
extern voidfunc do_recycle();
extern voidfunc do_set();
extern voidfunc do_shutdown();
extern voidfunc do_stats();
voidfunc do_sucstr();
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();

/*
 * 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 },
  /* 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 },
  /* finger */
  { FINGER_COMMAND, do_finger, 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 },
  /* 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 },
  { "p", 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 },
  { "r", 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[] = {
  /* @boot */
  { "boot", do_boot, 1 },
  /* @chownall */
  { "chownall", do_chownall, 2 },
  /* @chown */
  { "chown", do_chown, 2 },
  { "chow", do_chown, 2 },
  { "cho", do_chown, 2 },
  { "ch", do_chown, 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, 1 },
  { "di", do_dig, 1 },
  /* @dump */
  { "dump", do_dump, 0 },
  /* @describe */
  { "describe", do_descstr, 2 },
  { "describ", do_descstr, 2 },
  { "descri", do_descstr, 2 },
  { "descr", do_descstr, 2 },
  { "desc", do_descstr, 2 },
  { "des", do_descstr, 2 },
  { "de", do_descstr, 2 },
#ifdef DROP_FIELDS
  /* @drop */
  { "drop", do_dropstr, 2 },
  { "dro", do_dropstr, 2 },
  { "dr", do_dropstr, 2 },
#endif /*DROP_FIELDS*/
#ifdef EDIT
  /* @edit */
  { "edit", do_edit, 2 },
  { "edi", do_edit, 2 },
  { "ed", do_edit, 2 },
#endif /*EDIT*/
  /* @efind */
  { "efind", do_efind, 0 },
  { "efin", do_efind, 0 },
  { "efi", do_efind, 0 },
  { "ef", do_efind, 0 },
  /* @fail */
  { "fail", do_failstr, 2 },
  { "fai", do_failstr, 2 },
  { "fa", do_failstr, 2 },
  /* @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 },
  /* @gender */
  { "gender", do_genderstr, 2 },
  { "gende", do_genderstr, 2 },
  { "gend", do_genderstr, 2 },
  { "gen", do_genderstr, 2 },
  { "ge", do_genderstr, 2 },
  /* @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 },
  /* @name */
  { "name", do_namestr, 2 },
  { "nam", do_namestr, 2 },
  { "na", do_namestr, 2 },
  /* @newpassword */
  { "newpassword", do_newpassword, 2 },
  /* @oarrive */
  { "oarrive", do_oarrive, 2 },
  { "oarriv", do_oarrive, 2 },
  { "oarri", do_oarrive, 2 },
  { "oarr", do_oarrive, 2 },
  { "oar", do_oarrive, 2 },
  { "oa", do_oarrive, 2 },
#ifdef DROP_FIELDS
  /* @odrop */
  { "odrop", do_odropstr, 2 },
  { "odro", do_odropstr, 2 },
  { "odr", do_odropstr, 2 },
  { "od", do_odropstr, 2 },
#endif /*DROP_FIELDS*/
  /* @ofail */
  { "ofail", do_ofailstr, 2 },
  { "ofai", do_ofailstr, 2 },
  { "ofa", do_ofailstr, 2 },
  { "of", do_ofailstr, 2 },
  /* @oleave */
  { "oleave", do_oleave, 2 },
  { "oleav", do_oleave, 2 },
  { "olea", do_oleave, 2 },
  { "ole", do_oleave, 2 },
  { "ol", do_oleave, 2 },
  /* @open */
  { "open", do_open, 2 },
  { "ope", do_open, 2 },
  { "op", do_open, 2 },
  /* @osuccess */
  { "osuccess", do_osucstr, 2 },
  { "osucces", do_osucstr, 2 },
  { "osucce", do_osucstr, 2 },
  { "osucc", do_osucstr, 2 },
  { "osuc", do_osucstr, 2 },
  { "osu", do_osucstr, 2 },
  { "os", do_osucstr, 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 },
  { "pol", do_poll, 1 },
  { "po", do_poll, 1 },
  /* @purge */
  { "purge", do_purge, 1 },
  /* @recycle */
  { "recycle", do_recycle, 1 },
  { "recycl", do_recycle, 1 },
  { "recyc", do_recycle, 1 },
  { "recy", do_recycle, 1 },
  { "rec", do_recycle, 1 },
  /* @set */
  { "set", do_set, 2 },
  /* @sex */
  { "sex", do_genderstr, 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 },
  /* @success */
  { "success", do_sucstr, 2 },
  { "succes", do_sucstr, 2 },
  { "succe", do_sucstr, 2 },
  { "succ", do_sucstr, 2 },
  { "suc", do_sucstr, 2 },
  { "su", do_sucstr, 2 },
  /* @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 },
  { "versio", do_version, 0 },
  { "versi", do_version, 0 },
  { "vers", do_version, 0 },
  { "ver", do_version, 0 },
  { "ve", do_version, 0 },
  { "v", do_version, 0 },
  /* @wall */
  { "wall", do_wall, 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, *y, z;

    if(get_str_elt(player, NAME, &x) != -1){
      for(y = x; y && *y && *y != ' '; y++);
      z = *y; *y = '\0';
      log_command("COMMAND [%s(#%d)]: %s\n", x, player, cmd);
      *y = z;
    } 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] == '@'){
    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;

{
  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 {
    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)) != NULL){
    /* Ok. We have a list of exits. Cope with 'em. */
    do_go_attempt(player, here, exlist);
    return 1;
  } else
    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)])));
}

/* Dummy commands follow. */
voidfunc do_efind(player)
    int player;
{
  notify_player(player, "@efind: Try \"@find <name> = exit\".\r\n");
}
voidfunc do_descstr(player, s1, s2)
    int player;
    char *s1, *s2;
{
  do_set_string(player, s1, s2, DESC);
}
voidfunc do_failstr(player, s1, s2)
    int player;
    char *s1, *s2;
{
  do_set_string(player, s1, s2, FAIL);
}
voidfunc do_genderstr(player, s1, s2)
    int player;
    char *s1, *s2;
{
  do_set_string(player, s1, s2, GENDER);
}
voidfunc do_namestr(player, s1, s2)
    int player;
    char *s1, *s2;
{
  do_set_string(player, s1, s2, NAME);
}
voidfunc do_ofailstr(player, s1, s2)
    int player;
    char *s1, *s2;
{
  do_set_string(player, s1, s2, OFAIL);
}
voidfunc do_osucstr(player, s1, s2)
    int player;
    char *s1, *s2;
{
  do_set_string(player, s1, s2, OSUC);
}
voidfunc do_sucstr(player, s1, s2)
    int player;
    char *s1, *s2;
{
  do_set_string(player, s1, s2, SUC);
}
#ifdef DROP_FIELDS
voidfunc do_dropstr(player, s1, s2)
    int player;
    char *s1, *s2;
{
  do_set_string(player, s1, s2, DROP);
}
voidfunc do_odropstr(player, s1, s2)
    int player;
    char *s1, *s2;
{
  do_set_string(player, s1, s2, ODROP);
}
#endif /*DROP_FIELDS*/
voidfunc do_oarrive(player, s1, s2)
    int player;
    char *s1, *s2;
{
  do_set_string(player, s1, s2, OARRIVE);
}
voidfunc do_oleave(player, s1, s2)
    int player;
    char *s1, *s2;
{
  do_set_string(player, s1, s2, OLEAVE);
}