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