/* $Header: /belch_a/users/rearl/tinymuck/src/RCS/game.c,v 1.15 90/09/28 12:21:03 rearl Exp $ */

/*
 * $Log:	game.c,v $
 * Revision 1.15  90/09/28  12:21:03  rearl
 * Added logging of interactive player commands.
 * 
 * Revision 1.14  90/09/16  04:42:11  rearl
 * Preparation code added for disk-based MUCK.
 * 
 * Revision 1.13  90/09/15  22:20:04  rearl
 * Fixed non-GOD_PRIV problem.
 * 
 * Revision 1.12  90/09/13  06:22:31  rearl
 * Added optional argument to @dump for changing the dumpfile in emergencies.
 * 
 * Revision 1.11  90/09/10  02:19:54  rearl
 * Fixed a possible bug in database checkpointing, minimized
 * command-line space-smashing.
 * 
 * Revision 1.10  90/09/01  05:57:55  rearl
 * Fixed bugs in macro dumpfile.
 * 
 * Revision 1.9  90/08/27  03:24:52  rearl
 * Disk-based MUF source code, added environment support code.
 * 
 * Revision 1.8  90/08/11  04:00:20  rearl
 * *** empty log message ***
 * 
 * Revision 1.7  90/08/05  14:43:34  rearl
 * One more silly bug fixed in the command handler.
 * 
 * Revision 1.6  90/08/02  18:55:04  rearl
 * Fixed some calls to logging functions.
 * 
 * Revision 1.5  90/07/30  00:11:07  rearl
 * Added @owned command.
 * 
 * Revision 1.4  90/07/29  17:34:05  rearl
 * Fixed bug in = once and for all, also some bugs hiding in the command
 * processing switch.
 * 
 * Revision 1.3  90/07/21  16:32:23  casie
 * more log calls added
 * 
 * Revision 1.2  90/07/21  01:43:51  casie
 * added support for logging.
 * 
 * Revision 1.1  90/07/19  23:03:33  casie
 * Initial revision
 * 
 *
 */

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

#include <stdio.h>
#include <ctype.h>
#include <signal.h>
#include <sys/wait.h>

#include "db.h"
#include "params.h"
#include "interface.h"
#include "match.h"
#include "externs.h"
#include "strings.h"
#include "patchlevel.h"

/* declarations */
static char *dumpfile = 0;
static int epoch = 0;
static int alarm_triggered = 0;
static int alarm_block = 0;

static void fork_and_dump(void);
void dump_database(void);
#include <sys/time.h>

void do_dump(dbref player, const char *newfile)
{
  char buf[BUFFER_LEN];
  
  if(Wizard(player)) {
    alarm_triggered = 1;
    if (*newfile
#ifdef GOD_PRIV
	&& God(player)
#endif /* GOD_PRIV */
	) {
      strcpy(dumpfile, newfile);
      sprintf(buf, "Dumping to file %s...", dumpfile);
    } else {
      sprintf(buf, "Dumping...");
    }
    notify(player, buf);
  } else {
    notify(player, "Permission denied.");
  }
}

void do_shutdown(dbref player)
{
  if(Wizard(player)) {
    log_status("SHUTDOWN: by %s\n", unparse_object(player, player));
    shutdown_flag = 1;
  } else {
    notify(player, "Your delusions of grandeur have been duly noted.");
    log_status("ILLEGAL SHUTDOWN: tried by %s\n", unparse_object(player, player));
  }
}

/* should be void, but it's defined as int */
static void alarm_handler(int x)
{
  x = x;				/* just so it dont complain */
  alarm_triggered = 1;
  if(!alarm_block) {
    fork_and_dump();
  }
}

static void dump_database_internal(void)
{
  char tmpfile[2048];
  FILE *f;
  
  sprintf(tmpfile, "%s.#%d#", dumpfile, epoch - 1);
  (void) unlink(tmpfile);		/* nuke our predecessor */
  
  sprintf(tmpfile, "%s.#%d#", dumpfile, epoch);
  
  if((f = fopen(tmpfile, "w")) != NULL) {
    db_write(f);
    fclose(f);
    if(rename(tmpfile, dumpfile) < 0) perror(tmpfile);
  } else {
    perror(tmpfile);
  }
  
  /* Write out the macros */
  
  sprintf(tmpfile, "%s.#%d#", MACRO_FILE, epoch - 1);
  (void) unlink(tmpfile);
  
  sprintf(tmpfile, "%s.#%d#", MACRO_FILE, epoch);
  
  if((f = fopen(tmpfile, "w")) != NULL) {
    macrodump(macrotop, f);
    fclose(f);
    if (rename(tmpfile, MACRO_FILE) < 0) perror(tmpfile);
  } else {
    perror(tmpfile);
  }
}

