/* game.c */

#include <ctype.h>
#include <fcntl.h>
#include <string.h>
#ifdef XENIX
#include <sys/signal.h>
#else
#include <signal.h>
#include <sys/wait.h>
#endif				/* xenix */

#include <sys/time.h>
#include <sys/types.h>

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

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

#ifdef USE_NALLOC
#include "nalloc.h"
NALLOC *glurp;
#endif

/* declarations */
char dumpfile[200];
static int epoch = 0;
int reserved;
int depth = 0;			/* excessive recursion prevention */
extern dbref cplr;
extern char ccom[];

void fork_and_dump();
void dump_database();
extern void exec();

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

void do_dump(player)
    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()
{
  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
void do_purge(player)
    dbref player;
{
  if (Wizard(player)) {
    FIX;
    notify(player, "Purge complete.");
  } else
    notify(player, "Sorry, you are a mortal.");
}

void dest_info(thing, tt)
    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(player, msg)
    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, 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, '^', ':', msg);
	/* unlike normal @listen, don't pass the message on */
      }
      /* now check for multi listeners */
    }
  depth--;
}

void do_shutdown(player)
    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.");
  }
}
#ifdef XENIX
/* rename hack!!! */
rename(s1, s2)
    char *s1;
    char *s2;
{
  char buff[300];
  sprintf(buff, "mv %s %s", s1, s2);
  system(buff);
}
#endif

static void dump_database_internal()
{
  char tmpfl[2048];
  FILE *f;
  extern int unlink();
  
  sprintf(tmpfl, "%s.#%d#", dumpfile, epoch - 1);
  unlink(tmpfl);		/* nuke our predecessor */

  sprintf(tmpfl, "%s.#%d#", dumpfile, epoch);
#ifdef DBCOMP
  if ((f = popen(tprintf("compress >%s", tmpfl), "w")) != NULL) {
    db_write(f);
    pclose(f);
    if (rename(tmpfl, dumpfile) < 0)
      perror(tmpfl);
  } else
    perror(tmpfl);
#ifdef USE_MAILER
  sprintf(tmpfl, "maildb.Z.#%d#", epoch - 1);
  unlink(tmpfl);
  sprintf(tmpfl, "maildb.Z.#%d#", epoch);
  if (mdb_top > 0)
    if ((f = popen(tprintf("compress >%s", tmpfl), "w")) != NULL) {
      dump_mail(f);
      pclose(f);
      if (rename(tmpfl, "maildb.Z") < 0)
	perror(tmpfl);
    } else
      perror(tmpfl);
#endif
#else
  if ((f = fopen(tmpfl, "w")) != NULL) {
    db_write(f);
    fclose(f);
    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, "w")) != NULL) {
    db_write(f);
    fclose(f);
    if (rename(tmpfl, "maildb") < 0)
      perror(tmpfl);
  } else
    perror(tmpfl);
#endif
#endif
}

void panic(message)
    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, "w")) == NULL) {
    perror("CANNOT OPEN PANIC FILE, YOU LOSE:");
    _exit(135);
  } else {
    fprintf(stderr, "DUMPING: %s\n", panicfile);
    db_write(f);
    fclose(f);
    fprintf(stderr, "DUMPING: %s (done)\n", panicfile);
    _exit(136);
  }
}

