]> git.ipfire.org Git - thirdparty/bash.git/commitdiff
commit bash-20200427 snapshot
authorChet Ramey <chet.ramey@case.edu>
Thu, 30 Apr 2020 15:59:39 +0000 (11:59 -0400)
committerChet Ramey <chet.ramey@case.edu>
Thu, 30 Apr 2020 15:59:39 +0000 (11:59 -0400)
13 files changed:
CWRU/CWRU.chlog
MANIFEST
arrayfunc.c
arrayfunc.h
patchlevel.h
subst.c
tests/assoc.right
tests/assoc11.sub
tests/run-varenv
tests/varenv.right
tests/varenv.tests
tests/varenv18.sub
tests/varenv19.sub [new file with mode: 0644]

index da7f24c60f3022571e087771e3140645d7615f31..051945bc0c3eae6670cb396eee32aa7b793f874a 100644 (file)
@@ -8211,3 +8211,46 @@ subst.c
          or at least -- (if we are inheriting), because  we don't want the
          declare to get skipped before we perform the word assignment. Fixes
          bug reported by Ross Goldberg <ross.goldberg@gmail.com>
+
+                                  4/28
+                                  ----
+subst.c
+       - expand_declaration_argument: new function, broke code that handles
+         compound assignments that are arguments to declaration commands out
+         of shell_expand_word_list into this function. No functional change
+         yet
+
+                                  4/29
+                                  ----
+subst.c
+       - expand_compound_assignment_word: helper function for
+         expand_declaration_argument: takes NAME[+]=( VALUE ), converts VALUE
+         to a list of words, then single-quotes each word and reconstructs
+         the original word. This assumes the result will go to
+         do_word_assignment, which will remove the single quotes
+       - expand_oneword: helper function for expand_compound_assignment_word,
+         takes VALUE and performs the splitting into words, and then the
+         expansion and single-quoting of each individual word. Indexed and
+         associative arrays take different code paths, because they undergo
+         different expansions and associative arrays need special handling to
+         avoid having to scan for the end of the subscript multiple times
+       - expand_declaration_argument: call expand_compound_assignment_word to
+         get the expansion-before-calling-builtins word expansion sequence
+         correct. Better fix for for bug report from Kevin Locke
+         <kevin@kevinlocke.name>,
+         https://savannah.gnu.org/support/index.php?109669
+
+arrayfunc.c
+       - quote_array_compound_word: take [IND]=VALUE and convert it to
+         ['IND']='VALUE'. Called by quote_compound_array_list for each word
+         in the list
+       - expand_and_quote_assoc_qword: take [KEY]=VALUE and convert it to
+         ['expanded-key']='expanded-value' (or VALUE to 'expanded-value').
+         Called by subst.c:expand_oneword() for each word in the list
+       - quote_compound_array_list: take a list of words and convert each
+         [IND]=VALUE to ['IND']='VALUE' (or just 'VALUE' if there is no
+         [IND]=). Used for indexed arrays
+
+arrayfunc.h
+       - expand_and_quote_assoc_word,quote_compound_array_list: new extern
+         declarations
index b31e8595dddbb3a708b0c8d6f9897e6c1c6ce980..1beac61f1f171d5285be05ce484d1adf2eb4cd10 100644 (file)
--- a/MANIFEST
+++ b/MANIFEST
@@ -1447,6 +1447,7 @@ tests/varenv15.in f
 tests/varenv16.sub     f
 tests/varenv17.sub     f
 tests/varenv18.sub     f
+tests/varenv19.sub     f
 tests/version          f
 tests/version.mini     f
 tests/vredir.tests     f
index 6d0aa66330d9978f695dda3dbf1b26e3ace74f39..5b0dc2f1e9b4fbf078f3a910ae3da8dd86cfd67d 100644 (file)
 
 #include "builtins/common.h"
 
+#ifndef LBRACK
+#  define LBRACK '['
+#  define RBRACK ']'
+#endif
+
 /* This variable means to not expand associative array subscripts more than
    once, when performing variable expansion. */
 int assoc_expand_once = 0;
