pennmush/game/data/
pennmush/game/log/
pennmush/game/save/
pennmush/game/txt/evt/
pennmush/game/txt/nws/
pennmush/os2/
pennmush/po/
pennmush/win32/msvc.net/
pennmush/win32/msvc6/
/**
 * \file fundb.c
 *
 * \brief Dbref-related functions for mushcode.
 *
 *
 */

#include "copyrite.h"

#include "config.h"
#include <string.h>
#include "conf.h"
#include "externs.h"
#include "dbdefs.h"
#include "flags.h"

#include "match.h"
#include "parse.h"
#include "command.h"
#include "game.h"
#include "mushdb.h"
#include "privtab.h"
#include "lock.h"
#include "log.h"
#include "attrib.h"
#include "function.h"
#include "confmagic.h"

#ifdef WIN32
#pragma warning( disable : 4761)	/* NJG: disable warning re conversion */
#endif

extern PRIV attr_privs[];
static lock_type get_locktype(char *str);
extern struct db_stat_info *get_stats(dbref owner);
static int lattr_helper(dbref player, dbref thing, dbref parent,
			char const *pattern, ATTR *atr, void *args);
static dbref
 dbwalk(char *buff, char **bp, dbref executor, dbref enactor,
	int type, dbref loc, dbref after, int skipdark,
	int start, int count, int *retcount);


const char *
do_get_attrib(dbref executor, dbref thing, const char *attrib)
{
  ATTR *a;
  char const *value;

  a = atr_get(thing, strupper(attrib));
  if (a) {
    if (Can_Read_Attr(executor, thing, a)) {
      if (strlen(value = atr_value(a)) < BUFFER_LEN)
	return value;
      else
	return T("#-1 ATTRIBUTE LENGTH TOO LONG");
    }
    return T(e_atrperm);
  }
  a = atr_match(attrib);
  if (a) {
    if (Can_Read_Attr(executor, thing, a))
      return "";
    return T(e_atrperm);
  }
  if (!Can_Examine(executor, thing))
    return T(e_atrperm);
  return "";
}

/** Structure containing arguments for lattr_helper */
struct lh_args {
  int first;		/**< Is this is the first attribute, or later? */
  int nattr;
  int start;		/**< Where do we start counting? */
  int count;		/**< How many do we count? */
  char *buff;		/**< Buffer to store output */
  char **bp;		/**< Pointer to address of insertion point in buff */
};

/* this function produces a space-separated list of attributes that are
 * on an object.
 */
/* ARGSUSED */
static int
lattr_helper(dbref player __attribute__ ((__unused__)),
	     dbref thing __attribute__ ((__unused__)),
	     dbref parent __attribute__ ((__unused__)),
	     char const *pattern __attribute__ ((__unused__)),
	     ATTR *atr, void *args)
{
  struct lh_args *lh = args;
  lh->nattr++;
  if (lh->count < 1
      || (lh->nattr >= lh->start && lh->nattr < (lh->count + lh->start))) {
    if (lh->first)
      lh->first = 0;
    else
      safe_chr(' ', lh->buff, lh->bp);
    safe_str(AL_NAME(atr), lh->buff, lh->bp);
    return 1;
  }
  return 1;
}

/* ARGSUSED */
FUNCTION(fun_nattr)
{
  dbref thing;
  int doparent;
  static const char *matchall = "**";	/* count atrees by default too */
  char *pattern;

  pattern = strchr(args[0], '/');
  if (pattern)
    *pattern++ = '\0';
  else
    pattern = (char *) matchall;	/* match anything */

  thing = match_thing(executor, args[0]);
  if (!GoodObject(thing)) {
    safe_str(T(e_notvis), buff, bp);
    return;
  }

  doparent = strchr(called_as, 'P') ? 1 : 0;

  if (!doparent && pattern == matchall && Can_Examine(executor, thing)) {
    safe_integer(AttrCount(thing), buff, bp);
  } else {
    safe_integer(atr_pattern_count(executor, thing, pattern, doparent,
				   !Can_Examine(executor, thing)), buff, bp);
  }
}

/* ARGSUSED */
FUNCTION(fun_lattr)
{
  dbref thing;
  char *pattern;
  struct lh_args lh;

  lh.nattr = lh.start = lh.count = 0;

  if (*called_as == 'X') {
    if (!is_strict_integer(args[1]) || !is_strict_integer(args[2])) {
      safe_str(T(e_int), buff, bp);
      return;
    }

    lh.start = parse_integer(args[1]);
    lh.count = parse_integer(args[2]);

    if (lh.start < 1 || lh.count < 1) {
      safe_str(T("#-1 ARGUMENT OUT OF RANGE"), buff, bp);
      return;
    }
  }

  pattern = strchr(args[0], '/');
  if (pattern)
    *pattern++ = '\0';
  else
    pattern = (char *) "*";	/* match anything */

  thing = match_thing(executor, args[0]);
  if (!GoodObject(thing)) {
    safe_str(T(e_notvis), buff, bp);
    return;
  }
  lh.first = 1;
  lh.buff = buff;
  lh.bp = bp;
  if (strchr(called_as, 'P')) {
    (void) atr_iter_get_parent(executor, thing, pattern,
			       !Can_Examine(executor, thing), lattr_helper,
			       &lh);
  } else {
    (void) atr_iter_get(executor, thing, pattern,
			!Can_Examine(executor, thing), lattr_helper, &lh);
  }
}

/* ARGSUSED */
FUNCTION(fun_hasattr)
{
  dbref thing;
  ATTR *a;

  thing = match_thing(executor, args[0]);
  if (!GoodObject(thing)) {
    safe_str(T(e_notvis), buff, bp);
    return;
  }
  if (strchr(called_as, 'P'))
    a = atr_get(thing, upcasestr(args[1]));
  else
    a = atr_get_noparent(thing, upcasestr(args[1]));
  if (a && Can_Read_Attr(executor, thing, a)) {
    if (strchr(called_as, 'V'))
      safe_chr(*AL_STR(a) ? '1' : '0', buff, bp);
    else
      safe_chr('1', buff, bp);
    return;
  } else if (a || !Can_Examine(executor, thing)) {
    safe_str(T(e_perm), buff, bp);
    return;
  }
  safe_chr('0', buff, bp);
}

/* ARGSUSED */
FUNCTION(fun_get)
{
  dbref thing;
  char *s;

  s = strchr(args[0], '/');
  if (!s) {
    safe_str(T("#-1 BAD ARGUMENT FORMAT TO GET"), buff, bp);
    return;
  }
  *s++ = '\0';
  thing = match_thing(executor, args[0]);
  if (!GoodObject(thing)) {
    safe_str(T(e_notvis), buff, bp);
    return;
  }
  safe_str(do_get_attrib(executor, thing, s), buff, bp);
}

/* Functions like get, but uses the standard way of passing arguments */
/* to a function, and thus doesn't choke on nested functions within.  */

/* ARGSUSED */
FUNCTION(fun_xget)
{
  dbref thing;

  thing = match_thing(executor, args[0]);
  if (!GoodObject(thing)) {
    safe_str(T(e_notvis), buff, bp);
    return;
  }
  safe_str(do_get_attrib(executor, thing, args[1]), buff, bp);
}

/* Functions like get, but includes a default response if the
 * attribute isn't present or is null
 */
