/* look.c */

#include "copyright.h"

/* commands which look at things */
#include <string.h>

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

static void look_exits(player, loc, exit_name)
    dbref player;
    dbref loc;
    const char *exit_name;
{
  dbref thing;
  char tbuf1[BUFFER_LEN];
  char *e = tbuf1;

  /* make sure location is a room */
  if ((Typeof(loc) != TYPE_ROOM) || (db[loc].flags & DARK))
    return;
  for (thing = db[loc].exits;
       (thing != NOTHING) && (db[thing].flags & DARK);
       thing = db[thing].next) ;
  if (thing == NOTHING)
    return;
  notify(player, exit_name);
  for (thing = db[loc].exits; thing != NOTHING; thing = db[thing].next)
    if (!(db[thing].flags & DARK))
      /* chop off first exit alias to display */
    {
      char *s;
      if (db[thing].name && ((e - tbuf1) < BUFFER_LEN)) {
	for (s = (char *) db[thing].name; *s && (*s != ';') &&
	     (e - tbuf1 < BUFFER_LEN); *e++ = *s++) ;
	*e++ = ' ';
	*e++ = ' ';
      }
    }
  *e++ = 0;
  notify(player, tbuf1);
}


static void look_contents(player, loc, contents_name)
    dbref player;
    dbref loc;
    const char *contents_name;
{
  dbref thing;
  dbref can_see_loc;
  /* check to see if he can see the location */
  /*
   * patched so that player can't see in dark rooms even if owned by that
   * player.  (he must use examine command)
   */
  can_see_loc = !Dark(loc);

  /* check to see if there is anything there */
  DOLIST(thing, db[loc].contents) {
    if (can_see(player, thing, can_see_loc)) {
      /* something exists!  show him everything */
      notify(player, contents_name);
      DOLIST(thing, db[loc].contents) {
	if (can_see(player, thing, can_see_loc)) {
	  notify(player, unparse_object(player, thing));
	}
      }
      break;			/* we're done */
    }
  }
}

void look_atrs(player, thing)
    dbref player;
    dbref thing;
{
  ALIST *list = db[thing].list;
  while (list) {
    if (!AL_BAD(list) &&
	!(AL_FLAGS(list) & AF_DARK) &&
	string_compare(AL_NAME(list), "DESCRIBE") &&
        (controls(player, thing) ||
         Visual(thing) ||
#ifdef ROYALTY_FLAG
	 Royalty(player) ||
#endif
         (db[AL_CREATOR(list)].owner == db[player].owner))) {
      char *r = safe_uncompress(AL_STR(list));
      notify(player, tprintf("%s [#%d]: %s", AL_NAME(list),
			     db[AL_CREATOR(list)].owner, r));
      free(r);
    }
    list = AL_NEXT(list);
  }
}

static void look_simple(player, thing)
    dbref player;
    dbref thing;
{
  Access(thing);
  if (controls(player, thing) || (db[thing].flags & VISUAL))
    notify(player, unparse_object(player, thing));
  did_it(player, thing, "DESCRIBE", "You see nothing special.", "ODESCRIBE",
         NULL, "ADESCRIBE", NOTHING);
  if((Typeof(thing) == TYPE_EXIT) && (db[thing].flags & EXIT_TRANSPARENT)) {
    if (db[thing].location == HOME)
      look_room(player, db[player].exits, 1);
    else
      look_room(player, db[thing].location, 1);
  }
}

