/
2.0.5/doc/
2.0.5/gnu/
2.0.5/sha/
/* commands.c */

#include "config.h"

/*
 *		       This file is part of TeenyMUD II.
 *		 Copyright(C) 1993, 1994, 1995 by Jason Downs.
 *                           All rights reserved.
 * 
 * TeenyMUD II is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 2 of the License, or
 * (at your option) any later version.
 *
 * TeenyMUD II is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program (see the file 'COPYING'); if not, write to
 * the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston,
 * MA 02111-1307, USA.
 *
 */

/* AIX requires this to be the first thing in the file. */
#ifdef __GNUC__
#define alloca	__builtin_alloca
#else	/* not __GNUC__ */
#ifdef HAVE_ALLOCA_H
#include <alloca.h>
#else	/* not HAVE_ALLOCA_H */
#ifdef _AIX
 #pragma alloca
#endif	/* not _AIX */
#endif	/* not HAVE_ALLOCA_H */
#endif 	/* not __GNUC__ */

#include <stdio.h>
#include <ctype.h>
#include <sys/types.h>
#ifdef HAVE_STRING_H
#include <string.h>
#else
#include <strings.h>
#endif			/* HAVE_STRING_H */
#ifdef HAVE_STDLIB_H
#include <stdlib.h>
#endif			/* HAVE_STDLIB_H */

#include "conf.h"
#include "teeny.h"
#include "commands.h"
#include "externs.h"

#include "hash/hash.h"

/* all new main command parser. */

/* all command "switch" arguments are of this type. */
typedef struct cmd_switch {
  char *name;
  int matlen;
  int code;
  int flags;
} SWITCH;

/* all commands are of this type. */
typedef struct commands {
  char *name;
  VOID (*function) ();
  SWITCH *switches;
  int nargs;
  int perms;
} CMDS;

/* flag definitions - permissions. */
#define CMDS_NORMAL     0x00000000
#define CMDS_GOD        0x00000001
#define CMDS_WIZARD     0x00000002
#define CMDS_BUILDER    0x00000003
#define CMDS_MASK       0x00000007
#define CMDS_NGUEST     0x00000010
#define CMDS_PLAYER     0x00000020
#define CMDS_NOSED	0x00000040
#define CMDS_SPEECH	0x00000080		/* speech command */

/* switch types. */
#define SWITCH_MULTIPLE 0x00001000
#define SWITCH_SINGULAR 0x00002000

/* misc. flags. */
#define CMDS_NOEXEC	0x00100000
#define CMDS_VARSLASH	0x00200000
#define CMDS_LOCREQ	0x00400000

#define CMDS_VARARGS	-1

/* "switch" declarations. */

/*
 * First come the ``global'' switches-- ones that are parsed for in
 * every command.  The command itself may or may not actually support
 * them, of course.
 */

static SWITCH globalcmd_switches[] =
{
  {"quiet", 5, CMD_QUIET, CMDS_NORMAL | SWITCH_MULTIPLE},
  {(char *)NULL, 0, 0, 0}
};

static SWITCH ex_switches[] =
{
  {"rooms", 1, EXAMINE_ROOMS, CMDS_NORMAL | SWITCH_MULTIPLE},
  {"parents", 1, EXAMINE_PARENT, CMDS_NORMAL | SWITCH_MULTIPLE},
  {"attrs", 1, EXAMINE_ATTRS, CMDS_NORMAL | SWITCH_MULTIPLE},
  {"sort", 1, EXAMINE_SORT, CMDS_NORMAL | SWITCH_MULTIPLE},
  {(char *) NULL, 0, 0, 0}
};

static SWITCH recycle_switches[] =
{
  {"override", 4, RECYCLE_OVERRIDE, CMDS_WIZARD | SWITCH_SINGULAR},
  {(char *) NULL, 0, 0, 0}
};

static SWITCH link_switches[] =
{
  {"no_chown", 2, LINK_NOCHOWN, CMDS_WIZARD | SWITCH_SINGULAR},
  {(char *) NULL, 0, 0, 0}
};

static SWITCH stats_switches[] =
{
  {"full", 3, STATS_FULL, CMDS_NORMAL | SWITCH_SINGULAR},
  {(char *) NULL, 0, 0, 0}
};

static SWITCH find_switches[] =
{
  {"owned", 3, FIND_OWNED, CMDS_WIZARD | SWITCH_SINGULAR},
  {(char *) NULL, 0, 0, 0}
};

static SWITCH kill_switches[] =
{
  {"slay", 4, KILL_SLAY, CMDS_WIZARD | SWITCH_SINGULAR},
  {(char *) NULL, 0, 0, 0}
};

static SWITCH wall_switches[] =
{
  {"god", 1, WALL_GOD, CMDS_GOD | SWITCH_SINGULAR},
  {"wiz", 1, WALL_WIZ, CMDS_WIZARD | SWITCH_SINGULAR},
  {"pose", 1, WALL_POSE, CMDS_WIZARD | SWITCH_MULTIPLE},
  {"no_space", 1, WALL_NOSPACE, CMDS_WIZARD | SWITCH_MULTIPLE},
  {"emit", 1, WALL_EMIT, CMDS_WIZARD | SWITCH_MULTIPLE},
  {(char *) NULL, 0, 0, 0}
};

static SWITCH halt_switches[] =
{
  {"all", 3, HALT_ALL, CMDS_WIZARD | SWITCH_MULTIPLE},
/*{"quiet", 1, HALT_QUIET, CMDS_WIZARD | SWITCH_MULTIPLE},*/
  {(char *)NULL, 0, 0, 0}
};

