/* game.c */

#include "os.h"
#include "config.h"
#include "db.h"
#include "externs.h"
#include "interface.h"
#include "match.h"

#ifdef MEM_CHECK
#include "mem_check.h"
#endif

/* declarations */
char dumpfile[200];
static int epoch = 0;
int depth = 0;                  /* excessive recursion prevention */

static int list_check (dbref thing, dbref player, int type, int end, char *str);
static int alias_list_check (dbref thing, const char *command, char *type);
static int quick_alias_check (dbref loc, const char *command, char *type);
static void do_poor(dbref player, char *arg1);
static void do_version(dbref player);
static void do_dolist(dbref player, char *inlist, char *command, dbref cause);
static void do_config(dbref player);


/*
 * used to allocate storage for temporary stuff, cleared before command
 * execution
 */

void do_dump (dbref player)
{
  time_t tt;
  tt = time ((time_t *) 0);
  if (Wizard (player)) {
    notify (player, "Dumping...");
    fprintf (stderr, "** DUMP ** done by %s(#%d) at %s",
      db[player].name, player, ctime (&tt));
    fork_and_dump ();
  } else {
    notify (player, "Sorry, you are in a no dumping zone.");
  }
}


/* print out stuff into error file */
void report (void)
{
  fprintf (stderr, "****REPORT TRACE!****\n\tCommand:%s\tdepth:%d\n", ccom,
    depth);
  fflush (stderr);
  if ((cplr > 0) && (cplr <= db_top))
    fprintf (stderr, "\tPlayer #%d\n\tlocation #%d\n", cplr,
      db[cplr].location);
  fflush (stderr);
}

#ifdef DESTROY
static void do_purge (dbref player)
{
  if (Wizard (player)) {
    FIX;
    notify (player, "Purge complete.");
  } else
    notify (player, "Sorry, you are a mortal.");
}

void dest_info (dbref thing, dbref tt)
{
  if (thing == NOTHING && !Floating (tt)) {
    if (db[tt].name) {
      notify (db[tt].owner, tprintf ("You own a disconnected room, %s(#%d)",
          db[tt].name, tt));
    } else
      fprintf (stderr, "ERROR: no name for room\n");
    return;
  }
  switch (Typeof (thing)) {
  case TYPE_ROOM:              /* Tell all players room has gone away */
    notify_except (db[thing].contents, 0,
      "The floor disappears under your feet, you fall through NOTHINGness and then:");
    break;
  case TYPE_PLAYER:            /* Show them where they arrived */
    enter_room (thing, HOME);
    break;
  }
}
#endif /* DESTROY */

dbref speaker = NOTHING;

void notify (dbref player, const char *msg)
{
  ATTR *d;
  char tbuf1[BUFFER_LEN];

  if ((player < 0) || (player >= db_top))
    return;
  if (depth++ > 7) {
    depth--;
    return;
  }
  switch (Typeof (player)) {
  case TYPE_PLAYER:
    raw_notify (player, msg);
    break;
  case TYPE_THING:
    if (db[player].flags & THING_PUPPET &&
      (db[player].location != db[db[player].owner].location ||
        speaker == player)) {
      sprintf (tbuf1, "%s> %s", db[player].name, msg);
      raw_notify (db[player].owner, tbuf1);
    }
    d = atr_get (player, "LISTEN");
    if (d) {
      sprintf (tbuf1, "%s", uncompress (d->value));
      if (wild_match (tbuf1, (char*)msg)) {
        if (speaker != player)
          did_it (speaker, player, 0, NULL, 0, NULL, "AHEAR", NOTHING);
        else
          did_it (speaker, player, 0, NULL, 0, NULL, "AMHEAR", NOTHING);
        did_it (speaker, player, 0, NULL, 0, NULL, "AAHEAR", NOTHING);
        /* also pass the message on
         * Note: not telling player protects against two forms
         * of recursion:
         * player doesn't tell itself (as container) or as contents
         * using teleport it is possible to create a recursive loop
         * but this will be terminated when the depth variable exceeds 30
         */
        if (!member (speaker, db[player].contents)) {
          if ((Typeof (player) == TYPE_THING) &&
            (db[player].flags & THING_PUPPET)) {
            notify_except2 (db[player].contents, player,
              db[player].owner, msg);
          } else {
            notify_except (db[player].contents, player, msg);
          }
        }
      }
    } else {
      /* if there is no listen, check for ^ listen patterns */
      /* these are like AHEAR - object cannot trigger itself */
      if (speaker != player)
        atr_comm_match (player, player, '^', ':', (char*)msg);
      /* unlike normal @listen, don't pass the message on */
    }
    /* now check for multi listeners */
  }
  depth--;
}

void do_shutdown (dbref player)
{
  if (Wizard (player)) {
    raw_broadcast (0, "GAME: Shutdown by %s", db[player].name);
    fprintf (stderr, "SHUTDOWN: by %s\n", unparse_object (player, player));
    fflush (stderr);

/* This will create a file used to check if a restart should occur */
#ifdef AUTORESTART
    system ("touch NORESTART");
#endif

    shutdown_flag = 1;
  } else {
    notify (player, "Your delusions of grandeur have been duly noted.");
  }
}


