gawk-diffs
[Top][All Lists]
Advanced

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

[gawk-diffs] [SCM] gawk branch, master, updated. bcb8bd6d6671a3400c4bfe3


From: Arnold Robbins
Subject: [gawk-diffs] [SCM] gawk branch, master, updated. bcb8bd6d6671a3400c4bfe3e34eb4d5d66050f32
Date: Mon, 18 Apr 2011 07:24:24 +0000

This is an automated email from the git hooks/post-receive script. It was
generated because a ref change was pushed to the repository containing
the project "gawk".

The branch, master has been updated
       via  bcb8bd6d6671a3400c4bfe3e34eb4d5d66050f32 (commit)
       via  98eb78c2d05870952fe25bbe37b86df2017f42fc (commit)
       via  0398ef5eaa6a074cf6dfdeae7972319900604061 (commit)
      from  b50ab48160736da057107f5565c68c7e17877793 (commit)

Those revisions listed above that are new to this repository have
not appeared on any other notification email; so we list those
revisions in full, below.

- Log -----------------------------------------------------------------
http://git.sv.gnu.org/cgit/gawk.git/commit/?id=bcb8bd6d6671a3400c4bfe3e34eb4d5d66050f32

commit bcb8bd6d6671a3400c4bfe3e34eb4d5d66050f32
Author: Arnold D. Robbins <address@hidden>
Date:   Mon Apr 18 10:23:54 2011 +0300

    More array sorting changes from John.

diff --git a/ChangeLog b/ChangeLog
index 4e6a2d8..ffe1ad5 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,3 +1,31 @@
+Mon Apr 18 10:18:26 2011  John Haque      <address@hidden>
+
+       * array.c (assoc_list): New function to construct, and optionally
+       sort, a list of array elements.
+       (asort_actual): Use the new function to sort array elements.
+       (assoc_sort_inplace, assoc_from_list, merge_sort, merge): Nuked.
+       (sort_selection): Simplify handling of error and warning messages.
+       (sorted_in, sort_match): Nuked, related code in sort_selection() and
+       assoc_list().
+       (sort_ignorecase, sort_up_index_ignrcase, sort_down_index_ignrcase,
+       sort_maybe_numeric_index, sort_cmp_nodes, cmp_func, sort_up_value,
+       sort_down_value): Nuked. Ignorecase handling done in the corresponding
+       non-ignorecase versions.
+       (cmp_string): New routine for string comparisons.
+       (sort_up_value_string, sort_down_value_string, sort_up_value_number,
+       sort_down_value_number, sort_force_index_number,
+       sort_force_value_number, sort_force_value_string): New routines.
+       * awk.h (struct exp_node): New field sub.hash.num to store the
+       numeric value of an array index.
+       (ahname_num): New define.
+       (SORT_CTXT): New typedef.
+       * awkgram.y (tokentab): Accept three args for asort() and asorti().
+       (snode): Adjust for the extra args.
+       * eval.c (r_interpret): In case Op_arrayfor_init, call assoc_list()
+       for a list of array elements.
+       * debug.c (print_array): Call assoc_list() for a sorted list of array
+       elements.
+
 Wed Apr 13 10:17:37 2011  John Haque      <address@hidden>
 
        * builtin.c (do_strftime): Make the third argument to strftime
