/* wiz.c */

#include "copyright.h"

/* Wizard-only commands */

#include <string.h>
#include <math.h>
#include <sys/time.h>
#include "config.h"
#include "db.h"
#include "interface.h"
#include "match.h"
#include "externs.h"

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

#define  ANY_OWNER	-2

extern char *strchr();

void do_search(player, arg1, arg3)
    dbref player;
    const char *arg1;
    const char *arg3;
{
  int flag;
  char *arg2;
  dbref thing;
  dbref from;
  dbref to;
  int is_wizard;
  int destitute = 1;
  dbref restrict_zone;
  char *restrict_name;
  dbref restrict_owner;
  object_flag_type flag_mask;
  object_flag_type restrict_type;
  char tbuf1[BUFFER_LEN];
  int rcount, ecount, pcount, ocount;
  struct dblist *found = NULL, *ptr = NULL, *rlist = NULL,
                *elist = NULL, *olist = NULL, *plist = NULL;
  
  /* parse first argument into two */
  if (!arg1 || *arg1 == '\0')
    arg1 = "me";
  arg2 = strchr(arg1, ' ');
  if (arg2 != NULL)
    *arg2++ = '\0';		/* arg1, arg2, arg3 */
  else {
    if (*arg3 == '\0')
      arg2 = (char *) "";	/* arg1 */
    else {
      arg2 = (char *) arg1;	/* arg2, arg3 */
      arg1 = (char *) "";
    }
  }

  is_wizard = Wizard(player);

  restrict_zone = NOTHING;

  /* set limits on who we search */
  restrict_owner = NOTHING;
  if (*arg1 == '\0')
    restrict_owner = is_wizard ? ANY_OWNER : player;
  else if (arg1[0] == '#') {
    restrict_owner = atoi(&arg1[1]);
    if (restrict_owner < 0 || db_top <= restrict_owner)
      restrict_owner = NOTHING;
    else if (Typeof(restrict_owner) != TYPE_PLAYER)
      restrict_owner = NOTHING;
  } else if (strcmp(arg1, "me") == 0)
    restrict_owner = player;
  else
    restrict_owner = lookup_player(arg1);

  if (restrict_owner == NOTHING) {
    notify(player, tprintf("%s: No such player.", arg1));
    return;
  }
  /* set limits on what we search for */
  flag = 0;
  flag_mask = 0;
  restrict_name = NULL;
  restrict_type = NOTYPE;
  switch (arg2[0]) {
    case '\0':
      /* the no class requested class  :)  */
      break;
    case 'e':
      if (string_prefix("exits", arg2)) {
	restrict_name = (char *) arg3;
	restrict_type = TYPE_EXIT;
      } else
	flag = 1;
      break;
    case 'f':
      if (string_prefix("flags", arg2)) {
	/*
	 * convert_flags ignores previous values of flag_mask and
	 * restrict_type while setting them
	 */
	if (!convert_flags(player, arg3,
			   &flag_mask, &restrict_type))
	  return;
      } else
	flag = 1;
      break;
    case 'n':
      if (string_prefix("name", arg2))
	restrict_name = (char *) arg3;
      else
	flag = 1;
      break;
    case 'o':
      if (string_prefix("objects", arg2)) {
	restrict_name = (char *) arg3;
	restrict_type = TYPE_THING;
      } else
	flag = 1;
      break;
    case 'p':
      if (string_prefix("players", arg2)) {
	restrict_name = (char *) arg3;
	if (*arg1 == '\0')
	  restrict_owner = ANY_OWNER;
	restrict_type = TYPE_PLAYER;
      } else
	flag = 1;
      break;
    case 'r':
      if (string_prefix("rooms", arg2)) {
	restrict_name = (char *) arg3;
	restrict_type = TYPE_ROOM;
      } else
	flag = 1;
      break;
    case 't':
      if (string_prefix("type", arg2)) {
	if (arg3[0] == '\0')
	  break;
	if (string_prefix("room", arg3))
	  restrict_type = TYPE_ROOM;
	else if (string_prefix("exit", arg3))
	  restrict_type = TYPE_EXIT;
	else if (string_prefix("object", arg3))
	  restrict_type = TYPE_THING;
	else if (string_prefix("player", arg3)) {
	  if (*arg1 == '\0')
	    restrict_owner = ANY_OWNER;
	  restrict_type = TYPE_PLAYER;
	} else {
	  notify(player, tprintf("%s: unknown type.", arg3));
	  return;
	}
      } else
	flag = 1;
      break;
    case 'z':
      if (string_prefix("zone", arg2)) {
	init_match(player, arg3, TYPE_THING);
	match_absolute();
	restrict_zone = match_result();
      } else
	flag = 1;
      break;
    default:
      flag = 1;
  }
  if (flag) {
    notify(player, tprintf("%s: unknown class.", arg2));
    return;
  }
  /* make sure player is authorized to do requested search */
  if (!is_wizard && restrict_type != TYPE_PLAYER &&
      (restrict_owner == ANY_OWNER || restrict_owner != player)) {
    notify(player, "You need a search warrant to do that!");
    return;
  }
  /* make sure player has money to do the search */
  if (!payfor(player, FIND_COST)) {
    notify(player, tprintf("Searches cost %d %s", FIND_COST,
			   ((FIND_COST == 1) ? MONEY : MONIES)));
    return;
  }

  /* the search loop here */
  flag = 1;
  for (thing = 0; thing < db_top; thing++) {
    switch(Typeof(thing)) {
      case TYPE_PLAYER:
	found = plist;
	break;
      case TYPE_EXIT:
	found = elist;
	break;
      case TYPE_THING:
	found = olist;
	break;
      case TYPE_ROOM:
	found = rlist;
	break;
      default:
	continue;
    }
    if (restrict_type != NOTYPE && restrict_type != Typeof(thing))
	    continue;
    if ((db[thing].flags & flag_mask) != flag_mask)
	    continue;
#ifdef DESTROY
    if (db[thing].flags & GOING)
	    continue;
#endif
    if (Typeof(thing) == TYPE_PLAYER) {
	    if (is_wizard)
		    if (restrict_owner != ANY_OWNER &&
			restrict_owner != db[thing].owner)
			    continue;
    } else {
	    if (restrict_owner != ANY_OWNER &&
		restrict_owner != db[thing].owner)
		    continue;
    }
    if (restrict_name != NULL)
	    if (!string_match(db[thing].name, restrict_name))
		    continue;
    if (restrict_zone != NOTHING)
      if (restrict_zone != getzone(thing))
	continue;
    if (!found) {
      flag = 0;
      destitute = 0;
      found = listcreate(thing);
    } else
      listadd(found, thing);

    switch(Typeof(thing)) {
      case TYPE_PLAYER:
        plist = found;
        break;
      case TYPE_EXIT:
        elist = found;
        break;
      case TYPE_THING:
        olist = found;
        break;
      case TYPE_ROOM:
        rlist = found;
        break;
      default:
        break;
    }
  }
  /* if nothing found matching search criteria */
  if (destitute) {
    notify(player, "Nothing found.");
    return;
  }
  
  /* Walk down the list and print */
  rcount = ecount = pcount = ocount = 0;
  if (restrict_type == TYPE_ROOM || restrict_type == NOTYPE) {
    notify(player, "\nROOMS:");
    for (ptr = rlist; ptr; ptr = ptr->next) {
      notify(player, unparse_object(player, ptr->obj));
      rcount++;
    }
  }
  if (restrict_type == TYPE_EXIT || restrict_type == NOTYPE) {
    notify(player, "\nEXITS:");
    for (ptr = elist; ptr; ptr = ptr->next) {
      if(db[ptr->obj].exits == NOTHING)
	from = find_entrance(thing);
      else
	from = db[ptr->obj].exits;
      to = db[ptr->obj].location;
      sprintf(tbuf1, "%s [from ", unparse_object(player, ptr->obj));
      strcat(tbuf1, tprintf("%s to ", (from == NOTHING) ? "NOWHERE" :
			    unparse_object(player, from)));
      strcat(tbuf1, tprintf("%s]", (to == NOTHING) ? "NOWHERE" :
			    unparse_object(player, to)));
      notify(player, tbuf1);
      ecount++;
    }
  }
  if (restrict_type == TYPE_THING || restrict_type == NOTYPE) {
    notify(player, "\nOBJECTS:");
    for (ptr = olist; ptr; ptr = ptr->next) {
      sprintf(tbuf1, "%s [owner: ", unparse_object(player, ptr->obj));
      strcat(tbuf1, tprintf("%s]",
			    unparse_object(player, db[ptr->obj].owner)));
      notify(player, tbuf1);
      ocount++;
    }
  }
  if (restrict_type == TYPE_PLAYER ||
      (is_wizard && restrict_type == NOTYPE)) {
    notify(player, "\nPLAYERS:");
    for (ptr = plist; ptr; ptr = ptr->next) {
      strcpy(tbuf1, unparse_object(player, ptr->obj));
      if (is_wizard) {
	strcat(tbuf1, " [location: ");
	strcat(tbuf1, unparse_object(player, db[ptr->obj].location));
	strcat(tbuf1, "]");
      }
      notify(player, tbuf1);
      pcount++;
    }
  }
  notify(player, "----------  Search Done  ----------");
  notify(player,
    tprintf("Totals: Rooms...%d  Exits...%d  Objects...%d  Players...%d",
	    rcount, ecount, ocount, pcount));
  if (plist)
    listfree(plist);
  if (rlist)
    listfree(rlist);
  if(olist)
    listfree(olist);
  if(elist)
    listfree(elist);
}

