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 speech.c
 *
 * \brief Speech-related commands in PennMUSH.
 *
 *
 */
/* speech.c */

#include "copyrite.h"
#include "config.h"
#include <ctype.h>
#include <string.h>
#include <stdlib.h>
#include "conf.h"
#include "ansi.h"
#include "externs.h"
#include "mushdb.h"
#include "dbdefs.h"
#include "lock.h"
#include "flags.h"
#include "log.h"
#include "match.h"
#include "attrib.h"
#include "parse.h"
#include "game.h"
#include "pcre.h"
#include "confmagic.h"

int okay_pemit(dbref player, dbref target);
static dbref speech_loc(dbref thing);
void propagate_sound(dbref thing, const char *msg);
static void do_audible_stuff(dbref loc, dbref *excs, int numexcs,
			     const char *msg);
static void do_one_remit(dbref player, const char *target, const char *msg,
			 int flags);
dbref na_zemit(dbref current, void *data);

const char *
spname(dbref thing)
{
  /* if FULL_INVIS is defined, dark wizards and dark objects will be
   * Someone and Something, respectively.
   */

  if (FULL_INVIS && DarkLegal(thing)) {
    if (IsPlayer(thing))
      return "Someone";
    else
      return "Something";
  } else {
    return accented_name(thing);
  }
}

/** Can player pemit to target?
 * You can pemit if you're pemit_all, if you're pemitting to yourself,
 * if you're pemitting to a non-player, or if you pass target's
 * pagelock and target isn't HAVEN.
 * \param player dbref attempting to pemit.
 * \param target target dbref to pemit to.
 * \retval 1 player may pemit to target.
 * \retval 0 player may not pemit to target.
 */
int
okay_pemit(dbref player, dbref target)
{
  if (Pemit_All(player))
    return 1;
  if (IsPlayer(target) && Haven(target))
    return 0;
  if (!eval_lock(player, target, Page_Lock)) {
    fail_lock(player, target, Page_Lock, NULL, NOTHING);
    return 0;
  }
  return 1;
}

static dbref
speech_loc(dbref thing)
{
  /* This is the place where speech, poses, and @emits by thing should be
   * heard. For things and players, it's the loc; For rooms, it's the room
   * itself; for exits, it's the source. */
  if (!GoodObject(thing))
    return NOTHING;
  switch (Typeof(thing)) {
  case TYPE_ROOM:
    return thing;
  case TYPE_EXIT:
    return Home(thing);
  default:
    return Location(thing);
  }
}

/** The teach command.
 * \param player the enactor.
 * \param cause the object causing the command to run.
 * \param tbuf1 the command being taught.
 */
void
do_teach(dbref player, dbref cause, const char *tbuf1)
{
  dbref loc;
  static int recurse = 0;
  char *command;

  loc = speech_loc(player);
  if (!GoodObject(loc))
    return;

  if (!eval_lock(player, loc, Speech_Lock)) {
    fail_lock(player, loc, Speech_Lock, T("You may not speak here!"), NOTHING);
    return;
  }

  if (recurse) {
    /* Somebody tried to teach the teach command. Cute. Dumb. */
    notify(player, T("You can't teach 'teach', sorry."));
    recurse = 0;
    return;
  }

  if (!tbuf1 || !*tbuf1) {
    notify(player, T("What command do you want to teach?"));
    return;
  }

  recurse = 1;			/* Protect use from recursive teach */
  notify_except(Contents(loc), NOTHING,
		tprintf(T("%s types --> %s%s%s"), spname(player),
			ANSI_HILITE, tbuf1, ANSI_NORMAL), NA_INTER_HEAR);
  command = mush_strdup(tbuf1, "string");	/* process_command is destructive */
  process_command(player, command, cause, 1);
  mush_free(command, "string");
  recurse = 0;			/* Ok, we can be called again safely */
}

/** The say command.
 * \param player the enactor.
 * \param tbuf1 the message to say.
 */
void
do_say(dbref player, const char *tbuf1)
{
  dbref loc;

  loc = speech_loc(player);
  if (!GoodObject(loc))
    return;

  if (!eval_lock(player, loc, Speech_Lock)) {
    fail_lock(player, loc, Speech_Lock, T("You may not speak here!"), NOTHING);
    return;
  }

  if (*tbuf1 == SAY_TOKEN && CHAT_STRIP_QUOTE)
    tbuf1++;

  /* notify everybody */
  notify_format(player, T("You say, \"%s\""), tbuf1);
  notify_except(Contents(loc), player,
		tprintf(T("%s says, \"%s\""), spname(player), tbuf1),
		NA_INTER_HEAR);
}

/** A comparator for raw dbrefs.
 * This exists because the i_comp function in funlist.c got hairy some time ago.
 * \param a the first element to compare.
 * \param b the second element to compare.
 */
static int
dbref_comp(const void *a, const void *b)
{
  const dbref *x = (const dbref *) a;
  const dbref *y = (const dbref *) b;
  return (int) *x - (int) *y;
}

/** The oemit(/list) command.
 * \verbatim
 * This implements @oemit and @oemit/list.
 * \endverbatim
 * \param player the enactor.
 * \param list the list of dbrefs to oemit from the emit.
 * \param message the message to emit.
 * \param flags PEMIT_* flags.
 */
