commit bash-20180112 snapshot

This commit is contained in:
Chet Ramey
2018-01-16 09:57:29 -05:00
parent 879213c630
commit 911ae06ca9
13 changed files with 302 additions and 21 deletions
+48
View File
@@ -14800,3 +14800,51 @@ subst.c
'G' to the options list if W_CHKLOCAL is set in the word's flags.
This makes builtins like `readonly' that modify local variables in
a function behave the same for scalar and array variables
1/11
----
parse.y
- shell_getc: move code that decides whether to append a space to an
alias expansion here from mk_alexpansion, so we can inhibit adding
a space if we're currently parsing a single or double quoted string
1/12
----
parse.y
- clear_string_list_expander: take a pointer to an alias that's about
to be freed and make sure there aren't any pointers to it in the
list of pushed strings. If there are, zero it out in the pushed
string list to avoid referencing freed memory in pop_string()
alias.c
- free_alias_data: if an alias being freed is currently being expanded,
call clear_string_list_expander to remove references to it from the
list of pushed strings
1/14
----
pcomplib.c
- progcomp_search: add code to look up an alias for the CMD argument
and return the completions for the first word of that alias if one
is found. Just a start at completing aliases, a much-requested
feature
pcomplete.h
- COPT_LASTUSER: last flag value used by user-settable completion
options
- PCOMP_RETRYFAIL, PCOMP_NOTFOUND: new #defines, possible return values
from programmable_completions in FOUNDP argument. Moved RETRYFAIL
define here from pcomplete.c to avoid collisions with user-settable
option values (COPT_*)
1/15
----
pcomplete.c
- programmable_completions: if we don't find any completions for a
command, and RETRY is 0, see if the command is a defined alias,
expand it, and try to expand the first word of the value as a
command, and find any programmable completions for it. Here right
now, could be moved to attempt_shell_completion later if we need
to do more analysis of the expanded line. We'll see how it works
in practice.
+1
View File
@@ -820,6 +820,7 @@ tests/alias.tests f
tests/alias1.sub f
tests/alias2.sub f
tests/alias3.sub f
tests/alias4.sub f
tests/alias.right f
tests/appendop.tests f
tests/appendop1.sub f
+4
View File
@@ -158,6 +158,10 @@ free_alias_data (data)
register alias_t *a;
a = (alias_t *)data;
if (a->flags & AL_BEINGEXPANDED)
clear_string_list_expander (a); /* call back to the parser */
free (a->value);
free (a->name);
free (data);
+3
View File
@@ -67,4 +67,7 @@ extern char *alias_expand_word __P((char *));
/* Return a new line, with any aliases expanded. */
extern char *alias_expand __P((char *));
/* Helper definition for the parser */
extern void clear_string_list_expander __P((alias_t *));
#endif /* _ALIAS_H_ */
+10
View File
@@ -681,6 +681,8 @@ compgen_builtin (list)
COMPSPEC *cs;
STRINGLIST *sl;
char *word, **matches;
char *old_line;
int old_ind;
if (list == 0)
return (EXECUTION_SUCCESS);
@@ -721,7 +723,15 @@ compgen_builtin (list)
cs->filterpat = STRDUP (Xarg);
rval = EXECUTION_FAILURE;
/* probably don't have to save these, just being safe */
old_line = pcomp_line;
old_ind = pcomp_ind;
pcomp_line = (char *)NULL;
pcomp_ind = 0;
sl = gen_compspec_completions (cs, "compgen", word, 0, 0, 0);
pcomp_line = old_line;
pcomp_ind = old_ind;
/* If the compspec wants the bash default completions, temporarily
turn off programmable completion and call the bash completion code. */
+2 -2
View File
@@ -40,11 +40,11 @@ Options which set attributes:
-a to make NAMEs indexed arrays (if supported)
-A to make NAMEs associative arrays (if supported)
-i to make NAMEs have the `integer' attribute
-l to convert NAMEs to lower case on assignment
-l to convert the value of each NAME to lower case on assignment
-n make NAME a reference to the variable named by its value
-r to make NAMEs readonly
-t to make NAMEs have the `trace' attribute
-u to convert NAMEs to upper case on assignment
-u to convert the value of each NAME to upper case on assignment
-x to make NAMEs export
Using `+' instead of `-' turns off the given attribute.
+1 -1
View File
@@ -4373,7 +4373,7 @@ Remove a trailing @var{delim} (default newline) from each line read.
@item -u
Read lines from file descriptor @var{fd} instead of the standard input.
@item -C
Evaluate @var{callback} each time @var{quantum}P lines are read.
Evaluate @var{callback} each time @var{quantum} lines are read.
The @option{-c} option specifies @var{quantum}.
@item -c
Specify the number of lines read between each call to @var{callback}.
+51 -5
View File
@@ -95,6 +95,8 @@ typedef void *alias_t;
#define RE_READ_TOKEN -99
#define NO_EXPANSION -100
#define END_ALIAS -2
#ifdef DEBUG
# define YYDEBUG 1
#else
@@ -1967,6 +1969,23 @@ parser_restore_alias ()
#endif
}
#if defined (ALIAS)
/* Before freeing AP, make sure that there aren't any cases of pointer
aliasing that could cause us to reference freed memory later on. */
void
clear_string_list_expander (ap)
alias_t *ap;
{
register STRING_SAVER *t;
for (t = pushed_string_list; t; t = t->next)
{
if (t->expander && t->expander == ap)
t->expander = 0;
}
}
#endif
void
clear_shell_input_line ()
{
@@ -2500,6 +2519,30 @@ next_alias_char:
parsing an alias, we have just saved one (push_string, when called by
the parse_dparen code) In this case, just go on as well. The PSH_SOURCE
case is handled below. */
/* If we're at the end of an alias expansion add a space to make sure that
the alias remains marked as being in use while we expand its last word.
This makes sure that pop_string doesn't mark the alias as not in use
before the string resulting from the alias expansion is tokenized and
checked for alias expansion, preventing recursion. At this point, the
last character in shell_input_line is the last character of the alias
expansion. We test that last character to determine whether or not to
return the space that will delimit the token and postpone the pop_string.
This set of conditions duplicates what used to be in mk_alexpansion ()
below, with the addition that we don't add a space if we're currently
reading a quoted string. */
#ifndef OLD_ALIAS_HACK
if (uc == 0 && pushed_string_list && pushed_string_list->flags != PSH_SOURCE &&
shell_input_line_index > 0 &&
shell_input_line[shell_input_line_index-1] != ' ' &&
shell_input_line[shell_input_line_index-1] != '\n' &&
shellmeta (shell_input_line[shell_input_line_index-1]) == 0 &&
(current_delimiter (dstack) != '\'' && current_delimiter (dstack) != '"'))
{
return ' '; /* END_ALIAS */
}
#endif
pop_alias:
if (uc == 0 && pushed_string_list && pushed_string_list->flags != PSH_SOURCE)
{
@@ -2518,10 +2561,9 @@ pop_alias:
/* What do we do here if we're expanding an alias whose definition
includes an escaped newline? If that's the last character in the
alias expansion, we just pop the pushed string list (recall that
we inhibit the appending of a space in mk_alexpansion() if newline
is the last character). If it's not the last character, we need
to consume the quoted newline and move to the next character in
the expansion. */
we inhibit the appending of a space if newline is the last
character). If it's not the last character, we need to consume the
quoted newline and move to the next character in the expansion. */
#if defined (ALIAS)
if (expanding_alias () && shell_input_line[shell_input_line_index+1] == '\0')
{
@@ -2829,13 +2871,15 @@ mk_alexpansion (s)
l = strlen (s);
r = xmalloc (l + 2);
strcpy (r, s);
#ifdef OLD_ALIAS_HACK
/* If the last character in the alias is a newline, don't add a trailing
space to the expansion. Works with shell_getc above. */
/* Need to do something about the case where the alias expansion contains
an unmatched quoted string, since appending this space affects the
subsequent output. */
if (r[l - 1] != ' ' && r[l - 1] != '\n' && shellmeta(r[l - 1]) == 0)
if (l > 0 && r[l - 1] != ' ' && r[l - 1] != '\n' && shellmeta(r[l - 1]) == 0)
r[l++] = ' ';
#endif
r[l] = '\0';
return r;
}
@@ -2856,12 +2900,14 @@ alias_expand_token (tokstr)
if (ap && (ap->flags & AL_BEINGEXPANDED))
return (NO_EXPANSION);
#ifdef OLD_ALIAS_HACK
/* mk_alexpansion puts an extra space on the end of the alias expansion,
so the lookahead by the parser works right (the alias needs to remain
`in use' while parsing its last word to avoid alias recursion for
something like "alias echo=echo"). If this gets changed, make sure
the code in shell_getc that deals with reaching the end of an
expanded alias is changed with it. */
#endif
expanded = ap ? mk_alexpansion (ap->value) : (char *)NULL;
if (expanded)
+97 -13
View File
@@ -1,6 +1,6 @@
/* pcomplete.c - functions to generate lists of matches for programmable completion. */
/* Copyright (C) 1999-2012 Free Software Foundation, Inc.
/* Copyright (C) 1999-2018 Free Software Foundation, Inc.
This file is part of GNU Bash, the Bourne Again SHell.
@@ -71,8 +71,6 @@
#include <readline/readline.h>
#include <readline/history.h>
#define PCOMP_RETRYFAIL 256
#ifdef STRDUP
# undef STRDUP
#endif
@@ -153,6 +151,10 @@ static int progcomp_debug = 0;
int prog_completion_enabled = 1;
#ifdef ALIAS
int progcomp_alias = 0; /* unavailable to user code for now */
#endif
/* These are used to manage the arrays of strings for possible completions. */
ITEMLIST it_aliases = { 0, it_init_aliases, (STRINGLIST *)0 };
ITEMLIST it_arrayvars = { LIST_DYNAMIC, it_init_arrayvars, (STRINGLIST *)0 };
@@ -183,6 +185,9 @@ COMPSPEC *pcomp_curcs;
const char *pcomp_curcmd;
const char *pcomp_curtxt;
char *pcomp_line;
int pcomp_ind;
#ifdef DEBUG
/* Debugging code */
static void
@@ -1380,14 +1385,14 @@ gen_compspec_completions (cs, cmd, word, start, end, foundp)
/* If we have a command or function to execute, we need to first break
the command line into individual words, find the number of words,
and find the word in the list containing the word to be completed. */
line = substring (rl_line_buffer, start, end);
line = substring (pcomp_line, start, end);
llen = end - start;
#ifdef DEBUG
debug_printf ("command_line_to_word_list (%s, %d, %d, %p, %p)",
line, llen, rl_point - start, &nw, &cw);
line, llen, pcomp_ind - start, &nw, &cw);
#endif
lwords = command_line_to_word_list (line, llen, rl_point - start, &nw, &cw);
lwords = command_line_to_word_list (line, llen, pcomp_ind - start, &nw, &cw);
/* If we skipped a NULL word at the beginning of the line, add it back */
if (lwords && lwords->word && cmd[0] == 0 && lwords->word->word[0] != 0)
{
@@ -1414,7 +1419,7 @@ gen_compspec_completions (cs, cmd, word, start, end, foundp)
if (cs->funcname)
{
foundf = 0;
tmatches = gen_shell_function_matches (cs, cmd, word, line, rl_point - start, lwords, nw, cw, &foundf);
tmatches = gen_shell_function_matches (cs, cmd, word, line, pcomp_ind - start, lwords, nw, cw, &foundf);
if (foundf != 0)
found = foundf;
if (tmatches)
@@ -1434,7 +1439,7 @@ gen_compspec_completions (cs, cmd, word, start, end, foundp)
if (cs->command)
{
tmatches = gen_command_matches (cs, cmd, word, line, rl_point - start, lwords, nw, cw);
tmatches = gen_command_matches (cs, cmd, word, line, pcomp_ind - start, lwords, nw, cw);
if (tmatches)
{
#ifdef DEBUG
@@ -1608,7 +1613,8 @@ gen_progcomp_completions (ocmd, cmd, word, start, end, foundp, retryp, lastcs)
/* The driver function for the programmable completion code. Returns a list
of matches for WORD, which is an argument to command CMD. START and END
bound the command currently being completed in rl_line_buffer. */
bound the command currently being completed in pcomp_line (usually
rl_line_buffer). */
char **
programmable_completions (cmd, word, start, end, foundp)
const char *cmd;
@@ -1619,26 +1625,95 @@ programmable_completions (cmd, word, start, end, foundp)
STRINGLIST *ret;
char **rmatches, *t;
int found, retry, count;
char *ocmd;
int oend;
#if defined (ALIAS)
alias_t *al;
#endif
lastcs = 0;
found = count = 0;
pcomp_line = rl_line_buffer;
pcomp_ind = rl_point;
ocmd = (char *)cmd;
oend = end;
do
{
retry = 0;
/* We look at the basename of CMD if the full command does not have
an associated COMPSPEC. */
ret = gen_progcomp_completions (cmd, cmd, word, start, end, &found, &retry, &lastcs);
ret = gen_progcomp_completions (ocmd, ocmd, word, start, oend, &found, &retry, &lastcs);
if (found == 0)
{
t = strrchr (cmd, '/');
t = strrchr (ocmd, '/');
if (t && *(++t))
ret = gen_progcomp_completions (t, cmd, word, start, end, &found, &retry, &lastcs);
ret = gen_progcomp_completions (t, ocmd, word, start, oend, &found, &retry, &lastcs);
}
if (found == 0)
ret = gen_progcomp_completions (DEFAULTCMD, cmd, word, start, end, &found, &retry, &lastcs);
ret = gen_progcomp_completions (DEFAULTCMD, ocmd, word, start, oend, &found, &retry, &lastcs);
#if defined (ALIAS)
/* Look up any alias for CMD, try to gen completions for it */
/* Look up the alias, find the value, build a new line replacing CMD
with that value, offsetting PCOMP_IND and END appropriately, reset
PCOMP_LINE to the new line and OCMD with the new command name, then
call gen_progcomp_completions again. We could use alias_expand for
this, but it does more (and less) than we need right now. */
if (found == 0 && retry == 0 && progcomp_alias && (al = find_alias (ocmd)))
{
char *ncmd, *nline, *ntxt;
int ind, lendiff;
size_t nlen, olen, llen;
/* We found an alias for OCMD. Take the value and build a new line */
ntxt = al->value;
nlen = strlen (ntxt);
if (nlen == 0)
break;
olen = strlen (ocmd);
lendiff = nlen - olen; /* can be negative */
llen = strlen (pcomp_line);
nline = (char *)xmalloc (llen + lendiff + 1);
if (start > 0)
strncpy (nline, pcomp_line, start);
strncpy (nline + start, ntxt, nlen);
strcpy (nline + start + nlen, pcomp_line + start + olen);
/* Find the first word of the alias value and use that as OCMD. We
don't check the alias value to see whether it begins with a valid
command name, so this can be fooled. */
ind = skip_to_delim (ntxt, 0, "()<>;&| \t\n", SD_NOJMP|SD_COMPLETE); /*)*/
if (ind > 0)
ncmd = substring (ntxt, 0, ind);
else
{
free (nline);
break; /* will free pcomp_line and ocmd later */
}
/* Adjust PCOMP_IND and OEND appropriately */
pcomp_ind += lendiff;
oend += lendiff;
/* Set up values with new line. WORD stays the same. */
if (ocmd != cmd)
free (ocmd);
if (pcomp_line != rl_line_buffer)
free (pcomp_line);
ocmd = ncmd;
pcomp_line = nline;
/* And go back and start over. */
retry = 1;
}
#endif /* ALIAS */
count++;
@@ -1650,6 +1725,11 @@ programmable_completions (cmd, word, start, end, foundp)
}
while (retry);
if (pcomp_line != rl_line_buffer)
free (pcomp_line);
if (ocmd != cmd)
free (ocmd);
if (ret)
{
rmatches = ret->list;
@@ -1664,6 +1744,10 @@ programmable_completions (cmd, word, start, end, foundp)
if (lastcs) /* XXX - should be while? */
compspec_dispose (lastcs);
/* XXX restore pcomp_line and pcomp_ind? */
pcomp_line = rl_line_buffer;
pcomp_ind = rl_point;
return (rmatches);
}
+11
View File
@@ -77,6 +77,12 @@ typedef struct compspec {
#define COPT_PLUSDIRS (1<<7)
#define COPT_NOSORT (1<<8)
#define COPT_LASTUSER COPT_NOSORT
#define PCOMP_RETRYFAIL (COPT_LASTUSER << 1)
#define PCOMP_NOTFOUND (COPT_LASTUSER << 2)
/* List of items is used by the code that implements the programmable
completions. */
typedef struct _list_of_items {
@@ -103,7 +109,12 @@ typedef struct _list_of_items {
#define DEFAULTCMD "_DefaultCmD_"
extern HASH_TABLE *prog_completes;
extern char *pcomp_line;
extern int pcomp_ind;
extern int prog_completion_enabled;
extern int progcomp_alias;
/* Not all of these are used yet. */
extern ITEMLIST it_aliases;
+12
View File
@@ -18,3 +18,15 @@ one
two
three
four
Error: bar
ok 1
ok 2
text
whoops: nullalias
foo
a
a b
a b
a a b
ok 3
ok 4
+1
View File
@@ -39,3 +39,4 @@ unalias foo bar baz
${THIS_SH} ./alias1.sub
${THIS_SH} ./alias2.sub
${THIS_SH} ./alias3.sub
${THIS_SH} ./alias4.sub
+61
View File
@@ -0,0 +1,61 @@
shopt -s expand_aliases
# from an austin-group report
alias foo="echo 'Error:"
foo bar'
# from some FreeBSD sh tests
v=1
alias a='unalias -a
v=2'
eval a
[ "$v" = 2 ] && echo ok 1
v=1
alias a='unalias a
v=2'
eval a
[ "$v" = 2 ] && echo ok 2
# make sure command doesn't ever reset anything even if it's made a keyword
unalias -a
alias command=command
alias true='echo bad'
eval 'command true'
unalias -a
alias alias0=command
alias true='echo bad'
eval 'alias0 true'
# make sure null aliases are ok
unalias -a
alias nullalias=''
alias foo='echo '
foo nullalias text
unalias foo
# aliases shouldn't be expanded in quoted strings even when the previous word
# is an alias whose expansion ends in a space
alias foo="echo 'whoops: "
foo nullalias'
unalias -a
# recursive alias definitions
alias echo=echo
eval echo foo
alias echo='echo a'
echo
echo b
eval echo b
echo $(eval echo b)
unalias -a
# alias expansion when in a command position after redirections
alias e=echo
eval '</dev/null e ok 3'
eval 'a=true e ok 4'