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

/* Functions to cope with flags */
#include "config.h"

#ifdef I_SYS_TIME
#include <sys/time.h>
#else
#include <time.h>
#endif
#ifdef I_STRING
#include <string.h>
#else
#include <strings.h>
#endif
#ifdef I_STDLIB
#include <stdlib.h>
#endif

#include "conf.h"
#include "mushdb.h"
#include "externs.h"
#include "intrface.h"
#include "match.h"
#include "htab.h"
#include "confmagic.h"

extern void hide_player _((dbref player, int hide));

#define FLAG_HASH_SIZE 256
#define FLAG_HASH_MASK 255

typedef struct flag_info FLAG;

struct flag_info {
  const char *name;
  char letter;
  int type;
  int flag;
  int perms;
  int negate_perms;
  FLAG *next;
};

typedef struct flag_entry FLAGENT;

struct flag_entry {
  const char *name;
  FLAG *entry;
  FLAGENT *next;
};

typedef struct flag_alias FLAG_ALIAS;

struct flag_alias {
  const char *alias;
  const char *realname;
};

typedef struct power_info POWER;
struct power_info {
  const char *name;
  int flag;
  POWER *next;
};

typedef struct power_alias POWER_ALIAS;
struct power_alias {
  const char *alias;
  const char *realname;
};

static FLAG *flag_hash_lookup _((const char *name));
static void flag_hash_insert _((const char *name, FLAG *entry));
void init_flag_hashtab _((void));
int can_set_flag _((dbref player, dbref thing, FLAG *flagp, int negate));
const char *unparse_flags _((dbref thing, dbref player));
const char *flag_description _((dbref player, dbref thing));
const char *togglemask_to_string _((int type, object_flag_type mask));
static FLAG *letter_to_flagptr _((char c, int type, int *toggle));
object_flag_type letter_to_flag _((char c, int type, int *toggle));
object_flag_type find_flag _((char *name, int type, int *toggle, int is_conf));
void decompile_flags _((dbref player, dbref thing, const char *name));
int convert_flags _((dbref player, char *s, object_flag_type *p_mask, object_flag_type *p_toggle, object_flag_type *p_type));
void set_flag _((dbref player, dbref thing, char *flag, int negate, int hear, int listener));
int handle_flaglists _((dbref player, char *name, char *fstr, int type));
int sees_flag _((dbref privs, dbref thing, char *name));

HASHTAB htab_flag;

