LCOV - code coverage report
Current view: top level - bash-4.4.23 - arrayfunc.c (source / functions) Hit Total Coverage
Test: cov-bash.info Lines: 202 528 38.3 %
Date: 2020-10-29 14:49:28 Functions: 19 28 67.9 %

          Line data    Source code
       1             : /* arrayfunc.c -- High-level array functions used by other parts of the shell. */
       2             : 
       3             : /* Copyright (C) 2001-2016 Free Software Foundation, Inc.
       4             : 
       5             :    This file is part of GNU Bash, the Bourne Again SHell.
       6             : 
       7             :    Bash is free software: you can redistribute it and/or modify
       8             :    it under the terms of the GNU General Public License as published by
       9             :    the Free Software Foundation, either version 3 of the License, or
      10             :    (at your option) any later version.
      11             : 
      12             :    Bash is distributed in the hope that it will be useful,
      13             :    but WITHOUT ANY WARRANTY; without even the implied warranty of
      14             :    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
      15             :    GNU General Public License for more details.
      16             : 
      17             :    You should have received a copy of the GNU General Public License
      18             :    along with Bash.  If not, see <http://www.gnu.org/licenses/>.
      19             : */
      20             : 
      21             : #include "config.h"
      22             : 
      23             : #if defined (ARRAY_VARS)
      24             : 
      25             : #if defined (HAVE_UNISTD_H)
      26             : #  include <unistd.h>
      27             : #endif
      28             : #include <stdio.h>
      29             : 
      30             : #include "bashintl.h"
      31             : 
      32             : #include "shell.h"
      33             : #include "pathexp.h"
      34             : 
      35             : #include "shmbutil.h"
      36             : #if defined (HAVE_MBSTR_H) && defined (HAVE_MBSCHR)
      37             : #  include <mbstr.h>              /* mbschr */
      38             : #endif
      39             : 
      40             : #include "builtins/common.h"
      41             : 
      42             : extern char *this_command_name;
      43             : extern int last_command_exit_value;
      44             : extern int array_needs_making;
      45             : 
      46             : static SHELL_VAR *bind_array_var_internal __P((SHELL_VAR *, arrayind_t, char *, char *, int));
      47             : static SHELL_VAR *assign_array_element_internal __P((SHELL_VAR *, char *, char *, char *, int, char *, int));
      48             : 
      49             : static char *quote_assign __P((const char *));
      50             : static void quote_array_assignment_chars __P((WORD_LIST *));
      51             : static char *array_value_internal __P((const char *, int, int, int *, arrayind_t *));
      52             : 
      53             : /* Standard error message to use when encountering an invalid array subscript */
      54             : const char * const bash_badsub_errmsg = N_("bad array subscript");
      55             : 
      56             : /* **************************************************************** */
      57             : /*                                                                  */
      58             : /*  Functions to manipulate array variables and perform assignments */
      59             : /*                                                                  */
      60             : /* **************************************************************** */
      61             : 
      62             : /* Convert a shell variable to an array variable.  The original value is
      63             :    saved as array[0]. */
      64             : SHELL_VAR *
      65           0 : convert_var_to_array (var)
      66             :      SHELL_VAR *var;
      67             : {
      68           0 :   char *oldval;
      69           0 :   ARRAY *array;
      70             : 
      71           0 :   oldval = value_cell (var);
      72           0 :   array = array_create ();
      73           0 :   if (oldval)
      74           0 :     array_insert (array, 0, oldval);
      75             : 
      76           0 :   FREE (value_cell (var));
      77           0 :   var_setarray (var, array);
      78             : 
      79             :   /* these aren't valid anymore */
      80           0 :   var->dynamic_value = (sh_var_value_func_t *)NULL;
      81           0 :   var->assign_func = (sh_var_assign_func_t *)NULL;
      82             : 
      83           0 :   INVALIDATE_EXPORTSTR (var);
      84           0 :   if (exported_p (var))
      85           0 :     array_needs_making++;
      86             : 
      87           0 :   VSETATTR (var, att_array);
      88           0 :   VUNSETATTR (var, att_invisible);
      89             : 
      90             :   /* Make sure it's not marked as an associative array any more */
      91           0 :   VUNSETATTR (var, att_assoc);
      92             : 
      93             :   /* Since namerefs can't be array variables, turn off nameref attribute */
      94           0 :   VUNSETATTR (var, att_nameref);
      95             : 
      96           0 :   return var;
      97             : }
      98             : 
      99             : /* Convert a shell variable to an array variable.  The original value is
     100             :    saved as array[0]. */
     101             : SHELL_VAR *
     102           0 : convert_var_to_assoc (var)
     103             :      SHELL_VAR *var;
     104             : {
     105           0 :   char *oldval;
     106           0 :   HASH_TABLE *hash;
     107             : 
     108           0 :   oldval = value_cell (var);
     109           0 :   hash = assoc_create (0);
     110           0 :   if (oldval)
     111           0 :     assoc_insert (hash, savestring ("0"), oldval);
     112             : 
     113           0 :   FREE (value_cell (var));
     114           0 :   var_setassoc (var, hash);
     115             : 
     116             :   /* these aren't valid anymore */
     117           0 :   var->dynamic_value = (sh_var_value_func_t *)NULL;
     118           0 :   var->assign_func = (sh_var_assign_func_t *)NULL;
     119             : 
     120           0 :   INVALIDATE_EXPORTSTR (var);
     121           0 :   if (exported_p (var))
     122           0 :     array_needs_making++;
     123             : 
     124           0 :   VSETATTR (var, att_assoc);
     125           0 :   VUNSETATTR (var, att_invisible);
     126             : 
     127             :   /* Make sure it's not marked as an indexed array any more */
     128           0 :   VUNSETATTR (var, att_array);
     129             : 
     130             :   /* Since namerefs can't be array variables, turn off nameref attribute */
     131           0 :   VUNSETATTR (var, att_nameref);
     132             : 
     133           0 :   return var;
     134             : }
     135             : 
     136             : char *
     137          27 : make_array_variable_value (entry, ind, key, value, flags)
     138             :      SHELL_VAR *entry;
     139             :      arrayind_t ind;
     140             :      char *key;
     141             :      char *value;
     142             :      int flags;
     143             : {
     144          27 :   SHELL_VAR *dentry;
     145          27 :   char *newval;
     146             : 
     147             :   /* If we're appending, we need the old value of the array reference, so
     148             :      fake out make_variable_value with a dummy SHELL_VAR */
     149          27 :   if (flags & ASS_APPEND)
     150             :     {
     151           0 :       dentry = (SHELL_VAR *)xmalloc (sizeof (SHELL_VAR));
     152           0 :       dentry->name = savestring (entry->name);
     153           0 :       if (assoc_p (entry))
     154           0 :         newval = assoc_reference (assoc_cell (entry), key);
     155             :       else
     156           0 :         newval = array_reference (array_cell (entry), ind);
     157           0 :       if (newval)
     158           0 :         dentry->value = savestring (newval);
     159             :       else
     160             :         {
     161           0 :           dentry->value = (char *)xmalloc (1);
     162           0 :           dentry->value[0] = '\0';
     163             :         }
     164           0 :       dentry->exportstr = 0;
     165           0 :       dentry->attributes = entry->attributes & ~(att_array|att_assoc|att_exported);
     166             :       /* Leave the rest of the members uninitialized; the code doesn't look
     167             :          at them. */
     168           0 :       newval = make_variable_value (dentry, value, flags);       
     169           0 :       dispose_variable (dentry);
     170             :     }
     171             :   else
     172          27 :     newval = make_variable_value (entry, value, flags);
     173             : 
     174          27 :   return newval;
     175             : }
     176             :   
     177             : static SHELL_VAR *
     178          27 : bind_array_var_internal (entry, ind, key, value, flags)
     179             :      SHELL_VAR *entry;
     180             :      arrayind_t ind;
     181             :      char *key;
     182             :      char *value;
     183             :      int flags;
     184             : {
     185          27 :   char *newval;
     186             : 
     187          27 :   newval = make_array_variable_value (entry, ind, key, value, flags);
     188             : 
     189          27 :   if (entry->assign_func)
     190           0 :     (*entry->assign_func) (entry, newval, ind, key);
     191          27 :   else if (assoc_p (entry))
     192           0 :     assoc_insert (assoc_cell (entry), key, newval);
     193             :   else
     194          27 :     array_insert (array_cell (entry), ind, newval);
     195          27 :   FREE (newval);
     196             : 
     197          27 :   VUNSETATTR (entry, att_invisible);    /* no longer invisible */
     198          27 :   return (entry);
     199             : }
     200             : 
     201             : /* Perform an array assignment name[ind]=value.  If NAME already exists and
     202             :    is not an array, and IND is 0, perform name=value instead.  If NAME exists
     203             :    and is not an array, and IND is not 0, convert it into an array with the
     204             :    existing value as name[0].
     205             : 
     206             :    If NAME does not exist, just create an array variable, no matter what
     207             :    IND's value may be. */
     208             : SHELL_VAR *
     209           9 : bind_array_variable (name, ind, value, flags)
     210             :      char *name;
     211             :      arrayind_t ind;
     212             :      char *value;
     213             :      int flags;
     214             : {
     215           9 :   SHELL_VAR *entry;
     216             : 
     217           9 :   entry = find_shell_variable (name);
     218             : 
     219           9 :   if (entry == (SHELL_VAR *) 0)
     220             :     {
     221             :       /* Is NAME a nameref variable that points to an unset variable? */
     222           9 :       entry = find_variable_nameref_for_create (name, 0);
     223           9 :       if (entry == INVALID_NAMEREF_VALUE)
     224             :         return ((SHELL_VAR *)0);
     225           9 :       if (entry && nameref_p (entry))
     226           0 :         entry = make_new_array_variable (nameref_cell (entry));
     227             :     }
     228           9 :   if (entry == (SHELL_VAR *) 0)
     229           9 :     entry = make_new_array_variable (name);
     230           0 :   else if ((readonly_p (entry) && (flags&ASS_FORCE) == 0) || noassign_p (entry))
     231             :     {
     232           0 :       if (readonly_p (entry))
     233           0 :         err_readonly (name);
     234           0 :       return (entry);
     235             :     }
     236           0 :   else if (array_p (entry) == 0)
     237           0 :     entry = convert_var_to_array (entry);
     238             : 
     239             :   /* ENTRY is an array variable, and ARRAY points to the value. */
     240           9 :   return (bind_array_var_internal (entry, ind, 0, value, flags));
     241             : }
     242             : 
     243             : SHELL_VAR *
     244           0 : bind_array_element (entry, ind, value, flags)
     245             :      SHELL_VAR *entry;
     246             :      arrayind_t ind;
     247             :      char *value;
     248             :      int flags;
     249             : {
     250           0 :   return (bind_array_var_internal (entry, ind, 0, value, flags));
     251             : }
     252             :                     
     253             : SHELL_VAR *
     254           0 : bind_assoc_variable (entry, name, key, value, flags)
     255             :      SHELL_VAR *entry;
     256             :      char *name;
     257             :      char *key;
     258             :      char *value;
     259             :      int flags;
     260             : {
     261           0 :   if ((readonly_p (entry) && (flags&ASS_FORCE) == 0) || noassign_p (entry))
     262             :     {
     263           0 :       if (readonly_p (entry))
     264           0 :         err_readonly (name);
     265           0 :       return (entry);
     266             :     }
     267             : 
     268           0 :   return (bind_array_var_internal (entry, 0, key, value, flags));
     269             : }
     270             : 
     271             : /* Parse NAME, a lhs of an assignment statement of the form v[s], and
     272             :    assign VALUE to that array element by calling bind_array_variable(). */
     273             : SHELL_VAR *
     274           9 : assign_array_element (name, value, flags)
     275             :      char *name, *value;
     276             :      int flags;
     277             : {
     278           9 :   char *sub, *vname;
     279           9 :   int sublen;
     280           9 :   SHELL_VAR *entry;
     281             : 
     282           9 :   vname = array_variable_name (name, &sub, &sublen);
     283             : 
     284           9 :   if (vname == 0)
     285             :     return ((SHELL_VAR *)NULL);
     286             : 
     287           9 :   if ((ALL_ELEMENT_SUB (sub[0]) && sub[1] == ']') || (sublen <= 1))
     288             :     {
     289           0 :       free (vname);
     290           0 :       err_badarraysub (name);
     291           0 :       return ((SHELL_VAR *)NULL);
     292             :     }
     293             : 
     294           9 :   entry = find_variable (vname);
     295           9 :   entry = assign_array_element_internal (entry, name, vname, sub, sublen, value, flags);
     296             : 
     297           9 :   free (vname);
     298           9 :   return entry;
     299             : }
     300             : 
     301             : static SHELL_VAR *
     302           9 : assign_array_element_internal (entry, name, vname, sub, sublen, value, flags)
     303             :      SHELL_VAR *entry;
     304             :      char *name;                /* only used for error messages */
     305             :      char *vname;
     306             :      char *sub;
     307             :      int sublen;
     308             :      char *value;
     309             :      int flags;
     310             : {
     311           9 :   char *akey;
     312           9 :   arrayind_t ind;
     313             : 
     314           9 :   if (entry && assoc_p (entry))
     315             :     {
     316           0 :       sub[sublen-1] = '\0';
     317           0 :       akey = expand_assignment_string_to_string (sub, 0);       /* [ */
     318           0 :       sub[sublen-1] = ']';
     319           0 :       if (akey == 0 || *akey == 0)
     320             :         {
     321           0 :           err_badarraysub (name);
     322           0 :           FREE (akey);
     323           0 :           return ((SHELL_VAR *)NULL);
     324             :         }
     325           0 :       entry = bind_assoc_variable (entry, vname, akey, value, flags);
     326             :     }
     327             :   else
     328             :     {
     329           9 :       ind = array_expand_index (entry, sub, sublen);
     330             :       /* negative subscripts to indexed arrays count back from end */
     331           9 :       if (entry && ind < 0)
     332           0 :         ind = (array_p (entry) ? array_max_index (array_cell (entry)) : 0) + 1 + ind;
     333           9 :       if (ind < 0)
     334             :         {
     335           0 :           err_badarraysub (name);
     336           0 :           return ((SHELL_VAR *)NULL);
     337             :         }
     338           9 :       entry = bind_array_variable (vname, ind, value, flags);
     339             :     }
     340             : 
     341             :   return (entry);
     342             : }
     343             : 
     344             : /* Find the array variable corresponding to NAME.  If there is no variable,
     345             :    create a new array variable.  If the variable exists but is not an array,
     346             :    convert it to an indexed array.  If FLAGS&1 is non-zero, an existing
     347             :    variable is checked for the readonly or noassign attribute in preparation
     348             :    for assignment (e.g., by the `read' builtin).  If FLAGS&2 is non-zero, we
     349             :    create an associative array. */
     350             : SHELL_VAR *
     351          18 : find_or_make_array_variable (name, flags)
     352             :      char *name;
     353             :      int flags;
     354             : {
     355          18 :   SHELL_VAR *var;
     356             : 
     357          18 :   var = find_variable (name);
     358          18 :   if (var == 0)
     359             :     {
     360             :       /* See if we have a nameref pointing to a variable that hasn't been
     361             :          created yet. */
     362          18 :       var = find_variable_last_nameref (name, 1);
     363          18 :       if (var && nameref_p (var) && invisible_p (var))
     364             :         {
     365           0 :           internal_warning (_("%s: removing nameref attribute"), name);
     366           0 :           VUNSETATTR (var, att_nameref);
     367             :         }
     368          18 :       if (var && nameref_p (var))
     369             :         {
     370           0 :           if (valid_nameref_value (nameref_cell (var), 2) == 0)
     371             :             {
     372           0 :               sh_invalidid (nameref_cell (var));
     373           0 :               return ((SHELL_VAR *)NULL);
     374             :             }
     375           0 :           var = (flags & 2) ? make_new_assoc_variable (nameref_cell (var)) : make_new_array_variable (nameref_cell (var));
     376             :         }
     377             :     }
     378             : 
     379          18 :   if (var == 0)
     380          18 :     var = (flags & 2) ? make_new_assoc_variable (name) : make_new_array_variable (name);
     381           0 :   else if ((flags & 1) && (readonly_p (var) || noassign_p (var)))
     382             :     {
     383           0 :       if (readonly_p (var))
     384           0 :         err_readonly (name);
     385           0 :       return ((SHELL_VAR *)NULL);
     386             :     }
     387           0 :   else if ((flags & 2) && array_p (var))
     388             :     {
     389           0 :       last_command_exit_value = 1;
     390           0 :       report_error (_("%s: cannot convert indexed to associative array"), name);
     391           0 :       return ((SHELL_VAR *)NULL);
     392             :     }
     393           0 :   else if (array_p (var) == 0 && assoc_p (var) == 0)
     394           0 :     var = convert_var_to_array (var);
     395             : 
     396             :   return (var);
     397             : }
     398             :   
     399             : /* Perform a compound assignment statement for array NAME, where VALUE is
     400             :    the text between the parens:  NAME=( VALUE ) */
     401             : SHELL_VAR *
     402          18 : assign_array_from_string (name, value, flags)
     403             :      char *name, *value;
     404             :      int flags;
     405             : {
     406          18 :   SHELL_VAR *var;
     407          18 :   int vflags;
     408             : 
     409          18 :   vflags = 1;
     410          18 :   if (flags & ASS_MKASSOC)
     411           0 :     vflags |= 2;
     412             : 
     413          18 :   var = find_or_make_array_variable (name, vflags);
     414          18 :   if (var == 0)
     415             :     return ((SHELL_VAR *)NULL);
     416             : 
     417          18 :   return (assign_array_var_from_string (var, value, flags));
     418             : }
     419             : 
     420             : /* Sequentially assign the indices of indexed array variable VAR from the
     421             :    words in LIST. */
     422             : SHELL_VAR *
     423           0 : assign_array_var_from_word_list (var, list, flags)
     424             :      SHELL_VAR *var;
     425             :      WORD_LIST *list;
     426             :      int flags;
     427             : {
     428           0 :   register arrayind_t i;
     429           0 :   register WORD_LIST *l;
     430           0 :   ARRAY *a;
     431             : 
     432           0 :   a = array_cell (var);
     433           0 :   i = (flags & ASS_APPEND) ? array_max_index (a) + 1 : 0;
     434             : 
     435           0 :   for (l = list; l; l = l->next, i++)
     436           0 :     bind_array_var_internal (var, i, 0, l->word->word, flags & ~ASS_APPEND);
     437             : 
     438           0 :   VUNSETATTR (var, att_invisible);      /* no longer invisible */
     439             : 
     440           0 :   return var;
     441             : }
     442             : 
     443             : WORD_LIST *
     444          18 : expand_compound_array_assignment (var, value, flags)
     445             :      SHELL_VAR *var;
     446             :      char *value;
     447             :      int flags;
     448             : {
     449          18 :   WORD_LIST *list, *nlist;
     450          18 :   char *val;
     451          18 :   int ni;
     452             : 
     453             :   /* This condition is true when invoked from the declare builtin with a
     454             :      command like
     455             :         declare -a d='([1]="" [2]="bdef" [5]="hello world" "test")' */
     456          18 :   if (*value == '(')    /*)*/
     457             :     {
     458           0 :       ni = 1;
     459           0 :       val = extract_array_assignment_list (value, &ni);
     460           0 :       if (val == 0)
     461             :         return (WORD_LIST *)NULL;
     462             :     }
     463             :   else
     464             :     val = value;
     465             : 
     466             :   /* Expand the value string into a list of words, performing all the
     467             :      shell expansions including pathname generation and word splitting. */
     468             :   /* First we split the string on whitespace, using the shell parser
     469             :      (ksh93 seems to do this). */
     470          18 :   list = parse_string_to_word_list (val, 1, "array assign");
     471             : 
     472          18 :   if (var && assoc_p (var))
     473             :     {
     474           0 :       if (val != value)
     475           0 :         free (val);
     476           0 :       return list;
     477             :     }
     478             : 
     479             :   /* If we're using [subscript]=value, we need to quote each [ and ] to
     480             :      prevent unwanted filename expansion.  This doesn't need to be done
     481             :      for associative array expansion, since that uses a different expansion
     482             :      function (see assign_compound_array_list below). */
     483          18 :   if (list)
     484           9 :     quote_array_assignment_chars (list);
     485             : 
     486             :   /* Now that we've split it, perform the shell expansions on each
     487             :      word in the list. */
     488          18 :   nlist = list ? expand_words_no_vars (list) : (WORD_LIST *)NULL;
     489             : 
     490          18 :   dispose_words (list);
     491             : 
     492          18 :   if (val != value)
     493           0 :     free (val);
     494             : 
     495             :   return nlist;
     496             : }
     497             : 
     498             : /* Callers ensure that VAR is not NULL */
     499             : void
     500          18 : assign_compound_array_list (var, nlist, flags)
     501             :      SHELL_VAR *var;
     502             :      WORD_LIST *nlist;
     503             :      int flags;
     504             : {
     505          18 :   ARRAY *a;
     506          18 :   HASH_TABLE *h;
     507          18 :   WORD_LIST *list;
     508          18 :   char *w, *val, *nval, *savecmd;
     509          18 :   int len, iflags, free_val;
     510          18 :   arrayind_t ind, last_ind;
     511          18 :   char *akey;
     512             : 
     513          18 :   a = (var && array_p (var)) ? array_cell (var) : (ARRAY *)0;
     514          18 :   h = (var && assoc_p (var)) ? assoc_cell (var) : (HASH_TABLE *)0;
     515             : 
     516          18 :   akey = (char *)0;
     517          18 :   ind = 0;
     518             : 
     519             :   /* Now that we are ready to assign values to the array, kill the existing
     520             :      value. */
     521          18 :   if ((flags & ASS_APPEND) == 0)
     522             :     {
     523          18 :       if (a && array_p (var))
     524          18 :         array_flush (a);
     525           0 :       else if (h && assoc_p (var))
     526           0 :         assoc_flush (h);
     527             :     }
     528             : 
     529          18 :   last_ind = (a && (flags & ASS_APPEND)) ? array_max_index (a) + 1 : 0;
     530             : 
     531          36 :   for (list = nlist; list; list = list->next)
     532             :     {
     533             :       /* Don't allow var+=(values) to make assignments in VALUES append to
     534             :          existing values by default. */
     535          18 :       iflags = flags & ~ASS_APPEND;
     536          18 :       w = list->word->word;
     537             : 
     538             :       /* We have a word of the form [ind]=value */
     539          18 :       if ((list->word->flags & W_ASSIGNMENT) && w[0] == '[')
     540             :         {
     541             :           /* Don't have to handle embedded quotes specially any more, since
     542             :              associative array subscripts have not been expanded yet (see
     543             :              above). */
     544           0 :           len = skipsubscript (w, 0, 0);
     545             : 
     546             :           /* XXX - changes for `+=' */
     547           0 :           if (w[len] != ']' || (w[len+1] != '=' && (w[len+1] != '+' || w[len+2] != '=')))
     548             :             {
     549           0 :               if (assoc_p (var))
     550             :                 {
     551           0 :                   err_badarraysub (w);
     552           0 :                   continue;
     553             :                 }
     554           0 :               nval = make_variable_value (var, w, flags);
     555           0 :               if (var->assign_func)
     556           0 :                 (*var->assign_func) (var, nval, last_ind, 0);
     557             :               else
     558           0 :                 array_insert (a, last_ind, nval);
     559           0 :               FREE (nval);
     560           0 :               last_ind++;
     561           0 :               continue;
     562             :             }
     563             : 
     564           0 :           if (len == 1)
     565             :             {
     566           0 :               err_badarraysub (w);
     567           0 :               continue;
     568             :             }
     569             : 
     570           0 :           if (ALL_ELEMENT_SUB (w[1]) && len == 2)
     571             :             {
     572           0 :               last_command_exit_value = 1;
     573           0 :               if (assoc_p (var))
     574           0 :                 report_error (_("%s: invalid associative array key"), w);
     575             :               else
     576           0 :                 report_error (_("%s: cannot assign to non-numeric index"), w);
     577           0 :               continue;
     578             :             }
     579             : 
     580           0 :           if (array_p (var))
     581             :             {
     582           0 :               ind = array_expand_index (var, w + 1, len);
     583             :               /* negative subscripts to indexed arrays count back from end */
     584           0 :               if (ind < 0)
     585           0 :                 ind = array_max_index (array_cell (var)) + 1 + ind;
     586           0 :               if (ind < 0)
     587             :                 {
     588           0 :                   err_badarraysub (w);
     589           0 :                   continue;
     590             :                 }
     591             : 
     592             :               last_ind = ind;
     593             :             }
     594           0 :           else if (assoc_p (var))
     595             :             {
     596             :               /* This is not performed above, see expand_compound_array_assignment */
     597           0 :               w[len] = '\0';    /*[*/
     598           0 :               akey = expand_assignment_string_to_string (w+1, 0);
     599           0 :               w[len] = ']';
     600             :               /* And we need to expand the value also, see below */
     601           0 :               if (akey == 0 || *akey == 0)
     602             :                 {
     603           0 :                   err_badarraysub (w);
     604           0 :                   FREE (akey);
     605           0 :                   continue;
     606             :                 }
     607             :             }
     608             : 
     609             :           /* XXX - changes for `+=' -- just accept the syntax.  ksh93 doesn't do this */
     610           0 :           if (w[len + 1] == '+' && w[len + 2] == '=')
     611             :             {
     612           0 :               iflags |= ASS_APPEND;
     613           0 :               val = w + len + 3;
     614             :             }
     615             :           else
     616           0 :             val = w + len + 2;      
     617             :         }
     618          18 :       else if (assoc_p (var))
     619             :         {
     620           0 :           last_command_exit_value = 1;
     621           0 :           report_error (_("%s: %s: must use subscript when assigning associative array"), var->name, w);
     622           0 :           continue;
     623             :         }
     624             :       else              /* No [ind]=value, just a stray `=' */
     625             :         {
     626             :           ind = last_ind;
     627             :           val = w;
     628             :         }
     629             : 
     630          18 :       free_val = 0;
     631             :       /* See above; we need to expand the value here */
     632          18 :       if (assoc_p (var))
     633             :         {
     634           0 :           val = expand_assignment_string_to_string (val, 0);
     635           0 :           if (val == 0)
     636             :             {
     637           0 :               val = (char *)xmalloc (1);
     638           0 :               val[0] = '\0';    /* like do_assignment_internal */
     639             :             }
     640             :           free_val = 1;
     641             :         }
     642             : 
     643          18 :       savecmd = this_command_name;
     644          18 :       if (integer_p (var))
     645           0 :         this_command_name = (char *)NULL;       /* no command name for errors */
     646          18 :       bind_array_var_internal (var, ind, akey, val, iflags);
     647          18 :       last_ind++;
     648          18 :       this_command_name = savecmd;
     649             : 
     650          18 :       if (free_val)
     651           0 :         free (val);
     652             :     }
     653          18 : }
     654             : 
     655             : /* Perform a compound array assignment:  VAR->name=( VALUE ).  The
     656             :    VALUE has already had the parentheses stripped. */
     657             : SHELL_VAR *
     658          18 : assign_array_var_from_string (var, value, flags)
     659             :      SHELL_VAR *var;
     660             :      char *value;
     661             :      int flags;
     662             : {
     663          18 :   WORD_LIST *nlist;
     664             : 
     665          18 :   if (value == 0)
     666             :     return var;
     667             : 
     668          18 :   nlist = expand_compound_array_assignment (var, value, flags);
     669          18 :   assign_compound_array_list (var, nlist, flags);
     670             : 
     671          18 :   if (nlist)
     672           9 :     dispose_words (nlist);
     673             : 
     674          18 :   if (var)
     675          18 :     VUNSETATTR (var, att_invisible);    /* no longer invisible */
     676             : 
     677             :   return (var);
     678             : }
     679             : 
     680             : /* Quote globbing chars and characters in $IFS before the `=' in an assignment
     681             :    statement (usually a compound array assignment) to protect them from
     682             :    unwanted filename expansion or word splitting. */
     683             : static char *
     684           0 : quote_assign (string)
     685             :      const char *string;
     686             : {
     687           0 :   size_t slen;
     688           0 :   int saw_eq;
     689           0 :   char *temp, *t, *subs;
     690           0 :   const char *s, *send;
     691           0 :   int ss, se;
     692           0 :   DECLARE_MBSTATE;
     693             : 
     694           0 :   slen = strlen (string);
     695           0 :   send = string + slen;
     696             : 
     697           0 :   t = temp = (char *)xmalloc (slen * 2 + 1);
     698           0 :   saw_eq = 0;
     699           0 :   for (s = string; *s; )
     700             :     {
     701           0 :       if (*s == '=')
     702             :         saw_eq = 1;
     703           0 :       if (saw_eq == 0 && *s == '[')             /* looks like a subscript */
     704             :         {
     705           0 :           ss = s - string;
     706           0 :           se = skipsubscript (string, ss, 0);
     707           0 :           subs = substring (s, ss, se);
     708           0 :           *t++ = '\\';
     709           0 :           strcpy (t, subs);
     710           0 :           t += se - ss;
     711           0 :           *t++ = '\\';
     712           0 :           *t++ = ']';
     713           0 :           s += se + 1;
     714           0 :           free (subs);
     715           0 :           continue;
     716             :         }
     717           0 :       if (saw_eq == 0 && (glob_char_p (s) || isifs (*s)))
     718           0 :         *t++ = '\\';
     719             : 
     720           0 :       COPY_CHAR_P (t, s, send);
     721             :     }
     722           0 :   *t = '\0';
     723           0 :   return temp;
     724             : }
     725             : 
     726             : /* For each word in a compound array assignment, if the word looks like
     727             :    [ind]=value, quote globbing chars and characters in $IFS before the `='. */
     728             : static void
     729           9 : quote_array_assignment_chars (list)
     730             :      WORD_LIST *list;
     731             : {
     732           9 :   char *nword;
     733           9 :   WORD_LIST *l;
     734             : 
     735          27 :   for (l = list; l; l = l->next)
     736             :     {
     737          18 :       if (l->word == 0 || l->word->word == 0 || l->word->word[0] == '\0')
     738             :         continue;       /* should not happen, but just in case... */
     739             :       /* Don't bother if it hasn't been recognized as an assignment or
     740             :          doesn't look like [ind]=value */
     741          18 :       if ((l->word->flags & W_ASSIGNMENT) == 0)
     742             :         continue;
     743           0 :       if (l->word->word[0] != '[' || mbschr (l->word->word, '=') == 0) /* ] */
     744           0 :         continue;
     745             : 
     746           0 :       nword = quote_assign (l->word->word);
     747           0 :       free (l->word->word);
     748           0 :       l->word->word = nword;
     749           0 :       l->word->flags |= W_NOGLOB; /* XXX - W_NOSPLIT also? */
     750             :     }
     751           9 : }
     752             : 
     753             : /* skipsubscript moved to subst.c to use private functions. 2009/02/24. */
     754             : 
     755             : /* This function is called with SUB pointing to just after the beginning
     756             :    `[' of an array subscript and removes the array element to which SUB
     757             :    expands from array VAR.  A subscript of `*' or `@' unsets the array. */
     758             : int
     759           0 : unbind_array_element (var, sub)
     760             :      SHELL_VAR *var;
     761             :      char *sub;
     762             : {
     763           0 :   int len;
     764           0 :   arrayind_t ind;
     765           0 :   char *akey;
     766           0 :   ARRAY_ELEMENT *ae;
     767             : 
     768           0 :   len = skipsubscript (sub, 0, (var && assoc_p(var)));
     769           0 :   if (sub[len] != ']' || len == 0)
     770             :     {
     771           0 :       builtin_error ("%s[%s: %s", var->name, sub, _(bash_badsub_errmsg));
     772           0 :       return -1;
     773             :     }
     774           0 :   sub[len] = '\0';
     775             : 
     776           0 :   if (ALL_ELEMENT_SUB (sub[0]) && sub[1] == 0)
     777             :     {
     778           0 :       if (array_p (var) || assoc_p (var))
     779             :         {
     780           0 :           unbind_variable (var->name);       /* XXX -- {array,assoc}_flush ? */
     781           0 :           return (0);
     782             :         }
     783             :       else
     784             :         return -2;      /* don't allow this to unset scalar variables */
     785             :     }
     786             : 
     787           0 :   if (assoc_p (var))
     788             :     {
     789           0 :       akey = expand_assignment_string_to_string (sub, 0);     /* [ */
     790           0 :       if (akey == 0 || *akey == 0)
     791             :         {
     792           0 :           builtin_error ("[%s]: %s", sub, _(bash_badsub_errmsg));
     793           0 :           FREE (akey);
     794           0 :           return -1;
     795             :         }
     796           0 :       assoc_remove (assoc_cell (var), akey);
     797           0 :       free (akey);
     798             :     }
     799           0 :   else if (array_p (var))
     800             :     {
     801           0 :       ind = array_expand_index (var, sub, len+1);
     802             :       /* negative subscripts to indexed arrays count back from end */
     803           0 :       if (ind < 0)
     804           0 :         ind = array_max_index (array_cell (var)) + 1 + ind;
     805           0 :       if (ind < 0)
     806             :         {
     807           0 :           builtin_error ("[%s]: %s", sub, _(bash_badsub_errmsg));
     808           0 :           return -1;
     809             :         }
     810           0 :       ae = array_remove (array_cell (var), ind);
     811           0 :       if (ae)
     812           0 :         array_dispose_element (ae);
     813             :     }
     814             :   else  /* array_p (var) == 0 && assoc_p (var) == 0 */
     815             :     {
     816           0 :       akey = this_command_name;
     817           0 :       ind = array_expand_index (var, sub, len+1);
     818           0 :       this_command_name = akey;
     819           0 :       if (ind == 0)
     820             :         {
     821           0 :           unbind_variable (var->name);
     822           0 :           return (0);
     823             :         }
     824             :       else
     825             :         return -2;      /* any subscript other than 0 is invalid with scalar variables */
     826             :     }
     827             : 
     828             :   return 0;
     829             : }
     830             : 
     831             : /* Format and output an array assignment in compound form VAR=(VALUES),
     832             :    suitable for re-use as input. */
     833             : void
     834        1481 : print_array_assignment (var, quoted)
     835             :      SHELL_VAR *var;
     836             :      int quoted;
     837             : {
     838        1481 :   char *vstr;
     839             : 
     840        1481 :   vstr = array_to_assign (array_cell (var), quoted);
     841             : 
     842        1481 :   if (vstr == 0)
     843        2148 :     printf ("%s=%s\n", var->name, quoted ? "'()'" : "()");
     844             :   else
     845             :     {
     846         407 :       printf ("%s=%s\n", var->name, vstr);
     847         407 :       free (vstr);
     848             :     }
     849        1481 : }
     850             : 
     851             : /* Format and output an associative array assignment in compound form
     852             :    VAR=(VALUES), suitable for re-use as input. */
     853             : void
     854         358 : print_assoc_assignment (var, quoted)
     855             :      SHELL_VAR *var;
     856             :      int quoted;
     857             : {
     858         358 :   char *vstr;
     859             : 
     860         358 :   vstr = assoc_to_assign (assoc_cell (var), quoted);
     861             : 
     862         358 :   if (vstr == 0)
     863         716 :     printf ("%s=%s\n", var->name, quoted ? "'()'" : "()");
     864             :   else
     865             :     {
     866           0 :       printf ("%s=%s\n", var->name, vstr);
     867           0 :       free (vstr);
     868             :     }
     869         358 : }
     870             : 
     871             : /***********************************************************************/
     872             : /*                                                                     */
     873             : /* Utility functions to manage arrays and their contents for expansion */
     874             : /*                                                                     */
     875             : /***********************************************************************/
     876             : 
     877             : /* Return 1 if NAME is a properly-formed array reference v[sub]. */
     878             : int
     879    28622100 : valid_array_reference (name, flags)
     880             :      const char *name;
     881             :      int flags;
     882             : {
     883    28622100 :   char *t;
     884    28622100 :   int r, len;
     885             : 
     886    28622100 :   t = mbschr (name, '[');       /* ] */
     887    28622100 :   if (t)
     888             :     {
     889          93 :       *t = '\0';
     890          93 :       r = legal_identifier (name);
     891          93 :       *t = '[';
     892          93 :       if (r == 0)
     893             :         return 0;
     894             :       /* Check for a properly-terminated non-blank subscript. */
     895          66 :       len = skipsubscript (t, 0, 0);
     896          66 :       if (t[len] != ']' || len == 1)
     897             :         return 0;
     898          44 :       if (t[len+1] != '\0')
     899             :         return 0;
     900          44 :       for (r = 1; r < len; r++)
     901          44 :         if (whitespace (t[r]) == 0)
     902             :           return 1;
     903             :       return 0;
     904             :     }
     905             :   return 0;
     906             : }
     907             : 
     908             : /* Expand the array index beginning at S and extending LEN characters. */
     909             : arrayind_t
     910           9 : array_expand_index (var, s, len)
     911             :      SHELL_VAR *var;
     912             :      char *s;
     913             :      int len;
     914             : {
     915           9 :   char *exp, *t, *savecmd;
     916           9 :   int expok;
     917           9 :   arrayind_t val;
     918             : 
     919           9 :   exp = (char *)xmalloc (len);
     920           9 :   strncpy (exp, s, len - 1);
     921           9 :   exp[len - 1] = '\0';
     922           9 :   t = expand_arith_string (exp, Q_DOUBLE_QUOTES|Q_ARITH|Q_ARRAYSUB);    /* XXX - Q_ARRAYSUB for future use */
     923           9 :   savecmd = this_command_name;
     924           9 :   this_command_name = (char *)NULL;
     925           9 :   val = evalexp (t, &expok);
     926           9 :   this_command_name = savecmd;
     927           9 :   free (t);
     928           9 :   free (exp);
     929           9 :   if (expok == 0)
     930             :     {
     931           0 :       last_command_exit_value = EXECUTION_FAILURE;
     932             : 
     933           0 :       if (no_longjmp_on_fatal_error)
     934             :         return 0;
     935           0 :       top_level_cleanup ();      
     936           0 :       jump_to_top_level (DISCARD);
     937             :     }
     938             :   return val;
     939             : }
     940             : 
     941             : /* Return the name of the variable specified by S without any subscript.
     942             :    If SUBP is non-null, return a pointer to the start of the subscript
     943             :    in *SUBP. If LENP is non-null, the length of the subscript is returned
     944             :    in *LENP.  This returns newly-allocated memory. */
     945             : char *
     946          20 : array_variable_name (s, subp, lenp)
     947             :      const char *s;
     948             :      char **subp;
     949             :      int *lenp;
     950             : {
     951          20 :   char *t, *ret;
     952          20 :   int ind, ni;
     953             : 
     954          20 :   t = mbschr (s, '[');
     955          20 :   if (t == 0)
     956             :     {
     957           0 :       if (subp)
     958           0 :         *subp = t;
     959           0 :       if (lenp)
     960           0 :         *lenp = 0;
     961           0 :       return ((char *)NULL);
     962             :     }
     963          20 :   ind = t - s;
     964          20 :   ni = skipsubscript (s, ind, 0);
     965          20 :   if (ni <= ind + 1 || s[ni] != ']')
     966             :     {
     967           0 :       err_badarraysub (s);
     968           0 :       if (subp)
     969           0 :         *subp = t;
     970           0 :       if (lenp)
     971           0 :         *lenp = 0;
     972           0 :       return ((char *)NULL);
     973             :     }
     974             : 
     975          20 :   *t = '\0';
     976          20 :   ret = savestring (s);
     977          20 :   *t++ = '[';           /* ] */
     978             : 
     979          20 :   if (subp)
     980          20 :     *subp = t;
     981          20 :   if (lenp)
     982          20 :     *lenp = ni - ind;
     983             : 
     984             :   return ret;
     985             : }
     986             : 
     987             : /* Return the variable specified by S without any subscript.  If SUBP is
     988             :    non-null, return a pointer to the start of the subscript in *SUBP.
     989             :    If LENP is non-null, the length of the subscript is returned in *LENP. */
     990             : SHELL_VAR *
     991          11 : array_variable_part (s, subp, lenp)
     992             :      const char *s;
     993             :      char **subp;
     994             :      int *lenp;
     995             : {
     996          11 :   char *t;
     997          11 :   SHELL_VAR *var;
     998             : 
     999          11 :   t = array_variable_name (s, subp, lenp);
    1000          11 :   if (t == 0)
    1001             :     return ((SHELL_VAR *)NULL);
    1002          11 :   var = find_variable (t);              /* XXX - handle namerefs here? */
    1003             : 
    1004          11 :   free (t);
    1005          11 :   return var;   /* now return invisible variables; caller must handle */
    1006             : }
    1007             : 
    1008             : #define INDEX_ERROR() \
    1009             :   do \
    1010             :     { \
    1011             :       if (var) \
    1012             :         err_badarraysub (var->name); \
    1013             :       else \
    1014             :         { \
    1015             :           t[-1] = '\0'; \
    1016             :           err_badarraysub (s); \
    1017             :           t[-1] = '[';  /* ] */\
    1018             :         } \
    1019             :       return ((char *)NULL); \
    1020             :     } \
    1021             :   while (0)
    1022             : 
    1023             : /* Return a string containing the elements in the array and subscript
    1024             :    described by S.  If the subscript is * or @, obeys quoting rules akin
    1025             :    to the expansion of $* and $@ including double quoting.  If RTYPE
    1026             :    is non-null it gets 1 if the array reference is name[*], 2 if the
    1027             :    reference is name[@], and 0 otherwise. */
    1028             : static char *
    1029          11 : array_value_internal (s, quoted, flags, rtype, indp)
    1030             :      const char *s;
    1031             :      int quoted, flags, *rtype;
    1032             :      arrayind_t *indp;
    1033             : {
    1034          11 :   int len;
    1035          11 :   arrayind_t ind = 0;
    1036          11 :   char *akey;
    1037          11 :   char *retval, *t, *temp;
    1038          11 :   WORD_LIST *l;
    1039          11 :   SHELL_VAR *var;
    1040             : 
    1041          11 :   var = array_variable_part (s, &t, &len);
    1042             : 
    1043             :   /* Expand the index, even if the variable doesn't exist, in case side
    1044             :      effects are needed, like ${w[i++]} where w is unset. */
    1045             : #if 0
    1046             :   if (var == 0)
    1047             :     return (char *)NULL;
    1048             : #endif
    1049             : 
    1050          11 :   if (len == 0)
    1051             :     return ((char *)NULL);      /* error message already printed */
    1052             : 
    1053             :   /* [ */
    1054          11 :   akey = 0;
    1055          11 :   if (ALL_ELEMENT_SUB (t[0]) && t[1] == ']')
    1056             :     {
    1057          11 :       if (rtype)
    1058          11 :         *rtype = (t[0] == '*') ? 1 : 2;
    1059          11 :       if ((flags & AV_ALLOWALL) == 0)
    1060             :         {
    1061           0 :           err_badarraysub (s);
    1062           0 :           return ((char *)NULL);
    1063             :         }
    1064          11 :       else if (var == 0 || value_cell (var) == 0)       /* XXX - check for invisible_p(var) ? */
    1065             :         return ((char *)NULL);
    1066           0 :       else if (array_p (var) == 0 && assoc_p (var) == 0)
    1067           0 :         l = add_string_to_list (value_cell (var), (WORD_LIST *)NULL);
    1068           0 :       else if (assoc_p (var))
    1069             :         {
    1070           0 :           l = assoc_to_word_list (assoc_cell (var));
    1071           0 :           if (l == (WORD_LIST *)NULL)
    1072             :             return ((char *)NULL);
    1073             :         }
    1074             :       else
    1075             :         {
    1076           0 :           l = array_to_word_list (array_cell (var));
    1077           0 :           if (l == (WORD_LIST *)NULL)
    1078             :             return ((char *) NULL);
    1079             :         }
    1080             : 
    1081           0 :       if (t[0] == '*' && (quoted & (Q_HERE_DOCUMENT|Q_DOUBLE_QUOTES)))
    1082             :         {
    1083           0 :           temp = string_list_dollar_star (l);
    1084           0 :           retval = quote_string (temp);         /* XXX - leak here */
    1085           0 :           free (temp);
    1086             :         }
    1087             :       else      /* ${name[@]} or unquoted ${name[*]} */
    1088             :         /* XXX - bash-4.4/bash-5.0 test AV_ASSIGNRHS and pass PF_ASSIGNRHS */
    1089           0 :         retval = string_list_dollar_at (l, quoted, (flags & AV_ASSIGNRHS) ? PF_ASSIGNRHS : 0);      /* XXX - leak here */
    1090             : 
    1091           0 :       dispose_words (l);
    1092             :     }
    1093             :   else
    1094             :     {
    1095           0 :       if (rtype)
    1096           0 :         *rtype = 0;
    1097           0 :       if (var == 0 || array_p (var) || assoc_p (var) == 0)
    1098             :         {
    1099           0 :           if ((flags & AV_USEIND) == 0 || indp == 0)
    1100             :             {
    1101           0 :               ind = array_expand_index (var, t, len);
    1102           0 :               if (ind < 0)
    1103             :                 {
    1104             :                   /* negative subscripts to indexed arrays count back from end */
    1105           0 :                   if (var && array_p (var))
    1106           0 :                     ind = array_max_index (array_cell (var)) + 1 + ind;
    1107           0 :                   if (ind < 0)
    1108           0 :                     INDEX_ERROR();
    1109             :                 }
    1110           0 :               if (indp)
    1111           0 :                 *indp = ind;
    1112             :             }
    1113           0 :           else if (indp)
    1114           0 :             ind = *indp;
    1115             :         }
    1116           0 :       else if (assoc_p (var))
    1117             :         {
    1118           0 :           t[len - 1] = '\0';
    1119           0 :           akey = expand_assignment_string_to_string (t, 0);     /* [ */
    1120           0 :           t[len - 1] = ']';
    1121           0 :           if (akey == 0 || *akey == 0)
    1122             :             {
    1123           0 :               FREE (akey);
    1124           0 :               INDEX_ERROR();
    1125             :             }
    1126             :         }
    1127             :      
    1128           0 :       if (var == 0 || value_cell (var) == 0)    /* XXX - check invisible_p(var) ? */
    1129             :         {
    1130           0 :           FREE (akey);
    1131           0 :           return ((char *)NULL);
    1132             :         }
    1133           0 :       if (array_p (var) == 0 && assoc_p (var) == 0)
    1134           0 :         return (ind == 0 ? value_cell (var) : (char *)NULL);
    1135           0 :       else if (assoc_p (var))
    1136             :         {
    1137           0 :           retval = assoc_reference (assoc_cell (var), akey);
    1138           0 :           free (akey);
    1139             :         }
    1140             :       else
    1141           0 :         retval = array_reference (array_cell (var), ind);
    1142             :     }
    1143             : 
    1144             :   return retval;
    1145             : }
    1146             : 
    1147             : /* Return a string containing the elements described by the array and
    1148             :    subscript contained in S, obeying quoting for subscripts * and @. */
    1149             : char *
    1150          11 : array_value (s, quoted, flags, rtype, indp)
    1151             :      const char *s;
    1152             :      int quoted, flags, *rtype;
    1153             :      arrayind_t *indp;
    1154             : {
    1155          11 :   return (array_value_internal (s, quoted, flags|AV_ALLOWALL, rtype, indp));
    1156             : }
    1157             : 
    1158             : /* Return the value of the array indexing expression S as a single string.
    1159             :    If (FLAGS & AV_ALLOWALL) is 0, do not allow `@' and `*' subscripts.  This
    1160             :    is used by other parts of the shell such as the arithmetic expression
    1161             :    evaluator in expr.c. */
    1162             : char *
    1163           0 : get_array_value (s, flags, rtype, indp)
    1164             :      const char *s;
    1165             :      int flags, *rtype;
    1166             :      arrayind_t *indp;
    1167             : {
    1168           0 :   return (array_value_internal (s, 0, flags, rtype, indp));
    1169             : }
    1170             : 
    1171             : char *
    1172           0 : array_keys (s, quoted)
    1173             :      char *s;
    1174             :      int quoted;
    1175             : {
    1176           0 :   int len;
    1177           0 :   char *retval, *t, *temp;
    1178           0 :   WORD_LIST *l;
    1179           0 :   SHELL_VAR *var;
    1180             : 
    1181           0 :   var = array_variable_part (s, &t, &len);
    1182             : 
    1183             :   /* [ */
    1184           0 :   if (var == 0 || ALL_ELEMENT_SUB (t[0]) == 0 || t[1] != ']')
    1185             :     return (char *)NULL;
    1186             : 
    1187           0 :   if (var_isset (var) == 0 || invisible_p (var))
    1188             :     return (char *)NULL;
    1189             : 
    1190           0 :   if (array_p (var) == 0 && assoc_p (var) == 0)
    1191           0 :     l = add_string_to_list ("0", (WORD_LIST *)NULL);
    1192           0 :   else if (assoc_p (var))
    1193           0 :     l = assoc_keys_to_word_list (assoc_cell (var));
    1194             :   else
    1195           0 :     l = array_keys_to_word_list (array_cell (var));
    1196           0 :   if (l == (WORD_LIST *)NULL)
    1197             :     return ((char *) NULL);
    1198             : 
    1199           0 :   if (t[0] == '*' && (quoted & (Q_HERE_DOCUMENT|Q_DOUBLE_QUOTES)))
    1200             :     {
    1201           0 :       temp = string_list_dollar_star (l);
    1202           0 :       retval = quote_string (temp);
    1203           0 :       free (temp);
    1204             :     }
    1205             :   else  /* ${!name[@]} or unquoted ${!name[*]} */
    1206           0 :     retval = string_list_dollar_at (l, quoted, 0);
    1207             : 
    1208           0 :   dispose_words (l);
    1209           0 :   return retval;
    1210             : }
    1211             : #endif /* ARRAY_VARS */

Generated by: LCOV version 1.14.0.6.4058