/* ARGSUSED */
FUNCTION(fun_default)
{
  dbref thing;
  ATTR *attrib;
  char *dp;
  const char *sp;
  char mstr[BUFFER_LEN];
  /* find our object and attribute */
  dp = mstr;
  sp = args[0];
  process_expression(mstr, &dp, &sp, executor, caller, enactor,
		     PE_DEFAULT, PT_DEFAULT, pe_info);
  *dp = '\0';
  parse_attrib(executor, mstr, &thing, &attrib);
  if (GoodObject(thing) && attrib && Can_Read_Attr(executor, thing, attrib)) {
    /* Ok, we've got it */
    dp = safe_atr_value(attrib);
    safe_str(dp, buff, bp);
    free(dp);
    return;
  }
  /* We couldn't get it. Evaluate args[1] and return it */
  sp = args[1];
  process_expression(buff, bp, &sp, executor, caller, enactor,
		     PE_DEFAULT, PT_DEFAULT, pe_info);
  return;
}

/* ARGSUSED */
FUNCTION(fun_eval)
{
  /* like xget, except pronoun substitution is done */

  dbref thing;
  char *tbuf;
  char const *tp;
  ATTR *a;

  thing = match_thing(executor, args[0]);
  if (!GoodObject(thing)) {
    safe_str(T(e_notvis), buff, bp);
    return;
  }
  a = atr_get(thing, upcasestr(args[1]));
  if (a && Can_Read_Attr(executor, thing, a)) {
    if (!CanEvalAttr(executor, thing, a)) {
      safe_str(T(e_perm), buff, bp);
      return;
    }
    tp = tbuf = safe_atr_value(a);
    add_check("fun_eval.attr_value");
    process_expression(buff, bp, &tp, thing, executor, executor,
		       PE_DEFAULT, PT_DEFAULT, pe_info);
    mush_free((Malloc_t) tbuf, "fun_eval.attr_value");
    return;
  } else if (a || !Can_Examine(executor, thing)) {
    safe_str(T(e_atrperm), buff, bp);
    return;
  }
  return;
}

/* ARGSUSED */
FUNCTION(fun_get_eval)
{
  /* like eval, except uses obj/attr syntax. 2.x compatibility */

  dbref thing;
  char *tbuf, *s;
  char const *tp;
  ATTR *a;

  s = strchr(args[0], '/');
  if (!s) {
    safe_str(T("#-1 BAD ARGUMENT FORMAT TO GET_EVAL"), buff, bp);
    return;
  }
  *s++ = '\0';
  thing = match_thing(executor, args[0]);
  if (!GoodObject(thing)) {
    safe_str(T(e_notvis), buff, bp);
    return;
  }
  a = atr_get(thing, upcasestr(s));
  if (a && Can_Read_Attr(executor, thing, a)) {
    if (!CanEvalAttr(executor, thing, a)) {
      safe_str(T(e_perm), buff, bp);
      return;
    }
    tp = tbuf = safe_atr_value(a);
    add_check("fun_eval.attr_value");
    process_expression(buff, bp, &tp, thing, executor, executor,
		       PE_DEFAULT, PT_DEFAULT, pe_info);
    mush_free((Malloc_t) tbuf, "fun_eval.attr_value");
    return;
  } else if (a || !Can_Examine(executor, thing)) {
    safe_str(T(e_atrperm), buff, bp);
    return;
  }
  return;
}

/* Functions like eval, but includes a default response if the
 * attribute isn't present or is null
 */
/* ARGSUSED */
FUNCTION(fun_edefault)
{
  dbref thing;
  ATTR *attrib;
  char *dp, *sbuf;
  char const *sp;
  char mstr[BUFFER_LEN];
  /* find our object and attribute */
  dp = mstr;
  sp = args[0];
  process_expression(mstr, &dp, &sp, executor, caller, enactor,
		     PE_DEFAULT, PT_DEFAULT, pe_info);
  *dp = '\0';
  parse_attrib(executor, mstr, &thing, &attrib);
  if (GoodObject(thing) && attrib && Can_Read_Attr(executor, thing, attrib)) {
    if (!CanEvalAttr(executor, thing, attrib)) {
      safe_str(T(e_perm), buff, bp);
      return;
    }
    /* Ok, we've got it */
    sp = sbuf = safe_atr_value(attrib);
    add_check("fun_edefault.attr_value");
    process_expression(buff, bp, &sp, thing, executor, executor,
		       PE_DEFAULT, PT_DEFAULT, pe_info);
    mush_free((Malloc_t) sbuf, "fun_edefault.attr_value");
    return;
  }
  /* We couldn't get it. Evaluate args[1] and return it */
  sp = args[1];
  process_expression(buff, bp, &sp, executor, caller, enactor,
		     PE_DEFAULT, PT_DEFAULT, pe_info);
  return;
}

/* ARGSUSED */
FUNCTION(fun_v)
{
  /* handle 0-9, va-vz, n, l, # */

  int c;

  if (args[0][0] && !args[0][1]) {
    switch (c = args[0][0]) {
    case '0':
    case '1':
    case '2':
    case '3':
    case '4':
    case '5':
    case '6':
    case '7':
    case '8':
    case '9':
      if (global_eval_context.wenv[c - '0'])
	safe_str(global_eval_context.wenv[c - '0'], buff, bp);
      return;
    case '#':
      /* enactor dbref */
      safe_dbref(enactor, buff, bp);
      return;
    case '@':
      /* caller dbref */
      safe_dbref(caller, buff, bp);
      return;
    case '!':
      /* executor dbref */
      safe_dbref(executor, buff, bp);
      return;
    case 'n':
    case 'N':
      /* enactor name */
      safe_str(Name(enactor), buff, bp);
      return;
    case 'l':
    case 'L':
      /* giving the location does not violate security,
       * since the object is the enactor.  */
      safe_dbref(Location(enactor), buff, bp);
      return;
    case 'c':
    case 'C':
      safe_str(global_eval_context.ccom, buff, bp);
      return;
    }
  }
  safe_str(do_get_attrib(executor, executor, args[0]), buff, bp);
}

/* ARGSUSED */
FUNCTION(fun_flags)
{
  dbref thing;
  char *p;
  ATTR *a;
  if (nargs == 0) {
    safe_str(list_all_flags("FLAG", NULL, executor, 0x1), buff, bp);
    return;
  }
  if ((p = strchr(args[0], '/')))
    *p++ = '\0';
  thing = match_thing(executor, args[0]);
  if (!GoodObject(thing)) {
    safe_str(T(e_notvis), buff, bp);
    return;
  }
  if (p) {
    /* Attribute flags, you must be able to read the attribute */
    a = atr_get_noparent(thing, upcasestr(p));
    if (!a || !Can_Read_Attr(executor, thing, a)) {
      safe_str("#-1", buff, bp);
      return;
    }
    safe_str(privs_to_letters(attr_privs, AL_FLAGS(a)), buff, bp);
    if (atr_sub_branch(a))
      safe_chr('`', buff, bp);
  } else {
    /* Object flags, visible to all */
    safe_str(unparse_flags(thing, executor), buff, bp);
  }
}

/* ARGSUSED */
FUNCTION(fun_lflags)
{
  dbref thing;
  char *p;
  ATTR *a;
  if (nargs == 0) {
    safe_str(list_all_flags("FLAG", NULL, executor, 0x2), buff, bp);
    return;
  }
  if ((p = strchr(args[0], '/')))
    *p++ = '\0';
  thing = match_thing(executor, args[0]);
  if (!GoodObject(thing)) {
    safe_str(T(e_notvis), buff, bp);
    return;
  }
  if (p) {
    /* Attribute flags, you must be able to read the attribute */
    a = atr_get_noparent(thing, upcasestr(p));
    if (!a || !Can_Read_Attr(executor, thing, a)) {
      safe_str("#-1", buff, bp);
      return;
    }
    safe_str(privs_to_string(attr_privs, AL_FLAGS(a)), buff, bp);
  } else {
    /* Object flags, visible to all */
    safe_str(bits_to_string("FLAG", Flags(thing), executor, thing), buff, bp);
  }
}

