pennmush/game/
pennmush/game/data/
pennmush/game/log/
pennmush/game/save/
pennmush/game/txt/evt/
pennmush/game/txt/nws/
pennmush/os2/
/* command.c
 * Set up a hash table for the commands, and parse input for commands
 * This implementation is almost totally by Thorvald Natvig, with
 * various mods by Javelin
 */

#include "copyrite.h"
#include "config.h"

#ifdef I_STRING
#include <string.h>
#else
#include <strings.h>
#endif

#include "dbdefs.h"
#include "conf.h"

#include "mushdb.h"
#include "game.h"
#include "externs.h"
#include "intrface.h"
#include "match.h"
#include "globals.h"
#include "extmail.h"
#ifdef CHAT_SYSTEM
#include "extchat.h"
#endif
#include "getpgsiz.h"
#include "parse.h"
#include "access.h"
#include "version.h"

#include "htab.h"
#include "function.h"
#include "command.h"
#include "mymalloc.h"
#include "confmagic.h"

HASHTAB htab_command;
HASHTAB htab_reserved_aliases;

void test_command _((dbref player, switch_mask *sw, char *args));
int restrict_command _((const char *name, const char *restriction));
static char *command_isattr _((char *command));
extern void local_commands _((void));
extern void reserve_aliases _((void));
static int command_check _((dbref player, COMMAND_INFO * cmd));
static int switch_find _((COMMAND_INFO * cmd, char *sw));