void look_room(player, loc, remote)
    dbref player;
    dbref loc;
    int remote;
{
  ATTR *s;

  if(loc == NOTHING)
    return;

  /* tell him the name, and the number if he can link to it */
  if(!remote)
    notify(player, unparse_object(player, loc));
  if (Typeof(loc) != TYPE_ROOM) {
    if (!(db[player].flags & TERSE)) {
      if ((s = atr_get(loc, "IDESCRIBE")) ||
	  (s = atr_get(loc, "DESCRIBE"))) {
	char *r = safe_uncompress(s->value);
	notify(player, r);
	free(r);
      }
    }
  }
  /* tell him the description */
  else {
    if(!remote) {
      if (!(db[player].flags & TERSE))
	did_it(player, loc, "DESCRIBE", NULL, "ODESCRIBE", NULL, "ADESCRIBE",
	       NOTHING);
      else
	did_it(player, loc, NULL, NULL, "ODESCRIBE", NULL, "ADESCRIBE",
	       NOTHING);
    } else
      did_it(player, loc, "DESCRIBE", NULL, NULL, NULL, NULL, NOTHING);
  }
  /* tell him the appropriate messages if he has the key */
  if (Typeof(loc) == TYPE_ROOM && !remote) {
    if (db[player].flags & TERSE) {
      if (could_doit(player, loc))
	did_it(player, loc, NULL, NULL, "OSUCCESS", NULL, "ASUCCESS",
	       NOTHING);
      else
	did_it(player, loc, NULL, NULL, "OFAILURE", NULL, "AFAILURE",
	       NOTHING);
    } else
      if (could_doit(player, loc))
	did_it(player, loc, "SUCCESS", NULL, "OSUCCESS", NULL, "ASUCCESS",
	       NOTHING);
      else
	did_it(player, loc, "FAILURE", NULL, "OFAILURE", NULL, "AFAILURE",
	       NOTHING);
  }
  /* tell him the contents */
  look_contents(player, loc, "Contents:");
  if(!remote)
    look_exits(player, loc, "Obvious exits:");
}

void do_look_around(player)
    dbref player;
{
  dbref loc;
  if ((loc = getloc(player)) == NOTHING)
    return;
  look_room(player, loc, 0);
}

void do_look_at(player, name)
    dbref player;
    const char *name;
{
  dbref thing;
  if (*name == '\0') {
    if ((thing = getloc(player)) != NOTHING) {
      look_room(player, thing, 0);
    }
  } else {
    if(((thing = is_possess(player,name)) == NOTHING) ||
       (thing == AMBIGUOUS) ||
       ((db[db[thing].location].flags & OPAQUE) &&
	!controls(player,db[thing].location))) {
      /* look at a thing here */
      init_match(player, name, NOTYPE);
      match_neighbor();
      match_possession();
      if (Hasprivs(player)) {
        match_absolute();
        match_player();
      }
      match_here();
      match_me();
      if ((thing = match_result()) == NOTHING)
        match_exit();

      if ((thing = noisy_match_result()) != NOTHING) {
        switch (Typeof(thing)) {
   	  case TYPE_ROOM:
	    look_room(player, thing, 0);
	    /* look_atrs(player, thing); */
	    break;
 	  case TYPE_THING:
	  case TYPE_PLAYER:
	    look_simple(player, thing);
	    /* look_atrs(player,thing); */
	    if (controls(player, thing) ||
	        (!(db[thing].flags & OPAQUE) || Hasprivs(player))) {
	      look_contents(player, thing, "Carrying:");
	    }
	    break;
	  default:
	    look_simple(player, thing);
	    /* look_atrs(player,thing); */
	    break;
        }
      }
    } else {
      look_simple(player,thing);
      /* look_atrs(player,thing); */
    }
  }
}