object_flag_type convert_flags(player, s, p_mask, p_type)
    dbref player;
    char *s;
    object_flag_type *p_mask;
    object_flag_type *p_type;
{
  static struct {
    int id, type;
    object_flag_type bits;
  } fdata[] = {
    { 'c', TYPE_PLAYER, PLAYER_CONNECT },
    { 'd', TYPE_THING, THING_DEST_OK },
    { 'e', NOTYPE, ENTER_OK },
    { 'g', TYPE_PLAYER, PLAYER_GAGGED },
    { 'h', NOTYPE, HALT },
    { 'i', TYPE_THING, THING_IMMORTAL },
    { 'n', TYPE_ROOM, ROOM_NO_TEL },
    { 'p', TYPE_THING, THING_PUPPET },
#ifdef ROYALTY_FLAG
    { 'r', NOTYPE , ROYALTY},
#endif
    { 't', TYPE_EXIT, EXIT_TRANSPARENT },
    { 'u', TYPE_PLAYER, PLAYER_SUSPECT },
    { 'v', TYPE_THING, THING_VERBOSE },
    { 'x', NOTYPE, TERSE },
    { 'A', TYPE_ROOM, ROOM_ABODE },
#ifdef RESTRICTED_BUILDING
    { 'B', TYPE_PLAYER, PLAYER_BUILD },
#endif
    { 'C', NOTYPE, CHOWN_OK },
    { 'D', NOTYPE, DARK },
    { 'E', TYPE_EXIT, 0 },
    { 'F', TYPE_ROOM, ROOM_FLOATING },
    { 'G', NOTYPE, GOING },
    { 'H', NOTYPE, HAVEN },
#ifdef INHERIT_FLAG
    { 'I', NOTYPE, INHERIT },
#endif
    { 'J', TYPE_ROOM, ROOM_JUMP_OK },
    { 'K', TYPE_THING, THING_KEY },
    { 'K', TYPE_EXIT, EXIT_KEY },
    { 'L', TYPE_ROOM, LINK_OK },
    { 'N', NOTYPE, NOSPOOF},
    { 'O', NOTYPE, OPAQUE },
    { 'P', TYPE_PLAYER, 0 },
    { 'Q', NOTYPE, QUIET },
    { 'R', TYPE_ROOM, 0 },
    { 'S', NOTYPE, STICKY },
    { 'T', TYPE_ROOM, ROOM_TEMPLE },
    { 'U', TYPE_PLAYER, PLAYER_DARK },
    { 'V', NOTYPE, VISUAL },
    { 'W', NOTYPE, WIZARD },
    { 'X', TYPE_THING, THING_SAFE },
    { 0, 0, 0 }
  };

  int i;
  char last_id = ' ';
  object_flag_type mask, type;
  mask = 0;
  type = NOTYPE;
  while (*s != '\0') {
    for (i = 0; fdata[i].id != 0; i++) {
      if (*s == fdata[i].id) {
	/* handle object specific flag problems */
	if (fdata[i].type != NOTYPE) {
	  /* make sure we aren't specific to a different type */
	  if (type != NOTYPE && type != fdata[i].type) {
	    notify(player,
		   tprintf("Flag '%c' conflicts with '%c'.",
			   last_id, fdata[i].id));
	    return 0;
	  }
	  /* make us object specific to this type */
	  type = fdata[i].type;

	  /* always save last specific flag id */
	  last_id = *s;
	}
	/* add new flag into search mask */
	mask |= fdata[i].bits;

	/* stop searching for *this* flag */
	break;
      }
    }

    /* flag not found */
    if (fdata[i].id == 0) {
      notify(player, tprintf("%c: unknown flag.", (int) *s));
      return 0;
    }
    ++s;
  }

  /* return new mask and type */
  *p_mask = mask;
  *p_type = type;
  return 1;
}
#ifdef WCREAT
void do_pcreate(creator, player_name, player_password)
    dbref creator;
    const char *player_name;
    const char *player_password;
{
  dbref player;
  if (!Wizard(creator)) {
    notify(creator, "Only wizards are allowed to create people!");
    return;
  }
  player = create_player(player_name, player_password);
  if (player == NOTHING) {
    notify(creator, tprintf("failure creating '%s'", player_name));
    return;
  }
  notify(creator, tprintf("New player '%s' created with password '%s'",
			  player_name, player_password));
  fprintf(stderr, "CREATION: %s(#%d) created %s(#%d)\n",
	  db[creator].name, creator, player_name, player);
}
#endif

