]> git.ipfire.org Git - thirdparty/bash.git/commitdiff
fix for posix semantics for the := word expansion when assigning array elements
authorChet Ramey <chet.ramey@case.edu>
Tue, 14 Dec 2021 19:18:00 +0000 (14:18 -0500)
committerChet Ramey <chet.ramey@case.edu>
Tue, 14 Dec 2021 19:18:00 +0000 (14:18 -0500)
17 files changed:
CWRU/CWRU.chlog
MANIFEST
array.h
arrayfunc.c
arrayfunc.h
builtins/common.c
builtins/declare.def
doc/bash.1
doc/bashref.texi
subst.c
tests/array.right
tests/array.tests
tests/array30.sub [new file with mode: 0644]
tests/exp.right
tests/exp.tests
tests/exp13.sub [new file with mode: 0644]
variables.c

index 9995e6167370e7e1b25ec6dbeb24685fb0770b60..570502c094575043d91f0346ae3b5cd0618db9fb 100644 (file)
@@ -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
index 8755145b943146d6385786d2b9ba5c05a38f28fd..0e1bd57105bf5702b46cad272ba6f585fbde5b9d 100644 (file)
--- 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 24d2b632d9e80fbb3f9ac601658498a3e4251848..4214e8b414933d5b4fd950f5bedd9d75486a8645 100644 (file)
--- 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;
index 8165605242bc46b65a9cc25f9e7a9652f6612097..420547cf70223c7c48ba6d83057b0944e30cc9f4 100644 (file)
@@ -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);
 }
 
index 87569e43426477d6d73b0b912bc08bb6a7dcdfd4..136313d11ec7314250192c6e1600a87979030e9c 100644 (file)
@@ -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));
 
index 563a062627f5a8aa364a698b0152b9ca0e8d2898..ad97874df50da0d392d92c895b2c148307f0e26c 100644 (file)
@@ -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 */
index 58413853dfb0e0cdb2f4f818468063aca787db1c..c8535ab0ed5dcd26ffa18b403d892133b6849fd4 100644 (file)
@@ -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 */
            {
index 1cf056cc22a25721aea3dfb94de8d19162182db5..43fc4530302d0ce74cfd013c1cf1274fe884f9fb 100644 (file)
@@ -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)),
index ec538a38438cd36bb8d145044dea27334d7ee380..f831ff049ab86b37c16644c35832ec25c270ca27 100644 (file)
@@ -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 20755b6fecee0500abdf611893ffd0284963b504..7ba7ede324be1674d4cf3381e3f0226c4d29700c 100644 (file)
--- 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
index 939d6a2a6e8fa488c4fe00e4261a05a6a0352de9..62278852a6348bd9bee4024a44ebcb071468df7b 100644 (file)
@@ -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" )
index 74d1482129b6559d26d16be2a549f7e6a1535bdc..d0bb08b74466ef0e83e420faa8209c713aa7e0a3 100644 (file)
@@ -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 (file)
index 0000000..14f9798
--- /dev/null
@@ -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 <http://www.gnu.org/licenses/>.
+#
+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
index b6b3747d6258e4cf33e21b4d71a1cacde392f868..60241a1db02fe1363cc07b8df00cfbe185c91072 100644 (file)
@@ -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"
index 3e69db4fad60915274f40f90717a93b375211b2c..61a39d3b4c02a22c70e31ece3b4cd073432bfbff 100644 (file)
@@ -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 (file)
index 0000000..80e1463
--- /dev/null
@@ -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 <http://www.gnu.org/licenses/>.
+#
+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
index b5bdec201ad8ebff62ab717d2fcb8af09f8e5edf..65f5ce54070383a82811b4787d5f8ae3dfe26561 100644 (file)
@@ -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