pennmush/game/
pennmush/game/data/
pennmush/game/log/
pennmush/game/save/
pennmush/game/txt/evt/
pennmush/game/txt/nws/
pennmush/os2/
#include "copyrite.h"

#include "config.h"
#ifdef I_STRING
#include <string.h>
#else
#include <strings.h>
#endif
#include "conf.h"
#include "dbdefs.h"
#include "externs.h"
#include "flags.h"
#include "intrface.h"
#include "match.h"
#include "parse.h"
#include "mushdb.h"
#ifdef MEM_CHECK
#include "memcheck.h"
#endif
#include "confmagic.h"

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

#ifdef CHAT_SYSTEM
#include "extchat.h"
extern CHAN *channels;
#endif

extern char ccom[BUFFER_LEN];	/* bsd.c */
char *do_get_attrib _((dbref executor, dbref thing, char *attrib));
dbref next_con _((dbref player, dbref this));
dbref next_exit _((dbref player, dbref this));
static lock_type get_locktype _((dbref obj, const char *str));
extern struct db_stat_info *get_stats _((dbref player, dbref owner));
extern ATTR *aname_hash_lookup _((const char *name));
static int lattr_helper _((dbref player, dbref thing, char const *pattern,
			   ATTR *atr, void *args));


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

  a = atr_get(thing, upcasestr(attrib));
  if (a) {
    if (Can_Read_Attr(executor, thing, a)) {
      if (strlen(value = uncompress(a->value)) < BUFFER_LEN)
	return (char *) value;
      else
	return (char *) "#-1 ATTRIBUTE LENGTH TOO LONG";
    }
    return (char *) "#-1 NO PERMISSION TO GET ATTRIBUTE";
  }

  a = atr_match(upcasestr(attrib));
  if (a) {
    if (Can_Read_Attr(executor, thing, a))
      return (char *) "";
    return (char *) "#-1 NO PERMISSION TO GET ATTRIBUTE";
  }

  if (!Can_Examine(executor, thing))
    return (char *) "#-1 NO PERMISSION TO GET ATTRIBUTE";
  return (char *) "";
}

/* this function produces a space-separated list of attributes that are
 * on an object.
 */
struct lh_args {
  int first;
  char *buff;
  char **bp;
};

