]> git.ipfire.org Git - thirdparty/bash.git/blame - alias.c
Imported from ../bash-2.04.tar.gz.
[thirdparty/bash.git] / alias.c
CommitLineData
726f6388
JA
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
bb70624e 10 the Free Software Foundation; either version 2, or (at your option)
726f6388
JA
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
bb70624e 20 Software Foundation, 59 Temple Place, Suite 330, Boston, MA 02111 USA. */
726f6388 21
ccc6cda3
JA
22#include "config.h"
23
24#if defined (ALIAS)
25
26#if defined (HAVE_UNISTD_H)
cce855bc
JA
27# ifdef _MINIX
28# include <sys/types.h>
29# endif
ccc6cda3
JA
30# include <unistd.h>
31#endif
32
726f6388
JA
33#include <stdio.h>
34#include "bashansi.h"
726f6388
JA
35#include "command.h"
36#include "general.h"
ccc6cda3 37#include "externs.h"
726f6388
JA
38#include "alias.h"
39
bb70624e
JA
40#if defined (PROGRAMMABLE_COMPLETION)
41# include "pcomplete.h"
42#endif
43
726f6388
JA
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
ccc6cda3
JA
61 if the alias doesn't exist, else a pointer to the alias_t. */
62alias_t *
726f6388
JA
63find_alias (name)
64 char *name;
65{
66 BUCKET_CONTENTS *al;
67
ccc6cda3
JA
68 if (aliases == 0)
69 return ((alias_t *)NULL);
726f6388 70
ccc6cda3
JA
71 al = find_hash_item (name, aliases);
72 return (al ? (alias_t *)al->data : (alias_t *)NULL);
726f6388
JA
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{
ccc6cda3
JA
80 alias_t *alias;
81
82 if (aliases == 0)
726f6388 83 return ((char *)NULL);
ccc6cda3
JA
84
85 alias = find_alias (name);
86 return (alias ? alias->value : (char *)NULL);
726f6388
JA
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{
ccc6cda3
JA
95 BUCKET_CONTENTS *elt;
96 alias_t *temp;
97 int n;
726f6388
JA
98
99 if (!aliases)
ccc6cda3
JA
100 {
101 initialize_aliases ();
102 temp = (alias_t *)NULL;
103 }
726f6388
JA
104 else
105 temp = find_alias (name);
106
107 if (temp)
108 {
109 free (temp->value);
110 temp->value = savestring (value);
ccc6cda3
JA
111 n = value[strlen (value) - 1];
112 if (n == ' ' || n == '\t')
113 temp->flags |= AL_EXPANDNEXT;
726f6388
JA
114 }
115 else
116 {
ccc6cda3 117 temp = (alias_t *)xmalloc (sizeof (alias_t));
726f6388
JA
118 temp->name = savestring (name);
119 temp->value = savestring (value);
ccc6cda3
JA
120 temp->flags = 0;
121
122 n = value[strlen (value) - 1];
123 if (n == ' ' || n == '\t')
124 temp->flags |= AL_EXPANDNEXT;
726f6388
JA
125
126 elt = add_hash_item (savestring (name), aliases);
127 elt->data = (char *)temp;
bb70624e
JA
128#if defined (PROGRAMMABLE_COMPLETION)
129 set_itemlist_dirty (&it_aliases);
130#endif
726f6388
JA
131 }
132}
133
ccc6cda3
JA
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
726f6388
JA
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
ccc6cda3 156 if (aliases == 0)
726f6388
JA
157 return (-1);
158
159 elt = remove_hash_item (name, aliases);
160 if (elt)
161 {
ccc6cda3 162 free_alias_data (elt->data);
726f6388 163 free (elt->key); /* alias name */
d166f048 164 free (elt); /* XXX */
bb70624e
JA
165#if defined (PROGRAMMABLE_COMPLETION)
166 set_itemlist_dirty (&it_aliases);
167#endif
726f6388
JA
168 return (aliases->nentries);
169 }
170 return (-1);
171}
172
726f6388
JA
173/* Delete all aliases. */
174void
175delete_all_aliases ()
176{
ccc6cda3 177 if (aliases == 0)
726f6388
JA
178 return;
179
ccc6cda3 180 flush_hash_table (aliases, free_alias_data);
d166f048 181 dispose_hash_table (aliases);
726f6388 182 aliases = (HASH_TABLE *)NULL;
bb70624e
JA
183#if defined (PROGRAMMABLE_COMPLETION)
184 set_itemlist_dirty (&it_aliases);
185#endif
726f6388
JA
186}
187
188/* Return an array of aliases that satisfy the conditions tested by FUNCTION.
189 If FUNCTION is NULL, return all aliases. */
ccc6cda3 190static alias_t **
726f6388
JA
191map_over_aliases (function)
192 Function *function;
193{
194 register int i;
195 register BUCKET_CONTENTS *tlist;
ccc6cda3
JA
196 alias_t *alias, **list;
197 int list_index, list_size;
726f6388 198
ccc6cda3
JA
199 list = (alias_t **)NULL;
200 for (i = list_index = list_size = 0; i < aliases->nbuckets; i++)
726f6388
JA
201 {
202 tlist = get_hash_bucket (i, aliases);
203
204 while (tlist)
205 {
ccc6cda3 206 alias = (alias_t *)tlist->data;
726f6388
JA
207
208 if (!function || (*function) (alias))
209 {
210 if (list_index + 1 >= list_size)
ccc6cda3
JA
211 list = (alias_t **)
212 xrealloc ((char *)list, (list_size += 20) * sizeof (alias_t *));
726f6388
JA
213
214 list[list_index++] = alias;
ccc6cda3 215 list[list_index] = (alias_t *)NULL;
726f6388
JA
216 }
217 tlist = tlist->next;
218 }
219 }
220 return (list);
221}
222
223static void
224sort_aliases (array)
ccc6cda3 225 alias_t **array;
726f6388 226{
ccc6cda3 227 qsort (array, array_len ((char **)array), sizeof (alias_t *), qsort_alias_compare);
726f6388
JA
228}
229
230static int
231qsort_alias_compare (as1, as2)
ccc6cda3 232 alias_t **as1, **as2;
726f6388
JA
233{
234 int result;
ccc6cda3 235
726f6388
JA
236 if ((result = (*as1)->name[0] - (*as2)->name[0]) == 0)
237 result = strcmp ((*as1)->name, (*as2)->name);
238
239 return (result);
240}
ccc6cda3
JA
241
242/* Return a sorted list of all defined aliases */
243alias_t **
726f6388
JA
244all_aliases ()
245{
ccc6cda3 246 alias_t **list;
726f6388
JA
247
248 if (!aliases)
ccc6cda3 249 return ((alias_t **)NULL);
726f6388
JA
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{
ccc6cda3 261 alias_t *r;
726f6388 262
ccc6cda3
JA
263 r = find_alias (s);
264 return (r ? savestring (r->value) : (char *)NULL);
726f6388
JA
265}
266
d166f048
JA
267/* Readline support functions -- expand all aliases in a line. */
268
269#if defined (READLINE)
270
726f6388
JA
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)
ccc6cda3 408 char *string;
726f6388
JA
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
ccc6cda3 425 have. The presence of the quotes in the token serves then to
726f6388
JA
426 inhibit expansion. */
427 if (quote_char (string[i]))
428 {
429 i = skipquotes (string, i);
cce855bc
JA
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
726f6388
JA
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{
726f6388 449 register int i, j, start;
bb70624e
JA
450 char *line, *token;
451 int line_len, tl, real_start, expand_next, expand_this_token;
ccc6cda3 452 alias_t *alias;
726f6388 453
bb70624e
JA
454 line_len = strlen (string) + 1;
455 line = xmalloc (line_len);
456 token = xmalloc (line_len);
457
726f6388
JA
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
bb70624e
JA
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
726f6388
JA
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 */
ccc6cda3 486 RESIZE_MALLOCED_BUFFER (line, j, (tl + 1), line_len, (tl + 50));
726f6388
JA
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 {
ccc6cda3
JA
529 char *v;
530 int vlen, llen;
531
532 v = alias->value;
533 vlen = strlen (v);
534 llen = strlen (line);
535
726f6388 536 /* +3 because we possibly add one more character below. */
ccc6cda3 537 RESIZE_MALLOCED_BUFFER (line, llen, (vlen + 3), line_len, (vlen + 50));
726f6388 538
ccc6cda3 539 strcpy (line + llen, v);
726f6388 540
ccc6cda3 541 if ((expand_this_token && vlen && whitespace (v[vlen - 1])) ||
726f6388
JA
542 alias_expand_all)
543 expand_next = 1;
544 }
545 else
546 {
ccc6cda3
JA
547 int llen, tlen;
548
549 llen = strlen (line);
550 tlen = i - real_start; /* tlen == strlen(token) */
726f6388 551
ccc6cda3 552 RESIZE_MALLOCED_BUFFER (line, llen, (tlen + 1), line_len, (llen + tlen + 50));
726f6388 553
ccc6cda3
JA
554 strncpy (line + llen, string + real_start, tlen);
555 line[llen + tlen] = '\0';
726f6388
JA
556 }
557 command_word = 0;
558 }
559}
d166f048 560#endif /* READLINE */
ccc6cda3 561#endif /* ALIAS */