diff --git a/array.c b/array.c
index 5d1c588..15053bb 100644
--- a/array.c
+++ b/array.c
@@ -54,24 +54,16 @@ static unsigned long awk_hash(const char *s, size_t len, 
unsigned long hsize, si
 
 unsigned long (*hash)(const char *s, size_t len, unsigned long hsize, size_t 
*code) = awk_hash;
 
-/* used by sort_maybe_numeric_index() and sort_up_index_number() */
-struct sort_num { AWKNUM sort_numbr; NODE *sort_index; };
-
-/* sorting support code */
-static qsort_compfunc sort_selection(NODE *, const char *, int);
-static size_t sort_match(const char *, size_t, const char *);
-static int sort_ignorecase(const char *, size_t, const char *, size_t);
-static int sort_cmp_nodes(NODE *, NODE *);
-
-/* qsort callback routines */
+/* qsort comparison function */
 static int sort_up_index_string(const void *, const void *);
 static int sort_down_index_string(const void *, const void *);
-static int sort_up_index_ignrcase(const void *, const void *);
-static int sort_down_index_ignrcase(const void *, const void *);
 static int sort_up_index_number(const void *, const void *);
 static int sort_down_index_number(const void *, const void *);
-static int sort_up_value(const void *, const void *);
-static int sort_down_value(const void *, const void *);
+static int sort_up_value_string(const void *, const void *);
+static int sort_down_value_string(const void *, const void *);
+static int sort_up_value_number(const void *, const void *);
+static int sort_down_value_number(const void *, const void *);
+
 
 /* array_init --- possibly temporary function for experimentation purposes */
 
@@ -951,9 +943,9 @@ do_adump(int nargs)
  * address@hidden
  */
 
-/* dup_table --- duplicate input symbol table "symbol" */
+/* dup_table --- recursively duplicate input array "symbol" */
 
-static void
+static NODE *
 dup_table(NODE *symbol, NODE *newsymb)
 {
        NODE **old, **new, *chain, *bucket;
@@ -986,6 +978,7 @@ dup_table(NODE *symbol, NODE *newsymb)
                                        bucket->type = Node_ahash;
                                        bucket->flags |= MALLOC;
                                        bucket->ahname_ref = 1;
+                                       bucket->ahcode = chain->ahcode;
 
                                        /*
                                         * copy the corresponding name and
@@ -1007,8 +1000,7 @@ dup_table(NODE *symbol, NODE *newsymb)
                                                emalloc(aname, char *, 
aname_len + 2, "dup_table");
                                                sprintf(aname, "%s[\"%.*s\"]", 
newsymb->vname, (int) chain->ahname_len, chain->ahname_str);
                                                r->vname = aname;
-                                               dup_table(chain->ahvalue, r);
-                                               bucket->ahvalue = r;
+                                               bucket->ahvalue = 
dup_table(chain->ahvalue, r);
                                        } else
                                                bucket->ahvalue = 
dupnode(chain->ahvalue);
 
@@ -1026,237 +1018,30 @@ dup_table(NODE *symbol, NODE *newsymb)
 
        newsymb->var_array = new;
        newsymb->array_size = cursize;
-}
-
-/* merge --- do a merge of two sorted lists */
-
-static NODE *
-merge(NODE *left, NODE *right)
-{
-       NODE *ans, *cur;
-
-       /*
-        * Use sort_cmp_nodes(): see the comment there as to why. That
-        * function will call cmp_nodes() on strings, which means that
-        * IGNORECASE influences the comparison.  This is OK, but it may
-        * be surprising.  This comment serves to remind us that we
-        * know about this and that it's OK.
-        */
-       if (sort_cmp_nodes(left->ahvalue, right->ahvalue) <= 0) {
-               ans = cur = left;
-               left = left->ahnext;
-       } else {
-               ans = cur = right;
-               right = right->ahnext;
-       }
-
-       while (left != NULL && right != NULL) {
-               if (cmp_nodes(left->ahvalue, right->ahvalue) <= 0) {
-                       cur->ahnext = left;
-                       cur = left;
-                       left  = left->ahnext;
-               } else {
-                       cur->ahnext = right;
-                       cur = right;
-                       right = right->ahnext;
-               }
-       }
-
-       cur->ahnext = (left != NULL ? left : right);
-
-       return ans;
-}
-
-/* merge_sort --- recursively sort the left and right sides of a list */
-
-static NODE *
-merge_sort(NODE *left, unsigned long size)
-{
-       NODE *right, *tmp;
-       int i, half;
-
-       if (size <= 1)
-               return left;
-
-       /* walk down the list, till just one before the midpoint */
-       tmp = left;
-       half = size / 2;
-       for (i = 0; i < half-1; i++)
-               tmp = tmp->ahnext;
-
-       /* split the list into two parts */
-       right = tmp->ahnext;
-       tmp->ahnext = NULL;
-
-       /* sort the left and right parts of the list */
-       left  = merge_sort(left,       half);
-       right = merge_sort(right, size-half);
-
-       /* merge the two sorted parts of the list */
-       return merge(left, right);
-}
-
-
-/*
- * assoc_from_list -- Populate an array with the contents of a list of NODEs, 
- * using increasing integers as the key.
- */
-
-static void
-assoc_from_list(NODE *symbol, NODE *list)
-{
-       NODE *next;
-       unsigned long i = 0;
-       unsigned long hash1;
-       char buf[100];
-
-       for (; list != NULL; list = next) {
-               size_t code;
-
-               next = list->ahnext;
-
-               /* make an int out of i++ */
-               i++;
-               sprintf(buf, "%lu", i);
-               assert(list->ahname_str == NULL);
-               assert(list->ahname_ref == 1);
-               emalloc(list->ahname_str, char *, strlen(buf) + 2, 
"assoc_from_list");
-               list->ahname_len = strlen(buf);
-               strcpy(list->ahname_str, buf);
-
-               /* find the bucket where it belongs */
-               hash1 = hash(list->ahname_str, list->ahname_len,
-                               symbol->array_size, & code);
-               list->ahcode = code;
-
-               /* link the node into the chain at that bucket */
-               list->ahnext = symbol->var_array[hash1];
-               symbol->var_array[hash1] = list;
-       }
-}
-
-/*
- * assoc_sort_inplace --- sort all the values in symbol[], replacing
- * the sorted values back into symbol[], indexed by integers starting with 1.
- */
-
-typedef enum asort_how { VALUE, INDEX } ASORT_TYPE;
-
-static NODE *
-assoc_sort_inplace(NODE *symbol, ASORT_TYPE how)
-{
-       unsigned long i, num;
-       NODE *bucket, *next, *list;
-
-       if (symbol->var_array == NULL
-           || symbol->array_size <= 0
-           || symbol->table_size <= 0)
-               return make_number((AWKNUM) 0);
-
-       /* build a linked list out of all the entries in the table */
-       if (how == VALUE) {
-               list = NULL;
-               num = 0;
-               for (i = 0; i < symbol->array_size; i++) {
-                       for (bucket = symbol->var_array[i]; bucket != NULL; 
bucket = next) {
-                               if (bucket->ahvalue->type == Node_var_array)
-                                       fatal(_("attempt to use array in a 
scalar context"));
-                               next = bucket->ahnext;
-                               if (bucket->ahname_ref == 1) {
-                                       efree(bucket->ahname_str);
-                                       bucket->ahname_str = NULL;
-                                       bucket->ahname_len = 0;
-                               } else {
-                                       NODE *r;
-
-                                       getnode(r);
-                                       *r = *bucket;
-                                       ahash_unref(bucket);
-                                       bucket = r;
-                                       bucket->flags |= MALLOC;
-                                       bucket->ahname_ref = 1;
-                                       bucket->ahname_str = NULL;
-                                       bucket->ahname_len = 0;
-                               }
-                               bucket->ahnext = list;
-                               list = bucket;
-                               num++;
-                       }
-                       symbol->var_array[i] = NULL;
-               }
-       } else {        /* how == INDEX */
-               list = NULL;
-               num = 0;
-               for (i = 0; i < symbol->array_size; i++) {
-                       for (bucket = symbol->var_array[i]; bucket != NULL; 
bucket = next) {
-                               next = bucket->ahnext;
-
-                               /* toss old value */
-                               if (bucket->ahvalue->type == Node_var_array) {
-                                       NODE *r = bucket->ahvalue;
-                                       assoc_clear(r);
-                                       efree(r->vname);
-                                       freenode(r);
-                               } else
-                                       unref(bucket->ahvalue);
-
-                               /* move index into value */
-                               if (bucket->ahname_ref == 1) {
-                                       bucket->ahvalue = 
make_str_node(bucket->ahname_str,
-                                                               
bucket->ahname_len, ALREADY_MALLOCED);
-                                       bucket->ahname_str = NULL;
-                                       bucket->ahname_len = 0;
-                               } else {
-                                       NODE *r;
-
-                                       bucket->ahvalue = 
make_string(bucket->ahname_str, bucket->ahname_len);
-                                       getnode(r);
-                                       *r = *bucket;
-                                       ahash_unref(bucket);
-                                       bucket = r;
-                                       bucket->flags |= MALLOC;
-                                       bucket->ahname_ref = 1;
-                                       bucket->ahname_str = NULL;
-                                       bucket->ahname_len = 0;
-                               }
-
-                               bucket->ahnext = list;
-                               list = bucket;
-                               num++;
-                       }
-                       symbol->var_array[i] = NULL;
-               }
-       }
-
-       /*
-        * Sort the linked list of NODEs.
-        * (The especially nice thing about using a merge sort here is that
-        * we require absolutely no additional storage. This is handy if the
-        * array has grown to be very large.)
-        */
-       list = merge_sort(list, num);
-
-       /*
-        * now repopulate the original array, using increasing
-        * integers as the key
-        */
-       assoc_from_list(symbol, list);
-
-       return make_number((AWKNUM) num);
+       newsymb->flags = symbol->flags; /* ARRAYMAXED */
+       return newsymb;
 }
 
 /* asort_actual --- do the actual work to sort the input array */
 
 static NODE *
-asort_actual(int nargs, ASORT_TYPE how)
+asort_actual(int nargs, SORT_CTXT ctxt)
 {
-       NODE *dest = NULL;
-       NODE *array;
-
-       if (nargs == 2) {  /* 2nd optional arg */
+       NODE *array, *dest = NULL, *result;
+       NODE *r, *subs, *sort_str;
+       NODE **list, **ptr;
+       static char buf[102];
+       unsigned long num_elems, i;
+
+       if (nargs == 3)  /* 3rd optional arg */
+               sort_str = POP_STRING();
+       else
+               sort_str = Nnull_string;        /* "" => default sorting */
+ 
+       if (nargs >= 2) {  /* 2nd optional arg */
                dest = POP_PARAM();
                if (dest->type != Node_var_array) {
-                       fatal(how == VALUE ?
+                       fatal(ctxt == ASORT ?
                                _("asort: second argument not an array") :
                                _("asorti: second argument not an array"));
                }
@@ -1264,18 +1049,89 @@ asort_actual(int nargs, ASORT_TYPE how)
 
        array = POP_PARAM();
        if (array->type != Node_var_array) {
-               fatal(how == VALUE ?
+               fatal(ctxt == ASORT ?
                        _("asort: first argument not an array") :
                        _("asorti: first argument not an array"));
        }
 
+       num_elems = array->table_size;
+       if (num_elems == 0 || array->var_array == NULL) {       /* source array 
is empty */
+               if (dest != NULL && dest != array)
+                       assoc_clear(dest);
+               return make_number((AWKNUM) 0);
+       }
+
+       list = assoc_list(array, sort_str, ctxt);
+       DEREF(sort_str);
+
+       /* Must not assoc_clear() the source array before constructing
+        * the output array. assoc_list() does not duplicate array values
+        * which are needed for asort().
+        */
+
        if (dest != NULL && dest != array) {
                assoc_clear(dest);
-               dup_table(array, dest);
-               array = dest;
+               result = dest;
+       } else {
+               /* use 'result' as a temporary destination array */
+               getnode(result);
+               memset(result, '\0', sizeof(NODE));
+               result->type = Node_var_array;
+               result->vname = array->vname;
        }
 
-       return assoc_sort_inplace(array, how);
+       subs = make_str_node(buf, 100, ALREADY_MALLOCED);   /* fake it */
+       subs->flags &= ~MALLOC;         /* safety */
+       for (i = 1, ptr = list; i <= num_elems; i++) {
+               sprintf(buf, "%lu", i);
+               subs->stlen = strlen(buf);
+               r = *ptr++;
+               if (ctxt == ASORTI) {
+                       /* We want the indices of the source array as values
+                        * of the 'result' array.
+                        */
+
+                       *assoc_lookup(result, subs, FALSE) =
+                                               make_string(r->ahname_str, 
r->ahname_len);
+               } else {
+                       NODE *val;
+
+                       /* We want the values of the source array. */
+
+                       val = r->ahvalue;
+                       if (val->type == Node_val)
+                               *assoc_lookup(result, subs, FALSE) = 
dupnode(val);
+                       else {
+                               const char *arr_name = make_aname(result, subs);
+                               NODE *arr;
+
+                               /* there isn't any reference counting for 
subarrays, so
+                                * recursively copy subarrays using dup_table().
+                                */
+                               getnode(arr);
+                               arr->type = Node_var_array;
+                               arr->var_array = NULL;
+                               arr->vname = estrdup(arr_name, 
strlen(arr_name));
+                               *assoc_lookup(result, subs, FALSE) = 
dup_table(val, arr);
+                       }
+               }
+
+               ahash_unref(r);
+       }
+
+       freenode(subs); /* stptr(buf) not malloc-ed */
+       efree(list);
+
+       if (result != dest) {
+               /* dest == NULL or dest == array */
+               assoc_clear(array);
+               *array = *result;       /* copy result into array */
+               freenode(result);
+       } /* else
+               result == dest
+               dest != NULL and dest != array */
+
+       return make_number((AWKNUM) num_elems);
 }
 
 /* do_asort --- sort array by value */
@@ -1283,7 +1139,7 @@ asort_actual(int nargs, ASORT_TYPE how)
 NODE *
 do_asort(int nargs)
 {
-       return asort_actual(nargs, VALUE);
+       return asort_actual(nargs, ASORT);
 }
 
 /* do_asorti --- sort array by index */
@@ -1291,127 +1147,83 @@ do_asort(int nargs)
 NODE *
 do_asorti(int nargs)
 {
-       return asort_actual(nargs, INDEX);
-}
-
-
-/* comp_func --- array index comparison function for qsort, used in debug.c */
-
-int
-comp_func(const void *p1, const void *p2)
-{
-       return sort_up_index_string(p1, p2);
+       return asort_actual(nargs, ASORTI);
 }
 
-/*
- * sort_cmp_nodes --- compare two nodes.  Unlike cmp_nodes(), we don't
- * mix numeric and string comparisons.  Numbers compare as less than
- * strings, which in turn compare as less than sub-arrays.  All
- * sub-arrays compare equal to each other, regardless of their contents.
+/* cmp_string --- compare two strings; logic similar to cmp_nodes() in eval.c
+ *     except the extra case-sensitive comparison when the case-insensitive 
result
+ *     is a match.
  */
 
 static int
-sort_cmp_nodes(NODE *n1, NODE *n2)
+cmp_string(const NODE *n1, const NODE *n2)
 {
+       char *s1, *s2;
+       size_t len1, len2;
        int ret;
-
-       if (n1->type == Node_var_array) {
-               /* return 0 if n2 is a sub-array too, else return 1 */
-               ret = (n2->type != Node_var_array);
-       } else if (n2->type == Node_var_array) {
-               ret = -1;       /* n1 (scalar) < n2 (sub-array) */
+       size_t lmin;
+
+       assert(n1->type == n2->type);
+       if (n1->type == Node_ahash) {
+               s1 = n1->ahname_str;
+               len1 = n1->ahname_len;
+               s2 =  n2->ahname_str;
+               len2 = n2->ahname_len;
        } else {
-               /*
-                * Both scalars; we can't settle for a regular
-                * MAYBE_NUM comparison because that can cause
-                * problems when strings fall between numbers
-                * whose value makes them be ordered differently
-                * when handled as strings than as numbers.
-                * For example, { 10, 5, "3D" } yields a cycle:
-                * 5 < 10, "10" < "3D", "3D" < "5", so would
-                * produce different sort results depending upon
-                * the order in which comparisons between pairs
-                * of values happen to be performed.  We force
-                * numbers to be less than strings in order to
-                * avoid that: 5 < 10, 10 < "3D", 5 < "3D".
-                */
-               /* first resolve any undecided types */
-               if (n1->flags & MAYBE_NUM)
-                       (void) force_number(n1);
-               if (n2->flags & MAYBE_NUM)
-                       (void) force_number(n2);
-               /*
-                * If both types are the same, use cmp_nodes();
-                * otherwise, force the numeric value to be less
-                * than the string one.
-                */
-               if ((n1->flags & NUMBER) == (n2->flags & NUMBER))
-                       ret = cmp_nodes(n1, n2);
-               else
-                       ret = (n1->flags & NUMBER) ? -1 : 1;
+               s1 = n1->stptr;
+               len1 = n1->stlen;
+               s2 =  n2->stptr;
+               len2 = n2->stlen;
        }
-       return ret;
-}
 
-/* sort_ignorecase --- case insensitive string comparison from cmp_nodes() */
+       if (len1 == 0)
+               return len2 == 0 ? 0 : -1;
+       if (len2 == 0)
+               return 1;
 
-static int
-sort_ignorecase(const char *s1, size_t l1, const char *s2, size_t l2)
-{
-       size_t l;
-       int ret;
+       /* len1 > 0 && len2 > 0 */
+       lmin = len1 < len2 ? len1 : len2;
+
+       if (IGNORECASE) {
+               const unsigned char *cp1 = (const unsigned char *) s1;
+               const unsigned char *cp2 = (const unsigned char *) s2;
 
-       l = (l1 < l2) ? l1 : l2;
 #ifdef MBS_SUPPORT
-       if (gawk_mb_cur_max > 1) {
-               ret = strncasecmpmbs((const unsigned char *) s1,
-                                    (const unsigned char *) s2, l);
-       } else
+               if (gawk_mb_cur_max > 1) {
+                       ret = strncasecmpmbs((const unsigned char *) cp1,
+                                            (const unsigned char *) cp2, lmin);
+               } else
 #endif
-       for (ret = 0; l-- > 0 && ret == 0; s1++, s2++)
-               ret = casetable[*(unsigned char *) s1]
-                     - casetable[*(unsigned char *) s2];
-       if (ret == 0 && l1 != l2)
-               ret = (l1 < l2) ? -1 : 1;
-       return ret;
+               for (ret = 0; lmin-- > 0 && ret == 0; cp1++, cp2++)
+                       ret = casetable[*cp1] - casetable[*cp2];
+               if (ret != 0)
+                       return ret;
+               /* if case insensitive result is "they're the same",
+                * use case sensitive comparison to force distinct order
+                */
+       }
+
+       ret = memcmp(s1, s2, lmin);
+       if (ret != 0 || len1 == len2)
+               return ret;
+       return (len1 < len2) ? -1 : 1;
 }
 
-/*
- * sort_up_index_string --- qsort comparison function; ascending index strings;
- * index strings are distinct within an array, so no comparisons will yield
- * equal and warrant disambiguation
- */
+
+/* sort_up_index_string --- qsort comparison function; ascending index 
strings. */
 
 static int
 sort_up_index_string(const void *p1, const void *p2)
 {
        const NODE *t1, *t2;
-       const char *str1, *str2;
-       size_t len1, len2;
-       int ret;
 
-       /* Array indexes are strings and distinct, never equal */
+       /* Array indexes are strings */
        t1 = *((const NODE *const *) p1);
        t2 = *((const NODE *const *) p2);
-
-       str1 = t1->ahname_str;
-       len1 = t1->ahname_len;
-       str2 = t2->ahname_str;
-       len2 = t2->ahname_len;
-
-       ret = memcmp(str1, str2, (len1 < len2) ? len1 : len2);
-       /*
-        * if they compared equal but their lengths differ, the
-        * shorter one is a prefix of the longer and compares as less
-        */
-       if (ret == 0 && len1 != len2)
-               ret = (len1 < len2) ? -1 : 1;
-
-       /* indices are unique within an array, so result should never be 0 */
-       assert(ret != 0);
-       return ret;
+       return cmp_string(t1, t2);
 }
 
+
 /* sort_down_index_string --- descending index strings */
 
 static int
@@ -1429,69 +1241,30 @@ sort_down_index_string(const void *p1, const void *p2)
        return -sort_up_index_string(p1, p2);
 }
 
-/*
- * sort_up_index_ignrcase --- ascending index string, case insensitive;
- * case insensitive comparison can cause distinct index strings to compare
- * equal, so disambiguation in necessary
- */
-
-static int
-sort_up_index_ignrcase(const void *p1, const void *p2)
-{
-       const NODE *t1, *t2;
-       int ret;
-
-       t1 = *((const NODE *const *) p1);
-       t2 = *((const NODE *const *) p2);
-
-       ret = sort_ignorecase(t1->ahname_str, t1->ahname_len,
-                             t2->ahname_str, t2->ahname_len);
-
-       /*
-        * if case insensitive result is "they're the same",
-        * use case sensitive comparison to force distinct order
-        */
-       if (ret == 0)
-               ret = sort_up_index_string(p1, p2);
-       return ret;
-}
-
-/* sort_down_index_ignrcase --- descending index strings, case insensitive */
-
-static int
-sort_down_index_ignrcase(const void *p1, const void *p2)
-{
-       return -sort_up_index_ignrcase(p1, p2);
-}
 
-/*
- * sort_up_index_number --- qsort comparison function; ascending index numbers;
- * the args have been built for us by sort_maybe_numeric_index() and point
- * to custom structs rather than to gawk nodes
- */
+/* sort_up_index_number --- qsort comparison function; ascending index 
numbers. */
 
 static int
 sort_up_index_number(const void *p1, const void *p2)
 {
-       const struct sort_num *n1, *n2;
+       const NODE *n1, *n2;
        int ret;
 
-       n1 = *((const struct sort_num *const *) p1);
-       n2 = *((const struct sort_num *const *) p2);
+       n1 = *((const NODE *const *) p1);
+       n2 = *((const NODE *const *) p2);
 
-       if (n1->sort_numbr < n2->sort_numbr)
+       if (n1->ahname_num < n2->ahname_num)
                ret = -1;
        else
-               ret = (n1->sort_numbr > n2->sort_numbr);
+               ret = (n1->ahname_num > n2->ahname_num);
 
        /* break a tie with the index string itself */
        if (ret == 0)
-               return sort_up_index_string((const void *) &n1->sort_index,
-                                           (const void *) &n2->sort_index);
+               return cmp_string(n1, n2);
        return ret;
 }
 
-/* sort_down_index_number --- descending index numbers */
+/* sort_down_index_number --- qsort comparison function; descending index 
numbers */
 
 static int
 sort_down_index_number(const void *p1, const void *p2)
@@ -1499,144 +1272,131 @@ sort_down_index_number(const void *p1, const void *p2)
        return -sort_up_index_number(p1, p2);
 }
 
-/* sort_up_value --- qsort comparison function; ascending values */
+
+/* sort_up_value_string --- qsort comparison function; ascending value string 
*/
 
 static int
-sort_up_value(const void *p1, const void *p2)
+sort_up_value_string(const void *p1, const void *p2)
 {
-       const NODE *idx1, *idx2;
-       NODE *val1, *val2;
-       int ret;
+       const NODE *t1, *t2;
+       NODE *n1, *n2;
 
        /* we're passed a pair of index (array subscript) nodes */
-       idx1 = *(const NODE *const *) p1;
-       idx2 = *(const NODE *const *) p2;
-       if (idx1->type != Node_ahash || idx2->type != Node_ahash)
-               cant_happen();
+       t1 = *(const NODE *const *) p1;
+       t2 = *(const NODE *const *) p2;
 
        /* and we want to compare the element values they refer to */
-       val1 = idx1->hvalue;
-       val2 = idx2->hvalue;
+       n1 = t1->ahvalue;
+       n2 = t2->ahvalue;
 
-       ret = sort_cmp_nodes(val1, val2);
+       if (n1->type == Node_var_array && n2->type == Node_val)
+               return 1;       /* n1 is more */
+       if (n1->type == Node_val && n2->type == Node_var_array)
+               return -1;      /* n1 is less */
 
-       /* disambiguate ties to force a deterministic order */
-       if (ret == 0)
-               ret = sort_up_index_string(p1, p2);
-       return ret;
+       if (n1->type == Node_var_array && n2->type == Node_var_array) {
+               /* sub-array names contain respective indices, effectively 
resulting
+                * in an index-based (in parent array) ordering.
+                */
+               return strcmp(n1->vname, n2->vname);
+       }
+
+       /* n1 and n2 both have string values; See sort_force_value_string(). */
+       return cmp_string(n1, n2);
 }
 
-/* sort_down_value --- descending value maybe-nums */
+
+/* sort_down_value_string --- descending value string */
 
 static int
-sort_down_value(const void *p1, const void *p2)
+sort_down_value_string(const void *p1, const void *p2)
 {
-       return -sort_up_value(p1, p2);
+       return -sort_up_value_string(p1, p2);
 }
 
-/* sort_maybe_numeric_index --- special handling for sorting indices as 
numbers */
+/* sort_up_value_number --- qsort comparison function; ascending value number 
*/
 
-void
-sort_maybe_numeric_index(qsort_compfunc func, NODE **list,
-                        size_t num_elems, int setup)
+static int
+sort_up_value_number(const void *p1, const void *p2)
 {
-       static const NODE empty_node;
+       const NODE *t1, *t2;
+       NODE *n1, *n2;
+       int ret;
 
-       size_t i;
-       NODE temp_node;
-       struct sort_num *fake_node;
+       /* we're passed a pair of index (array subscript) nodes */
+       t1 = *(const NODE *const *) p1;
+       t2 = *(const NODE *const *) p2;
 
-       /* if we're not sorting indices by numeric value, do nothing */
-       if (func != sort_up_index_number && func != sort_down_index_number)
-               return;
+       /* and we want to compare the element values they refer to */
+       n1 = t1->ahvalue;
+       n2 = t2->ahvalue;
+
+       if (n1->type == Node_var_array && n2->type == Node_val)
+               return 1;       /* n1 is more */
+       if (n1->type == Node_val && n2->type == Node_var_array)
+               return -1;      /* n1 is less */
+       if (n1->type == Node_var_array && n2->type == Node_var_array) {
+               /* sub-array names contain respective indices, effectively 
resulting
+                * in an index-based (in parent array) ordering.
+                */
 
-       /*
-        * Insert a new struct in front of each index, so there's somewhere
-        * to store its numeric value after that's calculated once, avoiding
-        * having the qsort callback recalculate it for every comparison.
-        * Our caller will call us back to destroy this intermediate array
-        * as soon as qsort() finishes; the rest of gawk won't see it.
-        * We could use--or perhaps mis-use--gawk nodes here, but they'd tie
-        * up quite a bit more memory during the sort.
-        */
-       if (setup) {
-               temp_node = empty_node; /* init union fields to 0,NULL */
-               for (i = 0; i < num_elems; ++i) {
-                       emalloc(fake_node, struct sort_num *,
-                               sizeof (struct sort_num), "sort numeric index");
-                       /* populate enough fields to satisfy force_number();
-                          we don't need to maintain reference counts */
-                       temp_node.type = Node_val;
-                       temp_node.stptr = list[i]->ahname_str;
-                       temp_node.stlen = list[i]->ahname_len;
-                       temp_node.flags = PERM | STRING | MAYBE_NUM;
-                       /* insert the extra data in front of our input node */
-                       fake_node->sort_numbr = force_number(&temp_node);
-                       fake_node->sort_index = list[i];
-                       list[i] = (NODE *) fake_node;
-               }
-       } else {        /* post sort cleanup */
-               for (i = 0; i < num_elems; ++i) {
-                       /* undo the setup manipulations */
-                       fake_node = (struct sort_num *) list[i];
-                       list[i] = fake_node->sort_index;
-                       efree((void *) fake_node);
-               }
+               return strcmp(n1->vname, n2->vname);
        }
-}
 
-/* sort_match --- compare leading part of a string against a sort option */
+       /* n1 and n2 both Node_val, and force_number'ed */
+       if (n1->numbr < n2->numbr)
+               ret = -1;
+       else
+               ret = (n1->numbr > n2->numbr);
 
-static size_t
-sort_match(const char *str, size_t slen, const char *target)
-{
-       const char *endofword;
+       if (ret != 0)
+               return ret;
 
-       /*
-        * STR comes from the user and is SLEN characters long;
-        * if it has spaces then we care about the subset up until
-        * its first space rather than the full length.
-        * We check whether STR is the same as--or proper prefix
-        * of--TARGET.
-        * Accept "by-foo" or "as-foo" as a synonym match for "foo".
+       /* break a tie using string comparison. First, make sure both
+        * n1 and n2 have string values.
         */
-       endofword = strchr(str, ' ');
-       if (endofword != NULL)
-               slen = (size_t) (endofword - str);
-       /* try for exact match */
-       if (slen > 0 && strncasecmp(str, target, slen) == 0)
-               return slen;
-       /* ingore "by-" or "as-" prefix and try again */
-       if (slen > 3
-           && (strncasecmp(str, "by-", 3) == 0
-               || strncasecmp(str, "as-", 3) == 0)
-           && strncasecmp(str + 3, target, slen - 3) == 0)
-               return slen;
-       /* no match */
-       return 0;
+       (void) force_string(n1);
+       (void) force_string(n2);
+       return cmp_string(n1, n2);
+}
+
+
+/* sort_down_value_string --- descending value number */
+
+static int
+sort_down_value_number(const void *p1, const void *p2)
+{
+       return -sort_up_value_number(p1, p2);
 }
 
+
 /*
- * sort_selection --- parse user-specified sort ordering
- * ("ascending index" or "value number" and so forth);
- * returns a qsort comparison function
+ * sort_selection --- parse user-specified sort specification;
+ * returns an index into sort_funcs table located in assoc_list().
  */
 
-static qsort_compfunc
-sort_selection(NODE *r, const char *warn_arg, int default_by_value)
+static int
+sort_selection(NODE *sort_str, SORT_CTXT sort_ctxt)
 {
+       /* first 4 bits used to calculate index into sort_funcs table in 
assoc_list(),
+        * next 4 bits for grouping individual components. Note that "Unsorted"
+        * belongs to all the groups.
+        */
+
        enum sort_bits {
-               unrecognized =    0,
-               Unsorted     = 0x01,
-               Ascending    = 0x02,
-               Descending   = 0x04,
-               by_Index     = 0x08,
-               by_Value     = 0x10,
-               as_String    = 0x20,
-               as_Number    = 0x40,
-               as_Inserted  = 0x80,
-               allbitsused  = 0xff
+               Unrecognized = 0xFF,    /* not part of a sort phrase */
+               Unsorted     = 0xF8,
+               Ascending    = 0x40,
+               Descending   = 0x44,
+               by_Index     = 0x20,
+               by_Value     = 0x22,
+               as_String    = 0x10,
+               as_Number    = 0x11
        };
+
+#define INDEX_MASK     0x0F
+#define GROUP_MASK     0xF0
+
        /*
         * The documented values are singular, but it's trivial to accept
         * "index numbers" and "descending values" since we're using a
@@ -1644,214 +1404,231 @@ sort_selection(NODE *r, const char *warn_arg, int 
default_by_value)
         */
        static const struct sort_keys {
                const char *const keyword;
+               const size_t keyword_len;
                enum  sort_bits keybit;
        } sorts[] = {
-               { "unsorted",   Unsorted    },  /* not part of a three-part key 
*/
-               { "ascending",  Ascending   },  /* ascending vs descending */
-               { "descending", Descending  },
-               { "indexes",    by_Index    },  /* by_Index vs by_Number */
-               { "indices",    by_Index    },  /* synonym for plural match */
-               { "values",     by_Value    },
-               { "strings",    as_String   },  /* as_String vs as_Number */
-               { "numbers",    as_Number   },
-               { "numeric",    as_Number   },  /* synonym; singular only */
-               { "inserted",   as_Inserted },  /* not part of a three-part key 
*/
-               { "insertion",  as_Inserted },  /* another synonym */
+               { "unsorted",   8, Unsorted },
+               { "ascending",  9, Ascending }, /* ascending vs descending */
+               { "descending", 10, Descending  },
+               { "indexes", 7, by_Index  },    /* by_Index vs by_Value */
+               { "indices", 7, by_Index  },    /* synonym for plural match */
+               { "values", 6,  by_Value  },
+               { "strings", 7, as_String },    /* as_String vs as_Number */
+               { "numbers", 7, as_Number },
+               { "numeric", 7, as_Number },    /* synonym; singular only */
        };
+       static int num_keys = sizeof(sorts) / sizeof(sorts[0]);
+       int num_words, i;
+       char *word, *s;
+       size_t word_len;
+       enum sort_bits allparts, bval;
+
+       if (sort_str == NULL) {
+               assert(sort_ctxt == SORTED_IN);
+               return (Unsorted & INDEX_MASK);         /* no sorting */
+       }
 
-       static short warned_unrecognized = FALSE;
-       static enum sort_bits prev_invalid = unrecognized;
+       (void) force_string(sort_str);
+       sort_str->stptr[sort_str->stlen] = '\0';        /* safety */
 
-       char *s;
-       size_t l, matchlen;
-       unsigned i;
-       const char *errfmt;
-       enum sort_bits allparts;
-       qsort_compfunc sort_func, default_sort_func;
-
-       /* deduce our caller from the args provided */
-       if (warn_arg != NULL)           /* for-in statement */
-               default_sort_func = (qsort_compfunc) 0; /* unsorted */
-       else if (default_by_value)      /* asort() */
-               default_sort_func = sort_up_value;
-       else                            /* asorti() */
-               default_sort_func = ! IGNORECASE ? sort_up_index_string
-                                               : sort_up_index_ignrcase;
-
-       r = force_string(r);
-       s = r->stptr;
-       l = r->stlen;
-
-       /* treat empty sort-order string as request for default */
-       if (l == 0)
-               return default_sort_func;
-
-       /* no ordering specification yet */
-       allparts = unrecognized;
-       /*
-        * Scan through S until length L is exhausted, checking its
-        * starting word against each possible ordering choice and
-        * then skipping past that to get to the next word.  If no
-        * choice matches, an error has been detected.  Duplicate
-        * words are accepted; contradictory ones get noticed and
-        * rejected after the string parsing has completed.
+       /* Initialize with the context-sensitive defaults; Note that group-bits 
aren't
+        * copied, doing so will prevent the user from explicitely specifying 
the defaults.
         */
-       while (l > 0) {
-               matchlen = 0;   /* lint suppression */
-               for (i = 0; i < (sizeof sorts / sizeof *sorts); ++i) {
-                       matchlen = sort_match(s, l, sorts[i].keyword);
-                       if (matchlen > 0) {
-                               allparts |= sorts[i].keybit;
+ 
+       if (sort_ctxt == ASORT)
+               allparts = (Ascending|by_Value|as_String) & INDEX_MASK;
+       else
+               allparts = (Ascending|by_Index|as_String) & INDEX_MASK;
+
+       num_words = 0;
+
+       for (s = sort_str->stptr; *s != '\0'; ) {
+               /* skip leading spaces */
+               while (*s == ' ')
+                       s++;
+               if (*s == '\0')
+                       break;
+               word = s;
+               /* find the end of the word */
+               while (*s && *s != ' ')
+                       s++;
+               word_len = (size_t) (s - word);
+
+               if (++num_words > 3)    /* too many words in phrase */
+                       goto un_recognized;
+
+               bval = Unrecognized;
+               for (i = 0; i < num_keys; i++) {
+                       if (word_len <= sorts[i].keyword_len
+                                     && strncasecmp(sorts[i].keyword, word, 
word_len) == 0) {
+                               bval = sorts[i].keybit;
                                break;
                        }
                }
-               if (matchlen == 0) {
-                       /* failed to match any possible component */
-                       allparts = unrecognized;
-                       break;
-               }
-               /* move past the part just handled */
-               s += matchlen, l -= matchlen;
-               /* (l > 0) here would accept a trailing space; (l > 1) won't */
-               if (l > 1 && *s == ' ')
-                       ++s, --l;
-       } /* while (l > 0) */
+                               
+               if (bval == Unrecognized         /* invalid word in phrase */
+                       ||      (sort_ctxt == ASORT
+                                       && (bval == by_Index || bval == 
Unsorted)
+                               )                        /* "index" used in 
phrase for asort etc. */
+                       ||      (sort_ctxt == ASORTI
+                                       && (bval == by_Value || bval == 
Unsorted)
+                               )                        /* "value" used in 
phrase for asorti etc. */
+                       ||      ((allparts & bval) & GROUP_MASK
+                               )                        /* invalid grouping of 
words e.g. "str num" */
+               )
+                       goto un_recognized;
+
+               allparts |= bval;
+       }
 
-       /*
-        * If we have one or two parts of a three part key, fill in
-        * whichever parts are missing with default values.  For key
-        * type the default is specified by our caller, for direction
-        * it is Ascending, and for comparison mode it is as_String
-        * but only when sorting by_Index.  When sorting by_Value,
-        * comparison mode is out of the user's control and we don't
-        * need or want to fill in either as_String or as_Number.
-        */
-       if ((allparts & (Ascending|Descending|as_String|as_Number)) != 0
-           && (allparts & (Unsorted|by_Index|by_Value|as_Inserted)) == 0)
-               allparts |= default_by_value ? by_Value : by_Index;
-       if ((allparts & (by_Index|by_Value|as_String|as_Number)) != 0
-           && (allparts & (Unsorted|Ascending|Descending|as_Inserted)) == 0)
-               allparts |= Ascending;
-       /* by_Value is handled differently from by_Index */
-       if ((allparts & (Ascending|Descending|by_Index)) != 0
-           && (allparts & (Unsorted|by_Value|as_String|as_Number|as_Inserted)) 
== 0)
-               allparts |= as_String;
+       /* num_words <= 3 */
+       return (allparts & INDEX_MASK);
 
-       /*
-        * ALLPARTS is a bit mask of all the user's specified ordering
-        * choices; partial specifications have been extended into full
-        * three part key.  Valid combinations yield a qsort(3) comparison
-        * routine result.  Invalid ones are rejected and yield default
-        * sort, with an error message for asort()/asorti(), a warning
-        * for for-in statement if this is a different invalid combination
-        * than the most recently warned about one, or a one-time warning
-        * if this is an unrecognized ordering specification and we're
-        * running in lint checking mode.
-        */
-       sort_func = default_sort_func;
-       errfmt = NULL;
-       switch (allparts) {
-       case Unsorted:
-               sort_func = (qsort_compfunc) 0;
-               break;
-       case Ascending|by_Index|as_String:
-               sort_func = ! IGNORECASE ? sort_up_index_string
-                                       : sort_up_index_ignrcase;
-               break;
-       case Descending|by_Index|as_String:
-               sort_func = ! IGNORECASE ? sort_down_index_string
-                                       : sort_down_index_ignrcase;
-               break;
-       case Ascending|by_Index|as_Number:
-               sort_func = sort_up_index_number;
-               break;
-       case Descending|by_Index|as_Number:
-               sort_func = sort_down_index_number;
-               break;
-       case Ascending|by_Value:
-               sort_func = sort_up_value;
-               break;
-       case Descending|by_Value:
-               sort_func = sort_down_value;
-               break;
-       /* for value sorts, distinction between string and number is not
-          allowed; they're always ordered by basic awk MAYBE_NUM comparison */
-       case Ascending|by_Value|as_String:
-       case Descending|by_Value|as_String:
-               if (errfmt == NULL)
-                       errfmt = _("%s: sorting by value can't be forced to use 
string comparison");
-               /* FALL THROUGH */
-       case Ascending|by_Value|as_Number:
-       case Descending|by_Value|as_Number:
-               if (errfmt == NULL)
-                       errfmt = _("%s: sorting by value can't be forced to use 
numeric comparison");
-               /* FALL THROUGH */
-       /* gawk doesn't maintain the data necessary to sort by
-          order of insertion */
-       case as_Inserted:
-               if (errfmt == NULL)
-                       errfmt = _("%s: sort ordering \"as-inserted\" is not 
implemented");
-               /* FALL THROUGH */
+un_recognized:
+       switch (sort_ctxt) {
+       case ASORT:
+               fatal(_("asort: invalid sort specification `%s'"), 
sort_str->stptr);
+       case ASORTI:
+               fatal(_("asorti: invalid sort specification `%s'"), 
sort_str->stptr);
+
+       case SORTED_IN:
+               /* fall through */
        default:
-               /* sort_func still has default value */
-               if (errfmt == NULL)
-                       errfmt = (allparts == unrecognized)
-                               ? _("%s: sort specification is not recognized")
-                               : _("%s: sort specification is invalid");
-               if (warn_arg == NULL) {
-                       /* asort()/asorti() bad sort specification argument */
-                       error(errfmt, default_by_value ? "asort" : "asorti");
-               } else if (allparts != unrecognized) {
-                       /* invalid combination of valid ordering components */
-                       if (allparts != prev_invalid)
-                               warning(errfmt, warn_arg);
-                       prev_invalid = allparts;
-               } else if (do_lint) {
-                       /* unrecognized ordering specification string */        
      
-                       if (! warned_unrecognized)
-                               lintwarn(errfmt, warn_arg);
-                       warned_unrecognized = TRUE;
+               if (do_lint) {
+                       static NODE *warned_str = NULL;
+
+                       /* warning for each UNIQUE unrecognized sort_str 
specification */              
+                       if (warned_str == NULL || ! STREQ(warned_str->stptr, 
sort_str->stptr)) {
+                               lintwarn(_("PROCINFO[\"sorted_in\"]: invalid 
sort specification `%s'"),
+                                               sort_str->stptr);
+                               unref(warned_str);      /* unref(NULL) is OK */ 
+                               warned_str = dupnode(sort_str);
+                       }
                }
                break;
        }
-       return sort_func;
+       return (Unsorted & INDEX_MASK);
+
+#undef INDEX_MASK
+#undef GROUP_MASK
 }
 
-/*
- * sorted_in --- fetch value of PROCINFO["sorted_in"] and use sort_selection()
- * to parse it; controls whether and how traversal of ``for (index in array)''
- * should be sorted; returns a qsort comparison function
- */
+/* sort_force_index_number -- pre-process list items for sorting indices as 
numbers */
 
-qsort_compfunc
-sorted_in(void)                /* called from r_interpret(eval.c) */
+static void
+sort_force_index_number(NODE **list, size_t num_elems)
 {
-       static NODE *sorted_str = NULL;
-       static short warned_extension = FALSE;
-
+       size_t i;
        NODE *r;
+       static NODE temp_node;
+
+       for (i = 0; i < num_elems; i++) {
+               r = list[i];
+
+               /* Kludge: the use of NUMCUR flag for a Node_ahash is only to 
detect
+                * multiple conversion attempts. Also, the flag value of a 
Node_ahash
+                * isn't used anywhere else in the gawk source. 
+                * Maybe a new flag other than NUMCUR should be used ?
+                */
+
+               if ((r->flags & NUMCUR) != 0)   /* once in a lifetime is more 
than enough */
+                       continue;
+               temp_node.type = Node_val;
+               temp_node.stptr = r->ahname_str;
+               temp_node.stlen = r->ahname_len;
+               temp_node.flags = 0;    /* only interested in the return value 
of r_force_number */
+               r->ahname_num = r_force_number(&temp_node);
+               r->flags |= NUMCUR;
+       }
+}
 
-       /* if there's no PROCINFO[], there's no ["sorted_in"], so no sorting */
-       if (PROCINFO_node == NULL)
-               return (qsort_compfunc) 0;
+/* sort_force_value_number -- pre-process list items for sorting values as 
numbers */
 
-       if (sorted_str == NULL)         /* do this once */
-               sorted_str = make_string("sorted_in", 9);
+static void
+sort_force_value_number(NODE **list, size_t num_elems)
+{
+       size_t i;
+       NODE *r, *val;
+
+       for (i = 0; i < num_elems; i++) {
+               r = list[i];
+               val = r->ahvalue;
+               if (val->type == Node_val)
+                       (void) force_number(val);
+       }
+}
 
-       r = (NODE *) NULL;
-       if (in_array(PROCINFO_node, sorted_str))
-               r = *assoc_lookup(PROCINFO_node, sorted_str, TRUE);
-       /* if there's no PROCINFO["sorted_in"], there's no sorting */
-       if (r == NULL)
-               return (qsort_compfunc) 0;
+/* sort_force_value_string -- pre-process list items for sorting values as 
strings */
+
+static void
+sort_force_value_string(NODE **list, size_t num_elems)
+{
+       size_t i;
+       NODE *r, *val;
 
-       /* we're going to make use of PROCINFO["sorted_in"] */
-       if (do_lint && ! warned_extension) {
-               warned_extension = TRUE;
-               lintwarn(_("sorted array traversal is a gawk extension"));
+       for (i = 0; i < num_elems; i++) {
+               r = list[i];
+               val = r->ahvalue;
+               if (val->type == Node_val)
+                       r->ahvalue = force_string(val);
        }
+}
+
+
+/* assoc_list -- construct, and optionally sort, a list of array elements */  
+
+NODE **
+assoc_list(NODE *array, NODE *sort_str, SORT_CTXT sort_ctxt)
+{
+       typedef void (*qsort_prefunc)(NODE **, size_t);
+       typedef int (*qsort_compfunc)(const void *, const void *);
+
+       static const struct qsort_funcs {
+               qsort_compfunc comp_func;
+               qsort_prefunc pre_func;         /* pre-processing of list items 
*/
+       } sort_funcs[] = {
+               { sort_up_index_string,         0                       },      
/* ascending index string */
+               { sort_up_index_number,         sort_force_index_number },      
/* ascending index number */
+               { sort_up_value_string,         sort_force_value_string },      
/* ascending value string */
+               { sort_up_value_number,         sort_force_value_number },      
/* ascending value number */
+               { sort_down_index_string,       0                       },      
/* descending index string */ 
+               { sort_down_index_number,       sort_force_index_number },      
/* descending index number */   
+               { sort_down_value_string,       sort_force_value_string },      
/* descending value string */ 
+               { sort_down_value_number,       sort_force_value_number },      
/* descending value number */
+               { 0,    0 }                                             /* 
unsorted */
+       };
+       int qi;
+       NODE **list;
+       NODE *r;
+       size_t num_elems, i, j;
+       qsort_compfunc cmp_func;
+       qsort_prefunc pre_func;
+
+       num_elems = array->table_size;
+       assert(num_elems > 0);
+
+       qi = sort_selection(sort_str, sort_ctxt);
+
+       /* allocate space for array; the extra space is used in for(i in a) 
opcode (eval.c) */
+       emalloc(list, NODE **, (num_elems + 1) * sizeof(NODE *), "assoc_list");
+
+       /* populate it */
+       for (i = j = 0; i < array->array_size; i++)
+               for (r = array->var_array[i]; r != NULL; r = r->ahnext)
+                       list[j++] = ahash_dupnode(r);
+       list[num_elems] = NULL;
+
+       cmp_func = sort_funcs[qi].comp_func;
+       if (! cmp_func) /* unsorted */  
+               return list;
+
+       /* special pre-processing of list items */
+       pre_func = sort_funcs[qi].pre_func;
+       if (pre_func)
+               pre_func(list, num_elems);
 
-       return sort_selection(r, "`PROCINFO[\"sorted_in\"]'", FALSE);
+       qsort(list, num_elems, sizeof(NODE *), cmp_func); /* shazzam! */
+       return list;
 }
 
 
diff --git a/awk.h b/awk.h
index d781ff6..52ce5bf 100644
--- a/awk.h
+++ b/awk.h
@@ -336,6 +336,7 @@ typedef struct exp_node {
 #endif
                } val;
                struct {
+                       AWKNUM num;
                        struct exp_node *next;
                        char *name;
                        size_t length;
@@ -351,6 +352,7 @@ typedef struct exp_node {
 #define        ahnext          sub.hash.next
 #define        ahname_str      sub.hash.name
 #define ahname_len     sub.hash.length
+#define ahname_num     sub.hash.num
 #define        ahvalue sub.hash.value
 #define ahname_ref     sub.hash.ref
 #define        ahcode  sub.hash.code
@@ -845,9 +847,6 @@ struct flagtab {
 #endif
 #define UNLIMITED    LONG_MAX 
 
-/* qsort comparison function */
-typedef int (*qsort_compfunc)(const void *,const void *);
-
 /* -------------------------- External variables -------------------------- */
 /* gawk builtin variables */
 extern long NF;
@@ -1109,6 +1108,8 @@ if (--val)        \
 typedef int (*Func_print)(FILE *, const char *, ...);
 
 /* array.c */
+typedef enum sort_context { SORTED_IN = 1, ASORT, ASORTI } SORT_CTXT;
+extern NODE **assoc_list(NODE *array, NODE *sort_str, SORT_CTXT sort_ctxt);
 extern NODE *get_array(NODE *symbol, int canfatal);
 extern char *array_vname(const NODE *symbol);
 extern char *make_aname(NODE *array, NODE *subs);
@@ -1125,9 +1126,6 @@ extern NODE *assoc_dump(NODE *symbol, int indent_level);
 extern NODE *do_adump(int nargs);
 extern NODE *do_asort(int nargs);
 extern NODE *do_asorti(int nargs);
-extern int comp_func(const void *p1, const void *p2);
-extern qsort_compfunc sorted_in(void);
-extern void sort_maybe_numeric_index(qsort_compfunc, NODE **, size_t, int);
 extern unsigned long (*hash)(const char *s, size_t len, unsigned long hsize, 
size_t *code);
 /* awkgram.c */
 extern NODE *mk_symbol(NODETYPE type, NODE *value);
diff --git a/awkgram.c b/awkgram.c
index bc911d5..9b56293 100644
--- a/awkgram.c
+++ b/awkgram.c
@@ -4475,8 +4475,8 @@ static const struct token tokentab[] = {
 {"adump",      Op_builtin,    LEX_BUILTIN,     GAWKX|A(1),     do_adump},
 #endif
 {"and",                Op_builtin,    LEX_BUILTIN,     GAWKX|A(2),     do_and},
-{"asort",      Op_builtin,      LEX_BUILTIN,   GAWKX|A(1)|A(2),        
do_asort},
-{"asorti",     Op_builtin,      LEX_BUILTIN,   GAWKX|A(1)|A(2),        
do_asorti},
+{"asort",      Op_builtin,      LEX_BUILTIN,   GAWKX|A(1)|A(2)|A(3),   
do_asort},
+{"asorti",     Op_builtin,      LEX_BUILTIN,   GAWKX|A(1)|A(2)|A(3),   
do_asorti},
 {"atan2",      Op_builtin,      LEX_BUILTIN,   NOT_OLD|A(2),   do_atan2},
 {"bindtextdomain",     Op_builtin,      LEX_BUILTIN,   GAWKX|A(1)|A(2),        
do_bindtextdomain},
 {"break",      Op_K_break,      LEX_BREAK,     0,              0},
@@ -6443,12 +6443,12 @@ snode(INSTRUCTION *subn, INSTRUCTION *r)
        } else if (r->builtin == do_asort || r->builtin == do_asorti) {
                arg = subn->nexti;      /* 1st arg list */
                ip = arg->lasti;
-               if (/* ip == arg->nexti && */ ip->opcode == Op_push)
+               if (ip->opcode == Op_push)
                        ip->opcode = Op_push_array;
-               if (nexp == 2) {
+               if (nexp >= 2) {
                        arg = ip->nexti;
                        ip = arg->lasti;
-                       if (/* ip == arg->nexti && */ ip->opcode == Op_push)
+                       if (ip->opcode == Op_push)
                                ip->opcode = Op_push_array;
                }
        }
diff --git a/awkgram.y b/awkgram.y
index 0165912..cf8b9e9 100644
--- a/awkgram.y
+++ b/awkgram.y
@@ -1828,8 +1828,8 @@ static const struct token tokentab[] = {
 {"adump",      Op_builtin,    LEX_BUILTIN,     GAWKX|A(1),     do_adump},
 #endif
 {"and",                Op_builtin,    LEX_BUILTIN,     GAWKX|A(2),     do_and},
-{"asort",      Op_builtin,      LEX_BUILTIN,   GAWKX|A(1)|A(2),        
do_asort},
-{"asorti",     Op_builtin,      LEX_BUILTIN,   GAWKX|A(1)|A(2),        
do_asorti},
+{"asort",      Op_builtin,      LEX_BUILTIN,   GAWKX|A(1)|A(2)|A(3),   
do_asort},
+{"asorti",     Op_builtin,      LEX_BUILTIN,   GAWKX|A(1)|A(2)|A(3),   
do_asorti},
 {"atan2",      Op_builtin,      LEX_BUILTIN,   NOT_OLD|A(2),   do_atan2},
 {"bindtextdomain",     Op_builtin,      LEX_BUILTIN,   GAWKX|A(1)|A(2),        
do_bindtextdomain},
 {"break",      Op_K_break,      LEX_BREAK,     0,              0},
@@ -3796,12 +3796,12 @@ snode(INSTRUCTION *subn, INSTRUCTION *r)
        } else if (r->builtin == do_asort || r->builtin == do_asorti) {
                arg = subn->nexti;      /* 1st arg list */
                ip = arg->lasti;
-               if (/* ip == arg->nexti && */ ip->opcode == Op_push)
+               if (ip->opcode == Op_push)
                        ip->opcode = Op_push_array;
-               if (nexp == 2) {
+               if (nexp >= 2) {
                        arg = ip->nexti;
                        ip = arg->lasti;
-                       if (/* ip == arg->nexti && */ ip->opcode == Op_push)
+                       if (ip->opcode == Op_push)
                                ip->opcode = Op_push_array;
                }
        }
diff --git a/debug.c b/debug.c
index 3726f05..8980ab0 100644
--- a/debug.c
+++ b/debug.c
@@ -1061,59 +1061,48 @@ print_field(long field_num)
 /* print_array --- print the contents of an array */
 
 static int
-print_array(NODE *arr, char *arr_name, Func_print print_func)
+print_array(volatile NODE *arr, char *arr_name)
 {
        NODE *bucket;
-       NODE **list = NULL;
-       int i, j;
+       NODE **list;
+       int i;
        size_t num_elems = 0;
+       volatile NODE *r;
+       volatile int ret = 0;
+       volatile jmp_buf pager_quit_tag_stack;
 
        if (arr->var_array == NULL || arr->table_size == 0) {
-               print_func(out_fp, _("array `%s' is empty\n"), arr_name);
+               gprintf(out_fp, _("array `%s' is empty\n"), arr_name);
                return 0;
        }
 
-       /* sort indices */
-
-       /* allocate space for array */
        num_elems = arr->table_size;
-       emalloc(list, NODE **, num_elems * sizeof(NODE *), "print_array");
 
-       /* populate it */
-       for (i = j = 0; i < arr->array_size; i++) {
-               bucket = arr->var_array[i];
-               if (bucket == NULL)
-                       continue;
-               for (; bucket != NULL; bucket = bucket->ahnext)
-                       list[j++] = bucket;
-       }
-       qsort(list, num_elems, sizeof(NODE *), comp_func); /* shazzam! */
-
-       for (i = 0; i < num_elems; i++) {
-               bucket = list[i];
-               if (bucket->ahvalue->type == Node_var_array) {
-                       arr = bucket->ahvalue;
-                       if (pager_quit_tag_valid) {     /* we are using pager */
-                               volatile jmp_buf pager_quit_tag_stack;
-                               volatile int ret = 1;
-
-                               PUSH_BINDING(pager_quit_tag_stack, 
pager_quit_tag, pager_quit_tag_valid);
-                               if (setjmp(pager_quit_tag) == 0)
-                                       ret = print_array(arr, arr->vname, 
print_func);
-                               POP_BINDING(pager_quit_tag_stack, 
pager_quit_tag, pager_quit_tag_valid);
-                               if (ret == 1) {
-                                       efree(list);
-                                       return 1;
-                               }
-                       } else
-                               (void) print_array(arr, arr->vname, print_func);
-               } else {
-                       print_func(out_fp, "%s[\"%s\"] = ", arr_name, 
bucket->ahname_str);
-                       valinfo(bucket->ahvalue, print_func, out_fp);
+       /* sort indices, sub_arrays are also sorted! */
+       list = assoc_list((NODE *) arr, Nnull_string, SORTED_IN);
+
+       PUSH_BINDING(pager_quit_tag_stack, pager_quit_tag, 
pager_quit_tag_valid);
+       if (setjmp(pager_quit_tag) == 0) {
+               for (i = 0; ret == 0 && i < num_elems; i++) {
+                       bucket = list[i];
+                       r = bucket->ahvalue;
+                       if (r->type == Node_var_array)
+                               ret = print_array(r, r->vname);
+                       else {
+                               gprintf(out_fp, "%s[\"%s\"] = ", arr_name, 
bucket->ahname_str);
+                               valinfo((NODE *) r, gprintf, out_fp);
+                       }
                }
-       }
+       } else
+               ret = 1;
+
+       POP_BINDING(pager_quit_tag_stack, pager_quit_tag, pager_quit_tag_valid);
+
+       for (i = 0; i < num_elems; i++)
+               ahash_unref(list[i]);
        efree(list);
-       return 0;
+
+       return ret;
 }
 
 /* print_subscript --- print an array element */
@@ -1192,10 +1181,7 @@ do_print_var(CMDARG *arg, int cmd ATTRIBUTE_UNUSED)
                                }
                                if (count == 0) {
                                        initialize_pager(out_fp);
-                                       pager_quit_tag_valid = TRUE;
-                                       if (setjmp(pager_quit_tag) == 0)
-                                               print_array(r, name, gprintf);
-                                       pager_quit_tag_valid = FALSE;
+                                       print_array((volatile NODE *) r, name);
                                }
                        }
                        break;
diff --git a/doc/gawk.1 b/doc/gawk.1
index bac3176..629899b 100644
--- a/doc/gawk.1
+++ b/doc/gawk.1
@@ -1089,10 +1089,12 @@ loops.
 Supported values are
 \fB"ascending index string"\fR,
 \fB"ascending index number"\fR,
-\fB"ascending value"\fR,
+\fB"ascending value string"\fR,
+\fB"ascending value number"\fR,
 \fB"descending index string"\fR,
 \fB"descending index number"\fR,
-\fB"descending value"\fR, and
+\fB"descending value string"\fR,
+\fB"descending value number"\fR, and
 \fB"unsorted"\fR.
 The order specification words can be truncated, or omitted (provided
 that at least one is present), or given in any order.
@@ -2439,7 +2441,7 @@ number generator.
 has the following built-in string functions:
 .PP
 .TP "\w'\fBsprintf(\^\fIfmt\fB\^, \fIexpr-list\^\fB)\fR'u+1n"
-\fBasort(\fIs \fR[\fB, \fId\fR]\fB)\fR
+\fBasort(\fIs \fR[\fB, \fId\fR [\fB, \fIhow\fR] ]\fB)\fR
 Return the number of elements in the source
 array
 .IR s .
@@ -2466,9 +2468,15 @@ and then sort
 leaving the indices of the
 source array
 .I s
-unchanged.
+unchanged. The optional string
+.I how
+controls the direction and the comparsion mode.
+Valid values for
+.I how
+are "ascending string", "ascending number",
+"descending string" and "descending number".
 .TP "\w'\fBsprintf(\^\fIfmt\fB\^, \fIexpr-list\^\fB)\fR'u+1n"
-\fBasorti(\fIs \fR[\fB, \fId\fR]\fB)\fR
+\fBasorti(\fIs \fR[\fB, \fId\fR [\fB, \fIhow\fR] ]\fB)\fR
 Return the number of elements in the source
 array
 .IR s .
@@ -2481,6 +2489,11 @@ When done, the array is indexed numerically, and
 the values are those of the original indices.
 The original values are lost; thus provide
 a second array if you wish to preserve the original.
+The purpose of the optional string
+.I how
+is the same as described in
+.BR asort()
+above.
 .TP
 \fBgensub(\fIr\fB, \fIs\fB, \fIh \fR[\fB, \fIt\fR]\fB)\fR
 Search the target string
diff --git a/doc/gawk.info b/doc/gawk.info
index a67eafb..06d2dbb 100644
--- a/doc/gawk.info
+++ b/doc/gawk.info
@@ -9409,24 +9409,23 @@ with a pound sign (`#').
           words; separate pairs of words by a single space.  One word
           controls sort direction, `ascending' or `descending'; another
           controls the sort key, `index' or `value'; and the remaining
-          one, which is only valid for sorting by index, is comparison
-          mode, `string' or `number'.  When two or three words are
-          present, they may be specified in any order, so `ascending
-          index string' and `string ascending index' are equivalent.
-          Also, each word may be truncated, so `asc index str' and `a i
-          s' are also equivalent.  Note that a separating space is
-          required even when the words have been shortened down to one
-          letter each.
+          one affects comparison mode, `string' or `number'.  When two
+          or three words are present, they may be specified in any
+          order, so `ascending index string' and `string ascending
+          index' are equivalent.  Also, each word may be truncated, so
+          `asc index str' and `a i s' are also equivalent.  Note that a
+          separating space is required even when the words have been
+          shortened down to one letter each.
 
           You can omit direction and/or key type and/or comparison
           mode.  Provided that at least one is present, the missing
           parts of a sort specification default to `ascending',
-          `index', and (for indices only) `string', respectively.  An
-          empty string, `""', is the same as `unsorted' and will cause
-          `for (index in array) ...' to process the indices in
-          arbitrary order.  Another thing to note is that the array
-          sorting takes place at the time the `for' loop is about to
-          start executing, so changing the value of
+          `index', and `string', respectively.  An empty string, `""',
+          is the same as `ascending index string', and a value of
+          `unsorted' will cause `for (index in array) ...' to process
+          the indices in arbitrary order.  Another thing to note is
+          that the array sorting takes place at the time the `for' loop
+          is about to start executing, so changing the value of
           `PROCINFO["sorted_in"]' during loop execution does not have
           any effect on the order in which any remaining array elements
           get processed.  *Note Scanning an Array::, for more
@@ -9972,11 +9971,14 @@ are available:
      process.  Any index with non-numeric value will end up positioned
      as if it were zero.
 
-`ascending value'
-     Order by element values rather than by indices.  Comparisons are
-     done as numeric when both values being compared are numeric, or
-     done as strings when either or both aren't numeric (*note Variable
-     Typing::).  Subarrays, if present, come out last.
+`ascending value string'
+     Order by element values rather than by indices.  Scalar values are
+     compared as strings.  Subarrays, if present, come out last.
+
+`ascending value number'
+     Order by values but force scalar values to be treated as numbers
+     for the purpose of comparison.  If there are subarrays, those
+     appear at the end of the sorted list.
 
 `descending index string'
      Reverse order from the most basic sort.
@@ -9984,22 +9986,31 @@ are available:
 `descending index number'
      Numeric indices ordered from high to low.
 
-`descending value'
-     Element values ordered from high to low.  Subarrays, if present,
-     come out first.
+`descending value string'
+     Element values, treated as strings, ordered from high to low.
+     Subarrays, if present, come out first.
+
+`descending value number'
+     Element values, treated as numbers, ordered from high to low.
+     Subarrays, if present, come out first.
 
 `unsorted'
      Array elements are processed in arbitrary order, the normal `awk'
-     behavior.
+     behavior. You can also get the normal behavior by just deleting
+     the `"sorted_in"' item from the `PROCINFO' array, if it previously
+     had a value assigned to it.
 
    The array traversal order is determined before the `for' loop starts
-to run. Changing `PROCINFO["sorted_in"]' in the looop body will not
+to run. Changing `PROCINFO["sorted_in"]' in the loop body will not
 affect the loop.
 
    Portions of the sort specification string may be truncated or
 omitted.  The default is `ascending' for direction, `index' for sort
-key type, and (when sorting by index only) `string' for comparison mode.
-For example:
+key type, and `string' for comparison mode.  This implies that one can
+simply assign the empty string, "", instead of "ascending index string"
+to `PROCINFO["sorted_in"]' for the same effect.
+
+   For example:
 
      $ gawk 'BEGIN {
      >    a[4] = 4
@@ -10025,11 +10036,6 @@ value, regardless of what the subarray itself 
contains, and all
 subarrays are treated as being equal to each other.  Their order
 relative to each other is determined by their index strings.
 
-   Sorting by array element values (for values other than subarrays)
-always uses basic `awk' comparison mode:  if both values happen to be
-numbers then they're compared as numbers, otherwise they're compared as
-strings.
-
    When string comparisons are made during a sort, either for element
 values where one or both aren't numbers or for element indices handled
 as strings, the value of `IGNORECASE' (*note Built-in Variables::)
@@ -10365,8 +10371,7 @@ For example:
    After the call to `asort()', the array `data' is indexed from 1 to
 some number N, the total number of elements in `data'.  (This count is
 `asort()''s return value.)  `data[1]' <= `data[2]' <= `data[3]', and so
-on.  The comparison of array elements is done using `gawk''s usual
-comparison rules (*note Typing and Comparison::).
+on.  The array elements are compared as strings.
 
    An important side effect of calling `asort()' is that _the array's
 original indices are irrevocably lost_.  As this isn't always
@@ -10381,6 +10386,19 @@ desirable, `asort()' accepts a second argument:
 and then sorts `dest', destroying its indices.  However, the `source'
 array is not affected.
 
+   `asort()' and `asorti()' accept a third string argument to control
+the comparison rule for the array elements, and the direction of the
+sorted results.  The valid comparison modes are `string' and `number',
+and the direction can be either `ascending' or `descending'.  Either
+mode or direction, or both, can be omitted in which case the defaults,
+`string' or `ascending' is assumed for the comparison mode and the
+direction, respectively.  Seperate comparison mode from direction with
+a single space, and they can appear in any order.  To compare the
+elements as numbers, and to reverse the elements of the `dest' array,
+the call to asort in the above example can be replaced with:
+
+     asort(source, dest, "descending number")
+
    Often, what's needed is to sort on the values of the _indices_
 instead of the values of the elements.  To do that, use the `asorti()'
 function.  The interface is identical to that of `asort()', except that
@@ -10403,7 +10421,8 @@ result array:
    Sorting the array by replacing the indices provides maximal
 flexibility.  To traverse the elements in decreasing order, use a loop
 that goes from N down to 1, either over the elements or over the
-indices.
+indices.  This is an alternative to specifying `descending' for the
+sorting order using the optional third argument.
 
    Copying array indices and elements isn't expensive in terms of
 memory.  Internally, `gawk' maintains "reference counts" to data.  For
@@ -10411,12 +10430,10 @@ example, when `asort()' copies the first array to the 
second one, there
 is only one copy of the original array elements' data, even though both
 arrays use the values.
 
-   We said previously that comparisons are done using `gawk''s "usual
-comparison rules."  Because `IGNORECASE' affects string comparisons,
-the value of `IGNORECASE' also affects sorting for both `asort()' and
-`asorti()'.  Note also that the locale's sorting order does _not_ come
-into play; comparisons are based on character values only.(1) Caveat
-Emptor.
+   Because `IGNORECASE' affects string comparisons, the value of
+`IGNORECASE' also affects sorting for both `asort()' and `asorti()'.
+Note also that the locale's sorting order does _not_ come into play;
+comparisons are based on character values only.(1) Caveat Emptor.
 
    ---------- Footnotes ----------
 
@@ -10780,15 +10797,26 @@ pound sign (`#'):
                                 `&' with `sub()', `gsub()', and
                                 `gensub()'.
 
-`asort(SOURCE [, DEST]) #'
+`asort(SOURCE [, DEST [, HOW  ] ]) #'
      Return the number of elements in the array SOURCE.  `gawk' sorts
-     the contents of SOURCE using the normal rules for comparing values
-     (in particular, `IGNORECASE' affects the sorting) and replaces the
-     indices of the sorted values of SOURCE with sequential integers
-     starting with one. If the optional array DEST is specified, then
-     SOURCE is duplicated into DEST.  DEST is then sorted, leaving the
-     indices of SOURCE unchanged.  For example, if the contents of `a'
-     are as follows:
+     the contents of SOURCE and replaces the indices of the sorted
+     values of SOURCE with sequential integers starting with one.  If
+     the optional array DEST is specified, then SOURCE is duplicated
+     into DEST.  DEST is then sorted, leaving the indices of SOURCE
+     unchanged.  The optional third argument HOW is a string which
+     controls the rule for comparing values, and the sort direction.  A
+     single space is required between the comparison mode, `string' or
+     `number', and the direction specification, `ascending' or
+     `descending'.  You can omit direction and/or mode in which case it
+     will default to `ascending' and `string', respectively.  An empty
+     string "" is the same as the default `"ascending string"' for the
+     value of HOW.  If the `source' array contains subarrays as values,
+     they will come out last(first) in the `dest' array for
+     `ascending'(`descending') order specification.  The value of
+     `IGNORECASE' affects the sorting.  *Note Scanning an Array::, for
+     more information.
+
+     For example, if the contents of `a' are as follows:
 
           a["last"] = "de"
           a["first"] = "sac"
@@ -10804,16 +10832,20 @@ pound sign (`#'):
           a[2] = "de"
           a[3] = "sac"
 
+     In order to reverse the direction of the sorted results in the
+     above example, `asort()' can be called with three arguments as
+     follows:
+
+          asort(a, a, "descending")
+
      The `asort()' function is described in more detail in *note Array
      Sorting::.  `asort()' is a `gawk' extension; it is not available
      in compatibility mode (*note Options::).
 
-`asorti(SOURCE [, DEST]) #'
+`asorti(SOURCE [, DEST [, HOW  ] ]) #'
      Return the number of elements in the array SOURCE.  It works
      similarly to `asort()', however, the _indices_ are sorted, instead
-     of the values.  As array indices are always strings, the
-     comparison performed is always a string comparison.  (Here too,
-     `IGNORECASE' affects the sorting.)
+     of the values. (Here too, `IGNORECASE' affects the sorting.)
 
      The `asorti()' function is described in more detail in *note Array
      Sorting::.  `asorti()' is a `gawk' extension; it is not available
@@ -24444,7 +24476,7 @@ Index
                                                               (line  67)
 * advanced features, data files as single record: Records.    (line 175)
 * advanced features, fixed-width data:   Constant Size.       (line   9)
-* advanced features, FNR/NR variables:   Auto-set.            (line 230)
+* advanced features, FNR/NR variables:   Auto-set.            (line 229)
 * advanced features, gawk:               Advanced Features.   (line   6)
 * advanced features, gawk, network programming: TCP/IP Networking.
                                                               (line   6)
@@ -24518,7 +24550,7 @@ Index
 * arrays, names of:                      Arrays.              (line  18)
 * arrays, scanning:                      Scanning an Array.   (line   6)
 * arrays, sorting:                       Array Sorting.       (line   6)
-* arrays, sorting, IGNORECASE variable and: Array Sorting.    (line  68)
+* arrays, sorting, IGNORECASE variable and: Array Sorting.    (line  81)
 * arrays, sparse:                        Array Intro.         (line  71)
 * arrays, subscripts:                    Numeric Array Subscripts.
                                                               (line   6)
@@ -24531,7 +24563,7 @@ Index
 * asort() function (gawk) <1>:           String Functions.    (line  29)
 * asort() function (gawk):               Array Sorting.       (line   6)
 * asort() function (gawk), arrays, sorting: Array Sorting.    (line   6)
-* asorti() function (gawk):              String Functions.    (line  57)
+* asorti() function (gawk):              String Functions.    (line  74)
 * assert() function (C library):         Assert Function.     (line   6)
 * assert() user-defined function:        Assert Function.     (line  28)
 * assertions:                            Assert Function.     (line   6)
@@ -24775,7 +24807,7 @@ Index
 * caret (^), in bracket expressions:     Bracket Expressions. (line  16)
 * case keyword:                          Switch Statement.    (line   6)
 * case sensitivity, array indices and:   Array Intro.         (line  92)
-* case sensitivity, converting case:     String Functions.    (line 504)
+* case sensitivity, converting case:     String Functions.    (line 519)
 * case sensitivity, example programs:    Library Functions.   (line  42)
 * case sensitivity, gawk:                Case-sensitivity.    (line  26)
 * case sensitivity, regexps and <1>:     User-modified.       (line  82)
@@ -24850,7 +24882,7 @@ Index
 * common extensions, fflush() function:  I/O Functions.       (line  25)
 * common extensions, func keyword:       Definition Syntax.   (line  83)
 * common extensions, length() applied to an array: String Functions.
-                                                              (line 178)
+                                                              (line 193)
 * common extensions, nextfile statement: Nextfile Statement.  (line   6)
 * common extensions, RS as a regexp:     Records.             (line 115)
 * common extensions, single character fields: Single Character Fields.
@@ -24892,7 +24924,7 @@ Index
 * constants, types of:                   Constants.           (line   6)
 * continue statement:                    Continue Statement.  (line   6)
 * control statements:                    Statements.          (line   6)
-* converting, case:                      String Functions.    (line 504)
+* converting, case:                      String Functions.    (line 519)
 * converting, dates to timestamps:       Time Functions.      (line  74)
 * converting, during subscripting:       Numeric Array Subscripts.
                                                               (line  31)
@@ -24944,13 +24976,13 @@ Index
                                                               (line  47)
 * dark corner, FILENAME variable <1>:    Auto-set.            (line  92)
 * dark corner, FILENAME variable:        Getline Notes.       (line  19)
-* dark corner, FNR/NR variables:         Auto-set.            (line 230)
+* dark corner, FNR/NR variables:         Auto-set.            (line 229)
 * dark corner, format-control characters: Control Letters.    (line  18)
 * dark corner, FS as null string:        Single Character Fields.
                                                               (line  20)
 * dark corner, input files:              Records.             (line  98)
 * dark corner, invoking awk:             Command Line.        (line  16)
-* dark corner, length() function:        String Functions.    (line 164)
+* dark corner, length() function:        String Functions.    (line 179)
 * dark corner, multiline records:        Multiple Line.       (line  35)
 * dark corner, NF variable, decrementing: Changing Fields.    (line 107)
 * dark corner, OFMT variable:            OFMT.                (line  27)
@@ -24960,7 +24992,7 @@ Index
                                                               (line 148)
 * dark corner, regexp constants, as arguments to user-defined functions: Using 
Constant Regexps.
                                                               (line  43)
-* dark corner, split() function:         String Functions.    (line 343)
+* dark corner, split() function:         String Functions.    (line 358)
 * dark corner, strings, storing:         Records.             (line 191)
 * dark corner, value of ARGV[0]:         Auto-set.            (line  35)
 * data, fixed-width:                     Constant Size.       (line   9)
@@ -25091,7 +25123,7 @@ Index
 * deleting elements in arrays:           Delete.              (line   6)
 * deleting entire arrays:                Delete.              (line  39)
 * dgawk:                                 Debugger.            (line   6)
-* differences between gawk and awk:      String Functions.    (line 178)
+* differences between gawk and awk:      String Functions.    (line 193)
 * differences in awk and gawk, ARGC/ARGV variables: ARGC and ARGV.
                                                               (line  88)
 * differences in awk and gawk, ARGIND variable: Auto-set.     (line  40)
@@ -25131,7 +25163,7 @@ Index
                                                               (line  34)
 * differences in awk and gawk, LINT variable: User-modified.  (line  98)
 * differences in awk and gawk, match() function: String Functions.
-                                                              (line 241)
+                                                              (line 256)
 * differences in awk and gawk, next/nextfile statements: Nextfile Statement.
                                                               (line   6)
 * differences in awk and gawk, print/printf statements: Format Modifiers.
@@ -25143,15 +25175,15 @@ Index
 * differences in awk and gawk, regular expressions: Case-sensitivity.
                                                               (line  26)
 * differences in awk and gawk, RS/RT variables: Records.      (line 167)
-* differences in awk and gawk, RT variable: Auto-set.         (line 219)
+* differences in awk and gawk, RT variable: Auto-set.         (line 218)
 * differences in awk and gawk, single-character fields: Single Character 
Fields.
                                                               (line   6)
 * differences in awk and gawk, split() function: String Functions.
-                                                              (line 331)
+                                                              (line 346)
 * differences in awk and gawk, strings:  Scalar Constants.    (line  20)
 * differences in awk and gawk, strings, storing: Records.     (line 187)
 * differences in awk and gawk, strtonum() function (gawk): String Functions.
-                                                              (line 386)
+                                                              (line 401)
 * differences in awk and gawk, TEXTDOMAIN variable: User-modified.
                                                               (line 153)
 * differences in awk and gawk, trunc-mod operation: Arithmetic Ops.
@@ -25299,7 +25331,7 @@ Index
 * extensions, common, fflush() function: I/O Functions.       (line  25)
 * extensions, common, func keyword:      Definition Syntax.   (line  83)
 * extensions, common, length() applied to an array: String Functions.
-                                                              (line 178)
+                                                              (line 193)
 * extensions, common, nextfile statement: Nextfile Statement. (line   6)
 * extensions, common, RS as a regexp:    Records.             (line 115)
 * extensions, common, single character fields: Single Character Fields.
@@ -25424,7 +25456,7 @@ Index
 * floating-point, numbers, AWKNUM internal type: Internals.   (line  19)
 * FNR variable <1>:                      Auto-set.            (line 102)
 * FNR variable:                          Records.             (line   6)
-* FNR variable, changing:                Auto-set.            (line 230)
+* FNR variable, changing:                Auto-set.            (line 229)
 * for statement:                         For Statement.       (line   6)
 * for statement, in arrays:              Scanning an Array.   (line  24)
 * force_number() internal function:      Internals.           (line  27)
@@ -25561,7 +25593,7 @@ Index
 * gawk, functions, adding:               Dynamic Extensions.  (line  10)
 * gawk, hexadecimal numbers and:         Nondecimal-numbers.  (line  42)
 * gawk, IGNORECASE variable in <1>:      String Functions.    (line  29)
-* gawk, IGNORECASE variable in <2>:      Array Sorting.       (line  68)
+* gawk, IGNORECASE variable in <2>:      Array Sorting.       (line  81)
 * gawk, IGNORECASE variable in <3>:      Array Intro.         (line  92)
 * gawk, IGNORECASE variable in <4>:      User-modified.       (line  82)
 * gawk, IGNORECASE variable in:          Case-sensitivity.    (line  26)
@@ -25596,7 +25628,7 @@ Index
 * gawk, regular expressions, operators:  GNU Regexp Operators.
                                                               (line   6)
 * gawk, regular expressions, precedence: Regexp Operators.    (line 157)
-* gawk, RT variable in <1>:              Auto-set.            (line 219)
+* gawk, RT variable in <1>:              Auto-set.            (line 218)
 * gawk, RT variable in <2>:              Getline/Variable/File.
                                                               (line  10)
 * gawk, RT variable in <3>:              Multiple Line.       (line 129)
@@ -25614,7 +25646,7 @@ Index
                                                               (line  63)
 * General Public License (GPL):          Glossary.            (line 306)
 * General Public License, See GPL:       Manual History.      (line  11)
-* gensub() function (gawk) <1>:          String Functions.    (line  68)
+* gensub() function (gawk) <1>:          String Functions.    (line  83)
 * gensub() function (gawk):              Using Constant Regexps.
                                                               (line  43)
 * gensub() function (gawk), escape processing: Gory Details.  (line   6)
@@ -25683,10 +25715,10 @@ Index
 * group database, reading:               Group Functions.     (line   6)
 * group file:                            Group Functions.     (line   6)
 * groups, information about:             Group Functions.     (line   6)
-* gsub() function <1>:                   String Functions.    (line 121)
+* gsub() function <1>:                   String Functions.    (line 136)
 * gsub() function:                       Using Constant Regexps.
                                                               (line  43)
-* gsub() function, arguments of:         String Functions.    (line 444)
+* gsub() function, arguments of:         String Functions.    (line 459)
 * gsub() function, escape processing:    Gory Details.        (line   6)
 * h debugger command (alias for help):   Miscellaneous Dgawk Commands.
                                                               (line  68)
@@ -25720,11 +25752,11 @@ Index
 * igawk.sh program:                      Igawk Program.       (line 124)
 * ignore debugger command:               Breakpoint Control.  (line  86)
 * IGNORECASE variable <1>:               String Functions.    (line  29)
-* IGNORECASE variable <2>:               Array Sorting.       (line  68)
+* IGNORECASE variable <2>:               Array Sorting.       (line  81)
 * IGNORECASE variable <3>:               Array Intro.         (line  92)
 * IGNORECASE variable <4>:               User-modified.       (line  82)
 * IGNORECASE variable:                   Case-sensitivity.    (line  26)
-* IGNORECASE variable, array sorting and: Array Sorting.      (line  68)
+* IGNORECASE variable, array sorting and: Array Sorting.      (line  81)
 * IGNORECASE variable, array subscripts and: Array Intro.     (line  92)
 * IGNORECASE variable, in example programs: Library Functions.
                                                               (line  42)
@@ -25741,7 +25773,7 @@ Index
 * in operator, arrays and:               Reference to Elements.
                                                               (line  37)
 * increment operators:                   Increment Ops.       (line   6)
-* index() function:                      String Functions.    (line 137)
+* index() function:                      String Functions.    (line 152)
 * indexing arrays:                       Array Intro.         (line  50)
 * indirect function calls:               Indirect Calls.      (line   6)
 * info debugger command:                 Dgawk Info.          (line  12)
@@ -25877,7 +25909,7 @@ Index
                                                               (line  11)
 * left shift, bitwise:                   Bitwise Functions.   (line  32)
 * leftmost longest match:                Multiple Line.       (line  26)
-* length() function:                     String Functions.    (line 148)
+* length() function:                     String Functions.    (line 163)
 * Lesser General Public License (LGPL):  Glossary.            (line 385)
 * LGPL (Lesser General Public License):  Glossary.            (line 385)
 * libmawk:                               Other Versions.      (line 104)
@@ -25961,9 +25993,9 @@ Index
                                                               (line   6)
 * marked strings, extracting:            String Extraction.   (line   6)
 * Marx, Groucho:                         Increment Ops.       (line  61)
-* match() function:                      String Functions.    (line 188)
+* match() function:                      String Functions.    (line 203)
 * match() function, RSTART/RLENGTH variables: String Functions.
-                                                              (line 205)
+                                                              (line 220)
 * matching, expressions, See comparison expressions: Typing and Comparison.
                                                               (line   9)
 * matching, leftmost longest:            Multiple Line.       (line  26)
@@ -26038,7 +26070,7 @@ Index
 * not Boolean-logic operator:            Boolean Ops.         (line   6)
 * NR variable <1>:                       Auto-set.            (line 118)
 * NR variable:                           Records.             (line   6)
-* NR variable, changing:                 Auto-set.            (line 230)
+* NR variable, changing:                 Auto-set.            (line 229)
 * null strings <1>:                      Basic Data Typing.   (line  50)
 * null strings <2>:                      Truth Values.        (line   6)
 * null strings <3>:                      Regexp Field Splitting.
@@ -26162,7 +26194,7 @@ Index
 * parentheses ():                        Regexp Operators.    (line  79)
 * parentheses (), pgawk program:         Profiling.           (line 141)
 * password file:                         Passwd Functions.    (line  16)
-* patsplit() function:                   String Functions.    (line 275)
+* patsplit() function:                   String Functions.    (line 290)
 * patterns:                              Patterns and Actions.
                                                               (line   6)
 * patterns, comparison expressions as:   Expression Patterns. (line  14)
@@ -26220,7 +26252,7 @@ Index
 * portability, gawk:                     New Ports.           (line   6)
 * portability, gettext library and:      Explaining gettext.  (line  10)
 * portability, internationalization and: I18N Portability.    (line   6)
-* portability, length() function:        String Functions.    (line 157)
+* portability, length() function:        String Functions.    (line 172)
 * portability, new awk vs. old awk:      Conversion.          (line  55)
 * portability, next statement in user-defined functions: Pass By 
Value/Reference.
                                                               (line  91)
@@ -26228,7 +26260,7 @@ Index
 * portability, operators:                Increment Ops.       (line  61)
 * portability, operators, not in POSIX awk: Precedence.       (line  98)
 * portability, POSIXLY_CORRECT environment variable: Options. (line 305)
-* portability, substr() function:        String Functions.    (line 494)
+* portability, substr() function:        String Functions.    (line 509)
 * portable object files <1>:             Translator i18n.     (line   6)
 * portable object files:                 Explaining gettext.  (line  36)
 * portable object files, converting to message object files: I18N Example.
@@ -26264,7 +26296,7 @@ Index
 * POSIX awk, field separators and:       Fields.              (line   6)
 * POSIX awk, FS variable and:            User-modified.       (line  66)
 * POSIX awk, function keyword in:        Definition Syntax.   (line  83)
-* POSIX awk, functions and, length():    String Functions.    (line 157)
+* POSIX awk, functions and, length():    String Functions.    (line 172)
 * POSIX awk, GNU long options and:       Options.             (line  15)
 * POSIX awk, interval expressions in:    Regexp Operators.    (line 135)
 * POSIX awk, next/nextfile statements and: Next Statement.    (line  45)
@@ -26402,7 +26434,7 @@ Index
 * recursive functions:                   Definition Syntax.   (line  73)
 * redirection of input:                  Getline/File.        (line   6)
 * redirection of output:                 Redirection.         (line   6)
-* reference counting, sorting arrays:    Array Sorting.       (line  62)
+* reference counting, sorting arrays:    Array Sorting.       (line  75)
 * regexp constants <1>:                  Comparison Operators.
                                                               (line 103)
 * regexp constants <2>:                  Regexp Constants.    (line   6)
@@ -26468,8 +26500,8 @@ Index
 * right angle bracket (>), >> operator (I/O): Redirection.    (line  50)
 * right shift, bitwise:                  Bitwise Functions.   (line  32)
 * Ritchie, Dennis:                       Basic Data Typing.   (line  74)
-* RLENGTH variable:                      Auto-set.            (line 206)
-* RLENGTH variable, match() function and: String Functions.   (line 205)
+* RLENGTH variable:                      Auto-set.            (line 205)
+* RLENGTH variable, match() function and: String Functions.   (line 220)
 * Robbins, Arnold <1>:                   Future Extensions.   (line   6)
 * Robbins, Arnold <2>:                   Bugs.                (line  32)
 * Robbins, Arnold <3>:                   Contributors.        (line 106)
@@ -26493,9 +26525,9 @@ Index
 * RS variable:                           Records.             (line  20)
 * RS variable, multiline records and:    Multiple Line.       (line  17)
 * rshift() function (gawk):              Bitwise Functions.   (line  51)
-* RSTART variable:                       Auto-set.            (line 212)
-* RSTART variable, match() function and: String Functions.    (line 205)
-* RT variable <1>:                       Auto-set.            (line 219)
+* RSTART variable:                       Auto-set.            (line 211)
+* RSTART variable, match() function and: String Functions.    (line 220)
+* RT variable <1>:                       Auto-set.            (line 218)
 * RT variable <2>:                       Getline/Variable/File.
                                                               (line  10)
 * RT variable <3>:                       Multiple Line.       (line 129)
@@ -26521,7 +26553,7 @@ Index
 * search paths, for source files <2>:    PC Using.            (line  11)
 * search paths, for source files <3>:    Igawk Program.       (line 364)
 * search paths, for source files:        AWKPATH Variable.    (line   6)
-* searching:                             String Functions.    (line 137)
+* searching:                             String Functions.    (line 152)
 * searching, files for regular expressions: Egrep Program.    (line   6)
 * searching, for words:                  Dupword Program.     (line   6)
 * sed utility <1>:                       Glossary.            (line  12)
@@ -26560,7 +26592,7 @@ Index
 * side effects:                          Concatenation.       (line  42)
 * side effects, array indexing:          Reference to Elements.
                                                               (line  42)
-* side effects, asort() function:        Array Sorting.       (line  25)
+* side effects, asort() function:        Array Sorting.       (line  24)
 * side effects, assignment expressions:  Assignment Ops.      (line  23)
 * side effects, Boolean operators:       Boolean Ops.         (line  30)
 * side effects, conditional expressions: Conditional Exp.     (line  22)
@@ -26612,10 +26644,10 @@ Index
 * sparse arrays:                         Array Intro.         (line  71)
 * Spencer, Henry:                        Glossary.            (line  12)
 * split utility:                         Split Program.       (line   6)
-* split() function:                      String Functions.    (line 297)
+* split() function:                      String Functions.    (line 312)
 * split() function, array elements, deleting: Delete.         (line  57)
 * split.awk program:                     Split Program.       (line  30)
-* sprintf() function <1>:                String Functions.    (line 362)
+* sprintf() function <1>:                String Functions.    (line 377)
 * sprintf() function:                    OFMT.                (line  15)
 * sprintf() function, OFMT variable and: User-modified.       (line 124)
 * sprintf() function, print/printf statements and: Round Function.
@@ -26664,14 +26696,14 @@ Index
 * strings, null:                         Regexp Field Splitting.
                                                               (line  43)
 * strings, numeric:                      Variable Typing.     (line   6)
-* strings, splitting:                    String Functions.    (line 317)
-* strtonum() function (gawk):            String Functions.    (line 369)
+* strings, splitting:                    String Functions.    (line 332)
+* strtonum() function (gawk):            String Functions.    (line 384)
 * strtonum() function (gawk), --non-decimal-data option and: Nondecimal Data.
                                                               (line  36)
-* sub() function <1>:                    String Functions.    (line 390)
+* sub() function <1>:                    String Functions.    (line 405)
 * sub() function:                        Using Constant Regexps.
                                                               (line  43)
-* sub() function, arguments of:          String Functions.    (line 444)
+* sub() function, arguments of:          String Functions.    (line 459)
 * sub() function, escape processing:     Gory Details.        (line   6)
 * subscript separators:                  User-modified.       (line 147)
 * subscripts in arrays, multidimensional: Multi-dimensional.  (line  10)
@@ -26684,7 +26716,7 @@ Index
 * SUBSEP variable:                       User-modified.       (line 147)
 * SUBSEP variable, multidimensional arrays: Multi-dimensional.
                                                               (line  16)
-* substr() function:                     String Functions.    (line 463)
+* substr() function:                     String Functions.    (line 478)
 * Sumner, Andrew:                        Other Versions.      (line  55)
 * switch statement:                      Switch Statement.    (line   6)
 * syntactic ambiguity: /= operator vs. /=.../ regexp constant: Assignment Ops.
@@ -26735,8 +26767,8 @@ Index
 * timestamps, converting dates to:       Time Functions.      (line  74)
 * timestamps, formatted:                 Gettimeofday Function.
                                                               (line   6)
-* tolower() function:                    String Functions.    (line 505)
-* toupper() function:                    String Functions.    (line 511)
+* tolower() function:                    String Functions.    (line 520)
+* toupper() function:                    String Functions.    (line 526)
 * tr utility:                            Translate Program.   (line   6)
 * trace debugger command:                Miscellaneous Dgawk Commands.
                                                               (line 110)
@@ -26759,9 +26791,9 @@ Index
 * troubleshooting, gawk, fatal errors, function arguments: Calling Built-in.
                                                               (line  16)
 * troubleshooting, getline function:     File Checking.       (line  25)
-* troubleshooting, gsub()/sub() functions: String Functions.  (line 454)
-* troubleshooting, match() function:     String Functions.    (line 270)
-* troubleshooting, patsplit() function:  String Functions.    (line 293)
+* troubleshooting, gsub()/sub() functions: String Functions.  (line 469)
+* troubleshooting, match() function:     String Functions.    (line 285)
+* troubleshooting, patsplit() function:  String Functions.    (line 308)
 * troubleshooting, print statement, omitting commas: Print Examples.
                                                               (line  31)
 * troubleshooting, printing:             Redirection.         (line 118)
@@ -26770,7 +26802,7 @@ Index
 * troubleshooting, regexp constants vs. string constants: Computed Regexps.
                                                               (line  38)
 * troubleshooting, string concatenation: Concatenation.       (line  27)
-* troubleshooting, substr() function:    String Functions.    (line 481)
+* troubleshooting, substr() function:    String Functions.    (line 496)
 * troubleshooting, system() function:    I/O Functions.       (line  85)
 * troubleshooting, typographical errors, global variables: Options.
                                                               (line  94)
@@ -27122,225 +27154,225 @@ Node: Built-in Variables383628
 Node: User-modified384723
 Ref: User-modified-Footnote-1392749
 Node: Auto-set392811
-Ref: Auto-set-Footnote-1403553
-Node: ARGC and ARGV403758
-Node: Arrays407609
-Node: Array Basics409180
-Node: Array Intro409891
-Node: Reference to Elements414209
-Node: Assigning Elements416479
-Node: Array Example416970
-Node: Scanning an Array418702
-Node: Controlling Scanning421078
-Node: Delete424416
-Ref: Delete-Footnote-1426851
-Node: Numeric Array Subscripts426908
-Node: Uninitialized Subscripts429091
-Node: Multi-dimensional430719
-Node: Multi-scanning433810
-Node: Array Sorting435394
-Ref: Array Sorting-Footnote-1438488
-Node: Arrays of Arrays438682
-Node: Functions443255
-Node: Built-in444077
-Node: Calling Built-in445155
-Node: Numeric Functions447143
-Ref: Numeric Functions-Footnote-1450908
-Ref: Numeric Functions-Footnote-2451265
-Ref: Numeric Functions-Footnote-3451313
-Node: String Functions451582
-Ref: String Functions-Footnote-1474084
-Ref: String Functions-Footnote-2474213
-Ref: String Functions-Footnote-3474461
-Node: Gory Details474548
-Ref: table-sub-escapes476227
-Ref: table-posix-sub477541
-Ref: table-gensub-escapes478454
-Node: I/O Functions479625
-Ref: I/O Functions-Footnote-1486280
-Node: Time Functions486427
-Ref: Time Functions-Footnote-1497319
-Ref: Time Functions-Footnote-2497387
-Ref: Time Functions-Footnote-3497545
-Ref: Time Functions-Footnote-4497656
-Ref: Time Functions-Footnote-5497768
-Ref: Time Functions-Footnote-6497995
-Node: Bitwise Functions498261
-Ref: table-bitwise-ops498819
-Ref: Bitwise Functions-Footnote-1502979
-Node: Type Functions503163
-Node: I18N Functions503633
-Node: User-defined505260
-Node: Definition Syntax506064
-Ref: Definition Syntax-Footnote-1510974
-Node: Function Example511043
-Node: Function Caveats513637
-Node: Calling A Function514058
-Node: Variable Scope515173
-Node: Pass By Value/Reference517148
-Node: Return Statement520588
-Node: Dynamic Typing523569
-Node: Indirect Calls524304
-Node: Internationalization533989
-Node: I18N and L10N535415
-Node: Explaining gettext536101
-Ref: Explaining gettext-Footnote-1541167
-Ref: Explaining gettext-Footnote-2541351
-Node: Programmer i18n541516
-Node: Translator i18n545716
-Node: String Extraction546509
-Ref: String Extraction-Footnote-1547470
-Node: Printf Ordering547556
-Ref: Printf Ordering-Footnote-1550340
-Node: I18N Portability550404
-Ref: I18N Portability-Footnote-1552853
-Node: I18N Example552916
-Ref: I18N Example-Footnote-1555551
-Node: Gawk I18N555623
-Node: Advanced Features556240
-Node: Nondecimal Data557559
-Node: Two-way I/O559140
-Ref: Two-way I/O-Footnote-1564574
-Node: TCP/IP Networking564644
-Node: Profiling567488
-Node: Library Functions574962
-Ref: Library Functions-Footnote-1577969
-Node: Library Names578140
-Ref: Library Names-Footnote-1581611
-Ref: Library Names-Footnote-2581831
-Node: General Functions581917
-Node: Strtonum Function582870
-Node: Assert Function585800
-Node: Round Function589126
-Node: Cliff Random Function590669
-Node: Ordinal Functions591685
-Ref: Ordinal Functions-Footnote-1594755
-Ref: Ordinal Functions-Footnote-2595007
-Node: Join Function595216
-Ref: Join Function-Footnote-1596987
-Node: Gettimeofday Function597187
-Node: Data File Management600902
-Node: Filetrans Function601534
-Node: Rewind Function605673
-Node: File Checking607060
-Node: Empty Files608154
-Node: Ignoring Assigns610384
-Node: Getopt Function611937
-Ref: Getopt Function-Footnote-1623241
-Node: Passwd Functions623444
-Ref: Passwd Functions-Footnote-1632419
-Node: Group Functions632507
-Node: Walking Arrays640591
-Node: Sample Programs642160
-Node: Running Examples642825
-Node: Clones643553
-Node: Cut Program644777
-Node: Egrep Program654622
-Ref: Egrep Program-Footnote-1662395
-Node: Id Program662505
-Node: Split Program666121
-Ref: Split Program-Footnote-1669640
-Node: Tee Program669768
-Node: Uniq Program672571
-Node: Wc Program680000
-Ref: Wc Program-Footnote-1684266
-Ref: Wc Program-Footnote-2684466
-Node: Miscellaneous Programs684558
-Node: Dupword Program685746
-Node: Alarm Program687777
-Node: Translate Program692526
-Ref: Translate Program-Footnote-1696913
-Ref: Translate Program-Footnote-2697141
-Node: Labels Program697275
-Ref: Labels Program-Footnote-1700646
-Node: Word Sorting700730
-Node: History Sorting704614
-Node: Extract Program706453
-Ref: Extract Program-Footnote-1713936
-Node: Simple Sed714064
-Node: Igawk Program717126
-Ref: Igawk Program-Footnote-1732159
-Ref: Igawk Program-Footnote-2732360
-Node: Anagram Program732498
-Node: Signature Program735566
-Node: Debugger736666
-Node: Debugging737577
-Node: Debugging Concepts737990
-Node: Debugging Terms739846
-Node: Awk Debugging742468
-Node: Sample dgawk session743360
-Node: dgawk invocation743852
-Node: Finding The Bug745034
-Node: List of Debugger Commands751520
-Node: Breakpoint Control752831
-Node: Dgawk Execution Control756467
-Node: Viewing And Changing Data759818
-Node: Dgawk Stack763155
-Node: Dgawk Info764615
-Node: Miscellaneous Dgawk Commands768563
-Node: Readline Support773991
-Node: Dgawk Limitations774829
-Node: Language History777018
-Node: V7/SVR3.1778456
-Node: SVR4780777
-Node: POSIX782219
-Node: BTL783227
-Node: POSIX/GNU783961
-Node: Common Extensions789062
-Node: Contributors790163
-Node: Installation794302
-Node: Gawk Distribution795196
-Node: Getting795680
-Node: Extracting796506
-Node: Distribution contents798198
-Node: Unix Installation803420
-Node: Quick Installation804037
-Node: Additional Configuration Options805999
-Node: Configuration Philosophy807476
-Node: Non-Unix Installation809818
-Node: PC Installation810276
-Node: PC Binary Installation811575
-Node: PC Compiling813423
-Node: PC Testing816367
-Node: PC Using817543
-Node: Cygwin821728
-Node: MSYS822728
-Node: VMS Installation823242
-Node: VMS Compilation823845
-Ref: VMS Compilation-Footnote-1824852
-Node: VMS Installation Details824910
-Node: VMS Running826545
-Node: VMS Old Gawk828152
-Node: Bugs828626
-Node: Other Versions832536
-Node: Notes837815
-Node: Compatibility Mode838507
-Node: Additions839290
-Node: Accessing The Source840102
-Node: Adding Code841527
-Node: New Ports847494
-Node: Dynamic Extensions851607
-Node: Internals852983
-Node: Plugin License862086
-Node: Sample Library862720
-Node: Internal File Description863406
-Node: Internal File Ops867121
-Ref: Internal File Ops-Footnote-1871902
-Node: Using Internal File Ops872042
-Node: Future Extensions874419
-Node: Basic Concepts876923
-Node: Basic High Level877680
-Ref: Basic High Level-Footnote-1881715
-Node: Basic Data Typing881900
-Node: Floating Point Issues886425
-Node: String Conversion Precision887508
-Ref: String Conversion Precision-Footnote-1889202
-Node: Unexpected Results889311
-Node: POSIX Floating Point Problems891137
-Ref: POSIX Floating Point Problems-Footnote-1894839
-Node: Glossary894877
-Node: Copying919020
-Node: GNU Free Documentation License956577
-Node: Index981714
+Ref: Auto-set-Footnote-1403524
+Node: ARGC and ARGV403729
+Node: Arrays407580
+Node: Array Basics409151
+Node: Array Intro409862
+Node: Reference to Elements414180
+Node: Assigning Elements416450
+Node: Array Example416941
+Node: Scanning an Array418673
+Node: Controlling Scanning421049
+Node: Delete424695
+Ref: Delete-Footnote-1427130
+Node: Numeric Array Subscripts427187
+Node: Uninitialized Subscripts429370
+Node: Multi-dimensional430998
+Node: Multi-scanning434089
+Node: Array Sorting435673
+Ref: Array Sorting-Footnote-1439452
+Node: Arrays of Arrays439646
+Node: Functions444219
+Node: Built-in445041
+Node: Calling Built-in446119
+Node: Numeric Functions448107
+Ref: Numeric Functions-Footnote-1451872
+Ref: Numeric Functions-Footnote-2452229
+Ref: Numeric Functions-Footnote-3452277
+Node: String Functions452546
+Ref: String Functions-Footnote-1475818
+Ref: String Functions-Footnote-2475947
+Ref: String Functions-Footnote-3476195
+Node: Gory Details476282
+Ref: table-sub-escapes477961
+Ref: table-posix-sub479275
+Ref: table-gensub-escapes480188
+Node: I/O Functions481359
+Ref: I/O Functions-Footnote-1488014
+Node: Time Functions488161
+Ref: Time Functions-Footnote-1499053
+Ref: Time Functions-Footnote-2499121
+Ref: Time Functions-Footnote-3499279
+Ref: Time Functions-Footnote-4499390
+Ref: Time Functions-Footnote-5499502
+Ref: Time Functions-Footnote-6499729
+Node: Bitwise Functions499995
+Ref: table-bitwise-ops500553
+Ref: Bitwise Functions-Footnote-1504713
+Node: Type Functions504897
+Node: I18N Functions505367
+Node: User-defined506994
+Node: Definition Syntax507798
+Ref: Definition Syntax-Footnote-1512708
+Node: Function Example512777
+Node: Function Caveats515371
+Node: Calling A Function515792
+Node: Variable Scope516907
+Node: Pass By Value/Reference518882
+Node: Return Statement522322
+Node: Dynamic Typing525303
+Node: Indirect Calls526038
+Node: Internationalization535723
+Node: I18N and L10N537149
+Node: Explaining gettext537835
+Ref: Explaining gettext-Footnote-1542901
+Ref: Explaining gettext-Footnote-2543085
+Node: Programmer i18n543250
+Node: Translator i18n547450
+Node: String Extraction548243
+Ref: String Extraction-Footnote-1549204
+Node: Printf Ordering549290
+Ref: Printf Ordering-Footnote-1552074
+Node: I18N Portability552138
+Ref: I18N Portability-Footnote-1554587
+Node: I18N Example554650
+Ref: I18N Example-Footnote-1557285
+Node: Gawk I18N557357
+Node: Advanced Features557974
+Node: Nondecimal Data559293
+Node: Two-way I/O560874
+Ref: Two-way I/O-Footnote-1566308
+Node: TCP/IP Networking566378
+Node: Profiling569222
+Node: Library Functions576696
+Ref: Library Functions-Footnote-1579703
+Node: Library Names579874
+Ref: Library Names-Footnote-1583345
+Ref: Library Names-Footnote-2583565
+Node: General Functions583651
+Node: Strtonum Function584604
+Node: Assert Function587534
+Node: Round Function590860
+Node: Cliff Random Function592403
+Node: Ordinal Functions593419
+Ref: Ordinal Functions-Footnote-1596489
+Ref: Ordinal Functions-Footnote-2596741
+Node: Join Function596950
+Ref: Join Function-Footnote-1598721
+Node: Gettimeofday Function598921
+Node: Data File Management602636
+Node: Filetrans Function603268
+Node: Rewind Function607407
+Node: File Checking608794
+Node: Empty Files609888
+Node: Ignoring Assigns612118
+Node: Getopt Function613671
+Ref: Getopt Function-Footnote-1624975
+Node: Passwd Functions625178
+Ref: Passwd Functions-Footnote-1634153
+Node: Group Functions634241
+Node: Walking Arrays642325
+Node: Sample Programs643894
+Node: Running Examples644559
+Node: Clones645287
+Node: Cut Program646511
+Node: Egrep Program656356
+Ref: Egrep Program-Footnote-1664129
+Node: Id Program664239
+Node: Split Program667855
+Ref: Split Program-Footnote-1671374
+Node: Tee Program671502
+Node: Uniq Program674305
+Node: Wc Program681734
+Ref: Wc Program-Footnote-1686000
+Ref: Wc Program-Footnote-2686200
+Node: Miscellaneous Programs686292
+Node: Dupword Program687480
+Node: Alarm Program689511
+Node: Translate Program694260
+Ref: Translate Program-Footnote-1698647
+Ref: Translate Program-Footnote-2698875
+Node: Labels Program699009
+Ref: Labels Program-Footnote-1702380
+Node: Word Sorting702464
+Node: History Sorting706348
+Node: Extract Program708187
+Ref: Extract Program-Footnote-1715670
+Node: Simple Sed715798
+Node: Igawk Program718860
+Ref: Igawk Program-Footnote-1733893
+Ref: Igawk Program-Footnote-2734094
+Node: Anagram Program734232
+Node: Signature Program737300
+Node: Debugger738400
+Node: Debugging739311
+Node: Debugging Concepts739724
+Node: Debugging Terms741580
+Node: Awk Debugging744202
+Node: Sample dgawk session745094
+Node: dgawk invocation745586
+Node: Finding The Bug746768
+Node: List of Debugger Commands753254
+Node: Breakpoint Control754565
+Node: Dgawk Execution Control758201
+Node: Viewing And Changing Data761552
+Node: Dgawk Stack764889
+Node: Dgawk Info766349
+Node: Miscellaneous Dgawk Commands770297
+Node: Readline Support775725
+Node: Dgawk Limitations776563
+Node: Language History778752
+Node: V7/SVR3.1780190
+Node: SVR4782511
+Node: POSIX783953
+Node: BTL784961
+Node: POSIX/GNU785695
+Node: Common Extensions790796
+Node: Contributors791897
+Node: Installation796036
+Node: Gawk Distribution796930
+Node: Getting797414
+Node: Extracting798240
+Node: Distribution contents799932
+Node: Unix Installation805154
+Node: Quick Installation805771
+Node: Additional Configuration Options807733
+Node: Configuration Philosophy809210
+Node: Non-Unix Installation811552
+Node: PC Installation812010
+Node: PC Binary Installation813309
+Node: PC Compiling815157
+Node: PC Testing818101
+Node: PC Using819277
+Node: Cygwin823462
+Node: MSYS824462
+Node: VMS Installation824976
+Node: VMS Compilation825579
+Ref: VMS Compilation-Footnote-1826586
+Node: VMS Installation Details826644
+Node: VMS Running828279
+Node: VMS Old Gawk829886
+Node: Bugs830360
+Node: Other Versions834270
+Node: Notes839549
+Node: Compatibility Mode840241
+Node: Additions841024
+Node: Accessing The Source841836
+Node: Adding Code843261
+Node: New Ports849228
+Node: Dynamic Extensions853341
+Node: Internals854717
+Node: Plugin License863820
+Node: Sample Library864454
+Node: Internal File Description865140
+Node: Internal File Ops868855
+Ref: Internal File Ops-Footnote-1873636
+Node: Using Internal File Ops873776
+Node: Future Extensions876153
+Node: Basic Concepts878657
+Node: Basic High Level879414
+Ref: Basic High Level-Footnote-1883449
+Node: Basic Data Typing883634
+Node: Floating Point Issues888159
+Node: String Conversion Precision889242
+Ref: String Conversion Precision-Footnote-1890936
+Node: Unexpected Results891045
+Node: POSIX Floating Point Problems892871
+Ref: POSIX Floating Point Problems-Footnote-1896573
+Node: Glossary896611
+Node: Copying920754
+Node: GNU Free Documentation License958311
+Node: Index983448
 
 End Tag Table
diff --git a/doc/gawk.texi b/doc/gawk.texi
index 2adad8b..def2a01 100644
--- a/doc/gawk.texi
+++ b/doc/gawk.texi
@@ -12755,9 +12755,8 @@ The value should contain one to three words; separate 
pairs of words
 by a single space.
 One word controls sort direction, @samp{ascending} or @samp{descending};
 another controls the sort key, @samp{index} or @samp{value}; and the remaining
-one, which is only valid for sorting by index, is comparison mode,
address@hidden or @samp{number}.  When two or three words are present, they may
-be specified in any order, so @samp{ascending index string} and 
+one affects comparison mode, @samp{string} or @samp{number}.  When two or three
+words are present, they may be specified in any order, so @samp{ascending 
index string} and 
 @samp{string ascending index} are equivalent.  Also, each word may
 be truncated, so @samp{asc index str} and @samp{a i s} are also
 equivalent.  Note that a separating space is required even when the
@@ -12765,11 +12764,10 @@ words have been shortened down to one letter each.
 
 You can omit direction and/or key type and/or comparison mode.  Provided
 that at least one is present, the missing parts of a sort specification
-default to @samp{ascending}, @samp{index}, and (for indices only) 
@samp{string},
-respectively.
-An empty string, @code{""}, is the same as @samp{unsorted} and will cause
address@hidden (index in array) @dots{}} to process the indices in
-arbitrary order.  Another thing to note is that the array sorting
+default to @samp{ascending}, @samp{index}, and @samp{string}, respectively.
+An empty string, @code{""}, is the same as @samp{ascending index string},
+and a value of @samp{unsorted} will cause @samp{for (index in array) @dots{}} 
to process
+the indices in arbitrary order.  Another thing to note is that the array 
sorting
 takes place at the time the @code{for} loop is about to
 start executing, so changing the value of @code{PROCINFO["sorted_in"]}
 during loop execution does not have any effect on the order in which any
@@ -13465,11 +13463,14 @@ the index is actually @code{"10"} rather than numeric 
10.)
 Order by indices but force them to be treated as numbers in the process.
 Any index with non-numeric value will end up positioned as if it were zero. 
 
address@hidden ascending value
-Order by element values rather than by indices.  Comparisons are done
-as numeric when both values being compared are numeric, or done as
-strings when either or both aren't numeric (@pxref{Variable Typing}).
-Subarrays, if present, come out last.
address@hidden ascending value string
+Order by element values rather than by indices.  Scalar values are 
+compared as strings.  Subarrays, if present, come out last.
+
address@hidden ascending value number
+Order by values but force scalar values to be treated as numbers
+for the purpose of comparison.  If there are subarrays, those appear
+at the end of the sorted list.
 
 @item descending index string
 Reverse order from the most basic sort.
@@ -13477,22 +13478,31 @@ Reverse order from the most basic sort.
 @item descending index number
 Numeric indices ordered from high to low.
 
address@hidden descending value
-Element values ordered from high to low.  Subarrays, if present,
address@hidden descending value string
+Element values, treated as strings, ordered from high to low.  Subarrays, if 
present,
+come out first.
+
address@hidden descending value number
+Element values, treated as numbers, ordered from high to low.  Subarrays, if 
present,
 come out first.
 
 @item unsorted
 Array elements are processed in arbitrary order, the normal @command{awk}
-behavior.
+behavior. You can also get the normal behavior by just
+deleting the @code{"sorted_in"} item from the @code{PROCINFO} array, if
+it previously had a value assigned to it.
 @end table
 
 The array traversal order is determined before the @code{for} loop
-starts to run. Changing @code{PROCINFO["sorted_in"]} in the looop body
+starts to run. Changing @code{PROCINFO["sorted_in"]} in the loop body
 will not affect the loop.
 
 Portions of the sort specification string may be truncated or omitted.
 The default is @samp{ascending} for direction, @samp{index} for sort key type,
-and (when sorting by index only) @samp{string} for comparison mode.
+and @samp{string} for comparison mode.  This implies that one can
+simply assign the empty string, "", instead of "ascending index string" to
address@hidden"sorted_in"]} for the same effect.
+
 For example:
 
 @example
@@ -13521,11 +13531,6 @@ numeric value, regardless of what the subarray itself 
contains,
 and all subarrays are treated as being equal to each other.  Their 
 order relative to each other is determined by their index strings.
 
-Sorting by array element values (for values other than subarrays)
-always uses basic @command{awk} comparison mode:  if both values
-happen to be numbers then they're compared as numbers, otherwise
-they're compared as strings.
-
 When string comparisons are made during a sort, either for element
 values where one or both aren't numbers or for element indices
 handled as strings, the value of @code{IGNORECASE}
@@ -13952,9 +13957,7 @@ After the call to @code{asort()}, the array @code{data} 
is indexed from 1
 to some number @var{n}, the total number of elements in @code{data}.
 (This count is @code{asort()}'s return value.)
 @code{data[1]} @value{LEQ} @code{data[2]} @value{LEQ} @code{data[3]}, and so 
on.
-The comparison of array elements is done
-using @command{gawk}'s usual comparison rules
-(@pxref{Typing and Comparison}).
+The array elements are compared as strings.
 
 @cindex side effects, @code{asort()} function
 An important side effect of calling @code{asort()} is that
@@ -13973,6 +13976,22 @@ In this case, @command{gawk} copies the @code{source} 
array into the
 @code{dest} array and then sorts @code{dest}, destroying its indices.
 However, the @code{source} array is not affected.
 
address@hidden()} and @code{asorti()} accept a third string argument
+to control the comparison rule for the array elements, and the direction
+of the sorted results.  The valid comparison modes are @samp{string} and 
@samp{number},
+and the direction can be either @samp{ascending} or @samp{descending}.   
+Either mode or direction, or both, can be omitted in which
+case the defaults, @samp{string} or @samp{ascending} is assumed
+for the comparison mode and the direction, respectively.  Seperate comparison
+mode from direction with a single space, and they can appear in any
+order.  To compare the elements as numbers, and to reverse the elements
+of the @code{dest} array, the call to asort in the above example can be
+replaced with:
+
address@hidden
+asort(source, dest, "descending number")
address@hidden example
+
 Often, what's needed is to sort on the values of the @emph{indices}
 instead of the values of the elements.
 To do that, use the
@@ -13997,7 +14016,9 @@ END @{
 
 Sorting the array by replacing the indices provides maximal flexibility.
 To traverse the elements in decreasing order, use a loop that goes from
address@hidden down to 1, either over the elements or over the indices.
address@hidden down to 1, either over the elements or over the indices.  This
+is an alternative to specifying @samp{descending} for the sorting order
+using the optional third argument.
 
 @cindex reference counting, sorting arrays
 Copying array indices and elements isn't expensive in terms of memory.
@@ -14011,10 +14032,8 @@ both arrays use the values.
 @cindex @code{IGNORECASE} variable
 @cindex arrays, sorting, @code{IGNORECASE} variable and
 @cindex @code{IGNORECASE} variable, array sorting and
-We said previously that comparisons are done using @command{gawk}'s
-``usual comparison rules.''  Because @code{IGNORECASE} affects
-string comparisons, the value of @code{IGNORECASE} also
-affects sorting for both @code{asort()} and @code{asorti()}.
+Because @code{IGNORECASE} affects string comparisons, the value
+of @code{IGNORECASE} also affects sorting for both @code{asort()} and 
@code{asorti()}.
 Note also that the locale's sorting order does @emph{not}
 come into play; comparisons are based on character values address@hidden
 is true because locale-based comparison occurs only when in POSIX
@@ -14439,20 +14458,29 @@ pound address@hidden (@samp{#}):}
 @end menu
 
 @table @code
address@hidden asort(@var{source} @r{[}, @address@hidden) #
address@hidden asort(@var{source} @r{[}, @var{dest} @r{[}, @var{how}  @r{]} 
@r{]}) #
 @cindex arrays, elements, retrieving number of
 @cindex @code{asort()} function (@command{gawk})
 @cindex @command{gawk}, @code{IGNORECASE} variable in
 @cindex @code{IGNORECASE} variable
 Return the number of elements in the array @var{source}.
 @command{gawk} sorts the contents of @var{source}
-using the normal rules for comparing values
-(in particular, @code{IGNORECASE} affects the sorting)
 and replaces the indices
 of the sorted values of @var{source} with sequential
-integers starting with one. If the optional array @var{dest} is specified,
+integers starting with one.  If the optional array @var{dest} is specified,
 then @var{source} is duplicated into @var{dest}.  @var{dest} is then
-sorted, leaving the indices of @var{source} unchanged.
+sorted, leaving the indices of @var{source} unchanged.  The optional third
+argument @var{how} is a string which controls the rule for comparing values,
+and the sort direction.  A single space is required between the
+comparison mode, @samp{string} or @samp{number}, and the direction 
specification,
address@hidden or @samp{descending}.  You can omit direction and/or mode
+in which case it will default to @samp{ascending} and @samp{string}, 
respectively. 
+An empty string "" is the same as the default @code{"ascending string"}
+for the value of @var{how}.  If the @samp{source} array contains subarrays as 
values,
+they will come out last(first) in the @samp{dest} array for 
@samp{ascending}(@samp{descending})
+order specification.  The value of @code{IGNORECASE} affects the sorting.
address@hidden an Array}, for more information.
+
 For example, if the contents of @code{a} are as follows:
 
 @example
@@ -14477,17 +14505,23 @@ a[2] = "de"
 a[3] = "sac"
 @end example
 
+In order to reverse the direction of the sorted results in the above example,
address@hidden()} can be called with three arguments as follows:
+
address@hidden
+asort(a, a, "descending")
address@hidden example
+
 The @code{asort()} function is described in more detail in
 @ref{Array Sorting}.
 @code{asort()} is a @command{gawk} extension; it is not available
 in compatibility mode (@pxref{Options}).
 
address@hidden asorti(@var{source} @r{[}, @address@hidden) #
address@hidden asorti(@var{source} @r{[}, @var{dest} @r{[}, @var{how}  @r{]} 
@r{]}) #
 @cindex @code{asorti()} function (@command{gawk})
 Return the number of elements in the array @var{source}.
 It works similarly to @code{asort()}, however, the @emph{indices}
-are sorted, instead of the values.  As array indices are always strings,
-the comparison performed is always a string comparison.  (Here too,
+are sorted, instead of the values. (Here too,
 @code{IGNORECASE} affects the sorting.)
 
 The @code{asorti()} function is described in more detail in
diff --git a/eval.c b/eval.c
index 7c8f9b0..3328bfb 100644
--- a/eval.c
+++ b/eval.c
@@ -2168,10 +2168,9 @@ post:
                case Op_arrayfor_init:
                {
                        NODE **list = NULL;
-                       NODE *array;
+                       NODE *array, *sort_str;
                        size_t num_elems = 0;
-                       size_t i, j;
-                       qsort_compfunc sort_compare;
+                       static NODE *sorted_in = NULL;
 
                        /* get the array */
                        array = POP_ARRAY();
@@ -2180,35 +2179,18 @@ post:
                        if (array->var_array == NULL || array->table_size == 0)
                                goto arrayfor;
 
-                       /* allocate space for array */
                        num_elems = array->table_size;
-                       emalloc(list, NODE **, (num_elems + 1) * sizeof(NODE 
*), "interpret");
-
-                       /* populate it */
-                       for (i = j = 0; i < array->array_size; i++) {
-                               r = array->var_array[i];
-                               if (r == NULL)
-                                       continue;
-                               for (; r != NULL; r = r->ahnext) {
-                                       list[j++] = ahash_dupnode(r);
-                                       assert(list[j-1] == r);
-                               }
-                       }
 
-                       sort_compare = sorted_in();
-                       if (sort_compare) {
-                               /* ordering might be "* index number"
-                                  in which case we want more than just
-                                  a simple array of element nodes */
-                               sort_maybe_numeric_index(sort_compare, list,
-                                                        num_elems, TRUE);
-                               /* sort the array of element pointers */
-                               qsort(list, num_elems, sizeof(NODE *),
-                                     sort_compare); /* shazzam! */
-                               /* clean up by-index as-number */
-                               sort_maybe_numeric_index(sort_compare, list,
-                                                        num_elems, FALSE);
-                       }
+                       if (sorted_in == NULL)          /* do this once */
+                               sorted_in = make_string("sorted_in", 9);
+
+                       sort_str = NULL;
+                       /* if there's no PROCINFO[], there's no ["sorted_in"], 
so no sorting */
+                       if (PROCINFO_node != NULL)
+                               sort_str = in_array(PROCINFO_node, sorted_in);
+
+                       list = assoc_list(array, sort_str, SORTED_IN);
+
                        list[num_elems] = array;      /* actual array for use in
                                                       * lint warning in 
Op_arrayfor_incr
                                                       */
diff --git a/test/ChangeLog b/test/ChangeLog
index 94bf692..89aa4d1 100644
--- a/test/ChangeLog
+++ b/test/ChangeLog
@@ -1,3 +1,7 @@
+Mon Apr 18 10:22:28 2011  John Haque         <address@hidden>
+
+       * arraysort.awk, arraysort.ok, sort1.awk, sort1.ok: Updated.
+
 Fri Apr 15 13:49:36 2011  Arnold D. Robbins  <address@hidden>
 
        * ofmta.awk, ofmta.ok: New files from John Haque.
diff --git a/test/arraysort.awk b/test/arraysort.awk
index 912d588..0992204 100644
--- a/test/arraysort.awk
+++ b/test/arraysort.awk
@@ -66,16 +66,25 @@ BEGIN {
        n = split(" 4 \n 3\n3D\nD3\n3\n0\n2\n4\n1\n5", a, "\n")
        for (i = 1; i <= n; i++)
                b[a[i]] = a[i]
+       print "--unsorted--"
+       PROCINFO["sorted_in"] = "unsorted"
+       for (i in b)
+               print "|"i"|"b[i]"|"
+
        print "--asc ind str--"
        PROCINFO["sorted_in"] = "asc ind str"
        for (i in b)
-               print "|"i"|"b[i]
+               print "|"i"|"b[i]"|"
+       print "--asc val str--"
+       PROCINFO["sorted_in"] = "asc val str"
+       for (i in b)
+               print "|"i"|"b[i]"|"
        print "--asc ind num--"
        PROCINFO["sorted_in"] = "asc ind num"
        for (i in b)
-               print "|"i"|"b[i]
+               print "|"i"|"b[i]"|"
        print "--asc val num--"
        PROCINFO["sorted_in"] = "asc val num"
        for (i in b)
-               print "|"i"|"b[i]
+               print "|"i"|"b[i]"|"
 }
diff --git a/test/arraysort.ok b/test/arraysort.ok
index bf48ecd..23d7c44 100644
--- a/test/arraysort.ok
+++ b/test/arraysort.ok
@@ -1,7 +1,7 @@
 --- test1 ---
-   5  5 
   10  10
   3D  3D
+   5  5 
 --- test2 ---
 x 1
 y 1
@@ -26,37 +26,58 @@ y 1
 "3 "
 "4"
 --- test7 ---
+--unsorted--
+|4|4|
+|5|5|
+|D3|D3|
+|3D|3D|
+| 3| 3|
+|0|0|
+|1|1|
+|2|2|
+|3|3|
+| 4 | 4 |
 --asc ind str--
-| 3| 3
-| 4 | 4 
-|0|0
-|1|1
-|2|2
-|3|3
-|3D|3D
-|4|4
-|5|5
-|D3|D3
+| 3| 3|
+| 4 | 4 |
+|0|0|
+|1|1|
+|2|2|
+|3|3|
+|3D|3D|
+|4|4|
+|5|5|
+|D3|D3|
+--asc val str--
+| 3| 3|
+| 4 | 4 |
+|0|0|
+|1|1|
+|2|2|
+|3|3|
+|3D|3D|
+|4|4|
+|5|5|
+|D3|D3|
 --asc ind num--
-|0|0
-|D3|D3
-|1|1
-|2|2
-| 3| 3
-|3|3
-|3D|3D
-| 4 | 4 
-|4|4
-|5|5
+|0|0|
+|D3|D3|
+|1|1|
+|2|2|
+| 3| 3|
+|3|3|
+|3D|3D|
+| 4 | 4 |
+|4|4|
+|5|5|
 --asc val num--
-gawk: arraysort.awk:79: warning: `PROCINFO["sorted_in"]': sorting by value 
can't be forced to use numeric comparison
-|4|4
-|5|5
-|D3|D3
-|3D|3D
-| 3| 3
-|0|0
-|1|1
-|2|2
-|3|3
-| 4 | 4 
+|0|0|
+|D3|D3|
+|1|1|
+|2|2|
+| 3| 3|
+|3|3|
+|3D|3D|
+| 4 | 4 |
+|4|4|
+|5|5|
diff --git a/test/sort1.awk b/test/sort1.awk
index 3800c40..44af59f 100644
--- a/test/sort1.awk
+++ b/test/sort1.awk
@@ -1,31 +1,113 @@
 BEGIN{
-  a[1] = "barz";
-  a[2] = "blattt";
-  a[3] = "Zebra";
-  a[4] = 1234;
+  for (IGNORECASE=0; IGNORECASE <= 1; IGNORECASE++) {
+       SORT_STR = -1
 
-  testit1(a)
+       makea(a)
+       asort1(a, "")
+       printf("---end asort(a), IGNORECASE = %d---\n", IGNORECASE)
 
-  delete a
+       makea(a)
+       asort2(a, "")
+       printf("---end asort(a, b), IGNORECASE = %d---\n", IGNORECASE)
 
-  a[1] = "barz";
-  a[2] = "blattt";
-  a[3] = "Zebra";
-  a[4] = 1234;
+       makea(a)
+       SORT_STR = ""
+       asort1(a)
+       printf("---end asort(a, a), IGNORECASE = %d---\n", IGNORECASE)
 
-  n = asort(a, b);
+       makea(a)
+       SORT_STR = "num"
+       asort2(a, "")
+       printf("---end asort(a, b, \"num\"), IGNORECASE = %d---\n", IGNORECASE)
 
-  print "N = ", n;
+       makea(a)
+       SORT_STR = "desc str"
+       asort1(a, "")
+       printf("---end asort(a, a, \"desc str\"), IGNORECASE = %d---\n", 
IGNORECASE)
 
-  for(i=1; i <= n; i++)
-    print i, a[i], b[i];
+       makea(a)
+       SORT_STR = "val str"
+       proc_sort(a, "")
+       printf("---end PROCINFO[\"sorted_in\"] = \"val str\", IGNORECASE = 
%d---\n",
+               IGNORECASE)
+
+       makea(a)
+       SORT_STR = "val num"
+       proc_sort(a, "")
+       printf("---end PROCINFO[\"sorted_in\"] = \"val num\", IGNORECASE = 
%d---\n",
+               IGNORECASE)
+
+       makea(a)
+       SORT_STR = "desc val str"
+       proc_sort(a, "")
+       printf("---end PROCINFO[\"sorted_in\"] = \"desc val str\", IGNORECASE = 
%d---\n",
+               IGNORECASE)
+
+       makea(a)
+       SORT_STR = "desc val num"
+       proc_sort(a, "")
+       printf("---end PROCINFO[\"sorted_in\"] = \"desc val num\", IGNORECASE = 
%d---\n",
+               IGNORECASE)
+  }
+}
+
+function makea(aa)
+{
+  delete aa
+  aa[1] = "barz";
+  aa[2] = "blattt";
+  aa[3] = "Zebra";
+  aa[4] = 1234;
+  aa[5] = 234;
+  aa[6][1] = 4321;
+#  aa[6][2] = 432;
+  aa[6][3] = "tttalb";
+  aa[6][2] = "arbeZ";
+  aa[6][4] = "zrab";
 }
 
-function testit1(a,    count, j)
+
+# source array != destination array 
+function asort2(c, s,  d, k, m) 
+{
+  if (SORT_STR < 0)
+       m = asort(c, d);
+  else
+       m = asort(c, d, SORT_STR);
+  for(k=1; k <= m; k++) {
+       if (isarray(d[k]))
+               asort2(d[k], s"["k"]")
+       else
+               printf("%10s:%10s%10s\n", s"["k"]", c[k], d[k])
+  }
+}
+
+# source array == destination array
+function asort1(c, s,  k, m) 
+{
+  if (SORT_STR < 0)
+       m = asort(c)
+  else if (SORT_STR != "")
+       m = asort(c, c, SORT_STR)
+  else
+       m = asort(c, c);
+
+  for(k=1; k <= m; k++) {
+       if (isarray(c[k]))
+               asort1(c[k], s"["k"]")
+       else
+               printf("%10s:%10s\n", s"["k"]", c[k])
+  }
+}
+
+
+function proc_sort(c, s,       k) 
 {
-       print "start testit"
-       count = asort(a)
-       for (j = 1; j <= count; j++)
-               print j, a[j]
-       print "end testit"
+  PROCINFO["sorted_in"] = SORT_STR
+  for(k in c) {
+       if (isarray(c[k]))
+               proc_sort(c[k], s"["k"]")
+       else
+               printf("%10s:%10s\n", s"["k"]", c[k])
+  }
 }
diff --git a/test/sort1.ok b/test/sort1.ok
index 4838dab..8ba2bcd 100644
--- a/test/sort1.ok
+++ b/test/sort1.ok
@@ -1,11 +1,180 @@
-start testit
-1 1234
-2 Zebra
-3 barz
-4 blattt
-end testit
-N =  4
-1 barz 1234
-2 blattt Zebra
-3 Zebra barz
-4 1234 blattt
+       [1]:      1234
+       [2]:       234
+       [3]:     Zebra
+       [4]:      barz
+       [5]:    blattt
+    [6][1]:      4321
+    [6][2]:     arbeZ
+    [6][3]:    tttalb
+    [6][4]:      zrab
+---end asort(a), IGNORECASE = 0---
+       [1]:      barz      1234
+       [2]:    blattt       234
+       [3]:     Zebra     Zebra
+       [4]:      1234      barz
+       [5]:       234    blattt
+    [6][1]:      4321      4321
+    [6][2]:     arbeZ     arbeZ
+    [6][3]:    tttalb    tttalb
+    [6][4]:      zrab      zrab
+---end asort(a, b), IGNORECASE = 0---
+       [1]:      1234
+       [2]:       234
+       [3]:     Zebra
+       [4]:      barz
+       [5]:    blattt
+    [6][1]:      4321
+    [6][2]:     arbeZ
+    [6][3]:    tttalb
+    [6][4]:      zrab
+---end asort(a, a), IGNORECASE = 0---
+       [1]:      barz     Zebra
+       [2]:    blattt      barz
+       [3]:     Zebra    blattt
+       [4]:      1234       234
+       [5]:       234      1234
+    [6][1]:      4321     arbeZ
+    [6][2]:     arbeZ    tttalb
+    [6][3]:    tttalb      zrab
+    [6][4]:      zrab      4321
+---end asort(a, b, "num"), IGNORECASE = 0---
+    [1][1]:      zrab
+    [1][2]:    tttalb
+    [1][3]:     arbeZ
+    [1][4]:      4321
+       [2]:    blattt
+       [3]:      barz
+       [4]:     Zebra
+       [5]:       234
+       [6]:      1234
+---end asort(a, a, "desc str"), IGNORECASE = 0---
+       [4]:      1234
+       [5]:       234
+       [3]:     Zebra
+       [1]:      barz
+       [2]:    blattt
+    [6][1]:      4321
+    [6][2]:     arbeZ
+    [6][3]:    tttalb
+    [6][4]:      zrab
+---end PROCINFO["sorted_in"] = "val str", IGNORECASE = 0---
+       [3]:     Zebra
+       [1]:      barz
+       [2]:    blattt
+       [5]:       234
+       [4]:      1234
+    [6][2]:     arbeZ
+    [6][3]:    tttalb
+    [6][4]:      zrab
+    [6][1]:      4321
+---end PROCINFO["sorted_in"] = "val num", IGNORECASE = 0---
+    [6][4]:      zrab
+    [6][3]:    tttalb
+    [6][2]:     arbeZ
+    [6][1]:      4321
+       [2]:    blattt
+       [1]:      barz
+       [3]:     Zebra
+       [5]:       234
+       [4]:      1234
+---end PROCINFO["sorted_in"] = "desc val str", IGNORECASE = 0---
+    [6][1]:      4321
+    [6][4]:      zrab
+    [6][3]:    tttalb
+    [6][2]:     arbeZ
+       [4]:      1234
+       [5]:       234
+       [2]:    blattt
+       [1]:      barz
+       [3]:     Zebra
+---end PROCINFO["sorted_in"] = "desc val num", IGNORECASE = 0---
+       [1]:      1234
+       [2]:       234
+       [3]:      barz
+       [4]:    blattt
+       [5]:     Zebra
+    [6][1]:      4321
+    [6][2]:     arbeZ
+    [6][3]:    tttalb
+    [6][4]:      zrab
+---end asort(a), IGNORECASE = 1---
+       [1]:      barz      1234
+       [2]:    blattt       234
+       [3]:     Zebra      barz
+       [4]:      1234    blattt
+       [5]:       234     Zebra
+    [6][1]:      4321      4321
+    [6][2]:     arbeZ     arbeZ
+    [6][3]:    tttalb    tttalb
+    [6][4]:      zrab      zrab
+---end asort(a, b), IGNORECASE = 1---
+       [1]:      1234
+       [2]:       234
+       [3]:      barz
+       [4]:    blattt
+       [5]:     Zebra
+    [6][1]:      4321
+    [6][2]:     arbeZ
+    [6][3]:    tttalb
+    [6][4]:      zrab
+---end asort(a, a), IGNORECASE = 1---
+       [1]:      barz      barz
+       [2]:    blattt    blattt
+       [3]:     Zebra     Zebra
+       [4]:      1234       234
+       [5]:       234      1234
+    [6][1]:      4321     arbeZ
+    [6][2]:     arbeZ    tttalb
+    [6][3]:    tttalb      zrab
+    [6][4]:      zrab      4321
+---end asort(a, b, "num"), IGNORECASE = 1---
+    [1][1]:      zrab
+    [1][2]:    tttalb
+    [1][3]:     arbeZ
+    [1][4]:      4321
+       [2]:     Zebra
+       [3]:    blattt
+       [4]:      barz
+       [5]:       234
+       [6]:      1234
+---end asort(a, a, "desc str"), IGNORECASE = 1---
+       [4]:      1234
+       [5]:       234
+       [1]:      barz
+       [2]:    blattt
+       [3]:     Zebra
+    [6][1]:      4321
+    [6][2]:     arbeZ
+    [6][3]:    tttalb
+    [6][4]:      zrab
+---end PROCINFO["sorted_in"] = "val str", IGNORECASE = 1---
+       [1]:      barz
+       [2]:    blattt
+       [3]:     Zebra
+       [5]:       234
+       [4]:      1234
+    [6][2]:     arbeZ
+    [6][3]:    tttalb
+    [6][4]:      zrab
+    [6][1]:      4321
+---end PROCINFO["sorted_in"] = "val num", IGNORECASE = 1---
+    [6][4]:      zrab
+    [6][3]:    tttalb
+    [6][2]:     arbeZ
+    [6][1]:      4321
+       [3]:     Zebra
+       [2]:    blattt
+       [1]:      barz
+       [5]:       234
+       [4]:      1234
+---end PROCINFO["sorted_in"] = "desc val str", IGNORECASE = 1---
+    [6][1]:      4321
+    [6][4]:      zrab
+    [6][3]:    tttalb
+    [6][2]:     arbeZ
+       [4]:      1234
+       [5]:       234
+       [3]:     Zebra
+       [2]:    blattt
+       [1]:      barz
+---end PROCINFO["sorted_in"] = "desc val num", IGNORECASE = 1---

http://git.sv.gnu.org/cgit/gawk.git/commit/?id=98eb78c2d05870952fe25bbe37b86df2017f42fc

commit 98eb78c2d05870952fe25bbe37b86df2017f42fc
Author: Arnold D. Robbins <address@hidden>
Date:   Mon Apr 18 10:20:30 2011 +0300

    Add new test files.

diff --git a/test/ofmta.awk b/test/ofmta.awk
new file mode 100644
index 0000000..8f3bc6e
--- /dev/null
+++ b/test/ofmta.awk
@@ -0,0 +1,30 @@
+# Date: Thu, 14 Apr 2011 08:18:55 -0500
+# From: address@hidden
+# To: address@hidden
+# Subject: CONVFMT test for the test suite
+# Message-ID: <address@hidden>
+# 
+# Hi,
+# 
+# Please consider adding this to the test suite. 3.1.8 segfaults
+# with this.
+# 
+# Thanks,
+# 
+# John
+# 
+# 
+BEGIN {
+       i=1.2345
+       i=3+i
+       a[i]="hi"
+       OFMT="%.1f"
+       print i                       
+       for (x in a) print x, a[x]
+       print a[i]
+       print "--------"
+       CONVFMT=OFMT="%.3f"
+       print i                       
+       for (x in a) print x, a[x]
+       print a[i]
+}
diff --git a/test/ofmta.ok b/test/ofmta.ok
new file mode 100644
index 0000000..f050dc0
--- /dev/null
+++ b/test/ofmta.ok
@@ -0,0 +1,7 @@
+4.2
+4.2345 hi
+hi
+--------
+4.234
+4.2345 hi
+

http://git.sv.gnu.org/cgit/gawk.git/commit/?id=0398ef5eaa6a074cf6dfdeae7972319900604061

commit 0398ef5eaa6a074cf6dfdeae7972319900604061
Author: Arnold D. Robbins <address@hidden>
Date:   Mon Apr 18 10:16:54 2011 +0300

    Fix typos.

diff --git a/ChangeLog b/ChangeLog
index d60c0c4..4e6a2d8 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -458,7 +458,7 @@ Sat Dec 25 19:36:27 2010  Arnold D. Robbins  
<address@hidden>
 
 Fri Dec 24 12:56:46 2010  Arnold D. Robbins  <address@hidden>
 
-       * custom.h: Remove defs for RIPS MiscOS.
+       * custom.h: Remove defs for MIPS RiscOS.
        * configure.ac, aclocal.m4: Updated to Autoconf 2.68.
 
 Wed Dec 22 21:21:28 2010  Arnold D. Robbins  <address@hidden>

-----------------------------------------------------------------------

Summary of changes:
 ChangeLog          |   30 ++-
 array.c            | 1125 +++++++++++++++++++++-------------------------------
 awk.h              |   10 +-
 awkgram.c          |   10 +-
 awkgram.y          |   10 +-
 debug.c            |   78 ++---
 doc/gawk.1         |   23 +-
 doc/gawk.info      |  682 +++++++++++++++++---------------
 doc/gawk.texi      |  112 ++++--
 eval.c             |   42 +--
 test/ChangeLog     |    4 +
 test/arraysort.awk |   15 +-
 test/arraysort.ok  |   85 +++--
 test/ofmta.awk     |   30 ++
 test/ofmta.ok      |    7 +
 test/sort1.awk     |  122 +++++-
 test/sort1.ok      |  191 +++++++++-
 17 files changed, 1374 insertions(+), 1202 deletions(-)
 create mode 100644 test/ofmta.awk
 create mode 100644 test/ofmta.ok


hooks/post-receive
-- 
gawk



reply via email to

[Prev in Thread] Current Thread [Next in Thread]