/* ARGSUSED */
static int
lattr_helper(player, thing, pattern, atr, args)
    dbref player;
    dbref thing;
    char const *pattern;
    ATTR *atr;
    void *args;
{
  struct lh_args *lh = args;
  if (lh->first)
    lh->first = 0;
  else
    safe_chr(' ', lh->buff, lh->bp);
  safe_str(AL_NAME(atr), lh->buff, lh->bp);
  return 1;
}

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

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

  thing = match_thing(executor, args[0]);
  if (thing == NOTHING) {
    safe_str("#-1 NO MATCH", buff, bp);
    return;
  }
  if (!Can_Examine(executor, thing)) {
    safe_str("#-1 PERMISSION DENIED", buff, bp);
    return;
  }
  lh.first = 1;
  lh.buff = buff;
  lh.bp = bp;
  (void) atr_iter_get(executor, thing, pattern, lattr_helper, &lh);
}

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

  thing = match_thing(executor, args[0]);
  if (thing == NOTHING) {
    safe_str("#-1 NO SUCH OBJECT VISIBLE", 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("#-1 PERMISSION DENIED", buff, bp);
    return;
  }
  safe_chr('0', buff, bp);
}

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

  s = strchr(args[0], '/');
  if (!s) {
    safe_str("#-1 BAD ARGUMENT FORMAT TO GET", buff, bp);
    return;
  }
  *s++ = '\0';
  thing = match_thing(executor, args[0]);
  if (thing == NOTHING) {
    safe_str("#-1 NO SUCH OBJECT VISIBLE", 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 (thing == NOTHING) {
    safe_str("#-1 NO SUCH OBJECT VISIBLE", 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;
  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)) {
    /* Ok, we've got it */
    sp = safe_uncompress(attrib->value);
    safe_str(sp, buff, bp);
    free((Malloc_t) sp);
    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 (thing == NOTHING) {
    safe_str("#-1 NO SUCH OBJECT VISIBLE", buff, bp);
    return;
  }
  a = atr_get(thing, upcasestr(args[1]));
  if (a && Can_Read_Attr(executor, thing, a)) {
    tp = tbuf = safe_uncompress(a->value);
#ifdef MEM_CHECK
    add_check("fun_eval.attr_value");
#endif
    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("#-1 NO PERMISSION TO GET ATTRIBUTE", 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("#-1 BAD ARGUMENT FORMAT TO GET_EVAL", buff, bp);
    return;
  }
  *s++ = '\0';
  thing = match_thing(executor, args[0]);
  if (thing == NOTHING) {
    safe_str("#-1 NO SUCH OBJECT VISIBLE", buff, bp);
    return;
  }
  a = atr_get(thing, upcasestr(s));
  if (a && Can_Read_Attr(executor, thing, a)) {
    tp = tbuf = safe_uncompress(a->value);
#ifdef MEM_CHECK
    add_check("fun_eval.attr_value");
#endif
    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("#-1 NO PERMISSION TO GET ATTRIBUTE", 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)) {
    /* Ok, we've got it */
    sp = sbuf = safe_uncompress(attrib->value);
#ifdef MEM_CHECK
    add_check("fun_edefault.attr_value");
#endif
    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 (wenv[c - '0'])
	safe_str(wenv[c - '0'], buff, bp);
      return;
    case '#':
      /* enactor dbref */
      safe_str(unparse_dbref(enactor), buff, bp);
      return;
    case '@':
      /* caller dbref */
      safe_str(unparse_dbref(caller), buff, bp);
      return;
    case '!':
      /* executor dbref */
      safe_str(unparse_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_str(unparse_dbref(Location(enactor)), buff, bp);
      return;
    case 'c':
    case 'C':
      safe_str(ccom, buff, bp);
      return;
    }
  }
  safe_str(do_get_attrib(executor, executor, args[0]), buff, bp);
}

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

  if (thing == NOTHING)
    safe_str("#-1", buff, bp);
  else
    safe_str(unparse_flags(thing, executor), buff, bp);
}

/* ARGSUSED */
FUNCTION(fun_num)
{
  safe_str(unparse_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("#-1 NO MATCH", buff, bp);
      break;
    case AMBIGUOUS:
      safe_str("#-1 AMBIGUOUS MATCH", buff, bp);
      break;
    default:
      safe_str(unparse_dbref(thing), buff, bp);
    }
  } else
    safe_str("#-1", buff, bp);
}

/*
 * fun_lcon, fun_lexits, fun_con, fun_exit, fun_next, and next_exit were all
 * re-coded by d'mike, 7/12/91.  next_con was added at this time as well.
 *
 * 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.
 *
 * You can get the contents of any room you control, regardless of whether
 * or not the object is dark. You can get the contents of your current
 * location, _except_ for dark objects (and DARK/OPAQUE rooms). You CANNOT 
 * get the contents of anything else, regardless of whether or not you have 
 * objects in it. This latter behavior is exhibited by 2.0.
 *
 * The same behavior is true for exits, except OPAQUE doesn't apply.
 */

/* ARGSUSED */
FUNCTION(fun_lcon)
{
  dbref thing;
  int first;
  dbref it = match_thing(executor, args[0]);

  if ((it != NOTHING) &&
      (Can_Examine(executor, it) || (Location(executor) == it) ||
       (enactor == it))) {
    first = 1;
    DOLIST_VISIBLE(thing, Contents(it), executor) {
      if (first)
	first = 0;
      else
	safe_chr(' ', buff, bp);
      safe_str(unparse_dbref(thing), buff, bp);
    }
  } else
    safe_str("#-1", buff, bp);
}

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

  if ((it != NOTHING) &&
      (Can_Examine(executor, it) || (where_is(executor) == it) ||
       (enactor == it))) {
    it = first_visible(executor, it);
    if (it == NOTHING)
      safe_str("#-1", buff, bp);
    else
      safe_str(unparse_dbref(Contents(it)), buff, bp);
  } else {
    safe_str("#-1", buff, bp);
  }
}

/* return next contents that is ok to see */
dbref
next_con(player, this)
    dbref player;
    dbref this;
{
  dbref loc;
  int sees_loc;

  if ((this == NOTHING) || ((loc = Location(this)) == NOTHING))
    return NOTHING;
  sees_loc = Can_Examine(player, loc);

  if (sees_loc || ((Location(player) == loc) && !Dark(loc) && !Opaque(loc))) {
    this = first_visible(player, this);
  }
  return (this);
}

