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