static SWITCH ps_switches[] =
{
  {"all", 3, PS_ALL, CMDS_WIZARD | SWITCH_SINGULAR},
  {"cause", 1, PS_CAUSE, CMDS_NORMAL | SWITCH_MULTIPLE},
  {"owned", 3, PS_OWNED, CMDS_NORMAL | SWITCH_MULTIPLE},
  {(char *)NULL, 0, 0, 0}
};

static SWITCH pose_switches[] =
{
  {"no_space", 1, POSE_NOSPACE, CMDS_NORMAL | SWITCH_SINGULAR},
  {(char *) NULL, 0, 0, 0}
};

static SWITCH version_switches[] = 
{
  {"full", 1, VERSION_FULL, CMDS_NORMAL | SWITCH_MULTIPLE},
  {(char *) NULL, 0, 0, 0}
};

static SWITCH config_switches[] =
{
  {"set", 1, CONFIG_SET, CMDS_GOD | SWITCH_SINGULAR},
  {"alias", 1, CONFIG_ALIAS, CMDS_GOD | SWITCH_SINGULAR},
  {"unalias", 1, CONFIG_UNALIAS, CMDS_GOD | SWITCH_SINGULAR},
  {"expand", 1, CONFIG_EXPAND, CMDS_GOD | SWITCH_SINGULAR},
  {(char *)NULL, 0, 0, 0}
};

static SWITCH notify_switches[] = 
{
  {"all", 1, NOTIFY_ALL, CMDS_NORMAL | SWITCH_MULTIPLE},
  {"drain", 1, NOTIFY_DRAIN, CMDS_NORMAL | SWITCH_MULTIPLE},
  {(char *)NULL, 0, 0, 0}
};

static SWITCH motd_switches[] =
{
  {"set", 1, MOTD_SET, CMDS_WIZARD | SWITCH_SINGULAR},
  {(char *)NULL, 0, 0, 0}
};

static SWITCH boot_switches[] =
{
  {"nomesg", 2, BOOT_NOMESG, CMDS_WIZARD | SWITCH_SINGULAR},
  {(char *)NULL, 0, 0, 0}
};

static SWITCH case_switches[] =
{
  {"first", 2, CASE_FIRST, CMDS_NORMAL | SWITCH_SINGULAR},
  {(char *)NULL, 0, 0, 0}
};

static SWITCH group_switches[] =
{
  {"delete", 6, GROUP_DISOLVE, CMDS_NGUEST | SWITCH_SINGULAR},
  {"open", 4, GROUP_OPEN, CMDS_NGUEST | SWITCH_SINGULAR},
  {"close", 5, GROUP_CLOSE, CMDS_NGUEST | SWITCH_SINGULAR},
  {"boot", 4, GROUP_BOOT, CMDS_NGUEST | SWITCH_SINGULAR},
  {"say", 1, GROUP_SAY, CMDS_NORMAL | SWITCH_SINGULAR},
  {"emote", 1, GROUP_EMOTE, CMDS_NORMAL | SWITCH_SINGULAR},
  {"display", 3, GROUP_DISPLAY, CMDS_NORMAL | SWITCH_SINGULAR},
  {"all", 3, GROUP_ALL, CMDS_NORMAL | SWITCH_MULTIPLE},
  {"follow", 3, GROUP_FOLLOW, CMDS_NORMAL | SWITCH_SINGULAR},
  {"join", 1, GROUP_JOIN, CMDS_NORMAL | SWITCH_SINGULAR},
  {"silence", 3, GROUP_SILENCE, CMDS_NORMAL | SWITCH_SINGULAR},
  {"noisy", 3, GROUP_NOISY, CMDS_NORMAL | SWITCH_SINGULAR},
  {(char *)NULL, 0, 0, 0}
};

static SWITCH emit_switches[] =
{
  {"others", 1, EMIT_OTHERS, CMDS_NORMAL | SWITCH_SINGULAR},
  {"html", 4, EMIT_HTML, CMDS_NORMAL | SWITCH_MULTIPLE},
  {(char *)NULL, 0, 0, 0}
};

static SWITCH pemit_switches[] =
{
  {"html", 4, PEMIT_HTML, CMDS_NORMAL | SWITCH_MULTIPLE},
  {(char *)NULL, 0, 0, 0}
};

static SWITCH edit_switches[] =
{
  {"all", 1, EDIT_ALL, CMDS_NORMAL | SWITCH_MULTIPLE},
  {"no_space", 2, EDIT_NOSPACE, CMDS_NORMAL | SWITCH_MULTIPLE},
  {(char *)NULL, 0, 0, 0}
};

static SWITCH lockout_switches[] =
{
  {"all", 3, LOCKOUT_ALL, CMDS_WIZARD | SWITCH_SINGULAR},
  {"allow", 5, LOCKOUT_ALLOW, CMDS_WIZARD | SWITCH_SINGULAR},
  {"clear", 5, LOCKOUT_CLEAR, CMDS_WIZARD | SWITCH_SINGULAR},
  {"enable", 2, LOCKOUT_ENABLE, CMDS_WIZARD | SWITCH_SINGULAR},
  {"dump", 3, LOCKOUT_DUMP, CMDS_WIZARD | SWITCH_SINGULAR},
  {"register", 2, LOCKOUT_REGISTER, CMDS_WIZARD | SWITCH_SINGULAR},
  {(char *)NULL, 0, 0, 0}
};

static SWITCH copy_switches[] =
{
  {"move", 1, COPY_MOVE, CMDS_NORMAL | SWITCH_SINGULAR},
  {(char *)NULL, 0, 0, 0}
};

static SWITCH savestty_switches[] =
{
  {"clear", 1, SAVESTTY_CLEAR, CMDS_NORMAL | SWITCH_SINGULAR},
  {"restore", 1, SAVESTTY_RESTORE, CMDS_NORMAL | SWITCH_SINGULAR},
  {(char *)NULL, 0, 0, 0}
};