COMLIST commands[] =
{
  {"@COMMAND", "ON OFF QUIET ENABLE DISABLE", cmd_command, CMD_T_PLAYER, WIZARD, 0, 0},
  {"@@", NULL, cmd_null, CMD_T_ANY, 0, 0, 0},
  {"@ALLHALT", NULL, cmd_allhalt, CMD_T_ANY, WIZARD, 0, HALT_ANYTHING},
#ifdef QUOTA
{"@ALLQUOTA", "QUIET", cmd_allquota, CMD_T_ANY, WIZARD, 0, CHANGE_QUOTAS},
#endif
  {"@ATRLOCK", NULL, cmd_atrlock, CMD_T_ANY | CMD_T_EQSPLIT, 0, 0, 0},
  {"@ATRCHOWN", NULL, cmd_atrchown, CMD_T_ANY | CMD_T_EQSPLIT, 0, 0, 0},
  {"@ATTRIBUTE", "ACCESS DELETE RENAME RETROACTIVE", cmd_attribute, CMD_T_ANY | CMD_T_EQSPLIT, WIZARD, 0, 0},
  {"@BOOT", "PORT ME", cmd_boot, CMD_T_ANY, 0, 0, 0},
#ifdef CHAT_SYSTEM
  {"@CEMIT", "NOEVAL", cmd_cemit, CMD_T_ANY | CMD_T_EQSPLIT, WIZARD | ROYALTY, 0, CEMIT},
  {"@CHANNEL", "LIST ADD DELETE RENAME NAME PRIVS QUIET DECOMPILE DESC CHOWN WIPE MUTE GAG HIDE WHAT TITLE", cmd_channel, CMD_T_ANY | CMD_T_SWITCHES | CMD_T_EQSPLIT | CMD_T_NOGAGGED, 0, 0, 0},
  {"@CHAT", NULL, cmd_chat, CMD_T_ANY | CMD_T_EQSPLIT, 0, 0, 0},
#endif
  {"@CHOWNALL", NULL, cmd_chownall, CMD_T_ANY | CMD_T_EQSPLIT, WIZARD, 0, 0},
  {"@CHOWN", NULL, cmd_chown, CMD_T_ANY | CMD_T_EQSPLIT | CMD_T_NOGAGGED, 0, 0, 0},
  {"@CHZONEALL", NULL, cmd_chzoneall, CMD_T_ANY | CMD_T_EQSPLIT, 0, 0, 0},
  {"@CHZONE", NULL, cmd_chzone, CMD_T_ANY | CMD_T_EQSPLIT | CMD_T_NOGAGGED, 0, 0, 0},
  {"@CONFIG", "LIST GLOBALS DEFAULTS COSTS FUNCTIONS COMMANDS ATTRIBS", cmd_config, CMD_T_ANY, 0, 0, 0},
  {"@CPATTR", NULL, cmd_cpattr, CMD_T_ANY | CMD_T_EQSPLIT | CMD_T_RS_ARGS, 0, 0, 0},
  {"@CREATE", NULL, cmd_create, CMD_T_ANY | CMD_T_EQSPLIT | CMD_T_NOGAGGED, 0, 0, 0},
  {"@CLONE", NULL, cmd_clone, CMD_T_ANY | CMD_T_NOGAGGED, 0, 0, 0},
#ifdef CHAT_SYSTEM
  {"@CLOCK", "JOIN SPEAK MOD SEE HIDE", cmd_clock, CMD_T_ANY | CMD_T_EQSPLIT, 0, 0, 0},
#endif
  {"@DBCK", NULL, cmd_dbck, CMD_T_ANY, WIZARD, 0, 0},
{"@DECOMPILE", "DB TF FLAGS ATTRIBS", cmd_decompile, CMD_T_ANY, 0, 0, 0},
  {"@DESTROY", "OVERRIDE", cmd_destroy, CMD_T_ANY, 0, 0, 0},
  {"@DIG", "TELEPORT", cmd_dig, CMD_T_ANY | CMD_T_EQSPLIT | CMD_T_RS_ARGS | CMD_T_NOGAGGED, 0, 0, 0},
  {"@DISABLE", NULL, cmd_disable, CMD_T_ANY, 0, 0, 0},
  {"@DOING", "HEADER", cmd_doing, CMD_T_ANY | CMD_T_NOPARSE | CMD_T_NOGAGGED, 0, 0, 0},
  {"@DOLIST", "NOTIFY", cmd_dolist, CMD_T_ANY | CMD_T_EQSPLIT | CMD_T_RS_NOPARSE, 0, 0, 0},
  {"@DRAIN", NULL, cmd_drain, CMD_T_ANY | CMD_T_EQSPLIT, 0, 0, 0},
  {"@DUMP", "PARANOID DEBUG", cmd_dump, CMD_T_ANY, WIZARD, 0, 0},
  {"@EDIT", NULL, cmd_edit, CMD_T_ANY | CMD_T_EQSPLIT | CMD_T_RS_ARGS | CMD_T_RS_NOPARSE | CMD_T_NOGAGGED, 0, 0, 0},
  {"@ELOCK", NULL, cmd_elock, CMD_T_ANY | CMD_T_EQSPLIT | CMD_T_NOGAGGED, 0, 0, 0},
  {"@EMIT", "ROOM NOEVAL", cmd_emit, CMD_T_ANY | CMD_T_NOGAGGED, 0, 0, 0},
  {"@ENABLE", NULL, cmd_enable, CMD_T_ANY | CMD_T_NOGAGGED, 0, 0, 0},
  {"@ENTRANCES", "EXITS THINGS PLAYERS ROOMS", cmd_entrances, CMD_T_ANY | CMD_T_EQSPLIT | CMD_T_RS_ARGS | CMD_T_NOGAGGED, 0, 0, 0},
  {"@EUNLOCK", NULL, cmd_eunlock, CMD_T_ANY | CMD_T_NOGAGGED, 0, 0, 0},
  {"@FIND", NULL, cmd_find, CMD_T_ANY | CMD_T_EQSPLIT | CMD_T_RS_ARGS | CMD_T_NOGAGGED, 0, 0, 0},
  {"@FIRSTEXIT", NULL, cmd_firstexit, CMD_T_ANY, 0, 0, 0},
  {"@FIXDB", "LOCATION CONTENTS EXITS NEXT", cmd_fixdb, CMD_T_ANY | CMD_T_EQSPLIT, 0, 0, 0},
  {"@FORCE", NULL, cmd_force, CMD_T_ANY | CMD_T_EQSPLIT | CMD_T_NOGAGGED, 0, 0, 0},
  {"@FUNCTION", "DELETE", cmd_function, CMD_T_ANY | CMD_T_EQSPLIT | CMD_T_RS_ARGS | CMD_T_NOGAGGED, 0, 0, 0},
  {"@GEDIT", NULL, cmd_edit, CMD_T_ANY | CMD_T_EQSPLIT | CMD_T_RS_ARGS | CMD_T_RS_NOPARSE | CMD_T_NOGAGGED, 0, 0, 0},
  {"@GREP", "LIST PRINT ILIST IPRINT", cmd_grep, CMD_T_ANY | CMD_T_EQSPLIT | CMD_T_RS_NOPARSE | CMD_T_NOGAGGED, 0, 0, 0},
  {"@HALT", "ALL", cmd_halt, CMD_T_ANY | CMD_T_EQSPLIT, 0, 0, 0},
  {"@HIDE", "NO OFF YES ON", cmd_hide, CMD_T_ANY, 0, 0, 0},
  {"@KICK", NULL, cmd_kick, CMD_T_ANY, WIZARD, 0, 0},
  {"@LEMIT", "NOEVAL", cmd_lemit, CMD_T_ANY | CMD_T_EQSPLIT | CMD_T_NOGAGGED, 0, 0, 0},
  {"@LINK", NULL, cmd_link, CMD_T_ANY | CMD_T_EQSPLIT | CMD_T_NOGAGGED, 0, 0, 0},
  {"@LISTMOTD", NULL, cmd_listmotd, CMD_T_ANY, 0, 0, 0},
  {"@LIST", "MOTD FUNCTIONS COMMANDS ATTRIBS", cmd_list, CMD_T_ANY, 0, 0, 0},
  {"@LOCK", NULL, cmd_lock, CMD_T_ANY | CMD_T_EQSPLIT | CMD_T_SWITCHES | CMD_T_NOGAGGED, 0, 0, 0},
  {"@LOG", "CHECK CMD CONN ERR TRACE WIZ", cmd_log, CMD_T_ANY | CMD_T_NOGAGGED, WIZARD, 0, 0},
#ifdef USE_MAILER
  {"@MAIL", "NOEVAL STATS DSTATS FSTATS DEBUG NUKE FOLDER UNFOLDER LIST READ CLEAR UNCLEAR PURGE FILE TAG UNTAG FWD FORWARD SEND SILENT URGENT", cmd_mail, CMD_T_ANY | CMD_T_EQSPLIT | CMD_T_NOGAGGED, 0, 0, 0},
#endif
  {"@MAP", NULL, cmd_map, CMD_T_ANY | CMD_T_EQSPLIT | CMD_T_RS_NOPARSE, 0, 0, 0},
  {"@MOTD", "CONNECT LIST WIZARD DOWN FULL", cmd_motd, CMD_T_ANY | CMD_T_NOGAGGED, 0, 0, 0},
  {"@MVATTR", NULL, cmd_mvattr, CMD_T_ANY | CMD_T_EQSPLIT | CMD_T_RS_ARGS, 0, 0, 0},
  {"@NAME", NULL, cmd_name, CMD_T_ANY | CMD_T_EQSPLIT | CMD_T_NOGAGGED, 0, 0, 0},
  {"@NEWPASSWORD", NULL, cmd_newpassword, CMD_T_ANY | CMD_T_EQSPLIT, 0, 0, 0},
  {"@NOTIFY", "ALL", cmd_notify, CMD_T_ANY | CMD_T_EQSPLIT, 0, 0, 0},
  {"@NUKE", NULL, cmd_nuke, CMD_T_ANY | CMD_T_NOGAGGED, 0, 0, 0},
  {"@OEMIT", "NOEVAL", cmd_oemit, CMD_T_ANY | CMD_T_EQSPLIT | CMD_T_NOGAGGED, 0, 0, 0},
  {"@OPEN", NULL, cmd_open, CMD_T_ANY | CMD_T_EQSPLIT | CMD_T_RS_ARGS | CMD_T_NOGAGGED, 0, 0, 0},
  {"@PARENT", NULL, cmd_parent, CMD_T_ANY | CMD_T_EQSPLIT, 0, 0, 0},
  {"@PASSWORD", NULL, cmd_password, CMD_T_ANY | CMD_T_EQSPLIT, 0, 0, 0},
  {"@PCREATE", NULL, cmd_pcreate, CMD_T_ANY | CMD_T_EQSPLIT, 0, 0, 0},
  {"@PEMIT", "LIST CONTENTS SILENT NOISY NOEVAL", cmd_pemit, CMD_T_ANY | CMD_T_EQSPLIT | CMD_T_NOGAGGED, 0, 0, 0},
  {"@POLL", NULL, cmd_poll, CMD_T_ANY, 0, 0, 0},
  {"@POOR", NULL, cmd_poor, CMD_T_ANY, 0, 0, 0},
  {"@POWER", NULL, cmd_power, CMD_T_ANY | CMD_T_EQSPLIT, WIZARD, 0, 0},
  {"@PS", "ALL SUMMARY COUNT QUICK", cmd_ps, CMD_T_ANY, 0, 0, 0},
  {"@PURGE", NULL, cmd_purge, CMD_T_ANY, 0, 0, 0},
#ifdef QUOTA
  {"@QUOTA", "ALL SET", cmd_quota, CMD_T_ANY | CMD_T_EQSPLIT, 0, 0, 0},
#endif
  {"@READCACHE", NULL, cmd_readcache, CMD_T_ANY, WIZARD, 0, 0},
  {"@RECYCLE", "OVERRIDE", cmd_destroy, CMD_T_ANY | CMD_T_NOGAGGED, 0, 0, 0},
  {"@REMIT", "NOEVAL", cmd_remit, CMD_T_ANY | CMD_T_EQSPLIT | CMD_T_NOGAGGED, 0, 0, 0},
  {"@REJECTMOTD", NULL, cmd_rejectmotd, CMD_T_ANY, WIZARD, 0, 0},
  {"@RESTART", "ALL", cmd_restart, CMD_T_ANY | CMD_T_NOGAGGED, 0, 0, 0},
#ifdef ROYALTY_FLAG
  {"@RWALL", NULL, cmd_rwall, CMD_T_ANY, WIZARD | ROYALTY, 0, 0},
  {"@RWALLPOSE", NULL, cmd_rwallpose, CMD_T_ANY, WIZARD | ROYALTY, 0, 0},
  {"@RWALLEMIT", NULL, cmd_rwallemit, CMD_T_ANY, WIZARD | ROYALTY, 0, 0},
#endif
  {"@SCAN", "ROOM SELF ZONE GLOBALS", cmd_scan, CMD_T_ANY | CMD_T_NOGAGGED, 0, 0, 0},
  {"@SEARCH", NULL, cmd_search, CMD_T_ANY | CMD_T_EQSPLIT | CMD_T_RS_ARGS | CMD_T_RS_NOPARSE, 0, 0, 0},
  {"@SELECT", NULL, cmd_select, CMD_T_ANY | CMD_T_EQSPLIT | CMD_T_RS_ARGS | CMD_T_RS_NOPARSE, 0, 0, 0},
  {"@SET", NULL, cmd_set, CMD_T_ANY | CMD_T_EQSPLIT | CMD_T_NOGAGGED, 0, 0, 0},
  {"@SHUTDOWN", "PANIC REBOOT PARANOID", cmd_shutdown, CMD_T_ANY, WIZARD, 0, 0},
  {"@SITELOCK", "BAN REGISTER NAME", cmd_sitelock, CMD_T_ANY | CMD_T_EQSPLIT, WIZARD, 0, 0},
  {"@STATS", NULL, cmd_stats, CMD_T_ANY, 0, 0, 0},
  {"@SWEEP", "CONNECTED HERE INVENTORY EXITS", cmd_sweep, CMD_T_ANY, 0, 0, 0},
  {"@SWITCH", "FIRST ALL", cmd_switch, CMD_T_ANY | CMD_T_EQSPLIT | CMD_T_RS_ARGS | CMD_T_RS_NOPARSE | CMD_T_NOGAGGED, 0, 0, 0},
#ifdef QUOTA
  {"@SQUOTA", NULL, cmd_squota, CMD_T_ANY | CMD_T_EQSPLIT, 0, 0, 0},
#endif
  {"@TELEPORT", NULL, cmd_teleport, CMD_T_ANY | CMD_T_EQSPLIT | CMD_T_NOGAGGED, 0, 0, 0},
  {"@TRIGGER", NULL, cmd_trigger, CMD_T_ANY | CMD_T_EQSPLIT | CMD_T_RS_ARGS | CMD_T_NOGAGGED, 0, 0, 0},
  {"@TOAD", NULL, cmd_toad, CMD_T_ANY, WIZARD, 0, 0},
  {"@ULOCK", NULL, cmd_ulock, CMD_T_ANY | CMD_T_EQSPLIT | CMD_T_NOGAGGED, 0, 0, 0},
{"@UNDESTROY", NULL, cmd_undestroy, CMD_T_ANY | CMD_T_NOGAGGED, 0, 0, 0},
  {"@UNLINK", NULL, cmd_unlink, CMD_T_ANY | CMD_T_NOGAGGED, 0, 0, 0},
  {"@UNLOCK", NULL, cmd_unlock, CMD_T_ANY | CMD_T_EQSPLIT | CMD_T_SWITCHES | CMD_T_NOGAGGED, 0, 0, 0},
{"@UNRECYCLE", NULL, cmd_undestroy, CMD_T_ANY | CMD_T_NOGAGGED, 0, 0, 0},
  {"@UPTIME", NULL, cmd_uptime, CMD_T_ANY, 0, 0, 0},
  {"@UUNLOCK", NULL, cmd_uunlock, CMD_T_ANY | CMD_T_NOGAGGED, 0, 0, 0},
  {"@VERB", NULL, cmd_verb, CMD_T_ANY | CMD_T_EQSPLIT | CMD_T_RS_ARGS, 0, 0, 0},
  {"@VERSION", NULL, cmd_version, CMD_T_ANY, 0, 0, 0},
  {"@WAIT", NULL, cmd_wait, CMD_T_ANY | CMD_T_EQSPLIT | CMD_T_RS_NOPARSE, 0, 0, 0},
  {"@WALL", "WIZARD ROYALTY EMIT POSE", cmd_wall, CMD_T_ANY, WIZARD | ROYALTY, 0, CAN_WALL},
  {"@WALLPOSE", NULL, cmd_wallpose, CMD_T_ANY, WIZARD | ROYALTY, 0, CAN_WALL},
  {"@WALLEMIT", NULL, cmd_wallemit, CMD_T_ANY, WIZARD | ROYALTY, 0, CAN_WALL},
#ifdef USE_WARNINGS
  {"@WARNINGS", NULL, cmd_warnings, CMD_T_ANY | CMD_T_EQSPLIT, 0, 0, 0},
  {"@WCHECK", "ALL", cmd_wcheck, CMD_T_ANY, 0, 0, 0},
#endif
  {"@WHEREIS", NULL, cmd_whereis, CMD_T_ANY | CMD_T_NOGAGGED, 0, 0, 0},
  {"@WIPE", NULL, cmd_wipe, CMD_T_ANY, 0, 0, 0},
  {"@WIZWALL", NULL, cmd_wizwall, CMD_T_ANY, WIZARD, 0, 0},
  {"@WIZPOSE", NULL, cmd_wizpose, CMD_T_ANY, WIZARD, 0, 0},
  {"@WIZEMIT", NULL, cmd_wizemit, CMD_T_ANY, WIZARD, 0, 0},
  {"@WIZMOTD", NULL, cmd_wizmotd, CMD_T_ANY, WIZARD, 0, 0},
  {"@ZEMIT", NULL, cmd_zemit, CMD_T_ANY | CMD_T_EQSPLIT | CMD_T_NOGAGGED, 0, 0, 0},

  {"AHELP", NULL, cmd_ahelp, CMD_T_ANY | CMD_T_NOPARSE, 0, 0, 0},
  {"ANEWS", NULL, cmd_anews, CMD_T_ANY | CMD_T_NOPARSE, 0, 0, 0},
  {"BRIEF", NULL, cmd_brief, CMD_T_ANY, 0, 0, 0},
  {"DROP", NULL, cmd_drop, CMD_T_PLAYER | CMD_T_THING, 0, 0, 0},
  {"EXAMINE", "BRIEF DEBUG MORTAL", cmd_examine, CMD_T_ANY, 0, 0, 0},
  {"ENTER", NULL, cmd_enter, CMD_T_ANY, 0, 0, 0},
  {"EVENTS", NULL, cmd_events, CMD_T_ANY | CMD_T_NOPARSE, 0, 0, 0},
#ifdef FOLLOW
  {"FOLLOW", NULL, cmd_follow, CMD_T_PLAYER | CMD_T_THING | CMD_T_NOGAGGED, 0, 0, 0},
#endif
  {"GET", NULL, cmd_get, CMD_T_PLAYER | CMD_T_THING | CMD_T_NOGAGGED, 0, 0, 0},
  {"GIVE", NULL, cmd_give, CMD_T_ANY | CMD_T_EQSPLIT | CMD_T_NOGAGGED, 0, 0, 0},
  {"GOTO", NULL, cmd_goto, CMD_T_PLAYER | CMD_T_THING, 0, 0, 0},
  {"HELP", NULL, cmd_help, CMD_T_ANY | CMD_T_NOPARSE, 0, 0, 0},
  {"INVENTORY", NULL, cmd_inventory, CMD_T_ANY, 0, 0, 0},
  {"INDEX", NULL, cmd_index, CMD_T_ANY | CMD_T_NOPARSE, 0, 0, 0},
  {"KILL", NULL, cmd_kill, CMD_T_ANY | CMD_T_EQSPLIT | CMD_T_NOGAGGED, 0, 0, 0},
  {"LOOK", "OUTSIDE", cmd_look, CMD_T_ANY, 0, 0, 0},
  {"LEAVE", NULL, cmd_leave, CMD_T_PLAYER | CMD_T_THING, 0, 0, 0},
  {"MOVE", NULL, cmd_goto, CMD_T_PLAYER | CMD_T_THING, 0, 0, 0},
  {"NEWS", NULL, cmd_news, CMD_T_ANY | CMD_T_NOPARSE, 0, 0, 0},
  {"PAGE", "BLIND NOEVAL LIST", cmd_page, CMD_T_ANY | CMD_T_EQSPLIT | CMD_T_NOGAGGED, 0, 0, 0},
  {"POSE", "NOEVAL NOSPACE", cmd_pose, CMD_T_ANY, 0, 0, 0},
  {"ROB", NULL, cmd_rob, CMD_T_ANY, 0, 0, 0},
#ifdef ALLOW_RPAGE
  {"RPAGE", "LIST ADD DELETE", cmd_rpage, CMD_T_ANY | CMD_T_EQSPLIT, 0, 0, 0},
#endif
  {"RULES", NULL, cmd_rules, CMD_T_ANY | CMD_T_NOPARSE, 0, 0, 0},
  {"READ", NULL, cmd_look, CMD_T_ANY, 0, 0, 0},
  {"SCORE", NULL, cmd_score, CMD_T_ANY, 0, 0, 0},
  {"SAY", "NOEVAL", cmd_say, CMD_T_ANY | CMD_T_NOGAGGED, 0, 0, 0},
  {"SEMIPOSE", "NOEVAL", cmd_semipose, CMD_T_ANY, 0, 0, 0},
  {"SLAY", NULL, cmd_slay, CMD_T_ANY, 0, 0, 0},
  {"TAKE", NULL, cmd_take, CMD_T_PLAYER | CMD_T_THING | CMD_T_NOGAGGED, 0, 0, 0},
  {"THINK", NULL, cmd_think, CMD_T_ANY | CMD_T_NOGAGGED, 0, 0, 0},
  {"WHISPER", "NOISY SILENT NOEVAL", cmd_whisper, CMD_T_ANY | CMD_T_EQSPLIT | CMD_T_NOGAGGED, 0, 0, 0},
  {"USE", NULL, cmd_use, CMD_T_ANY | CMD_T_NOGAGGED, 0, 0, 0},

/* ATTRIB_SET is an undocumented command - it's sugar to make it possible
 * enable/disable attribute setting with &XX or @XX
 */
  {"ATTRIB_SET", NULL, command_atrset, CMD_T_ANY | CMD_T_EQSPLIT | CMD_T_NOGAGGED | CMD_T_INTERNAL, 0, 0, 0},
  {NULL, NULL, NULL, 0, 0, 0, 0}
};


