/
roa/
roa/lib/boards/
roa/lib/config/
roa/lib/edits/
roa/lib/help/
roa/lib/misc/
roa/lib/plrobjs/
roa/lib/quests/
roa/lib/socials/
roa/lib/www/
roa/lib/www/LEDSign/
roa/lib/www/LEDSign/fonts/
roa/lib/www/LEDSign/scripts/
roa/src/s_inc/
roa/src/sclient/
roa/src/sclient/binary/
roa/src/sclient/text/
roa/src/util/
/************************************************************************
Realms of Aurealis 		James Rhone aka Vall of RoA

alias.c				Code dealing with the loading/saving 
				of player aliases and other code from
				circleMUD3.0/2.2.	

		******** Heavily modified and expanded ********
		*** BE AWARE OF ALL RIGHTS AND RESERVATIONS ***
		******** Heavily modified and expanded ********
		        All rights reserved henceforth. 

    Please note that no guarantees are associated with any code from
Realms of Aurealis.  All code which has been released to the general
public has been done so with an 'as is' pretense.  RoA is based on both
Diku and CircleMUD and ALL licenses from both *MUST* be adhered to as well
as the RoA license.   *** Read, Learn, Understand, Improve ***
*************************************************************************/
#include "conf.h"
#include "sysdep.h"

#include "structures.h"
#include "utils.h"
#include "comm.h"
#include "handler.h"
#include "interpreter.h"
#include "acmd.h"
#include "db.h"
#include "mudlimits.h"
#include "screen.h"
#include "objsave.h"
#include "global.h"

#define ALIAS_DIRNAME "aliases"

char *delete_doubledollar(char *string);
void load_aliases(chdata *ch);
void save_aliases(chdata *ch);

BOOL    delete_alias_file(chdata *ch)
{
   char filename[50];
   char name[100];

   if (IS_NPC(ch)) return FALSE;
   str_cpy(name, GET_NAME(ch), 100, "delete_alias_file");

   if (!get_pdata_filename(name, "als", filename))
      return FALSE;

   if (unlink(filename) < 0) {
      if (errno != ENOENT) { /* if it fails, NOT because of no file */
         sprintf(buf1, "SYSERR: deleting Alias file %s (2)", filename);
         perror(buf1);
      }
   }
   return(1);
}

// return the number of aliases a given character has
int num_aliases(chdata *ch)
{
  int i;
  struct alias *a = GET_ALIASES(ch);

  for (i = 0; a; a = a->next, i++)
    ;
  return i;
}

// given an aliast list and a word, search for that alias in the list
// return ptr to the alias containing the correct one
// we can use alias_list to traverse, not getting modified (call by value)
struct alias *find_alias(struct alias *alias_list, char *str)
{
  if (!str || !*str) return NULL;

  while (alias_list) 
  {
    if (*str == *alias_list->alias) // if the first character matches
      if (!str_cmp(str, alias_list->alias))
	return alias_list;
    alias_list = alias_list->next;
  }

  // not found
  return NULL;
}

// zap the strings of the alias then the alias itself
// must set it to NULL from caller as it's not de-referenced
struct alias *free_alias(struct alias *a)
{
  FREENULL(a->alias);
  FREENULL(a->replacement);
  FREENULL(a);
  return NULL;
}

