pennmush/game/
pennmush/game/data/
pennmush/game/log/
pennmush/game/save/
pennmush/game/txt/evt/
pennmush/game/txt/nws/
pennmush/os2/
/* predicat.c */

#include "copyrite.h"
#include "config.h"

/* Predicates for testing various conditions */

#include <stdio.h>
#ifdef I_STDARG
#include <stdarg.h>
#else
#include <varargs.h>
#endif
#include <ctype.h>
#ifdef I_STRING
#include <string.h>
#else
#include <strings.h>
#endif
#ifdef I_SYS_TYPES
#include <sys/types.h>
#endif
#ifdef I_SYS_TIME
#include <sys/time.h>
#else
#include <time.h>
#endif
#ifdef I_STDLIB
#include <stdlib.h>
#endif

#include "conf.h"
#include "externs.h"
#include "mushdb.h"
#include "intrface.h"
#include "globals.h"
#include "match.h"
#include "ansi.h"
#include "parse.h"
#include "dbdefs.h"
#ifdef MEM_CHECK
#include "memcheck.h"
#endif
#include "mymalloc.h"
#include "confmagic.h"

extern int first_free;		/* free object list, from destroy.c */

extern int quick_wild _((const char *tstr, const char *dstr));

int could_doit _((dbref player, dbref thing));
void charge_action _((dbref player, dbref thing, const char *awhat));
void did_it _((dbref player, dbref thing, const char *what, const char *def, const char *owhat, const char *odef, const char *awhat, dbref loc));
int can_see _((dbref player, dbref thing, int can_see_loc));
int controls _((dbref who, dbref what));
int can_pay_fees _((dbref who, int pennies));
void giveto _((dbref who, dbref pennies));
int payfor _((dbref who, int cost));
int forbidden_name _((const char *name));
int ok_name _((const char *name));
int ok_player_name _((const char *name));
int ok_password _((const char *password));
void sstrcat _((char *string, char *app));
void do_switch _((dbref player, char *expression, char **argv, dbref cause, int first));
dbref parse_match_possessive _((dbref player, const char *str));
void page_return _((dbref player, dbref target, const char *type, const char *message, const char *def));
dbref where_is _((dbref thing));
int nearby _((dbref obj1, dbref obj2));
void do_verb _((dbref player, dbref cause, char *arg1, char **argv));
char *grep_util _((dbref player, dbref thing, char *pattern, char *lookfor, int len, int insensitive));
static int grep_util_helper _((dbref player, dbref thing, char const *pattern,
			       ATTR *atr, void *args));
static int grep_helper _((dbref player, dbref thing, char const *pattern,
			  ATTR *atr, void *args));
void do_grep _((dbref player, char *obj, char *lookfor, int flag, int insensitive));
char *tprintf _((const char *fmt,...));

#ifdef I_STDARG
char *
tprintf(const char *fmt,...)
#else
char *
tprintf(va_alist)
    va_dcl
#endif
{
  /* this is a generic function used to generate a format string */

  static char buff[BUFFER_LEN * 3];	/* safety margin */
  va_list args;
#ifndef I_STDARG
  char *fmt;

  va_start(args);
  fmt = va_arg(args, char *);
#else
  va_start(args, fmt);
#endif

  (void) vsprintf(buff, fmt, args);
  buff[BUFFER_LEN - 1] = '\0';
  va_end(args);
  return (buff);
}

int
could_doit(player, thing)
    dbref player;
    dbref thing;
{
  /* lock evaluation -- determines if player passes lock on thing, for
   * the purposes of picking up an object or moving through an exit
   */

  if (Typeof(thing) != TYPE_ROOM && Location(thing) == NOTHING)
    return 0;
  return (eval_lock(player, thing, Basic_Lock));
}

