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