#ifdef WIN32
#pragma warning( disable : 4761)	/* Disable bogus conversion warning */
#endif
/* ARGSUSED */
FUNCTION(fun_haspower)
{
  dbref it;

  it = match_thing(executor, args[0]);
  if (!GoodObject(it)) {
    safe_str(T(e_notvis), buff, bp);
    return;
  }
  safe_boolean(sees_flag("POWER", executor, it, args[1]), buff, bp);
}

/* ARGSUSED */
FUNCTION(fun_powers)
{
  dbref it;

  if (nargs == 2) {
    if (!command_check_byname(executor, "@power") || fun->flags & FN_NOSIDEFX) {
      safe_str(T(e_perm), buff, bp);
      return;
    }
    if (FUNCTION_SIDE_EFFECTS)
      do_power(executor, args[0], args[1]);
    else
      safe_str(T(e_disabled), buff, bp);
    return;
  }
  it = match_thing(executor, args[0]);
  if (!GoodObject(it)) {
    safe_str(T(e_notvis), buff, bp);
    return;
  }
  safe_str(power_description(executor, it), buff, bp);
}

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

/* ARGSUSED */
FUNCTION(fun_num)
{
  safe_dbref(match_thing(executor, args[0]), buff, bp);
}

/* ARGSUSED */
FUNCTION(fun_rnum)
{
  dbref place = match_thing(executor, args[0]);
  char *name = args[1];
  dbref thing;
  if ((place != NOTHING) &&
      (Can_Examine(executor, place) || (Location(executor) == place) ||
       (enactor == place))) {
    switch (thing = match_result(place, name, NOTYPE, MAT_REMOTE)) {
    case NOTHING:
      safe_str(T(e_match), buff, bp);
      break;
    case AMBIGUOUS:
      safe_str(T("#-1 AMBIGUOUS MATCH"), buff, bp);
      break;
    default:
      safe_dbref(thing, buff, bp);
    }
  } else
    safe_str("#-1", buff, bp);
}

/*
 * fun_lcon, fun_lexits, fun_con, fun_exit, fun_next were all
 * re-coded by d'mike, 7/12/91.
  *
 * The function behavior was changed by Amberyl, to remove what she saw
 * as a security problem, since mortals could get the contents of rooms
 * they didn't control, thus (if they were willing to go through the trouble)
 * they could build a scanner to locate anything they wanted.
 *
 * The functions were completely reorganized and largely unified by Talek,
 * in September 2001, to finally cure persistent problems with next() and
 * con() and exit() returning different results/having different permissions
 * than lcon() and lexits().
 *
 * You can get the complete contents of any room you may examine, regardless
 * of whether or not objects are dark.  You can get the partial contents
 * (obeying DARK/LIGHT/etc.) of your current location or the enactor.
 * You CANNOT get the contents of anything else, regardless of whether
 * or not you have objects in it.
 *
 * The same behavior is true for exits.
 *
 * The lvcon/lvexits/lvplayers forms work the same way, but never return
 * a dark object or disconnected player, and are useful for parent rooms.
 */

/* Valid types for this function:
 * TYPE_EXIT    (lexits, lvexits, exit, next)
 * TYPE_THING   (lcon, lvcon, con, next) - really means 'things and players'
 * TYPE_PLAYER  (lplayers, lvplayers)
 */
static dbref
dbwalk(char *buff, char **bp, dbref executor, dbref enactor,
       int type, dbref loc, dbref after, int skipdark,
       int start, int count, int *retcount)
{
  dbref result;
  int first;
  int nthing;
  dbref thing;
  int validloc;
  dbref startdb;

  nthing = 0;

  if (!GoodObject(loc)) {
    if (buff)
      safe_str("#-1", buff, bp);
    return NOTHING;
  }
  if (type == TYPE_EXIT) {
    validloc = IsRoom(loc);
    startdb = Exits(loc);
  } else {
    validloc = !IsExit(loc);
    startdb = Contents(loc);
  }

  result = NOTHING;
  if (GoodObject(loc) && validloc &&
      (Can_Examine(executor, loc) || (Location(executor) == loc) ||
       (enactor == loc))) {
    first = 1;
    DOLIST_VISIBLE(thing, startdb, executor) {
      /* Skip if:
       * - We're not checking this type
       * - We can't interact with this thing
       * - We're only listing visual objects, and it's dark
       * - It's a player, not connected, and skipdark is true
       *   Funkily, lvcon() shows unconnected players, so we
       *   use type == TYPE_PLAYER for this check. :-/.
       */
      if (!(Typeof(thing) & type) ||
	  !can_interact(thing, executor, INTERACT_SEE) ||
	  (skipdark && Dark(thing)) ||
	  ((type == TYPE_PLAYER) && skipdark && !Connected(thing)))
	continue;
      nthing += 1;
      if (count < 1 || (nthing >= start && nthing < start + count)) {
	if (buff) {
	  if (first)
	    first = 0;
	  else
	    safe_chr(' ', buff, bp);
	  safe_dbref(thing, buff, bp);
	}
      }
      if (result == NOTHING) {
	if (after == NOTHING)
	  result = thing;
	if (after == thing)
	  after = NOTHING;
      }
      if (retcount)
	*retcount = nthing;
    }
  } else if (buff)
    safe_str("#-1", buff, bp);
  return result;
}

/* ARGSUSED */
FUNCTION(fun_dbwalker)
{
  dbref loc = match_thing(executor, args[0]);
  int start, count;
  int vis = 0;
  int type = 0;
  int result = 0;
  const char *ptr = called_as;
  char *buffptr = buff;
  char **bptr = bp;

  start = count = 0;
  buffptr = buff;
  bptr = bp;

  switch (*(ptr++)) {
  case 'X':
    if (!is_strict_integer(args[1]) || !is_strict_integer(args[2])) {
      safe_str(T(e_int), buff, bp);
      return;
    }
    start = parse_integer(args[1]);
    count = parse_integer(args[2]);
    if (start < 1 || count < 1) {
      safe_str(T("#-1 ARGUMENT OUT OF RANGE"), buff, bp);
      return;
    }
    break;
  case 'N':
    buffptr = NULL;
    bptr = NULL;
    break;
  }

  if (*ptr == 'V') {
    vis = 1;
    ptr++;
  }

  switch (*ptr) {
  case 'C':			/* con */
    type = TYPE_THING | TYPE_PLAYER;
    break;
  case 'T':			/* things */
    type = TYPE_THING;
    break;
  case 'P':			/* players */
    type = TYPE_PLAYER;
    break;
  case 'E':			/* exits */
    type = TYPE_EXIT;
    break;
  default:
    /* This should never be reached ... */
    type = TYPE_THING | TYPE_PLAYER;
  }

  dbwalk(buffptr, bptr, executor, enactor, type, loc, NOTHING,
	 vis, start, count, &result);

  if (!buffptr) {
    safe_integer(result, buff, bp);
  }
}

/* ARGSUSED */
FUNCTION(fun_con)
{
  dbref loc = match_thing(executor, args[0]);
  safe_dbref(dbwalk
	     (NULL, NULL, executor, enactor, TYPE_THING | TYPE_PLAYER, loc,
	      NOTHING, 0, 0, 0, NULL), buff, bp);
}

/* ARGSUSED */
FUNCTION(fun_exit)
{
  dbref loc = match_thing(executor, args[0]);
  safe_dbref(dbwalk
	     (NULL, NULL, executor, enactor, TYPE_EXIT, loc, NOTHING, 0, 0, 0,
	      NULL), buff, bp);
}

