nakedmud-mod/
nakedmud-mod/html/tutorials/
nakedmud-mod/html/tutorials/building_extras/
nakedmud-mod/html/tutorials/c/
nakedmud-mod/html/tutorials/reference/
nakedmud-mod/html/tutorials/scripting/
nakedmud-mod/html/tutorials/scripting_extras/
nakedmud-mod/lib/
nakedmud-mod/lib/help/A/
nakedmud-mod/lib/help/B/
nakedmud-mod/lib/help/C/
nakedmud-mod/lib/help/D/
nakedmud-mod/lib/help/G/
nakedmud-mod/lib/help/H/
nakedmud-mod/lib/help/J/
nakedmud-mod/lib/help/L/
nakedmud-mod/lib/help/M/
nakedmud-mod/lib/help/O/
nakedmud-mod/lib/help/P/
nakedmud-mod/lib/help/R/
nakedmud-mod/lib/help/S/
nakedmud-mod/lib/help/W/
nakedmud-mod/lib/logs/
nakedmud-mod/lib/misc/
nakedmud-mod/lib/players/
nakedmud-mod/lib/pymodules/polc/
nakedmud-mod/lib/txt/
nakedmud-mod/lib/world/
nakedmud-mod/lib/world/zones/examples/
nakedmud-mod/lib/world/zones/examples/mproto/
nakedmud-mod/lib/world/zones/examples/oproto/
nakedmud-mod/lib/world/zones/examples/reset/
nakedmud-mod/lib/world/zones/examples/rproto/
nakedmud-mod/lib/world/zones/examples/trigger/
nakedmud-mod/lib/world/zones/limbo/
nakedmud-mod/lib/world/zones/limbo/room/
nakedmud-mod/lib/world/zones/limbo/rproto/
nakedmud-mod/src/alias/
nakedmud-mod/src/dyn_vars/
nakedmud-mod/src/editor/
nakedmud-mod/src/example_module/
nakedmud-mod/src/help2/
nakedmud-mod/src/set_val/
nakedmud-mod/src/socials/
nakedmud-mod/src/time/
//*****************************************************************************
//
// alias.c
//
// Aliases allow a player to set up one command as another (e.g. "eat bread"
// as "food").
//
//*****************************************************************************

#include "../mud.h"
#include "../utils.h"
#include "../auxiliary.h"
#include "../storage.h"
#include "../character.h"
#include "../socket.h"
#include "../action.h"
#include "../hooks.h"

#include "alias.h"



//*****************************************************************************
// mandatory modules
//*****************************************************************************
#include "../scripts/scripts.h"
#include "../scripts/pychar.h"



//*****************************************************************************
// auxiliary data
//*****************************************************************************
typedef struct alias_aux_data {
  HASHTABLE *aliases;
  int    alias_queue;
} ALIAS_AUX_DATA;


ALIAS_AUX_DATA *
newAliasAuxData() {
  ALIAS_AUX_DATA *data = calloc(1, sizeof(ALIAS_AUX_DATA));
  //
  // Hashtables can take up lots of storage space. Because of this, let's
  // not create any tables until it's actually needed. This will cut down
  // on lots of memory usage w.r.t. NPCs who do not use aliases
  //  data->aliases        = newHashtable();
  //
  return data;
}


void
deleteAliasAuxData(ALIAS_AUX_DATA *data) {
  if(data->aliases) {
    HASH_ITERATOR *hash_i = newHashIterator(data->aliases);
    const char     *alias = NULL;
    char             *cmd = NULL;

    ITERATE_HASH(alias, cmd, hash_i) 
      free(cmd);
    deleteHashIterator(hash_i);
    deleteHashtable(data->aliases);
  }
  free(data);
}


void
aliasAuxDataCopyTo(ALIAS_AUX_DATA *from, ALIAS_AUX_DATA *to) {
  // aliases are for personal use, and it is doubtful we would
  // ever have a motivation for copying them over. Thus, we shall
  // not do any copying here.
}


ALIAS_AUX_DATA *
aliasAuxDataCopy(ALIAS_AUX_DATA *data) {
  // as with aliasAuxDataCopyTo, there seems to be little reason
  // why we would ever want to copy aliases. They are personal,
  // and have no reason for being copied. Thus, let us just return
  // a new datastructure.
  return newAliasAuxData();
}