/*   Name       Lettter Type            Flag            Perms   Negate_Perm */
FLAG flag_table[] =
{

  {"CHOWN_OK", 'C', NOTYPE, CHOWN_OK, F_ANY, F_ANY},
  {"DARK", 'D', NOTYPE, DARK, F_ANY, F_ANY},
  {"GOING", 'G', NOTYPE, GOING, F_INTERNAL, F_ANY},
  {"HAVEN", 'H', NOTYPE, HAVEN, F_ANY, F_ANY},
  {"INHERIT", 'I', NOTYPE, INHERIT, F_INHERIT, F_INHERIT},
  {"LINK_OK", 'L', NOTYPE, LINK_OK, F_ANY, F_ANY},
  {"OPAQUE", 'O', NOTYPE, OPAQUE, F_ANY, F_ANY},
  {"QUIET", 'Q', NOTYPE, QUIET, F_ANY, F_ANY},
  {"STICKY", 'S', NOTYPE, STICKY, F_ANY, F_ANY},
  {"UNFINDABLE", 'U', NOTYPE, UNFIND, F_ANY, F_ANY},
  {"VISUAL", 'V', NOTYPE, VISUAL, F_ANY, F_ANY},
  {"WIZARD", 'W', NOTYPE, WIZARD, F_INHERIT | F_WIZARD,
   F_INHERIT | F_WIZARD},
  {"SAFE", 'X', NOTYPE, SAFE, F_ANY, F_ANY},
  {"AUDIBLE", 'a', NOTYPE, AUDIBLE, F_ANY, F_ANY},
  {"DEBUG", 'b', NOTYPE, DEBUGGING, F_ANY, F_ANY},
#ifdef USE_WARNINGS
  {"NO_WARN", 'w', NOTYPE, NOWARN, F_ANY, F_ANY},
#endif
  {"ENTER_OK", 'e', NOTYPE, ENTER_OK, F_ANY, F_ANY},
  {"HALT", 'h', NOTYPE, HALT, F_ANY, F_ANY},
  {"NO_COMMAND", 'n', NOTYPE, NO_COMMAND, F_ANY, F_ANY},
  {"LIGHT", 'l', NOTYPE, LIGHT, F_ANY, F_ANY},
#ifdef ROYALTY_FLAG
  {"ROYALTY", 'r', NOTYPE, ROYALTY, F_INHERIT | F_ROYAL,
   F_INHERIT | F_ROYAL},
#endif
  {"TRANSPARENT", 't', NOTYPE, TRANSPARENTED, F_ANY, F_ANY},
  {"VERBOSE", 'v', NOTYPE, VERBOSE, F_ANY, F_ANY},
  {"STARTUP", 'z', NOTYPE, STARTUP, F_INTERNAL,
   F_INTERNAL},

  {"ANSI", 'A', TYPE_PLAYER, PLAYER_ANSI, F_ANY, F_ANY},
#ifdef EXTENDED_ANSI
  {"COLOR", 'C', TYPE_PLAYER, PLAYER_COLOR, F_ANY, F_ANY},
  {"FORCE_WHITE", 'f', TYPE_PLAYER, PLAYER_FORCEWHITE, F_ANY, F_ANY},
#endif
  {"MONITOR", 'M', TYPE_PLAYER, PLAYER_MONITOR, F_ROYAL, F_ANY},
  {"NOSPOOF", 'N', TYPE_PLAYER, PLAYER_NOSPOOF, F_ANY, F_ANY},
  {"ZONE", 'Z', TYPE_PLAYER, PLAYER_ZONE, F_ANY, F_ANY},
  {"CONNECTED", 'c', TYPE_PLAYER, PLAYER_CONNECT, F_INTERNAL | F_MDARK,
   F_INTERNAL | F_MDARK},
  {"GAGGED", 'g', TYPE_PLAYER, PLAYER_GAGGED, F_WIZARD, F_WIZARD},
  {"MYOPIC", 'm', TYPE_PLAYER, PLAYER_MYOPIC, F_ANY, F_ANY},
  {"TERSE", 'x', TYPE_PLAYER, PLAYER_TERSE, F_ANY, F_ANY},
#ifdef JURY_OK
  {"JURY_OK", 'j', TYPE_PLAYER, PLAYER_JURY, F_ROYAL, F_ROYAL},
  {"JUDGE", 'J', TYPE_PLAYER, PLAYER_JUDGE, F_ROYAL, F_ROYAL},
#endif
#ifdef FIXED_FLAG
  {"FIXED", 'F', TYPE_PLAYER, PLAYER_FIXED, F_WIZARD, F_WIZARD},
#endif
#ifdef ONLINE_REG
  {"UNREGISTERED", '?', TYPE_PLAYER, PLAYER_UNREG, F_ROYAL, F_ROYAL},
#endif
#ifdef VACATION_FLAG
  {"ON-VACATION", 'o', TYPE_PLAYER, PLAYER_VACATION, F_ANY, F_ANY},
#endif
  {"SUSPECT", 's', TYPE_PLAYER, PLAYER_SUSPECT, F_WIZARD | F_MDARK,
   F_WIZARD | F_MDARK},

  {"MONITOR", 'M', TYPE_THING, THING_LISTEN, F_ANY, F_ANY},
  {"DESTROY_OK", 'd', TYPE_THING, THING_DEST_OK, F_ANY, F_ANY},
  {"PUPPET", 'p', TYPE_THING, THING_PUPPET, F_ANY, F_ANY},
  {"NO_LEAVE", 'N', TYPE_THING, THING_NOLEAVE, F_ANY, F_ANY},

  {"ABODE", 'A', TYPE_ROOM, ROOM_ABODE, F_ANY, F_ANY},
  {"FLOATING", 'F', TYPE_ROOM, ROOM_FLOATING, F_ANY, F_ANY},
  {"JUMP_OK", 'J', TYPE_ROOM, ROOM_JUMP_OK, F_ANY, F_ANY},
  {"MONITOR", 'M', TYPE_ROOM, ROOM_LISTEN, F_ANY, F_ANY},
  {"Z_TEL", 'Z', TYPE_ROOM, ROOM_Z_TEL, F_ANY, F_ANY},
  {"NO_TEL", 'N', TYPE_ROOM, ROOM_NO_TEL, F_ANY, F_ANY},
#ifdef UNINSPECTED_FLAG
  {"UNINSPECTED", 'u', TYPE_ROOM, ROOM_UNINSPECT, F_ROYAL, F_ROYAL},
#endif

  {"CLOUDY", 'x', TYPE_EXIT, EXIT_CLOUDY, F_ANY, F_ANY},

  {"ACCESSED", '\0', NOTYPE, ACCESSED, F_INTERNAL | F_DARK,
   F_INTERNAL | F_DARK},
  {"MARKED", '\0', NOTYPE, MARKED, F_INTERNAL | F_DARK,
   F_INTERNAL | F_DARK},
  {"GOING_TWICE", '\0', NOTYPE, GOING_TWICE, F_INTERNAL | F_DARK,
   F_INTERNAL | F_DARK},
  {NULL, '\0', 0, 0, 0, 0}
};

FLAG type_table[] =
{
  {"PLAYER", 'P', TYPE_PLAYER, TYPE_PLAYER, F_INTERNAL,
   F_INTERNAL},
  {"ROOM", 'R', TYPE_ROOM, TYPE_ROOM, F_INTERNAL,
   F_INTERNAL},
  {"EXIT", 'E', TYPE_EXIT, TYPE_EXIT, F_INTERNAL,
   F_INTERNAL},
  {"THING", 'T', TYPE_THING, TYPE_THING, F_INTERNAL,
   F_INTERNAL},
  {NULL, '\0', 0, 0, 0, 0}
};