/* ARGSUSED */
FUNCTION(fun_next)
{
  dbref it = match_thing(executor, args[0]);

  if (GoodObject(it)) {
    switch (Typeof(it)) {
    case TYPE_EXIT:
      safe_dbref(dbwalk
		 (NULL, NULL, executor, enactor, TYPE_EXIT, Source(it), it, 0,
		  0, 0, NULL), buff, bp);
      break;
    case TYPE_THING:
    case TYPE_PLAYER:
      safe_dbref(dbwalk
		 (NULL, NULL, executor, enactor, TYPE_THING | TYPE_PLAYER,
		  Location(it), it, 0, 0, 0, NULL), buff, bp);
      break;
    default:
      safe_str("#-1", buff, bp);
      break;
    }
  } else
    safe_str("#-1", buff, bp);
}


/* ARGSUSED */
FUNCTION(fun_entrances)
{
  /* All args are optional.
   * The first argument is the dbref to check (default = this room)
   * The second argument to this function is a set of characters:
   * (a)ll (default), (e)xits, (t)hings, (p)layers, (r)ooms
   * The third and fourth args limit the range of dbrefs (default=0,db_top)
   */
  dbref where = Location(executor);
  dbref low = 0;
  dbref high = db_top - 1;
  dbref counter;
  dbref entrance;
  int found;
  int exd, td, pd, rd;		/* what we're looking for */
  char *p;

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

  if (nargs > 0)
    where = match_result(executor, args[0], NOTYPE, MAT_EVERYTHING);
  if (!GoodObject(where)) {
    safe_str(T("#-1 INVALID LOCATION"), buff, bp);
    return;
  }
  exd = td = pd = rd = 0;
  if (nargs > 1) {
    if (!args[1] || !*args[1]) {
      safe_str(T("#-1 INVALID SECOND ARGUMENT"), buff, bp);
      return;
    }
    p = args[1];
    while (*p) {
      switch (*p) {
      case 'a':
      case 'A':
	exd = td = pd = rd = 1;
	break;
      case 'e':
      case 'E':
	exd = 1;
	break;
      case 't':
      case 'T':
	td = 1;
	break;
      case 'p':
      case 'P':
	pd = 1;
	break;
      case 'r':
      case 'R':
	rd = 1;
	break;
      default:
	safe_str(T("#-1 INVALID SECOND ARGUMENT"), buff, bp);
	return;
      }
      p++;
    }
  }
  if (!exd && !td && !pd && !rd) {
    exd = td = pd = rd = 1;
  }
  if (nargs > 2) {
    if (is_integer(args[2])) {
      low = parse_integer(args[2]);
    } else if (is_dbref(args[2])) {
      low = parse_dbref(args[2]);
    } else {
      safe_str(T(e_ints), buff, bp);
      return;
    }
  }
  if (nargs > 3) {
    if (is_integer(args[3])) {
      high = parse_integer(args[3]);
    } else if (is_dbref(args[3])) {
      high = parse_dbref(args[3]);
    } else {
      safe_str(T(e_ints), buff, bp);
      return;
    }
  }
  if (!GoodObject(low))
    low = 0;
  if (!GoodObject(high))
    high = db_top - 1;

  if (!controls(executor, where) && !Search_All(executor)) {
    safe_str(T(e_perm), buff, bp);
    return;
  }
  if (!payfor(executor, FIND_COST)) {
    notify_format(executor, T("You don't have %d %s to do that."),
		  FIND_COST, ((FIND_COST == 1) ? MONEY : MONIES));
    safe_str("#-1", buff, bp);
    return;
  }
  /* Ok, do the real work */
  found = 0;
  for (counter = low; counter <= high; counter++) {
    if (controls(executor, where) || controls(executor, counter)) {
      if ((exd && IsExit(counter)) ||
	  (td && IsThing(counter)) ||
	  (pd && IsPlayer(counter)) || (rd && IsRoom(counter))) {
	if (Mobile(counter))
	  entrance = Home(counter);
	else
	  entrance = Location(counter);
	if (entrance == where) {
	  if (!found)
	    found = 1;
	  else
	    safe_chr(' ', buff, bp);
	  safe_dbref(counter, buff, bp);
	}
      }
    }
  }
  return;
}

/* ARGSUSED */
FUNCTION(fun_nearby)
{
  dbref obj1 = match_thing(executor, args[0]);
  dbref obj2 = match_thing(executor, args[1]);

  if (!controls(executor, obj1) && !controls(executor, obj2)
      && !See_All(executor)
      && !nearby(executor, obj1) && !nearby(executor, obj2)) {
    safe_str(T("#-1 NO OBJECTS CONTROLLED"), buff, bp);
    return;
  }
  if (!GoodObject(obj1) || !GoodObject(obj2)) {
    safe_str("#-1", buff, bp);
    return;
  }
  safe_chr(nearby(obj1, obj2) ? '1' : '0', buff, bp);
}

/* ARGSUSED */
FUNCTION(fun_controls)
{
  dbref it = match_thing(executor, args[0]);
  dbref thing = match_thing(executor, args[1]);

  if (!GoodObject(it))
    safe_str(T("#-1 ARG1 NOT FOUND"), buff, bp);
  else if (!GoodObject(thing))
    safe_str(T("#-1 ARG2 NOT FOUND"), buff, bp);
  else if (!(controls(executor, it) || controls(executor, thing)
	     || See_All(executor)))
    safe_str(T(e_perm), buff, bp);
  else
    safe_chr(controls(it, thing) ? '1' : '0', buff, bp);
}

/* ARGSUSED */
FUNCTION(fun_visible)
{
  /* check to see if we have an object-attribute pair. If we don't,
   * then we want to know about the whole object; otherwise, we're
   * just interested in a single attribute.
   * If we encounter an error, we return 0 rather than an error
   * code, since if it doesn't exist, it obviously can't see 
   * anything or be seen.
   */

  dbref it = match_thing(executor, args[0]);
  dbref thing;
  char *name;
  ATTR *a;

  if (!GoodObject(it)) {
    safe_str(T(e_notvis), buff, bp);
    return;
  }
  if ((name = strchr(args[1], '/')))
    *name++ = '\0';
  thing = match_thing(executor, args[1]);
  if (!GoodObject(thing)) {
    safe_chr('0', buff, bp);
    return;
  }
  if (name) {
    a = atr_get(thing, upcasestr(name));
    safe_chr((a && Can_Read_Attr(it, thing, a)) ? '1' : '0', buff, bp);
  } else {
    safe_boolean(Can_Examine(it, thing), buff, bp);
  }
}

/* ARGSUSED */
FUNCTION(fun_type)
{
  dbref it = match_thing(executor, args[0]);
  if (!GoodObject(it)) {
    safe_str(T(e_notvis), buff, bp);
    return;
  }
  switch (Typeof(it)) {
  case TYPE_PLAYER:
    safe_str("PLAYER", buff, bp);
    break;
  case TYPE_THING:
    safe_str("THING", buff, bp);
    break;
  case TYPE_EXIT:
    safe_str("EXIT", buff, bp);
    break;
  case TYPE_ROOM:
    safe_str("ROOM", buff, bp);
    break;
  case TYPE_GARBAGE:
    safe_str("GARBAGE", buff, bp);
    break;
  default:
    safe_str("WEIRD OBJECT", buff, bp);
    do_rawlog(LT_ERR, T("WARNING: Weird object #%d (type %d)\n"), it,
	      Typeof(it));
  }
}