STORAGE_SET *aliasAuxDataStore(ALIAS_AUX_DATA *data) {
  // if we have no hashtable, return an empty set
  if(data->aliases == NULL || hashSize(data->aliases) == 0)
    return new_storage_set();

  STORAGE_SET *set       = new_storage_set();
  STORAGE_SET_LIST *list = new_storage_list();
  HASH_ITERATOR  *hash_i = newHashIterator(data->aliases);
  const char       *name = NULL;
  const char        *cmd = NULL;

  store_list(set, "aliases", list);
  ITERATE_HASH(name, cmd, hash_i) {
    STORAGE_SET *alias_set = new_storage_set();
    store_string(alias_set, "key", name);
    store_string(alias_set, "val", hashIteratorCurrentVal(hash_i));
    storage_list_put(list, alias_set);
  }
  deleteHashIterator(hash_i);
  return set;
}


ALIAS_AUX_DATA *aliasAuxDataRead(STORAGE_SET *set) {
  // if the file contains no alias data, then don't load any
  if(!storage_contains(set, "aliases"))
    return newAliasAuxData();

  ALIAS_AUX_DATA *data   = newAliasAuxData();
  data->aliases          = newHashtable();
  STORAGE_SET_LIST *list = read_list(set, "aliases");
  STORAGE_SET *var       = NULL;

  while( (var = storage_list_next(list)) != NULL)
    hashPut(data->aliases, read_string(var, "key"), 
	    strdup(read_string(var, "val")));
  return data;
}



//*****************************************************************************
// functions for interacting with character aliases
//*****************************************************************************
LIST *charGetAliases(CHAR_DATA *ch) {
  ALIAS_AUX_DATA *data = charGetAuxiliaryData(ch, "alias_aux_data");
  if(data->aliases == NULL)
    return newList();
  return hashCollect(data->aliases);
}

const char *charGetAlias(CHAR_DATA *ch, const char *alias) {
  ALIAS_AUX_DATA *data = charGetAuxiliaryData(ch, "alias_aux_data");
  if(data->aliases == NULL)
    return NULL;
  return hashGet(data->aliases, alias);
}

void charClearAliases(CHAR_DATA *ch) {
  LIST *aliases = charGetAliases(ch);
  LIST_ITERATOR *alias_i = newListIterator(aliases);
  const char      *alias = NULL;
  ITERATE_LIST(alias, alias_i) {
    charSetAlias(ch, alias, NULL);
  } deleteListIterator(alias_i);
  deleteListWith(aliases, free);
}

void charSetAlias(CHAR_DATA *ch, const char *alias, const char *cmd){
  ALIAS_AUX_DATA *data = charGetAuxiliaryData(ch, "alias_aux_data");
  // if our alias table is NULL, create a new one
  if(data->aliases == NULL)
    data->aliases = newHashtable();

  // pull out the last one
  char *oldcmd = hashRemove(data->aliases, alias);

  if(oldcmd != NULL)
    free(oldcmd);

  // put in the new one if it exists
  if(cmd && *cmd) {
    hashPut(data->aliases, alias, strdup(cmd));
  }
}

int charGetAliasesQueued(CHAR_DATA *ch) {
  ALIAS_AUX_DATA *data = charGetAuxiliaryData(ch, "alias_aux_data");
  return data->alias_queue;
}

void charSetAliasesQueued(CHAR_DATA *ch, int amnt) {
  ALIAS_AUX_DATA *data = charGetAuxiliaryData(ch, "alias_aux_data");
  data->alias_queue = amnt;
}