void
do_oemit_list(dbref player, char *list, const char *message, int flags)
{
  char *temp, *p, *s;
  dbref who;
  dbref pass[12], locs[10];
  int i, oneloc = 0;
  int na_flags = NA_INTER_HEAR;

  /* If no message, further processing is pointless.
   * If no list, they should have used @remit. */
  if (!message || !*message || !list || !*list)
    return;

  orator = player;
  pass[0] = 0;
  /* Find out what room to do this in. If they supplied a db# before
   * the '/', then oemit to anyone in the room who's not on list.
   * Otherwise, oemit to every location which has at least one of the
   * people in the list. This is intended for actions which involve
   * players who are in different rooms, e.g.:
   *
   * X (in #0) fires an arrow at Y (in #2).
   *
   * X sees: You fire an arrow at Y. (pemit to X)
   * Y sees: X fires an arrow at you! (pemit to Y)
   * #0 sees: X fires an arrow at Y. (oemit/list to X Y)
   * #2 sees: X fires an arrow at Y. (from the same oemit)
   */
  /* Find out what room to do this in. They should have supplied a db#
   * before the '/'. */
  if ((temp = strchr(list, '/'))) {
    *temp++ = '\0';
    pass[1] = noisy_match_result(player, list, NOTYPE, MAT_EVERYTHING);
    if (!GoodObject(pass[1])) {
      notify(player, T("I can't find that room."));
      return;
    }

    if (!eval_lock(player, pass[1], Speech_Lock)) {
      fail_lock(player, pass[1], Speech_Lock, T("You may not speak there!"),
		NOTHING);
      return;
    }

    oneloc = 1;			/* we are only oemitting to one location */
  } else {
    temp = list;
  }

  s = trim_space_sep(temp, ' ');
  while (s) {
    p = split_token(&s, ' ');
    /* If a room was given, we match relative to the room */
    if (oneloc)
      who = match_result(pass[1], p, NOTYPE, MAT_POSSESSION | MAT_ABSOLUTE);
    else
      who = noisy_match_result(player, p, NOTYPE, MAT_OBJECTS);
    /* pass[0] tracks the number of valid players we've found.
     * pass[1] is the given room (possibly nothing right now)
     * pass[2..12] are dbrefs of players
     * locs[0..10] are corresponding dbrefs of locations
     */
    if (GoodObject(who) && GoodObject(Location(who))
	&& eval_lock(player, Location(who), Speech_Lock)
      ) {
      if (pass[0] < 10) {
	locs[pass[0]] = Location(who);
	pass[pass[0] + 2] = who;
	pass[0]++;
      } else {
	notify(player, T("Too many people to oemit to."));
	break;
      }
    }
  }

  /* Sort the list of rooms to oemit to so we don't oemit to the same           
   * room twice */
  qsort((void *) locs, pass[0], sizeof(locs[0]), dbref_comp);

  if (flags & PEMIT_SPOOF)
    na_flags |= NA_SPOOF;
  for (i = 0; i < pass[0]; i++) {
    if (i != 0 && locs[i] == locs[i - 1])
      continue;
    pass[1] = locs[i];
    notify_anything_loc(orator, na_exceptN, pass, ns_esnotify, na_flags,
			message, locs[i]);
    do_audible_stuff(pass[1], &pass[2], pass[0], message);
  }
}


/** The whisper command.
 * \param player the enactor.
 * \param arg1 name of the object to whisper to.
 * \param arg2 message to whisper.
 * \param noisy if 1, others overhear that a whisper has occurred.
 */
void
do_whisper(dbref player, const char *arg1, const char *arg2, int noisy)
{
  dbref who;
  int key;
  const char *gap;
  char *tbuf, *tp;
  char *p;
  dbref good[100];
  int gcount = 0;
  const char *head;
  int overheard;
  char *current;
  const char **start;

  if (!arg1 || !*arg1) {
    notify(player, T("Whisper to whom?"));
    return;
  }
  if (!arg2 || !*arg2) {
    notify(player, T("Whisper what?"));
    return;
  }
  tp = tbuf = (char *) mush_malloc(BUFFER_LEN, "string");
  if (!tbuf)
    mush_panic("Unable to allocate memory in do_whisper_list");

  overheard = 0;
  head = arg1;
  start = &head;
  /* Figure out what kind of message */
  gap = " ";
  switch (*arg2) {
  case SEMI_POSE_TOKEN:
    gap = "";
  case POSE_TOKEN:
    key = 1;
    arg2++;
    break;
  default:
    key = 2;
    break;
  }

  *tp = '\0';
  /* Make up a list of good and bad names */
  while (head && *head) {
    current = next_in_list(start);
    who = match_result(player, current, TYPE_PLAYER, MAT_NEAR_THINGS |
		       MAT_CONTAINER);
    if (!GoodObject(who) || !can_interact(player, who, INTERACT_HEAR)) {
      safe_chr(' ', tbuf, &tp);
      safe_str_space(current, tbuf, &tp);
      if (GoodObject(who))
	notify_format(player, T("%s can't hear you."), Name(who));
    } else {
      /* A good whisper */
      good[gcount++] = who;
      if (gcount >= 100) {
	notify(player, T("Too many people to whisper to."));
	break;
      }
    }
  }

  *tp = '\0';
  if (*tbuf)
    notify_format(player, T("Unable to whisper to:%s"), tbuf);

  if (!gcount) {
    mush_free((Malloc_t) tbuf, "string");
    return;
  }

  /* Drunk wizards... */
  if (Dark(player))
    noisy = 0;

  /* Set up list of good names */
  tp = tbuf;
  safe_str(" to ", tbuf, &tp);
  for (who = 0; who < gcount; who++) {
    if (noisy && (get_random_long(0, 100) < WHISPER_LOUDNESS))
      overheard = 1;
    safe_itemizer(who + 1, (who == gcount - 1), ",", T("and"), " ", tbuf, &tp);
    safe_str(Name(good[who]), tbuf, &tp);
  }
  *tp = '\0';

  if (key == 1) {
    notify_format(player, (gcount > 1) ? T("%s sense: %s%s%s") :
		  T("%s senses: %s%s%s"), tbuf + 4, Name(player), gap, arg2);
    p = tprintf("You sense: %s%s%s", Name(player), gap, arg2);
  } else {
    notify_format(player, T("You whisper, \"%s\"%s."), arg2, tbuf);
    p = tprintf("%s whispers%s: %s", Name(player),
		gcount > 1 ? tbuf : "", arg2);
  }

  for (who = 0; who < gcount; who++) {
    notify_must_puppet(good[who], p);
    if (Location(good[who]) != Location(player))
      overheard = 0;
  }
  if (overheard) {
    dbref first = Contents(Location(player));
    if (!GoodObject(first))
      return;
    p = tprintf("%s whispers%s.", Name(player), tbuf);
    DOLIST(first, first) {
      overheard = 1;
      for (who = 0; who < gcount; who++) {
	if ((first == player) || (first == good[who])) {
	  overheard = 0;
	  break;
	}
      }
      if (overheard)
	notify_noecho(first, p);
    }
  }
  mush_free((Malloc_t) tbuf, "string");
}