static SWITCH help_switches[] =
{
  {"reload", 1, HELP_RELOAD, CMDS_WIZARD | SWITCH_SINGULAR},
  {"nohtml", 3, HELP_NOHTML, CMDS_NORMAL | SWITCH_MULTIPLE},
  {(char *)NULL, 0, 0, 0}
};

static SWITCH news_switches[] =
{
  {"reload", 1, NEWS_RELOAD, CMDS_WIZARD | SWITCH_SINGULAR},
  {"nohtml", 3, NEWS_NOHTML, CMDS_NORMAL | SWITCH_MULTIPLE},
  {(char *)NULL, 0, 0, 0}
};

/* static commands table. */
static CMDS cmddefs[] =
{
 /* drop */
  {"drop", do_drop, NULL, 1, CMDS_NORMAL|CMDS_NOSED},
 /* enter */
  {"enter", do_enter, NULL, 1, CMDS_NORMAL|CMDS_NOSED},
 /* examine */
  {"examine", do_examine, ex_switches, 2, CMDS_NORMAL},
 /* give */
  {"give", do_give, NULL, 2, CMDS_NGUEST|CMDS_NOSED},
 /* go */
  {"go", do_go, NULL, 1, CMDS_NORMAL|CMDS_NOSED},
 /* gripe */
  {"gripe", do_gripe, NULL, 1, CMDS_NORMAL},
 /* hand */
  {"hand", do_hand, NULL, 2, CMDS_NORMAL},
 /* help */
  {"help", do_help, help_switches, 1, CMDS_NORMAL},
 /* home */
  {"home", do_home, NULL, 0, CMDS_NORMAL|CMDS_NOSED},
 /* inventory */
  {"inventory", do_inventory, NULL, 0, CMDS_NORMAL},
 /* kill */
  {"kill", do_kill, kill_switches, 2, CMDS_NGUEST},
 /* leave */
  {"leave", do_leave, NULL, 0, CMDS_NORMAL|CMDS_NOSED},
 /* look */
  {"look", do_look, NULL, 1, CMDS_NORMAL},
 /* news */
  {"news", do_news, news_switches, 1, CMDS_NORMAL},
 /* page */
  {"page", do_page, NULL, 2, CMDS_NORMAL|CMDS_SPEECH},
 /* pose */
  {"pose", do_pose, pose_switches, 1, CMDS_NORMAL|CMDS_SPEECH},
 /* say */
  {"say", do_say, NULL, 1, CMDS_NORMAL|CMDS_SPEECH},
 /* score */
  {"score", do_score, NULL, 0, CMDS_NORMAL},
 /* take */
  {"take", do_take, NULL, 1, CMDS_NORMAL|CMDS_NOSED},
 /* use */
  {"use", do_use, NULL, 1, CMDS_NORMAL},
 /* whisper */
  {"whisper", do_whisper, NULL, 2, CMDS_NORMAL|CMDS_SPEECH},
 /* @attach */
  {"@attach", do_attach, NULL, 2, CMDS_NGUEST},
 /* @boot */
  {"@boot", do_boot, boot_switches, 2, CMDS_WIZARD|CMDS_NGUEST},
 /* @charge */
  {"@charge", do_charge, NULL, 2, CMDS_NGUEST},
 /* @case */
  {"@case", do_case, case_switches, CMDS_VARARGS, CMDS_NORMAL},
 /* @children */
  {"@children", do_children, NULL, 2, CMDS_NGUEST},
 /* @chownall */
  {"@chownall", do_chownall, NULL, 2, CMDS_WIZARD|CMDS_NGUEST},
 /* @chown */
  {"@chown", do_chown, NULL, 2, CMDS_NGUEST},
 /* @clone */
  {"@clone", do_clone, NULL, 2, CMDS_BUILDER|CMDS_NGUEST|CMDS_NOSED},
 /* @config */
  {"@config", do_config, config_switches, 2, CMDS_NORMAL},
 /* @copy */
  {"@copy", do_copy, copy_switches, 2, CMDS_NGUEST},
 /* @cost */
  {"@cost", do_cost, NULL, 2, CMDS_NGUEST},
 /* @create */
  {"@create", do_create, NULL, 1, CMDS_BUILDER|CMDS_NGUEST|CMDS_NOSED},
 /* @dig */
  {"@dig", do_dig, NULL, 2, CMDS_BUILDER|CMDS_NGUEST|CMDS_NOSED},
 /* @doing */
  {"@doing", do_doing, NULL, 2, CMDS_PLAYER|CMDS_SPEECH},
 /* @dump */
  {"@dump", do_dump, NULL, 0, CMDS_WIZARD},
 /* @edit */
  {"@edit", do_edit, edit_switches, CMDS_VARARGS,
   CMDS_NGUEST|CMDS_VARSLASH|CMDS_NOEXEC},
 /* @emit */
  {"@emit", do_emit, emit_switches, 1, CMDS_NORMAL|CMDS_SPEECH},
 /* @entrances */
  {"@entrances", do_entrances, NULL, 1, CMDS_NGUEST},
 /* @foreach */
  {"@foreach", do_foreach, NULL, 2, CMDS_NGUEST|CMDS_NOEXEC},
 /* @force */
  {"@force", do_force, NULL, 2, CMDS_NGUEST},
 /* @find */
  {"@find", do_find, find_switches, 2, CMDS_NGUEST},
 /* @group */
  {"@group", do_group, group_switches, 1, CMDS_NORMAL},
 /* @halt */
  {"@halt", do_halt, halt_switches, 1, CMDS_NGUEST},
 /* @lock */
  {"@lock", do_lock, NULL, 2, CMDS_NGUEST},
 /* @lockout */
  {"@lockout", do_lockout, lockout_switches, 2, CMDS_WIZARD},
 /* @link */
  {"@link", do_link, link_switches, 2, CMDS_NGUEST},
 /* @motd */
  {"@motd", do_motd, motd_switches, 2, CMDS_NORMAL},
 /* @name */
  {"@name", do_name, NULL, 2, CMDS_NGUEST|CMDS_NOEXEC},
 /* @newpassword */
  {"@newpassword", do_newpassword, NULL, 2,
		CMDS_WIZARD|CMDS_NGUEST|CMDS_NOEXEC},
 /* @notify */
  {"@notify", do_notify, notify_switches, 2, CMDS_NGUEST},
 /* @open */
  {"@open", do_open, NULL, 2, CMDS_BUILDER|CMDS_NGUEST|CMDS_NOSED},
 /* @owned */
  {"@owned", do_owned, NULL, 2, CMDS_WIZARD|CMDS_NGUEST},
 /* @palias */
  {"@palias", do_palias, NULL, 2, CMDS_NGUEST},
 /* @parent */
  {"@parent", do_parent, NULL, 2, CMDS_BUILDER|CMDS_NGUEST},
 /* @password */
  {"@password", do_password, NULL, 2, CMDS_NGUEST|CMDS_NOEXEC},
 /* @pemit */
  {"@pemit", do_pemit, pemit_switches, 2, CMDS_NORMAL|CMDS_SPEECH},
 /* @pcreate */
  {"@pcreate", do_pcreate, NULL, 2, CMDS_GOD|CMDS_NGUEST|CMDS_NOSED},
 /* @poll */
  {"@poll", do_poll, NULL, 1, CMDS_WIZARD|CMDS_NGUEST},
 /* @poor */
  {"@poor", do_poor, NULL, 1, CMDS_GOD|CMDS_NGUEST},
 /* @ps */
  {"@ps", do_ps, ps_switches, 1, CMDS_NGUEST},
 /* @purge */
  {"@purge", do_purge, NULL, 1, CMDS_WIZARD | CMDS_NGUEST},
 /* @quota */
  {"@quota", do_quota, NULL, 2, CMDS_NGUEST},
 /* @recycle */
  {"@recycle", do_recycle, recycle_switches, 1, CMDS_NGUEST},
 /* @savestty */
  {"@savestty", do_savestty, savestty_switches, 1, CMDS_NGUEST},
 /* @semaphore */
  {"@semaphore", do_semaphore, NULL, 2, CMDS_NGUEST|CMDS_NOEXEC},
 /* @set */
  {"@set", do_set, NULL, 2, CMDS_NGUEST|CMDS_NOEXEC},
 /* @shutdown */
  {"@shutdown", do_shutdown, NULL, 0, CMDS_WIZARD|CMDS_NGUEST},
 /* @stats */
  {"@stats", do_stats, stats_switches, 1, CMDS_NGUEST},
 /* @sweep */
  {"@sweep", do_sweep, NULL, 1, CMDS_NORMAL},
 /* @systat */
  {"@systat", do_systat, NULL, 0, CMDS_WIZARD|CMDS_NGUEST},
 /* @teleport */
  {"@teleport", do_teleport, NULL, 2, CMDS_NGUEST},
 /* @toad */
  {"@toad", do_toad, NULL, 2, CMDS_WIZARD|CMDS_NGUEST},
 /* @trace */
  {"@trace", do_trace, NULL, 2, CMDS_NGUEST},
 /* @trigger */
  {"@trigger", do_trigger, NULL, CMDS_VARARGS, CMDS_NGUEST|CMDS_VARSLASH},
 /* @unlink */
  {"@unlink", do_unlink, NULL, 1, CMDS_NGUEST},
 /* @unlock */
  {"@unlock", do_unlock, NULL, 1, CMDS_NGUEST},
 /* @version */
  {"@version", do_version, version_switches, 0, CMDS_NORMAL},
 /* @wait */
  {"@wait", do_wait, NULL, 2, CMDS_NGUEST|CMDS_NOEXEC},
 /* @wall */
  {"@wall", do_wall, wall_switches, 2, CMDS_WIZARD|CMDS_NGUEST},
 /* @wipe */
  {"@wipe", do_wipe, NULL, CMDS_VARARGS, CMDS_NGUEST|CMDS_VARSLASH|CMDS_NOEXEC},
  {NULL, NULL, NULL, 0, 0}
};

