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