mirror of
https://https.git.savannah.gnu.org/git/bash.git
synced 2026-06-27 23:53:18 +02:00
fixed a bug with expanding unquoted $* when the separator is not whitespace and one or more of the positional parameters ends with the separator, resulting in an extra blank field being generated; fixed an issue with adjusting SHLVL in subshells that caused the environment to be rebuilt too many times; fix to make sure function substitution doesn't leave the -v option unset
This commit is contained in:
@@ -12877,3 +12877,64 @@ lib/sh/stringvec.c,lib/sh/stringlist.c
|
||||
of list_append/list_length, since they're always called with
|
||||
WORD_LIST * arguments
|
||||
|
||||
5/8
|
||||
---
|
||||
command.h
|
||||
- PF_STRINGEND,PF_STRINGBEG: new flags for the parameter expansion
|
||||
functions; set if there are no characters following or preceding the
|
||||
expansion, respectively
|
||||
|
||||
subst.c
|
||||
- string_list_dollar_atstar: new function, expands unquoted $* and $@
|
||||
when word splitting will take place and the separator is not
|
||||
whitespace, and the positional parameters can potentially contain
|
||||
the separator. It splits the positional parameters before separating
|
||||
them with the separator, taking care to handle null positional
|
||||
parameters and proper merging with text immediately preceding or
|
||||
following the expansion. Fixes bug that added a spurious empty
|
||||
argument if one of the positional parameters ended with the
|
||||
separator
|
||||
- string_list_internal: take a new flag value that says to add an
|
||||
additional separator at the end of the returned string if the
|
||||
last word is a null word and it will be joined with text following
|
||||
the expansion
|
||||
- string_list_pos_params: call string_list_dollar_atstar as
|
||||
appropriate
|
||||
- list_string: take a new word flag: W_SPLITONLY. This means not to
|
||||
perform quoted null character removal while splitting the word
|
||||
and don't allow CTLESC to quote anything, and to return "" results
|
||||
of splitting verbatim, but marked as W_QUOTED to preserve null
|
||||
arguments
|
||||
- expand_word_internal,param_expand: set PF_STRINGEND and PF_STRINGBEG
|
||||
appropriately
|
||||
- word_split: pass W_SPLITONLY through to list_string()
|
||||
These changes fix the bug with adding an extra empty word if one of
|
||||
the positional parameters ends with the first character of $IFS and
|
||||
it's not whitespace. Changes tests/dollar-at-star6.sub output.
|
||||
|
||||
arrayfunc.c
|
||||
- array_value_internal: if we have an unquoted array expansion
|
||||
subscripted with * or @, call string_list_dollar_atstar if
|
||||
appropriate
|
||||
|
||||
5/13
|
||||
----
|
||||
execute_cmd.c
|
||||
- execute_disk_command: if we update SHLVL, do it after (maybe)
|
||||
making the export environment and then update the export env in
|
||||
place, so we don't have to rebuild it twice or rebuild it more
|
||||
times than necessary
|
||||
Report and fix from Gao Xiang <gaoxiang@kylinos.cn>
|
||||
|
||||
5/14
|
||||
----
|
||||
variables.c
|
||||
- adjust_shell_level,set_pwd: make sure variable binding works before
|
||||
trying to set the variable as exported
|
||||
Report and patch from Grisha Levit <grishalevit@gmail.com>
|
||||
|
||||
subst.c
|
||||
- function_substitute: make sure funsubs don't leave `-v' unset
|
||||
after they return by adding an unwind-protect function
|
||||
uw_restore_verbose()
|
||||
Report and patch from Grisha Levit <grishalevit@gmail.com>
|
||||
|
||||
@@ -1135,6 +1135,8 @@ tests/dollar-at-star8.sub f
|
||||
tests/dollar-at-star9.sub f
|
||||
tests/dollar-at-star10.sub f
|
||||
tests/dollar-at-star11.sub f
|
||||
tests/dollar-at-star12.sub f
|
||||
tests/dollar-at-star13.sub f
|
||||
tests/dollar-at1.sub f
|
||||
tests/dollar-at2.sub f
|
||||
tests/dollar-at3.sub f
|
||||
|
||||
@@ -1581,6 +1581,10 @@ array_value_internal (const char *s, int quoted, int flags, array_eltstate_t *es
|
||||
retval = quote_nosplit (temp);
|
||||
free (temp);
|
||||
}
|
||||
else if (quoted == 0 && (flags & AV_ASSIGNRHS) == 0 &&
|
||||
ifs_is_set && ifs_is_null == 0 &&
|
||||
spctabnl (ifs_firstc[0]) == 0)
|
||||
retval = string_list_dollar_atstar (l, quoted, 0);
|
||||
else /* ${name[@]} or unquoted ${name[*]} */
|
||||
retval = string_list_dollar_at (l, quoted, (flags & AV_ASSIGNRHS) ? PF_ASSIGNRHS : 0);
|
||||
|
||||
|
||||
@@ -116,6 +116,8 @@ enum command_type { cm_for, cm_case, cm_while, cm_if, cm_simple, cm_select,
|
||||
#define PF_ALLINDS 0x40 /* array, act as if [@] was supplied */
|
||||
#define PF_BACKQUOTE 0x80 /* differentiate `` from $() for command_substitute */
|
||||
#define PF_COMSUBNLS 0x100 /* for ${; ...; } and read_comsub() to not strip trailing newlines */
|
||||
#define PF_STRINGEND 0x200 /* set if there are no chars following this expansion */
|
||||
#define PF_STRINGBEG 0x400 /* set if there are no chars before this expansion */
|
||||
|
||||
/* Possible values for subshell_environment */
|
||||
#define SUBSHELL_ASYNC 0x01 /* subshell caused by `command &' */
|
||||
|
||||
@@ -103,7 +103,7 @@ INC = -I. -I.. -I$(topdir) -I$(topdir)/lib -I$(topdir)/builtins -I${srcdir} \
|
||||
ALLPROG = print truefalse sleep finfo logname basename dirname fdflags \
|
||||
tty pathchk tee head mkdir rmdir mkfifo mktemp printenv id whoami \
|
||||
uname sync push ln unlink realpath strftime mypid setpgid seq rm \
|
||||
accept csv dsv cut stat getconf kv strptime chmod fltexpr jobid
|
||||
accept csv dsv cut stat getconf kv strptime chmod fltexpr jobid rev
|
||||
OTHERPROG = necho hello cat pushd asort
|
||||
|
||||
SUBDIRS = perl
|
||||
|
||||
@@ -364,7 +364,7 @@ cutline (SHELL_VAR *v, arrayind_t ind, char *line, struct cutop *ops)
|
||||
static int
|
||||
cutfile (SHELL_VAR *v, WORD_LIST *list, struct cutop *ops)
|
||||
{
|
||||
int fd, unbuffered_read, r;
|
||||
int fd, unbuffered_read, r, closefd;
|
||||
char *line, *b;
|
||||
size_t llen;
|
||||
WORD_LIST *l;
|
||||
@@ -378,11 +378,16 @@ cutfile (SHELL_VAR *v, WORD_LIST *list, struct cutop *ops)
|
||||
l = list;
|
||||
do
|
||||
{
|
||||
closefd = 0;
|
||||
|
||||
/* for each file */
|
||||
if (l == 0 || (l->word->word[0] == '-' && l->word->word[1] == '\0'))
|
||||
fd = 0;
|
||||
else
|
||||
fd = open (l->word->word, O_RDONLY);
|
||||
{
|
||||
fd = open (l->word->word, O_RDONLY);
|
||||
closefd = fd != -1;
|
||||
}
|
||||
if (fd < 0)
|
||||
{
|
||||
file_error (l->word->word);
|
||||
@@ -403,7 +408,8 @@ cutfile (SHELL_VAR *v, WORD_LIST *list, struct cutop *ops)
|
||||
r = cutline (v, ind, line, ops); /* can modify line */
|
||||
ind += r;
|
||||
}
|
||||
if (fd > 0)
|
||||
|
||||
if (closefd)
|
||||
close (fd);
|
||||
|
||||
QUIT;
|
||||
|
||||
+22
-6
@@ -5937,17 +5937,26 @@ execute_disk_command (WORD_LIST *words, REDIRECT *redirects, char *command_line,
|
||||
|
||||
if (command)
|
||||
{
|
||||
/* If we're optimizing out the fork (implicit `exec'), decrement the
|
||||
shell level like `exec' would do. Don't do this if we are already
|
||||
in a pipeline environment, assuming it's already been done. */
|
||||
if (nofork && pipe_in == NO_PIPE && pipe_out == NO_PIPE && (subshell_environment & SUBSHELL_PIPE) == 0)
|
||||
adjust_shell_level (-1);
|
||||
|
||||
#if defined (STRICT_POSIX)
|
||||
if (posixly_correct == 0 || subst_assign_varlist == 0) /* Done below. */
|
||||
#endif
|
||||
{
|
||||
maybe_make_export_env ();
|
||||
/* If we're optimizing out the fork (implicit `exec'), decrement the
|
||||
shell level like `exec' would do. Don't do this if we are already
|
||||
in a pipeline environment, assuming it's already been done.
|
||||
Since we've just (possibly) remade the export environment, it's
|
||||
marked as not dirty, so we update SHLVL in place rather than
|
||||
remake the whole thing again or force a remake we would not
|
||||
otherwise have to do. This mostly hits command substitutions with
|
||||
a lot of exported variables, since command_substitute already
|
||||
calls maybe_make_export_env (). */
|
||||
if (nofork && pipe_in == NO_PIPE && pipe_out == NO_PIPE && (subshell_environment & SUBSHELL_PIPE) == 0)
|
||||
{
|
||||
adjust_shell_level (-1);
|
||||
update_export_env_inplace ("SHLVL=", 6, get_string_value ("SHLVL"));
|
||||
array_needs_making = 0;
|
||||
}
|
||||
put_command_name_into_env (command);
|
||||
}
|
||||
}
|
||||
@@ -6035,6 +6044,13 @@ execute_disk_command (WORD_LIST *words, REDIRECT *redirects, char *command_line,
|
||||
expand_assignment_statements (command, 0);
|
||||
|
||||
maybe_make_export_env ();
|
||||
/* See above for why we do this here. */
|
||||
if (nofork && pipe_in == NO_PIPE && pipe_out == NO_PIPE && (subshell_environment & SUBSHELL_PIPE) == 0)
|
||||
{
|
||||
adjust_shell_level (-1);
|
||||
update_export_env_inplace ("SHLVL=", 6, get_string_value ("SHLVL"));
|
||||
array_needs_making = 0;
|
||||
}
|
||||
put_command_name_into_env (command);
|
||||
}
|
||||
#endif
|
||||
|
||||
@@ -2875,6 +2875,9 @@ string_list_internal (WORD_LIST *list, char *sep, int flags)
|
||||
result_size += strlen (t->word->word);
|
||||
}
|
||||
|
||||
if (flags & 1)
|
||||
result_size += sep_len;
|
||||
|
||||
r = result = (char *)xmalloc (result_size + 1);
|
||||
|
||||
for (t = list; t; t = t->next)
|
||||
@@ -2893,6 +2896,20 @@ string_list_internal (WORD_LIST *list, char *sep, int flags)
|
||||
word_len = strlen (t->word->word);
|
||||
FASTCOPY (t->word->word, r, word_len);
|
||||
r += word_len;
|
||||
|
||||
/* FLAGS & 1 means to add an additional SEP to the end of the returned
|
||||
string if and only if the last word is a null word, since it will be
|
||||
joined with text following the expansion. */
|
||||
if ((flags & 1) && t->next == 0 && (t->word->word == 0 || t->word->word[0] == '\0') && (t->word->flags & W_QUOTED))
|
||||
{
|
||||
if (sep_len > 1)
|
||||
{
|
||||
FASTCOPY (sep, r, sep_len);
|
||||
r += sep_len;
|
||||
}
|
||||
else if (sep_len)
|
||||
*r++ = sep[0];
|
||||
}
|
||||
}
|
||||
|
||||
*r = '\0';
|
||||
@@ -3071,6 +3088,107 @@ string_list_dollar_at (WORD_LIST *list, int quoted, int flags)
|
||||
return ret;
|
||||
}
|
||||
|
||||
/* Expand $* or $@ in a context where word splitting will be performed. We
|
||||
separate the words in LIST with the first character of IFS and assume
|
||||
that a later split will recreate the list.
|
||||
To avoid issues with a non-whitespace separator producing spurious empty
|
||||
words if the last character of one of the words in LIST is that same
|
||||
separator, we split LIST and join each resultant word with SEP. We remove
|
||||
quoted nulls from the result because the callers expect them not to
|
||||
be present before another word splitting pass.
|
||||
This does not use FLAGS yet, but could. */
|
||||
char *
|
||||
string_list_dollar_atstar (WORD_LIST *list, int quoted, int flags)
|
||||
{
|
||||
char *ret;
|
||||
WORD_LIST *l, *l2, *tl;
|
||||
#if defined (HANDLE_MULTIBYTE)
|
||||
# if defined (__GNUC__)
|
||||
char sep[MB_CUR_MAX + 1];
|
||||
# else
|
||||
char *sep = 0;
|
||||
# endif
|
||||
#else
|
||||
char sep[2];
|
||||
#endif
|
||||
|
||||
if (list == 0)
|
||||
return (NULL);
|
||||
|
||||
#if defined (HANDLE_MULTIBYTE)
|
||||
# if !defined (__GNUC__)
|
||||
sep = (char *)xmalloc (locale_mb_cur_max + 1);
|
||||
# endif /* !__GNUC__ */
|
||||
if (ifs_firstc_len == 1)
|
||||
{
|
||||
sep[0] = ifs_firstc[0];
|
||||
sep[1] = '\0';
|
||||
}
|
||||
else
|
||||
{
|
||||
memcpy (sep, ifs_firstc, ifs_firstc_len);
|
||||
sep[ifs_firstc_len] = '\0';
|
||||
}
|
||||
#else
|
||||
sep[0] = ifs_firstc;
|
||||
sep[1] = '\0';
|
||||
#endif
|
||||
|
||||
/* We want to split non-empty arguments, but preserve null arguments.
|
||||
Preserving the null positional parameters and following them with SEP
|
||||
is backwards compatible. */
|
||||
/* If we want to remove (unquoted) null arguments, so $* is like $1 $2 ...,
|
||||
then use PF_STRINGBEG and PF_STRINGEND to inhibit separators after the
|
||||
first and last arguments so they are joined to whatever comes before
|
||||
and after the $* and simply skip over them otherwise. */
|
||||
|
||||
/* Pre-process the list */
|
||||
/* We arrange for null arguments to be preserved. */
|
||||
for (l2 = tl = NULL, l = list; l; l = l->next)
|
||||
{
|
||||
char *new_string;
|
||||
WORD_DESC *new_word;
|
||||
|
||||
new_word = copy_word (l->word);
|
||||
new_word->flags |= W_SPLITONLY;
|
||||
if (l->word->word[0] == '\0')
|
||||
new_word->flags |= W_SAWQUOTEDNULL;
|
||||
|
||||
if (l2 == NULL)
|
||||
l2 = tl = make_word_list (new_word, l2);
|
||||
else
|
||||
{
|
||||
tl->next = make_word_list (new_word, (WORD_LIST *)NULL);
|
||||
tl = tl->next;
|
||||
}
|
||||
}
|
||||
|
||||
/* XXX - this turns words that look like quoted nulls into "", which we
|
||||
don't want here. */
|
||||
l = word_list_split (l2); /* pre-split, preserving empty arguments */
|
||||
|
||||
/* We want to turn words that are QUOTED_NULL with W_HASQUOTEDNULL set in
|
||||
the word flags back into "" but leave every other $'\177' alone. */
|
||||
for (l2 = l; l2; l2 = l2->next)
|
||||
if (QUOTED_NULL (l2->word->word) && (l2->word->flags & W_HASQUOTEDNULL))
|
||||
{
|
||||
l2->word->word[0] = '\0';
|
||||
l2->word->flags &= ~W_HASQUOTEDNULL;
|
||||
}
|
||||
|
||||
list_quote_escapes (l);
|
||||
|
||||
ret = string_list_internal (l, sep, (flags & PF_STRINGEND) ? 1 : 0);
|
||||
#if defined (HANDLE_MULTIBYTE) && !defined (__GNUC__)
|
||||
free (sep);
|
||||
#endif
|
||||
|
||||
dispose_words (l2);
|
||||
dispose_words (l);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
/* Turn the positional parameters into a string, understanding quoting and
|
||||
the various subtleties of using the first character of $IFS as the
|
||||
separator. Calls string_list_dollar_at, string_list_dollar_star, and
|
||||
@@ -3100,6 +3218,14 @@ string_list_pos_params (int pchar, WORD_LIST *list, int quoted, int pflags)
|
||||
ret = expand_no_split_dollar_star ? string_list_dollar_star (list, quoted, 0) : string_list_dollar_at (list, quoted, 0); /* Posix interp 888 */
|
||||
else if (pchar == '*' && quoted == 0 && (pflags & PF_ASSIGNRHS)) /* XXX */
|
||||
ret = expand_no_split_dollar_star ? string_list_dollar_star (list, quoted, 0) : string_list_dollar_at (list, quoted, 0); /* Posix interp 888 */
|
||||
else if (pchar == '*' && quoted == 0 && (pflags & PF_ASSIGNRHS) == 0 &&
|
||||
ifs_is_set && ifs_is_null == 0 &&
|
||||
#if defined (HANDLE_MULTIBYTE)
|
||||
spctabnl (ifs_firstc[0]) == 0)
|
||||
#else
|
||||
spctabnl (ifs_firstc) == 0)
|
||||
#endif
|
||||
ret = string_list_dollar_atstar (list, quoted, 0);
|
||||
else if (pchar == '*')
|
||||
{
|
||||
/* Even when unquoted, string_list_dollar_star does the right thing
|
||||
@@ -3123,14 +3249,16 @@ string_list_pos_params (int pchar, WORD_LIST *list, int quoted, int pflags)
|
||||
that quotes the escapes. We could use string_list_internal with " "
|
||||
as the second argument. */
|
||||
ret = string_list_dollar_at (list, quoted, pflags); /* Posix interp 888 */
|
||||
else if (pchar == '@')
|
||||
#if 0
|
||||
/* XXX - param_expand uses string_list_dollar_at() for this case. */
|
||||
/* string_list_dollar_at quotes CTLESC, even if quoted == 0 */
|
||||
ret = string_list_dollar_at (list, quoted, 0);
|
||||
else if (pchar == '@' && quoted == 0 && (pflags & PF_ASSIGNRHS) == 0 &&
|
||||
ifs_is_set && ifs_is_null == 0 &&
|
||||
#if defined (HANDLE_MULTIBYTE)
|
||||
spctabnl (ifs_firstc[0]) == 0) /* separate these cases for now */
|
||||
#else
|
||||
ret = string_list_dollar_star (list, quoted, 0);
|
||||
spctabnl (ifs_firstc) == 0) /* separate these cases for now */
|
||||
#endif
|
||||
ret = string_list_dollar_atstar (list, quoted, 0);
|
||||
else if (pchar == '@')
|
||||
ret = string_list_dollar_star (list, quoted, 0);
|
||||
else
|
||||
ret = string_list ((quoted & (Q_HERE_DOCUMENT|Q_DOUBLE_QUOTES)) ? quote_list (list) : list);
|
||||
|
||||
@@ -3202,6 +3330,9 @@ list_string (char *string, char *separators, int flags)
|
||||
else if (*s == CTLNUL) xflags |= SX_NOESCCTLNUL;
|
||||
}
|
||||
|
||||
if (flags & W_SPLITONLY)
|
||||
xflags |= SX_NOCTLESC|SX_NOESCCTLNUL;
|
||||
|
||||
slen = 0;
|
||||
/* Remove sequences of whitespace at the beginning of STRING, as
|
||||
long as those characters appear in IFS. Do not do this if
|
||||
@@ -3242,7 +3373,7 @@ list_string (char *string, char *separators, int flags)
|
||||
want to preserve the quoted null character iff this is a quoted
|
||||
empty string; otherwise the quoted null characters are removed
|
||||
below. */
|
||||
if (QUOTED_NULL (current_word))
|
||||
if ((flags & W_SPLITONLY) == 0 && QUOTED_NULL (current_word))
|
||||
{
|
||||
t = alloc_word_desc ();
|
||||
t->word = make_quoted_char ('\0');
|
||||
@@ -3253,7 +3384,8 @@ list_string (char *string, char *separators, int flags)
|
||||
{
|
||||
/* If we have something, then add it regardless. However,
|
||||
perform quoted null character removal on the current word. */
|
||||
remove_quoted_nulls (current_word);
|
||||
if ((flags & W_SPLITONLY) == 0)
|
||||
remove_quoted_nulls (current_word);
|
||||
|
||||
/* We don't want to set the word flags based on the string contents
|
||||
here -- that's mostly for the parser -- so we just allocate a
|
||||
@@ -3271,6 +3403,15 @@ list_string (char *string, char *separators, int flags)
|
||||
if (current_word == 0 || current_word[0] == '\0')
|
||||
result->word->flags |= W_SAWQUOTEDNULL;
|
||||
}
|
||||
else if ((flags & W_SPLITONLY) && current_word[0] == '\0')
|
||||
{
|
||||
t = alloc_word_desc ();
|
||||
t->word = current_word;
|
||||
/* W_QUOTED for string_list_internal () */
|
||||
t->flags |= W_QUOTED|W_HASQUOTEDNULL;
|
||||
result = make_word_list (t, result);
|
||||
free_word = 0;
|
||||
}
|
||||
|
||||
/* If we're not doing sequences of separators in the traditional
|
||||
Bourne shell style, then add a quoted null argument. */
|
||||
@@ -7008,6 +7149,13 @@ uw_restore_errexit (void *eflag)
|
||||
set_shellopts ();
|
||||
}
|
||||
|
||||
static void
|
||||
uw_restore_verbose (void *vflag)
|
||||
{
|
||||
change_flag ('v', (intptr_t) vflag ? FLAG_ON : FLAG_OFF);
|
||||
set_shellopts ();
|
||||
}
|
||||
|
||||
/* Quote the output of nofork varsub command substitution in the way that the
|
||||
caller of function_substitute expects. The caller guarantees that STRING
|
||||
is non-null. This is equivalent to what read_comsub does to the output it
|
||||
@@ -7146,7 +7294,8 @@ function_substitute (char *string, int quoted, int flags)
|
||||
push_context (lambdafunc.name, 1, temporary_env); /* make local variables work */
|
||||
this_shell_function = &lambdafunc;
|
||||
|
||||
unwind_protect_int (verbose_flag);
|
||||
|
||||
add_unwind_protect (uw_restore_verbose, (void *) (intptr_t) verbose_flag);
|
||||
change_flag ('v', FLAG_OFF);
|
||||
|
||||
/* When inherit_errexit option is not enabled, command substitution does
|
||||
@@ -10821,6 +10970,20 @@ param_expand (char *string, size_t *sindex, int quoted,
|
||||
# endif
|
||||
/* Posix interp 888: not RHS, no splitting, IFS set to '' */
|
||||
temp = string_list_dollar_star (list, quoted, 0);
|
||||
else if (expand_no_split_dollar_star == 0 && (pflags & PF_ASSIGNRHS) == 0 &&
|
||||
ifs_is_set && ifs_is_null == 0 &&
|
||||
# if defined (HANDLE_MULTIBYTE)
|
||||
spctabnl (ifs_firstc[0]) == 0)
|
||||
# else
|
||||
spctabnl (ifs_firstc) == 0)
|
||||
# endif
|
||||
{
|
||||
int nflags;
|
||||
/* XXX - only if $# > 1? */
|
||||
nflags = (string[zindex+1] == '\0') ? PF_STRINGEND : 0;
|
||||
|
||||
temp = string_list_dollar_atstar (list, quoted, nflags);
|
||||
}
|
||||
else
|
||||
{
|
||||
temp = string_list_dollar_at (list, quoted, 0);
|
||||
@@ -10927,6 +11090,20 @@ param_expand (char *string, size_t *sindex, int quoted,
|
||||
else
|
||||
temp = string_list_dollar_at (list, quoted, pflags);
|
||||
}
|
||||
else if (quoted == 0 &&
|
||||
ifs_is_set && ifs_is_null == 0 &&
|
||||
#if defined (HANDLE_MULTIBYTE)
|
||||
spctabnl (ifs_firstc[0]) == 0)
|
||||
#else
|
||||
spctabnl (ifs_firstc) == 0)
|
||||
#endif
|
||||
{
|
||||
int nflags;
|
||||
/* XXX - only if $# > 1? */
|
||||
nflags = (string[zindex+1] == '\0') ? PF_STRINGEND : 0;
|
||||
|
||||
temp = string_list_dollar_atstar (list, quoted, pflags|nflags);
|
||||
}
|
||||
else
|
||||
temp = string_list_dollar_at (list, quoted, pflags);
|
||||
|
||||
@@ -11520,6 +11697,8 @@ add_string:
|
||||
if (temp)
|
||||
{
|
||||
istring = sub_append_string (temp, istring, &istring_index, &istring_size);
|
||||
if (istring_index > 0)
|
||||
pflags &= ~PF_STRINGBEG;
|
||||
temp = (char *)0;
|
||||
}
|
||||
|
||||
@@ -11694,6 +11873,8 @@ add_string:
|
||||
pflags |= PF_ASSIGNRHS;
|
||||
if (word->flags & W_COMPLETE)
|
||||
pflags |= PF_COMPLETE;
|
||||
if (istring_index == 0)
|
||||
pflags |= PF_STRINGBEG;
|
||||
|
||||
tword = param_expand (string, &sindex, quoted, &local_expanded,
|
||||
&temp_has_dollar_at, "ed_dollar_at,
|
||||
@@ -12554,7 +12735,7 @@ word_split (WORD_DESC *w, char *ifs_chars)
|
||||
char *xifs;
|
||||
|
||||
xifs = ((w->flags & W_QUOTED) || ifs_chars == 0) ? "" : ifs_chars;
|
||||
result = list_string (w->word, xifs, w->flags & W_QUOTED);
|
||||
result = list_string (w->word, xifs, w->flags & (W_QUOTED|W_SPLITONLY));
|
||||
}
|
||||
else
|
||||
result = (WORD_LIST *)NULL;
|
||||
|
||||
@@ -130,6 +130,11 @@ extern char *string_list_dollar_star (WORD_LIST *, int, int);
|
||||
/* Expand $@ into a single string, obeying POSIX rules. */
|
||||
extern char *string_list_dollar_at (WORD_LIST *, int, int);
|
||||
|
||||
/* A special function for expanding unquoted $* and $@ in a context where
|
||||
word splitting will be performed using a non-whitespace separator and
|
||||
the positional parameters potentially contain that separator. */
|
||||
extern char *string_list_dollar_atstar (WORD_LIST *, int, int);
|
||||
|
||||
/* Turn the positional parameters into a string, understanding quoting and
|
||||
the various subtleties of using the first character of $IFS as the
|
||||
separator. Calls string_list_dollar_at, string_list_dollar_star, and
|
||||
|
||||
@@ -266,6 +266,11 @@ test_runsub ./dollar-at-star9.sub
|
||||
test_runsub ./dollar-at-star10.sub
|
||||
test_runsub ./dollar-at-star11.sub
|
||||
|
||||
# tests for unquoted expansions of $* and $@ when word splitting will take
|
||||
# place and the separator is not whitespace
|
||||
test_runsub ./dollar-at-star12.sub
|
||||
test_runsub ./dollar-at-star13.sub
|
||||
|
||||
# tests for special expansion of "$*" and "${array[*]}" when used with other
|
||||
# expansions -- bugs through bash-2.05b
|
||||
test_runsub ./dollar-star1.sub
|
||||
|
||||
@@ -0,0 +1,60 @@
|
||||
OIFS=$IFS
|
||||
|
||||
set -- a b+ c +d e; IFS=+;
|
||||
|
||||
# these should be the same
|
||||
printf '<%s> ' $1 $2 $3 $4 $5; echo
|
||||
printf '<%s> ' $* ; echo
|
||||
printf '<%s> ' $@ ; echo
|
||||
|
||||
set -- 'b+' 'c'
|
||||
|
||||
# these should be the same
|
||||
echo =====
|
||||
printf '<%s> ' $1 $2 $3; echo
|
||||
printf '<%s> ' $* ; echo
|
||||
printf '<%s> ' $@ ; echo
|
||||
printf '<%s> ' ${*} ; echo
|
||||
printf '<%s> ' ${@} ; echo
|
||||
|
||||
# these should be the same
|
||||
echo =====
|
||||
set -- a 'b+c' '+d+e' 'f+g+' h
|
||||
|
||||
printf '<%s> ' $* ; echo
|
||||
printf '<%s> ' $@ ; echo
|
||||
|
||||
echo =====
|
||||
array=(a 'b+' c '+d' e)
|
||||
printf '<%s> ' ${array[0]} ${array[1]} ${array[2]} ${array[3]} ${array[4]}; echo
|
||||
printf '<%s> ' ${array[*]} ; echo
|
||||
printf '<%s> ' ${array[@]} ; echo
|
||||
unset array
|
||||
|
||||
array=(a 'b+c' '+d+e' 'f+g+' h)
|
||||
printf '<%s> ' ${array[0]} ${array[1]} ${array[2]} ${array[3]} ${array[4]}; echo
|
||||
printf '<%s> ' ${array[*]} ; echo
|
||||
printf '<%s> ' ${array[@]} ; echo
|
||||
unset array
|
||||
|
||||
echo =====
|
||||
set -- $'\001\177+' 'b+' c; IFS=+;
|
||||
|
||||
recho $1 $2 $3
|
||||
recho $*
|
||||
|
||||
set -- $'\177+' 'b+' c; IFS=+;
|
||||
recho $1 $2 $3
|
||||
recho $*
|
||||
|
||||
set -- $'a\177+' 'b+' c; IFS=+;
|
||||
recho $1 $2 $3
|
||||
recho $*
|
||||
|
||||
set -- $'\001+' b+ c; IFS=+;
|
||||
recho $1 $2 $3
|
||||
recho $*
|
||||
|
||||
set -- $'a\001+' b+ c; IFS=+;
|
||||
recho $1 $2 $3
|
||||
recho $*
|
||||
@@ -0,0 +1,41 @@
|
||||
# from back in 2018 on dash@vger.kernel.org
|
||||
|
||||
# IFS the default, these arguments should not be split
|
||||
set -- , ,
|
||||
IFS=,
|
||||
recho $@
|
||||
recho $*
|
||||
recho ${@}
|
||||
recho ${*}
|
||||
|
||||
set -- $@
|
||||
recho $#
|
||||
|
||||
# IFS already set, these arguments should be split
|
||||
set -- , ,
|
||||
recho $@
|
||||
recho $*
|
||||
recho ${@}
|
||||
recho ${*}
|
||||
|
||||
set -- $@
|
||||
recho $#
|
||||
|
||||
# need the space to be preserved
|
||||
set -- , ' ,'
|
||||
IFS=,
|
||||
recho $@
|
||||
recho $*
|
||||
|
||||
set -- $@
|
||||
recho $#
|
||||
|
||||
# need the , to preserve an empty argument before the trailing char
|
||||
# from back in 2018 on dash@vger.kernel.org
|
||||
set -- ',' ','
|
||||
|
||||
recho ${@}a
|
||||
recho ${*}b
|
||||
|
||||
recho a${@}
|
||||
recho b${*}
|
||||
+91
-10
@@ -247,22 +247,18 @@ argv[3] = <'c'>
|
||||
argv[1] = <>
|
||||
argv[2] = <a>
|
||||
argv[3] = <>
|
||||
argv[4] = <>
|
||||
argv[5] = <b>
|
||||
argv[6] = <>
|
||||
argv[7] = <>
|
||||
argv[8] = <c>
|
||||
argv[4] = <b>
|
||||
argv[5] = <>
|
||||
argv[6] = <c>
|
||||
argv[1] = <'a'>
|
||||
argv[2] = <'b'>
|
||||
argv[3] = <'c'>
|
||||
argv[1] = <>
|
||||
argv[2] = <a>
|
||||
argv[3] = <>
|
||||
argv[4] = <>
|
||||
argv[5] = <b>
|
||||
argv[6] = <>
|
||||
argv[7] = <>
|
||||
argv[8] = <c>
|
||||
argv[4] = <b>
|
||||
argv[5] = <>
|
||||
argv[6] = <c>
|
||||
argv[1] = <'a'>
|
||||
argv[2] = <'b'>
|
||||
argv[3] = <'c'>
|
||||
@@ -477,6 +473,91 @@ argv[1] = <>
|
||||
argv[1] = <>
|
||||
argv[1] = </>
|
||||
1:1
|
||||
dollar-at-star12.sub
|
||||
<a> <b> <c> <> <d> <e>
|
||||
<a> <b> <c> <> <d> <e>
|
||||
<a> <b> <c> <> <d> <e>
|
||||
=====
|
||||
<b> <c>
|
||||
<b> <c>
|
||||
<b> <c>
|
||||
<b> <c>
|
||||
<b> <c>
|
||||
=====
|
||||
<a> <b> <c> <> <d> <e> <f> <g> <h>
|
||||
<a> <b> <c> <> <d> <e> <f> <g> <h>
|
||||
=====
|
||||
<a> <b> <c> <> <d> <e>
|
||||
<a> <b> <c> <> <d> <e>
|
||||
<a> <b> <c> <> <d> <e>
|
||||
<a> <b> <c> <> <d> <e> <f> <g> <h>
|
||||
<a> <b> <c> <> <d> <e> <f> <g> <h>
|
||||
<a> <b> <c> <> <d> <e> <f> <g> <h>
|
||||
=====
|
||||
argv[1] = <^A^?>
|
||||
argv[2] = <b>
|
||||
argv[3] = <c>
|
||||
argv[1] = <^A^?>
|
||||
argv[2] = <b>
|
||||
argv[3] = <c>
|
||||
argv[1] = <^?>
|
||||
argv[2] = <b>
|
||||
argv[3] = <c>
|
||||
argv[1] = <^?>
|
||||
argv[2] = <b>
|
||||
argv[3] = <c>
|
||||
argv[1] = <a^?>
|
||||
argv[2] = <b>
|
||||
argv[3] = <c>
|
||||
argv[1] = <a^?>
|
||||
argv[2] = <b>
|
||||
argv[3] = <c>
|
||||
argv[1] = <^A>
|
||||
argv[2] = <b>
|
||||
argv[3] = <c>
|
||||
argv[1] = <^A>
|
||||
argv[2] = <b>
|
||||
argv[3] = <c>
|
||||
argv[1] = <a^A>
|
||||
argv[2] = <b>
|
||||
argv[3] = <c>
|
||||
argv[1] = <a^A>
|
||||
argv[2] = <b>
|
||||
argv[3] = <c>
|
||||
dollar-at-star13.sub
|
||||
argv[1] = <>
|
||||
argv[2] = <>
|
||||
argv[1] = <>
|
||||
argv[2] = <>
|
||||
argv[1] = <>
|
||||
argv[2] = <>
|
||||
argv[1] = <>
|
||||
argv[2] = <>
|
||||
argv[1] = <2>
|
||||
argv[1] = <>
|
||||
argv[2] = <>
|
||||
argv[1] = <>
|
||||
argv[2] = <>
|
||||
argv[1] = <>
|
||||
argv[2] = <>
|
||||
argv[1] = <>
|
||||
argv[2] = <>
|
||||
argv[1] = <2>
|
||||
argv[1] = <>
|
||||
argv[2] = < >
|
||||
argv[1] = <>
|
||||
argv[2] = < >
|
||||
argv[1] = <2>
|
||||
argv[1] = <>
|
||||
argv[2] = <>
|
||||
argv[3] = <a>
|
||||
argv[1] = <>
|
||||
argv[2] = <>
|
||||
argv[3] = <b>
|
||||
argv[1] = <a>
|
||||
argv[2] = <>
|
||||
argv[1] = <b>
|
||||
argv[2] = <>
|
||||
dollar-star1.sub
|
||||
xa|xb|xc
|
||||
xa|xb|xc
|
||||
|
||||
+1
-1
@@ -1,4 +1,4 @@
|
||||
unset GROUPS UID 2>/dev/null
|
||||
unset GROUPS 2>/dev/null
|
||||
|
||||
${THIS_SH} ./test.tests >${BASH_TSTOUT} 2>&1
|
||||
diff ${BASH_TSTOUT} test.right && rm -f ${BASH_TSTOUT}
|
||||
|
||||
+8
-5
@@ -888,8 +888,8 @@ adjust_shell_level (int change)
|
||||
new_level[3] = '\0';
|
||||
}
|
||||
|
||||
temp_var = bind_variable ("SHLVL", new_level, 0);
|
||||
set_auto_export (temp_var);
|
||||
if (temp_var = bind_variable ("SHLVL", new_level, 0))
|
||||
set_auto_export (temp_var);
|
||||
}
|
||||
|
||||
static void
|
||||
@@ -929,7 +929,8 @@ set_pwd (void)
|
||||
if (posixly_correct && current_dir)
|
||||
{
|
||||
temp_var = bind_variable ("PWD", current_dir, 0);
|
||||
set_auto_export (temp_var);
|
||||
if (temp_var)
|
||||
set_auto_export (temp_var);
|
||||
}
|
||||
free (current_dir);
|
||||
}
|
||||
@@ -938,7 +939,8 @@ set_pwd (void)
|
||||
{
|
||||
set_working_directory (home_string);
|
||||
temp_var = bind_variable ("PWD", home_string, 0);
|
||||
set_auto_export (temp_var);
|
||||
if (temp_var)
|
||||
set_auto_export (temp_var);
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -946,7 +948,8 @@ set_pwd (void)
|
||||
if (temp_string)
|
||||
{
|
||||
temp_var = bind_variable ("PWD", temp_string, 0);
|
||||
set_auto_export (temp_var);
|
||||
if (temp_var)
|
||||
set_auto_export (temp_var);
|
||||
free (temp_string);
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user