]> git.ipfire.org Git - thirdparty/bash.git/blobdiff - array.c
Bash-4.3 patch 32
[thirdparty/bash.git] / array.c
diff --git a/array.c b/array.c
index 7b0a5d7ba1159e71025b1d05b2c903a3902a9581..4359a52fa1d88e4d52fcff439092bb89950e9084 100644 (file)
--- a/array.c
+++ b/array.c
@@ -9,23 +9,23 @@
  * chet@ins.cwru.edu
  */
 
-/* Copyright (C) 1997-2004 Free Software Foundation, Inc.
+/* 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 2, or (at your option) any later
-   version.
+   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.
+   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; see the file COPYING.  If not, write to the Free Software
-   Foundation, 59 Temple Place, Suite 330, Boston, MA 02111 USA. */
+   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"
 
 
 static char *array_to_string_internal __P((ARRAY_ELEMENT *, ARRAY_ELEMENT *, char *, int));
 
+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 *
 array_create()
 {
@@ -87,6 +116,7 @@ ARRAY        *a;
        a->head->next = a->head->prev = a->head;
        a->max_index = -1;
        a->num_elements = 0;
+       INVALIDATE_LASTREF(a);
 }
 
 void
@@ -107,7 +137,7 @@ ARRAY       *a;
        ARRAY   *a1;
        ARRAY_ELEMENT   *ae, *new;
 
-       if (!a)
+       if (a == 0)
                return((ARRAY *) NULL);
        a1 = array_create();
        a1->type = a->type;
@@ -120,7 +150,6 @@ ARRAY       *a;
        return(a1);
 }
 
-#ifdef INCLUDE_UNUSED
 /*
  * Make and return a new array composed of the elements in array A from
  * S to E, inclusive.
@@ -138,16 +167,15 @@ ARRAY_ELEMENT     *s, *e;
        a = array_create ();
        a->type = array->type;
 
-       for (p = s, i = 0; p != e; p = element_forw(p), i++) {
+       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(ae);
+               mi = element_index(n);
        }
        a->num_elements = i;
        a->max_index = mi;
        return a;
 }
-#endif
 
 /*
  * Walk the array, calling FUNC once for each element, with the array
@@ -187,6 +215,7 @@ int n, flags;
        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) {
@@ -216,7 +245,7 @@ int n, flags;
                element_index(ae) -= n; /* renumber retained indices */
 
        a->num_elements -= n;           /* modify bookkeeping information */
-       a->max_index -= n;
+       a->max_index = element_index(a->head->prev);
 
        if (flags & AS_DISPOSE) {
                for (ae = ret; ae; ) {
@@ -243,9 +272,9 @@ char        *s;
 {
        register ARRAY_ELEMENT  *ae, *new;
 
-       if (a == 0)
+       if (a == 0 || (array_empty(a) && s == 0))
                return 0;
-       if (n <= 0)
+       else if (n <= 0)
                return (a->num_elements);
 
        ae = element_forw(a->head);
@@ -253,16 +282,21 @@ char      *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;
+               }
        }
 
-       a->max_index += n;
-
        /*
         * 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);
 }
 
@@ -281,14 +315,14 @@ char      *v;
        return (array_rshift (a, 1, v));
 }
 
-ARRAY  *
+ARRAY *
 array_quote(array)
 ARRAY  *array;
 {
        ARRAY_ELEMENT   *a;
        char    *t;
 
-       if (array == 0 || array->head == 0 || array_empty (array))
+       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);
@@ -298,6 +332,71 @@ ARRAY      *array;
        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.
@@ -309,11 +408,13 @@ ARRAY     *a;
 arrayind_t     start, nelem;
 int    starsub, quoted;
 {
+       ARRAY           *a2;
        ARRAY_ELEMENT   *h, *p;
        arrayind_t      i;
-       char            *ifs, sep[2];
+       char            *ifs, *sifs, *t;
+       int             slen;
 
-       p = array_head (a);
+       p = a ? array_head (a) : 0;
        if (p == 0 || array_empty (a) || start > array_max_index(a))
                return ((char *)NULL);
 
@@ -334,14 +435,36 @@ int       starsub, quoted;
        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))) {
-               ifs = getifs();
-               sep[0] = ifs ? *ifs : '\0';
+               /* ${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
-               sep[0] = ' ';
-       sep[1] = '\0';
+               t = array_to_string (a2, " ", 0);
+       array_dispose(a2);
 
-       return (array_to_string_internal (h, p, sep, quoted));
+       return t;
 }
 
 char *
@@ -352,12 +475,13 @@ int       mflags;
 {
        ARRAY           *a2;
        ARRAY_ELEMENT   *e;
-       char    *t, *ifs, sifs[2];
+       char    *t, *sifs, *ifs;
+       int     slen;
 
-       if (array_head (a) == 0 || array_empty (a))
+       if (a == 0 || array_head(a) == 0 || array_empty(a))
                return ((char *)NULL);
 
-       a2 = array_copy (a);
+       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));
@@ -365,12 +489,27 @@ int       mflags;
        }
 
        if (mflags & MATCH_QUOTED)
-               array_quote (a2);
+               array_quote(a2);
+       else
+               array_quote_escapes(a2);
+
        if (mflags & MATCH_STARSUB) {
-               ifs = getifs();
-               sifs[0] = ifs ? *ifs : '\0';
-               sifs[1] = '\0';
+               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);
@@ -378,6 +517,56 @@ int        mflags;
        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.
@@ -425,9 +614,9 @@ 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 = array_create_element(i, v);
        if (i > array_max_index(a)) {
@@ -439,26 +628,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.
                         */
                        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 */
 }
 
@@ -471,17 +672,28 @@ 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);
@@ -495,13 +707,19 @@ 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();
        return((char *) NULL);
 }
 
@@ -588,8 +806,8 @@ ARRAY       *a;
 }
        
 /*
- * 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)
@@ -653,7 +871,7 @@ int quoted;
                is = inttostr (element_index(ae), indstr, sizeof(indstr));
                valstr = element_value (ae) ? 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++] = '[';