static int CmdHash_Inited = 0;
static Hash_Table CmdHash;

static int AliasSw_Inited = 0;
static Hash_Table AliasSw;

static int fexec = 0;

static void cmdhash_init _ANSI_ARGS_((void));
static int cmdaccess _ANSI_ARGS_((int, int));
static void notify_huh _ANSI_ARGS_((int, int));
static void notify_halt _ANSI_ARGS_((int, int));
static void parse_argtwo _ANSI_ARGS_((int, int, char *, char **, char **, int,
				      int, char *[]));
static void parse_argvee _ANSI_ARGS_((int, int, char *, int *, char *[], int,
				      int, char *[]));
static int handle_exit_cmd _ANSI_ARGS_((int, int, char *));
static int parse_switches _ANSI_ARGS_((int, int, CMDS *, char *));

int command_alias(cmd, alias, switches)
    char *cmd, *alias, *switches;
{
  Hash_Entry *find, *entry;
  int new;

  if((cmd == (char *)NULL) || (*cmd == '\0')
     || (alias == (char *)NULL) || (*alias == '\0'))
    return(-1);

  if(!CmdHash_Inited)
    cmdhash_init();

  if((find = Hash_FindEntry(&CmdHash, cmd)) == NULL)
    return(-1);
  if((entry = Hash_FindEntry(&CmdHash, alias)) == NULL) {
    entry = Hash_CreateEntry(&CmdHash, alias, (int *)NULL);
    Hash_SetValue(entry, Hash_GetValue(find));

    if(switches != (char *)NULL) {
      if(!AliasSw_Inited) {
        Hash_InitTable(&AliasSw, 20, HASH_STRCASE_KEYS);
	AliasSw_Inited++;
      }

      entry = Hash_CreateEntry(&AliasSw, alias, &new);
      if(!new)
        ty_free((VOID *)Hash_GetValue(entry));
      Hash_SetValue(entry, (int *)ty_strdup(switches, "command_alias"));
    }
  } else
    return(-1);
  return(0);
}