void
charge_action(player, thing, awhat)
    dbref player;
    dbref thing;
    const char *awhat;
{
  ATTR *d;
  ATTR *b;
  char tbuf[BUFFER_LEN];
  char tbuf2[BUFFER_LEN];
  int num;

  if (awhat && *awhat && (d = atr_get(thing, awhat))) {
    strcpy(tbuf, uncompress(d->value));

    /* check if object has # of charges */
    b = atr_get_noparent(thing, "CHARGES");

    if (!b) {
      /* no charges set, just execute the action */
      parse_que(thing, tbuf, player);
      return;
    } else {
      strcpy(tbuf2, uncompress(b->value));
      num = atoi(tbuf2);
      if (num) {
	/* charges left, decrement and execute */
	(void) atr_add(thing, "CHARGES", tprintf("%d", num - 1),
		       db[b->creator].owner, NOTHING);
	parse_que(thing, tbuf, player);
	return;
      } else if (!(d = atr_get(thing, "RUNOUT")))
	/* no charges left and no runout; do nothing */
	return;
      /* no charges left, execute runout */
      strcpy(tbuf, uncompress(d->value));
      parse_que(thing, tbuf, player);
    }
  }
}

void
did_it(player, thing, what, def, owhat, odef, awhat, loc)
    dbref player;
    dbref thing;
    const char *what;
    const char *def;
    const char *owhat;
    const char *odef;
    const char *awhat;
    dbref loc;
{
  /* executes the @attr, @oattr, @aattr for a command - gives a message
   * to the enactor and others in the room with the enactor, and executes
   * an action.
   */

  ATTR *d;
  char buff[BUFFER_LEN], *bp, *sp;
  char const *asave, *ap;
  int j;
  char *preserve[10];
  int need_pres = 0;

  loc = (loc == NOTHING) ? db[player].location : loc;

  /* only give messages if the location is good */
  if (GoodObject(loc)) {

    /* message to player */
    if (what && *what) {
      d = atr_get(thing, what);
      if (d) {
	asave = safe_uncompress(d->value);
	ap = asave;
	bp = buff;
	process_expression(buff, &bp, &ap, thing, player, player,
			   PE_DEFAULT, PT_DEFAULT, NULL);
	*bp = '\0';
 	notify_by(thing,player,buff);
	free((Malloc_t) asave);
      } else if (def && *def)
	notify_by(thing,player, def);
    }
    /* message to neighbors */
    if (!Dark(player)) {
      if (owhat && *owhat) {
	d = atr_get(thing, owhat);
	if (d) {
	  if (!need_pres) {
	    need_pres = 1;
	    save_global_regs("did_it_save", preserve);
	  }
	  asave = safe_uncompress(d->value);
	  ap = asave;
	  bp = buff;
	  safe_str(Name(player), buff, &bp);
	  safe_chr(' ', buff, &bp);
	  sp = bp;
	  process_expression(buff, &bp, &ap, thing, player, player,
			     PE_DEFAULT, PT_DEFAULT, NULL);
	  *bp = '\0';
	  if (bp != sp)
	    notify_except2(Contents(loc), player, thing, buff);
	  free((Malloc_t) asave);
	} else {
	  if (odef && *odef) {
	    notify_except2(db[loc].contents, player, thing,
			   tprintf("%s %s", Name(player), odef));
	  }
	}
      }
    }
  }
  if (need_pres)
    restore_global_regs("did_it_save", preserve);
  for (j = 0; j < 10; j++) {
    wnxt[j] = NULL;
    rnxt[j] = NULL;
  }
  charge_action(player, thing, awhat);
}

dbref
first_visible(player, thing)
    dbref player;
    dbref thing;
{
  int lck = 0;
  int ldark;
  dbref loc;

  if (!(GoodObject(thing)))
    return NOTHING;
  loc = (Typeof(thing) == TYPE_EXIT) ? Source(thing) : Location(thing);
  ldark = (Typeof(loc) == TYPE_PLAYER) ? (Flags(loc) & OPAQUE) : (Flags(loc) & DARK);

  while (GoodObject(thing)) {
    if (Dark(thing) || (ldark && !Light(thing))) {
      if (!lck) {
	if (See_All(player) || (loc == player) || controls(player, loc))
	  return thing;
	lck = 1;
      }
      if (controls(player, thing))
	return thing;
    } else {
      return thing;
    }
    thing = Next(thing);
  }
  return thing;
}