BUFFER *expand_alias(CHAR_DATA *ch, const char *alias, const char *arg) {
  static int func_depth      = 0;
  static int MAX_ALIAS_DEPTH = 10;
  BUFFER *cmd = newBuffer(SMALL_BUFFER);
  func_depth++;

  BUFFER *filled_alias = newBuffer(SMALL_BUFFER);
  bufferCat(filled_alias, alias);
  // now, replace all of our parameters 
  int i;
  for(i = 1; i < 10; i++) {
    char param[3];
    char one_arg[SMALL_BUFFER];
    sprintf(param, "$%d", i);
    arg_num(arg, one_arg, i);
    bufferReplace(filled_alias, param, one_arg, TRUE);
  }

  // then we replace the wildcard
  bufferReplace(filled_alias, "$*", arg, TRUE);
  alias = bufferString(filled_alias);

  if(func_depth <= MAX_ALIAS_DEPTH) {
    for(i = 0; alias[i] != '\0'; i++) {
      // do we have an embedded alias or not?
      if(alias[i] != '[')
	bprintf(cmd, "%c", alias[i]);

      else {
	// figure out the start and end of the embedded alias
	int start = i;
	int depth = 1;
	for(i++; alias[i] != '\0' && depth > 0; i++) {
	  if(alias[i] == '[')
	    depth++;
	  else if(alias[i] == ']')
	    depth--;
	}

	// only cat something if we closed the alias off
	if(depth == 0) {
	  // make a copy of it, minus the opening and closing braces
	  char *newstring = strdup(alias+start+1);
	  newstring[i-start-2] = '\0';
	  char newcmd[SMALL_BUFFER];
	  char *newarg = one_arg(newstring, newcmd);
	  const char *format = charGetAlias(ch, newcmd);
	  
	  // do we have a format? if so, expand the new alias and cat it in
	  if(format != NULL) {
	    BUFFER *newbuf = expand_alias(ch, format, newarg);
	    bufferCat(cmd, bufferString(newbuf));
	    deleteBuffer(newbuf);
	  }

	  // clean up our mess
	  free(newstring);
	}

	// because we'll be incrementing on the next go around the loop
	i--;
      }
    }
  }

  func_depth--;
  deleteBuffer(filled_alias);
  return cmd;
}



//*****************************************************************************
// character commands
//*****************************************************************************

//
// Set or delete an alias. If no argument is supplied, all aliases are listed.
COMMAND(cmd_alias) {
  // list off all of the aliases
  if(!arg || !*arg) {
    ALIAS_AUX_DATA *data = charGetAuxiliaryData(ch, "alias_aux_data");
    send_to_char(ch, "Current aliases:\r\n");
    if(data->aliases == NULL || hashSize(data->aliases) == 0)
      send_to_char(ch, "  none\r\n");
    else {
      LIST             *keys = hashCollect(data->aliases);
      listSortWith(keys, strcasecmp);
      LIST_ITERATOR   *key_i = newListIterator(keys);
      const char        *key = NULL;

      ITERATE_LIST(key, key_i) {
	send_to_char(ch, "  %-20s %s\r\n", key, (char *)hashGet(data->aliases, key));
      } deleteListIterator(key_i);
      deleteListWith(keys, free);
    }
  }
  else {
    char alias[SMALL_BUFFER];
    arg = one_arg(arg, alias);

    // try to delete an alias
    if(!arg || !*arg) {
      const char *curr_cmd = charGetAlias(ch, alias);
      if(!curr_cmd)
	send_to_char(ch, "You do not have such an alias.\r\n");
      else {
	charSetAlias(ch, alias, arg);
      }
    }

    // try to set an alias
    else {
      charSetAlias(ch, alias, arg);
      send_to_char(ch, "Alias set.\r\n");
    }
  }
}



//*****************************************************************************
// Python extensions
//*****************************************************************************
PyObject *PyChar_GetAlias(PyObject *self, PyObject *args) {
  char   *alias = NULL;
  CHAR_DATA *ch = NULL;
  if(!PyArg_ParseTuple(args, "s", &alias)) {
    PyErr_Format(PyExc_TypeError, "aliases must be string names.");
    return NULL;
  }

  if( (ch = PyChar_AsChar(self)) == NULL) {
    PyErr_Format(PyExc_TypeError, "Character %d does not exist.",
		 PyChar_AsUid(self));
    return NULL;
  }

  return Py_BuildValue("z", charGetAlias(ch, alias));
}