/* ARGSUSED */
FUNCTION(fun_hasflag)
{
  dbref thing;
  ATTR *attrib;
  int f;

  if (strchr(args[0], '/')) {
    parse_attrib(executor, args[0], &thing, &attrib);
    if (!attrib)
      safe_str("#-1", buff, bp);
    else if ((f = string_to_atrflag(executor, args[1])) < 0)
      safe_str("#-1", buff, bp);
    else
      safe_boolean(AL_FLAGS(attrib) & f, buff, bp);
  } else {
    thing = match_thing(executor, args[0]);
    if (!GoodObject(thing))
      safe_str(T(e_notvis), buff, bp);
    else
      safe_boolean(sees_flag("FLAG", executor, thing, args[1]), buff, bp);
  }
}

/* ARGSUSED */
FUNCTION(fun_hastype)
{
  dbref it = match_thing(executor, args[0]);
  if (!GoodObject(it)) {
    safe_str(T(e_notvis), buff, bp);
    return;
  }
  switch (*args[1]) {
  case 'r':
  case 'R':
    safe_boolean(IsRoom(it), buff, bp);
    break;
  case 'e':
  case 'E':
    safe_boolean(IsExit(it), buff, bp);
    break;
  case 'p':
  case 'P':
    safe_boolean(IsPlayer(it), buff, bp);
    break;
  case 't':
  case 'T':
    safe_boolean(IsThing(it), buff, bp);
    break;
  case 'g':
  case 'G':
    safe_boolean(IsGarbage(it), buff, bp);
    break;
  default:
    safe_str(T("#-1 NO SUCH TYPE"), buff, bp);
    break;
  };
}

/* ARGSUSED */
FUNCTION(fun_orflags)
{
  dbref it = match_thing(executor, args[0]);
  if (!strcmp(called_as, "ORPOWERS"))
    safe_boolean(flaglist_check("POWER", executor, it, args[1], 0), buff, bp);
  else
    safe_boolean(flaglist_check("FLAG", executor, it, args[1], 0), buff, bp);
}

/* ARGSUSED */
FUNCTION(fun_andflags)
{
  dbref it = match_thing(executor, args[0]);
  if (!strcmp(called_as, "ANDPOWERS"))
    safe_boolean(flaglist_check("POWER", executor, it, args[1], 1), buff, bp);
  else
    safe_boolean(flaglist_check("FLAG", executor, it, args[1], 1), buff, bp);
}

/* ARGSUSED */
FUNCTION(fun_orlflags)
{
  dbref it = match_thing(executor, args[0]);
  if (!strcmp(called_as, "ORLPOWERS"))
    safe_boolean(flaglist_check_long("POWER", executor, it, args[1], 0), buff,
		 bp);
  else
    safe_boolean(flaglist_check_long("FLAG", executor, it, args[1], 0), buff,
		 bp);
}

/* ARGSUSED */
FUNCTION(fun_andlflags)
{
  dbref it = match_thing(executor, args[0]);
  if (!strcmp(called_as, "ANDLPOWERS"))
    safe_boolean(flaglist_check_long("POWER", executor, it, args[1], 1), buff,
		 bp);
  else
    safe_boolean(flaglist_check_long("FLAG", executor, it, args[1], 1), buff,
		 bp);
}

static lock_type
get_locktype(char *str)
{
  /* figure out a lock type */

  if (!str || !*str)
    return Basic_Lock;
  if (!strncasecmp(str, "USER:", 5))
    str += 5;
  return upcasestr(str);
}

/* ARGSUSED */
FUNCTION(fun_locks)
{
  dbref thing = match_thing(executor, args[0]);
  lock_list *ll;
  const lock_list *p;
  int first = 1;

  if (!GoodObject(thing)) {
    safe_str(T(e_notvis), buff, bp);
    return;
  }

  for (ll = Locks(thing); ll; ll = ll->next) {
    p = get_lockproto(L_TYPE(ll));
    if (!first) {
      safe_chr(' ', buff, bp);
    }
    first = 0;
    if (!p)
      safe_str("USER:", buff, bp);
    safe_str(L_TYPE(ll), buff, bp);
  }
}

/* ARGSUSED */
FUNCTION(fun_lockflags)
{
  dbref it;
  char *p;
  int fullname = 0;
  lock_list *ll;
  lock_type ltype;

  if (called_as[1] == 'L')	/* LLOCKFLAGS */
    fullname = 1;

  if (nargs == 0) {
    if (fullname)
      list_lock_flags_long(buff, bp);
    else
      list_lock_flags(buff, bp);
    return;
  }

  if ((p = strchr(args[0], '/')))
    *(p++) = '\0';

  it = match_thing(executor, args[0]);
  if (!GoodObject(it)) {
    safe_str(T(e_notvis), buff, bp);
    return;
  }
  ltype = get_locktype(p);

  if (GoodObject(it) && (ltype != NULL)
      && Can_Read_Lock(executor, it, ltype)) {
    ll = getlockstruct(it, ltype);
    if (ll) {
      if (fullname)
	safe_str(lock_flags_long(ll), buff, bp);
      else
	safe_str(lock_flags(ll), buff, bp);
      return;
    } else {
      safe_str("#-1 NO SUCH LOCK", buff, bp);
      return;
    }
  }
  safe_str("#-1 NO SUCH LOCK", buff, bp);
}

/* ARGSUSED */
FUNCTION(fun_lset)
{
  if (!FUNCTION_SIDE_EFFECTS) {
    safe_str(T(e_disabled), buff, bp);
    return;
  }
  if (!command_check_byname(executor, "@lset") || fun->flags & FN_NOSIDEFX) {
    safe_str(T(e_perm), buff, bp);
    return;
  }
  do_lset(executor, args[0], args[1]);
}

/* ARGSUSED */
FUNCTION(fun_lock)
{
  dbref it;
  char *ltype = NULL;
  lock_type real_ltype;

  if ((ltype = strchr(args[0], '/'))) {
    *ltype++ = '\0';
    upcasestr(ltype);
  }

  real_ltype = get_locktype(ltype);

  it = match_thing(executor, args[0]);

  if (nargs == 2) {
    if (!command_check_byname(executor, "@lock") || fun->flags & FN_NOSIDEFX) {
      safe_str(T(e_perm), buff, bp);
      return;
    }
    if (FUNCTION_SIDE_EFFECTS)
      do_lock(executor, args[0], args[1], ltype);
    else {
      safe_str(T(e_disabled), buff, bp);
      return;
    }
  }
  if (GoodObject(it) && (real_ltype != NULL)
      && Can_Read_Lock(executor, it, real_ltype)) {
    safe_str(unparse_boolexp(executor, getlock(it, real_ltype), UB_DBREF),
	     buff, bp);
    return;
  }
  safe_str("#-1", buff, bp);
}

/* ARGSUSED */
FUNCTION(fun_elock)
{
  char *p;
  lock_type ltype;
  dbref it;
  dbref victim = match_thing(executor, args[1]);

  p = strchr(args[0], '/');
  if (p)
    *p++ = '\0';

  it = match_thing(executor, args[0]);
  ltype = get_locktype(p);

  if (!GoodObject(it) || (ltype == NULL) || !Can_Read_Lock(executor, it, ltype)) {
    safe_str("#-1", buff, bp);
    return;
  }
  if (Can_Locate(executor, victim))
    safe_boolean(eval_lock(victim, it, ltype), buff, bp);
  else
    safe_str("#-1", buff, bp);
  return;
}

/* ARGSUSED */
FUNCTION(fun_findable)
{
  dbref obj = match_thing(executor, args[0]);
  dbref victim = match_thing(executor, args[1]);

  if (!GoodObject(obj))
    safe_str(T("#-1 ARG1 NOT FOUND"), buff, bp);
  else if (!GoodObject(victim))
    safe_str(T("#-1 ARG2 NOT FOUND"), buff, bp);
  else if (!See_All(executor) && !controls(executor, obj) &&
	   !controls(executor, victim))
    safe_str(T(e_perm), buff, bp);
  else
    safe_boolean(Can_Locate(obj, victim), buff, bp);
}

