Imported from ../bash-2.05b.tar.gz.
[platform/upstream/bash.git] / array.c
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-2002 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
56 static char *array_to_string_internal __P((ARRAY_ELEMENT *, ARRAY_ELEMENT *, char *, int));
57
58 ARRAY *
59 array_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
74 void
75 array_flush (a)
76 ARRAY   *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
92 void
93 array_dispose(a)
94 ARRAY   *a;
95 {
96         if (a == 0)
97                 return;
98         array_flush (a);
99         array_dispose_element(a->head);
100         free(a);
101 }
102
103 ARRAY *
104 array_copy(a)
105 ARRAY   *a;
106 {
107         ARRAY   *a1;
108         ARRAY_ELEMENT   *ae, *new;
109
110         if (!a)
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 #ifdef INCLUDE_UNUSED
124 /*
125  * Make and return a new array composed of the elements in array A from
126  * S to E, inclusive.
127  */
128 ARRAY *
129 array_slice(array, s, e)
130 ARRAY           *array;
131 ARRAY_ELEMENT   *s, *e;
132 {
133         ARRAY   *a;
134         ARRAY_ELEMENT *p, *n;
135         int     i;
136         arrayind_t mi;
137
138         a = array_create ();
139         a->type = array->type;
140
141         for (p = s, i = 0; p != e; p = element_forw(p), i++) {
142                 n = array_create_element (element_index(p), element_value(p));
143                 ADD_BEFORE(a->head, n);
144                 mi = element_index(ae);
145         }
146         a->num_elements = i;
147         a->max_index = mi;
148         return a;
149 }
150 #endif
151
152 /*
153  * Walk the array, calling FUNC once for each element, with the array
154  * element as the argument.
155  */
156 void
157 array_walk(a, func)
158 ARRAY   *a;
159 sh_ae_map_func_t *func;
160 {
161         register ARRAY_ELEMENT *ae;
162
163         if (a == 0 || array_empty(a))
164                 return;
165         for (ae = element_forw(a->head); ae != a->head; ae = element_forw(ae))
166                 if ((*func)(ae) < 0)
167                         return;
168 }
169
170 /*
171  * Shift the array A N elements to the left.  Delete the first N elements
172  * and subtract N from the indices of the remaining elements.  If FLAGS
173  * does not include AS_DISPOSE, this returns a singly-linked null-terminated
174  * list of elements so the caller can dispose of the chain.  If FLAGS
175  * includes AS_DISPOSE, this function disposes of the shifted-out elements
176  * and returns NULL.
177  */
178 ARRAY_ELEMENT *
179 array_shift(a, n, flags)
180 ARRAY   *a;
181 int     n, flags;
182 {
183         register ARRAY_ELEMENT *ae, *ret;
184         register int i;
185
186         if (a == 0 || array_empty(a) || n <= 0)
187                 return ((ARRAY_ELEMENT *)NULL);
188
189         for (i = 0, ret = ae = element_forw(a->head); ae != a->head && i < n; ae = element_forw(ae), i++)
190                 ;
191         if (ae == a->head) {
192                 /* Easy case; shifting out all of the elements */
193                 if (flags & AS_DISPOSE) {
194                         array_flush (a);
195                         return ((ARRAY_ELEMENT *)NULL);
196                 }
197                 for (ae = ret; element_forw(ae) != a->head; ae = element_forw(ae))
198                         ;
199                 element_forw(ae) = (ARRAY_ELEMENT *)NULL;
200                 a->head->next = a->head->prev = a->head;
201                 a->max_index = -1;
202                 a->num_elements = 0;
203                 return ret;
204         }
205         /*
206          * ae now points to the list of elements we want to retain.
207          * ret points to the list we want to either destroy or return.
208          */
209         ae->prev->next = (ARRAY_ELEMENT *)NULL;         /* null-terminate RET */
210
211         a->head->next = ae;             /* slice RET out of the array */
212         ae->prev = a->head;
213
214         for ( ; ae != a->head; ae = element_forw(ae))
215                 element_index(ae) -= n; /* renumber retained indices */
216
217         a->num_elements -= n;           /* modify bookkeeping information */
218         a->max_index -= n;
219
220         if (flags & AS_DISPOSE) {
221                 for (ae = ret; ae; ) {
222                         ret = element_forw(ae);
223                         array_dispose_element(ae);
224                         ae = ret;
225                 }
226                 return ((ARRAY_ELEMENT *)NULL);
227         }
228
229         return ret;
230 }
231
232 /*
233  * Shift array A right N indices.  If S is non-null, it becomes the value of
234  * the new element 0.  Returns the number of elements in the array after the
235  * shift.
236  */
237 int
238 array_rshift (a, n, s)
239 ARRAY   *a;
240 int     n;
241 char    *s;
242 {
243         register ARRAY_ELEMENT  *ae, *new;
244
245         if (a == 0)
246                 return 0;
247         if (n <= 0)
248                 return (a->num_elements);
249
250         ae = element_forw(a->head);
251         if (s) {
252                 new = array_create_element(0, s);
253                 ADD_BEFORE(ae, new);
254                 a->num_elements++;
255         }
256
257         a->max_index += n;
258
259         /*
260          * Renumber all elements in the array except the one we just added.
261          */
262         for ( ; ae != a->head; ae = element_forw(ae))
263                 element_index(ae) += n;
264
265         return (a->num_elements);
266 }
267
268 ARRAY   *
269 array_quote(array)
270 ARRAY   *array;
271 {
272         ARRAY_ELEMENT   *a;
273         char    *t;
274
275         if (array == 0 || array->head == 0 || array_empty (array))
276                 return (ARRAY *)NULL;
277         for (a = element_forw(array->head); a != array->head; a = element_forw(a)) {
278                 t = quote_string (a->value);
279                 FREE(a->value);
280                 a->value = t;
281         }
282         return array;
283 }
284
285 char *
286 array_subrange (a, start, end, quoted)
287 ARRAY   *a;
288 arrayind_t      start, end;
289 int     quoted;
290 {
291         ARRAY_ELEMENT   *h, *p;
292         arrayind_t      i;
293
294         p = array_head (a);
295         if (p == 0 || array_empty (a) || start > array_num_elements (a))
296                 return ((char *)NULL);
297
298         for (i = 0, p = element_forw(p); p != a->head && i < start; i++, p = element_forw(p))
299                 ;
300         if (p == a->head)
301                 return ((char *)NULL);
302         for (h = p; p != a->head && i < end; i++, p = element_forw(p))
303                 ;
304
305         return (array_to_string_internal (h, p, " ", quoted));
306 }
307
308 char *
309 array_patsub (a, pat, rep, mflags)
310 ARRAY   *a;
311 char    *pat, *rep;
312 int     mflags;
313 {
314         ARRAY           *a2;
315         ARRAY_ELEMENT   *e;
316         char    *t;
317
318         if (array_head (a) == 0 || array_empty (a))
319                 return ((char *)NULL);
320
321         a2 = array_copy (a);
322         for (e = element_forw(a2->head); e != a2->head; e = element_forw(e)) {
323                 t = pat_subst(element_value(e), pat, rep, mflags);
324                 FREE(element_value(e));
325                 e->value = t;
326         }
327
328         if (mflags & MATCH_QUOTED)
329                 array_quote (a2);
330         t = array_to_string (a2, " ", 0);
331         array_dispose (a2);
332
333         return t;
334 }
335
336 /*
337  * Allocate and return a new array element with index INDEX and value
338  * VALUE.
339  */
340 ARRAY_ELEMENT *
341 array_create_element(indx, value)
342 arrayind_t      indx;
343 char    *value;
344 {
345         ARRAY_ELEMENT *r;
346
347         r = (ARRAY_ELEMENT *)xmalloc(sizeof(ARRAY_ELEMENT));
348         r->ind = indx;
349         r->value = value ? savestring(value) : (char *)NULL;
350         r->next = r->prev = (ARRAY_ELEMENT *) NULL;
351         return(r);
352 }
353
354 #ifdef INCLUDE_UNUSED
355 ARRAY_ELEMENT *
356 array_copy_element(ae)
357 ARRAY_ELEMENT   *ae;
358 {
359         return(ae ? array_create_element(element_index(ae), element_value(ae))
360                   : (ARRAY_ELEMENT *) NULL);
361 }
362 #endif
363
364 void
365 array_dispose_element(ae)
366 ARRAY_ELEMENT   *ae;
367 {
368         FREE(ae->value);
369         free(ae);
370 }
371
372 /*
373  * Add a new element with index I and value V to array A (a[i] = v).
374  */
375 int
376 array_insert(a, i, v)
377 ARRAY   *a;
378 arrayind_t      i;
379 char    *v;
380 {
381         register ARRAY_ELEMENT *new, *ae;
382
383         if (!a)
384                 return(-1);
385         new = array_create_element(i, v);
386         if (i > array_max_index(a)) {
387                 /*
388                  * Hook onto the end.  This also works for an empty array.
389                  * Fast path for the common case of allocating arrays
390                  * sequentially.
391                  */
392                 ADD_BEFORE(a->head, new);
393                 a->max_index = i;
394                 a->num_elements++;
395                 return(0);
396         }
397         /*
398          * Otherwise we search for the spot to insert it.
399          */
400         for (ae = element_forw(a->head); ae != a->head; ae = element_forw(ae)) {
401                 if (element_index(ae) == i) {
402                         /*
403                          * Replacing an existing element.
404                          */
405                         array_dispose_element(new);
406                         free(element_value(ae));
407                         ae->value = savestring(v);
408                         return(0);
409                 } else if (element_index(ae) > i) {
410                         ADD_BEFORE(ae, new);
411                         a->num_elements++;
412                         return(0);
413                 }
414         }
415         return (-1);            /* problem */
416 }
417
418 /*
419  * Delete the element with index I from array A and return it so the
420  * caller can dispose of it.
421  */
422 ARRAY_ELEMENT *
423 array_remove(a, i)
424 ARRAY   *a;
425 arrayind_t      i;
426 {
427         register ARRAY_ELEMENT *ae;
428
429         if (!a || array_empty(a))
430                 return((ARRAY_ELEMENT *) NULL);
431         for (ae = element_forw(a->head); ae != a->head; ae = element_forw(ae))
432                 if (element_index(ae) == i) {
433                         ae->next->prev = ae->prev;
434                         ae->prev->next = ae->next;
435                         a->num_elements--;
436                         if (i == array_max_index(a))
437                                 a->max_index = element_index(ae->prev);
438                         return(ae);
439                 }
440         return((ARRAY_ELEMENT *) NULL);
441 }
442
443 /*
444  * Return the value of a[i].
445  */
446 char *
447 array_reference(a, i)
448 ARRAY   *a;
449 arrayind_t      i;
450 {
451         register ARRAY_ELEMENT *ae;
452
453         if (a == 0 || array_empty(a))
454                 return((char *) NULL);
455         for (ae = element_forw(a->head); ae != a->head; ae = element_forw(ae))
456                 if (element_index(ae) == i)
457                         return(element_value(ae));
458         return((char *) NULL);
459 }
460
461 /* Convenience routines for the shell to translate to and from the form used
462    by the rest of the code. */
463 WORD_LIST *
464 array_to_word_list(a)
465 ARRAY   *a;
466 {
467         WORD_LIST       *list;
468         ARRAY_ELEMENT   *ae;
469
470         if (a == 0 || array_empty(a))
471                 return((WORD_LIST *)NULL);
472         list = (WORD_LIST *)NULL;
473         for (ae = element_forw(a->head); ae != a->head; ae = element_forw(ae))
474                 list = make_word_list (make_bare_word(element_value(ae)), list);
475         return (REVERSE_LIST(list, WORD_LIST *));
476 }
477
478 ARRAY *
479 array_from_word_list (list)
480 WORD_LIST       *list;
481 {
482         ARRAY   *a;
483
484         if (list == 0)
485                 return((ARRAY *)NULL);
486         a = array_create();
487         return (array_assign_list (a, list));
488 }
489
490 ARRAY *
491 array_assign_list (array, list)
492 ARRAY   *array;
493 WORD_LIST       *list;
494 {
495         register WORD_LIST *l;
496         register arrayind_t i;
497
498         for (l = list, i = 0; l; l = l->next, i++)
499                 array_insert(array, i, l->word->word);
500         return array;
501 }
502
503 char **
504 array_to_argv (a)
505 ARRAY   *a;
506 {
507         char            **ret, *t;
508         int             i;
509         ARRAY_ELEMENT   *ae;
510
511         if (a == 0 || array_empty(a))
512                 return ((char **)NULL);
513         ret = strvec_create (array_num_elements (a) + 1);
514         i = 0;
515         for (ae = element_forw(a->head); ae != a->head; ae = element_forw(ae)) {
516                 t = element_value (ae);
517                 ret[i++] = t ? savestring (t) : (char *)NULL;
518         }
519         ret[i] = (char *)NULL;
520         return (ret);
521 }
522         
523 /*
524  * Return a string that is the concatenation of all the elements in A,
525  * separated by SEP.
526  */
527 static char *
528 array_to_string_internal (start, end, sep, quoted)
529 ARRAY_ELEMENT   *start, *end;
530 char    *sep;
531 int     quoted;
532 {
533         char    *result, *t;
534         ARRAY_ELEMENT *ae;
535         int     slen, rsize, rlen, reg;
536
537         if (start == end)       /* XXX - should not happen */
538                 return ((char *)NULL);
539
540         slen = strlen(sep);
541         result = NULL;
542         for (rsize = rlen = 0, ae = start; ae != end; ae = element_forw(ae)) {
543                 if (rsize == 0)
544                         result = (char *)xmalloc (rsize = 64);
545                 if (element_value(ae)) {
546                         t = quoted ? quote_string(element_value(ae)) : element_value(ae);
547                         reg = strlen(t);
548                         RESIZE_MALLOCED_BUFFER (result, rlen, (reg + slen + 2),
549                                                 rsize, rsize);
550                         strcpy(result + rlen, t);
551                         rlen += reg;
552                         if (quoted && t)
553                                 free(t);
554                         /*
555                          * Add a separator only after non-null elements.
556                          */
557                         if (element_forw(ae) != end) {
558                                 strcpy(result + rlen, sep);
559                                 rlen += slen;
560                         }
561                 }
562         }
563         if (result)
564           result[rlen] = '\0';  /* XXX */
565         return(result);
566 }
567
568 char *
569 array_to_assign (a, quoted)
570 ARRAY   *a;
571 int     quoted;
572 {
573         char    *result, *valstr, *is;
574         char    indstr[INT_STRLEN_BOUND(intmax_t) + 1];
575         ARRAY_ELEMENT *ae;
576         int     rsize, rlen, elen;
577
578         if (a == 0 || array_empty (a))
579                 return((char *)NULL);
580
581         result = (char *)xmalloc (rsize = 128);
582         result[0] = '(';
583         rlen = 1;
584
585         for (ae = element_forw(a->head); ae != a->head; ae = element_forw(ae)) {
586                 is = inttostr (element_index(ae), indstr, sizeof(indstr));
587                 valstr = element_value (ae) ? sh_double_quote (element_value(ae))
588                                             : (char *)NULL;
589                 elen = STRLEN (indstr) + 8 + STRLEN (valstr);
590                 RESIZE_MALLOCED_BUFFER (result, rlen, (elen + 1), rsize, rsize);
591
592                 result[rlen++] = '[';
593                 strcpy (result + rlen, is);
594                 rlen += STRLEN (is);
595                 result[rlen++] = ']';
596                 result[rlen++] = '=';
597                 if (valstr) {
598                         strcpy (result + rlen, valstr);
599                         rlen += STRLEN (valstr);
600                 }
601
602                 if (element_forw(ae) != a->head)
603                   result[rlen++] = ' ';
604
605                 FREE (valstr);
606         }
607         RESIZE_MALLOCED_BUFFER (result, rlen, 1, rsize, 8);
608         result[rlen++] = ')';
609         result[rlen] = '\0';
610         if (quoted) {
611                 /* This is not as efficient as it could be... */
612                 valstr = sh_single_quote (result);
613                 free (result);
614                 result = valstr;
615         }
616         return(result);
617 }
618
619 char *
620 array_to_string (a, sep, quoted)
621 ARRAY   *a;
622 char    *sep;
623 int     quoted;
624 {
625         if (a == 0)
626                 return((char *)NULL);
627         if (array_empty(a))
628                 return(savestring(""));
629         return (array_to_string_internal (element_forw(a->head), a->head, sep, quoted));
630 }
631
632 #if defined (INCLUDE_UNUSED) || defined (TEST_ARRAY)
633 /*
634  * Return an array consisting of elements in S, separated by SEP
635  */
636 ARRAY *
637 array_from_string(s, sep)
638 char    *s, *sep;
639 {
640         ARRAY   *a;
641         WORD_LIST *w;
642
643         if (s == 0)
644                 return((ARRAY *)NULL);
645         w = list_string (s, sep, 0);
646         if (w == 0)
647                 return((ARRAY *)NULL);
648         a = array_from_word_list (w);
649         return (a);
650 }
651 #endif
652
653 #if defined (TEST_ARRAY)
654 /*
655  * To make a running version, compile -DTEST_ARRAY and link with:
656  *      xmalloc.o syntax.o lib/malloc/libmalloc.a lib/sh/libsh.a
657  */
658 int interrupt_immediately = 0;
659
660 int
661 signal_is_trapped(s)
662 int     s;
663 {
664         return 0;
665 }
666
667 void
668 fatal_error(const char *s, ...)
669 {
670         fprintf(stderr, "array_test: fatal memory error\n");
671         abort();
672 }
673
674 void
675 programming_error(const char *s, ...)
676 {
677         fprintf(stderr, "array_test: fatal programming error\n");
678         abort();
679 }
680
681 WORD_DESC *
682 make_bare_word (s)
683 const char      *s;
684 {
685         WORD_DESC *w;
686
687         w = (WORD_DESC *)xmalloc(sizeof(WORD_DESC));
688         w->word = s ? savestring(s) : savestring ("");
689         w->flags = 0;
690         return w;
691 }
692
693 WORD_LIST *
694 make_word_list(x, l)
695 WORD_DESC       *x;
696 WORD_LIST       *l;
697 {
698         WORD_LIST *w;
699
700         w = (WORD_LIST *)xmalloc(sizeof(WORD_LIST));
701         w->word = x;
702         w->next = l;
703         return w;
704 }
705
706 WORD_LIST *
707 list_string(s, t, i)
708 char    *s, *t;
709 int     i;
710 {
711         char    *r, *a;
712         WORD_LIST       *wl;
713
714         if (s == 0)
715                 return (WORD_LIST *)NULL;
716         r = savestring(s);
717         wl = (WORD_LIST *)NULL;
718         a = strtok(r, t);
719         while (a) {
720                 wl = make_word_list (make_bare_word(a), wl);
721                 a = strtok((char *)NULL, t);
722         }
723         return (REVERSE_LIST (wl, WORD_LIST *));
724 }
725
726 GENERIC_LIST *
727 list_reverse (list)
728 GENERIC_LIST    *list;
729 {
730         register GENERIC_LIST *next, *prev;
731
732         for (prev = 0; list; ) {
733                 next = list->next;
734                 list->next = prev;
735                 prev = list;
736                 list = next;
737         }
738         return prev;
739 }
740
741 char *
742 pat_subst(s, t, u, i)
743 char    *s, *t, *u;
744 int     i;
745 {
746         return ((char *)NULL);
747 }
748
749 char *
750 quote_string(s)
751 char    *s;
752 {
753         return savestring(s);
754 }
755
756 print_element(ae)
757 ARRAY_ELEMENT   *ae;
758 {
759         char    lbuf[INT_STRLEN_BOUND (intmax_t) + 1];
760
761         printf("array[%s] = %s\n",
762                 inttostr (element_index(ae), lbuf, sizeof (lbuf)),
763                 element_value(ae));
764 }
765
766 print_array(a)
767 ARRAY   *a;
768 {
769         printf("\n");
770         array_walk(a, print_element);
771 }
772
773 main()
774 {
775         ARRAY   *a, *new_a, *copy_of_a;
776         ARRAY_ELEMENT   *ae, *aew;
777         char    *s;
778
779         a = array_create();
780         array_insert(a, 1, "one");
781         array_insert(a, 7, "seven");
782         array_insert(a, 4, "four");
783         array_insert(a, 1029, "one thousand twenty-nine");
784         array_insert(a, 12, "twelve");
785         array_insert(a, 42, "forty-two");
786         print_array(a);
787         s = array_to_string (a, " ", 0);
788         printf("s = %s\n", s);
789         copy_of_a = array_from_string(s, " ");
790         printf("copy_of_a:");
791         print_array(copy_of_a);
792         array_dispose(copy_of_a);
793         printf("\n");
794         free(s);
795         ae = array_remove(a, 4);
796         array_dispose_element(ae);
797         ae = array_remove(a, 1029);
798         array_dispose_element(ae);
799         array_insert(a, 16, "sixteen");
800         print_array(a);
801         s = array_to_string (a, " ", 0);
802         printf("s = %s\n", s);
803         copy_of_a = array_from_string(s, " ");
804         printf("copy_of_a:");
805         print_array(copy_of_a);
806         array_dispose(copy_of_a);
807         printf("\n");
808         free(s);
809         array_insert(a, 2, "two");
810         array_insert(a, 1029, "new one thousand twenty-nine");
811         array_insert(a, 0, "zero");
812         array_insert(a, 134, "");
813         print_array(a);
814         s = array_to_string (a, ":", 0);
815         printf("s = %s\n", s);
816         copy_of_a = array_from_string(s, ":");
817         printf("copy_of_a:");
818         print_array(copy_of_a);
819         array_dispose(copy_of_a);
820         printf("\n");
821         free(s);
822         new_a = array_copy(a);
823         print_array(new_a);
824         s = array_to_string (new_a, ":", 0);
825         printf("s = %s\n", s);
826         copy_of_a = array_from_string(s, ":");
827         free(s);
828         printf("copy_of_a:");
829         print_array(copy_of_a);
830         array_shift(copy_of_a, 2, AS_DISPOSE);
831         printf("copy_of_a shifted by two:");
832         print_array(copy_of_a);
833         ae = array_shift(copy_of_a, 2, 0);
834         printf("copy_of_a shifted by two:");
835         print_array(copy_of_a);
836         for ( ; ae; ) {
837                 aew = element_forw(ae);
838                 array_dispose_element(ae);
839                 ae = aew;
840         }
841         array_rshift(copy_of_a, 1, (char *)0);
842         printf("copy_of_a rshift by 1:");
843         print_array(copy_of_a);
844         array_rshift(copy_of_a, 2, "new element zero");
845         printf("copy_of_a rshift again by 2 with new element zero:");
846         print_array(copy_of_a);
847         s = array_to_assign(copy_of_a, 0);
848         printf("copy_of_a=%s\n", s);
849         free(s);
850         ae = array_shift(copy_of_a, array_num_elements(copy_of_a), 0);
851         for ( ; ae; ) {
852                 aew = element_forw(ae);
853                 array_dispose_element(ae);
854                 ae = aew;
855         }
856         array_dispose(copy_of_a);
857         printf("\n");
858         array_dispose(a);
859         array_dispose(new_a);
860 }
861
862 #endif /* TEST_ARRAY */
863 #endif /* ARRAY_VARS */