// flags.cpp -- Flag manipulation routines.
//
// $Id: flags.cpp 2734 2007-10-28 23:02:55Z brazilofmux $
//

#include "copyright.h"
#include "autoconf.h"
#include "config.h"
#include "externs.h"

#include "command.h"
#include "powers.h"
#if defined(FIRANMUX)
#include "attrs.h"
#endif // FIRANMUX
#include "ansi.h"

/* ---------------------------------------------------------------------------
 * fh_any: set or clear indicated bit, no security checking
 */

static bool fh_any(dbref target, dbref player, FLAG flag, int fflags, bool reset)
{
    // Never let God drop his/her own wizbit.
    //
    if (  God(target)
       && reset
       && flag == WIZARD
       && fflags == FLAG_WORD1)
    {
        notify(player, "You cannot make God mortal.");
        return false;
    }

    // Otherwise we can go do it.
    //
    if (reset)
    {
        db[target].fs.word[fflags] &= ~flag;
    }
    else
    {
        db[target].fs.word[fflags] |= flag;
    }
    return true;
}

/* ---------------------------------------------------------------------------
 * fh_god: only GOD may set or clear the bit
 */

static bool fh_god(dbref target, dbref player, FLAG flag, int fflags, bool reset)
{
    if (!God(player))
    {
        return false;
    }
    return (fh_any(target, player, flag, fflags, reset));
}

/*
 * ---------------------------------------------------------------------------
 * * fh_wiz: only WIZARDS (or GOD) may set or clear the bit
 */

static bool fh_wiz(dbref target, dbref player, FLAG flag, int fflags, bool reset)
{
    if (!Wizard(player))
    {
        return false;
    }
    return (fh_any(target, player, flag, fflags, reset));
}

/*
 * ---------------------------------------------------------------------------
 * * fh_wizroy: only WIZARDS, ROYALTY, (or GOD) may set or clear the bit
 */

static bool fh_wizroy(dbref target, dbref player, FLAG flag, int fflags, bool reset)
{
    if (!WizRoy(player))
    {
        return false;
    }
    return (fh_any(target, player, flag, fflags, reset));
}

/*
 * ---------------------------------------------------------------------------
 * * fh_restrict_player (renamed from fh_fixed): Only Wizards can set
 * * this on players, but ordinary players can set it on other types
 * * of objects.
 */
static bool fh_restrict_player
(
    dbref target,
    dbref player,
    FLAG flag,
    int fflags,
    bool reset
)
{
    if (  isPlayer(target)
       && !Wizard(player))
    {
        return false;
    }
    return (fh_any(target, player, flag, fflags, reset));
}

/* ---------------------------------------------------------------------------
 * fh_privileged: You can set this flag on a non-player object, if you
 * yourself have this flag and are a player who owns themselves (i.e.,
 * no robots). Only God can set this on a player.
 */
static bool fh_privileged
(
    dbref target,
    dbref player,
    FLAG flag,
    int fflags,
    bool reset
)
{
    if (!God(player))
    {
        if (  isPlayer(target)
#if !defined(FIRANMUX)
           || !isPlayer(player)
           || player != Owner(player)
#endif // FIRANMUX
           || (db[player].fs.word[fflags] & flag) == 0)
        {
            return false;
        }
    }
    return (fh_any(target, player, flag, fflags, reset));
}

/*
 * ---------------------------------------------------------------------------
 * * fh_inherit: only players may set or clear this bit.
 */

static bool fh_inherit(dbref target, dbref player, FLAG flag, int fflags, bool reset)
{
    if (!Inherits(player))
    {
        return false;
    }
    return (fh_any(target, player, flag, fflags, reset));
}

/*
 * ---------------------------------------------------------------------------
 * * fh_dark_bit: manipulate the dark bit. Nonwizards may not set on players.
 */

static bool fh_dark_bit(dbref target, dbref player, FLAG flag, int fflags, bool reset)
{
    if (  !reset
       && isPlayer(target)
       && !(  (target == player)
           && Can_Hide(player))
       && !Wizard(player))
    {
        return false;
    }
    return (fh_any(target, player, flag, fflags, reset));
}

/*
 * ---------------------------------------------------------------------------
 * * fh_going_bit: manipulate the going bit.  Non-gods may only clear.
 */

static bool fh_going_bit(dbref target, dbref player, FLAG flag, int fflags, bool reset)
{
    if (  Going(target)
       && reset
       && (Typeof(target) != TYPE_GARBAGE))
    {
        notify(player, "Your object has been spared from destruction.");
        return (fh_any(target, player, flag, fflags, reset));
    }
    if (!God(player))
    {
        return false;
    }

    // Even God should not be allowed set protected dbrefs GOING.
    //
    if (  !reset
       && (  target == 0
          || God(target)
          || target == mudconf.start_home
          || target == mudconf.start_room
          || target == mudconf.default_home
          || target == mudconf.master_room))
    {
        return false;
    }
    return (fh_any(target, player, flag, fflags, reset));
}

/*
 * ---------------------------------------------------------------------------
 * * fh_hear_bit: set or clear bits that affect hearing.
 */

static bool fh_hear_bit(dbref target, dbref player, FLAG flag, int fflags, bool reset)
{
    if (isPlayer(target) && (flag & MONITOR))
    {
        if (Can_Monitor(player))
        {
            return (fh_any(target, player, flag, fflags, reset));
        }
        else
        {
            return false;
        }
    }

    bool could_hear = Hearer(target);
    bool result = fh_any(target, player, flag, fflags, reset);
    handle_ears(target, could_hear, Hearer(target));
    return result;
}


/* ---------------------------------------------------------------------------
 * fh_player_bit: Can set and reset this on everything but players.
 */
static bool fh_player_bit
(
    dbref target,
    dbref player,
    FLAG flag,
    int fflags,
    bool reset
)
{
    if (isPlayer(target))
    {
        return false;
    }
    return (fh_any(target, player, flag, fflags, reset));
}

/* ---------------------------------------------------------------------------
 * fh_staff: only STAFF, WIZARDS, ROYALTY, (or GOD) may set or clear
 * the bit.
 */
static bool fh_staff
(
    dbref target,
    dbref player,
    FLAG flag,
    int fflags,
    bool reset
)
{
    if (!Staff(player) && !God(player))
    {
        return false;
    }
    return (fh_any(target, player, flag, fflags, reset));
}

