dmuck0.15-beta/docs/muf/
dmuck0.15-beta/game/
dmuck0.15-beta/game/logs/
dmuck0.15-beta/game/muf/
dmuck0.15-beta/game/muf/text/
#include "config.h"   /* Its nice to know params.h #includes config.h */

/*  PLEASE go read the copyright notice contained in mush.h  --Howard  */

#ifdef MUSH           /* Are we running any MUSH type functions? */

#include "copyright.h"
#include "params.h"
#include "db.h"
#include "interface.h"
#include "externs.h"
#include "mush.h"
#include "match.h"
#include "money.h"
#include "version.h"
#include <ctype.h>
#include <varargs.h>
#include <string.h>
#include <math.h>
#include <sys/types.h>

dbref Parent(dbref thing)
{
 char *str;
 dbref temp;

  if(!GoodObject(thing)) return NOTHING;
  str = get_property_data(thing, "*PARENT", ACCESS_WI);
       if(!str)
          return NOTHING;
       else {
          temp = atol(str);
          if(!GoodObject(temp)) temp = NOTHING;
       }
  return temp;
}

struct dbref_list *listcreate(dbref ref)
{      /* takes a dbref and returns a pointer to the head of a dbref_list */
  struct dbref_list *ptr;

  ptr = (struct dbref_list *) malloc((unsigned)sizeof(struct dbref_list));
  if (!ptr) panic("Out of memory.");
  ptr->object = ref;
  ptr->next = NULL;
  return(ptr);
}

void listfree(dbref_list *head)
{                /* takes a pointer to a dbref_list and recursively frees it */
  struct dbref_list *ptra = head, *ptrb;

  while (ptra->next) {
    ptrb = ptra->next;
    free(ptra);
    ptra = ptrb;
  }
}

void listadd(dbref_list *head, dbref ref)
{
  struct dbref_list *ptr = head;
 /* takes a pointer to a dbref_list and a dbref and adds the dbref to the
  * end of the list.  */

  while (ptr->next)
          ptr = ptr->next;

  ptr->next = listcreate(ref);
}

#ifdef FULL_INVIS
char *spname(dbref thing)
{
  /* if FULL_INVIS is defined, dark objects and dark wizards will be
   * Something and Someone, respectively.
   */

  if (!Dark(thing))
    return (unparse_name(thing));
  else {
    if (Typeof(thing) != TYPE_PLAYER)
      return ("Something");
    else
      return ("Someone");
  }
}

#else
#define spname(x)      (unparse_name(x))
#endif				/* FULL_INVIS */

dbref find_entrance(dbref door)
{
  dbref room, thing;

  for (room = 0; room < db_top; room++)
    if (Typeof(room) == TYPE_ROOM) {
      thing = DBFETCH(room)->exits;
      while (thing != NOTHING) {
        if (thing == door)
          return room;
        thing = DBFETCH(thing)->next;
      }
    }
  return NOTHING;
}

void do_mset(__DO_PROTO)
{
  char *tmp, *tmp1;
  char string[BUFFER_LEN];

    /* find arg1 -- move over command word */
    for(tmp = arg1; *tmp && !isspace(*tmp); tmp++);

    /* truncate command */
    if(*tmp) *tmp++ = '\0';
    
    /* move over spaces */
    while(*arg1 && isspace(*arg1)) arg1++;
  
    /* Eat any lingering spaces */
    tmp1 = tmp;
    while(*tmp1 && isspace(*tmp1)) tmp1++;

    sprintf(string, "%s:%s", arg1, arg2);
    do_set(player, tmp1, string, string);
}

/* check the current location for bugs */
void do_sweep(dbref player, char *arg1)
{
  char tbuf1[BUFFER_LEN];
  char *p;
  dbref here = DBFETCH(player)->location;
  int connect_flag = 0;
  int here_flag = 0;
  int inven_flag = 0;
  int exit_flag = 0;

  if (here == NOTHING) return;

  if(arg1 && *arg1) {
    if (string_prefix(arg1, "connected"))
      connect_flag = 1;
    else if (string_prefix(arg1, "here"))
      here_flag = 1;
    else if (string_prefix(arg1, "inventory"))
      inven_flag = 1;
    else if (string_prefix(arg1, "exits"))
      exit_flag = 1;
    else {
      notify(player, player, "Invalid parameter.");
      return;
    }
  }

  if (!inven_flag && !exit_flag) {
    notify(player, player, "Listening in ROOM:");

    if(connect_flag) {
      /* only worry about puppet and players who's owner's are connected */
      if (Connected(here) || (Puppet(here) && Connected(OWNER(here)))) {
	if(Typeof(here) == TYPE_PLAYER) {
	  notify(player, player, tprintf("%s is listening", 
                 unparse_name(here)));
	} else {
	  notify(player , player, tprintf("%s [owner: %s] is listening.", 
	         unparse_name(here), unparse_name(OWNER(here))));
	}
      }
    } else {
      if (Hearer(here) || Listener(here)) {
	if (Connected(here))
	  notify(player, player, tprintf("%s (this room) [speech]. (connected)",
				 unparse_name(here)));
	else
	  notify(player, player, tprintf("%s (this room) [speech].", 
                unparse_name(here)));
      }
      if (Commer(here))
	notify(player, player, tprintf("%s (this room) [commands].", 
               unparse_name(here)));
      if (Audible(here))
	notify(player, player, tprintf("%s (this room) [broadcasting].", 
			       unparse_name(here)));
    }

    for (here = DBFETCH(here)->contents; here != NOTHING; 
         here = DBFETCH(here)->next) {
      if(connect_flag) {
	/* only worry about puppet and players who's owner's are connected */
	if (Connected(here) || (Puppet(here) && Connected(OWNER(here)))) {
	  if(Typeof(here) == TYPE_PLAYER) {
	    notify(player, player, tprintf("%s is listening", 
                   unparse_name(here)));
	  } else {
	    notify(player , player, tprintf("%s [owner: %s] is listening.", 
		unparse_name(here), unparse_name(OWNER(here))));
	  }
	}
      } else {
	if (Hearer(here) || Listener(here)) {
	  if (Connected(here))
	    notify(player, player, tprintf("%s [speech]. (connected)", 
                   unparse_name(here)));
	  else
	    notify(player, player, tprintf("%s [speech].", unparse_name(here)));
	} 
	if(Commer(here))
	  notify(player, player, tprintf("%s [commands].", unparse_name(here)));
      }
    }
  }

  if (!connect_flag && !inven_flag && 
      (Typeof(Location(player)) == TYPE_ROOM)) {
    notify(player, player, "Listening EXITS:");

      for (here = DBFETCH(Location(player))->exits; here != NOTHING;
	   here = DBFETCH(here)->next) {
	if (FLAGS(here) & AUDIBLE || FLAGS(here) & HAVEN) {
	  strcpy(tbuf1, unparse_name(here));
	  for (p = tbuf1; *p && (*p != ';'); p++)
	    ;
	  *p = '\0';
	  notify(player, player, tprintf("%s [broadcasting].", tbuf1));
	}
      }
  }

  if (!here_flag && !exit_flag) {
    notify(player, player, "Listening in your INVENTORY:");

    for (here = Contents(player); here != NOTHING; here = DBFETCH(here)->next) {
      if(connect_flag) {
	/* only worry about puppet and players who's owner's are connected */
	if (Connected(here) || (Puppet(here) && Connected(OWNER(here)))) {
	  if(Typeof(here) == TYPE_PLAYER) {
	    notify(player, player, tprintf("%s is listening", 
                   unparse_name(here)));
	  } else {
	    notify(player , player, tprintf("%s [owner: %s] is listening.", 
		unparse_name(here), unparse_name(OWNER(here))));
	  }
	}
      } else {
	if (Hearer(here) || Listener(here)) {
	  if (Typeof(here) == TYPE_PLAYER && Connected(here))
	    notify(player, player, tprintf("%s [speech]. (connected)", 
                   unparse_name(here)));
	  else
	    notify(player, player, tprintf("%s [speech].", unparse_name(here)));
	} 
	if(Commer(here))
	  notify(player, player, tprintf("%s [commands].", unparse_name(here)));
      }
    }
  }
}