/* return next exit that is ok to see */
dbref
next_exit(player, this)
    dbref player;
    dbref this;
{
  dbref loc;
  int sees_loc;

  if ((this == NOTHING) || ((loc = Home(this)) == NOTHING))
    return NOTHING;
  sees_loc = Can_Examine(player, loc);

  if (sees_loc || ((Location(player) == loc) && !Dark(loc))) {
    this = first_visible(player, this);
  }
  return (this);
}

/* ARGSUSED */
FUNCTION(fun_lexits)
{
  dbref it = match_thing(executor, args[0]);
  dbref thing;
  int first;
  int sees_loc;

  sees_loc = Can_Examine(executor, it);

  first = 1;
  if (GoodObject(it) && (Typeof(it) == TYPE_ROOM)) {
    if (sees_loc || (Location(executor) == it)) {
      DOLIST_VISIBLE(thing, Exits(it), executor) {
	if (first)
	  first = 0;
	else
	  safe_chr(' ', buff, bp);
	safe_str(unparse_dbref(thing), buff, bp);
      }
    }
  } else
    safe_str("#-1", buff, bp);
}

/* fun_exit is really just a wrapper for next_exit now... */
/* ARGSUSED */
FUNCTION(fun_exit)
{
  dbref it = match_thing(executor, args[0]);
  if (GoodObject(it) && (Typeof(it) == TYPE_ROOM))
    safe_str(unparse_dbref(next_exit(executor, Exits(it))), buff, bp);
  else
    safe_str("#-1", buff, bp);
  return;
}

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

  if (it != NOTHING)
    safe_str(unparse_dbref(next_exit(executor, Next(it))), buff, bp);
  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 (options.daytime) {
    notify(executor, "Function disabled.");
    safe_str("#-1", buff, bp);
    return;
  }
  if (!payfor(executor, FIND_COST)) {
    notify(executor, tprintf("You don't have %d %s to do that.",
			     FIND_COST,
			     ((FIND_COST == 1) ? MONEY : MONIES)));
    safe_str("#-1", buff, bp);
    return;
  }
  if (nargs > 0)
    where = match_result(executor, args[0], NOTYPE, MAT_EVERYTHING);
  if (!GoodObject(where)) {
    safe_str("#-1 INVALID LOCATION", buff, bp);
    return;
  }
  exd = td = pd = rd = 0;
  if (nargs > 1) {
    if (!args[1] || !*args[1]) {
      safe_str("#-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("#-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(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(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("#-1 PERMISSION DENIED.", 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 && (Typeof(counter) == TYPE_EXIT)) ||
	  (td && (Typeof(counter) == TYPE_THING)) ||
	  (pd && (Typeof(counter) == TYPE_PLAYER)) ||
	  (rd && (Typeof(counter) == TYPE_ROOM))) {
	if (Typeof(counter) == TYPE_PLAYER || Typeof(counter) == TYPE_THING)
	  entrance = Home(counter);
	else
	  entrance = Location(counter);
	if (entrance == where) {
	  if (!found)
	    found = 1;
	  else
	    safe_chr(' ', buff, bp);
	  safe_str(unparse_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)
      && !nearby(executor, obj1) && !nearby(executor, obj2)) {
    safe_str("#-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 (it == NOTHING)
    safe_str("#-1 ARG1 NOT FOUND", buff, bp);
  else if (thing == NOTHING)
    safe_str("#-1 ARG2 NOT FOUND", buff, bp);
  else if (!(controls(executor, it) || See_All(executor)))
    safe_str("#-1 PERMISSION DENIED", 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, thing;
  char *name;
  ATTR *a;

  if ((it = match_thing(executor, args[0])) == NOTHING) {
    safe_chr('0', buff, bp);
    return;
  }
  if ((name = strchr(args[1], '/')))
    *name++ = '\0';
  if ((thing = match_thing(executor, args[1])) == NOTHING) {
    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_chr(Can_Examine(it, thing) ? '1' : '0', buff, bp);
  }
}

/* ARGSUSED */
FUNCTION(fun_type)
{
  dbref it = match_thing(executor, args[0]);
  if (it == NOTHING) {
    safe_str("#-1", 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;
  default:
    safe_str("WEIRD OBJECT", buff, bp);
    do_rawlog(LT_ERR, "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_chr((attrib->flags & f) ? '1' : '0', buff, bp);
  } else {
    thing = match_thing(executor, args[0]);
    if (thing == NOTHING)
      safe_str("#-1", buff, bp);
    else
      safe_chr(sees_flag(executor, thing, args[1]) ? '1' : '0', buff, bp);
  }
}

/* ARGSUSED */
FUNCTION(fun_hastype)
{
  dbref it = match_thing(executor, args[0]);
  if (it == NOTHING) {
    safe_str("#-1", buff, bp);
    return;
  }
  switch (*args[1]) {
  case 'r':
  case 'R':
    safe_chr((Typeof(it) == TYPE_ROOM) ? '1' : '0', buff, bp);
    break;
  case 'e':
  case 'E':
    safe_chr((Typeof(it) == TYPE_EXIT) ? '1' : '0', buff, bp);
    break;
  case 'p':
  case 'P':
    safe_chr((Typeof(it) == TYPE_PLAYER) ? '1' : '0', buff, bp);
    break;
  case 't':
  case 'T':
    safe_chr((Typeof(it) == TYPE_THING) ? '1' : '0', buff, bp);
    break;
  default:
    safe_str("#-1 NO SUCH TYPE", buff, bp);
    break;
  };
}

/* ARGSUSED */
FUNCTION(fun_orflags)
{
  safe_chr(handle_flaglists(executor, args[0], args[1], 0) ? '1' : '0',
	   buff, bp);
}

/* ARGSUSED */
FUNCTION(fun_andflags)
{
  safe_chr(handle_flaglists(executor, args[0], args[1], 1) ? '1' : '0',
	   buff, bp);
}

static lock_type
get_locktype(obj, str)
    dbref obj;
    char const *str;
{
  /* figure out a lock type */

  if (!str || !*str)
    return Basic_Lock;
  return str;
}

/* ARGSUSED */
FUNCTION(fun_lock)
{
  dbref it;
  char *p;
  lock_type ltype;

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

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

  if (nargs == 2) {
#ifdef FUNCTION_SIDE_EFFECTS
    do_lock(executor, args[0], args[1], ltype);
#else
    safe_str("#-1 FUNCTION DISABLED", buff, bp);
#endif
    return;
  }
  if ((it != NOTHING) && (ltype != NULL) && Can_Read_Lock(executor, it, ltype)) {
    safe_str(unparse_boolexp(executor, getlock(it, ltype), 1), 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 = (char *) strchr(args[0], '/');
  if (p)
    *p++ = '\0';

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

  if ((it == NOTHING) ||
      (ltype == NULL) ||
      !Can_Run_Lock(executor, it, ltype)) {
    safe_str("#-1", buff, bp);
    return;
  }
  safe_chr(eval_lock(victim, it, ltype) ? '1' : '0', buff, bp);
  return;
}

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

  if (obj == NOTHING)
    safe_str("#-1 ARG1 NOT FOUND", buff, bp);
  else if (victim == NOTHING)
    safe_str("#-1 ARG2 NOT FOUND", buff, bp);
  else
    safe_chr(Can_Locate(obj, victim) ? '1' : '0', buff, bp);
}

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

#ifdef CREATION_TIMES
/* ARGSUSED */
FUNCTION(fun_ctime)
{
  dbref it = match_thing(executor, args[0]);
  if ((it != NOTHING) && Can_Examine(executor, it)) {
    safe_str(ctime(&CreTime(it)), buff, bp);
    (*bp)--;
    return;
  }
  safe_str("#-1", buff, bp);
}

/* ARGSUSED */
FUNCTION(fun_mtime)
{
  dbref it = match_thing(executor, args[0]);
  if ((it != NOTHING) && Can_Examine(executor, it) &&
      (Typeof(it) != TYPE_PLAYER)) {
    safe_str(ctime(&ModTime(it)), buff, bp);
    (*bp)--;
    return;
  }
  safe_str("#-1", buff, bp);
}
#endif

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

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

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

  if ((it != NOTHING) && Can_Locate(executor, it)) {
    room = Location(it);
    if (!GoodObject(room)) {
      safe_str("#-1", buff, bp);
      return;
    }
    while (Typeof(room) != TYPE_ROOM) {
      room = Location(room);
      rec++;
      if (rec > 20) {
	notify(executor, "Too many containers.");
	safe_str("#-1", buff, bp);
	return;
      }
    }
    safe_str(unparse_dbref(room), buff, bp);
  } else {
    notify(executor, "Permission denied.");
    safe_str("#-1", 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 ((it != NOTHING) && Can_Locate(executor, it)) {
    for (i = 0; i < deep; i++) {
      if (!GoodObject(it) || (Typeof(it) == TYPE_ROOM))
	break;
      it = Location(it);
    }
    safe_str(unparse_dbref(it), buff, bp);
    return;
  }
  safe_str("#-1", buff, bp);
}

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

  if (nargs == 2) {
#ifdef FUNCTION_SIDE_EFFECTS
    do_chzone(executor, args[0], args[1]);
#else
    safe_str("#-1 FUNCTION DISABLED", buff, bp);
    return;
#endif
  }
  it = match_thing(executor, args[0]);
  if (it == NOTHING || !Can_Examine(executor, it)) {
    safe_str("#-1", buff, bp);
    return;
  }
  safe_str(unparse_dbref(Zone(it)), buff, bp);
}

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

  if (nargs == 2) {
#ifdef FUNCTION_SIDE_EFFECTS
    do_parent(executor, args[0], args[1]);
#else
    safe_str("#-1 FUNCTION DISABLED", buff, bp);
    return;
#endif
  }
  it = match_thing(executor, args[0]);
  if ((it == NOTHING) || !Can_Examine(executor, it)) {
    safe_str("#-1", buff, bp);
    return;
  }
  safe_str(unparse_dbref(Parent(it)), buff, bp);
}

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

  it = match_thing(executor, args[0]);
  if (!GoodObject(it)) {
    safe_str("#-1", buff, bp);
    return;
  }
  safe_str(unparse_dbref(it), buff, bp);
  par = Parent(it);
  while (GoodObject(par) && Can_Examine(executor, it)) {
    safe_chr(' ', buff, bp);
    safe_str(unparse_dbref(par), buff, bp);
    it = par;
    par = Parent(par);
  }
}

/* ARGSUSED */
FUNCTION(fun_home)
{
  dbref it = match_thing(executor, args[0]);
  if ((it == NOTHING) || !Can_Examine(executor, it) ||
      (Typeof(it) == TYPE_ROOM)) {
    safe_str("#-1", buff, bp);
    return;
  }
  safe_str(unparse_dbref(Exits(it)), buff, bp);
}

/* ARGSUSED */
FUNCTION(fun_money)
{
  dbref it = match_thing(executor, args[0]);
  if (it == NOTHING) {
    safe_str("#-1", buff, bp);
    return;
  }
  safe_str(unparse_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 (!attrib)
      safe_str("#-1", buff, bp);
    else
      safe_str(unparse_dbref(attrib->creator), buff, bp);
  } else {
    thing = match_thing(executor, args[0]);
    if (thing == NOTHING)
      safe_str("#-1", buff, bp);
    else
      safe_str(unparse_dbref(Owner(thing)), buff, bp);
  }
}

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

  if (nargs == 2) {
#ifdef FUNCTION_SIDE_EFFECTS
    do_name(executor, args[0], args[1]);
#else
    safe_str("#-1 FUNCTION DISABLED", buff, bp);
    return;
#endif
  }
  if ((it = match_thing(executor, args[0])) != NOTHING)
    safe_str(shortname(it), buff, bp);
}

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

  if ((it = match_thing(executor, args[0])) != NOTHING)
    safe_str(Name(it), 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_dbref(args[0]);
    if (!(GoodObject(target) && (Typeof(target) == TYPE_PLAYER))) {
      notify(executor, "No match.");
      safe_str("#-1", buff, bp);
      return;
    } else {
      safe_str(unparse_dbref(target), buff, bp);
      return;
    }
  } else
    target = lookup_player(args[0]);
  if (target == NOTHING) {
    if (*args[0] == '*')
      target = short_page(args[0] + 1);
    else
      target = short_page(args[0]);
    if (hidden(target) && !Priv_Who(executor))
      target = NOTHING;
  }
  switch (target) {
  case NOTHING:
    notify(executor, "No match.");
    safe_str("#-1", buff, bp);
    break;
  case AMBIGUOUS:
    notify(executor, "I'm not sure who you mean.");
    safe_str("#-2", buff, bp);
    break;
  default:
    safe_str(unparse_dbref(target), buff, bp);
  }
}

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

  /* find out what we're matching in relation to */
  looker = match_thing(executor, args[0]);
  if (looker == NOTHING) {
    notify(executor, "I don't see that here.");
    safe_str("#-1", buff, bp);
    return;
  }
  if (!See_All(executor) && !controls(executor, looker)) {
    notify(executor, "Permission denied.");
    safe_str("#-1", buff, bp);
    return;
  }
  /* find out our preferred match type */
  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;
    }
  }

  if (keys)
    match_flags = MAT_CHECK_KEYS;

  /* now figure out where what kinds of matches we want done */
  for (p = args[2]; *p && !done; p++) {
    switch (*p) {
    case '*':
      match_flags |= MAT_EVERYTHING;
      done = 1;
      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 'p':
      match_flags |= MAT_PLAYER;
      break;
    case 'N':
    case 'E':
    case 'P':
    case 'R':
    case 'T':
    case 'L':
      /* these are from previous type switch check. ignore. */
      break;
    case 'X':
      ambig_ok = 1;		/* okay to pick last match */
    default:
      notify(executor, tprintf("I don't understand switch '%c'.", *p));
      break;
    }
  }

  /* 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 (item == NOTHING)
    notify(executor, "Nothing found.");
  else if (item == AMBIGUOUS)
    notify(executor, "More than one match found.");
  safe_str(tprintf("#%d", item), buff, bp);
}

#ifdef FUNCTION_SIDE_EFFECTS

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

/* ARGSUSED */
FUNCTION(fun_create)
{
  int cost;
  if (nargs == 2)
    cost = parse_integer(args[1]);
  else
    cost = 10;
  safe_str(unparse_dbref(do_create(executor, args[0], cost)), buff, bp);
}

/* ARGSUSED */
FUNCTION(fun_open)
{
  safe_str(unparse_dbref(do_real_open(executor, args[0], args[1], NOTHING)),
	   buff, bp);
}

/* ARGSUSED */
FUNCTION(fun_dig)
{
  safe_str(unparse_dbref(do_dig(executor, args[0], args, 0)), buff, bp);
}

/* ARGSUSED */
FUNCTION(fun_clone)
{
  safe_str(unparse_dbref(do_clone(executor, args[0])), buff, bp);
}


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

/* ARGSUSED */
FUNCTION(fun_link)
{
  do_link(executor, args[0], args[1]);
}

/* ARGSUSED */
FUNCTION(fun_set)
{
  do_set(executor, args[0], args[1]);
}

/* ARGSUSED */
FUNCTION(fun_wipe)
{
  do_wipe(executor, args[0]);
}


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

/* ARGSUSED */
FUNCTION(fun_tel)
{
  do_teleport(executor, args[0], args[1]);
}


#else

/* ARGSUSED */
FUNCTION(fun_create)
{
  safe_str("#-1 FUNCTION DISABLED", buff, bp);
}
/* ARGSUSED */
FUNCTION(fun_open)
{
  safe_str("#-1 FUNCTION DISABLED", buff, bp);
}
/* ARGSUSED */
FUNCTION(fun_dig)
{
  safe_str("#-1 FUNCTION DISABLED", buff, bp);
}
/* ARGSUSED */
FUNCTION(fun_clone)
{
  safe_str("#-1 FUNCTION DISABLED", buff, bp);
}
/* ARGSUSED */
FUNCTION(fun_link)
{
  safe_str("#-1 FUNCTION DISABLED", buff, bp);
}
/* ARGSUSED */
FUNCTION(fun_set)
{
  safe_str("#-1 FUNCTION DISABLED", buff, bp);
}
/* ARGSUSED */
FUNCTION(fun_wipe)
{
  safe_str("#-1 FUNCTION DISABLED", buff, bp);
}
/* ARGSUSED */
FUNCTION(fun_tel)
{
  safe_str("#-1 FUNCTION DISABLED", buff, bp);
}

#endif				/* FUNCTION_SIDE_EFFECTS */

/* ARGSUSED */
FUNCTION(fun_isdbref)
{
  safe_chr((parse_dbref(args[0]) != NOTHING) ? '1' : '0', buff, bp);
}

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

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

/* Get database size statistics */
/* ARGSUSED */
FUNCTION(fun_lstats)
{
  dbref who;
  char tbuf1[BUFFER_LEN];
  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("#-1 NOT FOUND", buff, bp);
      return;
    }
  }
  if (!Search_All(executor)) {
    if (who != ANY_OWNER && who != executor) {
      safe_str("#-1 PERMISSION DENIED", buff, bp);
      return;
    }
  }
  si = get_stats(executor, who);
  if (who != ANY_OWNER) {
    sprintf(tbuf1, "%d %d %d %d %d", si->total - si->garbage, si->rooms, si->exits, si->things, si->players);
  } else {
    sprintf(tbuf1, "%d %d %d %d %d %d", si->total, si->rooms, si->exits, si->things,
	    si->players, si->garbage);
  }
  safe_str(tbuf1, buff, bp);
}


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

