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