void do_trigger(dbref player, char *objct, char *argv[])
{
  dbref thing;
  int a;
  char *s;
  char tbuf1[BUFFER_LEN];
  match_data md;

  strcpy(tbuf1, objct);
  for(s = tbuf1; *s && (*s != '/'); s++);
  if(!*s) {
    notify(player, player, "I need to know what attribute to trigger.");
    return;
  }
  *s++ = '\0';

  init_match(player, tbuf1, NOTYPE, &md);
  match_everything(&md);
  thing = noisy_match_result(&md);

  if(thing == NOTHING) return;

  if (!controls(player, thing)) {
    notify(player, player, "Permission denied.");
    return;
  }

  if (God(thing) && !God(player)) {
    notify(player, player, "You can't trigger God!");
    return;
  }

  /* trigger modifies the stack */
  for (a = 0; a < 10; a++)
    wptr[a] = argv[a + 1];

  /* we need to call did_it because trigger uses a charge */
  did_it(player, thing, NULL, NULL, NULL, NULL, s, NOTHING);
/*
  if(!Quiet(player) && !Quiet(thing))
*/
    notify(player, player, tprintf("%s - Triggered.", unparse_name(thing)));
}

void do_verb(dbref player, dbref cause, char *arg1, char *argv[])
{
  dbref victim;       /* user-defined verbs */
  dbref actor;
  char *sptr[10];
  match_data md;
  int i;

  /* find the object that we want to read the attributes off
   * (the object that was the victim of the command)
   */

  /* our victim object can be anything */
  init_match(player, arg1, NOTYPE, &md);
  match_everything(&md);
  victim = match_result(&md);

  if (victim == NOTHING) {
    notify(player, player, "What was the victim of the verb?");
    return;
  }

  /* find the object that executes the action */
 if(argv[1]) {
    init_match(player, argv[1], NOTYPE, &md);
    match_near_things(&md);
    actor = match_result(&md);
  }
  else
    actor = NOTHING;

  if (actor == NOTHING) {
    notify(player, player, "What do you want to do the verb?");
    return;
  }

  /* Control check is fascist. 
   * First check: we don't want <actor> to do something involuntarily.
   *   Both victim and actor have to be controlled by the thing which did 
   *   the @verb (for speed we do a WIZARD check first), or: cause controls
   *   actor plus the second check is passed.
   * Second check: we need read access to the attributes.
   *   Either the player controls victim or the player
   *   must be priviledged, or the victim has to be VISUAL.
   */

  if (!(Wizard(player) || 
	(controls(player, victim)  && controls(player, actor)) ||
	((controls(cause, actor) && Can_Examine(player, victim))))) {
    notify(player, player, "Permission denied.");
    return;
  }

  /* we're okay. Now we copy our args into the stack, saving
   * the old stack so we can restore it later.
   */
  
  for (i = 0; i < 10; i++)
    sptr[i] = wptr[i];

  for (i = 0; i < 10; i++)
    wptr[i] = argv[i + 7];

  /* do the command, then restore the stack */
  did_it(actor, victim, argv[2], argv[3], argv[4], argv[5], argv[6], 
         getloc(actor));

  for (i = 0; i < 10; i++)
    wptr[i] = sptr[i];
}

void do_poor(dbref player, char *arg1)
{
  int amt = atoi(arg1);
  dbref a;

  if (!God(player)) {
    notify(player, player, "Only God can cause financial ruin.");
    return;
  }

  for (a = 0; a < db_top; a++)
    if (Typeof(a) == TYPE_PLAYER) {
      DBSTORE(a, pennies, amt);
      DBDIRTY(a);
   }

  notify(player, player,
	 tprintf("The money supply of all players has been reset to %d %s.",
		 amt, PL_MONEY));
  log_status("** POOR done ** Money supply reset to %d %s. by %s.",
	 amt, PL_MONEY, unparse_name(player));
}

void do_lemit(dbref player, char *tbuf1)
{
  dbref room;      /* give a message to the "absolute" location of an object */
  int rec = 0;

  /* only players and things may use this command */
  if (!Mobile(player))
    return;

  /* prevent infinite loop if player is inside himself */
  if (((room = DBFETCH(player)->location) == player) || !GoodObject(room)) {
    notify(player, player, "Invalid container object.");
    fprintf(stderr, "** BAD CONTAINER **  #%d is inside #%d.\n",
	    player, room);
    return;
  }
  while ((Typeof(room) != TYPE_ROOM) && (rec < 15)) {
    room = DBFETCH(room)->location;
    rec++;
  }
  if (rec > 15) {
    notify(player, player, "Too many containers.");
    return;
  } else {
    notify(player, player, tprintf("You lemit: \"%s\"", tbuf1));
    oemit_notify_except(DBFETCH(room)->contents, player, room, tbuf1);
  }
}

void do_enter(dbref victim, char *arg1)
{
  dbref dest;
  match_data md;

  /* get destination */
  init_match(victim, arg1, NOTYPE, &md);
  match_here(&md);
  match_home(&md);
  match_absolute(&md);
  match_neighbor(&md);
  match_me(&md);
  match_player(&md);
  dest = match_result(&md);

  switch(dest) {
    case NOTHING:
      notify(victim, victim, "Enter what?");
      return;
    case AMBIGUOUS:
      notify(victim, victim, "I don't know which object you mean!");
      return;
  }

  if (parent_loop_check(victim, dest)) {
    notify(victim, victim, "Permission denied.");
    return;
  }

 if(Typeof(dest) == TYPE_GARBAGE) {
    notify(victim, victim, "Can't enter garbage.");
    return;
  }                        

   if(victim == dest) {
      notify(victim, victim, "Sorry, you must remain beside yourself!");
      return;
    }

  if ((((FLAGS(dest) & ENTER_OK) || controls(victim, dest)) &&
       (eval_boolexp(victim, get_ue_locks(victim, dest, ENTERLOCK), dest))))
        enter_room(victim, dest, DBFETCH(victim)->location);
  else 
         did_it(victim, dest, "EFAIL", "Permission denied.", "OEFAIL",
	        NULL, "AEFAIL", NOTHING);
}

void do_pemit(dbref player, char *arg1, char *arg2, int silent)
{
  dbref who;
  match_data md;

  init_match(player, arg1, NOTYPE, &md);
  match_neighbor(&md);
  match_possession(&md);
  match_container(&md);
  match_me(&md);
  match_here(&md);
  match_player(&md);
  match_absolute(&md);
  switch (who = match_result(&md)) {
  case NOTHING:
    notify(player, player, "I don't see that player here.");
    break;
  case AMBIGUOUS:
    notify(player, player, "I don't know who you mean!");
    break;
  default:
    if (Typeof(who) != TYPE_PLAYER && Typeof(who) != TYPE_THING) {
      notify(player, player, "Only players and things can hear @pemits.");
      break;
    }

    if ((player != who) && (Haven(who) && (Typeof(who) == TYPE_PLAYER))) {
      notify(player, player, 
             tprintf("I'm sorry, but %s wishes to be left alone now.",
			    unparse_name(who)));
      return;
    }

    if (!silent)
      notify(player, player,
	     tprintf("You pemit \"%s\" to %s.", arg2, unparse_name(who)));

      notify(player, who, tprintf("%s", arg2));
    break;
  }
}

void do_use(dbref player, char *what)
{
  dbref thing;
  match_data md;

  init_match(player, what, TYPE_THING, &md);
  match_near_things(&md);

  /* if we pass the use key, do it */
  if ((thing = noisy_match_result(&md)) != NOTHING) 
    if (!eval_boolexp(player, get_ue_locks(player, thing, USELOCK), thing)) {
      notify(player, player, "Permission denied.");
      return;
    } else
        did_it(player, thing, "USE", "Used.", "OUSE", NULL, "AUSE", NOTHING);
}

void do_wipe(dbref player, char *name)
{
  dbref thing;      /* obliterate all attribute from an object */
  match_data md;

  init_match(player, name, NOTYPE, &md);
  match_nearby(&md);
  if ((thing = noisy_match_result(&md)) == NOTHING) return;

  /* this is too destructive of a command to be used by someone who
   * doesn't own the object. Thus, the check is on Owns not controls.  */

  if (!Wizard(player) && (OWNER(player) != OWNER(thing))) {
    notify(player, player, "Permission denied.");
    return;
  }

  if (Safe(thing)) {
    notify(player, player, "That object is set SAFE.");
    return;
  }
   
  /*  ICK!  */
  burn_proptree(DBFETCHPROP(thing));
  DBSTOREPROP(thing, NULL);
  DBDIRTY(thing);
  notify(player, player, "Attributes wiped.");
}