static FLAG_ALIAS flag_alias_tab[] =
{
  {"CHOWN_O", "CHOWN_OK"},
  {"CHOWN_", "CHOWN_OK"},
  {"CHOWN", "CHOWN_OK"},
  {"CHOW", "CHOWN_OK"},
  {"CHO", "CHOWN_OK"},
  {"CH", "CHOWN_OK"},
  {"DAR", "DARK"},
  {"DA", "DARK"},
  {"GOIN", "GOING"},
  {"GOI", "GOING"},
  {"GO", "GOING"},
  {"HAVE", "HAVEN"},
  {"HAV", "HAVEN"},
  {"INHERI", "INHERIT"},
  {"INHER", "INHERIT"},
  {"INHE", "INHERIT"},
  {"INH", "INHERIT"},
  {"IN", "INHERIT"},
  {"I", "INHERIT"},
  {"LINK_O", "LINK_OK"},
  {"LINK_", "LINK_OK"},
  {"LINK", "LINK_OK"},
  {"LIN", "LINK_OK"},
  {"OPAQU", "OPAQUE"},
  {"OPAQ", "OPAQUE"},
  {"OPA", "OPAQUE"},
  {"OP", "OPAQUE"},
  {"O", "OPAQUE"},
  {"QUIE", "QUIET"},
  {"QUI", "QUIET"},
  {"QU", "QUIET"},
  {"Q", "QUIET"},
  {"STICK", "STICKY"},
  {"STIC", "STICKY"},
  {"STI", "STICKY"},
  {"ST", "STICKY"},
  {"UNFINDABL", "UNFINDABLE"},
  {"UNFINDABL", "UNFINDABLE"},
  {"UNFINDAB", "UNFINDABLE"},
  {"UNFINDA", "UNFINDABLE"},
  {"UNFIND", "UNFINDABLE"},
  {"UNFIN", "UNFINDABLE"},
  {"UNFI", "UNFINDABLE"},
  {"UNF", "UNFINDABLE"},
  {"UN", "UNFINDABLE"},
  {"U", "UNFINDABLE"},
  {"VISUA", "VISUAL"},
  {"VISU", "VISUAL"},
  {"VIS", "VISUAL"},
  {"VI", "VISUAL"},
  {"WIZAR", "WIZARD"},
  {"WIZA", "WIZARD"},
  {"WIZ", "WIZARD"},
  {"WI", "WIZARD"},
  {"W", "WIZARD"},
  {"SAF", "SAFE"},
  {"SA", "SAFE"},
  {"AUDIBL", "AUDIBLE"},
  {"AUDIB", "AUDIBLE"},
  {"AUDI", "AUDIBLE"},
  {"AUD", "AUDIBLE"},
  {"AU", "AUDIBLE"},
  {"DEBU", "DEBUG"},
  {"DEB", "DEBUG"},
  {"TRACE", "DEBUG"},
  {"TRAC", "DEBUG"},
  {"ENTER_O", "ENTER_OK"},
  {"ENTER_", "ENTER_OK"},
  {"ENTER", "ENTER_OK"},
  {"ENTE", "ENTER_OK"},
  {"ENT", "ENTER_OK"},
  {"EN", "ENTER_OK"},
#ifdef USE_WARNINGS
  {"NO_WAR", "NO_WARN"},
  {"NO_WA", "NO_WARN"},
  {"NO_W", "NO_WARN"},
  {"NOWARN", "NO_WARN"},
  {"NOWAR", "NO_WARN"},
  {"NOWA", "NO_WARN"},
#endif
  {"HAL", "HALT"},
  {"NO_COMMAN", "NO_COMMAND"},
  {"NO_COMMA", "NO_COMMAND"},
  {"NO_COMM", "NO_COMMAND"},
  {"NO_COM", "NO_COMMAND"},
  {"NO_CO", "NO_COMMAND"},
  {"NO_C", "NO_COMMAND"},
#ifdef ROYALTY_FLAG
  {"ROYALT", "ROYALTY"},
  {"ROYAL", "ROYALTY"},
  {"ROYA", "ROYALTY"},
  {"ROY", "ROYALTY"},
#endif
  {"TRANSPAREN", "TRANSPARENT"},
  {"TRANSPARE", "TRANSPARENT"},
  {"TRANSPAR", "TRANSPARENT"},
  {"TRANSPA", "TRANSPARENT"},
  {"TRANSP", "TRANSPARENT"},
  {"TRANS", "TRANSPARENT"},
  {"TRAN", "TRANSPARENT"},
  {"TRA", "TRANSPARENT"},
  {"TR", "TRANSPARENT"},
  {"VERBOS", "VERBOSE"},
  {"VERBO", "VERBOSE"},
  {"VERB", "VERBOSE"},
  {"VER", "VERBOSE"},
  {"VE", "VERBOSE"},
  {"ANS", "ANSI"},
  {"AN", "ANSI"},
  {"LISTENER", "MONITOR"},
  {"LISTENE", "MONITOR"},
  {"LISTEN", "MONITOR"},
  {"LISTE", "MONITOR"},
  {"LIST", "MONITOR"},
  {"LIS", "MONITOR"},
  {"MONITO", "MONITOR"},
  {"MONIT", "MONITOR"},
  {"MONI", "MONITOR"},
  {"MON", "MONITOR"},
  {"MO", "MONITOR"},
  {"NOSPOO", "NOSPOOF"},
  {"NOSPO", "NOSPOOF"},
  {"NOSP", "NOSPOOF"},
  {"NOS", "NOSPOOF"},
  {"ZON", "ZONE"},
  {"ZO", "ZONE"},
  {"Z", "ZONE"},
  {"CONNECTE", "CONNECTED"},
  {"CONNECT", "CONNECTED"},
  {"CONNEC", "CONNECTED"},
  {"CONNE", "CONNECTED"},
  {"CONN", "CONNECTED"},
  {"CON", "CONNECTED"},
  {"CO", "CONNECTED"},
  {"GAG", "GAGGED"},
  {"MYOPI", "MYOPIC"},
  {"MYOP", "MYOPIC"},
  {"MYO", "MYOPIC"},
  {"MY", "MYOPIC"},
  {"TERS", "TERSE"},
  {"TER", "TERSE"},
#ifdef EXTENDED_ANSI
  {"COLOUR", "COLOR"},
  {"COLOU", "COLOR"},
  {"COLO", "COLOR"},
  {"COL", "COLOR"},
  {"FORCEWHITE", "FORCE_WHITE"},
  {"WHITE", "FORCE_WHITE"},
  {"WHIT", "FORCE_WHITE"},
  {"WHI", "FORCE_WHITE"},
  {"WEIRDANSI", "FORCE_WHITE"},
#endif
#ifdef JURY_OK
  {"JURYOK", "JURY_OK"},
  {"JURY", "JURY_OK"},
  {"JUR", "JURY_OK"},
  {"JUDG", "JUDGE"},
  {"JUD", "JUDGE"},
#endif
#ifdef FIXED_FLAG
  {"FIXE", "FIXED"},
  {"FIX", "FIXED"},
  {"FI", "FIXED"},
#endif
#ifdef ONLINE_REG
  {"UNREGISTER", "UNREGISTERED"},
  {"UNREGIST", "UNREGISTERED"},
  {"UNREGIS", "UNREGISTERED"},
  {"UNREGI", "UNREGISTERED"},
  {"UNREG", "UNREGISTERED"},
  {"UNRE", "UNREGISTERED"},
  {"UNR", "UNREGISTERED"},
#endif
#ifdef VACATION_FLAG
  {"ON-VACATI", "ON-VACATION"},
  {"ON-VACAT", "ON-VACATION"},
  {"ON-VACA", "ON-VACATION"},
  {"ON-VAC", "ON-VACATION"},
  {"ON-VA", "ON-VACATION"},
  {"ON-V", "ON-VACATION"},
  {"VACATION", "ON-VACATION"},
  {"VACATIO", "ON-VACATION"},
  {"VACATI", "ON-VACATION"},
  {"VACAT", "ON-VACATION"},
  {"VACA", "ON-VACATION"},
  {"VAC", "ON-VACATION"},
#endif
  {"SUSPEC", "SUSPECT"},
  {"SUSPE", "SUSPECT"},
  {"SUSP", "SUSPECT"},
  {"SUS", "SUSPECT"},
  {"SU", "SUSPECT"},
  {"DEST_OK", "DESTROY_OK"},
  {"DESTROY_O", "DESTROY_OK"},
  {"DESTROY_", "DESTROY_OK"},
  {"DESTROY", "DESTROY_OK"},
  {"DESTRO", "DESTROY_OK"},
  {"DESTR", "DESTROY_OK"},
  {"DEST", "DESTROY_OK"},
  {"DES", "DESTROY_OK"},
  {"DE", "DESTROY_OK"},
  {"PUPPE", "PUPPET"},
  {"PUPP", "PUPPET"},
  {"PUP", "PUPPET"},
  {"PU", "PUPPET"},
  {"P", "PUPPET"},
  {"NO_LEAV", "NO_LEAVE"},
  {"NO_LEA", "NO_LEAVE"},
  {"NO_LE", "NO_LEAVE"},
  {"NO_L", "NO_LEAVE"},
  {"NOLEAVE", "NO_LEAVE"},
  {"NOLEAV", "NO_LEAVE"},
  {"NOLEA", "NO_LEAVE"},
  {"NOLE", "NO_LEAVE"},
  {"NOL", "NO_LEAVE"},
  {"ABOD", "ABODE"},
  {"ABO", "ABODE"},
  {"AB", "ABODE"},
  {"FLOATIN", "FLOATING"},
  {"FLOATI", "FLOATING"},
  {"FLOAT", "FLOATING"},
  {"FLOA", "FLOATING"},
  {"FLO", "FLOATING"},
  {"FL", "FLOATING"},
  {"F", "FLOATING"},
  {"JUMP_O", "JUMP_OK"},
  {"JUMP_", "JUMP_OK"},
  {"JUMP", "JUMP_OK"},
  {"JUM", "JUMP_OK"},
  {"JU", "JUMP_OK"},
  {"J", "JUMP_OK"},
  {"NO_TE", "NO_TEL"},
  {"NO_T", "NO_TEL"},
  {"Z_TE", "Z_TEL"},
  {"Z_T", "Z_TEL"},
#ifdef UNINSPECTED_FLAG
  {"UNINSPECT", "UNINSPECTED"},
  {"UNINSPEC", "UNINSPECTED"},
  {"UNINSPE", "UNINSPECTED"},
  {"UNINSP", "UNINSPECTED"},
  {"UNINS", "UNINSPECTED"},
  {"UNIN", "UNINSPECTED"},
  {"UNI", "UNINSPECTED"},
#endif

  {NULL, NULL}
};