/** Send a message to a list of dbrefs. To avoid repeated generation
 * of the NOSPOOF string, we set it up the first time we encounter
 * something Nospoof, and then check for it thereafter.
 * The list is destructively modified.
 * \param player the enactor.
 * \param list the list of players to pemit to, destructively modified.
 * \param message the message to pemit.
 * \param flags PEMIT_* flags
 */
void
do_pemit_list(dbref player, char *list, const char *message, int flags)
{
  char *bp, *p;
  char *nsbuf, *nspbuf;
  char *l;
  dbref who;
  int nospoof;

  /* If no list or no message, further processing is pointless. */
  if (!message || !*message || !list || !*list)
    return;

  nspbuf = nsbuf = NULL;
  nospoof = (flags & PEMIT_SPOOF) ? 0 : 1;
  list[BUFFER_LEN - 1] = '\0';
  l = trim_space_sep(list, ' ');

  while ((p = split_token(&l, ' '))) {
    who = noisy_match_result(player, p, NOTYPE, MAT_ABSOLUTE);
    if (GoodObject(who) && okay_pemit(player, who)) {
      if (nospoof && Nospoof(who)) {
	if (Paranoid(who)) {
	  if (!nspbuf) {
	    bp = nspbuf = mush_malloc(BUFFER_LEN, "string");
	    if (player == Owner(player))
	      safe_format(nspbuf, &bp, "[%s(#%d)->] %s", Name(player),
			  player, message);
	    else
	      safe_format(nspbuf, &bp, "[%s(#%d)'s %s(#%d)->] %s",
			  Name(Owner(player)), Owner(player),
			  Name(player), player, message);
	    *bp = '\0';
	  }
	  notify(who, nspbuf);
	} else {
	  if (!nsbuf) {
	    bp = nsbuf = mush_malloc(BUFFER_LEN, "string");
	    safe_format(nsbuf, &bp, "[%s->] %s", Name(player), message);
	    *bp = '\0';
	  }
	  notify(who, nsbuf);
	}
      } else {
	notify_must_puppet(who, message);
      }
    }
  }
  if (nsbuf)
    mush_free(nsbuf, "string");
  if (nspbuf)
    mush_free(nspbuf, "string");

}

/** Send a message to an object.
 * \param player the enactor.
 * \param arg1 the name of the object to pemit to.
 * \param arg2 the message to pemit.
 * \param flags PEMIT_* flags.
 */
void
do_pemit(dbref player, const char *arg1, const char *arg2, int flags)
{
  dbref who;
  int silent, nospoof;

  silent = (flags & PEMIT_SILENT) ? 1 : 0;
  nospoof = (flags & PEMIT_SPOOF) ? 0 : 1;

  switch (who = match_result(player, arg1, NOTYPE,
			     MAT_OBJECTS | MAT_HERE | MAT_CONTAINER)) {
  case NOTHING:
    notify(player, T("I don't see that here."));
    break;
  case AMBIGUOUS:
    notify(player, T("I don't know who you mean!"));
    break;
  default:
    if (!okay_pemit(player, who)) {
      notify_format(player,
		    T("I'm sorry, but %s wishes to be left alone now."),
		    Name(who));
      return;
    }
    if (!silent)
      notify_format(player, T("You pemit \"%s\" to %s."), arg2, Name(who));
    if (nospoof && Nospoof(who)) {
      if (Paranoid(who)) {
	if (player == Owner(player))
	  notify_format(who, "[%s(#%d)->%s] %s", Name(player), player,
			Name(who), arg2);
	else
	  notify_format(who, "[%s(#%d)'s %s(#%d)->%s] %s",
			Name(Owner(player)), Owner(player),
			Name(player), player, Name(who), arg2);
      } else
	notify_format(who, "[%s->%s] %s", Name(player), Name(who), arg2);
    } else {
      notify_must_puppet(who, arg2);
    }
    break;
  }
}