void do_remit(dbref player, char *arg1, char *arg2)
{
  dbref room;
  match_data md;
  char *rmno;

  init_match(player, arg1, NOTYPE, &md);
  match_here(&md);
  match_absolute(&md);
  match_neighbor(&md);
  match_me(&md);
  match_player(&md);
  match_room_exits(DBFETCH(player)->location, &md);

  switch (room = match_result(&md)) {
    case NOTHING:
    case AMBIGUOUS:
      notify(player, player, "I can't find that.");
      break;
    default:
      if (Typeof(room) == TYPE_EXIT) {
	notify(player, player, "There can't be anything in that!");
	break;
      }
      if (Typeof(room) == TYPE_PLAYER && Haven(room)) {
	notify(player, player, 
               tprintf("I'm sorry, but %s wishes to be left alone now.",
			      unparse_name(room)));
	break;
      }

      rmno = unparse_object(player, room);
      notify(player, player,
        tprintf("You remit, \"%s\" in %s", arg2, rmno));
      oemit_notify_except(DBFETCH(room)->contents, player, room, arg2);
  }
}

void do_oemit(dbref player, char *arg1, char *arg2)
{
  dbref who;
  dbref loc;
  match_data md;
  void oemit_notify_except();

  init_match(player, arg1, TYPE_PLAYER, &md);
  if ((loc = getloc(player)) == NOTHING)
    return;
  match_neighbor(&md);
  match_me(&md);
  switch (who = match_result(&md)) {
    case NOTHING:
    case AMBIGUOUS:
      who = player;
    default:
      if (who != player)
	notify_noecho(player, arg2);
      oemit_notify_except(DBFETCH(loc)->contents, player, who, arg2);
  }
}

void oemit_notify_except(dbref first, dbref exc1, dbref exc2, char *msg)
{
  dbref loc = DBFETCH(first)->location;
  dbref e;
  char tbuf1[BUFFER_LEN];

  if(first < 0 || first >= db_top) return;

  if ((loc != exc1) && (loc != exc2))
    esnotify(loc, msg, exc1);
  DOLIST(first, first) {
    if (first != exc1 && first != exc2) {
      esnotify(first, msg, exc1);
    }
  }

  /* do AUDIBLE stuff */
  if (Audible(loc)) {
    if (Typeof(loc) == TYPE_ROOM) {
      strcpy(tbuf1, msg);
      DOLIST(e, DBFETCH(loc)->exits) {
	if (Audible(e))
	  propagate_sound(e, tbuf1);
      }
    } else {
      strcpy(tbuf1, msg);
      propagate_sound(loc, tbuf1);
    }
  }
}

void esnotify(dbref player, char *msg, dbref sender)
{
  if (player < 0 || player >= db_top) return;

  if (FLAGS(player) & NOSPOOF) {
#ifdef PARANOID_NOSPOOF
    notify_noecho(player, 
		  tprintf("[%s(#%d)] %s", spname(sender), sender, msg));
#else
    notify_noecho(player, tprintf("[%s:] %s", spname(sender), msg));
#endif
  } else {
    notify_noecho(player, msg);
  }
}

void do_emit(dbref player, char *tbuf1)
{
  dbref loc;

  if ((loc = getloc(player)) == NOTHING) return;

  /* notify everybody */
  notify_noecho(player, tbuf1);
  emit_notify_except(DBFETCH(loc)->contents, player, tbuf1);
}

void emit_notify_except(dbref first, dbref exception, char *msg)
{
  dbref loc = DBFETCH(first)->location;
  dbref e;
  char tbuf1[BUFFER_LEN];

  if(first < 0 || first >= db_top) return;

  if (loc != exception)
    esnotify(loc, msg, exception);
  DOLIST(first, first) {
    if (first != exception) {
      esnotify(first, msg, exception);
    }
  }

  /* do AUDIBLE stuff */
  if (Audible(loc)) {
    if (Typeof(loc) == TYPE_ROOM) {
      strcpy(tbuf1, msg);
      DOLIST(e, DBFETCH(loc)->exits) {
	if (Audible(e))
	  propagate_sound(e, tbuf1);
      }
    } else if (loc != exception) {
      strcpy(tbuf1, msg);
      propagate_sound(loc, tbuf1);
    }
  }
}

void do_decompile(dbref player, char *name)
{
  dbref thing;
  char *objct;
  char tbuf[BUFFER_LEN];
  char *attrib;
  match_data md;

  /* @decompile must always have an argument */
  if (!name || !*name) {
    notify(player, player, "What do you want to @decompile?");
    return;
  }

  attrib = (char *) index(name, '/');
  if (attrib)
    *attrib++ = '\0';

  /* find object */
  init_match(player, name, NOTYPE, &md);
  match_everything(&md);
  if ((thing = noisy_match_result(&md)) == NOTHING)
      return;

  if (!Can_Examine(player, thing)) {
    notify(player, player, "Permission denied.");
    return;
  }

  if (Typeof(thing) == TYPE_GARBAGE) {
    notify(player, player, "Garbage is garbage.");
    return;
  }

  /* if we have an attribute arg specified, wild match on it */
  if (attrib && *attrib) {
    decompile_atrs(player, thing, NAME(thing), attrib);
    return;
  }

  /* else we have a full decompile */

  /* determine creation and what we call the object */
  switch (Typeof(thing)) {
  case TYPE_PLAYER:
    objct = "me";
    break;
  case TYPE_THING:
    objct = NAME(thing);
    notify(player, player, tprintf("@create %s", objct));
    break;
  case TYPE_ROOM:
    notify(player, player, tprintf("@dig/teleport %s", NAME(thing)));
    objct = "here";
    break;
  case TYPE_EXIT:
    objct = NAME(thing);
    notify(player, player, tprintf("@open %s", objct));
    break;
  }

  if (Mobile(thing)) {
    if (DBFETCH(thing)->link)
      notify(player, player, 
           tprintf("@link %s = #%d", objct, DBFETCH(thing)->link));
    else if (DBFETCH(thing)->link == HOME)
      notify(player, player, tprintf("@link %s = HOME", objct));
  } else {
    if (GoodObject(Location(thing)))
      notify(player, player, 
              tprintf("@tel %s = #%d", objct, Location(thing)));
    else if (Location(thing) == HOME)
      notify(player, player, tprintf("@tel %s = HOME", objct));
  }

  sprintf(tbuf, "%s", unparse_boolexp(player, DBFETCH(thing)->key));
  if (strcmp(tbuf, "*UNLOCKED*"))
    notify(player, player, tprintf("@lock %s = %s", objct, tbuf));

  if(GET_SUCC(thing))
  {
    sprintf(tbuf, "@succ %s=%s", objct, GET_SUCC(thing));
    notify(player, player, tbuf);
  }

  if(GET_FAIL(thing))
  {
    sprintf(tbuf, "@fail %s=%s", objct, GET_FAIL(thing));
    notify(player, player, tbuf);
  }

  if (GET_DROP(thing))
  {
    sprintf(tbuf, "@drop %s=%s", objct, GET_DROP(thing));
    notify(player, player, tbuf);
  }

  if(GET_OSUCC(thing))
  {
    sprintf(tbuf, "@osucc %s=%s", objct, GET_OSUCC(thing));
    notify(player, player, tbuf);
  }

  if(GET_OFAIL(thing))
  {
    sprintf(tbuf, "@ofail %s=%s", objct, GET_OFAIL(thing));
    notify(player, player, tbuf);
  }

  if(GET_ODROP(thing))
  {
    sprintf(tbuf, "@odrop %s=%s", objct, GET_ODROP(thing));
    notify(player, player, tbuf);
  }

  decompile_flags(player, thing, objct);
  decompile_atrs(player, thing, objct, "*");
}

void decompile_atrs(dbref player, dbref thing, char *name, char *pattern)
{
  propdir *ptr;
 
  for (ptr = DBFETCHPROP(thing); ptr; ptr = ptr->next) {
      if (wild_match(pattern, ptr->name)) {
#ifdef COMPRESS
       if(ptr->data)
          notify(player, player, tprintf("@set %s=%s:%s", name, ptr->name, 
                                 uncompress(ptr->data)));
#else
       if(ptr->data)
          notify(player, player, tprintf("@set %s=%s:%s", name, 
                 ptr->name, ptr->data));
#endif
     }
  }
}


void do_grep(dbref player, char *obj, char *lookfor, int flag)
{
  dbref thing;
  char *pattern, *tp;
  match_data md;
  int len;
  
  if ((len = strlen(lookfor)) < 1) {
    notify(player, player, "What pattern do you want to grep for?");
    return;
  }

  /* find the attribute pattern */
  pattern = (char *) index(obj, '/');
  if (!pattern)
    pattern = (char *) "*";	/* set it to global match */
  else
    *pattern++ = '\0';

  /* now we've got the object. match for it. */
  init_match(player, obj, NOTYPE, &md);
  match_everything(&md);
  thing = noisy_match_result(&md);
  if (thing == NOTHING)
    return;
  if (!Can_Examine(player, thing)) {
    notify(player, player, "Permission denied.");
    return;
  }

    notify(player, player,
      tprintf("The following attributes on %s have contents matching '%s':",
	      unparse_name(thing), lookfor));
    tp = grep_util(thing, pattern, lookfor, len);
    notify(player, player, tp);
    if(tp) free(tp);
}

  /* returns a list of attributes which match <pattern> on <thing>
   * whose contents have <lookfor> */
