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-2004 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 it under
17   the terms of the GNU General Public License as published by the Free
18   Software Foundation; either version 2, or (at your option) any later
19   version.
20
21   Bash is distributed in the hope that it will be useful, but WITHOUT ANY
22   WARRANTY; without even the implied warranty of MERCHANTABILITY or
23   FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
24   for more details.
25
26   You should have received a copy of the GNU General Public License along
27   with Bash; see the file COPYING.  If not, write to the Free Software
28   Foundation, 59 Temple Place, Suite 330, Boston, MA 02111 USA. */
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 (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
318/*
319 * Return a string whose elements are the members of array A beginning at
320 * index START and spanning NELEM members.  Null elements are counted.
321 * Since arrays are sparse, unset array elements are not counted.
322 */
323char *
324array_subrange (a, start, nelem, starsub, quoted)
325ARRAY	*a;
326arrayind_t	start, nelem;
327int	starsub, quoted;
328{
329	ARRAY		*a2;
330	ARRAY_ELEMENT	*h, *p;
331	arrayind_t	i;
332	char		*ifs, sep[2], *t;
333
334	p = a ? array_head (a) : 0;
335	if (p == 0 || array_empty (a) || start > array_max_index(a))
336		return ((char *)NULL);
337
338	/*
339	 * Find element with index START.  If START corresponds to an unset
340	 * element (arrays can be sparse), use the first element whose index
341	 * is >= START.  If START is < 0, we count START indices back from
342	 * the end of A (not elements, even with sparse arrays -- START is an
343	 * index).
344	 */
345	for (p = element_forw(p); p != array_head(a) && start > element_index(p); p = element_forw(p))
346		;
347
348	if (p == a->head)
349		return ((char *)NULL);
350
351	/* Starting at P, take NELEM elements, inclusive. */
352	for (i = 0, h = p; p != a->head && i < nelem; i++, p = element_forw(p))
353		;
354
355	a2 = array_slice(a, h, p);
356
357	if (quoted & (Q_DOUBLE_QUOTES|Q_HERE_DOCUMENT))
358		array_quote(a2);
359	else
360		array_quote_escapes(a2);
361
362	if (starsub && (quoted & (Q_DOUBLE_QUOTES|Q_HERE_DOCUMENT))) {
363		ifs = getifs();
364		sep[0] = ifs ? *ifs : '\0';
365	} else
366		sep[0] = ' ';
367	sep[1] = '\0';
368
369	t = array_to_string (a2, sep, 0);
370	array_dispose(a2);
371
372	return t;
373}
374
375char *
376array_patsub (a, pat, rep, mflags)
377ARRAY	*a;
378char	*pat, *rep;
379int	mflags;
380{
381	ARRAY		*a2;
382	ARRAY_ELEMENT	*e;
383	char	*t, *ifs, sifs[2];
384
385	if (a == 0 || array_head(a) == 0 || array_empty(a))
386		return ((char *)NULL);
387
388	a2 = array_copy(a);
389	for (e = element_forw(a2->head); e != a2->head; e = element_forw(e)) {
390		t = pat_subst(element_value(e), pat, rep, mflags);
391		FREE(element_value(e));
392		e->value = t;
393	}
394
395	if (mflags & MATCH_QUOTED)
396		array_quote(a2);
397	else
398		array_quote_escapes(a2);
399	if (mflags & MATCH_STARSUB) {
400		ifs = getifs();
401		sifs[0] = ifs ? *ifs : '\0';
402		sifs[1] = '\0';
403		t = array_to_string (a2, sifs, 0);
404	} else
405		t = array_to_string (a2, " ", 0);
406	array_dispose (a2);
407
408	return t;
409}
410
411/*
412 * Allocate and return a new array element with index INDEX and value
413 * VALUE.
414 */
415ARRAY_ELEMENT *
416array_create_element(indx, value)
417arrayind_t	indx;
418char	*value;
419{
420	ARRAY_ELEMENT *r;
421
422	r = (ARRAY_ELEMENT *)xmalloc(sizeof(ARRAY_ELEMENT));
423	r->ind = indx;
424	r->value = value ? savestring(value) : (char *)NULL;
425	r->next = r->prev = (ARRAY_ELEMENT *) NULL;
426	return(r);
427}
428
429#ifdef INCLUDE_UNUSED
430ARRAY_ELEMENT *
431array_copy_element(ae)
432ARRAY_ELEMENT	*ae;
433{
434	return(ae ? array_create_element(element_index(ae), element_value(ae))
435		  : (ARRAY_ELEMENT *) NULL);
436}
437#endif
438
439void
440array_dispose_element(ae)
441ARRAY_ELEMENT	*ae;
442{
443	if (ae) {
444		FREE(ae->value);
445		free(ae);
446	}
447}
448
449/*
450 * Add a new element with index I and value V to array A (a[i] = v).
451 */
452int
453array_insert(a, i, v)
454ARRAY	*a;
455arrayind_t	i;
456char	*v;
457{
458	register ARRAY_ELEMENT *new, *ae;
459
460	if (a == 0)
461		return(-1);
462	new = array_create_element(i, v);
463	if (i > array_max_index(a)) {
464		/*
465		 * Hook onto the end.  This also works for an empty array.
466		 * Fast path for the common case of allocating arrays
467		 * sequentially.
468		 */
469		ADD_BEFORE(a->head, new);
470		a->max_index = i;
471		a->num_elements++;
472		return(0);
473	}
474	/*
475	 * Otherwise we search for the spot to insert it.
476	 */
477	for (ae = element_forw(a->head); ae != a->head; ae = element_forw(ae)) {
478		if (element_index(ae) == i) {
479			/*
480			 * Replacing an existing element.
481			 */
482			array_dispose_element(new);
483			free(element_value(ae));
484			ae->value = v ? savestring(v) : (char *)NULL;
485			return(0);
486		} else if (element_index(ae) > i) {
487			ADD_BEFORE(ae, new);
488			a->num_elements++;
489			return(0);
490		}
491	}
492	return (-1);		/* problem */
493}
494
495/*
496 * Delete the element with index I from array A and return it so the
497 * caller can dispose of it.
498 */
499ARRAY_ELEMENT *
500array_remove(a, i)
501ARRAY	*a;
502arrayind_t	i;
503{
504	register ARRAY_ELEMENT *ae;
505
506	if (a == 0 || array_empty(a))
507		return((ARRAY_ELEMENT *) NULL);
508	for (ae = element_forw(a->head); ae != a->head; ae = element_forw(ae))
509		if (element_index(ae) == i) {
510			ae->next->prev = ae->prev;
511			ae->prev->next = ae->next;
512			a->num_elements--;
513			if (i == array_max_index(a))
514				a->max_index = element_index(ae->prev);
515			return(ae);
516		}
517	return((ARRAY_ELEMENT *) NULL);
518}
519
520/*
521 * Return the value of a[i].
522 */
523char *
524array_reference(a, i)
525ARRAY	*a;
526arrayind_t	i;
527{
528	register ARRAY_ELEMENT *ae;
529
530	if (a == 0 || array_empty(a))
531		return((char *) NULL);
532	for (ae = element_forw(a->head); ae != a->head; ae = element_forw(ae))
533		if (element_index(ae) == i)
534			return(element_value(ae));
535	return((char *) NULL);
536}
537
538/* Convenience routines for the shell to translate to and from the form used
539   by the rest of the code. */
540
541WORD_LIST *
542array_to_word_list(a)
543ARRAY	*a;
544{
545	WORD_LIST	*list;
546	ARRAY_ELEMENT	*ae;
547
548	if (a == 0 || array_empty(a))
549		return((WORD_LIST *)NULL);
550	list = (WORD_LIST *)NULL;
551	for (ae = element_forw(a->head); ae != a->head; ae = element_forw(ae))
552		list = make_word_list (make_bare_word(element_value(ae)), list);
553	return (REVERSE_LIST(list, WORD_LIST *));
554}
555
556ARRAY *
557array_from_word_list (list)
558WORD_LIST	*list;
559{
560	ARRAY	*a;
561
562	if (list == 0)
563		return((ARRAY *)NULL);
564	a = array_create();
565	return (array_assign_list (a, list));
566}
567
568WORD_LIST *
569array_keys_to_word_list(a)
570ARRAY	*a;
571{
572	WORD_LIST	*list;
573	ARRAY_ELEMENT	*ae;
574	char		*t;
575
576	if (a == 0 || array_empty(a))
577		return((WORD_LIST *)NULL);
578	list = (WORD_LIST *)NULL;
579	for (ae = element_forw(a->head); ae != a->head; ae = element_forw(ae)) {
580		t = itos(element_index(ae));
581		list = make_word_list (make_bare_word(t), list);
582		free(t);
583	}
584	return (REVERSE_LIST(list, WORD_LIST *));
585}
586
587ARRAY *
588array_assign_list (array, list)
589ARRAY	*array;
590WORD_LIST	*list;
591{
592	register WORD_LIST *l;
593	register arrayind_t i;
594
595	for (l = list, i = 0; l; l = l->next, i++)
596		array_insert(array, i, l->word->word);
597	return array;
598}
599
600char **
601array_to_argv (a)
602ARRAY	*a;
603{
604	char		**ret, *t;
605	int		i;
606	ARRAY_ELEMENT	*ae;
607
608	if (a == 0 || array_empty(a))
609		return ((char **)NULL);
610	ret = strvec_create (array_num_elements (a) + 1);
611	i = 0;
612	for (ae = element_forw(a->head); ae != a->head; ae = element_forw(ae)) {
613		t = element_value (ae);
614		ret[i++] = t ? savestring (t) : (char *)NULL;
615	}
616	ret[i] = (char *)NULL;
617	return (ret);
618}
619
620/*
621 * Return a string that is the concatenation of all the elements in A,
622 * separated by SEP.
623 */
624static char *
625array_to_string_internal (start, end, sep, quoted)
626ARRAY_ELEMENT	*start, *end;
627char	*sep;
628int	quoted;
629{
630	char	*result, *t;
631	ARRAY_ELEMENT *ae;
632	int	slen, rsize, rlen, reg;
633
634	if (start == end)	/* XXX - should not happen */
635		return ((char *)NULL);
636
637	slen = strlen(sep);
638	result = NULL;
639	for (rsize = rlen = 0, ae = start; ae != end; ae = element_forw(ae)) {
640		if (rsize == 0)
641			result = (char *)xmalloc (rsize = 64);
642		if (element_value(ae)) {
643			t = quoted ? quote_string(element_value(ae)) : element_value(ae);
644			reg = strlen(t);
645			RESIZE_MALLOCED_BUFFER (result, rlen, (reg + slen + 2),
646						rsize, rsize);
647			strcpy(result + rlen, t);
648			rlen += reg;
649			if (quoted && t)
650				free(t);
651			/*
652			 * Add a separator only after non-null elements.
653			 */
654			if (element_forw(ae) != end) {
655				strcpy(result + rlen, sep);
656				rlen += slen;
657			}
658		}
659	}
660	if (result)
661	  result[rlen] = '\0';	/* XXX */
662	return(result);
663}
664
665char *
666array_to_assign (a, quoted)
667ARRAY	*a;
668int	quoted;
669{
670	char	*result, *valstr, *is;
671	char	indstr[INT_STRLEN_BOUND(intmax_t) + 1];
672	ARRAY_ELEMENT *ae;
673	int	rsize, rlen, elen;
674
675	if (a == 0 || array_empty (a))
676		return((char *)NULL);
677
678	result = (char *)xmalloc (rsize = 128);
679	result[0] = '(';
680	rlen = 1;
681
682	for (ae = element_forw(a->head); ae != a->head; ae = element_forw(ae)) {
683		is = inttostr (element_index(ae), indstr, sizeof(indstr));
684		valstr = element_value (ae) ? sh_double_quote (element_value(ae))
685					    : (char *)NULL;
686		elen = STRLEN (is) + 8 + STRLEN (valstr);
687		RESIZE_MALLOCED_BUFFER (result, rlen, (elen + 1), rsize, rsize);
688
689		result[rlen++] = '[';
690		strcpy (result + rlen, is);
691		rlen += STRLEN (is);
692		result[rlen++] = ']';
693		result[rlen++] = '=';
694		if (valstr) {
695			strcpy (result + rlen, valstr);
696			rlen += STRLEN (valstr);
697		}
698
699		if (element_forw(ae) != a->head)
700		  result[rlen++] = ' ';
701
702		FREE (valstr);
703	}
704	RESIZE_MALLOCED_BUFFER (result, rlen, 1, rsize, 8);
705	result[rlen++] = ')';
706	result[rlen] = '\0';
707	if (quoted) {
708		/* This is not as efficient as it could be... */
709		valstr = sh_single_quote (result);
710		free (result);
711		result = valstr;
712	}
713	return(result);
714}
715
716char *
717array_to_string (a, sep, quoted)
718ARRAY	*a;
719char	*sep;
720int	quoted;
721{
722	if (a == 0)
723		return((char *)NULL);
724	if (array_empty(a))
725		return(savestring(""));
726	return (array_to_string_internal (element_forw(a->head), a->head, sep, quoted));
727}
728
729#if defined (INCLUDE_UNUSED) || defined (TEST_ARRAY)
730/*
731 * Return an array consisting of elements in S, separated by SEP
732 */
733ARRAY *
734array_from_string(s, sep)
735char	*s, *sep;
736{
737	ARRAY	*a;
738	WORD_LIST *w;
739
740	if (s == 0)
741		return((ARRAY *)NULL);
742	w = list_string (s, sep, 0);
743	if (w == 0)
744		return((ARRAY *)NULL);
745	a = array_from_word_list (w);
746	return (a);
747}
748#endif
749
750#if defined (TEST_ARRAY)
751/*
752 * To make a running version, compile -DTEST_ARRAY and link with:
753 * 	xmalloc.o syntax.o lib/malloc/libmalloc.a lib/sh/libsh.a
754 */
755int interrupt_immediately = 0;
756
757int
758signal_is_trapped(s)
759int	s;
760{
761	return 0;
762}
763
764void
765fatal_error(const char *s, ...)
766{
767	fprintf(stderr, "array_test: fatal memory error\n");
768	abort();
769}
770
771void
772programming_error(const char *s, ...)
773{
774	fprintf(stderr, "array_test: fatal programming error\n");
775	abort();
776}
777
778WORD_DESC *
779make_bare_word (s)
780const char	*s;
781{
782	WORD_DESC *w;
783
784	w = (WORD_DESC *)xmalloc(sizeof(WORD_DESC));
785	w->word = s ? savestring(s) : savestring ("");
786	w->flags = 0;
787	return w;
788}
789
790WORD_LIST *
791make_word_list(x, l)
792WORD_DESC	*x;
793WORD_LIST	*l;
794{
795	WORD_LIST *w;
796
797	w = (WORD_LIST *)xmalloc(sizeof(WORD_LIST));
798	w->word = x;
799	w->next = l;
800	return w;
801}
802
803WORD_LIST *
804list_string(s, t, i)
805char	*s, *t;
806int	i;
807{
808	char	*r, *a;
809	WORD_LIST	*wl;
810
811	if (s == 0)
812		return (WORD_LIST *)NULL;
813	r = savestring(s);
814	wl = (WORD_LIST *)NULL;
815	a = strtok(r, t);
816	while (a) {
817		wl = make_word_list (make_bare_word(a), wl);
818		a = strtok((char *)NULL, t);
819	}
820	return (REVERSE_LIST (wl, WORD_LIST *));
821}
822
823GENERIC_LIST *
824list_reverse (list)
825GENERIC_LIST	*list;
826{
827	register GENERIC_LIST *next, *prev;
828
829	for (prev = 0; list; ) {
830		next = list->next;
831		list->next = prev;
832		prev = list;
833		list = next;
834	}
835	return prev;
836}
837
838char *
839pat_subst(s, t, u, i)
840char	*s, *t, *u;
841int	i;
842{
843	return ((char *)NULL);
844}
845
846char *
847quote_string(s)
848char	*s;
849{
850	return savestring(s);
851}
852
853print_element(ae)
854ARRAY_ELEMENT	*ae;
855{
856	char	lbuf[INT_STRLEN_BOUND (intmax_t) + 1];
857
858	printf("array[%s] = %s\n",
859		inttostr (element_index(ae), lbuf, sizeof (lbuf)),
860		element_value(ae));
861}
862
863print_array(a)
864ARRAY	*a;
865{
866	printf("\n");
867	array_walk(a, print_element, (void *)NULL);
868}
869
870main()
871{
872	ARRAY	*a, *new_a, *copy_of_a;
873	ARRAY_ELEMENT	*ae, *aew;
874	char	*s;
875
876	a = array_create();
877	array_insert(a, 1, "one");
878	array_insert(a, 7, "seven");
879	array_insert(a, 4, "four");
880	array_insert(a, 1029, "one thousand twenty-nine");
881	array_insert(a, 12, "twelve");
882	array_insert(a, 42, "forty-two");
883	print_array(a);
884	s = array_to_string (a, " ", 0);
885	printf("s = %s\n", s);
886	copy_of_a = array_from_string(s, " ");
887	printf("copy_of_a:");
888	print_array(copy_of_a);
889	array_dispose(copy_of_a);
890	printf("\n");
891	free(s);
892	ae = array_remove(a, 4);
893	array_dispose_element(ae);
894	ae = array_remove(a, 1029);
895	array_dispose_element(ae);
896	array_insert(a, 16, "sixteen");
897	print_array(a);
898	s = array_to_string (a, " ", 0);
899	printf("s = %s\n", s);
900	copy_of_a = array_from_string(s, " ");
901	printf("copy_of_a:");
902	print_array(copy_of_a);
903	array_dispose(copy_of_a);
904	printf("\n");
905	free(s);
906	array_insert(a, 2, "two");
907	array_insert(a, 1029, "new one thousand twenty-nine");
908	array_insert(a, 0, "zero");
909	array_insert(a, 134, "");
910	print_array(a);
911	s = array_to_string (a, ":", 0);
912	printf("s = %s\n", s);
913	copy_of_a = array_from_string(s, ":");
914	printf("copy_of_a:");
915	print_array(copy_of_a);
916	array_dispose(copy_of_a);
917	printf("\n");
918	free(s);
919	new_a = array_copy(a);
920	print_array(new_a);
921	s = array_to_string (new_a, ":", 0);
922	printf("s = %s\n", s);
923	copy_of_a = array_from_string(s, ":");
924	free(s);
925	printf("copy_of_a:");
926	print_array(copy_of_a);
927	array_shift(copy_of_a, 2, AS_DISPOSE);
928	printf("copy_of_a shifted by two:");
929	print_array(copy_of_a);
930	ae = array_shift(copy_of_a, 2, 0);
931	printf("copy_of_a shifted by two:");
932	print_array(copy_of_a);
933	for ( ; ae; ) {
934		aew = element_forw(ae);
935		array_dispose_element(ae);
936		ae = aew;
937	}
938	array_rshift(copy_of_a, 1, (char *)0);
939	printf("copy_of_a rshift by 1:");
940	print_array(copy_of_a);
941	array_rshift(copy_of_a, 2, "new element zero");
942	printf("copy_of_a rshift again by 2 with new element zero:");
943	print_array(copy_of_a);
944	s = array_to_assign(copy_of_a, 0);
945	printf("copy_of_a=%s\n", s);
946	free(s);
947	ae = array_shift(copy_of_a, array_num_elements(copy_of_a), 0);
948	for ( ; ae; ) {
949		aew = element_forw(ae);
950		array_dispose_element(ae);
951		ae = aew;
952	}
953	array_dispose(copy_of_a);
954	printf("\n");
955	array_dispose(a);
956	array_dispose(new_a);
957}
958
959#endif /* TEST_ARRAY */
960#endif /* ARRAY_VARS */
961