/** The pose and semipose command.
 * \param player the enactor.
 * \param tbuf1 the message to pose.
 * \param space if 1, put a space between name and pose; if 0, don't (semipose)
 */
void
do_pose(dbref player, const char *tbuf1, int space)
{
  dbref loc;

  loc = speech_loc(player);
  if (!GoodObject(loc))
    return;

  if (!eval_lock(player, loc, Speech_Lock)) {
    fail_lock(player, loc, Speech_Lock, T("You may not speak here!"), NOTHING);
    return;
  }

  /* notify everybody */
  if (!space)
    notify_except(Contents(loc), NOTHING,
		  tprintf("%s %s", spname(player), tbuf1), NA_INTER_HEAR);
  else
    notify_except(Contents(loc), NOTHING,
		  tprintf("%s%s", spname(player), tbuf1), NA_INTER_HEAR);
}

/** The *wall commands.
 * \param player the enactor.
 * \param message message to broadcast.
 * \param target type of broadcast (all, royalty, wizard)
 * \param emit if 1, this is a wallemit.
 */
void
do_wall(dbref player, const char *message, enum wall_type target, int emit)
{
  const char *gap = "", *prefix;
  const char *mask;
  int pose = 0;

  /* Only @wall is available to those with the announce power.
   * Only @rwall is available to royalty.
   */
  if (!(Wizard(player) ||
	((target == WALL_ALL) && Can_Announce(player)) ||
	((target == WALL_RW) && Royalty(player)))) {
    notify(player, T("Posing as a wizard could be hazardous to your health."));
    return;
  }
  /* put together the message and figure out what type it is */
  if (!emit) {
    gap = " ";
    switch (*message) {
    case SAY_TOKEN:
      if (CHAT_STRIP_QUOTE)
	message++;
      break;
    case SEMI_POSE_TOKEN:
      gap = "";
    case POSE_TOKEN:
      pose = 1;
      message++;
      break;
    }
  }

  if (!*message) {
    notify(player, T("What did you want to say?"));
    return;
  }
  if (target == WALL_WIZ) {
    /* to wizards only */
    mask = "WIZARD";
    prefix = WIZWALL_PREFIX;
  } else if (target == WALL_RW) {
    /* to wizards and royalty */
    mask = "WIZARD ROYALTY";
    prefix = RWALL_PREFIX;
  } else {
    /* to everyone */
    mask = NULL;
    prefix = WALL_PREFIX;
  }

  /* broadcast the message */
  if (pose)
    flag_broadcast(mask, 0, "%s %s%s%s", prefix, Name(player), gap, message);
  else if (emit)
    flag_broadcast(mask, 0, "%s [%s]: %s", prefix, Name(player), message);
  else
    flag_broadcast(mask, 0,
		   "%s %s %s, \"%s\"", prefix, Name(player),
		   target == WALL_ALL ? "shouts" : "says", message);
}

/** The page command.
 * \param player the enactor.
 * \param arg1 the list of players to page.
 * \param arg2 the message to page.
 * \param cause the object that caused the command to run.
 * \param noeval if 1, page/noeval.
 * \param multipage if 1, a page/list; if 0, a page/blind.
 * \param override if 1, page/override.
 */