/* The interface to the outside world: do_alias */
// now with max_number check -roa
ACMD(do_alias)
{
  char *repl = NULL, *repu = NULL, *argu = NULL;
  struct alias *a = NULL, *temp = NULL;
  BOOL found = FALSE;

  if (IS_NPC(ch))
    return;

  if (strlen(argument) > 115)
  {
    send_to_char("Alias too long.  Not stored.\n\r",ch);
    return;
  }

  repl = any_one_arg(argument, arg);

  if (!*arg) /* no argument specified -- list currently defined aliases */
  {			
    send_to_char("%6Currently defined aliases%0:\r\n", ch);
    if ((a = GET_ALIASES(ch)) == NULL)
    {
      send_to_char(" None.\r\n", ch);
      return;
    }

    *buf = '\0';
    while (a)
    {
      sprintf(buf+strlen(buf), "%%B%-15s%%0 %s\r\n", a->alias,a->replacement);
      a = a->next;
    }

    sprintf(buf+strlen(buf), "%%B%d%%0 out of %%B%d%%0 aliases defined.\n\r", num_aliases(ch),
            max_aliases);

    if (num_aliases(ch) > max_aliases)
      sprintf(buf+strlen(buf), "%%1WARNING:%%0 You have exceeded the alias limit.\n\r"
	      "Please remove the overage immediately.\n\r");

    page_string(ch->desc, buf, 1);

    return;
  } 

  // else we have an argument

  /* is this an alias we've already defined? */
  if ((a = find_alias(GET_ALIASES(ch), arg)) != NULL) 
  {
    REMOVE_FROM_LIST(a, GET_ALIASES(ch), next);
    free_alias(a);
    found = TRUE;
  }

  /* if no replacement string is specified, assume we want to delete */
  if (!*repl) 
  {
    if (found)
    {
      send_to_char("Alias deleted.\r\n", ch);
      save_aliases(ch);
    }
    else
      send_to_char("No such alias.\r\n", ch);

    return;
  } 

  if (!str_cmp(arg, "alias")) 
  {
    send_to_char("You can't alias 'alias'.\r\n", ch);
    return;
  }

  if (num_aliases(ch) >= max_aliases)
  {
    send_to_char("%1WARNING:%0 You have too many aliases.\n\r",ch);
    return;
  }

  // ok, time to create a new one, dup strings over to new alias struct
  CREATE(a, struct alias, 1);

  argu = arg;
  skip_spaces(&argu);
  a->alias = str_dup(argu);

  repu = repl;
  skip_spaces(&repu);
  delete_doubledollar(repu);
  a->replacement = str_dup(repu);

  // determine this alias's type
  if (strchr(repu, ALIAS_SEP_CHAR) || strchr(repu, ALIAS_VAR_CHAR))
    a->type = ALIAS_COMPLEX;
  else
    a->type = ALIAS_SIMPLE;

  // insert new one to head of list
  a->next = GET_ALIASES(ch);
  GET_ALIASES(ch) = a;

  send_to_char("Alias added.\r\n", ch);
  save_aliases(ch);
}

/*
 * Valid numeric replacements are only &1 .. &9 (makes parsing a little
 * easier, and it's not that much of a limitation anyway.)  Also valid
 * is "&*", which stands for the entire original line after the alias.
 * ";" is used to delimit commands.
 */
#define NUM_TOKENS       9

// adopted from circle 3.0 alias, again, wasnt nulling out buf -roa
void perform_complex_alias(struct txt_q *input_q, char *orig, struct alias *a)
{
  struct txt_q temp_queue;
  char *tokens[NUM_TOKENS], *temp, *write_point;
  int num_of_tokens = 0, num;

  /* First, parse the original string */
  temp = strtok(strcpy(buf2, orig), " ");
  while (temp != NULL && num_of_tokens < NUM_TOKENS) 
  {
    tokens[num_of_tokens++] = temp;
    temp = strtok(NULL, " ");
  }

  /* initialize */
  write_point = buf;
  temp_queue.head = temp_queue.tail = NULL;

  /* now parse the alias */
  for (temp = a->replacement; *temp; temp++) 
  {
    if (*temp == ALIAS_SEP_CHAR) 
    {
      *write_point = '\0';
      buf[MAX_INPUT_LENGTH - 1] = '\0';  // keep it under max length
      write_to_q(buf, &temp_queue, 1);
      write_point = buf;
    } 
    else 
    if (*temp == ALIAS_VAR_CHAR) 
    {
      temp++;
      if ((num = *temp - '1') < num_of_tokens && num >= 0) 
      {
	strcpy(write_point, tokens[num]);
	write_point += strlen(tokens[num]);
      } 
      else 
      if (*temp == ALIAS_GLOB_CHAR) 
      {
	strcpy(write_point, orig);
	write_point += strlen(orig);
      } 
      else 
      if ((*(write_point++) = *temp) == '$')	/* redouble $ for act safety */
	*(write_point++) = '$';
    } 
    else
      *(write_point++) = *temp;
  }

  *write_point = '\0';
  buf[MAX_INPUT_LENGTH - 1] = '\0';
  write_to_q(buf, &temp_queue, 1);

  /* push our temp_queue on to the _front_ of the input queue */
  if (input_q->head == NULL)
    *input_q = temp_queue;
  else {
    temp_queue.tail->next = input_q->head;
    input_q->head = temp_queue.head;
  }
}

/*
 * Given a character and a string, perform alias replacement on it.
 *
 * Return values:
 *   0: String was modified in place; call command_interpreter immediately.
 *   1: String was _not_ modified in place; rather, the expanded aliases
 *      have been placed at the front of the character's input queue.
 */