static void dump_database_internal (void)
{
  char tmpfl[2048];
  FILE *f;

  sprintf (tmpfl, "%s.#%d#", dumpfile, epoch - 1);
#ifdef _MSC_VER
  remove (tmpfl);             /* nuke our predecessor */
#else
  unlink (tmpfl);             /* nuke our predecessor */
#endif

  sprintf (tmpfl, "%s.#%d#", dumpfile, epoch);
#ifdef DBCOMP
  if ((f = (FILE*)popen (tprintf ("gzip >%s", tmpfl), "w")) != NULL) {
    db_write (f);
    pclose (f);
#ifdef WIN32
#ifdef _MSC_VER
    remove (dumpfile);
#else
    unlink (dumpfile);
#endif
#endif
    if (rename (tmpfl, dumpfile) < 0)
      perror (tmpfl);
  } else
    perror (tmpfl);
#ifdef USE_MAILER
  sprintf (tmpfl, "maildb.Z.#%d#", epoch - 1);
#ifdef _MSC_VER
  remove (tmpfl);
#else
  unlink (tmpfl);
#endif
  sprintf (tmpfl, "maildb.Z.#%d#", epoch);
  if (mdb_top > 0)
    if ((f = (FILE*)popen (tprintf ("gzip >%s", tmpfl), "w")) != NULL) {
      dump_mail (f);
      pclose (f);
#ifdef WIN32
#ifdef _MSC_VER
      remove ("maildb.Z");
#else
      unlink ("maildb.Z");
#endif
#endif
      if (rename (tmpfl, "maildb.Z") < 0)
        perror (tmpfl);
    } else
      perror (tmpfl);
#endif
#else
  if ((f = fopen (tmpfl, "wb")) != NULL) {
    db_write (f);
    fclose (f);
#ifdef WIN32
#ifdef _MSC_VER
    remove (dumpfile);
#else
    unlink (dumpfile);
#endif
#endif
    if (rename (tmpfl, dumpfile) < 0)
      perror (tmpfl);
  } else
    perror (tmpfl);
#ifdef USE_MAILER
  sprintf (tmpfl, "maildb.#%d#", epoch - 1);
  unlink (tmpfl);
  sprintf (tmpfl, "maildb.#%d#", epoch);
  if ((f = fopen (tmpfl, "wb")) != NULL) {
    db_write (f);
    fclose (f);
#ifdef WIN32
#ifdef _MSC_VER
    remove ("maildb");
#else
    unlink ("maildb");
#endif
#endif
    if (rename (tmpfl, "maildb") < 0)
      perror (tmpfl);
  } else
    perror (tmpfl);
#endif
#endif
}

void panic (const char *message)
{
  char panicfile[2048];
  FILE *f;
  int i;
  fprintf (stderr, "PANIC: %s\n", message);
  report ();
  /* turn off signals */
  for (i = 0; i < NSIG; i++) {
    signal (i, SIG_IGN);
  }

  /* shut down interface */
  emergency_shutdown ();

  /* dump panic file */
  sprintf (panicfile, "%s.PANIC", dumpfile);
  if ((f = fopen (panicfile, "wb")) == NULL) {
    perror ("CANNOT OPEN PANIC FILE, YOU LOSE:");
    WIN32CLEANUP
    _exit (135);
  } else {
    fprintf (stderr, "DUMPING: %s\n", panicfile);
    db_write (f);
    fclose (f);
    fprintf (stderr, "DUMPING: %s (done)\n", panicfile);
    WIN32CLEANUP
    _exit (136);
  }
}

void dump_database (void)
{
  epoch++;

  fprintf (stderr, "DUMPING: %s.#%d#\n", dumpfile, epoch);
  dump_database_internal ();
  fprintf (stderr, "DUMPING: %s.#%d# (done)\n", dumpfile, epoch);
}

#ifndef WIN32
static int reaper (void)
{
  int stat;

  while (wait3 (&stat, WNOHANG, 0) > 0);
  return 0;
}
#endif

void fork_and_dump (void)
{
#ifndef WIN32
  int child;
#endif
  epoch++;

  fprintf (stderr, "CHECKPOINTING: %s.#%d#\n", dumpfile, epoch);
#ifndef WIN32
#ifndef NO_FORK
#ifdef USE_VFORK
  raw_broadcast (0, "GAME: Dumping. The game will freeze for a few minutes.");
  child = vfork ();
#else /* USE_VFORK */
  child = fork ();
#endif /* USE_VFORK */
#else /* NO FORK */
  raw_broadcast (0, "GAME: Dumping. The game will freeze for a few minutes.");
  child = 0;
#endif /* NO_FORK */
  if (child == 0) {
    /* in the child */
#endif // WIN32
    dump_database_internal ();
#ifndef WIN32
#ifndef NO_FORK
    _exit (0);                  /* !!! */
#endif /* NO_FORK */
  } else if (child < 0) {
    perror ("fork_and_dump: fork()");
  }
#endif // WIN32
}

static void do_restart (void)
{
  dbref thing;
  ATTR *s;
  for (thing = 0; thing < db_top; thing++)
    if (!(db[thing].flags & GOING) && (s = atr_get (thing, "STARTUP"))) {
      char *r = safe_uncompress (s->value);
      parse_que (thing, r, thing);
      free (r);
    }
}

int init_game (const char *infile, const char *outfile)
{
  FILE *f;
  int a;

  depth = 0;

  for (a = 0; a < 10; a++)
    wptr[a] = NULL;

#ifdef DBCOMP
  if ((f = (FILE*)popen (tprintf ("gzip -d < %s", infile), "r")) == NULL)
    return -1;
#else
  if ((f = fopen (infile, "rb")) == NULL)
    return -1;
#endif

  /* ok, read it in */
  fprintf (stderr, "LOADING: %s\n", infile);
  printf ("READING...\n");
  fflush (stdout);
  if (db_read (f) < 0) {
    fprintf (stderr, "ERROR LOADING\n");
    return -1;
  }
  printf ("READ\n");
  fflush (stdout);
  fprintf (stderr, "LOADING: %s (done)\n", infile);

  /* everything ok */
#ifdef DBCOMP
  pclose (f);
#else
  fclose (f);
#endif

#ifdef USE_MAILER
/* read mail database */
#ifdef DBCOMP
  if ((f = (FILE*)popen ("gzip -d <maildb.Z", "r")) == NULL)
    mail_init ();
#else
  if ((f = fopen ("maildb", "rb")) == NULL)
    mail_init ();
#endif

  /* okay, read it in */
  else {
    fprintf (stderr, "LOADING: maildb.Z\n");
    printf ("READING...\n");
    fflush (stdout);
    load_mail (f);
    printf ("READ\n");
    fflush (stdout);
    fprintf (stderr, "LOADING: maildb.Z (done)\n");
  }
  /* everything okay */
#ifdef DBCOMP
  pclose (f);
#else
  fclose (f);
#endif
#endif /* USE_MAILER */

#ifndef WIN32
  /* initialize random number generator */
  OS_SRAND (getpid ());
#endif

  /* set up dumper */
  strcpy (dumpfile, outfile);
  init_timer ();
#ifndef WIN32
  signal (SIGCHLD, (void *) reaper);
#endif
  /* everything else ok restart all robots */
  do_restart ();
  return 0;
}