void
do_page(dbref player, const char *arg1, const char *arg2, dbref cause,
	int noeval, int multipage, int override)
{
  dbref target;
  const char *message;
  const char *gap;
  int key;
  char *tbuf, *tp;
  char *tbuf2, *tp2;
  dbref good[100];
  int gcount = 0;
  char *msgbuf, *mb;
  const char *head;
  const char *hp = NULL;
  const char **start;
  char *current;
  int i;
  int repage = 0;
  int fails_lock;
  int is_haven;
  ATTR *a;

  tp2 = tbuf2 = (char *) mush_malloc(BUFFER_LEN, "string");
  if (!tbuf2)
    mush_panic("Unable to allocate memory in do_page");

  if (arg2 && *arg2 && *arg1) {
    /* page to=msg. Always evaluate to, maybe evaluate msg */
    process_expression(tbuf2, &tp2, &arg1, player, cause, cause,
		       PE_DEFAULT, PT_DEFAULT, NULL);
    *tp2 = '\0';
    head = tbuf2;
    message = arg2;
  } else if (arg2 && *arg2) {
    /* page =msg */
    message = arg2;
    repage = 1;
  } else {
    /* page msg */
    message = arg1;
    repage = 1;
  }
  if (repage) {
    a = atr_get_noparent(player, "LASTPAGED");
    if (!a || !*((hp = head = safe_atr_value(a)))) {
      notify(player, T("You haven't paged anyone since connecting."));
      mush_free((Malloc_t) tbuf2, "string");
      return;
    }
    if (!message || !*message) {
      notify_format(player, T("You last paged %s."), head);
      mush_free((Malloc_t) tbuf2, "string");
      if (hp)
	free((Malloc_t) hp);
      return;
    }
  }

  tp = tbuf = (char *) mush_malloc(BUFFER_LEN, "string");
  if (!tbuf)
    mush_panic("Unable to allocate memory in do_page");

  if (override && !Pemit_All(player)) {
    notify(player, "Try again after you get the pemit_all power.");
    override = 0;
  }

  start = &head;
  while (head && *head && (gcount < 99)) {
    current = next_in_list(start);
    target = lookup_player(current);
    if (!GoodObject(target))
      target = short_page(current);
    if (target == NOTHING) {
      notify_format(player,
		    T("I can't find who you're trying to page with: %s"),
		    current);
      safe_chr(' ', tbuf, &tp);
      safe_str_space(current, tbuf, &tp);
    } else if (target == AMBIGUOUS) {
      notify_format(player,
		    T("I'm not sure who you want to page with: %s"), current);
      safe_chr(' ', tbuf, &tp);
      safe_str_space(current, tbuf, &tp);
    } else {
      fails_lock = !(override || eval_lock(player, target, Page_Lock));
      is_haven = !override && Haven(target);
      if (!Connected(target) || (Dark(target) && (is_haven || fails_lock))) {
	/* A player isn't connected if they aren't connected, or if
	 * they're DARK and HAVEN, or DARK and the pagelock fails. */
	page_return(player, target, "Away", "AWAY",
		    tprintf(T("%s is not connected."), Name(target)));
	if (fails_lock)
	  fail_lock(player, target, Page_Lock, NULL, NOTHING);
	safe_chr(' ', tbuf, &tp);
	safe_str_space(current, tbuf, &tp);
      } else if (is_haven) {
	page_return(player, target, "Haven", "HAVEN",
		    tprintf(T("%s is not accepting any pages."), Name(target)));
	safe_chr(' ', tbuf, &tp);
	safe_str_space(Name(target), tbuf, &tp);
      } else if (fails_lock) {
	page_return(player, target, "Haven", "HAVEN",
		    tprintf(T("%s is not accepting your pages."),
			    Name(target)));
	fail_lock(player, target, Page_Lock, NULL, NOTHING);
	safe_chr(' ', tbuf, &tp);
	safe_str_space(Name(target), tbuf, &tp);
      } else {
	/* This is a good page */
	good[gcount] = target;
	gcount++;
      }
    }
  }

  /* Reset tbuf2 to use later */
  tp2 = tbuf2;

  /* We now have an array of good[] dbrefs, a gcount of the good ones,
   * and a tbuf with bad ones.
   */

  /* We don't know what the heck's going on here, but we're not paging
   * anyone, this looks like a spam attack. */
  if (gcount == 99) {
    notify(player, T("You're trying to page too many people at once."));
    mush_free((Malloc_t) tbuf, "string");
    mush_free((Malloc_t) tbuf2, "string");
    if (hp)
      free((Malloc_t) hp);
    return;
  }

  /* We used to stick 'Unable to page' on at the start, but this is
   * faster for the 90% of the cases where there isn't a bad name
   * That may sound high, but, remember, we (almost) never have a bad
   * name if we're repaging, which is probably 75% of all pages */
  *tp = '\0';
  if (*tbuf)
    notify_format(player, T("Unable to page:%s"), tbuf);

  if (!gcount) {
    /* Well, that was a total waste of time. */
    mush_free((Malloc_t) tbuf, "string");
    mush_free((Malloc_t) tbuf2, "string");
    if (hp)
      free((Malloc_t) hp);
    return;
  }

  /* Can the player afford to pay for this thing? */
  if (!payfor(player, PAGE_COST * gcount)) {
    notify_format(player, T("You don't have enough %s."), MONIES);
    mush_free((Malloc_t) tbuf, "string");
    mush_free((Malloc_t) tbuf2, "string");
    if (hp)
      free((Malloc_t) hp);
    return;
  }

  /* Okay, we have a real page, the player can pay for it, and it's
   * actually going to someone. We're in this for keeps now. */

  /* Evaluate the message if we need to. */
  if (noeval)
    msgbuf = NULL;
  else {
    mb = msgbuf = (char *) mush_malloc(BUFFER_LEN, "string");
    if (!msgbuf)
      mush_panic("Unable to allocate memory in do_page");

    process_expression(msgbuf, &mb, &message, player, cause, cause,
		       PE_DEFAULT, PT_DEFAULT, NULL);
    *mb = '\0';
    message = msgbuf;
  }

  if (Haven(player))
    notify(player, T("You are set HAVEN and cannot receive pages."));

  /* Figure out what kind of message */
  global_eval_context.wenv[0] = (char *) message;
  gap = " ";
  switch (*message) {
  case SEMI_POSE_TOKEN:
    gap = "";
  case POSE_TOKEN:
    key = 1;
    message++;
    break;
  default:
    key = 3;
    break;
  }

  /* Reset tbuf and tbuf2 to use later */
  tp = tbuf;
  tp2 = tbuf2;

  /* tbuf2 is used to hold a fancy formatted list of names,
   * with commas and the word 'and' , if needed. */
  /* tbuf holds a space-separated list of names for repaging */

  /* Set up a pretty formatted list. */
  for (i = 0; i < gcount; i++) {
    if (i)
      safe_chr(' ', tbuf, &tp);
    safe_str_space(Name(good[i]), tbuf, &tp);
    safe_itemizer(i + 1, (i == gcount - 1), ",", T("and"), " ", tbuf2, &tp2);
    safe_str(Name(good[i]), tbuf2, &tp2);
  }
  *tp = '\0';
  *tp2 = '\0';
  (void) atr_add(player, "LASTPAGED", tbuf, GOD, NOTHING);

  /* Reset tbuf to use later */
  tp = tbuf;

  /* Figure out the one success message, and send it */
  if (key == 1)
    notify_format(player, T("Long distance to %s%s: %s%s%s"),
		  ((gcount > 1) && (!multipage)) ? "(blind) " : "", tbuf2,
		  Name(player), gap, message);
  else
    notify_format(player, T("You paged %s%s with '%s'"),
		  ((gcount > 1) && (!multipage)) ? "(blind) " : "", tbuf2,
		  message);

  /* Figure out the 'name' of the player */
  if (PAGE_ALIASES && (a = atr_get_noparent(player, "ALIAS")))
    current = tprintf("%s (%s)", Name(player), atr_value(a));
  else
    current = (char *) Name(player);

  /* Now, build the thing we want to send to the pagees,
   * and put it in tbuf */

  /* Build the header */
  if (key == 1) {
    safe_str(T("From afar"), tbuf, &tp);
    if ((gcount > 1) && (multipage)) {
      safe_str(T(" (to "), tbuf, &tp);
      safe_str(tbuf2, tbuf, &tp);
      safe_chr(')', tbuf, &tp);
    }
    safe_str(", ", tbuf, &tp);
    safe_str(current, tbuf, &tp);
    safe_str(gap, tbuf, &tp);
  } else {
    safe_str(current, tbuf, &tp);
    safe_str(T(" pages"), tbuf, &tp);
    if ((gcount > 1) && (multipage)) {
      safe_chr(' ', tbuf, &tp);
      safe_str(tbuf2, tbuf, &tp);
    }
    safe_str(": ", tbuf, &tp);
  }
  /* Tack on the message */
  safe_str(message, tbuf, &tp);
  *tp = '\0';

  /* Tell each page recipient with tbuf */
  for (i = 0; i < gcount; i++) {
    if (!IsPlayer(player) && Nospoof(good[i]))
      notify_format(good[i], "[#%d] %s", player, tbuf);
    else
      notify(good[i], tbuf);

    page_return(player, good[i], "Idle", "IDLE", NULL);
  }

  mush_free((Malloc_t) tbuf, "string");
  mush_free((Malloc_t) tbuf2, "string");
  if (msgbuf)
    mush_free((Malloc_t) msgbuf, "string");
  if (hp)
    free((Malloc_t) hp);
}