char *grep_util(dbref thing, char *pattern, char *lookfor, int len)
{
  char *tbuf1;
  char *s, *bp;
  int found;
  propdir *a;

  tbuf1 = (char *) malloc(BUFFER_LEN + 1);
  bp = tbuf1;

  for (a = DBFETCHPROP(thing); a; a = a->next) {
    if (wild_match(pattern, a->name)) {
      s = (char *) uncompress(a->data);              /* warning: static */
      found = 0;
      while (*s && !found) {
	if (!strncmp(lookfor, s, len))
	  found = 1;
	else 
	  s++;
      }
      if (found) {
	if (bp != tbuf1)
	  safe_chr(' ', tbuf1, &bp);
	safe_str(a->name, tbuf1, &bp);
      }
    }
  }
  *bp = '\0';
  return tbuf1;
}

void do_clone(dbref player, char *name)
{
  dbref clone, thing;
  match_data md;

  init_match(player, name, NOTYPE, &md);
  match_everything(&md);

  thing = noisy_match_result(&md);
  if ((thing == NOTHING)) return;
  if (!controls(player, thing)) {
    notify(player, player, "Permission denied.");
    return;
  }

  /* make sure owner can afford it */
  switch (Typeof(thing)) {
  case TYPE_THING:
    if (!Builder(player)) {
      notify(player, player, "Command is for builders only.");
      return;
    }
  if (!Wizard(player)) {
      if (!payfor(player, OBJECT_DEPOSIT(DBFETCH(thing)->pennies)))
        notify(player, player, tprintf("You don't have enough %s.", PL_MONEY));
  }
   
  clone = new_object();
 *DBFETCH(clone)= *DBFETCH(thing);
  DBSTORE(clone, name, dup_string(NAME(thing)));
#ifndef USE_DBP_STR
  DBSTORE(clone, desc, dup_string(GET_DESC(thing)));
  DBSTORE(clone, succ, dup_string(GET_SUCC(thing)));
  DBSTORE(clone, fail, dup_string(GET_FAIL(thing)));
  DBSTORE(clone, drop, dup_string(GET_DROP(thing)));
  DBSTORE(clone, osucc, dup_string(GET_OSUCC(thing)));
  DBSTORE(clone, ofail, dup_string(GET_OFAIL(thing)));
  DBSTORE(clone, odrop, dup_string(GET_ODROP(thing)));
#else
  add_property(clone, "desc", GET_DESC(thing), PERMS_COREAD | PERMS_COWRITE |
      PERMS_OTREAD, ACCESS_CO);
  add_property(clone, "desc", GET_SUCC(thing), PERMS_COREAD | PERMS_COWRITE |
      PERMS_OTREAD, ACCESS_CO);
  add_property(clone, "desc", GET_FAIL(thing), PERMS_COREAD | PERMS_COWRITE |
      PERMS_OTREAD, ACCESS_CO);
  add_property(clone, "desc", GET_DROP(thing), PERMS_COREAD | PERMS_COWRITE |
      PERMS_OTREAD, ACCESS_CO);
  add_property(clone, "desc", GET_OSUCC(thing), PERMS_COREAD | PERMS_COWRITE |
      PERMS_OTREAD, ACCESS_CO);
  add_property(clone, "desc", GET_OFAIL(thing), PERMS_COREAD | PERMS_COWRITE |
      PERMS_OTREAD, ACCESS_CO);
  add_property(clone, "desc", GET_ODROP(thing), PERMS_COREAD | PERMS_COWRITE |
      PERMS_OTREAD, ACCESS_CO);
#endif
  DBSTORE(clone, pennies, DBFETCH(thing)->pennies);
  DBSTORE(clone, key, copy_bool(DBFETCH(thing)->key));
  DBSTORE(clone, exits, NOTHING);
  copy_prop(thing, clone, access_rights(player, thing, NOTHING));
  add_backlocks_parse(clone, DBFETCH(clone)->key);
  add_ownerlist(clone);
  add_backlinks(clone);
  moveto(clone, DBFETCH(player)->location);
  FLAGS(clone) &= ~WIZARD;      /*  Don't let cloned object inherit privs */
  FLAGS(clone) &= ~GOD;
  DBDIRTY(clone);

  notify(player, player, tprintf("Cloned: Object #%ld.", clone));
  did_it(player, clone, NULL, NULL, NULL, NULL, "ACLONE", NOTHING);
  break;
  default:
    notify(player, player, "You can only clone things.");
  }
}

