]> git.ipfire.org Git - thirdparty/bash.git/blobdiff - array.c
fix for SIGINT in sourced script
[thirdparty/bash.git] / array.c
diff --git a/array.c b/array.c
index 379eb43604b316d3b9a504bc30c2ae56493cccea..6a6db77637b2321b2d02279893ca3508537f5f1e 100644 (file)
--- a/array.c
+++ b/array.c
@@ -8,11 +8,33 @@
  * Chet Ramey
  * chet@ins.cwru.edu
  */
+
+/* Copyright (C) 1997-2009 Free Software Foundation, Inc.
+
+   This file is part of GNU Bash, the Bourne Again SHell.
+
+   Bash is free software: you can redistribute it and/or modify
+   it under the terms of the GNU General Public License as published by
+   the Free Software Foundation, either version 3 of the License, or
+   (at your option) any later version.
+
+   Bash is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+   GNU General Public License for more details.
+
+   You should have received a copy of the GNU General Public License
+   along with Bash.  If not, see <http://www.gnu.org/licenses/>.
+*/
+
 #include "config.h"
 
 #if defined (ARRAY_VARS)
 
 #if defined (HAVE_UNISTD_H)
+#  ifdef _MINIX
+#    include <sys/types.h>
+#  endif
 #  include <unistd.h>
 #endif
 
@@ -23,8 +45,6 @@
 #include "array.h"
 #include "builtins/common.h"
 
-extern char *quote_string ();  /* XXX */
-
 #define ADD_BEFORE(ae, new) \
        do { \
                ae->prev->next = new; \
@@ -33,50 +53,58 @@ extern char *quote_string ();       /* XXX */
                new->next = ae; \
        } while(0)
 
-/*
- * Allocate and return a new array element with index INDEX and value
- * VALUE.
- */
-ARRAY_ELEMENT *
-new_array_element(indx, value)
-arrayind_t     indx;
-char   *value;
-{
-       ARRAY_ELEMENT *r;
+static char *array_to_string_internal __P((ARRAY_ELEMENT *, ARRAY_ELEMENT *, char *, int));
 
-       r = (ARRAY_ELEMENT *) xmalloc(sizeof(ARRAY_ELEMENT));
-       r->ind = indx;
-       r->value = value ? savestring(value) : (char *)NULL;
-       r->next = r->prev = (ARRAY_ELEMENT *) NULL;
-       return(r);
-}
+/* lastref should be moved into the array structure so each array can be
+   optimized separately */
 
-void
-destroy_array_element(ae)
-ARRAY_ELEMENT  *ae;
-{
-       FREE(ae->value);
-       free(ae);
-}
+static ARRAY *lastarray = 0;
+static ARRAY_ELEMENT *lastref = 0;
+
+#define IS_LASTREF(a)  (lastarray && (a) == lastarray)
+
+#define LASTREF_START(a, i) \
+       (IS_LASTREF(a) && i >= element_index(lastref)) ? lastref \
+                                                      : element_forw(a->head)
+
+#define INVALIDATE_LASTREF(a) \
+do { \
+       if ((a) == lastarray) { \
+               lastarray = 0; \
+               lastref = 0; \
+       } \
+} while (0)
+
+#define SET_LASTREF(a, e) \
+do { \
+       lastarray = (a); \
+       lastref = (e); \
+} while (0)
+
+#define UNSET_LASTREF() \
+do { \
+       lastarray = 0; \
+       lastref = 0; \
+} while (0)
 
 ARRAY *
-new_array()
+array_create()
 {
        ARRAY   *r;
        ARRAY_ELEMENT   *head;
 
-       r =(ARRAY *) xmalloc(sizeof(ARRAY));
+       r =(ARRAY *)xmalloc(sizeof(ARRAY));
        r->type = array_indexed;
-       r->max_index = r->max_size = -1;
+       r->max_index = -1;
        r->num_elements = 0;
-       head = new_array_element(-1, (char *)NULL);     /* dummy head */
+       head = array_create_element(-1, (char *)NULL);  /* dummy head */
        head->prev = head->next = head;
        r->head = head;
        return(r);
 }
 
 void
-empty_array (a)
+array_flush (a)
 ARRAY  *a;
 {
        register ARRAY_ELEMENT *r, *r1;
@@ -85,94 +113,515 @@ ARRAY     *a;
                return;
        for (r = element_forw(a->head); r != a->head; ) {
                r1 = element_forw(r);
-               destroy_array_element(r);
+               array_dispose_element(r);
                r = r1;
        }
        a->head->next = a->head->prev = a->head;
-       a->max_index = a->max_size = -1;
-       a->num_elements = a->max_size = 0;
+       a->max_index = -1;
+       a->num_elements = 0;
+       INVALIDATE_LASTREF(a);
 }
 
 void
-dispose_array(a)
+array_dispose(a)
 ARRAY  *a;
 {
        if (a == 0)
                return;
-       empty_array (a);
-       destroy_array_element(a->head);
+       array_flush (a);
+       array_dispose_element(a->head);
        free(a);
 }
 
 ARRAY *
-dup_array(a)
+array_copy(a)
 ARRAY  *a;
 {
        ARRAY   *a1;
        ARRAY_ELEMENT   *ae, *new;
 
-       if (!a)
+       if (a == 0)
                return((ARRAY *) NULL);
-       a1 = new_array();
+       a1 = array_create();
        a1->type = a->type;
        a1->max_index = a->max_index;
        a1->num_elements = a->num_elements;
-       a1->max_size = a->max_size;
        for (ae = element_forw(a->head); ae != a->head; ae = element_forw(ae)) {
-               new = new_array_element(element_index(ae), element_value(ae));
+               new = array_create_element(element_index(ae), element_value(ae));
                ADD_BEFORE(a1->head, new);
        }
        return(a1);
 }
 
