/* look.c */
#include "os.h"
#include "copyright.h"

/* commands which look at things */
#include "config.h"
#include "db.h"
#include "interface.h"
#include "match.h"
#include "externs.h"

static void look_exits (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 (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 */
    }
  }
}

static void look_atrs (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 (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 (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 (dbref player)
{
  dbref loc;
  if ((loc = getloc (player)) == NOTHING)
    return;
  look_room (player, loc, 0);
}

void do_look_at (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, (char*)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 (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 (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 (dbref player)
{

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

void do_inventory (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 (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 (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 (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 (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;
      }
    }
  }
}

static void decompile_atrs (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);
  }
}

static void decompile_flags (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 (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);
}