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