static FLAGBITENT fbeAbode          = { ABODE,        'A',    FLAG_WORD2, 0,                    fh_any};
static FLAGBITENT fbeAnsi           = { ANSI,         'X',    FLAG_WORD2, 0,                    fh_any};
static FLAGBITENT fbeAudible        = { HEARTHRU,     'a',    FLAG_WORD1, 0,                    fh_hear_bit};
static FLAGBITENT fbeAuditorium     = { AUDITORIUM,   'b',    FLAG_WORD2, 0,                    fh_any};
static FLAGBITENT fbeBlind          = { BLIND,        'B',    FLAG_WORD2, 0,                    fh_wiz};
static FLAGBITENT fbeChownOk        = { CHOWN_OK,     'C',    FLAG_WORD1, 0,                    fh_any};
static FLAGBITENT fbeConnected      = { CONNECTED,    'c',    FLAG_WORD2, CA_NO_DECOMP,         fh_god};
static FLAGBITENT fbeDark           = { DARK,         'D',    FLAG_WORD1, 0,                    fh_dark_bit};
static FLAGBITENT fbeDestroyOk      = { DESTROY_OK,   'd',    FLAG_WORD1, 0,                    fh_any};
static FLAGBITENT fbeEnterOk        = { ENTER_OK,     'e',    FLAG_WORD1, 0,                    fh_any};
static FLAGBITENT fbeFixed          = { FIXED,        'f',    FLAG_WORD2, 0,                    fh_restrict_player};
static FLAGBITENT fbeFloating       = { FLOATING,     'F',    FLAG_WORD2, 0,                    fh_any};
static FLAGBITENT fbeGagged         = { GAGGED,       'j',    FLAG_WORD2, 0,                    fh_wiz};
static FLAGBITENT fbeGoing          = { GOING,        'G',    FLAG_WORD1, CA_NO_DECOMP,         fh_going_bit};
static FLAGBITENT fbeHalted         = { HALT,         'h',    FLAG_WORD1, 0,                    fh_any};
static FLAGBITENT fbeHasDaily       = { HAS_DAILY,    '*',    FLAG_WORD2, CA_GOD|CA_NO_DECOMP,  fh_god};
static FLAGBITENT fbeHasForwardList = { HAS_FWDLIST,  '&',    FLAG_WORD2, CA_GOD|CA_NO_DECOMP,  fh_god};
static FLAGBITENT fbeHasListen      = { HAS_LISTEN,   '@',    FLAG_WORD2, CA_GOD|CA_NO_DECOMP,  fh_god};
static FLAGBITENT fbeHasStartup     = { HAS_STARTUP,  '+',    FLAG_WORD1, CA_GOD|CA_NO_DECOMP,  fh_god};
static FLAGBITENT fbeHaven          = { HAVEN,        'H',    FLAG_WORD1, 0,                    fh_any};
static FLAGBITENT fbeHead           = { HEAD_FLAG,    '?',    FLAG_WORD2, 0,                    fh_wiz};
static FLAGBITENT fbeHtml           = { HTML,         '(',    FLAG_WORD2, 0,                    fh_any};
static FLAGBITENT fbeImmortal       = { IMMORTAL,     'i',    FLAG_WORD1, 0,                    fh_wiz};
static FLAGBITENT fbeInherit        = { INHERIT,      'I',    FLAG_WORD1, 0,                    fh_inherit};
static FLAGBITENT fbeJumpOk         = { JUMP_OK,      'J',    FLAG_WORD1, 0,                    fh_any};
static FLAGBITENT fbeKeepAlive      = { CKEEPALIVE,   'k',    FLAG_WORD2, 0,                    fh_any};
static FLAGBITENT fbeKey            = { KEY,          'K',    FLAG_WORD2, 0,                    fh_any};
static FLAGBITENT fbeLight          = { LIGHT,        'l',    FLAG_WORD2, 0,                    fh_any};
static FLAGBITENT fbeLinkOk         = { LINK_OK,      'L',    FLAG_WORD1, 0,                    fh_any};
static FLAGBITENT fbeMonitor        = { MONITOR,      'M',    FLAG_WORD1, 0,                    fh_hear_bit};
static FLAGBITENT fbeMyopic         = { MYOPIC,       'm',    FLAG_WORD1, 0,                    fh_any};
static FLAGBITENT fbeNoCommand      = { NO_COMMAND,   'n',    FLAG_WORD2, 0,                    fh_any};
static FLAGBITENT fbeNoAccents      = { NOACCENTS,    '~',    FLAG_WORD2, 0,                    fh_any};
static FLAGBITENT fbeNoBleed        = { NOBLEED,      '-',    FLAG_WORD2, 0,                    fh_any};
static FLAGBITENT fbeNoSpoof        = { NOSPOOF,      'N',    FLAG_WORD1, 0,                    fh_any};
static FLAGBITENT fbeOpaque         = { TM_OPAQUE,    'O',    FLAG_WORD1, 0,                    fh_any};
static FLAGBITENT fbeOpenOk         = { OPEN_OK,      'z',    FLAG_WORD2, 0,                    fh_wiz};
static FLAGBITENT fbeParentOk       = { PARENT_OK,    'Y',    FLAG_WORD2, 0,                    fh_any};
static FLAGBITENT fbePlayerMails    = { PLAYER_MAILS, ' ',    FLAG_WORD2, CA_GOD|CA_NO_DECOMP,  fh_god};
static FLAGBITENT fbePuppet         = { PUPPET,       'p',    FLAG_WORD1, 0,                    fh_hear_bit};
static FLAGBITENT fbeQuiet          = { QUIET,        'Q',    FLAG_WORD1, 0,                    fh_any};
static FLAGBITENT fbeRobot          = { ROBOT,        'r',    FLAG_WORD1, 0,                    fh_player_bit};
static FLAGBITENT fbeRoyalty        = { ROYALTY,      'Z',    FLAG_WORD1, 0,                    fh_wiz};
static FLAGBITENT fbeSafe           = { SAFE,         's',    FLAG_WORD1, 0,                    fh_any};
static FLAGBITENT fbeSlave          = { SLAVE,        'x',    FLAG_WORD2, CA_WIZARD,            fh_wiz};
static FLAGBITENT fbeStaff          = { STAFF,        'w',    FLAG_WORD2, 0,                    fh_wiz};
static FLAGBITENT fbeSticky         = { STICKY,       'S',    FLAG_WORD1, 0,                    fh_any};
static FLAGBITENT fbeSuspect        = { SUSPECT,      'u',    FLAG_WORD2, CA_WIZARD,            fh_wiz};
static FLAGBITENT fbeTerse          = { TERSE,        'q',    FLAG_WORD1, 0,                    fh_any};
static FLAGBITENT fbeTrace          = { TRACE,        'T',    FLAG_WORD1, 0,                    fh_any};
static FLAGBITENT fbeTransparent    = { SEETHRU,      't',    FLAG_WORD1, 0,                    fh_any};
static FLAGBITENT fbeUnfindable     = { UNFINDABLE,   'U',    FLAG_WORD2, 0,                    fh_any};
static FLAGBITENT fbeUninspected    = { UNINSPECTED,  'g',    FLAG_WORD2, 0,                    fh_wizroy};
static FLAGBITENT fbeVacation       = { VACATION,     '|',    FLAG_WORD2, 0,                    fh_restrict_player};
static FLAGBITENT fbeVerbose        = { VERBOSE,      'v',    FLAG_WORD1, 0,                    fh_any};
static FLAGBITENT fbeVisual         = { VISUAL,       'V',    FLAG_WORD1, 0,                    fh_any};
static FLAGBITENT fbeWizard         = { WIZARD,       'W',    FLAG_WORD1, 0,                    fh_god};
static FLAGBITENT fbeSitemon        = { SITEMON,      '$',    FLAG_WORD3, 0,                    fh_wiz};
#ifdef WOD_REALMS
static FLAGBITENT fbeFae            = { FAE,          '0',    FLAG_WORD3, CA_STAFF,             fh_wizroy};
static FLAGBITENT fbeChimera        = { CHIMERA,      '1',    FLAG_WORD3, CA_STAFF,             fh_wizroy};
static FLAGBITENT fbePeering        = { PEERING,      '2',    FLAG_WORD3, CA_STAFF,             fh_wizroy};
static FLAGBITENT fbeUmbra          = { UMBRA,        '3',    FLAG_WORD3, CA_STAFF,             fh_wizroy};
static FLAGBITENT fbeShroud         = { SHROUD,       '4',    FLAG_WORD3, CA_STAFF,             fh_wizroy};
static FLAGBITENT fbeMatrix         = { MATRIX,       '5',    FLAG_WORD3, CA_STAFF,             fh_wizroy};
static FLAGBITENT fbeObf            = { OBF,          '6',    FLAG_WORD3, CA_STAFF,             fh_wizroy};
static FLAGBITENT fbeHss            = { HSS,          '7',    FLAG_WORD3, CA_STAFF,             fh_wizroy};
static FLAGBITENT fbeMedium         = { MEDIUM,       '8',    FLAG_WORD3, CA_STAFF,             fh_wizroy};
static FLAGBITENT fbeDead           = { DEAD,         '9',    FLAG_WORD3, CA_STAFF,             fh_wizroy};
static FLAGBITENT fbeMarker0        = { MARK_0,       ' ',    FLAG_WORD3, 0,                    fh_god};
static FLAGBITENT fbeMarker1        = { MARK_1,       ' ',    FLAG_WORD3, 0,                    fh_god};
static FLAGBITENT fbeMarker2        = { MARK_2,       ' ',    FLAG_WORD3, 0,                    fh_god};
static FLAGBITENT fbeMarker3        = { MARK_3,       ' ',    FLAG_WORD3, 0,                    fh_god};
static FLAGBITENT fbeMarker4        = { MARK_4,       ' ',    FLAG_WORD3, 0,                    fh_god};
static FLAGBITENT fbeMarker5        = { MARK_5,       ' ',    FLAG_WORD3, 0,                    fh_god};
static FLAGBITENT fbeMarker6        = { MARK_6,       ' ',    FLAG_WORD3, 0,                    fh_god};
static FLAGBITENT fbeMarker7        = { MARK_7,       ' ',    FLAG_WORD3, 0,                    fh_god};
static FLAGBITENT fbeMarker8        = { MARK_8,       ' ',    FLAG_WORD3, 0,                    fh_god};
static FLAGBITENT fbeMarker9        = { MARK_9,       ' ',    FLAG_WORD3, 0,                    fh_god};
#else // WOD_REALMS
static FLAGBITENT fbeMarker0        = { MARK_0,       '0',    FLAG_WORD3, 0,                    fh_god};
static FLAGBITENT fbeMarker1        = { MARK_1,       '1',    FLAG_WORD3, 0,                    fh_god};
static FLAGBITENT fbeMarker2        = { MARK_2,       '2',    FLAG_WORD3, 0,                    fh_god};
static FLAGBITENT fbeMarker3        = { MARK_3,       '3',    FLAG_WORD3, 0,                    fh_god};
static FLAGBITENT fbeMarker4        = { MARK_4,       '4',    FLAG_WORD3, 0,                    fh_god};
static FLAGBITENT fbeMarker5        = { MARK_5,       '5',    FLAG_WORD3, 0,                    fh_god};
static FLAGBITENT fbeMarker6        = { MARK_6,       '6',    FLAG_WORD3, 0,                    fh_god};
static FLAGBITENT fbeMarker7        = { MARK_7,       '7',    FLAG_WORD3, 0,                    fh_god};
static FLAGBITENT fbeMarker8        = { MARK_8,       '8',    FLAG_WORD3, 0,                    fh_god};
static FLAGBITENT fbeMarker9        = { MARK_9,       '9',    FLAG_WORD3, 0,                    fh_god};
#endif // WOD_REALMS
#if defined(FIRANMUX)
static FLAGBITENT fbeImmobile       = { IMMOBILE,     '#',    FLAG_WORD3, 0,                    fh_wiz};
static FLAGBITENT fbeLineWrap       = { LINEWRAP,     '>',    FLAG_WORD3, 0,                    fh_any};
static FLAGBITENT fbeQuell          = { QUELL,        ' ',    FLAG_WORD3, 0,                    fh_inherit};
static FLAGBITENT fbeRestricted     = { RESTRICTED,   '!',    FLAG_WORD3, CA_WIZARD,            fh_wiz};
static FLAGBITENT fbeParent         = { PARENT,       '+',    FLAG_WORD3, 0,                    fh_any};
#endif // FIRANMUX

