]> git.ipfire.org Git - thirdparty/bash.git/blame - array.c
bash-5.0 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
d233b485 12/* Copyright (C) 1997-2016 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
7117c2d2 64static char *array_to_string_internal __P((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 *
b80f6443 401array_subrange (a, start, nelem, starsub, quoted)
7117c2d2 402ARRAY *a;
b80f6443
JA
403arrayind_t start, nelem;
404int starsub, quoted;
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;
439 t = string_list_pos_params(starsub ? '*' : '@', wl, quoted);
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
CR
451 char *t;
452 int pchar, qflags;
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;
470
471 t = string_list_pos_params (pchar, save, qflags);
472 dispose_words(save);
7117c2d2
JA
473
474 return t;
475}
476
3185942a
JA
477char *
478array_modcase (a, pat, modop, mflags)
479ARRAY *a;
480char *pat;
481int modop;
482int mflags;
483{
d233b485
CR
484 char *t;
485 int pchar, qflags;
486 WORD_LIST *wl, *save;
3185942a
JA
487
488 if (a == 0 || array_head(a) == 0 || array_empty(a))
489 return ((char *)NULL);
490
d233b485
CR
491 wl = array_to_word_list(a);
492 if (wl == 0)
493 return ((char *)NULL);
494
495 for (save = wl; wl; wl = wl->next) {
496 t = sh_modcase(wl->word->word, pat, modop);
497 FREE(wl->word->word);
498 wl->word->word = t;
3185942a
JA
499 }
500
d233b485
CR
501 pchar = (mflags & MATCH_STARSUB) == MATCH_STARSUB ? '*' : '@';
502 qflags = (mflags & MATCH_QUOTED) == MATCH_QUOTED ? Q_DOUBLE_QUOTES : 0;
503
504 t = string_list_pos_params (pchar, save, qflags);
505 dispose_words(save);
3185942a
JA
506
507 return t;
508}
d233b485 509
7117c2d2
JA
510/*
511 * Allocate and return a new array element with index INDEX and value
512 * VALUE.
513 */
514ARRAY_ELEMENT *
515array_create_element(indx, value)
516arrayind_t indx;
517char *value;
518{
519 ARRAY_ELEMENT *r;
520
521 r = (ARRAY_ELEMENT *)xmalloc(sizeof(ARRAY_ELEMENT));
522 r->ind = indx;
523 r->value = value ? savestring(value) : (char *)NULL;
524 r->next = r->prev = (ARRAY_ELEMENT *) NULL;
525 return(r);
526}
527
cce855bc 528#ifdef INCLUDE_UNUSED
ccc6cda3 529ARRAY_ELEMENT *
7117c2d2 530array_copy_element(ae)
ccc6cda3
JA
531ARRAY_ELEMENT *ae;
532{
7117c2d2 533 return(ae ? array_create_element(element_index(ae), element_value(ae))
ccc6cda3
JA
534 : (ARRAY_ELEMENT *) NULL);
535}
cce855bc 536#endif
ccc6cda3 537
7117c2d2
JA
538void
539array_dispose_element(ae)
540ARRAY_ELEMENT *ae;
541{
b80f6443
JA
542 if (ae) {
543 FREE(ae->value);
544 free(ae);
545 }
7117c2d2
JA
546}
547
ccc6cda3
JA
548/*
549 * Add a new element with index I and value V to array A (a[i] = v).
550 */
551int
7117c2d2 552array_insert(a, i, v)
ccc6cda3
JA
553ARRAY *a;
554arrayind_t i;
555char *v;
556{
ac50fbac 557 register ARRAY_ELEMENT *new, *ae, *start;
d233b485
CR
558 arrayind_t startind;
559 int direction;
ccc6cda3 560
95732b49 561 if (a == 0)
ccc6cda3 562 return(-1);
7117c2d2 563 new = array_create_element(i, v);
ccc6cda3
JA
564 if (i > array_max_index(a)) {
565 /*
566 * Hook onto the end. This also works for an empty array.
567 * Fast path for the common case of allocating arrays
568 * sequentially.
569 */
570 ADD_BEFORE(a->head, new);
571 a->max_index = i;
572 a->num_elements++;
0001803f 573 SET_LASTREF(a, new);
ccc6cda3 574 return(0);
d233b485
CR
575 } else if (i < array_first_index(a)) {
576 /* Hook at the beginning */
577 ADD_AFTER(a->head, new);
578 a->num_elements++;
579 SET_LASTREF(a, new);
580 return(0);
ccc6cda3 581 }
ac50fbac 582#if OPTIMIZE_SEQUENTIAL_ARRAY_ASSIGNMENT
ccc6cda3 583 /*
ac50fbac
CR
584 * Otherwise we search for the spot to insert it. The lastref
585 * handle optimizes the case of sequential or almost-sequential
586 * assignments that are not at the end of the array.
ccc6cda3 587 */
d233b485
CR
588 start = LASTREF(a);
589 /* Use same strategy as array_reference to avoid paying large penalty
590 for semi-random assignment pattern. */
591 startind = element_index(start);
592 if (i < startind/2) {
593 start = element_forw(a->head);
594 startind = element_index(start);
595 direction = 1;
596 } else if (i >= startind) {
597 direction = 1;
598 } else {
599 direction = -1;
600 }
ac50fbac
CR
601#else
602 start = element_forw(ae->head);
d233b485
CR
603 startind = element_index(start);
604 direction = 1;
ac50fbac 605#endif
d233b485 606 for (ae = start; ae != a->head; ) {
ccc6cda3
JA
607 if (element_index(ae) == i) {
608 /*
609 * Replacing an existing element.
610 */
ccc6cda3 611 free(element_value(ae));
d233b485
CR
612 /* Just swap in the new value */
613 ae->value = new->value;
614 new->value = 0;
615 array_dispose_element(new);
0001803f 616 SET_LASTREF(a, ae);
ccc6cda3 617 return(0);
d233b485 618 } else if (direction == 1 && element_index(ae) > i) {
ccc6cda3
JA
619 ADD_BEFORE(ae, new);
620 a->num_elements++;
0001803f 621 SET_LASTREF(a, new);
ccc6cda3 622 return(0);
d233b485
CR
623 } else if (direction == -1 && element_index(ae) < i) {
624 ADD_AFTER(ae, new);
625 a->num_elements++;
626 SET_LASTREF(a, new);
627 return(0);
ccc6cda3 628 }
d233b485 629 ae = direction == 1 ? element_forw(ae) : element_back(ae);
ccc6cda3 630 }
ac50fbac 631 array_dispose_element(new);
0001803f 632 INVALIDATE_LASTREF(a);
ccc6cda3
JA
633 return (-1); /* problem */
634}
635
636/*
637 * Delete the element with index I from array A and return it so the
638 * caller can dispose of it.
639 */
640ARRAY_ELEMENT *
7117c2d2 641array_remove(a, i)
ccc6cda3
JA
642ARRAY *a;
643arrayind_t i;
644{
ac50fbac 645 register ARRAY_ELEMENT *ae, *start;
d233b485
CR
646 arrayind_t startind;
647 int direction;
ccc6cda3 648
95732b49 649 if (a == 0 || array_empty(a))
ccc6cda3 650 return((ARRAY_ELEMENT *) NULL);
d233b485
CR
651 if (i > array_max_index(a) || i < array_first_index(a))
652 return((ARRAY_ELEMENT *)NULL); /* Keep roving pointer into array to optimize sequential access */
653 start = LASTREF(a);
654 /* Use same strategy as array_reference to avoid paying large penalty
655 for semi-random assignment pattern. */
656 startind = element_index(start);
657 if (i < startind/2) {
658 start = element_forw(a->head);
659 startind = element_index(start);
660 direction = 1;
661 } else if (i >= startind) {
662 direction = 1;
663 } else {
664 direction = -1;
665 }
666 for (ae = start; ae != a->head; ) {
ccc6cda3
JA
667 if (element_index(ae) == i) {
668 ae->next->prev = ae->prev;
669 ae->prev->next = ae->next;
670 a->num_elements--;
671 if (i == array_max_index(a))
672 a->max_index = element_index(ae->prev);
ac50fbac 673#if 0
0001803f 674 INVALIDATE_LASTREF(a);
ac50fbac
CR
675#else
676 if (ae->next != a->head)
677 SET_LASTREF(a, ae->next);
678 else if (ae->prev != a->head)
679 SET_LASTREF(a, ae->prev);
680 else
681 INVALIDATE_LASTREF(a);
682#endif
ccc6cda3
JA
683 return(ae);
684 }
d233b485
CR
685 ae = (direction == 1) ? element_forw(ae) : element_back(ae);
686 if (direction == 1 && element_index(ae) > i)
687 break;
688 else if (direction == -1 && element_index(ae) < i)
689 break;
690 }
ccc6cda3
JA
691 return((ARRAY_ELEMENT *) NULL);
692}
693
694/*
695 * Return the value of a[i].
696 */
697char *
698array_reference(a, i)
699ARRAY *a;
700arrayind_t i;
701{
ac50fbac 702 register ARRAY_ELEMENT *ae, *start;
d233b485
CR
703 arrayind_t startind;
704 int direction;
ccc6cda3
JA
705
706 if (a == 0 || array_empty(a))
707 return((char *) NULL);
d233b485 708 if (i > array_max_index(a) || i < array_first_index(a))
ac50fbac 709 return((char *)NULL); /* Keep roving pointer into array to optimize sequential access */
d233b485
CR
710 start = LASTREF(a); /* lastref pointer */
711 startind = element_index(start);
712 if (i < startind/2) { /* XXX - guess */
713 start = element_forw(a->head);
714 startind = element_index(start);
715 direction = 1;
716 } else if (i >= startind) {
717 direction = 1;
718 } else {
719 direction = -1;
720 }
721 for (ae = start; ae != a->head; ) {
0001803f
CR
722 if (element_index(ae) == i) {
723 SET_LASTREF(a, ae);
ccc6cda3 724 return(element_value(ae));
0001803f 725 }
d233b485
CR
726 ae = (direction == 1) ? element_forw(ae) : element_back(ae);
727 /* Take advantage of index ordering to short-circuit */
728 /* If we don't find it, set the lastref pointer to the element
729 that's `closest', assuming that the unsuccessful reference
730 will quickly be followed by an assignment. No worse than
731 not changing it from the previous value or resetting it. */
732 if (direction == 1 && element_index(ae) > i) {
733 start = ae; /* use for SET_LASTREF below */
734 break;
735 } else if (direction == -1 && element_index(ae) < i) {
736 start = ae; /* use for SET_LASTREF below */
737 break;
738 }
739 }
740#if 0
741 UNSET_LASTREF(a);
742#else
743 SET_LASTREF(a, start);
744#endif
ccc6cda3
JA
745 return((char *) NULL);
746}
747
7117c2d2
JA
748/* Convenience routines for the shell to translate to and from the form used
749 by the rest of the code. */
b80f6443 750
7117c2d2
JA
751WORD_LIST *
752array_to_word_list(a)
ccc6cda3 753ARRAY *a;
ccc6cda3 754{
7117c2d2
JA
755 WORD_LIST *list;
756 ARRAY_ELEMENT *ae;
ccc6cda3
JA
757
758 if (a == 0 || array_empty(a))
7117c2d2
JA
759 return((WORD_LIST *)NULL);
760 list = (WORD_LIST *)NULL;
ccc6cda3 761 for (ae = element_forw(a->head); ae != a->head; ae = element_forw(ae))
7117c2d2
JA
762 list = make_word_list (make_bare_word(element_value(ae)), list);
763 return (REVERSE_LIST(list, WORD_LIST *));
ccc6cda3
JA
764}
765
7117c2d2
JA
766ARRAY *
767array_from_word_list (list)
768WORD_LIST *list;
769{
770 ARRAY *a;
771
772 if (list == 0)
773 return((ARRAY *)NULL);
774 a = array_create();
775 return (array_assign_list (a, list));
776}
777
b80f6443
JA
778WORD_LIST *
779array_keys_to_word_list(a)
780ARRAY *a;
781{
782 WORD_LIST *list;
783 ARRAY_ELEMENT *ae;
784 char *t;
785
786 if (a == 0 || array_empty(a))
787 return((WORD_LIST *)NULL);
788 list = (WORD_LIST *)NULL;
789 for (ae = element_forw(a->head); ae != a->head; ae = element_forw(ae)) {
790 t = itos(element_index(ae));
791 list = make_word_list (make_bare_word(t), list);
792 free(t);
793 }
794 return (REVERSE_LIST(list, WORD_LIST *));
795}
796
7117c2d2
JA
797ARRAY *
798array_assign_list (array, list)
799ARRAY *array;
800WORD_LIST *list;
801{
802 register WORD_LIST *l;
803 register arrayind_t i;
804
805 for (l = list, i = 0; l; l = l->next, i++)
806 array_insert(array, i, l->word->word);
807 return array;
808}
809
810char **
811array_to_argv (a)
812ARRAY *a;
813{
814 char **ret, *t;
815 int i;
816 ARRAY_ELEMENT *ae;
817
818 if (a == 0 || array_empty(a))
819 return ((char **)NULL);
820 ret = strvec_create (array_num_elements (a) + 1);
821 i = 0;
822 for (ae = element_forw(a->head); ae != a->head; ae = element_forw(ae)) {
823 t = element_value (ae);
824 ret[i++] = t ? savestring (t) : (char *)NULL;
825 }
826 ret[i] = (char *)NULL;
827 return (ret);
828}
829
ccc6cda3 830/*
3185942a
JA
831 * Return a string that is the concatenation of the elements in A from START
832 * to END, separated by SEP.
ccc6cda3
JA
833 */
834static char *
835array_to_string_internal (start, end, sep, quoted)
836ARRAY_ELEMENT *start, *end;
837char *sep;
838int quoted;
839{
840 char *result, *t;
841 ARRAY_ELEMENT *ae;
842 int slen, rsize, rlen, reg;
843
844 if (start == end) /* XXX - should not happen */
845 return ((char *)NULL);
846
847 slen = strlen(sep);
f73dda09 848 result = NULL;
ccc6cda3
JA
849 for (rsize = rlen = 0, ae = start; ae != end; ae = element_forw(ae)) {
850 if (rsize == 0)
f73dda09 851 result = (char *)xmalloc (rsize = 64);
ccc6cda3
JA
852 if (element_value(ae)) {
853 t = quoted ? quote_string(element_value(ae)) : element_value(ae);
854 reg = strlen(t);
855 RESIZE_MALLOCED_BUFFER (result, rlen, (reg + slen + 2),
856 rsize, rsize);
857 strcpy(result + rlen, t);
858 rlen += reg;
a0c0a00f 859 if (quoted)
ccc6cda3
JA
860 free(t);
861 /*
862 * Add a separator only after non-null elements.
863 */
864 if (element_forw(ae) != end) {
865 strcpy(result + rlen, sep);
866 rlen += slen;
867 }
868 }
869 }
f73dda09
JA
870 if (result)
871 result[rlen] = '\0'; /* XXX */
ccc6cda3
JA
872 return(result);
873}
874
875char *
7117c2d2 876array_to_assign (a, quoted)
ccc6cda3 877ARRAY *a;
ccc6cda3
JA
878int quoted;
879{
7117c2d2
JA
880 char *result, *valstr, *is;
881 char indstr[INT_STRLEN_BOUND(intmax_t) + 1];
ccc6cda3
JA
882 ARRAY_ELEMENT *ae;
883 int rsize, rlen, elen;
884
885 if (a == 0 || array_empty (a))
886 return((char *)NULL);
887
f73dda09 888 result = (char *)xmalloc (rsize = 128);
ccc6cda3
JA
889 result[0] = '(';
890 rlen = 1;
891
892 for (ae = element_forw(a->head); ae != a->head; ae = element_forw(ae)) {
7117c2d2 893 is = inttostr (element_index(ae), indstr, sizeof(indstr));
a0c0a00f
CR
894 valstr = element_value (ae) ?
895 (ansic_shouldquote (element_value (ae)) ?
896 ansic_quote (element_value(ae), 0, (int *)0) :
897 sh_double_quote (element_value (ae)))
ccc6cda3 898 : (char *)NULL;
f1be666c 899 elen = STRLEN (is) + 8 + STRLEN (valstr);
ccc6cda3
JA
900 RESIZE_MALLOCED_BUFFER (result, rlen, (elen + 1), rsize, rsize);
901
902 result[rlen++] = '[';
7117c2d2
JA
903 strcpy (result + rlen, is);
904 rlen += STRLEN (is);
ccc6cda3
JA
905 result[rlen++] = ']';
906 result[rlen++] = '=';
907 if (valstr) {
908 strcpy (result + rlen, valstr);
909 rlen += STRLEN (valstr);
910 }
911
912 if (element_forw(ae) != a->head)
913 result[rlen++] = ' ';
914
ccc6cda3
JA
915 FREE (valstr);
916 }
917 RESIZE_MALLOCED_BUFFER (result, rlen, 1, rsize, 8);
918 result[rlen++] = ')';
919 result[rlen] = '\0';
7117c2d2
JA
920 if (quoted) {
921 /* This is not as efficient as it could be... */
922 valstr = sh_single_quote (result);
923 free (result);
924 result = valstr;
925 }
ccc6cda3
JA
926 return(result);
927}
928
929char *
7117c2d2 930array_to_string (a, sep, quoted)
ccc6cda3 931ARRAY *a;
7117c2d2
JA
932char *sep;
933int quoted;
ccc6cda3 934{
7117c2d2
JA
935 if (a == 0)
936 return((char *)NULL);
937 if (array_empty(a))
938 return(savestring(""));
939 return (array_to_string_internal (element_forw(a->head), a->head, sep, quoted));
ccc6cda3 940}
ccc6cda3 941
d166f048 942#if defined (INCLUDE_UNUSED) || defined (TEST_ARRAY)
ccc6cda3
JA
943/*
944 * Return an array consisting of elements in S, separated by SEP
945 */
946ARRAY *
7117c2d2 947array_from_string(s, sep)
ccc6cda3
JA
948char *s, *sep;
949{
950 ARRAY *a;
951 WORD_LIST *w;
952
953 if (s == 0)
954 return((ARRAY *)NULL);
955 w = list_string (s, sep, 0);
956 if (w == 0)
957 return((ARRAY *)NULL);
7117c2d2 958 a = array_from_word_list (w);
ccc6cda3
JA
959 return (a);
960}
d166f048 961#endif
ccc6cda3 962
7117c2d2
JA
963#if defined (TEST_ARRAY)
964/*
965 * To make a running version, compile -DTEST_ARRAY and link with:
966 * xmalloc.o syntax.o lib/malloc/libmalloc.a lib/sh/libsh.a
967 */
968int interrupt_immediately = 0;
ccc6cda3 969
7117c2d2
JA
970int
971signal_is_trapped(s)
972int s;
973{
974 return 0;
ccc6cda3
JA
975}
976
7117c2d2
JA
977void
978fatal_error(const char *s, ...)
bb70624e 979{
7117c2d2
JA
980 fprintf(stderr, "array_test: fatal memory error\n");
981 abort();
982}
bb70624e 983
7117c2d2
JA
984void
985programming_error(const char *s, ...)
986{
987 fprintf(stderr, "array_test: fatal programming error\n");
988 abort();
bb70624e 989}
7117c2d2
JA
990
991WORD_DESC *
992make_bare_word (s)
993const char *s;
ccc6cda3 994{
7117c2d2 995 WORD_DESC *w;
ccc6cda3 996
7117c2d2
JA
997 w = (WORD_DESC *)xmalloc(sizeof(WORD_DESC));
998 w->word = s ? savestring(s) : savestring ("");
999 w->flags = 0;
1000 return w;
ccc6cda3
JA
1001}
1002
7117c2d2
JA
1003WORD_LIST *
1004make_word_list(x, l)
1005WORD_DESC *x;
1006WORD_LIST *l;
ccc6cda3 1007{
7117c2d2 1008 WORD_LIST *w;
ccc6cda3 1009
7117c2d2
JA
1010 w = (WORD_LIST *)xmalloc(sizeof(WORD_LIST));
1011 w->word = x;
1012 w->next = l;
1013 return w;
ccc6cda3
JA
1014}
1015
7117c2d2
JA
1016WORD_LIST *
1017list_string(s, t, i)
1018char *s, *t;
1019int i;
ccc6cda3 1020{
7117c2d2
JA
1021 char *r, *a;
1022 WORD_LIST *wl;
ccc6cda3 1023
7117c2d2
JA
1024 if (s == 0)
1025 return (WORD_LIST *)NULL;
1026 r = savestring(s);
1027 wl = (WORD_LIST *)NULL;
1028 a = strtok(r, t);
1029 while (a) {
1030 wl = make_word_list (make_bare_word(a), wl);
1031 a = strtok((char *)NULL, t);
ccc6cda3 1032 }
7117c2d2 1033 return (REVERSE_LIST (wl, WORD_LIST *));
ccc6cda3
JA
1034}
1035
7117c2d2
JA
1036GENERIC_LIST *
1037list_reverse (list)
1038GENERIC_LIST *list;
ccc6cda3 1039{
7117c2d2 1040 register GENERIC_LIST *next, *prev;
ccc6cda3 1041
7117c2d2
JA
1042 for (prev = 0; list; ) {
1043 next = list->next;
1044 list->next = prev;
1045 prev = list;
1046 list = next;
1047 }
1048 return prev;
ccc6cda3
JA
1049}
1050
1051char *
7117c2d2
JA
1052pat_subst(s, t, u, i)
1053char *s, *t, *u;
1054int i;
ccc6cda3 1055{
7117c2d2 1056 return ((char *)NULL);
ccc6cda3
JA
1057}
1058
7117c2d2
JA
1059char *
1060quote_string(s)
1061char *s;
1062{
1063 return savestring(s);
1064}
ccc6cda3 1065
ccc6cda3
JA
1066print_element(ae)
1067ARRAY_ELEMENT *ae;
1068{
7117c2d2
JA
1069 char lbuf[INT_STRLEN_BOUND (intmax_t) + 1];
1070
1071 printf("array[%s] = %s\n",
1072 inttostr (element_index(ae), lbuf, sizeof (lbuf)),
1073 element_value(ae));
ccc6cda3
JA
1074}
1075
1076print_array(a)
1077ARRAY *a;
1078{
1079 printf("\n");
b80f6443 1080 array_walk(a, print_element, (void *)NULL);
ccc6cda3
JA
1081}
1082
1083main()
1084{
1085 ARRAY *a, *new_a, *copy_of_a;
7117c2d2 1086 ARRAY_ELEMENT *ae, *aew;
ccc6cda3
JA
1087 char *s;
1088
7117c2d2
JA
1089 a = array_create();
1090 array_insert(a, 1, "one");
1091 array_insert(a, 7, "seven");
1092 array_insert(a, 4, "four");
1093 array_insert(a, 1029, "one thousand twenty-nine");
1094 array_insert(a, 12, "twelve");
1095 array_insert(a, 42, "forty-two");
ccc6cda3 1096 print_array(a);
d166f048 1097 s = array_to_string (a, " ", 0);
ccc6cda3 1098 printf("s = %s\n", s);
7117c2d2 1099 copy_of_a = array_from_string(s, " ");
ccc6cda3
JA
1100 printf("copy_of_a:");
1101 print_array(copy_of_a);
7117c2d2 1102 array_dispose(copy_of_a);
ccc6cda3
JA
1103 printf("\n");
1104 free(s);
7117c2d2
JA
1105 ae = array_remove(a, 4);
1106 array_dispose_element(ae);
1107 ae = array_remove(a, 1029);
1108 array_dispose_element(ae);
1109 array_insert(a, 16, "sixteen");
ccc6cda3 1110 print_array(a);
d166f048 1111 s = array_to_string (a, " ", 0);
ccc6cda3 1112 printf("s = %s\n", s);
7117c2d2 1113 copy_of_a = array_from_string(s, " ");
ccc6cda3
JA
1114 printf("copy_of_a:");
1115 print_array(copy_of_a);
7117c2d2 1116 array_dispose(copy_of_a);
ccc6cda3
JA
1117 printf("\n");
1118 free(s);
7117c2d2
JA
1119 array_insert(a, 2, "two");
1120 array_insert(a, 1029, "new one thousand twenty-nine");
1121 array_insert(a, 0, "zero");
1122 array_insert(a, 134, "");
ccc6cda3 1123 print_array(a);
d166f048 1124 s = array_to_string (a, ":", 0);
ccc6cda3 1125 printf("s = %s\n", s);
7117c2d2 1126 copy_of_a = array_from_string(s, ":");
ccc6cda3
JA
1127 printf("copy_of_a:");
1128 print_array(copy_of_a);
7117c2d2 1129 array_dispose(copy_of_a);
ccc6cda3
JA
1130 printf("\n");
1131 free(s);
7117c2d2 1132 new_a = array_copy(a);
ccc6cda3 1133 print_array(new_a);
d166f048 1134 s = array_to_string (new_a, ":", 0);
ccc6cda3 1135 printf("s = %s\n", s);
7117c2d2
JA
1136 copy_of_a = array_from_string(s, ":");
1137 free(s);
ccc6cda3
JA
1138 printf("copy_of_a:");
1139 print_array(copy_of_a);
7117c2d2
JA
1140 array_shift(copy_of_a, 2, AS_DISPOSE);
1141 printf("copy_of_a shifted by two:");
1142 print_array(copy_of_a);
1143 ae = array_shift(copy_of_a, 2, 0);
1144 printf("copy_of_a shifted by two:");
1145 print_array(copy_of_a);
1146 for ( ; ae; ) {
1147 aew = element_forw(ae);
1148 array_dispose_element(ae);
1149 ae = aew;
1150 }
1151 array_rshift(copy_of_a, 1, (char *)0);
1152 printf("copy_of_a rshift by 1:");
1153 print_array(copy_of_a);
1154 array_rshift(copy_of_a, 2, "new element zero");
1155 printf("copy_of_a rshift again by 2 with new element zero:");
1156 print_array(copy_of_a);
1157 s = array_to_assign(copy_of_a, 0);
1158 printf("copy_of_a=%s\n", s);
ccc6cda3 1159 free(s);
7117c2d2
JA
1160 ae = array_shift(copy_of_a, array_num_elements(copy_of_a), 0);
1161 for ( ; ae; ) {
1162 aew = element_forw(ae);
1163 array_dispose_element(ae);
1164 ae = aew;
1165 }
1166 array_dispose(copy_of_a);
1167 printf("\n");
1168 array_dispose(a);
1169 array_dispose(new_a);
ccc6cda3
JA
1170}
1171
1172#endif /* TEST_ARRAY */
1173#endif /* ARRAY_VARS */