SWITCH_VALUE switch_list[] =
{
#include "switchinc.c"
  {NULL, 0}
};

/* These aliases are added before commands are auto-aliased, so they
 * take precedence. That is, "l" will always be "look", no matter what.
 * If you don't do this, then since "l" look ambiguous to the autoaliaser
 * (it could be look or leave), it won't get aliased at all.
 */
COMALIAS command_alias[] =
{
  {"EXAMINE", "E"},
  {"BRIEF", "B"},
  {"LOOK", "L"},
  {"INVENTORY", "I"},
  {"PAGE", "P"},
  {"WHISPER", "W"},
  {"@RWALL", "@RW"},
  {"@WIZWALL", "@WIZ"},
  {"@TRIGGER", "@TR"},
  {NULL, NULL}
};

static char nullstring[] = "";


void
strccat(to, from)
    char *to;
    const char *from;
{
  if (*to)
    strcat(to, ", ");
  strcat(to, from);
}

static int
switch_find(cmd, sw)
    COMMAND_INFO *cmd;
    char *sw;
{
  SWITCH_VALUE *sw_val;
  int i = 0;
  int len;
  len = strlen(sw);
  /* Special case, for init */
  sw_val = switch_list;
  if (!cmd) {
    while (sw_val->name) {
      if (strcmp(sw_val->name, sw) == 0)
	return sw_val->value;
      sw_val++;
    }
    return 0;
  } else {
    while (sw_val->name) {
      i++;
      if (SW_ISSET(cmd->sw, i) && (strncmp(sw_val->name, sw, len) == 0))
	return i;
      sw_val++;
    }
  }
  return 0;
}

