/** * \file atr_tab.c * * \brief The table of standard attributes and code to manipulate it. * * */ #include "config.h" #include <string.h> #include <stdlib.h> #include <stdio.h> #include "conf.h" #include "externs.h" #include "attrib.h" #include "atr_tab.h" #include "ptab.h" #include "privtab.h" #include "mymalloc.h" #include "dbdefs.h" #include "log.h" #include "parse.h" #include "confmagic.h" /** An alias for an attribute. */ typedef struct atr_alias { const char *alias; /**< The alias. */ const char *realname; /**< The attribute's canonical name. */ } ATRALIAS; /** Prefix table for standard attribute names */ PTAB ptab_attrib; /** Attribute flags */ PRIV attr_privs[] = { {"no_command", '$', AF_NOPROG, AF_NOPROG}, {"no_inherit", 'i', AF_PRIVATE, AF_PRIVATE}, {"private", 'i', AF_PRIVATE, AF_PRIVATE}, {"no_clone", 'c', AF_NOCOPY, AF_NOCOPY}, {"wizard", 'w', AF_WIZARD, AF_WIZARD}, {"visual", 'v', AF_VISUAL, AF_VISUAL}, {"mortal_dark", 'm', AF_MDARK, AF_MDARK}, {"hidden", 'm', AF_MDARK, AF_MDARK}, {"regexp", 'R', AF_REGEXP, AF_REGEXP}, {"case", 'C', AF_CASE, AF_CASE}, {"locked", '+', AF_LOCKED, AF_LOCKED}, {"safe", 'S', AF_SAFE, AF_SAFE}, {"internal", '\0', AF_INTERNAL, AF_INTERNAL}, {"prefixmatch", '\0', AF_PREFIXMATCH, AF_PREFIXMATCH}, {"veiled", 'V', AF_VEILED, AF_VEILED}, {"debug", 'b', AF_DEBUG, AF_DEBUG}, {"public", 'p', AF_PUBLIC, AF_PUBLIC}, {"nearby", 'n', AF_NEARBY, AF_NEARBY}, {"noname", 'N', AF_NONAME, AF_NONAME}, {"nospace", 's', AF_NOSPACE, AF_NOSPACE}, {NULL, '\0', 0, 0} }; /*---------------------------------------------------------------------- * Prefix-table functions of various sorts */ static ATTR *aname_find_exact(const char *name); void init_aname_table(void); /** Attribute table lookup by name or alias. * given an attribute name, look it up in the complete attribute table * (real names plus aliases), and return the appropriate real attribute. */ ATTR * aname_hash_lookup(const char *name) { ATTR *ap; /* Exact matches always work */ if ((ap = (ATTR *) ptab_find_exact(&ptab_attrib, name))) return ap; /* Prefix matches work if the attribute is AF_PREFIXMATCH */ if ((ap = (ATTR *) ptab_find(&ptab_attrib, name)) && AF_Prefixmatch(ap)) return ap; return NULL; } /** Build the basic attribute table. */ void init_aname_table(void) { ATTR *ap; ptab_init(&ptab_attrib); ptab_start_inserts(&ptab_attrib); for (ap = attr; ap->name; ap++) ptab_insert(&ptab_attrib, ap->name, ap); ptab_end_inserts(&ptab_attrib); } /** Associate a new alias with an existing attribute. */ int alias_attribute(const char *atr, const char *alias) { ATTR *ap; /* Make sure the alias doesn't exist already */ if (aname_find_exact(alias)) return 0; /* Look up the original */ ap = aname_find_exact(atr); if (!ap) return 0; ptab_start_inserts(&ptab_attrib); ptab_insert(&ptab_attrib, strupper(alias), ap); ptab_end_inserts(&ptab_attrib); return 1; } static ATTR * aname_find_exact(const char *name) { char atrname[BUFFER_LEN]; strcpy(atrname, name); upcasestr(atrname); return (ATTR *) ptab_find_exact(&ptab_attrib, atrname); } /** Add new standard attributes, or change permissions on them. * \verbatim * Given the name and permission string for an attribute, add it to * the attribute table (or modify the permissions if it's already * there). Permissions may be changed retroactively, which modifies * permissions on any copies of that attribute set on objects in the * database. This is the top-level code for @attribute/access. * \endverbatim * \param player the enactor. * \param name the attribute name. * \param perms a string of attribute permissions, space-separated. * \param retroactive if true, apply the permissions retroactively. */ void do_attribute_access(dbref player, char *name, char *perms, int retroactive) { ATTR *ap, *ap2; int flags = 0; int i; int insert = 0; /* Parse name and perms */ if (!name || !*name) { notify(player, T("Which attribute do you mean?")); return; } flags = string_to_privs(attr_privs, perms, 0); if (!flags) { notify(player, T("I don't understand those permissions.")); return; } upcasestr(name); /* Is this attribute already in the table? */ ap = (ATTR *) ptab_find_exact(&ptab_attrib, name); if (ap) { if (AF_Internal(ap)) { /* Don't muck with internal attributes */ notify(player, T("That attribute's permissions can not be changed.")); return; } } else { /* Create fresh if the name is ok */ if (!good_atr_name(name)) { notify(player, T("Invalid attribute name.")); return; } insert = 1; ap = (ATTR *) mush_malloc(sizeof(ATTR), "ATTR"); if (!ap) { notify(player, "Critical memory failure - Alert God!"); do_log(LT_ERR, 0, 0, "do_attribute_access: unable to malloc ATTR"); return; } AL_NAME(ap) = strdup(name); ap->data = NULL_CHUNK_REFERENCE; } AL_FLAGS(ap) = flags; AL_CREATOR(ap) = player; /* Only insert when it's not already in the table */ if (insert) { ptab_start_inserts(&ptab_attrib); ptab_insert(&ptab_attrib, name, ap); ptab_end_inserts(&ptab_attrib); } /* Ok, now we need to see if there are any attributes of this name * set on objects in the db. If so, and if we're retroactive, set * perms/creator */ if (retroactive) { for (i = 0; i < db_top; i++) { if ((ap2 = atr_get_noparent(i, name))) { AL_FLAGS(ap2) = flags; AL_CREATOR(ap2) = player; } } } notify_format(player, T("%s -- Attribute permissions now: %s"), name, privs_to_string(attr_privs, flags)); } /** Delete an attribute from the attribute table. * \verbatim * Top-level function for @attrib/delete. * \endverbatim * \param player the enactor. * \param name the name of the attribute to delete. */ void do_attribute_delete(dbref player, char *name) { ATTR *ap; if (!name || !*name) { notify(player, T("Which attribute do you mean?")); return; } /* Is this attribute in the table? */ ap = (ATTR *) ptab_find_exact(&ptab_attrib, name); if (!ap) { notify(player, T("That attribute isn't in the attribute table")); return; } /* Ok, take it out of the hash table */ ptab_delete(&ptab_attrib, name); notify_format(player, T("Removed %s from attribute table."), name); return; } /** Rename an attribute in the attribute table. * \verbatim * Top-level function for @attrib/rename. * \endverbatim * \param player the enactor. * \param old the name of the attribute to rename. * \param newname the new name (surprise!) */ void do_attribute_rename(dbref player, char *old, char *newname) { ATTR *ap; if (!old || !*old || !newname || !*newname) { notify(player, T("Which attributes do you mean?")); return; } upcasestr(old); upcasestr(newname); /* Is the new name valid? */ if (!good_atr_name(newname)) { notify(player, T("Invalid attribute name.")); return; } /* Is the new name already in use? */ ap = (ATTR *) ptab_find_exact(&ptab_attrib, newname); if (ap) { notify_format(player, T("The name %s is already used in the attribute table."), newname); return; } /* Is the old name a real attribute? */ ap = (ATTR *) ptab_find_exact(&ptab_attrib, old); if (!ap) { notify(player, T("That attribute isn't in the attribute table")); return; } /* Ok, take it out and put it back under the new name */ ptab_delete(&ptab_attrib, old); /* This causes a slight memory leak if you rename an attribute added via /access. But that doesn't happen often. Will fix someday. */ AL_NAME(ap) = strdup(newname); ptab_start_inserts(&ptab_attrib); ptab_insert(&ptab_attrib, newname, ap); ptab_end_inserts(&ptab_attrib); notify_format(player, T("Renamed %s to %s in attribute table."), old, newname); return; } /** Display information on an attribute from the table. * \verbatim * Top-level function for @attribute. * \endverbatim * \param player the enactor. * \param name the name of the attribute. */ void do_attribute_info(dbref player, char *name) { ATTR *ap; if (!name || !*name) { notify(player, T("Which attribute do you mean?")); return; } /* Is this attribute in the table? */ if (*name == '@') name++; ap = aname_hash_lookup(name); if (!ap) { notify(player, T("That attribute isn't in the attribute table")); return; } notify_format(player, "Attribute: %s", AL_NAME(ap)); notify_format(player, " Flags: %s", privs_to_string(attr_privs, AL_FLAGS(ap))); notify_format(player, " Creator: %s", unparse_dbref(AL_CREATOR(ap))); return; } /** Display a list of standard attributes. * \verbatim * Top-level function for @list/attribs. * \endverbatim * \param player the enactor. * \param lc if true, display the list in lowercase; otherwise uppercase. */ void do_list_attribs(dbref player, int lc) { char *b = list_attribs(); notify_format(player, "Attribs: %s", lc ? strlower(b) : b); } /** Return a list of standard attributes. * This functions returns the list of standard attributes, separated by * spaces, in a statically allocated buffer. */ char * list_attribs(void) { ATTR *ap; const char *ptrs[BUFFER_LEN / 2]; static char buff[BUFFER_LEN]; char *bp; int nptrs = 0, i; ap = (ATTR *) ptab_firstentry(&ptab_attrib); while (ap) { ptrs[nptrs++] = AL_NAME(ap); ap = (ATTR *) ptab_nextentry(&ptab_attrib); } bp = buff; safe_str(ptrs[0], buff, &bp); for (i = 1; i < nptrs; i++) { safe_chr(' ', buff, &bp); safe_str(ptrs[i], buff, &bp); } *bp = '\0'; return buff; } /** Attr things to be done after the config file is loaded but before * objects are restarted. */ void attr_init_postconfig(void) { ATTR *a; /* read_remote_desc affects AF_NEARBY flag on DESCRIBE attribute */ a = aname_hash_lookup("DESCRIBE"); if (READ_REMOTE_DESC) a->flags &= ~AF_NEARBY; else a->flags |= AF_NEARBY; }