pennmush/game/data/
pennmush/game/log/
pennmush/game/save/
pennmush/game/txt/evt/
pennmush/game/txt/nws/
pennmush/os2/
pennmush/po/
pennmush/win32/msvc.net/
pennmush/win32/msvc6/
/**
 * \file wiz.c
 *
 * \brief Wizard commands in PennMUSH.
 *
 *
 */

#include "copyrite.h"
#include "config.h"

#ifdef I_UNISTD
#include <unistd.h>
#endif
#include <string.h>
#include <math.h>
#ifdef I_SYS_TIME
#include <sys/time.h>
#else
#include <time.h>
#endif
#include <stdlib.h>
#include <ctype.h>
#include <signal.h>
#ifdef I_FCNTL
#include <fcntl.h>
#endif
#ifdef WIN32
#include <windows.h>
#include "process.h"
#endif
#include "conf.h"
#include "externs.h"
#include "mushdb.h"
#include "attrib.h"
#include "match.h"
#include "access.h"
#include "parse.h"
#include "mymalloc.h"
#include "flags.h"
#include "lock.h"
#include "log.h"
#include "game.h"
#include "command.h"
#include "dbdefs.h"
#include "extmail.h"


#include "confmagic.h"

extern dbref find_entrance(dbref door);
struct db_stat_info *get_stats(dbref owner);
extern dbref find_player_by_desc(int port);


#ifndef WIN32
#ifdef I_SYS_FILE
#include <sys/file.h>
#endif
#endif

static int tport_dest_ok(dbref player, dbref victim, dbref dest);
static int tport_control_ok(dbref player, dbref victim, dbref loc);
static int mem_usage(dbref thing);
static int raw_search(dbref player, const char *owner, const char *class,
		      const char *restriction, const char *start,
		      const char *stop, dbref **result, PE_Info * pe_info);

#ifdef INFO_SLAVE
void kill_info_slave(void);
#endif

extern char confname[BUFFER_LEN];
extern char errlog[BUFFER_LEN];

/** Create a player by Wizard fiat.
 * \verbatim
 * This implements @pcreate.
 * \endverbatim
 * \param creator the enactor.
 * \param player_name name of player to create.
 * \param player_password password for player.
 * \return dbref of created player object, or NOTHING if failure.
 */
dbref
do_pcreate(dbref creator, const char *player_name, const char *player_password)
{
  dbref player;

  if (!Create_Player(creator)) {
    notify(creator, T("You do not have the power over body and mind!"));
    return NOTHING;
  }
  if (!can_pay_fees(creator, 0))
    return NOTHING;
  player = create_player(player_name, player_password, "None", "None");
  if (player == NOTHING) {
    notify_format(creator, T("Failure creating '%s' (bad name)"), player_name);
    return NOTHING;
  }
  if (player == AMBIGUOUS) {
    notify_format(creator, T("Failure creating '%s' (bad password)"),
		  player_name);
    return NOTHING;
  }
  notify_format(creator, T("New player '%s' (#%d) created with password '%s'"),
		player_name, player, player_password);
  do_log(LT_WIZ, creator, player, T("Player creation"));
  return player;
}

/** Set or check a player's quota.
 * \verbatim
 * This implements @quota and @squota.
 * \endverbatim
 * \param player the enactor.
 * \param arg1 name of player whose quota should be set or checked.
 * \param arg2 amount to set or adjust quota, ignored if checking.
 * \param set_q if 1, set quota; if 0, check quota.
 */