FLAGNAMEENT gen_flag_names[] =
{
    {"ABODE",           true, &fbeAbode          },
    {"ACCENTS",        false, &fbeNoAccents      },
    {"ANSI",            true, &fbeAnsi           },
    {"AUDIBLE",         true, &fbeAudible        },
    {"AUDITORIUM",      true, &fbeAuditorium     },
    {"BLEED",          false, &fbeNoBleed        },
    {"BLIND",           true, &fbeBlind          },
    {"COMMANDS",       false, &fbeNoCommand      },
    {"CHOWN_OK",        true, &fbeChownOk        },
    {"CONNECTED",       true, &fbeConnected      },
    {"DARK",            true, &fbeDark           },
    {"DESTROY_OK",      true, &fbeDestroyOk      },
    {"ENTER_OK",        true, &fbeEnterOk        },
    {"FIXED",           true, &fbeFixed          },
    {"FLOATING",        true, &fbeFloating       },
    {"GAGGED",          true, &fbeGagged         },
    {"GOING",           true, &fbeGoing          },
    {"HALTED",          true, &fbeHalted         },
    {"HAS_DAILY",       true, &fbeHasDaily       },
    {"HAS_FORWARDLIST", true, &fbeHasForwardList },
    {"HAS_LISTEN",      true, &fbeHasListen      },
    {"HAS_STARTUP",     true, &fbeHasStartup     },
    {"HAVEN",           true, &fbeHaven          },
    {"HEAD",            true, &fbeHead           },
    {"HTML",            true, &fbeHtml           },
    {"IMMORTAL",        true, &fbeImmortal       },
    {"INHERIT",         true, &fbeInherit        },
    {"JUMP_OK",         true, &fbeJumpOk         },
    {"KEEPALIVE",       true, &fbeKeepAlive      },
    {"KEY",             true, &fbeKey            },
    {"LIGHT",           true, &fbeLight          },
    {"LINK_OK",         true, &fbeLinkOk         },
    {"MARKER0",         true, &fbeMarker0        },
    {"MARKER1",         true, &fbeMarker1        },
    {"MARKER2",         true, &fbeMarker2        },
    {"MARKER3",         true, &fbeMarker3        },
    {"MARKER4",         true, &fbeMarker4        },
    {"MARKER5",         true, &fbeMarker5        },
    {"MARKER6",         true, &fbeMarker6        },
    {"MARKER7",         true, &fbeMarker7        },
    {"MARKER8",         true, &fbeMarker8        },
    {"MARKER9",         true, &fbeMarker9        },
    {"MONITOR",         true, &fbeMonitor        },
    {"MYOPIC",          true, &fbeMyopic         },
    {"NO_COMMAND",      true, &fbeNoCommand      },
    {"NOACCENTS",       true, &fbeNoAccents      },
    {"NOBLEED",         true, &fbeNoBleed        },
    {"NOSPOOF",         true, &fbeNoSpoof        },
    {"OPAQUE",          true, &fbeOpaque         },
    {"OPEN_OK",         true, &fbeOpenOk         },
    {"PARENT_OK",       true, &fbeParentOk       },
    {"PLAYER_MAILS",    true, &fbePlayerMails    },
    {"PUPPET",          true, &fbePuppet         },
    {"QUIET",           true, &fbeQuiet          },
    {"ROBOT",           true, &fbeRobot          },
    {"ROYALTY",         true, &fbeRoyalty        },
    {"SAFE",            true, &fbeSafe           },
    {"SITEMON",         true, &fbeSitemon        },
    {"SLAVE",           true, &fbeSlave          },
    {"SPOOF",          false, &fbeNoSpoof        },
    {"STAFF",           true, &fbeStaff          },
    {"STICKY",          true, &fbeSticky         },
    {"SUSPECT",         true, &fbeSuspect        },
    {"TERSE",           true, &fbeTerse          },
    {"TRACE",           true, &fbeTrace          },
    {"TRANSPARENT",     true, &fbeTransparent    },
    {"UNFINDABLE",      true, &fbeUnfindable     },
    {"UNINSPECTED",     true, &fbeUninspected    },
    {"VACATION",        true, &fbeVacation       },
    {"VERBOSE",         true, &fbeVerbose        },
    {"VISUAL",          true, &fbeVisual         },
    {"WIZARD",          true, &fbeWizard         },
#ifdef WOD_REALMS
    {"FAE",             true, &fbeFae            },
    {"CHIMERA",         true, &fbeChimera        },
    {"PEERING",         true, &fbePeering        },
    {"UMBRA",           true, &fbeUmbra          },
    {"SHROUD",          true, &fbeShroud         },
    {"MATRIX",          true, &fbeMatrix         },
    {"OBF",             true, &fbeObf            },
    {"HSS",             true, &fbeHss            },
    {"MEDIUM",          true, &fbeMedium         },
    {"DEAD",            true, &fbeDead           },
#endif // WOD_REALMS
#if defined(FIRANMUX)
    {"IMMOBILE",        true, &fbeImmobile       },
    {"LINEWRAP",        true, &fbeLineWrap       },
    {"QUELL",           true, &fbeQuell          },
    {"RESTRICTED",      true, &fbeRestricted     },
    {"PARENT",          true, &fbeParent         },
#endif // FIRANMUX
    {NULL,             false, NULL}
};