#ifdef FUNCTION_SIDE_EFFECTS
  if (nargs == 1)
    status = 0;
  else {
    if (!strcasecmp(args[1], "on")) {
      status = 1;
    } else if (!strcasecmp(args[1], "off")) {
      status = 2;
    } else
      status = 0;
  }
#else
  status = 0;
#endif

  if (!args[0] || !*args[0]) {
    safe_str("#-1 ARGUMENT MUST BE OBJ/ATTR", buff, bp);
    return;
  }
  if (!(p = (char *) index(args[0], '/')) || !(*(p + 1))) {
    safe_str("#-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("#-1", buff, bp);
    return;
  }
  for (ptr = db[thing].list; ptr; ptr = AL_NEXT(ptr))
    if (!strcasecmp(AL_NAME(ptr), p)
#ifndef VISIBLE_EMPTY_ATTRS
	&& *AL_STR(ptr)
#endif
      )
      break;

  if (!ptr) {
    ptr = aname_hash_lookup(strupper(p));
    if (!ptr || !strcmp(p, AL_NAME(ptr)))
      ptr = NULL;
    else {
      p = (char *) AL_NAME(ptr);
      for (ptr = db[thing].list; ptr; ptr = AL_NEXT(ptr))
	if (!strcmp(AL_NAME(ptr), p)
#ifndef VISIBLE_EMPTY_ATTRS
	    && *AL_STR(ptr)
#endif
	  )
	  break;
    }
  }
  if (ptr) {
    switch (status) {
    case 0:
      safe_str(unparse_integer(!!(AL_FLAGS(ptr) & AF_LOCKED)), buff, bp);
      return;
    case 1:
      if (!Wizard(executor) &&
	  (Owner(AL_CREATOR(ptr)) != Owner(executor))) {
	safe_str("#-1 PERMISSION DENIED", buff, bp);
	return;
      }
      AL_FLAGS(ptr) |= AF_LOCKED;
      return;
    case 2:
      if (!Wizard(executor) &&
	  (Owner(AL_CREATOR(ptr)) != Owner(executor))) {
	safe_str("#-1 PERMISSION DENIED", buff, bp);
	return;
      }
      AL_FLAGS(ptr) &= ~AF_LOCKED;
      return;
    default:
      safe_str("#-1", buff, bp);
      return;
    }
  } else
    safe_str("#-1 NO SUCH ATTRIBUTE", buff, bp);
  return;
}

#ifdef CHAT_SYSTEM
FUNCTION(fun_channels)
{
  dbref it;

  if (nargs == 1) {
    CHANLIST *c;
    /* Given an argument, return list of channels it's on */
    it = match_thing(executor, args[0]);
    if ((it == NOTHING) || !Can_Examine(executor, it)) {
      safe_str("#-1", buff, bp);
      return;
    }
    for (c = Chanlist(it); c; c = c->next) {
      if (c != Chanlist(it))
	safe_chr(' ', buff, bp);
      safe_str(ChanName(c->chan), buff, bp);
    }
  } else {
    CHAN *c;
    /* No arguments - return list of all channels */
    for (c = channels; c; c = c->next) {
      if (Chan_Can_See(c, executor)) {
	if (c != channels)
	  safe_chr(' ', buff, bp);
	safe_str(ChanName(c), buff, bp);
      }
    }
  }

  return;
}
#endif