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