OBJENT object_types[8] =
{
    {"ROOM",    'R', CA_PUBLIC, OF_CONTENTS|OF_EXITS|OF_DROPTO|OF_HOME},
    {"THING",   ' ', CA_PUBLIC, OF_CONTENTS|OF_LOCATION|OF_EXITS|OF_HOME|OF_SIBLINGS},
    {"EXIT",    'E', CA_PUBLIC, OF_SIBLINGS},
    {"PLAYER",  'P', CA_PUBLIC, OF_CONTENTS|OF_LOCATION|OF_EXITS|OF_HOME|OF_OWNER|OF_SIBLINGS},
    {"TYPE5",   '+', CA_GOD,    0},
    {"GARBAGE", '-', CA_PUBLIC, OF_CONTENTS|OF_LOCATION|OF_EXITS|OF_HOME|OF_SIBLINGS},
    {"GARBAGE", '#', CA_GOD,    0},
    {"GARBAGE", '=', CA_GOD,    0}
};

/* ---------------------------------------------------------------------------
 * init_flagtab: initialize flag hash tables.
 */

void init_flagtab(void)
{
    char *nbuf = alloc_sbuf("init_flagtab");
    for (FLAGNAMEENT *fp = gen_flag_names; fp->pOrigName; fp++)
    {
        fp->flagname = (char *)fp->pOrigName;
        mux_strncpy(nbuf, fp->pOrigName, SBUF_SIZE-1);
        mux_strlwr(nbuf);
        if (!hashfindLEN(nbuf, strlen(nbuf), &mudstate.flags_htab))
        {
            hashaddLEN(nbuf, strlen(nbuf), fp, &mudstate.flags_htab);
        }
    }
    free_sbuf(nbuf);
}