int perform_alias(dsdata *d, char *comm)
{
  char first_arg[MAX_INPUT_LENGTH], *ptr;
  struct alias *a, *tmp;

  if (IS_NPC(d->character))
    return 0;

  /* bail out immediately if the guy doesn't have any aliases */
  if (!(tmp = GET_ALIASES(d->character)))
    return 0;

  /* find the alias we're supposed to match */
  ptr = any_one_arg(comm, first_arg); 

  /* bail out if it's null */
  if (!*first_arg)
    return 0;

  /* if the first arg is not an alias, return without doing anything */
  if (!(a = find_alias(tmp, first_arg)))
    return 0;

  if (a->type == ALIAS_SIMPLE) {
    strcpy(comm, a->replacement);
    return 0;
  } else {
    perform_complex_alias(&d->input, ptr, a);
    return 1;
  }
}

/* Remove double dollar signs */
char *delete_doubledollar(char *string)
{
  char *read, *write;

  if (!string) 
    return string;

  if ((write = strchr(string, '$')) == NULL)
    return string;

  read = write;

  while (*read)
    if ((*(write++) = *(read++)) == '$')
      if (*read == '$')
	read++;

  *write = '\0';

  return string;
}

/* loading - saving of aliases */
// 10-21-96 going thru and getting rid of mem leaks and tightening -roa
void load_aliases(chdata *ch) 
{ 
  FILE *fp; 
  char name[MAX_INPUT_LENGTH]; 
  char comm[MAX_INPUT_LENGTH]; 
  char raw[MAX_INPUT_LENGTH];
  char tmp[MAX_INPUT_LENGTH];
  struct alias *a = NULL;
  char *aname, *acomm;
  int i = 0;
  extern struct alias *free_alias_list(struct alias *head);
 
  if (IS_NPC(ch))  
    return; 
 
  // might have some allocated, free em to be sure -roa
  GET_ALIASES(ch) = free_alias_list(GET_ALIASES(ch));

  get_pdata_filename(GET_NAME(ch), "als", raw);

  if (!(fp = fopen(raw, "r")))
    return; 
 
  // read in data one line at a time, til newline or eol or #chars == 120
  while(fgets(raw,120,fp)) 
  { 
    a = NULL;

    /* get rid of the newline on the very end */
    for(i=0; (i < 119) && *(raw + i) && !ISNEWL(*(raw+i)); i++)
      ;
    *(raw+i) = '\0';

    // if there is stuff after the 120 mark, scan to eol past it -roa
    if (i >= 120)
      if (fgets(tmp, 1, fp))
        while (fgets(tmp, 1, fp) && !ISNEWL(*tmp))
          ;

    // split up raw into first word and trailing words name & comm
    half_chop(raw, name, comm); 

    // dupe first word into alias
    aname = str_dup(name);
    skip_spaces(&aname);

    acomm = str_dup(comm);
    skip_spaces(&acomm);

    if((!aname || !*aname || ISNEWL(*aname)) || (!acomm || !*acomm || ISNEWL(*acomm))) 
    {
      FREENULL(aname);
      FREENULL(acomm);
      continue;
    }

    /* OK, looks ok now, alloc an alias structure and assign strings to it*/
    CREATE(a, struct alias, 1);
    a->alias = str_dup(aname);
    a->replacement = str_dup(acomm);

    FREENULL(aname);
    FREENULL(acomm);

    if(strchr(comm, ALIAS_SEP_CHAR) || strchr(comm, ALIAS_VAR_CHAR) || strchr(comm, '&'))
      a->type = ALIAS_COMPLEX;
    else
      a->type = ALIAS_SIMPLE;

    // now insert into list
    a->next = GET_ALIASES(ch);
    GET_ALIASES(ch) = a;
  }  
  fclose(fp); 
} 

/* saving of aliases */
// 10-21-96 going thru and getting rid of mem leaks and tightening -roa
void  save_aliases(chdata *ch)
{ 
  struct alias *a; 
  FILE *fp; 
  char raw[MAX_INPUT_LENGTH]; 

  if (!GET_ALIASES(ch)) 
    return; 

  get_pdata_filename(GET_NAME(ch), "als", raw);

  if (!(fp = fopen(raw, "w")))  {
    send_to_char("SYSERR: Unable to open your alias file. Report!", ch);
    return; 
  }  

  a = GET_ALIASES(ch);
  while(a) 
  { 
    skip_spaces(&a->alias);
    skip_spaces(&a->replacement);
    if (a->alias && *a->alias && a->replacement && *a->replacement)
      fprintf(fp,"%s %s\n", a->alias, a->replacement); 
    else
      send_to_char("Invalid alias not stored...\n\r",ch);
    a = a->next; 
  } 
  fclose(fp); 
}