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