/* ---------------------------------------------------------------------------
 * display_flags: display available flags.
 */

void display_flagtab(dbref player)
{
    char *buf, *bp;
    FLAGNAMEENT *fp;

    bp = buf = alloc_lbuf("display_flagtab");
    safe_str("Flags:", buf, &bp);
    for (fp = gen_flag_names; fp->flagname; fp++)
    {
        FLAGBITENT *fbe = fp->fbe;
        if (  (  (fbe->listperm & CA_WIZARD)
              && !Wizard(player))
           || (  (fbe->listperm & CA_GOD)
              && !God(player)))
        {
            continue;
        }
        safe_chr(' ', buf, &bp);
        safe_str(fp->flagname, buf, &bp);
        if (fbe->flaglett != ' ')
        {
            safe_chr('(', buf, &bp);
            if (!fp->bPositive)
            {
                safe_chr('!', buf, &bp);
            }
            safe_chr(fbe->flaglett, buf, &bp);
            safe_chr(')', buf, &bp);
        }
    }
    *bp = '\0';
    notify(player, buf);
    free_lbuf(buf);
}

char *MakeCanonicalFlagName
(
    const char *pName,
    int *pnName,
    bool *pbValid
)
{
    static char buff[SBUF_SIZE];
    char *p = buff;
    int nName = 0;

    while (*pName && nName < SBUF_SIZE)
    {
        *p = mux_tolower(*pName);
        p++;
        pName++;
        nName++;
    }
    *p = '\0';
    if (  0 < nName
       && nName < SBUF_SIZE)
    {
        *pnName = nName;
        *pbValid = true;
        return buff;
    }
    else
    {
        *pnName = 0;
        *pbValid = false;
        return NULL;
    }
}

static FLAGNAMEENT *find_flag(const char *flagname)
{
    // Convert flagname to canonical lowercase format.
    //
    int nName;
    bool bValid;
    char *pName = MakeCanonicalFlagName(flagname, &nName, &bValid);
    FLAGNAMEENT *fe = NULL;
    if (bValid)
    {
        fe = (FLAGNAMEENT *)hashfindLEN(pName, nName, &mudstate.flags_htab);
    }
    return fe;
}

// ---------------------------------------------------------------------------
// flag_set: Set or clear a specified flag on an object.
//
void flag_set(dbref target, dbref player, char *flag, int key)
{
    bool bDone = false;

    do
    {
        // Trim spaces, and handle the negation character.
        //
        while (mux_isspace(*flag))
        {
            flag++;
        }

        bool bNegate = false;
        if (*flag == '!')
        {
            bNegate = true;
            do
            {
                flag++;
            } while (mux_isspace(*flag));
        }

        // Beginning of flag name is now 'flag'.
        //
        char *nflag = flag;
        while (  *nflag != '\0'
              && !mux_isspace(*nflag))
        {
            nflag++;
        }

        if (*nflag == '\0')
        {
            bDone = true;
        }
        else
        {
            *nflag = '\0';
        }

        // Make sure a flag name was specified.
        //
        if (*flag == '\0')
        {
            if (bNegate)
            {
                notify(player, "You must specify a flag to clear.");
            }
            else
            {
                notify(player, "You must specify a flag to set.");
            }
        }
        else
        {
            FLAGNAMEENT *fp = find_flag(flag);
            if (!fp)
            {
                notify(player, "I do not understand that flag.");
            }
            else
            {
                FLAGBITENT *fbe = fp->fbe;

                bool bClearSet = bNegate;
                if (!fp->bPositive)
                {
                    bNegate = !bNegate;
                }

                // Invoke the flag handler, and print feedback.
                //
                if (!fbe->handler(target, player, fbe->flagvalue, fbe->flagflag, bNegate))
                {
                    notify(player, NOPERM_MESSAGE);
                }
                else if (!(key & SET_QUIET) && !Quiet(player))
                {
                    notify(player, (bClearSet ? "Cleared." : "Set."));
                }
            }
        }
        flag = nflag + 1;

    } while (!bDone);
}

/*
 * ---------------------------------------------------------------------------
 * * decode_flags: converts a flags word into corresponding letters.
 */

char *decode_flags(dbref player, FLAGSET *fs)
{
    char *buf, *bp;
    buf = bp = alloc_sbuf("decode_flags");
    *bp = '\0';

    if (!Good_obj(player))
    {
        mux_strncpy(buf, "#-2 ERROR", SBUF_SIZE-1);
        return buf;
    }
    int flagtype = fs->word[FLAG_WORD1] & TYPE_MASK;
    bool bNeedColon = true;
    if (object_types[flagtype].lett != ' ')
    {
        safe_sb_chr(object_types[flagtype].lett, buf, &bp);
        bNeedColon = false;
    }

    FLAGNAMEENT *fp;
    for (fp = gen_flag_names; fp->flagname; fp++)
    {
        FLAGBITENT *fbe = fp->fbe;
        if (  !fp->bPositive
           || fbe->flaglett == ' ')
        {
            // Only look at positive-sense entries that have non-space flag
            // letters.
            //
            continue;
        }
        if (fs->word[fbe->flagflag] & fbe->flagvalue)
        {
            if (  (  (fbe->listperm & CA_STAFF)
                  && !Staff(player))
               || (  (fbe->listperm & CA_ADMIN)
                  && !WizRoy(player))
               || (  (fbe->listperm & CA_WIZARD)
                  && !Wizard(player))
               || (  (fbe->listperm & CA_GOD)
                  && !God(player)))
            {
                continue;
            }

            // Don't show CONNECT on dark wizards to mortals
            //
            if (  flagtype == TYPE_PLAYER
               && fbe->flagflag == FLAG_WORD2
               && fbe->flagvalue == CONNECTED
               && (fs->word[FLAG_WORD1] & (WIZARD | DARK)) == (WIZARD | DARK)
               && !See_Hidden(player))
            {
                continue;
            }

            if (  bNeedColon
               && mux_isdigit(fbe->flaglett))
            {
                // We can't allow numerical digits at the beginning.
                //
                safe_sb_chr(':', buf, &bp);
            }
            safe_sb_chr(fbe->flaglett, buf, &bp);
            bNeedColon = false;
        }
    }
    *bp = '\0';
    return buf;
}

