Imported from ../bash-2.01.1.tar.gz.
[platform/upstream/bash.git] / alias.c
1 /* alias.c -- Not a full alias, but just the kind that we use in the
2    shell.  Csh style alias is somewhere else (`over there, in a box'). */
3
4 /* Copyright (C) 1987,1991 Free Software Foundation, Inc.
5
6    This file is part of GNU Bash, the Bourne Again SHell.
7
8    Bash is free software; you can redistribute it and/or modify it
9    under the terms of the GNU General Public License as published by
10    the Free Software Foundation; either version 1, or (at your option)
11    any later version.
12
13    Bash is distributed in the hope that it will be useful, but WITHOUT
14    ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
15    or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public
16    License for more details.
17
18    You should have received a copy of the GNU General Public License
19    along with Bash; see the file COPYING.  If not, write to the Free
20    Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. */
21
22 #include "config.h"
23
24 #if defined (ALIAS)
25
26 #if defined (HAVE_UNISTD_H)
27 #  include <unistd.h>
28 #endif
29
30 #include <stdio.h>
31 #include "bashansi.h"
32 #include "command.h"
33 #include "general.h"
34 #include "externs.h"
35 #include "alias.h"
36
37 static int qsort_alias_compare ();
38
39 /* Non-zero means expand all words on the line.  Otherwise, expand
40    after first expansion if the expansion ends in a space. */
41 int alias_expand_all = 0;
42
43 /* The list of aliases that we have. */
44 HASH_TABLE *aliases = (HASH_TABLE *)NULL;
45
46 void
47 initialize_aliases ()
48 {
49   if (!aliases)
50     aliases = make_hash_table (0);
51 }
52
53 /* Scan the list of aliases looking for one with NAME.  Return NULL
54    if the alias doesn't exist, else a pointer to the alias_t. */
55 alias_t *
56 find_alias (name)
57      char *name;
58 {
59   BUCKET_CONTENTS *al;
60
61   if (aliases == 0)
62     return ((alias_t *)NULL);
63
64   al = find_hash_item (name, aliases);
65   return (al ? (alias_t *)al->data : (alias_t *)NULL);
66 }
67
68 /* Return the value of the alias for NAME, or NULL if there is none. */
69 char *
70 get_alias_value (name)
71      char *name;
72 {
73   alias_t *alias;
74
75   if (aliases == 0)
76     return ((char *)NULL);
77
78   alias = find_alias (name);
79   return (alias ? alias->value : (char *)NULL);
80 }
81
82 /* Make a new alias from NAME and VALUE.  If NAME can be found,
83    then replace its value. */
84 void
85 add_alias (name, value)
86      char *name, *value;
87 {
88   BUCKET_CONTENTS *elt;
89   alias_t *temp;
90   int n;
91
92   if (!aliases)
93     {
94       initialize_aliases ();
95       temp = (alias_t *)NULL;
96     }
97   else
98     temp = find_alias (name);
99
100   if (temp)
101     {
102       free (temp->value);
103       temp->value = savestring (value);
104       n = value[strlen (value) - 1];
105       if (n == ' ' || n == '\t')
106         temp->flags |= AL_EXPANDNEXT;
107     }
108   else
109     {
110       temp = (alias_t *)xmalloc (sizeof (alias_t));
111       temp->name = savestring (name);
112       temp->value = savestring (value);
113       temp->flags = 0;
114
115       n = value[strlen (value) - 1];
116       if (n == ' ' || n == '\t')
117         temp->flags |= AL_EXPANDNEXT;
118
119       elt = add_hash_item (savestring (name), aliases);
120       elt->data = (char *)temp;
121     }
122 }
123
124 /* Delete a single alias structure. */
125 static void
126 free_alias_data (data)
127      char *data;
128 {
129   register alias_t *a;
130
131   a = (alias_t *)data;
132   free (a->value);
133   free (a->name);
134   free (data);
135 }
136
137 /* Remove the alias with name NAME from the alias table.  Returns
138    the number of aliases left in the table, or -1 if the alias didn't
139    exist. */
140 int
141 remove_alias (name)
142      char *name;
143 {
144   BUCKET_CONTENTS *elt;
145
146   if (aliases == 0)
147     return (-1);
148
149   elt = remove_hash_item (name, aliases);
150   if (elt)
151     {
152       free_alias_data (elt->data);
153       free (elt->key);          /* alias name */
154       free (elt);               /* XXX */
155       return (aliases->nentries);
156     }
157   return (-1);
158 }
159
160 /* Delete all aliases. */
161 void
162 delete_all_aliases ()
163 {
164   if (aliases == 0)
165     return;
166
167   flush_hash_table (aliases, free_alias_data);
168   dispose_hash_table (aliases);
169   aliases = (HASH_TABLE *)NULL;
170 }
171
172 /* Return an array of aliases that satisfy the conditions tested by FUNCTION.
173    If FUNCTION is NULL, return all aliases. */
174 static alias_t **
175 map_over_aliases (function)
176      Function *function;
177 {
178   register int i;
179   register BUCKET_CONTENTS *tlist;
180   alias_t *alias, **list;
181   int list_index, list_size;
182
183   list = (alias_t **)NULL;
184   for (i = list_index = list_size = 0; i < aliases->nbuckets; i++)
185     {
186       tlist = get_hash_bucket (i, aliases);
187
188       while (tlist)
189         {
190           alias = (alias_t *)tlist->data;
191
192           if (!function || (*function) (alias))
193             {
194               if (list_index + 1 >= list_size)
195                 list = (alias_t **)
196                   xrealloc ((char *)list, (list_size += 20) * sizeof (alias_t *));
197
198               list[list_index++] = alias;
199               list[list_index] = (alias_t *)NULL;
200             }
201           tlist = tlist->next;
202         }
203     }
204   return (list);
205 }
206
207 static void
208 sort_aliases (array)
209      alias_t **array;
210 {
211   qsort (array, array_len ((char **)array), sizeof (alias_t *), qsort_alias_compare);
212 }
213
214 static int
215 qsort_alias_compare (as1, as2)
216      alias_t **as1, **as2;
217 {
218   int result;
219
220   if ((result = (*as1)->name[0] - (*as2)->name[0]) == 0)
221     result = strcmp ((*as1)->name, (*as2)->name);
222
223   return (result);
224 }
225
226 /* Return a sorted list of all defined aliases */
227 alias_t **
228 all_aliases ()
229 {
230   alias_t **list;
231
232   if (!aliases)
233     return ((alias_t **)NULL);
234
235   list = map_over_aliases ((Function *)NULL);
236   if (list)
237     sort_aliases (list);
238   return (list);
239 }
240
241 char *
242 alias_expand_word (s)
243      char *s;
244 {
245   alias_t *r;
246
247   r = find_alias (s);
248   return (r ? savestring (r->value) : (char *)NULL);
249 }
250
251 /* Readline support functions -- expand all aliases in a line. */
252
253 #if defined (READLINE)
254
255 /* Return non-zero if CHARACTER is a member of the class of characters
256    that are self-delimiting in the shell (this really means that these
257    characters delimit tokens). */
258 #define self_delimiting(character) (member ((character), " \t\n\r;|&()"))
259
260 /* Return non-zero if CHARACTER is a member of the class of characters
261    that delimit commands in the shell. */
262 #define command_separator(character) (member ((character), "\r\n;|&("))
263
264 /* If this is 1, we are checking the next token read for alias expansion
265    because it is the first word in a command. */
266 static int command_word;
267
268 /* This is for skipping quoted strings in alias expansions. */
269 #define quote_char(c)  (((c) == '\'') || ((c) == '"'))
270
271 /* Consume a quoted string from STRING, starting at string[START] (so
272    string[START] is the opening quote character), and return the index
273    of the closing quote character matching the opening quote character.
274    This handles single matching pairs of unquoted quotes; it could afford
275    to be a little smarter... This skips words between balanced pairs of
276    quotes, words where the first character is quoted with a `\', and other
277    backslash-escaped characters. */
278
279 static int
280 skipquotes (string, start)
281      char *string;
282      int start;
283 {
284   register int i;
285   int delimiter = string[start];
286
287   /* i starts at START + 1 because string[START] is the opening quote
288      character. */
289   for (i = start + 1 ; string[i] ; i++)
290     {
291       if (string[i] == '\\')
292         {
293           i++;          /* skip backslash-quoted quote characters, too */
294           continue;
295         }
296
297       if (string[i] == delimiter)
298         return i;
299     }
300   return (i);
301 }
302
303 /* Skip the white space and any quoted characters in STRING, starting at
304    START.  Return the new index into STRING, after zero or more characters
305    have been skipped. */
306 static int
307 skipws (string, start)
308      char *string;
309      int start;
310 {
311   register int i = 0;
312   int pass_next, backslash_quoted_word, peekc;
313
314   /* skip quoted strings, in ' or ", and words in which a character is quoted
315      with a `\'. */
316   backslash_quoted_word = pass_next = 0;
317
318   /* Skip leading whitespace (or separator characters), and quoted words.
319      But save it in the output.  */
320
321   for (i = start; string[i]; i++)
322     {
323       if (pass_next)
324         {
325           pass_next = 0;
326           continue;
327         }
328
329       if (whitespace (string[i]))
330         {
331           backslash_quoted_word = 0; /* we are no longer in a backslash-quoted word */
332           continue;
333         }
334
335       if (string[i] == '\\')
336         {
337           peekc = string[i+1];
338           if (isletter (peekc))
339             backslash_quoted_word++;    /* this is a backslash-quoted word */
340           else
341             pass_next++;
342           continue;
343         }
344
345       /* This only handles single pairs of non-escaped quotes.  This
346          overloads backslash_quoted_word to also mean that a word like
347          ""f is being scanned, so that the quotes will inhibit any expansion
348          of the word. */
349       if (quote_char(string[i]))
350         {
351           i = skipquotes (string, i);
352           /* This could be a line that contains a single quote character,
353              in which case skipquotes () terminates with string[i] == '\0'
354              (the end of the string).  Check for that here. */
355           if (string[i] == '\0')
356             break;
357
358           peekc = string[i + 1];
359           if (isletter (peekc))
360             backslash_quoted_word++;
361           continue;
362         }
363
364       /* If we're in the middle of some kind of quoted word, let it
365          pass through. */
366       if (backslash_quoted_word)
367         continue;
368
369       /* If this character is a shell command separator, then set a hint for
370          alias_expand that the next token is the first word in a command. */
371
372       if (command_separator (string[i]))
373         {
374           command_word++;
375           continue;
376         }
377       break;
378     }
379   return (i);
380 }
381
382 /* Characters that may appear in a token.  Basically, anything except white
383    space and a token separator. */
384 #define token_char(c)   (!((whitespace (string[i]) || self_delimiting (string[i]))))
385
386 /* Read from START in STRING until the next separator character, and return
387    the index of that separator.  Skip backslash-quoted characters.  Call
388    skipquotes () for quoted strings in the middle or at the end of tokens,
389    so all characters show up (e.g. foo'' and foo""bar) */
390 static int
391 rd_token (string, start)
392      char *string;
393      int start;
394 {
395   register int i;
396
397   /* From here to next separator character is a token. */
398   for (i = start; string[i] && token_char (string[i]); i++)
399     {
400       if (string[i] == '\\')
401         {
402           i++;  /* skip backslash-escaped character */
403           continue;
404         }
405
406       /* If this character is a quote character, we want to call skipquotes
407          to get the whole quoted portion as part of this word.  That word
408          will not generally match an alias, even if te unquoted word would
409          have.  The presence of the quotes in the token serves then to
410          inhibit expansion. */
411       if (quote_char (string[i]))
412         {
413           i = skipquotes (string, i);
414           /* Now string[i] is the matching quote character, and the
415              quoted portion of the token has been scanned. */
416           continue;
417         }
418     }
419   return (i);
420 }
421
422 /* Return a new line, with any aliases substituted. */
423 char *
424 alias_expand (string)
425      char *string;
426 {
427   int line_len = 1 + strlen (string);
428   char *line = (char *)xmalloc (line_len);
429   register int i, j, start;
430   char *token = xmalloc (line_len);
431   int tl, real_start, expand_next, expand_this_token;
432   alias_t *alias;
433
434   line[0] = i = 0;
435   expand_next = 0;
436   command_word = 1; /* initialized to expand the first word on the line */
437
438   /* Each time through the loop we find the next word in line.  If it
439      has an alias, substitute
440      the alias value.  If the value ends in ` ', then try again
441      with the next word.  Else, if there is no value, or if
442      the value does not end in space, we are done. */
443
444   for (;;)
445     {
446
447       token[0] = 0;
448       start = i;
449
450       /* Skip white space and quoted characters */
451       i = skipws (string, start);
452
453       if (start == i && string[i] == '\0')
454         {
455           free (token);
456           return (line);
457         }
458
459       /* copy the just-skipped characters into the output string,
460          expanding it if there is not enough room. */
461       j = strlen (line);
462       tl = i - start;   /* number of characters just skipped */
463       RESIZE_MALLOCED_BUFFER (line, j, (tl + 1), line_len, (tl + 50));
464       strncpy (line + j, string + start, tl);
465       line[j + tl] = '\0';
466
467       real_start = i;
468
469       command_word = command_word || (command_separator (string[i]));
470       expand_this_token = (command_word || expand_next);
471       expand_next = 0;
472
473       /* Read the next token, and copy it into TOKEN. */
474       start = i;
475       i = rd_token (string, start);
476
477       tl = i - start;   /* token length */
478
479       /* If tl == 0, but we're not at the end of the string, then we have a
480          single-character token, probably a delimiter */
481       if (tl == 0 && string[i] != '\0')
482         {
483           tl = 1;
484           i++;          /* move past it */
485         }
486
487       strncpy (token, string + start, tl);
488       token [tl] = '\0';
489
490       /* If there is a backslash-escaped character quoted in TOKEN,
491          then we don't do alias expansion.  This should check for all
492          other quoting characters, too. */
493       if (strchr (token, '\\'))
494         expand_this_token = 0;
495
496       /* If we should be expanding here, if we are expanding all words, or if
497          we are in a location in the string where an expansion is supposed to
498          take place, see if this word has a substitution.  If it does, then do
499          the expansion.  Note that we defer the alias value lookup until we
500          are sure we are expanding this token. */
501
502       if ((token[0]) &&
503           (expand_this_token || alias_expand_all) &&
504           (alias = find_alias (token)))
505         {
506           char *v;
507           int vlen, llen;
508
509           v = alias->value;
510           vlen = strlen (v);
511           llen = strlen (line);
512
513           /* +3 because we possibly add one more character below. */
514           RESIZE_MALLOCED_BUFFER (line, llen, (vlen + 3), line_len, (vlen + 50));
515
516           strcpy (line + llen, v);
517
518           if ((expand_this_token && vlen && whitespace (v[vlen - 1])) ||
519               alias_expand_all)
520             expand_next = 1;
521         }
522       else
523         {
524           int llen, tlen;
525
526           llen = strlen (line);
527           tlen = i - real_start; /* tlen == strlen(token) */
528
529           RESIZE_MALLOCED_BUFFER (line, llen, (tlen + 1), line_len, (llen + tlen + 50));
530
531           strncpy (line + llen, string + real_start, tlen);
532           line[llen + tlen] = '\0';
533         }
534       command_word = 0;
535     }
536 }
537 #endif /* READLINE */
538 #endif /* ALIAS */