/** Does a message match a filter pattern on an object?
 * \param thing object with the filter.
 * \param msg message to match.
 * \param flag if 0, filter; if 1, infilter.
 * \retval 1 message matches filter.
 * \retval 0 message does not match filter.
 */
int
filter_found(dbref thing, const char *msg, int flag)
{
  char *filter;
  ATTR *a;
  char *p, *bp;
  char *temp;			/* need this so we don't leak memory     
				 * by failing to free the storage
				 * allocated by safe_uncompress
				 */
  int i;
  int matched = 0;

  if (!flag)
    a = atr_get(thing, "FILTER");
  else
    a = atr_get(thing, "INFILTER");
  if (!a)
    return matched;

  filter = safe_atr_value(a);
  temp = filter;

  for (i = 0; (i < MAX_ARG) && !matched; i++) {
    p = bp = filter;
    process_expression(p, &bp, (char const **) &filter, 0, 0, 0,
		       PE_NOTHING, PT_COMMA, NULL);
    if (*filter == ',')
      *filter++ = '\0';
    if (*p == '\0' && *filter == '\0')	/* No more filters */
      break;
    if (*p == '\0')		/* Empty filter */
      continue;
    if (AF_Regexp(a))
      matched = quick_regexp_match(p, msg, AF_Case(a));
    else
      matched = local_wild_match_case(p, msg, AF_Case(a));
  }

  free((Malloc_t) temp);
  return matched;
}

/** Copy a message into a buffer, adding an object's PREFIX attribute.
 * \param thing object with prefix attribute.
 * \param msg message.
 * \param tbuf1 destination buffer.
 */
void
make_prefixstr(dbref thing, const char *msg, char *tbuf1)
{
  char *bp, *asave;
  char const *ap;
  char *wsave[10], *preserve[NUMQ];
  ATTR *a;
  int j;

  a = atr_get(thing, "PREFIX");

  bp = tbuf1;

  if (!a) {
    safe_str("From ", tbuf1, &bp);
    safe_str(Name(IsExit(thing) ? Source(thing) : thing), tbuf1, &bp);
    safe_str(", ", tbuf1, &bp);
  } else {
    for (j = 0; j < 10; j++) {
      wsave[j] = global_eval_context.wenv[j];
      global_eval_context.wenv[j] = NULL;
    }
    global_eval_context.wenv[0] = (char *) msg;
    save_global_regs("prefix_save", preserve);
    asave = safe_atr_value(a);
    ap = asave;
    process_expression(tbuf1, &bp, &ap, thing, orator, orator,
		       PE_DEFAULT, PT_DEFAULT, NULL);
    free((Malloc_t) asave);
    restore_global_regs("prefix_save", preserve);
    for (j = 0; j < 10; j++)
      global_eval_context.wenv[j] = wsave[j];
    if (bp != tbuf1)
      safe_chr(' ', tbuf1, &bp);
  }
  safe_str(msg, tbuf1, &bp);
  *bp = '\0';
  return;
}

