]> git.ipfire.org Git - thirdparty/bash.git/blob - alias.c
Imported from ../bash-2.04.tar.gz.
[thirdparty/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 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
44 static 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. */
48 int alias_expand_all = 0;
49
50 /* The list of aliases that we have. */
51 HASH_TABLE *aliases = (HASH_TABLE *)NULL;
52
53 void
54 initialize_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. */
62 alias_t *
63 find_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. */
76 char *
77 get_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. */
91 void
92 add_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. */
135 static void
136 free_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. */
150 int
151 remove_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. */
174 void
175 delete_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. */
190 static alias_t **
191 map_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
223 static void
224 sort_aliases (array)
225 alias_t **array;
226 {
227 qsort (array, array_len ((char **)array), sizeof (alias_t *), qsort_alias_compare);
228 }
229
230 static int
231 qsort_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 */
243 alias_t **
244 all_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
257 char *
258 alias_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. */
282 static 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
295 static int
296 skipquotes (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. */
322 static int
323 skipws (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) */
406 static int
407 rd_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. */
445 char *
446 alias_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 */