/**
* \file flags.c
*
* \brief Flags and powers (and sometimes object types) in PennMUSH
*
*
* Functions to cope with flags and powers (and also object types,
* in some cases).
*
* Flag functions actually involve with several related entities:
* Flag spaces (FLAGSPACE objects)
* Flag definitions (FLAG objects)
* Bitmasks representing sets of flags (object_flag_type's). The
* bits involved may differ between dbs.
* Strings of space-separated flag names. This is a string representation
* of a bitmask, suitable for display and storage
* Strings of flag characters
*
*/
#include "config.h"
#ifdef I_SYS_TIME
#include <sys/time.h>
#else
#include <time.h>
#endif
#include <string.h>
#include <stdlib.h>
#include "conf.h"
#include "externs.h"
#include "command.h"
#include "attrib.h"
#include "mushdb.h"
#include "parse.h"
#include "match.h"
#include "ptab.h"
#include "htab.h"
#include "privtab.h"
#include "game.h"
#include "flags.h"
#include "dbdefs.h"
#include "lock.h"
#include "log.h"
#include "dbio.h"
#include "oldflags.h"
#include "confmagic.h"
static int can_set_flag(dbref player, dbref thing, FLAG *flagp, int negate);
static FLAG *letter_to_flagptr(FLAGSPACE * n, char c, int type);
static void flag_add(FLAGSPACE * n, const char *name, FLAG *f);
static int has_flag_ns(FLAGSPACE * n, dbref thing, FLAG *f);
static FLAG *flag_read(FILE * in);
static FLAG *flag_read_oldstyle(FILE * in);
static void flag_read_all_oldstyle(FILE * in, const char *ns);
static void flag_write(FILE * out, FLAG *f, const char *name);
static FLAG *flag_hash_lookup(FLAGSPACE * n, const char *name, int type);
static FLAG *clone_flag(FLAG *f);
static FLAG *new_flag(void);
static void flag_add_additional(FLAGSPACE * n);
static char *list_aliases(FLAGSPACE * n, FLAG *given);
static void realloc_object_flag_bitmasks(FLAGSPACE * n);
static FLAG *match_flag_ns(FLAGSPACE * n, const char *name);
PTAB ptab_flag; /**< Table of flags by name, inc. aliases */
PTAB ptab_power; /**< Table of powers by name, inc. aliases */
HASHTAB htab_flagspaces; /**< Hash of flagspaces */
extern PTAB ptab_command; /* Uses flag bitmasks */
/** Attempt to find a flagspace from its name */
#define Flagspace_Lookup(n,ns) if (!(n = (FLAGSPACE *)hashfind(ns,&htab_flagspaces))) mush_panic("Unable to locate flagspace");
/** This is the old default flag table. We still use it when we have to
* convert old dbs, but once you have a converted db, it's the flag
* table in the db that counts, not this one.
*/
/* Name Letter Type(s) Flag Perms Negate_Perm */
static 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},
{"TRUST", 'I', NOTYPE, INHERIT, F_INHERIT, F_INHERIT},
{"LINK_OK", 'L', NOTYPE, LINK_OK, F_ANY, F_ANY},
{"OPAQUE", 'O', NOTYPE, LOOK_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_LOG,
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},
{"NO_WARN", 'w', NOTYPE, NOWARN, F_ANY, F_ANY},
{"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},
{"ROYALTY", 'r', NOTYPE, ROYALTY, F_INHERIT | F_ROYAL | F_LOG,
F_INHERIT | F_ROYAL},
{"TRANSPARENT", 't', NOTYPE, TRANSPARENTED, F_ANY, F_ANY},
{"VERBOSE", 'v', NOTYPE, VERBOSE, F_ANY, F_ANY},
{"ANSI", 'A', TYPE_PLAYER, PLAYER_ANSI, F_ANY, F_ANY},
{"COLOR", 'C', TYPE_PLAYER, PLAYER_COLOR, F_ANY, F_ANY},
{"MONITOR", 'M', TYPE_PLAYER | TYPE_ROOM | TYPE_THING, 0, F_ANY, F_ANY},
{"NOSPOOF", '"', TYPE_PLAYER, PLAYER_NOSPOOF, F_ANY | F_ODARK,
F_ANY | F_ODARK},
{"SHARED", '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 | TYPE_THING, PLAYER_TERSE, F_ANY, F_ANY},
{"JURY_OK", 'j', TYPE_PLAYER, PLAYER_JURY, F_ROYAL, F_ROYAL},
{"JUDGE", 'J', TYPE_PLAYER, PLAYER_JUDGE, F_ROYAL, F_ROYAL},
{"FIXED", 'F', TYPE_PLAYER, PLAYER_FIXED, F_WIZARD, F_WIZARD},
{"UNREGISTERED", '?', TYPE_PLAYER, PLAYER_UNREG, F_ROYAL, F_ROYAL},
{"ON-VACATION", 'o', TYPE_PLAYER, PLAYER_VACATION, F_ANY, F_ANY},
{"SUSPECT", 's', TYPE_PLAYER, PLAYER_SUSPECT, F_WIZARD | F_MDARK | F_LOG,
F_WIZARD | F_MDARK},
{"PARANOID", '\0', TYPE_PLAYER, PLAYER_PARANOID, F_ANY | F_ODARK,
F_ANY | F_ODARK},
{"NOACCENTS", '~', TYPE_PLAYER, PLAYER_NOACCENTS, 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},
{"LISTEN_PARENT", '^', TYPE_THING | TYPE_ROOM, 0, F_ANY, F_ANY},
{"Z_TEL", 'Z', TYPE_THING | TYPE_ROOM, 0, 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},
{"NO_TEL", 'N', TYPE_ROOM, ROOM_NO_TEL, F_ANY, F_ANY},
{"UNINSPECTED", 'u', TYPE_ROOM, ROOM_UNINSPECT, F_ROYAL, F_ROYAL},
{"CLOUDY", 'x', TYPE_EXIT, EXIT_CLOUDY, F_ANY, F_ANY},
{"GOING_TWICE", '\0', NOTYPE, GOING_TWICE, F_INTERNAL | F_DARK,
F_INTERNAL | F_DARK},
{NULL, '\0', 0, 0, 0, 0}
};
/** The old table to kludge multi-type toggles. Now used only
* for conversion.
*/
static FLAG hack_table[] = {
{"MONITOR", 'M', TYPE_PLAYER, PLAYER_MONITOR, F_ROYAL, F_ROYAL},
{"MONITOR", 'M', TYPE_THING, THING_LISTEN, F_ANY, F_ANY},
{"MONITOR", 'M', TYPE_ROOM, ROOM_LISTEN, F_ANY, F_ANY},
{"LISTEN_PARENT", '^', TYPE_THING, THING_INHEARIT, F_ANY, F_ANY},
{"LISTEN_PARENT", '^', TYPE_ROOM, ROOM_INHEARIT, F_ANY, F_ANY},
{"Z_TEL", 'Z', TYPE_THING, THING_Z_TEL, F_ANY, F_ANY},
{"Z_TEL", 'Z', TYPE_ROOM, ROOM_Z_TEL, F_ANY, F_ANY},
{NULL, '\0', 0, 0, 0, 0}
};
/** A table of types, as if they were flags. Some functions that
* expect flags also accept, for historical reasons, types.
*/
static 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}
};
/** A table of types, as privileges. */
static PRIV type_privs[] = {
{"PLAYER", 'P', TYPE_PLAYER, TYPE_PLAYER},
{"ROOM", 'R', TYPE_ROOM, TYPE_ROOM},
{"EXIT", 'E', TYPE_EXIT, TYPE_EXIT},
{"THING", 'T', TYPE_THING, TYPE_THING},
{NULL, '\0', 0, 0}
};
/** The old default aliases for flags. This table is only used in conversion
* of old databases. Once a database is converted, the alias list in the
* database is what counts.
*/
static FLAG_ALIAS flag_alias_tab[] = {
{"INHERIT", "TRUST"},
{"TRACE", "DEBUG"},
{"NOWARN", "NO_WARN"},
{"NOCOMMAND", "NO_COMMAND"},
{"LISTENER", "MONITOR"},
{"WATCHER", "MONITOR"},
{"ZONE", "SHARED"},
{"COLOUR", "COLOR"},
{"JURYOK", "JURY_OK"},
#ifdef VACATION_FLAG
{"VACATION", "ON-VACATION"},
#endif
{"DEST_OK", "DESTROY_OK"},
{"NOLEAVE", "NO_LEAVE"},
{"TEL_OK", "JUMP_OK"},
{"TELOK", "JUMP_OK"},
{"TEL-OK", "JUMP_OK"},
{"^", "LISTEN_PARENT"},
{NULL, NULL}
};
/** This is the old defaultpowr table. We still use it when we
* have to convert old dbs, but once you have a converted db,
* it's the power table in the db that counts, not this one.
*/
/* Name Flag */
static FLAG power_table[] = {
{"Announce", '\0', NOTYPE, CAN_WALL, F_WIZARD | F_LOG, F_WIZARD},
{"Boot", '\0', NOTYPE, CAN_BOOT, F_WIZARD | F_LOG, F_WIZARD},
{"Builder", '\0', NOTYPE, CAN_BUILD, F_WIZARD | F_LOG, F_WIZARD},
{"Cemit", '\0', NOTYPE, CEMIT, F_WIZARD | F_LOG, F_WIZARD},
{"Chat_Privs", '\0', NOTYPE, CHAT_PRIVS, F_WIZARD | F_LOG, F_WIZARD},
{"Functions", '\0', NOTYPE, GLOBAL_FUNCS, F_WIZARD | F_LOG, F_WIZARD},
{"Guest", '\0', NOTYPE, IS_GUEST, F_WIZARD | F_LOG, F_WIZARD},
{"Halt", '\0', NOTYPE, HALT_ANYTHING, F_WIZARD | F_LOG, F_WIZARD},
{"Hide", '\0', NOTYPE, CAN_HIDE, F_WIZARD | F_LOG, F_WIZARD},
{"Idle", '\0', NOTYPE, UNLIMITED_IDLE, F_WIZARD | F_LOG, F_WIZARD},
{"Immortal", '\0', NOTYPE, NO_PAY | NO_QUOTA | UNKILLABLE, F_WIZARD,
F_WIZARD},
{"Link_Anywhere", '\0', NOTYPE, LINK_ANYWHERE, F_WIZARD | F_LOG, F_WIZARD},
{"Login", '\0', NOTYPE, LOGIN_ANYTIME, F_WIZARD | F_LOG, F_WIZARD},
{"Long_Fingers", '\0', NOTYPE, LONG_FINGERS, F_WIZARD | F_LOG, F_WIZARD},
{"No_Pay", '\0', NOTYPE, NO_PAY, F_WIZARD | F_LOG, F_WIZARD},
{"No_Quota", '\0', NOTYPE, NO_QUOTA, F_WIZARD | F_LOG, F_WIZARD},
{"Open_Anywhere", '\0', NOTYPE, OPEN_ANYWHERE, F_WIZARD | F_LOG, F_WIZARD},
{"Pemit_All", '\0', NOTYPE, PEMIT_ALL, F_WIZARD | F_LOG, F_WIZARD},
{"Player_Create", '\0', NOTYPE, CREATE_PLAYER, F_WIZARD | F_LOG, F_WIZARD},
{"Poll", '\0', NOTYPE, SET_POLL, F_WIZARD | F_LOG, F_WIZARD},
{"Queue", '\0', NOTYPE, HUGE_QUEUE, F_WIZARD | F_LOG, F_WIZARD},
{"Quotas", '\0', NOTYPE, CHANGE_QUOTAS, F_WIZARD | F_LOG, F_WIZARD},
{"Search", '\0', NOTYPE, SEARCH_EVERYTHING, F_WIZARD | F_LOG, F_WIZARD},
{"See_All", '\0', NOTYPE, SEE_ALL, F_WIZARD | F_LOG, F_WIZARD},
{"See_Queue", '\0', NOTYPE, PS_ALL, F_WIZARD | F_LOG, F_WIZARD},
{"Tport_Anything", '\0', NOTYPE, TEL_OTHER, F_WIZARD | F_LOG, F_WIZARD},
{"Tport_Anywhere", '\0', NOTYPE, TEL_ANYWHERE, F_WIZARD | F_LOG, F_WIZARD},
{"Unkillable", '\0', NOTYPE, UNKILLABLE, F_WIZARD | F_LOG, F_WIZARD},
{"Can_nspemit", '\0', NOTYPE, CAN_NSPEMIT, F_WIZARD | F_LOG, F_WIZARD},
{NULL, '\0', 0, 0, 0, 0}
};
/** A table of aliases for powers. */
static FLAG_ALIAS power_alias_tab[] = {
{"@cemit", "Cemit"},
{"@wall", "Announce"},
{"wall", "Announce"},
{NULL, NULL}
};
/** The table of flag privilege bits. */
static PRIV flag_privs[] = {
{"trusted", '\0', F_INHERIT, F_INHERIT},
{"owned", '\0', F_OWNED, F_OWNED},
{"royalty", '\0', F_ROYAL, F_ROYAL},
{"wizard", '\0', F_WIZARD, F_WIZARD},
{"god", '\0', F_GOD, F_GOD},
{"internal", '\0', F_INTERNAL, F_INTERNAL},
{"dark", '\0', F_DARK, F_DARK},
{"mdark", '\0', F_MDARK, F_MDARK},
{"odark", '\0', F_ODARK, F_ODARK},
{"disabled", '\0', F_DISABLED, F_DISABLED},
{"log", '\0', F_LOG, F_LOG},
{NULL, '\0', 0, 0}
};
/*---------------------------------------------------------------------------
* Flag definition functions, including flag hash table handlers
*/
/** Convenience function to return a pointer to a flag struct
* given the name.
* \param name name of flag to find.
* \return poiner to flag structure, or NULL.
*/
FLAG *
match_flag(const char *name)
{
return (FLAG *) match_flag_ns(hashfind("FLAG", &htab_flagspaces), name);
}
/** Convenience function to return a pointer to a flag struct
* given the name.
* \param name name of flag to find.
* \return poiner to flag structure, or NULL.
*/
FLAG *
match_power(const char *name)
{
return (FLAG *) match_flag_ns(hashfind("POWER", &htab_flagspaces), name);
}
/** Convenience function to return a pointer to a flag struct
* given the name.
* \param name name of flag to find.
* \return poiner to flag structure, or NULL.
*/
static FLAG *
match_flag_ns(FLAGSPACE * n, const char *name)
{
return (FLAG *) ptab_find(n->tab, name);
}
/** Given a flag name and mask of types, return a pointer to a flag struct.
* This function first attempts to match the flag name to a flag of the
* right type. If that fails, it tries to match flag characters if the
* name is a single character. If all else fails, it tries to match
* against an object type name.
* \param n pointer to flagspace to search.
* \param name name of flag to find.
* \param type mask of desired flag object types.
* \return pointer to flag structure, or NULL.
*/
static FLAG *
flag_hash_lookup(FLAGSPACE * n, const char *name, int type)
{
FLAG *f;
f = match_flag_ns(n, name);
if (f && !(f->perms & F_DISABLED)) {
if (f->type & type)
return f;
return NULL;
}
/* If the name is a single character, search the flag characters */
if (name && *name && !*(name + 1)) {
if ((f = letter_to_flagptr(n, *name, type)))
return f;
}
if (n->tab == &ptab_flag) {
/* provided for backwards compatibility: type flag checking */
if (n->flag_table == flag_table) {
for (f = type_table; f->name != NULL; f++)
if (string_prefix(name, f->name))
return f;
}
}
return NULL;
}
/* Allocate a new FLAG definition */
static FLAG *
new_flag(void)
{
FLAG *f = (FLAG *) mush_malloc(sizeof(FLAG), "flag");
if (!f)
mush_panic("Unable to allocate memory for a new flag!\n");
return f;
}
/* Deallocate all flag-related memory */
static void
clear_all_flags(FLAGSPACE * n)
{
ptab_free(n->tab);
/* Finally, the flags array */
if (n->flags)
free(n->flags);
n->flags = NULL;
n->flagbits = 0;
}
static FLAG *
clone_flag(FLAG *f)
{
FLAG *clone = new_flag();
clone->name = mush_strdup(f->name, "flag name");
clone->letter = f->letter;
clone->type = f->type;
clone->bitpos = f->bitpos;
clone->perms = f->perms;
clone->negate_perms = f->negate_perms;
return clone;
}
/* This is a stub function to add a flag. It performs no error-checking,
* so it's up to you to be sure you're adding a flag that's properly
* set up and that'll work ok. If called with autopos == 0, this
* auto-allocates the next bitpos. Otherwise, bitpos is ignored and
* f->bitpos is used.
*/
static void
flag_add(FLAGSPACE * n, const char *name, FLAG *f)
{
/* If this flag has no bitpos assigned, assign it the next one.
* We could improve this algorithm to use the next available
* slot after deletions, too, but this will do for now.
*/
if (f->bitpos < 0)
f->bitpos = n->flagbits;
/* Insert the flag in the ptab by the given name (maybe an alias) */
ptab_start_inserts(n->tab);
ptab_insert(n->tab, name, f);
ptab_end_inserts(n->tab);
/* Is this a canonical flag (as opposed to an alias?)
* If it's an alias, we're done.
* A canonical flag has either been given a new bitpos
* or has not yet been stored in the flags array.
* (An alias would have a previously used bitpos that's already
* indexing a flag in the flags array)
*/
if ((f->bitpos >= n->flagbits) || (n->flags[f->bitpos] == NULL)) {
/* It's a canonical flag */
int i;
if (f->bitpos >= n->flagbits) {
/* Oops, we need a bigger array */
if (n->flagbits == 0)
n->flags = (FLAG **) malloc(sizeof(FLAG *));
else {
n->flags =
(FLAG **) realloc(n->flags, (f->bitpos + 1) * sizeof(FLAG *));
if (!n->flags)
mush_panic("Unable to reallocate flags array!\n");
}
/* Make sure the new space is full of NULLs */
for (i = n->flagbits; i <= f->bitpos; i++)
n->flags[i] = NULL;
}
/* Put the canonical flag in the flags array */
n->flags[f->bitpos] = f;
n->flagbits = f->bitpos + 1;
if (n->flagbits % 8 == 1) {
/* We've crossed over a byte boundary, so we need to realloc
* all the flags on all our objects to get them an additional
* byte.
*/
realloc_object_flag_bitmasks(n);
}
}
}
static void
realloc_object_flag_bitmasks(FLAGSPACE * n)
{
dbref it;
object_flag_type p;
COMMAND_INFO *command;
int numbytes = (n->flagbits + 7) / 8;
for (it = 0; it < db_top; it++) {
if (n->tab == &ptab_flag) {
Flags(it) = (object_flag_type) realloc(Flags(it), numbytes);
p = Flags(it) + numbytes - 1;
} else {
Powers(it) = (object_flag_type) realloc(Powers(it), numbytes);
p = Powers(it) + numbytes - 1;
}
/* Zero them out */
memset(p, 0, 1);
}
/* We also need to make sure that all the command flagmasks are
* reallocated!
*/
command = (COMMAND_INFO *) ptab_firstentry(&ptab_command);
while (command) {
if (n->tab == &ptab_flag && command->flagmask) {
command->flagmask =
(object_flag_type) realloc(command->flagmask, numbytes);
/* Zero them out */
p = command->flagmask + numbytes - 1;
memset(p, 0, 1);
}
if (n->tab == &ptab_power && command->powers) {
command->powers = (object_flag_type) realloc(command->powers, numbytes);
/* Zero them out */
p = command->powers + numbytes - 1;
memset(p, 0, 1);
}
command = (COMMAND_INFO *) ptab_nextentry(&ptab_command);
}
}
/* Read in a flag from a file and return it */
static FLAG *
flag_read_oldstyle(FILE * in)
{
FLAG *f;
char *c;
c = mush_strdup(getstring_noalloc(in), "flag name");
if (!strcmp(c, "FLAG ALIASES")) {
mush_free(c, "flag name");
return NULL; /* We're done */
}
f = new_flag();
f->name = c;
c = (char *) getstring_noalloc(in);
f->letter = *c;
f->bitpos = -1;
f->type = getref(in);
f->perms = getref(in);
f->negate_perms = getref(in);
return f;
}
static FLAG *
flag_alias_read_oldstyle(FILE * in, char *alias, FLAGSPACE * n)
{
FLAG *f;
char *c;
/* Real name first */
c = mush_strdup(getstring_noalloc(in), "flag alias");
if (!strcmp(c, "END OF FLAGS")) {
mush_free(c, "flag alias");
return NULL; /* We're done */
}
f = match_flag_ns(n, c);
if (!f) {
/* Corrupt db. Recover as well as we can. */
do_rawlog(LT_ERR,
T
("FLAG READ: flag alias %s matches no known flag. Skipping aliases."),
c);
mush_free(c, "flag alias");
do {
c = (char *) getstring_noalloc(in);
} while (strcmp(c, "END OF FLAGS"));
return NULL;
} else
mush_free(c, "flag alias");
/* Get the alias name */
strcpy(alias, getstring_noalloc(in));
return f;
}
/** Read flags and aliases from the database. This function expects
* to receive file pointer that's already reading in a database file
* and pointing at the start of the flag table. It reads the flags,
* reads the aliases, and then does any additional flag adding that
* needs to happen.
* \param in file pointer to read from.
* \param ns name of namespace to search.
*/
static void
flag_read_all_oldstyle(FILE * in, const char *ns)
{
FLAG *f;
FLAGSPACE *n;
char alias[BUFFER_LEN];
if (!(n = (FLAGSPACE *) hashfind(ns, &htab_flagspaces))) {
do_rawlog(LT_ERR, T("FLAG READ: Unable to locate flagspace %s."), ns);
return;
}
/* If we are reading flags from the db, they are definitive. */
clear_all_flags(n);
while ((f = flag_read_oldstyle(in))) {
flag_add(n, f->name, f);
}
/* Assumes we'll always have at least one alias */
while ((f = flag_alias_read_oldstyle(in, alias, n))) {
flag_add(n, alias, f);
}
flag_add_additional(n);
}
/* Read in a flag from a file and return it */
static FLAG *
flag_read(FILE * in)
{
FLAG *f;
char *c;
char *tmp;
db_read_this_labeled_string(in, "name", &tmp);
c = mush_strdup(tmp, "flag name");
f = new_flag();
f->name = c;
db_read_this_labeled_string(in, "letter", &tmp);
f->letter = *tmp;
f->bitpos = -1;
db_read_this_labeled_string(in, "type", &tmp);
f->type = string_to_privs(type_privs, tmp, 0);
db_read_this_labeled_string(in, "perms", &tmp);
f->perms = string_to_privs(flag_privs, tmp, 0);
db_read_this_labeled_string(in, "negate_perms", &tmp);
f->negate_perms = string_to_privs(flag_privs, tmp, 0);
return f;
}
static FLAG *
flag_alias_read(FILE * in, char *alias, FLAGSPACE * n)
{
FLAG *f;
char *c;
char *tmp;
/* Real name first */
db_read_this_labeled_string(in, "name", &tmp);
c = mush_strdup(tmp, "flag alias");
f = match_flag_ns(n, c);
if (!f) {
/* Corrupt db. Recover as well as we can. */
do_rawlog(LT_ERR,
T
("FLAG READ: flag alias %s matches no known flag. Skipping this alias."),
c);
mush_free(c, "flag alias");
(void) getstring_noalloc(in);
return NULL;
} else
mush_free(c, "flag alias");
/* Get the alias name */
db_read_this_labeled_string(in, "alias", &tmp);
strcpy(alias, tmp);
return f;
}
/** Read flags and aliases from the database. This function expects
* to receive file pointer that's already reading in a database file
* and pointing at the start of the flag table. It reads the flags,
* reads the aliases, and then does any additional flag adding that
* needs to happen.
* \param in file pointer to read from.
* \param ns name of namespace to search.
*/
void
flag_read_all(FILE * in, const char *ns)
{
FLAG *f;
FLAGSPACE *n;
char alias[BUFFER_LEN];
int count;
if (!(globals.indb_flags & DBF_LABELS)) {
flag_read_all_oldstyle(in, ns);
return;
}
if (!(n = (FLAGSPACE *) hashfind(ns, &htab_flagspaces))) {
do_rawlog(LT_ERR, T("FLAG READ: Unable to locate flagspace %s."), ns);
return;
}
/* If we are reading flags from the db, they are definitive. */
clear_all_flags(n);
db_read_this_labeled_number(in, "flagcount", &count);
for (; count > 0; count--) {
if ((f = flag_read(in)))
flag_add(n, f->name, f);
}
/* Assumes we'll always have at least one alias */
db_read_this_labeled_number(in, "flagaliascount", &count);
for (; count > 0; count--) {
if ((f = flag_alias_read(in, alias, n)))
flag_add(n, alias, f);
}
flag_add_additional(n);
}
/* Write a flag out to a file */
static void
flag_write(FILE * out, FLAG *f, const char *name)
{
db_write_labeled_string(out, " name", name);
db_write_labeled_string(out, " letter", tprintf("%c", f->letter));
db_write_labeled_string(out, " type", privs_to_string(type_privs, f->type));
db_write_labeled_string(out, " perms",
privs_to_string(flag_privs, f->perms));
db_write_labeled_string(out, " negate_perms",
privs_to_string(flag_privs, f->negate_perms));
}
/* Write a flag alias out to a file */
static void
flag_alias_write(FILE * out, FLAG *f, const char *name)
{
db_write_labeled_string(out, " name", f->name);
db_write_labeled_string(out, " alias", name);
}
/** Write flags and aliases to the database. This function expects
* to receive file pointer that's already writing in a database file.
* It writes the flags, writes the aliases.
* \param out file pointer to write to.
*/
void
flag_write_all(FILE * out, const char *ns)
{
int i, count;
FLAG *f;
FLAGSPACE *n;
char flagname[BUFFER_LEN];
if (!(n = (FLAGSPACE *) hashfind(ns, &htab_flagspaces))) {
do_rawlog(LT_ERR, T("FLAG WRITE: Unable to locate flagspace %s."), ns);
return;
}
/* Write out canonical flags first */
count = 0;
for (i = 0; i < n->flagbits; i++) {
if (n->flags[i])
count++;
}
db_write_labeled_number(out, "flagcount", count);
for (i = 0; i < n->flagbits; i++) {
if (n->flags[i])
flag_write(out, n->flags[i], n->flags[i]->name);
}
/* Now write out aliases. An alias is a flag in the ptab whose
* name isn't the same as the name of the canonical flag in its
* bit position
*/
count = 0;
f = ptab_firstentry_new(n->tab, flagname);
while (f) {
if (strcmp(n->flags[f->bitpos]->name, flagname))
count++;
f = ptab_nextentry_new(n->tab, flagname);
}
db_write_labeled_number(out, "flagaliascount", count);
f = ptab_firstentry_new(n->tab, flagname);
while (f) {
if (strcmp(n->flags[f->bitpos]->name, flagname)) {
/* This is an alias! */
flag_alias_write(out, f, flagname);
}
f = ptab_nextentry_new(n->tab, flagname);
}
}
/** Initialize the flagspaces.
*/
void
init_flagspaces(void)
{
FLAGSPACE *flags;
hashinit(&htab_flagspaces, 4, sizeof(FLAGSPACE));
flags = (FLAGSPACE *) mush_malloc(sizeof(FLAGSPACE), "flagspace");
flags->name = strdup("FLAG");
flags->tab = &ptab_flag;
flags->flagbits = 0;
flags->flags = NULL;
flags->flag_table = flag_table;
flags->flag_alias_table = flag_alias_tab;
hashadd("FLAG", (void *) flags, &htab_flagspaces);
flags = (FLAGSPACE *) mush_malloc(sizeof(FLAGSPACE), "flagspace");
flags->name = strdup("POWER");
flags->tab = &ptab_power;
flags->flagbits = 0;
flags->flags = NULL;
flags->flag_table = power_table;
flags->flag_alias_table = power_alias_tab;
hashadd("POWER", (void *) flags, &htab_flagspaces);
}
/** Initialize a flag table with defaults.
* This function loads the standard flags as a baseline
* (and for dbs that haven't yet converted).
* \param ns name of flagspace to initialize.
*/
void
init_flag_table(const char *ns)
{
FLAG *f, *cf;
FLAG_ALIAS *a;
FLAGSPACE *n;
if (!(n = (FLAGSPACE *) hashfind(ns, &htab_flagspaces))) {
do_rawlog(LT_ERR, T("FLAG INIT: Unable to locate flagspace %s."), ns);
return;
}
ptab_init(n->tab);
/* do regular flags first */
for (f = n->flag_table; f->name; f++) {
cf = clone_flag(f);
cf->bitpos = -1;
flag_add(n, cf->name, cf);
}
/* now add in the aliases */
for (a = n->flag_alias_table; a->alias; a++) {
if ((f = match_flag_ns(n, a->realname)))
flag_add(n, a->alias, f);
else
do_rawlog(LT_ERR,
T("FLAG INIT: flag alias %s matches no known flag."), a->alias);
}
flag_add_additional(n);
}
/* This is where the developers will put flag_add statements to create
* new flags in future penn versions. Hackers should avoid this,
* and use local_flags() in flaglocal.c instead.
*/
static void
flag_add_additional(FLAGSPACE * n)
{
FLAG *f;
FLAGSPACE *flags;
if (n->tab == &ptab_flag) {
add_flag("MISTRUST", 'm', TYPE_THING | TYPE_EXIT | TYPE_ROOM, F_INHERIT,
F_INHERIT);
add_flag("ORPHAN", 'i', NOTYPE, F_ANY, F_ANY);
add_flag("HEAVY", '\0', NOTYPE, F_ROYAL, F_ANY);
if ((f = match_flag("TERSE")))
f->type |= TYPE_THING;
if ((f = match_flag("PUPPET")))
f->type |= TYPE_ROOM;
if ((f = match_flag("SUSPECT")))
f->type = NOTYPE;
if ((f = match_flag("CHOWN_OK")))
f->type = TYPE_THING | TYPE_ROOM | TYPE_EXIT;
if ((f = match_flag("NOSPOOF"))) {
f->type = NOTYPE;
f->letter = '"';
}
if ((f = match_flag("PARANOID"))) {
f->type = NOTYPE;
f->letter = '\0';
}
f = add_flag("CHAN_USEFIRSTMATCH", '\0', NOTYPE, F_INHERIT, F_INHERIT);
flags = hashfind("FLAG", &htab_flagspaces);
if (!match_flag("CHAN_FIRSTMATCH"))
flag_add(flags, "CHAN_FIRSTMATCH", f);
if (!match_flag("CHAN_MATCHFIRST"))
flag_add(flags, "CHAN_MATCHFIRST", f);
if ((f = match_flag("SUSPECT")))
f->perms |= F_LOG;
if ((f = match_flag("WIZARD")))
f->perms |= F_LOG;
if ((f = match_flag("ROYALTY")))
f->perms |= F_LOG;
} else if (n->tab == &ptab_power) {
if (!(globals.indb_flags & DBF_POWERS_LOGGED)) {
int i;
for (i = 0; i < n->flagbits; i++)
n->flags[i]->perms |= F_LOG;
}
add_power("Sql_Ok", '\0', NOTYPE, F_WIZARD | F_LOG, F_ANY);
add_power("Debit", '\0', NOTYPE, F_WIZARD | F_LOG, F_ANY);
}
local_flags(n);
}
/** Extract object type from old-style flag value.
* Before 1.7.7p5, object types were stored in the lowest 3 bits of the
* flag value. Now they get their own place in the object structure,
* but if we're reading an older database, we need to extract the types
* from the old flag value.
* \param old_flags an old-style flag bitmask.
* \return a type bitflag.
*/
int
type_from_old_flags(long old_flags)
{
switch (old_flags & OLD_TYPE_MASK) {
case OLD_TYPE_PLAYER:
return TYPE_PLAYER;
case OLD_TYPE_ROOM:
return TYPE_ROOM;
case OLD_TYPE_EXIT:
return TYPE_EXIT;
case OLD_TYPE_THING:
return TYPE_THING;
case OLD_TYPE_GARBAGE:
return TYPE_GARBAGE;
}
/* If we get here, we're in trouble. */
return -1;
}
/** Extract flags from old-style flag and toggle values.
* This function takes the flag and toggle bitfields from older databases,
* allocates a new flag bitmask, and populates it appropriately
* by looking up each flag/toggle value in the old flag table.
* It also works for powers (in which case old_toggles should be 0).
* \param ns flagspace in which to locate values.
* \param old_flags an old-style flag bitmask.
* \param old_toggles an old-style toggle bitmask.
* \param type the object type.
* \return a newly allocated flag bitmask representing the flags and toggles.
*/
object_flag_type
flags_from_old_flags(const char *ns, long old_flags, long old_toggles, int type)
{
FLAG *f, *newf;
FLAGSPACE *n;
object_flag_type bitmask = new_flag_bitmask(ns);
Flagspace_Lookup(n, ns);
for (f = n->flag_table; f->name; f++) {
if (f->type == NOTYPE) {
if (f->bitpos & old_flags) {
newf = match_flag_ns(n, f->name);
set_flag_bitmask(bitmask, newf->bitpos);
}
} else if (f->type & type) {
if (f->bitpos & old_toggles) {
newf = match_flag_ns(n, f->name);
set_flag_bitmask(bitmask, newf->bitpos);
}
}
}
for (f = hack_table; f->name; f++) {
if ((f->type & type) && (f->bitpos & old_toggles)) {
newf = match_flag_ns(n, f->name);
set_flag_bitmask(bitmask, newf->bitpos);
}
}
return bitmask;
}
/** Macro to detrmine if flag f's name is n */
#define is_flag(f,n) (!strcmp(f->name,n))
/* Given a single character, return the matching flag definition */
static FLAG *
letter_to_flagptr(FLAGSPACE * n, char c, int type)
{
FLAG *f;
int i;
for (i = 0; i < n->flagbits; i++)
if ((f = n->flags[i])) {
if ((n->tab == &ptab_flag) && ((f->letter == c) && (f->type & type)))
return f;
}
/* Do we need to do this? */
return NULL;
}
/*----------------------------------------------------------------------
* Functions for managing bitmasks
*/
/** Locate a specific byte given a bit position */
#define FlagByte(x) (x / 8)
/** Locate a specific bit within a byte given a bit position */
#define FlagBit(x) (7 - (x % 8))
/** How many bytes do we need for a flag bitmask? */
#define FlagBytes(n) ((size_t)((n->flagbits + 7) / 8))
/** Allocate a new flag bitmask.
* This function allocates a new flag bitmask of sufficient length
* to include the current number of flags. It zeroes out the entire
* bitmask and returns it.
* \return a newly allocated zeroed flag bitmask.
*/
object_flag_type
new_flag_bitmask(const char *ns)
{
object_flag_type bitmask;
FLAGSPACE *n;
Flagspace_Lookup(n, ns);
bitmask = (object_flag_type) mush_malloc(FlagBytes(n), "flag_bitmask");
if (!bitmask)
mush_panic("Unable to allocate memory for flag bitmask");
memset(bitmask, 0, FlagBytes(n));
return bitmask;
}
/** Copy flag bitmask, allocating a new bitmask for the copy.
* This function allocates a new flag bitmask of sufficient length
* to include the current number of flags, and copies all the flags
* from a given bitmask into the new bitmask.
* \param ns name of namespace to search.
* \param given a flag bitmask.
* \return a newly allocated clone of the given bitmask.
*/
object_flag_type
clone_flag_bitmask(const char *ns, object_flag_type given)
{
object_flag_type bitmask;
FLAGSPACE *n;
Flagspace_Lookup(n, ns);
bitmask = (object_flag_type) mush_malloc(FlagBytes(n), "flag_bitmask");
if (!bitmask)
mush_panic("Unable to allocate memory for flag bitmask");
memcpy(bitmask, given, FlagBytes(n));
return bitmask;
}
/** Copy one flag bitmask into another (already allocated).
* This is a convenience function - it's memcpy for flag bitmasks.
* \param ns name of namespace to search.
* \param dest destination bitmask for the given data.
* \param given a flag bitmask to copy.
*/
/* Copy a given bitmask to an already-allocated destination bitmask */
void
copy_flag_bitmask(const char *ns, object_flag_type dest, object_flag_type given)
{
FLAGSPACE *n;
Flagspace_Lookup(n, ns);
memcpy((void *) dest, (void *) given, FlagBytes(n));
}
/** Deallocate a flag bitmask.
* \param bitmask a flag bitmask to free.
*/
void
destroy_flag_bitmask(object_flag_type bitmask)
{
mush_free((Malloc_t) bitmask, "flag_bitmask");
}
/** Add a bit into a bitmask.
* This function sets a particular bit in a bitmask (e.g. bit 42),
* by computing the appropriate byte, and the appropriate bit within the byte,
* and setting it.
* \param bitmask a flag bitmask.
* \param bit the bit to set.
*/
void
set_flag_bitmask(object_flag_type bitmask, int bit)
{
int bytepos = FlagByte(bit);
int bitpos = FlagBit(bit);
if (!bitmask)
return;
*(bitmask + bytepos) |= (1 << bitpos);
}
/** Add a bit into a bitmask.
* This function clears a particular bit in a bitmask (e.g. bit 42),
* by computing the appropriate byte, and the appropriate bit within the byte,
* and clearing it.
* \param bitmask a flag bitmask.
* \param bit the bit to clear.
*/
void
clear_flag_bitmask(object_flag_type bitmask, int bit)
{
int bytepos = FlagByte(bit);
int bitpos = FlagBit(bit);
if (!bitmask)
return;
*(bitmask + bytepos) &= ~(1 << bitpos);
}
/** Test a bit in a bitmask.
* This function tests a particular bit in a bitmask (e.g. bit 42),
* by computing the appropriate byte, and the appropriate bit within the byte,
* and testing it.
* \param bitmask a flag bitmask.
* \param bitpos the bit to test.
* \retval 1 bit is set.
* \retval 0 bit is not set.
*/
int
has_bit(object_flag_type bitmask, int bitpos)
{
int bytepos, bits_in_byte;
/* Garbage objects, for example, have no bits set */
if (!bitmask)
return 0;
bytepos = FlagByte(bitpos);
bits_in_byte = FlagBit(bitpos);
return *(bitmask + bytepos) & (1 << bits_in_byte);
}
/** Test a set of bits in one bitmask against all those in another.
* This function determines if one bitmask contains (at least)
* all of the bits set in another bitmask.
* \param ns name of namespace to search.
* \param source the bitmask to test.
* \param bitmask the bitmask containing the bits to look for.
* \retval 1 all bits in bitmask are set in source.
* \retval 0 at least one bit in bitmask is not set in source.
*/
int
has_all_bits(const char *ns, object_flag_type source, object_flag_type bitmask)
{
unsigned int i;
int ok = 1;
FLAGSPACE *n;
Flagspace_Lookup(n, ns);
for (i = 0; i < FlagBytes(n); i++)
ok &= ((*(bitmask + i) & *(source + i)) == *(bitmask + i));
return ok;
}
/** Test to see if a bitmask is entirely 0 bits.
* \param ns name of namespace to search.
* \param source the bitmask to test.
* \retval 1 all bits in bitmask are 0.
* \retval 0 at least one bit in bitmask is 1.
*/
int
null_flagmask(const char *ns, object_flag_type source)
{
unsigned int i;
int bad = 0;
FLAGSPACE *n;
Flagspace_Lookup(n, ns);
for (i = 0; i < FlagBytes(n); i++)
bad |= *(source + i);
return (!bad);
}
/** Test a set of bits in one bitmask against any of those in another.
* This function determines if one bitmask contains any
* of the bits set in another bitmask.
* \param ns name of namespace to search.
* \param source the bitmask to test.
* \param bitmask the bitmask containing the bits to look for.
* \retval 1 at least one bit in bitmask is set in source.
* \retval 0 no bits in bitmask are set in source.
*/
int
has_any_bits(const char *ns, object_flag_type source, object_flag_type bitmask)
{
unsigned int i;
int ok = 0;
FLAGSPACE *n;
Flagspace_Lookup(n, ns);
for (i = 0; i < FlagBytes(n); i++)
ok |= (*(bitmask + i) & *(source + i));
return ok;
}
/** Produce a space-separated list of flag names, given a bitmask.
* This function returns the string representation of a flag bitmask.
* \param ns name of namespace to search.
* \param bitmask a flag bitmask.
* \param privs dbref for privilege checking for flag visibility.
* \param thing object for which bitmask is the flag bitmask.
* \return string representation of bitmask (list of flags).
*/
const char *
bits_to_string(const char *ns, object_flag_type bitmask, dbref privs,
dbref thing)
{
FLAG *f;
FLAGSPACE *n;
int i;
int first = 1;
static char buf[BUFFER_LEN];
char *bp;
Flagspace_Lookup(n, ns);
bp = buf;
for (i = 0; i < n->flagbits; i++) {
if ((f = n->flags[i])) {
if (has_bit(bitmask, f->bitpos) &&
(!GoodObject(thing) || Can_See_Flag(privs, thing, f))) {
if (!first)
safe_chr(' ', buf, &bp);
safe_str(f->name, buf, &bp);
first = 0;
}
}
}
*bp = '\0';
return buf;
}
/** Convert a flag list string to a bitmask.
* Given a space-separated list of flag names, convert them to
* a bitmask array (which we malloc) and return it.
* \param ns name of namespace to search.
* \param str list of flag names.
* \return a newly allocated flag bitmask.
*/
object_flag_type
string_to_bits(const char *ns, const char *str)
{
object_flag_type bitmask;
char *copy, *s, *sp;
FLAG *f;
FLAGSPACE *n;
bitmask = new_flag_bitmask(ns);
if (!(n = (FLAGSPACE *) hashfind(ns, &htab_flagspaces))) {
do_rawlog(LT_ERR, T("FLAG: Unable to locate flagspace %s."), ns);
return bitmask;
}
if (!str)
return bitmask; /* We're done, then */
copy = mush_strdup(str, "flagstring");
s = trim_space_sep(copy, ' ');
while (s) {
sp = split_token(&s, ' ');
if (!(f = match_flag_ns(n, sp)))
/* Now what do we do? Ignore it? */
continue;
set_flag_bitmask(bitmask, f->bitpos);
}
mush_free(copy, "flagstring");
return bitmask;
}
/*----------------------------------------------------------------------
* Functions for working with flags on objects
*/
/** Check an object for a flag.
* This function tests to see if an object has a flag. It is the
* function to use for this purpose from outside of this file.
* \param ns name of flagspace to use.
* \param thing object to check.
* \param flag name of flag to check for (a string).
* \param type allowed types of flags to check for.
* \retval 1 object has the flag.
* \retval 0 object does not have the flag.
*/
int
has_flag_in_space_by_name(const char *ns, dbref thing, const char *flag,
int type)
{
FLAG *f;
FLAGSPACE *n;
n = hashfind(ns, &htab_flagspaces);
f = flag_hash_lookup(n, flag, type);
if (!f)
return 0;
return has_flag_ns(n, thing, f);
}
static int
has_flag_ns(FLAGSPACE * n, dbref thing, FLAG *f)
{
if (!GoodObject(thing) || IsGarbage(thing))
return 0;
return (n->tab == &ptab_flag) ?
has_bit(Flags(thing), f->bitpos) : has_bit(Powers(thing), f->bitpos);
}
static int
can_set_flag_generic(dbref player, dbref thing, FLAG *flagp, int negate)
{
int myperms;
if (!flagp || !GoodObject(player) || !GoodObject(thing))
return 0;
myperms = negate ? flagp->negate_perms : flagp->perms;
if ((myperms & F_INTERNAL) || (myperms & F_DISABLED))
return 0;
if (!(flagp->type & Typeof(thing)))
return 0;
if ((myperms & F_INHERIT) && !Wizard(player) &&
(!Inheritable(player) || !Owns(player, thing)))
return 0;
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;
return 1;
}
static int
can_set_power(dbref player, dbref thing, FLAG *flagp, int negate)
{
if (!can_set_flag_generic(player, thing, flagp, negate))
return 0;
if (Hasprivs(thing) && (is_flag(flagp, "GUEST"))) {
notify(player, T("You can't make admin into guests."));
return 0;
}
return 1;
}
static int
can_set_flag(dbref player, dbref thing, FLAG *flagp, int negate)
{
if (!can_set_flag_generic(player, thing, flagp, negate))
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 (is_flag(flagp, "CHOWN_OK") || is_flag(flagp, "DESTROY_OK")) {
if (!Owns(player, thing) && !Wizard(player))
return 0;
else
return 1;
}
/* You must be privileged to set/clear the MONITOR flag on a player */
if (IsPlayer(thing) && is_flag(flagp, "MONITOR") && !Hasprivs(player))
return 0;
/* Checking for the ZONE flag. If you set this, the player had
* better be zone-locked!
*/
if (!negate && is_flag(flagp, "ZONE") &&
(getlock(thing, Zone_Lock) == TRUE_BOOLEXP)) {
notify(player, T("You must @lock/zone 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) && is_flag(flagp, "GAGGED"))
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.
*/
/* A wiz can set things he owns WIZ, but nothing else. */
if (is_flag(flagp, "WIZARD") && !negate)
return (Wizard(player) && Owns(player, thing) && !IsPlayer(thing));
/* A wiz can unset the WIZ bit on any non-player */
if (is_flag(flagp, "WIZARD") && negate)
return (Wizard(player) && !IsPlayer(thing));
/* Wizards can set or unset anything royalty. Royalty can set anything
* they own royalty, but cannot reset their own bits. */
if (is_flag(flagp, "ROYALTY")) {
return (!Guest(thing) && (Wizard(player) || (Royalty(player) &&
Owns(player, thing)
&& !IsPlayer(thing))));
}
return 1;
}
/** Return a list of flag symbols that one object can see on another.
* \param thing object to list flag symbols for.
* \param player looker, for permission checking.
* \return a string containing all the visible type and flag symbols.
*/
const char *
unparse_flags(dbref thing, dbref player)
{
/* print out the flag symbols (letters) */
static char buf[BUFFER_LEN];
char *p;
FLAG *f;
int i;
FLAGSPACE *n;
Flagspace_Lookup(n, "FLAG");
p = buf;
switch (Typeof(thing)) {
case TYPE_GARBAGE:
*p = '\0';
return buf;
case TYPE_ROOM:
*p++ = 'R';
break;
case TYPE_EXIT:
*p++ = 'E';
break;
case TYPE_THING:
*p++ = 'T';
break;
case TYPE_PLAYER:
*p++ = 'P';
break;
}
for (i = 0; i < n->flagbits; i++) {
if ((f = n->flags[i])) {
if (has_flag_ns(n, thing, f) && Can_See_Flag(player, thing, f))
*p++ = f->letter;
}
}
*p = '\0';
return buf;
}
/** Return the object's type and its flag list for examine.
* \param thing object to list flag symbols for.
* \param player looker, for permission checking.
* \return a string containing all the visible type and flag symbols.
*/
const char *
flag_description(dbref player, dbref thing)
{
static char buf[BUFFER_LEN];
char *bp;
bp = buf;
safe_str(T("Type: "), buf, &bp);
safe_str(privs_to_string(type_privs, Typeof(thing)), buf, &bp);
safe_str(T(" Flags: "), buf, &bp);
safe_str(bits_to_string("FLAG", Flags(thing), player, thing), buf, &bp);
*bp = '\0';
return buf;
}
/** Print out the flags for a decompile.
* \param player looker, for permission checking.
* \param thing object being decompiled.
* \param name name by which object is referred to in the decompile.
* \param ns name of namespace to search.
* \param command name of command used to set the 'flag'.
*/
void
decompile_flags_generic(dbref player, dbref thing, const char *name,
const char *ns, const char *command, const char *prefix)
{
FLAG *f;
int i;
FLAGSPACE *n;
Flagspace_Lookup(n, ns);
for (i = 0; i < n->flagbits; i++)
if ((f = n->flags[i])) {
if (has_flag_ns(n, thing, f) && Can_See_Flag(player, thing, f))
notify_format(player, "%s%s %s = %s", prefix, command, name, f->name);
}
}
/** Set or clear flags on an object, without permissions/hear checking.
* This function is for server internal use, only, when a flag should
* be set or cleared unequivocally.
* \param ns name of namespace to search.
* \param thing object on which to set or clear flag.
* \param flag name of flag to set or clear.
* \param negate if 1, clear the flag, if 0, set the flag.
*/
void
twiddle_flag_internal(const char *ns, dbref thing, const char *flag, int negate)
{
FLAG *f;
FLAGSPACE *n;
if (IsGarbage(thing))
return;
n = (FLAGSPACE *) hashfind(ns, &htab_flagspaces);
f = flag_hash_lookup(n, flag, Typeof(thing));
if (f && (n->flag_table != type_table))
twiddle_flag(n, thing, f, negate);
}
/** Set or clear flags on an object, with full permissions/hear checking.
* \verbatim
* This function is used to set and clear flags through @set and the like.
* It does permission checking and handles the "is now listening" messages.
* \endverbatim
* \param player the enactor.
* \param thing object on which to set or clear flag.
* \param flag name of flag to set or clear.
* \param negate if 1, clear the flag, if 0, set the flag.
* \param hear 1 if object is a hearer.
* \param listener 1 if object is a listener.
*/
void
set_flag(dbref player, dbref thing, const char *flag, int negate,
int hear, int listener)
{
FLAG *f;
char tbuf1[BUFFER_LEN];
char *tp;
FLAGSPACE *n;
n = (FLAGSPACE *) hashfind("FLAG", &htab_flagspaces);
if ((f = flag_hash_lookup(n, flag, Typeof(thing))) == NULL) {
notify_format(player, T("%s - I don't recognize that flag."), flag);
return;
}
if (!can_set_flag(player, thing, f, negate)) {
notify(player, T("Permission denied."));
return;
}
/* The only players who can be Dark are wizards. */
if (is_flag(f, "DARK") && !negate && Alive(thing) && !Wizard(thing)) {
notify(player, T("Permission denied."));
return;
}
twiddle_flag(n, thing, f, negate);
if (negate) {
/* log if necessary */
if (f->perms & F_LOG)
do_log(LT_WIZ, player, thing, "%s FLAG CLEARED", f->name);
/* those who unDARK return to the WHO */
if (is_flag(f, "DARK") && IsPlayer(thing))
hide_player(thing, 0);
/* notify the area if something stops listening, but only if it
wasn't listening before */
if (!IsPlayer(thing) && (hear || listener) &&
!Hearer(thing) && !Listener(thing)) {
tp = tbuf1;
safe_format(tbuf1, &tp, T("%s is no longer listening."), Name(thing));
*tp = '\0';
if (GoodObject(Location(thing)))
notify_except(Contents(Location(thing)), NOTHING, tbuf1,
NA_INTER_PRESENCE);
notify_except(Contents(thing), NOTHING, tbuf1, 0);
}
if (IsRoom(thing) && is_flag(f, "MONITOR") && !hear && !Listener(thing)) {
tp = tbuf1;
safe_format(tbuf1, &tp, T("%s is no longer listening."), Name(thing));
*tp = '\0';
notify_except(Contents(thing), NOTHING, tbuf1, NA_INTER_PRESENCE);
}
if (is_flag(f, "AUDIBLE")) {
switch (Typeof(thing)) {
case TYPE_EXIT:
if (Audible(Source(thing))) {
tp = tbuf1;
safe_format(tbuf1, &tp, T("Exit %s is no longer broadcasting."),
Name(thing));
*tp = '\0';
notify_except(Contents(Source(thing)), NOTHING, tbuf1, 0);
}
break;
case TYPE_ROOM:
notify_except(Contents(thing), NOTHING,
T("Audible exits in this room have been deactivated."),
0);
break;
case TYPE_THING:
case TYPE_PLAYER:
notify_except(Contents(thing), thing,
T("This room is no longer broadcasting."), 0);
notify(thing, T("Your contents can no longer be heard from outside."));
break;
}
}
if (is_flag(f, "QUIET") || (!AreQuiet(player, thing))) {
tp = tbuf1;
safe_str(Name(thing), tbuf1, &tp);
safe_str(" - ", tbuf1, &tp);
safe_str(f->name, tbuf1, &tp);
safe_str(T(" reset."), tbuf1, &tp);
*tp = '\0';
notify(player, tbuf1);
}
} else {
/* log if necessary */
if (f->perms & F_LOG)
do_log(LT_WIZ, player, thing, "%s FLAG SET", f->name);
if (is_flag(f, "TRUST") && GoodObject(Zone(thing)))
notify(player, T("Warning: Setting trust flag on zoned object"));
if (is_flag(f, "SHARED"))
check_zone_lock(player, thing, 1);
/* DARK players should be treated as logged out */
if (is_flag(f, "DARK") && IsPlayer(thing))
hide_player(thing, 1);
/* notify area if something starts listening */
if (!IsPlayer(thing) &&
(is_flag(f, "PUPPET") || is_flag(f, "MONITOR")) && !hear && !listener) {
tp = tbuf1;
safe_format(tbuf1, &tp, T("%s is now listening."), Name(thing));
*tp = '\0';
if (GoodObject(Location(thing)))
notify_except(Contents(Location(thing)), NOTHING, tbuf1,
NA_INTER_PRESENCE);
notify_except(Contents(thing), NOTHING, tbuf1, 0);
}
if (IsRoom(thing) && is_flag(f, "MONITOR") && !hear && !listener) {
tp = tbuf1;
safe_format(tbuf1, &tp, T("%s is now listening."), Name(thing));
*tp = '\0';
notify_except(Contents(thing), NOTHING, tbuf1, 0);
}
/* notify for audible exits */
if (is_flag(f, "AUDIBLE")) {
switch (Typeof(thing)) {
case TYPE_EXIT:
if (Audible(Source(thing))) {
tp = tbuf1;
safe_format(tbuf1, &tp, T("Exit %s is now broadcasting."),
Name(thing));
*tp = '\0';
notify_except(Contents(Source(thing)), NOTHING, tbuf1, 0);
}
break;
case TYPE_ROOM:
notify_except(Contents(thing), NOTHING,
T("Audible exits in this room have been activated."), 0);
break;
case TYPE_PLAYER:
case TYPE_THING:
notify_except(Contents(thing), thing,
T("This room is now broadcasting."), 0);
notify(thing, T("Your contents can now be heard from outside."));
break;
}
}
if (is_flag(f, "QUIET") || (!AreQuiet(player, thing))) {
tp = tbuf1;
safe_str(Name(thing), tbuf1, &tp);
safe_str(" - ", tbuf1, &tp);
safe_str(f->name, tbuf1, &tp);
safe_str(T(" set."), tbuf1, &tp);
*tp = '\0';
notify(player, tbuf1);
}
}
}
/** Set or clear powers on an object, with full permissions checking.
* \verbatim
* This function is used to set and clear powers through @power and the like.
* It does permission checking.
* \endverbatim
* \param player the enactor.
* \param thing object on which to set or clear flag.
* \param flag name of flag to set or clear.
* \param negate if 1, clear the flag, if 0, set the flag.
*/
void
set_power(dbref player, dbref thing, const char *flag, int negate)
{
FLAG *f;
FLAGSPACE *n;
char tbuf1[BUFFER_LEN], *tp;
n = (FLAGSPACE *) hashfind("POWER", &htab_flagspaces);
if ((f = flag_hash_lookup(n, flag, Typeof(thing))) == NULL) {
notify_format(player, T("%s - I don't recognize that power."), flag);
return;
}
if (!can_set_power(player, thing, f, negate)) {
notify(player, T("Permission denied."));
return;
}
twiddle_flag(n, thing, f, negate);
if (!AreQuiet(player, thing)) {
tp = tbuf1;
safe_str(Name(thing), tbuf1, &tp);
safe_str(" - ", tbuf1, &tp);
safe_str(f->name, tbuf1, &tp);
safe_str(negate ? T(" removed.") : T(" granted."), tbuf1, &tp);
*tp = '\0';
notify(player, tbuf1);
}
if (f->perms & F_LOG)
do_log(LT_WIZ, player, thing, "%s POWER %s", f->name,
negate ? "CLEARED" : "SET");
}
/** Check if an object has one or all of a list of flag characters.
* This function is used by orflags and andflags to check to see
* if an object has one or all of the flags signified by a list
* of flag characters.
* \param ns name of namespace to search.
* \param player the object checking, for permissions
* \param it the object on which to check for flags.
* \param fstr string of flag characters to check for.
* \param type 0=orflags, 1=andflags.
* \retval 1 object has any (or all) flags.
* \retval 0 object has no (or not all) flags.
*/
int
flaglist_check(const char *ns, dbref player, dbref it, const char *fstr,
int type)
{
char *s;
FLAG *fp;
int negate, temp;
int ret = type;
FLAGSPACE *n;
negate = temp = 0;
if (!GoodObject(it))
return 0;
if (!(n = (FLAGSPACE *) hashfind(ns, &htab_flagspaces))) {
do_rawlog(LT_ERR, T("FLAG: Unable to locate flagspace %s"), ns);
return 0;
}
for (s = (char *) 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. */
fp = letter_to_flagptr(n, *s, Typeof(it));
if (!fp) {
if (n->tab == &ptab_flag) {
/* Maybe *s is a type specifier (P, T, E, R). These aren't really
* flags, but we grandfather them in to preserve old code
*/
if ((*s == 'T') || (*s == 'R') || (*s == 'E') || (*s == 'P')) {
temp = (*s == 'T') ? (Typeof(it) == TYPE_THING) :
((*s == 'R') ? (Typeof(it) == TYPE_ROOM) :
((*s == 'E') ? (Typeof(it) == TYPE_EXIT) :
(Typeof(it) == TYPE_PLAYER)));
if ((type == 1) && ((negate && temp) || (!negate && !temp)))
return 0;
else if ((type == 0) && ((!negate && temp) || (negate && !temp)))
ret |= 1;
} else {
/* 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 {
if (type == 1)
return 0;
else
continue;
}
} else {
/* does the object have this flag? */
temp = (has_flag_ns(n, it, fp) && Can_See_Flag(player, it, fp));
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;
}
/** Check if an object has one or all of a list of flag names.
* This function is used by orlflags and andlflags to check to see
* if an object has one or all of the flags signified by a list
* of flag names.
* \param ns name of namespace to search.
* \param player the object checking, for permissions
* \param it the object on which to check for flags.
* \param fstr string of flag names, space-separated, to check for.
* \param type 0=orlflags, 1=andlflags.
* \retval 1 object has any (or all) flags.
* \retval 0 object has no (or not all) flags.
*/
int
flaglist_check_long(const char *ns, dbref player, dbref it, const char *fstr,
int type)
{
char *s, *copy, *sp;
FLAG *fp;
int negate, temp;
int ret = type;
FLAGSPACE *n;
negate = temp = 0;
if (!GoodObject(it))
return 0;
if (!(n = (FLAGSPACE *) hashfind(ns, &htab_flagspaces))) {
do_rawlog(LT_ERR, T("FLAG: Unable to locate flagspace %s"), ns);
return 0;
}
copy = mush_strdup(fstr, "flaglistlong");
sp = trim_space_sep(copy, ' ');
while (sp) {
s = split_token(&sp, ' ');
/* 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 !D c means (!D && !c), instead of (!D && c). */
}
if (!*s) {
/* We got a '!' that wasn't followed by a string.
* Fail the check. */
if (type == 1)
ret = 0;
break;
}
/* Find the flag. */
if (!(fp = flag_hash_lookup(n, s, Typeof(it)))) {
/* 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) {
ret = 0;
break;
} else
continue;
} else {
/* does the object have this flag? There's a special case
* here, as we want (for consistency with flaglist_check)
* to allow types to match as well
*/
int in_flags = (n->tab == &ptab_flag);
if (in_flags && !strcmp(fp->name, "PLAYER"))
temp = IsPlayer(it);
else if (in_flags && !strcmp(fp->name, "THING"))
temp = IsThing(it);
else if (in_flags && !strcmp(fp->name, "ROOM"))
temp = IsRoom(it);
else if (in_flags && !strcmp(fp->name, "EXIT"))
temp = IsExit(it);
else
temp = (has_flag_ns(n, it, fp) && Can_See_Flag(player, it, fp));
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.
*/
ret = 0;
break;
} 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. */
}
}
mush_free(copy, "flaglistlong");
return ret;
}
/** Can a player see a flag?
* \param ns name of the flagspace to use.
* \param privs looker.
* \param thing object on which to look for flag.
* \param name name of flag to look for.
* \retval 1 object has the flag and looker can see it.
* \retval 0 looker can not see flag on object.
*/
int
sees_flag(const char *ns, dbref privs, dbref thing, const char *name)
{
/* Does thing have the flag named name && can privs see it? */
FLAG *f;
FLAGSPACE *n;
n = hashfind(ns, &htab_flagspaces);
if ((f = flag_hash_lookup(n, name, Typeof(thing))) == NULL)
return 0;
return has_flag_ns(n, thing, f) && Can_See_Flag(privs, thing, f);
}
/** A hacker interface for adding a flag.
* \verbatim
* This function is used to add a new flag to the game. It's
* called by @flag (via do_flag_add()), and is the right function to
* call in flaglocal.c if you're writing a hardcoded system patch that
* needs to add its own flags. It will not add the same flag twice.
* \endverbatim
* \param ns name of namespace to add flag to.
* \param name flag name.
* \param letter flag character (or ascii 0)
* \param type mask of object types to which the flag applies.
* \param perms mask of permissions to see/set the flag.
* \param negate_perms mask of permissions to clear the flag.
*/
FLAG *
add_flag_generic(const char *ns, const char *name, const char letter, int type,
int perms, int negate_perms)
{
FLAG *f;
FLAGSPACE *n;
Flagspace_Lookup(n, ns);
/* Don't double-add */
if ((f = match_flag_ns(n, strupper(name)))) {
if (strcasecmp(f->name, name) == 0)
return f;
}
f = new_flag();
f->name = mush_strdup(strupper(name), "flag name");
f->letter = letter;
f->type = type;
f->perms = perms;
f->negate_perms = negate_perms;
f->bitpos = -1;
flag_add(n, f->name, f);
return f;
}
/*--------------------------------------------------------------------------
* MUSHcode interface
*/
/** User interface to list flags.
* \verbatim
* This function implements @flag/list.
* \endverbatim
* \param ns name of the flagspace to use.
* \param player the enactor.
* \param arg wildcard pattern of flag names to list, or NULL for all.
* \param lc if 1, list flags in lowercase.
* \param label label to prefix to list.
*/
void
do_list_flags(const char *ns, dbref player, const char *arg, int lc,
const char *label)
{
char *b = list_all_flags(ns, arg, player, 0x3);
notify_format(player, "%s: %s", label, lc ? strlower(b) : b);
}
/** User interface to show flag detail.
* \verbatim
* This function implements @flag <flag>.
* \endverbatim
* \param ns name of namespace to search.
* \param player the enactor.
* \param name name of the flag to describe.
*/
void
do_flag_info(const char *ns, dbref player, const char *name)
{
FLAG *f;
FLAGSPACE *n;
/* Find the flagspace */
if (!(n = (FLAGSPACE *) hashfind(ns, &htab_flagspaces))) {
do_rawlog(LT_ERR, T("FLAG: Unable to locate flagspace %s"), ns);
return;
}
/* Find the flag */
f = flag_hash_lookup(n, name, NOTYPE);
if (!f && God(player))
f = match_flag_ns(n, name);
if (!f) {
notify_format(player, T("No such %s."), strlower(ns));
return;
}
notify_format(player, " Name: %s", f->name);
notify_format(player, "Character: %c", f->letter);
notify_format(player, " Aliases: %s", list_aliases(n, f));
notify_format(player, " Type(s): %s", privs_to_string(type_privs, f->type));
notify_format(player, " Perms: %s", privs_to_string(flag_privs, f->perms));
notify_format(player, "ResetPrms: %s",
privs_to_string(flag_privs, f->negate_perms));
}
/** Change the permissions on a flag.
* \verbatim
* This is the user-interface to @flag/restrict, which uses this syntax:
*
* @flag/restrict <flag> = <perms>, <negate_perms>
*
* If no comma is given, use <perms> for both.
* \endverbatim
* \param ns name of namespace to search.
* \param player the enactor.
* \param name name of flag to modify.
* \param args_right array of arguments on the right of the equal sign.
*/
void
do_flag_restrict(const char *ns, dbref player, const char *name,
char *args_right[])
{
FLAG *f;
FLAGSPACE *n;
int perms, negate_perms;
if (!God(player)) {
notify(player, T("You don't have enough magic for that."));
return;
}
n = hashfind(ns, &htab_flagspaces);
if (!(f = flag_hash_lookup(n, name, NOTYPE))) {
notify_format(player, T("No such %s."), strlower(ns));
return;
}
if (!args_right[1] || !*args_right[1]) {
notify_format(player, T("How do you want to restrict that %s?"),
strlower(ns));
return;
}
if (!strcasecmp(args_right[1], "any")) {
perms = F_ANY;
} else {
perms = string_to_privs(flag_privs, args_right[1], 0);
if ((!perms) || (perms & (F_INTERNAL | F_DISABLED))) {
notify(player, T("I don't understand those permissions."));
return;
}
}
if (args_right[2] && *args_right[2]) {
if (!strcasecmp(args_right[2], "any")) {
negate_perms = F_ANY;
} else {
negate_perms = string_to_privs(flag_privs, args_right[2], 0);
if ((!negate_perms) || (negate_perms & (F_INTERNAL | F_DISABLED))) {
notify(player, T("I don't understand those permissions."));
return;
}
}
} else {
negate_perms = string_to_privs(flag_privs, args_right[1], 0);
}
f->perms = perms;
f->negate_perms = negate_perms;
notify_format(player, T("Permissions on %s %s set."), f->name, strlower(ns));
}
/** Change the type of a flag.
* \verbatim
* This is the user-interface to @flag/type, which uses this syntax:
*
* @flag/type <flag> = <type(s)>
*
* We refuse to change the type of a flag if there are objects
* with the flag that would no longer be of the correct type.
* \endverbatim
* \param ns name of the flagspace to use.
* \param player the enactor.
* \param name name of flag to modify.
* \param type_string list of types.
*/
void
do_flag_type(const char *ns, dbref player, const char *name, char *type_string)
{
FLAG *f;
FLAGSPACE *n;
int type;
dbref it;
if (!God(player)) {
notify(player, T("You don't have enough magic for that."));
return;
}
n = hashfind(ns, &htab_flagspaces);
if (!(f = flag_hash_lookup(n, name, NOTYPE))) {
notify_format(player, T("No such %s."), strlower(ns));
return;
}
if (!type_string || !*type_string) {
notify_format(player, T("What type do you want to make that %s?"),
strlower(ns));
return;
}
if (!strcasecmp(type_string, "any")) {
type = NOTYPE;
} else {
type = string_to_privs(type_privs, type_string, 0);
if (!type) {
notify(player, T("I don't understand the list of types."));
return;
}
/* Are there any objects with the flag that don't match these
* types?
*/
for (it = 0; it < db_top; it++) {
if (!(type & Typeof(it)) && has_flag_ns(n, it, f)) {
notify_format(player,
T
("Objects of other types already have this %s set. Search for them and remove it first."),
strlower(ns));
return;
}
}
}
f->type = type;
notify_format(player, T("Type of %s %s set."), f->name, strlower(ns));
}
/** Add a new flag
* \verbatim
* This function implements @flag/add, which uses this syntax:
*
* @flag/add <flag> = <letter>, <type(s)>, <perms>, <negate_perms>
*
* <letter> defaults to none. If given, it must not match an existing
* flag character that could apply to the given type
* <type(s)> defaults to NOTYPE
* <perms> defaults to any
* <negate_perms> defaults to <perms>
* \endverbatim
* \param ns name of the flagspace to use.
* \param player the enactor.
* \param name name of the flag to add.
* \param args_right array of arguments on the right side of the equal sign.
*/
void
do_flag_add(const char *ns, dbref player, const char *name, char *args_right[])
{
char letter = '\0';
int type = NOTYPE;
int perms = F_ANY;
int negate_perms = F_ANY;
FLAG *f;
FLAGSPACE *n;
if (!God(player)) {
notify(player, T("You don't have enough magic for that."));
return;
}
if (!name || !*name) {
notify_format(player, T("You must provide a name for the %s."),
strlower(ns));
return;
}
if (strlen(name) == 1) {
notify_format(player, T("%s names must be longer than one character."),
strinitial(ns));
return;
}
if (strchr(name, ' ')) {
notify_format(player, T("%s names may not contain spaces."),
strinitial(ns));
return;
}
Flagspace_Lookup(n, ns);
/* Do we have a letter? */
if (!args_right) {
notify(player, T("You must provide more information."));
return;
}
if (args_right[1]) {
if (strlen(args_right[1]) > 1) {
notify_format(player, T("%s characters must be single characters."),
strinitial(ns));
return;
}
letter = *args_right[1];
/* Do we have a type? */
if (args_right[2]) {
if (*args_right[2] && strcasecmp(args_right[2], "any"))
type = string_to_privs(type_privs, args_right[2], 0);
if (!type) {
notify(player, T("I don't understand the list of types."));
return;
}
}
/* Is this letter already in use for this type? */
if (*args_right[1]) {
if ((f = letter_to_flagptr(n, *args_right[1], type))) {
notify_format(player, T("Letter conflicts with the %s %s."), f->name,
strlower(ns));
return;
}
}
/* Do we have perms? */
if (args_right[3] && *args_right[3]) {
if (!strcasecmp(args_right[3], "any")) {
perms = F_ANY;
} else {
perms = string_to_privs(flag_privs, args_right[3], 0);
if ((!perms) || (perms & (F_INTERNAL | F_DISABLED))) {
notify(player, T("I don't understand those permissions."));
return;
}
}
}
if (args_right[4] && *args_right[4]) {
if (!strcasecmp(args_right[4], "any")) {
negate_perms = F_ANY;
} else {
negate_perms = string_to_privs(flag_privs, args_right[4], 0);
if ((!negate_perms) || (negate_perms & (F_INTERNAL | F_DISABLED))) {
notify(player, T("I don't understand those permissions."));
return;
}
}
} else
negate_perms = perms;
}
/* Ok, let's do it. */
add_flag_generic(ns, name, letter, type, perms, negate_perms);
/* Did it work? */
if ((f = match_flag_ns(n, name)))
do_flag_info(ns, player, name);
else
notify_format(player, T("Unknown failure adding %s."), strlower(ns));
}
/** Alias a flag.
* \verbatim
* This function implements the @flag/alias commmand.
* \endverbatim
* \param ns name of the flagspace to use.
* \param player the enactor.
* \param name name of the flag to alias.
* \param alias name of the alias.
*/
void
do_flag_alias(const char *ns, dbref player, const char *name, const char *alias)
{
FLAG *f;
FLAGSPACE *n;
if (!God(player)) {
notify(player, T("You don't look like God."));
return;
}
if (!alias || !*alias) {
notify(player, T("You must provide a name for the alias."));
return;
}
if (strlen(alias) == 1) {
notify_format(player, T("%s aliases must be longer than one character."),
strinitial(ns));
return;
}
if (strchr(alias, ' ')) {
notify_format(player, T("%s aliases may not contain spaces."),
strinitial(ns));
return;
}
n = hashfind(ns, &htab_flagspaces);
f = match_flag_ns(n, alias);
if (f) {
notify_format(player, T("That alias already matches the %s %s."),
f->name, strlower(ns));
return;
}
f = match_flag_ns(n, name);
if (!f) {
notify_format(player, T("I don't know that %s."), strlower(ns));
return;
}
if (f->perms & F_DISABLED) {
notify_format(player, T("That %s is disabled."), strlower(ns));
return;
}
/* Insert the flag in the ptab by the given alias */
ptab_start_inserts(n->tab);
ptab_insert(n->tab, alias, f);
ptab_end_inserts(n->tab);
if ((f = match_flag_ns(n, alias)))
do_flag_info(ns, player, alias);
else
notify(player, T("Unknown failure adding alias."));
}
/** Change a flag's letter.
* \param ns name of the flagspace to use.
* \param player the enactor.
* \param name name of the flag.
* \param letter The new alias, or an empty string to remove the alias.
*/
void
do_flag_letter(const char *ns, dbref player, const char *name,
const char *letter)
{
FLAG *f;
FLAGSPACE *n;
if (!God(player)) {
notify(player, T("You don't look like God."));
return;
}
Flagspace_Lookup(n, ns);
f = match_flag_ns(n, name);
if (!f) {
notify_format(player, T("I don't know that %s."), strlower(ns));
return;
}
if (letter && *letter) {
FLAG *other;
if (strlen(letter) > 1) {
notify_format(player, T("%s characters must be single characters."),
strinitial(ns));
return;
}
if ((other = letter_to_flagptr(n, *letter, f->type))) {
notify_format(player, T("Letter conflicts with the %s %s."),
other->name, strlower(ns));
return;
}
f->letter = *letter;
notify_format(player, T("Letter for %s %s set to '%c'."),
strlower(ns), f->name, *letter);
} else { /* Clear a flag */
f->letter = '\0';
notify_format(player, T("Letter for %s %s cleared."), strlower(ns),
f->name);
}
}
/** Disable a flag.
* \verbatim
* This function implements @flag/disable.
* Only God can do this, and it makes the flag effectively
* unusuable - it's invisible to all but God, and can't be set, cleared,
* or tested against.
* \endverbatim
* \param ns name of the flagspace to use.
* \param player the enactor.
* \param name name of the flag to disable.
*/
void
do_flag_disable(const char *ns, dbref player, const char *name)
{
FLAG *f;
FLAGSPACE *n;
if (!God(player)) {
notify(player, T("You don't look like God."));
return;
}
Flagspace_Lookup(n, ns);
f = match_flag_ns(n, name);
if (!f) {
notify_format(player, T("I don't know that %s."), strlower(ns));
return;
}
if (f->perms & F_DISABLED) {
notify_format(player, T("That %s is already disabled."), strlower(ns));
return;
}
/* Do it. */
f->perms |= F_DISABLED;
notify_format(player, T("%s %s disabled."), strinitial(ns), f->name);
}
/** Delete a flag.
* \verbatim
* This function implements @flag/delete.
* Only God can do this, and clears the flag on everyone
* and then removes it and its aliases from the tables.
* Danger, Will Robinson!
* \endverbatim
* \param ns name of the flagspace to use.
* \param player the enactor.
* \param name name of the flag to delete.
*/
void
do_flag_delete(const char *ns, dbref player, const char *name)
{
FLAG *f, *tmpf;
char flagname[BUFFER_LEN];
dbref i;
int got_one;
FLAGSPACE *n;
if (!God(player)) {
notify(player, T("You don't look like God."));
return;
}
n = (FLAGSPACE *) hashfind(ns, &htab_flagspaces);
f = ptab_find_exact(n->tab, name);
if (!f) {
notify_format(player, T("I don't know that %s."), strlower(ns));
return;
}
if (f->perms & F_INTERNAL) {
notify(player, T("There are probably easier ways to crash your MUSH."));
return;
}
/* Remove aliases. Convoluted because ptab_delete probably trashes
* the firstentry/nextentry stuff
*/
do {
got_one = 0;
tmpf = ptab_firstentry_new(n->tab, flagname);
while (tmpf) {
if (!strcmp(tmpf->name, f->name) &&
strcmp(n->flags[f->bitpos]->name, flagname)) {
ptab_delete(n->tab, flagname);
got_one = 1;
break;
}
tmpf = ptab_nextentry_new(n->tab, flagname);
}
} while (got_one);
/* Reset the flag on all objects */
for (i = 0; i < db_top; i++)
twiddle_flag(n, i, f, 1);
/* Remove the flag's entry in flags */
n->flags[f->bitpos] = NULL;
/* Remove the flag from the ptab */
ptab_delete(n->tab, f->name);
notify_format(player, T("%s %s deleted."), strinitial(ns), f->name);
/* Free the flag. */
mush_free((char *) f->name, "flag name");
mush_free(f, "flag");
}
/** Enable a disabled flag.
* \verbatim
* This function implements @flag/enable.
* Only God can do this, and it reverses /disable.
* \endverbatim
* \param ns name of the flagspace to use.
* \param player the enactor.
* \param name name of the flag to enable.
*/
void
do_flag_enable(const char *ns, dbref player, const char *name)
{
FLAG *f;
FLAGSPACE *n;
if (!God(player)) {
notify(player, T("You don't look like God."));
return;
}
Flagspace_Lookup(n, ns);
f = match_flag_ns(n, name);
if (!f) {
notify_format(player, T("I don't know that %s."), strlower(ns));
return;
}
if (!(f->perms & F_DISABLED)) {
notify_format(player, T("That %s is not disabled."), strlower(ns));
return;
}
/* Do it. */
f->perms &= ~F_DISABLED;
notify_format(player, T("%s %s enabled."), strinitial(ns), f->name);
}
static char *
list_aliases(FLAGSPACE * n, FLAG *given)
{
FLAG *f;
static char buf[BUFFER_LEN];
char *bp;
char flagname[BUFFER_LEN];
int first = 1;
bp = buf;
f = ptab_firstentry_new(n->tab, flagname);
while (f) {
if (!strcmp(given->name, f->name) &&
strcmp(n->flags[f->bitpos]->name, flagname)) {
/* This is an alias! */
if (!first)
safe_chr(' ', buf, &bp);
first = 0;
safe_str(flagname, buf, &bp);
}
f = ptab_nextentry_new(n->tab, flagname);
}
*bp = '\0';
return buf;
}
/** Return a list of all flags.
* \param ns name of namespace to search.
* \param name wildcard to match against flag names, or NULL for all.
* \param privs the looker, for permission checking.
* \param which a bitmask of 0x1 (flag chars) and 0x2 (flag names).
*/
char *
list_all_flags(const char *ns, const char *name, dbref privs, int which)
{
FLAG *f;
char **ptrs;
int i, numptrs = 0;
static char buf[BUFFER_LEN];
char *bp;
int disallowed;
FLAGSPACE *n;
Flagspace_Lookup(n, ns);
disallowed = God(privs) ? F_INTERNAL : (F_INTERNAL | F_DISABLED);
if (!Hasprivs(privs))
disallowed |= (F_DARK | F_MDARK);
ptrs = (char **) malloc(n->flagbits * sizeof(char *));
for (i = 0; i < n->flagbits; i++) {
if ((f = n->flags[i]) && !(f->perms & disallowed)) {
if (!name || !*name || quick_wild(name, f->name))
ptrs[numptrs++] = (char *) f->name;
}
}
do_gensort(privs, ptrs, numptrs, ALPHANUM_LIST);
bp = buf;
for (i = 0; i < numptrs; i++) {
switch (which) {
case 0x3:
if (i)
safe_strl(", ", 2, buf, &bp);
safe_str(ptrs[i], buf, &bp);
f = match_flag_ns(n, ptrs[i]);
if (!f)
break;
if (f->letter != '\0')
safe_format(buf, &bp, " (%c)", f->letter);
if (f->perms & F_DISABLED)
safe_str(T(" (disabled)"), buf, &bp);
break;
case 0x2:
if (i)
safe_chr(' ', buf, &bp);
safe_str(ptrs[i], buf, &bp);
break;
case 0x1:
f = match_flag_ns(n, ptrs[i]);
if (f && (f->letter != '\0'))
safe_chr(f->letter, buf, &bp);
break;
}
}
*bp = '\0';
free(ptrs);
return buf;
}
/*--------------------------------------------------------------------------
* Powers
*/
/** Return the object's power for examine.
* \param player looker, for permission checking.
* \param thing object to list powers for.
* \return a string containing all the visible power names on the object.
*/
const char *
power_description(dbref player, dbref thing)
{
static char buf[BUFFER_LEN];
char *bp;
bp = buf;
safe_str(bits_to_string("POWER", Powers(thing), player, thing), buf, &bp);
*bp = '\0';
return buf;
}
/** Show the flags and powers associated with a command.
* \param flagmask the command's flagmask.
* \param powers the command's power mask.
* \return string output of powers and flags.
*/
const char *
show_command_flags(object_flag_type flagmask, object_flag_type powers)
{
static char fbuf[BUFFER_LEN];
char *bp;
bp = fbuf;
if (powers) {
safe_str("Powers : ", fbuf, &bp);
safe_str(bits_to_string("POWER", powers, GOD, NOTHING), fbuf, &bp);
}
/* do generic flags */
if (flagmask) {
if (powers)
safe_chr('\n', fbuf, &bp);
safe_str("Flags : ", fbuf, &bp);
safe_str(bits_to_string("FLAG", flagmask, GOD, NOTHING), fbuf, &bp);
}
*bp = '\0';
return fbuf;
}