/** pass a message on, for AUDIBLE, prepending a prefix, unless the
 * message matches a filter pattern.
 * \param thing object to check for filter and prefix.
 * \param msg message to pass.
 */
void
propagate_sound(dbref thing, const char *msg)
{
  char tbuf1[BUFFER_LEN];
  dbref loc = Location(thing);
  dbref pass[2];

  if (!GoodObject(loc))
    return;

  /* check to see that filter doesn't suppress message */
  if (filter_found(thing, msg, 0))
    return;

  /* figure out the prefix */
  make_prefixstr(thing, msg, tbuf1);

  /* Exits pass the message on to things in the next room.
   * Objects pass the message on to the things outside.
   * Don't tell yourself your own message.
   */

  if (IsExit(thing)) {
    notify_anything(orator, na_next, &Contents(loc), NULL, NA_INTER_HEAR,
		    tbuf1);
  } else {
    pass[0] = Contents(loc);
    pass[1] = thing;
    notify_anything(orator, na_nextbut, pass, NULL, NA_INTER_HEAR, tbuf1);
  }
}

static void
do_audible_stuff(dbref loc, dbref *excs, int numexcs, const char *msg)
{
  dbref e;
  int exclude = 0;
  int i;

  if (!Audible(loc))
    return;

  if (IsRoom(loc)) {
    DOLIST(e, Exits(loc)) {
      if (Audible(e))
	propagate_sound(e, msg);
    }
  } else {
    for (i = 0; i < numexcs; i++)
      if (*(excs + i) == loc)
	exclude = 1;
    if (!exclude)
      propagate_sound(loc, msg);
  }
}

/** notify_anthing() wrapper to notify everyone in a location except one
 * object.
 * \param first object in location to notify.
 * \param exception dbref of object not to notify, or NOTHING.
 * \param msg message to send.
 * \param flags flags to pass to notify_anything().
 */
void
notify_except(dbref first, dbref exception, const char *msg, int flags)
{
  dbref loc;
  dbref pass[2];

  if (!GoodObject(first))
    return;
  loc = Location(first);
  if (!GoodObject(loc))
    return;

  if (exception == NOTHING)
    exception = AMBIGUOUS;

  pass[0] = loc;
  pass[1] = exception;

  notify_anything(orator, na_except, pass, ns_esnotify, flags, msg);

  do_audible_stuff(loc, &pass[1], 1, msg);
}

/** notify_anthing() wrapper to notify everyone in a location except two
 * objects.
 * \param first object in location to notify.
 * \param exc1 dbref of one object not to notify, or NOTHING.
 * \param exc2 dbref of another object not to notify, or NOTHING.
 * \param msg message to send.
 * \param flags interaction flags to control type of interaction.
 */
void
notify_except2(dbref first, dbref exc1, dbref exc2, const char *msg, int flags)
{
  dbref loc;
  dbref pass[3];

  if (!GoodObject(first))
    return;
  loc = Location(first);
  if (!GoodObject(loc))
    return;

  if (exc1 == NOTHING)
    exc1 = AMBIGUOUS;
  if (exc2 == NOTHING)
    exc2 = AMBIGUOUS;

  pass[0] = loc;
  pass[1] = exc1;
  pass[2] = exc2;

  notify_anything(orator, na_except2, pass, ns_esnotify, flags, msg);

  do_audible_stuff(loc, &pass[1], 2, msg);
}

/** The think command.
 * \param player the enactor.
 * \param message the message to think.
 */
void
do_think(dbref player, const char *message)
{
  notify(player, message);
}


/** The emit command.
 * \verbatim
 * This implements @emit.
 * \endverbatim
 * \param player the enactor.
 * \param tbuf1 the message to emit.
 * \param flags bitmask of notification flags.
 */
void
do_emit(dbref player, const char *tbuf1, int flags)
{
  dbref loc;
  int na_flags = NA_INTER_HEAR;

  loc = speech_loc(player);
  if (!GoodObject(loc))
    return;

  if (!eval_lock(player, loc, Speech_Lock)) {
    fail_lock(player, loc, Speech_Lock, T("You may not speak here!"), NOTHING);
    return;
  }

  /* notify everybody */
  if (flags & PEMIT_SPOOF)
    na_flags |= NA_SPOOF;
  notify_anything(player, na_loc, &loc, ns_esnotify, na_flags, tbuf1);

  do_audible_stuff(loc, NULL, 0, tbuf1);
}

/** Remit a message to a single room.
 * \param player the enactor.
 * \param target string containing dbref of room to remit in.
 * \param msg message to emit.
 * \param flags PEMIT_* flags
 */
