From: Chet Ramey Date: Tue, 14 Dec 2021 19:18:00 +0000 (-0500) Subject: fix for posix semantics for the := word expansion when assigning array elements X-Git-Url: http://git.ipfire.org/?a=commitdiff_plain;h=5f2dd5ff95a0bc46a6bb167d67f5627c995fb4d5;p=thirdparty%2Fbash.git fix for posix semantics for the := word expansion when assigning array elements --- diff --git a/CWRU/CWRU.chlog b/CWRU/CWRU.chlog index 9995e6167..570502c09 100644 --- a/CWRU/CWRU.chlog +++ b/CWRU/CWRU.chlog @@ -2640,3 +2640,25 @@ variables.c doc/Makefile.in - changes to allow man pages that include others (.so FN) to be built outside the source tree + + 12/13 + ----- +arrayfunc.c + - assign_array_element_internal: take an additional argument: char **NVALP. + If non-null, it gets the value eventually assigned to the array + element + - assign_array_element: take an additional NVALP argument; pass it to + assign_array_element_internal + +arrayfunc.h + - assign_array_element: new extern function declaration + +{subst,variables}.c,builtins/{common.c,declare.def} + - assign_array_element: change callers + +subst.c + - parameter_brace_expand_rhs: for the ${param:=value}, use the value + returned by assign_array_element in NVALP as the return value, since + it's the value ultimately assigned to the variable after possible + modification (e.g., arithmetic evaluation). Reported by + oguzismailuysal@gmail.com after flawed fix applied 11/16 diff --git a/MANIFEST b/MANIFEST index 8755145b9..0e1bd5710 100644 --- a/MANIFEST +++ b/MANIFEST @@ -933,6 +933,7 @@ tests/array26.sub f tests/array27.sub f tests/array28.sub f tests/array29.sub f +tests/array30.sub f tests/array-at-star f tests/array2.right f tests/assoc.tests f diff --git a/array.h b/array.h index 24d2b632d..4214e8b41 100644 --- a/array.h +++ b/array.h @@ -27,8 +27,6 @@ typedef intmax_t arrayind_t; -enum atype {array_indexed, array_assoc}; /* not used */ - typedef struct array { arrayind_t max_index; arrayind_t num_elements; diff --git a/arrayfunc.c b/arrayfunc.c index 816560524..420547cf7 100644 --- a/arrayfunc.c +++ b/arrayfunc.c @@ -53,7 +53,7 @@ int assoc_expand_once = 0; int array_expand_once = 0; static SHELL_VAR *bind_array_var_internal PARAMS((SHELL_VAR *, arrayind_t, char *, char *, int)); -static SHELL_VAR *assign_array_element_internal PARAMS((SHELL_VAR *, char *, char *, char *, int, char *, int)); +static SHELL_VAR *assign_array_element_internal PARAMS((SHELL_VAR *, char *, char *, char *, int, char *, int, char **)); static void assign_assoc_from_kvlist PARAMS((SHELL_VAR *, WORD_LIST *, HASH_TABLE *, int)); @@ -322,9 +322,10 @@ bind_assoc_variable (entry, name, key, value, flags) assign VALUE to that array element by calling bind_array_variable(). Flags are ASS_ assignment flags */ SHELL_VAR * -assign_array_element (name, value, flags) +assign_array_element (name, value, flags, nvalp) char *name, *value; int flags; + char **nvalp; { char *sub, *vname; int sublen, isassoc; @@ -352,14 +353,14 @@ assign_array_element (name, value, flags) return ((SHELL_VAR *)NULL); } - entry = assign_array_element_internal (entry, name, vname, sub, sublen, value, flags); + entry = assign_array_element_internal (entry, name, vname, sub, sublen, value, flags, nvalp); free (vname); return entry; } static SHELL_VAR * -assign_array_element_internal (entry, name, vname, sub, sublen, value, flags) +assign_array_element_internal (entry, name, vname, sub, sublen, value, flags, nvalp) SHELL_VAR *entry; char *name; /* only used for error messages */ char *vname; @@ -367,9 +368,11 @@ assign_array_element_internal (entry, name, vname, sub, sublen, value, flags) int sublen; char *value; int flags; + char **nvalp; { char *akey; arrayind_t ind; + char *newval; if (entry && assoc_p (entry)) { @@ -386,6 +389,7 @@ assign_array_element_internal (entry, name, vname, sub, sublen, value, flags) return ((SHELL_VAR *)NULL); } entry = bind_assoc_variable (entry, vname, akey, value, flags); + newval = entry ? assoc_reference (assoc_cell (entry), akey) : 0; } else { @@ -399,8 +403,14 @@ assign_array_element_internal (entry, name, vname, sub, sublen, value, flags) return ((SHELL_VAR *)NULL); } entry = bind_array_variable (vname, ind, value, flags); + newval = entry ? array_reference (array_cell (entry), ind) : 0; } + /* If the caller asks, return the (possibly modified) final value assigned. + This saves subseqent lookups. */ + if (nvalp) + *nvalp = newval; + return (entry); } diff --git a/arrayfunc.h b/arrayfunc.h index 87569e434..136313d11 100644 --- a/arrayfunc.h +++ b/arrayfunc.h @@ -56,7 +56,7 @@ extern char *make_array_variable_value PARAMS((SHELL_VAR *, arrayind_t, char *, extern SHELL_VAR *bind_array_variable PARAMS((char *, arrayind_t, char *, int)); extern SHELL_VAR *bind_array_element PARAMS((SHELL_VAR *, arrayind_t, char *, int)); -extern SHELL_VAR *assign_array_element PARAMS((char *, char *, int)); +extern SHELL_VAR *assign_array_element PARAMS((char *, char *, int, char **)); extern SHELL_VAR *bind_assoc_variable PARAMS((SHELL_VAR *, char *, char *, char *, int)); diff --git a/builtins/common.c b/builtins/common.c index 563a06262..ad97874df 100644 --- a/builtins/common.c +++ b/builtins/common.c @@ -1004,7 +1004,7 @@ builtin_bind_variable (name, value, flags) if (valid_array_reference (name, vflags) == 0) v = bind_variable (name, value, flags); else - v = assign_array_element (name, value, bindflags); + v = assign_array_element (name, value, bindflags, (char **)0); #else /* !ARRAY_VARS */ v = bind_variable (name, value, flags); #endif /* !ARRAY_VARS */ diff --git a/builtins/declare.def b/builtins/declare.def index 58413853d..c8535ab0e 100644 --- a/builtins/declare.def +++ b/builtins/declare.def @@ -949,7 +949,7 @@ restart_new_var_name: local_aflags = aflags&ASS_APPEND; local_aflags |= assoc_noexpand ? ASS_NOEXPAND : 0; local_aflags |= ASS_ALLOWALLSUB; /* allow declare a[@]=at */ - var = assign_array_element (name, value, local_aflags); /* XXX - not aflags */ + var = assign_array_element (name, value, local_aflags, (char **)0); /* XXX - not aflags */ *subscript_start = '\0'; if (var == 0) /* some kind of assignment error */ { diff --git a/doc/bash.1 b/doc/bash.1 index 1cf056cc2..43fc45303 100644 --- a/doc/bash.1 +++ b/doc/bash.1 @@ -281,7 +281,7 @@ An \fIinteractive\fP shell is one started without non-option arguments (unless \fB\-s\fP is specified) and without the .B \-c -option +option, whose standard input and error are both connected to terminals (as determined by .IR isatty (3)), diff --git a/doc/bashref.texi b/doc/bashref.texi index ec538a384..f831ff049 100644 --- a/doc/bashref.texi +++ b/doc/bashref.texi @@ -7091,8 +7091,9 @@ the same, but the effective user id is not reset. @subsection What is an Interactive Shell? An interactive shell -is one started without non-option arguments, unless @option{-s} is -specified, without specifying the @option{-c} option, and +is one started without non-option arguments +(unless @option{-s} is specified) +and without specifying the @option{-c} option, whose input and error output are both connected to terminals (as determined by @code{isatty(3)}), or one started with the @option{-i} option. diff --git a/subst.c b/subst.c index 20755b6fe..7ba7ede32 100644 --- a/subst.c +++ b/subst.c @@ -3308,7 +3308,7 @@ do_assignment_internal (word, expand) ASSIGN_RETURN (0); } aflags |= ASS_ALLOWALLSUB; /* allow a[@]=value for existing associative arrays */ - entry = assign_array_element (name, value, aflags); + entry = assign_array_element (name, value, aflags, (char **)0); if (entry == 0) ASSIGN_RETURN (0); } @@ -7227,8 +7227,8 @@ parameter_brace_expand_rhs (name, value, op, quoted, pflags, qdollaratp, hasdoll { WORD_DESC *w; WORD_LIST *l, *tl; - char *t, *t1, *temp, *vname; - int l_hasdollat, sindex; + char *t, *t1, *temp, *vname, *newval; + int l_hasdollat, sindex, arrayref; SHELL_VAR *v; /*itrace("parameter_brace_expand_rhs: %s:%s pflags = %d", name, value, pflags);*/ @@ -7374,9 +7374,13 @@ parameter_brace_expand_rhs (name, value, op, quoted, pflags, qdollaratp, hasdoll } } + arrayref = 0; #if defined (ARRAY_VARS) if (valid_array_reference (vname, 0)) - v = assign_array_element (vname, t1, 0); + { + v = assign_array_element (vname, t1, ASS_ALLOWALLSUB, &newval); + arrayref = 1; + } else #endif /* ARRAY_VARS */ v = bind_variable (vname, t1, 0); @@ -7399,16 +7403,20 @@ parameter_brace_expand_rhs (name, value, op, quoted, pflags, qdollaratp, hasdoll stupidly_hack_special_variables (vname); - if (vname != name) - free (vname); - /* "In all cases, the final value of parameter shall be substituted." */ if (shell_compatibility_level > 51) { FREE (t1); +#if defined (ARRAY_VARS) + t1 = arrayref ? newval : get_variable_value (v); +#else t1 = value_cell (v); +#endif } + if (vname != name) + free (vname); + /* From Posix group discussion Feb-March 2010. Issue 7 0000221 */ /* If we are double-quoted or if we are not going to be performing word diff --git a/tests/array.right b/tests/array.right index 939d6a2a6..62278852a 100644 --- a/tests/array.right +++ b/tests/array.right @@ -773,3 +773,15 @@ declare -A foo=([v]=$'\001\001\001\001' ) declare -A foo=([v]=$'\001\001\001\001' ) declare -A foo=([$'\001']=$'ab\001c' ) declare -A foo=([$'\001']=$'ab\001c' ) +foo +declare -a a=([42]="foo") +foo +declare -a a=([42]="foo") +7 +declare -ai a=([42]="7") +42 +declare -ai a=([42]="42") +FOO +declare -Au A=([Darwin]="FOO" ) +FOO +declare -Au A=(["@"]="FOO" ) diff --git a/tests/array.tests b/tests/array.tests index 74d148212..d0bb08b74 100644 --- a/tests/array.tests +++ b/tests/array.tests @@ -426,3 +426,4 @@ ${THIS_SH} ./array26.sub ${THIS_SH} ./array27.sub ${THIS_SH} ./array28.sub ${THIS_SH} ./array29.sub +${THIS_SH} ./array30.sub diff --git a/tests/array30.sub b/tests/array30.sub new file mode 100644 index 000000000..14f97980c --- /dev/null +++ b/tests/array30.sub @@ -0,0 +1,46 @@ +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program. If not, see . +# +declare -a a +a=() + +echo ${a[42]=foo} +declare -p a + +a=() +echo ${a[$(echo 42)]=foo} +declare -p a + +unset a + +declare -ai a +a=() +echo ${a[42]=4+3} +declare -p a + +a=() +echo ${a[$(echo 42)]=42} +declare -p a + +unset a + +declare -A A +declare -u A +A=() +echo ${A[$(echo Darwin)]=foo} + +declare -p A +A=() + +echo ${A[@]:=foo} +declare -p A diff --git a/tests/exp.right b/tests/exp.right index b6b3747d6..60241a1db 100644 --- a/tests/exp.right +++ b/tests/exp.right @@ -409,3 +409,11 @@ cdefg abcdefg abcde abcdefg +foo +declare -- a="foo" +7 +declare -i a="7" +42 +declare -- a="42" +FOO +declare -u A="FOO" diff --git a/tests/exp.tests b/tests/exp.tests index 3e69db4fa..61a39d3b4 100644 --- a/tests/exp.tests +++ b/tests/exp.tests @@ -423,3 +423,4 @@ ${THIS_SH} ./exp9.sub ${THIS_SH} ./exp10.sub ${THIS_SH} ./exp11.sub ${THIS_SH} ./exp12.sub +${THIS_SH} ./exp13.sub diff --git a/tests/exp13.sub b/tests/exp13.sub new file mode 100644 index 000000000..80e14635a --- /dev/null +++ b/tests/exp13.sub @@ -0,0 +1,34 @@ +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program. If not, see . +# +unset a + +echo ${a:=foo} +declare -p a + +unset a + +declare -i a +echo ${a:=4+3} +declare -p a + +unset a +echo ${a:=42} +declare -p a + +unset a +declare -u A +A= +echo ${A:=foo} + +declare -p A diff --git a/variables.c b/variables.c index b5bdec201..65f5ce540 100644 --- a/variables.c +++ b/variables.c @@ -2495,7 +2495,7 @@ get_variable_value (var) /* Return the string value of a variable. Return NULL if the variable doesn't exist. Don't cons a new string. This is a potential memory leak if the variable is found in the temporary environment, but doesn't - leak in practice. Since functions and variables have separate name + leak in practice. Since functions and variables have separate name spaces, returns NULL if var_name is a shell function only. */ char * get_string_value (var_name) @@ -3111,7 +3111,7 @@ bind_variable_internal (name, value, table, hflags, aflags) assign_array_element will eventually do it itself based on newval and aflags. */ - entry = assign_array_element (newval, value, aflags|ASS_NAMEREF); + entry = assign_array_element (newval, value, aflags|ASS_NAMEREF, (char **)0); if (entry == 0) return entry; } @@ -3268,7 +3268,7 @@ bind_variable (name, value, flags) return (bind_variable_internal (nv->name, value, nvc->table, 0, flags)); #if defined (ARRAY_VARS) else if (valid_array_reference (nameref_cell (nv), 0)) - return (assign_array_element (nameref_cell (nv), value, flags)); + return (assign_array_element (nameref_cell (nv), value, flags, (char **)0)); else #endif return (bind_variable_internal (nameref_cell (nv), value, nvc->table, 0, flags)); @@ -3433,7 +3433,7 @@ bind_int_variable (lhs, rhs, flags) #if defined (ARRAY_VARS) if (isarr) - v = assign_array_element (lhs, rhs, flags); + v = assign_array_element (lhs, rhs, flags, (char **)0); else if (implicitarray) v = bind_array_variable (lhs, 0, rhs, 0); /* XXX - check on flags */ else