PyObject *PyChar_SetAlias(PyObject *self, PyObject *args) {
  char     *cmd = NULL;
  char   *alias = NULL;
  CHAR_DATA *ch = NULL;
  if(!PyArg_ParseTuple(args, "sz", &alias, &cmd)) {
    PyErr_Format(PyExc_TypeError, "aliases and commands must be string names.");
    return NULL;
  }

  if( (ch = PyChar_AsChar(self)) == NULL) {
    PyErr_Format(PyExc_TypeError, "Character %d does not exist.",
		 PyChar_AsUid(self));
    return NULL;
  }

  charSetAlias(ch, alias, cmd);
  return Py_BuildValue("i", 1);
}

PyObject *PyChar_GetAliases(PyObject *self, void *closure) {
  CHAR_DATA *ch = NULL;
  if((ch = PyChar_AsChar(self)) == NULL) {
    PyErr_Format(PyExc_TypeError, "Character %d does not exist.",
		 PyChar_AsUid(self));
    return NULL;
  }

  PyObject       *pylist = PyList_New(0);
  LIST             *list = charGetAliases(ch);
  LIST_ITERATOR *alias_i = newListIterator(list);
  const char      *alias = NULL;
  ITERATE_LIST(alias, alias_i) {
    PyObject *str = Py_BuildValue("s", alias);
    PyList_Append(pylist, str);
    Py_DECREF(str);
  } deleteListIterator(alias_i);
  deleteListWith(list, free);
  return pylist;
}



//*****************************************************************************
// implementation of alias.h
//*****************************************************************************
void init_aliases() {
  // install aliases on the character datastructure
  auxiliariesInstall("alias_aux_data",
		     newAuxiliaryFuncs(AUXILIARY_TYPE_CHAR,
				       newAliasAuxData, deleteAliasAuxData,
				       aliasAuxDataCopyTo, aliasAuxDataCopy,
				       aliasAuxDataStore, aliasAuxDataRead));

  // allow people to view their aliases
  add_cmd("alias", NULL, cmd_alias, "player", TRUE);

  // Python extensions
  PyChar_addMethod("get_alias", PyChar_GetAlias,   METH_VARARGS, 
    "get_alias(name)\n"
    "\n"
    "Return character's alias by the specified name, or None.");
  PyChar_addMethod("set_alias", PyChar_SetAlias,   METH_VARARGS, 
    "set_alias(name, value)\n"
    "\n"
    "Set a character's alias. Value must be a string.");
  PyChar_addGetSetter("aliases",PyChar_GetAliases, NULL, 
    "A list of all aliases the character currently has defined. Immutable.");
}


bool try_alias(CHAR_DATA *ch, char *command, char *arg) {
  // is this command from an alias that executes multi commands?
  // if it is, don't let it trigger any further aliases, or else we might
  // get stuck in an infinite loop
  bool    aliases_ok = TRUE;
  int aliases_queued = charGetAliasesQueued(ch);
  if(aliases_queued > 0) {
    // this command came from an alias. ergo, there is going to be no echo
    // of the command. send the socket a newline so we don't print on the
    // person's prompt
    send_to_char(ch, "\r\n");
    aliases_ok = FALSE;
  }

  // make sure it didn't come from another alias
  if(!aliases_ok)
    return FALSE;
  else {
    const char *alias = charGetAlias(ch, command);
    // see if the alias exists
    if(alias == NULL)
      return FALSE;
    else {
      // break the buffer contents up into multiple commands, if there are any
      BUFFER     *buf = expand_alias(ch, alias, arg);
      LIST      *cmds = parse_strings(bufferString(buf), ';');
      char     *first = listPop(cmds);
      
      // queue all of our commands after the first onto the command list
      if(charGetSocket(ch) && listSize(cmds) > 0) {
	LIST_ITERATOR *cmd_i = newListIterator(cmds);
	char            *cmd = NULL;
	ITERATE_LIST(cmd, cmd_i) {
	  socketQueueCommand(charGetSocket(ch), cmd);
	} deleteListIterator(cmd_i);
	charSetAliasesQueued(ch, listSize(cmds) + 1);
      }
      if(first != NULL)
	do_cmd(ch, first, FALSE);

      deleteListWith(cmds, free);
      if(first) free(first);
      deleteBuffer(buf);
      return TRUE;
    }
  }
}