/* ARGSUSED */
FUNCTION(fun_loc)
{
  dbref it = match_thing(executor, args[0]);
  if (GoodObject(it) && Can_Locate(executor, it))
    safe_dbref(Location(it), buff, bp);
  else
    safe_str("#-1", buff, bp);
}

/* ARGSUSED */
FUNCTION(fun_objid)
{
  dbref it = match_thing(executor, args[0]);

  if (GoodObject(it)) {
    safe_dbref(it, buff, bp);
    safe_chr(':', buff, bp);
    safe_integer(CreTime(it), buff, bp);
  } else
    safe_str(T(e_notvis), buff, bp);
}

/* ARGSUSED */
FUNCTION(fun_ctime)
{
  dbref it = match_thing(executor, args[0]);

  if (GoodObject(it))
    safe_str(show_time(CreTime(it), 0), buff, bp);
  else
    safe_str(T(e_notvis), buff, bp);
}

/* ARGSUSED */
FUNCTION(fun_mtime)
{
  dbref it = match_thing(executor, args[0]);
  if (!GoodObject(it))
    safe_str(T(e_notvis), buff, bp);
  else if (!Can_Examine(executor, it) || IsPlayer(it))
    safe_str(T(e_perm), buff, bp);
  else
    safe_str(show_time(ModTime(it), 0), buff, bp);
}

/* ARGSUSED */
FUNCTION(fun_where)
{
  /* finds the "real" location of an object */

  dbref it = match_thing(executor, args[0]);
  if (GoodObject(it) && Can_Locate(executor, it))
    safe_dbref(where_is(it), buff, bp);
  else
    safe_str("#-1", buff, bp);
}

/* ARGSUSED */
FUNCTION(fun_room)
{
  dbref it = match_thing(executor, args[0]);

  if (!GoodObject(it))
    safe_str(T(e_notvis), buff, bp);
  else if (!Can_Locate(executor, it))
    safe_str(T(e_perm), buff, bp);
  else {
    dbref room = absolute_room(it);
    if (!GoodObject(room)) {
      safe_strl("#-1", 3, buff, bp);
      return;
    }
    safe_dbref(room, buff, bp);
  }
}

/* ARGSUSED */
FUNCTION(fun_rloc)
{
  int i;
  int deep = parse_integer(args[1]);
  dbref it = match_thing(executor, args[0]);

  if (deep > 20)
    deep = 20;
  if (deep < 0)
    deep = 0;

  if (!GoodObject(it))
    safe_str(T(e_notvis), buff, bp);
  else if (!Can_Locate(executor, it))
    safe_str(T(e_perm), buff, bp);
  else {
    for (i = 0; i < deep; i++) {
      if (!GoodObject(it) || IsRoom(it))
	break;
      it = Location(it);
    }
    safe_dbref(it, buff, bp);
    return;
  }
}

/* ARGSUSED */
FUNCTION(fun_zone)
{
  dbref it;

  if (nargs == 2) {
    if (!command_check_byname(executor, "@chzone") || fun->flags & FN_NOSIDEFX) {
      safe_str(T(e_perm), buff, bp);
      return;
    }
    if (FUNCTION_SIDE_EFFECTS)
      (void) do_chzone(executor, args[0], args[1], 1);
    else {
      safe_str(T(e_disabled), buff, bp);
      return;
    }
  }
  it = match_thing(executor, args[0]);
  if (!GoodObject(it))
    safe_str(T(e_notvis), buff, bp);
  else if (!Can_Examine(executor, it))
    safe_str(T(e_perm), buff, bp);
  else
    safe_dbref(Zone(it), buff, bp);
}

/* ARGSUSED */
FUNCTION(fun_parent)
{
  dbref it;

  if (nargs == 2) {
    if (!command_check_byname(executor, "@parent") || fun->flags & FN_NOSIDEFX) {
      safe_str(T(e_perm), buff, bp);
      return;
    }
    if (FUNCTION_SIDE_EFFECTS)
      do_parent(executor, args[0], args[1]);
    else {
      safe_str(T(e_disabled), buff, bp);
      return;
    }
  }
  it = match_thing(executor, args[0]);
  if (!GoodObject(it))
    safe_str(T(e_notvis), buff, bp);
  else if (!Can_Examine(executor, it))
    safe_str(T(e_perm), buff, bp);
  else
    safe_dbref(Parent(it), buff, bp);
}

/* ARGSUSED */
FUNCTION(fun_lparent)
{
  dbref it;
  dbref par;

  it = match_thing(executor, args[0]);
  if (!GoodObject(it)) {
    safe_str(T(e_notvis), buff, bp);
    return;
  }
  safe_dbref(it, buff, bp);
  par = Parent(it);
  while (GoodObject(par) && Can_Examine(executor, it)) {
    if (safe_chr(' ', buff, bp))
      break;
    safe_dbref(par, buff, bp);
    it = par;
    par = Parent(par);
  }
}

/* ARGSUSED */
FUNCTION(fun_home)
{
  dbref it = match_thing(executor, args[0]);
  if (!GoodObject(it))
    safe_str(T(e_notvis), buff, bp);
  else if (!Can_Examine(executor, it))
    safe_str(T(e_perm), buff, bp);
  else if (IsExit(it))
    safe_dbref(Source(it), buff, bp);
  else if (IsRoom(it))
    safe_dbref(Location(it), buff, bp);
  else
    safe_dbref(Home(it), buff, bp);
}

/* ARGSUSED */
FUNCTION(fun_money)
{
  /* Are we asking about something's money? */
  dbref it = match_result(executor, args[0], NOTYPE, MAT_EVERYTHING);

  if (!GoodObject(it)) {
    /* Well, are we asking for the plural/singular for some amount? */
    if (is_integer(args[0])) {
      int a = parse_integer(args[0]);
      if (abs(a) == 1)
	safe_str(MONEY, buff, bp);
      else
	safe_str(MONIES, buff, bp);
    } else {
      /* Guess we're just making a typo or something. */
      safe_str("#-1", buff, bp);
    }
    return;
  } else if (!GoodObject(it)) {
    /* Catch ambiguous matches */
    safe_str("#-1", buff, bp);
    return;
  }
  /* If the thing in question has unlimited money, respond with the
   * max money possible. We don't use the NoPay macro, though, because
   * we want to return the amount of money stored in an object, even
   * if its owner is no_pay. Softcode can check money(owner(XX)) if 
   * they want to allow objects to pay like their owners.
   */
  if (has_power_by_name(it, "NO_PAY", NOTYPE))
    safe_integer(MAX_PENNIES, buff, bp);
  else
    safe_integer(Pennies(it), buff, bp);
}

/* ARGSUSED */
FUNCTION(fun_owner)
{
  dbref thing;
  ATTR *attrib;

  if (strchr(args[0], '/')) {
    parse_attrib(executor, args[0], &thing, &attrib);
    if (!GoodObject(thing) || !attrib
	|| !Can_Read_Attr(executor, thing, attrib))
      safe_str("#-1", buff, bp);
    else
      safe_dbref(attrib->creator, buff, bp);
  } else {
    thing = match_thing(executor, args[0]);
    if (!GoodObject(thing))
      safe_str(T(e_notvis), buff, bp);
    else
      safe_dbref(Owner(thing), buff, bp);
  }
}