int command_unalias(cmd)
    char *cmd;
{
  Hash_Entry *entry;

  if((cmd == (char *)NULL) || (*cmd == '\0'))
    return(-1);

  if(!CmdHash_Inited)
    cmdhash_init();

  if((entry = Hash_FindEntry(&CmdHash, cmd)) == NULL)
    return(-1);
  Hash_DeleteEntry(&CmdHash, entry);

  if(AliasSw_Inited) {
    entry = Hash_FindEntry(&AliasSw, cmd);
    if(entry != NULL) {
      ty_free((VOID *)Hash_GetValue(entry));
      Hash_DeleteEntry(&AliasSw, entry);
    }
  }
  return(0);
}

int command_aliasexp(cmd, buf, bufsiz)
    char *cmd, *buf;
    int bufsiz;
{
  register Hash_Entry *centry, *sentry;
  register char *bptr, *iptr;

  if((cmd == (char *)NULL) || (cmd[0] == '\0') || !CmdHash_Inited)
    return(-1);

  if((centry = Hash_FindEntry(&CmdHash, cmd)) == (Hash_Entry *)NULL)
    return(-1);
  if(AliasSw_Inited) {
    sentry = Hash_FindEntry(&AliasSw, cmd);
  } else
    sentry = (Hash_Entry *)NULL;

  /* Copy it in. */
  bptr = buf;
  iptr = (char *)Hash_GetValue(centry);
  while(((bptr - buf) < (bufsiz - 1)) && *iptr)
    *bptr++ = *iptr++;

  if((bptr - buf) < (bufsiz - 1))
    *bptr++ = '/';

  if(sentry != (Hash_Entry *)NULL) {
    iptr = (char *)Hash_GetValue(sentry);
    while(((bptr - buf) < (bufsiz - 1)) && *iptr)
      *bptr++ = *iptr++;
  }
  *bptr = '\0';

  return(0);
}

static void cmdhash_init()
{
  register CMDS *cptr;
  register Hash_Entry *entry;

  Hash_InitTable(&CmdHash, sizeof(cmddefs) / sizeof(CMDS), HASH_STRCASE_KEYS);
  for(cptr = cmddefs; cptr->name != NULL; cptr++){
    entry = Hash_CreateEntry(&CmdHash, cptr->name, NULL);
    Hash_SetValue(entry, (int *)cptr);
  }
  CmdHash_Inited = 1;
}

static int cmdaccess(player, perms)
    int player, perms;
{
  if ((perms & CMDS_NGUEST) && isGUEST(player))
    return (0);
  if ((perms & CMDS_PLAYER) && !isPLAYER(player))
    return (0);
  if ((perms & CMDS_NOSED) && (isROOM(player) || isEXIT(player)))
    return (0);

  switch (perms & CMDS_MASK) {
  case CMDS_NORMAL:
    return (1);
  case CMDS_GOD:
    return (isGOD(player));
  case CMDS_WIZARD:
    return (isWIZARD(player));
  case CMDS_BUILDER:
    return (isBUILDER(player) || isWIZARD(player));
  }
  return (0);
}

static void notify_huh(player, cause)
    int player, cause;
{
  notify_player(player, cause, player, "Huh?  (Type \"help\" for help.)", 0);
}

static void notify_halt(player, cause)
    int player, cause;
{
  char buff[BUFFSIZ];
  int owner;

  if(get_int_elt(player, OWNER, &owner) != -1) {
    snprintf(buff, sizeof(buff),
	     "Attempt to execute command by halted object #%d.", player);
    notify_player(owner, cause, player, buff, 0);
  }
}

#define noexec(_f, _p)	((_f & CMDS_NOEXEC) \
			 || ((_f & CMDS_SPEECH) && isELOQUENT(_p)))

static void parse_argtwo(player, cause, str, argone, argtwo, flags,
			 eargc, eargv)
    int player, cause;
    char *str, **argone, **argtwo;
    int flags, eargc;
    char *eargv[];
{
  register char *ptr1, *ptr2;
  register int len;

  if(!*str) {
    *argone = (char *)NULL;
    *argtwo = (char *)NULL;
    return;
  }

  /* find argtwo, if there is one. */
  ptr1 = parse_to(str, '\0', '=');

  /* eat trailing whitespace of argone, if there is one. */
  if(*str) {
    for(ptr2 = &str[strlen(str)]; (ptr2 > str) &&
  	((*ptr2 == '\0') || isspace(*ptr2)); ptr2--);
    ptr2[1] = '\0';
  }

  /* set up return values. */
  if(!fexec && noexec(flags, player)) {
    /* there isn't always an argone. */
    if(*str) {
      len = strlen(str)+1;
      *argone = (char *)ty_malloc(len, "parse_argtwo.argone");
      parse_copy(*argone, str, len);
    } else
      *argone = (char *)NULL;

    if(ptr1) {	/* there is an argtwo. */
      /* eat leading whitespace of argtwo. */
      while(*ptr1 && isspace(*ptr1))
        ptr1++;
      /* if we ran out, we loose. */
      if(ptr1) {
        len = strlen(ptr1)+1;
        *argtwo = (char *)ty_malloc(len, "parse_argtwo.argtwo");
        parse_copy(*argtwo, ptr1, len);
      } else
        *argtwo = NULL;
    } else
      *argtwo = NULL;
  } else {	/* exec stuff. */
    /* there isn't always an argone. */
    if(*str) {
      *argone = exec(player, cause, player, str, 0, eargc, eargv);
    } else {
      *argone = (char *)NULL;
    }

    /* eat leading whitespace of argtwo. */
    while(ptr1 && *ptr1 && isspace(*ptr1))
      ptr1++;

    if(ptr1) {	/* there is an argtwo. */
      *argtwo = exec(player, cause, player, ptr1, 0, eargc, eargv);
    } else {
      *argtwo = NULL;
    }
  }
}

