1 | /* arrayfunc.c -- High-level array functions used by other parts of the shell. */
|
---|
2 |
|
---|
3 | /* Copyright (C) 2001-2005 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 it under
|
---|
8 | the terms of the GNU General Public License as published by the Free
|
---|
9 | Software Foundation; either version 2, or (at your option) any later
|
---|
10 | version.
|
---|
11 |
|
---|
12 | Bash is distributed in the hope that it will be useful, but WITHOUT ANY
|
---|
13 | WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
---|
14 | FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
---|
15 | for more details.
|
---|
16 |
|
---|
17 | You should have received a copy of the GNU General Public License along
|
---|
18 | with Bash; see the file COPYING. If not, write to the Free Software
|
---|
19 | Foundation, 59 Temple Place, Suite 330, Boston, MA 02111 USA. */
|
---|
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 |
|
---|
34 | #include "shmbutil.h"
|
---|
35 |
|
---|
36 | #include "builtins/common.h"
|
---|
37 |
|
---|
38 | extern char *this_command_name;
|
---|
39 | extern int last_command_exit_value;
|
---|
40 | extern int array_needs_making;
|
---|
41 |
|
---|
42 | static SHELL_VAR *bind_array_var_internal __P((SHELL_VAR *, arrayind_t, char *, int));
|
---|
43 |
|
---|
44 | static void quote_array_assignment_chars __P((WORD_LIST *));
|
---|
45 | static char *array_value_internal __P((char *, int, int, int *));
|
---|
46 |
|
---|
47 | /* Standard error message to use when encountering an invalid array subscript */
|
---|
48 | char *bash_badsub_errmsg = N_("bad array subscript");
|
---|
49 |
|
---|
50 | /* **************************************************************** */
|
---|
51 | /* */
|
---|
52 | /* Functions to manipulate array variables and perform assignments */
|
---|
53 | /* */
|
---|
54 | /* **************************************************************** */
|
---|
55 |
|
---|
56 | /* Convert a shell variable to an array variable. The original value is
|
---|
57 | saved as array[0]. */
|
---|
58 | SHELL_VAR *
|
---|
59 | convert_var_to_array (var)
|
---|
60 | SHELL_VAR *var;
|
---|
61 | {
|
---|
62 | char *oldval;
|
---|
63 | ARRAY *array;
|
---|
64 |
|
---|
65 | oldval = value_cell (var);
|
---|
66 | array = array_create ();
|
---|
67 | if (oldval)
|
---|
68 | array_insert (array, 0, oldval);
|
---|
69 |
|
---|
70 | FREE (value_cell (var));
|
---|
71 | var_setarray (var, array);
|
---|
72 |
|
---|
73 | /* these aren't valid anymore */
|
---|
74 | var->dynamic_value = (sh_var_value_func_t *)NULL;
|
---|
75 | var->assign_func = (sh_var_assign_func_t *)NULL;
|
---|
76 |
|
---|
77 | INVALIDATE_EXPORTSTR (var);
|
---|
78 | if (exported_p (var))
|
---|
79 | array_needs_making++;
|
---|
80 |
|
---|
81 | VSETATTR (var, att_array);
|
---|
82 | VUNSETATTR (var, att_invisible);
|
---|
83 |
|
---|
84 | return var;
|
---|
85 | }
|
---|
86 |
|
---|
87 | static SHELL_VAR *
|
---|
88 | bind_array_var_internal (entry, ind, value, flags)
|
---|
89 | SHELL_VAR *entry;
|
---|
90 | arrayind_t ind;
|
---|
91 | char *value;
|
---|
92 | int flags;
|
---|
93 | {
|
---|
94 | SHELL_VAR *dentry;
|
---|
95 | char *newval;
|
---|
96 |
|
---|
97 | /* If we're appending, we need the old value of the array reference, so
|
---|
98 | fake out make_variable_value with a dummy SHELL_VAR */
|
---|
99 | if (flags & ASS_APPEND)
|
---|
100 | {
|
---|
101 | dentry = (SHELL_VAR *)xmalloc (sizeof (SHELL_VAR));
|
---|
102 | dentry->name = savestring (entry->name);
|
---|
103 | newval = array_reference (array_cell (entry), ind);
|
---|
104 | if (newval)
|
---|
105 | dentry->value = savestring (newval);
|
---|
106 | else
|
---|
107 | {
|
---|
108 | dentry->value = (char *)xmalloc (1);
|
---|
109 | dentry->value[0] = '\0';
|
---|
110 | }
|
---|
111 | dentry->exportstr = 0;
|
---|
112 | dentry->attributes = entry->attributes & ~(att_array|att_exported);
|
---|
113 | /* Leave the rest of the members uninitialized; the code doesn't look
|
---|
114 | at them. */
|
---|
115 | newval = make_variable_value (dentry, value, flags);
|
---|
116 | dispose_variable (dentry);
|
---|
117 | }
|
---|
118 | else
|
---|
119 | newval = make_variable_value (entry, value, flags);
|
---|
120 |
|
---|
121 | if (entry->assign_func)
|
---|
122 | (*entry->assign_func) (entry, newval, ind);
|
---|
123 | else
|
---|
124 | array_insert (array_cell (entry), ind, newval);
|
---|
125 | FREE (newval);
|
---|
126 |
|
---|
127 | return (entry);
|
---|
128 | }
|
---|
129 |
|
---|
130 | /* Perform an array assignment name[ind]=value. If NAME already exists and
|
---|
131 | is not an array, and IND is 0, perform name=value instead. If NAME exists
|
---|
132 | and is not an array, and IND is not 0, convert it into an array with the
|
---|
133 | existing value as name[0].
|
---|
134 |
|
---|
135 | If NAME does not exist, just create an array variable, no matter what
|
---|
136 | IND's value may be. */
|
---|
137 | SHELL_VAR *
|
---|
138 | bind_array_variable (name, ind, value, flags)
|
---|
139 | char *name;
|
---|
140 | arrayind_t ind;
|
---|
141 | char *value;
|
---|
142 | int flags;
|
---|
143 | {
|
---|
144 | SHELL_VAR *entry;
|
---|
145 |
|
---|
146 | entry = var_lookup (name, shell_variables);
|
---|
147 |
|
---|
148 | if (entry == (SHELL_VAR *) 0)
|
---|
149 | entry = make_new_array_variable (name);
|
---|
150 | else if (readonly_p (entry) || noassign_p (entry))
|
---|
151 | {
|
---|
152 | if (readonly_p (entry))
|
---|
153 | err_readonly (name);
|
---|
154 | return (entry);
|
---|
155 | }
|
---|
156 | else if (array_p (entry) == 0)
|
---|
157 | entry = convert_var_to_array (entry);
|
---|
158 |
|
---|
159 | /* ENTRY is an array variable, and ARRAY points to the value. */
|
---|
160 | return (bind_array_var_internal (entry, ind, value, flags));
|
---|
161 | }
|
---|
162 |
|
---|
163 | /* Parse NAME, a lhs of an assignment statement of the form v[s], and
|
---|
164 | assign VALUE to that array element by calling bind_array_variable(). */
|
---|
165 | SHELL_VAR *
|
---|
166 | assign_array_element (name, value, flags)
|
---|
167 | char *name, *value;
|
---|
168 | int flags;
|
---|
169 | {
|
---|
170 | char *sub, *vname;
|
---|
171 | arrayind_t ind;
|
---|
172 | int sublen;
|
---|
173 | SHELL_VAR *entry;
|
---|
174 |
|
---|
175 | vname = array_variable_name (name, &sub, &sublen);
|
---|
176 |
|
---|
177 | if (vname == 0)
|
---|
178 | return ((SHELL_VAR *)NULL);
|
---|
179 |
|
---|
180 | if ((ALL_ELEMENT_SUB (sub[0]) && sub[1] == ']') || (sublen <= 1))
|
---|
181 | {
|
---|
182 | free (vname);
|
---|
183 | err_badarraysub (name);
|
---|
184 | return ((SHELL_VAR *)NULL);
|
---|
185 | }
|
---|
186 |
|
---|
187 | ind = array_expand_index (sub, sublen);
|
---|
188 | if (ind < 0)
|
---|
189 | {
|
---|
190 | free (vname);
|
---|
191 | err_badarraysub (name);
|
---|
192 | return ((SHELL_VAR *)NULL);
|
---|
193 | }
|
---|
194 |
|
---|
195 | entry = bind_array_variable (vname, ind, value, flags);
|
---|
196 |
|
---|
197 | free (vname);
|
---|
198 | return (entry);
|
---|
199 | }
|
---|
200 |
|
---|
201 | /* Find the array variable corresponding to NAME. If there is no variable,
|
---|
202 | create a new array variable. If the variable exists but is not an array,
|
---|
203 | convert it to an indexed array. If CHECK_FLAGS is non-zero, an existing
|
---|
204 | variable is checked for the readonly or noassign attribute in preparation
|
---|
205 | for assignment (e.g., by the `read' builtin). */
|
---|
206 | SHELL_VAR *
|
---|
207 | find_or_make_array_variable (name, check_flags)
|
---|
208 | char *name;
|
---|
209 | int check_flags;
|
---|
210 | {
|
---|
211 | SHELL_VAR *var;
|
---|
212 |
|
---|
213 | var = find_variable (name);
|
---|
214 |
|
---|
215 | if (var == 0)
|
---|
216 | var = make_new_array_variable (name);
|
---|
217 | else if (check_flags && (readonly_p (var) || noassign_p (var)))
|
---|
218 | {
|
---|
219 | if (readonly_p (var))
|
---|
220 | err_readonly (name);
|
---|
221 | return ((SHELL_VAR *)NULL);
|
---|
222 | }
|
---|
223 | else if (array_p (var) == 0)
|
---|
224 | var = convert_var_to_array (var);
|
---|
225 |
|
---|
226 | return (var);
|
---|
227 | }
|
---|
228 |
|
---|
229 | /* Perform a compound assignment statement for array NAME, where VALUE is
|
---|
230 | the text between the parens: NAME=( VALUE ) */
|
---|
231 | SHELL_VAR *
|
---|
232 | assign_array_from_string (name, value, flags)
|
---|
233 | char *name, *value;
|
---|
234 | int flags;
|
---|
235 | {
|
---|
236 | SHELL_VAR *var;
|
---|
237 |
|
---|
238 | var = find_or_make_array_variable (name, 1);
|
---|
239 | if (var == 0)
|
---|
240 | return ((SHELL_VAR *)NULL);
|
---|
241 |
|
---|
242 | return (assign_array_var_from_string (var, value, flags));
|
---|
243 | }
|
---|
244 |
|
---|
245 | /* Sequentially assign the indices of indexed array variable VAR from the
|
---|
246 | words in LIST. */
|
---|
247 | SHELL_VAR *
|
---|
248 | assign_array_var_from_word_list (var, list, flags)
|
---|
249 | SHELL_VAR *var;
|
---|
250 | WORD_LIST *list;
|
---|
251 | int flags;
|
---|
252 | {
|
---|
253 | register arrayind_t i;
|
---|
254 | register WORD_LIST *l;
|
---|
255 | ARRAY *a;
|
---|
256 |
|
---|
257 | a = array_cell (var);
|
---|
258 | i = (flags & ASS_APPEND) ? array_max_index (a) + 1 : 0;
|
---|
259 |
|
---|
260 | for (l = list; l; l = l->next, i++)
|
---|
261 | if (var->assign_func)
|
---|
262 | (*var->assign_func) (var, l->word->word, i);
|
---|
263 | else
|
---|
264 | array_insert (a, i, l->word->word);
|
---|
265 | return var;
|
---|
266 | }
|
---|
267 |
|
---|
268 | /* Perform a compound array assignment: VAR->name=( VALUE ). The
|
---|
269 | VALUE has already had the parentheses stripped. */
|
---|
270 | SHELL_VAR *
|
---|
271 | assign_array_var_from_string (var, value, flags)
|
---|
272 | SHELL_VAR *var;
|
---|
273 | char *value;
|
---|
274 | int flags;
|
---|
275 | {
|
---|
276 | ARRAY *a;
|
---|
277 | WORD_LIST *list, *nlist;
|
---|
278 | char *w, *val, *nval;
|
---|
279 | int ni, len;
|
---|
280 | arrayind_t ind, last_ind;
|
---|
281 |
|
---|
282 | if (value == 0)
|
---|
283 | return var;
|
---|
284 |
|
---|
285 | /* If this is called from declare_builtin, value[0] == '(' and
|
---|
286 | xstrchr(value, ')') != 0. In this case, we need to extract
|
---|
287 | the value from between the parens before going on. */
|
---|
288 | if (*value == '(') /*)*/
|
---|
289 | {
|
---|
290 | ni = 1;
|
---|
291 | val = extract_array_assignment_list (value, &ni);
|
---|
292 | if (val == 0)
|
---|
293 | return var;
|
---|
294 | }
|
---|
295 | else
|
---|
296 | val = value;
|
---|
297 |
|
---|
298 | /* Expand the value string into a list of words, performing all the
|
---|
299 | shell expansions including pathname generation and word splitting. */
|
---|
300 | /* First we split the string on whitespace, using the shell parser
|
---|
301 | (ksh93 seems to do this). */
|
---|
302 | list = parse_string_to_word_list (val, 1, "array assign");
|
---|
303 |
|
---|
304 | /* If we're using [subscript]=value, we need to quote each [ and ] to
|
---|
305 | prevent unwanted filename expansion. */
|
---|
306 | if (list)
|
---|
307 | quote_array_assignment_chars (list);
|
---|
308 |
|
---|
309 | /* Now that we've split it, perform the shell expansions on each
|
---|
310 | word in the list. */
|
---|
311 | nlist = list ? expand_words_no_vars (list) : (WORD_LIST *)NULL;
|
---|
312 |
|
---|
313 | dispose_words (list);
|
---|
314 |
|
---|
315 | if (val != value)
|
---|
316 | free (val);
|
---|
317 |
|
---|
318 | a = array_cell (var);
|
---|
319 |
|
---|
320 | /* Now that we are ready to assign values to the array, kill the existing
|
---|
321 | value. */
|
---|
322 | if (a && (flags & ASS_APPEND) == 0)
|
---|
323 | array_flush (a);
|
---|
324 | last_ind = (flags & ASS_APPEND) ? array_max_index (a) + 1 : 0;
|
---|
325 |
|
---|
326 | for (list = nlist; list; list = list->next)
|
---|
327 | {
|
---|
328 | w = list->word->word;
|
---|
329 |
|
---|
330 | /* We have a word of the form [ind]=value */
|
---|
331 | if ((list->word->flags & W_ASSIGNMENT) && w[0] == '[')
|
---|
332 | {
|
---|
333 | len = skipsubscript (w, 0);
|
---|
334 |
|
---|
335 | #if 1
|
---|
336 | /* XXX - changes for `+=' */
|
---|
337 | if (w[len] != ']' || (w[len+1] != '=' && (w[len+1] != '+' || w[len+2] != '=')))
|
---|
338 | #else
|
---|
339 | if (w[len] != ']' || w[len+1] != '=')
|
---|
340 | #endif
|
---|
341 | {
|
---|
342 | nval = make_variable_value (var, w, flags);
|
---|
343 | if (var->assign_func)
|
---|
344 | (*var->assign_func) (var, nval, last_ind);
|
---|
345 | else
|
---|
346 | array_insert (a, last_ind, nval);
|
---|
347 | FREE (nval);
|
---|
348 | last_ind++;
|
---|
349 | continue;
|
---|
350 | }
|
---|
351 |
|
---|
352 | if (len == 1)
|
---|
353 | {
|
---|
354 | err_badarraysub (w);
|
---|
355 | continue;
|
---|
356 | }
|
---|
357 |
|
---|
358 | if (ALL_ELEMENT_SUB (w[1]) && len == 2)
|
---|
359 | {
|
---|
360 | report_error (_("%s: cannot assign to non-numeric index"), w);
|
---|
361 | continue;
|
---|
362 | }
|
---|
363 |
|
---|
364 | ind = array_expand_index (w + 1, len);
|
---|
365 | if (ind < 0)
|
---|
366 | {
|
---|
367 | err_badarraysub (w);
|
---|
368 | continue;
|
---|
369 | }
|
---|
370 | last_ind = ind;
|
---|
371 | /* XXX - changes for `+=' */
|
---|
372 | if (w[len + 1] == '+' && w[len + 2] == '=')
|
---|
373 | {
|
---|
374 | flags |= ASS_APPEND;
|
---|
375 | val = w + len + 3;
|
---|
376 | }
|
---|
377 | else
|
---|
378 | val = w + len + 2;
|
---|
379 | }
|
---|
380 | else /* No [ind]=value, just a stray `=' */
|
---|
381 | {
|
---|
382 | ind = last_ind;
|
---|
383 | val = w;
|
---|
384 | }
|
---|
385 |
|
---|
386 | if (integer_p (var))
|
---|
387 | this_command_name = (char *)NULL; /* no command name for errors */
|
---|
388 | bind_array_var_internal (var, ind, val, flags);
|
---|
389 | last_ind++;
|
---|
390 | }
|
---|
391 |
|
---|
392 | dispose_words (nlist);
|
---|
393 | return (var);
|
---|
394 | }
|
---|
395 |
|
---|
396 | /* For each word in a compound array assignment, if the word looks like
|
---|
397 | [ind]=value, quote the `[' and `]' before the `=' to protect them from
|
---|
398 | unwanted filename expansion. */
|
---|
399 | static void
|
---|
400 | quote_array_assignment_chars (list)
|
---|
401 | WORD_LIST *list;
|
---|
402 | {
|
---|
403 | char *s, *t, *nword;
|
---|
404 | int saw_eq;
|
---|
405 | WORD_LIST *l;
|
---|
406 |
|
---|
407 | for (l = list; l; l = l->next)
|
---|
408 | {
|
---|
409 | if (l->word == 0 || l->word->word == 0 || l->word->word[0] == '\0')
|
---|
410 | continue; /* should not happen, but just in case... */
|
---|
411 | /* Don't bother if it doesn't look like [ind]=value */
|
---|
412 | if (l->word->word[0] != '[' || xstrchr (l->word->word, '=') == 0) /* ] */
|
---|
413 | continue;
|
---|
414 | s = nword = (char *)xmalloc (strlen (l->word->word) * 2 + 1);
|
---|
415 | saw_eq = 0;
|
---|
416 | for (t = l->word->word; *t; )
|
---|
417 | {
|
---|
418 | if (*t == '=')
|
---|
419 | saw_eq = 1;
|
---|
420 | if (saw_eq == 0 && (*t == '[' || *t == ']'))
|
---|
421 | *s++ = '\\';
|
---|
422 | *s++ = *t++;
|
---|
423 | }
|
---|
424 | *s = '\0';
|
---|
425 | free (l->word->word);
|
---|
426 | l->word->word = nword;
|
---|
427 | }
|
---|
428 | }
|
---|
429 |
|
---|
430 | /* This function assumes s[i] == '['; returns with s[ret] == ']' if
|
---|
431 | an array subscript is correctly parsed. */
|
---|
432 | int
|
---|
433 | skipsubscript (s, i)
|
---|
434 | const char *s;
|
---|
435 | int i;
|
---|
436 | {
|
---|
437 | int count, c;
|
---|
438 | #if defined (HANDLE_MULTIBYTE)
|
---|
439 | mbstate_t state, state_bak;
|
---|
440 | size_t slength, mblength;
|
---|
441 | size_t mb_cur_max;
|
---|
442 | #endif
|
---|
443 |
|
---|
444 | #if defined (HANDLE_MULTIBYTE)
|
---|
445 | memset (&state, '\0', sizeof (mbstate_t));
|
---|
446 | slength = strlen (s + i);
|
---|
447 | mb_cur_max = MB_CUR_MAX;
|
---|
448 | #endif
|
---|
449 |
|
---|
450 | count = 1;
|
---|
451 | while (count)
|
---|
452 | {
|
---|
453 | /* Advance one (possibly multibyte) character in S starting at I. */
|
---|
454 | #if defined (HANDLE_MULTIBYTE)
|
---|
455 | if (mb_cur_max > 1)
|
---|
456 | {
|
---|
457 | state_bak = state;
|
---|
458 | mblength = mbrlen (s + i, slength, &state);
|
---|
459 |
|
---|
460 | if (MB_INVALIDCH (mblength))
|
---|
461 | {
|
---|
462 | state = state_bak;
|
---|
463 | i++;
|
---|
464 | slength--;
|
---|
465 | }
|
---|
466 | else if (MB_NULLWCH (mblength))
|
---|
467 | return i;
|
---|
468 | else
|
---|
469 | {
|
---|
470 | i += mblength;
|
---|
471 | slength -= mblength;
|
---|
472 | }
|
---|
473 | }
|
---|
474 | else
|
---|
475 | #endif
|
---|
476 | ++i;
|
---|
477 |
|
---|
478 | c = s[i];
|
---|
479 |
|
---|
480 | if (c == 0)
|
---|
481 | break;
|
---|
482 | else if (c == '[')
|
---|
483 | count++;
|
---|
484 | else if (c == ']')
|
---|
485 | count--;
|
---|
486 | }
|
---|
487 |
|
---|
488 | return i;
|
---|
489 | }
|
---|
490 |
|
---|
491 | /* This function is called with SUB pointing to just after the beginning
|
---|
492 | `[' of an array subscript and removes the array element to which SUB
|
---|
493 | expands from array VAR. A subscript of `*' or `@' unsets the array. */
|
---|
494 | int
|
---|
495 | unbind_array_element (var, sub)
|
---|
496 | SHELL_VAR *var;
|
---|
497 | char *sub;
|
---|
498 | {
|
---|
499 | int len;
|
---|
500 | arrayind_t ind;
|
---|
501 | ARRAY_ELEMENT *ae;
|
---|
502 |
|
---|
503 | len = skipsubscript (sub, 0);
|
---|
504 | if (sub[len] != ']' || len == 0)
|
---|
505 | {
|
---|
506 | builtin_error ("%s[%s: %s", var->name, sub, _(bash_badsub_errmsg));
|
---|
507 | return -1;
|
---|
508 | }
|
---|
509 | sub[len] = '\0';
|
---|
510 |
|
---|
511 | if (ALL_ELEMENT_SUB (sub[0]) && sub[1] == 0)
|
---|
512 | {
|
---|
513 | unbind_variable (var->name);
|
---|
514 | return (0);
|
---|
515 | }
|
---|
516 | ind = array_expand_index (sub, len+1);
|
---|
517 | if (ind < 0)
|
---|
518 | {
|
---|
519 | builtin_error ("[%s]: %s", sub, _(bash_badsub_errmsg));
|
---|
520 | return -1;
|
---|
521 | }
|
---|
522 | ae = array_remove (array_cell (var), ind);
|
---|
523 | if (ae)
|
---|
524 | array_dispose_element (ae);
|
---|
525 | return 0;
|
---|
526 | }
|
---|
527 |
|
---|
528 | /* Format and output an array assignment in compound form VAR=(VALUES),
|
---|
529 | suitable for re-use as input. */
|
---|
530 | void
|
---|
531 | print_array_assignment (var, quoted)
|
---|
532 | SHELL_VAR *var;
|
---|
533 | int quoted;
|
---|
534 | {
|
---|
535 | char *vstr;
|
---|
536 |
|
---|
537 | vstr = array_to_assign (array_cell (var), quoted);
|
---|
538 |
|
---|
539 | if (vstr == 0)
|
---|
540 | printf ("%s=%s\n", var->name, quoted ? "'()'" : "()");
|
---|
541 | else
|
---|
542 | {
|
---|
543 | printf ("%s=%s\n", var->name, vstr);
|
---|
544 | free (vstr);
|
---|
545 | }
|
---|
546 | }
|
---|
547 |
|
---|
548 | /***********************************************************************/
|
---|
549 | /* */
|
---|
550 | /* Utility functions to manage arrays and their contents for expansion */
|
---|
551 | /* */
|
---|
552 | /***********************************************************************/
|
---|
553 |
|
---|
554 | /* Return 1 if NAME is a properly-formed array reference v[sub]. */
|
---|
555 | int
|
---|
556 | valid_array_reference (name)
|
---|
557 | char *name;
|
---|
558 | {
|
---|
559 | char *t;
|
---|
560 | int r, len;
|
---|
561 |
|
---|
562 | t = xstrchr (name, '['); /* ] */
|
---|
563 | if (t)
|
---|
564 | {
|
---|
565 | *t = '\0';
|
---|
566 | r = legal_identifier (name);
|
---|
567 | *t = '[';
|
---|
568 | if (r == 0)
|
---|
569 | return 0;
|
---|
570 | /* Check for a properly-terminated non-blank subscript. */
|
---|
571 | len = skipsubscript (t, 0);
|
---|
572 | if (t[len] != ']' || len == 1)
|
---|
573 | return 0;
|
---|
574 | for (r = 1; r < len; r++)
|
---|
575 | if (whitespace (t[r]) == 0)
|
---|
576 | return 1;
|
---|
577 | return 0;
|
---|
578 | }
|
---|
579 | return 0;
|
---|
580 | }
|
---|
581 |
|
---|
582 | /* Expand the array index beginning at S and extending LEN characters. */
|
---|
583 | arrayind_t
|
---|
584 | array_expand_index (s, len)
|
---|
585 | char *s;
|
---|
586 | int len;
|
---|
587 | {
|
---|
588 | char *exp, *t;
|
---|
589 | int expok;
|
---|
590 | arrayind_t val;
|
---|
591 |
|
---|
592 | exp = (char *)xmalloc (len);
|
---|
593 | strncpy (exp, s, len - 1);
|
---|
594 | exp[len - 1] = '\0';
|
---|
595 | t = expand_arith_string (exp, 0);
|
---|
596 | this_command_name = (char *)NULL;
|
---|
597 | val = evalexp (t, &expok);
|
---|
598 | free (t);
|
---|
599 | free (exp);
|
---|
600 | if (expok == 0)
|
---|
601 | {
|
---|
602 | last_command_exit_value = EXECUTION_FAILURE;
|
---|
603 | jump_to_top_level (DISCARD);
|
---|
604 | }
|
---|
605 | return val;
|
---|
606 | }
|
---|
607 |
|
---|
608 | /* Return the name of the variable specified by S without any subscript.
|
---|
609 | If SUBP is non-null, return a pointer to the start of the subscript
|
---|
610 | in *SUBP. If LENP is non-null, the length of the subscript is returned
|
---|
611 | in *LENP. This returns newly-allocated memory. */
|
---|
612 | char *
|
---|
613 | array_variable_name (s, subp, lenp)
|
---|
614 | char *s, **subp;
|
---|
615 | int *lenp;
|
---|
616 | {
|
---|
617 | char *t, *ret;
|
---|
618 | int ind, ni;
|
---|
619 |
|
---|
620 | t = xstrchr (s, '[');
|
---|
621 | if (t == 0)
|
---|
622 | {
|
---|
623 | if (subp)
|
---|
624 | *subp = t;
|
---|
625 | if (lenp)
|
---|
626 | *lenp = 0;
|
---|
627 | return ((char *)NULL);
|
---|
628 | }
|
---|
629 | ind = t - s;
|
---|
630 | ni = skipsubscript (s, ind);
|
---|
631 | if (ni <= ind + 1 || s[ni] != ']')
|
---|
632 | {
|
---|
633 | err_badarraysub (s);
|
---|
634 | if (subp)
|
---|
635 | *subp = t;
|
---|
636 | if (lenp)
|
---|
637 | *lenp = 0;
|
---|
638 | return ((char *)NULL);
|
---|
639 | }
|
---|
640 |
|
---|
641 | *t = '\0';
|
---|
642 | ret = savestring (s);
|
---|
643 | *t++ = '['; /* ] */
|
---|
644 |
|
---|
645 | if (subp)
|
---|
646 | *subp = t;
|
---|
647 | if (lenp)
|
---|
648 | *lenp = ni - ind;
|
---|
649 |
|
---|
650 | return ret;
|
---|
651 | }
|
---|
652 |
|
---|
653 | /* Return the variable specified by S without any subscript. If SUBP is
|
---|
654 | non-null, return a pointer to the start of the subscript in *SUBP.
|
---|
655 | If LENP is non-null, the length of the subscript is returned in *LENP. */
|
---|
656 | SHELL_VAR *
|
---|
657 | array_variable_part (s, subp, lenp)
|
---|
658 | char *s, **subp;
|
---|
659 | int *lenp;
|
---|
660 | {
|
---|
661 | char *t;
|
---|
662 | SHELL_VAR *var;
|
---|
663 |
|
---|
664 | t = array_variable_name (s, subp, lenp);
|
---|
665 | if (t == 0)
|
---|
666 | return ((SHELL_VAR *)NULL);
|
---|
667 | var = find_variable (t);
|
---|
668 |
|
---|
669 | free (t);
|
---|
670 | return (var == 0 || invisible_p (var)) ? (SHELL_VAR *)0 : var;
|
---|
671 | }
|
---|
672 |
|
---|
673 | /* Return a string containing the elements in the array and subscript
|
---|
674 | described by S. If the subscript is * or @, obeys quoting rules akin
|
---|
675 | to the expansion of $* and $@ including double quoting. If RTYPE
|
---|
676 | is non-null it gets 1 if the array reference is name[@] or name[*]
|
---|
677 | and 0 otherwise. */
|
---|
678 | static char *
|
---|
679 | array_value_internal (s, quoted, allow_all, rtype)
|
---|
680 | char *s;
|
---|
681 | int quoted, allow_all, *rtype;
|
---|
682 | {
|
---|
683 | int len;
|
---|
684 | arrayind_t ind;
|
---|
685 | char *retval, *t, *temp;
|
---|
686 | WORD_LIST *l;
|
---|
687 | SHELL_VAR *var;
|
---|
688 |
|
---|
689 | var = array_variable_part (s, &t, &len);
|
---|
690 |
|
---|
691 | /* Expand the index, even if the variable doesn't exist, in case side
|
---|
692 | effects are needed, like ${w[i++]} where w is unset. */
|
---|
693 | #if 0
|
---|
694 | if (var == 0)
|
---|
695 | return (char *)NULL;
|
---|
696 | #endif
|
---|
697 |
|
---|
698 | if (len == 0)
|
---|
699 | return ((char *)NULL); /* error message already printed */
|
---|
700 |
|
---|
701 | /* [ */
|
---|
702 | if (ALL_ELEMENT_SUB (t[0]) && t[1] == ']')
|
---|
703 | {
|
---|
704 | if (rtype)
|
---|
705 | *rtype = 1;
|
---|
706 | if (allow_all == 0)
|
---|
707 | {
|
---|
708 | err_badarraysub (s);
|
---|
709 | return ((char *)NULL);
|
---|
710 | }
|
---|
711 | else if (var == 0 || value_cell (var) == 0)
|
---|
712 | return ((char *)NULL);
|
---|
713 | else if (array_p (var) == 0)
|
---|
714 | l = add_string_to_list (value_cell (var), (WORD_LIST *)NULL);
|
---|
715 | else
|
---|
716 | {
|
---|
717 | l = array_to_word_list (array_cell (var));
|
---|
718 | if (l == (WORD_LIST *)NULL)
|
---|
719 | return ((char *) NULL);
|
---|
720 | }
|
---|
721 |
|
---|
722 | if (t[0] == '*' && (quoted & (Q_HERE_DOCUMENT|Q_DOUBLE_QUOTES)))
|
---|
723 | {
|
---|
724 | temp = string_list_dollar_star (l);
|
---|
725 | retval = quote_string (temp);
|
---|
726 | free (temp);
|
---|
727 | }
|
---|
728 | else /* ${name[@]} or unquoted ${name[*]} */
|
---|
729 | retval = string_list_dollar_at (l, quoted);
|
---|
730 |
|
---|
731 | dispose_words (l);
|
---|
732 | }
|
---|
733 | else
|
---|
734 | {
|
---|
735 | if (rtype)
|
---|
736 | *rtype = 0;
|
---|
737 | ind = array_expand_index (t, len);
|
---|
738 | if (ind < 0)
|
---|
739 | {
|
---|
740 | if (var)
|
---|
741 | err_badarraysub (var->name);
|
---|
742 | else
|
---|
743 | {
|
---|
744 | t[-1] = '\0';
|
---|
745 | err_badarraysub (s);
|
---|
746 | t[-1] = '['; /* ] */
|
---|
747 | }
|
---|
748 | return ((char *)NULL);
|
---|
749 | }
|
---|
750 | if (var == 0)
|
---|
751 | return ((char *)NULL);
|
---|
752 | if (array_p (var) == 0)
|
---|
753 | return (ind == 0 ? value_cell (var) : (char *)NULL);
|
---|
754 | retval = array_reference (array_cell (var), ind);
|
---|
755 | }
|
---|
756 |
|
---|
757 | return retval;
|
---|
758 | }
|
---|
759 |
|
---|
760 | /* Return a string containing the elements described by the array and
|
---|
761 | subscript contained in S, obeying quoting for subscripts * and @. */
|
---|
762 | char *
|
---|
763 | array_value (s, quoted, rtype)
|
---|
764 | char *s;
|
---|
765 | int quoted, *rtype;
|
---|
766 | {
|
---|
767 | return (array_value_internal (s, quoted, 1, rtype));
|
---|
768 | }
|
---|
769 |
|
---|
770 | /* Return the value of the array indexing expression S as a single string.
|
---|
771 | If ALLOW_ALL is 0, do not allow `@' and `*' subscripts. This is used
|
---|
772 | by other parts of the shell such as the arithmetic expression evaluator
|
---|
773 | in expr.c. */
|
---|
774 | char *
|
---|
775 | get_array_value (s, allow_all, rtype)
|
---|
776 | char *s;
|
---|
777 | int allow_all, *rtype;
|
---|
778 | {
|
---|
779 | return (array_value_internal (s, 0, allow_all, rtype));
|
---|
780 | }
|
---|
781 |
|
---|
782 | char *
|
---|
783 | array_keys (s, quoted)
|
---|
784 | char *s;
|
---|
785 | int quoted;
|
---|
786 | {
|
---|
787 | int len;
|
---|
788 | char *retval, *t, *temp;
|
---|
789 | WORD_LIST *l;
|
---|
790 | SHELL_VAR *var;
|
---|
791 |
|
---|
792 | var = array_variable_part (s, &t, &len);
|
---|
793 |
|
---|
794 | /* [ */
|
---|
795 | if (var == 0 || ALL_ELEMENT_SUB (t[0]) == 0 || t[1] != ']')
|
---|
796 | return (char *)NULL;
|
---|
797 |
|
---|
798 | if (array_p (var) == 0)
|
---|
799 | l = add_string_to_list ("0", (WORD_LIST *)NULL);
|
---|
800 | else
|
---|
801 | {
|
---|
802 | l = array_keys_to_word_list (array_cell (var));
|
---|
803 | if (l == (WORD_LIST *)NULL)
|
---|
804 | return ((char *) NULL);
|
---|
805 | }
|
---|
806 |
|
---|
807 | if (t[0] == '*' && (quoted & (Q_HERE_DOCUMENT|Q_DOUBLE_QUOTES)))
|
---|
808 | {
|
---|
809 | temp = string_list_dollar_star (l);
|
---|
810 | retval = quote_string (temp);
|
---|
811 | free (temp);
|
---|
812 | }
|
---|
813 | else /* ${!name[@]} or unquoted ${!name[*]} */
|
---|
814 | retval = string_list_dollar_at (l, quoted);
|
---|
815 |
|
---|
816 | dispose_words (l);
|
---|
817 | return retval;
|
---|
818 | }
|
---|
819 | #endif /* ARRAY_VARS */
|
---|