void
do_quota(dbref player, const char *arg1, const char *arg2, int set_q)
{
  dbref who, thing;
  int owned, limit, adjust;

  /* determine the victim */
  if (!arg1 || !*arg1 || !strcmp(arg1, "me"))
    who = player;
  else {
    who = lookup_player(arg1);
    if (who == NOTHING) {
      notify(player, T("No such player."));
      return;
    }
  }

  /* check permissions */
  if (!Wizard(player) && set_q) {
    notify(player, T("Only wizards may change a quota."));
    return;
  }
  if (!Do_Quotas(player) && !See_All(player) && (player != who)) {
    notify(player, T("You can't 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 (Owner(thing) == who)
      if (!IsGarbage(thing))
	++owned;
  }

  /* the quotas of priv'ed players are unlimited and cannot be set. */
  if (NoQuota(who) || !USE_QUOTA) {
    notify_format(player, T("Objects: %d   Limit: UNLIMITED"), owned);
    return;
  }
  /* if we're not doing a change, determine the mortal's quota limit. 
   * RQUOTA is the objects _left_, not the quota itself.
   */

  if (!set_q) {
    limit = get_current_quota(who);
    notify_format(player, T("Objects: %d   Limit: %d"), owned, owned + limit);
    return;
  }
  /* set a new quota */
  if (!arg2 || !*arg2) {
    limit = get_current_quota(who);
    notify_format(player, T("Objects: %d   Limit: %d"), owned, owned + limit);
    notify(player, T("What do you want to set the quota to?"));
    return;
  }
  adjust = ((*arg2 == '+') || (*arg2 == '-'));
  if (adjust)
    limit = owned + get_current_quota(who) + atoi(arg2);
  else
    limit = atoi(arg2);
  if (limit < owned)		/* always have enough quota for your objects */
    limit = owned;

  (void) atr_add(Owner(who), "RQUOTA", tprintf("%d", limit - owned), GOD,
		 NOTHING);

  notify_format(player, T("Objects: %d   Limit: %d"), owned, limit);
}


/** Check or set quota globally.
 * \verbatim
 * This implements @allquota.
 * \endverbatim
 * \param player the enactor.
 * \param arg1 new quota limit, as a string.
 * \param quiet if 1, don't display every player's quota.
 */
void
do_allquota(dbref player, const char *arg1, int quiet)
{
  int oldlimit, limit, owned;
  dbref who, thing;

  if (!God(player)) {
    notify(player, T("Who do you think you are, GOD?"));
    return;
  }
  if (!arg1 || !*arg1) {
    limit = -1;
  } else if (!is_integer(arg1)) {
    notify(player, T("You can only set quotas to a number."));
    return;
  } else {
    limit = parse_integer(arg1);
    if (limit < 0) {
      notify(player, T("You can only set quotas to a positive number."));
      return;
    }
  }

  for (who = 0; who < db_top; who++) {
    if (!IsPlayer(who))
      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 (Owner(thing) == who)
	if (!IsGarbage(thing))
	  ++owned;
    }

    if (NoQuota(who)) {
      if (!quiet)
	notify_format(player, "%s: Objects: %d   Limit: UNLIMITED",
		      Name(who), owned);
      continue;
    }
    if (!quiet) {
      oldlimit = get_current_quota(who);
      notify_format(player, "%s: Objects: %d   Limit: %d",
		    Name(who), owned, oldlimit);
    }
    if (limit != -1) {
      if (limit <= owned)
	(void) atr_add(who, "RQUOTA", "0", GOD, NOTHING);
      else
	(void) atr_add(who, "RQUOTA", tprintf("%d", limit - owned), GOD,
		       NOTHING);
    }
  }
  if (limit == -1)
    notify(player, T("Quotas not changed."));
  else
    notify_format(player, T("All quotas changed to %d."), limit);
}

static int
tport_dest_ok(dbref player, dbref victim, dbref dest)
{
  /* can player legitimately send something to dest */

  if (Tel_Anywhere(player))
    return 1;

  if (controls(player, dest))
    return 1;

  /* beyond this point, if you don't control it and it's not a room, no hope */
  if (!IsRoom(dest))
    return 0;

  /* Check for a teleport lock. It fails if the player is not wiz or
   * royalty, and the room is tport-locked against the victim, and the
   * victim does not control the room.
   */
  if (!eval_lock(victim, dest, Tport_Lock))
    return 0;

  if (JumpOk(dest))
    return 1;

  return 0;
}

static int
tport_control_ok(dbref player, dbref victim, dbref loc)
{
  /* can player legitimately move victim from loc */

  if (God(victim) && !God(player))
    return 0;

  if (Tel_Anything(player))
    return 1;

  if (controls(player, victim))
    return 1;

  /* mortals can't @tel HEAVY players just on basis of location ownership */

  if (controls(player, loc) && (!Heavy(victim) || Owns(player, victim)))
    return 1;

  return 0;
}

/** Teleport something somewhere.
 * \verbatim
 * This implements @tel.
 * \endverbatim
 * \param player the enactor.
 * \param arg1 the object to teleport (or location if no object given)
 * \param arg2 the location to teleport to.
 * \param silent if 1, don't trigger teleport messagse.
 * \param inside if 1, always @tel to inventory, even of a player
 */
void
do_teleport(dbref player, const char *arg1, const char *arg2, int silent,
	    int inside)
{
  dbref victim;
  dbref destination;
  dbref loc;
  const char *to;
  dbref absroom;		/* "absolute room", for NO_TEL check */

  /* get victim, destination */
  if (*arg2 == '\0') {
    victim = player;
    to = arg1;
  } else {
    if ((victim =
	 noisy_match_result(player, arg1, NOTYPE,
			    MAT_OBJECTS | MAT_ENGLISH)) == NOTHING) {
      return;
    }
    to = arg2;
  }

  if (IsRoom(victim)) {
    notify(player, T("You can't teleport rooms."));
    return;
  }
  if (IsGarbage(victim)) {
    notify(player, T("Garbage belongs in the garbage dump."));
    return;
  }
  /* get destination */

  if (!strcasecmp(to, "home")) {
    /* If the object is @tel'ing itself home, treat it the way we'd  
     * treat a 'home' command 
     */
    if (player == victim) {
      if (Fixed(victim))
	notify(player, T("You can't do that IC!"));
      else
	safe_tel(victim, HOME, silent);
      return;
    } else
      destination = Home(victim);
  } else {
    destination = match_result(player, to, TYPE_PLAYER, MAT_EVERYTHING);
  }

  switch (destination) {
  case NOTHING:
    notify(player, T("No match."));
    break;
  case AMBIGUOUS:
    notify(player, T("I don't know which destination you mean!"));
    break;
  case HOME:
    destination = Home(victim);
    /* FALL THROUGH */
  default:
    /* check victim, destination types, teleport if ok */
    if (!GoodObject(destination) || IsGarbage(destination)) {
      notify(player, T("Bad destination."));
      return;
    }
    if (recursive_member(destination, victim, 0)
	|| (victim == destination)) {
      notify(player, T("Bad destination."));
      return;
    }
    if (!Tel_Anywhere(player) && IsPlayer(victim) && IsPlayer(destination)) {
      notify(player, T("Bad destination."));
      return;
    }
    if (IsExit(victim)) {
      /* Teleporting an exit means moving its source */
      if (!IsRoom(destination)) {
	notify(player, T("Exits can only be teleported to other rooms."));
	return;
      }
      if (Going(destination)) {
	notify(player,
	       T("You can't move an exit to someplace that's crumbling."));
	return;
      }
      if (!GoodObject(Home(victim)))
	loc = find_entrance(victim);
      else
	loc = Home(victim);
      /* Unlike normal teleport, you must control the destination 
       * or have the open_anywhere power
       */
      if (!(tport_control_ok(player, victim, loc) &&
	    (controls(player, destination) || Open_Anywhere(player)))) {
	notify(player, T("Permission denied."));
	return;
      }
      /* Remove it from its old room */
      Exits(loc) = remove_first(Exits(loc), victim);
      /* Put it into its new room */
      Source(victim) = destination;
      PUSH(victim, Exits(destination));
      if (!Quiet(player) && !(Quiet(victim) && (Owner(victim) == player)))
	notify(player, T("Teleported."));
      return;
    }
    loc = Location(victim);

    /* if royal or wiz and destination is player, tel to location unless
     * using @tel/inside
     */
    if (IsPlayer(destination) && Tel_Anywhere(player) && IsPlayer(victim)
	&& !inside) {
      if (!silent && loc != Location(destination))
	did_it(victim, victim, NULL, NULL, "OXTPORT", NULL, NULL, loc);
      safe_tel(victim, Location(destination), silent);
      if (!silent && loc != Location(destination))
	did_it(victim, victim, "TPORT", NULL, "OTPORT", NULL, "ATPORT",
	       Location(destination));
      return;
    }
    /* check needed for NOTHING. Especially important for unlinked exits */
    if ((absroom = Location(victim)) == NOTHING) {
      notify(victim, T("You're in the Void. This is not a good thing."));
      /* At this point, they're in a bad location, so let's check
       * if home is valid before sending them there. */
      if (!GoodObject(Home(victim)))
	Home(victim) = PLAYER_START;
      do_move(victim, "home", MOVE_NORMAL);
      return;
    } else {
      /* valid location, perform other checks */

      /* if player is inside himself, send him home */
      if (absroom == victim) {
	notify(player, T("What are you doing inside of yourself?"));
	if (Home(victim) == absroom)
	  Home(victim) = PLAYER_START;
	do_move(victim, "home", MOVE_NORMAL);
	return;
      }
      /* find the "absolute" room */
      absroom = absolute_room(victim);

      if (absroom == NOTHING) {
	notify(victim, T("You're in the void - sending you home."));
	if (Home(victim) == Location(victim))
	  Home(victim) = PLAYER_START;
	do_move(victim, "home", MOVE_NORMAL);
	return;
      }
      /* if there are a lot of containers, send him home */
      if (absroom == AMBIGUOUS) {
	notify(victim, T("You're in too many containers."));
	if (Home(victim) == Location(victim))
	  Home(victim) = PLAYER_START;
	do_move(victim, "home", MOVE_NORMAL);
	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 (NoTel(absroom) && !controls(player, absroom)
	  && !Tel_Anywhere(player)) {
	notify(player, T("Teleports are not allowed in this room."));
	return;
      }

      /* Check leave lock on room, if necessary */
      if (!controls(player, absroom) && !Tel_Anywhere(player) &&
	  !eval_lock(player, absroom, Leave_Lock)) {
	fail_lock(player, absroom, Leave_Lock,
		  T("Teleports are not allowed in this room."), NOTHING);
	return;
      }

      /* Now check the Z_TEL status of the victim's room.
       * Just like NO_TEL above, except that if the room (or its
       * Zone Master Room, if any, is Z_TEL,
       * the destination must also be a room in the same zone
       */
      if (GoodObject(Zone(absroom)) && (ZTel(absroom) || ZTel(Zone(absroom)))
	  && !controls(player, absroom) && !Tel_Anywhere(player)
	  && (Zone(absroom) != Zone(destination))) {
	notify(player,
	       T("You may not teleport out of the zone from this room."));
	return;
      }
    }

    if (!IsExit(destination)) {
      if (tport_control_ok(player, victim, Location(victim)) &&
	  tport_dest_ok(player, victim, destination)
	  && (Tel_Anything(player) ||
	      (Tel_Anywhere(player) && (player == victim)) ||
	      (destination == Owner(victim)) ||
	      (!Fixed(Owner(victim)) && !Fixed(player)))) {
	if (!silent && loc != destination)
	  did_it(victim, victim, NULL, NULL, "OXTPORT", NULL, NULL, loc);
	safe_tel(victim, destination, silent);
	if (!silent && loc != destination)
	  did_it(victim, victim, "TPORT", NULL, "OTPORT", NULL, "ATPORT",
		 destination);
	if ((victim != player) && !(Puppet(victim) &&
				    (Owner(victim) == Owner(player)))) {
	  if (!Quiet(player) && !(Quiet(victim) && (Owner(victim) == player)))
	    notify(player, T("Teleported."));
	}
	return;
      }
      /* we can't do it */
      fail_lock(player, destination, Enter_Lock, T("Permission denied."),
		Location(player));
      return;
    } else {
      /* attempted teleport to an exit */
      if (tport_control_ok(player, victim, Location(victim))
	  && (Tel_Anything(player) ||
	      (Tel_Anywhere(player) && (player == victim)) ||
	      (!Fixed(Owner(victim)) && !Fixed(player)))) {
	do_move(victim, to, MOVE_NORMAL);
      } else {
	notify(player, T("Permission denied."));
	if (victim != player)
	  notify_format(victim,
			T("%s tries to impose his will on you and fails."),
			Name(player));
      }
    }
  }
}

/** Force an object to run a command.
 * \verbatim
 * This implements @force.
 * \endverbatim
 * \param player the enactor.
 * \param what name of the object to force.
 * \param command command to force the object to run.
 */
void
do_force(dbref player, const char *what, char *command)
{
  dbref victim;
  int j;

  if ((victim = match_controlled(player, what)) == NOTHING) {
    notify(player, "Sorry.");
    return;
  }
  if (options.log_forces) {
    if (Wizard(player)) {
      /* Log forces by wizards */
      if (Owner(victim) != Owner(player))
	do_log(LT_WIZ, player, victim, "** FORCE: %s", command);
      else
	do_log(LT_WIZ, player, victim, "FORCE: %s", command);
    } else if (Wizard(Owner(victim))) {
      /* Log forces of wizards */
      do_log(LT_WIZ, player, victim, "** FORCE WIZ-OWNED: %s", command);
    }
  }
  if (God(victim) && !God(player)) {
    notify(player, T("You can't force God!"));
    return;
  }
  /* force victim to do command */
  for (j = 0; j < 10; j++)
    global_eval_context.wnxt[j] = global_eval_context.wenv[j];
  for (j = 0; j < NUMQ; j++)
    global_eval_context.rnxt[j] = global_eval_context.renv[j];
  parse_que(victim, command, player);
}

/** Parse a force token command, but don't force with it.
 * \verbatim
 * This function hacks up something of the form "#<dbref> <action>",
 * finding the two args, and returns 1 if it's a sensible force,
 * otherwise 0. We know only that the command starts with #.
 * \endverbatim
 * \param command the command to parse.
 * \retval 1 sensible force command
 * \retval 0 command failed (no action given, etc.)
 */
int
parse_force(char *command)
{
  char *s;

  s = command + 1;
  while (*s && !isspace((unsigned char) *s)) {
    if (!isdigit((unsigned char) *s))
      return 0;			/* #1a is no good */
    s++;
  }
  if (!*s)
    return 0;			/* dbref with no action is no good */
  *s = '=';			/* Replace the first space with = so we have #3= <action> */
  return 1;
}


extern struct db_stat_info current_state;

/** Count up the number of objects of each type owned.
 * \param owner player to count for (or ANY_OWNER for all).
 * \return pointer to a static db_stat_info structure.
 */
struct db_stat_info *
get_stats(dbref owner)
{
  dbref i;
  static struct db_stat_info si;

  if (owner == ANY_OWNER)
    return &current_state;

  si.total = si.rooms = si.exits = si.things = si.players = si.garbage = 0;
  for (i = 0; i < db_top; i++) {
    if (owner == ANY_OWNER || owner == Owner(i)) {
      si.total++;
      if (IsGarbage(i)) {
	si.garbage++;
      } else {
	switch (Typeof(i)) {
	case TYPE_ROOM:
	  si.rooms++;
	  break;
	case TYPE_EXIT:
	  si.exits++;
	  break;
	case TYPE_THING:
	  si.things++;
	  break;
	case TYPE_PLAYER:
	  si.players++;
	  break;
	default:
	  break;
	}
      }
    }
  }
  return &si;
}

/** The stats command.
 * \verbatim
 * This implements @stats.
 * \endverbatim
 * \param player the enactor.
 * \param name name of player to check object stats for.
 */
void
do_stats(dbref player, const char *name)
{
  struct db_stat_info *si;
  dbref owner;

  if (*name == '\0')
    owner = ANY_OWNER;
  else if (*name == '#') {
    owner = atoi(&name[1]);
    if (!GoodObject(owner))
      owner = NOTHING;
    else if (!IsPlayer(owner))
      owner = NOTHING;
  } else if (strcasecmp(name, "me") == 0)
    owner = player;
  else
    owner = lookup_player(name);
  if (owner == NOTHING) {
    notify_format(player, T("%s: No such player."), name);
    return;
  }
  if (!Search_All(player)) {
    if (owner != ANY_OWNER && owner != player) {
      notify(player, T("You need a search warrant to do that!"));
      return;
    }
  }
  si = get_stats(owner);
  if (owner == ANY_OWNER) {
    notify_format(player,
		  T
		  ("%d objects = %d rooms, %d exits, %d things, %d players, %d garbage."),
		  si->total, si->rooms, si->exits, si->things, si->players,
		  si->garbage);
    if (first_free != NOTHING)
      notify_format(player, T("The next object to be created will be #%d."),
		    first_free);
  } else {
    notify_format(player,
		  T
		  ("%d objects = %d rooms, %d exits, %d things, %d players."),
		  si->total - si->garbage, si->rooms, si->exits, si->things,
		  si->players);
  }
}

/** Reset a player's password.
 * \verbatim
 * This implements @newpassword.
 * \endverbatim
 * \param player the executor.
 * \param cause the enactor.
 * \param name the name of the player whose password is to be reset.
 * \param password the new password for the player.
 */
void
do_newpassword(dbref player, dbref cause,
	       const char *name, const char *password)
{
  dbref victim;

  if (!global_eval_context.process_command_port) {
    char pass_eval[BUFFER_LEN];
    char const *sp;
    char *bp;

    sp = password;
    bp = pass_eval;
    process_expression(pass_eval, &bp, &sp, player, player, cause,
		       PE_DEFAULT, PT_DEFAULT, NULL);
    *bp = '\0';
    password = pass_eval;
  }

  if ((victim = lookup_player(name)) == NOTHING) {
    notify(player, T("No such player."));
  } else if (*password != '\0' && !ok_password(password)) {
    /* Wiz can set null passwords, but not bad passwords */
    notify(player, T("Bad password."));
  } else if (God(victim) && !God(player)) {
    notify(player, T("You cannot change that player's password."));
  } else {
    /* it's ok, do it */
    (void) atr_add(victim, "XYXXY", mush_crypt(password), GOD, NOTHING);
    notify_format(player, T("Password for %s changed."), Name(victim));
    notify_format(victim, T("Your password has been changed by %s."),
		  Name(player));
    do_log(LT_WIZ, player, victim, "*** NEWPASSWORD ***");
  }
}

/** Disconnect a player, forcibly.
 * \verbatim
 * This implements @boot.
 * \endverbatim
 * \param player the enactor.
 * \param name name of the player or descriptor to boot.
 * \param flag the type of booting to do.
 */
void
do_boot(dbref player, const char *name, enum boot_type flag)
{
  dbref victim;
  DESC *d = NULL;

  victim = NOTHING;
  switch (flag) {
  case BOOT_SELF:
    /* self boot */
    victim = player;
    break;
  case BOOT_DESC:
    /* boot by descriptor */
    victim = find_player_by_desc(atoi(name));
    if (victim == NOTHING) {
      d = port_desc(atoi(name));
      if (!d && Can_Boot(player)) {
	notify(player, "There is no one connected on that descriptor.");
	return;
      } else
	victim = AMBIGUOUS;
    }
    break;
  case BOOT_SILENT:
  case BOOT_NAME:
    /* boot by name */
    if ((victim =
	 noisy_match_result(player, name, TYPE_PLAYER,
			    MAT_LIMITED | MAT_ME)) == NOTHING) {
      notify(player, T("No such connected player."));
      return;
    }
    if (victim == player)
      flag = BOOT_SELF;
    break;
  }

  if ((victim != player) && !Can_Boot(player)) {
    notify(player, T("You can't boot other people!"));
    return;
  }
  if (God(victim) && !God(player)) {
    notify(player, T("You're good.  That's spelled with two 'o's."));
    return;
  }
  /* look up descriptor */
  switch (flag) {
  case BOOT_SILENT:
  case BOOT_NAME:
    d = player_desc(victim);
    break;
  case BOOT_DESC:
    d = port_desc(atoi(name));
    break;
  case BOOT_SELF:
    d = inactive_desc(victim);
    break;
  }

  if (victim == player
      && (!d || d->descriptor == global_eval_context.process_command_port)) {
    notify(player, T("If you want to quit, use QUIT."));
    return;
  }

  /* check for more errors */
  if (!d) {
    if (flag == BOOT_SELF)
      notify(player, T("None of your connections are idle."));
    else
      notify(player, T("That player isn't connected!"));
  } else {
    do_log(LT_WIZ, player, victim, "*** BOOT ***");
    if (flag == BOOT_SELF)
      notify(player, T("You boot an idle self."));
    else if (victim == AMBIGUOUS)
      notify_format(player, T("You booted unconnected port %s!"), name);
    else if (victim == player)
      notify(player, T("You boot a duplicate self."));
    else {
      notify_format(player, T("You booted %s off!"), Name(victim));
      if (flag != BOOT_SILENT)
	notify(victim, T("You are politely shown to the door."));
    }
    boot_desc(d);
  }
}

/** Chown all of a player's objects.
 * \verbatim
 * This implements @chownall
 * \endverbatim
 * \param player the enactor.
 * \param name name of player whose objects are to be chowned.
 * \param target name of new owner for objects.
 * \param preserve if 1, keep privileges and don't halt objects.
 */
void
do_chownall(dbref player, const char *name, const char *target, int preserve)
{
  int i;
  dbref victim;
  dbref n_target;
  int count = 0;

  if (!Wizard(player)) {
    notify(player, T("Try asking them first!"));
    return;
  }
  if ((victim = noisy_match_result(player, name, TYPE_PLAYER, MAT_LIMITED))
      == NOTHING)
    return;

  if (!target || !*target) {
    n_target = player;
  } else {
    if ((n_target =
	 noisy_match_result(player, target, TYPE_PLAYER,
			    MAT_LIMITED)) == NOTHING)
      return;
  }

  for (i = 0; i < db_top; i++) {
    if ((Owner(i) == victim) && (!IsPlayer(i))) {
      chown_object(player, i, n_target, preserve);
      count++;
    }
  }

  /* change quota (this command is wiz only and we can assume that
   * we intend for the recipient to get all the objects, so we
   * don't do a quota check earlier.
   */
  change_quota(victim, count);
  change_quota(n_target, -count);

  notify_format(player, T("Ownership changed for %d objects."), count);
}

/** Change the zone of all of a player's objects.
 * \verbatim
 * This implements @chzoneall.
 * \endverbatim
 * \param player the enactor.
 * \param name name of player whose objects should be rezoned.
 * \param target string containing new zone master for objects.
 */
void
do_chzoneall(dbref player, const char *name, const char *target)
{
  int i;
  dbref victim;
  dbref zone;
  int count = 0;

  if (!Wizard(player)) {
    notify(player, T("You do not have the power to change reality."));
    return;
  }
  if ((victim = noisy_match_result(player, name, TYPE_PLAYER, MAT_LIMITED))
      == NOTHING)
    return;

  if (!target || !*target) {
    notify(player, T("No zone specified."));
    return;
  }
  if (!strcasecmp(target, "none"))
    zone = NOTHING;
  else {
    switch (zone = match_result(player, target, NOTYPE, MAT_EVERYTHING)) {
    case NOTHING:
      notify(player, T("I can't seem to find that."));
      return;
    case AMBIGUOUS:
      notify(player, T("I don't know which one you mean!"));
      return;
    }
  }

  /* Okay, now that we know we're not going to spew all sorts of errors,
   * call the normal do_chzone for all the relevant objects.  This keeps
   * consistency on things like flag resetting, etc... */
  for (i = 0; i < db_top; i++) {
    if (Owner(i) == victim && Zone(i) != zone) {
      count += do_chzone(player, unparse_dbref(i), target, 0);
    }
  }
  notify_format(player, T("Zone changed for %d objects."), count);
}

/*-----------------------------------------------------------------------
 * Nasty management: @kick, examine/debug
 */

/** Execute a number of commands off the queue immediately.
 * \verbatim
 * This implements @kick, which is nasty.
 * \endverbatim
 * \param player the enactor.
 * \param num string containing number of commands to run from the queue.
 */
void
do_kick(dbref player, const char *num)
{
  int n;

  if (!Wizard(player)) {
    notify(player, T("Permission denied."));
    return;
  }
  if (!num || !*num) {
    notify(player, T("How many commands do you want to execute?"));
    return;
  }
  n = atoi(num);

  if (n <= 0) {
    notify(player, T("Number out of range."));
    return;
  }
  n = do_top(n);

  notify_format(player, T("%d commands executed."), n);
}

/** examine/debug.
 * This implements examine/debug, which provides some raw values for
 * object structure elements of an examined object.
 * \param player the enactor.
 * \param name name of object to examine.
 */
void
do_debug_examine(dbref player, const char *name)
{
  MAIL *mp;
  dbref thing;

  if (!Hasprivs(player)) {
    notify(player, T("Permission denied."));
    return;
  }
  /* find it */
  thing = noisy_match_result(player, name, NOTYPE, MAT_EVERYTHING);
  if (!GoodObject(thing))
    return;

  notify(player, object_header(player, thing));
  notify_format(player, T("Flags value: %s"),
		bits_to_string("FLAG", Flags(thing), GOD, NOTHING));
  notify_format(player, T("Powers value: %s"),
		bits_to_string("POWER", Powers(thing), GOD, NOTHING));

  notify_format(player, "Next: %d", Next(thing));
  notify_format(player, "Contents: %d", Contents(thing));
  notify_format(player, "Pennies: %d", Pennies(thing));

  switch (Typeof(thing)) {
  case TYPE_PLAYER:
    mp = desc_mail(thing);
    notify_format(player, T("First mail sender: %d"), mp ? mp->from : NOTHING);
  case TYPE_THING:
    notify_format(player, "Location: %d", Location(thing));
    notify_format(player, "Home: %d", Home(thing));
    break;
  case TYPE_EXIT:
    notify_format(player, "Destination: %d", Location(thing));
    notify_format(player, "Source: %d", Source(thing));
    break;
  case TYPE_ROOM:
    notify_format(player, "Drop-to: %d", Location(thing));
    notify_format(player, "Exits: %d", Exits(thing));
    break;
  case TYPE_GARBAGE:
    break;
  default:
    notify(player, T("Bad object type."));
  }
}

/*-------------------------------------------------------------------------
 * Powers stuff
 */

/** Set a power on an object.
 * \verbatim
 * This implements @power.
 * \endverbatim
 * \param player the enactor.
 * \param name name of the object on which to set the power.
 * \param power name of the power to set.
 */
void
do_power(dbref player, const char *name, const char *power)
{
  int revoke_it = 0;
  char powerbuff[BUFFER_LEN], *p, *f;
  dbref thing;

  if (!power || !*power) {
    /* @power <power> */
    do_flag_info("POWER", player, name);
    return;
  }
  if (!Wizard(player)) {
    notify(player, T("Only wizards may grant powers."));
    return;
  }
  if ((thing = noisy_match_result(player, name, NOTYPE, MAT_EVERYTHING)) ==
      NOTHING)
    return;
  if (Unregistered(thing)) {
    notify(player, T("You can't grant powers to unregistered players."));
    return;
  }
  if (God(thing) && !God(player)) {
    notify(player, T("God is already all-powerful."));
    return;
  }

  strcpy(powerbuff, power);
  p = trim_space_sep(powerbuff, ' ');
  if (*p == '\0') {
    notify(player, T("You must specify a power to set."));
    return;
  }
  do {
    f = split_token(&p, ' ');
    revoke_it = 0;
    if (*f == NOT_TOKEN && *(f + 1)) {
      revoke_it = 1;
      f++;
    }
    set_power(player, thing, f, revoke_it);
  } while (p);

}

/*----------------------------------------------------------------------------
 * Search functions
 */

/** User command to search the db for matching objects.
 * \verbatim
 * This implements @search.
 * \endverbatim
 * \param player the enactor.
 * \param arg1 name of player whose objects are to be searched.
 * \param arg3 additional search arguments.
 */
void
do_search(dbref player, const char *arg1, char **arg3)
{
  char tbuf[BUFFER_LEN], *arg2 = tbuf, *tbp;
  dbref *results = NULL;
  int nresults;

  /* parse first argument into two */
  if (!arg1 || *arg1 == '\0')
    arg1 = "me";

  /* First argument is a player, so we could have a quoted name */
  if (PLAYER_NAME_SPACES && *arg1 == '\"') {
    for (; *arg1 && ((*arg1 == '\"') || isspace((unsigned char) *arg1));
	 arg1++) ;
    strcpy(tbuf, arg1);
    while (*arg2 && (*arg2 != '\"')) {
      while (*arg2 && (*arg2 != '\"'))
	arg2++;
      if (*arg2 == '\"') {
	*arg2++ = '\0';
	while (*arg2 && isspace((unsigned char) *arg2))
	  arg2++;
	break;
      }
    }
  } else {
    strcpy(tbuf, arg1);
    while (*arg2 && !isspace((unsigned char) *arg2))
      arg2++;
    if (*arg2)
      *arg2++ = '\0';
    while (*arg2 && isspace((unsigned char) *arg2))
      arg2++;
  }

  if (!*arg2) {
    if (!arg3[1] || !*arg3[1])
      arg2 = (char *) "";	/* arg1 */
    else {
      arg2 = (char *) arg1;	/* arg2=arg3 */
      tbuf[0] = '\0';
    }
  }

  nresults = raw_search(player, tbuf, arg2, arg3[1], arg3[2], arg3[3],
			&results, NULL);

  if (nresults == 0) {
    notify(player, T("Nothing found."));
  } else if (nresults > 0) {
    /* Split the results up by type and report. */
    int n;
    int nthings = 0, nexits = 0, nrooms = 0, nplayers = 0;
    dbref *things, *exits, *rooms, *players;

    things = (dbref *) mush_malloc(sizeof(dbref) * nresults, "dbref_list");
    exits = (dbref *) mush_malloc(sizeof(dbref) * nresults, "dbref_list");
    rooms = (dbref *) mush_malloc(sizeof(dbref) * nresults, "dbref_list");
    players = (dbref *) mush_malloc(sizeof(dbref) * nresults, "dbref_list");

    for (n = 0; n < nresults; n++) {
      switch (Typeof(results[n])) {
      case TYPE_THING:
	things[nthings++] = results[n];
	break;
      case TYPE_EXIT:
	exits[nexits++] = results[n];
	break;
      case TYPE_ROOM:
	rooms[nrooms++] = results[n];
	break;
      case TYPE_PLAYER:
	players[nplayers++] = results[n];
	break;
      default:
	/* Unknown type. Ignore. */
	do_rawlog(LT_ERR, T("Weird type for dbref #%d"), results[n]);
      }
    }

    if (nrooms) {
      notify(player, "\nROOMS:");
      for (n = 0; n < nrooms; n++) {
	tbp = tbuf;
	safe_format(tbuf, &tbp, "%s [owner: ", object_header(player, rooms[n]));
	safe_str(object_header(player, Owner(rooms[n])), tbuf, &tbp);
	safe_chr(']', tbuf, &tbp);
	*tbp = '\0';
	notify(player, tbuf);
      }
    }

    if (nexits) {
      dbref from, to;

      notify(player, "\nEXITS:");
      for (n = 0; n < nexits; n++) {
	tbp = tbuf;
	if (Source(exits[n]) == NOTHING)
	  from = NOTHING;
	else
	  from = Source(exits[n]);
	to = Destination(exits[n]);
	safe_format(tbuf, &tbp, "%s [from ", object_header(player, exits[n]));
	safe_str((from == NOTHING) ? "NOWHERE" : object_header(player, from),
		 tbuf, &tbp);
	safe_str(" to ", tbuf, &tbp);
	safe_str((to == NOTHING) ? "NOWHERE" : object_header(player, to),
		 tbuf, &tbp);
	safe_chr(']', tbuf, &tbp);
	*tbp = '\0';
	notify(player, tbuf);
      }
    }

    if (nthings) {
      notify(player, "\nOBJECTS:");
      for (n = 0; n < nthings; n++) {
	tbp = tbuf;
	safe_format(tbuf, &tbp, "%s [owner: ",
		    object_header(player, things[n]));
	safe_str(object_header(player, Owner(things[n])), tbuf, &tbp);
	safe_chr(']', tbuf, &tbp);
	*tbp = '\0';
	notify(player, tbuf);
      }
    }

    if (nplayers) {
      int is_wizard = Search_All(player) || See_All(player);
      notify(player, "\nPLAYERS:");
      for (n = 0; n < nplayers; n++) {
	tbp = tbuf;
	safe_str(object_header(player, players[n]), tbuf, &tbp);
	if (is_wizard)
	  safe_format(tbuf, &tbp,
		      T(" [location: %s]"),
		      object_header(player, Location(players[n])));
	*tbp = '\0';
	notify(player, tbuf);
      }
    }

    notify(player, T("----------  Search Done  ----------"));
    notify_format(player,
		  T
		  ("Totals: Rooms...%d  Exits...%d  Objects...%d  Players...%d"),
		  nrooms, nexits, nthings, nplayers);
    mush_free((Malloc_t) rooms, "dbref_list");
    mush_free((Malloc_t) exits, "dbref_list");
    mush_free((Malloc_t) things, "dbref_list");
    mush_free((Malloc_t) players, "dbref_list");
  }
  if (results)
    mush_free(results, "search_results");
}

FUNCTION(fun_lsearch)
{
  int nresults;
  dbref *results = NULL;
  int rev = !strcmp(called_as, "LSEARCHR");

  if (!command_check_byname(executor, "@search")) {
    safe_str(T(e_perm), buff, bp);
    return;
  }

  if (!strcmp(called_as, "CHILDREN"))
    nresults = raw_search(executor, NULL, "PARENT", args[0], NULL,
			  NULL, &results, pe_info);
  else
    nresults =
      raw_search(executor, args[0], args[1], args[2], args[3], args[4],
		 &results, pe_info);

  if (nresults < 0) {
    safe_str("#-1", buff, bp);
  } else if (nresults == 0) {
    notify(executor, T("Nothing found."));
  } else {
    int first = 1, n;
    if (!rev) {
      for (n = 0; n < nresults; n++) {
	if (first)
	  first = 0;
	else if (safe_chr(' ', buff, bp))
	  break;
	if (safe_dbref(results[n], buff, bp))
	  break;
      }
    } else {
      for (n = nresults - 1; n >= 0; n--) {
	if (first)
	  first = 0;
	else if (safe_chr(' ', buff, bp))
	  break;
	if (safe_dbref(results[n], buff, bp))
	  break;
      }
    }
  }
  if (results)
    mush_free(results, "search_results");
}


/** Type of limitation to apply to a search of the db */
enum search_class {
  S_OWNER,	/**< Limit to a single owner */
  S_TYPE,	/**< Limit to a single type */
  S_PARENT,	/**< Limit to objects with a given parent */
  S_ZONE,	/**< Limit to objects in a given zone */
  S_FLAG,	/**< Limit to objects with given flag characters */
  S_POWER,	/**< Limit to objects with given power */
  S_EVAL,	/**< Limit to objects for which an expression evals true */
  S_NAME,	/**< Limit to objects prefix-matching a given name */
  S_LFLAG	/**< Limit to objects with given flag names */
};

/* Does the actual searching */
static int
raw_search(dbref player, const char *owner, const char *class,
	   const char *restriction, const char *start, const char *stop,
	   dbref **result, PE_Info * pe_info)
{
  size_t result_size;
  size_t nresults = 0;
  enum search_class sclass = S_OWNER;
  int n;
  int restrict_type = NOTYPE;
  dbref restrict_obj = NOTHING, restrict_owner = ANY_OWNER;
  int is_wiz;
  dbref low = 0, high = db_top - 1;

  is_wiz = Search_All(player) || See_All(player);

  /* Range limits */
  if (start && *start) {
    size_t offset = 0;
    if (start[0] == '#')
      offset = 1;
    low = parse_integer(start + offset);
    if (!GoodObject(low))
      low = 0;
  }
  if (stop && *stop) {
    size_t offset = 0;
    if (stop[0] == '#')
      offset = 1;
    high = parse_integer(stop + offset);
    if (!GoodObject(high))
      high = db_top - 1;
  }

  /* set limits on who we search */
  if (!owner || !*owner || strcasecmp(owner, "all") == 0)
    restrict_owner = is_wiz ? ANY_OWNER : player;
  else if (strcasecmp(owner, "me") == 0)
    restrict_owner = player;
  else
    restrict_owner = lookup_player(owner);
  if (restrict_owner == NOTHING) {
    notify(player, T("Unknown owner."));
    return -1;
  }

  /* Figure out the class */
  if (!class || !*class || strcasecmp(class, "none") == 0) {
    sclass = S_OWNER;
  } else if (string_prefix("type", class)) {
    sclass = S_TYPE;
    if (string_prefix("things", restriction)
	|| string_prefix("objects", restriction)) {
      restrict_type = TYPE_THING;
    } else if (string_prefix("rooms", restriction)) {
      restrict_type = TYPE_ROOM;
    } else if (string_prefix("exits", restriction)) {
      restrict_type = TYPE_EXIT;
    } else if (string_prefix("rooms", restriction)) {
      restrict_type = TYPE_ROOM;
    } else if (string_prefix("players", restriction)) {
      restrict_type = TYPE_PLAYER;
    } else {
      notify(player, T("Unknown type."));
      return -1;
    }
  } else if (string_prefix("things", class)
	     || string_prefix("objects", class)) {
    sclass = S_NAME;
    restrict_type = TYPE_THING;
  } else if (string_prefix("exits", class)) {
    sclass = S_NAME;
    restrict_type = TYPE_EXIT;
  } else if (string_prefix("rooms", class)) {
    sclass = S_NAME;
    restrict_type = TYPE_ROOM;
  } else if (string_prefix("players", class)) {
    sclass = S_NAME;
    restrict_type = TYPE_PLAYER;
  } else if (string_prefix("name", class)) {
    sclass = S_NAME;
  } else if (string_prefix("parent", class)) {
    sclass = S_PARENT;
    if (!is_objid(restriction)) {
      notify(player, T("Unknown parent."));
      return -1;
    }
    restrict_obj = parse_objid(restriction);
    if (!GoodObject(restrict_obj)) {
      notify(player, T("Unknown parent."));
      return -1;
    }
  } else if (string_prefix("zone", class)) {
    sclass = S_ZONE;
    if (!is_objid(restriction)) {
      notify(player, T("Unknown zone."));
      return -1;
    }
    restrict_obj = parse_objid(restriction);
    if (!GoodObject(restrict_obj)) {
      notify(player, T("Unknown zone."));
      return -1;
    }
  } else if (string_prefix("eval", class)) {
    sclass = S_EVAL;
  } else if (string_prefix("ethings", class) ||
	     string_prefix("eobjects", class)) {
    sclass = S_EVAL;
    restrict_type = TYPE_THING;
  } else if (string_prefix("eexits", class)) {
    sclass = S_EVAL;
    restrict_type = TYPE_EXIT;
  } else if (string_prefix("erooms", class)) {
    sclass = S_EVAL;
    restrict_type = TYPE_ROOM;
  } else if (string_prefix("eplayers", class)) {
    sclass = S_EVAL;
    restrict_type = TYPE_PLAYER;
  } else if (string_prefix("powers", class)) {
    /* Handle the checking later.  */
    sclass = S_POWER;
    if (!restriction || !*restriction) {
      notify(player, T("You must give a list of power names."));
      return -1;
    }
  } else if (string_prefix("flags", class)) {
    /* Handle the checking later.  */
    sclass = S_FLAG;
    if (!restriction || !*restriction) {
      notify(player, T("You must give a string of flag characters."));
      return -1;
    }
  } else if (string_prefix("lflags", class)) {
    /* Handle the checking later.  */
    sclass = S_LFLAG;
    if (!restriction || !*restriction) {
      notify(player, T("You must give a list of flag names."));
      return -1;
    }
  } else {
    notify(player, T("Unknown search class."));
    return -1;
  }

  if ((restrict_owner != ANY_OWNER && restrict_owner != player) &&
      !(is_wiz || (sclass == S_TYPE && restrict_type == TYPE_PLAYER))) {
    notify(player, T("You need a search warrant to do that."));
    return -1;
  }

  /* make sure player has money to do the search */
  if (!payfor(player, FIND_COST)) {
    notify_format(player, T("Searches cost %d %s."), FIND_COST,
		  ((FIND_COST == 1) ? MONEY : MONIES));
    return -1;
  }

  result_size = (db_top / 4) + 1;
  *result =
    (dbref *) mush_malloc(sizeof(dbref) * result_size, "search_results");
  if (!*result)
    mush_panic(T("Couldn't allocate memory in search!"));

  switch (sclass) {
  case S_OWNER:		/* @search someone */
  case S_TYPE:			/* @search type=whatever */
    for (n = low; n <= high; n++) {
      if ((restrict_owner == ANY_OWNER || Owner(n) == restrict_owner)
	  && (restrict_type == NOTYPE || Typeof(n) == restrict_type)) {
	if (nresults >= result_size) {
	  dbref *newresults;
	  result_size *= 2;
	  newresults = (dbref *) realloc((Malloc_t) *result,
					 sizeof(dbref) * result_size);
	  if (!newresults)
	    mush_panic(T("Couldn't reallocate memory in search!"));
	  *result = newresults;
	}
	(*result)[nresults++] = (dbref) n;
      }
    }
    break;
  case S_ZONE:			/* @search ZONE=#1234 */
    for (n = low; n <= high; n++) {
      if ((restrict_owner == ANY_OWNER || Owner(n) == restrict_owner)
	  && Zone(n) == restrict_obj) {
	if (nresults >= result_size) {
	  dbref *newresults;
	  result_size *= 2;
	  newresults =
	    (dbref *) realloc((Malloc_t) *result, sizeof(dbref) * result_size);
	  if (!newresults)
	    mush_panic(T("Couldn't reallocate memory in search!"));
	  *result = newresults;
	}

	(*result)[nresults++] = (dbref) n;
      }
    }
    break;
  case S_PARENT:		/* @search parent=#1234 */
    for (n = low; n <= high; n++) {
      if ((restrict_owner == ANY_OWNER || Owner(n) == restrict_owner)
	  && Parent(n) == restrict_obj) {
	if (nresults >= result_size) {
	  dbref *newresults;
	  result_size *= 2;
	  newresults =
	    (dbref *) realloc((Malloc_t) *result, sizeof(dbref) * result_size);
	  if (!newresults)
	    mush_panic(T("Couldn't reallocate memory in search!"));
	  *result = newresults;
	}

	(*result)[nresults++] = (dbref) n;
      }
    }
    break;
  case S_NAME:			/* @search (?:name|exits|objects|rooms|players|things)=name */
    for (n = low; n <= high; n++) {
      if ((restrict_owner == ANY_OWNER || Owner(n) == restrict_owner)
	  && (restrict_type == NOTYPE || Typeof(n) == restrict_type)
	  && string_match(Name(n), restriction)) {
	if (nresults >= result_size) {
	  dbref *newresults;
	  result_size *= 2;
	  newresults =
	    (dbref *) realloc((Malloc_t) *result, sizeof(dbref) * result_size);
	  if (!newresults)
	    mush_panic(T("Couldn't reallocate memory in search!"));
	  *result = newresults;
	}

	(*result)[nresults++] = (dbref) n;
      }
    }
    break;
  case S_EVAL:			/* @search (?:eval|ething|eroom|eplayer|eexit)=code */
    {
      char *ebuf1;
      const char *ebuf2;
      char tbuf1[BUFFER_LEN];
      char *bp;

      if (!restriction || !*restriction)
	break;

      for (n = low; n <= high; n++) {
	if (!((restrict_owner == ANY_OWNER || Owner(n) == restrict_owner)
	      && (restrict_type == NOTYPE || Typeof(n) == restrict_type)))
	  continue;

	ebuf1 = replace_string("##", unparse_dbref(n), restriction);
	ebuf2 = ebuf1;
	bp = tbuf1;
	process_expression(tbuf1, &bp, &ebuf2, player, player, player,
			   PE_DEFAULT, PT_DEFAULT, pe_info);
	mush_free((Malloc_t) ebuf1, "replace_string.buff");
	*bp = '\0';
	if (!parse_boolean(tbuf1))
	  continue;

	if (nresults >= result_size) {
	  dbref *newresults;
	  result_size *= 2;
	  newresults =
	    (dbref *) realloc((Malloc_t) *result, sizeof(dbref) * result_size);
	  if (!newresults)
	    mush_panic(T("Couldn't reallocate memory in search!"));
	  *result = newresults;
	}
	(*result)[nresults++] = (dbref) n;
	if (pe_info && pe_info->fun_invocations >= FUNCTION_LIMIT)
	  break;
      }
    }
    break;
  case S_FLAG:
  case S_LFLAG:
  case S_POWER:
    for (n = low; n <= high; n++) {
      if ((restrict_owner == ANY_OWNER || Owner(n) == restrict_owner)
	  && (restrict_type == NOTYPE || Typeof(n) == restrict_type)
	  && ((sclass == S_POWER) ?
	      flaglist_check_long("POWER", player, n, restriction, 1) :
	      ((sclass == S_FLAG) ?
	       flaglist_check("FLAG", player, n, restriction, 1)
	       : flaglist_check_long("FLAG", player, n, restriction, 1)))) {
	if (nresults >= result_size) {
	  dbref *newresults;
	  result_size *= 2;
	  newresults =
	    (dbref *) realloc((Malloc_t) *result, sizeof(dbref) * result_size);
	  if (!newresults)
	    mush_panic(T("Couldn't reallocate memory in search!"));
	  *result = newresults;
	}

	(*result)[nresults++] = (dbref) n;
      }
    }
    break;
  }

  return (int) nresults;
}

#ifdef WIN32
#pragma warning( disable : 4761)	/* Disable bogus conversion warning */
#endif
/* ARGSUSED */
FUNCTION(fun_hidden)
{
  dbref it = match_thing(executor, args[0]);
  if (!See_All(executor)) {
    notify(executor, T("Permission denied."));
    safe_str("#-1", buff, bp);
    return;
  }
  if ((it == NOTHING) || (!IsPlayer(it))) {
    notify(executor, T("Couldn't find that player."));
    safe_str("#-1", buff, bp);
    return;
  }
  safe_boolean(hidden(it), buff, bp);
  return;
}

#ifdef WIN32
#pragma warning( default : 4761)	/* Re-enable conversion warning */
#endif

/* ARGSUSED */
FUNCTION(fun_quota)
{
  int owned;
  /* Tell us player's quota */
  dbref thing;
  dbref who = lookup_player(args[0]);
  if ((who == NOTHING) || !IsPlayer(who)) {
    notify(executor, T("Couldn't find that player."));
    safe_str("#-1", buff, bp);
    return;
  }
  if (!(Do_Quotas(executor) || See_All(executor)
	|| controls(executor, who))) {
    notify(executor, T("You can't see someone else's quota!"));
    safe_str("#-1", buff, bp);
    return;
  }
  if (NoQuota(who)) {
    /* Unlimited, but return a big number to be sensible */
    safe_str("99999", buff, bp);
    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 (Owner(thing) == who)
      if (!IsGarbage(thing))
	++owned;
  }

  safe_integer(owned + get_current_quota(who), buff, bp);
  return;
}

/** Modify access rules for a site.
 * \verbatim
 * This implements @sitelock.
 * \endverbatim
 * \param player the enactor.
 * \param site name of site.
 * \param opts access rules to apply.
 * \param who string containing dbref of player to whom rule applies.
 * \param type sitelock operation to do.
 */
void
do_sitelock(dbref player, const char *site, const char *opts,
	    const char *who, enum sitelock_type type)
{
  if (!Wizard(player)) {
    notify(player, T("Your delusions of grandeur have been noted."));
    return;
  }
  if (opts && *opts) {
    int can, cant;
    dbref whod = AMBIGUOUS;
    /* Options form of the command. */
    if (!site || !*site) {
      notify(player, T("What site did you want to lock?"));
      return;
    }
    can = cant = 0;
    if (!parse_access_options(opts, NULL, &can, &cant, player)) {
      notify(player, T("No valid options found."));
      return;
    }
    if (who && *who) {		/* Specify a character */
      whod = lookup_player(who);
      if (!GoodObject(whod)) {
	notify(player, T("Who do you want to lock?"));
	return;
      }
    }

    add_access_sitelock(player, site, whod, can, cant);
    write_access_file();
    if (whod != AMBIGUOUS) {
      notify_format(player,
		    T("Site %s access options for %s(%s) set to %s"),
		    site, Name(whod), unparse_dbref(whod), opts);
      do_log(LT_WIZ, player, NOTHING,
	     T("*** SITELOCK *** %s for %s(%s) --> %s"), site,
	     Name(whod), unparse_dbref(whod), opts);
    } else {
      notify_format(player, T("Site %s access options set to %s"), site, opts);
      do_log(LT_WIZ, player, NOTHING, "*** SITELOCK *** %s --> %s", site, opts);
    }
    return;
  } else {
    /* Backward-compatible non-options form of the command,
     * or @sitelock/name
     */
    switch (type) {
    case SITELOCK_LIST:
      /* List bad sites */
      do_list_access(player);
      return;
    case SITELOCK_ADD:
      add_access_sitelock(player, site, AMBIGUOUS, 0, ACS_CREATE);
      write_access_file();
      notify_format(player, T("Site %s locked"), site);
      do_log(LT_WIZ, player, NOTHING, "*** SITELOCK *** %s", site);
      break;
    case SITELOCK_BAN:
      add_access_sitelock(player, site, AMBIGUOUS, 0, ACS_DEFAULT);
      write_access_file();
      notify_format(player, T("Site %s banned"), site);
      do_log(LT_WIZ, player, NOTHING, "*** SITELOCK *** %s", site);
      break;
    case SITELOCK_CHECK:{
	struct access *ap;
	char tbuf[BUFFER_LEN], *bp;
	int rulenum;
	if (!site || !*site) {
	  do_list_access(player);
	  return;
	}
	ap = site_check_access(site, AMBIGUOUS, &rulenum);
	bp = tbuf;
	format_access(ap, rulenum, AMBIGUOUS, tbuf, &bp);
	*bp = '\0';
	notify(player, tbuf);
	break;
      }
    case SITELOCK_REMOVE:{
	int n;
	n = remove_access_sitelock(site);
	if (n > 0)
	  write_access_file();
	notify_format(player, T("%d sitelocks removed."), n);
	break;
      }
    }
  }
}

/** Edit the list of restricted player names
 * \verbatim
 * This implements @sitelock/name
 * \endverbatim
 * \param player the player doing the command.
 * \param name the name (Actually, wildcard pattern) to restrict.
 */
void
do_sitelock_name(dbref player, const char *name)
{
  FILE *fp, *fptmp;
  char buffer[BUFFER_LEN];
  char *p;

  if (!Wizard(player)) {
    notify(player, T("Your delusions of grandeur have been noted."));
    return;
  }

  release_fd();

  if (!name || !*name) {
    /* List bad names */
    if ((fp = fopen(NAMES_FILE, FOPEN_READ)) == NULL) {
      notify(player, T("Unable to open names file."));
    } else {
      notify(player, T("Any name matching these wildcard patterns is banned:"));
      while (fgets(buffer, sizeof buffer, fp)) {
	if ((p = strchr(buffer, '\r')) != NULL)
	  *p = '\0';
	else if ((p = strchr(buffer, '\n')) != NULL)
	  *p = '\0';
	notify(player, buffer);
      }
      fclose(fp);
    }
  } else if (name[0] == '!') {	/* Delete a name */
    if ((fp = fopen(NAMES_FILE, FOPEN_READ)) != NULL) {
      if ((fptmp = fopen("tmp.tmp", FOPEN_WRITE)) == NULL) {
	notify(player, T("Unable to delete name."));
	fclose(fp);
      } else {
	while (fgets(buffer, sizeof buffer, fp)) {
	  if ((p = strchr(buffer, '\r')) != NULL)
	    *p = '\0';
	  else if ((p = strchr(buffer, '\n')) != NULL)
	    *p = '\0';
	  if (strcasecmp(buffer, name + 1) == 0)
	    /* Replace the name with #NAME, to allow things like
	       keeping track of unlocked feature names. */
	    fprintf(fptmp, "#%s\n", buffer);
	  else
	    fprintf(fptmp, "%s\n", buffer);
	}
	fclose(fp);
	fclose(fptmp);
	if (rename_file("tmp.tmp", NAMES_FILE) == 0) {
	  notify(player, T("Name removed."));
	  do_log(LT_WIZ, player, NOTHING, "*** UNLOCKED NAME *** %s", name + 1);
	} else {
	  notify(player, T("Unable to delete name."));
	}
      }
    } else
      notify(player, T("Unable to delete name."));
  } else {			/* Add a name */
    if ((fp = fopen(NAMES_FILE, FOPEN_READ)) != NULL) {
      if ((fptmp = fopen("tmp.tmp", FOPEN_WRITE)) == NULL) {
	notify(player, T("Unable to lock name."));
      } else {
	/* Read the names file, looking for #NAME and writing it
	   without the commenting #. Otherwise, add the new name
	   to the end of the file unless it's already present */
	char commented[BUFFER_LEN + 1];
	int found = 0;
	commented[0] = '#';
	strcpy(commented + 1, name);
	while (fgets(buffer, sizeof buffer, fp) != NULL) {
	  if ((p = strchr(buffer, '\r')) != NULL)
	    *p = '\0';
	  else if ((p = strchr(buffer, '\n')) != NULL)
	    *p = '\0';
	  if (strcasecmp(commented, buffer) == 0) {
	    fprintf(fptmp, "%s\n", buffer + 1);
	    found = 1;
	  } else {
	    fprintf(fptmp, "%s\n", buffer);
	    if (strcasecmp(name, buffer) == 0)
	      found = 1;
	  }
	}
	if (!found)
	  fprintf(fptmp, "%s\n", name);
	fclose(fp);
	fclose(fptmp);

	if (rename_file("tmp.tmp", NAMES_FILE) == 0) {
	  notify_format(player, T("Name %s locked."), name);
	  do_log(LT_WIZ, player, NOTHING, "*** NAMELOCK *** %s", name);
	} else
	  notify(player, T("Unable to lock name."));
      }
    }
  }
  reserve_fd();
}

/*-----------------------------------------------------------------
 * Functions which give memory information on objects or players.
 * Source code originally by Kalkin, modified by Javelin
 */

static int
mem_usage(dbref thing)
{
  int k;
  ATTR *m;
  lock_list *l;
  k = sizeof(struct object);	/* overhead */
  k += strlen(Name(thing)) + 1;	/* The name */
  for (m = List(thing); m; m = AL_NEXT(m)) {
    k += sizeof(ATTR);
    if (AL_STR(m) && *AL_STR(m))
      k += u_strlen(AL_STR(m)) + 1;
    /* NOTE! In the above, we're getting the size of the
     * compressed attrib, not the uncompressed one (as Kalkin did)
     * because (1) it's more efficient, (2) it's more accurate
     * since that's how the object is in memory. This relies on
     * compressed attribs being terminated with \0's, which they
     * are in compress.c. If that changes, this breaks.
     */
  }
  for (l = Locks(thing); l; l = l->next) {
    k += sizeof(lock_list);
    k += sizeof_boolexp(l->key);
  }
  return k;
}

/* ARGSUSED */
FUNCTION(fun_objmem)
{
  dbref thing;
  if (!Search_All(executor)) {
    safe_str(T(e_perm), buff, bp);
    return;
  }
  if (!strcasecmp(args[0], "me"))
    thing = executor;
  else if (!strcasecmp(args[0], "here"))
    thing = Location(executor);
  else {
    thing = noisy_match_result(executor, args[0], NOTYPE, MAT_OBJECTS);
  }
  if (!GoodObject(thing)) {
    safe_str(T(e_match), buff, bp);
    return;
  }
  if (!Can_Examine(executor, thing)) {
    safe_str(T(e_perm), buff, bp);
    return;
  }
  safe_integer(mem_usage(thing), buff, bp);
}



/* ARGSUSED */
FUNCTION(fun_playermem)
{
  int tot = 0;
  dbref thing;
  dbref j;

  if (!Search_All(executor)) {
    safe_str(T(e_perm), buff, bp);
    return;
  }
  if (!strcasecmp(args[0], "me") && IsPlayer(executor))
    thing = executor;
  else if (*args[0] && *args[0] == '*')
    thing = lookup_player(args[0] + 1);
  else if (*args[0] && *args[0] == '#')
    thing = atoi(args[0] + 1);
  else
    thing = lookup_player(args[0]);
  if (!GoodObject(thing) || !IsPlayer(thing)) {
    safe_str(T(e_match), buff, bp);
    return;
  }
  if (!Can_Examine(executor, thing)) {
    safe_str(T(e_perm), buff, bp);
    return;
  }
  for (j = 0; j < db_top; j++)
    if (Owner(j) == thing)
      tot += mem_usage(j);
  safe_integer(tot, buff, bp);
}


/** Reboot the game without disconnecting players.
 * \verbatim
 * This implements @shutdown/reboot, which performs a dump, saves
 * information about which player is associated with which socket,
 * and then re-execs the mush process without closing the sockets.
 * \endverbatim
 * \param player the enactor.
 * \param flag if 0, normal dump; if 1, paranoid dump.
 */
void
do_reboot(dbref player, int flag)
{
  if (player == NOTHING) {
    flag_broadcast(0, 0,
		   T
		   ("GAME: Reboot w/o disconnect from game account, please wait."));
  } else {
    flag_broadcast(0, 0,
		   T
		   ("GAME: Reboot w/o disconnect by %s, please wait."),
		   Name(Owner(player)));
  }
  if (flag) {
    globals.paranoid_dump = 1;
    globals.paranoid_checkpt = db_top / 5;
    if (globals.paranoid_checkpt < 1)
      globals.paranoid_checkpt = 1;
  }
#ifdef HAS_OPENSSL
  close_ssl_connections();
#endif
  sql_shutdown();
  shutdown_queues();
  fork_and_dump(0);
#ifndef PROFILING
#ifndef WIN32
  /* Some broken libcs appear to retain the itimer across exec!
   * So we make sure that if we get a SIGPROF in our next incarnation,
   * we ignore it until our proper handler is set up.
   */
  ignore_signal(SIGPROF);
#endif
#endif
  dump_reboot_db();
#ifdef INFO_SLAVE
  kill_info_slave();
#endif
  local_shutdown();
  end_all_logs();
#ifndef WIN32
  execl("netmush", "netmush", confname, NULL);
#else
  execl("pennmush.exe", "pennmush.exe", "/run", NULL);
#endif				/* WIN32 */
  exit(1);			/* Shouldn't ever get here, but just in case... */
}