#ifdef QUOTA
void do_quota(player, arg1, arg2)
    dbref player;
    char *arg1, *arg2;
{
  dbref who, thing;
  ATTR *a;
  int owned, limit;

  if (!arg1 && !*arg1)
    who = player;
  else {
    init_match(player, arg1, TYPE_PLAYER);
    match_me();
    match_player();
    match_neighbor();
    match_absolute();
    if ((who = noisy_match_result()) == NOTHING)
      return;
  }

  if (!Wizard(player)) {
    if (*arg2 != '\0') {
      notify(player, "Only wizards may change a quota.");
      return;
    }
    if (player != who) {
      notify(player, "Only wizards may look at someone else's quota.");
      return;
    }
  }
  /* count up all owned objects */
  owned = -1;			/* a player is never included in his own
				 * quota */
  for (thing = 0; thing < db_top; thing++) {
    if (db[thing].owner == who)
      if ((db[thing].flags & (TYPE_THING | GOING)) != (TYPE_THING | GOING))
	++owned;
  }

  if (Wizard(who))
    notify(player, tprintf("Objects: %d   Limit: UNLIMITED", owned));
  else {
    /* calculate and/or set new  */
    if (*arg2 == '\0') {
      a = atr_get(who, "RQUOTA");
      if(a)
        limit = owned + atoi(uncompress(a->value));
      else
	limit = owned;
    } else {
      limit = atoi(arg2);
    }
    /* stored as a relative value */
    (void) atr_add(who, "RQUOTA", tprintf("%d", limit-owned), GOD, NOTHING);

    notify(player, tprintf("Objects: %d   Limit: %d", owned, limit));
  }
}