#ifdef FLAGS_ON_EXAMINE
static const char *flag_description(thing)
    dbref thing;
{
  static char buf[BUFFER_LEN];
  strcpy(buf, "Type: ");
  switch (Typeof(thing)) {
    case TYPE_ROOM:
      strcat(buf, "Room");
      break;
    case TYPE_EXIT:
      strcat(buf, "Exit");
      break;
    case TYPE_THING:
      strcat(buf, "Thing");
      break;
    case TYPE_PLAYER:
      strcat(buf, "Player");
      break;
    default:
      strcat(buf, "***UNKNOWN TYPE***");
      break;
  }

  if (db[thing].flags & ~TYPE_MASK) {
    /* print flags */
    strcat(buf, " Flags:");
    switch (Typeof(thing)) {
     case TYPE_EXIT:
       if (db[thing].flags & EXIT_KEY)
	  strcat(buf, " KEY");
       if (db[thing].flags & EXIT_TRANSPARENT)
	  strcat(buf, " TRANSPARENT");
       break;
      case TYPE_PLAYER:
	if (db[thing].flags & PLAYER_DARK)
	  strcat(buf, " UNFIND");
        if (db[thing].flags & PLAYER_SUSPECT)
	  strcat(buf, " SUSPECT");
       if (db[thing].flags & PLAYER_CONNECT)
	 strcat(buf, " CONNECTED");
#ifdef RESTRICTED_BUILDING
        if (db[thing].flags & PLAYER_BUILD)
          strcat(buf, " BUILDER");
#endif				/* RESTRICTED_BUILDING */
	break;
      case TYPE_THING:
	if (db[thing].flags & THING_PUPPET)
	  strcat(buf, " PUPPET");
	if (db[thing].flags & THING_KEY)
	  strcat(buf, " KEY");
       if (db[thing].flags & THING_SAFE)
	 strcat(buf, " SAFE");
       if (db[thing].flags & THING_VERBOSE)
	 strcat(buf, " VERBOSE");
       if (db[thing].flags & THING_IMMORTAL)
	 strcat(buf, " IMMORTAL");
#ifdef DESTROY
	if (db[thing].flags & THING_DEST_OK)
	  strcat(buf, " DESTROY_OK");
#endif DESTROY
	break;
      case TYPE_ROOM:
	if (db[thing].flags & LINK_OK)
	  strcat(buf, " LINK_OK");
	if (db[thing].flags & ROOM_TEMPLE)
	  strcat(buf, " TEMPLE");
	if (db[thing].flags & ROOM_ABODE)
	  strcat(buf, " ABODE");
	if (db[thing].flags & ROOM_JUMP_OK)
	  strcat(buf, " JUMP_OK");
	if (db[thing].flags & ROOM_FLOATING)
	  strcat(buf, " FLOAT");
        if (db[thing].flags & ROOM_NO_TEL)
	  strcat(buf, " NO_TEL");
	break;
    }
    if (db[thing].flags & WIZARD)
      strcat(buf, " WIZARD");
    if (db[thing].flags & DARK)
      strcat(buf, " DARK");
    if (db[thing].flags & STICKY)
      strcat(buf, " STICKY");
    if (db[thing].flags & HAVEN)
      strcat(buf, " HAVEN");
    if (db[thing].flags & HALT)
      strcat(buf, " HALT");
    if (db[thing].flags & QUIET)
      strcat(buf," QUIET");
    if (db[thing].flags & TERSE)
      strcat(buf, " TERSE");
#ifdef DESTROY
    if (db[thing].flags & GOING)
      strcat(buf, " GOING");
#endif	/* DESTROY */
    if (db[thing].flags & CHOWN_OK)
      strcat(buf, " CHOWN_OK");
    if (db[thing].flags & ENTER_OK)
      strcat(buf, " ENTER_OK");
    if (db[thing].flags & VISUAL)
      strcat(buf, " VISUAL");
    if (db[thing].flags & OPAQUE)
      strcat(buf, " OPAQUE");
    if (db[thing].flags & NOSPOOF)
      strcat(buf, " NOSPOOF");
#ifdef INHERIT_FLAG
    if (db[thing].flags & INHERIT)
      strcat(buf, " INHERIT");
#endif
#ifdef ROYALTY_FLAG
    if (db[thing].flags & ROYALTY)
      strcat(buf, " ROYALTY");
#endif
  }
  return buf;
}
#endif				/* FLAGS_ON_EXAMINE */

void do_examine(player, name, brief)
    dbref player;
    const char *name;
    int brief;
{
  dbref thing;
  ATTR *a;
  dbref content;
  dbref exit;
  char *real_name = NULL, *attrib_name = NULL;

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

    if((attrib_name = index(name,'/')) != NULL) {
      *attrib_name = '\0';
      attrib_name++;
    }
    real_name = (char *)name; 
    /* look it up */
    init_match(player, real_name, NOTYPE);
    match_exit();
    match_neighbor();
    match_possession();
    match_absolute();
    /* only Wizards and Immortals can examine other players */
    if (Hasprivs(player))
      match_player();
    match_here();
    match_me();
/*  changed to be consistent with other matches  */
/*    if ((thing = match_result()) == NOTHING)   */
/*      match_exit();                            */

    /* get result */
    if ((thing = noisy_match_result()) == NOTHING)
      return;
  }
  /*  only look at some of the attributes */
  if(attrib_name && *attrib_name) {
    ALIST *ptr;

    notify(player, tprintf("The following attributes on %s match %s:",
			   db[thing].name, attrib_name));
    for(ptr = db[thing].list; ptr; ptr = AL_NEXT(ptr)) {
      if(!AL_BAD(ptr)) {
	if(AL_FLAGS(ptr) & AF_DARK) continue;
	if(wild_match(attrib_name, AL_NAME(ptr)) &&
	   (controls(player, thing) ||
            Visual(thing) ||
#ifdef ROYALTY_FLAG
	    Royalty(player) ||
#endif
            (db[AL_CREATOR(ptr)].owner == db[player].owner))) {
	  char *r = safe_uncompress(AL_STR(ptr));
	  notify(player, tprintf("%s [#%d]: %s", AL_NAME(ptr),
				 db[AL_CREATOR(ptr)].owner, r));
	  free(r);
	}
      }
    }
    notify(player, "Done.");
    return;
  }

  /*  can't examine destructed objects  */
  if (db[thing].flags & GOING) {
    notify(player, "Garbage is garbage.");
    return;
  }

  /*  "short" examine for mortals  */
  if (!(db[thing].flags & VISUAL)
#ifdef ROYALTY_FLAG
      && !Royalty(player)
#endif
     ) {
    if (!can_link(player, thing)) {
      static char th[BUFFER_LEN], own[BUFFER_LEN];
      strcpy(th, unparse_object(player, thing));
      strcpy(own, unparse_object(player, db[thing].owner));

      notify(player, tprintf("%s is owned by %s", th, own));
      look_atrs(player, thing);

      /* check to see if there is anything there */
      /* as long as he owns what is there */
      DOLIST(content, db[thing].contents) {
	if (can_link(player, content)) {
	  notify(player, "Contents:");
	  DOLIST(content, db[thing].contents) {
	    if (can_link(player, content)) {
	      notify(player, unparse_object(player, content));
	    }
	  }
	  break;		/* we're done */
	}
      }
      return;
    }
  }

  /*  Full examine  */
  notify(player, unparse_object(player, thing));
