]> git.ipfire.org Git - thirdparty/bash.git/blame - array.c
Bash-5.1 patch 4: fix key-value pair associative array assignment word expansions
[thirdparty/bash.git] / array.c
CommitLineData
ccc6cda3
JA
1/*
2 * array.c - functions to create, destroy, access, and manipulate arrays
3 * of strings.
4 *
5 * Arrays are sparse doubly-linked lists. An element's index is stored
6 * with it.
7 *
8 * Chet Ramey
9 * chet@ins.cwru.edu
10 */
bb70624e 11
8868edaf 12/* Copyright (C) 1997-2020 Free Software Foundation, Inc.
bb70624e
JA
13
14 This file is part of GNU Bash, the Bourne Again SHell.
15
3185942a
JA
16 Bash is free software: you can redistribute it and/or modify
17 it under the terms of the GNU General Public License as published by
18 the Free Software Foundation, either version 3 of the License, or
19 (at your option) any later version.
bb70624e 20
3185942a
JA
21 Bash is distributed in the hope that it will be useful,
22 but WITHOUT ANY WARRANTY; without even the implied warranty of
23 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
24 GNU General Public License for more details.
bb70624e 25
3185942a
JA
26 You should have received a copy of the GNU General Public License
27 along with Bash. If not, see <http://www.gnu.org/licenses/>.
28*/
bb70624e 29
ccc6cda3
JA
30#include "config.h"
31
32#if defined (ARRAY_VARS)
33
34#if defined (HAVE_UNISTD_H)
cce855bc
JA
35# ifdef _MINIX
36# include <sys/types.h>
37# endif
ccc6cda3
JA
38# include <unistd.h>
39#endif
40
41#include <stdio.h>
d166f048
JA
42#include "bashansi.h"
43
ccc6cda3
JA
44#include "shell.h"
45#include "array.h"
46#include "builtins/common.h"
47
ccc6cda3
JA
48#define ADD_BEFORE(ae, new) \
49 do { \
50 ae->prev->next = new; \
51 new->prev = ae->prev; \
52 ae->prev = new; \
53 new->next = ae; \
54 } while(0)
d233b485
CR
55
56#define ADD_AFTER(ae, new) \
57 do { \
58 ae->next->prev = new; \
59 new->next = ae->next; \
60 new->prev = ae; \
61 ae->next = new; \
62 } while (0)
ccc6cda3 63
8868edaf 64static char *array_to_string_internal PARAMS((ARRAY_ELEMENT *, ARRAY_ELEMENT *, char *, int));
ccc6cda3 65
d233b485 66static char *spacesep = " ";
0001803f 67
d233b485 68#define IS_LASTREF(a) (a->lastref)
ac50fbac
CR
69
70#define LASTREF_START(a, i) \
d233b485
CR
71 (IS_LASTREF(a) && i >= element_index(a->lastref)) ? a->lastref \
72 : element_forw(a->head)
73
74#define LASTREF(a) (a->lastref ? a->lastref : element_forw(a->head))
75
76#define INVALIDATE_LASTREF(a) a->lastref = 0
77#define SET_LASTREF(a, e) a->lastref = (e)
78#define UNSET_LASTREF(a) a->lastref = 0;
0001803f 79
ccc6cda3 80ARRAY *
7117c2d2 81array_create()
ccc6cda3
JA
82{
83 ARRAY *r;
84 ARRAY_ELEMENT *head;
85
d233b485 86 r = (ARRAY *)xmalloc(sizeof(ARRAY));
ccc6cda3 87 r->type = array_indexed;
7117c2d2 88 r->max_index = -1;
ccc6cda3 89 r->num_elements = 0;
d233b485 90 r->lastref = (ARRAY_ELEMENT *)0;
7117c2d2 91 head = array_create_element(-1, (char *)NULL); /* dummy head */
ccc6cda3
JA
92 head->prev = head->next = head;
93 r->head = head;
94 return(r);
95}
96
97void
7117c2d2 98array_flush (a)
ccc6cda3
JA
99ARRAY *a;
100{
101 register ARRAY_ELEMENT *r, *r1;
102
103 if (a == 0)
104 return;
105 for (r = element_forw(a->head); r != a->head; ) {
106 r1 = element_forw(r);
7117c2d2 107 array_dispose_element(r);
ccc6cda3
JA
108 r = r1;
109 }
110 a->head->next = a->head->prev = a->head;
7117c2d2
JA
111 a->max_index = -1;
112 a->num_elements = 0;
0001803f 113 INVALIDATE_LASTREF(a);
ccc6cda3
JA
114}
115
116void
7117c2d2 117array_dispose(a)
ccc6cda3
JA
118ARRAY *a;
119{
120 if (a == 0)
121 return;
7117c2d2
JA
122 array_flush (a);
123 array_dispose_element(a->head);
ccc6cda3
JA
124 free(a);
125}
126
127ARRAY *
7117c2d2 128array_copy(a)
ccc6cda3
JA
129ARRAY *a;
130{
131 ARRAY *a1;
132 ARRAY_ELEMENT *ae, *new;
133
95732b49 134 if (a == 0)
ccc6cda3 135 return((ARRAY *) NULL);
7117c2d2 136 a1 = array_create();
ccc6cda3
JA
137 a1->type = a->type;
138 a1->max_index = a->max_index;
139 a1->num_elements = a->num_elements;
ccc6cda3 140 for (ae = element_forw(a->head); ae != a->head; ae = element_forw(ae)) {
7117c2d2 141 new = array_create_element(element_index(ae), element_value(ae));
ccc6cda3 142 ADD_BEFORE(a1->head, new);
d233b485
CR
143 if (ae == LASTREF(a))
144 SET_LASTREF(a1, new);
ccc6cda3
JA
145 }
146 return(a1);
147}
148
149/*
150 * Make and return a new array composed of the elements in array A from
151 * S to E, inclusive.
152 */
153ARRAY *
7117c2d2 154array_slice(array, s, e)
ccc6cda3
JA
155ARRAY *array;
156ARRAY_ELEMENT *s, *e;
157{
158 ARRAY *a;
159 ARRAY_ELEMENT *p, *n;
7117c2d2
JA
160 int i;
161 arrayind_t mi;
ccc6cda3 162
7117c2d2 163 a = array_create ();
ccc6cda3
JA
164 a->type = array->type;
165
3185942a 166 for (mi = 0, p = s, i = 0; p != e; p = element_forw(p), i++) {
7117c2d2 167 n = array_create_element (element_index(p), element_value(p));
ccc6cda3 168 ADD_BEFORE(a->head, n);
f1be666c 169 mi = element_index(n);
ccc6cda3 170 }
7117c2d2
JA
171 a->num_elements = i;
172 a->max_index = mi;
ccc6cda3
JA
173 return a;
174}
175
7117c2d2
JA
176/*
177 * Walk the array, calling FUNC once for each element, with the array
178 * element as the argument.
179 */
180void
b80f6443 181array_walk(a, func, udata)
7117c2d2
JA
182ARRAY *a;
183sh_ae_map_func_t *func;
b80f6443 184void *udata;
7117c2d2
JA
185{
186 register ARRAY_ELEMENT *ae;
187
188 if (a == 0 || array_empty(a))
189 return;
190 for (ae = element_forw(a->head); ae != a->head; ae = element_forw(ae))
b80f6443 191 if ((*func)(ae, udata) < 0)
7117c2d2
JA
192 return;
193}
194
195/*
196 * Shift the array A N elements to the left. Delete the first N elements
197 * and subtract N from the indices of the remaining elements. If FLAGS
198 * does not include AS_DISPOSE, this returns a singly-linked null-terminated
199 * list of elements so the caller can dispose of the chain. If FLAGS
200 * includes AS_DISPOSE, this function disposes of the shifted-out elements
201 * and returns NULL.
202 */
203ARRAY_ELEMENT *
204array_shift(a, n, flags)
205ARRAY *a;
206int n, flags;
207{
208 register ARRAY_ELEMENT *ae, *ret;
209 register int i;
210
211 if (a == 0 || array_empty(a) || n <= 0)
212 return ((ARRAY_ELEMENT *)NULL);
213
0001803f 214 INVALIDATE_LASTREF(a);
7117c2d2
JA
215 for (i = 0, ret = ae = element_forw(a->head); ae != a->head && i < n; ae = element_forw(ae), i++)
216 ;
217 if (ae == a->head) {
218 /* Easy case; shifting out all of the elements */
219 if (flags & AS_DISPOSE) {
220 array_flush (a);
221 return ((ARRAY_ELEMENT *)NULL);
222 }
223 for (ae = ret; element_forw(ae) != a->head; ae = element_forw(ae))
224 ;
225 element_forw(ae) = (ARRAY_ELEMENT *)NULL;
226 a->head->next = a->head->prev = a->head;
227 a->max_index = -1;
228 a->num_elements = 0;
229 return ret;
230 }
231 /*
232 * ae now points to the list of elements we want to retain.
233 * ret points to the list we want to either destroy or return.
234 */
235 ae->prev->next = (ARRAY_ELEMENT *)NULL; /* null-terminate RET */
236
237 a->head->next = ae; /* slice RET out of the array */
238 ae->prev = a->head;
239
240 for ( ; ae != a->head; ae = element_forw(ae))
241 element_index(ae) -= n; /* renumber retained indices */
242
243 a->num_elements -= n; /* modify bookkeeping information */
0001803f 244 a->max_index = element_index(a->head->prev);
7117c2d2
JA
245
246 if (flags & AS_DISPOSE) {
247 for (ae = ret; ae; ) {
248 ret = element_forw(ae);
249 array_dispose_element(ae);
250 ae = ret;
251 }
252 return ((ARRAY_ELEMENT *)NULL);
253 }
254
255 return ret;
256}
257
258/*
259 * Shift array A right N indices. If S is non-null, it becomes the value of
260 * the new element 0. Returns the number of elements in the array after the
261 * shift.
262 */
263int
264array_rshift (a, n, s)
265ARRAY *a;
266int n;
267char *s;
268{
269 register ARRAY_ELEMENT *ae, *new;
270
95732b49 271 if (a == 0 || (array_empty(a) && s == 0))
7117c2d2 272 return 0;
95732b49 273 else if (n <= 0)
7117c2d2
JA
274 return (a->num_elements);
275
276 ae = element_forw(a->head);
277 if (s) {
278 new = array_create_element(0, s);
279 ADD_BEFORE(ae, new);
280 a->num_elements++;
0001803f
CR
281 if (array_num_elements(a) == 1) { /* array was empty */
282 a->max_index = 0;
95732b49 283 return 1;
0001803f 284 }
7117c2d2
JA
285 }
286
7117c2d2
JA
287 /*
288 * Renumber all elements in the array except the one we just added.
289 */
290 for ( ; ae != a->head; ae = element_forw(ae))
291 element_index(ae) += n;
292
95732b49
JA
293 a->max_index = element_index(a->head->prev);
294
0001803f 295 INVALIDATE_LASTREF(a);
7117c2d2
JA
296 return (a->num_elements);
297}
298
b80f6443
JA
299ARRAY_ELEMENT *
300array_unshift_element(a)
301ARRAY *a;
302{
303 return (array_shift (a, 1, 0));
304}
305
306int
307array_shift_element(a, v)
308ARRAY *a;
309char *v;
310{
311 return (array_rshift (a, 1, v));
312}
313
3185942a 314ARRAY *
7117c2d2
JA
315array_quote(array)
316ARRAY *array;
317{
318 ARRAY_ELEMENT *a;
319 char *t;
320
95732b49 321 if (array == 0 || array_head(array) == 0 || array_empty(array))
7117c2d2
JA
322 return (ARRAY *)NULL;
323 for (a = element_forw(array->head); a != array->head; a = element_forw(a)) {
324 t = quote_string (a->value);
325 FREE(a->value);
326 a->value = t;
327 }
328 return array;
329}
330
3185942a 331ARRAY *
f1be666c
JA
332array_quote_escapes(array)
333ARRAY *array;
334{
335 ARRAY_ELEMENT *a;
336 char *t;
337
338 if (array == 0 || array_head(array) == 0 || array_empty(array))
339 return (ARRAY *)NULL;
340 for (a = element_forw(array->head); a != array->head; a = element_forw(a)) {
341 t = quote_escapes (a->value);
342 FREE(a->value);
343 a->value = t;
344 }
345 return array;
346}
347
3185942a
JA
348ARRAY *
349array_dequote(array)
350ARRAY *array;
351{
352 ARRAY_ELEMENT *a;
353 char *t;
354
355 if (array == 0 || array_head(array) == 0 || array_empty(array))
356 return (ARRAY *)NULL;
357 for (a = element_forw(array->head); a != array->head; a = element_forw(a)) {
358 t = dequote_string (a->value);
359 FREE(a->value);
360 a->value = t;
361 }
362 return array;
363}
364
365ARRAY *
366array_dequote_escapes(array)
367ARRAY *array;
368{
369 ARRAY_ELEMENT *a;
370 char *t;
371
372 if (array == 0 || array_head(array) == 0 || array_empty(array))
373 return (ARRAY *)NULL;
374 for (a = element_forw(array->head); a != array->head; a = element_forw(a)) {
375 t = dequote_escapes (a->value);
376 FREE(a->value);
377 a->value = t;
378 }
379 return array;
380}
381
382ARRAY *
383array_remove_quoted_nulls(array)
384ARRAY *array;
385{
386 ARRAY_ELEMENT *a;
3185942a
JA
387
388 if (array == 0 || array_head(array) == 0 || array_empty(array))
389 return (ARRAY *)NULL;
390 for (a = element_forw(array->head); a != array->head; a = element_forw(a))
391 a->value = remove_quoted_nulls (a->value);
392 return array;
393}
394
b80f6443
JA
395/*
396 * Return a string whose elements are the members of array A beginning at
397 * index START and spanning NELEM members. Null elements are counted.
398 * Since arrays are sparse, unset array elements are not counted.
399 */
7117c2d2 400char *
8868edaf 401array_subrange (a, start, nelem, starsub, quoted, pflags)
7117c2d2 402ARRAY *a;
b80f6443 403arrayind_t start, nelem;
8868edaf 404int starsub, quoted, pflags;
7117c2d2 405{
f1be666c 406 ARRAY *a2;
7117c2d2
JA
407 ARRAY_ELEMENT *h, *p;
408 arrayind_t i;
d233b485
CR
409 char *t;
410 WORD_LIST *wl;
7117c2d2 411
95732b49 412 p = a ? array_head (a) : 0;
b80f6443 413 if (p == 0 || array_empty (a) || start > array_max_index(a))
7117c2d2
JA
414 return ((char *)NULL);
415
b80f6443
JA
416 /*
417 * Find element with index START. If START corresponds to an unset
418 * element (arrays can be sparse), use the first element whose index
419 * is >= START. If START is < 0, we count START indices back from
420 * the end of A (not elements, even with sparse arrays -- START is an
421 * index).
422 */
423 for (p = element_forw(p); p != array_head(a) && start > element_index(p); p = element_forw(p))
7117c2d2 424 ;
b80f6443 425
7117c2d2
JA
426 if (p == a->head)
427 return ((char *)NULL);
b80f6443
JA
428
429 /* Starting at P, take NELEM elements, inclusive. */
430 for (i = 0, h = p; p != a->head && i < nelem; i++, p = element_forw(p))
7117c2d2
JA
431 ;
432
f1be666c
JA
433 a2 = array_slice(a, h, p);
434
d233b485 435 wl = array_to_word_list(a2);
f1be666c 436 array_dispose(a2);
d233b485
CR
437 if (wl == 0)
438 return (char *)NULL;
8868edaf 439 t = string_list_pos_params(starsub ? '*' : '@', wl, quoted, pflags); /* XXX */
d233b485 440 dispose_words(wl);
f1be666c
JA
441
442 return t;
7117c2d2
JA
443}
444
445char *
446array_patsub (a, pat, rep, mflags)
447ARRAY *a;
448char *pat, *rep;
449int mflags;
450{
d233b485 451 char *t;
8868edaf 452 int pchar, qflags, pflags;
d233b485 453 WORD_LIST *wl, *save;
7117c2d2 454
95732b49 455 if (a == 0 || array_head(a) == 0 || array_empty(a))
7117c2d2
JA
456 return ((char *)NULL);
457
d233b485
CR
458 wl = array_to_word_list(a);
459 if (wl == 0)
460 return (char *)NULL;
461
462 for (save = wl; wl; wl = wl->next) {
463 t = pat_subst (wl->word->word, pat, rep, mflags);
464 FREE (wl->word->word);
465 wl->word->word = t;
7117c2d2
JA
466 }
467
d233b485
CR
468 pchar = (mflags & MATCH_STARSUB) == MATCH_STARSUB ? '*' : '@';
469 qflags = (mflags & MATCH_QUOTED) == MATCH_QUOTED ? Q_DOUBLE_QUOTES : 0;
8868edaf 470 pflags = (mflags & MATCH_ASSIGNRHS) ? PF_ASSIGNRHS : 0;
d233b485 471
8868edaf 472 t = string_list_pos_params (pchar, save, qflags, pflags);
d233b485 473 dispose_words(save);
7117c2d2
JA
474
475 return t;
476}
477
3185942a
JA
478char *
479array_modcase (a, pat, modop, mflags)
480ARRAY *a;
481char *pat;
482int modop;
483int mflags;
484{
d233b485 485 char *t;
8868edaf 486 int pchar, qflags, pflags;
d233b485 487 WORD_LIST *wl, *save;
3185942a
JA
488
489 if (a == 0 || array_head(a) == 0 || array_empty(a))
490 return ((char *)NULL);
491
d233b485
CR
492 wl = array_to_word_list(a);
493 if (wl == 0)
494 return ((char *)NULL);
495
496 for (save = wl; wl; wl = wl->next) {
497 t = sh_modcase(wl->word->word, pat, modop);
498 FREE(wl->word->word);
499 wl->word->word = t;
3185942a
JA
500 }
501
d233b485
CR
502 pchar = (mflags & MATCH_STARSUB) == MATCH_STARSUB ? '*' : '@';
503 qflags = (mflags & MATCH_QUOTED) == MATCH_QUOTED ? Q_DOUBLE_QUOTES : 0;
8868edaf 504 pflags = (mflags & MATCH_ASSIGNRHS) ? PF_ASSIGNRHS : 0;
d233b485 505
8868edaf 506 t = string_list_pos_params (pchar, save, qflags, pflags);
d233b485 507 dispose_words(save);
3185942a
JA
508
509 return t;
510}
d233b485 511
7117c2d2
JA
512/*
513 * Allocate and return a new array element with index INDEX and value
514 * VALUE.
515 */
516ARRAY_ELEMENT *
517array_create_element(indx, value)
518arrayind_t indx;
519char *value;
520{
521 ARRAY_ELEMENT *r;
522
523 r = (ARRAY_ELEMENT *)xmalloc(sizeof(ARRAY_ELEMENT));
524 r->ind = indx;
525 r->value = value ? savestring(value) : (char *)NULL;
526 r->next = r->prev = (ARRAY_ELEMENT *) NULL;
527 return(r);
528}
529
cce855bc 530#ifdef INCLUDE_UNUSED
ccc6cda3 531ARRAY_ELEMENT *
7117c2d2 532array_copy_element(ae)
ccc6cda3
JA
533ARRAY_ELEMENT *ae;
534{
7117c2d2 535 return(ae ? array_create_element(element_index(ae), element_value(ae))
ccc6cda3
JA
536 : (ARRAY_ELEMENT *) NULL);
537}
cce855bc 538#endif
ccc6cda3 539
7117c2d2
JA
540void
541array_dispose_element(ae)
542ARRAY_ELEMENT *ae;
543{
b80f6443
JA
544 if (ae) {
545 FREE(ae->value);
546 free(ae);
547 }
7117c2d2
JA
548}
549
ccc6cda3
JA
550/*
551 * Add a new element with index I and value V to array A (a[i] = v).
552 */
553int
7117c2d2 554array_insert(a, i, v)
ccc6cda3
JA
555ARRAY *a;
556arrayind_t i;
557char *v;
558{
ac50fbac 559 register ARRAY_ELEMENT *new, *ae, *start;
d233b485
CR
560 arrayind_t startind;
561 int direction;
ccc6cda3 562
95732b49 563 if (a == 0)
ccc6cda3 564 return(-1);
7117c2d2 565 new = array_create_element(i, v);
ccc6cda3
JA
566 if (i > array_max_index(a)) {
567 /*
568 * Hook onto the end. This also works for an empty array.
569 * Fast path for the common case of allocating arrays
570 * sequentially.
571 */
572 ADD_BEFORE(a->head, new);
573 a->max_index = i;
574 a->num_elements++;
0001803f 575 SET_LASTREF(a, new);
ccc6cda3 576 return(0);
d233b485
CR
577 } else if (i < array_first_index(a)) {
578 /* Hook at the beginning */
579 ADD_AFTER(a->head, new);
580 a->num_elements++;
581 SET_LASTREF(a, new);
582 return(0);
ccc6cda3 583 }
ac50fbac 584#if OPTIMIZE_SEQUENTIAL_ARRAY_ASSIGNMENT
ccc6cda3 585 /*
ac50fbac
CR
586 * Otherwise we search for the spot to insert it. The lastref
587 * handle optimizes the case of sequential or almost-sequential
588 * assignments that are not at the end of the array.
ccc6cda3 589 */
d233b485
CR
590 start = LASTREF(a);
591 /* Use same strategy as array_reference to avoid paying large penalty
592 for semi-random assignment pattern. */
593 startind = element_index(start);
594 if (i < startind/2) {
595 start = element_forw(a->head);
596 startind = element_index(start);
597 direction = 1;
598 } else if (i >= startind) {
599 direction = 1;
600 } else {
601 direction = -1;
602 }
ac50fbac
CR
603#else
604 start = element_forw(ae->head);
d233b485
CR
605 startind = element_index(start);
606 direction = 1;
ac50fbac 607#endif
d233b485 608 for (ae = start; ae != a->head; ) {
ccc6cda3
JA
609 if (element_index(ae) == i) {
610 /*
611 * Replacing an existing element.
612 */
ccc6cda3 613 free(element_value(ae));
d233b485
CR
614 /* Just swap in the new value */
615 ae->value = new->value;
616 new->value = 0;
617 array_dispose_element(new);
0001803f 618 SET_LASTREF(a, ae);
ccc6cda3 619 return(0);
d233b485 620 } else if (direction == 1 && element_index(ae) > i) {
ccc6cda3
JA
621 ADD_BEFORE(ae, new);
622 a->num_elements++;
0001803f 623 SET_LASTREF(a, new);
ccc6cda3 624 return(0);
d233b485
CR
625 } else if (direction == -1 && element_index(ae) < i) {
626 ADD_AFTER(ae, new);
627 a->num_elements++;
628 SET_LASTREF(a, new);
629 return(0);
ccc6cda3 630 }
d233b485 631 ae = direction == 1 ? element_forw(ae) : element_back(ae);
ccc6cda3 632 }
ac50fbac 633 array_dispose_element(new);
0001803f 634 INVALIDATE_LASTREF(a);
ccc6cda3
JA
635 return (-1); /* problem */
636}
637
638/*
639 * Delete the element with index I from array A and return it so the
640 * caller can dispose of it.
641 */
642ARRAY_ELEMENT *
7117c2d2 643array_remove(a, i)
ccc6cda3
JA
644ARRAY *a;
645arrayind_t i;
646{
ac50fbac 647 register ARRAY_ELEMENT *ae, *start;
d233b485
CR
648 arrayind_t startind;
649 int direction;
ccc6cda3 650
95732b49 651 if (a == 0 || array_empty(a))
ccc6cda3 652 return((ARRAY_ELEMENT *) NULL);
d233b485
CR
653 if (i > array_max_index(a) || i < array_first_index(a))
654 return((ARRAY_ELEMENT *)NULL); /* Keep roving pointer into array to optimize sequential access */
655 start = LASTREF(a);
656 /* Use same strategy as array_reference to avoid paying large penalty
657 for semi-random assignment pattern. */
658 startind = element_index(start);
659 if (i < startind/2) {
660 start = element_forw(a->head);
661 startind = element_index(start);
662 direction = 1;
663 } else if (i >= startind) {
664 direction = 1;
665 } else {
666 direction = -1;
667 }
668 for (ae = start; ae != a->head; ) {
ccc6cda3
JA
669 if (element_index(ae) == i) {
670 ae->next->prev = ae->prev;
671 ae->prev->next = ae->next;
672 a->num_elements--;
673 if (i == array_max_index(a))
674 a->max_index = element_index(ae->prev);
ac50fbac 675#if 0
0001803f 676 INVALIDATE_LASTREF(a);
ac50fbac
CR
677#else
678 if (ae->next != a->head)
679 SET_LASTREF(a, ae->next);
680 else if (ae->prev != a->head)
681 SET_LASTREF(a, ae->prev);
682 else
683 INVALIDATE_LASTREF(a);
684#endif
ccc6cda3
JA
685 return(ae);
686 }
d233b485
CR
687 ae = (direction == 1) ? element_forw(ae) : element_back(ae);
688 if (direction == 1 && element_index(ae) > i)
689 break;
690 else if (direction == -1 && element_index(ae) < i)
691 break;
692 }
ccc6cda3
JA
693 return((ARRAY_ELEMENT *) NULL);
694}
695
696/*
697 * Return the value of a[i].
698 */
699char *
700array_reference(a, i)
701ARRAY *a;
702arrayind_t i;
703{
ac50fbac 704 register ARRAY_ELEMENT *ae, *start;
d233b485
CR
705 arrayind_t startind;
706 int direction;
ccc6cda3
JA
707
708 if (a == 0 || array_empty(a))
709 return((char *) NULL);
d233b485 710 if (i > array_max_index(a) || i < array_first_index(a))
ac50fbac 711 return((char *)NULL); /* Keep roving pointer into array to optimize sequential access */
d233b485
CR
712 start = LASTREF(a); /* lastref pointer */
713 startind = element_index(start);
714 if (i < startind/2) { /* XXX - guess */
715 start = element_forw(a->head);
716 startind = element_index(start);
717 direction = 1;
718 } else if (i >= startind) {
719 direction = 1;
720 } else {
721 direction = -1;
722 }
723 for (ae = start; ae != a->head; ) {
0001803f
CR
724 if (element_index(ae) == i) {
725 SET_LASTREF(a, ae);
ccc6cda3 726 return(element_value(ae));
0001803f 727 }
d233b485
CR
728 ae = (direction == 1) ? element_forw(ae) : element_back(ae);
729 /* Take advantage of index ordering to short-circuit */
730 /* If we don't find it, set the lastref pointer to the element
731 that's `closest', assuming that the unsuccessful reference
732 will quickly be followed by an assignment. No worse than
733 not changing it from the previous value or resetting it. */
734 if (direction == 1 && element_index(ae) > i) {
735 start = ae; /* use for SET_LASTREF below */
736 break;
737 } else if (direction == -1 && element_index(ae) < i) {
738 start = ae; /* use for SET_LASTREF below */
739 break;
740 }
741 }
742#if 0
743 UNSET_LASTREF(a);
744#else
745 SET_LASTREF(a, start);
746#endif
ccc6cda3
JA
747 return((char *) NULL);
748}
749
7117c2d2
JA
750/* Convenience routines for the shell to translate to and from the form used
751 by the rest of the code. */
b80f6443 752
7117c2d2
JA
753WORD_LIST *
754array_to_word_list(a)
ccc6cda3 755ARRAY *a;
ccc6cda3 756{
7117c2d2
JA
757 WORD_LIST *list;
758 ARRAY_ELEMENT *ae;
ccc6cda3
JA
759
760 if (a == 0 || array_empty(a))
7117c2d2
JA
761 return((WORD_LIST *)NULL);
762 list = (WORD_LIST *)NULL;
ccc6cda3 763 for (ae = element_forw(a->head); ae != a->head; ae = element_forw(ae))
7117c2d2
JA
764 list = make_word_list (make_bare_word(element_value(ae)), list);
765 return (REVERSE_LIST(list, WORD_LIST *));
ccc6cda3
JA
766}
767
7117c2d2
JA
768ARRAY *
769array_from_word_list (list)
770WORD_LIST *list;
771{
772 ARRAY *a;
773
774 if (list == 0)
775 return((ARRAY *)NULL);
776 a = array_create();
777 return (array_assign_list (a, list));
778}
779
b80f6443
JA
780WORD_LIST *
781array_keys_to_word_list(a)
782ARRAY *a;
783{
784 WORD_LIST *list;
785 ARRAY_ELEMENT *ae;
786 char *t;
787
788 if (a == 0 || array_empty(a))
789 return((WORD_LIST *)NULL);
790 list = (WORD_LIST *)NULL;
791 for (ae = element_forw(a->head); ae != a->head; ae = element_forw(ae)) {
792 t = itos(element_index(ae));
793 list = make_word_list (make_bare_word(t), list);
794 free(t);
795 }
796 return (REVERSE_LIST(list, WORD_LIST *));
797}
798
7117c2d2
JA
799ARRAY *
800array_assign_list (array, list)
801ARRAY *array;
802WORD_LIST *list;
803{
804 register WORD_LIST *l;
805 register arrayind_t i;
806
807 for (l = list, i = 0; l; l = l->next, i++)
808 array_insert(array, i, l->word->word);
809 return array;
810}
811
812char **
8868edaf 813array_to_argv (a, countp)
7117c2d2 814ARRAY *a;
8868edaf 815int *countp;
7117c2d2
JA
816{
817 char **ret, *t;
818 int i;
819 ARRAY_ELEMENT *ae;
820
8868edaf
CR
821 if (a == 0 || array_empty(a)) {
822 if (countp)
823 *countp = 0;
7117c2d2 824 return ((char **)NULL);
8868edaf 825 }
7117c2d2
JA
826 ret = strvec_create (array_num_elements (a) + 1);
827 i = 0;
828 for (ae = element_forw(a->head); ae != a->head; ae = element_forw(ae)) {
829 t = element_value (ae);
8868edaf
CR
830 if (t)
831 ret[i++] = savestring (t);
7117c2d2
JA
832 }
833 ret[i] = (char *)NULL;
8868edaf
CR
834 if (countp)
835 *countp = i;
7117c2d2
JA
836 return (ret);
837}
838
ccc6cda3 839/*
3185942a
JA
840 * Return a string that is the concatenation of the elements in A from START
841 * to END, separated by SEP.
ccc6cda3
JA
842 */
843static char *
844array_to_string_internal (start, end, sep, quoted)
845ARRAY_ELEMENT *start, *end;
846char *sep;
847int quoted;
848{
849 char *result, *t;
850 ARRAY_ELEMENT *ae;
851 int slen, rsize, rlen, reg;
852
853 if (start == end) /* XXX - should not happen */
854 return ((char *)NULL);
855
856 slen = strlen(sep);
f73dda09 857 result = NULL;
ccc6cda3
JA
858 for (rsize = rlen = 0, ae = start; ae != end; ae = element_forw(ae)) {
859 if (rsize == 0)
f73dda09 860 result = (char *)xmalloc (rsize = 64);
ccc6cda3
JA
861 if (element_value(ae)) {
862 t = quoted ? quote_string(element_value(ae)) : element_value(ae);
863 reg = strlen(t);
864 RESIZE_MALLOCED_BUFFER (result, rlen, (reg + slen + 2),
865 rsize, rsize);
866 strcpy(result + rlen, t);
867 rlen += reg;
a0c0a00f 868 if (quoted)
ccc6cda3
JA
869 free(t);
870 /*
871 * Add a separator only after non-null elements.
872 */
873 if (element_forw(ae) != end) {
874 strcpy(result + rlen, sep);
875 rlen += slen;
876 }
877 }
878 }
f73dda09
JA
879 if (result)
880 result[rlen] = '\0'; /* XXX */
ccc6cda3
JA
881 return(result);
882}
883
8868edaf
CR
884char *
885array_to_kvpair (a, quoted)
886ARRAY *a;
887int quoted;
888{
889 char *result, *valstr, *is;
890 char indstr[INT_STRLEN_BOUND(intmax_t) + 1];
891 ARRAY_ELEMENT *ae;
892 int rsize, rlen, elen;
893
894 if (a == 0 || array_empty (a))
895 return((char *)NULL);
896
897 result = (char *)xmalloc (rsize = 128);
898 result[rlen = 0] = '\0';
899
900 for (ae = element_forw(a->head); ae != a->head; ae = element_forw(ae)) {
901 is = inttostr (element_index(ae), indstr, sizeof(indstr));
902 valstr = element_value (ae) ?
903 (ansic_shouldquote (element_value (ae)) ?
904 ansic_quote (element_value(ae), 0, (int *)0) :
905 sh_double_quote (element_value (ae)))
906 : (char *)NULL;
907 elen = STRLEN (is) + 8 + STRLEN (valstr);
908 RESIZE_MALLOCED_BUFFER (result, rlen, (elen + 1), rsize, rsize);
909
910 strcpy (result + rlen, is);
911 rlen += STRLEN (is);
912 result[rlen++] = ' ';
913 if (valstr) {
914 strcpy (result + rlen, valstr);
915 rlen += STRLEN (valstr);
916 } else {
917 strcpy (result + rlen, "\"\"");
918 rlen += 2;
919 }
920
921 if (element_forw(ae) != a->head)
922 result[rlen++] = ' ';
923
924 FREE (valstr);
925 }
926 RESIZE_MALLOCED_BUFFER (result, rlen, 1, rsize, 8);
927 result[rlen] = '\0';
928
929 if (quoted) {
930 /* This is not as efficient as it could be... */
931 valstr = sh_single_quote (result);
932 free (result);
933 result = valstr;
934 }
935 return(result);
936}
937
ccc6cda3 938char *
7117c2d2 939array_to_assign (a, quoted)
ccc6cda3 940ARRAY *a;
ccc6cda3
JA
941int quoted;
942{
7117c2d2
JA
943 char *result, *valstr, *is;
944 char indstr[INT_STRLEN_BOUND(intmax_t) + 1];
ccc6cda3
JA
945 ARRAY_ELEMENT *ae;
946 int rsize, rlen, elen;
947
948 if (a == 0 || array_empty (a))
949 return((char *)NULL);
950
f73dda09 951 result = (char *)xmalloc (rsize = 128);
ccc6cda3
JA
952 result[0] = '(';
953 rlen = 1;
954
955 for (ae = element_forw(a->head); ae != a->head; ae = element_forw(ae)) {
7117c2d2 956 is = inttostr (element_index(ae), indstr, sizeof(indstr));
a0c0a00f
CR
957 valstr = element_value (ae) ?
958 (ansic_shouldquote (element_value (ae)) ?
959 ansic_quote (element_value(ae), 0, (int *)0) :
960 sh_double_quote (element_value (ae)))
ccc6cda3 961 : (char *)NULL;
f1be666c 962 elen = STRLEN (is) + 8 + STRLEN (valstr);
ccc6cda3
JA
963 RESIZE_MALLOCED_BUFFER (result, rlen, (elen + 1), rsize, rsize);
964
965 result[rlen++] = '[';
7117c2d2
JA
966 strcpy (result + rlen, is);
967 rlen += STRLEN (is);
ccc6cda3
JA
968 result[rlen++] = ']';
969 result[rlen++] = '=';
970 if (valstr) {
971 strcpy (result + rlen, valstr);
972 rlen += STRLEN (valstr);
973 }
974
975 if (element_forw(ae) != a->head)
976 result[rlen++] = ' ';
977
ccc6cda3
JA
978 FREE (valstr);
979 }
980 RESIZE_MALLOCED_BUFFER (result, rlen, 1, rsize, 8);
981 result[rlen++] = ')';
982 result[rlen] = '\0';
7117c2d2
JA
983 if (quoted) {
984 /* This is not as efficient as it could be... */
985 valstr = sh_single_quote (result);
986 free (result);
987 result = valstr;
988 }
ccc6cda3
JA
989 return(result);
990}
991
992char *
7117c2d2 993array_to_string (a, sep, quoted)
ccc6cda3 994ARRAY *a;
7117c2d2
JA
995char *sep;
996int quoted;
ccc6cda3 997{
7117c2d2
JA
998 if (a == 0)
999 return((char *)NULL);
1000 if (array_empty(a))
1001 return(savestring(""));
1002 return (array_to_string_internal (element_forw(a->head), a->head, sep, quoted));
ccc6cda3 1003}
ccc6cda3 1004
d166f048 1005#if defined (INCLUDE_UNUSED) || defined (TEST_ARRAY)
ccc6cda3
JA
1006/*
1007 * Return an array consisting of elements in S, separated by SEP
1008 */
1009ARRAY *
7117c2d2 1010array_from_string(s, sep)
ccc6cda3
JA
1011char *s, *sep;
1012{
1013 ARRAY *a;
1014 WORD_LIST *w;
1015
1016 if (s == 0)
1017 return((ARRAY *)NULL);
1018 w = list_string (s, sep, 0);
1019 if (w == 0)
1020 return((ARRAY *)NULL);
7117c2d2 1021 a = array_from_word_list (w);
ccc6cda3
JA
1022 return (a);
1023}
d166f048 1024#endif
ccc6cda3 1025
7117c2d2
JA
1026#if defined (TEST_ARRAY)
1027/*
1028 * To make a running version, compile -DTEST_ARRAY and link with:
1029 * xmalloc.o syntax.o lib/malloc/libmalloc.a lib/sh/libsh.a
1030 */
1031int interrupt_immediately = 0;
ccc6cda3 1032
7117c2d2
JA
1033int
1034signal_is_trapped(s)
1035int s;
1036{
1037 return 0;
ccc6cda3
JA
1038}
1039
7117c2d2
JA
1040void
1041fatal_error(const char *s, ...)
bb70624e 1042{
7117c2d2
JA
1043 fprintf(stderr, "array_test: fatal memory error\n");
1044 abort();
1045}
bb70624e 1046
7117c2d2
JA
1047void
1048programming_error(const char *s, ...)
1049{
1050 fprintf(stderr, "array_test: fatal programming error\n");
1051 abort();
bb70624e 1052}
7117c2d2
JA
1053
1054WORD_DESC *
1055make_bare_word (s)
1056const char *s;
ccc6cda3 1057{
7117c2d2 1058 WORD_DESC *w;
ccc6cda3 1059
7117c2d2
JA
1060 w = (WORD_DESC *)xmalloc(sizeof(WORD_DESC));
1061 w->word = s ? savestring(s) : savestring ("");
1062 w->flags = 0;
1063 return w;
ccc6cda3
JA
1064}
1065
7117c2d2
JA
1066WORD_LIST *
1067make_word_list(x, l)
1068WORD_DESC *x;
1069WORD_LIST *l;
ccc6cda3 1070{
7117c2d2 1071 WORD_LIST *w;
ccc6cda3 1072
7117c2d2
JA
1073 w = (WORD_LIST *)xmalloc(sizeof(WORD_LIST));
1074 w->word = x;
1075 w->next = l;
1076 return w;
ccc6cda3
JA
1077}
1078
7117c2d2
JA
1079WORD_LIST *
1080list_string(s, t, i)
1081char *s, *t;
1082int i;
ccc6cda3 1083{
7117c2d2
JA
1084 char *r, *a;
1085 WORD_LIST *wl;
ccc6cda3 1086
7117c2d2
JA
1087 if (s == 0)
1088 return (WORD_LIST *)NULL;
1089 r = savestring(s);
1090 wl = (WORD_LIST *)NULL;
1091 a = strtok(r, t);
1092 while (a) {
1093 wl = make_word_list (make_bare_word(a), wl);
1094 a = strtok((char *)NULL, t);
ccc6cda3 1095 }
7117c2d2 1096 return (REVERSE_LIST (wl, WORD_LIST *));
ccc6cda3
JA
1097}
1098
7117c2d2
JA
1099GENERIC_LIST *
1100list_reverse (list)
1101GENERIC_LIST *list;
ccc6cda3 1102{
7117c2d2 1103 register GENERIC_LIST *next, *prev;
ccc6cda3 1104
7117c2d2
JA
1105 for (prev = 0; list; ) {
1106 next = list->next;
1107 list->next = prev;
1108 prev = list;
1109 list = next;
1110 }
1111 return prev;
ccc6cda3
JA
1112}
1113
1114char *
7117c2d2
JA
1115pat_subst(s, t, u, i)
1116char *s, *t, *u;
1117int i;
ccc6cda3 1118{
7117c2d2 1119 return ((char *)NULL);
ccc6cda3
JA
1120}
1121
7117c2d2
JA
1122char *
1123quote_string(s)
1124char *s;
1125{
1126 return savestring(s);
1127}
ccc6cda3 1128
ccc6cda3
JA
1129print_element(ae)
1130ARRAY_ELEMENT *ae;
1131{
7117c2d2
JA
1132 char lbuf[INT_STRLEN_BOUND (intmax_t) + 1];
1133
1134 printf("array[%s] = %s\n",
1135 inttostr (element_index(ae), lbuf, sizeof (lbuf)),
1136 element_value(ae));
ccc6cda3
JA
1137}
1138
1139print_array(a)
1140ARRAY *a;
1141{
1142 printf("\n");
b80f6443 1143 array_walk(a, print_element, (void *)NULL);
ccc6cda3
JA
1144}
1145
1146main()
1147{
1148 ARRAY *a, *new_a, *copy_of_a;
7117c2d2 1149 ARRAY_ELEMENT *ae, *aew;
ccc6cda3
JA
1150 char *s;
1151
7117c2d2
JA
1152 a = array_create();
1153 array_insert(a, 1, "one");
1154 array_insert(a, 7, "seven");
1155 array_insert(a, 4, "four");
1156 array_insert(a, 1029, "one thousand twenty-nine");
1157 array_insert(a, 12, "twelve");
1158 array_insert(a, 42, "forty-two");
ccc6cda3 1159 print_array(a);
d166f048 1160 s = array_to_string (a, " ", 0);
ccc6cda3 1161 printf("s = %s\n", s);
7117c2d2 1162 copy_of_a = array_from_string(s, " ");
ccc6cda3
JA
1163 printf("copy_of_a:");
1164 print_array(copy_of_a);
7117c2d2 1165 array_dispose(copy_of_a);
ccc6cda3
JA
1166 printf("\n");
1167 free(s);
7117c2d2
JA
1168 ae = array_remove(a, 4);
1169 array_dispose_element(ae);
1170 ae = array_remove(a, 1029);
1171 array_dispose_element(ae);
1172 array_insert(a, 16, "sixteen");
ccc6cda3 1173 print_array(a);
d166f048 1174 s = array_to_string (a, " ", 0);
ccc6cda3 1175 printf("s = %s\n", s);
7117c2d2 1176 copy_of_a = array_from_string(s, " ");
ccc6cda3
JA
1177 printf("copy_of_a:");
1178 print_array(copy_of_a);
7117c2d2 1179 array_dispose(copy_of_a);
ccc6cda3
JA
1180 printf("\n");
1181 free(s);
7117c2d2
JA
1182 array_insert(a, 2, "two");
1183 array_insert(a, 1029, "new one thousand twenty-nine");
1184 array_insert(a, 0, "zero");
1185 array_insert(a, 134, "");
ccc6cda3 1186 print_array(a);
d166f048 1187 s = array_to_string (a, ":", 0);
ccc6cda3 1188 printf("s = %s\n", s);
7117c2d2 1189 copy_of_a = array_from_string(s, ":");
ccc6cda3
JA
1190 printf("copy_of_a:");
1191 print_array(copy_of_a);
7117c2d2 1192 array_dispose(copy_of_a);
ccc6cda3
JA
1193 printf("\n");
1194 free(s);
7117c2d2 1195 new_a = array_copy(a);
ccc6cda3 1196 print_array(new_a);
d166f048 1197 s = array_to_string (new_a, ":", 0);
ccc6cda3 1198 printf("s = %s\n", s);
7117c2d2
JA
1199 copy_of_a = array_from_string(s, ":");
1200 free(s);
ccc6cda3
JA
1201 printf("copy_of_a:");
1202 print_array(copy_of_a);
7117c2d2
JA
1203 array_shift(copy_of_a, 2, AS_DISPOSE);
1204 printf("copy_of_a shifted by two:");
1205 print_array(copy_of_a);
1206 ae = array_shift(copy_of_a, 2, 0);
1207 printf("copy_of_a shifted by two:");
1208 print_array(copy_of_a);
1209 for ( ; ae; ) {
1210 aew = element_forw(ae);
1211 array_dispose_element(ae);
1212 ae = aew;
1213 }
1214 array_rshift(copy_of_a, 1, (char *)0);
1215 printf("copy_of_a rshift by 1:");
1216 print_array(copy_of_a);
1217 array_rshift(copy_of_a, 2, "new element zero");
1218 printf("copy_of_a rshift again by 2 with new element zero:");
1219 print_array(copy_of_a);
1220 s = array_to_assign(copy_of_a, 0);
1221 printf("copy_of_a=%s\n", s);
ccc6cda3 1222 free(s);
7117c2d2
JA
1223 ae = array_shift(copy_of_a, array_num_elements(copy_of_a), 0);
1224 for ( ; ae; ) {
1225 aew = element_forw(ae);
1226 array_dispose_element(ae);
1227 ae = aew;
1228 }
1229 array_dispose(copy_of_a);
1230 printf("\n");
1231 array_dispose(a);
1232 array_dispose(new_a);
ccc6cda3
JA
1233}
1234
1235#endif /* TEST_ARRAY */
1236#endif /* ARRAY_VARS */