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

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