nakedmudv3.3/
nakedmudv3.3/lib/
nakedmudv3.3/lib/logs/
nakedmudv3.3/lib/misc/
nakedmudv3.3/lib/players/
nakedmudv3.3/lib/txt/
nakedmudv3.3/lib/world/
nakedmudv3.3/lib/world/examples/
nakedmudv3.3/lib/world/examples/mproto/
nakedmudv3.3/lib/world/examples/oproto/
nakedmudv3.3/lib/world/examples/reset/
nakedmudv3.3/lib/world/examples/rproto/
nakedmudv3.3/lib/world/examples/trigger/
nakedmudv3.3/lib/world/limbo/
nakedmudv3.3/lib/world/limbo/room/
nakedmudv3.3/lib/world/limbo/rproto/
nakedmudv3.3/src/alias/
nakedmudv3.3/src/char_vars/
nakedmudv3.3/src/editor/
nakedmudv3.3/src/example_module/
nakedmudv3.3/src/help/
nakedmudv3.3/src/set_val/
nakedmudv3.3/src/socials/
nakedmudv3.3/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 "alias.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
//*****************************************************************************
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 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(MAX_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 {
      HASH_ITERATOR *alias_i = newHashIterator(data->aliases);
      const char      *alias = NULL;
      const char        *cmd = NULL;

      ITERATE_HASH(alias, cmd, alias_i)
	send_to_char(ch, "  %-20s %s\r\n", alias, cmd);
      deleteHashIterator(alias_i);
    }
  }
  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);
	send_to_char(ch, "Alias deleted.\r\n");
      }
    }

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



//*****************************************************************************
// 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, POS_UNCONSCIOUS, POS_FLYING, 
	  "player", FALSE, TRUE);
}


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;
    }
  }
}