@@ -54,6 +59,7 @@ static void assign_assoc_from_kvlist PARAMS((SHELL_VAR *, WORD_LIST *, HASH_TABL
 
 static char *quote_assign PARAMS((const char *));
 static void quote_array_assignment_chars PARAMS((WORD_LIST *));
+static char *quote_compound_array_word PARAMS((char *, int));
 static char *array_value_internal PARAMS((const char *, int, int, int *, arrayind_t *));
 
 /* Standard error message to use when encountering an invalid array subscript */
@@ -594,9 +600,11 @@ assign_assoc_from_kvlist (var, nlist, h, flags)
 #endif
      
 /* Callers ensure that VAR is not NULL. Associative array assignments have not
-   been expanded when this is called, so we don't have to scan through the
-   expanded subscript to find the ending bracket; indexed array assignments
-   have been expanded.
+   been expanded when this is called, or have been expanded once and single-
+   quoted, so we don't have to scan through an unquoted expanded subscript to
+   find the ending bracket; indexed array assignments have been expanded and
+   possibly single-quoted to prevent further expansion.
+
    If this is an associative array, we perform the assignments into NHASH and
    set NHASH to be the value of VAR after processing the assignments in NLIST */
 void
@@ -632,7 +640,7 @@ assign_compound_array_list (var, nlist, flags)
   last_ind = (a && (flags & ASS_APPEND)) ? array_max_index (a) + 1 : 0;
 
 #if ASSOC_KVPAIR_ASSIGNMENT
-  if (assoc_p (var) && nlist && (nlist->word->flags & W_ASSIGNMENT) == 0 && nlist->word->word[0] != '[')               /*]*/
+  if (assoc_p (var) && nlist && (nlist->word->flags & W_ASSIGNMENT) == 0 && nlist->word->word[0] != '[')       /*]*/
     {
       iflags = flags & ~ASS_APPEND;
       assign_assoc_from_kvlist (var, nlist, nhash, iflags);
@@ -851,6 +859,118 @@ quote_assign (string)
   return temp;
 }
 
+/* Take a word W of the form [IND]=VALUE and transform it to ['IND]='VALUE'
+   to prevent further expansion. This is called for compound assignments to
+   indexed arrays. W has already undergone word expansions. If W has no [IND]=,
+   just single-quote and return it. */
+static char *
+quote_compound_array_word (w, type)
+     char *w;
+     int type;
+{
+  char *nword, *sub, *value, *t;
+  int ind, wlen, i;
+
+  if (w[0] != LBRACK)
+    return (sh_single_quote (w));
+  ind = skipsubscript (w, 0, 0);
+  if (w[ind] != RBRACK)
+    return (sh_single_quote (w));
+
+  wlen = strlen (w);
+  w[ind] = '\0';
+  sub = sh_single_quote (w+1);
+  w[ind] = RBRACK;
+
+  nword = xmalloc (wlen * 4 + 5);      /* wlen*4 is max single quoted length */
+  nword[0] = LBRACK;
+  i = STRLEN (sub);
+  memcpy (nword+1, sub, i);
+  i++;                         /* accommodate the opening LBRACK */
+  nword[i++] = w[ind++];       /* RBRACK */
+  if (w[ind] == '+')
+    nword[i++] = w[ind++];
+  nword[i++] = w[ind++];
+  value = sh_single_quote (w + ind);
+  strcpy (nword + i, value);
+
+  return nword;
+}
+
+/* Expand the key and value in W, which is of the form [KEY]=VALUE, and
+   reconstruct W with the expanded and single-quoted version:
+   ['expanded-key']='expanded-value'. If there is no [KEY]=, single-quote the
+   word and return it. Very similar to previous function, but does not assume
+   W has already been expanded, and expands the KEY and VALUE separately.
+   Used for compound assignments to associative arrays that are arguments to
+   declaration builtins (declare -A a=( list )). */
+char *
+expand_and_quote_assoc_word (w, type)
+     char *w;
+     int type;
+{
+  char *nword, *key, *value, *t;
+  int ind, wlen, i;
+
+  if (w[0] != LBRACK)
+    return (sh_single_quote (w));     
+  ind = skipsubscript (w, 0, 0);
+  if (w[ind] != RBRACK)
+    return (sh_single_quote (w));
+
+  w[ind] = '\0';
+  t = expand_assignment_string_to_string (w+1, 0);
+  w[ind] = RBRACK;
+  key = sh_single_quote (t ? t : "");
+  free (t);
+
+  wlen = STRLEN (key);
+  nword = xmalloc (wlen + 5);
+  nword[0] = LBRACK;
+  memcpy (nword+1, key, wlen);
+  i = wlen + 1;                        /* accommodate the opening LBRACK */
+
+  nword[i++] = w[ind++];       /* RBRACK */
+  if (w[ind] == '+')
+    nword[i++] = w[ind++];
+  nword[i++] = w[ind++];
+
+  t = expand_assignment_string_to_string (w+ind, 0);
+  value = sh_single_quote (t ? t : "");
+  free (t);
+  nword = xrealloc (nword, wlen + 5 + STRLEN (value));
+  strcpy (nword + i, value);
+
+  free (key);
+  free (value);
+
+  return nword;
+}
+
+/* For each word in a compound array assignment, if the word looks like
+   [ind]=value, single-quote ind and value, but leave the brackets and
+   the = sign (and any `+') alone. This is used for indexed arrays. */
+void
+quote_compound_array_list (list, type)
+     WORD_LIST *list;
+     int type;
+{
+  char *t;
+  WORD_LIST *l;
+
+  for (l = list; l; l = l->next)
+    {
+      if (l->word == 0 || l->word->word == 0 || l->word->word[0] == '\0')
+       continue;       /* should not happen, but just in case... */
+      if ((l->word->flags & W_ASSIGNMENT) == 0)
+       t = sh_single_quote (l->word->word);
+      else 
+       t = quote_compound_array_word (l->word->word, type);
+      free (l->word->word);
+      l->word->word = t;
+    }
+}
+
 /* For each word in a compound array assignment, if the word looks like
    [ind]=value, quote globbing chars and characters in $IFS before the `='. */
 static void
index cad13ab429331075663f69dfbe96c66ca8130754..c2bbb98a66586514197b0fb5fef3116a40535146 100644 (file)
@@ -1,6 +1,6 @@
 /* arrayfunc.h -- declarations for miscellaneous array functions in arrayfunc.c */
 
-/* Copyright (C) 2001-2010 Free Software Foundation, Inc.
+/* Copyright (C) 2001-2020 Free Software Foundation, Inc.
 
    This file is part of GNU Bash, the Bourne Again SHell.
 
@@ -44,41 +44,44 @@ extern int array_expand_once;
 #define VA_NOEXPAND    0x001
 #define VA_ONEWORD     0x002
 
-extern SHELL_VAR *convert_var_to_array __P((SHELL_VAR *));
-extern SHELL_VAR *convert_var_to_assoc __P((SHELL_VAR *));
+extern SHELL_VAR *convert_var_to_array PARAMS((SHELL_VAR *));
+extern SHELL_VAR *convert_var_to_assoc PARAMS((SHELL_VAR *));
 
-extern char *make_array_variable_value __P((SHELL_VAR *, arrayind_t, char *, char *, int));
+extern char *make_array_variable_value PARAMS((SHELL_VAR *, arrayind_t, char *, char *, int));
 
-extern SHELL_VAR *bind_array_variable __P((char *, arrayind_t, char *, int));
-extern SHELL_VAR *bind_array_element __P((SHELL_VAR *, arrayind_t, char *, int));
-extern SHELL_VAR *assign_array_element __P((char *, char *, int));
+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 *bind_assoc_variable __P((SHELL_VAR *, char *, char *, char *, int));
+extern SHELL_VAR *bind_assoc_variable PARAMS((SHELL_VAR *, char *, char *, char *, int));
 
-extern SHELL_VAR *find_or_make_array_variable __P((char *, int));
+extern SHELL_VAR *find_or_make_array_variable PARAMS((char *, int));
 
-extern SHELL_VAR *assign_array_from_string  __P((char *, char *, int));
-extern SHELL_VAR *assign_array_var_from_word_list __P((SHELL_VAR *, WORD_LIST *, int));
+extern SHELL_VAR *assign_array_from_string  PARAMS((char *, char *, int));
+extern SHELL_VAR *assign_array_var_from_word_list PARAMS((SHELL_VAR *, WORD_LIST *, int));
 
-extern WORD_LIST *expand_compound_array_assignment __P((SHELL_VAR *, char *, int));
-extern void assign_compound_array_list __P((SHELL_VAR *, WORD_LIST *, int));
-extern SHELL_VAR *assign_array_var_from_string __P((SHELL_VAR *, char *, int));
+extern WORD_LIST *expand_compound_array_assignment PARAMS((SHELL_VAR *, char *, int));
+extern void assign_compound_array_list PARAMS((SHELL_VAR *, WORD_LIST *, int));
+extern SHELL_VAR *assign_array_var_from_string PARAMS((SHELL_VAR *, char *, int));
 
-extern int unbind_array_element __P((SHELL_VAR *, char *, int));
-extern int skipsubscript __P((const char *, int, int));
+extern char *expand_and_quote_assoc_word PARAMS((char *, int));
+extern void quote_compound_array_list PARAMS((WORD_LIST *, int));
 
-extern void print_array_assignment __P((SHELL_VAR *, int));
-extern void print_assoc_assignment __P((SHELL_VAR *, int));
+extern int unbind_array_element PARAMS((SHELL_VAR *, char *, int));
+extern int skipsubscript PARAMS((const char *, int, int));
 
-extern arrayind_t array_expand_index __P((SHELL_VAR *, char *, int, int));
-extern int valid_array_reference __P((const char *, int));
-extern char *array_value __P((const char *, int, int, int *, arrayind_t *));
-extern char *get_array_value __P((const char *, int, int *, arrayind_t *));
+extern void print_array_assignment PARAMS((SHELL_VAR *, int));
+extern void print_assoc_assignment PARAMS((SHELL_VAR *, int));
 
-extern char *array_keys __P((char *, int, int));
+extern arrayind_t array_expand_index PARAMS((SHELL_VAR *, char *, int, int));
+extern int valid_array_reference PARAMS((const char *, int));
+extern char *array_value PARAMS((const char *, int, int, int *, arrayind_t *));
+extern char *get_array_value PARAMS((const char *, int, int *, arrayind_t *));
 
-extern char *array_variable_name __P((const char *, int, char **, int *));
-extern SHELL_VAR *array_variable_part __P((const char *, int, char **, int *));
+extern char *array_keys PARAMS((char *, int, int));
+
+extern char *array_variable_name PARAMS((const char *, int, char **, int *));
+extern SHELL_VAR *array_variable_part PARAMS((const char *, int, char **, int *));
 
 #else
 
index 9074f4ddf987981a48310ff80666659c95600300..98e714da8ade026f11f000ded6eefc148ad26a95 100644 (file)
@@ -25,6 +25,6 @@
    regexp `^#define[   ]*PATCHLEVEL', since that's what support/mkversion.sh
    looks for to find the patch level (for the sccs version string). */
 
-#define PATCHLEVEL 16
+#define PATCHLEVEL 17
 
 #endif /* _PATCHLEVEL_H_ */
diff --git a/subst.c b/subst.c
index e5e05df22e70fd98d67b5175ba22c5a87862f93a..05f46c510f642b3dc4fbaf3a2a1cfe653087020b 100644 (file)
--- a/subst.c
+++ b/subst.c
@@ -354,6 +354,8 @@ static WORD_LIST *brace_expand_word_list PARAMS((WORD_LIST *, int));
 #endif
 #if defined (ARRAY_VARS)
 static int make_internal_declare PARAMS((char *, char *, char *));
+static void expand_compound_assignment_word PARAMS((WORD_LIST *, int));
+static WORD_LIST *expand_declaration_argument PARAMS((WORD_LIST *, WORD_LIST *));
 #endif
 static WORD_LIST *shell_expand_word_list PARAMS((WORD_LIST *, int));
 static WORD_LIST *expand_word_list_internal PARAMS((WORD_LIST *, int));
@@ -11504,7 +11506,260 @@ make_internal_declare (word, option, cmd)
   dispose_words (wl);
   return r;
 }  
-#endif
+
+/* Expand VALUE in NAME[+]=( VALUE ) to a list of words. FLAGS is 1 if NAME
+   is an associative array.
+
+   If we are  processing an indexed array, expand_compound_array_assignment
+   will expand all the individual words and quote_compound_array_list will
+   single-quote them. If we are processing an associative array, we use
+   parse_string_to_word_list to split VALUE into a list of words instead of
+   faking up a shell variable and calling expand_compound_array_assignment.
+   expand_and_quote_assoc_word expands and single-quotes each word in VALUE
+   together so we don't have problems finding the end of the subscript when
+   quoting it.
+
+   Words in VALUE can be individual words, which are expanded and single-quoted,
+   or words of the form [IND]=VALUE, which end up as explained below, as
+   ['expanded-ind']='expanded-value'. */
+
+static WORD_LIST *
+expand_oneword (value, flags)
+     char *value;
+     int flags;
+{
+  WORD_LIST *l, *nl;
+  char *t;
+  
+  if (flags == 0)
+    {
+      /* Indexed array */
+      l = expand_compound_array_assignment ((SHELL_VAR *)NULL, value, flags);
+      /* Now we quote the results of the expansion above to prevent double
+        expansion. */
+      quote_compound_array_list (l, flags);
+      return l;
+    }
+  else
+    {
+      /* Associative array */
+      l = parse_string_to_word_list (value, 1, "array assign");
+      /* For associative arrays, with their arbitrary subscripts, we have to
+        expand and quote in one step so we don't have to search for the
+        closing right bracket more than once. */
+      for (nl = l; nl; nl = nl->next)
+       {
+         if ((nl->word->flags & W_ASSIGNMENT) == 0)
+           t = sh_single_quote (nl->word->word ? nl->word->word : "");
+         else
+           t = expand_and_quote_assoc_word (nl->word->word, flags);
+         free (nl->word->word);
+         nl->word->word = t;
+       }
+      return l;
+    }
+}
+
+/* Expand a single compound assignment argument to a declaration builtin.
+   This word takes the form NAME[+]=( VALUE ). The NAME[+]= is passed through
+   unchanged. The VALUE is expanded and each word in the result is single-
+   quoted. Words of the form [key]=value end up as
+   ['expanded-key']='expanded-value'. Associative arrays have special
+   handling, see expand_oneword() above. The return value is
+   NAME[+]=( expanded-and-quoted-VALUE ). */
+static void
+expand_compound_assignment_word (tlist, flags)
+     WORD_LIST *tlist;
+     int flags;
+{
+  WORD_LIST *l;
+  int wlen, oind, t;
+  char *value, *temp;
+
+/*itrace("expand_compound_assignment_word: original word = -%s-", tlist->word->word);*/
+  wlen = strlen (tlist->word->word);
+  t = assignment (tlist->word->word, 0);
+
+  /* value doesn't have the open and close parens */
+  oind = 1;
+  value = extract_array_assignment_list (tlist->word->word + t + 1, &oind);
+  /* This performs one round of expansion on the index/key and value and
+     single-quotes each word in the result. */
+  l = expand_oneword (value, flags);
+  free (value);
+
+  value = string_list (l);
+  wlen = STRLEN (value);
+
+  /* Now, let's rebuild the string */
+  temp = xmalloc (t + 3 + wlen + 1);   /* name[+]=(value) */
+  memcpy (temp, tlist->word->word, ++t);
+  temp[t++] = '(';
+  if (value)
+    memcpy (temp + t, value, wlen);
+  t += wlen;
+  temp[t++] = ')';
+  temp[t] = '\0';
+/*itrace("expand_compound_assignment_word: reconstructed word = -%s-", temp);*/
+
+  free (tlist->word->word);
+  tlist->word->word = temp;
+
+  free (value);
+}
+
+/* Expand and process an argument to a declaration command. We have already
+   set flags in TLIST->word->flags depending on the declaration command
+   (declare, local, etc.) and the options supplied to it (-a, -A, etc.).
+   TLIST->word->word is of the form NAME[+]=( VALUE ).   
+
+   This does several things, all using pieces of other functions to get the
+   evaluation sequence right. It's called for compound array assignments with
+   the W_ASSIGNMENT flag set (basically, valid identifier names on the lhs).
+   It parses out which flags need to be set for declare to create the variable
+   correctly, then calls declare internally (make_internal_declare) to make
+   sure the variable exists with the correct attributes. Before the variable
+   is created, it calls expand_compound_assignment_word to expand VALUE to a
+   list of words, appropriately quoted for further evaluation. This preserves
+   the semantics of word-expansion-before-calling-builtins. Finally, it calls
+   do_word_assignment to perform the expansion and assignment with the same
+   expansion semantics as a standalone assignment statement (no word splitting,
+   etc.) even though the word is single-quoted so all that needs to happen is
+   quote removal. */
+static WORD_LIST *
+expand_declaration_argument (tlist, wcmd)
+     WORD_LIST *tlist, *wcmd;
+{
+  char opts[16], omap[128];
+  int t, opti, oind, skip, inheriting;
+  WORD_LIST *l;
+
+  inheriting = localvar_inherit;
+  opti = 0;
+  if (tlist->word->flags & (W_ASSIGNASSOC|W_ASSNGLOBAL|W_CHKLOCAL|W_ASSIGNARRAY))
+    opts[opti++] = '-';
+
+  if ((tlist->word->flags & (W_ASSIGNASSOC|W_ASSNGLOBAL)) == (W_ASSIGNASSOC|W_ASSNGLOBAL))
+    {
+      opts[opti++] = 'g';
+      opts[opti++] = 'A';
+    }
+  else if (tlist->word->flags & W_ASSIGNASSOC)
+    {
+      opts[opti++] = 'A';
+    }
+  else if ((tlist->word->flags & (W_ASSIGNARRAY|W_ASSNGLOBAL)) == (W_ASSIGNARRAY|W_ASSNGLOBAL))
+    {
+      opts[opti++] = 'g';
+      opts[opti++] = 'a';
+    }
+  else if (tlist->word->flags & W_ASSIGNARRAY)
+    {
+      opts[opti++] = 'a';
+    }
+  else if (tlist->word->flags & W_ASSNGLOBAL)
+    opts[opti++] = 'g';
+
+  if (tlist->word->flags & W_CHKLOCAL)
+    opts[opti++] = 'G';
+
+  /* If we have special handling note the integer attribute and others
+     that transform the value upon assignment.  What we do is take all
+     of the option arguments and scan through them looking for options
+     that cause such transformations, and add them to the `opts' array. */
+
+  memset (omap, '\0', sizeof (omap));
+  for (l = wcmd->next; l != tlist; l = l->next)
+    {
+      if (l->word->word[0] != '-')
+       break;  /* non-option argument */
+      if (l->word->word[0] == '-' && l->word->word[1] == '-' && l->word->word[2] == 0)
+       break;  /* -- signals end of options */
+      for (oind = 1; l->word->word[oind]; oind++)
+       switch (l->word->word[oind])
+         {
+           case 'I':
+             inheriting = 1;
+           case 'i':
+           case 'l':
+           case 'u':
+           case 'c':
+             omap[l->word->word[oind]] = 1;
+             if (opti == 0)
+               opts[opti++] = '-';
+             break;
+           default:
+             break;
+         }
+    }
+
+  for (oind = 0; oind < sizeof (omap); oind++)
+    if (omap[oind])
+      opts[opti++] = oind;
+
+  /* If there are no -a/-A options, but we have a compound assignment,
+     we have a choice: we can set opts[0]='-', opts[1]='a', since the
+     default is to create an indexed array, and call
+     make_internal_declare with that, or we can just skip the -a and let
+     declare_builtin deal with it.  Once we're here, we're better set
+     up for the latter, since we don't want to deal with looking up
+     any existing variable here -- better to let declare_builtin do it.
+     We need the variable created, though, especially if it's local, so
+     we get the scoping right before we call do_word_assignment.
+     To ensure that make_local_declare gets called, we add `--' if there
+     aren't any options. */
+  if ((tlist->word->flags & (W_ASSIGNASSOC|W_ASSIGNARRAY)) == 0)
+    {
+      if (opti == 0)
+       {
+         opts[opti++] = '-';
+          opts[opti++] = '-';
+       }
+    }
+  opts[opti] = '\0';
+
+  /* This isn't perfect, but it's a start. Improvements later. We expand
+     tlist->word->word and single-quote the results to avoid multiple
+     expansions by, say, do_assignment_internal(). We have to weigh the
+     cost of reconstructing the compound assignment string with its single
+     quoting and letting the declare builtin handle it. The single quotes
+     will prevent any unwanted additional expansion or word splitting. */
+  expand_compound_assignment_word (tlist, (tlist->word->flags & W_ASSIGNASSOC) ? 1 : 0);
+
+  skip = 0;
+  if (opti > 0)
+    {
+      t = make_internal_declare (tlist->word->word, opts, wcmd ? wcmd->word->word : (char *)0);
+      if (t != EXECUTION_SUCCESS)
+       {
+         last_command_exit_value = t;
+         if (tlist->word->flags & W_FORCELOCAL)        /* non-fatal error */
+           skip = 1;
+         else
+           exp_jump_to_top_level (DISCARD);
+       }
+    }
+
+  if (skip == 0)
+    {
+      t = do_word_assignment (tlist->word, 0);
+      if (t == 0)
+       {
+         last_command_exit_value = EXECUTION_FAILURE;
+         exp_jump_to_top_level (DISCARD);
+       }
+    }
+
+  /* Now transform the word as ksh93 appears to do and go on */
+  t = assignment (tlist->word->word, 0);
+  tlist->word->word[t] = '\0';
+  if (tlist->word->word[t - 1] == '+')
+    tlist->word->word[t - 1] = '\0';   /* cut off append op */
+  tlist->word->flags &= ~(W_ASSIGNMENT|W_NOSPLIT|W_COMPASSIGN|W_ASSIGNARG|W_ASSIGNASSOC|W_ASSIGNARRAY);
+
+  return (tlist);
+}
+#endif /* ARRAY_VARS */
 
 static WORD_LIST *
 shell_expand_word_list (tlist, eflags)
@@ -11515,13 +11770,13 @@ shell_expand_word_list (tlist, eflags)
   int expanded_something, has_dollar_at;
 
   /* We do tilde expansion all the time.  This is what 1003.2 says. */
-  new_list = (WORD_LIST *)NULL;
-  for (wcmd = tlist; wcmd; wcmd = wcmd->next)
-    if (wcmd->word->flags & W_ASSNBLTIN)
-      break;
+  wcmd = new_list = (WORD_LIST *)NULL;
 
   for (orig_list = tlist; tlist; tlist = next)
     {
+      if (wcmd == 0 && (tlist->word->flags & W_ASSNBLTIN))
+       wcmd = tlist;
+       
       next = tlist->next;
 
 #if defined (ARRAY_VARS)
@@ -11532,137 +11787,7 @@ shell_expand_word_list (tlist, eflags)
          because `declare' does some evaluation of compound assignments on
          its own. */
       if ((tlist->word->flags & (W_COMPASSIGN|W_ASSIGNARG)) == (W_COMPASSIGN|W_ASSIGNARG))
-       {
-         int t;
-         char opts[16];
-         int opti, skip, inheriting, array;
-
-         inheriting = localvar_inherit;
-         opti = 0;
-         if (tlist->word->flags & (W_ASSIGNASSOC|W_ASSNGLOBAL|W_CHKLOCAL|W_ASSIGNARRAY))
-           opts[opti++] = '-';
-
-         if ((tlist->word->flags & (W_ASSIGNASSOC|W_ASSNGLOBAL)) == (W_ASSIGNASSOC|W_ASSNGLOBAL))
-           {
-             opts[opti++] = 'g';
-             opts[opti++] = 'A';
-           }
-         else if (tlist->word->flags & W_ASSIGNASSOC)
-           {
-             opts[opti++] = 'A';
-             /* This doesn't work right if a variable with the same name but
-                a different type exists at a previous scope; it generates
-                errors that a user would find confusing. */
-/*           opts[opti++] = 'I'; */
-           }
-         else if ((tlist->word->flags & (W_ASSIGNARRAY|W_ASSNGLOBAL)) == (W_ASSIGNARRAY|W_ASSNGLOBAL))
-           {
-             opts[opti++] = 'g';
-             opts[opti++] = 'a';
-           }
-         else if (tlist->word->flags & W_ASSIGNARRAY)
-           {
-             opts[opti++] = 'a';
-/*           opts[opti++] = 'I'; */
-           }
-         else if (tlist->word->flags & W_ASSNGLOBAL)
-           opts[opti++] = 'g';
-
-         if (tlist->word->flags & W_CHKLOCAL)
-           opts[opti++] = 'G';
-
-         /* If we have special handling note the integer attribute and others
-            that transform the value upon assignment.  What we do is take all
-            of the option arguments and scan through them looking for options
-            that cause such transformations, and add them to the `opts' array. */
-/*       if (opti > 0) */
-           {
-             char omap[128];
-             int oind;
-             WORD_LIST *l;
-
-             memset (omap, '\0', sizeof (omap));
-             for (l = orig_list->next; l != tlist; l = l->next)
-               {
-                 if (l->word->word[0] != '-')
-                   break;      /* non-option argument */
-                 if (l->word->word[0] == '-' && l->word->word[1] == '-' && l->word->word[2] == 0)
-                   break;      /* -- signals end of options */
-                 for (oind = 1; l->word->word[oind]; oind++)
-                   switch (l->word->word[oind])
-                     {
-                       case 'I':
-                         inheriting = 1;
-                       case 'i':
-                       case 'l':
-                       case 'u':
-                       case 'c':
-                         omap[l->word->word[oind]] = 1;
-                         if (opti == 0)
-                           opts[opti++] = '-';
-                         break;
-                       default:
-                         break;
-                     }
-               }
-
-             for (oind = 0; oind < sizeof (omap); oind++)
-               if (omap[oind])
-                 opts[opti++] = oind;
-           }
-
-         /* If there are no -a/-A options, but we have a compound assignment,
-            we have a choice: we can set opts[0]='-', opt[1]='a', since the
-            default is to create an indexed array, and call
-            make_internal_declare, or we can just skip it and let
-            declare_builtin deal with it.  Once we're here, we're better set
-            up for the former. We don't do this if we're inheriting local
-            variables' attributes and values here, since that makes `-a' no
-            longer the default; we pass `--' instead if we don't have any
-            options at all, and just leave off the -a if we have some. */
-         if ((tlist->word->flags & (W_ASSIGNASSOC|W_ASSIGNARRAY)) == 0)
-           {
-             if (opti == 0)
-               {
-                 opts[opti++] = '-';
-                 opts[opti++] = inheriting ? '-' : 'a';
-               }
-             else if (inheriting == 0)
-               opts[opti++] = 'a';
-           }
-
-         opts[opti] = '\0';
-         skip = 0;
-         if (opti > 0)
-           {
-             t = make_internal_declare (tlist->word->word, opts, wcmd ? wcmd->word->word : (char *)0);
-             if (t != EXECUTION_SUCCESS)
-               {
-                 last_command_exit_value = t;
-                 if (tlist->word->flags & W_FORCELOCAL)        /* non-fatal error */
-                   skip = 1;
-                 else
-                   exp_jump_to_top_level (DISCARD);
-               }
-           }
-
-         if (skip == 0)
-           {
-             t = do_word_assignment (tlist->word, 0);
-             if (t == 0)
-               {
-                 last_command_exit_value = EXECUTION_FAILURE;
-                 exp_jump_to_top_level (DISCARD);
-               }
-           }
-
-         /* Now transform the word as ksh93 appears to do and go on */
-         t = assignment (tlist->word->word, 0);
-         tlist->word->word[t] = '\0';
-         if (tlist->word->word[t - 1] == '+')
-           tlist->word->word[t - 1] = '\0';    /* cut off append op */
-         tlist->word->flags &= ~(W_ASSIGNMENT|W_NOSPLIT|W_COMPASSIGN|W_ASSIGNARG|W_ASSIGNASSOC|W_ASSIGNARRAY);
-       }
+       expand_declaration_argument (tlist, wcmd);
 #endif
 
       expanded_something = 0;
index e1e45fcbc20c053d8edbf9169fdf1c59b44bb168..d176bcd47f97d37ebc8509461e47dca5ad1aecd5 100644 (file)
@@ -249,3 +249,11 @@ declare -A dict=(["?"]="quest" ["*"]="star" ["'"]="squote" ["\$"]="dol" ["\""]="
 dict=( "?" "quest" "*" "star" "'" "squote" "\$" "dol" "\"" "dquote" "\\" "bslash" "@" "at" "}" "rbrace" "{" "lbrace" "\`" "bquote" )
 declare -A foo=([two]="" [one]="1" )
 foo=( two "" one "1" )
+rparen dquote rbrace bs
+declare -A a=([")"]="rparen" ["\""]="dquote" ["]"]="rbrace" ["\\"]="bs" )
+")" "rparen" "\"" "dquote" "]" "rbrace" "\\" "bs"
+declare -A a=([")"]="rparen" ["\""]="dquote" ["]"]="rbrace" ["\\"]="bs" )
+declare -A a=([")"]="rparen" ["\""]="dquote" ["]"]="rbrace" ["\\"]="bs" )
+declare -A a=([")"]="rparen" ["\""]="dquote" ["]"]="rbrace" ["\\"]="bs" )
+declare -Arx foo=([two]="2" [three]="3" [one]="1" )
+./assoc11.sub: line 90: foo: readonly variable
index 73200f35b3cc5c1ac1f3dcdf1e973cdb3228340e..13111a52b14a47ccdb275a9eb1ec309cba79dafd 100644 (file)
@@ -68,3 +68,23 @@ loaddict
 foo=(one 1 two)
 declare -p foo
 echo foo=\( ${foo[@]@K} \)
+
+typeset -A a=( [\\]=bs [\"]=dquote [\)]=rparen [\]]=rbrace )
+echo ${a[@]}
+declare -p a
+
+echo ${a[@]@K}
+echo ${a[@]@A}
+
+eval "${a[@]@A}"
+declare -p a
+
+eval "a=( ${a[@]@K} )"
+declare -p a
+
+unset a foo
+readonly -A foo=( one 1 two 2 three 3 )
+
+export foo
+declare -p foo
+declare foo+=( seven 7 eight 8 )
index f147d70bf648898d190bf3f455ccd271cb5e8c4c..be394cc745c5312a7d0e4e7e574e5ba3f181ef27 100644 (file)
@@ -1,2 +1,4 @@
+echo "warning: some of these tests will fail if arrays have not" >&2
+echo "warning: been compiled into the shell" >&2
 ${THIS_SH} ./varenv.tests 2>&1 | grep -v '^expect' > ${BASH_TSTOUT}
 diff ${BASH_TSTOUT} varenv.right && rm -f ${BASH_TSTOUT}
index 34a130f8f076c742a39127a1ebb1e28221c94806..81cdd09f6a1da25b4f07fbf12cee7731388c1bba 100644 (file)
@@ -246,9 +246,16 @@ declare -- var
 declare -- var="local"
 declare -- var="f1"
 declare -- var="local"
-declare -a arr=()
+declare -a arr=([0]="zero" [1]="one" [2]="two" [3]="three" [4]="four" [5]="five")
+declare -a arr=([0]="zero" [1]="one" [2]="two")
 declare -a arr=([0]="three" [1]="four" [2]="five")
-./varenv18.sub: line 39: !name: unbound variable
+declare -a arr=([0]="zero" [1]="one" [2]="two")
+ddd 0
+aaa 1 2 3
+bbb 4 5 6
+ccc 7 8 9
+declare -a x=([0]="one" [1]="two" [2]="three")
+./varenv19.sub: line 51: declare: x: not found
 a=z
 a=b
 a=z
index 495fe7edafc0b056c72550c0ad00092edde53bc6..16371a85128b00ce4e7f0937c44e0f1586255755 100644 (file)
@@ -256,6 +256,7 @@ ${THIS_SH} ./varenv15.sub
 ${THIS_SH} ./varenv16.sub
 ${THIS_SH} ./varenv17.sub
 ${THIS_SH} ./varenv18.sub
+${THIS_SH} ./varenv19.sub
 
 # make sure variable scoping is done right
 tt() { typeset a=b;echo a=$a; };a=z;echo a=$a;tt;echo a=$a
index e587a7c75a14f3f10541c880968fe25ed600992f..4ad7f1a46a003a3171793236355d2f0a70c1e02d 100644 (file)
 #   along with this program.  If not, see <http://www.gnu.org/licenses/>.
 #
 
-# THIS DOESN'T WORK RIGHT YET
-
 arr=(zero one two)
 four=four
 
 f()
 {
        local -a arr=( "${arr[@]}" )
+       arr+=(three four five)
        declare -p arr
 }
-
 f
+declare -p arr
 
 f1()
 {
diff --git a/tests/varenv19.sub b/tests/varenv19.sub
new file mode 100644 (file)
index 0000000..753f508
--- /dev/null
@@ -0,0 +1,51 @@
+#   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/>.
+#
+
+# variable attribute inheritance problems without specifying -a or -A
+
+function aaa() {
+        local x='1 2 3'
+        echo "aaa ${x}"
+}
+
+function bbb {
+        local x
+        x=(4 5 6)
+        echo "bbb ${x[*]}"
+}
+
+ccc()
+{
+        local x=(7 8 9)
+        echo "ccc ${x[*]}"
+}
+
+function ddd
+{
+        local -r x='0'
+        echo "ddd ${x}"
+        aaa
+        bbb
+        ccc
+}
+
+ddd
+
+f()
+{
+       local x=(one two three)
+       declare -p x
+}
+f
+declare -p x