static void parse_argvee(player, cause, str, argc, argv, flags,
			 eargc, eargv)
    int player, cause;
    register char *str;
    int *argc;
    char *argv[];
    int flags, eargc;
    char *eargv[];
{
  register char *ptr1, *ptr2;
  register int count = 0, len;
  int fixhack = 0;

  *argc = 0;
  if(*str) {
    /* first argument: up to first '=', or '/'. */
    if((flags & CMDS_VARSLASH) && (ptr1 = parse_to(str, '\0', '/'))) {
      if(*str) {
        for(ptr2 = &str[strlen(str)]; (ptr2 > str) &&
      	  ((*ptr2 == '\0') || isspace(*ptr2)); ptr2--);
        ptr2[1] = '\0';
      }

      if(!fexec && noexec(flags, player)) {
        len = strlen(str)+1;
	argv[count] = (char *)ty_malloc(len, "parse_argvee.argv");
	parse_copy(argv[count++], str, len);
      } else
	argv[count++] = exec(player, cause, player, str, 0, eargc, eargv);
      str = ptr1;
    } else if((flags & CMDS_VARSLASH) && (ptr1 == NULL))
      fixhack++;

    ptr1 = parse_to(str, '\0', '=');
    if(*str) {
      for(ptr2 = &str[strlen(str)]; (ptr2 > str) &&
    	  ((*ptr2 == '\0') || isspace(*ptr2)); ptr2--);
      ptr2[1] = '\0';
    }

    if(!fexec && noexec(flags, player)) {
      len = strlen(str)+1;
      argv[count] = (char *)ty_malloc(len, "parse_argvee.argv");
      parse_copy(argv[count++], str, len);
    } else {
      argv[count++] = exec(player, cause, player, str, 0, eargc, eargv);
    }

    /* eat any leading whitespace. */
    while(ptr1 && *ptr1 && isspace(*ptr1))
      ptr1++;

    if(ptr1 && *ptr1) {
      /* skip one, if need be. */
      count += fixhack;

      /* now parse up to ','. */
      while(count < MAXARGS){
        for(str = ptr1; *str && isspace(*str); str++);
        if(!*str)
          break;	/* we're done. */

        ptr1 = parse_to(str, '\0', ',');
	if(*str) {
          for(ptr2 = &str[strlen(str)]; (ptr2 > str) &&
      	      ((*ptr2 == '\0') || isspace(*ptr2)); ptr2--);
          ptr2[1] = '\0';   
	}

        if(!fexec && noexec(flags, player)) {
          len = strlen(str)+1;
          argv[count] = (char *)ty_malloc(len, "parse_argvee.argv");
	  parse_copy(argv[count++], str, len);
        } else {
          argv[count++] = exec(player, cause, player, str, 0, eargc, eargv);
        }

        if(!ptr1 || !*ptr1)
          break;	/* we're done. */
      }
    }
  }
  *argc = count;
}

static int handle_exit_cmd(player, cause, str)
    int player, cause;
    char *str;
{
  int here, theex;

  if((get_int_elt(player, LOC, &here) == -1) || !exists_object(here)) {
    logfile(LOG_ERROR, "handle_exit_cmd: bad location of player #%d\n",
	    player);
    return(0);
  }
  theex = match_exit_command(player, cause, str);
  if(theex != -1) {
    do_go_attempt(player, cause, here, theex);
    return(1);
  }
  return(0);
}

/*
 * Switch parser, broken out for versatility's sake.
 *
 * This way, everything sees at least global switches.
 */
static int parse_switches(player, cause, cptr, swptr)
    int player, cause;
    CMDS *cptr;
    char *swptr;
{
  register int overtotal = 0;	/* overall total. */
  register int intotal = 0;	/* inner total. */
  register int doglob = 0;	/* doing globals. */
  register SWITCH *sptr;
  int switches;

  switches = 0;
  if (swptr != NULL) {
    if((cptr != NULL) && (cptr->switches != NULL))
      sptr = cptr->switches;
    else {
      doglob++;
      sptr = globalcmd_switches;
    }

    while (*swptr) {
      while(*swptr && (sptr->name != NULL)) {
	if (!strncasecmp(sptr->name, swptr, sptr->matlen)) {
	  if (!cmdaccess(player, sptr->flags)) {
	    notify_player(player, cause, player, "Permission denied.",
			  NOT_QUIET);
	    return(-1);
	  }
	  if (overtotal && (sptr->flags & SWITCH_SINGULAR)) {
	    notify_player(player, cause, player,
			  "Illegal combination of command switches.",
			  NOT_QUIET);
	    return(-1);
	  } else {
	    switches |= sptr->code;
	    if (sptr->flags & SWITCH_SINGULAR) {
	      overtotal++;
	    }
	  }
	  intotal++;
	}
	sptr++;
      }

      /*
       * If we've not checked globals yet, do it now.
       */
      if (!doglob) {
	doglob++;

	sptr = globalcmd_switches;
	continue;  /* Go back to the top of the while. */
      } else
	doglob = 0;

      if (!intotal) {
	notify_player(player, cause, player, "Illegal command switch.",
		      NOT_QUIET);
	return(-1);
      }
      for (; *swptr && (*swptr != '/'); swptr++);
      if (*swptr)
	swptr++;
    }
    if (!switches) {
      notify_player(player, cause, player, "Illegal switches.", NOT_QUIET);
      return(-1);
    }
  }
  return(switches);
}