int
can_see(player, thing, can_see_loc)
    dbref player;
    dbref thing;
    int can_see_loc;
{
  /*
   * 1) your own body isn't listed in a 'look' 2) exits aren't listed in a
   * 'look' 3) unconnected (sleeping) players aren't listed in a 'look'
   */
  if (player == thing ||
      Typeof(thing) == TYPE_EXIT ||
      ((Typeof(thing) == TYPE_PLAYER) &&
       !IS(thing, TYPE_PLAYER, PLAYER_CONNECT)))
    return 0;

  /* if the room is lit, you can see any non-dark objects */
  else if (can_see_loc)
    return (!Dark(thing));

  /* otherwise room is dark and you can only see lit things */
  else
    return (Light(thing) && !Dark(thing));
}

int
controls(who, what)
    dbref who;
    dbref what;
{
  /* Wizard controls everything 
   * owners control their stuff
   * something which is in the enterlock of a ZMO controls non-INHERIT
   * and non-player objects.
   * INHERIT checks between two objects are checked in the code for the
   * specific function in question (do_trigger, do_set, etc.)
   * Those who pass the enterlock of a ZoneMaster control his objects,
   * but not the ZoneMaster himself.
   */

  if (!GoodObject(what))
    return 0;

  if (God(what) && !God(who))
    return 0;

  if (Wizard(who))
    return 1;

  if (Wizard(what) || (Hasprivs(what) && !Hasprivs(who)))
    return 0;

  if (Owns(who, what) && (!Inherit(what) || Inherit(who)))
    return 1;

  if ((Zone(what) != NOTHING) &&
      (Typeof(what) != TYPE_PLAYER) &&
      !Inherit(what) &&
      (eval_lock(who, Zone(what), Zone_Lock)))
    return 1;

  if (ZMaster(Owner(what)) && (Typeof(what) != TYPE_PLAYER) &&
      (eval_lock(who, Owner(what), Zone_Lock)))
    return 1;

  return 0;
}

int
can_pay_fees(who, pennies)
    dbref who;
    int pennies;
{
  /* does who have enough pennies to pay for something, and if something
   * is being built, does who have enough quota? Wizards, roys
   * aren't subject to either.
   */

#ifdef QUOTA
  int pay_quota();
#endif				/* QUOTA */

  /* check database size -- EVERYONE is subject to this! */
  if (DBTOP_MAX && (db_top >= DBTOP_MAX + 1) && (first_free == NOTHING)) {
    notify(who, "Sorry, there is no more room in the database.");
    return 0;
  }
  /* Can they afford it? */
  if (!NoPay(who) && (Pennies(Owner(who)) < pennies)) {
    notify(who, tprintf("Sorry, you don't have enough %s.", MONIES));
    return 0;
  }
  /* check building quota */
#ifdef QUOTA
  if (!NoQuota(who) && !pay_quota(who, QUOTA_COST)) {
    notify(who, "Sorry, your building quota has run out.");
    return 0;
  }
#endif				/* QUOTA */

  /* charge */
  payfor(who, pennies);

  return 1;
}

void
giveto(who, pennies)
    dbref who;
    dbref pennies;
{
  /* give who pennies */

  /* wizards and royalty don't need pennies */
  if (NoPay(who))
    return;

  who = Owner(who);
  s_Pennies(who, Pennies(who) + pennies);
}

int
payfor(who, cost)
    dbref who;
    int cost;
{
  /* subtract cost from who's pennies */

  dbref tmp;
  if (NoPay(who))
    return 1;
  else if ((tmp = Pennies(Owner(who))) >= cost) {
    s_Pennies(Owner(who), tmp - cost);
    return 1;
  } else
    return 0;
}

#ifdef QUOTA

