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'). */
4 /* Copyright (C) 1987,1991 Free Software Foundation, Inc.
6 This file is part of GNU Bash, the Bourne Again SHell.
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)
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.
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. */
26 #if defined (HAVE_UNISTD_H)
28 # include <sys/types.h>
40 static int qsort_alias_compare ();
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;
46 /* The list of aliases that we have. */
47 HASH_TABLE *aliases = (HASH_TABLE *)NULL;
53 aliases = make_hash_table (0);
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. */
65 return ((alias_t *)NULL);
67 al = find_hash_item (name, aliases);
68 return (al ? (alias_t *)al->data : (alias_t *)NULL);
71 /* Return the value of the alias for NAME, or NULL if there is none. */
73 get_alias_value (name)
79 return ((char *)NULL);
81 alias = find_alias (name);
82 return (alias ? alias->value : (char *)NULL);
85 /* Make a new alias from NAME and VALUE. If NAME can be found,
86 then replace its value. */
88 add_alias (name, value)
97 initialize_aliases ();
98 temp = (alias_t *)NULL;
101 temp = find_alias (name);
106 temp->value = savestring (value);
107 n = value[strlen (value) - 1];
108 if (n == ' ' || n == '\t')
109 temp->flags |= AL_EXPANDNEXT;
113 temp = (alias_t *)xmalloc (sizeof (alias_t));
114 temp->name = savestring (name);
115 temp->value = savestring (value);
118 n = value[strlen (value) - 1];
119 if (n == ' ' || n == '\t')
120 temp->flags |= AL_EXPANDNEXT;
122 elt = add_hash_item (savestring (name), aliases);
123 elt->data = (char *)temp;
127 /* Delete a single alias structure. */
129 free_alias_data (data)
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
147 BUCKET_CONTENTS *elt;
152 elt = remove_hash_item (name, aliases);
155 free_alias_data (elt->data);
156 free (elt->key); /* alias name */
157 free (elt); /* XXX */
158 return (aliases->nentries);
163 /* Delete all aliases. */
165 delete_all_aliases ()
170 flush_hash_table (aliases, free_alias_data);
171 dispose_hash_table (aliases);
172 aliases = (HASH_TABLE *)NULL;
175 /* Return an array of aliases that satisfy the conditions tested by FUNCTION.
176 If FUNCTION is NULL, return all aliases. */
178 map_over_aliases (function)
182 register BUCKET_CONTENTS *tlist;
183 alias_t *alias, **list;
184 int list_index, list_size;
186 list = (alias_t **)NULL;
187 for (i = list_index = list_size = 0; i < aliases->nbuckets; i++)
189 tlist = get_hash_bucket (i, aliases);
193 alias = (alias_t *)tlist->data;
195 if (!function || (*function) (alias))
197 if (list_index + 1 >= list_size)
199 xrealloc ((char *)list, (list_size += 20) * sizeof (alias_t *));
201 list[list_index++] = alias;
202 list[list_index] = (alias_t *)NULL;
214 qsort (array, array_len ((char **)array), sizeof (alias_t *), qsort_alias_compare);
218 qsort_alias_compare (as1, as2)
219 alias_t **as1, **as2;
223 if ((result = (*as1)->name[0] - (*as2)->name[0]) == 0)
224 result = strcmp ((*as1)->name, (*as2)->name);
229 /* Return a sorted list of all defined aliases */
236 return ((alias_t **)NULL);
238 list = map_over_aliases ((Function *)NULL);
245 alias_expand_word (s)
251 return (r ? savestring (r->value) : (char *)NULL);
254 /* Readline support functions -- expand all aliases in a line. */
256 #if defined (READLINE)
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;|&()"))
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;|&("))
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;
271 /* This is for skipping quoted strings in alias expansions. */
272 #define quote_char(c) (((c) == '\'') || ((c) == '"'))
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. */
283 skipquotes (string, start)
288 int delimiter = string[start];
290 /* i starts at START + 1 because string[START] is the opening quote
292 for (i = start + 1 ; string[i] ; i++)
294 if (string[i] == '\\')
296 i++; /* skip backslash-quoted quote characters, too */
300 if (string[i] == delimiter)
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. */
310 skipws (string, start)
315 int pass_next, backslash_quoted_word, peekc;
317 /* skip quoted strings, in ' or ", and words in which a character is quoted
319 backslash_quoted_word = pass_next = 0;
321 /* Skip leading whitespace (or separator characters), and quoted words.
322 But save it in the output. */
324 for (i = start; string[i]; i++)
332 if (whitespace (string[i]))
334 backslash_quoted_word = 0; /* we are no longer in a backslash-quoted word */
338 if (string[i] == '\\')
341 if (isletter (peekc))
342 backslash_quoted_word++; /* this is a backslash-quoted word */
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
352 if (quote_char(string[i]))
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')
361 peekc = string[i + 1];
362 if (isletter (peekc))
363 backslash_quoted_word++;
367 /* If we're in the middle of some kind of quoted word, let it
369 if (backslash_quoted_word)
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. */
375 if (command_separator (string[i]))
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]))))
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) */
394 rd_token (string, start)
400 /* From here to next separator character is a token. */
401 for (i = start; string[i] && token_char (string[i]); i++)
403 if (string[i] == '\\')
405 i++; /* skip backslash-escaped character */
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]))
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')
423 /* Now string[i] is the matching quote character, and the
424 quoted portion of the token has been scanned. */
431 /* Return a new line, with any aliases substituted. */
433 alias_expand (string)
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;
445 command_word = 1; /* initialized to expand the first word on the line */
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. */
459 /* Skip white space and quoted characters */
460 i = skipws (string, start);
462 if (start == i && string[i] == '\0')
468 /* copy the just-skipped characters into the output string,
469 expanding it if there is not enough room. */
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);
478 command_word = command_word || (command_separator (string[i]));
479 expand_this_token = (command_word || expand_next);
482 /* Read the next token, and copy it into TOKEN. */
484 i = rd_token (string, start);
486 tl = i - start; /* token length */
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')
493 i++; /* move past it */
496 strncpy (token, string + start, tl);
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;
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. */
512 (expand_this_token || alias_expand_all) &&
513 (alias = find_alias (token)))
520 llen = strlen (line);
522 /* +3 because we possibly add one more character below. */
523 RESIZE_MALLOCED_BUFFER (line, llen, (vlen + 3), line_len, (vlen + 50));
525 strcpy (line + llen, v);
527 if ((expand_this_token && vlen && whitespace (v[vlen - 1])) ||
535 llen = strlen (line);
536 tlen = i - real_start; /* tlen == strlen(token) */
538 RESIZE_MALLOCED_BUFFER (line, llen, (tlen + 1), line_len, (llen + tlen + 50));
540 strncpy (line + llen, string + real_start, tlen);
541 line[llen + tlen] = '\0';
546 #endif /* READLINE */