#ifdef FLAGS_ON_EXAMINE
  notify(player, flag_description(thing));
#endif

  if (!brief) {
    a = atr_get(thing, "DESCRIBE");
    if (a) {
      char *r = safe_uncompress(a->value);
      notify(player, r);
      free(r);
    }
  }

  notify(player,
	 tprintf("Owner: %s  Key: %s  %s: %d",
		 db[db[thing].owner].name,
		 unparse_boolexp(player, db[thing].key, 0),
		 MONIES,
		 Pennies(thing)));
  notify(player, tprintf("Zone: %s", unparse_object(player, db[thing].zone)));
  if (Typeof(thing) == TYPE_PLAYER)
    notify(player, tprintf("Page Key: %s", unparse_boolexp(player,
       db[thing].usekey, 0)));
  else
    notify(player, tprintf("Use Key: %s", unparse_boolexp(player,
       db[thing].usekey, 0)));
  notify(player, tprintf("Enter Key: %s", unparse_boolexp(player,
     db[thing].enterkey, 0)));

  if (!brief)
    look_atrs(player, thing);

  /* show him the contents */
  if (db[thing].contents != NOTHING) {
    notify(player, "Contents:");
    DOLIST(content, db[thing].contents) {
      notify(player, unparse_object(player, content));
    }
  }
  switch (Typeof(thing)) {
    case TYPE_ROOM:
      /* tell him about exits */
      if (db[thing].exits != NOTHING) {
	notify(player, "Exits:");
	DOLIST(exit, db[thing].exits) {
	  notify(player, unparse_object(player, exit));
	}
      } else {
	notify(player, "No exits.");
      }

      /* print dropto if present */
      if (db[thing].location != NOTHING) {
	notify(player,
	       tprintf("Dropped objects go to: %s",
		       unparse_object(player, db[thing].location)));
      }
      break;
    case TYPE_THING:
    case TYPE_PLAYER:
      /* print home */
      notify(player,
	     tprintf("Home: %s",
		     unparse_object(player, db[thing].exits)));	/* home */
      /* print location if player can link to it */
      if (db[thing].location != NOTHING
	  && (Royalty(player) || controls(player, db[thing].location) 
	      || controls(player, thing)
	      || can_link_to(player, db[thing].location))) {
	notify(player,
	       tprintf("Location: %s",
		       unparse_object(player, db[thing].location)));
      }
      break;
    case TYPE_EXIT:
      /* print source */
      switch (db[thing].exits) {
      case NOTHING:
	fprintf(stderr,
	   "*** BLEAH *** Weird exit %s(#%d) in #%d with source NOTHING.\n",
	   db[thing].name, thing, db[thing].location);
	break;
      case HOME:
	fprintf(stderr,
           "*** BLEAH *** Weird exit %s(#%d) in #%d with source HOME.\n",
	   db[thing].name, thing, db[thing].location);
	break;
      default:
	notify(player,
	       tprintf("Source: %s",
		       unparse_object(player, db[thing].exits)));
	break;
      }
      /* print destination */
      switch (db[thing].location) {
	case NOTHING:
	  notify(player, "Destination: *UNLINKED*");
	  break;
	case HOME:
	  notify(player, "Destination: *HOME*");
	  break;
	default:
	  notify(player,
		 tprintf("Destination: %s",
			 unparse_object(player, db[thing].location)));
	  break;
      }
      break;
    default:
      /* do nothing */
      break;
  }
}