/*
 * ---------------------------------------------------------------------------
 * * has_flag: does object have flag visible to player?
 */

bool has_flag(dbref player, dbref it, const char *flagname)
{
    FLAGNAMEENT *fp = find_flag(flagname);
    if (!fp)
    {
        return false;
    }
    FLAGBITENT *fbe = fp->fbe;

    if (  (  fp->bPositive
          && (db[it].fs.word[fbe->flagflag] & fbe->flagvalue))
       || (  !fp->bPositive
          && (db[it].fs.word[fbe->flagflag] & fbe->flagvalue) == 0))
    {
        if (  (  (fbe->listperm & CA_STAFF)
              && !Staff(player))
           || (  (fbe->listperm & CA_ADMIN)
              && !WizRoy(player))
           || (  (fbe->listperm & CA_WIZARD)
              && !Wizard(player))
           || (  (fbe->listperm & CA_GOD)
              && !God(player)))
        {
            return false;
        }

        // Don't show CONNECT on dark wizards to mortals
        //
        if (  isPlayer(it)
           && (fbe->flagvalue == CONNECTED)
           && (fbe->flagflag == FLAG_WORD2)
           && Hidden(it)
           && !See_Hidden(player))
        {
            return false;
        }
        return true;
    }
    return false;
}

/*
 * ---------------------------------------------------------------------------
 * * flag_description: Return an mbuf containing the type and flags on thing.
 */

char *flag_description(dbref player, dbref target)
{
    // Allocate the return buffer.
    //
    int otype = Typeof(target);
    char *buff = alloc_mbuf("flag_description");
    char *bp = buff;

    // Store the header strings and object type.
    //
    safe_mb_str("Type: ", buff, &bp);
    safe_mb_str(object_types[otype].name, buff, &bp);
    safe_mb_str(" Flags:", buff, &bp);
    if (object_types[otype].perm != CA_PUBLIC)
    {
        *bp = '\0';
        return buff;
    }

    // Store the type-invariant flags.
    //
    FLAGNAMEENT *fp;
    for (fp = gen_flag_names; fp->flagname; fp++)
    {
        if (!fp->bPositive)
        {
            continue;
        }
        FLAGBITENT *fbe = fp->fbe;
        if (db[target].fs.word[fbe->flagflag] & fbe->flagvalue)
        {
            if (  (  (fbe->listperm & CA_STAFF)
                  && !Staff(player))
               || (  (fbe->listperm & CA_ADMIN)
                  && !WizRoy(player))
               || (  (fbe->listperm & CA_WIZARD)
                  && !Wizard(player))
               || (  (fbe->listperm & CA_GOD)
                  && !God(player)))
            {
                continue;
            }

            // Don't show CONNECT on dark wizards to mortals.
            //
            if (  isPlayer(target)
               && (fbe->flagvalue == CONNECTED)
               && (fbe->flagflag == FLAG_WORD2)
               && Hidden(target)
               && !See_Hidden(player))
            {
                continue;
            }
            safe_mb_chr(' ', buff, &bp);
            safe_mb_str(fp->flagname, buff, &bp);
        }
    }

    // Terminate the string, and return the buffer to the caller.
    //
    *bp = '\0';
    return buff;
}

/*
 * ---------------------------------------------------------------------------
 * * Return an lbuf containing the name and number of an object
 */

char *unparse_object_numonly(dbref target)
{
    char *buf = alloc_lbuf("unparse_object_numonly");
    if (target < 0)
    {
        mux_strncpy(buf, aszSpecialDBRefNames[-target], LBUF_SIZE-1);
    }
    else if (!Good_obj(target))
    {
        mux_sprintf(buf, LBUF_SIZE, "*ILLEGAL*(#%d)", target);
    }
    else
    {
        // Leave 100 bytes on the end for the dbref.
        //
        size_t vw;
        size_t nLen = ANSI_TruncateToField(Name(target), LBUF_SIZE-100,
            buf, LBUF_SIZE, &vw, ANSI_ENDGOAL_NORMAL);
        char *bp = buf + nLen;

        safe_str("(#", buf, &bp);
        safe_ltoa(target, buf, &bp);
        safe_chr(')', buf, &bp);
        *bp = '\0';
    }
    return buf;
}

#if defined(FIRANMUX)
static bool AcquireColor(dbref player, dbref target, char SimplifiedCodes[8])
{
    int   aflags;
    dbref aowner;

    // Get the value of the object's '@color' attribute (or on a parent).
    //
    char *color_attr = alloc_lbuf("AcquireColor.1");
    atr_pget_str(color_attr, target, A_COLOR, &aowner, &aflags);

    if ('\0' == color_attr[0])
    {
        free_lbuf(color_attr);
        return false;
    }
    else
    {
        char *AnsiCodes = alloc_lbuf("AcquireColor.2");
        char *ac = AnsiCodes;
        char *cp = color_attr;
        mux_exec(AnsiCodes, &ac, player, target, target,
                AttrTrace(aflags, EV_EVAL|EV_TOP|EV_FCHECK), &cp, NULL, 0);
        *ac = '\0';
        free_lbuf(color_attr);

        SimplifyColorLetters(SimplifiedCodes, AnsiCodes);
        free_lbuf(AnsiCodes);
        return true;
    }
}
#endif // FIRANMUX

/*
 * ---------------------------------------------------------------------------
 * * Return an lbuf pointing to the object name and possibly the db# and flags
 */