/*   Name      Flag   */
POWER power_table[] =
{
  {"Announce", CAN_WALL},
  {"Boot", CAN_BOOT},
  {"Builder", CAN_BUILD},
  {"Cemit", CEMIT},
  {"Chat_Privs", CHAT_PRIVS},
  {"Functions", GLOBAL_FUNCS},
  {"Guest", IS_GUEST},
  {"Halt", HALT_ANYTHING},
  {"Hide", CAN_HIDE},
  {"Idle", UNLIMITED_IDLE},
  {"Immortal", NO_PAY | NO_QUOTA | UNKILLABLE},
  {"Login", LOGIN_ANYTIME},
  {"Long_Fingers", LONG_FINGERS},
  {"No_Pay", NO_PAY},
  {"No_Quota", NO_QUOTA},
  {"Open_Anywhere", OPEN_ANYWHERE},
  {"Pemit_All", PEMIT_ALL},
  {"Player_Create", CREATE_PLAYER},
  {"Poll", SET_POLL},
  {"Queue", HUGE_QUEUE},
  {"Quotas", CHANGE_QUOTAS},
  {"Search", SEARCH_ALL},
  {"See_All", SEE_ALL},
  {"See_Queue", PS_ALL},
  {"Tport_Anything", TEL_OTHER},
  {"Tport_Anywhere", TEL_ANYWHERE},
  {"Unkillable", UNKILLABLE},
  {NULL, 0}
};

static POWER_ALIAS power_alias_tab[] =
{
  {"@cemit", "Cemit"},
  {"@wall", "Announce"},
  {"wall", "Announce"},
  {NULL, NULL}
};




/*---------------------------------------------------------------------------
 * Flag hash table handlers 
 */


