source: trunk/third/bash/alias.c @ 16807

Revision 16807, 14.1 KB checked in by zacheiss, 23 years ago (diff)
This commit was generated by cvs2svn to compensate for changes in r16806, which included commits to RCS files with non-trunk default branches.
Line 
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
44static 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. */
48int alias_expand_all = 0;
49
50/* The list of aliases that we have. */
51HASH_TABLE *aliases = (HASH_TABLE *)NULL;
52
53void
54initialize_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. */
62alias_t *
63find_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. */
76char *
77get_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. */
91void
92add_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. */
135static void
136free_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. */
150int
151remove_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. */
174void
175delete_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. */
190static alias_t **
191map_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
223static void
224sort_aliases (array)
225     alias_t **array;
226{
227  qsort (array, array_len ((char **)array), sizeof (alias_t *), qsort_alias_compare);
228}
229
230static int
231qsort_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 */
243alias_t **
244all_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
257char *
258alias_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. */
282static 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
295static int
296skipquotes (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. */
322static int
323skipws (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) */
406static int
407rd_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. */
445char *
446alias_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 */
Note: See TracBrowser for help on using the repository browser.