void do_search(dbref player, char *arg1, char *arg3[])
{
  int flag;
  char *arg2;
  dbref thing;
  dbref from;
/*  dbref to; */
  int is_wizard;
  int destitute = 1;
  char *restrict_name;
  dbref restrict_owner = NOTHING;
  dbref restrict_zone = NOTHING;
  dbref restrict_parent = NOTHING;
  object_flag_type flag_mask, toggle_mask, restrict_type;
  object_flag_type power_mask;
  char tbuf1[BUFFER_LEN], tmp[BUFFER_LEN];
  int rcount, ecount, pcount, ocount, i=0;
  struct dbref_list *found = NULL, *ptr = NULL, *rlist = NULL,
                *elist = NULL, *olist = NULL, *plist = NULL;
  int bot = 0;
  int top = db_top;
  match_data md;

/*
  if (o_daytime) {
    notify(player, "Sorry, that command has been temporarily disabled.");
    return;
  }
*/

  /* make sure player has money to do the search */
  if (!payfor(player, LOOKUP_COST)) {
    notify(player, player, tprintf("Searches cost %d %s.", LOOKUP_COST,
			   ((LOOKUP_COST == 1) ? S_MONEY : PL_MONEY)));
    return;
  }
  
  /* 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[1] || !*arg3[1])
      arg2 = (char *) "";	/* arg1 */
    else {
      arg2 = (char *) arg1;	/* arg2, arg3 */
      arg1 = (char *) "";
    }
  }

  is_wizard = Wizard(player);

  /* set limits on who we search */
  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, player, tprintf("%s: No such player.", arg1));
    return;
  }

  /* set limits on what we search for */
  flag = 0;
  flag_mask = 0;
  power_mask = 0;
  toggle_mask = 0;
  restrict_name = NULL;
  restrict_type = NOTYPE;
  switch (arg2[0]) {
    case '\0':
      /* the no class requested class  :)  */
      break;
    case 'e':
    case 'E':
      if (string_prefix("exits", arg2)) {
	restrict_name = (char *) arg3[1];
	restrict_type = TYPE_EXIT;
      } else
	flag = 1;
      break;
    case 'f':
    case 'F':
      if (string_prefix("flags", arg2)) {
	/*
	 * convert_flags ignores previous values of flag_mask and
	 * restrict_type while setting them
	 */
	if (arg3[1] && *arg3[1] && 
	    !convert_flags(player, arg3[1], &flag_mask, &toggle_mask,
			   &restrict_type))
	  return;
      } else
	flag = 1;
      break;
    case 'n':
    case 'N':
      if (string_prefix("name", arg2))
	restrict_name = (char *) arg3[1];
      else
	flag = 1;
      break;
    case 'o':
    case 'O':
      if (string_prefix("objects", arg2)) {
	restrict_name = (char *) arg3[1];
	restrict_type = TYPE_THING;
      } else
	flag = 1;
      break;
    case 'p':
    case 'P':
      switch (arg2[1]) {
      case 'a':
      case 'A':
	if (string_prefix("parent", arg2)) {
	  if (arg3[1] && *arg3[1]) {
	    init_match(player, arg3[1], NOTYPE, &md);
	    match_absolute(&md);
	    restrict_parent = match_result(&md);
	  }
	  if (restrict_parent == NOTHING) {
	    notify(player, player, "Unknown parent.");
	    return;
	  }
	} else
	  flag = 1;
	break;
      case 'l':
      case 'L':
	if (string_prefix("players", arg2)) {
	  restrict_name = (char *) arg3[1];
	  if (!arg1 || !*arg1)
	    restrict_owner = ANY_OWNER;
	  restrict_type = TYPE_PLAYER;
	} else
	  flag = 1;
	break;
      case 'o':
      case 'O':
	if (string_prefix("powers", arg2)) {
/*	  power_mask = find_power(arg3[1]); */
	  power_mask = -1;
	  if (power_mask == -1) {
	    notify(player, player, "No such power to search for.");
	    return;
	  }
	} else
	  flag = 1;
	break;
      default:
	flag = 1;
      }
      break;
    case 'r':
    case 'R':
      if (string_prefix("rooms", arg2)) {
	restrict_name = (char *) arg3[1];
	restrict_type = TYPE_ROOM;
      } else
	flag = 1;
      break;
    case 't':
    case 'T':
      if (string_prefix("type", arg2)) {
	if (!arg3[1] || !*arg3[1])
	  break;
	if (string_prefix("rooms", arg3[1]))
	  restrict_type = TYPE_ROOM;
	else if (string_prefix("exits", arg3[1]))
	  restrict_type = TYPE_EXIT;
	else if (string_prefix("objects", arg3[1]))
	  restrict_type = TYPE_THING;
	else if (string_prefix("players", arg3[1])) {
	  if (!arg1 || !*arg1)
	    restrict_owner = ANY_OWNER;
	  restrict_type = TYPE_PLAYER;
	} else {
	  notify(player, player, tprintf("%s: unknown type.", arg3[1]));
	  return;
	}
      } else
	flag = 1;
      break;
    case 'z':
    case 'Z':
      if (string_prefix("zone", arg2)) {
	if (arg3[1] && *arg3[1]) {
	  init_match(player, arg3[1], TYPE_THING, &md);
	  match_absolute(&md);
	  restrict_zone = match_result(&md);
	}
	if (restrict_zone == NOTHING) {
	  notify(player, player, "Unknown zone.");
	  return;
	}
      } else
	flag = 1;
      break;
    default:
      flag = 1;
  }
  if (flag) {
    notify(player, 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, player, "You need a search warrant to do that!");
    return;
  }

  /* find range */
  if (arg3[2] && *arg3[2])
    bot = atoi(arg3[2]);
  if (bot < 0)
    bot = 0;
  if (arg3[3] && *arg3[3])
    top = atoi(arg3[3]) + 1;
  if (top > db_top)
    top = db_top;

  /* the search loop here */
  flag = 1;
  for (thing = bot; thing < 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 ((FLAGS(thing) & flag_mask) != flag_mask)
	    continue;
    if (Typeof(thing) == TYPE_GARBAGE)
	    continue;
/*
    if ((Toggles(thing) & toggle_mask) != toggle_mask)
            continue;
    if ((Powers(thing) & power_mask) != power_mask)
            continue;
*/
    if ((restrict_owner != ANY_OWNER) && (restrict_owner != OWNER(thing)))
      continue;
    if (restrict_name != NULL)
	    if (!string_match(unparse_name(thing), restrict_name))
		    continue;
    if (restrict_parent != NOTHING)
      if (restrict_parent != Parent(thing))
	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, 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, player, "\nROOMS:");
    for (ptr = rlist; ptr; ptr = ptr->next) {
      sprintf(tbuf1, "%s [owner: ", unparse_object(player, ptr->object));
      strcat(tbuf1, tprintf("%s]",
			    unparse_object(player, DBFETCH(ptr->object)->owner)));
      notify(player, player, tbuf1);
      rcount++;
    }
  }
  if (restrict_type == TYPE_EXIT || restrict_type == NOTYPE) {
    notify(player, player, "\nEXITS:");
    for (ptr = elist; ptr; ptr = ptr->next) {
      from = DBFETCH(ptr->object)->location;

      sprintf(tbuf1, "%s [from ", unparse_object(player, ptr->object));
      sprintf(tmp, "%s to ", unparse_object(player, from));
      strcat(tbuf1, tmp); 

      if (DBFETCH(ptr->object)->sp.exit.ndest > 0) {
        for (i = 0; i < DBFETCH(ptr->object)->sp.exit.ndest; i++)
             strcat(tbuf1, tprintf("%s]", unparse_object(player,
                    (DBFETCH(ptr->object)->sp.exit.dest)[i])));
      } else
          strcat(tbuf1, tprintf("%s]", "NOWHERE"));

      notify(player, player, tbuf1);
      ecount++;
    }
  }
  if (restrict_type == TYPE_THING || restrict_type == NOTYPE) {
    notify(player, player, "\nOBJECTS:");
    for (ptr = olist; ptr; ptr = ptr->next) {
      sprintf(tbuf1, "%s [owner: ", unparse_object(player, ptr->object));
      strcat(tbuf1, tprintf("%s]",
			    unparse_object(player, DBFETCH(ptr->object)->owner)));
      notify(player, player, tbuf1);
      ocount++;
    }
  }
  if ((restrict_type == TYPE_PLAYER) || (restrict_type == NOTYPE)) {
    notify(player, player, "\nPLAYERS:");
    for (ptr = plist; ptr; ptr = ptr->next) {
      strcpy(tbuf1, unparse_object(player, ptr->object));
      if (is_wizard) {
	strcat(tbuf1, " [location: ");
	strcat(tbuf1, unparse_object(player, DBFETCH(ptr->object)->location));
	strcat(tbuf1, "]");
      }
      notify(player, player, tbuf1);
      pcount++;
    }
  }
  notify(player, player, "----------  Search Done  ----------");
  notify(player, 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);
}

void do_whereis(dbref player, char *name)
{
  dbref thing;
  char buff[BUFFER_LEN];

  if (*name == '\0') {
    notify(player, player, "You must specify a valid player name.");
    return;
  }

  if ((thing = lookup_player(name)) == NOTHING) {
    notify(player, player, "That player does not seem to exist.");
    return;
  }

  if (Unfindable(thing) || Unfindable(Location(thing))) {
    notify(player, player, "That player wishes to have some privacy.");
    notify(thing, thing, tprintf("%s tried to locate you and failed.",
	   unparse_name(player)));
    return;
  }
 
    strcpy(buff, unparse_name(thing));
    notify(player, player, tprintf("%s is at: %s.", buff,
           unparse_object(player, DBFETCH(thing)->location)));

    notify(thing, thing, tprintf("%s has just located your position.",
	   unparse_name(player)));
}

void do_parent(dbref player, char *name, char *parent_name)
{
  dbref thing;
  dbref parent;
  dbref check;
  match_data md;
  char temp[100];
  int i;

  init_match(player, name, NOTYPE, &md);
  match_nearby(&md);
  if ((thing = noisy_match_result(&md)) == NOTHING) return;

  /* players may not be parented */
  if (Typeof(thing) == TYPE_PLAYER) {
    notify(player, player, "Don't you like your biological parents?");
    return;
  }

  if (!parent_name || !*parent_name || !strcasecmp(parent_name, "none"))
    parent = NOTHING;
  else {
    init_match(player, parent_name, NOTYPE, &md);
    match_everything(&md);
    if ((parent = noisy_match_result(&md)) == NOTHING)
      return;
  }

  /* do control check */
  if (!controls(player, thing)) {
    notify(player, player, "Permission denied.");
    return;
  }

  /* a player may change an object's parent to NOTHING or to an 
   * object he owns, or one that is LINK_OK.
   */
  if ((parent != NOTHING) && !Wizard(player) &&
      (OWNER(parent) != player) && !LinkOk(parent)) {
    notify(player, player, "Permission denied.");
    return;
  }

  /* check to make sure no recursion can happen */
  if (parent == thing) {
    notify(player, player, "A thing cannot be its own ancestor!");
    return;
  }

  if (parent != NOTHING) {
    for (i = 0, check = Parent(parent); 
	 (i < MAX_PARENTS) && (check != NOTHING);
	 i++, check = Parent(check)) {
      if (check == thing) {
	notify(player, player, "You are not allowed to be your own ancestor!");
	return;
      }
    }
    if (i >= MAX_PARENTS) {
      notify(player, player, "Too many ancestors.");
      return;
    }
  }
#ifdef TIMESTAMPS
    DBSTORE(thing, time_modified, time(NULL));
#endif

  /* everything is okay, do the change */
  if(parent != NOTHING) {
     sprintf(temp, "%ld", parent);
     add_property(thing, "*PARENT", temp, default_perms("*PARENT"), ACCESS_WI);
     sprintf(temp, "Parent changed to %s.", unparse_name(parent));
   } else {
     remove_property(thing, "*PARENT", ACCESS_WI);
     strcpy(temp, "Parent deleted.");
    }

   notify(player, player, temp);
}

void do_think(dbref player, char *message)
{
  /* privately tell yourself a message */
  /* notify the player only, with no special fanfare */
  notify(player, player, tprintf("%s", message));
}