void do_allquota(player, arg1)
    dbref player;
    char *arg1;
{
  int limit, owned, max;
  dbref who, thing;
  ATTR *a;

  if (!God(player)) {
    notify(player, "Who do you think you are, GOD?");
    return;
  }
  limit = atoi(arg1);
  notify(player, "working...limit is %d",limit);
  for (who = 0; who < db_top; who++) {
    if (Typeof(who) != TYPE_PLAYER)
      continue;

    /* count up all owned objects */
    owned = -1;			/* a player is never included in his own
				 * quota */
    for (thing = 0; thing < db_top; thing++) {
      if (db[thing].owner == who)
	if ((db[thing].flags & (TYPE_THING | GOING)) != (TYPE_THING | GOING))
	  ++owned;
    }

    a = atr_get(who, "RQUOTA");
    if(a)    
      max = atoi(uncompress(a->value));
    else
      max = 0;
    notify(player, tprintf("%s: left(%d) owned(%d)",db[who].name, max, owned));

    /* stored as a relative value */
    (void) atr_add(who, "RQUOTA", tprintf("%d", limit-owned), GOD, NOTHING);
  }
  notify(player, "done.");
}
#endif QUOTA

void do_teleport(player, arg1, arg2)
    dbref player;
    const char *arg1;
    const char *arg2;
{
  dbref victim;
  dbref destination;
  const char *to;
  dbref absroom;       /* "absolute room", for NO_TEL check */
  int rec = 0;         /* recursion counter */

  /* get victim, destination */
  if (*arg2 == '\0') {
    victim = player;
    to = arg1;
  } else {
    init_match(player, arg1, NOTYPE);
    match_neighbor();
    match_possession();
    match_me();
    match_absolute();
    match_player();

    if ((victim = noisy_match_result()) == NOTHING) {
      return;
    }
    to = arg2;
  }

  /* get destination */

  if (!string_compare(to, "home")) {
    destination = db[victim].exits;
  } else {
    init_match(player, to, TYPE_PLAYER);
    match_here();
    match_absolute();
    match_neighbor();
    match_me();
    match_player();
    match_exit();
    destination = match_result();
  }

  switch (destination) {
    case NOTHING:
      notify(player, "No match.");
      break;
    case AMBIGUOUS:
      notify(player, "I don't know which destination you mean!");
      break;
    default:
      /* check victim, destination types, teleport if ok */
      if(recursive_member(destination, victim, 0) || (victim == destination)) {
	notify(player, "Bad destination.");
	return;
      }

      if (!Hasprivs(player) &&
	  (Typeof(victim) == TYPE_EXIT ||
	   Typeof(victim) == TYPE_ROOM ||
	   ((Typeof(victim) == TYPE_PLAYER) &&
	    (Typeof(destination) == TYPE_PLAYER)))) {
	notify(player, "Bad destination.");
	return;
      }

      /* if royal or wiz and destination is player, tel to location */
      if ((Typeof(destination) == TYPE_PLAYER) &&
	  (Hasprivs(player)) && (Typeof(victim) == TYPE_PLAYER) &&
	  (destination != HOME)) {
	safe_tel(victim, db[destination].location);
	return;
      }

      /* check needed for NOTHING. Especially important for unlinked exits */
      if ((absroom = db[victim].location) == NOTHING) {
	if (Typeof(victim) != TYPE_EXIT) {
	  notify(victim, "You're in the Void. This is not a good thing.");
	  do_move(victim, "home", 0);
	  return;
	}
	/* if the thing is an unlinked exit, no need for NO_TEL check */
      }
      else {
	/* valid location, perform other checks */

	/* if player is inside himself, send him home */
	if (absroom == victim) {
	  notify(player, "What are you doing inside of yourself?");
	  do_move(victim, "home", 0);
	  return;
	}

        /* find the "absolute" room */
	while ((Typeof(absroom) != TYPE_ROOM) && (rec < 15)) {
	  absroom = db[absroom].location;
	  rec++;
	}

	/* if there are a lot of containers, send him home */
	if (rec > 15) {
	  notify(victim, "You're in too many containers.");
	  do_move(victim, "home", 0);
	  return;
	}
	
	/* note that we check the NO_TEL status of the victim rather than */
	/* the player that issued the command. This prevents someone in a */
        /* NO_TEL room from having one of his objects @tel him out. The   */
        /* control check, however, is detemined by command-giving player. */

	/* now check to see if the absolute room is set NO_TEL */
	if ((db[absroom].flags & ROOM_NO_TEL)
	    && !controls(player, absroom)
	    && !Hasprivs(player)) {
	  notify(player, "Teleports are not allowed in this room.");
	  return;
	}
      }

      if (Typeof(destination) != TYPE_EXIT) {
	if (Hasprivs(player) ||
	    (controls(player,victim) &&
	     (((Typeof(destination) == TYPE_ROOM) &&
	       (db[destination].flags & ROOM_JUMP_OK)) ||
	      controls(player,destination))) ||
	    (controls(player, db[victim].location) &&
	     (controls(player, destination) ||
	      ((Typeof(destination) == TYPE_ROOM) &&
	       (db[destination].flags & ROOM_JUMP_OK))))) {
	  safe_tel(victim, destination);
	  if ((victim != player) &&
	      !((Typeof(victim) == TYPE_THING) &&
		(db[victim].flags & THING_PUPPET) &&
		(db[victim].owner == db[player].owner)))
            notify(player, "Teleported.");
	  return;
	}
	notify(player, "Permission denied.");
	return;
      } else {
	if (controls(player, victim) ||
	    controls(player, db[victim].location)) {
	  do_move(victim, to, 0);
        } else {
	  notify(victim,
		 tprintf("%s tries to impose his will on you and fails.",
			 db[player].name));
        }
      }
  }
}