int
get_current_quota(who)
    dbref who;
{
  /* figure out a player's quota. Add the RQUOTA attribute if he doesn't
   * have one already. This function returns the REMAINING quota, not
   * the TOTAL limit.
   */

  ATTR *a;
  int i;
  int limit;
  int owned = 0;

  /* if he's got an RQUOTA attribute, his remaining quota is that */
  a = atr_get_noparent(Owner(who), "RQUOTA");
  if (a)
    return (atoi(uncompress(a->value)));

  /* else, count up his objects. If he has less than the START_QUOTA,
   * then his remaining quota is that minus his number of current objects.
   * Otherwise, it's his current number of objects. Add the attribute
   * if he doesn't have it.
   */

  for (i = 0; i < db_top; i++)
    if (Owner(i) == Owner(who))
      owned++;
  owned--;			/* don't count the player himself */

  if (owned <= START_QUOTA)
    limit = START_QUOTA - owned;
  else
    limit = owned;

  atr_add(Owner(who), "RQUOTA", tprintf("%d", limit), GOD, NOTHING);

  return (limit);
}


void
change_quota(who, payment)
    dbref who;
    int payment;
{
  /* add or subtract from quota */

  /* wizards and royalty don't need a quota */
  if (NoQuota(Owner(who)))
    return;

  atr_add(Owner(who), "RQUOTA",
	  tprintf("%d", get_current_quota(who) + payment),
	  GOD, NOTHING);
}

int
pay_quota(who, cost)
    dbref who;
    int cost;
{
  /* determine if we've got enough quota to pay for an object,
   * and, if so, return true, and subtract from the quota.
   */

  int curr;

  /* wizards and royalty don't need a quota */
  if (NoQuota(Owner(who)))
    return 1;

  /* figure out how much we have, and if it's big enough */
  curr = get_current_quota(who);

  if (curr - cost < 0)		/* not enough */
    return 0;

  change_quota(who, -cost);

  return 1;
}
#endif				/* QUOTA */

int
forbidden_name(name)
    const char *name;
{
  /* checks to see if name is in the forbidden names file */

  char buf[BUFFER_LEN], *newlin, *ptr;
  FILE *fp;

  fp = fopen(NAMES_FILE, "r");
  if (!fp)
    return 0;
  while (!feof(fp)) {
    fgets(buf, BUFFER_LEN, fp);
    /* step on the newline */
    if ((newlin = (char *) index(buf, '\n')) != NULL)
      *newlin = '\0';
    ptr = buf;
    if (name && ptr && quick_wild(ptr, name)) {
      fclose(fp);
      return 1;
    }
  }
  fclose(fp);
  return 0;
}

int
ok_name(name)
    const char *name;
{
  /* is name valid for an object? */

  const char *p;

  /* No leading spaces */
  if (isspace(*name))
    return 0;

  /* only printable characters */
  for (p = name; p && *p; p++) {
    if (!isprint(*p))
      return 0;
    if ((*p == '[') || (*p == ']') ||
	(*p == '%') || (*p == '\\'))
      return 0;
  }

  /* No trailing spaces */
  p--;
  if (isspace(*p))
    return 0;

  /* No magic cookies */

  return (name
	  && *name
	  && *name != LOOKUP_TOKEN
	  && *name != NUMBER_TOKEN
	  && *name != NOT_TOKEN
	  && !index(name, ARG_DELIMITER)
	  && !index(name, AND_TOKEN)
	  && !index(name, OR_TOKEN)
	  && strcasecmp(name, "me")
	  && strcasecmp(name, "home")
	  && strcasecmp(name, "here"));
}

int
ok_player_name(name)
    const char *name;
{
  /* is name okay for a player? */

  const char *scan, *good;

  if (!ok_name(name) || forbidden_name(name) ||
      strlen(name) >= PLAYER_NAME_LIMIT)
    return 0;

  good = PLAYER_NAME_SPACES ? " `$_-.,'" : "`$_-.,'";

  /* Make sure that the name contains legal characters only */
  for (scan = name; scan && *scan; scan++) {
    if (isalpha(*scan) || isdigit(*scan))
      continue;
    if (!index(good, *scan))
      return 0;
  }

  return (lookup_player(name) == NOTHING);
}