char *unparse_object(dbref player, dbref target, bool obey_myopic, bool bAddColor)
{
    char *buf = alloc_lbuf("unparse_object");
    if (NOPERM <= target && target < 0)
    {
        mux_strncpy(buf, aszSpecialDBRefNames[-target], LBUF_SIZE-1);
    }
    else if (!Good_obj(target))
    {
        mux_sprintf(buf, LBUF_SIZE, "*ILLEGAL*(#%d)", target);
    }
    else
    {
        bool exam;
        if (obey_myopic)
        {
            exam = MyopicExam(player, target);
        }
        else
        {
            exam = Examinable(player, target);
        }

        // Leave and extra 100 bytes for the dbref and flags at the end and
        // color at the beginning if necessary..
        //
        size_t vw;
        size_t nLen = ANSI_TruncateToField(Moniker(target), LBUF_SIZE-100,
            buf, LBUF_SIZE, &vw, ANSI_ENDGOAL_NORMAL);

        char *bp = buf + nLen;

#if defined(FIRANMUX)
        if (  vw == nLen
           && bAddColor)
        {
            // There is no color in the name, so look for @color, or highlight.
            //
            char *buf2 = alloc_lbuf("unparse_object.color");
            char *bp2  = buf2;

            char SimplifiedCodes[8];
            if (AcquireColor(player, target, SimplifiedCodes))
            {
                for (int i = 0; SimplifiedCodes[i]; i++)
                {
                    const char *pColor = ColorTable[(unsigned char)SimplifiedCodes[i]];
                    if (pColor)
                    {
                        safe_str(pColor, buf2, &bp2);
                    }
                }
            }
            else
            {
                safe_str(ANSI_HILITE, buf2, &bp2);
            }

            *bp = '\0';
            safe_str(buf, buf2, &bp2);
            safe_str(ANSI_NORMAL, buf2, &bp2);

            // Swap buffers.
            //
            free_lbuf(buf);
            buf = buf2;
            bp  = bp2;
        }
#else
        UNUSED_PARAMETER(bAddColor);
#endif // FIRANMUX

        if (  exam
           || (Flags(target) & (CHOWN_OK | JUMP_OK | LINK_OK | DESTROY_OK))
           || (Flags2(target) & ABODE))
        {
            // Show everything.
            //
            char *fp = decode_flags(player, &(db[target].fs));

            safe_str("(#", buf, &bp);
            safe_ltoa(target, buf, &bp);
            safe_str(fp, buf, &bp);
            safe_chr(')', buf, &bp);

            free_sbuf(fp);
        }
        *bp = '\0';
    }
    return buf;
}


/* ---------------------------------------------------------------------------
 * cf_flag_access: Modify who can set a flag.
 */

CF_HAND(cf_flag_access)
{
    UNUSED_PARAMETER(vp);
    UNUSED_PARAMETER(pExtra);
    UNUSED_PARAMETER(nExtra);

    MUX_STRTOK_STATE tts;
    mux_strtok_src(&tts, str);
    mux_strtok_ctl(&tts, " \t=,");
    char *fstr = mux_strtok_parse(&tts);
    char *permstr = mux_strtok_parse(&tts);

    if (  NULL == fstr
       || '\0' == fstr[0]
       || NULL == permstr
       || '\0' == permstr[0])
    {
        return -1;
    }

    FLAGNAMEENT *fp;
    if ((fp = find_flag(fstr)) == NULL)
    {
        cf_log_notfound(player, cmd, "No such flag", fstr);
        return -1;
    }
    FLAGBITENT *fbe = fp->fbe;

    // Don't change the handlers on special things.
    //
    if (  (fbe->handler != fh_any)
       && (fbe->handler != fh_wizroy)
       && (fbe->handler != fh_wiz)
       && (fbe->handler != fh_god)
       && (fbe->handler != fh_restrict_player)
       && (fbe->handler != fh_privileged))
    {
        STARTLOG(LOG_CONFIGMODS, "CFG", "PERM");
        log_text("Cannot change access for flag: ");
        log_text(fp->flagname);
        ENDLOG;
        return -1;
    }

    if (!strcmp(permstr, "any"))
    {
        fbe->handler = fh_any;
    }
    else if (!strcmp(permstr, "royalty"))
    {
        fbe->handler = fh_wizroy;
    }
    else if (!strcmp(permstr, "wizard"))
    {
        fbe->handler = fh_wiz;
    }
    else if (!strcmp(permstr, "god"))
    {
        fbe->handler = fh_god;
    }
    else if (!strcmp(permstr, "restrict_player"))
    {
        fbe->handler = fh_restrict_player;
    }
    else if (!strcmp(permstr, "privileged"))
    {
        fbe->handler = fh_privileged;
    }
    else if (!strcmp(permstr, "staff"))
    {
        fbe->handler = fh_staff;
    }
    else
    {
        cf_log_notfound(player, cmd, "Flag access", permstr);
        return -1;
    }
    return 0;
}

/*
 * ---------------------------------------------------------------------------
 * * convert_flags: convert a list of flag letters into its bit pattern.
 * * Also set the type qualifier if specified and not already set.
 */

bool convert_flags(dbref player, char *flaglist, FLAGSET *fset, FLAG *p_type)
{
    FLAG type = NOTYPE;
    FLAGSET flagmask;
    memset(&flagmask, 0, sizeof(flagmask));
    int i;

    char *s;
    bool handled;
    for (s = flaglist; *s; s++)
    {
        handled = false;

        // Check for object type.
        //
        for (i = 0; i <= 7 && !handled; i++)
        {
            if (  object_types[i].lett == *s
               && !(  (  (object_types[i].perm & CA_STAFF)
                      && !Staff(player))
                   || (  (object_types[i].perm & CA_ADMIN)
                      && !WizRoy(player))
                   || (  (object_types[i].perm & CA_WIZARD)
                      && !Wizard(player))
                   || (  (object_types[i].perm & CA_GOD)
                      && !God(player))))
            {
                if (  type != NOTYPE
                   && type != i)
                {
                    char *p = tprintf("%c: Conflicting type specifications.",
                        *s);
                    notify(player, p);
                    return false;
                }
                type = i;
                handled = true;
            }
        }

        // Check generic flags.
        //
        if (handled)
        {
            continue;
        }
        FLAGNAMEENT *fp;
        for (fp = gen_flag_names; fp->flagname && !handled; fp++)
        {
            FLAGBITENT *fbe = fp->fbe;
            if (  !fp->bPositive
               || fbe->flaglett == ' ')
            {
                continue;
            }
            if (  fbe->flaglett == *s
               && !(  (  (fbe->listperm & CA_STAFF)
                      && !Staff(player))
                   || (  (fbe->listperm & CA_ADMIN)
                      && !WizRoy(player))
                   || (  (fbe->listperm & CA_WIZARD)
                      && !Wizard(player))
                   || (  (fbe->listperm & CA_GOD)
                      && !God(player))))
            {
                flagmask.word[fbe->flagflag] |= fbe->flagvalue;
                handled = true;
            }
        }

        if (!handled)
        {
            notify(player,
                   tprintf("%c: Flag unknown or not valid for specified object type",
                       *s));
            return false;
        }
    }

    // Return flags to search for and type.
    //
    *fset = flagmask;
    *p_type = type;
    return true;
}