/*
 * use this only in process_command
 */
#define Matched(string) { if(!string_prefix((string), command)) goto bad; }

/* the two versions of argument parsing */
static char *do_argtwo (dbref player, char *rest, dbref cause, char *buff)
{
  exec (&rest, buff, player, cause, 0);
  return (buff);
}

static char **do_argbee (dbref player, char *rest, dbref cause, char **arge,
  char *buff)
{
  int a;
  char *p;
  char tbuf1[BUFFER_LEN];

  for (a = 1; a < MAX_ARG; a++) {
    if (arge[a]) {
      free ((char *) arge[a]);
#ifdef MEM_CHECK
      del_check ("process_comm_args");
#endif
    }
    p = parse_up (&rest, ',');
    if (p) {
      strcpy ((arge[a] = (char *) malloc (strlen (p) + 1)), p);
#ifdef MEM_CHECK
      add_check ("process_comm_args");
#endif
    } else
      arge[a] = NULL;
  }
  /* rest of delimiters are ,'s */

  for (a = 1; a < MAX_ARG; a++)
    if (arge[a]) {
      strcpy (tbuf1, arge[a]);
      p = tbuf1;
      exec (&p, buff, player, cause, 0);
      free ((char *) arge[a]);
      strcpy ((arge[a] = (char *) malloc (strlen (buff) + 1)), buff);
    }
  return (arge);
}

#define arg2 do_argtwo(player,rest,cause,buff)
#define argv do_argbee(player,rest,cause,arge,buff)