-#ifdef INCLUDE_UNUSED
 /*
  * Make and return a new array composed of the elements in array A from
  * S to E, inclusive.
  */
 ARRAY *
-dup_array_subrange(array, s, e)
+array_slice(array, s, e)
 ARRAY          *array;
 ARRAY_ELEMENT  *s, *e;
 {
        ARRAY   *a;
        ARRAY_ELEMENT *p, *n;
        int     i;
+       arrayind_t mi;
 
-       a = new_array ();
+       a = array_create ();
        a->type = array->type;
 
-       for (p = s, i = 0; p != e; p = element_forw(p), i++) {
-               n = new_array_element (i, element_value(p));
+       for (mi = 0, p = s, i = 0; p != e; p = element_forw(p), i++) {
+               n = array_create_element (element_index(p), element_value(p));
                ADD_BEFORE(a->head, n);
+               mi = element_index(n);
        }
-       a->num_elements = a->max_index = i;
+       a->num_elements = i;
+       a->max_index = mi;
        return a;
 }
-#endif
 
+/*
+ * Walk the array, calling FUNC once for each element, with the array
+ * element as the argument.
+ */
+void
+array_walk(a, func, udata)
+ARRAY  *a;
+sh_ae_map_func_t *func;
+void   *udata;
+{
+       register ARRAY_ELEMENT *ae;
+
+       if (a == 0 || array_empty(a))
+               return;
+       for (ae = element_forw(a->head); ae != a->head; ae = element_forw(ae))
+               if ((*func)(ae, udata) < 0)
+                       return;
+}
+
+/*
+ * Shift the array A N elements to the left.  Delete the first N elements
+ * and subtract N from the indices of the remaining elements.  If FLAGS
+ * does not include AS_DISPOSE, this returns a singly-linked null-terminated
+ * list of elements so the caller can dispose of the chain.  If FLAGS
+ * includes AS_DISPOSE, this function disposes of the shifted-out elements
+ * and returns NULL.
+ */
+ARRAY_ELEMENT *
+array_shift(a, n, flags)
+ARRAY  *a;
+int    n, flags;
+{
+       register ARRAY_ELEMENT *ae, *ret;
+       register int i;
+
+       if (a == 0 || array_empty(a) || n <= 0)
+               return ((ARRAY_ELEMENT *)NULL);
+
+       INVALIDATE_LASTREF(a);
+       for (i = 0, ret = ae = element_forw(a->head); ae != a->head && i < n; ae = element_forw(ae), i++)
+               ;
+       if (ae == a->head) {
+               /* Easy case; shifting out all of the elements */
+               if (flags & AS_DISPOSE) {
+                       array_flush (a);
+                       return ((ARRAY_ELEMENT *)NULL);
+               }
+               for (ae = ret; element_forw(ae) != a->head; ae = element_forw(ae))
+                       ;
+               element_forw(ae) = (ARRAY_ELEMENT *)NULL;
+               a->head->next = a->head->prev = a->head;
+               a->max_index = -1;
+               a->num_elements = 0;
+               return ret;
+       }
+       /*
+        * ae now points to the list of elements we want to retain.
+        * ret points to the list we want to either destroy or return.
+        */
+       ae->prev->next = (ARRAY_ELEMENT *)NULL;         /* null-terminate RET */
+
+       a->head->next = ae;             /* slice RET out of the array */
+       ae->prev = a->head;
+
+       for ( ; ae != a->head; ae = element_forw(ae))
+               element_index(ae) -= n; /* renumber retained indices */
+
+       a->num_elements -= n;           /* modify bookkeeping information */
+       a->max_index = element_index(a->head->prev);
+
+       if (flags & AS_DISPOSE) {
+               for (ae = ret; ae; ) {
+                       ret = element_forw(ae);
+                       array_dispose_element(ae);
+                       ae = ret;
+               }
+               return ((ARRAY_ELEMENT *)NULL);
+       }
+
+       return ret;
+}
+
+/*
+ * Shift array A right N indices.  If S is non-null, it becomes the value of
+ * the new element 0.  Returns the number of elements in the array after the
+ * shift.
+ */
+int
+array_rshift (a, n, s)
+ARRAY  *a;
+int    n;
+char   *s;
+{
+       register ARRAY_ELEMENT  *ae, *new;
+
+       if (a == 0 || (array_empty(a) && s == 0))
+               return 0;
+       else if (n <= 0)
+               return (a->num_elements);
+
+       ae = element_forw(a->head);
+       if (s) {
+               new = array_create_element(0, s);
+               ADD_BEFORE(ae, new);
+               a->num_elements++;
+               if (array_num_elements(a) == 1) {       /* array was empty */
+                       a->max_index = 0;
+                       return 1;
+               }
+       }
+
+       /*
+        * Renumber all elements in the array except the one we just added.
+        */
+       for ( ; ae != a->head; ae = element_forw(ae))
+               element_index(ae) += n;
+
+       a->max_index = element_index(a->head->prev);
+
+       INVALIDATE_LASTREF(a);
+       return (a->num_elements);
+}
+
+ARRAY_ELEMENT *
+array_unshift_element(a)
+ARRAY  *a;
+{
+       return (array_shift (a, 1, 0));
+}
+
+int
+array_shift_element(a, v)
+ARRAY  *a;
+char   *v;
+{
+       return (array_rshift (a, 1, v));
+}
+
+ARRAY *
+array_quote(array)
+ARRAY  *array;
+{
+       ARRAY_ELEMENT   *a;
+       char    *t;
+
+       if (array == 0 || array_head(array) == 0 || array_empty(array))
+               return (ARRAY *)NULL;
+       for (a = element_forw(array->head); a != array->head; a = element_forw(a)) {
+               t = quote_string (a->value);
+               FREE(a->value);
+               a->value = t;
+       }
+       return array;
+}
+
+ARRAY *
+array_quote_escapes(array)
+ARRAY  *array;
+{
+       ARRAY_ELEMENT   *a;
+       char    *t;
+
+       if (array == 0 || array_head(array) == 0 || array_empty(array))
+               return (ARRAY *)NULL;
+       for (a = element_forw(array->head); a != array->head; a = element_forw(a)) {
+               t = quote_escapes (a->value);
+               FREE(a->value);
+               a->value = t;
+       }
+       return array;
+}
+
+ARRAY *
+array_dequote(array)
+ARRAY  *array;
+{
+       ARRAY_ELEMENT   *a;
+       char    *t;
+
+       if (array == 0 || array_head(array) == 0 || array_empty(array))
+               return (ARRAY *)NULL;
+       for (a = element_forw(array->head); a != array->head; a = element_forw(a)) {
+               t = dequote_string (a->value);
+               FREE(a->value);
+               a->value = t;
+       }
+       return array;
+}
+
+ARRAY *
+array_dequote_escapes(array)
+ARRAY  *array;
+{
+       ARRAY_ELEMENT   *a;
+       char    *t;
+
+       if (array == 0 || array_head(array) == 0 || array_empty(array))
+               return (ARRAY *)NULL;
+       for (a = element_forw(array->head); a != array->head; a = element_forw(a)) {
+               t = dequote_escapes (a->value);
+               FREE(a->value);
+               a->value = t;
+       }
+       return array;
+}
+
+ARRAY *
+array_remove_quoted_nulls(array)
+ARRAY  *array;
+{
+       ARRAY_ELEMENT   *a;
+       char    *t;
+
+       if (array == 0 || array_head(array) == 0 || array_empty(array))
+               return (ARRAY *)NULL;
+       for (a = element_forw(array->head); a != array->head; a = element_forw(a))
+               a->value = remove_quoted_nulls (a->value);
+       return array;
+}
+
+/*
+ * Return a string whose elements are the members of array A beginning at
+ * index START and spanning NELEM members.  Null elements are counted.
+ * Since arrays are sparse, unset array elements are not counted.
+ */
+char *
+array_subrange (a, start, nelem, starsub, quoted)
+ARRAY  *a;
+arrayind_t     start, nelem;
+int    starsub, quoted;
+{
+       ARRAY           *a2;
+       ARRAY_ELEMENT   *h, *p;
+       arrayind_t      i;
+       char            *ifs, *sifs, *t;
+       int             slen;
+
+       p = a ? array_head (a) : 0;
+       if (p == 0 || array_empty (a) || start > array_max_index(a))
+               return ((char *)NULL);
+
+       /*
+        * Find element with index START.  If START corresponds to an unset
+        * element (arrays can be sparse), use the first element whose index
+        * is >= START.  If START is < 0, we count START indices back from
+        * the end of A (not elements, even with sparse arrays -- START is an
+        * index).
+        */
+       for (p = element_forw(p); p != array_head(a) && start > element_index(p); p = element_forw(p))
+               ;
+
+       if (p == a->head)
+               return ((char *)NULL);
+
+       /* Starting at P, take NELEM elements, inclusive. */
+       for (i = 0, h = p; p != a->head && i < nelem; i++, p = element_forw(p))
+               ;
+
+       a2 = array_slice(a, h, p);
+
+       if (quoted & (Q_DOUBLE_QUOTES|Q_HERE_DOCUMENT))
+               array_quote(a2);
+       else
+               array_quote_escapes(a2);
+
+       if (starsub && (quoted & (Q_DOUBLE_QUOTES|Q_HERE_DOCUMENT))) {
+               /* ${array[*]} */
+               array_remove_quoted_nulls (a2);
+               sifs = ifs_firstchar ((int *)NULL);
+               t = array_to_string (a2, sifs, 0);
+               free (sifs);
+       } else if (quoted & (Q_DOUBLE_QUOTES|Q_HERE_DOCUMENT)) {
+               /* ${array[@]} */
+               sifs = ifs_firstchar (&slen);
+               ifs = getifs ();
+               if (ifs == 0 || *ifs == 0) {
+                       if (slen < 2)
+                               sifs = xrealloc(sifs, 2);
+                       sifs[0] = ' ';
+                       sifs[1] = '\0';
+               }
+               t = array_to_string (a2, sifs, 0);
+               free (sifs);
+       } else
+               t = array_to_string (a2, " ", 0);
+       array_dispose(a2);
+
+       return t;
+}
+
+char *
+array_patsub (a, pat, rep, mflags)
+ARRAY  *a;
+char   *pat, *rep;
+int    mflags;
+{
+       ARRAY           *a2;
+       ARRAY_ELEMENT   *e;
+       char    *t, *sifs, *ifs;
+       int     slen;
+
+       if (a == 0 || array_head(a) == 0 || array_empty(a))
+               return ((char *)NULL);
+
+       a2 = array_copy(a);
+       for (e = element_forw(a2->head); e != a2->head; e = element_forw(e)) {
+               t = pat_subst(element_value(e), pat, rep, mflags);
+               FREE(element_value(e));
+               e->value = t;
+       }
+
+       if (mflags & MATCH_QUOTED)
+               array_quote(a2);
+       else
+               array_quote_escapes(a2);
+
+       if (mflags & MATCH_STARSUB) {
+               array_remove_quoted_nulls (a2);
+               sifs = ifs_firstchar((int *)NULL);
+               t = array_to_string (a2, sifs, 0);
+               free(sifs);
+       } else if (mflags & MATCH_QUOTED) {
+               /* ${array[@]} */
+               sifs = ifs_firstchar (&slen);
+               ifs = getifs ();
+               if (ifs == 0 || *ifs == 0) {
+                       if (slen < 2)
+                               sifs = xrealloc (sifs, 2);
+                       sifs[0] = ' ';
+                       sifs[1] = '\0';
+               }
+               t = array_to_string (a2, sifs, 0);
+               free(sifs);
+       } else
+               t = array_to_string (a2, " ", 0);
+       array_dispose (a2);
+
+       return t;
+}
+
+char *
+array_modcase (a, pat, modop, mflags)
+ARRAY  *a;
+char   *pat;
+int    modop;
+int    mflags;
+{
+       ARRAY           *a2;
+       ARRAY_ELEMENT   *e;
+       char    *t, *sifs, *ifs;
+       int     slen;
+
+       if (a == 0 || array_head(a) == 0 || array_empty(a))
+               return ((char *)NULL);
+
+       a2 = array_copy(a);
+       for (e = element_forw(a2->head); e != a2->head; e = element_forw(e)) {
+               t = sh_modcase(element_value(e), pat, modop);
+               FREE(element_value(e));
+               e->value = t;
+       }
+
+       if (mflags & MATCH_QUOTED)
+               array_quote(a2);
+       else
+               array_quote_escapes(a2);
+
+       if (mflags & MATCH_STARSUB) {
+               array_remove_quoted_nulls (a2);
+               sifs = ifs_firstchar((int *)NULL);
+               t = array_to_string (a2, sifs, 0);
+               free(sifs);
+       } else if (mflags & MATCH_QUOTED) {
+               /* ${array[@]} */
+               sifs = ifs_firstchar (&slen);
+               ifs = getifs ();
+               if (ifs == 0 || *ifs == 0) {
+                       if (slen < 2)
+                               sifs = xrealloc (sifs, 2);
+                       sifs[0] = ' ';
+                       sifs[1] = '\0';
+               }
+               t = array_to_string (a2, sifs, 0);
+               free(sifs);
+       } else
+               t = array_to_string (a2, " ", 0);
+       array_dispose (a2);
+
+       return t;
+}
+/*
+ * Allocate and return a new array element with index INDEX and value
+ * VALUE.
+ */
 ARRAY_ELEMENT *
-copy_array_element(ae)
+array_create_element(indx, value)
+arrayind_t     indx;
+char   *value;
+{
+       ARRAY_ELEMENT *r;
+
+       r = (ARRAY_ELEMENT *)xmalloc(sizeof(ARRAY_ELEMENT));
+       r->ind = indx;
+       r->value = value ? savestring(value) : (char *)NULL;
+       r->next = r->prev = (ARRAY_ELEMENT *) NULL;
+       return(r);
+}
+
+#ifdef INCLUDE_UNUSED
+ARRAY_ELEMENT *
+array_copy_element(ae)
 ARRAY_ELEMENT  *ae;
 {
-       return(ae ? new_array_element(element_index(ae), element_value(ae))
+       return(ae ? array_create_element(element_index(ae), element_value(ae))
                  : (ARRAY_ELEMENT *) NULL);
 }
+#endif
+
+void
+array_dispose_element(ae)
+ARRAY_ELEMENT  *ae;
+{
+       if (ae) {
+               FREE(ae->value);
+               free(ae);
+       }
+}
 
 /*
  * Add a new element with index I and value V to array A (a[i] = v).
  */
 int
-array_add_element(a, i, v)
+array_insert(a, i, v)
 ARRAY  *a;
 arrayind_t     i;
 char   *v;
 {
-       register ARRAY_ELEMENT *new, *ae;
+       register ARRAY_ELEMENT *new, *ae, *start;
 
-       if (!a)
+       if (a == 0)
                return(-1);
-       new = new_array_element(i, v);
+       new = array_create_element(i, v);
        if (i > array_max_index(a)) {
                /*
                 * Hook onto the end.  This also works for an empty array.
@@ -182,26 +631,38 @@ char      *v;
                ADD_BEFORE(a->head, new);
                a->max_index = i;
                a->num_elements++;
+               SET_LASTREF(a, new);
                return(0);
        }
+#if OPTIMIZE_SEQUENTIAL_ARRAY_ASSIGNMENT
        /*
-        * Otherwise we search for the spot to insert it.
+        * Otherwise we search for the spot to insert it.  The lastref
+        * handle optimizes the case of sequential or almost-sequential
+        * assignments that are not at the end of the array.
         */
-       for (ae = element_forw(a->head); ae != a->head; ae = element_forw(ae)) {
+       start = LASTREF_START(a, i);
+#else
+       start = element_forw(ae->head);
+#endif
+       for (ae = start; ae != a->head; ae = element_forw(ae)) {
                if (element_index(ae) == i) {
                        /*
                         * Replacing an existing element.
                         */
-                       destroy_array_element(new);
+                       array_dispose_element(new);
                        free(element_value(ae));
-                       ae->value = savestring(v);
+                       ae->value = v ? savestring(v) : (char *)NULL;
+                       SET_LASTREF(a, ae);
                        return(0);
                } else if (element_index(ae) > i) {
                        ADD_BEFORE(ae, new);
                        a->num_elements++;
+                       SET_LASTREF(a, new);
                        return(0);
                }
        }
+       array_dispose_element(new);
+       INVALIDATE_LASTREF(a);
        return (-1);            /* problem */
 }
 
@@ -210,21 +671,32 @@ char      *v;
  * caller can dispose of it.
  */
 ARRAY_ELEMENT *
-array_delete_element(a, i)
+array_remove(a, i)
 ARRAY  *a;
 arrayind_t     i;
 {
-       register ARRAY_ELEMENT *ae;
+       register ARRAY_ELEMENT *ae, *start;
 
-       if (!a || array_empty(a))
+       if (a == 0 || array_empty(a))
                return((ARRAY_ELEMENT *) NULL);
-       for (ae = element_forw(a->head); ae != a->head; ae = element_forw(ae))
+       start = LASTREF_START(a, i);
+       for (ae = start; ae != a->head; ae = element_forw(ae))
                if (element_index(ae) == i) {
                        ae->next->prev = ae->prev;
                        ae->prev->next = ae->next;
                        a->num_elements--;
                        if (i == array_max_index(a))
                                a->max_index = element_index(ae->prev);
+#if 0
+                       INVALIDATE_LASTREF(a);
+#else
+                       if (ae->next != a->head)
+                               SET_LASTREF(a, ae->next);
+                       else if (ae->prev != a->head)
+                               SET_LASTREF(a, ae->prev);
+                       else
+                               INVALIDATE_LASTREF(a);
+#endif
                        return(ae);
                }
        return((ARRAY_ELEMENT *) NULL);
@@ -238,36 +710,107 @@ array_reference(a, i)
 ARRAY  *a;
 arrayind_t     i;
 {
-       register ARRAY_ELEMENT *ae;
+       register ARRAY_ELEMENT *ae, *start;
 
        if (a == 0 || array_empty(a))
                return((char *) NULL);
-       for (ae = element_forw(a->head); ae != a->head; ae = element_forw(ae))
-               if (element_index(ae) == i)
+       if (i > array_max_index(a))
+               return((char *)NULL);   /* Keep roving pointer into array to optimize sequential access */
+       start = LASTREF_START(a, i);
+       for (ae = start; ae != a->head; ae = element_forw(ae))
+               if (element_index(ae) == i) {
+                       SET_LASTREF(a, ae);
                        return(element_value(ae));
+               }
+       UNSET_LASTREF();                /* XXX SET_LASTREF(a, start) ? */
        return((char *) NULL);
 }
 
-/*
- * Walk the array, calling FUNC once for each element, with the array
- * element as the argument.
- */
-void
-array_walk(a, func)
+/* Convenience routines for the shell to translate to and from the form used
+   by the rest of the code. */
+
+WORD_LIST *
+array_to_word_list(a)
 ARRAY  *a;
-Function *func;
 {
-       register ARRAY_ELEMENT *ae;
+       WORD_LIST       *list;
+       ARRAY_ELEMENT   *ae;
 
        if (a == 0 || array_empty(a))
-               return;
+               return((WORD_LIST *)NULL);
+       list = (WORD_LIST *)NULL;
        for (ae = element_forw(a->head); ae != a->head; ae = element_forw(ae))
-               (*func)(ae);
+               list = make_word_list (make_bare_word(element_value(ae)), list);
+       return (REVERSE_LIST(list, WORD_LIST *));
+}
+
+ARRAY *
+array_from_word_list (list)
+WORD_LIST      *list;
+{
+       ARRAY   *a;
+
+       if (list == 0)
+               return((ARRAY *)NULL);
+       a = array_create();
+       return (array_assign_list (a, list));
 }
 
+WORD_LIST *
+array_keys_to_word_list(a)
+ARRAY  *a;
+{
+       WORD_LIST       *list;
+       ARRAY_ELEMENT   *ae;
+       char            *t;
+
+       if (a == 0 || array_empty(a))
+               return((WORD_LIST *)NULL);
+       list = (WORD_LIST *)NULL;
+       for (ae = element_forw(a->head); ae != a->head; ae = element_forw(ae)) {
+               t = itos(element_index(ae));
+               list = make_word_list (make_bare_word(t), list);
+               free(t);
+       }
+       return (REVERSE_LIST(list, WORD_LIST *));
+}
+
+ARRAY *
+array_assign_list (array, list)
+ARRAY  *array;
+WORD_LIST      *list;
+{
+       register WORD_LIST *l;
+       register arrayind_t i;
+
+       for (l = list, i = 0; l; l = l->next, i++)
+               array_insert(array, i, l->word->word);
+       return array;
+}
+
+char **
+array_to_argv (a)
+ARRAY  *a;
+{
+       char            **ret, *t;
+       int             i;
+       ARRAY_ELEMENT   *ae;
+
+       if (a == 0 || array_empty(a))
+               return ((char **)NULL);
+       ret = strvec_create (array_num_elements (a) + 1);
+       i = 0;
+       for (ae = element_forw(a->head); ae != a->head; ae = element_forw(ae)) {
+               t = element_value (ae);
+               ret[i++] = t ? savestring (t) : (char *)NULL;
+       }
+       ret[i] = (char *)NULL;
+       return (ret);
+}
+       
 /*
- * Return a string that is the concatenation of all the elements in A,
- * separated by SEP.
+ * Return a string that is the concatenation of the elements in A from START
+ * to END, separated by SEP.
  */
 static char *
 array_to_string_internal (start, end, sep, quoted)
@@ -283,9 +826,10 @@ int        quoted;
                return ((char *)NULL);
 
        slen = strlen(sep);
+       result = NULL;
        for (rsize = rlen = 0, ae = start; ae != end; ae = element_forw(ae)) {
                if (rsize == 0)
-                       result = xmalloc (rsize = 64);
+                       result = (char *)xmalloc (rsize = 64);
                if (element_value(ae)) {
                        t = quoted ? quote_string(element_value(ae)) : element_value(ae);
                        reg = strlen(t);
@@ -293,7 +837,7 @@ int quoted;
                                                rsize, rsize);
                        strcpy(result + rlen, t);
                        rlen += reg;
-                       if (quoted && t)
+                       if (quoted)
                                free(t);
                        /*
                         * Add a separator only after non-null elements.
@@ -304,48 +848,41 @@ int       quoted;
                        }
                }
        }
-       result[rlen] = '\0';    /* XXX */
+       if (result)
+         result[rlen] = '\0';  /* XXX */
        return(result);
 }
 
 char *
-array_to_string (a, sep, quoted)
+array_to_assign (a, quoted)
 ARRAY  *a;
-char   *sep;
 int    quoted;
 {
-       if (a == 0)
-               return((char *)NULL);
-       if (array_empty(a))
-               return(savestring(""));
-       return (array_to_string_internal (element_forw(a->head), a->head, sep, quoted));
-}
-
-char *
-array_to_assignment_string (a)
-ARRAY  *a;
-{
-       char    *result, *indstr, *valstr;
+       char    *result, *valstr, *is;
+       char    indstr[INT_STRLEN_BOUND(intmax_t) + 1];
        ARRAY_ELEMENT *ae;
        int     rsize, rlen, elen;
 
        if (a == 0 || array_empty (a))
                return((char *)NULL);
 
-       result = xmalloc (rsize = 128);
+       result = (char *)xmalloc (rsize = 128);
        result[0] = '(';
        rlen = 1;
 
        for (ae = element_forw(a->head); ae != a->head; ae = element_forw(ae)) {
-               indstr = itos (element_index(ae));
-               valstr = element_value (ae) ? double_quote (element_value(ae))
+               is = inttostr (element_index(ae), indstr, sizeof(indstr));
+               valstr = element_value (ae) ?
+                               (ansic_shouldquote (element_value (ae)) ?
+                                  ansic_quote (element_value(ae), 0, (int *)0) :
+                                  sh_double_quote (element_value (ae)))
                                            : (char *)NULL;
-               elen = STRLEN (indstr) + 8 + STRLEN (valstr);
+               elen = STRLEN (is) + 8 + STRLEN (valstr);
                RESIZE_MALLOCED_BUFFER (result, rlen, (elen + 1), rsize, rsize);
 
                result[rlen++] = '[';
-               strcpy (result + rlen, indstr);
-               rlen += STRLEN (indstr);
+               strcpy (result + rlen, is);
+               rlen += STRLEN (is);
                result[rlen++] = ']';
                result[rlen++] = '=';
                if (valstr) {
@@ -356,52 +893,39 @@ ARRAY     *a;
                if (element_forw(ae) != a->head)
                  result[rlen++] = ' ';
 
-               FREE (indstr);
                FREE (valstr);
        }
        RESIZE_MALLOCED_BUFFER (result, rlen, 1, rsize, 8);
        result[rlen++] = ')';
        result[rlen] = '\0';
+       if (quoted) {
+               /* This is not as efficient as it could be... */
+               valstr = sh_single_quote (result);
+               free (result);
+               result = valstr;
+       }
        return(result);
 }
 
 char *
-quoted_array_assignment_string (a)
+array_to_string (a, sep, quoted)
 ARRAY  *a;
+char   *sep;
+int    quoted;
 {
-       char *vstr, *sv;
-
-       sv = array_to_assignment_string (a);
-       if (sv == 0)
-               return ((char *)NULL);
-
-       vstr = single_quote (sv);
-       free (sv);
-       return (vstr);
-}
-
-#if 0
-/* Determine if s2 occurs in s1.  If so, return a pointer to the
-   match in s1.  The compare is case sensitive. */
-static char *
-sindex (s1, s2)
-register char *s1, *s2;
-{
-       register int i, l, len;
-
-       for (i = 0, l = strlen(s2), len = strlen(s1); (len - i) >= l; i++)
-               if (strncmp (s1 + i, s2, l) == 0)
-                       return (s1 + i);
-       return ((char *)NULL);
+       if (a == 0)
+               return((char *)NULL);
+       if (array_empty(a))
+               return(savestring(""));
+       return (array_to_string_internal (element_forw(a->head), a->head, sep, quoted));
 }
-#endif
 
 #if defined (INCLUDE_UNUSED) || defined (TEST_ARRAY)
 /*
  * Return an array consisting of elements in S, separated by SEP
  */
 ARRAY *
-string_to_array(s, sep)
+array_from_string(s, sep)
 char   *s, *sep;
 {
        ARRAY   *a;
@@ -412,196 +936,218 @@ char    *s, *sep;
        w = list_string (s, sep, 0);
        if (w == 0)
                return((ARRAY *)NULL);
-       a = word_list_to_array (w);
+       a = array_from_word_list (w);
        return (a);
 }
 #endif
 
-/* Convenience routines for the shell to translate to and from the form used
-   by the rest of the code. */
-WORD_LIST *
-array_to_word_list(a)
-ARRAY  *a;
-{
-       WORD_LIST       *list;
-       ARRAY_ELEMENT   *ae;
+#if defined (TEST_ARRAY)
+/*
+ * To make a running version, compile -DTEST_ARRAY and link with:
+ *     xmalloc.o syntax.o lib/malloc/libmalloc.a lib/sh/libsh.a
+ */
+int interrupt_immediately = 0;
 
-       if (a == 0 || array_empty(a))
-               return((WORD_LIST *)NULL);
-       list = (WORD_LIST *)NULL;
-       for (ae = element_forw(a->head); ae != a->head; ae = element_forw(ae))
-               list = make_word_list (make_bare_word(element_value(ae)), list);
-       return (REVERSE_LIST(list, WORD_LIST *));
+int
+signal_is_trapped(s)
+int    s;
+{
+       return 0;
 }
 
-ARRAY *
-assign_word_list (array, list)
-ARRAY  *array;
-WORD_LIST      *list;
+void
+fatal_error(const char *s, ...)
 {
-       register WORD_LIST *l;
-       register arrayind_t i;
-
-       for (l = list, i = 0; l; l = l->next, i++)
-               array_add_element(array, i, l->word->word);
-       return array;
+       fprintf(stderr, "array_test: fatal memory error\n");
+       abort();
 }
 
-ARRAY *
-word_list_to_array (list)
-WORD_LIST      *list;
+void
+programming_error(const char *s, ...)
 {
-       ARRAY   *a;
-
-       if (list == 0)
-               return((ARRAY *)NULL);
-       a = new_array();
-       return (assign_word_list (a, list));
+       fprintf(stderr, "array_test: fatal programming error\n");
+       abort();
 }
 
-ARRAY  *
-array_quote(array)
-ARRAY  *array;
+WORD_DESC *
+make_bare_word (s)
+const char     *s;
 {
-       ARRAY_ELEMENT   *a;
-       char    *t;
+       WORD_DESC *w;
 
-       if (array == 0 || array->head == 0 || array_empty (array))
-               return (ARRAY *)NULL;
-       for (a = element_forw(array->head); a != array->head; a = element_forw(a)) {
-               t = quote_string (a->value);
-               FREE(a->value);
-               a->value = t;
-       }
-       return array;
+       w = (WORD_DESC *)xmalloc(sizeof(WORD_DESC));
+       w->word = s ? savestring(s) : savestring ("");
+       w->flags = 0;
+       return w;
 }
 
-char *
-array_subrange (a, start, end, quoted)
-ARRAY  *a;
-int    start, end, quoted;
+WORD_LIST *
+make_word_list(x, l)
+WORD_DESC      *x;
+WORD_LIST      *l;
 {
-       ARRAY_ELEMENT   *h, *p;
-       int     i;
+       WORD_LIST *w;
 
-       p = array_head (a);
-       if (p == 0 || array_empty (a) || start > array_num_elements (a))
-               return ((char *)NULL);
+       w = (WORD_LIST *)xmalloc(sizeof(WORD_LIST));
+       w->word = x;
+       w->next = l;
+       return w;
+}
 
-       for (i = 0, p = element_forw(p); p != a->head && i < start; i++, p = element_forw(p))
-               ;
-       if (p == a->head)
-               return ((char *)NULL);
-       for (h = p; p != a->head && i < end; i++, p = element_forw(p))
-               ;
+WORD_LIST *
+list_string(s, t, i)
+char   *s, *t;
+int    i;
+{
+       char    *r, *a;
+       WORD_LIST       *wl;
 
-       return (array_to_string_internal (h, p, " ", quoted));
+       if (s == 0)
+               return (WORD_LIST *)NULL;
+       r = savestring(s);
+       wl = (WORD_LIST *)NULL;
+       a = strtok(r, t);
+       while (a) {
+               wl = make_word_list (make_bare_word(a), wl);
+               a = strtok((char *)NULL, t);
+       }
+       return (REVERSE_LIST (wl, WORD_LIST *));
 }
 
-char *
-array_pat_subst (a, pat, rep, mflags)
-ARRAY  *a;
-char   *pat, *rep;
-int    mflags;
+GENERIC_LIST *
+list_reverse (list)
+GENERIC_LIST   *list;
 {
-       ARRAY           *a2;
-       ARRAY_ELEMENT   *e;
-       char    *t;
+       register GENERIC_LIST *next, *prev;
 
-       if (array_head (a) == 0 || array_empty (a))
-               return ((char *)NULL);
-
-       a2 = dup_array (a);
-       for (e = element_forw(a2->head); e != a2->head; e = element_forw(e)) {
-               t = pat_subst(element_value(e), pat, rep, mflags);
-               FREE(element_value(e));
-               e->value = t;
+       for (prev = 0; list; ) {
+               next = list->next;
+               list->next = prev;
+               prev = list;
+               list = next;
        }
+       return prev;
+}
 
-       if (mflags & MATCH_QUOTED)
-               array_quote (a2);
-       t = array_to_string (a2, " ", 0);
-       dispose_array (a2);
-
-       return t;
+char *
+pat_subst(s, t, u, i)
+char   *s, *t, *u;
+int    i;
+{
+       return ((char *)NULL);
 }
 
+char *
+quote_string(s)
+char   *s;
+{
+       return savestring(s);
+}
 
-#if defined (TEST_ARRAY)
 print_element(ae)
 ARRAY_ELEMENT  *ae;
 {
-       printf("array[%d] = %s\n",(int)element_index(ae), element_value(ae));
+       char    lbuf[INT_STRLEN_BOUND (intmax_t) + 1];
+
+       printf("array[%s] = %s\n",
+               inttostr (element_index(ae), lbuf, sizeof (lbuf)),
+               element_value(ae));
 }
 
 print_array(a)
 ARRAY  *a;
 {
        printf("\n");
-       array_walk(a, print_element);
+       array_walk(a, print_element, (void *)NULL);
 }
 
 main()
 {
        ARRAY   *a, *new_a, *copy_of_a;
-       ARRAY_ELEMENT   *ae;
+       ARRAY_ELEMENT   *ae, *aew;
        char    *s;
 
-       a = new_array();
-       array_add_element(a, 1, "one");
-       array_add_element(a, 7, "seven");
-       array_add_element(a, 4, "four");
-       array_add_element(a, 1029, "one thousand twenty-nine");
-       array_add_element(a, 12, "twelve");
-       array_add_element(a, 42, "forty-two");
+       a = array_create();
+       array_insert(a, 1, "one");
+       array_insert(a, 7, "seven");
+       array_insert(a, 4, "four");
+       array_insert(a, 1029, "one thousand twenty-nine");
+       array_insert(a, 12, "twelve");
+       array_insert(a, 42, "forty-two");
        print_array(a);
        s = array_to_string (a, " ", 0);
        printf("s = %s\n", s);
-       copy_of_a = string_to_array(s, " ");
+       copy_of_a = array_from_string(s, " ");
        printf("copy_of_a:");
        print_array(copy_of_a);
-       dispose_array(copy_of_a);
+       array_dispose(copy_of_a);
        printf("\n");
        free(s);
-       ae = array_delete_element(a, 4);
-       destroy_array_element(ae);
-       ae = array_delete_element(a, 1029);
-       destroy_array_element(ae);
-       array_add_element(a, 16, "sixteen");
+       ae = array_remove(a, 4);
+       array_dispose_element(ae);
+       ae = array_remove(a, 1029);
+       array_dispose_element(ae);
+       array_insert(a, 16, "sixteen");
        print_array(a);
        s = array_to_string (a, " ", 0);
        printf("s = %s\n", s);
-       copy_of_a = string_to_array(s, " ");
+       copy_of_a = array_from_string(s, " ");
        printf("copy_of_a:");
        print_array(copy_of_a);
-       dispose_array(copy_of_a);
+       array_dispose(copy_of_a);
        printf("\n");
        free(s);
-       array_add_element(a, 2, "two");
-       array_add_element(a, 1029, "new one thousand twenty-nine");
-       array_add_element(a, 0, "zero");
-       array_add_element(a, 134, "");
+       array_insert(a, 2, "two");
+       array_insert(a, 1029, "new one thousand twenty-nine");
+       array_insert(a, 0, "zero");
+       array_insert(a, 134, "");
        print_array(a);
        s = array_to_string (a, ":", 0);
        printf("s = %s\n", s);
-       copy_of_a = string_to_array(s, ":");
+       copy_of_a = array_from_string(s, ":");
        printf("copy_of_a:");
        print_array(copy_of_a);
-       dispose_array(copy_of_a);
+       array_dispose(copy_of_a);
        printf("\n");
        free(s);
-       new_a = copy_array(a);
+       new_a = array_copy(a);
        print_array(new_a);
        s = array_to_string (new_a, ":", 0);
        printf("s = %s\n", s);
-       copy_of_a = string_to_array(s, ":", 0);
+       copy_of_a = array_from_string(s, ":");
+       free(s);
        printf("copy_of_a:");
        print_array(copy_of_a);
-       dispose_array(copy_of_a);
-       printf("\n");
+       array_shift(copy_of_a, 2, AS_DISPOSE);
+       printf("copy_of_a shifted by two:");
+       print_array(copy_of_a);
+       ae = array_shift(copy_of_a, 2, 0);
+       printf("copy_of_a shifted by two:");
+       print_array(copy_of_a);
+       for ( ; ae; ) {
+               aew = element_forw(ae);
+               array_dispose_element(ae);
+               ae = aew;
+       }
+       array_rshift(copy_of_a, 1, (char *)0);
+       printf("copy_of_a rshift by 1:");
+       print_array(copy_of_a);
+       array_rshift(copy_of_a, 2, "new element zero");
+       printf("copy_of_a rshift again by 2 with new element zero:");
+       print_array(copy_of_a);
+       s = array_to_assign(copy_of_a, 0);
+       printf("copy_of_a=%s\n", s);
        free(s);
-       dispose_array(a);
-       dispose_array(new_a);
+       ae = array_shift(copy_of_a, array_num_elements(copy_of_a), 0);
+       for ( ; ae; ) {
+               aew = element_forw(ae);
+               array_dispose_element(ae);
+               ae = aew;
+       }
+       array_dispose(copy_of_a);
+       printf("\n");
+       array_dispose(a);
+       array_dispose(new_a);
 }
 
 #endif /* TEST_ARRAY */