/* scan for possible matches of user-def'ed commands */
void do_scan(dbref player, char *command, int flag)
{ 
#define ScanFind(p,x)  \
  (Can_Examine(p,x) && \
      ((num = atr_comm_match(x, p, '$', ':', command, 1)) != 0))

  dbref thing;
  int num;

  if (!GoodObject(Location(player))) {
    notify(player, player, "Sorry, you are in an invalid location.");
    return;
  }
  if (!command || !*command) {
    notify(player, player, "What command do you want to scan for?");
    return;
  }

  if (flag & CHECK_NEIGHBORS) {
    notify(player, player, "Matches on contents of this room:");
    DOLIST(thing, DBFETCH(Location(player))->contents) {
      if (ScanFind(player, thing))
	notify(player, player,
	       tprintf("%s  [%d]", unparse_object(player, thing), num));
    }
  }

  if (flag & CHECK_HERE) {
    if (ScanFind(player, Location(player)))
      notify(player, player, tprintf("Matched here: %s  [%d]", 
			     unparse_object(player, Location(player)), num));
  }

  if (flag & CHECK_INVENTORY) {
    notify(player, player, "Matches on carried objects:");
    DOLIST(thing, DBFETCH(player)->contents) {
      if (ScanFind(player, thing))
	notify(player, player, tprintf("%s  [%d]", 
			       unparse_object(player, thing), num));
    }
  }

  if (flag & CHECK_SELF) {
    if (ScanFind(player, player))
      notify(player, player, tprintf("Matched self: %s  [%d]", 
			     unparse_object(player, player), num));
  }

  if (flag & CHECK_ZONE) {
/*
    if (Zone(Location(player)) != NOTHING) {
      if (Typeof(Zone(Location(player))) == TYPE_ROOM) {
	if (Location(player) != Zone(player)) {
	  notify(player, player, "Matches on parent room of location:");
	  DOLIST(thing, db[Zone(Location(player))].contents) {
	    if (ScanFind(player, thing))
	      notify(player, player, tprintf("%s  [%d]", 
				     unparse_object(player, thing), num));
	  }
	}
      } else {
	if (ScanFind(player, Zone(Location(player))))
	  notify(player,  player,
		 tprintf("Matched zone of location: %s  [%d]",
			 unparse_object(player, Zone(Location(player))), num));
      }
    }
    if ((Zone(player) != NOTHING) && 
	(Zone(player) != Zone(Location(player)))) {
      if (ScanFind(player, Zone(player)))
	notify(player, player, tprintf("Matched personal zone: %s  [%d]",
			       unparse_object(player, Zone(player)), num));
    }
*/
    notify(player, player, "Zones arn't supported on this MUD.");
  }

#ifdef DO_GLOBALS
  if ((flag & CHECK_GLOBAL) && (Location(player) != GLOBAL_ENVIRONMENT)) {
    /* try Master Room stuff */
    notify(player, player, "Matches on objects in the Master Room:");
    DOLIST(thing, DBFETCH(GLOBAL_ENVIRONMENT)->contents) {
      if (ScanFind(player, thing))
	notify(player, player, tprintf("%s  [%d]", 
			       unparse_object(player, thing), num));
    }
  }
#endif				/* DO_GLOBALS */
}

void do_cpattr(dbref player, char *oldpair, char *newpair[])
{
  /* the command is of the format:
   * @cpattr oldobj/oldattr = newobj1/newattr1, newobj2/newattr2, etc.
   */

  dbref oldobj;
  char tbuf1[BUFFER_LEN], tmp[BUFFER_LEN];
  char *p, *a, *text;
  match_data md;
  int i, j;

  /* must copy from something */
  if (!oldpair || !*oldpair) {
    notify(player, player, "What do you want to copy from?");
    return;
  }

  /* find the old object */
  strcpy(tbuf1, oldpair);
  p = (char *) index(tbuf1, '/');
  if (!p || !*p) {
    notify(player, player, 
           "What object do you want to copy the attribute from?");
    return;
  }
  *p++ = '\0';
  init_match(player, tbuf1, NOTYPE, &md);
  match_everything(&md);
  oldobj = noisy_match_result(&md);
  if (oldobj == NOTHING) return;

  strcpy(tmp, p);
  j=strlen(tmp);
  for(i=0; i < j; i++)
    if(tmp[i] == ' ') tmp[i] = '\0';

  /* find the old attribute */
  a = get_property_data(oldobj, tmp, access_rights(player, oldobj, NOTHING));
  if (!a) {
    notify(player, player, "No such attribute to copy from.");
    return;
  } 

  /* we can read it. Copy the value. */
  text = a;

  /* now we loop through our new object pairs and copy, calling @set. */
  i=1;
  while (i < MAX_MUSH_ARG && newpair[i]) {
    if (!*newpair[i]) {
      notify(player, player, "What do you want to copy to?");
    } else {
      strcpy(tbuf1, newpair[i]);
      p = (char *) index(tbuf1, '/');
      if (!p || !*p) {
	notify(player, player, 
                "What object do you want to copy the attribute to?");
      } else {
	*p++ = '\0';
	do_set(player, tbuf1, tprintf("%s:%s", p, text), tbuf1);
      }
    }
   i++;
  }
  notify(player, player, "Attributes copied.");
}

void do_leave(dbref player)
{
  if (Typeof(Location(player)) == TYPE_ROOM) {
    notify(player, player, "You can't leave");
    return;
  }
  enter_room(player, DBFETCH(DBFETCH(player)->location)->location, 
            DBFETCH(player)->location);
}

