pennmush/game/data/
pennmush/game/log/
pennmush/game/save/
pennmush/game/txt/evt/
pennmush/game/txt/nws/
pennmush/os2/
pennmush/po/
pennmush/win32/msvc.net/
pennmush/win32/msvc6/
/**
 * \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;
}