static FLAG *
flag_hash_lookup(name)
    const char *name;
{
  FLAG *t;

  t = (FLAG *) hashfind(strupper(name), &htab_flag);
  if (t)
    return t;

  /* provided for backwards compatibility: type flag checking */
  for (t = type_table; t->name != NULL; t++)
    if (string_prefix(name, t->name))
      return t;

  return NULL;
}

static void
flag_hash_insert(name, entry)
    const char *name;
    FLAG *entry;
{
  hashadd(name, (void *) entry, &htab_flag);
}

void
init_flag_hashtab()
{
  FLAG *f;
  FLAG_ALIAS *a;

  hashinit(&htab_flag, 128);

  /* do regular flags first */
  for (f = flag_table; f->name; f++)
    flag_hash_insert(f->name, f);

  /* now add in the aliases */
  for (a = flag_alias_tab; a->alias; a++) {
    if ((f = flag_hash_lookup(a->realname)) != NULL)
      flag_hash_insert(a->alias, (FLAG *) f);
    else
      fprintf(stderr,
	      "FLAG INIT: flag alias %s matches no known flag.\n",
	      a->alias);
  }
}

/*---------------------------------------------------------------------------
 * Other functions dealing with flags
 */

int
can_set_flag(player, thing, flagp, negate)
    dbref player;
    dbref thing;
    FLAG *flagp;
    int negate;
{
  /* returns 1 if player can set a flag on thing. */
  int myperms;

  if (!flagp || !GoodObject(player) || !GoodObject(thing))
    return 0;

  myperms = negate ? flagp->negate_perms : flagp->perms;

  if (myperms & F_INTERNAL)
    return 0;

  if ((flagp->type != NOTYPE) && (flagp->type != Typeof(thing)))
    return 0;

  if ((myperms & F_INHERIT) && !Wizard(player) &&
      (!Inherit(player) || !Owns(player, thing)))
    return 0;

  /* You've got to *own* something (or be Wizard) to set it
   * chown_ok or dest_ok. This prevents subversion of the
   * zone-restriction on @chown and @dest
   */
  if (((flagp->flag == CHOWN_OK) && (flagp->type == NOTYPE)) ||
      ((flagp->flag == THING_DEST_OK) && (flagp->type == TYPE_THING))) {
    if (!Owns(player, thing) && !Wizard(player))
      return 0;
    else
      return 1;
  }
  if (myperms & F_ANY)
    return 1;
  if ((myperms & F_WIZARD) && !Wizard(player))
    return 0;
  else if ((myperms & F_ROYAL) && !Hasprivs(player))
    return 0;
  else if ((myperms & F_GOD) && !God(player))
    return 0;


  /* Checking for the ZONE flag. If you set this, the player had
   * better be @elocked! 
   */

  if ((flagp->type == TYPE_PLAYER) && (flagp->flag == PLAYER_ZONE) &&
      !negate && (getlock(thing, Zone_Lock) == TRUE_BOOLEXP)) {
    notify(player, "You must @elock before you can set a player ZONE");
    return 0;
  }
  /* special case for the privileged flags. We do need to check
   * for royalty setting flags on objects they don't own, because
   * the controls check will not stop the flag set if the royalty
   * player is in a zone. Also, only God can set the wizbit on
   * players.
   */

  if (Wizard(thing) && (flagp->flag == PLAYER_GAGGED) &&
      (flagp->type == TYPE_PLAYER))
    return 0;			/* can't gag wizards/God */

  if (God(player))		/* God can do (almost) anything) */
    return 1;

  /* Make sure we don't accidentally permission-check toggles when
   * checking priv bits.
   */
  if (flagp->type == NOTYPE) {

    /* A wiz can set things he owns WIZ, but can't reset his own bit. */
    if (flagp->flag == WIZARD) {
      return (Wizard(player) && Owns(player, thing) &&
	      (Typeof(thing) != TYPE_PLAYER));
    }
    /* Wizards can set or unset anything royalty. Royalty can set anything
     * they own royalty, but cannot reset their own bits. */
#ifdef ROYALTY_FLAG
    if (flagp->flag == ROYALTY) {
      return (!Guest(thing) && (Wizard(player) ||
				(Royalty(player) && Owns(player, thing) &&
				 (Typeof(thing) != TYPE_PLAYER))));
    }
#endif
  }
  return 1;
}

const char *
unparse_flags(thing, player)
    dbref thing;
    dbref player;
{
  /* print out the flag symbols (letters) */

  static char buf[BUFFER_LEN];
  char *p;
  static const char type_codes[] = "R-EP--*";
  FLAG *f;
  int t;

  p = buf;
  t = Typeof(thing);
  if (t != TYPE_THING)
    *p++ = type_codes[t];

  /* get generic flags */
  for (f = flag_table; f->type == NOTYPE; f++) {
    if ((Flags(thing) & f->flag) && Can_See_Flag(player, thing, f))
      *p++ = f->letter;
  }

  /* exits have no special flags. This may change in the future */
  if (t == TYPE_EXIT || t == TYPE_GARBAGE) {
    *p = '\0';
    return buf;
  }
  /* go to beginning of list of flags for the type and print flags for
   * that type.
   */
  while (f->type != t)
    f++;
  for (; f->type == t; f++) {
    if ((Toggles(thing) & f->flag) && Can_See_Flag(player, thing, f))
      *p++ = f->letter;
  }

  *p = '\0';

  return buf;
}