void panic(const char *message)
{
  char panicfile[2048];
  FILE *f;
  int i;
  
  log_status("PANIC: %s\n", message);
  fprintf(stderr, "PANIC: %s\n", message);
  
  /* turn off signals */
  for(i = 0; i < NSIG; i++) {
    signal(i, SIG_IGN);
  }
  
  /* shut down interface */
  /*  emergency_shutdown();*/
  /* emergency_warn();*/ /* who cares... lets not tell'm. >:) */
  
  /* dump panic file */
  sprintf(panicfile, "%s.PANIC", dumpfile);
  if((f = fopen(panicfile, "w")) == NULL) {
    perror("CANNOT OPEN PANIC FILE, YOU LOSE");
#ifdef NOCOREDUMP
#else /* !NOCOREDUMP */
    signal(SIGIOT, SIG_DFL);
#endif /* NOCOREDUMP */
  } else {
    log_status("DUMPING: %s\n", panicfile);
    fprintf(stderr, "DUMPING: %s\n", panicfile);
    db_write(f);
    fclose(f);
    log_status("DUMPING: %s (done)\n", panicfile);
    fprintf(stderr, "DUMPING: %s (done)\n", panicfile);
#ifdef NOCOREDUMP
#else /* !NOCOREDUMP */
    signal(SIGIOT, SIG_DFL);
#endif /* NOCOREDUMP */
  }
  execl("./restart", "restart", NULL, NULL); /* try and restart it... */
#ifdef NOCOREDUMP
  _exit(136);
#else
  signal(SIGIOT,SIG_DFL);
  abort();
#endif
}

void dump_database(void)
{
  epoch++;
  
  log_status("DUMPING: %s.#%d#\n", dumpfile, epoch);
  dump_database_internal();
  log_status("DUMPING: %s.#%d# (done)\n", dumpfile, epoch);
}

static void fork_and_dump(void)
{
  int child;
  
  epoch++;
  
  log_status("CHECKPOINTING: %s.#%d#\n", dumpfile, epoch);
#ifdef USE_VFORK
  child = vfork();
#else /* USE_VFORK */
  child = fork();
#endif /* USE_VFORK */
  if(child == 0) {
    /* in the child */
    close(0);		/* get that file descriptor back */
    dump_database_internal();
    _exit(0);		/* !!! */
  } else if(child < 0) {
    perror("fork_and_dump: fork()");
  }
  
  /* in the parent */
  /* reset alarm */
  alarm_triggered = 0;
  alarm(DUMP_INTERVAL);
}
dbref global_trigger;
static void reaper(int x)
{
  union wait stat;
  x = x;				/* so it dont complain */
  while(wait3(&stat, WNOHANG, 0) > 0);
}
time_t startup_time;
int init_game(const char *infile, const char *outfile)
{
  FILE *f;
  
  if ((f = fopen(MACRO_FILE, "r")) == NULL)
    log_status("INIT: Macro storage file %s is tweaked.\n", MACRO_FILE);
  else {
    macroload(f);
    fclose(f);
  }
  
  if((f = fopen((char *)infile, "r")) == NULL) return -1;
  
  /* ok, read it in */
  log_status("LOADING: %s\n", infile);
  fprintf(stderr, "LOADING: %s\n", infile);
  if(db_read(f) < 0) return -1;
  log_status("LOADING: %s (done)\n", infile);
  fprintf(stderr, "LOADING: %s (done)\n", infile);
  
  /* everything ok */
  fclose(f);
  
  /* initialize random number generator */
  srandom(getpid());
  
  /* set up dumper */
  if(dumpfile) free((void *) dumpfile);
  dumpfile = alloc_string(outfile);
  signal(SIGALRM, (void *) alarm_handler);
  signal(SIGHUP, (void *) alarm_handler);
  signal(SIGCHLD, (void *) reaper);
  alarm_triggered = 0;
  alarm(DUMP_INTERVAL);

  /* set up uptime */
  return 0;
}

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