void process_command (dbref player, char *command, dbref cause)
{
  char *arg1;
  int a;
  char *q;                      /* utility */
  char *p;                      /* utility */
  char *r;
  char buff[BUFFER_LEN], buff2[BUFFER_LEN], buff3[BUFFER_LEN];
  char *arge[MAX_ARG];          /* pointers to arguments (null for empty) */
  char unp[BUFFER_LEN];         /* unparsed command */
  char oldarg1[BUFFER_LEN];
  char *rest;
  /* general form command arg0=arg1,arg2...arg10 */
  int gagged = 0;
  char temp[BUFFER_LEN];        /* utility */
  int i;                        /* utility */

  for (a = 0; a < MAX_ARG; a++)
    arge[a] = NULL;

  depth = 0;
  if (command == 0) {
    fprintf (stderr, "ERROR: No command!!!");
    return;
  }
  /* just to have fun, let's reinit the random number gen (with the time) */
  OS_SRAND ((int) time ((time_t *) 0));

  if (God (player) && !God (cause))
    return;
  /* robustify player */
  if ((player < 0) || (player >= db_top)) {
    fprintf (stderr, "ERROR: bad player %d in process_command\n", player);
    return;
  }

  gagged = IS (db[player].owner, TYPE_PLAYER, PLAYER_GAGGED);
  /* Access the player */
  Access (player);

  if ((db[player].flags & GOING) ||
    ((Typeof (player) != TYPE_PLAYER) && (db[player].flags & HALT))) {
    notify (db[player].owner,
      tprintf ("Attempt to execute command by halted object #%d", player));
    return;
  }
  /* The following check is removed due to a security hole it causes! */
  /* if player is an exit or room execute command as owner */
  /* if ((Typeof(player) == TYPE_ROOM) || (Typeof(player) == TYPE_EXIT))
     player = db[player].owner;  */
  speaker = player;

#ifdef LOG_COMMANDS
  fprintf (stderr, "COMMAND from %s(%d) in %s(%d): %s\n",
    (player == NOTHING ? "NOTHING" : db[player].name), player,
    (db[player].location == NOTHING ? "NOTHING"
      : db[db[player].location].name), db[player].location, command);
#endif /* LOG_COMMANDS */

  /* log all commands from suspect players */
  if (Suspect (player)) {
    fprintf (stderr, "SUSPECT %s(#%d) in %s(#%d): %s\n",
      (player == NOTHING ? "NOTHING" : db[player].name), player,
      (db[player].location == NOTHING ? "NOTHING"
        : db[db[player].location].name), db[player].location, command);
  }

  /* eat leading whitespace */
  while (*command && isspace ((int)*command))
    command++;
  /* eat extra white space */
  q = p = command;
  while (*p) {
    /* scan over word */
    while (*p && !isspace ((int)*p))
      *q++ = *p++;
    /* smash spaces */
    while (*p && isspace ((int)*++p));
    if (*p)
      *q++ = ' ';               /* add a space to separate next word */
  }
  /* terminate */
  *q = '\0';

  /* important home checking comes first! */
  if (strcmp (command, "home") == 0) {
    if (Typeof (player) == TYPE_EXIT || Typeof (player) == TYPE_ROOM)
      return;
    do_move (player, command, 0);
    return;
  }
  if (!gagged && try_force (player, command))
    return;
  /* check for single-character commands */
  if (*command == SAY_TOKEN && !gagged) {
    do_say (player, command + 1, NULL);
  } else if (*command == POSE_TOKEN && !gagged) {
    do_pose (player, command + 1, NULL, 0);
  } else if (*command == SEMI_POSE_TOKEN && !gagged) {
    do_pose (player, command + 1, NULL, 1);
    /* now check if command is an exact match for an exit in the room */
  } else if (can_move (player, command)) {
    if (Typeof (player) == TYPE_ROOM || Typeof (player) == TYPE_EXIT)
      return;
    do_move (player, command, 0);
  } else {
    strcpy (unp, command);
    /* parse arguments */

    /* find arg1 */
    /* move over command word */
    for (arg1 = command; *arg1 && !isspace ((int)*arg1); arg1++);
    /* truncate command */
    if (*arg1)
      *arg1++ = '\0';

    /* move over spaces */
    while (*arg1 && isspace ((int)*arg1))
      arg1++;

    r = parse_up (&arg1, '=');  /* first delimiter is ='s */
    if (r) {
      if (arge[0]) {
        free ((char *) arge[0]);
#ifdef MEM_CHECK
        del_check ("process_comm_args");
#endif
      }
      strcpy ((arge[0] = (char *) malloc (strlen (r) + 1)), r);
#ifdef MEM_CHECK
      add_check ("process_comm_args");
#endif
    }
    rest = arg1;                /* either arg2 or argv */
    if (arge[0]) {
      /* Let's unfutz the news/help commands, shall we? */
      strcpy (oldarg1, arge[0]);
      strcpy (buff3, arge[0]);
      r = buff3;
      exec (&r, buff2, player, cause, 0);
      free ((char *) arge[0]);
      strcpy ((arge[0] = (char *) malloc (strlen (r) + 1)), r);
    } else {
      oldarg1[0] = '\0';
    }
    arg1 = ((char *) arge[0]) ? (char *) buff2 : (char *) "";
    if (!gagged && test_set (player, command, arg1, arg2)) {
      /* let's free up all that nice memory, shall we? */
      for (a = 0; a < MAX_ARG; a++) {
        if (arge[a]) {
          free ((char *) arge[a]);
#ifdef MEM_CHECK
          del_check ("process_comm_args");
#endif
        }
      }
      return;
    }
    switch (command[0]) {
    case '@':
      switch (command[1]) {
      case 'a':
      case 'A':
        if (!string_compare (command, "@allhalt")) {
          do_allhalt (player);
          break;
        }
        switch (command[2]) {
#ifdef QUOTA
        case 'l':
        case 'L':
          Matched ("@allquota");
          do_allquota (player, arg1);
          break;
#endif /* QUOTA */
        case 't':
        case 'T':
          if (string_prefix ("@atrlock", command)) {
            do_atrlock (player, arg1, arg2);
          } else {
            Matched ("@atrchown");
            do_atrchown (player, arg1, arg2);
          }
          break;
        default:
          goto bad;
        }
        break;
      case 'b':
      case 'B':
        Matched ("@boot");
        do_boot (player, arg1);
        break;
      case 'c':
      case 'C':
        /* chown, create */
        switch (command[2]) {
        case 'h':
        case 'H':
          switch (command[3]) {
          case 'o':
          case 'O':
            if (!string_compare (command, "@chownall")) {
              do_chownall (player, arg1, arg2);
              break;
            } else {
              if (gagged)
                break;
              Matched ("@chown");
              do_chown (player, arg1, arg2);
              break;
            }
          case 'z':
          case 'Z':
            if (!string_compare (command, "@chzoneall")) {
              do_chzoneall (player, arg1, arg2);
              break;
            } else {
              if (gagged)
                break;
              Matched ("@chzone");
              do_chzone (player, arg1, arg2);
              break;
            }
          }
          break;
        case 'o':
        case 'O':
          Matched ("@config");
          do_config (player);
          break;
        case 'r':
        case 'R':
          if (gagged)
            break;
          Matched ("@create");
          do_create (player, arg1, atol (arg2));
          break;
        case 'l':
        case 'L':
          if (gagged)
            break;
          Matched ("@clone");
          do_clone (player, arg1);
          break;
        default:
          goto bad;
        }
        break;
      case 'd':
      case 'D':
        /* dbck,  dig, or dump */
        switch (command[2]) {
#ifdef DESTROY
        case 'b':
        case 'B':
          Matched ("@dbck");
          do_dbck (player);
          break;
#endif
        case 'E':
        case 'e':
          switch (command[3]) {
          case 'c':
          case 'C':
            Matched ("@decompile");
            do_decompile (player, arg1);
            break;
#ifdef DESTROY
          case 's':
          case 'S':
            Matched ("@destroy");
            do_destroy (player, arg1, 0);
            break;
#endif /* DESTROY */
          default:
            goto bad;
          }
          break;
        case 'i':
        case 'I':
          if (gagged)
            break;
          Matched ("@dig");
          do_dig (player, arg1, argv);
          break;
        case 'o':
        case 'O':
          switch (command[3]) {
#ifdef AT_DOING
          case 'i':
          case 'I':
            if (gagged)
              break;
            if (Typeof (player) != TYPE_PLAYER)
              break;
            Matched ("@doing");
            do_doing (player, arg1, arg2);
            break;
#endif
          case 'l':
          case 'L':
            Matched ("@dolist");
            do_dolist (player, arg1, arg2, cause);
            break;
          }
          break;
        case 'u':
        case 'U':
          Matched ("@dump");
          do_dump (player);
          break;
        default:
          goto bad;
        }
        break;
      case 'E':
      case 'e':
        switch (command[2]) {
        case 'd':
        case 'D':
          if (gagged)
            break;
          Matched ("@edit");
          do_edit (player, arg1, argv);
          break;
        case 'l':
        case 'L':
          if (gagged)
            break;
          Matched ("@elock");
          do_lock (player, arg1, arg2, ENTERLOCK);
          break;
        case 'm':
        case 'M':
          if (gagged)
            break;
          Matched ("@emit");
          do_emit (player, arg1, arg2);
          break;
        case 'n':
        case 'N':
          if (gagged)
            break;
          Matched ("@entrances");
          do_entrances (player, arg1);
          break;
        case 'u':
        case 'U':
          if (gagged)
            break;
          Matched ("@eunlock");
          do_unlock (player, arg1, ENTERLOCK);
          break;
        default:
          goto bad;
        }
        break;
      case 'F':
      case 'f':
        /* find, or force */
        switch (command[2]) {
        case 'i':
        case 'I':
          if (gagged)
            break;
          Matched ("@find");
          do_find (player, arg1);
          break;
        case 'o':
        case 'O':
          if (gagged)
            break;
          Matched ("@force");
          do_force (player, arg1, arg2);
          break;
        default:
          goto bad;
        }
        break;
      case 'g':
      case 'G':
        if (gagged)
          break;
        Matched ("@gedit");
        do_gedit (player, arg1, argv);
        break;
      case 'h':
      case 'H':
        /* halt */
        Matched ("@halt");
        do_halt1 (player, arg1, arg2);
        break;
      case 'l':
      case 'L':
        /* lock or link */
        switch (command[2]) {
        case 'e':
        case 'E':
          if (gagged)
            break;
          Matched ("@lemit");
          do_lemit (player, arg1, arg2);
          break;
        case 'i':
        case 'I':
          if (gagged)
            break;
          if (string_prefix ("@link", command)) {
            do_link (player, arg1, arg2);
            break;
          } else {
            Matched ("@listmotd");
            do_motd (player, 3, "", "");
            break;
          }
        case 'o':
        case 'O':
          if (gagged)
            break;
          if (string_prefix ("@login", command)) {
            do_login (player, arg1);
            break;
          } else {
            Matched ("@lock");
            do_lock (player, arg1, arg2, BASICLOCK);
            break;
          }
        default:
          goto bad;
        }
        break;
      case 'm':
      case 'M':
        /* @mail, @motd */
        switch (command[2]) {
#ifdef USE_MAILER
        case 'a':
        case 'A':
          if (gagged)
            break;
          Matched ("@mail");
          do_mail (player, arg1, arg2);
          break;
#endif
        case 'o':
        case 'O':
          if (gagged)
            break;
          Matched ("@motd");
          do_motd (player, 1, arg1, arg2);
          break;
        default:
          goto bad;
        }
        break;
      case 'n':
      case 'N':
        /* @name, @newpassword */
        switch (command[2]) {
        case 'a':
        case 'A':
          if (gagged)
            break;
          Matched ("@name");
          do_name (player, arg1, arg2);
          break;
        case 'e':
        case 'E':
          if (strcmp (command, "@newpassword"))
            goto bad;
          do_newpassword (player, arg1, arg2);
          break;
#ifdef DESTROY
        case 'u':
          if (gagged)
            break;
          Matched ("@nuke");
          do_destroy (player, arg1, 1);
          break;
#endif
        default:
          goto bad;
        }
        break;
      case 'o':
      case 'O':
        /* @oemit, @open */
        switch (command[2]) {
        case 'e':
        case 'E':
          if (gagged)
            break;
          Matched ("@oemit");
          do_oemit (player, arg1, arg2);
          break;
        case 'p':
        case 'P':
          if (gagged)
            break;
          Matched ("@open");
          do_open (player, arg1, argv);
          break;
        default:
          goto bad;
        }
        break;
      case 'p':
      case 'P':
        switch (command[2]) {
        case 'a':
        case 'A':
          Matched ("@password");
          do_password (player, arg1, arg2);
          break;
#ifdef WCREAT
        case 'C':
        case 'c':
          Matched ("@pcreate");
          do_pcreate (player, arg1, arg2);
          break;
#endif
        case 'E':
        case 'e':
          if (gagged)
            break;
          Matched ("@pemit");
          do_pemit (player, arg1, arg2);
          break;
        case 'O':
        case 'o':
          switch (command[3]) {
          case 'l':
          case 'L':
            Matched ("@poll");
            do_poll (player, arg1, arg2);
            break;
          case 'o':
          case 'O':
            if (strcmp (command, "@poor"))
              goto bad;
            do_poor (player, arg1);
            break;
          default:
            goto bad;
          }
          break;
        case 'S':
        case 's':
          Matched ("@ps");
          do_queue (player, arg1);
          break;
#ifdef DESTROY
        case 'u':
        case 'U':
          Matched ("@purge");
          do_purge (player);
          break;
#endif /* DESTROY */
        default:
          goto bad;
        }
        break;
#ifdef QUOTA
      case 'q':
      case 'Q':
        Matched ("@quota");
        do_quota (player, arg1, "");
        break;
#endif /* QUOTA */
      case 'r':
      case 'R':
        switch (command[2]) {
        case 'e':
        case 'E':
          if (gagged)
            break;
          if (string_prefix ("@remit", command)) {
            do_remit (player, arg1, arg2);
          } else {
            Matched ("@rejectmotd");
            do_motd (player, 4, arg1, arg2);
          }
          break;
#ifdef ROYALTY_FLAG
        case 'w':
        case 'W':
          if (string_prefix ("@rwall", command)) {
            do_wizwall (player, arg1, arg2, 1, 1);
          } else if (string_prefix ("@rwallpose", command)) {
            do_wizwall (player, arg1, arg2, 1, 2);
          } else if (string_prefix ("@rwallemit", command)) {
            do_wizwall (player, arg1, arg2, 1, 3);
          } else
            goto bad;
          break;
#endif
        }
        break;
      case 's':
      case 'S':
        /* set, shutdown, success */
        switch (command[2]) {
        case 'e':
        case 'E':
          /* patched to add 'search' command */
          switch (command[3]) {
          case 'a':
          case 'A':
            Matched ("@search");
            do_search (player, arg1, arg2);
            break;
          case 't':
          case 'T':
            if (gagged)
              break;
            Matched ("@set");
            do_set (player, arg1, arg2);
            break;
          default:
            goto bad;
          }
          break;
        case 'h':
        case 'H':
          if (strcmp (command, "@shutdown"))
            goto bad;
          do_shutdown (player);
          break;
        case 't':
        case 'T':
          Matched ("@stats");
          do_stats (player, arg1);
          break;
        case 'w':
        case 'W':
          switch (command[3]) {
          case 'e':
          case 'E':
            Matched ("@sweep");
            do_sweep (player, arg1);
            break;
          case 'i':
          case 'I':
            if (gagged)
              break;
            Matched ("@switch");
            do_switch (player, arg1, argv, cause);
            break;
          default:
            goto bad;
          }
          break;
#ifdef QUOTA
        case 'q':
        case 'Q':
          Matched ("@squota");
          do_quota (player, arg1, arg2);
          break;
#endif /* QUOTA */
        default:
          goto bad;
        }
        break;
      case 't':
      case 'T':
        switch (command[2]) {
        case 'e':
        case 'E':
          if (gagged)
            break;
          Matched ("@teleport");
          do_teleport (player, arg1, arg2);
          break;
        case 'r':
        case 'R':
          if (gagged)
            break;
          Matched ("@trigger");
          do_trigger (player, arg1, argv);
          break;
        case 'O':
        case 'o':
          if (strcmp (command, "@toad"))
            goto bad;
          do_toad (player, arg1);
          break;
        default:
          goto bad;
        }
        break;
      case 'u':
      case 'U':
        switch (command[2]) {
        case 'l':
        case 'L':
          if (gagged)
            break;
          Matched ("@ulock");
          do_lock (player, arg1, arg2, USELOCK);
          break;
        case 'n':
        case 'N':
          switch (command[4]) {
          case 'i':
          case 'I':
            if (gagged)
              break;
            Matched ("@unlink");
            do_unlink (player, arg1);
            break;
          case 'o':
          case 'O':
            if (gagged)
              break;
            Matched ("@unlock");
            do_unlock (player, arg1, BASICLOCK);
            break;
          default:
            goto bad;
          }
          break;
        case 's':
        case 'S':
          if (gagged)
            break;
          Matched ("@use-does");
          sprintf (buff3, "%s/does", arg1);
          do_trigger (player, buff3, argv);
          break;
        case 'u':
        case 'U':
          if (gagged)
            break;
          Matched ("@uunlock");
          do_unlock (player, arg1, USELOCK);
          break;
        default:
          goto bad;
        }
        break;
      case 'v':
      case 'V':
        if (string_prefix ("@version", command)) {
          do_version (player);
        }
        break;
      case 'w':
      case 'W':
        switch (command[2]) {
        case 'a':
        case 'A':
          if (string_prefix ("@wait", command)) {
            wait_que (player, atoi (arg1), arg2, cause);
          } else if (string_prefix ("@wall", command)) {
            Matched ("@wall");
            do_wall (player, arg1, arg2, 1);
          } else if (string_prefix ("@wallpose", command)) {
            do_wall (player, arg1, arg2, 2);
          } else if (string_prefix ("@wallemit", command)) {
            do_wall (player, arg1, arg2, 3);
          } else
            goto bad;
          break;
#ifdef PLAYER_LOCATE
        case 'h':
        case 'H':
          if (gagged)
            break;
          Matched ("@whereis");
          do_whereis (player, arg1);
          break;
#endif /* PLAYER_LOCATE */
        case 'i':
        case 'I':
          if (string_prefix ("@wizwall", command)) {
            do_wizwall (player, arg1, arg2, 0, 1);
          } else if (string_prefix ("@wizmotd", command)) {
            do_motd (player, 2, arg1, arg2);
          } else if (string_prefix ("@wizpose", command)) {
            do_wizwall (player, arg1, arg2, 0, 2);
          } else if (string_prefix ("@wizemit", command)) {
            do_wizwall (player, arg1, arg2, 0, 3);
          } else
            goto bad;
          break;
        default:
          goto bad;
        }
        break;
      case 'z':
      case 'Z':
        if (gagged)
          break;
        Matched ("@zemit");
        do_zemit (player, arg1, arg2);
        break;
      default:
        goto bad;
      }
      break;
    case 'b':
    case 'B':
      Matched ("brief");
      do_examine (player, arg1, 1);
      break;
    case 'd':
    case 'D':
      if (Typeof (player) == TYPE_ROOM || Typeof (player) == TYPE_EXIT)
        break;
      Matched ("drop");
      do_drop (player, arg1);
      break;
    case 'e':
    case 'E':
      switch (command[1]) {
      case 'X':
      case 'x':
      case '\0':
        Matched ("examine");
        do_examine (player, arg1, 0);
        break;
      case 'N':
      case 'n':
        if (Typeof (player) == TYPE_EXIT || Typeof (player) == TYPE_ROOM)
          break;
        Matched ("enter");
        do_enter (player, arg1);
        break;
      default:
        goto bad;
      }
      break;
    case 'g':
    case 'G':
      /* get, give, go, or gripe */
      switch (command[1]) {
      case 'e':
      case 'E':
        if (gagged ||
          Typeof (player) == TYPE_ROOM || Typeof (player) == TYPE_EXIT)
          break;
        Matched ("get");
        do_get (player, arg1);
        break;
      case 'i':
      case 'I':
        if (gagged)
          break;
        Matched ("give");
        do_give (player, arg1, arg2);
        break;
      case 'o':
      case 'O':
        if (Typeof (player) == TYPE_EXIT || Typeof (player) == TYPE_ROOM)
          break;
        Matched ("goto");
        do_move (player, arg1, 0);
        break;
      case 'r':
      case 'R':
        if (gagged)
          break;
        Matched ("gripe");
        do_gripe (player, arg1, arg2);
        break;
      default:
        goto bad;
      }
      break;
    case 'h':
    case 'H':
      Matched ("help");
      do_help (player, oldarg1);
      break;
    case 'i':
    case 'I':
      Matched ("inventory");
      do_inventory (player);
      break;
    case 'k':
    case 'K':
      if (gagged)
        break;
      Matched ("kill");
      do_kill (player, arg1, atol (arg2), 0);
      break;
    case 'l':
    case 'L':
      switch (command[1]) {
      case 'o':
      case 'O':
      case '\0':               /* patch allow 'l' command to do a look */
        Matched ("look");
        do_look_at (player, arg1);
        break;
      case 'E':
      case 'e':
        if (Typeof (player) == TYPE_ROOM || Typeof (player) == TYPE_EXIT)
          break;
        Matched ("leave");
        do_leave (player);
        break;
      default:
        goto bad;
      }
      break;
    case 'm':
    case 'M':
      if (Typeof (player) == TYPE_ROOM || Typeof (player) == TYPE_EXIT)
        break;
      Matched ("move");
      do_move (player, arg1, 0);
      break;
    case 'n':
    case 'N':
      /* news */
      if (string_compare (command, "news"))
        goto bad;
      do_news (player, oldarg1);
      break;
    case 'p':
    case 'P':
      if (gagged)
        break;
      Matched ("page");
      do_page (player, arg1, arg2);
      break;
    case 'r':
    case 'R':
      Matched ("read");         /* undocumented alias for look at */
      do_look_at (player, arg1);
      break;
    case 's':
    case 'S':
      /* say, "score" */
      switch (command[1]) {
      case 'a':
      case 'A':
        if (gagged)
          break;
        Matched ("say");
        do_say (player, arg1, arg2);
        break;
      case 'c':
      case 'C':
        Matched ("score");
        do_score (player);
        break;
      case 'l':
      case 'L':
        Matched ("slay");
        do_kill (player, arg1, 0, 1);
        break;
      default:
        goto bad;
      }
      break;
    case 't':
    case 'T':
      switch (command[1]) {
      case 'a':
      case 'A':
        if (gagged ||
          Typeof (player) == TYPE_ROOM || Typeof (player) == TYPE_EXIT)
          break;
        Matched ("take");
        do_get (player, arg1);
        break;
      case 'h':
      case 'H':
        if (Typeof (player) == TYPE_ROOM || Typeof (player) == TYPE_EXIT)
          break;
        Matched ("throw");
        do_drop (player, arg1);
        break;
      default:
        goto bad;
      }
      break;
    case 'w':
    case 'W':
      if (gagged)
        break;
      Matched ("whisper");
      do_whisper (player, arg1, arg2);
      break;
    case 'u':
    case 'U':
      if (gagged)
        break;
      Matched ("use");
      do_use (player, arg1);
      break;
    default:
    bad:
      /* try matching enter aliases */
      if ((i = alias_list_check (db[getloc (player)].contents, unp,
            "EALIAS")) != -1) {
        sprintf (temp, "#%d", i);
        do_enter (player, temp);
        return;
      }
      /* if that didn't work, try matching leave aliases */
      if ((Typeof (db[player].location) != TYPE_ROOM) &&
        (quick_alias_check (db[player].location, unp, "LALIAS"))) {
        do_leave (player);
        return;
      }
      /* try matching user defined functions before chopping */
      a = 0;
      if (!gagged) {
        if (getloc (player) != NOTHING) {
          a +=
            list_check (db[getloc (player)].contents, player, '$', ':', unp);
          if (getloc (player) != player)
            a += atr_comm_match (getloc (player), player, '$', ':', unp);
        }
        if (getloc (player) != player)
          a += list_check (db[player].contents, player, '$', ':', unp);

        /* try matching commands on area zone object if nothing is matched */
        if ((!a) && (getzone (db[player].location) != NOTHING))
          a += atr_comm_match (getzone (db[player].location), player,
            '$', ':', unp);
        /* try matching commands on your zone object if nothing is matched */
        if ((!a) && (getzone (player) != NOTHING) &&
          (getzone (db[player].location) != getzone (player)))
          a += atr_comm_match (getzone (player), player, '$', ':', unp);

#ifdef DO_GLOBALS
        /* check global exits only if no other commands are matched */
        if ((!a) && (getloc (player) != MASTER_ROOM)) {
          if (global_exit (player, unp)) {
            if ((Typeof (player) == TYPE_ROOM) ||
              (Typeof (player) == TYPE_EXIT))
              return;
            else {
              do_move (player, unp, 1);
              return;
            }
          } else
            /* global user-defined commands checked if all else fails. */
            /* May match more than one command in the master room. */
            a += list_check (db[MASTER_ROOM].contents, player, '$', ':', unp);
        }
#endif
      }
      if (!a) {
        notify (player, "Huh?  (Type \"help\" for help.)");
#ifdef LOG_FAILED_COMMANDS
        if (!controls (player, getloc (player)) {
          fprintf (stderr, "HUH from %s(%d) in %s(%d)[%s]: %s %s\n",
              db[player].name, player,
              db[getloc (player)].name, getloc (player);
              db[db[getloc (player)].owner].name,
              command, reconstruct_message (arg1, arg2));}
#endif /* LOG_FAILED_COMMANDS */
        }
      break;}
    }
    for (a = 0; a < MAX_ARG; a++) {
      if (arge[a]) {
        free ((char *) arge[a]);
#ifdef MEM_CHECK
        del_check ("process_comm_args");
#endif
      arge[a] = NULL;
    }
  }
}

/* match a list of things */
static int list_check (dbref thing, dbref player, int type, int end, char *str) {
  int match = 0;
  while (thing != NOTHING) {
    if (atr_comm_match (thing, player, type, end, str))
      match = 1;
    thing = db[thing].next;
  }
  return (match);
}

static int alias_list_check (dbref thing, const char *command, char *type) {
  ATTR * a;
  char alias[BUFFER_LEN];
  while (thing != NOTHING) {
    a = atr_get (thing, type);
    if (a) {
      strcpy (alias, uncompress (a->value));
      if (!string_compare (alias, command))
        return thing;           /* matched an alias */
    }
    thing = db[thing].next;
  }
  return -1;
}

static int quick_alias_check (dbref loc, const char *command, char *type) {
  ATTR * a;
  char alias[BUFFER_LEN];
  a = atr_get (loc, type);
  if (a) {
    strcpy (alias, uncompress (a->value));
    return (!string_compare (alias, command));
  } else
    return 0;
}

int Hearer (dbref thing) {
  ALIST * ptr;
  if (((Typeof (thing) == TYPE_PLAYER) &&
    (db[thing].flags & PLAYER_CONNECT)) ||
    ((Typeof (thing) == TYPE_THING) && (db[thing].flags & THING_PUPPET)))
    return (1);
  for (ptr = db[thing].list; ptr; ptr = AL_NEXT (ptr)) {
    if (!AL_BAD (ptr) && !string_compare (AL_NAME (ptr), "LISTEN"))
      return 1;
  }
  return (0);
}

int Commer (dbref thing) {
  ALIST * ptr;
  for (ptr = db[thing].list; ptr; ptr = AL_NEXT (ptr)) {
    if (!AL_BAD (ptr)) {
      if (*AL_STR (ptr) == '$')
        return (1);
    }
  }
  return (0);
}

int Listener (dbref thing) {
  ALIST * ptr;
  for (ptr = db[thing].list; ptr; ptr = AL_NEXT (ptr)) {
    if (!AL_BAD (ptr)) {
      if (*AL_STR (ptr) == '^')
        return (1);
    }
  }
  return (0);
}

static void do_poor (dbref player, char *arg1) {
  int amt = atoi (arg1);
  dbref a;
  if (!God (player))
    return;
  for (a = 0; a < db_top; a++)
    if (Typeof (a) == TYPE_PLAYER)
      s_Pennies (a, amt);
}

static void do_version (dbref player) {
  notify (player, tprintf ("The code running is %s", VERSION));
}

static void do_dolist (dbref player, char *inlist, char *command, dbref cause) {
  char *list, *first, *rest, *fix1, *fix2;
  char buff[BUFFER_LEN], tbuf1[BUFFER_LEN], tbuf2[BUFFER_LEN];
  tbuf2[0] = tbuf1[0] = '\0';
  strcpy (tbuf2, inlist);
  strcpy (tbuf1, pronoun_substitute (cause, tbuf2, player));
  list = tbuf1;
  while (first = parse_up (&list, ' ')) {
    fix1 = buff;
    fix2 = command;
    while (*fix2) {
      rest = first;
      while (*fix2 && (*fix2 != NUMBER_TOKEN))
        *fix1++ = *fix2++;
      if ((*fix2 == NUMBER_TOKEN) && (*(fix2 + 1) == NUMBER_TOKEN)) {
        fix2++; fix2++;
        while (rest && *rest)
          *fix1++ = *rest++;
      } else
        *fix1++ = *fix2++;
    }
    *fix1 = '\0';
    strcpy (tbuf2, pronoun_substitute (cause, buff, player));
    parse_que (player, tbuf2, cause);
  }
}

static void do_config (dbref player) {
  notify (player, "-----  MUSH CONFIGURATION  -----");
#ifdef RESTRICTED_BUILDING
#ifdef FREE_OBJECTS
  notify (player, "Players without a BUILDER bit can only create objects.");
#else
  notify (player, "Players without a BUILDER bit cannot build anything.");
#endif
#endif
#ifdef QUOTA
  notify (player, "Quota restrictions are in effect.");
#endif
#ifdef LOG_COMMANDS
  notify (player, "All commands are being logged.");
#endif
#ifdef LOG_FAILED_COMMANDS
  notify (player, "Commands which produce a " Huh ? " are being logged.");
#endif
#ifdef NO_FORK
  notify (player, "Forking is disabled. Game will freeze during dumps.");
#endif
#ifdef USE_MAILER
  notify (player, "The built-in MUSH mailing system is being used.");
#endif
#ifdef LOCKOUT
  notify (player, "Site lockout is enabled.");
#endif
#ifdef WCREAT
  notify (player, "Player registration is in effect.");
#endif
#ifdef DESTROY
  notify (player, "Object recycling is enabled.");
#endif
#ifdef FLAGS_ON_EXAMINE
  notify (player, "Expanded flag list is shown on examines.");
#endif
#ifdef FULL_INVIS
  notify (player, "Dark players/objects show up as Someone/Something.");
#endif
#ifdef PLAYER_LOCATE
  notify (player,
      "The location of players not set UNFINDABLE can be found.");
#endif
#ifdef AT_DOING
  notify (player, "Doing polls are enabled.");
#endif
#ifdef ROYALTY_FLAG
  notify (player, "The ROYALTY flag is enabled.");
#endif
#ifdef INHERIT_FLAG
  notify (player, "The INHERIT flag is enabled.");
#endif
#ifdef DO_GLOBALS
  notify (player, tprintf ("The master room is #%d.", MASTER_ROOM));
#endif
#ifdef GUEST_RESTRICT
  notify (player, tprintf ("The guest player is #%d.", GUEST_PLAYER));
#endif
#ifdef IDLE_TIMEOUT
  notify (player,
    tprintf ("The inactivity limit is %d minutes.", INACTIVITY_LIMIT));
#endif
  notify (player, "-----  BUILDING COSTS  -----");
  notify (player, tprintf ("Object creation....%d", OBJECT_COST));
  notify (player, tprintf ("Room creation......%d", ROOM_COST));
  notify (player, tprintf ("Exit creation......%d", EXIT_COST));
  notify (player, tprintf ("Linking............%d", LINK_COST));
  notify (player, tprintf ("Queue deposit......%d", QUEUE_COST));
  notify (player, tprintf ("Quota per object...%d", QUOTA_COST));
  notify (player, "-----  COMMAND COSTS  -----");
  notify (player, tprintf ("@find..............%d", FIND_COST));
  notify (player, tprintf ("page...............%d", PAGE_COST));
  notify (player, tprintf ("kill base cost.....%d", KILL_BASE_COST));
  notify (player, tprintf ("kill minimum cost..%d", KILL_MIN_COST));
  notify (player, tprintf ("kill insurance.....%d", KILL_BONUS));
}

#undef Matched