COMMAND_INFO *
command_add(name, type, flags, toggles, powers, sw, func)
    const char *name;
    int type;
    int flags;
    int toggles;
    int powers;
    switch_mask *sw;
    void (*func) ();
{
  COMMAND_INFO *cmd;
  cmd = (COMMAND_INFO *) mush_malloc(sizeof(COMMAND_INFO), "command");
  memset(cmd, 0, sizeof(COMMAND_INFO));
  cmd->name = name;
  cmd->func = func;
  cmd->type = type;
  cmd->flags = flags;
  cmd->toggles = toggles;
  cmd->powers = powers;
  if (sw)
    memcpy(cmd->sw, sw, sizeof(switch_mask));
  else
    SW_ZERO(cmd->sw);
  hashadd(name, (void *) cmd, &htab_command);
  return cmd;
}

COMMAND_INFO *
command_find(name)
    const char *name;
{
  char cmdname[BUFFER_LEN];
  strcpy(cmdname, name);
  upcasestr(cmdname);
  return (COMMAND_INFO *) hashfind(cmdname, &htab_command);
}

COMMAND_INFO *
command_modify(name, type, flags, toggles, powers, sw, func)
    const char *name;
    int type;
    int flags;
    int toggles;
    int powers;
    switch_mask *sw;
    void (*func) ();
{
  COMMAND_INFO *cmd;
  cmd = command_find(name);
  if (!cmd)
    return NULL;
  if (type != -1)
    cmd->type = type;
  if (flags != -1)
    cmd->flags = flags;
  if (toggles != -1)
    cmd->toggles = toggles;
  if (powers != -1)
    cmd->powers = powers;
  if (sw)
    memcpy(cmd->sw, sw, sizeof(switch_mask));
  if (func)
    cmd->func = func;
  return cmd;
}