void do_force(player, what, command)
    dbref player;
    const char *what;
    char *command;
{
  dbref victim;
  
  if ((victim = match_controlled(player, what)) == NOTHING) {
    notify(player, "Sorry.");
    return;
  }
  if ((Typeof(victim) == TYPE_ROOM) || (Typeof(victim) == TYPE_EXIT)) {
    notify(player, "You can only force players and things.");
    return;
  }
  if (Wizard(player)) {
    static char pl[BUFFER_LEN], vic[BUFFER_LEN];
    strcpy(pl, unparse_object(player, player));
    strcpy(vic, unparse_object(player, victim));
    if (db[victim].owner != db[player].owner)
      fprintf(stderr, "** FORCE ** [%s: %s] %s\n", pl, vic, command);
    else
      fprintf(stderr, "FORCE [%s:%s] %s\n", pl, vic, command);
  }
  if (victim == GOD) {
    notify(player, "You can't force God!");
    return;
  }
#ifdef INHERIT_FLAG
  if (!Inherit(player) && Inherit(victim)) {
    notify(player, "Authorization failed.");
    return;
  }
#endif
  /* force victim to do command */
  parse_que(victim, command, player);
}

int try_force(player, command)
    dbref player;
    const char *command;
{
  dbref thing;
  char *s;
  char tbuf1[BUFFER_LEN];

  /* first see if command prefixed by object # */
  if (*command == '#') {
    strcpy(tbuf1, command);
    for (s = tbuf1; *s && *s != ' '; s++) ;
    if (!*s)
      return (0);
    *s++ = 0;
  } else {
    /* try inventory */
    if(((thing=pref_match(player, db[player].contents, command))!=NOTHING) ||
	((db[player].location != NOTHING) &&
	 ((thing = pref_match(player, db[db[player].location].contents,
			      command)) != NOTHING))) {
      strcpy(tbuf1, command);
      s = tbuf1 + strlen(db[thing].name);
      if (!*s)
        return (0);
      *s++ = 0;
    } else
      return (0);
  }
  do_force(player, tbuf1, s);
  return (1);
}