/* ARGSUSED */
FUNCTION(fun_name)
{
  dbref it;

  /* Special case for backward compatibility */
  if (nargs == 0)
    return;
  if (nargs == 2) {
    if (!command_check_byname(executor, "@name") || fun->flags & FN_NOSIDEFX) {
      safe_str(T(e_perm), buff, bp);
      return;
    }
    if (FUNCTION_SIDE_EFFECTS)
      do_name(executor, args[0], args[1]);
    else
      safe_str(T(e_disabled), buff, bp);
    return;
  }
  it = match_thing(executor, args[0]);
  if (GoodObject(it))
    safe_str(shortname(it), buff, bp);
  else
    safe_str(T(e_notvis), buff, bp);
}

/* ARGSUSED */
FUNCTION(fun_fullname)
{
  dbref it = match_thing(executor, args[0]);
  if (GoodObject(it))
    safe_str(Name(it), buff, bp);
  else
    safe_str(T(e_notvis), buff, bp);
}

/* ARGSUSED */
FUNCTION(fun_accname)
{
  dbref it = match_thing(executor, args[0]);
  if (GoodObject(it))
    safe_str(accented_name(it), buff, bp);
  else
    safe_str(T(e_notvis), buff, bp);
}

/* ARGSUSED */
FUNCTION(fun_iname)
{
  dbref it = match_thing(executor, args[0]);
  char tbuf1[BUFFER_LEN];

  if (GoodObject(it)) {
    /* You must either be see_all, control it, or be inside it */
    if (!(controls(executor, it) || See_All(executor) ||
	  (Location(executor) == it))) {
      safe_str(T(e_perm), buff, bp);
      return;
    }
    if (nameformat(executor, it, tbuf1))
      safe_str(tbuf1, buff, bp);
    else if (IsExit(it))
      safe_str(shortname(it), buff, bp);
    else
      safe_str(accented_name(it), buff, bp);
  } else
    safe_str(T(e_notvis), buff, bp);
}

/* ARGSUSED */
FUNCTION(fun_pmatch)
{
  dbref target;

  if (*args[0] == '*')
    target = lookup_player(args[0] + 1);
  else if (*args[0] == NUMBER_TOKEN) {
    target = parse_objid(args[0]);
    if (!(GoodObject(target) && IsPlayer(target))) {
      notify(executor, T("No match."));
      safe_str("#-1", buff, bp);
      return;
    } else {
      safe_dbref(target, buff, bp);
      return;
    }
  } else
    target = lookup_player(args[0]);
  if (target == NOTHING) {
    if (*args[0] == '*')
      target = visible_short_page(executor, args[0] + 1);
    else
      target = visible_short_page(executor, args[0]);
  }
  switch (target) {
  case NOTHING:
    notify(executor, T("No match."));
    safe_str("#-1", buff, bp);
    break;
  case AMBIGUOUS:
    notify(executor, T("I'm not sure who you mean."));
    safe_str("#-2", buff, bp);
    break;
  default:
    safe_dbref(target, buff, bp);
  }
}

/* ARGSUSED */
FUNCTION(fun_locate)
{
  dbref looker;
  int pref_type;
  dbref item, loc;
  char *p;
  int keys = 0;
  int ambig_ok = 0;
  int force_type = 0;
  long match_flags = 0;

  /* find out what we're matching in relation to */
  looker = match_thing(executor, args[0]);
  if (!GoodObject(looker)) {
    safe_str("#-1", buff, bp);
    return;
  }
  if (!See_All(executor) && !controls(executor, looker)) {
    safe_str("#-1", buff, bp);
    return;
  }

  /* find out our preferred match type and flags */
  pref_type = NOTYPE;
  for (p = args[2]; *p; p++) {
    switch (*p) {
    case 'N':
      pref_type = NOTYPE;
      break;
    case 'E':
      pref_type = TYPE_EXIT;
      break;
    case 'P':
      pref_type = TYPE_PLAYER;
      break;
    case 'R':
      pref_type = TYPE_ROOM;
      break;
    case 'T':
      pref_type = TYPE_THING;
      break;
    case 'L':
      keys = 1;
      break;
    case 'F':
      force_type = 1;
      break;
    case '*':
      match_flags |= MAT_EVERYTHING;
      break;
    case 'a':
      match_flags |= MAT_ABSOLUTE;
      break;
    case 'c':
      match_flags |= MAT_CARRIED_EXIT;
      break;
    case 'e':
      match_flags |= MAT_EXIT;
      break;
    case 'h':
      match_flags |= MAT_HERE;
      break;
    case 'i':
      match_flags |= MAT_POSSESSION;
      break;
    case 'l':
      match_flags |= MAT_CONTAINER;
      break;
    case 'm':
      match_flags |= MAT_ME;
      break;
    case 'n':
      match_flags |= MAT_NEIGHBOR;
      break;
    case 'y':
      match_flags |= MAT_PMATCH;
      break;
    case 'p':
      match_flags |= MAT_PLAYER;
      break;
    case 'z':
      match_flags |= MAT_ENGLISH;
      break;
    case 'X':
      ambig_ok = 1;		/* okay to pick last match */
      break;
    default:
      notify_format(executor, T("I don't understand switch '%c'."), *p);
      break;
    }
  }

  if (keys)
    match_flags = MAT_CHECK_KEYS;

  /* report the results */
  if (!ambig_ok)
    item = match_result(looker, args[1], pref_type, match_flags);
  else
    item = last_match_result(looker, args[1], pref_type, match_flags);

  if (!GoodObject(item)) {
    safe_dbref(item, buff, bp);
    return;
  }

  if (force_type && pref_type != NOTYPE && !(Typeof(item) == pref_type)) {
    safe_dbref(NOTHING, buff, bp);
    return;
  }

  /* To locate it, you must either be able to examine its location
   * or be able to see the item.
   */
  loc = Location(item);
  if (GoodObject(loc)) {
    if (Can_Examine(executor, loc))
      safe_dbref(item, buff, bp);
    else if (can_interact(item, executor, INTERACT_SEE)
	     && (!DarkLegal(item) || (Dark(loc) && Light(item))))
      safe_dbref(item, buff, bp);
    else
      safe_dbref(NOTHING, buff, bp);
  } else {
    if (can_interact(item, executor, INTERACT_SEE)
	&& (See_All(executor) || !DarkLegal(item)))
      safe_dbref(item, buff, bp);
    else
      safe_dbref(NOTHING, buff, bp);
  }
  return;
}


/* --------------------------------------------------------------------------
 * Creation functions: CREATE, PCREATE, OPEN, DIG
 */

/* ARGSUSED */
FUNCTION(fun_create)
{
  int cost;

  if (!FUNCTION_SIDE_EFFECTS) {
    safe_str(T(e_disabled), buff, bp);
    return;
  }

  if (!command_check_byname(executor, "@create") || fun->flags & FN_NOSIDEFX) {
    safe_str(T(e_perm), buff, bp);
    return;
  }
  if (nargs == 2)
    cost = parse_integer(args[1]);
  else
    cost = OBJECT_COST;
  safe_dbref(do_create(executor, args[0], cost), buff, bp);
}

/* ARGSUSED */
FUNCTION(fun_pcreate)
{
  if (!FUNCTION_SIDE_EFFECTS) {
    safe_str(T(e_disabled), buff, bp);
    return;
  }
  if (!command_check_byname(executor, "@pcreate") || fun->flags & FN_NOSIDEFX) {
    safe_str(T(e_perm), buff, bp);
    return;
  }
  safe_dbref(do_pcreate(executor, args[0], args[1]), buff, bp);
}

/* ARGSUSED */
FUNCTION(fun_open)
{
  if (!FUNCTION_SIDE_EFFECTS) {
    safe_str(T(e_disabled), buff, bp);
    return;
  }
  if (!command_check_byname(executor, "@open") || fun->flags & FN_NOSIDEFX) {
    safe_str(T(e_perm), buff, bp);
    return;
  }
  safe_dbref(do_real_open(executor, args[0], args[1], NOTHING), buff, bp);
}

