1/* arrayfunc.c -- High-level array functions used by other parts of the shell. */
2
3/* Copyright (C) 2001-2009 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
37#include "builtins/common.h"
38
39extern char *this_command_name;
40extern int last_command_exit_value;
41extern int array_needs_making;
42
43static SHELL_VAR *bind_array_var_internal __P((SHELL_VAR *, arrayind_t, char *, char *, int));
44
45static char *quote_assign __P((const char *));
46static void quote_array_assignment_chars __P((WORD_LIST *));
47static char *array_value_internal __P((char *, int, int, int *));
48
49/* Standard error message to use when encountering an invalid array subscript */
50const char * const bash_badsub_errmsg = N_("bad array subscript");
51
52/* **************************************************************** */
53/*								    */
54/*  Functions to manipulate array variables and perform assignments */
55/*								    */
56/* **************************************************************** */
57
58/* Convert a shell variable to an array variable.  The original value is
59   saved as array[0]. */
60SHELL_VAR *
61convert_var_to_array (var)
62     SHELL_VAR *var;
63{
64  char *oldval;
65  ARRAY *array;
66
67  oldval = value_cell (var);
68  array = array_create ();
69  if (oldval)
70    array_insert (array, 0, oldval);
71
72  FREE (value_cell (var));
73  var_setarray (var, array);
74
75  /* these aren't valid anymore */
76  var->dynamic_value = (sh_var_value_func_t *)NULL;
77  var->assign_func = (sh_var_assign_func_t *)NULL;
78
79  INVALIDATE_EXPORTSTR (var);
80  if (exported_p (var))
81    array_needs_making++;
82
83  VSETATTR (var, att_array);
84  VUNSETATTR (var, att_invisible);
85
86  return var;
87}
88
89/* Convert a shell variable to an array variable.  The original value is
90   saved as array[0]. */
91SHELL_VAR *
92convert_var_to_assoc (var)
93     SHELL_VAR *var;
94{
95  char *oldval;
96  HASH_TABLE *hash;
97
98  oldval = value_cell (var);
99  hash = assoc_create (0);
100  if (oldval)
101    assoc_insert (hash, savestring ("0"), oldval);
102
103  FREE (value_cell (var));
104  var_setassoc (var, hash);
105
106  /* these aren't valid anymore */
107  var->dynamic_value = (sh_var_value_func_t *)NULL;
108  var->assign_func = (sh_var_assign_func_t *)NULL;
109
110  INVALIDATE_EXPORTSTR (var);
111  if (exported_p (var))
112    array_needs_making++;
113
114  VSETATTR (var, att_assoc);
115  VUNSETATTR (var, att_invisible);
116
117  return var;
118}
119
120static SHELL_VAR *
121bind_array_var_internal (entry, ind, key, value, flags)
122     SHELL_VAR *entry;
123     arrayind_t ind;
124     char *key;
125     char *value;
126     int flags;
127{
128  SHELL_VAR *dentry;
129  char *newval;
130
131  /* If we're appending, we need the old value of the array reference, so
132     fake out make_variable_value with a dummy SHELL_VAR */
133  if (flags & ASS_APPEND)
134    {
135      dentry = (SHELL_VAR *)xmalloc (sizeof (SHELL_VAR));
136      dentry->name = savestring (entry->name);
137      if (assoc_p (entry))
138	newval = assoc_reference (assoc_cell (entry), key);
139      else
140	newval = array_reference (array_cell (entry), ind);
141      if (newval)
142	dentry->value = savestring (newval);
143      else
144	{
145	  dentry->value = (char *)xmalloc (1);
146	  dentry->value[0] = '\0';
147	}
148      dentry->exportstr = 0;
149      dentry->attributes = entry->attributes & ~(att_array|att_assoc|att_exported);
150      /* Leave the rest of the members uninitialized; the code doesn't look
151	 at them. */
152      newval = make_variable_value (dentry, value, flags);
153      dispose_variable (dentry);
154    }
155  else
156    newval = make_variable_value (entry, value, flags);
157
158  if (entry->assign_func)
159    (*entry->assign_func) (entry, newval, ind, key);
160  else if (assoc_p (entry))
161    assoc_insert (assoc_cell (entry), key, newval);
162  else
163    array_insert (array_cell (entry), ind, newval);
164  FREE (newval);
165
166  return (entry);
167}
168
169/* Perform an array assignment name[ind]=value.  If NAME already exists and
170   is not an array, and IND is 0, perform name=value instead.  If NAME exists
171   and is not an array, and IND is not 0, convert it into an array with the
172   existing value as name[0].
173
174   If NAME does not exist, just create an array variable, no matter what
175   IND's value may be. */
176SHELL_VAR *
177bind_array_variable (name, ind, value, flags)
178     char *name;
179     arrayind_t ind;
180     char *value;
181     int flags;
182{
183  SHELL_VAR *entry;
184
185  entry = var_lookup (name, shell_variables);
186
187  if (entry == (SHELL_VAR *) 0)
188    entry = make_new_array_variable (name);
189  else if (readonly_p (entry) || noassign_p (entry))
190    {
191      if (readonly_p (entry))
192	err_readonly (name);
193      return (entry);
194    }
195  else if (array_p (entry) == 0)
196    entry = convert_var_to_array (entry);
197
198  /* ENTRY is an array variable, and ARRAY points to the value. */
199  return (bind_array_var_internal (entry, ind, 0, value, flags));
200}
201
202SHELL_VAR *
203bind_array_element (entry, ind, value, flags)
204     SHELL_VAR *entry;
205     arrayind_t ind;
206     char *value;
207     int flags;
208{
209  return (bind_array_var_internal (entry, ind, 0, value, flags));
210}
211
212SHELL_VAR *
213bind_assoc_variable (entry, name, key, value, flags)
214     SHELL_VAR *entry;
215     char *name;
216     char *key;
217     char *value;
218     int flags;
219{
220  SHELL_VAR *dentry;
221  char *newval;
222
223  if (readonly_p (entry) || noassign_p (entry))
224    {
225      if (readonly_p (entry))
226	err_readonly (name);
227      return (entry);
228    }
229
230  return (bind_array_var_internal (entry, 0, key, value, flags));
231}
232
233/* Parse NAME, a lhs of an assignment statement of the form v[s], and
234   assign VALUE to that array element by calling bind_array_variable(). */
235SHELL_VAR *
236assign_array_element (name, value, flags)
237     char *name, *value;
238     int flags;
239{
240  char *sub, *vname, *akey;
241  arrayind_t ind;
242  int sublen;
243  SHELL_VAR *entry;
244
245  vname = array_variable_name (name, &sub, &sublen);
246
247  if (vname == 0)
248    return ((SHELL_VAR *)NULL);
249
250  if ((ALL_ELEMENT_SUB (sub[0]) && sub[1] == ']') || (sublen <= 1))
251    {
252      free (vname);
253      err_badarraysub (name);
254      return ((SHELL_VAR *)NULL);
255    }
256
257  entry = find_variable (vname);
258
259  if (entry && assoc_p (entry))
260    {
261      sub[sublen-1] = '\0';
262      akey = expand_assignment_string_to_string (sub, 0);	/* [ */
263      sub[sublen-1] = ']';
264      if (akey == 0 || *akey == 0)
265	{
266	  free (vname);
267	  err_badarraysub (name);
268	  return ((SHELL_VAR *)NULL);
269	}
270      entry = bind_assoc_variable (entry, vname, akey, value, flags);
271    }
272  else
273    {
274      ind = array_expand_index (sub, sublen);
275      if (ind < 0)
276	{
277	  free (vname);
278	  err_badarraysub (name);
279	  return ((SHELL_VAR *)NULL);
280	}
281      entry = bind_array_variable (vname, ind, value, flags);
282    }
283
284  free (vname);
285  return (entry);
286}
287
288/* Find the array variable corresponding to NAME.  If there is no variable,
289   create a new array variable.  If the variable exists but is not an array,
290   convert it to an indexed array.  If FLAGS&1 is non-zero, an existing
291   variable is checked for the readonly or noassign attribute in preparation
292   for assignment (e.g., by the `read' builtin).  If FLAGS&2 is non-zero, we
293   create an associative array. */
294SHELL_VAR *
295find_or_make_array_variable (name, flags)
296     char *name;
297     int flags;
298{
299  SHELL_VAR *var;
300
301  var = find_variable (name);
302
303  if (var == 0)
304    var = (flags & 2) ? make_new_assoc_variable (name) : make_new_array_variable (name);
305  else if ((flags & 1) && (readonly_p (var) || noassign_p (var)))
306    {
307      if (readonly_p (var))
308	err_readonly (name);
309      return ((SHELL_VAR *)NULL);
310    }
311  else if ((flags & 2) && array_p (var))
312    {
313      report_error (_("%s: cannot convert indexed to associative array"), name);
314      return ((SHELL_VAR *)NULL);
315    }
316  else if (array_p (var) == 0 && assoc_p (var) == 0)
317    var = convert_var_to_array (var);
318
319  return (var);
320}
321
322/* Perform a compound assignment statement for array NAME, where VALUE is
323   the text between the parens:  NAME=( VALUE ) */
324SHELL_VAR *
325assign_array_from_string (name, value, flags)
326     char *name, *value;
327     int flags;
328{
329  SHELL_VAR *var;
330  int vflags;
331
332  vflags = 1;
333  if (flags & ASS_MKASSOC)
334    vflags |= 2;
335
336  var = find_or_make_array_variable (name, vflags);
337  if (var == 0)
338    return ((SHELL_VAR *)NULL);
339
340  return (assign_array_var_from_string (var, value, flags));
341}
342
343/* Sequentially assign the indices of indexed array variable VAR from the
344   words in LIST. */
345SHELL_VAR *
346assign_array_var_from_word_list (var, list, flags)
347     SHELL_VAR *var;
348     WORD_LIST *list;
349     int flags;
350{
351  register arrayind_t i;
352  register WORD_LIST *l;
353  ARRAY *a;
354
355  a = array_cell (var);
356  i = (flags & ASS_APPEND) ? array_max_index (a) + 1 : 0;
357
358  for (l = list; l; l = l->next, i++)
359    if (var->assign_func)
360      (*var->assign_func) (var, l->word->word, i, 0);
361    else
362      array_insert (a, i, l->word->word);
363  return var;
364}
365
366WORD_LIST *
367expand_compound_array_assignment (var, value, flags)
368     SHELL_VAR *var;
369     char *value;
370     int flags;
371{
372  WORD_LIST *list, *nlist;
373  char *val;
374  int ni;
375
376  /* I don't believe this condition is ever true any more. */
377  if (*value == '(')	/*)*/
378    {
379      ni = 1;
380      val = extract_array_assignment_list (value, &ni);
381      if (val == 0)
382	return (WORD_LIST *)NULL;
383    }
384  else
385    val = value;
386
387  /* Expand the value string into a list of words, performing all the
388     shell expansions including pathname generation and word splitting. */
389  /* First we split the string on whitespace, using the shell parser
390     (ksh93 seems to do this). */
391  list = parse_string_to_word_list (val, 1, "array assign");
392
393  /* If we're using [subscript]=value, we need to quote each [ and ] to
394     prevent unwanted filename expansion. */
395  if (list)
396    quote_array_assignment_chars (list);
397
398  /* Now that we've split it, perform the shell expansions on each
399     word in the list. */
400  nlist = list ? expand_words_no_vars (list) : (WORD_LIST *)NULL;
401
402  dispose_words (list);
403
404  if (val != value)
405    free (val);
406
407  return nlist;
408}
409
410void
411assign_compound_array_list (var, nlist, flags)
412     SHELL_VAR *var;
413     WORD_LIST *nlist;
414     int flags;
415{
416  ARRAY *a;
417  HASH_TABLE *h;
418  WORD_LIST *list;
419  char *w, *val, *nval;
420  int len, iflags;
421  arrayind_t ind, last_ind;
422  char *akey;
423
424  a = (var && array_p (var)) ? array_cell (var) : (ARRAY *)0;
425  h = (var && assoc_p (var)) ? assoc_cell (var) : (HASH_TABLE *)0;
426
427  akey = (char *)0;
428  ind = 0;
429
430  /* Now that we are ready to assign values to the array, kill the existing
431     value. */
432  if ((flags & ASS_APPEND) == 0)
433    {
434      if (array_p (var) && a)
435	array_flush (a);
436      else if (assoc_p (var) && h)
437	assoc_flush (h);
438    }
439
440  last_ind = (a && (flags & ASS_APPEND)) ? array_max_index (a) + 1 : 0;
441
442  for (list = nlist; list; list = list->next)
443    {
444      iflags = flags;
445      w = list->word->word;
446
447      /* We have a word of the form [ind]=value */
448      if ((list->word->flags & W_ASSIGNMENT) && w[0] == '[')
449	{
450	  len = skipsubscript (w, 0);
451
452	  /* XXX - changes for `+=' */
453 	  if (w[len] != ']' || (w[len+1] != '=' && (w[len+1] != '+' || w[len+2] != '=')))
454	    {
455	      if (assoc_p (var))
456		{
457		  err_badarraysub (w);
458		  continue;
459		}
460	      nval = make_variable_value (var, w, flags);
461	      if (var->assign_func)
462		(*var->assign_func) (var, nval, last_ind, 0);
463	      else
464		array_insert (a, last_ind, nval);
465	      FREE (nval);
466	      last_ind++;
467	      continue;
468	    }
469
470	  if (len == 1)
471	    {
472	      err_badarraysub (w);
473	      continue;
474	    }
475
476	  if (ALL_ELEMENT_SUB (w[1]) && len == 2)
477	    {
478	      if (assoc_p (var))
479		report_error (_("%s: invalid associative array key"), w);
480	      else
481		report_error (_("%s: cannot assign to non-numeric index"), w);
482	      continue;
483	    }
484
485	  if (array_p (var))
486	    {
487	      ind = array_expand_index (w + 1, len);
488	      if (ind < 0)
489		{
490		  err_badarraysub (w);
491		  continue;
492		}
493
494	      last_ind = ind;
495	    }
496	  else if (assoc_p (var))
497	    {
498	      akey = substring (w, 1, len);
499	      if (akey == 0 || *akey == 0)
500		{
501		  err_badarraysub (w);
502		  continue;
503		}
504	    }
505
506	  /* XXX - changes for `+=' -- just accept the syntax.  ksh93 doesn't do this */
507	  if (w[len + 1] == '+' && w[len + 2] == '=')
508	    {
509	      iflags |= ASS_APPEND;
510	      val = w + len + 3;
511	    }
512	  else
513	    val = w + len + 2;
514	}
515      else if (assoc_p (var))
516	{
517	  report_error (_("%s: %s: must use subscript when assigning associative array"), var->name, w);
518	  continue;
519	}
520      else		/* No [ind]=value, just a stray `=' */
521	{
522	  ind = last_ind;
523	  val = w;
524	}
525
526      if (integer_p (var))
527	this_command_name = (char *)NULL;	/* no command name for errors */
528      bind_array_var_internal (var, ind, akey, val, iflags);
529      last_ind++;
530    }
531}
532
533/* Perform a compound array assignment:  VAR->name=( VALUE ).  The
534   VALUE has already had the parentheses stripped. */
535SHELL_VAR *
536assign_array_var_from_string (var, value, flags)
537     SHELL_VAR *var;
538     char *value;
539     int flags;
540{
541  WORD_LIST *nlist;
542
543  if (value == 0)
544    return var;
545
546  nlist = expand_compound_array_assignment (var, value, flags);
547  assign_compound_array_list (var, nlist, flags);
548
549  if (nlist)
550    dispose_words (nlist);
551  return (var);
552}
553
554/* Quote globbing chars and characters in $IFS before the `=' in an assignment
555   statement (usually a compound array assignment) to protect them from
556   unwanted filename expansion or word splitting. */
557static char *
558quote_assign (string)
559     const char *string;
560{
561  size_t slen;
562  int saw_eq;
563  char *temp, *t;
564  const char *s, *send;
565  DECLARE_MBSTATE;
566
567  slen = strlen (string);
568  send = string + slen;
569
570  t = temp = (char *)xmalloc (slen * 2 + 1);
571  saw_eq = 0;
572  for (s = string; *s; )
573    {
574      if (*s == '=')
575	saw_eq = 1;
576      if (saw_eq == 0 && (glob_char_p (s) || isifs (*s)))
577	*t++ = '\\';
578
579      COPY_CHAR_P (t, s, send);
580    }
581  *t = '\0';
582  return temp;
583}
584
585/* For each word in a compound array assignment, if the word looks like
586   [ind]=value, quote globbing chars and characters in $IFS before the `='. */
587static void
588quote_array_assignment_chars (list)
589     WORD_LIST *list;
590{
591  char *nword;
592  WORD_LIST *l;
593
594  for (l = list; l; l = l->next)
595    {
596      if (l->word == 0 || l->word->word == 0 || l->word->word[0] == '\0')
597	continue;	/* should not happen, but just in case... */
598      /* Don't bother if it doesn't look like [ind]=value */
599      if (l->word->word[0] != '[' || xstrchr (l->word->word, '=') == 0) /* ] */
600	continue;
601      nword = quote_assign (l->word->word);
602      free (l->word->word);
603      l->word->word = nword;
604    }
605}
606
607/* skipsubscript moved to subst.c to use private functions. 2009/02/24. */
608
609/* This function is called with SUB pointing to just after the beginning
610   `[' of an array subscript and removes the array element to which SUB
611   expands from array VAR.  A subscript of `*' or `@' unsets the array. */
612int
613unbind_array_element (var, sub)
614     SHELL_VAR *var;
615     char *sub;
616{
617  int len;
618  arrayind_t ind;
619  char *akey;
620  ARRAY_ELEMENT *ae;
621
622  len = skipsubscript (sub, 0);
623  if (sub[len] != ']' || len == 0)
624    {
625      builtin_error ("%s[%s: %s", var->name, sub, _(bash_badsub_errmsg));
626      return -1;
627    }
628  sub[len] = '\0';
629
630  if (ALL_ELEMENT_SUB (sub[0]) && sub[1] == 0)
631    {
632      unbind_variable (var->name);
633      return (0);
634    }
635
636  if (assoc_p (var))
637    {
638      akey = expand_assignment_string_to_string (sub, 0);     /* [ */
639      if (akey == 0 || *akey == 0)
640	{
641	  builtin_error ("[%s]: %s", sub, _(bash_badsub_errmsg));
642	  return -1;
643	}
644      assoc_remove (assoc_cell (var), akey);
645    }
646  else
647    {
648      ind = array_expand_index (sub, len+1);
649      if (ind < 0)
650	{
651	  builtin_error ("[%s]: %s", sub, _(bash_badsub_errmsg));
652	  return -1;
653	}
654      ae = array_remove (array_cell (var), ind);
655      if (ae)
656	array_dispose_element (ae);
657    }
658
659  return 0;
660}
661
662/* Format and output an array assignment in compound form VAR=(VALUES),
663   suitable for re-use as input. */
664void
665print_array_assignment (var, quoted)
666     SHELL_VAR *var;
667     int quoted;
668{
669  char *vstr;
670
671  vstr = array_to_assign (array_cell (var), quoted);
672
673  if (vstr == 0)
674    printf ("%s=%s\n", var->name, quoted ? "'()'" : "()");
675  else
676    {
677      printf ("%s=%s\n", var->name, vstr);
678      free (vstr);
679    }
680}
681
682/* Format and output an associative array assignment in compound form
683   VAR=(VALUES), suitable for re-use as input. */
684void
685print_assoc_assignment (var, quoted)
686     SHELL_VAR *var;
687     int quoted;
688{
689  char *vstr;
690
691  vstr = assoc_to_assign (assoc_cell (var), quoted);
692
693  if (vstr == 0)
694    printf ("%s=%s\n", var->name, quoted ? "'()'" : "()");
695  else
696    {
697      printf ("%s=%s\n", var->name, vstr);
698      free (vstr);
699    }
700}
701
702/***********************************************************************/
703/*								       */
704/* Utility functions to manage arrays and their contents for expansion */
705/*								       */
706/***********************************************************************/
707
708/* Return 1 if NAME is a properly-formed array reference v[sub]. */
709int
710valid_array_reference (name)
711     char *name;
712{
713  char *t;
714  int r, len;
715
716  t = xstrchr (name, '[');	/* ] */
717  if (t)
718    {
719      *t = '\0';
720      r = legal_identifier (name);
721      *t = '[';
722      if (r == 0)
723	return 0;
724      /* Check for a properly-terminated non-blank subscript. */
725      len = skipsubscript (t, 0);
726      if (t[len] != ']' || len == 1)
727	return 0;
728      for (r = 1; r < len; r++)
729	if (whitespace (t[r]) == 0)
730	  return 1;
731      return 0;
732    }
733  return 0;
734}
735
736/* Expand the array index beginning at S and extending LEN characters. */
737arrayind_t
738array_expand_index (s, len)
739     char *s;
740     int len;
741{
742  char *exp, *t;
743  int expok;
744  arrayind_t val;
745
746  exp = (char *)xmalloc (len);
747  strncpy (exp, s, len - 1);
748  exp[len - 1] = '\0';
749  t = expand_arith_string (exp, 0);
750  this_command_name = (char *)NULL;
751  val = evalexp (t, &expok);
752  free (t);
753  free (exp);
754  if (expok == 0)
755    {
756      last_command_exit_value = EXECUTION_FAILURE;
757
758      top_level_cleanup ();
759      jump_to_top_level (DISCARD);
760    }
761  return val;
762}
763
764/* Return the name of the variable specified by S without any subscript.
765   If SUBP is non-null, return a pointer to the start of the subscript
766   in *SUBP. If LENP is non-null, the length of the subscript is returned
767   in *LENP.  This returns newly-allocated memory. */
768char *
769array_variable_name (s, subp, lenp)
770     char *s, **subp;
771     int *lenp;
772{
773  char *t, *ret;
774  int ind, ni;
775
776  t = xstrchr (s, '[');
777  if (t == 0)
778    {
779      if (subp)
780      	*subp = t;
781      if (lenp)
782	*lenp = 0;
783      return ((char *)NULL);
784    }
785  ind = t - s;
786  ni = skipsubscript (s, ind);
787  if (ni <= ind + 1 || s[ni] != ']')
788    {
789      err_badarraysub (s);
790      if (subp)
791      	*subp = t;
792      if (lenp)
793	*lenp = 0;
794      return ((char *)NULL);
795    }
796
797  *t = '\0';
798  ret = savestring (s);
799  *t++ = '[';		/* ] */
800
801  if (subp)
802    *subp = t;
803  if (lenp)
804    *lenp = ni - ind;
805
806  return ret;
807}
808
809/* Return the variable specified by S without any subscript.  If SUBP is
810   non-null, return a pointer to the start of the subscript in *SUBP.
811   If LENP is non-null, the length of the subscript is returned in *LENP. */
812SHELL_VAR *
813array_variable_part (s, subp, lenp)
814     char *s, **subp;
815     int *lenp;
816{
817  char *t;
818  SHELL_VAR *var;
819
820  t = array_variable_name (s, subp, lenp);
821  if (t == 0)
822    return ((SHELL_VAR *)NULL);
823  var = find_variable (t);
824
825  free (t);
826  return (var == 0 || invisible_p (var)) ? (SHELL_VAR *)0 : var;
827}
828
829/* Return a string containing the elements in the array and subscript
830   described by S.  If the subscript is * or @, obeys quoting rules akin
831   to the expansion of $* and $@ including double quoting.  If RTYPE
832   is non-null it gets 1 if the array reference is name[*], 2 if the
833   reference is name[@], and 0 otherwise. */
834static char *
835array_value_internal (s, quoted, allow_all, rtype)
836     char *s;
837     int quoted, allow_all, *rtype;
838{
839  int len;
840  arrayind_t ind;
841  char *akey;
842  char *retval, *t, *temp;
843  WORD_LIST *l;
844  SHELL_VAR *var;
845
846  var = array_variable_part (s, &t, &len);
847
848  /* Expand the index, even if the variable doesn't exist, in case side
849     effects are needed, like ${w[i++]} where w is unset. */
850#if 0
851  if (var == 0)
852    return (char *)NULL;
853#endif
854
855  if (len == 0)
856    return ((char *)NULL);	/* error message already printed */
857
858  /* [ */
859  if (ALL_ELEMENT_SUB (t[0]) && t[1] == ']')
860    {
861      if (rtype)
862	*rtype = (t[0] == '*') ? 1 : 2;
863      if (allow_all == 0)
864	{
865	  err_badarraysub (s);
866	  return ((char *)NULL);
867	}
868      else if (var == 0 || value_cell (var) == 0)
869	return ((char *)NULL);
870      else if (array_p (var) == 0 && assoc_p (var) == 0)
871	l = add_string_to_list (value_cell (var), (WORD_LIST *)NULL);
872      else if (assoc_p (var))
873	{
874	  l = assoc_to_word_list (assoc_cell (var));
875	  if (l == (WORD_LIST *)NULL)
876	    return ((char *)NULL);
877	}
878      else
879	{
880	  l = array_to_word_list (array_cell (var));
881	  if (l == (WORD_LIST *)NULL)
882	    return ((char *) NULL);
883	}
884
885      if (t[0] == '*' && (quoted & (Q_HERE_DOCUMENT|Q_DOUBLE_QUOTES)))
886	{
887	  temp = string_list_dollar_star (l);
888	  retval = quote_string (temp);
889	  free (temp);
890	}
891      else	/* ${name[@]} or unquoted ${name[*]} */
892	retval = string_list_dollar_at (l, quoted);
893
894      dispose_words (l);
895    }
896  else
897    {
898      if (rtype)
899	*rtype = 0;
900      if (var == 0 || array_p (var) || assoc_p (var) == 0)
901	{
902	  ind = array_expand_index (t, len);
903	  if (ind < 0)
904	    {
905index_error:
906	      if (var)
907		err_badarraysub (var->name);
908	      else
909		{
910		  t[-1] = '\0';
911		  err_badarraysub (s);
912		  t[-1] = '[';	/* ] */
913		}
914	      return ((char *)NULL);
915	    }
916	}
917      else if (assoc_p (var))
918	{
919	  t[len - 1] = '\0';
920	  akey = expand_assignment_string_to_string (t, 0);	/* [ */
921	  t[len - 1] = ']';
922	  if (akey == 0 || *akey == 0)
923	    goto index_error;
924	}
925
926      if (var == 0)
927	return ((char *)NULL);
928      if (array_p (var) == 0 && assoc_p (var) == 0)
929	return (ind == 0 ? value_cell (var) : (char *)NULL);
930      else if (assoc_p (var))
931	retval = assoc_reference (assoc_cell (var), akey);
932      else
933	retval = array_reference (array_cell (var), ind);
934    }
935
936  return retval;
937}
938
939/* Return a string containing the elements described by the array and
940   subscript contained in S, obeying quoting for subscripts * and @. */
941char *
942array_value (s, quoted, rtype)
943     char *s;
944     int quoted, *rtype;
945{
946  return (array_value_internal (s, quoted, 1, rtype));
947}
948
949/* Return the value of the array indexing expression S as a single string.
950   If ALLOW_ALL is 0, do not allow `@' and `*' subscripts.  This is used
951   by other parts of the shell such as the arithmetic expression evaluator
952   in expr.c. */
953char *
954get_array_value (s, allow_all, rtype)
955     char *s;
956     int allow_all, *rtype;
957{
958  return (array_value_internal (s, 0, allow_all, rtype));
959}
960
961char *
962array_keys (s, quoted)
963     char *s;
964     int quoted;
965{
966  int len;
967  char *retval, *t, *temp;
968  WORD_LIST *l;
969  SHELL_VAR *var;
970
971  var = array_variable_part (s, &t, &len);
972
973  /* [ */
974  if (var == 0 || ALL_ELEMENT_SUB (t[0]) == 0 || t[1] != ']')
975    return (char *)NULL;
976
977  if (array_p (var) == 0 && assoc_p (var) == 0)
978    l = add_string_to_list ("0", (WORD_LIST *)NULL);
979  else if (assoc_p (var))
980    l = assoc_keys_to_word_list (assoc_cell (var));
981  else
982    l = array_keys_to_word_list (array_cell (var));
983  if (l == (WORD_LIST *)NULL)
984    return ((char *) NULL);
985
986  if (t[0] == '*' && (quoted & (Q_HERE_DOCUMENT|Q_DOUBLE_QUOTES)))
987    {
988      temp = string_list_dollar_star (l);
989      retval = quote_string (temp);
990      free (temp);
991    }
992  else	/* ${!name[@]} or unquoted ${!name[*]} */
993    retval = string_list_dollar_at (l, quoted);
994
995  dispose_words (l);
996  return retval;
997}
998#endif /* ARRAY_VARS */
999