void do_stats(player, name)
    dbref player;
    const char *name;
{
  dbref garbage;
  dbref rooms;
  dbref exits;
  dbref things;
  dbref players;
  dbref total;
  dbref i;
  dbref owner;
  /* Patched 12/9/90 */
  if (*name == '\0')
    owner = ANY_OWNER;
  else if (*name == '#') {
    owner = atoi(&name[1]);
    if (owner < 0 || db_top <= owner)
      owner = NOTHING;
    else if (Typeof(owner) != TYPE_PLAYER)
      owner = NOTHING;
  } else if (strcmp(name, "me") == 0)
    owner = player;
  else
    owner = lookup_player(name);
  if (owner == NOTHING) {
    notify(player, tprintf("%s: No such player.", name));
    return;
  }
  if (!Wizard(player))
    if (owner != ANY_OWNER && owner != player) {
      notify(player, "You need a search warrant to do that!");
      return;
    }
  total = rooms = exits = things = players = garbage = 0;
  for (i = 0; i < db_top; i++) {
    if (owner == ANY_OWNER || owner == db[i].owner) {
      if (db[i].flags & GOING) {
        garbage++;
      } else {
        total++;
        switch (Typeof(i)) {
	  case TYPE_ROOM:
	    rooms++;
	    break;
	  case TYPE_EXIT:
	    exits++;
	    break;
	  case TYPE_THING:
	    things++;
	    break;
	  case TYPE_PLAYER:
	    players++;
	    break;
	  default:
	    break;
        }
      }
    }
  }
  if (owner == ANY_OWNER) {
    total = total + garbage;
    notify(player, tprintf("%d objects = %d rooms, %d exits, %d things, %d players, %d garbage.",
                           total, rooms, exits, things, players, garbage));
  } else {
    notify(player, tprintf("%d objects = %d rooms, %d exits, %d things, %d players.",
                           total, rooms, exits, things, players));
  }
#ifdef MEM_CHECK
  if(God(player))
    list_mem_check(player);
#endif
#ifdef SLOW_STATISTICS
  if(God(player))
    dump_malloc_data(player);
#endif
#if defined(DEBUG) && defined(USE_SMALLOC)
  if(Wizard(player))
    notify(player, tprintf("%d bytes memory used.", memused()));
  if(God(player))
    verify_sfltable(player);
#endif
}

void do_toad(player, name)
    dbref player;
    const char *name;
{
  dbref victim;
  char tbuf1[BUFFER_LEN];

  if (!Wizard(player)) {
    notify(player, "Only a Wizard can toad a person.");
    return;
  }
  init_match(player, name, TYPE_PLAYER);
  match_neighbor();
  match_absolute();
  match_player();
  if ((victim = noisy_match_result()) == NOTHING)
    return;

  if (Typeof(victim) != TYPE_PLAYER) {
    notify(player, "Try @destroy instead!");
  } else if (Wizard(victim)) {
    notify(player, "You can't toad a Wizard.");
  } else if (db[victim].contents != NOTHING) {
    notify(player,"You need to destroy their contents first.");
  } else {
    /* we're ok */
    /* do it */
    s_Pass(victim, NULL);
    db[victim].flags = TYPE_THING;
    db[victim].owner = player;	/* you get it */
    s_Pennies(victim, 1);

    /* notify people */
    notify(victim,
	   "You have been turned into a slimy toad.");
    notify(player, tprintf("You toaded %s!", db[victim].name));
    fprintf(stderr, "*** TOAD *** %s(#%d) toaded %s(#%d)\n",
	    db[player].name, player, db[victim].name, victim);

    /* reset name */
    delete_player(victim);
    boot_off(victim);
    sprintf(tbuf1,
	    "A wart toad named %s", db[victim].name);
    SET(db[victim].name, tbuf1);
    do_chownall(player, name, "");
  }
}

void do_newpassword(player, name, password)
    dbref player;
    const char *name;
    const char *password;
{
  dbref victim;
  if (!Wizard(player)) {
    notify(player, "Your delusions of grandeur have been duly noted.");
    return;
  } else if ((victim = lookup_player(name)) == NOTHING) {
    notify(player, "No such player.");
  } else if (*password != '\0' && !ok_password(password)) {
    /* Wiz can set null passwords, but not bad passwords */
    notify(player, "Bad password.");
  } else if (God(victim) && !God(player)) {
    notify(player, "You cannot change that player's password.");
  } else {
    /* it's ok, do it */
    s_Pass(victim, crypt(password,"XX"));
    notify(player, "Password changed.");
    notify(victim, tprintf("Your password has been changed by %s.",
			   db[player].name));
    fprintf(stderr, "***NEWPASSWORD***: %s(#%d) changed password of %s(#%d)\n",
	    db[player].name, player, db[victim].name, victim);
  }
}

void do_boot(player, name)
    dbref player;
    const char *name;
{
  dbref victim;
  
  if (!Wizard(player)) {
    notify(player, "Only a Wizard can boot another player off!");
    return;
  }
  init_match(player, name, TYPE_PLAYER);
  match_neighbor();
  match_absolute();
  match_player();
  if ((victim = noisy_match_result()) == NOTHING)
    return;

  if (God(victim)) {
    notify(player, "Thunder booms in the distance.");
    return;
  }

  if (victim == player) {
    notify(player, "You're not that limber.");
    return;
  }

  if (Typeof(victim) != TYPE_PLAYER && !God(player)) {
    notify(player, "You can only boot off other players!");
  } else {
    static char th[BUFFER_LEN], own[BUFFER_LEN];

    strcpy(th, unparse_object(player, player));
    strcpy(own, unparse_object(player, victim));
    fprintf(stderr, "*** BOOT *** %s booted %s\n", th, own);
    /* we're ok */
    /* do it */
    /* notify people */
    notify(player, tprintf("You booted %s off!", db[victim].name));
    notify(victim, "You are politely shown to the door.");
    boot_off(victim);
    /* reset name */
  }
}