/* main command parser. */
void handle_cmd(player, cause, origcmd, eargc, eargv)
    int player, cause;
    char *origcmd;
    int eargc;
    char *eargv[];
{
  char *cmd, *argv[MAXARGS], *swptr, *argone, *argtwo;
  register char *ptr = (char *)NULL;
  register int i;
  register Hash_Entry *entry;
  CMDS *cptr;
  int argc = 0, switches = 0;
  char *name;

  if (mudconf.logcommands) {
    if(get_str_elt(player, NAME, &name) == -1)
      name = "???";	/* XXX */
    logfile(LOG_COMMAND, "COMMAND: %s(#%d): %s\n", name, player, origcmd);
  }

  fexec = 0;		/* Reset forced mode. */

  /* Kill any leading and trailing whitespace. */
  if((origcmd != (char *)NULL) && (origcmd[0] != '\0')) {
    while(*origcmd && isspace(*origcmd))
      origcmd++;
    if(*origcmd) {
      ptr = &origcmd[strlen(origcmd)-1];
      while(*ptr && isspace(*ptr) && (ptr >= origcmd))
	ptr--;
      ptr[1] = '\0';
      /* Leave ptr set for the exec check! */
    }

    /* Check if we want forced execution.  Use ptr from above. */
    if((origcmd[0] == '<') && (ptr != (char *)NULL) && (*ptr == '>')) {
      fexec++;

      origcmd++;	/* Skip past the leading token... */
      *ptr = '\0';	/* ...And eat the trailing one. */
    }
  }

  /* Check if we want forced execution.  Use ptr from above. */

  /* Perform sanity checks. */
  if((origcmd == (char *)NULL) || (origcmd[0] == '\0'))
    return;
  if(!exists_object(player) || !exists_object(cause)) {
    logfile(LOG_ERROR,
	    "handle_cmd: called with bad player (#%d) or cause (#%d)\n",
    	    player, cause);
    return;
  }

  /* Are they halted? */
  if(!isPLAYER(player) && isHALT(player)) {
    notify_halt(player, cause);
    return;
  }

  /* init the hash table, if need be. */
  if(!CmdHash_Inited)
    cmdhash_init();

  /* check these first. */
  if(origcmd[0] == '\"') {
    if(origcmd[1] && !fexec && !noexec(CMDS_SPEECH, player)) {
      ptr = exec(player, cause, player, &origcmd[1], 0, eargc, eargv);
      do_say(player, cause, switches, ptr);
      ty_free((VOID *)ptr);
    } else
      do_say(player, cause, switches, &origcmd[1]);
    return;
  } else if(origcmd[0] == ':') {
    if(origcmd[1] && !fexec && !noexec(CMDS_SPEECH, player)) {
      ptr = exec(player, cause, player, &origcmd[1], 0, eargc, eargv);
      do_pose(player, cause, switches, ptr);
      ty_free((VOID *)ptr);
    } else
      do_pose(player, cause, switches, &origcmd[1]);
    return;
  } else if((origcmd[0] == '@') && (origcmd[1] == '@')) {
    return;
  }

  /* lock the cache. */
  cache_lock();

  /* is this an exit? */
  if((origcmd[0] != '@') && (origcmd[0] != '&')) {
    if(handle_exit_cmd(player, cause, origcmd)) {
      if(mudstat.status != MUD_DUMPING)
        cache_unlock();
      return;
    }
  }

  /* save ourselves. */
  cmd = (char *)alloca(strlen(origcmd)+1);
  if(cmd == (char *)NULL)
    panic("handle_cmd(): stack allocation failed.\n");
  strcpy(cmd, origcmd);

  /* init argument array, for later. */
  for(i = 0; i < MAXARGS; i++)
    argv[i] = NULL;

  /* argument 0 is the first word, up to whitespace *or* slash. */
  for(ptr = cmd; *ptr && !isspace(*ptr) && (*ptr != '/'); ptr++);
  if(*ptr == '/') {
    /* parse switch, up to next whitespace. */
    for(*ptr++ = '\0', swptr = ptr; *ptr && !isspace(*ptr); ptr++);
  } else
    swptr = NULL;	/* no switches. */
  if(*ptr) {
    /* skip next whitespace. */
    for(*ptr++ = '\0'; *ptr && isspace(*ptr); ptr++);
  }

  /* check for aliased switches. */
  if(AliasSw_Inited) {
    entry = Hash_FindEntry(&AliasSw, cmd);
    if(entry != NULL){
      register char *tptr, *aptr;

      aptr = (char *)Hash_GetValue(entry);
      if(swptr != NULL) {
        tptr = (char *)alloca(strlen(aptr) + strlen(swptr) + 2);
        if(tptr == (char *)NULL)
          panic("handle_cmd: stack allocation failed.\n");
        strcpy(tptr, aptr);
	strcat(tptr, "/");
        strcat(tptr, swptr);
      } else {
        tptr = (char *)alloca(strlen(aptr) + 1);
        if(tptr == (char *)NULL)
          panic("handle_cmd: stack allocation failed.\n");
        strcpy(tptr, (char *)Hash_GetValue(entry));
      }
      swptr = tptr;
    }
  }

  /* try to find the command. */
  entry = Hash_FindEntry(&CmdHash, cmd);
  if(entry != NULL){
    cptr = (CMDS *)Hash_GetValue(entry);
    /* check access. */
    if(!cmdaccess(player, cptr->perms)) {
      notify_player(player, cause, player, "Permission denied.", 0);
      if(mudstat.status != MUD_DUMPING)
        cache_unlock();
      return;
    }

    /* parse switches. */
    switches = parse_switches(player, cause, cptr, swptr);
    if(switches == -1) {		/* abort */
      if (mudstat.status != MUD_DUMPING)
	cache_unlock();
      return;
    }

    /* Prevent excessive executing. */
    if(fexec)
      switches |= ARG_FEXEC;

    /* parse extra arguments if needed, and/or exec them, and run command. */
    switch(cptr->nargs){
    case 0:	/* simple case */
      (cptr->function) (player, cause, switches);
      break;
    case 1:	/* pretty simple case -- ptr is argument one. */
      if(*ptr && (fexec || !noexec(cptr->perms, player))) {
        register char *tptr = exec(player, cause, player, ptr, 0, eargc, eargv);
	(cptr->function) (player, cause, switches, tptr);
	ty_free((VOID *)tptr);
      } else
        (cptr->function) (player, cause, switches, ptr);
      break;
    case 2:	/* complex */
      /* ptr is at first argument, '=' seperates them. */
      parse_argtwo(player, cause, ptr, &argone, &argtwo, cptr->perms,
      		   eargc, eargv);
      (cptr->function) (player, cause, switches, argone, argtwo);
      ty_free((VOID *) argone);
      ty_free((VOID *) argtwo);
      break;
    case CMDS_VARARGS:	/* AUGH */
      /* ptr is at the start of the argument string. */
      parse_argvee(player, cause, ptr, &argc, argv, cptr->perms,
      		   eargc, eargv);
      (cptr->function) (player, cause, switches, argc, argv);
      free_argv(argc, argv);
      break;
    }
  } else {
    /* parse switches. */
    switches = parse_switches(player, cause, (CMDS *)NULL, swptr);
    if(switches == -1) {		/* abort */
      if (mudstat.status != MUD_DUMPING)
	cache_unlock();
      return;
    }

    switch(cmd[0]){
    case '@':		/* standard attribute, or exit. */
      for (i = 0; i < ATABLE_TOTAL; i++) {
        if (Atable[i].command && (stringprefix((Atable[i].calias) ?
                                               Atable[i].calias :
             Atable[i].name, cmd+1) || stringprefix(Atable[i].name, cmd+1)) &&
            cmd[1] && cmd[2] && cmd[2] != ' ') {
          parse_argtwo(player, cause, ptr, &argone, &argtwo, CMDS_NOEXEC,
	  	       eargc, eargv);
          do_set_string(player, cause, switches, argone, argtwo, &Atable[i]);
	  ty_free((VOID *)argone);
	  ty_free((VOID *)argtwo);
          if (mudstat.status != MUD_DUMPING)
            cache_unlock();
          cache_trim();
          return;
        }
      }

      /* check for an exit. */
      if(!handle_exit_cmd(player, cause, origcmd))
        notify_huh(player, cause);
      break;
    case '&':		/* attribute. */
      for (cmd++; *cmd && isspace(*cmd); cmd++);
      parse_argtwo(player, cause, ptr, &argone, &argtwo, CMDS_NOEXEC,
      		   eargc, eargv);
      i = resolve_object(player, cause, argone,
      			 (!(switches & CMD_QUIET) ? RSLV_EXITS|RSLV_NOISY
			 			  : RSLV_EXITS));
      if (i != -1) {
        if (cmd && *cmd) {
          set_attribute(player, cause, switches, i, cmd, argtwo);
        } else {
	  if(!(switches & CMD_QUIET))
            notify_player(player, cause, player, "Nothing to do.", NOT_QUIET);
        }
	ty_free((VOID *)argone);
	ty_free((VOID *)argtwo);
      }
      break;
    case '!':                   /* force */
      for (cmd++; *cmd && isspace(*cmd); cmd++);
      do_force(player, cause, switches, cmd, ptr);
      break;
    default:			/* try other things... */
      if(!attr_command(player, cause, '$', origcmd))
        notify_huh(player, cause);
      break;
    }
  }
  if(mudstat.status != MUD_DUMPING)
    cache_unlock();
}