void do_score(player)
    dbref player;
{

  notify(player,
	 tprintf("You have %d %s.",
		 Pennies(player),
		 Pennies(player) == 1 ? MONEY : MONIES));
}

void do_inventory(player)
    dbref player;
{
  dbref thing;
  if ((thing = db[player].contents) == NOTHING) {
    notify(player, "You aren't carrying anything.");
  } else {
    notify(player, "You are carrying:");
    DOLIST(thing, thing) {
      notify(player, unparse_object(player, thing));
    }
  }

  do_score(player);
}

void do_find(player, name)
    dbref player;
    const char *name;
{
  dbref i;
  if (!payfor(player, FIND_COST)) {
    notify(player, tprintf("Finds cost %d %s.", FIND_COST,
			   ((FIND_COST == 1) ? MONEY : MONIES)));
  } else {
    for (i = 0; i < db_top; i++) {
      if (Typeof(i) != TYPE_EXIT
	  && controls(player, i)
	  && (!*name || string_match(db[i].name, name))) {
	notify(player, unparse_object(player, i));
      }
    }
    notify(player, "***End of List***");
  }
}

/* check the current location for bugs */
void do_sweep(player, arg1)
    dbref player;
    const char *arg1;
{
  dbref here = db[player].location;
  int connect_flag = 0;

  if (here == NOTHING)
    return;

  if(arg1 && *arg1)
    connect_flag = !(string_compare(arg1, "connected"));

  notify(player, "Listening in ROOM:");

  if(connect_flag) {
  /* only worry about puppet and players who's owner's are connected */
    if((Typeof(here) == TYPE_PLAYER && (db[here].flags & PLAYER_CONNECT)) ||
       ((Typeof(here) == TYPE_THING && (db[here].flags & THING_PUPPET)) &&
        (Typeof(db[here].owner) == TYPE_PLAYER &&
         (db[db[here].owner].flags) & PLAYER_CONNECT))) {
      if(Typeof(here) == TYPE_PLAYER) {
	notify(player, tprintf("%s is listening", db[here].name));
      } else {
	notify(player , tprintf("%s [owner: %s] is listening.", db[here].name,
				db[db[here].owner].name));
      }
    }
  } else {
    if (Hearer(here) || Listener(here)) {
      if(Typeof(here) == TYPE_PLAYER && (db[here].flags & PLAYER_CONNECT))
        notify(player, tprintf("%s (this room) [speech]. (connected)",
               db[here].name));
      else
        notify(player, tprintf("%s (this room) [speech].", db[here].name));
    }
    if (Commer(here))
      notify(player, tprintf("%s (this room) [commands].", db[here].name));
  }

  for (here = db[here].contents; here != NOTHING; here = db[here].next) {
    if(connect_flag) {
    /* only worry about puppet and players who's owner's are connected */
      if((Typeof(here) == TYPE_PLAYER && (db[here].flags & PLAYER_CONNECT)) ||
         ((Typeof(here) == TYPE_THING && (db[here].flags & THING_PUPPET)) &&
          (Typeof(db[here].owner) == TYPE_PLAYER &&
           (db[db[here].owner].flags) & PLAYER_CONNECT))) {
        if(Typeof(here) == TYPE_PLAYER) {
          notify(player, tprintf("%s is listening", db[here].name));
        } else {
          notify(player , tprintf("%s [owner: %s] is listening.", db[here].name,
                                  db[db[here].owner].name));
        }
      }
    } else {
      if (Hearer(here) || Listener(here)) {
        if(Typeof(here) == TYPE_PLAYER && (db[here].flags & PLAYER_CONNECT))
          notify(player, tprintf("%s [speech]. (connected)", db[here].name));
        else
          notify(player, tprintf("%s [speech].", db[here].name));
      } 
      if(Commer(here))
        notify(player, tprintf("%s [commands].", db[here].name));
    }
  }
  notify(player, "Listening in your INVENTORY:");

  for (here = db[player].contents; here != NOTHING; here = db[here].next) {
    if(connect_flag) {
    /* only worry about puppet and players who's owner's are connected */
      if((Typeof(here) == TYPE_PLAYER && (db[here].flags & PLAYER_CONNECT)) ||
         ((Typeof(here) == TYPE_THING && (db[here].flags & THING_PUPPET)) &&
          (Typeof(db[here].owner) == TYPE_PLAYER &&
           (db[db[here].owner].flags) & PLAYER_CONNECT))) {
        if(Typeof(here) == TYPE_PLAYER) {
          notify(player, tprintf("%s is listening", db[here].name));
        } else {
          notify(player , tprintf("%s [owner: %s] is listening.", db[here].name,
                                  db[db[here].owner].name));
        }
      }
    } else {
      if (Hearer(here) || Listener(here)) {
        if(Typeof(here) == TYPE_PLAYER && (db[here].flags & PLAYER_CONNECT))
          notify(player, tprintf("%s [speech]. (connected)", db[here].name));
        else
          notify(player, tprintf("%s [speech].", db[here].name));
      } 
      if(Commer(here))
        notify(player, tprintf("%s [commands].", db[here].name));
    }
  }
}