void do_chownall(player, name, target)
    dbref player;
    const char *name;
    const char *target;
{
  int i;
  dbref victim;
  dbref n_target;

  if(!Wizard(player)) {
    notify(player,"Try asking them first!");
    return;
  }
  init_match(player,name,TYPE_PLAYER);
  match_neighbor();
  match_absolute();
  match_player();
  if((victim = noisy_match_result()) == NOTHING)
    return;

  if(!target || !*target) {
    n_target = NOTHING;
  } else {
    init_match(player, target, TYPE_PLAYER);
    match_neighbor();
    match_absolute();
    match_player();
    if((n_target = noisy_match_result()) == NOTHING)
      return;
  }

  for(i = 0; i <= db_top; i++) {
    if((db[i].owner == victim) && (Typeof(i) != TYPE_PLAYER))
      db[i].owner = (n_target == NOTHING ? player : n_target);
  }
  notify(player, "Object ownership changed.");
}

void do_chzoneall(player, name, target)
     dbref player;
     const char *name;
     const char *target;
{
  int i;
  dbref victim;
  dbref zone;

  if (!Wizard(player)) {
    notify(player, "You do not have the power to change reality.");
    return;
  }

  init_match(player, name, TYPE_PLAYER);
  match_neighbor();
  match_absolute();
  match_player();
  if ((victim = noisy_match_result()) == NOTHING)
    return;

  if (!target || !*target) {
    notify(player, "No zone specified.");
    return;
  }

  if (!string_compare(target, "none"))
    zone = NOTHING;
  else {
    init_match(player, target, TYPE_THING);
    match_neighbor();
    match_possession();
    match_absolute();
    switch (zone = match_result()) {
    case NOTHING:
      notify(player, "I can't seem to find that.");
      return;
    case AMBIGUOUS:
      notify(player, "I don't know which one you mean!");
      return;
    }
    if (Typeof(zone) != TYPE_THING) {
      notify(player, "Zones may only be defined by objects.");
      return;
    }
  }
   
  for (i = 0; i <= db_top; i++) {
    if (db[i].owner == victim)
      db[i].zone = zone;
  }
  notify(player, "Zone changed for all objects.");
}

extern char cf_motd_msg[BUFFER_LEN], cf_wizmotd_msg[BUFFER_LEN],
	    cf_nologinmotd_msg[BUFFER_LEN];

extern char *reconstruct_message();

void do_motd(player, key, arg1, arg2)
    dbref player;
    int key;
    const char *arg1, *arg2;
{
  char *message;

  if(!Wizard(player) && (key != 3)) {
    notify(player,
       "You may get 15 minutes of fame and glory in life, but not right now.");
    return;
  }
  message = reconstruct_message(arg1, arg2);

  switch(key) {
    case 1:
      strcpy(cf_motd_msg, message);
      notify(player, "Motd set.");
      break;
    case 2:
      strcpy(cf_wizmotd_msg, message);
      notify(player, "Wizard motd set.");
      break;
    case 4:
      strcpy(cf_nologinmotd_msg, message);
      notify(player, "Nologin motd set.");
      break;
    default:
      notify(player, tprintf("MOTD: %s", cf_motd_msg));
      if(Wizard(player)) {
        notify(player, tprintf("Wiz MOTD: %s", cf_wizmotd_msg));
        notify(player, tprintf("Nologin MOTD: %s", cf_nologinmotd_msg));
      }
  }
}

void do_login(player, state)
    dbref player;
    const char *state;
{
  extern int login_allow;

  time_t tt;
  tt = time((time_t *) 0);

  if(!Wizard(player)) {
    notify(player, "Unable to authenticate you, sorry.");
    return;
  }
  if(!string_compare(state, "off")) {
    notify(player, "Logins disabled.");
    login_allow = 0;
    fprintf(stderr, "*** LOGINS DISABLED *** by %s(#%d) at %s",
	    db[player].name, player, ctime(&tt));
  } else if (!string_compare(state, "on")) {
    notify(player, "Logins enabled.");
    login_allow = 1;
    fprintf(stderr, "*** LOGINS ENABLED *** by %s(#%d) at %s",
  	    db[player].name, player, ctime(&tt));
  } else {
    notify(player, tprintf("Logins are currently %s.",
                           ((login_allow) ? "enabled" : "disabled")));
  }
  return;
}