switch_mask *
switchmask(switches)
    const char *switches;
{
  static switch_mask sw;
  char buff[BUFFER_LEN];
  char *p, *s;
  int switchnum;
  SW_ZERO(sw);
  if (!switches || !switches[0])
    return NULL;
  strcpy(buff, switches);
  p = buff;
  while (p) {
    s = split_token(&p, ' ');
    switchnum = switch_find(NULL, s);
    if (!switchnum)
      return NULL;
    else
      SW_SET(sw, switchnum);
  }
  return &sw;
}

void
command_init_preconfig()
{
  COMLIST *cmd;
  static int done = 0;
  if (done == 1)
    return;
  done = 1;

  hashinit(&htab_command, 128);
  hashinit(&htab_reserved_aliases, 64);
  for (cmd = commands; cmd->name; cmd++) {
    command_add(cmd->name, cmd->type, cmd->flags, cmd->toggles, cmd->powers, switchmask(cmd->switches), cmd->func);
  }

  reserve_aliases();
  local_commands();
  command_mkalias();
}

void
command_init_postconfig()
{
  if (!*EVENT_FILE)
    restrict_command("EVENTS", "nobody");
  if (!*INDEX_FILE)
    restrict_command("INDEX", "nobody");
  if (!*RULES_FILE)
    restrict_command("RULES", "nobody");
  if (HATE_DEST)
    restrict_command("@DESTROY", "noplayer");
  else
    restrict_command("@RECYCLE", "nobody");
  if (!PLAYER_LOCATE)
    restrict_command("@WHEREIS", "nobody");
}


void
command_addalias(cmd, prev, next)
    COMMAND_INFO *cmd;
    char *prev;
    char *next;
{
  int i;
  char buff[BUFFER_LEN];
  if (!prev)
    prev = nullstring;
  if (!next)
    next = nullstring;
  strcpy(buff, cmd->name);
  for (i = strlen(buff) - 1;
       (i > 1) && strncmp(buff, prev, i) && strncmp(buff, next, i);
       i--) {
    buff[i] = '\0';
    if (!hashfind(buff, &htab_reserved_aliases))
      hashadd(buff, (void *) cmd, &htab_command);
  }
}