#ifdef PLAYER_LOCATE
void do_whereis(player, name)
    dbref player;
    const char *name;
{
  dbref thing;
  if (*name == '\0') {
    notify(player, "You must specify a valid player name.");
    return;
  }
  if ((thing = lookup_player(name)) == NOTHING) {
    notify(player, "That player does not seem to exist.");
    return;
  }
  /*
   * init_match(player, name, TYPE_PLAYER); match_player(); match_exit();
   * match_neighbor(); match_possession(); match_absolute();      match_me();
   */

  if (!Hasprivs(player) &&
      (Typeof(thing) == TYPE_PLAYER) &&
      (db[thing].flags & PLAYER_DARK)) {
    notify(player, "That player wishes to have some privacy.");
    notify(thing, tprintf("%s tried to locate you and failed.",
	   db[player].name));
    return;
  }
  notify(player,
	 tprintf("%s is at: %s.", db[thing].name,
		 unparse_object(player, db[thing].location)));
  notify(thing,
	 tprintf("%s has just located your position.",
		 db[player].name));
  return;

}
#endif /* PLAYER_LOCATE */

void do_entrances(player, where)
    dbref player;
    const char *where;
{
  dbref place;
  dbref counter;

  if(!payfor(player, FIND_COST)) {
    notify(player, tprintf("You don't have enough %d %s to do that.",
			   FIND_COST,
			   ((FIND_COST == 1) ? MONEY : MONIES)));
    return;
  }

  if(!where || !*where) {
    if((place = db[player].location) == NOTHING)
      return;
  } else {
    init_match(player, where, NOTYPE);
    match_everything();
    if((place =  noisy_match_result()) == NOTHING)
      return;
  }
  
  for(counter = 0; counter < db_top; counter++) {
    if (controls(player, place) || controls(player, counter)) {
      switch(Typeof(counter)) {
	case TYPE_EXIT:
	  if(db[counter].location == place) {
	    notify(player, tprintf("%s [from: %s]", db[counter].name,
		                   db[db[counter].exits].name));
	  }
	  break;
	case TYPE_ROOM:
	  if(db[counter].location == place) {
	    notify(player, tprintf("%s [dropto]", db[counter].name));
          }
          break;
	case TYPE_THING:
	case TYPE_PLAYER:
	  if(db[counter].exits == place) {
	    notify(player, tprintf("%s [home]", db[counter].name));
	  }
	  break;
      }
    }
  }
}

void decompile_atrs (player, thing, name)
  dbref player;
  dbref thing;
  const char *name;
{
  int a = 0;
  int reg = 0;
  ALIST *list = db[thing].list;
  while (list) {
    if (!AL_BAD(list) && !(AL_FLAGS(list) & AF_DARK)) {
      char *r = safe_uncompress(AL_STR(list));
      a = 0;
      while (!reg && attr[a].name) {
	if (string_prefix(attr[a].name, AL_NAME(list)))
	  reg = 1;   /* matched a regular pre-defined attribute */
	a++;
	}
      if (reg)
	notify(player, tprintf("@%s %s = %s", AL_NAME(list), name, r));
      else
	notify(player, tprintf("&%s %s = %s", AL_NAME(list), name, r));
      free(r);
    }
    reg = 0;
    list = AL_NEXT(list);
  }
}