/* do the command thing, recursively. */
void handle_cmds(player, cause, str, eargc, eargv)
    int player, cause;
    char *str;
    int eargc;
    char *eargv[];
{
  register char *ptr, *cmds;

  if((str == NULL) || (*str == '\0'))
    return;

  /* we don't want to write into str- it might be an attribute. */
  cmds = (char *)alloca(strlen(str)+1);
  if(cmds == (char *)NULL)
    panic("handle_cmds(): stack allocation failed\n");
  strcpy(cmds, str);

  /* commands are seperated by an unquoted semi-colon, or enclosed in braces. */
  /* we strip braces, but *don't* do anything with backslashes. */
  while(1) {
    while(*cmds && isspace(*cmds))
      cmds++;
    if(!*cmds)
      break;	/* we're done. */

    if(*cmds == '{') {
      cmds++;
      ptr = parse_to(cmds, '{', '}');
      if(ptr == NULL) {	/* fuck you, too. */
        handle_cmd(player, cause, &cmds[-1], eargc, eargv);
	return;		/* we're done. */
      }
      handle_cmd(player, cause, cmds, eargc, eargv);
      cmds = ptr;
    } else {
      ptr = parse_to(cmds, '\0', ';');
      handle_cmd(player, cause, cmds, eargc, eargv);
      if(ptr && *ptr)
        cmds = ptr;
      else
        break;		/* we're done. */
    }
  }
}