aboutsummaryrefslogtreecommitdiffstats
path: root/alias.c
diff options
context:
space:
mode:
authorJari Aalto <jari.aalto@cante.net>1996-08-26 18:22:31 +0000
committerJari Aalto <jari.aalto@cante.net>2009-09-12 16:46:49 +0000
commit726f63884db0132f01745f1fb4465e6621088ccf (patch)
tree6c2f7765a890a97e0e513cb539df43283a8f7c4d /alias.c
downloadandroid_external_bash-726f63884db0132f01745f1fb4465e6621088ccf.tar.gz
android_external_bash-726f63884db0132f01745f1fb4465e6621088ccf.tar.bz2
android_external_bash-726f63884db0132f01745f1fb4465e6621088ccf.zip
Imported from ../bash-1.14.7.tar.gz.
Diffstat (limited to 'alias.c')
-rw-r--r--alias.c535
1 files changed, 535 insertions, 0 deletions
diff --git a/alias.c b/alias.c
new file mode 100644
index 0000000..f9be0b4
--- /dev/null
+++ b/alias.c
@@ -0,0 +1,535 @@
+/* alias.c -- Not a full alias, but just the kind that we use in the
+ shell. Csh style alias is somewhere else (`over there, in a box'). */
+
+/* Copyright (C) 1987,1991 Free Software Foundation, Inc.
+
+ This file is part of GNU Bash, the Bourne Again SHell.
+
+ Bash is free software; you can redistribute it and/or modify it
+ under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 1, or (at your option)
+ any later version.
+
+ Bash is distributed in the hope that it will be useful, but WITHOUT
+ ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
+ or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public
+ License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with Bash; see the file COPYING. If not, write to the Free
+ Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. */
+
+#include <stdio.h>
+#include "bashansi.h"
+#include "config.h"
+#include "command.h"
+#include "general.h"
+#include "hash.h"
+#include "alias.h"
+
+static int qsort_alias_compare ();
+
+/* Non-zero means expand all words on the line. Otherwise, expand
+ after first expansion if the expansion ends in a space. */
+int alias_expand_all = 0;
+
+/* The list of aliases that we have. */
+HASH_TABLE *aliases = (HASH_TABLE *)NULL;
+
+void
+initialize_aliases ()
+{
+ if (!aliases)
+ aliases = make_hash_table (0);
+}
+
+/* Scan the list of aliases looking for one with NAME. Return NULL
+ if the alias doesn't exist, else a pointer to the assoc. */
+ASSOC *
+find_alias (name)
+ char *name;
+{
+ BUCKET_CONTENTS *al;
+
+ if (!aliases)
+ return ((ASSOC *)NULL);
+ else
+ al = find_hash_item (name, aliases);
+
+ if (al)
+ return ((ASSOC *)al->data);
+ else
+ return ((ASSOC *)NULL);
+}
+
+/* Return the value of the alias for NAME, or NULL if there is none. */
+char *
+get_alias_value (name)
+ char *name;
+{
+ ASSOC *alias = find_alias (name);
+ if (alias)
+ return (alias->value);
+ else
+ return ((char *)NULL);
+}
+
+/* Make a new alias from NAME and VALUE. If NAME can be found,
+ then replace its value. */
+void
+add_alias (name, value)
+ char *name, *value;
+{
+ ASSOC *temp = (ASSOC *)NULL;
+
+ if (!aliases)
+ initialize_aliases ();
+ else
+ temp = find_alias (name);
+
+ if (temp)
+ {
+ free (temp->value);
+ temp->value = savestring (value);
+ }
+ else
+ {
+ BUCKET_CONTENTS *elt;
+
+ temp = (ASSOC *)xmalloc (sizeof (ASSOC));
+ temp->name = savestring (name);
+ temp->value = savestring (value);
+
+ elt = add_hash_item (savestring (name), aliases);
+ elt->data = (char *)temp;
+ }
+}
+
+/* Remove the alias with name NAME from the alias table. Returns
+ the number of aliases left in the table, or -1 if the alias didn't
+ exist. */
+int
+remove_alias (name)
+ char *name;
+{
+ BUCKET_CONTENTS *elt;
+
+ if (!aliases)
+ return (-1);
+
+ elt = remove_hash_item (name, aliases);
+ if (elt)
+ {
+ ASSOC *t;
+
+ t = (ASSOC *)elt->data;
+ free (t->name);
+ free (t->value);
+ free (elt->key); /* alias name */
+ free (t);
+
+ return (aliases->nentries);
+ }
+ return (-1);
+}
+
+/* Delete a hash bucket chain of aliases. */
+static void
+delete_alias_list (alias_list)
+ BUCKET_CONTENTS *alias_list;
+{
+ register BUCKET_CONTENTS *bp, *temp;
+ register ASSOC *a;
+
+ for (bp = alias_list; bp; )
+ {
+ temp = bp->next;
+ a = (ASSOC *)bp->data;
+ free (a->value);
+ free (a->name);
+ free (bp->data);
+ free (bp->key);
+ free (bp);
+ bp = temp;
+ }
+}
+
+/* Delete all aliases. */
+void
+delete_all_aliases ()
+{
+ register int i;
+
+ if (!aliases)
+ return;
+
+ for (i = 0; i < aliases->nbuckets; i++)
+ {
+ register BUCKET_CONTENTS *bp;
+
+ bp = get_hash_bucket (i, aliases);
+ delete_alias_list (bp);
+ }
+ free (aliases);
+ aliases = (HASH_TABLE *)NULL;
+}
+
+/* Return an array of aliases that satisfy the conditions tested by FUNCTION.
+ If FUNCTION is NULL, return all aliases. */
+static ASSOC **
+map_over_aliases (function)
+ Function *function;
+{
+ register int i;
+ register BUCKET_CONTENTS *tlist;
+ ASSOC *alias, **list = (ASSOC **)NULL;
+ int list_index = 0, list_size = 0;
+
+ for (i = 0; i < aliases->nbuckets; i++)
+ {
+ tlist = get_hash_bucket (i, aliases);
+
+ while (tlist)
+ {
+ alias = (ASSOC *)tlist->data;
+
+ if (!function || (*function) (alias))
+ {
+ if (list_index + 1 >= list_size)
+ list = (ASSOC **)
+ xrealloc ((char *)list, (list_size += 20) * sizeof (ASSOC *));
+
+ list[list_index++] = alias;
+ list[list_index] = (ASSOC *)NULL;
+ }
+ tlist = tlist->next;
+ }
+ }
+ return (list);
+}
+
+static void
+sort_aliases (array)
+ ASSOC **array;
+{
+ qsort (array, array_len ((char **)array), sizeof (ASSOC *), qsort_alias_compare);
+}
+
+static int
+qsort_alias_compare (as1, as2)
+ ASSOC **as1, **as2;
+{
+ int result;
+
+ if ((result = (*as1)->name[0] - (*as2)->name[0]) == 0)
+ result = strcmp ((*as1)->name, (*as2)->name);
+
+ return (result);
+}
+
+/* Return a sorted list of all defined aliases */
+ASSOC **
+all_aliases ()
+{
+ ASSOC **list;
+
+ if (!aliases)
+ return ((ASSOC **)NULL);
+
+ list = map_over_aliases ((Function *)NULL);
+ if (list)
+ sort_aliases (list);
+ return (list);
+}
+
+char *
+alias_expand_word (s)
+ char *s;
+{
+ ASSOC *r = find_alias (s);
+
+ if (r)
+ return (savestring (r->value));
+ else
+ return ((char *)NULL);
+}
+
+/* Return non-zero if CHARACTER is a member of the class of characters
+ that are self-delimiting in the shell (this really means that these
+ characters delimit tokens). */
+#define self_delimiting(character) (member ((character), " \t\n\r;|&()"))
+
+/* Return non-zero if CHARACTER is a member of the class of characters
+ that delimit commands in the shell. */
+#define command_separator(character) (member ((character), "\r\n;|&("))
+
+/* If this is 1, we are checking the next token read for alias expansion
+ because it is the first word in a command. */
+static int command_word;
+
+/* This is for skipping quoted strings in alias expansions. */
+#define quote_char(c) (((c) == '\'') || ((c) == '"'))
+
+/* Consume a quoted string from STRING, starting at string[START] (so
+ string[START] is the opening quote character), and return the index
+ of the closing quote character matching the opening quote character.
+ This handles single matching pairs of unquoted quotes; it could afford
+ to be a little smarter... This skips words between balanced pairs of
+ quotes, words where the first character is quoted with a `\', and other
+ backslash-escaped characters. */
+
+static int
+skipquotes (string, start)
+ char *string;
+ int start;
+{
+ register int i;
+ int delimiter = string[start];
+
+ /* i starts at START + 1 because string[START] is the opening quote
+ character. */
+ for (i = start + 1 ; string[i] ; i++)
+ {
+ if (string[i] == '\\')
+ {
+ i++; /* skip backslash-quoted quote characters, too */
+ continue;
+ }
+
+ if (string[i] == delimiter)
+ return i;
+ }
+ return (i);
+}
+
+/* Skip the white space and any quoted characters in STRING, starting at
+ START. Return the new index into STRING, after zero or more characters
+ have been skipped. */
+static int
+skipws (string, start)
+ char *string;
+ int start;
+{
+ register int i = 0;
+ int pass_next, backslash_quoted_word, peekc;
+
+ /* skip quoted strings, in ' or ", and words in which a character is quoted
+ with a `\'. */
+ backslash_quoted_word = pass_next = 0;
+
+ /* Skip leading whitespace (or separator characters), and quoted words.
+ But save it in the output. */
+
+ for (i = start; string[i]; i++)
+ {
+ if (pass_next)
+ {
+ pass_next = 0;
+ continue;
+ }
+
+ if (whitespace (string[i]))
+ {
+ backslash_quoted_word = 0; /* we are no longer in a backslash-quoted word */
+ continue;
+ }
+
+ if (string[i] == '\\')
+ {
+ peekc = string[i+1];
+ if (isletter (peekc))
+ backslash_quoted_word++; /* this is a backslash-quoted word */
+ else
+ pass_next++;
+ continue;
+ }
+
+ /* This only handles single pairs of non-escaped quotes. This
+ overloads backslash_quoted_word to also mean that a word like
+ ""f is being scanned, so that the quotes will inhibit any expansion
+ of the word. */
+ if (quote_char(string[i]))
+ {
+ i = skipquotes (string, i);
+ /* This could be a line that contains a single quote character,
+ in which case skipquotes () terminates with string[i] == '\0'
+ (the end of the string). Check for that here. */
+ if (string[i] == '\0')
+ break;
+
+ peekc = string[i + 1];
+ if (isletter (peekc))
+ backslash_quoted_word++;
+ continue;
+ }
+
+ /* If we're in the middle of some kind of quoted word, let it
+ pass through. */
+ if (backslash_quoted_word)
+ continue;
+
+ /* If this character is a shell command separator, then set a hint for
+ alias_expand that the next token is the first word in a command. */
+
+ if (command_separator (string[i]))
+ {
+ command_word++;
+ continue;
+ }
+ break;
+ }
+ return (i);
+}
+
+/* Characters that may appear in a token. Basically, anything except white
+ space and a token separator. */
+#define token_char(c) (!((whitespace (string[i]) || self_delimiting (string[i]))))
+
+/* Read from START in STRING until the next separator character, and return
+ the index of that separator. Skip backslash-quoted characters. Call
+ skipquotes () for quoted strings in the middle or at the end of tokens,
+ so all characters show up (e.g. foo'' and foo""bar) */
+static int
+rd_token (string, start)
+ char *string;
+ int start;
+{
+ register int i;
+
+ /* From here to next separator character is a token. */
+ for (i = start; string[i] && token_char (string[i]); i++)
+ {
+ if (string[i] == '\\')
+ {
+ i++; /* skip backslash-escaped character */
+ continue;
+ }
+
+ /* If this character is a quote character, we want to call skipquotes
+ to get the whole quoted portion as part of this word. That word
+ will not generally match an alias, even if te unquoted word would
+ have. The presence of the quotes in the token serves then to
+ inhibit expansion. */
+ if (quote_char (string[i]))
+ {
+ i = skipquotes (string, i);
+ /* Now string[i] is the matching quote character, and the
+ quoted portion of the token has been scanned. */
+ continue;
+ }
+ }
+ return (i);
+}
+
+/* Return a new line, with any aliases substituted. */
+char *
+alias_expand (string)
+ char *string;
+{
+ int line_len = 1 + strlen (string);
+ char *line = (char *)xmalloc (line_len);
+ register int i, j, start;
+ char *token = xmalloc (line_len);
+ int tl, real_start, expand_next, expand_this_token;
+ ASSOC *alias;
+
+ line[0] = i = 0;
+ expand_next = 0;
+ command_word = 1; /* initialized to expand the first word on the line */
+
+ /* Each time through the loop we find the next word in line. If it
+ has an alias, substitute
+ the alias value. If the value ends in ` ', then try again
+ with the next word. Else, if there is no value, or if
+ the value does not end in space, we are done. */
+
+ for (;;)
+ {
+
+ token[0] = 0;
+ start = i;
+
+ /* Skip white space and quoted characters */
+ i = skipws (string, start);
+
+ if (start == i && string[i] == '\0')
+ {
+ free (token);
+ return (line);
+ }
+
+ /* copy the just-skipped characters into the output string,
+ expanding it if there is not enough room. */
+ j = strlen (line);
+ tl = i - start; /* number of characters just skipped */
+ if (1 + j + tl >= line_len)
+ line = (char *)xrealloc (line, line_len += (50 + tl));
+ strncpy (line + j, string + start, tl);
+ line[j + tl] = '\0';
+
+ real_start = i;
+
+ command_word = command_word || (command_separator (string[i]));
+ expand_this_token = (command_word || expand_next);
+ expand_next = 0;
+
+ /* Read the next token, and copy it into TOKEN. */
+ start = i;
+ i = rd_token (string, start);
+
+ tl = i - start; /* token length */
+
+ /* If tl == 0, but we're not at the end of the string, then we have a
+ single-character token, probably a delimiter */
+ if (tl == 0 && string[i] != '\0')
+ {
+ tl = 1;
+ i++; /* move past it */
+ }
+
+ strncpy (token, string + start, tl);
+ token [tl] = '\0';
+
+ /* If there is a backslash-escaped character quoted in TOKEN,
+ then we don't do alias expansion. This should check for all
+ other quoting characters, too. */
+ if (strchr (token, '\\'))
+ expand_this_token = 0;
+
+ /* If we should be expanding here, if we are expanding all words, or if
+ we are in a location in the string where an expansion is supposed to
+ take place, see if this word has a substitution. If it does, then do
+ the expansion. Note that we defer the alias value lookup until we
+ are sure we are expanding this token. */
+
+ if ((token[0]) &&
+ (expand_this_token || alias_expand_all) &&
+ (alias = find_alias (token)))
+ {
+ char *v = alias->value;
+ int l = strlen (v);
+
+ /* +3 because we possibly add one more character below. */
+ if ((l + 3) > line_len - (int)strlen (line))
+ line = (char *)xrealloc (line, line_len += (50 + l));
+
+ strcat (line, v);
+
+ if ((expand_this_token && l && whitespace (v[l - 1])) ||
+ alias_expand_all)
+ expand_next = 1;
+ }
+ else
+ {
+ int ll = strlen (line);
+ int tlen = i - real_start; /* tlen == strlen(token) */
+
+ if (ll + tlen + 2 > line_len)
+ line = (char *)xrealloc (line, line_len += 50 + ll + tlen);
+
+ strncpy (line + ll, string + real_start, tlen);
+ line[ll + tlen] = '\0';
+ }
+ command_word = 0;
+ }
+}