~ubuntu-branches/ubuntu/trusty/bash/trusty-security

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