int
ok_password(password)
    const char *password;
{
  /* is password an acceptable password? */

  const char *scan;
  if (*password == '\0')
    return 0;

  for (scan = password; *scan; scan++) {
    if (!(isprint(*scan) && !isspace(*scan))) {
      return 0;
    }
  }

  return 1;
}

void
sstrcat(string, app)
    char *string;
    char *app;
{
  char *s;
  char tbuf1[BUFFER_LEN];

  if ((strlen(app) + strlen(string)) >= BUFFER_LEN)
    return;
  sprintf(tbuf1, "%s", app);
  for (s = tbuf1; *s; s++)
    if ((*s == ',') || (*s == ';'))
      *s = ' ';
  strcat(string, tbuf1);
}

/* for lack of better place the @switch code is here */
void
do_switch(player, expression, argv, cause, first)
    dbref player;
    char *expression;
    char **argv;
    dbref cause;
    int first;




				/* 0, match all, 1, match first */
{
  int any = 0, a;
  char buff[BUFFER_LEN], *bp;
  char const *ap;
  char *tbuf1;

  if (!argv[1])
    return;

  /* set up environment for any spawned commands */
  for (a = 0; a < 10; a++) {
    wnxt[a] = wenv[a];
    rnxt[a] = renv[a];
  }

  /* now try a wild card match of buff with stuff in coms */
  for (a = 1;
       !(first && any) && (a < (MAX_ARG - 1)) && argv[a] && argv[a + 1];
       a += 2) {
    /* eval expression */
    ap = argv[a];
    bp = buff;
    process_expression(buff, &bp, &ap, player, cause, cause,
		       PE_DEFAULT, PT_DEFAULT, NULL);
    *bp = '\0';

    /* check for a match */
    if (local_wild_match(buff, expression)) {
      any = 1;
      tbuf1 = replace_string("#$", expression, argv[a + 1]);
      parse_que(player, tbuf1, cause);
      free(tbuf1);
#ifdef MEM_CHECK
      del_check("replace_string.buff");
#endif
    }
  }

  /* do default if nothing has been matched */
  if ((a < MAX_ARG) && !any && argv[a]) {
    tbuf1 = replace_string("#$", expression, argv[a]);
    parse_que(player, tbuf1, cause);
    free(tbuf1);
#ifdef MEM_CHECK
    del_check("replace_string.buff");
#endif
  }
}

dbref
parse_match_possessive(player, str)
    dbref player;
    const char *str;
{
  char *box;			/* name of container */
  char *obj;			/* name of object */
  dbref loc;			/* dbref of container */
  char name[BUFFER_LEN];	/* str would be destructively modified */

  strcpy(name, str);
  box = name;

  /* check to see if we have an 's sequence */
  if ((obj = (char *) index(name, '\'')) == NULL)
    return NOTHING;
  *obj++ = '\0';		/* terminate */
  if ((*obj == '\0') || ((*obj != 's') && (*obj != 'S')))
    return NOTHING;

  /* skip over the 's' and whitespace */
  do {
    obj++;
  } while (isspace(*obj));

  /* we already have a terminating null, so we're okay to just do matches */
  loc = match_result(player, box, NOTYPE, MAT_NEIGHBOR | MAT_POSSESSION);
  if (!GoodObject(loc))
    return NOTHING;

  /* now we match on the contents */
  return (match_result(loc, obj, NOTYPE, MAT_POSSESSION));
}