void
command_mkalias()
{
  COMMAND_INFO *cmd;
  COMSORTSTRUC *first, *this, *new;
  COMALIAS *alias;
  char *pname;

  /* Hash all the built-in aliases first, so they don't get 
   * overridden by autoaliases
   */
  for (alias = command_alias; alias->name; alias++) {
    cmd = (COMMAND_INFO *) command_find(alias->name);
    if (!cmd) {
      do_rawlog(LT_ERR, "ALIAS : %s for nonexistant command %s", alias->alias, alias->name);
    } else {
      if (!hashfind(alias->alias, &htab_reserved_aliases))
	hashadd(alias->alias, (void *) cmd, &htab_command);
    }
  }

  /* Go through the hash table and construct a sorted linked list of
   * command names.
   */
  first = NULL;
  cmd = hash_firstentry(&htab_command);
  while (cmd) {
    if (!(cmd->type & CMD_T_INTERNAL)) {
      this = first;
      /* Find the name after which this one should be inserted */
      while ((this) && (this->cmd != cmd) && (this->next) && (strcmp(this->next->cmd->name, cmd->name) <= 0))
	this = this->next;
      if (!(this && (this->cmd == cmd))) {
	new = (COMSORTSTRUC *) mush_malloc(sizeof(COMSORTSTRUC), "comsortstruc");
	new->cmd = cmd;
	if (this && (this != first)) {
	  new->next = this->next;
	  this->next = new;
	} else {
	  new->next = first;
	  first = new;
	}
      }
    }
    cmd = hash_nextentry(&htab_command);
  }

  /* Run through our sorted linked list, passing each entry, and those
   * immediately before and after, if any, so it can make unique
   * abbreviations into aliases
   */
  pname = NULL;
  this = first;
  while (this) {
    command_addalias(this->cmd, pname, (char *) ((this->next) ? this->next->cmd->name : NULL));
    new = this;
    pname = (char *) this->cmd->name;
    this = this->next;
    mush_free(new, "comsortstruc");
  }

}

/* Real work. Parse the command arguments into arrays */
void
command_argparse(player, cause, from, to, argv, cmd, right_side, forcenoparse)
    dbref player;
    dbref cause;
    char **from;
    char *to;
    char *argv[];
    COMMAND_INFO *cmd;
    int right_side;
    int forcenoparse;
{
  int parse, split, args, i, done;
  char *t, *f;
  char *aold;

  f = *from;

  parse = (right_side) ? (cmd->type & CMD_T_RS_NOPARSE) : (cmd->type & CMD_T_NOPARSE);
  if (parse || forcenoparse)
    parse = PE_NOTHING;
  else
    parse = PE_DEFAULT;

  if (right_side)
    split = PT_NOTHING;
  else
    split = (cmd->type & CMD_T_EQSPLIT) ? PT_EQUALS : PT_NOTHING;

  if (right_side) {
    if (cmd->type & CMD_T_RS_ARGS)
      args = (cmd->type & CMD_T_RS_SPACE) ? PT_SPACE : PT_COMMA;
    else
      args = 0;
  } else {
    if (cmd->type & CMD_T_LS_ARGS)
      args = (cmd->type & CMD_T_LS_SPACE) ? PT_SPACE : PT_COMMA;
    else
      args = 0;
  }

  if ((parse == PE_NOTHING) && args)
    parse = PE_STRIP_BRACES;

  i = 1;
  done = 0;
  *to = '\0';

  if (args) {
    t = to + 1;
  } else {
    t = to;
  }

  while (*f && !done) {
    aold = t;
    while (*f == ' ')
      f++;
    process_expression(to, &t, (const char **) &f, player, cause, cause,
		       parse, (split | args), NULL);
    *t = '\0';
    if (args) {
      argv[i] = aold;
      if (*f)
	f++;
      i++;
      t++;
    }
    if (split && (*f == '=')) {
      f++;
      *from = f;
      done = 1;
    }
  }

  *from = f;

  if (args)
    while (i < MAX_ARG)
      argv[i++] = NULL;
}

/* Is this command an attempt to set an attribute like @VA or &NUM?
 * If so, return the attrib's name. Otherwise, return NULL
 */
static char *
command_isattr(command)
    char *command;
{
  ATTR *a;
  char buff[BUFFER_LEN];
  char *f, *t;

  if (((command[0] == '&') && (command[1])) ||
      ((command[0] == '@') && (command[1] == '_') && (command[2]))) {
    /* User-defined attributes: @_NUM or &NUM */
    if (command[0] == '@')
      return command + 2;
    else
      return command + 1;
  } else if (command[0] == '@') {
    f = command + 1;
    t = buff;
    while ((*f) && (*f != '/'))
      *t++ = *f++;
    *t = 0;
    a = atr_match(buff);
    if (a)
      return (char *) AL_NAME(a);
  }
  return NULL;
}

/* Parse the commands. This is the big one! 
 * Return 1 if the command was recognized and handled, 0 otherwise
 */
