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