void do_mush_examine(dbref player, char *name, int brief)
{
  dbref thing, content, exit_dbref;
  char *tp;
  char *real_name = NULL, *attrib_name = NULL;
  char tbuf[BUFFER_LEN];
  match_data md;
  int ok = 0;

  if (*name == '\0') {
    if ((thing = Location(player)) == NOTHING)
      return;
    attrib_name = NULL;
  } else {

    if ((attrib_name = (char *) index(name,'/')) != NULL) {
      *attrib_name = '\0';
      attrib_name++;
    }
    real_name = (char *)name; 
    /* look it up */
    init_match(player, real_name, NOTYPE, &md);
    match_everything(&md);
    if(Wizard(player))
       match_absolute(&md);

    /* get result */
    if ((thing = noisy_match_result(&md)) == NOTHING)
      return;
  }

  /*  can't examine destructed objects  */
  if (Typeof(thing) == TYPE_GARBAGE) {
    notify(player, player, "Garbage is garbage.");
    return;
  }

  /*  only look at some of the attributes */
  if (attrib_name && *attrib_name) {
    examine_properties(player, thing, attrib_name, 0);
    return;
  }

  ok = Can_Examine(player, thing);

#ifdef EX_PUBLIC_ATTRIBS
  if (!ok && !nearby(player, thing)) {
#else
  if (!ok) {
#endif				/* EX_PUBLIC_ATTRIBS */
    /* if it's not examinable and we're not near it, we can only get the
     * name and the owner.
     */
    tp = tbuf;
    safe_str(unparse_object(player, thing), tbuf, &tp);
    safe_str((char *)" is owned by ", tbuf, &tp);
    safe_str(unparse_object(player, OWNER(thing)), tbuf, &tp);
    *tp = '\0';
    notify(player, player, tbuf);
    return;
  }

  if (ok) {
    notify(player, player, unparse_object(player, thing));
#ifdef FLAGS_ON_EXAMINE
    notify(player, player, flag_description(player, thing));
#endif
  }

#ifdef EX_PUBLIC_ATTRIBS
  if (!brief) {
    a = atr_get_noparent(thing, "DESCRIBE");
    if (a) {
      r = safe_uncompress(a->value);
      notify(player, player, r);
      free(r);
    }
  }
#endif

  if (ok) {
    notify(player, player,
	   tprintf("Owner: %s  Key: %s  %s: %d",
		   unparse_name(OWNER(thing)),
		   unparse_boolexp(player, DBFETCH(thing)->key),
		   PL_MONEY, DBFETCH(thing)->pennies));
    if (Typeof(thing) != TYPE_PLAYER)
      notify(player, player, tprintf("Parent: %s", 
			     unparse_object(player, Parent(thing))));
    notify(player, player, 
            tprintf("Zone: %s", unparse_object(player, Zone(thing))));

      notify(player, player, tprintf("Use Key: %s", 
	     unparse_boolexp(player, 
             get_ue_locks(player, thing, USELOCK))));

    if (Typeof(thing) == TYPE_ROOM)
      notify(player, player, tprintf("Teleport Key: %s", 
		unparse_boolexp(player, 
                get_ue_locks(player, thing, ENTERLOCK))));
    else
      notify(player, player, tprintf("Enter Key: %s", 
		unparse_boolexp(player, 
                get_ue_locks(player, thing, ENTERLOCK))));

/*    notify(player, player, powers_descriptions(thing));  */

#if (CHAT_SYSTEM >= 2)
    if (Typeof(thing) == TYPE_PLAYER)
      notify(player, player, channel_description(thing));
#endif				/* CHAT_SYSTEM */
  }

#ifdef EX_PUBLIC_ATTRIBS
  if (!brief) {
#else
  if (!brief && ok) {
#endif				/* EX_PUBLIC_ATTRIBS */
    examine_properties(player, thing, "", 0);
  }

  /* show contents */
  if (Contents(thing) != NOTHING) {
    notify(player, player, "Contents:");
    DOLIST(content, Contents(thing)) {
      if (ok || controls(player, content) || 
	  (!Dark(content) &&
	   !((Typeof(content) == TYPE_PLAYER) &&
	     (Connected(content)))))
	notify(player, player, unparse_object(player, content));
    }
  }

  if (!ok) {
    /* if not examinable, just show obvious exits and name and owner */
    if (Typeof(thing) == TYPE_ROOM)
      examine_exits(player, thing, "", 0);
    tp = tbuf;
    safe_str(unparse_object(player, thing), tbuf, &tp);
    safe_str((char *)" is owned by ", tbuf, &tp);
    safe_str(unparse_object(player, OWNER(thing)), tbuf, &tp);
    *tp = '\0';
    notify(player, player, tbuf);
    return;
  }

  switch (Typeof(thing)) {
  case TYPE_ROOM:
    /* tell him about exits */
    if (DBFETCH(thing)->exits != NOTHING) {
      notify(player, player, "Exits:");
      DOLIST(exit_dbref, DBFETCH(thing)->exits)
	notify(player, player, unparse_object(player, exit_dbref));
    } else
      notify(player, player, "No exits.");
    /* print dropto if present */
    if (Location(thing) != NOTHING) {
      notify(player, player,
	     tprintf("Dropped objects go to: %s",
		     unparse_object(player, Location(thing))));
    }
    break;
  case TYPE_THING:
  case TYPE_PLAYER:
    /* print home */
    notify(player, player,
	   tprintf("Home: %s",
		   unparse_object(player, DBFETCH(thing)->link)));
    /* print location if player can link to it */
    if (Location(thing) != NOTHING)
      notify(player, player,
	     tprintf("Location: %s",
		     unparse_object(player, Location(thing))));
    break;
  case TYPE_EXIT:
    /* print source */
    switch (DBFETCH(thing)->location) {
    case NOTHING:
      fprintf(stderr,
	      "*** BLEAH *** Weird exit %s(#%d) in #%d with source NOTHING.\n",
	      unparse_name(thing), thing, Location(thing));
      break;
    case HOME:
      fprintf(stderr,
	      "*** BLEAH *** Weird exit %s(#%d) in #%d with source HOME.\n",
	      unparse_name(thing), thing, Location(thing));
      break;
    default:
      notify(player, player,
	     tprintf("Source: %s",
		     unparse_object(player, DBFETCH(thing)->location)));
      break;
    }
    /* print destination */
    switch (Location(thing)) {
    case NOTHING:
      notify(player, player, "Destination: *UNLINKED*");
      break;
    case HOME:
      notify(player, player, "Destination: *HOME*");
      break;
    default:
      notify(player, player,
	     tprintf("Destination: %s",
		     unparse_object(player, Location(thing))));
      break;
    }
    break;
  default:     /* do nothing */
    break;
  }
}

void do_mush_lock(dbref player, char *name, char *keyname, int locktype)
{
  dbref thing = NOTHING;
  match_data md;
  boolexp *key;
  char *sp;

  /* check for '@lock <object>/<atr>'  */
  sp = (char *) index(name, '/');
  if (sp) {
    notify(player, player, "Please use the @permissions command instead.");
    return;
  }

  init_match(player, name, NOTYPE, &md);
  match_everything(&md);

  switch ((thing = match_result(&md))) {
    case NOTHING:
      notify(player, player, "I don't see what you want to lock!");
      return;
    case AMBIGUOUS:
      notify(player, player, "I don't know which one you want to lock!");
      return;
    default:
      if (!controls(player, thing)) {
	notify(player, player, "You can't lock that!");
	return;
      }
      if (Typeof(thing) == TYPE_GARBAGE) {
	notify(player, player, "Why would you want to lock garbage?");
	return;
      }
      break;
  }

  if(!keyname || !*keyname) {
     notify(player, player, "Empty key.");
     return;
   }

  key = parse_boolexp(player, keyname);

#ifdef TIMESTAMPS
    DBSTORE(thing, time_modified, time(NULL));
#endif

  /* do the lock */
  if (key == TRUE_BOOLEXP) {
    notify(player, player, "I don't understand that key.");
  } else {
    /* everything ok, do it */
    switch (locktype) {
    case BASICLOCK:
      remove_backlocks_parse(thing, DBFETCH(thing)->key);
      free_boolexp(DBFETCH(thing)->key);
      DBSTORE(thing, key, key);
      add_backlocks_parse(thing, DBFETCH(thing)->key);
      break;
    case USELOCK:
      do_use_enterlocks(thing, keyname, locktype, 1);
      break;
    case ENTERLOCK:
      do_use_enterlocks(thing, keyname, locktype, 1);
      break;
    }
    notify(player, player, "Locked.");
  }
}

void do_mush_unlock(dbref player, char *name, int locktype)
{
  dbref thing;
  char *sp;

  /* check for '@unlock <object>/<atr>'  */
  sp = (char *) index(name, '/');
  if (sp) {
    notify(player, player, "Please use the @permissions command instead.");
    return;
  }

  if ((thing = match_controlled(player, name)) != NOTHING) {
#ifdef TIMESTAMPS
    DBSTORE(thing, time_modified, time(NULL));
#endif
    switch (locktype) {
    case BASICLOCK:
      remove_backlocks_parse(thing, DBFETCH(thing)->key);
      free_boolexp(DBFETCH(thing)->key);
      DBSTORE(thing, key, TRUE_BOOLEXP);
      break;
    case USELOCK:
      do_use_enterlocks(thing, name, locktype, 0);
      break;
    case ENTERLOCK:
      do_use_enterlocks(thing, name, locktype, 0);
      break;
    }
    notify(player, player, "Unlocked.");
  }
}

void do_debug_examine(dbref player, char *name)
{
  dbref thing, tmp;
  match_data md;
  int i;

  if (!Wizard(player)) {
    notify(player, player, "Permission denied.");
    return;
  }

  /* find it */
  init_match(player, name, NOTYPE, &md);
  match_everything(&md);
  thing = noisy_match_result(&md);
  if (thing == NOTHING) return;

  if (Typeof(thing) == TYPE_GARBAGE) {
    notify(player, player, "Garbage is garbage.");
    return;
  }

  notify(player, player, unparse_object(player, thing));
  notify(player, player, tprintf("Flags value: %d", FLAGS(thing)));
/*  notify(player, player, tprintf("Toggles value: %d", Toggles(thing)));
  notify(player, player, tprintf("Powers value: %d", Powers(thing))); */
  if(DBFETCH(thing)->next != NOTHING)
     notify(player, player, tprintf("Next: %d", DBFETCH(thing)->next));
  if(DBFETCH(thing)->contents != NOTHING)
    DOLIST(tmp, DBFETCH(thing)->contents)
       notify(player, player, tprintf("Content: %d", tmp));

  switch (Typeof(thing)) {
  case TYPE_PLAYER:
    notify(player, player, tprintf("Location: %d", DBFETCH(thing)->location));
    notify(player, player, tprintf("Home: %d", DBFETCH(thing)->link));
  if(DBFETCH(thing)->exits != NOTHING)
    DOLIST(tmp, DBFETCH(thing)->exits)
          notify(player, player, tprintf("Exit: %d", tmp));
    break;
  case TYPE_THING:
    notify(player, player, tprintf("Location: %d", DBFETCH(thing)->location));
    notify(player, player, tprintf("Home: %d", DBFETCH(thing)->link));
  if(DBFETCH(thing)->exits != NOTHING)
    DOLIST(tmp, DBFETCH(thing)->exits)
          notify(player, player, tprintf("Exit: %d", tmp));
    break;
  case TYPE_EXIT:
    notify(player, player, tprintf("Source: %d", DBFETCH(thing)->location));
      if (DBFETCH(thing)->sp.exit.ndest > 0)
      {
        for (i = 0; i < DBFETCH(thing)->sp.exit.ndest; i++)
        {
          notify(player, player, tprintf("Destination: %s", 
          unparse_object_full(player, (DBFETCH(thing)->sp.exit.dest)[i])));
        }
      }
    break;
  case TYPE_ROOM:
    notify(player, player, tprintf("Drop-to: %d", DBFETCH(thing)->location));
  if(DBFETCH(thing)->exits != NOTHING)
    DOLIST(tmp, DBFETCH(thing)->exits)
           notify(player, player, tprintf("Exit: %d", tmp));
    break;
  default:
    notify(player, player, "Bad object type.");
  }
}

void do_config(dbref player, int type)
{
  if (type == 2) {
    notify(player, player, "Building costs:");
    notify(player, player, tprintf("  Object creation....%d", OBJECT_COST));
    notify(player, player, tprintf("  Room creation......%d", ROOM_COST));
    notify(player, player, tprintf("  Exit creation......%d", EXIT_COST));
    notify(player, player, tprintf("  Linking............%d", LINK_COST));
    notify(player, player, tprintf("  Queueing cost......%d", Q_COST));
    notify(player, player, tprintf("  Quota per object...%d", QUEUE_QUOTA));
    notify(player, player, "Command costs:");
    notify(player, player, tprintf("  page...............%d", LOOKUP_COST));
    notify(player, player, tprintf("  @find..............%d", LOOKUP_COST));
    notify(player, player, tprintf("  kill base cost.....%d", KILL_BASE_COST));
    notify(player, player, tprintf("  kill minimum cost..%d", KILL_MIN_COST));
    notify(player, player, tprintf("  kill insurance.....%d", KILL_BONUS));
    return;
  }

  if (type == 1) {
    notify(player, player, "Global parameters:");

    notify(player, player, tprintf("  Logins............%s",
			   (!wiz_only_flag) ? "enabled" : "disabled"));

    notify(player, player, tprintf("  Log commands......%s",
         (o_log_commands) ? "enabled" : "disabled"));

    notify(player, player, tprintf("  Log huhs..........%s",
           (o_log_huhs) ? "enabled" : "disabled"));

    notify(player, player, tprintf("  Log forces........%s", "enabled"));
    return;
  }

 notify(player, player, 
        "Players without a BUILDER bit cannot build anything.");
 
 if (o_lockouts)  
     notify(player, player, "Site lockout is enabled.");
 else
     notify(player, player, "Site lockout is not enabled.");

#ifdef TIMESTAMPS
  notify(player, player, "Timestamps are enabled.");
#else
  notify(player, player, "Timestamps are not enabled.");
#endif

#ifdef PLAYER_CHOWN
notify(player, player, "The @chown command and CHOWN_OK flag is enabled.");
#else
notify(player, player, "The @chown command and CHOWN_OK flag is not enabled.");
#endif

#ifdef COMPRESS
notify(player, player, "String compression is enabled.");
#else
notify(player, player, "String compression is not enabled.");
#endif

#ifdef GOD_PRIV
notify(player, player, "God privs are enabled.");
#else
notify(player, player, "God privs are not enabled.");
#endif

 if(o_mufconnects)
   notify(player, player, "MUF connects/disconnects are enabled.");
 else
   notify(player, player, "MUF connects/disconnects are not enabled.");

 if(o_taboonames)
   notify(player, player, "Player taboo names are enabled.");
 else
   notify(player, player, "Player taboo names are not enabled.");
 
 if(o_registration) {
   notify(player, player, "Selective player registration is in effect.");
 } else {
   notify(player, player, "Player registration is not in effect.");
  }
 
  notify(player, player, "Expanded flag list is shown on examines.");

#ifdef FULL_INVIS
  notify(player, player, "Dark players/objects show up as Someone/Something.");
#else
  notify(player, player, "Dark players/objects are not totally anonymous.");
#endif
 
  notify(player, player, "Nospoof notification shows name and object number.");
 
  notify(player, player, 
        "The location of players set UNFINDABLE can't be found.");

  notify(player, player, "Players cannot @listen.");
  notify(player, player, 
         tprintf("Player name limit is %d characters.", PLAYER_NAME_LIMIT));

#ifdef NOFORK
  notify(player, player, "Forking is disabled. Game will freeze during dumps.");
#else
#ifdef USE_VFORK
  notify(player, player, "Vfork is enabled. Game will freeze during dumps.");
#endif
  notify(player, player, "There should be no slowdown due to database saves.");
#endif
 
notify(player, player, tprintf("The database is being saved every %d seconds.",
        DUMP_INTERVAL));
 
if(o_rwho) {
  notify(player, player, tprintf("The RWHO server is %s",
        get_property_data((dbref) 0, RWHO_SERVER, ACCESS_WI)));
 } else {
  notify(player, player, "The MUCK is not connected to an RWHO server.");
 }

notify(player, player, tprintf("The starting location of players is #%d.",
		       PLAYER_START));
 
  notify(player, player, 
         tprintf("The master room is #%d.", GLOBAL_ENVIRONMENT));
 
  notify(player, player, "There is no internal guest character.");
 

  notify(player, player, tprintf("The maximum number of queued commands is %d.",
			 QUEUE_QUOTA));
  notify(player, player, tprintf("Queueing commands costs %d.", Q_COST));
  notify(player, player, tprintf("This server is running version %s", VERSION));
}

void do_gedit(dbref player, char *it, char *argv[])
{
  propdir *a;
  dbref thing;
  match_data md;
  char tbuf1[BUFFER_LEN];
  char *p, *q;
  int is_wild = 0;

  if(!(it && *it)) {
    notify(player, player, "I need to know what you want to edit.");
    return;
  }
  strcpy(tbuf1, it);
  q = (char *) index(tbuf1, '/');
  if (!(q && *q)) {
    notify(player, player, "I need to know what you want to edit.");
    return;
  }
  *q++ = '\0';
  init_match(player, tbuf1, NOTYPE, &md);
  match_everything(&md);
  thing = noisy_match_result(&md);

  if ((thing == NOTHING) || !controls(player, thing)) {
    notify(player, player, "Permission denied.");
    return;
  }
  if(*q == '_') q++;

  if (!argv[1] || !*argv[1]) {
    notify(player, player, "Nothing to do.");
    return;
  }

  /* first let's check for wildcard characters */
  for (p = q; *p && !is_wild; p++)
    if ((*p == '*') || (*p == '?'))
      is_wild = 1;
  
  if (!is_wild) {
    /* we only have one attribute. Check for possible abbreviation,
     * then edit that the attribute with that name.
     */
    /* I'm not going to impliment substring matching for properties! */
      do_medit(player, thing, q, argv);
  } else {
    /* we've got a wildcard, treat like old @gedit */
    for(a = DBFETCHPROP(thing); a; a = a->next)
      if(a && wild_match(q, a->name)) 
	do_medit(player, thing, a->name, argv);
  }
}

void do_medit(dbref player, dbref thing, char *q, char *argv[])
{
  int d, len, res;
  char *a;
  char *r, *s , *val;
  char tbuf1[BUFFER_LEN];

  val = argv[1];
  r = (argv[2]) ? argv[2] : (char *) "";

  a = get_property_data(thing, q, access_rights(player, thing, NOTHING));
  if(!a) {
    notify(player, player, "No such attribute, try set instead.");
    return;
  }
/*
  if ((a->flags & AF_LOCKED) && (OWNER(player) != OWNER(a->creator))) {
    notify(player, player, "You need to control an attribute to edit it.");
    return;
  }
*/

  s = a; /* warning: pointer to static buffer */
  if (!strcmp(val, "$")) {
    /* append */
    if (strlen(s) + strlen(r) < BUFFER_LEN) {
      strcpy(tbuf1, s);
      strcat(tbuf1, r);
    }
  } else if (!strcmp(val, "^")) {
    /* prepend */
    if (strlen(s) + strlen(r) < BUFFER_LEN) {
      strcpy(tbuf1, r);
      strcat(tbuf1, s);
    }
  } else {
    /* find and replace */
    len = strlen(val);
    for (d = 0; (d < BUFFER_LEN) && *s;)
      if (strncmp(val, s, len) == 0) {
	if ((d + strlen(r)) < BUFFER_LEN) {
	  strcpy(tbuf1 + d, r);
	  d += strlen(r);
	  s += len;
	} else
	  tbuf1[d++] = *s++;
      } else
	tbuf1[d++] = *s++;
    tbuf1[d++] = 0;
  }

  res = add_property(thing, q, tbuf1, default_perms(q), 
                     access_rights(player, thing, NOTHING));
  if(!res) {
    notify(player, player, "That attribute seems to have vanished!");
    return;
  }

/*
  if(res == -1) {
    notify(player, player, "You don't have the power to change that.");
    return;
  }
  if(!Quiet(player) && !Quiet(thing))
*/
    notify(player, player, tprintf("%s - Set: %s", q, tbuf1));
}
#endif /* MUSH */