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