/*
 * ---------------------------------------------------------------------------
 * * decompile_flags: Produce commands to set flags on target.
 */

void decompile_flags(dbref player, dbref thing, char *thingname)
{
    // Report generic flags.
    //
    FLAGNAMEENT *fp;
    for (fp = gen_flag_names; fp->flagname; fp++)
    {
        FLAGBITENT *fbe = fp->fbe;

        // Only handle positive-sense entries.
        // Skip if we shouldn't decompile this flag.
        // Skip if this flag isn't set.
        // Skip if we can't see this flag.
        //
        if (  !fp->bPositive
           || (fbe->listperm & CA_NO_DECOMP)
           || (db[thing].fs.word[fbe->flagflag] & fbe->flagvalue) == 0
           || !check_access(player, fbe->listperm))
        {
            continue;
        }

        // Report this flag.
        //
        notify(player, tprintf("@set %s=%s", strip_ansi(thingname),
            fp->flagname));
    }
}

// do_flag: Rename flags or remove flag aliases.
// Based on RhostMUSH code.
//
static bool flag_rename(char *alias, char *newname)
{
    int nAlias;
    bool bValidAlias;
    char *pCheckedAlias = MakeCanonicalFlagName(alias, &nAlias, &bValidAlias);
    if (!bValidAlias)
    {
        return false;
    }
    char *pAlias = alloc_sbuf("flag_rename.old");
    memcpy(pAlias, pCheckedAlias, nAlias+1);

    int nNewName;
    bool bValidNewName;
    char *pCheckedNewName = MakeCanonicalFlagName(newname, &nNewName, &bValidNewName);
    if (!bValidNewName)
    {
        free_sbuf(pAlias);
        return false;
    }
    char *pNewName = alloc_sbuf("flag_rename.new");
    memcpy(pNewName, pCheckedNewName, nNewName+1);

    FLAGNAMEENT *flag1;
    flag1 = (FLAGNAMEENT *)hashfindLEN(pAlias, nAlias, &mudstate.flags_htab);
    if (flag1 != NULL)
    {
        FLAGNAMEENT *flag2;
        flag2 = (FLAGNAMEENT *)hashfindLEN(pNewName, nNewName, &mudstate.flags_htab);
        if (flag2 == NULL)
        {
            hashaddLEN(pNewName, nNewName, flag1, &mudstate.flags_htab);

            if (flag1->flagname != flag1->pOrigName)
            {
                MEMFREE(flag1->flagname);
            }
            flag1->flagname = StringCloneLen(pNewName, nNewName);
            mux_strupr(flag1->flagname);

            free_sbuf(pAlias);
            free_sbuf(pNewName);
            return true;
        }
    }
    free_sbuf(pAlias);
    free_sbuf(pNewName);
    return false;
}

void do_flag(dbref executor, dbref caller, dbref enactor, int key, int nargs,
             char *flag1, char *flag2)
{
    UNUSED_PARAMETER(caller);
    UNUSED_PARAMETER(enactor);

    if (key & FLAG_REMOVE)
    {
        if (nargs == 2)
        {
            notify(executor, "Extra argument ignored.");
        }
        int nAlias;
        bool bValidAlias;
        char *pCheckedAlias = MakeCanonicalFlagName(flag1, &nAlias, &bValidAlias);
        if (bValidAlias)
        {
            FLAGNAMEENT *lookup;
            lookup = (FLAGNAMEENT *)hashfindLEN(pCheckedAlias, nAlias, &mudstate.flags_htab);
            if (lookup)
            {
                if (  lookup->flagname != lookup->pOrigName
                   && mux_stricmp(lookup->flagname, pCheckedAlias) == 0)
                {
                    MEMFREE(lookup->flagname);
                    lookup->flagname = (char *)lookup->pOrigName;
                    hashdeleteLEN(pCheckedAlias, nAlias, &mudstate.flags_htab);
                    notify(executor, tprintf("Flag name '%s' removed from the hash table.", pCheckedAlias));
                }
                else
                {
                    notify(executor, "Error: You can't remove the present flag name from the hash table.");
                }
            }
        }
    }
    else
    {
        if (nargs < 2)
        {
            notify(executor, "You must specify a flag and a name.");
            return;
        }
        if (flag_rename(flag1, flag2))
        {
            notify(executor, "Flag name changed.");
        }
        else
        {
            notify(executor, "Error: Bad flagname given or flag not found.");
        }
    }
}

/* ---------------------------------------------------------------------------
 * cf_flag_name: Rename a flag. Counterpart to @flag.
 */

CF_HAND(cf_flag_name)
{
    UNUSED_PARAMETER(vp);
    UNUSED_PARAMETER(pExtra);
    UNUSED_PARAMETER(nExtra);
    UNUSED_PARAMETER(player);
    UNUSED_PARAMETER(cmd);

    MUX_STRTOK_STATE tts;
    mux_strtok_src(&tts, str);
    mux_strtok_ctl(&tts, " \t=,");
    char *flagstr = mux_strtok_parse(&tts);
    char *namestr = mux_strtok_parse(&tts);

    if (  !flagstr
       || !*flagstr
       || !namestr
       || !*namestr)
    {
        return -1;
    }

    if (flag_rename(flagstr, namestr))
    {
        return 0;
    }
    else
    {
        return -1;
    }
}
