1 /* arrayfunc.c -- High-level array functions used by other parts of the shell. */
2 
3 /* Copyright (C) 2001-2020 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 "execute_cmd.h"
34 #include "pathexp.h"
35 
36 #include "shmbutil.h"
37 #if defined (HAVE_MBSTR_H) && defined (HAVE_MBSCHR)
38 #  include <mbstr.h>		/* mbschr */
39 #endif
40 
41 #include "builtins/common.h"
42 
43 #ifndef LBRACK
44 #  define LBRACK '['
45 #  define RBRACK ']'
46 #endif
47 
48 /* This variable means to not expand associative array subscripts more than
49    once, when performing variable expansion. */
50 int assoc_expand_once = 0;
51 
52 /* Ditto for indexed array subscripts -- currently unused */
53 int array_expand_once = 0;
54 
55 static SHELL_VAR *bind_array_var_internal PARAMS((SHELL_VAR *, arrayind_t, char *, char *, int));
56 static SHELL_VAR *assign_array_element_internal PARAMS((SHELL_VAR *, char *, char *, char *, int, char *, int));
57 
58 static void assign_assoc_from_kvlist PARAMS((SHELL_VAR *, WORD_LIST *, HASH_TABLE *, int));
59 
60 static char *quote_assign PARAMS((const char *));
61 static void quote_array_assignment_chars PARAMS((WORD_LIST *));
62 static char *quote_compound_array_word PARAMS((char *, int));
63 static char *array_value_internal PARAMS((const char *, int, int, int *, arrayind_t *));
64 
65 /* Standard error message to use when encountering an invalid array subscript */
66 const char * const bash_badsub_errmsg = N_("bad array subscript");
67 
68 /* **************************************************************** */
69 /*								    */
70 /*  Functions to manipulate array variables and perform assignments */
71 /*								    */
72 /* **************************************************************** */
73 
74 /* Convert a shell variable to an array variable.  The original value is
75    saved as array[0]. */
76 SHELL_VAR *
convert_var_to_array(var)77 convert_var_to_array (var)
78      SHELL_VAR *var;
79 {
80   char *oldval;
81   ARRAY *array;
82 
83   oldval = value_cell (var);
84   array = array_create ();
85   if (oldval)
86     array_insert (array, 0, oldval);
87 
88   FREE (value_cell (var));
89   var_setarray (var, array);
90 
91   /* these aren't valid anymore */
92   var->dynamic_value = (sh_var_value_func_t *)NULL;
93   var->assign_func = (sh_var_assign_func_t *)NULL;
94 
95   INVALIDATE_EXPORTSTR (var);
96   if (exported_p (var))
97     array_needs_making++;
98 
99   VSETATTR (var, att_array);
100   if (oldval)
101     VUNSETATTR (var, att_invisible);
102 
103   /* Make sure it's not marked as an associative array any more */
104   VUNSETATTR (var, att_assoc);
105 
106   /* Since namerefs can't be array variables, turn off nameref attribute */
107   VUNSETATTR (var, att_nameref);
108 
109   return var;
110 }
111 
112 /* Convert a shell variable to an array variable.  The original value is
113    saved as array[0]. */
114 SHELL_VAR *
convert_var_to_assoc(var)115 convert_var_to_assoc (var)
116      SHELL_VAR *var;
117 {
118   char *oldval;
119   HASH_TABLE *hash;
120 
121   oldval = value_cell (var);
122   hash = assoc_create (0);
123   if (oldval)
124     assoc_insert (hash, savestring ("0"), oldval);
125 
126   FREE (value_cell (var));
127   var_setassoc (var, hash);
128 
129   /* these aren't valid anymore */
130   var->dynamic_value = (sh_var_value_func_t *)NULL;
131   var->assign_func = (sh_var_assign_func_t *)NULL;
132 
133   INVALIDATE_EXPORTSTR (var);
134   if (exported_p (var))
135     array_needs_making++;
136 
137   VSETATTR (var, att_assoc);
138   if (oldval)
139     VUNSETATTR (var, att_invisible);
140 
141   /* Make sure it's not marked as an indexed array any more */
142   VUNSETATTR (var, att_array);
143 
144   /* Since namerefs can't be array variables, turn off nameref attribute */
145   VUNSETATTR (var, att_nameref);
146 
147   return var;
148 }
149 
150 char *
make_array_variable_value(entry,ind,key,value,flags)151 make_array_variable_value (entry, ind, key, value, flags)
152      SHELL_VAR *entry;
153      arrayind_t ind;
154      char *key;
155      char *value;
156      int flags;
157 {
158   SHELL_VAR *dentry;
159   char *newval;
160 
161   /* If we're appending, we need the old value of the array reference, so
162      fake out make_variable_value with a dummy SHELL_VAR */
163   if (flags & ASS_APPEND)
164     {
165       dentry = (SHELL_VAR *)xmalloc (sizeof (SHELL_VAR));
166       dentry->name = savestring (entry->name);
167       if (assoc_p (entry))
168 	newval = assoc_reference (assoc_cell (entry), key);
169       else
170 	newval = array_reference (array_cell (entry), ind);
171       if (newval)
172 	dentry->value = savestring (newval);
173       else
174 	{
175 	  dentry->value = (char *)xmalloc (1);
176 	  dentry->value[0] = '\0';
177 	}
178       dentry->exportstr = 0;
179       dentry->attributes = entry->attributes & ~(att_array|att_assoc|att_exported);
180       /* Leave the rest of the members uninitialized; the code doesn't look
181 	 at them. */
182       newval = make_variable_value (dentry, value, flags);
183       dispose_variable (dentry);
184     }
185   else
186     newval = make_variable_value (entry, value, flags);
187 
188   return newval;
189 }
190 
191 /* Assign HASH[KEY]=VALUE according to FLAGS. ENTRY is an associative array
192    variable; HASH is the hash table to assign into. HASH may or may not be
193    the hash table associated with ENTRY; if it's not, the caller takes care
194    of it.
195    XXX - make sure that any dynamic associative array variables recreate the
196    hash table on each assignment. BASH_CMDS and BASH_ALIASES already do this */
197 static SHELL_VAR *
bind_assoc_var_internal(entry,hash,key,value,flags)198 bind_assoc_var_internal (entry, hash, key, value, flags)
199      SHELL_VAR *entry;
200      HASH_TABLE *hash;
201      char *key;
202      char *value;
203      int flags;
204 {
205   char *newval;
206 
207   /* Use the existing array contents to expand the value */
208   newval = make_array_variable_value (entry, 0, key, value, flags);
209 
210   if (entry->assign_func)
211     (*entry->assign_func) (entry, newval, 0, key);
212   else
213     assoc_insert (hash, key, newval);
214 
215   FREE (newval);
216 
217   VUNSETATTR (entry, att_invisible);	/* no longer invisible */
218 
219   /* check mark_modified_variables if we ever want to export array vars */
220   return (entry);
221 }
222 
223 /* Perform ENTRY[IND]=VALUE or ENTRY[KEY]=VALUE. This is not called for every
224    assignment to an associative array; see assign_compound_array_list below. */
225 static SHELL_VAR *
bind_array_var_internal(entry,ind,key,value,flags)226 bind_array_var_internal (entry, ind, key, value, flags)
227      SHELL_VAR *entry;
228      arrayind_t ind;
229      char *key;
230      char *value;
231      int flags;
232 {
233   char *newval;
234 
235   newval = make_array_variable_value (entry, ind, key, value, flags);
236 
237   if (entry->assign_func)
238     (*entry->assign_func) (entry, newval, ind, key);
239   else if (assoc_p (entry))
240     assoc_insert (assoc_cell (entry), key, newval);
241   else
242     array_insert (array_cell (entry), ind, newval);
243   FREE (newval);
244 
245   VUNSETATTR (entry, att_invisible);	/* no longer invisible */
246 
247   /* check mark_modified_variables if we ever want to export array vars */
248   return (entry);
249 }
250 
251 /* Perform an array assignment name[ind]=value.  If NAME already exists and
252    is not an array, and IND is 0, perform name=value instead.  If NAME exists
253    and is not an array, and IND is not 0, convert it into an array with the
254    existing value as name[0].
255 
256    If NAME does not exist, just create an array variable, no matter what
257    IND's value may be. */
258 SHELL_VAR *
bind_array_variable(name,ind,value,flags)259 bind_array_variable (name, ind, value, flags)
260      char *name;
261      arrayind_t ind;
262      char *value;
263      int flags;
264 {
265   SHELL_VAR *entry;
266 
267   entry = find_shell_variable (name);
268 
269   if (entry == (SHELL_VAR *) 0)
270     {
271       /* Is NAME a nameref variable that points to an unset variable? */
272       entry = find_variable_nameref_for_create (name, 0);
273       if (entry == INVALID_NAMEREF_VALUE)
274 	return ((SHELL_VAR *)0);
275       if (entry && nameref_p (entry))
276 	entry = make_new_array_variable (nameref_cell (entry));
277     }
278   if (entry == (SHELL_VAR *) 0)
279     entry = make_new_array_variable (name);
280   else if ((readonly_p (entry) && (flags&ASS_FORCE) == 0) || noassign_p (entry))
281     {
282       if (readonly_p (entry))
283 	err_readonly (name);
284       return (entry);
285     }
286   else if (array_p (entry) == 0)
287     entry = convert_var_to_array (entry);
288 
289   /* ENTRY is an array variable, and ARRAY points to the value. */
290   return (bind_array_var_internal (entry, ind, 0, value, flags));
291 }
292 
293 SHELL_VAR *
bind_array_element(entry,ind,value,flags)294 bind_array_element (entry, ind, value, flags)
295      SHELL_VAR *entry;
296      arrayind_t ind;
297      char *value;
298      int flags;
299 {
300   return (bind_array_var_internal (entry, ind, 0, value, flags));
301 }
302 
303 SHELL_VAR *
bind_assoc_variable(entry,name,key,value,flags)304 bind_assoc_variable (entry, name, key, value, flags)
305      SHELL_VAR *entry;
306      char *name;
307      char *key;
308      char *value;
309      int flags;
310 {
311   if ((readonly_p (entry) && (flags&ASS_FORCE) == 0) || noassign_p (entry))
312     {
313       if (readonly_p (entry))
314 	err_readonly (name);
315       return (entry);
316     }
317 
318   return (bind_assoc_var_internal (entry, assoc_cell (entry), key, value, flags));
319 }
320 
321 /* Parse NAME, a lhs of an assignment statement of the form v[s], and
322    assign VALUE to that array element by calling bind_array_variable().
323    Flags are ASS_ assignment flags */
324 SHELL_VAR *
assign_array_element(name,value,flags)325 assign_array_element (name, value, flags)
326      char *name, *value;
327      int flags;
328 {
329   char *sub, *vname;
330   int sublen, isassoc;
331   SHELL_VAR *entry;
332 
333   vname = array_variable_name (name, (flags & ASS_NOEXPAND) != 0, &sub, &sublen);
334 
335   if (vname == 0)
336     return ((SHELL_VAR *)NULL);
337 
338   entry = find_variable (vname);
339   isassoc = entry && assoc_p (entry);
340 
341   if (((isassoc == 0 || (flags & ASS_NOEXPAND) == 0) && (ALL_ELEMENT_SUB (sub[0]) && sub[1] == ']')) || (sublen <= 1))
342     {
343       free (vname);
344       err_badarraysub (name);
345       return ((SHELL_VAR *)NULL);
346     }
347 
348   entry = assign_array_element_internal (entry, name, vname, sub, sublen, value, flags);
349 
350   free (vname);
351   return entry;
352 }
353 
354 static SHELL_VAR *
assign_array_element_internal(entry,name,vname,sub,sublen,value,flags)355 assign_array_element_internal (entry, name, vname, sub, sublen, value, flags)
356      SHELL_VAR *entry;
357      char *name;		/* only used for error messages */
358      char *vname;
359      char *sub;
360      int sublen;
361      char *value;
362      int flags;
363 {
364   char *akey;
365   arrayind_t ind;
366 
367   if (entry && assoc_p (entry))
368     {
369       sub[sublen-1] = '\0';
370       if ((flags & ASS_NOEXPAND) == 0)
371 	akey = expand_assignment_string_to_string (sub, 0);	/* [ */
372       else
373 	akey = savestring (sub);
374       sub[sublen-1] = ']';
375       if (akey == 0 || *akey == 0)
376 	{
377 	  err_badarraysub (name);
378 	  FREE (akey);
379 	  return ((SHELL_VAR *)NULL);
380 	}
381       entry = bind_assoc_variable (entry, vname, akey, value, flags);
382     }
383   else
384     {
385       ind = array_expand_index (entry, sub, sublen, 0);
386       /* negative subscripts to indexed arrays count back from end */
387       if (entry && ind < 0)
388 	ind = (array_p (entry) ? array_max_index (array_cell (entry)) : 0) + 1 + ind;
389       if (ind < 0)
390 	{
391 	  err_badarraysub (name);
392 	  return ((SHELL_VAR *)NULL);
393 	}
394       entry = bind_array_variable (vname, ind, value, flags);
395     }
396 
397   return (entry);
398 }
399 
400 /* Find the array variable corresponding to NAME.  If there is no variable,
401    create a new array variable.  If the variable exists but is not an array,
402    convert it to an indexed array.  If FLAGS&1 is non-zero, an existing
403    variable is checked for the readonly or noassign attribute in preparation
404    for assignment (e.g., by the `read' builtin).  If FLAGS&2 is non-zero, we
405    create an associative array. */
406 SHELL_VAR *
find_or_make_array_variable(name,flags)407 find_or_make_array_variable (name, flags)
408      char *name;
409      int flags;
410 {
411   SHELL_VAR *var;
412 
413   var = find_variable (name);
414   if (var == 0)
415     {
416       /* See if we have a nameref pointing to a variable that hasn't been
417 	 created yet. */
418       var = find_variable_last_nameref (name, 1);
419       if (var && nameref_p (var) && invisible_p (var))
420 	{
421 	  internal_warning (_("%s: removing nameref attribute"), name);
422 	  VUNSETATTR (var, att_nameref);
423 	}
424       if (var && nameref_p (var))
425 	{
426 	  if (valid_nameref_value (nameref_cell (var), 2) == 0)
427 	    {
428 	      sh_invalidid (nameref_cell (var));
429 	      return ((SHELL_VAR *)NULL);
430 	    }
431 	  var = (flags & 2) ? make_new_assoc_variable (nameref_cell (var)) : make_new_array_variable (nameref_cell (var));
432 	}
433     }
434 
435   if (var == 0)
436     var = (flags & 2) ? make_new_assoc_variable (name) : make_new_array_variable (name);
437   else if ((flags & 1) && (readonly_p (var) || noassign_p (var)))
438     {
439       if (readonly_p (var))
440 	err_readonly (name);
441       return ((SHELL_VAR *)NULL);
442     }
443   else if ((flags & 2) && array_p (var))
444     {
445       set_exit_status (EXECUTION_FAILURE);
446       report_error (_("%s: cannot convert indexed to associative array"), name);
447       return ((SHELL_VAR *)NULL);
448     }
449   else if (array_p (var) == 0 && assoc_p (var) == 0)
450     var = convert_var_to_array (var);
451 
452   return (var);
453 }
454 
455 /* Perform a compound assignment statement for array NAME, where VALUE is
456    the text between the parens:  NAME=( VALUE ) */
457 SHELL_VAR *
assign_array_from_string(name,value,flags)458 assign_array_from_string (name, value, flags)
459      char *name, *value;
460      int flags;
461 {
462   SHELL_VAR *var;
463   int vflags;
464 
465   vflags = 1;
466   if (flags & ASS_MKASSOC)
467     vflags |= 2;
468 
469   var = find_or_make_array_variable (name, vflags);
470   if (var == 0)
471     return ((SHELL_VAR *)NULL);
472 
473   return (assign_array_var_from_string (var, value, flags));
474 }
475 
476 /* Sequentially assign the indices of indexed array variable VAR from the
477    words in LIST. */
478 SHELL_VAR *
assign_array_var_from_word_list(var,list,flags)479 assign_array_var_from_word_list (var, list, flags)
480      SHELL_VAR *var;
481      WORD_LIST *list;
482      int flags;
483 {
484   register arrayind_t i;
485   register WORD_LIST *l;
486   ARRAY *a;
487 
488   a = array_cell (var);
489   i = (flags & ASS_APPEND) ? array_max_index (a) + 1 : 0;
490 
491   for (l = list; l; l = l->next, i++)
492     bind_array_var_internal (var, i, 0, l->word->word, flags & ~ASS_APPEND);
493 
494   VUNSETATTR (var, att_invisible);	/* no longer invisible */
495 
496   return var;
497 }
498 
499 WORD_LIST *
expand_compound_array_assignment(var,value,flags)500 expand_compound_array_assignment (var, value, flags)
501      SHELL_VAR *var;
502      char *value;
503      int flags;
504 {
505   WORD_LIST *list, *nlist;
506   char *val;
507   int ni;
508 
509   /* This condition is true when invoked from the declare builtin with a
510      command like
511 	declare -a d='([1]="" [2]="bdef" [5]="hello world" "test")' */
512   if (*value == '(')	/*)*/
513     {
514       ni = 1;
515       val = extract_array_assignment_list (value, &ni);
516       if (val == 0)
517 	return (WORD_LIST *)NULL;
518     }
519   else
520     val = value;
521 
522   /* Expand the value string into a list of words, performing all the
523      shell expansions including pathname generation and word splitting. */
524   /* First we split the string on whitespace, using the shell parser
525      (ksh93 seems to do this). */
526   list = parse_string_to_word_list (val, 1, "array assign");
527 
528   /* Note that we defer expansion of the assignment statements for associative
529      arrays here, so we don't have to scan the subscript and find the ending
530      bracket twice. See the caller below. */
531   if (var && assoc_p (var))
532     {
533       if (val != value)
534 	free (val);
535       return list;
536     }
537 
538   /* If we're using [subscript]=value, we need to quote each [ and ] to
539      prevent unwanted filename expansion.  This doesn't need to be done
540      for associative array expansion, since that uses a different expansion
541      function (see assign_compound_array_list below). */
542   if (list)
543     quote_array_assignment_chars (list);
544 
545   /* Now that we've split it, perform the shell expansions on each
546      word in the list. */
547   nlist = list ? expand_words_no_vars (list) : (WORD_LIST *)NULL;
548 
549   dispose_words (list);
550 
551   if (val != value)
552     free (val);
553 
554   return nlist;
555 }
556 
557 #if ASSOC_KVPAIR_ASSIGNMENT
558 static void
assign_assoc_from_kvlist(var,nlist,h,flags)559 assign_assoc_from_kvlist (var, nlist, h, flags)
560      SHELL_VAR *var;
561      WORD_LIST *nlist;
562      HASH_TABLE *h;
563      int flags;
564 {
565   WORD_LIST *list;
566   char *akey, *aval, *k, *v;
567 
568   for (list = nlist; list; list = list->next)
569     {
570       k = list->word->word;
571       v = list->next ? list->next->word->word : 0;
572 
573       if (list->next)
574         list = list->next;
575 
576       akey = expand_assignment_string_to_string (k, 0);
577       if (akey == 0 || *akey == 0)
578 	{
579 	  err_badarraysub (k);
580 	  FREE (akey);
581 	  continue;
582 	}
583 
584       aval = expand_assignment_string_to_string (v, 0);
585       if (aval == 0)
586 	{
587 	  aval = (char *)xmalloc (1);
588 	  aval[0] = '\0';	/* like do_assignment_internal */
589 	}
590 
591       bind_assoc_var_internal (var, h, akey, aval, flags);
592       free (aval);
593     }
594 }
595 
596 /* Return non-zero if L appears to be a key-value pair associative array
597    compound assignment. */
598 int
kvpair_assignment_p(l)599 kvpair_assignment_p (l)
600      WORD_LIST *l;
601 {
602   return (l && (l->word->flags & W_ASSIGNMENT) == 0 && l->word->word[0] != '[');	/*]*/
603 }
604 
605 char *
expand_and_quote_kvpair_word(w)606 expand_and_quote_kvpair_word (w)
607      char *w;
608 {
609   char *t, *r;
610 
611   t = w ? expand_assignment_string_to_string (w, 0) : 0;
612   r = sh_single_quote (t ? t : "");
613   free (t);
614   return r;
615 }
616 #endif
617 
618 /* Callers ensure that VAR is not NULL. Associative array assignments have not
619    been expanded when this is called, or have been expanded once and single-
620    quoted, so we don't have to scan through an unquoted expanded subscript to
621    find the ending bracket; indexed array assignments have been expanded and
622    possibly single-quoted to prevent further expansion.
623 
624    If this is an associative array, we perform the assignments into NHASH and
625    set NHASH to be the value of VAR after processing the assignments in NLIST */
626 void
assign_compound_array_list(var,nlist,flags)627 assign_compound_array_list (var, nlist, flags)
628      SHELL_VAR *var;
629      WORD_LIST *nlist;
630      int flags;
631 {
632   ARRAY *a;
633   HASH_TABLE *h, *nhash;
634   WORD_LIST *list;
635   char *w, *val, *nval, *savecmd;
636   int len, iflags, free_val;
637   arrayind_t ind, last_ind;
638   char *akey;
639 
640   a = (var && array_p (var)) ? array_cell (var) : (ARRAY *)0;
641   nhash = h = (var && assoc_p (var)) ? assoc_cell (var) : (HASH_TABLE *)0;
642 
643   akey = (char *)0;
644   ind = 0;
645 
646   /* Now that we are ready to assign values to the array, kill the existing
647      value. */
648   if ((flags & ASS_APPEND) == 0)
649     {
650       if (a && array_p (var))
651 	array_flush (a);
652       else if (h && assoc_p (var))
653 	nhash = assoc_create (h->nbuckets);
654     }
655 
656   last_ind = (a && (flags & ASS_APPEND)) ? array_max_index (a) + 1 : 0;
657 
658 #if ASSOC_KVPAIR_ASSIGNMENT
659   if (assoc_p (var) && kvpair_assignment_p (nlist))
660     {
661       iflags = flags & ~ASS_APPEND;
662       assign_assoc_from_kvlist (var, nlist, nhash, iflags);
663       if (nhash && nhash != h)
664 	{
665 	  h = assoc_cell (var);
666 	  var_setassoc (var, nhash);
667 	  assoc_dispose (h);
668 	}
669       return;
670     }
671 #endif
672 
673   for (list = nlist; list; list = list->next)
674     {
675       /* Don't allow var+=(values) to make assignments in VALUES append to
676 	 existing values by default. */
677       iflags = flags & ~ASS_APPEND;
678       w = list->word->word;
679 
680       /* We have a word of the form [ind]=value */
681       if ((list->word->flags & W_ASSIGNMENT) && w[0] == '[')
682 	{
683 	  /* Don't have to handle embedded quotes specially any more, since
684 	     associative array subscripts have not been expanded yet (see
685 	     above). */
686 	  len = skipsubscript (w, 0, 0);
687 
688 	  /* XXX - changes for `+=' */
689  	  if (w[len] != ']' || (w[len+1] != '=' && (w[len+1] != '+' || w[len+2] != '=')))
690 	    {
691 	      if (assoc_p (var))
692 		{
693 		  err_badarraysub (w);
694 		  continue;
695 		}
696 	      nval = make_variable_value (var, w, flags);
697 	      if (var->assign_func)
698 		(*var->assign_func) (var, nval, last_ind, 0);
699 	      else
700 		array_insert (a, last_ind, nval);
701 	      FREE (nval);
702 	      last_ind++;
703 	      continue;
704 	    }
705 
706 	  if (len == 1)
707 	    {
708 	      err_badarraysub (w);
709 	      continue;
710 	    }
711 
712 	  if (ALL_ELEMENT_SUB (w[1]) && len == 2)
713 	    {
714 	      set_exit_status (EXECUTION_FAILURE);
715 	      if (assoc_p (var))
716 		report_error (_("%s: invalid associative array key"), w);
717 	      else
718 		report_error (_("%s: cannot assign to non-numeric index"), w);
719 	      continue;
720 	    }
721 
722 	  if (array_p (var))
723 	    {
724 	      ind = array_expand_index (var, w + 1, len, 0);
725 	      /* negative subscripts to indexed arrays count back from end */
726 	      if (ind < 0)
727 		ind = array_max_index (array_cell (var)) + 1 + ind;
728 	      if (ind < 0)
729 		{
730 		  err_badarraysub (w);
731 		  continue;
732 		}
733 
734 	      last_ind = ind;
735 	    }
736 	  else if (assoc_p (var))
737 	    {
738 	      /* This is not performed above, see expand_compound_array_assignment */
739 	      w[len] = '\0';	/*[*/
740 	      akey = expand_assignment_string_to_string (w+1, 0);
741 	      w[len] = ']';
742 	      /* And we need to expand the value also, see below */
743 	      if (akey == 0 || *akey == 0)
744 		{
745 		  err_badarraysub (w);
746 		  FREE (akey);
747 		  continue;
748 		}
749 	    }
750 
751 	  /* XXX - changes for `+=' -- just accept the syntax.  ksh93 doesn't do this */
752 	  if (w[len + 1] == '+' && w[len + 2] == '=')
753 	    {
754 	      iflags |= ASS_APPEND;
755 	      val = w + len + 3;
756 	    }
757 	  else
758 	    val = w + len + 2;
759 	}
760       else if (assoc_p (var))
761 	{
762 	  set_exit_status (EXECUTION_FAILURE);
763 	  report_error (_("%s: %s: must use subscript when assigning associative array"), var->name, w);
764 	  continue;
765 	}
766       else		/* No [ind]=value, just a stray `=' */
767 	{
768 	  ind = last_ind;
769 	  val = w;
770 	}
771 
772       free_val = 0;
773       /* See above; we need to expand the value here */
774       if (assoc_p (var))
775 	{
776 	  val = expand_assignment_string_to_string (val, 0);
777 	  if (val == 0)
778 	    {
779 	      val = (char *)xmalloc (1);
780 	      val[0] = '\0';	/* like do_assignment_internal */
781 	    }
782 	  free_val = 1;
783 	}
784 
785       savecmd = this_command_name;
786       if (integer_p (var))
787 	this_command_name = (char *)NULL;	/* no command name for errors */
788       if (assoc_p (var))
789 	bind_assoc_var_internal (var, nhash, akey, val, iflags);
790       else
791 	bind_array_var_internal (var, ind, akey, val, iflags);
792       last_ind++;
793       this_command_name = savecmd;
794 
795       if (free_val)
796 	free (val);
797     }
798 
799   if (assoc_p (var) && nhash && nhash != h)
800     {
801       h = assoc_cell (var);
802       var_setassoc (var, nhash);
803       assoc_dispose (h);
804     }
805 }
806 
807 /* Perform a compound array assignment:  VAR->name=( VALUE ).  The
808    VALUE has already had the parentheses stripped. */
809 SHELL_VAR *
assign_array_var_from_string(var,value,flags)810 assign_array_var_from_string (var, value, flags)
811      SHELL_VAR *var;
812      char *value;
813      int flags;
814 {
815   WORD_LIST *nlist;
816 
817   if (value == 0)
818     return var;
819 
820   nlist = expand_compound_array_assignment (var, value, flags);
821   assign_compound_array_list (var, nlist, flags);
822 
823   if (nlist)
824     dispose_words (nlist);
825 
826   if (var)
827     VUNSETATTR (var, att_invisible);	/* no longer invisible */
828 
829   return (var);
830 }
831 
832 /* Quote globbing chars and characters in $IFS before the `=' in an assignment
833    statement (usually a compound array assignment) to protect them from
834    unwanted filename expansion or word splitting. */
835 static char *
quote_assign(string)836 quote_assign (string)
837      const char *string;
838 {
839   size_t slen;
840   int saw_eq;
841   char *temp, *t, *subs;
842   const char *s, *send;
843   int ss, se;
844   DECLARE_MBSTATE;
845 
846   slen = strlen (string);
847   send = string + slen;
848 
849   t = temp = (char *)xmalloc (slen * 2 + 1);
850   saw_eq = 0;
851   for (s = string; *s; )
852     {
853       if (*s == '=')
854 	saw_eq = 1;
855       if (saw_eq == 0 && *s == '[')		/* looks like a subscript */
856 	{
857 	  ss = s - string;
858 	  se = skipsubscript (string, ss, 0);
859 	  subs = substring (s, ss, se);
860 	  *t++ = '\\';
861 	  strcpy (t, subs);
862 	  t += se - ss;
863 	  *t++ = '\\';
864 	  *t++ = ']';
865 	  s += se + 1;
866 	  free (subs);
867 	  continue;
868 	}
869       if (saw_eq == 0 && (glob_char_p (s) || isifs (*s)))
870 	*t++ = '\\';
871 
872       COPY_CHAR_P (t, s, send);
873     }
874   *t = '\0';
875   return temp;
876 }
877 
878 /* Take a word W of the form [IND]=VALUE and transform it to ['IND']='VALUE'
879    to prevent further expansion. This is called for compound assignments to
880    indexed arrays. W has already undergone word expansions. If W has no [IND]=,
881    just single-quote and return it. */
882 static char *
quote_compound_array_word(w,type)883 quote_compound_array_word (w, type)
884      char *w;
885      int type;
886 {
887   char *nword, *sub, *value, *t;
888   int ind, wlen, i;
889 
890   if (w[0] != LBRACK)
891     return (sh_single_quote (w));
892   ind = skipsubscript (w, 0, 0);
893   if (w[ind] != RBRACK)
894     return (sh_single_quote (w));
895 
896   wlen = strlen (w);
897   w[ind] = '\0';
898   sub = sh_single_quote (w+1);
899   w[ind] = RBRACK;
900 
901   nword = xmalloc (wlen * 4 + 5);	/* wlen*4 is max single quoted length */
902   nword[0] = LBRACK;
903   i = STRLEN (sub);
904   memcpy (nword+1, sub, i);
905   i++;				/* accommodate the opening LBRACK */
906   nword[i++] = w[ind++];	/* RBRACK */
907   if (w[ind] == '+')
908     nword[i++] = w[ind++];
909   nword[i++] = w[ind++];
910   value = sh_single_quote (w + ind);
911   strcpy (nword + i, value);
912 
913   return nword;
914 }
915 
916 /* Expand the key and value in W, which is of the form [KEY]=VALUE, and
917    reconstruct W with the expanded and single-quoted version:
918    ['expanded-key']='expanded-value'. If there is no [KEY]=, single-quote the
919    word and return it. Very similar to previous function, but does not assume
920    W has already been expanded, and expands the KEY and VALUE separately.
921    Used for compound assignments to associative arrays that are arguments to
922    declaration builtins (declare -A a=( list )). */
923 char *
expand_and_quote_assoc_word(w,type)924 expand_and_quote_assoc_word (w, type)
925      char *w;
926      int type;
927 {
928   char *nword, *key, *value, *t;
929   int ind, wlen, i;
930 
931   if (w[0] != LBRACK)
932     return (sh_single_quote (w));
933   ind = skipsubscript (w, 0, 0);
934   if (w[ind] != RBRACK)
935     return (sh_single_quote (w));
936 
937   w[ind] = '\0';
938   t = expand_assignment_string_to_string (w+1, 0);
939   w[ind] = RBRACK;
940   key = sh_single_quote (t ? t : "");
941   free (t);
942 
943   wlen = STRLEN (key);
944   nword = xmalloc (wlen + 5);
945   nword[0] = LBRACK;
946   memcpy (nword+1, key, wlen);
947   i = wlen + 1;			/* accommodate the opening LBRACK */
948 
949   nword[i++] = w[ind++];	/* RBRACK */
950   if (w[ind] == '+')
951     nword[i++] = w[ind++];
952   nword[i++] = w[ind++];
953 
954   t = expand_assignment_string_to_string (w+ind, 0);
955   value = sh_single_quote (t ? t : "");
956   free (t);
957   nword = xrealloc (nword, wlen + 5 + STRLEN (value));
958   strcpy (nword + i, value);
959 
960   free (key);
961   free (value);
962 
963   return nword;
964 }
965 
966 /* For each word in a compound array assignment, if the word looks like
967    [ind]=value, single-quote ind and value, but leave the brackets and
968    the = sign (and any `+') alone. If it's not an assignment, just single-
969    quote the word. This is used for indexed arrays. */
970 void
quote_compound_array_list(list,type)971 quote_compound_array_list (list, type)
972      WORD_LIST *list;
973      int type;
974 {
975   char *t;
976   WORD_LIST *l;
977 
978   for (l = list; l; l = l->next)
979     {
980       if (l->word == 0 || l->word->word == 0)
981 	continue;	/* should not happen, but just in case... */
982       if ((l->word->flags & W_ASSIGNMENT) == 0)
983 	t = sh_single_quote (l->word->word);
984       else
985 	t = quote_compound_array_word (l->word->word, type);
986       free (l->word->word);
987       l->word->word = t;
988     }
989 }
990 
991 /* For each word in a compound array assignment, if the word looks like
992    [ind]=value, quote globbing chars and characters in $IFS before the `='. */
993 static void
quote_array_assignment_chars(list)994 quote_array_assignment_chars (list)
995      WORD_LIST *list;
996 {
997   char *nword;
998   WORD_LIST *l;
999 
1000   for (l = list; l; l = l->next)
1001     {
1002       if (l->word == 0 || l->word->word == 0 || l->word->word[0] == '\0')
1003 	continue;	/* should not happen, but just in case... */
1004       /* Don't bother if it hasn't been recognized as an assignment or
1005 	 doesn't look like [ind]=value */
1006       if ((l->word->flags & W_ASSIGNMENT) == 0)
1007 	continue;
1008       if (l->word->word[0] != '[' || mbschr (l->word->word, '=') == 0) /* ] */
1009 	continue;
1010 
1011       nword = quote_assign (l->word->word);
1012       free (l->word->word);
1013       l->word->word = nword;
1014       l->word->flags |= W_NOGLOB;	/* XXX - W_NOSPLIT also? */
1015     }
1016 }
1017 
1018 /* skipsubscript moved to subst.c to use private functions. 2009/02/24. */
1019 
1020 /* This function is called with SUB pointing to just after the beginning
1021    `[' of an array subscript and removes the array element to which SUB
1022    expands from array VAR.  A subscript of `*' or `@' unsets the array. */
1023 /* If FLAGS&1 we don't expand the subscript; we just use it as-is. */
1024 int
unbind_array_element(var,sub,flags)1025 unbind_array_element (var, sub, flags)
1026      SHELL_VAR *var;
1027      char *sub;
1028      int flags;
1029 {
1030   int len;
1031   arrayind_t ind;
1032   char *akey;
1033   ARRAY_ELEMENT *ae;
1034 
1035   len = skipsubscript (sub, 0, (flags&1) || (var && assoc_p(var)));	/* XXX */
1036   if (sub[len] != ']' || len == 0)
1037     {
1038       builtin_error ("%s[%s: %s", var->name, sub, _(bash_badsub_errmsg));
1039       return -1;
1040     }
1041   sub[len] = '\0';
1042 
1043   if (ALL_ELEMENT_SUB (sub[0]) && sub[1] == 0)
1044     {
1045       if (array_p (var) || assoc_p (var))
1046 	{
1047 	  unbind_variable (var->name);	/* XXX -- {array,assoc}_flush ? */
1048 	  return (0);
1049 	}
1050       else
1051 	return -2;	/* don't allow this to unset scalar variables */
1052     }
1053 
1054   if (assoc_p (var))
1055     {
1056       akey = (flags & 1) ? sub : expand_assignment_string_to_string (sub, 0);
1057       if (akey == 0 || *akey == 0)
1058 	{
1059 	  builtin_error ("[%s]: %s", sub, _(bash_badsub_errmsg));
1060 	  FREE (akey);
1061 	  return -1;
1062 	}
1063       assoc_remove (assoc_cell (var), akey);
1064       if (akey != sub)
1065 	free (akey);
1066     }
1067   else if (array_p (var))
1068     {
1069       ind = array_expand_index (var, sub, len+1, 0);
1070       /* negative subscripts to indexed arrays count back from end */
1071       if (ind < 0)
1072 	ind = array_max_index (array_cell (var)) + 1 + ind;
1073       if (ind < 0)
1074 	{
1075 	  builtin_error ("[%s]: %s", sub, _(bash_badsub_errmsg));
1076 	  return -1;
1077 	}
1078       ae = array_remove (array_cell (var), ind);
1079       if (ae)
1080 	array_dispose_element (ae);
1081     }
1082   else	/* array_p (var) == 0 && assoc_p (var) == 0 */
1083     {
1084       akey = this_command_name;
1085       ind = array_expand_index (var, sub, len+1, 0);
1086       this_command_name = akey;
1087       if (ind == 0)
1088 	{
1089 	  unbind_variable (var->name);
1090 	  return (0);
1091 	}
1092       else
1093 	return -2;	/* any subscript other than 0 is invalid with scalar variables */
1094     }
1095 
1096   return 0;
1097 }
1098 
1099 /* Format and output an array assignment in compound form VAR=(VALUES),
1100    suitable for re-use as input. */
1101 void
print_array_assignment(var,quoted)1102 print_array_assignment (var, quoted)
1103      SHELL_VAR *var;
1104      int quoted;
1105 {
1106   char *vstr;
1107 
1108   vstr = array_to_assign (array_cell (var), quoted);
1109 
1110   if (vstr == 0)
1111     printf ("%s=%s\n", var->name, quoted ? "'()'" : "()");
1112   else
1113     {
1114       printf ("%s=%s\n", var->name, vstr);
1115       free (vstr);
1116     }
1117 }
1118 
1119 /* Format and output an associative array assignment in compound form
1120    VAR=(VALUES), suitable for re-use as input. */
1121 void
print_assoc_assignment(var,quoted)1122 print_assoc_assignment (var, quoted)
1123      SHELL_VAR *var;
1124      int quoted;
1125 {
1126   char *vstr;
1127 
1128   vstr = assoc_to_assign (assoc_cell (var), quoted);
1129 
1130   if (vstr == 0)
1131     printf ("%s=%s\n", var->name, quoted ? "'()'" : "()");
1132   else
1133     {
1134       printf ("%s=%s\n", var->name, vstr);
1135       free (vstr);
1136     }
1137 }
1138 
1139 /***********************************************************************/
1140 /*								       */
1141 /* Utility functions to manage arrays and their contents for expansion */
1142 /*								       */
1143 /***********************************************************************/
1144 
1145 /* Return 1 if NAME is a properly-formed array reference v[sub]. */
1146 
1147 /* We need to reserve 1 for FLAGS, which we pass to skipsubscript. */
1148 int
valid_array_reference(name,flags)1149 valid_array_reference (name, flags)
1150      const char *name;
1151      int flags;
1152 {
1153   char *t;
1154   int r, len, isassoc;
1155   SHELL_VAR *entry;
1156 
1157   t = mbschr (name, '[');	/* ] */
1158   isassoc = 0;
1159   if (t)
1160     {
1161       *t = '\0';
1162       r = legal_identifier (name);
1163       if (flags & VA_NOEXPAND)	/* Don't waste a lookup if we don't need one */
1164 	isassoc = (entry = find_variable (name)) && assoc_p (entry);
1165       *t = '[';
1166       if (r == 0)
1167 	return 0;
1168 
1169       if (isassoc && ((flags & (VA_NOEXPAND|VA_ONEWORD)) == (VA_NOEXPAND|VA_ONEWORD)))
1170 	len = strlen (t) - 1;
1171       else if (isassoc)
1172 	len = skipsubscript (t, 0, flags&VA_NOEXPAND);	/* VA_NOEXPAND must be 1 */
1173       else
1174 	/* Check for a properly-terminated non-null subscript. */
1175 	len = skipsubscript (t, 0, 0);		/* arithmetic expression */
1176 
1177       if (t[len] != ']' || len == 1 || t[len+1] != '\0')
1178 	return 0;
1179 
1180 #if 0
1181       /* Could check and allow subscripts consisting only of whitespace for
1182 	 existing associative arrays, using isassoc */
1183       for (r = 1; r < len; r++)
1184 	if (whitespace (t[r]) == 0)
1185 	  return 1;
1186       return 0;
1187 #else
1188       /* This allows blank subscripts */
1189       return 1;
1190 #endif
1191     }
1192   return 0;
1193 }
1194 
1195 /* Expand the array index beginning at S and extending LEN characters. */
1196 arrayind_t
array_expand_index(var,s,len,flags)1197 array_expand_index (var, s, len, flags)
1198      SHELL_VAR *var;
1199      char *s;
1200      int len;
1201      int flags;
1202 {
1203   char *exp, *t, *savecmd;
1204   int expok;
1205   arrayind_t val;
1206 
1207   exp = (char *)xmalloc (len);
1208   strncpy (exp, s, len - 1);
1209   exp[len - 1] = '\0';
1210 #if 0	/* TAG: maybe bash-5.2 */
1211   if ((flags & AV_NOEXPAND) == 0)
1212     t = expand_arith_string (exp, Q_DOUBLE_QUOTES|Q_ARITH|Q_ARRAYSUB);	/* XXX - Q_ARRAYSUB for future use */
1213   else
1214     t = exp;
1215 #endif
1216   t = expand_arith_string (exp, Q_DOUBLE_QUOTES|Q_ARITH|Q_ARRAYSUB);	/* XXX - Q_ARRAYSUB for future use */
1217   savecmd = this_command_name;
1218   this_command_name = (char *)NULL;
1219   val = evalexp (t, EXP_EXPANDED, &expok);	/* XXX - was 0 but we expanded exp already */
1220   this_command_name = savecmd;
1221   if (t != exp)
1222     free (t);
1223   free (exp);
1224   if (expok == 0)
1225     {
1226       set_exit_status (EXECUTION_FAILURE);
1227 
1228       if (no_longjmp_on_fatal_error)
1229 	return 0;
1230       top_level_cleanup ();
1231       jump_to_top_level (DISCARD);
1232     }
1233   return val;
1234 }
1235 
1236 /* Return the name of the variable specified by S without any subscript.
1237    If SUBP is non-null, return a pointer to the start of the subscript
1238    in *SUBP. If LENP is non-null, the length of the subscript is returned
1239    in *LENP.  This returns newly-allocated memory. */
1240 char *
array_variable_name(s,flags,subp,lenp)1241 array_variable_name (s, flags, subp, lenp)
1242      const char *s;
1243      int flags;
1244      char **subp;
1245      int *lenp;
1246 {
1247   char *t, *ret;
1248   int ind, ni;
1249 
1250   t = mbschr (s, '[');
1251   if (t == 0)
1252     {
1253       if (subp)
1254       	*subp = t;
1255       if (lenp)
1256 	*lenp = 0;
1257       return ((char *)NULL);
1258     }
1259   ind = t - s;
1260   ni = skipsubscript (s, ind, flags);	/* XXX - was 0 not flags */
1261   if (ni <= ind + 1 || s[ni] != ']')
1262     {
1263       err_badarraysub (s);
1264       if (subp)
1265       	*subp = t;
1266       if (lenp)
1267 	*lenp = 0;
1268       return ((char *)NULL);
1269     }
1270 
1271   *t = '\0';
1272   ret = savestring (s);
1273   *t++ = '[';		/* ] */
1274 
1275   if (subp)
1276     *subp = t;
1277   if (lenp)
1278     *lenp = ni - ind;
1279 
1280   return ret;
1281 }
1282 
1283 /* Return the variable specified by S without any subscript.  If SUBP is
1284    non-null, return a pointer to the start of the subscript in *SUBP.
1285    If LENP is non-null, the length of the subscript is returned in *LENP. */
1286 SHELL_VAR *
array_variable_part(s,flags,subp,lenp)1287 array_variable_part (s, flags, subp, lenp)
1288      const char *s;
1289      int flags;
1290      char **subp;
1291      int *lenp;
1292 {
1293   char *t;
1294   SHELL_VAR *var;
1295 
1296   t = array_variable_name (s, flags, subp, lenp);
1297   if (t == 0)
1298     return ((SHELL_VAR *)NULL);
1299   var = find_variable (t);		/* XXX - handle namerefs here? */
1300 
1301   free (t);
1302   return var;	/* now return invisible variables; caller must handle */
1303 }
1304 
1305 #define INDEX_ERROR() \
1306   do \
1307     { \
1308       if (var) \
1309 	err_badarraysub (var->name); \
1310       else \
1311 	{ \
1312 	  t[-1] = '\0'; \
1313 	  err_badarraysub (s); \
1314 	  t[-1] = '[';	/* ] */\
1315 	} \
1316       return ((char *)NULL); \
1317     } \
1318   while (0)
1319 
1320 /* Return a string containing the elements in the array and subscript
1321    described by S.  If the subscript is * or @, obeys quoting rules akin
1322    to the expansion of $* and $@ including double quoting.  If RTYPE
1323    is non-null it gets 1 if the array reference is name[*], 2 if the
1324    reference is name[@], and 0 otherwise. */
1325 static char *
array_value_internal(s,quoted,flags,rtype,indp)1326 array_value_internal (s, quoted, flags, rtype, indp)
1327      const char *s;
1328      int quoted, flags, *rtype;
1329      arrayind_t *indp;
1330 {
1331   int len;
1332   arrayind_t ind;
1333   char *akey;
1334   char *retval, *t, *temp;
1335   WORD_LIST *l;
1336   SHELL_VAR *var;
1337 
1338   var = array_variable_part (s, (flags&AV_NOEXPAND) ? 1 : 0, &t, &len);	/* XXX */
1339 
1340   /* Expand the index, even if the variable doesn't exist, in case side
1341      effects are needed, like ${w[i++]} where w is unset. */
1342 #if 0
1343   if (var == 0)
1344     return (char *)NULL;
1345 #endif
1346 
1347   if (len == 0)
1348     return ((char *)NULL);	/* error message already printed */
1349 
1350   /* [ */
1351   akey = 0;
1352   if (ALL_ELEMENT_SUB (t[0]) && t[1] == ']')
1353     {
1354       if (rtype)
1355 	*rtype = (t[0] == '*') ? 1 : 2;
1356       if ((flags & AV_ALLOWALL) == 0)
1357 	{
1358 	  err_badarraysub (s);
1359 	  return ((char *)NULL);
1360 	}
1361       else if (var == 0 || value_cell (var) == 0)	/* XXX - check for invisible_p(var) ? */
1362 	return ((char *)NULL);
1363       else if (invisible_p (var))
1364 	return ((char *)NULL);
1365       else if (array_p (var) == 0 && assoc_p (var) == 0)
1366 	l = add_string_to_list (value_cell (var), (WORD_LIST *)NULL);
1367       else if (assoc_p (var))
1368 	{
1369 	  l = assoc_to_word_list (assoc_cell (var));
1370 	  if (l == (WORD_LIST *)NULL)
1371 	    return ((char *)NULL);
1372 	}
1373       else
1374 	{
1375 	  l = array_to_word_list (array_cell (var));
1376 	  if (l == (WORD_LIST *)NULL)
1377 	    return ((char *) NULL);
1378 	}
1379 
1380       /* Caller of array_value takes care of inspecting rtype and duplicating
1381 	 retval if rtype == 0, so this is not a memory leak */
1382       if (t[0] == '*' && (quoted & (Q_HERE_DOCUMENT|Q_DOUBLE_QUOTES)))
1383 	{
1384 	  temp = string_list_dollar_star (l, quoted, (flags & AV_ASSIGNRHS) ? PF_ASSIGNRHS : 0);
1385 	  retval = quote_string (temp);
1386 	  free (temp);
1387 	}
1388       else	/* ${name[@]} or unquoted ${name[*]} */
1389 	retval = string_list_dollar_at (l, quoted, (flags & AV_ASSIGNRHS) ? PF_ASSIGNRHS : 0);
1390 
1391       dispose_words (l);
1392     }
1393   else
1394     {
1395       if (rtype)
1396 	*rtype = 0;
1397       if (var == 0 || array_p (var) || assoc_p (var) == 0)
1398 	{
1399 	  if ((flags & AV_USEIND) == 0 || indp == 0)
1400 	    {
1401 	      ind = array_expand_index (var, t, len, flags);
1402 	      if (ind < 0)
1403 		{
1404 		  /* negative subscripts to indexed arrays count back from end */
1405 		  if (var && array_p (var))
1406 		    ind = array_max_index (array_cell (var)) + 1 + ind;
1407 		  if (ind < 0)
1408 		    INDEX_ERROR();
1409 		}
1410 	      if (indp)
1411 		*indp = ind;
1412 	    }
1413 	  else if (indp)
1414 	    ind = *indp;
1415 	}
1416       else if (assoc_p (var))
1417 	{
1418 	  t[len - 1] = '\0';
1419 	  if ((flags & AV_NOEXPAND) == 0)
1420 	    akey = expand_assignment_string_to_string (t, 0);	/* [ */
1421 	  else
1422 	    akey = savestring (t);
1423 	  t[len - 1] = ']';
1424 	  if (akey == 0 || *akey == 0)
1425 	    {
1426 	      FREE (akey);
1427 	      INDEX_ERROR();
1428 	    }
1429 	}
1430 
1431       if (var == 0 || value_cell (var) == 0)	/* XXX - check invisible_p(var) ? */
1432 	{
1433           FREE (akey);
1434 	  return ((char *)NULL);
1435 	}
1436       else if (invisible_p (var))
1437 	{
1438           FREE (akey);
1439 	  return ((char *)NULL);
1440 	}
1441       if (array_p (var) == 0 && assoc_p (var) == 0)
1442 	return (ind == 0 ? value_cell (var) : (char *)NULL);
1443       else if (assoc_p (var))
1444         {
1445 	  retval = assoc_reference (assoc_cell (var), akey);
1446 	  free (akey);
1447         }
1448       else
1449 	retval = array_reference (array_cell (var), ind);
1450     }
1451 
1452   return retval;
1453 }
1454 
1455 /* Return a string containing the elements described by the array and
1456    subscript contained in S, obeying quoting for subscripts * and @. */
1457 char *
array_value(s,quoted,flags,rtype,indp)1458 array_value (s, quoted, flags, rtype, indp)
1459      const char *s;
1460      int quoted, flags, *rtype;
1461      arrayind_t *indp;
1462 {
1463   return (array_value_internal (s, quoted, flags|AV_ALLOWALL, rtype, indp));
1464 }
1465 
1466 /* Return the value of the array indexing expression S as a single string.
1467    If (FLAGS & AV_ALLOWALL) is 0, do not allow `@' and `*' subscripts.  This
1468    is used by other parts of the shell such as the arithmetic expression
1469    evaluator in expr.c. */
1470 char *
get_array_value(s,flags,rtype,indp)1471 get_array_value (s, flags, rtype, indp)
1472      const char *s;
1473      int flags, *rtype;
1474      arrayind_t *indp;
1475 {
1476   return (array_value_internal (s, 0, flags, rtype, indp));
1477 }
1478 
1479 char *
array_keys(s,quoted,pflags)1480 array_keys (s, quoted, pflags)
1481      char *s;
1482      int quoted, pflags;
1483 {
1484   int len;
1485   char *retval, *t, *temp;
1486   WORD_LIST *l;
1487   SHELL_VAR *var;
1488 
1489   var = array_variable_part (s, 0, &t, &len);
1490 
1491   /* [ */
1492   if (var == 0 || ALL_ELEMENT_SUB (t[0]) == 0 || t[1] != ']')
1493     return (char *)NULL;
1494 
1495   if (var_isset (var) == 0 || invisible_p (var))
1496     return (char *)NULL;
1497 
1498   if (array_p (var) == 0 && assoc_p (var) == 0)
1499     l = add_string_to_list ("0", (WORD_LIST *)NULL);
1500   else if (assoc_p (var))
1501     l = assoc_keys_to_word_list (assoc_cell (var));
1502   else
1503     l = array_keys_to_word_list (array_cell (var));
1504   if (l == (WORD_LIST *)NULL)
1505     return ((char *) NULL);
1506 
1507   retval = string_list_pos_params (t[0], l, quoted, pflags);
1508 
1509   dispose_words (l);
1510   return retval;
1511 }
1512 #endif /* ARRAY_VARS */
1513