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