/** * \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; }