void dump_database()
{
  epoch++;

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

static int reaper()
{
#ifdef XENIX
  int status;

  wait(&status);
#else
  union wait my_stat;

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

void fork_and_dump()
{
  int child;
  epoch++;

  fprintf(stderr, "CHECKPOINTING: %s.#%d#\n", dumpfile, epoch);
#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 */
    close(reserved);		/* get that file descriptor back */
#ifdef CONCENTRATOR
#ifndef XENIX
    signal(SIGCHLD, SIG_DFL);
#else
    signal(SIGCLD, SIG_DFL);
#endif  /* XENIX */
#endif  /* CONCENTRATOR */
    dump_database_internal();
#ifndef NO_FORK
    _exit(0);			/* !!! */
#else				/* NO FORK */
    reserved = open("/dev/null", O_RDWR);
#ifdef CONCENTRATOR
#ifndef XENIX
    signal(SIGCHLD, (void *) reaper);
#else
    signal(SIGCLD, (void *) reaper);
#endif	/* XENIX */
#endif	/* CONCENTRATOR */
#endif	/* NO_FORK */
  } else if (child < 0) {
    perror("fork_and_dump: fork()");
  }
}

void do_restart()
{
  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(infile, outfile)
    const char *infile;
    const char *outfile;
{
  FILE *f;
  int a;
  extern void init_timer();
  
  depth = 0;

#ifdef USE_NALLOC
  glurp = na_open(sizeof(char));
#endif
  for (a = 0; a < 10; a++)
    wptr[a] = NULL;

#ifdef DBCOMP
  if ((f = popen(tprintf("uncompress < %s", infile), "r")) == NULL)
   return -1;
#else
  if ((f = fopen(infile, "r")) == 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 = popen("uncompress <maildb.Z", "r")) == NULL)
    mail_init();
#else
  if ((f = fopen("maildb", "r")) == 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 */

  /* initialize random number generator */
  srandom(getpid());

  /* set up dumper */
  strcpy(dumpfile, outfile);
  init_timer();
#ifndef XENIX
  signal(SIGCHLD, (void *)reaper);
#else				/* xenix */
  signal(SIGCLD, (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 */
char *do_argtwo(player, rest, cause, buff)
    dbref player;
    char *rest;
    dbref cause;
    char *buff;
{
  exec(&rest, buff, player, cause, 0);
  return (buff);
}

char **do_argbee(player, rest, cause, arge, buff)
    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]) {
#ifdef USE_NALLOC
      na_unalloc(glurp, arge[a]);
#else
      free((char *) arge[a]);
#endif
#ifdef MEM_CHECK
      del_check("process_comm_args");
#endif
    }
    p = parse_up(&rest, ',');
    if(p) {
#ifdef USE_NALLOC
      strcpy((arge[a] = (char *) na_ualloc(glurp, strlen(p)+1)), p);
#else
      strcpy((arge[a] = (char *)malloc(strlen(p)+1)), p);
#endif
#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);
#ifdef USE_NALLOC
      na_unalloc(glurp, arge[a]);
      strcpy((arge[a] = (char *) na_ualloc(glurp, strlen(buff) + 1)), buff);
#else
      free((char *)arge[a]);
      strcpy((arge[a] = (char *)malloc(strlen(buff)+1)), buff);
#endif
    }
  return (arge);
}

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

void process_command(player, command, cause)
    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 */

  void do_poor(), do_version(), do_dolist(), do_config();
  extern void wait_que();
  
#ifdef USE_NALLOC
  na_clear(glurp);
#endif
  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) */
  srandom((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(*command))
    command++;
  /* eat extra white space */
  q = p = command;
  while (*p) {
    /* scan over word */
    while (*p && !isspace(*p))
      *q++ = *p++;
    /* smash spaces */
    while (*p && isspace(*++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);
  } else {
    strcpy(unp, command);
    /* parse arguments */

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

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

    r = parse_up(&arg1, '=');	/* first delimiter is ='s */
    if(r) {
      if(arge[0]) {
#ifdef USE_NALLOC
	na_unalloc(glurp, arge[0]);
#else
	free((char *)arge[0]);
#endif
#ifdef MEM_CHECK
	del_check("process_comm_args");
#endif
      }
#ifdef USE_NALLOC
      strcpy((arge[0] = (char *) na_ualloc(glurp, strlen(r)+1)), r);
#else
      strcpy((arge[0] = (char *)malloc(strlen(r)+1)), r);
#endif
#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);
#ifdef USE_NALLOC
      na_unalloc(glurp, arge[0]);
      strcpy((arge[0] = (char *) na_ualloc(glurp, strlen(r) + 1)), r);
#else
      free((char *)arge[0]);
      strcpy((arge[0] = (char *)malloc(strlen(r)+1)), r);
#endif
    } 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]) {
#ifdef USE_NALLOC
	  na_unalloc(glurp, arge[a]);
#else
	  free((char *) arge[a]);
#endif
#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);
	    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);
	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]) {
#ifdef USE_NALLOC
      na_unalloc(glurp, (char *) arge[a]);
#else
      free((char *) arge[a]);
#endif
#ifdef MEM_CHECK
      del_check("process_comm_args");
#endif
      arge[a] = NULL;
    }
  }
}

/* match a list of things */
int list_check(thing, player, type, end, str)
    dbref thing, player;
    char type, 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);
}

int alias_list_check(thing, command, type)
  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;
}

int quick_alias_check(loc, command, type)
  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(thing)
    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(thing)
    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(thing)
     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);
}

void do_poor(player, arg1)
    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);
}

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

void do_dolist(player, inlist, command, cause)
    dbref player, cause;
    char *inlist, *command;
{
  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);
  }
}

void do_config(player)
  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 CONCENTRATOR
  notify(player, "Concentrator is enabled.");
#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