void
page_return(player, target, type, message, def)
    dbref player;
    dbref target;
    const char *type;
    const char *message;
    const char *def;
{
  /* code for auto-return page - HAVEN, IDLE, and AWAY messages */

  ATTR *d;
  char buff[BUFFER_LEN], *bp;
  char const *asave, *ap;
  struct tm *ptr;
  time_t t;

  if (message && *message) {
    d = atr_get(target, message);
    if (d) {
      asave = safe_uncompress(d->value);
      ap = asave;
      bp = buff;
      process_expression(buff, &bp, &ap, target, player, player,
			 PE_DEFAULT, PT_DEFAULT, NULL);
      *bp = '\0';
      free((Malloc_t) asave);
      if (*buff) {
	t = time(NULL);
	ptr = (struct tm *) localtime(&t);
	notify(player, tprintf("%s message from %s: %s", type,
			       db[target].name, buff));
	if (!Haven(target))
	  notify(target,
		 tprintf("[%d:%02d] %s message sent to %s.",
			 MILITARY_TIME ? ptr->tm_hour :
			 (ptr->tm_hour ? (ptr->tm_hour > 12 ? (ptr->tm_hour - 12) : ptr->tm_hour) : 12),
			 ptr->tm_min, type, Name(player)));
      }
    } else if (def && *def)
      notify(player, def);
  }
}

dbref
where_is(thing)
    dbref thing;
{
  /* returns "real" location of object. This is the location for players
   * and things, source for exits, and NOTHING for rooms.
   */

  if (!GoodObject(thing))
    return NOTHING;
  switch (Typeof(thing)) {
  case TYPE_ROOM:
    return NOTHING;
  case TYPE_EXIT:
    return Home(thing);
  default:
    return Location(thing);
  }
}

int
nearby(obj1, obj2)
    dbref obj1;
    dbref obj2;
{
  /* returns 1 if obj1 is "nearby" object2. "Nearby" is defined as:  
   *   obj1 is in the same room as obj2, obj1 is being carried by   
   *   obj2, obj1 is carrying obj2. Returns 0 if object isn't nearby 
   *   or the input is invalid.
   */
  dbref loc1, loc2;

  if (!GoodObject(obj1) || !GoodObject(obj2))
    return 0;
  loc1 = where_is(obj1);
  if (loc1 == obj2)
    return 1;
  loc2 = where_is(obj2);
  if ((loc2 == obj1) || (loc2 == loc1))
    return 1;
  return 0;
}

void
do_verb(player, cause, arg1, argv)
    dbref player;
    dbref cause;
    char *arg1;
    char **argv;
{
  /* user-defined verbs */

  dbref victim;
  dbref actor;
  int i;

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

  /* our victim object can be anything */
  victim = match_result(player, arg1, NOTYPE, MAT_EVERYTHING);

  if (victim == NOTHING) {
    notify(player, "What was the victim of the verb?");
    return;
  }
  /* find the object that executes the action */

  if (!argv || !argv[1] || !*argv[1]) {
    notify(player, "What do you want to do with the verb?");
    return;
  }
  actor = match_result(player, argv[1], NOTYPE, MAT_NEAR_THINGS);

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

  if (!(Wizard(player) ||
	(controls(player, victim) && controls(player, actor)) ||
	((controls(cause, actor) && Can_Examine(player, victim))))) {
    notify(player, "Permission denied.");
    return;
  }
  /* We're okay.  Send out messages. */

  did_it(actor, victim,
	 upcasestr(argv[2]), argv[3], upcasestr(argv[4]), argv[5],
	 NULL, Location(actor));

  /* Now we copy our args into the stack, and do the command. */

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

  charge_action(actor, victim, upcasestr(argv[6]));
}

struct guh_args {
  char *buff;
  char *bp;
  char *lookfor;
  int len;
  int insensitive;
};

static int
grep_util_helper(player, thing, pattern, atr, args)
    dbref player;
    dbref thing;
    char const *pattern;
    ATTR *atr;
    void *args;
{
  struct guh_args *guh = args;
  int found;
  char *s;

  s = (char *) uncompress(AL_STR(atr));		/* warning: static */
  found = 0;
  while (*s && !found) {
    if ((!guh->insensitive && !strncmp(guh->lookfor, s, guh->len)) ||
	(guh->insensitive && !strncasecmp(guh->lookfor, s, guh->len)))
      found = 1;
    else
      s++;
  }
  if (found) {
    if (guh->bp != guh->buff)
      safe_chr(' ', guh->buff, &guh->bp);
    safe_str(AL_NAME(atr), guh->buff, &guh->bp);
  }
  return found;
}