static void
do_one_remit(dbref player, const char *target, const char *msg, int flags)
{
  dbref room;
  int na_flags = NA_INTER_HEAR;
  room = match_result(player, target, NOTYPE, MAT_EVERYTHING);
  if (!GoodObject(room)) {
    notify(player, T("I can't find that."));
  } else {
    if (IsExit(room)) {
      notify(player, T("There can't be anything in that!"));
    } else if (!okay_pemit(player, room)) {
      notify_format(player,
		    T("I'm sorry, but %s wishes to be left alone now."),
		    Name(room));
    } else if (!eval_lock(player, room, Speech_Lock)) {
      fail_lock(player, room, Speech_Lock, T("You may not speak there!"),
		NOTHING);
    } else {
      if (!(flags & PEMIT_SILENT) && (Location(player) != room)) {
	const char *rmno;
	rmno = unparse_object(player, room);
	notify_format(player, T("You remit, \"%s\" in %s"), msg, rmno);
      }
      if (flags & PEMIT_SPOOF)
	na_flags |= NA_SPOOF;
      notify_anything_loc(player, na_loc, &room, ns_esnotify, na_flags,
			  msg, room);
      do_audible_stuff(room, NULL, 0, msg);
    }
  }
}

/** Remit a message
 * \verbatim
 * This implements @remit.
 * \endverbatim
 * \param player the enactor.
 * \param arg1 string containing dbref(s) of rooms to remit it.
 * \param arg2 message to emit.
 * \param flags for remit.
 */
void
do_remit(dbref player, char *arg1, const char *arg2, int flags)
{
  if (flags & PEMIT_LIST) {
    /* @remit/list */
    char *current;
    arg1 = trim_space_sep(arg1, ' ');
    while ((current = split_token(&arg1, ' ')) != NULL)
      do_one_remit(player, current, arg2, flags);
  } else {
    do_one_remit(player, arg1, arg2, flags);
  }
}

/** Emit a message to the absolute location of enactor.
 * \param player the enactor.
 * \param tbuf1 message to emit.
 * \param flags bitmask of notification flags.
 */
void
do_lemit(dbref player, const char *tbuf1, int flags)
{
  /* give a message to the "absolute" location of an object */
  dbref room;
  int rec = 0;
  int na_flags = NA_INTER_HEAR;
  int silent = (flags & PEMIT_SILENT) ? 1 : 0;

  /* only players and things may use this command */
  if (!Mobile(player))
    return;

  /* prevent infinite loop if player is inside himself */
  if (((room = Location(player)) == player) || !GoodObject(room)) {
    notify(player, T("Invalid container object."));
    do_rawlog(LT_ERR, T("** BAD CONTAINER **  #%d is inside #%d."), player,
	      room);
    return;
  }
  while (!IsRoom(room) && (rec < 15)) {
    room = Location(room);
    rec++;
  }
  if (rec > 15) {
    notify(player, T("Too many containers."));
    return;
  } else if (!eval_lock(player, room, Speech_Lock)) {
    fail_lock(player, room, Speech_Lock, T("You may not speak there!"),
	      NOTHING);
    return;
  } else {
    if (!silent && (Location(player) != room))
      notify_format(player, T("You lemit: \"%s\""), tbuf1);
    if (flags & PEMIT_SPOOF)
      na_flags |= NA_SPOOF;
    notify_anything(player, na_loc, &room, ns_esnotify, na_flags, tbuf1);
  }
}

/** notify_anything() function for zone emits.
 * \param current unused.
 * \param data array of notify data.
 * \return last object in zone, or NOTHING.
 */
dbref
na_zemit(dbref current __attribute__ ((__unused__)), void *data)
{
  dbref this;
  dbref room;
  dbref *dbrefs = data;
  this = dbrefs[0];
  do {
    if (this == NOTHING) {
      for (room = dbrefs[1]; room < db_top; room++) {
	if (IsRoom(room) && (Zone(room) == dbrefs[2])
	    && eval_lock(dbrefs[3], room, Speech_Lock)
	  )
	  break;
      }
      if (!(room < db_top))
	return NOTHING;
      this = room;
      dbrefs[1] = room + 1;
    } else if (IsRoom(this)) {
      this = Contents(this);
    } else {
      this = Next(this);
    }
  } while ((this == NOTHING) || (this == dbrefs[4]));
  dbrefs[0] = this;
  return this;
}

/** The zemit command.
 * \verbatim
 * This implements @zemit and @nszemit.
 * \endverbatim
 * \param player the enactor.
 * \param arg1 string containing dbref of ZMO.
 * \param arg2 message to emit.
 * \param flags bitmask of notificati flags.
 */
void
do_zemit(dbref player, const char *arg1, const char *arg2, int flags)
{
  const char *where;
  dbref zone;
  dbref pass[5];
  int na_flags = NA_INTER_HEAR;

  zone = match_result(player, arg1, NOTYPE, MAT_ABSOLUTE);
  if (!GoodObject(zone)) {
    notify(player, T("Invalid zone."));
    return;
  }
  if (!controls(player, zone)) {
    notify(player, T("Permission denied."));
    return;
  }
  /* this command is computationally expensive */
  if (!payfor(player, FIND_COST)) {
    notify(player, T("Sorry, you don't have enough money to do that."));
    return;
  }
  where = unparse_object(player, zone);
  notify_format(player, T("You zemit, \"%s\" in zone %s"), arg2, where);

  pass[0] = NOTHING;
  pass[1] = 0;
  pass[2] = zone;
  pass[3] = player;
  pass[4] = player;
  if (flags & PEMIT_SPOOF)
    na_flags |= NA_SPOOF;
  notify_anything(player, na_zemit, pass, ns_esnotify, na_flags, arg2);
}