/** * @file act_alias.c * * Alias routine implementations. * * @author Geoff Davis <geoff@circlemudsquared.org> * @ingroup alias * @license All rights reserved. See license.doc for complete information. * @package cs * @since v1.0 * * Copyright (C) 2006 Geoff Davis <geoff@circlemudsquared.org> * Greg Buxton <greg@circlemudsquared.org> * * Copyright (C) 1993, 94 by the Trustees of the Johns Hopkins University * CircleMUD is based on DikuMUD, Copyright (C) 1990, 1991. */ #define __ACT_ALIAS_C__ #include "conf.h" #include "sysdep.h" #include "structs.h" #include "utils.h" #include "alias.h" #include "comm.h" #include "command.h" #include "db.h" #include "interpreter.h" #include "log.h" /** * Writes a player's aliases to disk. * @param ch the player whose aliases are to be written to disk * @return none */ void write_aliases(charData_t *ch) { FILE *file; char fn[MAX_STRING_LENGTH]; aliasData_t *temp; get_filename(fn, sizeof(fn), ALIAS_FILE, GET_NAME(ch)); remove(fn); if (GET_ALIASES(ch) == NULL) return; if ((file = fopen(fn, "w")) == NULL) { log("SYSERR: Couldn't save aliases for %s in '%s'.", GET_NAME(ch), fn); perror("SYSERR: write_aliases"); return; } for (temp = GET_ALIASES(ch); temp; temp = temp->next) { int aliaslen = strlen(temp->alias); int repllen = strlen(temp->replacement) - 1; fprintf(file, "%d\n%s\n" /* Alias */ "%d\n%s\n" /* Replacement */ "%d\n", /* type */ aliaslen, temp->alias, repllen, temp->replacement + 1, temp->type); } fclose(file); } /** * Reads a player's aliases from disk. * @param ch the player whose aliases are to be read from disk * @return none */ void read_aliases(charData_t *ch) { FILE *file; char xbuf[MAX_STRING_LENGTH]; aliasData_t *t2, *prev = NULL; int length; get_filename(xbuf, sizeof(xbuf), ALIAS_FILE, GET_NAME(ch)); if ((file = fopen(xbuf, "r")) == NULL) { if (errno != ENOENT) { log("SYSERR: Couldn't open alias file '%s' for %s.", xbuf, GET_NAME(ch)); perror("SYSERR: read_aliases"); } return; } CREATE(GET_ALIASES(ch), aliasData_t, 1); t2 = GET_ALIASES(ch); for (;;) { /* Read the aliased command. */ if (fscanf(file, "%d\n", &length) != 1) goto read_alias_error; fgets(xbuf, length + 1, file); t2->alias = strdup(xbuf); /* Build the replacement. */ if (fscanf(file, "%d\n", &length) != 1) goto read_alias_error; *xbuf = ' '; /* Doesn't need terminated, fgets() will. */ fgets(xbuf + 1, length + 1, file); t2->replacement = strdup(xbuf); /* Figure out the alias type. */ if (fscanf(file, "%d\n", &length) != 1) goto read_alias_error; t2->type = length; if (feof(file)) break; CREATE(t2->next, aliasData_t, 1); prev = t2; t2 = t2->next; }; fclose(file); return; read_alias_error: if (t2->alias) free(t2->alias); free(t2); if (prev) prev->next = NULL; fclose(file); } /** * Deletes the aliases file for the named player. * @param charname the name of the player whose alias file is to be deleted * @return none */ void delete_aliases(const char *charname) { char filename[PATH_MAX]; if (!get_filename(filename, sizeof(filename), ALIAS_FILE, charname)) return; if (remove(filename) < 0 && errno != ENOENT) log("SYSERR: deleting alias file %s: %s", filename, strerror(errno)); } /** * Searches for an alias by name. * @param aliases the list of aliases to be searched * @param str the name of the alias for which to search * @return the named alias, or NULL if the alias cannot be found */ aliasData_t *find_alias(aliasData_t *aliases, const char *str) { /* Iterate over the list of aliases. */ while (aliases != NULL) { /* Check whether this is the alias we're searching for. */ if (strcasecmp(str, aliases->alias) == 0) { return (aliases); } /* Advance to the next alias in the list. */ aliases = aliases->next; } return (NULL); } /** * Frees an alias. * @param alias the alias to be freed * @return none */ void free_alias(aliasData_t *alias) { if (alias->alias) free(alias->alias); if (alias->replacement) free(alias->replacement); free(alias); } /** * The ALIAS command. * @param ch the character performing the command * @param argument the additional text passed after the command * @param cmd the command object that was performed * @returns none */ ACMD(do_alias) { char arg[MAX_INPUT_LENGTH]; char *repl; aliasData_t *a, *temp; if (IS_NPC(ch)) return; repl = any_one_arg(argument, arg); if (!*arg) { /* no argument specified -- list currently defined aliases */ send_to_char(ch, "Currently defined aliases:\r\n"); if ((a = GET_ALIASES(ch)) == NULL) send_to_char(ch, " None.\r\n"); else { while (a != NULL) { send_to_char(ch, "%-15s %s\r\n", a->alias, a->replacement); a = a->next; } } } else { /* otherwise, add or remove aliases */ /* 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); } /* if no replacement string is specified, assume we want to delete */ if (!*repl) { if (a == NULL) send_to_char(ch, "No such alias.\r\n"); else send_to_char(ch, "Alias deleted.\r\n"); } else { /* otherwise, either add or redefine an alias */ if (!str_cmp(arg, "alias")) { send_to_char(ch, "You can't alias 'alias'.\r\n"); return; } CREATE(a, aliasData_t, 1); a->alias = strdup(arg); delete_doubledollar(repl); a->replacement = strdup(repl); if (strchr(repl, ALIAS_SEP_CHAR) || strchr(repl, ALIAS_VAR_CHAR)) { a->type = ALIAS_COMPLEX; } else { a->type = ALIAS_SIMPLE; } a->next = GET_ALIASES(ch); GET_ALIASES(ch) = a; send_to_char(ch, "Alias added.\r\n"); } } } /* * 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 /** * Performs a complex alias replacement. * @param input_q the character's input queue * @param orig the original string to be processed * @param alias the corresponding alias * @return none */ void perform_complex_alias(textQueue_t *input_q, char *orig, aliasData_t *alias) { textQueue_t temp_queue; char *tokens[NUM_TOKENS], *temp, *write_point; char buf2[MAX_RAW_INPUT_LENGTH], buf[MAX_RAW_INPUT_LENGTH]; /* raw? */ int num_of_tokens = 0, num; /* First, parse the original string */ strcpy(buf2, orig); /* strcpy: OK (orig:MAX_INPUT_LENGTH < buf2:MAX_RAW_INPUT_LENGTH) */ temp = strtok(buf2, " "); 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 = alias->replacement; *temp; temp++) { if (*temp == ALIAS_SEP_CHAR) { *write_point = '\0'; buf[MAX_INPUT_LENGTH - 1] = '\0'; 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]); /* strcpy: OK */ write_point += strlen(tokens[num]); } else if (*temp == ALIAS_GLOB_CHAR) { strcpy(write_point, orig); /* strcpy: OK */ 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; } } /** * Performs an alias replacement on a given string. * @param d the descriptor at which the alias replacement occurs * @param orig the original string to be processed * @param maxlen the maximum length of the resulting string * @return 0 if the string was modified in place; * 1 if the string was _not_ modified in place and expanded aliases * have been placed at the front of the character's input queue */ int perform_alias(descriptorData_t *d, char *orig, size_t maxlen) { char first_arg[MAX_INPUT_LENGTH], *ptr; aliasData_t *a, *tmp; /* Mobs don't have alaises. */ if (IS_NPC(d->character)) return (0); /* bail out immediately if the guy doesn't have any aliases */ if ((tmp = GET_ALIASES(d->character)) == NULL) return (0); /* find the alias we're supposed to match */ ptr = any_one_arg(orig, 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)) == NULL) return (0); if (a->type == ALIAS_SIMPLE) { strlcpy(orig, a->replacement, maxlen); return (0); } else { perform_complex_alias(&d->input, ptr, a); return (1); } }