char *
grep_util(player, thing, pattern, lookfor, len, insensitive)
    dbref player;
    dbref thing;
    char *pattern;
    char *lookfor;
    int len;
    int insensitive;
				/* strlen(lookfor) */
				/* 1 if case-insensitive grep, 0 if not */
{
  /* returns a list of attributes which match <pattern> on <thing>
   * whose contents have <lookfor>
   */
  struct guh_args guh;

  guh.buff = (char *) malloc(BUFFER_LEN + 1);
  guh.bp = guh.buff;
  guh.lookfor = lookfor;
  guh.len = len;
  guh.insensitive = insensitive;
  (void) atr_iter_get(player, thing, pattern, grep_util_helper, &guh);
  *guh.bp = '\0';
  return guh.buff;
}

struct gh_args {
  char *lookfor;
  int len;
  int insensitive;
  char repl[BUFFER_LEN];
  int rlen;
};

static int
grep_helper(player, thing, pattern, atr, args)
    dbref player;
    dbref thing;
    char const *pattern;
    ATTR *atr;
    void *args;
{
  struct gh_args *gh = args;
  int found;
  int d;
  char *s;
  char tbuf1[BUFFER_LEN];

  found = 0;
  s = (char *) uncompress(AL_STR(atr));		/* warning: static */
  for (d = 0; (d < BUFFER_LEN) && *s;) {
    if ((gh->insensitive && !strncasecmp(gh->lookfor, s, gh->len)) ||
	(!gh->insensitive && !strncmp(gh->lookfor, s, gh->len))) {
      found = 1;
      if ((d + gh->rlen) < BUFFER_LEN) {
	strcpy(tbuf1 + d, gh->repl);
	d += gh->rlen;
	s += gh->len;
      } else
	tbuf1[d++] = *s++;
    } else
      tbuf1[d++] = *s++;
  }
  tbuf1[d++] = 0;
  /* if we got it, display it */
  if (found)
    notify(player, tprintf("%s%s [#%d%s%s %s",
		       ANSI_HILITE, AL_NAME(atr), Owner(AL_CREATOR(atr)),
			   (AL_FLAGS(atr) & AF_LOCKED) ? "+]:" : "]:",
			   ANSI_NORMAL, tbuf1));
  return found;
}

void
do_grep(player, obj, lookfor, flag, insensitive)
    dbref player;
    char *obj;
    char *lookfor;
    int flag;
    int insensitive;


				/* this is an UNPARSED arg */
				/* 0 for just list, 1 for hilites */
				/* 0 for case-sensitive, 1 if not */
{
  struct gh_args gh;
  dbref thing;
  char *pattern;
  int len;
  char *tp;

  /* check this first */
  if (flag && !ShowAnsi(player)) {
    notify(player, "You must be set ANSI to use this option.");
    return;
  }
  if ((len = strlen(lookfor)) < 1) {
    notify(player, "What pattern do you want to grep for?");
    return;
  }
  /* find the attribute pattern */
  pattern = (char *) index(obj, '/');
  if (!pattern)
    pattern = (char *) "*";	/* set it to global match */
  else
    *pattern++ = '\0';

  /* now we've got the object. match for it. */
  if ((thing = noisy_match_result(player, obj, NOTYPE, MAT_EVERYTHING)) == NOTHING)
    return;
  if (!Can_Examine(player, thing)) {
    notify(player, "Permission denied.");
    return;
  }
  /* we can think of adding in the hilites like doing a find and replace */
  sprintf(gh.repl, "%s%s%s", ANSI_HILITE, lookfor, ANSI_NORMAL);
  gh.rlen = strlen(gh.repl);

  if (flag) {
    gh.lookfor = lookfor;
    gh.len = len;
    gh.insensitive = insensitive;
    (void) atr_iter_get(player, thing, pattern, grep_helper, &gh);
  } else {
    tp = grep_util(player, thing, pattern, lookfor, len, insensitive);
    notify(player, tprintf("Matches of '%s' on %s(#%d): %s", lookfor,
			   Name(thing), thing, tp));
    free(tp);
  }
}