const char *
flag_description(player, thing)
    dbref player;
    dbref thing;
{
  static char buf[BUFFER_LEN];
  char fbuf[BUFFER_LEN];
  char *bp;
  FLAG *f;
  int t;

  t = Typeof(thing);

  strcpy(buf, "Type: ");
  switch (t) {
  case TYPE_ROOM:
    strcat(buf, "Room");
    break;
  case TYPE_EXIT:
    strcat(buf, "Exit");
    break;
  case TYPE_THING:
    strcat(buf, "Thing");
    break;
  case TYPE_PLAYER:
    strcat(buf, "Player");
    break;
  default:
    strcat(buf, "***UNKNOWN TYPE***");
    break;
  }
  strcat(buf, " Flags:");

  bp = fbuf;

  /* get generic flags */
  for (f = flag_table; f->type == NOTYPE; f++) {
    if ((Flags(thing) & f->flag) && Can_See_Flag(player, thing, f)) {
      safe_chr(' ', fbuf, &bp);
      safe_str(f->name, fbuf, &bp);
    }
  }

  /* go to beginning of list of flags for the type and print flags for
   * that type.
   */
  while ((f->type != t) && (f->type != NOTYPE))
    f++;
  for (; f->type == t; f++) {
    if ((Toggles(thing) & f->flag) && Can_See_Flag(player, thing, f)) {
      safe_chr(' ', fbuf, &bp);
      safe_str(f->name, fbuf, &bp);
    }
  }

  *bp = '\0';

  /* no length checking needed. We're never going to have an overflow. */
  sprintf(buf, "%s%s", buf, fbuf);
  return buf;
}


/* Used to show the default toggle configuration from do_config_list */
const char *
togglemask_to_string(type, mask)
    int type;
    object_flag_type mask;
{
  static char fbuf[BUFFER_LEN];
  char *bp;
  FLAG *f;

  bp = fbuf;
  f = flag_table;
  while (f->type != type)
    f++;
  for (; f->type == type; f++) {
    if (mask & f->flag) {
      if (bp != fbuf)
	safe_chr(' ', fbuf, &bp);
      safe_str(f->name, fbuf, &bp);
    }
  }
  *bp = '\0';
  return fbuf;
}

#ifdef CAN_NEWSTYLE
static FLAG *
letter_to_flagptr(char c, int type, int *toggle)
#else
static FLAG *
letter_to_flagptr(c, type, toggle)
    char c;
    int type;
    int *toggle;
#endif
{
  /* convert letter to flag */

  FLAG *f;

  *toggle = 0;

  /* try generic flags */
  for (f = flag_table; f->type == NOTYPE; f++) {
    if (c == f->letter)
      return (f);
  }

  /* try type-specific flags */

  *toggle = 1;
  while ((f->type != type) && (f->type != NOTYPE))
    f++;

  for (; f->type != NOTYPE; f++) {
    if (c == f->letter)
      return (f);
  }
  return NULL;
}

#ifdef CAN_NEWSTYLE
object_flag_type
letter_to_flag(char c, int type, int *toggle)
#else
object_flag_type
letter_to_flag(c, type, toggle)
    char c;
    int type;
    int *toggle;
#endif
{
  FLAG *f = letter_to_flagptr(c, type, toggle);
  if (f)
    return (f->flag);
  return -1;
}

object_flag_type
find_flag(name, type, toggle, is_conf)
    char *name;
    int type;
    int *toggle;
    int is_conf;
{
  /* given a flag name and an object type, return a flag, and set the
   * value of toggle to 0 if flag, 1 if toggle. We also check if it's
   * a legal flag to set, if it's a conf flag set.
   */

  FLAG *f;

  *toggle = 0;

  if ((f = flag_hash_lookup(upcasestr(name))) == NULL)
    return -1;

  if (is_conf && (f->perms == F_INTERNAL))
    return -1;

  if (f->type == NOTYPE) {
    return f->flag;
  } else {
    *toggle = 1;
    if (type != f->type) {
      if (is_conf)
	return -2;
      else
	return -1;
    } else {
      return f->flag;
    }
  }
  return f->flag;		/* NOTREACHED */
}

void
decompile_flags(player, thing, name)
    dbref player;
    dbref thing;
    const char *name;
{
  /* print out the flags for a decompile */

  FLAG *f;
  int t, ok;

  t = Typeof(thing);
  ok = !Suspect(thing);

  /* do generic flags */
  for (f = flag_table; f->type == NOTYPE; f++)
    if ((Flags(thing) & f->flag) && !(f->perms & F_INTERNAL) &&
	Can_See_Flag(player, thing, f))
      notify(player, tprintf("@set %s = %s", name, f->name));

  /* do normal flags */
  while ((f->type != t) && (f->type != NOTYPE))
    f++;
  for (; f->type == t; f++)
    if ((Toggles(thing) & f->flag) && Can_See_Flag(player, thing, f))
      notify(player, tprintf("@set %s = %s", name, f->name));
}


void
decompile_powers(player, thing, name)
    dbref player;
    dbref thing;
    const char *name;
{
  /* print out the powers for a decompile */

  POWER *p;

  for (p = power_table; p->name; p++) {
    /* Special case for immortal, which we don't show any more */
    if (!strcasecmp(p->name, "immortal"))
      continue;
    if (Powers(thing) & p->flag)
      notify(player, tprintf("@power %s = %s", name, p->name));
  }
}