void decompile_flags (player, thing, name)
  dbref player;
  dbref thing;
  const char *name;
{
  if (db[thing].flags & THING_PUPPET)
    notify(player, tprintf("@set %s=PUPPET", name));
  if (db[thing].flags & THING_KEY)
    notify(player, tprintf("@set %s=KEY", name));
  if (db[thing].flags & THING_DEST_OK)
    notify(player, tprintf("@set %s=DESTROY_OK", name));
  if (db[thing].flags & THING_SAFE)
    notify(player, tprintf("@set %s=SAFE", name));
  if (db[thing].flags & THING_VERBOSE)
    notify(player, tprintf("@set %s=VERBOSE", name));
  if (db[thing].flags & THING_IMMORTAL)
    notify(player, tprintf("@set %s=IMMORTAL", name));
  if (db[thing].flags & LINK_OK)
    notify(player, tprintf("@set %s=LINK_OK", name));
  if (db[thing].flags & WIZARD)
    notify(player, tprintf("@set %s=WIZARD", name));
  if (db[thing].flags & DARK)
    notify(player, tprintf("@set %s=DARK", name));
  if (db[thing].flags & STICKY)
    notify(player, tprintf("@set %s=STICKY", name));
  if (db[thing].flags & HAVEN)
    notify(player, tprintf("@set %s=HAVEN", name));
  if (db[thing].flags & HALT)
    notify(player, tprintf("@set %s=HALT", name));
  if (db[thing].flags & QUIET)
    notify(player, tprintf("@set %s=QUIET", name));
  if (db[thing].flags & CHOWN_OK)
    notify(player, tprintf("@set %s=CHOWN_OK", name));
  if (db[thing].flags & ENTER_OK)
    notify(player, tprintf("@set %s=ENTER_OK", name));
  if (db[thing].flags & VISUAL)
    notify(player, tprintf("@set %s=VISUAL", name));
  if (db[thing].flags & OPAQUE)
    notify(player, tprintf("@set %s=OPAQUE", name));
  if (db[thing].flags & NOSPOOF)
    notify(player, tprintf("@set %s=NOSPOOF", name));
  if (db[thing].flags & TERSE)
    notify(player, tprintf("@set %s=TERSE", name));
#ifdef INHERIT_FLAG
  if (db[thing].flags & INHERIT)
    notify(player, tprintf("@set %s=INHERIT", name));
#endif
#ifdef ROYALTY_FLAG
  if (db[thing].flags & ROYALTY)
    notify(player, tprintf("@set %s=ROYALTY", name));
#endif
}

void do_decompile(player, name)
  dbref player;
  const char *name;
{
  dbref thing;
  const char *object;
  char tbuf[BUFFER_LEN];

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

  /* find object */
  init_match(player, name, TYPE_THING);
  match_neighbor();
  match_possession();
  match_absolute();
  /* Don't need to do match_me, here, player, exit, because @decompile */
  /* is only valid for things */
  if ((thing = noisy_match_result()) == NOTHING)
      return;

  /* check to make sure the decompile is valid */
  if (Typeof(thing) != TYPE_THING) {
    notify(player, "Only objects may be @decompiled.");
    return;
  }
  if (!controls(player, thing) && !(db[thing].flags & VISUAL)
#ifdef ROYALTY_FLAG
      && !Royalty(player)
#endif
      ) {
    notify(player, "Permission denied.");
    return;
  }
  if (db[thing].flags & GOING) {
    notify(player, "Garbage is garbage.");
    return;
  }

  /* do actual decompile */
  object = db[thing].name;
  notify(player, tprintf("@create %s", object));
  notify(player, tprintf("@link %s = #%d", object, db[thing].exits));

  sprintf(tbuf, "%s", unparse_boolexp(player, db[thing].key, 1));
  if (string_compare(tbuf, "*UNLOCKED*"))
    notify(player, tprintf("@lock %s = %s", object, tbuf));
  sprintf(tbuf, "%s", unparse_boolexp(player, db[thing].enterkey, 1));
  if (string_compare(tbuf, "*UNLOCKED*"))
    notify(player, tprintf("@elock %s = %s", object, tbuf));

  decompile_flags(player, thing, object);
  decompile_atrs(player, thing, object);
}