/* ARGSUSED */
FUNCTION(fun_dig)
{
  if (!FUNCTION_SIDE_EFFECTS) {
    safe_str(T(e_disabled), buff, bp);
    return;
  }
  if (!command_check_byname(executor, "@dig") || fun->flags & FN_NOSIDEFX) {
    safe_str(T(e_perm), buff, bp);
    return;
  }
  safe_dbref(do_dig(executor, args[0], args, 0), buff, bp);
}

/* ARGSUSED */
FUNCTION(fun_clone)
{
  if (!FUNCTION_SIDE_EFFECTS) {
    safe_str(T(e_disabled), buff, bp);
    return;
  }
  if (!command_check_byname(executor, "@clone") || fun->flags & FN_NOSIDEFX) {
    safe_str(T(e_perm), buff, bp);
    return;
  }
  safe_dbref(do_clone(executor, args[0], NULL, 0), buff, bp);
}


/* --------------------------------------------------------------------------
 * Attribute functions: LINK, SET
 */

/* ARGSUSED */
FUNCTION(fun_link)
{
  if (!FUNCTION_SIDE_EFFECTS) {
    safe_str(T(e_disabled), buff, bp);
    return;
  }
  if (!command_check_byname(executor, "@link") || fun->flags & FN_NOSIDEFX) {
    safe_str(T(e_perm), buff, bp);
    return;
  }
  do_link(executor, args[0], args[1], 0);
}

/* ARGSUSED */
FUNCTION(fun_set)
{
  if (!FUNCTION_SIDE_EFFECTS) {
    safe_str(T(e_disabled), buff, bp);
    return;
  }
  if (!command_check_byname(executor, "@set") || fun->flags & FN_NOSIDEFX) {
    safe_str(T(e_perm), buff, bp);
    return;
  }
  do_set(executor, args[0], args[1]);
}

/* ARGSUSED */
FUNCTION(fun_wipe)
{
  if (!FUNCTION_SIDE_EFFECTS) {
    safe_str(T(e_disabled), buff, bp);
    return;
  }
  if (!command_check_byname(executor, "@wipe") || fun->flags & FN_NOSIDEFX) {
    safe_str(T(e_perm), buff, bp);
    return;
  }
  do_wipe(executor, args[0]);
}


/* --------------------------------------------------------------------------
 * Misc functions: TEL
 */

/* ARGSUSED */
FUNCTION(fun_tel)
{
  int silent = 0;
  int inside = 0;
  if (!FUNCTION_SIDE_EFFECTS) {
    safe_str(T(e_disabled), buff, bp);
    return;
  }
  if (!command_check_byname(executor, "@tel") || fun->flags & FN_NOSIDEFX) {
    safe_str(T(e_perm), buff, bp);
    return;
  }
  if (nargs > 2)
    silent = parse_boolean(args[2]);
  if (nargs > 3)
    silent = parse_boolean(args[3]);
  do_teleport(executor, args[0], args[1], silent, inside);
}


/* ARGSUSED */
FUNCTION(fun_isdbref)
{
  safe_boolean(parse_dbref(args[0]) != NOTHING, buff, bp);
}

/* ARGSUSED */
FUNCTION(fun_grep)
{
  char *tp;

  dbref it = match_thing(executor, args[0]);
  if (!GoodObject(it)) {
    safe_str(T(e_notvis), buff, bp);
    return;
  }
  /* make sure there's an attribute and a pattern */
  if (!*args[1]) {
    safe_str(T("#-1 NO SUCH ATTRIBUTE"), buff, bp);
    return;
  }
  if (!*args[2]) {
    safe_str(T("#-1 INVALID GREP PATTERN"), buff, bp);
    return;
  }
  tp = grep_util(executor, it, args[1], args[2], arglens[2],
		 strcmp(called_as, "GREP"));
  add_check("fun_grep.attr_list");
  safe_str(tp, buff, bp);
  mush_free((Malloc_t) tp, "fun_grep.attr_list");
}

/* Get database size statistics */
/* ARGSUSED */
FUNCTION(fun_lstats)
{
  dbref who;
  struct db_stat_info *si;

  if ((!args[0]) || !*args[0] || !strcasecmp(args[0], "all")) {
    who = ANY_OWNER;
  } else if (!strcasecmp(args[0], "me")) {
    who = executor;
  } else {
    who = lookup_player(args[0]);
    if (who == NOTHING) {
      safe_str(T(e_notvis), buff, bp);
      return;
    }
  }
  if (!Search_All(executor)) {
    if (who != ANY_OWNER && !controls(executor, who)) {
      safe_str(T(e_perm), buff, bp);
      return;
    }
  }
  si = get_stats(who);
  if (who != ANY_OWNER) {
    safe_format(buff, bp, "%d %d %d %d %d", si->total - si->garbage, si->rooms,
		si->exits, si->things, si->players);
  } else {
    safe_format(buff, bp, "%d %d %d %d %d %d", si->total, si->rooms, si->exits,
		si->things, si->players, si->garbage);
  }
}


/* ARGSUSED */
FUNCTION(fun_atrlock)
{
  dbref thing;
  char *p;
  ATTR *ptr;
  int status;

  if (nargs == 1)
    status = 0;
  else
    status = 1;

  if (status == 1) {
    if (FUNCTION_SIDE_EFFECTS) {
      if (!command_check_byname(executor, "@atrlock")
	  || fun->flags & FN_NOSIDEFX) {
	safe_str(T(e_perm), buff, bp);
	return;
      }
      do_atrlock(executor, args[0], args[1]);
      return;
    } else
      safe_str(T(e_disabled), buff, bp);
    return;
  }

  if (!args[0] || !*args[0]) {
    safe_str(T("#-1 ARGUMENT MUST BE OBJ/ATTR"), buff, bp);
    return;
  }
  if (!(p = strchr(args[0], '/')) || !(*(p + 1))) {
    safe_str(T("#-1 ARGUMENT MUST BE OBJ/ATTR"), buff, bp);
    return;
  }
  *p++ = '\0';

  if ((thing =
       noisy_match_result(executor, args[0], NOTYPE,
			  MAT_EVERYTHING)) == NOTHING) {
    safe_str(T(e_notvis), buff, bp);
    return;
  }

  ptr = atr_get_noparent(thing, strupper(p));
  if (ptr && Can_Read_Attr(executor, thing, ptr))
    safe_boolean(AF_Locked(ptr), buff, bp);
  else
    safe_str("#-1", buff, bp);
}

/* ARGSUSED */
FUNCTION(fun_followers)
{
  dbref thing;
  ATTR *a;
  char *s;
  char *res;

  thing = match_controlled(executor, args[0]);
  if (!GoodObject(thing)) {
    safe_str(T("#-1 INVALID OBJECT"), buff, bp);
    return;
  }
  a = atr_get_noparent(thing, "FOLLOWERS");
  if (!a)
    return;
  s = atr_value(a);
  res = trim_space_sep(s, ' ');
  safe_str(res, buff, bp);
  return;;
}

/* ARGSUSED */
FUNCTION(fun_following)
{
  dbref thing;
  ATTR *a;
  char *s;
  char *res;

  thing = match_controlled(executor, args[0]);
  if (!GoodObject(thing)) {
    safe_str(T("#-1 INVALID OBJECT"), buff, bp);
    return;
  }
  a = atr_get_noparent(thing, "FOLLOWING");
  if (!a)
    return;
  s = atr_value(a);
  res = trim_space_sep(s, ' ');
  safe_str(res, buff, bp);
  return;;
}