char *
command_parse(player, cause, string, fromport)
    dbref player;
    dbref cause;
    char *string;
    int fromport;
{
  char command[BUFFER_LEN];
  static char commandraw[BUFFER_LEN];
  char swtch[BUFFER_LEN];
  char ls[BUFFER_LEN];
  char rs[BUFFER_LEN];
  char switches[BUFFER_LEN];
  char *lsa[MAX_ARG];
  char *rsa[MAX_ARG];
  char *ap;
  char *swp;
  char *attrib;
  const char *replacer;
  COMMAND_INFO *cmd;
  char *p, *t, *c;
  char b;
  int switchnum;
  switch_mask sw;
  int noeval;

  p = string;
  replacer = NULL;
  attrib = NULL;
  cmd = NULL;
  c = command;
  /* All those one character commands.. Sigh */
  switch (*p) {
  case '\0':
    /* Just in case. You never know */
    return NULL;
  case '#':
    if (!IS(db[player].owner, TYPE_PLAYER, PLAYER_GAGGED) && Mobile(player))
      force_by_number(player, string);
    return NULL;
  case SAY_TOKEN:
    replacer = "SAY";
    break;
  case POSE_TOKEN:
    replacer = "POSE";
    break;
  case SEMI_POSE_TOKEN:
    replacer = "SEMIPOSE";
    break;
  case EMIT_TOKEN:
    replacer = "@EMIT";
    break;
#ifdef CHAT_SYSTEM
  case CHAT_TOKEN:
    if (command_check_byname(player, "@chat") && parse_chat(player, p + 1))
      return NULL;
#endif				/* CHAT_SYSTEM */
    /* And everything else */
  default:
    /* EXCEPT exits, which have priority */
    if (can_move(player, p)) {
      if (Mobile(player))
	do_move(player, p, 0);
      return NULL;
    }
    c = command;
    while (*p == ' ')
      p++;
    process_expression(command, &c, (const char **) &p, player, cause, cause, PE_DEFAULT, PT_SPACE, NULL);
    *c = '\0';
    strcpy(commandraw, command);
    upcasestr(command);

    /* Catch &XX and @XX attribute pairs. If that's what we've got,
     * use the magical ATTRIB_SET command
     */
    attrib = command_isattr(command);
    if (attrib) {
      cmd = command_find("ATTRIB_SET");
    } else {
      c = command;
      while ((*c) && (*c != '/') && (*c != ' '))
	c++;
      b = *c;
      *c = '\0';
      cmd = command_find(command);
      *c = b;
      /* Is this for internal use? If so, players can't use it! */
      if (cmd && (cmd->type & CMD_T_INTERNAL))
	cmd = NULL;
    }
  }

  if (replacer) {
    cmd = command_find(replacer);
    p++;
  }
  /* Test if this either isn't a command, or is a disabled one */
  /* If so, return Fully Parsed string for further processing */

  if (!cmd || (cmd->type & CMD_T_DISABLED)) {
    if (!cmd) {
      c = commandraw + strlen(commandraw);
    } else {
      if (*c == '/') {
	/* Oh... DAMN */
	c = commandraw;
	strcpy(switches, commandraw);
	safe_str(cmd->name, commandraw, &c);
	t = (char *) index(switches, '/');
	safe_str(t, commandraw, &c);
      } else {
	c = commandraw;
	safe_str(cmd->name, commandraw, &c);
      }
    }
    if (*p) {
      if (*p == ' ') {
	safe_chr(' ', commandraw, &c);
	p++;
      }
      process_expression(commandraw, &c, (const char **) &p, player, cause, cause, PE_DEFAULT, PT_DEFAULT, NULL);
    }
    *c = '\0';
    return commandraw;
  }
  /* Check the permissions */
  if (!command_check(player, cmd))
    return NULL;

  /* Parse out any switches */
  SW_ZERO(sw);
  swp = switches;
  *swp = '\0';

  t = NULL;

  /* Don't parse switches for one-char commands */
  if (!replacer) {
    while (*c == '/') {
      t = swtch;
      c++;
      while ((*c) && (*c != ' ') && (*c != '/'))
	*t++ = *c++;
      *t = '\0';
      switchnum = switch_find(cmd, upcasestr(swtch));
      if (!switchnum) {
	if (cmd->type & CMD_T_SWITCHES) {
	  if (*swp)
	    strcat(swp, " ");
	  strcat(swp, swtch);
	} else {
	  notify(player, tprintf("%s doesn't know switch %s.", cmd->name, swtch));
	  return NULL;
	}
      } else {
	SW_SET(sw, switchnum);
      }
    }
  }
  if (!t)
    SW_SET(sw, SWITCH_NONE);

  /* If we're calling ATTRIB_SET, the switch is the attribute name */
  if (attrib)
    swp = attrib;
  else if (!*swp)
    swp = NULL;

  if (*p == ' ')
    p++;
  ap = p;

  /* Don't eval arguments if we've got the /noeval switch or if
   * we're a directly connected player setting an attribute 
   */
  if ((cmd->func == command_atrset) && fromport)
    SW_SET(sw, SWITCH_NOEVAL);
  if (SW_ISSET(sw, SWITCH_NOEVAL))
    noeval = 1;
  else
    noeval = 0;

  if (cmd->type & CMD_T_EQSPLIT) {
    command_argparse(player, cause, &p, ls, lsa, cmd, 0, 0);
    command_argparse(player, cause, &p, rs, rsa, cmd, 1, noeval);
  } else {
    command_argparse(player, cause, &p, ls, lsa, cmd, 0, noeval);
  }

  if (cmd->func == NULL) {
    do_rawlog(LT_ERR, "No command vector on command %s.", cmd->name);
    return NULL;
  } else {
    cmd->func(cmd, player, cause, sw, string, swp, ap,
	      ls, lsa,
	      rs, rsa);
  }

  return NULL;
}

/* Given a command name and a restriction, apply the restriction to the
 * command in addition to whatever its usual restrictions are.
 * This is used by the configuration file startup in conf.c
 * Valid restrictions are:
 *   nobody     disable the command
 *   nogagged   can't be used by gagged players
 *   noguest    can't be used by guests
 *   admin      can only be used by royalty or wizards
 *   wizard     can only be used by wizards
 *   god        can only be used by god
 *   noplayer   can't be used by players, just objects/rooms/exits
 * Return 1 on success, 0 on failure.
 */
int
restrict_command(name, restriction)
    const char *name;
    const char *restriction;
{
  COMMAND_INFO *command;
  if (!name || !*name)
    return 0;
  command = command_find(name);
  if (!command)
    return 0;
  if (!strcasecmp(restriction, "nobody")) {
    command->type |= CMD_T_DISABLED;
  } else if (!strcasecmp(restriction, "nogag")) {
    command->type |= CMD_T_NOGAGGED;
  } else if (!strcasecmp(restriction, "noguest")) {
    command->type |= CMD_T_NOGUEST;
  } else if (!strcasecmp(restriction, "admin")) {
    command->flags |= ROYALTY | WIZARD;
  } else if (!strcasecmp(restriction, "wizard")) {
    command->flags |= WIZARD;
  } else if (!strcasecmp(restriction, "god")) {
    command->type |= CMD_T_GOD;
  } else if (!strcasecmp(restriction, "noplayer")) {
    command->type &= ~CMD_T_PLAYER;
  } else {
    return 0;
  }
  return 1;
}

/* This is the only command which should be defined in this
 * file, because it uses variables from this file, etc.
 */