int
convert_flags(player, s, p_mask, p_toggle, p_type)
    dbref player;
    char *s;
    object_flag_type *p_mask;
    object_flag_type *p_toggle;
    object_flag_type *p_type;
{
  /* convert flags for search */

  FLAG *f;
  object_flag_type mask, toggle, type;
  int done;
  mask = toggle = 0;
  type = NOTYPE;

  while (s && *s) {

    done = 0;

    switch (*s) {
    case 'P':
      type = TYPE_PLAYER;
      break;
    case 'R':
      type = TYPE_ROOM;
      break;
    case 'E':
      type = TYPE_EXIT;
      break;
    default:

      /* check generic flags first */
      for (f = flag_table; (f->type == NOTYPE) && !done; f++) {
	if (*s == f->letter) {
	  mask |= f->flag;
	  done = 1;
	}
      }

      /* try type-specific flags */

      /* if we have a type, start searching from that type */
      if (!done && (type != NOTYPE))
	while ((f->type != type) && (f->type != NOTYPE))
	  f++;

      for (; !done && (f->type != NOTYPE); f++) {
	if (*s == f->letter) {
	  /* if we don't have a type yet, we do now. If we do, we need
	   * to check for a conflict.
	   */
	  if (type == NOTYPE) {
	    type = f->type;
	    toggle |= f->flag;
	    done = 1;
	  } else if (type != f->type) {
	    notify(player, tprintf("Type conflict with flag '%c'.", *s));
	    return 0;
	  } else {
	    /* We've got a match */
	    toggle |= f->flag;
	    done = 1;
	  }
	}
      }

      /* if we get this far and still haven't found anything, error. */
      if (!done) {
	notify(player, tprintf("%c: unknown flag.", *s));
	return 0;
      }
    }
    s++;
  }

  *p_mask = mask;
  *p_toggle = toggle;
  *p_type = type;
  return 1;
}

static FLAG mon_table[] =
{
  {"MONITOR", 'M', TYPE_PLAYER, PLAYER_MONITOR, F_ROYAL},
  {"MONITOR", 'M', TYPE_THING, THING_LISTEN, F_ANY},
  {"MONITOR", 'M', TYPE_ROOM, ROOM_LISTEN, F_ANY},
  {NULL, '\0', 0, 0, 0}
};

void
set_flag(player, thing, flag, negate, hear, listener)
    dbref player;
    dbref thing;
    char *flag;
    int negate;
    int hear;
    int listener;
{
  /* attempt to set a flag on an object */

  FLAG *f;
  char tbuf1[BUFFER_LEN];

  if ((f = flag_hash_lookup(strupper(flag))) == NULL) {
    notify(player, "I don't recognize that flag.");
    return;
  }
  /* HORRIBLE HACK: added to make MONITOR work. This needs to
   * be fixed in the future, _somehow_...
   */
  if (!strcmp(f->name, "MONITOR")) {
    for (f = mon_table; f->name != NULL; f++)
      if (Typeof(thing) == f->type)
	break;
    if (f->name == NULL) {
      notify(player, "Permission denied.");
      return;
    }
  }
  if (!can_set_flag(player, thing, f, negate)) {
    notify(player, "Permission denied.");
    return;
  }
  /* The only players who can be Dark are wizards. */
  if ((f->flag == DARK) && !negate && (f->type == NOTYPE) &&
      Alive(thing) && !Wizard(thing)) {
    notify(player, "Permission denied.");
    return;
  }
  if (negate) {

    if ((f->flag == GOING) && (f->type == NOTYPE)) {
      /* This is, frankly, a bit of a hack. */
      notify(player, "@set !GOING has been disabled. Use @undestroy instead.");
      return;
    }
    /* remove the flag */
    if (f->type == NOTYPE)
      Flags(thing) &= ~(f->flag);
    else
      Toggles(thing) &= ~(f->flag);

    /* log if necessary */
    if ((f->flag == WIZARD) && (f->type == NOTYPE))
      do_log(LT_WIZ, player, thing, "WIZFLAG RESET");
#ifdef ROYALTY_FLAG
    else if ((f->flag == ROYALTY) && (f->type == NOTYPE))
      do_log(LT_WIZ, player, thing, "ROYAL FLAG RESET");
#endif
    else if ((f->flag == PLAYER_SUSPECT) && (f->type == TYPE_PLAYER))
      do_log(LT_WIZ, player, thing, "SUSPECT FLAG RESET");

    /* those who unDARK return to the WHO */
    if ((f->flag == DARK) && (f->type == NOTYPE) &&
	(Typeof(thing) == TYPE_PLAYER))
      hide_player(thing, 0);

    /* notify the area if something stops listening */
    if ((Typeof(thing) == TYPE_THING) && (f->type == TYPE_THING) &&
	(((f->flag == THING_PUPPET) && !listener && !Hearer(thing)) ||
	 ((f->flag == THING_LISTEN) && !hear && !Listener(thing)))) {
      sprintf(tbuf1, "%s is no longer listening.", Name(thing));
      notify_except(Contents(Location(thing)), NOTHING, tbuf1);
    }
    if ((f->flag == AUDIBLE) && (f->type == NOTYPE)) {
      switch (Typeof(thing)) {
      case TYPE_EXIT:
	if (Flags(Exits(thing)) & AUDIBLE) {
	  sprintf(tbuf1, "Exit %s is no longer broadcasting.", Name(thing));
	  notify_except(Contents(Exits(thing)), NOTHING, tbuf1);
	}
	break;
      case TYPE_ROOM:
	notify_except(Contents(thing), NOTHING,
		    "Audible exits in this room have been deactivated.");
	break;
      case TYPE_THING:
      case TYPE_PLAYER:
	notify_except(Contents(thing), thing,
		      "This room is no longer broadcasting.");
	notify(thing, "Your contents can no longer be heard from outside.");
	break;
      }
    }
    if (((f->flag == QUIET) && (f->type == NOTYPE)) ||
	(!Quiet(player) && !Quiet(thing)))
      notify(player, "Flag reset.");

  } else {

    /* set the flag */
    if (f->type == NOTYPE)
      Flags(thing) |= f->flag;
    else
      Toggles(thing) |= f->flag;

    /* log if necessary */
    if ((f->flag == WIZARD) && (f->type == NOTYPE))
      do_log(LT_WIZ, player, thing, "WIZFLAG SET");
#ifdef ROYALTY_FLAG
    if ((f->flag == ROYALTY) && (f->type == NOTYPE))
      do_log(LT_WIZ, player, thing, "ROYAL FLAG SET");
#endif
    else if ((f->flag == PLAYER_SUSPECT) && (f->type == TYPE_PLAYER))
      do_log(LT_WIZ, player, thing, "SUSPECT FLAG SET");

    if ((f->flag == INHERIT) && GoodObject(Zone(thing))) {
      notify(player, "Warning: Setting inherit flag on zoned object");
    }
    /* DARK players should be treated as logged out */
    if ((f->flag == DARK) && (f->type == NOTYPE) &&
	(Typeof(thing) == TYPE_PLAYER))
      hide_player(thing, 1);

    /* notify area if something starts listening */
    if ((Typeof(thing) == TYPE_THING) && (f->type == TYPE_THING) &&
	((f->flag == THING_PUPPET) || (f->flag == THING_LISTEN)) &&
	!hear && !listener) {
      sprintf(tbuf1, "%s is now listening.", Name(thing));
      notify_except(Contents(Location(thing)), NOTHING, tbuf1);
    }
    /* notify for audible exits */
    if ((f->flag == AUDIBLE) && (f->type == NOTYPE)) {
      switch (Typeof(thing)) {
      case TYPE_EXIT:
	if (db[db[thing].exits].flags & AUDIBLE) {
	  sprintf(tbuf1, "Exit %s is now broadcasting.", Name(thing));
	  notify_except(Contents(Exits(thing)), NOTHING, tbuf1);
	}
	break;
      case TYPE_ROOM:
	notify_except(Contents(thing), NOTHING,
		      "Audible exits in this room have been activated.");
	break;
      case TYPE_PLAYER:
      case TYPE_THING:
	notify_except(Contents(thing), thing,
		      "This room is now broadcasting.");
	notify(thing, "Your contents can now be heard from outside.");
	break;
      }
    }
    if (((f->flag == QUIET) && (f->flag == NOTYPE)) ||
	(!Quiet(player) && !Quiet(thing)))
      notify(player, "Flag set.");
  }
}

