From: Chet Ramey Date: Wed, 20 May 2026 14:18:32 +0000 (-0400) Subject: fixed a bug with expanding unquoted $* when the separator is not whitespace and one... X-Git-Url: http://git.ipfire.org/gitweb/index.cgi?a=commitdiff_plain;h=2d4ba0c618584d3554165b9484d921ec8c4e4523;p=thirdparty%2Fbash.git 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 --- diff --git a/CWRU/CWRU.chlog b/CWRU/CWRU.chlog index db0e4d06..eb8ab19a 100644 --- a/CWRU/CWRU.chlog +++ b/CWRU/CWRU.chlog @@ -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 + + 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 + +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 diff --git a/MANIFEST b/MANIFEST index 40a8739f..7a2d3aa6 100644 --- a/MANIFEST +++ b/MANIFEST @@ -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 diff --git a/arrayfunc.c b/arrayfunc.c index 3f34370e..db70eb0c 100644 --- a/arrayfunc.c +++ b/arrayfunc.c @@ -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); diff --git a/command.h b/command.h index 1c565cf7..8ed115c3 100644 --- a/command.h +++ b/command.h @@ -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 &' */ diff --git a/examples/loadables/Makefile.in b/examples/loadables/Makefile.in index c01b50c6..d23dc69f 100644 --- a/examples/loadables/Makefile.in +++ b/examples/loadables/Makefile.in @@ -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 diff --git a/examples/loadables/cut.c b/examples/loadables/cut.c index 2b9cce5c..b69e7bd0 100644 --- a/examples/loadables/cut.c +++ b/examples/loadables/cut.c @@ -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; diff --git a/execute_cmd.c b/execute_cmd.c index ea361106..9cfe5b35 100644 --- a/execute_cmd.c +++ b/execute_cmd.c @@ -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 diff --git a/subst.c b/subst.c index 121a7a3c..e4dffd34 100644 --- a/subst.c +++ b/subst.c @@ -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; diff --git a/subst.h b/subst.h index b6f0df9d..158fd8a7 100644 --- a/subst.h +++ b/subst.h @@ -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 diff --git a/tests/dollar-at-star b/tests/dollar-at-star index ff575895..efcd9ab7 100755 --- a/tests/dollar-at-star +++ b/tests/dollar-at-star @@ -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 diff --git a/tests/dollar-at-star12.sub b/tests/dollar-at-star12.sub new file mode 100644 index 00000000..dfebef98 --- /dev/null +++ b/tests/dollar-at-star12.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 $* diff --git a/tests/dollar-at-star13.sub b/tests/dollar-at-star13.sub new file mode 100644 index 00000000..b024c65c --- /dev/null +++ b/tests/dollar-at-star13.sub @@ -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${*} diff --git a/tests/dollar.right b/tests/dollar.right index b3b142d9..2c7c01f9 100644 --- a/tests/dollar.right +++ b/tests/dollar.right @@ -247,22 +247,18 @@ argv[3] = <'c'> argv[1] = <> argv[2] = argv[3] = <> -argv[4] = <> -argv[5] = -argv[6] = <> -argv[7] = <> -argv[8] = +argv[4] = +argv[5] = <> +argv[6] = argv[1] = <'a'> argv[2] = <'b'> argv[3] = <'c'> argv[1] = <> argv[2] = argv[3] = <> -argv[4] = <> -argv[5] = -argv[6] = <> -argv[7] = <> -argv[8] = +argv[4] = +argv[5] = <> +argv[6] = 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 + <> + <> + <> +===== + + + + + +===== + <> + <> +===== + <> + <> + <> + <> + <> + <> +===== +argv[1] = <^A^?> +argv[2] = +argv[3] = +argv[1] = <^A^?> +argv[2] = +argv[3] = +argv[1] = <^?> +argv[2] = +argv[3] = +argv[1] = <^?> +argv[2] = +argv[3] = +argv[1] = +argv[2] = +argv[3] = +argv[1] = +argv[2] = +argv[3] = +argv[1] = <^A> +argv[2] = +argv[3] = +argv[1] = <^A> +argv[2] = +argv[3] = +argv[1] = +argv[2] = +argv[3] = +argv[1] = +argv[2] = +argv[3] = + 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] = +argv[1] = <> +argv[2] = <> +argv[3] = +argv[1] = +argv[2] = <> +argv[1] = +argv[2] = <> dollar-star1.sub xa|xb|xc xa|xb|xc diff --git a/tests/run-test b/tests/run-test index d68791ca..03c9bd29 100644 --- a/tests/run-test +++ b/tests/run-test @@ -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} diff --git a/variables.c b/variables.c index dc74855b..1d316a1e 100644 --- a/variables.c +++ b/variables.c @@ -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); } }