COMMAND (cmd_command) {
  COMMAND_INFO *command;
  SWITCH_VALUE *sw_val;
  char buff[BUFFER_LEN];

  if (!arg_left[0]) {
    notify(player, "You must specify a command.");
    return;
  }
  command = command_find(arg_left);
  if (!command) {
    notify(player, "No such command.");
    return;
  }
  if (SW_ISSET(sw, SWITCH_ON) || SW_ISSET(sw, SWITCH_ENABLE))
    command->type &= ~CMD_T_DISABLED;
  else if (SW_ISSET(sw, SWITCH_OFF) || SW_ISSET(sw, SWITCH_DISABLE))
    command->type |= CMD_T_DISABLED;

  if ((command->func == cmd_command) && (command->type & CMD_T_DISABLED)) {
    notify(player, "@command is ALWAYS enabled.");
    command->type &= ~CMD_T_DISABLED;
  }
  if (!SW_ISSET(sw, SWITCH_QUIET)) {
    notify(player, tprintf("Name       : %s (%s)", command->name, (command->type & CMD_T_DISABLED) ? "Disabled" : "Enabled"));
    if ((command->type & CMD_T_ANY) == CMD_T_ANY)
      strcpy(buff, "Any");
    else {
      buff[0] = '\0';
      if (command->type & CMD_T_ROOM)
	strccat(buff, "Room");
      if (command->type & CMD_T_THING)
	strccat(buff, "Thing");
      if (command->type & CMD_T_EXIT)
	strccat(buff, "Exit");
      if (command->type & CMD_T_PLAYER)
	strccat(buff, "Player");
    }
    notify(player, tprintf("Types      : %s", buff));
    buff[0] = '\0';
    if (command->type & CMD_T_SWITCHES)
      strccat(buff, "Switches");
    if (command->type & CMD_T_NOGAGGED)
      strccat(buff, "Nogag");
    if (command->type & CMD_T_NOGUEST)
      strccat(buff, "Noguest");
    if (command->type & CMD_T_EQSPLIT)
      strccat(buff, "Eqsplit");
    if (command->type & CMD_T_GOD)
      strccat(buff, "God");
    notify(player, tprintf("Flags      : %s", buff));
    buff[0] = '\0';
    for (sw_val = switch_list; sw_val->name; sw_val++)
      if (SW_ISSET(command->sw, sw_val->value))
	strccat(buff, sw_val->name);
    notify(player, tprintf("Switches   : %s", buff));
    buff[0] = '\0';
    if (command->type & CMD_T_LS_ARGS) {
      if (command->type & CMD_T_LS_SPACE)
	strccat(buff, "Space-Args");
      else
	strccat(buff, "Args");
    }
    if (command->type & CMD_T_LS_NOPARSE)
      strccat(buff, "Noparse");
    if (command->type & CMD_T_EQSPLIT) {
      notify(player, tprintf("Leftside   : %s", buff));
      buff[0] = '\0';
      if (command->type & CMD_T_RS_ARGS) {
	if (command->type & CMD_T_RS_SPACE)
	  strccat(buff, "Space-Args");
	else
	  strccat(buff, "Args");
      }
      if (command->type & CMD_T_RS_NOPARSE)
	strccat(buff, "Noparse");
      notify(player, tprintf("Rightside  : %s", buff));
    } else {
      notify(player, tprintf("Arguments  : %s", buff));
    }
  }
}

void
do_list_commands(player)
    dbref player;
{
  COMMAND_INFO *command;
  char *ptrs[BUFFER_LEN / 2];
  char buff[BUFFER_LEN];
  char *bp;
  int nptrs = 0, i;
  command = hash_firstentry(&htab_command);
  while (command) {
    if (!(command->type & CMD_T_LISTED)) {
      ptrs[nptrs++] = (char *) command->name;
      command->type |= CMD_T_LISTED;
    }
    command = hash_nextentry(&htab_command);
  }
  command = hash_firstentry(&htab_command);
  while (command) {
    command->type &= ~CMD_T_LISTED;
    command = hash_nextentry(&htab_command);
  }
  do_gensort(ptrs, nptrs, 0);
  bp = buff;
  safe_str("Commands:", buff, &bp);
  for (i = 0; i < nptrs; i++) {
    safe_chr(' ', buff, &bp);
    safe_str(ptrs[i], buff, &bp);
  }
  *bp = '\0';
  notify(player, buff);
}


/* Check command permissions. Return 1 if player can use command,
 * 0 otherwise, and be noisy about it.
 */
static int
command_check(player, cmd)
    dbref player;
    COMMAND_INFO *cmd;
{
  int ok;

  /* If disabled, return silently */
  if (cmd->type & CMD_T_DISABLED)
    return 0;
  if ((cmd->type & CMD_T_NOGAGGED) &&
      IS(Owner(player), TYPE_PLAYER, PLAYER_GAGGED)) {
    notify(player, "Cannot do that while gagged.");
    return 0;
  }
  if ((cmd->type & CMD_T_NOGUEST) && Guest(player)) {
    notify(player, "Guests cannot do that.");
    return 0;
  }
  if ((cmd->type & CMD_T_GOD) && (!God(player))) {
    notify(player, "Only God can do that.");
    return 0;
  }
  switch (Typeof(player)) {
  case TYPE_ROOM:
    ok = (cmd->type & CMD_T_ROOM);
    break;
  case TYPE_THING:
    ok = (cmd->type & CMD_T_THING);
    break;
  case TYPE_EXIT:
    ok = (cmd->type & CMD_T_EXIT);
    break;
  case TYPE_PLAYER:
    ok = (cmd->type & CMD_T_PLAYER);
    break;
  default:
    ok = 0;
  }
  if (!ok) {
    notify(player, "Permission denied, command is type-restricted.");
    return 0;
  }
  /* A command can specify required flags, toggles, or powers, and if
   * any match, the player is ok to do the command. Toggles really
   * should only be used if the command is type-restricted, too!
   */
  if (!(
      ((cmd->flags == 0) && (cmd->toggles == 0) && (cmd->powers == 0)) ||
	 ((cmd->flags) && (cmd->flags & Flags(player))) ||
	 ((cmd->toggles) && (cmd->toggles & Toggles(player))) ||
	 ((cmd->powers) && (cmd->powers & Powers(player))))) {
    notify(player, "Permission denied.");
    return 0;
  }
  return 1;
}

/* command_check, but given a name, not a structure */
int
command_check_byname(player, name)
    dbref player;
    const char *name;
{
  COMMAND_INFO *cmd;
  cmd = command_find(name);
  if (!cmd)
    return 0;
  return command_check(player, cmd);
}