int
handle_flaglists(player, name, fstr, type)
    dbref player;
    char *name;
    char *fstr;
    int type;



				/* 0 for orflags, 1 for andflags */
{
  char *s;
  FLAG *fp;
  int toggle, negate, temp;
  int ret = type;
  dbref it = match_thing(player, name);

  toggle = negate = temp = 0;

  if (it == NOTHING)
    return 0;

  for (s = fstr; *s; s++) {

    /* Check for a negation sign. If we find it, we note it and 
     * increment the pointer to the next character.
     */

    if (*s == '!') {
      negate = 1;
      s++;
    } else {
      negate = 0;		/* It's important to clear this at appropriate times;
				 * else !Dc means (!D && !c), instead of (!D && c). */
    }

    if (!*s) {
      /* We got a '!' that wasn't followed by a letter.
       * Fail the check. */
      return (type == 1) ? 0 : ret;
    }
    /* Find the flag. */
    if ((fp = letter_to_flagptr(*s, Typeof(it), &toggle)) == NULL) {

      /* Either we got a '!' that wasn't followed by a letter, or
       * we couldn't find that flag. For AND, since we've failed
       * a check, we can return false. Otherwise we just go on.
       */

      if (type == 1)
	return 0;
      else
	continue;

    } else {

      /* does the object have this flag? */

      if ((!toggle && (Flags(it) & fp->flag)) ||
	  (toggle && (Toggles(it) & fp->flag) &&
	   Can_See_Flag(player, it, fp)))
	temp = 1;
      else
	temp = 0;

      if ((type == 1) && ((negate && temp) || (!negate && !temp))) {

	/* Too bad there's no NXOR function...
	 * At this point we've either got a flag and we don't want
	 * it, or we don't have a flag and we want it. Since it's
	 * AND, we return false.
	 */
	return 0;

      } else if ((type == 0) &&
		 ((!negate && temp) || (negate && !temp))) {

	/* We've found something we want, in an OR. We OR a
	 * true with the current value.
	 */

	ret |= 1;
      }
      /* Otherwise, we don't need to do anything. */
    }
  }
  return (ret);
}


int
sees_flag(privs, thing, name)
    dbref privs;
    dbref thing;
    char *name;
{
  /* Does thing have the flag named name && can privs see it? */
  FLAG *f;
  int retval;
  if ((f = flag_hash_lookup(upcasestr(name))) == NULL)
    return 0;

  if (f->type == NOTYPE) {
    retval = Flags(thing) & f->flag;
  } else {
    if (Typeof(thing) != f->type) {
      return 0;
    } else {
      retval = Toggles(thing) & f->flag;
    }
  }
  return retval && Can_See_Flag(privs, thing, f);
}


const char *
power_description(thing)
    dbref thing;
{
  static char fbuf[BUFFER_LEN];
  char *bp;
  POWER *p;

  bp = fbuf;

  for (p = power_table; p->name; p++) {
    /* Special case for immortal, which we don't show any more */
    if (!strcasecmp(p->name, "immortal"))
      continue;
    if (Powers(thing) & p->flag) {
      if (bp != fbuf)
	safe_chr(' ', fbuf, &bp);
      safe_str(p->name, fbuf, &bp);
    }
  }
  *bp = '\0';

  return fbuf;
}

object_flag_type
find_power(name)
    const char *name;
{
  POWER *p;
  POWER_ALIAS *a;

  for (p = power_table; p->name; p++) {
    if (string_prefix(p->name, name))
      return p->flag;
  }

  /* Check the alias table */
  for (a = power_alias_tab; a->alias; a++) {
    if (string_prefix(a->alias, name))
      return find_power(a->realname);
  }

  /* Got nothing. Return -1 */
  return -1;
}