void process_command(dbref player, const char *commie, dbref cause,
		     int no_prog)
{
  char *arg1;
  char *arg2;
  char *full_command;
  char *p;			/* utility */
  char *command;
  char commbuff[BUFFER_LEN];
  char pbuf[BUFSIZ];
  char xbuf[BUFSIZ];

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

  if (commie == 0) abort();
  strcpy(commbuff, commie); command = commbuff;
  global_trigger = player;
  global_cause = cause;

  /* robustify player */
  if(player < 0 || player >= db_top ||
     ((Typeof(player) != TYPE_PLAYER)&&(Typeof(player)!=TYPE_THING))) {
    log_status("process_command: bad player %d\n", player);
    return;
  }
  if(Typeof(player) == TYPE_EXIT || Typeof(player) == TYPE_ROOM)
    player = OWNER(player);

#ifdef LOG_COMMANDS
  if (!(FLAGS(player) & INTERACTIVE) || DBFETCH(player)->run)
    log_command("%s(%d) in %s(%d):%s %s\n", NAME(player), (int) player,
		NAME(DBFETCH(player)->location),
		(int) DBFETCH(player)->location,
		(FLAGS(player) & INTERACTIVE) ?
		" [interactive]" : " ", command);
  else
    log_command("%s(%d) in %s(%d):[editor] %s\n",NAME(player),(int) player,
		NAME(DBFETCH(player)->curr_prog),
		(int) DBFETCH(player)->curr_prog,
		command);
#endif /* LOG_COMMANDS */

  if(FLAGS(player)&PUPPET && Typeof(player) != TYPE_PLAYER) {
    sprintf(pbuf, "%s>> %s", NAME(player), command);
    notify_internal(DBFETCH(player)->owner, pbuf);
  }

  /* block dump to prevent db inconsistencies from showing up */
  alarm_block = 1;
  
  if (FLAGS(player) & INTERACTIVE && !no_prog) {
    interactive(player, command);
    return;
  }

  /* eat leading whitespace */
  while(*command && isspace(*command)) command++;
  
  /* check for single-character commands */
  if(*command == SAY_TOKEN) {
    sprintf(pbuf,"say %s", command+1);
    command = &pbuf[0];
  } else if((*command == POSE_TOKEN) || *command == ';') {
    sprintf(pbuf, "pose %s", command+1);
    command = &pbuf[0];
  } else if(*command == '#') {
    sprintf(pbuf,"@force %s", command);
    command = &pbuf[0];
  }
  
  if(can_move(player, command)) {
    /* command is an exact match for an exit */
    do_move(player, command);
    *match_args = 0;
  } else {
    full_command = strcpy(xbuf, command);
    for (; *full_command && !isspace(*full_command); full_command++);
    if (*full_command) full_command++;

    /* 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++;
    
    /* find end of arg1, start of arg2 */
    for(arg2 = arg1; *arg2 && *arg2 != ARG_DELIMITER; arg2++);
    
    /* truncate arg1 */
    for(p = arg2 - 1; p >= arg1 && isspace(*p); p--) *p = '\0';

    /* go past delimiter if present */
    if(*arg2) *arg2++ = '\0';
    while(*arg2 && isspace(*arg2)) arg2++;
    arg1 = check_arg(arg1, player, cause);
    arg2 = check_arg(arg2, player, cause);
    
    switch(command[0]) {
    case '@':
      switch(command[1]) {
      case '@':
	Matched("@@");       /* does nothing, useful for commenting */
	break;
      case 'a':
      case 'A':
	/* @action, @attach */
	switch(command[2]) {
	case 'a':
	case 'A':
	  Matched("@aahear");
	  do_attr(player,"Aahear",arg1,arg2);
	  break;
	case 'c':
	case 'C':
	  switch(command[3]) {
	  case 'o':
	  case 'O':
	    Matched("@aconnect");
	    do_attr(player, "Aconnect", arg1, arg2);
	    break;
	  case 't':
	  case 'T':
	    Matched("@action");
	    do_action(player, arg1, arg2);
	    break;
	  default:
	    goto bad;
	  }
	  break;
	case 'd':
	case 'D':
	  switch(command[3]) {
	  case 'e':
	  case 'E':
	    Matched("@adesc");
	    do_attr(player, "Adesc", arg1, arg2);
	    break;
	  case 'r':
	  case 'R':
	    Matched("@adrop");
	    do_attr(player,"Adrop", arg1, arg2);
	    break;
	  default:
	    goto bad;
	  }
	  break;
	case 'E':
	case 'e':
	  Matched("@aenter");
	  do_attr(player, "Aenter", arg1, arg2);
	  break;
	case 'f':
	case 'F':
	  Matched("@afail");
	  do_attr(player, "Afail", arg1, arg2);
	  break;
	case 'h':
	case 'H':
	  Matched("@ahear");
	  do_attr(player, "Ahear", arg1, arg2);
	  break;
	case 'k':
	case 'K':
	  Matched("@akill");
	  do_attr(player, "Akill", arg1, arg2);
	  break;
	case 'l':
	case 'L':
	  Matched("@aleave");
	  do_attr(player, "Aleave", arg1, arg2);
	  break;
	case 'm':
	case 'M':
	  Matched("@amhear");
	  do_attr(player, "Amhear", arg1, arg2);
	  break;
	case 'p':
	case 'P':
	  Matched("@apay");
	  do_attr(player, "Apay", arg1, arg2);
	  break;
	case 's':
	case 'S':
	  Matched("@asucc");
	  do_attr(player, "Asucc", arg1, arg2);
	  break;
	case 't':
	case 'T':
	  Matched("@attach");
	  do_attach(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':
	  Matched("@chown");
	  do_chown(player, arg1, arg2);
	  break;
	case 'o':
	case 'O':
	  Matched("@cost");
	  do_attr(player, "Cost", arg1, arg2);
	  break;
	case 'r':
	case 'R':
	  Matched("@create");
	  do_create(player, arg1, atoi(arg2));
	  break;
	default:
	  goto bad;
	}
	break;
      case 'd':
      case 'D':
	/* describe, dig, or dump */
	switch(command[2]) {
	case 'b':
	case 'B':
	  do_dboot(player, arg1);
	  break;
	case 'e':
	case 'E':
	  Matched("@describe");
	  do_attr(player, "Desc", arg1, arg2);
	  break;
	case 'i':
	case 'I':
	  Matched("@dig");
	  do_dig(player, arg1, arg2);
	  break;
	case 'r':
	case 'R':
	  Matched("@drop");
	  do_attr(player, "Drop", arg1, arg2);
	  break;
	case 'u':
	case 'U':
	  Matched("@dump");
	  do_dump(player, full_command);
	  break;
	default:
	  goto bad;
	}
	break;
      case 'e':
      case 'E':
	switch(command[2]) {
	case 'd':
	case 'D':
	  Matched("@edit");
	  do_edit(player, arg1);
	  break;
	case 'n':
	case 'N':
	  Matched("@enter");
	  do_attr(player, "Enter", arg1, arg2);
	  break;
	default: goto bad;
	} break;
      case 'f':
      case 'F':
	/* fail, field, find, or force */
	switch(command[2]) {
	case 'a':
	case 'A':
	  Matched("@fail");
	  do_attr(player, "Fail", arg1, arg2);
	  break;
	case 'i':
	case 'I':
	  switch(command[3]) {
	  case 'e':
	  case 'E':
	    Matched("@field");
	    do_field(player, arg1, arg2, cause);
	    break;
	  case 'n':
	  case 'N':
	    Matched("@find");
	    do_find(player, arg1, arg2);
	    break;
	  default:
	    goto bad;
	  }
	  break;
	case 'o':
	case 'O':
	  Matched("@force");
	  do_force(player, arg1, arg2);
	  break;
	default:
	  goto bad;
	}
	break;
      case 'h':
      case 'H':
	Matched("@halt");
	do_halt(player, arg1);
	break;
      case 'i':
      case 'I':
	Matched("@idesc");
	do_attr(player, "Idesc", arg1, arg2);
	break;
      case 'k':
      case 'K':
	Matched("@kill");
	do_attr(player, "Kill", arg1, arg2);
	break;
      case 'l':
      case 'L':
	/* lock or link */
	switch(command[2]) {
	case 'e':
	case 'E':
	  Matched("@leave");
	  do_attr(player, "Leave", arg1, arg2);
	  break;
	case 'i':
	case 'I':
	  switch(command[3]) {
	  case 'n':
	  case 'N':
	    Matched("@link");
	    do_link(player, arg1, arg2);
	    break;
	  case 's':
	  case 'S':
	    if(!string_compare(command,"@list")) {
	      Matched("@list");
	      match_and_list(player, arg1, arg2);
	    } else {
	      Matched("@listen");
	      do_attr(player, "Listen", arg1, arg2);
	    }
	    break;
	  default:
	    goto bad;
	  }
	  break;
	case 'o':
	case 'O':
	  Matched("@lock");
	  do_lock(player, arg1, arg2);
	  break;
	default:
	  goto bad;
	}
	break;
      case 'n':
      case 'N':
	/* @name or @newpassword */
	switch(command[2]) {
	case 'a':
	case 'A':
	  Matched("@name");
	  do_name(player, arg1, arg2);
	  break;
	case 'e':
	case 'E':
	  if(strcmp(command, "@newpassword")) goto bad;
	  do_newpassword(player, arg1, arg2);
	  break;
	default:
	  goto bad;
	}
	break;
      case 'o':
      case 'O':
	switch(command[2]) {
	case 'c':
	case 'C':
	  Matched("@oconnect");
	  do_attr(player, "Oconnect", arg1, arg2);
	  break;
	case 'd':
	case 'D':
	  switch(command[3]) {
	  case 'e':
	  case 'E':
	    Matched("@odesc");
	    do_attr(player, "Odesc", arg1, arg2);
	    break;
	  case 'i':
	  case 'I':
	    Matched("@odisconnect");
	    do_attr(player, "Odisconnect", arg1, arg2);
	    break;
	  case 'r':
	  case 'R':
	    Matched("@odrop");
	    do_attr(player, "Odrop", arg1, arg2);
	    break;
	  default:
	    goto bad;
	  }
	  break;
	case 'e':
	case 'E':
	  Matched("@oenter");
	  do_attr(player, "Oenter", arg1, arg2);
	  break;
	case 'f':
	case 'F':
	  Matched("@ofail");
	  do_attr(player, "Ofail", arg1, arg2);
	  break;
	case 'k':
	case 'K':
	  Matched("@okill");
	  do_attr(player, "Okill", arg1, arg2);
	  break;
	case 'l':
	case 'L':
	  Matched("@oleave");
	  do_attr(player, "Oleave", arg1, arg2);
	  break;
	case 'p':
	case 'P':
	  switch(command[3]) {
	  case 'e':
	  case 'E':
	    Matched("@open");
	    do_open(player, arg1, arg2);
	    break;
	  case 'a':
	  case 'A':
	    Matched("@opay");
	    do_attr(player, "Opay", arg1, arg2);
	    break;
	  default: goto bad;
	  }
	  break;
	case 's':
	case 'S':
	  Matched("@osuccess");
	  do_attr(player, "Osucc", arg1, arg2);
	  break;
	case 'w':
	case 'W':
	  Matched("@owned");
	  do_owned(player, arg1, arg2);
	  break;
	default:
	  goto bad;
	}
	break;
      case 'p':
      case 'P':
	switch(command[2])
	  {
	  case 'a':
	  case 'A':
	    switch(command[3]) {
	    case 's':
	    case 'S':
	      Matched("@password");
	      do_password(player, arg1, arg2);
	      break;
	    case 'y':
	      Matched("@pay");
	      do_attr(player, "Pay", arg1, arg2);
	      break;
	    default: goto bad;
	    } break;
	  case 'c':
	  case 'C':
	    Matched("@pcreate");
	    do_pcreate(player, arg1, arg2);
	    break;
	  case 'r':
	  case 'R':
	    Matched("@prog");
	    do_prog(player, arg1);
	    break;
	  case 's':
	  case 'S':
	    Matched("@ps");
	    do_ps(player, arg1);
	    break;
	  default:
	    goto bad;
	  }
	break;
      case 'r':
      case 'R':
	switch(command[2]) {
#ifdef RECYCLE
	case 'e':
	case 'E':
	  Matched("@recycle");
	  do_recycle(player, arg1);
	  break;
#endif /* RECYCLE */
	case 's':
	case 'S':
	  if(strcmp(command, "@rst")) goto bad;
	  do_restart(player);
	  break;
	default: goto bad;
	}
	break;
      case 's':
      case 'S':
	/* set, shutdown, success */
	switch(command[2]) {
	case 'e':
	case 'E':
	  switch(command[3]) {
	  case 't':
	  case 'T':
	    Matched("@set");
	    do_set(player, arg1, arg2);
	    break;
	  case 'x':
	  case 'X':
	    Matched("@sex");
	    do_attr(player, "Sex", 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':
	  switch(command[3]) {
	  case 'a':
	  case 'A':
	    switch(command[4]) {
	    case 't':
	    case 'T':
	      Matched("@stats");
	      do_stats(player, arg1);
	      break;
	    case 'r':
	    case 'R':
	      Matched("@startup");
	      do_attr(player, "Startup", arg1, arg2);
	      break;
	    default: goto bad;
	    } break;
	  default: goto bad;
	  } break;
	case 'u':
	case 'U':
	  Matched("@success");
	  do_attr(player, "Succ", arg1, arg2);
	  break;
	case 'w':
	case 'W':
	  switch(command[3]) {
	  case 'a':
	  case 'A':
	    Matched("@swap");
	    do_swap(player, arg1, arg2);
	    break;
	  case 'i':
	  case 'I':
	    Matched("@switch");
	    do_switch(player, arg1, arg2, cause);
	    break;
	  default:
	    goto bad;
	  }
	  break;
	default:
	  goto bad;
	}
	break;
      case 't':
      case 'T':
	switch(command[2]) {
	case 'e':
	case 'E':
	  Matched("@teleport");
	  do_teleport(player, arg1, arg2);
	  break;
	case 'o':
	case 'O':
	  if(strcmp(command, "@toad")) goto bad;
	  do_toad(player, arg1, arg2);
	  break;
	case 'r':
	case 'R':
	  switch(command[3]) {
	  case 'a':
	  case 'A':
	    Matched("@trace");
	    do_trace(player, arg1, atoi(arg2));
	    break;
	  case 'i':
	  case 'I':
	    Matched("@trigger");
	    do_trigger(player, arg1, arg2);
	    break;
	  default: goto bad;
	  }
	  break;
	default:
	  goto bad;
	}
	break;
      case 'u':
      case 'U':
	switch(command[2]) {
	case 'n':
	case 'N':
	  if(string_prefix(command, "@unli")) {
	    do_unlink(player, arg1);
	  } else if(string_prefix(command, "@unlo")) {
	    do_unlock(player, arg1);
	  } else {
	    goto bad;
	  }
	  break;
	case 'p':
	case 'P':
	  Matched("@uptime");
	  do_uptime(player);
	  break;
	default: goto bad;
	} break;
      case 'v':
      case 'V': /* v? fields */
	if(command[3]) {
	  Matched("@version");
	  do_version(player);
	}
	else {
	  char buf[3];
	  if(tolower(command[2])<'a' || tolower(command[2])>'z')
	    goto bad;
	  strcpy(buf,"V?");
	  buf[1] = tolower(command[2]);
	  do_attr(player, buf, arg1, arg2);
	}
	break;
      case 'w':
      case 'W':
	if(!strcmp(command, "@wall"))
	  do_wall(player, full_command);
	else {
	  Matched("@wait");
	  do_wait(player,arg1,arg2);
	}
	break;
      default:
	goto bad;
      }
      break;
    case 'd':
    case 'D':
      Matched("drop");
      do_drop(player, arg1);
      break;
    case 'e':
    case 'E':
      switch(command[1]) {
      case 'x':
      case 'X':
	Matched("examine");
	do_examine(player, arg1);
	break;
      case 'n':
      case 'N':
	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':
	Matched("get");
	do_get(player, arg1);
	break;
      case 'i':
      case 'I':
	Matched("give");
	do_give(player, arg1, atoi(arg2));
	break;
      case 'o':
      case 'O':
	Matched("goto");
	do_move(player, arg1);
	break;
      case 'r':
      case 'R':
	Matched("gripe");
	do_gripe(player, full_command);
	break;
      default:
	goto bad;
      }
      break;
    case 'h':
    case 'H':
      Matched("help");
      do_help(player);
      break;
    case 'i':
    case 'I':
      Matched("inventory");
      do_inventory(player);
      break;
    case 'k':
    case 'K':
      Matched("kill");
      do_kill(player, arg1, atoi(arg2));
      break;
    case 'l':
    case 'L':
      switch(command[1]) {
      case 'o':
      case 'O':
      case '\0': /* so just 'l' is look */
	Matched("look");
	do_look_at(player, arg1);
	break;
      case 'e':
      case 'E':
	Matched("leave");
	do_leave(player);
	break;
      default: goto bad;
      }
      break;
    case 'm':
    case 'M':
      if (string_prefix(command, "move")) {
	do_move(player, arg1);
	break;
      } else {
	if (string_compare(command, "man"))
	  goto bad;
	do_man(player);
      }
      break;
    case 'n':
    case 'N':
      /* news */
      if(string_compare(command, "news")) goto bad;
      do_news(player);
      break;
    case 'p':
    case 'P':
      switch(command[1]) {
      case 'a':
      case 'A':
	Matched("page");
	do_page(player, arg1, arg2);
	break;
      case 'o':
      case 'O':
	Matched("pose");
	do_pose(player, full_command);
	break;
      case 'u':
      case 'U':
	Matched("put");
	do_drop(player, arg1);
	break;
      default:
	goto bad;
      }
      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':
	Matched("say");
	do_say(player, full_command);
	break;
      case 'c':
      case 'C':
	Matched("score");
	do_score(player);
	break;
      default:
	goto bad;
      }
      break;
    case 't':
    case 'T':
      switch(command[1]) {
      case 'a':
      case 'A':
	Matched("take");
	do_get(player, arg1);
	break;
      case 'h':
      case 'H':
	Matched("throw");
	do_drop(player, arg1);
	break;
      default:
	goto bad;
      }
      break;
    case 'w':
    case 'W':
      Matched("whisper");
      do_whisper(player, arg1, arg2);
      break;
    default:
    bad:
      {
	char bebuf[BUFFER_LEN];
	if(*full_command)
	  sprintf(bebuf, "%s %s", command, full_command);
	else
	  sprintf(bebuf, "%s", command);
	if(!check_mushact(player, bebuf)) {
	  notify(player, "Huh?  (Type \"help\" for help.)");
#ifdef LOG_FAILED_COMMANDS
	  if(!controls(player, DBFETCH(player)->location)) {
	    log_status("HUH from %s(%d) in %s(%d)[%s]: %s %s\n",
		       NAME(player), player, NAME(DBFETCH(player)->location),
		       DBFETCH(player)->location,
		       NAME(OWNER(DBFETCH(player)->location)), command,
		       full_command);
	  }
#endif /* LOG_FAILED_COMMANDS */
	}
      }
      break;
    }
    if(arg1) free(arg1);
    if(arg2) free(arg2); /* check_arg */
  }
  /* unblock alarms */
  alarm_block = 0;
  if(alarm_triggered) {
    fork_and_dump();
  }
}

#undef Matched

int do_act(dbref what, const char *command, dbref player)
{
  int res = 0, loog;
  const char *y;
  static char x[4]= "v_\0";

  if(Halted(what))
    return 0;
  for(x[1] = 'a'; x[1] <= 'z' && !res; x[1]++) {
    if(y = get_attr(what, x)) { /* We can try this. */
      if(*y == '$' && index(y, ':')) { /* yay. we really can try this. */
	static char yy[BUFFER_LEN];
	strcpy(yy, y + 1);
	*index(yy, ':') ='\0';
	if(wild_match(yy, command)) {
	  res++;
	  trigobj(what, 1 + index(yy, '\0'), player);
	}
      }
    }
  }
  for(loog = 0; loog < 10; loog++)
    wptr[loog] = NULL;
  return res;
}

int check_mushact(dbref player, const char *command)
{
  dbref first;
  int res = 0;

  puts("player contents");
  for(first = DBFETCH(player)->contents; first != NOTHING;
      first = DBFETCH(first)->next)
    if(Typeof(first) == TYPE_THING)
      res += do_act(first, command, player);

  puts("location contents");
  if(DBFETCH(player)->location != NOTHING)
    for(first = DBFETCH(DBFETCH(player)->location)->contents; first != NOTHING;
	first = DBFETCH(first)->next)
      if(Typeof(first) == TYPE_THING)
	res += do_act(first, command, player);
  puts("Done.");
  return res;
}
dbref global_cause;

int do_restart(dbref who)
{
  if(!Wizard(who)) {
    notify(who, "Permission denied.");
    return 0;
  }
  kill(getpid(), 15); /* cause automatic restart. */
  return 0;
}

int do_uptime(dbref who)
{
  int x = time(NULL) - startup_time;
  sprintf(buf, "up %d days, %02d:%02d:%02d",
	  x/(60*60*24), (x/(60*60))%24, (x/60)%60,x%60);
  notify(who, buf);
  return 0;
}

int do_version(dbref who)
{
  sprintf(buf, "Version:  %s", VERSION_STRING);
  notify(who, buf);
  return 0;
}