void fun_lsearch(buff, args, privs, dumm1)
     char *buff;
     char *args[10];
     dbref privs;
     dbref dumm1;
{
  /* takes arguments in the form of: player, class, restriction */

  int is_wiz;
  dbref restrict_owner;
  char *who; char *class; char *restrict;
  int flag;
  object_flag_type flag_mask;
  char *restrict_name;
  object_flag_type restrict_type;
  dbref restrict_zone;
  dbref thing;
  int destitute = 1;
  struct dblist *found = NULL, *ptr = NULL, *list = NULL;
  
  if (!payfor(privs, FIND_COST)) {
    strcpy(buff, "#-1 NOT ENOUGH MONEY TO DO SEARCH");
    return;
  }

  is_wiz = Wizard(privs);

  who = args[0];
  class = args[1];
  restrict = args[2];

  restrict_zone = NOTHING;

  /* set limits on who we search */
  restrict_owner = NOTHING;
  if (!string_compare(who, "all"))
    restrict_owner = is_wiz ? ANY_OWNER : privs;
  else if (!string_compare(who, "me"))
    restrict_owner = privs;
  else
    restrict_owner = lookup_player(who);

  if (restrict_owner == NOTHING) {
    strcpy(buff, "#-1 NO SUCH PLAYER");
    return;
  }

  /* set limits on what we search for */
  flag = 0;
  flag_mask = 0;
  restrict_name = NULL;
  restrict_type = NOTYPE;
  switch (class[0]) {
  case 'e':
    if (string_prefix("exits", class)) {
      restrict_name = (char *) restrict;
      restrict_type = TYPE_EXIT;
    } else
      flag = 1;
    break;
  case 'f':
    if (string_prefix("flags", class)) {
      if (!convert_flags(privs, restrict, &flag_mask, &restrict_type)) {
	strcpy(buff, "#-1 UNKNOWN FLAG");
	return;
      }
    } else
      flag = 1;
    break;
  case 'n':
    if (string_prefix("name", class))
      restrict_name = (char *) restrict;
    else if (!string_prefix("none", class))
      flag = 1;
    break;
  case 'o':
    if (string_prefix("objects", class)) {
      restrict_name = (char *) restrict;
      restrict_type = TYPE_THING;
    } else
      flag = 1;
    break;
  case 'p':
    if (string_prefix("players", class)) {
      restrict_name = (char *) restrict;
      restrict_type = TYPE_PLAYER;
    } else
      flag = 1;
    break;
  case 'r':
    if (string_prefix("rooms", class)) {
      restrict_name = (char *) restrict;
      restrict_type = TYPE_ROOM;
    } else
      flag = 1;
    break;
  case 't':
    if (string_prefix("type", class)) {
      if (!string_compare(restrict, "none"))
	break;
      if (string_prefix("room", restrict))
	restrict_type = TYPE_ROOM;
      else if (string_prefix("exit", restrict))
	restrict_type = TYPE_EXIT;
      else if (string_prefix("object", restrict))
	restrict_type = TYPE_THING;
      else if (string_prefix("player", restrict))
	restrict_type = TYPE_PLAYER;
      else {
	strcpy(buff, "#-1 UNKNOWN TYPE");
	return;
      }
    } else
      flag = 1;
    break;
  case 'z':
    if (string_prefix("zone", class)) {
      init_match(privs, restrict, TYPE_THING);
      match_absolute();
      restrict_zone = match_result();
    } else
      flag = 1;
    break;
  default:
    flag = 1;
  }
  if (flag) {
    strcpy(buff, "#-1 UNKNOWN TYPE");
    return;
  }
 
  /* check privs */
  if (!is_wiz && restrict_type != TYPE_PLAYER &&
      (restrict_owner == ANY_OWNER || restrict_owner != privs)) {
    strcpy(buff, "#-1 PERMISSION DENIED");
    return;
  }

  /* search loop */
  flag = 1;
  for (thing = 0; thing < db_top; thing++) {
    found = list;
    if (restrict_type != NOTYPE && restrict_type != Typeof(thing))
      continue;
    if ((db[thing].flags & flag_mask) != flag_mask)
      continue;
#ifdef DESTROY
    if (db[thing].flags & GOING)
      continue;
#endif
    if (Typeof(thing) == TYPE_PLAYER) {
      if (is_wiz)
	if (restrict_owner != ANY_OWNER &&
	    restrict_owner != db[thing].owner)
	  continue;
    } else {
      if (restrict_owner != ANY_OWNER &&
	  restrict_owner != db[thing].owner)
	continue;
    }
    if (restrict_name != NULL)
      if (!string_match(db[thing].name, restrict_name))
	continue;
    if (restrict_zone != NOTHING)
      if (restrict_zone != getzone(thing))
	continue;
    if (!found) {
      flag = 0;
      destitute = 0;
      found = listcreate(thing);
    } else
      listadd(found, thing);

    list = found;
  }

  /* nothing found matching search criteria */
  if (destitute) {
    strcpy(buff, "#-1 NOTHING FOUND");
    return;
  }

  /* go down the list and print into a buffer */
  strcpy(buff, "");
  for (ptr = list; ptr; ptr = ptr->next) {
    if (strlen(buff) + 10 < BUFFER_LEN)
      strcat(buff, tprintf("#%d ", ptr->obj));
    else
      strcpy(buff, "#-1 SEARCH LIST TOO LONG");
  }

  if (list)
    listfree(list);
}