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. dc5f240cf358edaf8191f5a


From: Arnold Robbins
Subject: [gawk-diffs] [SCM] gawk branch, master, updated. dc5f240cf358edaf8191f5a36f9066b0f0817462
Date: Sat, 31 Dec 2011 19:06:23 +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  dc5f240cf358edaf8191f5a36f9066b0f0817462 (commit)
       via  ccb220159bbbc45aac0572c7ca1d3f0f2247d1f5 (commit)
       via  a89bd16ff78c74513461af3f676d87d4eb9cfd3c (commit)
      from  2c8f64a8d56bcede9e1dd08859b3b235fc9bd40f (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=dc5f240cf358edaf8191f5a36f9066b0f0817462

commit dc5f240cf358edaf8191f5a36f9066b0f0817462
Merge: ccb2201 a89bd16
Author: Arnold D. Robbins <address@hidden>
Date:   Sat Dec 31 21:05:39 2011 +0200

    Merge branch 'gawk-4.0-stable', minor fixes after exe merge.

diff --cc ChangeLog
index f904f7f,39dd5a6..292a03c
--- a/ChangeLog
+++ b/ChangeLog
@@@ -1,58 -1,9 +1,69 @@@
+ 2011-12-31         Arnold D. Robbins     <address@hidden>
+ 
++      * profile_p.c: Remove the file.
++      * msg.c (err): Remove check for name being dgawk.
++
++2011-12-31         Arnold D. Robbins     <address@hidden>
++
+       * awk.h [STREQ, STREQN]: Remove macros.
+       * awkgram.y, builtin.c, command.y, debug.c, eval.c,
+       io.c, msg.c: Change all uses to call strcmp, strncmp.
+ 
 +2011-12-28         Arnold D. Robbins     <address@hidden>
 +
 +      * int_array.c, str_array.c: Fix some compiler warnings 32/64
 +      bit system differences.
 +
 +2011-12-26         John Haque      <address@hidden>
 +
 +      Merge gawk, pgawk and dgawk into a single executable gawk.
 +
 +      * awk.h (DO_PRETTY_PRINT, DO_PROFILE, DO_DEBUG,
 +      do_pretty_print, do_debug): New defines.
 +      (interpret): New variable, a pointer to an interpreter routine.
 +      (enum exe_mode): Nuked.
 +      * main.c (opttab): New options --pretty-print and --debug;
 +      Remove option --command.
 +      (usage): Update usage messages.
 +      * interpret.h: New file.
 +      * eval.c (r_interpret): Move to the new file.
 +      (debug_interpret): New interpreter routine when debugging.
 +      (init_interpret): New routine to initialize interpreter related
 +      variables.
 +      * eval_d.c, eval_p.c: Delete files.
 +      * debug.c (interpret): Renamed to debug_prog.
 +      (DEFAULT_PROMPT, DEFAULT_HISTFILE, DEFAULT_OPTFILE): Remove prefix 'd'.
 +      * profile.c (init_profiling): Nuked.
 +      * Makefile.am: Adjusted.
 +
 +      Add command line option --load for loading extensions.
 +
 +      * awk.h (srctype): Add new source type SRC_EXTLIB.
 +      * ext.c(load_ext): New routine to load extension.
 +      (do_ext): Adjust to use load_ext().
 +      * main.c (opttab): Add new option --load.
 +      (main): Call load_ext() to load extensions.
 +      (usage): Add usage message for the new option.
 +      * io.c (get_cwd): New routine.
 +      (do_find_source): Use the new routine.
 +      (find_source): Handle new type SRC_EXTLIB.
 +      * awkgram.y (parse_program, next_sourcefile): Skip type SRC_EXTLIB.
 +      (add_srcfile): Adjust call to find_source.
 +      * debug.c (source_find): Same.
 +
 +      Unrelated:
 +
 +      * ext.c (get_argument): Fixed argument parsing.
 +      * array.c (null_array_func): Reworked array routines for an empty array.
 +      * str_array.c, int_array.c: Make GCC happy, use %u instead of %lu
 +      printf formats.
 +      * eval.c (node_Boolean): New array for TRUE and FALSE nodes.
 +      (init_interpret): Create the new nodes.
 +      (eval_condition): Add test for the new nodes.
 +      (setup_frame): Disable tail-recursion optimization when profiling.
 +      * interpret.h (r_interpret): Use the boolean nodes instead of making
 +      new ones when needed.
 +
  2011-12-26         Arnold D. Robbins     <address@hidden>
  
        Finish Rational Range Interpretation (!)
diff --cc awkgram.c
index 5eff6e3,665edbd..24c102d
--- a/awkgram.c
+++ b/awkgram.c
@@@ -2851,21 -2899,9 +2851,21 @@@ regular_loop
                if ((yyvsp[(3) - (4)]) == NULL) {
                        (yyval) = list_create((yyvsp[(1) - (4)]));
                        (void) list_prepend((yyval), instruction(Op_push_i));
 -                      (yyval)->nexti->memory = Nnull_string;
 -              } else
 +                      (yyval)->nexti->memory = dupnode(Nnull_string);
 +              } else {
 +                      if (do_optimize > 1
 +                              && (yyvsp[(3) - (4)])->lasti->opcode == 
Op_func_call
-                               && STREQ((yyvsp[(3) - (4)])->lasti->func_name, 
in_function)
++                              && strcmp((yyvsp[(3) - (4)])->lasti->func_name, 
in_function) == 0
 +                      ) {
 +                              /* Do tail recursion optimization. Tail
 +                               * call without a return value is recognized
 +                               * in mk_function().
 +                               */
 +                              ((yyvsp[(3) - (4)])->lasti + 1)->tail_call = 
TRUE;
 +                      }
 +
                        (yyval) = list_append((yyvsp[(3) - (4)]), (yyvsp[(1) - 
(4)]));
 +              }
          }
      break;
  
@@@ -6528,7 -6632,76 +6528,6 @@@ parms_shadow(INSTRUCTION *pc, int *shad
        return 0;
  }
  
--
 -/*
 - * install_symbol:
 - * Install a name in the symbol table, even if it is already there.
 - * Caller must check against redefinition if that is desired. 
 - */
 -
 -
 -NODE *
 -install_symbol(char *name, NODE *value)
 -{
 -      NODE *hp;
 -      size_t len;
 -      int bucket;
 -
 -      if (install_func)
 -              (*install_func)(name);
 -
 -      var_count++;
 -      len = strlen(name);
 -      bucket = hash(name, len, (unsigned long) HASHSIZE, NULL);
 -      getnode(hp);
 -      hp->type = Node_hashnode;
 -      hp->hnext = variables[bucket];
 -      variables[bucket] = hp;
 -      hp->hlength = len;
 -      hp->hvalue = value;
 -      hp->hname = name;
 -      hp->hvalue->vname = name;
 -      return hp->hvalue;
 -}
 -
 -/* lookup --- find the most recent hash node for name installed by 
install_symbol */
 -
 -NODE *
 -lookup(const char *name)
 -{
 -      NODE *bucket;
 -      size_t len;
 -
 -      len = strlen(name);
 -      for (bucket = variables[hash(name, len, (unsigned long) HASHSIZE, 
NULL)];
 -                      bucket != NULL; bucket = bucket->hnext)
 -              if (bucket->hlength == len && strncmp(bucket->hname, name, len) 
== 0)
 -                      return bucket->hvalue;
 -      return NULL;
 -}
 -
 -/* sym_comp --- compare two symbol (variable or function) names */
 -
 -static int
 -sym_comp(const void *v1, const void *v2)
 -{
 -      const NODE *const *npp1, *const *npp2;
 -      const NODE *n1, *n2;
 -      int minlen;
 -
 -      npp1 = (const NODE *const *) v1;
 -      npp2 = (const NODE *const *) v2;
 -      n1 = *npp1;
 -      n2 = *npp2;
 -
 -      if (n1->hlength > n2->hlength)
 -              minlen = n1->hlength;
 -      else
 -              minlen = n2->hlength;
 -
 -      return strncmp(n1->hname, n2->hname, minlen);
 -}
 -
  /* valinfo --- dump var info */
  
  void
@@@ -6607,31 -6848,57 +6606,30 @@@ shadow_funcs(
                lintwarn(_("there were shadowed variables."));
  }
  
 -/*
 - * func_install:
 - * check if name is already installed;  if so, it had better have Null value,
 - * in which case def is added as the value. Otherwise, install name with def
 - * as value. 
 - *
 - * Extra work, build up and save a list of the parameter names in a table
 - * and hang it off params->parmlist. This is used to set the `vname' field
 - * of each function parameter during a function call. See eval.c.
 +
 +/* mk_function --- finalize function definition node; remove parameters
 + *    out of the symbol table.
   */
  
 -static int
 -func_install(INSTRUCTION *func, INSTRUCTION *def)
 +static INSTRUCTION *
 +mk_function(INSTRUCTION *fi, INSTRUCTION *def)
  {
 -      NODE *params;
 -      NODE *r, *n, *thisfunc, *hp;
 -      char **pnames = NULL;
 -      char *fname;
 -      int pcount = 0;
 -      int i;
 +      NODE *thisfunc;
  
 -      params = func_params;
 +      thisfunc = fi->func_body;
 +      assert(thisfunc != NULL);
  
 -      /* check for function foo(foo) { ... }.  bleah. */
 -      for (n = params->rnode; n != NULL; n = n->rnode) {
 -              if (strcmp(n->param, params->param) == 0) {
 -                      error_ln(func->source_line,
 -                              _("function `%s': can't use function name as 
parameter name"), params->param);
 -                      return -1;
 -              } else if (is_std_var(n->param)) {
 -                      error_ln(func->source_line,
 -                              _("function `%s': can't use special variable 
`%s' as a function parameter"),
 -                                      params->param, n->param);
 -                      return -1;
 -              }
 -      }
 +      if (do_optimize > 1 && def->lasti->opcode == Op_pop) {
 +              /* tail call which does not return any value. */
  
 -      thisfunc = NULL;        /* turn off warnings */
 +              INSTRUCTION *t;
  
 -      fname = params->param;
 -      /* symbol table management */
 -      hp = remove_symbol(params->param);  /* remove function name out of 
symbol table */ 
 -      if (hp != NULL)
 -              freenode(hp);
 -      r = lookup(fname);
 -      if (r != NULL) {
 -              error_ln(func->source_line,
 -                       _("function name `%s' previously defined"), fname);
 -              return -1;
 -      } else if (fname == builtin_func)       /* not a valid function name */
 -              goto remove_params;
 +              for (t = def->nexti; t->nexti != def->lasti; t = t->nexti)
 +                      ;
 +              if (t->opcode == Op_func_call
-                       && STREQ(t->func_name, thisfunc->vname)
-               )
++                  && strcmp(t->func_name, thisfunc->vname) == 0)
 +                      (t + 1)->tail_call = TRUE;
 +      }
  
        /* add an implicit return at end;
         * also used by 'return' command in debugger
@@@ -6910,9 -7227,13 +6908,9 @@@ variable(int location, char *name, NODE
                        /*
                         * This is the only case in which we may not free the 
string.
                         */
 -                              if (type == Node_var)
 -                                      r = mk_symbol(type, Nnull_string);
 -                              else
 -                                      r = mk_symbol(type, (NODE *) NULL);
 -                              return install_symbol(name, r);
 +                              return install_symbol(name, type);
                        }
-                       if (STREQ(name, dv->name)) {
+                       if (strcmp(name, dv->name) == 0) {
                                r = (*dv->load_func)();
                                break;
                        }
diff --cc awkgram.y
index 968bf53,ce71bb2..a64fff0
--- a/awkgram.y
+++ b/awkgram.y
@@@ -824,21 -867,9 +824,21 @@@ non_compound_stm
                if ($3 == NULL) {
                        $$ = list_create($1);
                        (void) list_prepend($$, instruction(Op_push_i));
 -                      $$->nexti->memory = Nnull_string;
 -              } else
 +                      $$->nexti->memory = dupnode(Nnull_string);
 +              } else {
 +                      if (do_optimize > 1
 +                              && $3->lasti->opcode == Op_func_call
-                               && STREQ($3->lasti->func_name, in_function)
++                              && strcmp($3->lasti->func_name, in_function) == 0
 +                      ) {
 +                              /* Do tail recursion optimization. Tail
 +                               * call without a return value is recognized
 +                               * in mk_function().
 +                               */
 +                              ($3->lasti + 1)->tail_call = TRUE;
 +                      }
 +
                        $$ = list_append($3, $1);
 +              }
          }
        | simple_stmt statement_term
        ;
@@@ -3831,7 -3942,76 +3831,6 @@@ parms_shadow(INSTRUCTION *pc, int *shad
        return 0;
  }
  
--
 -/*
 - * install_symbol:
 - * Install a name in the symbol table, even if it is already there.
 - * Caller must check against redefinition if that is desired. 
 - */
 -
 -
 -NODE *
 -install_symbol(char *name, NODE *value)
 -{
 -      NODE *hp;
 -      size_t len;
 -      int bucket;
 -
 -      if (install_func)
 -              (*install_func)(name);
 -
 -      var_count++;
 -      len = strlen(name);
 -      bucket = hash(name, len, (unsigned long) HASHSIZE, NULL);
 -      getnode(hp);
 -      hp->type = Node_hashnode;
 -      hp->hnext = variables[bucket];
 -      variables[bucket] = hp;
 -      hp->hlength = len;
 -      hp->hvalue = value;
 -      hp->hname = name;
 -      hp->hvalue->vname = name;
 -      return hp->hvalue;
 -}
 -
 -/* lookup --- find the most recent hash node for name installed by 
install_symbol */
 -
 -NODE *
 -lookup(const char *name)
 -{
 -      NODE *bucket;
 -      size_t len;
 -
 -      len = strlen(name);
 -      for (bucket = variables[hash(name, len, (unsigned long) HASHSIZE, 
NULL)];
 -                      bucket != NULL; bucket = bucket->hnext)
 -              if (bucket->hlength == len && strncmp(bucket->hname, name, len) 
== 0)
 -                      return bucket->hvalue;
 -      return NULL;
 -}
 -
 -/* sym_comp --- compare two symbol (variable or function) names */
 -
 -static int
 -sym_comp(const void *v1, const void *v2)
 -{
 -      const NODE *const *npp1, *const *npp2;
 -      const NODE *n1, *n2;
 -      int minlen;
 -
 -      npp1 = (const NODE *const *) v1;
 -      npp2 = (const NODE *const *) v2;
 -      n1 = *npp1;
 -      n2 = *npp2;
 -
 -      if (n1->hlength > n2->hlength)
 -              minlen = n1->hlength;
 -      else
 -              minlen = n2->hlength;
 -
 -      return strncmp(n1->hname, n2->hname, minlen);
 -}
 -
  /* valinfo --- dump var info */
  
  void
@@@ -3910,31 -4158,57 +3909,30 @@@ shadow_funcs(
                lintwarn(_("there were shadowed variables."));
  }
  
 -/*
 - * func_install:
 - * check if name is already installed;  if so, it had better have Null value,
 - * in which case def is added as the value. Otherwise, install name with def
 - * as value. 
 - *
 - * Extra work, build up and save a list of the parameter names in a table
 - * and hang it off params->parmlist. This is used to set the `vname' field
 - * of each function parameter during a function call. See eval.c.
 +
 +/* mk_function --- finalize function definition node; remove parameters
 + *    out of the symbol table.
   */
  
 -static int
 -func_install(INSTRUCTION *func, INSTRUCTION *def)
 +static INSTRUCTION *
 +mk_function(INSTRUCTION *fi, INSTRUCTION *def)
  {
 -      NODE *params;
 -      NODE *r, *n, *thisfunc, *hp;
 -      char **pnames = NULL;
 -      char *fname;
 -      int pcount = 0;
 -      int i;
 +      NODE *thisfunc;
  
 -      params = func_params;
 +      thisfunc = fi->func_body;
 +      assert(thisfunc != NULL);
  
 -      /* check for function foo(foo) { ... }.  bleah. */
 -      for (n = params->rnode; n != NULL; n = n->rnode) {
 -              if (strcmp(n->param, params->param) == 0) {
 -                      error_ln(func->source_line,
 -                              _("function `%s': can't use function name as 
parameter name"), params->param);
 -                      return -1;
 -              } else if (is_std_var(n->param)) {
 -                      error_ln(func->source_line,
 -                              _("function `%s': can't use special variable 
`%s' as a function parameter"),
 -                                      params->param, n->param);
 -                      return -1;
 -              }
 -      }
 +      if (do_optimize > 1 && def->lasti->opcode == Op_pop) {
 +              /* tail call which does not return any value. */
  
 -      thisfunc = NULL;        /* turn off warnings */
 +              INSTRUCTION *t;
  
 -      fname = params->param;
 -      /* symbol table management */
 -      hp = remove_symbol(params->param);  /* remove function name out of 
symbol table */ 
 -      if (hp != NULL)
 -              freenode(hp);
 -      r = lookup(fname);
 -      if (r != NULL) {
 -              error_ln(func->source_line,
 -                       _("function name `%s' previously defined"), fname);
 -              return -1;
 -      } else if (fname == builtin_func)       /* not a valid function name */
 -              goto remove_params;
 +              for (t = def->nexti; t->nexti != def->lasti; t = t->nexti)
 +                      ;
 +              if (t->opcode == Op_func_call
-                       && STREQ(t->func_name, thisfunc->vname)
-               )
++                  && strcmp(t->func_name, thisfunc->vname) == 0)
 +                      (t + 1)->tail_call = TRUE;
 +      }
  
        /* add an implicit return at end;
         * also used by 'return' command in debugger
@@@ -4213,9 -4537,13 +4211,9 @@@ variable(int location, char *name, NODE
                        /*
                         * This is the only case in which we may not free the 
string.
                         */
 -                              if (type == Node_var)
 -                                      r = mk_symbol(type, Nnull_string);
 -                              else
 -                                      r = mk_symbol(type, (NODE *) NULL);
 -                              return install_symbol(name, r);
 +                              return install_symbol(name, type);
                        }
-                       if (STREQ(name, dv->name)) {
+                       if (strcmp(name, dv->name) == 0) {
                                r = (*dv->load_func)();
                                break;
                        }
diff --cc cint_array.c
index 400e91d,0000000..8ec0923
mode 100644,000000..100644
--- a/cint_array.c
+++ b/cint_array.c
@@@ -1,1237 -1,0 +1,1237 @@@
 +/*
 + * cint_array.c - routines for arrays of (mostly) consecutive positive 
integer indices.
 + */
 +
 +/* 
 + * Copyright (C) 1986, 1988, 1989, 1991-2011 the Free Software Foundation, 
Inc.
 + * 
 + * This file is part of GAWK, the GNU implementation of the
 + * AWK Programming Language.
 + * 
 + * GAWK is free software; you can redistribute it and/or modify
 + * it under the terms of the GNU General Public License as published by
 + * the Free Software Foundation; either version 3 of the License, or
 + * (at your option) any later version.
 + * 
 + * GAWK is distributed in the hope that it will be useful,
 + * but WITHOUT ANY WARRANTY; without even the implied warranty of
 + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 + * GNU General Public License for more details.
 + * 
 + * You should have received a copy of the GNU General Public License
 + * along with this program; if not, write to the Free Software
 + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, 
USA
 + */
 +
 +#include "awk.h"
 +
 +extern FILE *output_fp;
 +extern void indent(int indent_level);
 +extern NODE **is_integer(NODE *symbol, NODE *subs);
 +
 +/*
 + * NHAT         ---  maximum size of a leaf array (2^NHAT).
 + * THRESHOLD    ---  Maximum capacity waste; THRESHOLD >= 2^(NHAT + 1).
 + */
 +
 +static int NHAT = 10; 
 +static long THRESHOLD;
 +
 +/* What is the optimium NHAT ? timing results suggest that 10 is a good 
choice,
 + * although differences aren't that significant for > 10.
 + */
 +
 +
 +static NODE **cint_array_init(NODE *symbol, NODE *subs);
 +static NODE **is_uinteger(NODE *symbol, NODE *subs);
 +static NODE **cint_lookup(NODE *symbol, NODE *subs);
 +static NODE **cint_exists(NODE *symbol, NODE *subs);
 +static NODE **cint_clear(NODE *symbol, NODE *subs);
 +static NODE **cint_remove(NODE *symbol, NODE *subs);
 +static NODE **cint_list(NODE *symbol, NODE *t);
 +static NODE **cint_copy(NODE *symbol, NODE *newsymb);
 +static NODE **cint_dump(NODE *symbol, NODE *ndump);
 +#ifdef ARRAYDEBUG
 +static NODE **cint_option(NODE *opt, NODE *val);
 +static void cint_print(NODE *symbol);
 +#endif
 +
 +array_ptr cint_array_func[] = {
 +      cint_array_init,
 +      is_uinteger,
 +      cint_lookup,
 +      cint_exists,
 +      cint_clear,
 +      cint_remove,
 +      cint_list,
 +      cint_copy,
 +      cint_dump,
 +#ifdef ARRAYDEBUG
 +      cint_option,
 +#endif
 +};
 +
 +static inline int cint_hash(long k);
 +static inline NODE **cint_find(NODE *symbol, long k, int h1);
 +
 +static inline NODE *make_node(NODETYPE type);
 +
 +static NODE **tree_lookup(NODE *symbol, NODE *tree, long k, int m, long base);
 +static NODE **tree_exists(NODE *tree, long k);
 +static void tree_clear(NODE *tree);
 +static int tree_remove(NODE *symbol, NODE *tree, long k);
 +static void tree_copy(NODE *newsymb, NODE *tree, NODE *newtree);
 +static long tree_list(NODE *tree, NODE **list, unsigned int flags);
 +static inline NODE **tree_find(NODE *tree, long k, int i);
 +static void tree_info(NODE *tree, NODE *ndump, const char *aname);
 +static size_t tree_kilobytes(NODE *tree);
 +#ifdef ARRAYDEBUG
 +static void tree_print(NODE *tree, size_t bi, int indent_level);
 +#endif
 +
 +static inline NODE **leaf_lookup(NODE *symbol, NODE *array, long k, long 
size, long base);
 +static inline NODE **leaf_exists(NODE *array, long k);
 +static void leaf_clear(NODE *array);
 +static int leaf_remove(NODE *symbol, NODE *array, long k);
 +static void leaf_copy(NODE *newsymb, NODE *array, NODE *newarray);
 +static long leaf_list(NODE *array, NODE **list, unsigned int flags);
 +static void leaf_info(NODE *array, NODE *ndump, const char *aname);
 +#ifdef ARRAYDEBUG
 +static void leaf_print(NODE *array, size_t bi, int indent_level);
 +#endif
 +
 +/* powers of 2 table upto 2^30 */ 
 +static const long power_two_table[] = {
 +      1, 2, 4, 8, 16, 32, 64,
 +      128, 256, 512, 1024, 2048, 4096,
 +      8192, 16384, 32768, 65536, 131072, 262144,
 +      524288, 1048576, 2097152, 4194304, 8388608, 16777216,
 +      33554432, 67108864, 134217728, 268435456, 536870912, 1073741824
 +};
 +
 +
 +#define ISUINT(a, s)  ((((s)->flags & NUMINT) != 0 || is_integer(a, s) != 
NULL) \
 +                                    && (s)->numbr >= 0)
 +
 +/*
 + * To store 2^n integers, allocate top-level array of size n, elements
 + * of which are 1-Dimensional (leaf-array) of geometrically increasing
 + * size (power of 2).   
 + *
 + *  [0]   -->  [ 0 ]
 + *  [1]   -->  [ 1 ]
 + *  |2|   -->  [ 2 | 3 ]
 + *  |3|   -->  [ 4 | 5 | 6 | 7 ]
 + *  |.|
 + *  |k|   -->  [ 2^(k - 1)| ...  | 2^k - 1 ]
 + *  ...
 + *
 + * For a given integer n (> 0), the leaf-array is at 1 + floor(log2(n)). 
 + *
 + * The idea for the geometrically increasing array sizes is from:
 + *    Fast Functional Lists, Hash-Lists, Deques and Variable Length Arrays.
 + *    Bagwell, Phil (2002).
 + *    http://infoscience.epfl.ch/record/64410/files/techlists.pdf
 + *
 + * Disadvantage:
 + * Worst case memory waste > 99% and will happen when each of the
 + * leaf arrays contains only a single element. Even with consecutive
 + * integers, memory waste can be as high as 50%.
 + *
 + * Solution: Hashed Array Trees (HATs).
 + *
 + */
 +
 +/* cint_array_init ---  check relevant environment variables */
 +
 +static NODE **
 +cint_array_init(NODE *symbol ATTRIBUTE_UNUSED, NODE *subs ATTRIBUTE_UNUSED)
 +{
 +      long newval;
 +
 +      if ((newval = getenv_long("NHAT")) > 1 && newval < INT32_BIT)
 +              NHAT = newval;
 +      THRESHOLD = power_two_table[NHAT + 1];
 +      return (NODE **) ! NULL;
 +}
 +
 +
 +/* is_uinteger --- test if the subscript is an integer >= 0 */
 +
 +NODE **
 +is_uinteger(NODE *symbol, NODE *subs)
 +{
 +      if (is_integer(symbol, subs) != NULL && subs->numbr >= 0)
 +              return (NODE **) ! NULL;
 +      return NULL;
 +}
 +
 +
 +/* cint_lookup --- Find the subscript in the array; Install it if it isn't 
there. */
 +
 +static NODE **
 +cint_lookup(NODE *symbol, NODE *subs)
 +{
 +      NODE **lhs;
 +      long k;
 +      int h1 = -1, m, li;
 +      NODE *tn, *xn;
 +      long cint_size, capacity;
 +
 +      k = -1;
 +      if (ISUINT(symbol, subs)) {
 +              k = subs->numbr;        /* k >= 0 */
 +              h1 = cint_hash(k);      /* h1 >= NHAT */
 +              if ((lhs = cint_find(symbol, k, h1)) != NULL)
 +                      return lhs;
 +      }
 +      xn = symbol->xarray;
 +      if (xn != NULL && (lhs = xn->aexists(xn, subs)) != NULL)
 +              return lhs;
 +
 +      /* It's not there, install it */
 +
 +      if (k < 0)
 +              goto xinstall;
 +
 +      m = h1 - 1;     /* m >= (NHAT- 1) */
 +
 +      /* Estimate capacity upper bound.
 +       * capacity upper bound = current capacity + leaf array size.
 +       */
 +      li = m > NHAT ? m : NHAT;
 +      while (li >= NHAT) {
 +              /* leaf-array of a HAT */
 +              li = (li + 1) / 2;
 +      }
 +      capacity = symbol->array_capacity + power_two_table[li];
 +
 +      cint_size = (xn == NULL) ? symbol->table_size
 +                              : (symbol->table_size - xn->table_size);
 +      assert(cint_size >= 0);
 +      if ((capacity - cint_size) > THRESHOLD)
 +              goto xinstall;
 +
 +      if (symbol->nodes == NULL) {
 +              symbol->array_capacity = 0;
 +              assert(symbol->table_size == 0);
 +
 +              /* nodes[0] .. nodes[NHAT- 1] not used */
 +              emalloc(symbol->nodes, NODE **, INT32_BIT * sizeof(NODE *), 
"cint_lookup");
 +              memset(symbol->nodes, '\0', INT32_BIT * sizeof(NODE *));
 +      }
 +
 +      symbol->table_size++;   /* one more element in array */
 +
 +      tn = symbol->nodes[h1];
 +      if (tn == NULL) {
 +              tn = make_node(Node_array_tree);
 +              symbol->nodes[h1] = tn;
 +      }
 +
 +      if (m < NHAT)
 +              return tree_lookup(symbol, tn, k, NHAT, 0);
 +      return tree_lookup(symbol, tn, k, m, power_two_table[m]);
 +
 +xinstall:
 +
 +      symbol->table_size++;
 +      if (xn == NULL) {
 +              extern array_ptr int_array_func[];
 +              extern array_ptr str_array_func[];
 +
 +              xn = symbol->xarray = make_array();
 +              xn->vname = symbol->vname;      /* shallow copy */
 +
 +              /* Avoid using assoc_lookup(xn, subs) which may lead
 +               * to infinite recursion.
 +               */
 +
 +              if (is_integer(xn, subs))
 +                      xn->array_funcs = int_array_func;
 +              else
 +                      xn->array_funcs = str_array_func;
 +              xn->flags |= XARRAY;
 +      }
 +      return xn->alookup(xn, subs);
 +}
 +
 +
 +/* cint_exists --- test whether an index is in the array or not. */
 +
 +static NODE **
 +cint_exists(NODE *symbol, NODE *subs)
 +{
 +      NODE *xn;
 +
 +      if (ISUINT(symbol, subs)) {
 +              long k = subs->numbr;
 +              NODE **lhs;
 +              if ((lhs = cint_find(symbol, k, cint_hash(k))) != NULL)
 +                      return lhs;
 +      }
 +      if ((xn = symbol->xarray) == NULL)
 +              return NULL;
 +      return xn->aexists(xn, subs);
 +}
 +
 +
 +/* cint_clear --- flush all the values in symbol[] */
 +
 +static NODE **
 +cint_clear(NODE *symbol, NODE *subs ATTRIBUTE_UNUSED)
 +{
 +      size_t i;
 +      NODE *tn;
 +
 +      assert(symbol->nodes != NULL);
 +
 +      if (symbol->xarray != NULL) {
 +              NODE *xn = symbol->xarray;
 +              assoc_clear(xn);
 +              freenode(xn);
 +              symbol->xarray = NULL;
 +      }
 +
 +      for (i = NHAT; i < INT32_BIT; i++) {
 +              tn = symbol->nodes[i];
 +              if (tn != NULL) {
 +                      tree_clear(tn);
 +                      freenode(tn);
 +              }
 +      }
 +
 +      efree(symbol->nodes);
 +      init_array(symbol);     /* re-initialize symbol */
 +      return NULL;
 +}
 +
 +
 +/* cint_remove --- remove an index from the array */
 +
 +static NODE **
 +cint_remove(NODE *symbol, NODE *subs)
 +{
 +      long k;
 +      int h1;
 +      NODE *tn, *xn = symbol->xarray;
 +
 +      if (symbol->table_size == 0)
 +              return NULL;
 +
 +      if (! ISUINT(symbol, subs))
 +              goto xremove;
 +
 +      assert(symbol->nodes != NULL);
 +
 +      k = subs->numbr;
 +      h1 = cint_hash(k);
 +      tn = symbol->nodes[h1];
 +      if (tn == NULL || ! tree_remove(symbol, tn, k))
 +              goto xremove;
 +
 +      if (tn->table_size == 0) {
 +              freenode(tn);
 +              symbol->nodes[h1] = NULL;
 +      }
 +
 +      symbol->table_size--;
 +
 +      if (xn == NULL && symbol->table_size == 0) {
 +              efree(symbol->nodes);
 +              init_array(symbol);     /* re-initialize array 'symbol' */
 +      } else if(xn != NULL && symbol->table_size == xn->table_size) {
 +              /* promote xn to symbol */
 +
 +              xn->flags &= ~XARRAY;
 +              xn->parent_array = symbol->parent_array;
 +              efree(symbol->nodes);
 +              *symbol = *xn;
 +              freenode(xn);
 +      }
 +
 +      return (NODE **) ! NULL;
 +
 +xremove:
 +      xn = symbol->xarray;
 +      if (xn == NULL || xn->aremove(xn, subs) == NULL)
 +              return NULL;
 +      if (xn->table_size == 0) {
 +              freenode(xn);
 +              symbol->xarray = NULL;
 +      }
 +      symbol->table_size--;
 +      assert(symbol->table_size > 0);
 +
 +      return (NODE **) ! NULL;
 +}
 +
 +
 +/* cint_copy --- duplicate input array "symbol" */
 +
 +static NODE **
 +cint_copy(NODE *symbol, NODE *newsymb)
 +{
 +      NODE **old, **new;
 +      size_t i;
 +
 +      assert(symbol->nodes != NULL);
 +
 +      /* allocate new table */
 +      emalloc(new, NODE **, INT32_BIT * sizeof(NODE *), "cint_copy");
 +      memset(new, '\0', INT32_BIT * sizeof(NODE *));
 +
 +      old = symbol->nodes;
 +      for (i = NHAT; i < INT32_BIT; i++) {
 +              if (old[i] == NULL)
 +                      continue;
 +              new[i] = make_node(Node_array_tree); 
 +              tree_copy(newsymb, old[i], new[i]);
 +      }
 +
 +      if (symbol->xarray != NULL) {
 +              NODE *xn, *n;
 +              xn = symbol->xarray;
 +              n = make_array();
 +              n->vname = newsymb->vname;
 +              (void) xn->acopy(xn, n);
 +              newsymb->xarray = n;
 +      } else
 +              newsymb->xarray = NULL;
 +
 +      newsymb->nodes = new;
 +      newsymb->table_size = symbol->table_size;
 +      newsymb->array_capacity = symbol->array_capacity;
 +      newsymb->flags = symbol->flags;
 +
 +      return NULL;
 +}
 +
 +
 +/* cint_list --- return a list of items */
 +
 +static NODE**
 +cint_list(NODE *symbol, NODE *t)
 +{
 +      NODE **list = NULL;
 +      NODE *tn, *xn;
 +      unsigned long k = 0, num_elems, list_size;
 +      size_t j, ja, jd;
 +      int elem_size = 1;
 +
 +      num_elems = symbol->table_size;
 +      if (num_elems == 0)
 +              return NULL;
 +
 +      if ((t->flags & (AINDEX|AVALUE|ADELETE)) == (AINDEX|ADELETE))
 +              num_elems = 1;
 +
 +      if ((t->flags & (AINDEX|AVALUE)) == (AINDEX|AVALUE))
 +              elem_size = 2;
 +      list_size = num_elems * elem_size;
 +
 +      if (symbol->xarray != NULL) {
 +              xn = symbol->xarray;
 +              list = xn->alist(xn, t);
 +              assert(list != NULL);
 +              t->flags &= ~(AASC|ADESC);
 +              if (num_elems == 1 || num_elems == xn->table_size)
 +                      return list;
 +              erealloc(list, NODE **, list_size * sizeof(NODE *), 
"cint_list");
 +              k = elem_size * xn->table_size;
 +      } else
 +              emalloc(list, NODE **, list_size * sizeof(NODE *), "cint_list");
 +
 +
 +      if ((t->flags & AINUM) == 0)    /* not sorting by "index num" */
 +              t->flags &= ~(AASC|ADESC);
 +
 +      /* populate it with index in ascending or descending order */
 +
 +      for (ja = NHAT, jd = INT32_BIT - 1; ja < INT32_BIT && jd >= NHAT; ) {
 +              j = (t->flags & ADESC) ? jd-- : ja++;
 +              tn = symbol->nodes[j];
 +              if (tn == NULL)
 +                      continue;
 +              k += tree_list(tn, list + k, t->flags);
 +              if (k >= list_size)
 +                      return list;
 +      }
 +      return list;
 +}
 +
 +
 +/* cint_dump --- dump array info */
 +
 +static NODE **
 +cint_dump(NODE *symbol, NODE *ndump)
 +{
 +      NODE *tn, *xn = NULL;
 +      int indent_level;
 +      size_t i;
 +      long cint_size = 0, xsize = 0;
 +      AWKNUM kb = 0;
 +      extern AWKNUM int_kilobytes(NODE *symbol);
 +      extern AWKNUM str_kilobytes(NODE *symbol);
 +      extern array_ptr int_array_func[];
 +
 +      indent_level = ndump->alevel;
 +
 +      if (symbol->xarray != NULL) {
 +              xn = symbol->xarray;
 +              xsize = xn->table_size;
 +      }
 +      cint_size = symbol->table_size - xsize;
 +      
 +      if ((symbol->flags & XARRAY) == 0)
 +              fprintf(output_fp, "%s `%s'\n",
 +                      (symbol->parent_array == NULL) ? "array" : "sub-array",
 +                      array_vname(symbol));
 +      indent_level++;
 +      indent(indent_level);
 +      fprintf(output_fp, "array_func: cint_array_func\n");
 +      if (symbol->flags != 0) {
 +              indent(indent_level);
 +              fprintf(output_fp, "flags: %s\n", flags2str(symbol->flags));
 +      }
 +      indent(indent_level);
 +      fprintf(output_fp, "NHAT: %d\n", NHAT);
 +      indent(indent_level);
 +      fprintf(output_fp, "THRESHOLD: %ld\n", THRESHOLD);
 +      indent(indent_level);
 +      fprintf(output_fp, "table_size: %ld (total), %ld (cint), %ld (int + 
str)\n",
 +                              symbol->table_size, cint_size, xsize);
 +      indent(indent_level);
 +      fprintf(output_fp, "array_capacity: %lu\n", (unsigned long) 
symbol->array_capacity);
 +      indent(indent_level);
 +      fprintf(output_fp, "Load Factor: %.2g\n", (AWKNUM) cint_size / 
symbol->array_capacity);
 +
 +      for (i = NHAT; i < INT32_BIT; i++) {
 +              tn = symbol->nodes[i];
 +              if (tn == NULL)
 +                      continue;
 +              /* Node_array_tree  + HAT */
 +              kb += (sizeof(NODE) + tree_kilobytes(tn)) / 1024.0;
 +      }
 +      kb += (INT32_BIT * sizeof(NODE *)) / 1024.0;    /* symbol->nodes */     
 +      kb += (symbol->array_capacity * sizeof(NODE *)) / 1024.0;       /* 
value nodes in Node_array_leaf(s) */
 +      if (xn != NULL) {
 +              if (xn->array_funcs == int_array_func)
 +                      kb += int_kilobytes(xn);
 +              else
 +                      kb += str_kilobytes(xn);
 +      }
 +
 +      indent(indent_level);
 +      fprintf(output_fp, "memory: %.2g kB (total)\n", kb);
 +
 +      /* dump elements */
 +
 +      if (ndump->adepth >= 0) {
 +              const char *aname;
 +
 +              fprintf(output_fp, "\n");
 +              aname = make_aname(symbol);
 +              for (i = NHAT; i < INT32_BIT; i++) {
 +                      tn = symbol->nodes[i];
 +                      if (tn != NULL)
 +                              tree_info(tn, ndump, aname);
 +              }
 +      }
 +
 +      if (xn != NULL) {
 +              fprintf(output_fp, "\n");
 +              xn->adump(xn, ndump);
 +      }
 +
 +#ifdef ARRAYDEBUG
 +      if (ndump->adepth < -999)
 +              cint_print(symbol);
 +#endif
 +
 +      return NULL;
 +}
 +
 +
 +/* cint_hash --- locate the HAT for a given number 'k' */
 +
 +static inline int
 +cint_hash(long k)
 +{
 +      uint32_t num, r, shift;
 +
 +      assert(k >= 0);
 +      if (k == 0)
 +              return NHAT;
 +      num = k;
 +
 +      /* Find the Floor(log base 2 of 32-bit integer) */
 +
 +      /* Warren Jr., Henry S. (2002). Hacker's Delight.
 +       * Addison Wesley. pp. pp. 215. ISBN 978-0201914658.
 +       *
 +       *      r = 0;
 +       *      if (num >= 1<<16) { num >>= 16; r += 16; }
 +       *      if (num >= 1<< 8) { num >>=  8; r +=  8; }
 +       *      if (num >= 1<< 4) { num >>=  4; r +=  4; }
 +       *      if (num >= 1<< 2) { num >>=  2; r +=  2; }
 +       *      if (num >= 1<< 1) {             r +=  1; }
 +       */
 +
 +
 +      /* Slightly different code copied from:
 +       *
 +       * http://www-graphics.stanford.edu/~seander/bithacks.html
 +       * Bit Twiddling Hacks
 +       * By Sean Eron Anderson
 +       * address@hidden
 +       * Individually, the code snippets here are in the public domain
 +       * (unless otherwise noted) — feel free to use them however you 
please.
 +       * The aggregate collection and descriptions are © 1997-2005
 +       * Sean Eron Anderson. The code and descriptions are distributed in the
 +       * hope that they will be useful, but WITHOUT ANY WARRANTY and without
 +       * even the implied warranty of merchantability or fitness for a 
particular
 +       * purpose.  
 +       *
 +       */
 +
 +      r = (num > 0xFFFF) << 4; num >>= r;
 +      shift = (num > 0xFF) << 3; num >>= shift; r |= shift;
 +      shift = (num > 0x0F) << 2; num >>= shift; r |= shift;
 +      shift = (num > 0x03) << 1; num >>= shift; r |= shift;
 +      r |= (num >> 1);
 +
 +      /* We use a single HAT for 0 <= num < 2^NHAT */
 +      if (r < NHAT)
 +              return NHAT;
 +
 +      return (1 + r);
 +}
 +
 +
 +/* cint_find --- locate the integer subscript */
 +
 +static inline NODE **
 +cint_find(NODE *symbol, long k, int h1)
 +{
 +      NODE *tn;
 +
 +      if (symbol->nodes == NULL || (tn = symbol->nodes[h1]) == NULL)
 +              return NULL;
 +      return tree_exists(tn, k);
 +}
 +
 +
 +#ifdef ARRAYDEBUG
 +
 +static NODE **
 +cint_option(NODE *opt, NODE *val)
 +{
 +      NODE *tmp;
 +      NODE **ret = (NODE **) ! NULL;
 +
 +      tmp = force_string(opt);
 +      (void) force_number(val);
-       if (STREQ(tmp->stptr, "NHAT"))
++      if (strcmp(tmp->stptr, "NHAT") == 0)
 +              NHAT = (int) val->numbr;
 +      else
 +              ret = NULL;
 +      return ret;
 +}
 +
 +
 +/* cint_print --- print structural info */
 +
 +static void
 +cint_print(NODE *symbol)
 +{
 +      NODE *tn;
 +      size_t i;
 +
 +      fprintf(output_fp, "I[%4lu:%-4lu]\n", (unsigned long) INT32_BIT,
 +                              (unsigned long) symbol->table_size);
 +      for (i = NHAT; i < INT32_BIT; i++) {
 +              tn = symbol->nodes[i];
 +              if (tn == NULL)
 +                      continue;
 +              tree_print(tn, i, 1);
 +      }
 +}
 +
 +#endif
 +
 +
 +/*------------------------ Hashed Array Trees -----------------------------*/
 +
 +/*
 + * HATs: Hashed Array Trees
 + * Fast variable-length arrays
 + * Edward Sitarski
 + * http://www.drdobbs.com/architecture-and-design/184409965
 + *
 + *  HAT has a top-level array containing a power of two
 + *  number of leaf arrays. All leaf arrays are the same size as the
 + *  top-level array. A full HAT can hold n^2 elements,
 + *  where n (some power of 2) is the size of each leaf array.
 + *  [i/n][i & (n - 1)] locates the `i th' element in a HAT.
 + *
 + */
 +
 +/*
 + *  A half HAT is defined here as a HAT with a top-level array of size n^2/2
 + *  and holds the first n^2/2 elements.
 + * 
 + *   1. 2^8 elements can be stored in a full HAT of size 2^4.
 + *   2. 2^9 elements can be stored in a half HAT of size 2^5.     
 + *   3. When the number of elements is some power of 2, it
 + *      can be stored in a full or a half HAT.
 + *   4. When the number of elements is some power of 2, it
 + *      can be stored in a HAT (full or half) with HATs as leaf elements
 + *      (full or half),  and so on (e.g. 2^8 elements in a HAT of size 2^4 
(top-level
 + *      array dimension) with each leaf array being a HAT of size 2^2). 
 + *
 + *  IMPLEMENTATION DETAILS:
 + *    1. A HAT of 2^12 elements needs 2^6 house-keeping NODEs
 + *       of Node_array_leaf.
 + *
 + *    2. A HAT of HATS of 2^12 elements needs
 + *       2^6 * (1 Node_array_tree + 2^3 Node_array_leaf)
 + *       ~ 2^9 house-keeping NODEs.
 + *
 + *    3. When a leaf array (or leaf HAT) becomes empty, the memory
 + *       is deallocated, and when there is no leaf array (or leaf HAT) left,
 + *       the HAT is deleted.
 + *
 + *    4. A HAT stores the base (first) element, and locates the leaf array/HAT
 + *       for the `i th' element using integer division
 + *       (i - base)/n where n is the size of the top-level array.
 + *
 + */
 +
 +/* make_node --- initialize a NODE */
 +
 +static inline NODE *
 +make_node(NODETYPE type)
 +{
 +      NODE *n;
 +      getnode(n);
 +      memset(n, '\0', sizeof(NODE));
 +      n->type = type;
 +      return n;
 +}
 +
 +
 +/* tree_lookup --- Find an integer subscript in a HAT; Install it if it isn't 
there */
 +
 +static NODE **
 +tree_lookup(NODE *symbol, NODE *tree, long k, int m, long base)
 +{
 +      NODE **lhs;
 +      NODE *tn;
 +      int i, n;
 +      size_t size;
 +      long num = k;
 +
 +      /*
 +       * HAT size (size of Top & Leaf array) = 2^n
 +       * where n = Floor ((m + 1)/2). For an odd value of m,
 +       * only the first half of the HAT is needed.
 +       */
 +
 +      n = (m + 1) / 2;
 +      
 +      if (tree->table_size == 0) {
 +              size_t actual_size;
 +              NODE **table;
 +
 +              assert(tree->nodes == NULL);
 +
 +              /* initialize top-level array */
 +              size = actual_size = power_two_table[n];
 +              tree->array_base = base;
 +              tree->array_size = size;
 +              tree->table_size = 0;   /* # of elements in the array */
 +              if (n > m/2) {
 +                      /* only first half of the array used */
 +                      actual_size /= 2;
 +                      tree->flags |= HALFHAT;
 +              }
 +              emalloc(table, NODE **, actual_size * sizeof(NODE *), 
"tree_lookup");
 +              memset(table, '\0', actual_size * sizeof(NODE *));
 +              tree->nodes = table;
 +      } else
 +              size = tree->array_size;
 +
 +      num -= tree->array_base;
 +      i = num / size; /* top-level array index */
 +      assert(i >= 0);
 +
 +      if ((lhs = tree_find(tree, k, i)) != NULL)
 +              return lhs;
 +
 +      /* It's not there, install it */
 +
 +      tree->table_size++;
 +      base += (size * i);
 +      tn = tree->nodes[i];
 +      if (n > NHAT) {
 +              if (tn == NULL)
 +                      tn = tree->nodes[i] = make_node(Node_array_tree);
 +              return tree_lookup(symbol, tn, k, n, base);
 +      } else {
 +              if (tn == NULL)
 +                      tn = tree->nodes[i] = make_node(Node_array_leaf);
 +              return leaf_lookup(symbol, tn, k, size, base);
 +      }
 +}
 +
 +
 +/* tree_exists --- test whether integer subscript `k' exists or not */
 +
 +static NODE **
 +tree_exists(NODE *tree, long k)
 +{
 +      int i;
 +      NODE *tn;
 +
 +      i = (k - tree->array_base) / tree->array_size;
 +      assert(i >= 0);
 +      tn = tree->nodes[i];
 +      if (tn == NULL)
 +              return NULL;
 +      if (tn->type == Node_array_tree)
 +              return tree_exists(tn, k);
 +      return leaf_exists(tn, k);
 +}
 +
 +/* tree_clear --- flush all the values */
 +
 +static void
 +tree_clear(NODE *tree)
 +{
 +      NODE *tn;
 +      size_t  j, hsize;
 +
 +      hsize = tree->array_size;
 +      if ((tree->flags & HALFHAT) != 0)
 +              hsize /= 2;
 +
 +      for (j = 0; j < hsize; j++) {
 +              tn = tree->nodes[j];
 +              if (tn == NULL)
 +                      continue;
 +              if (tn->type == Node_array_tree)
 +                      tree_clear(tn);
 +              else
 +                      leaf_clear(tn);
 +              freenode(tn);
 +      }
 +
 +      efree(tree->nodes);
 +      memset(tree, '\0', sizeof(NODE));
 +      tree->type = Node_array_tree;
 +}
 +
 +
 +/* tree_remove --- If the integer subscript is in the HAT, remove it */
 +
 +static int
 +tree_remove(NODE *symbol, NODE *tree, long k)
 +{
 +      int i;
 +      NODE *tn;
 +
 +      i = (k - tree->array_base) / tree->array_size;
 +      assert(i >= 0);
 +      tn = tree->nodes[i];
 +      if (tn == NULL)
 +              return FALSE;
 +
 +      if (tn->type == Node_array_tree
 +                      && ! tree_remove(symbol, tn, k))
 +              return FALSE;
 +      else if (tn->type == Node_array_leaf
 +                      && ! leaf_remove(symbol, tn, k))
 +              return FALSE;
 +
 +      if (tn->table_size == 0) {
 +              freenode(tn);
 +              tree->nodes[i] = NULL;
 +      }
 +
 +      /* one less item in array */
 +      if (--tree->table_size == 0) {
 +              efree(tree->nodes);
 +              memset(tree, '\0', sizeof(NODE));
 +              tree->type = Node_array_tree;
 +      }
 +      return TRUE;
 +}
 +
 +
 +/* tree_find --- locate an interger subscript in the HAT */
 +
 +static inline NODE **
 +tree_find(NODE *tree, long k, int i)
 +{
 +      NODE *tn;
 +
 +      assert(tree->nodes != NULL);
 +      tn = tree->nodes[i];
 +      if (tn != NULL) {
 +              if (tn->type == Node_array_tree)
 +                      return tree_exists(tn, k);
 +              return leaf_exists(tn, k);
 +      }
 +      return NULL;
 +}
 +
 +
 +/* tree_list --- return a list of items in the HAT */
 +
 +static long
 +tree_list(NODE *tree, NODE **list, unsigned int flags)
 +{
 +      NODE *tn;
 +      size_t j, cj, hsize;
 +      long k = 0;
 +
 +      assert(list != NULL);
 +
 +      hsize = tree->array_size;
 +      if ((tree->flags & HALFHAT) != 0)
 +              hsize /= 2;
 +
 +      for (j = 0; j < hsize; j++) {
 +              cj = (flags & ADESC) ? (hsize - 1 - j) : j;
 +              tn = tree->nodes[cj];
 +              if (tn == NULL)
 +                      continue;
 +              if (tn->type == Node_array_tree)
 +                      k += tree_list(tn, list + k, flags);
 +              else
 +                      k += leaf_list(tn, list + k, flags);
 +              if ((flags & ADELETE) != 0 && k >= 1)
 +                      return k;
 +      }
 +      return k;
 +}
 +
 +
 +/* tree_copy --- duplicate a HAT */
 +
 +static void
 +tree_copy(NODE *newsymb, NODE *tree, NODE *newtree)
 +{ 
 +      NODE **old, **new;
 +      size_t j, hsize;
 +
 +      hsize = tree->array_size;
 +      if ((tree->flags & HALFHAT) != 0)
 +              hsize /= 2;
 +
 +      emalloc(new, NODE **, hsize * sizeof(NODE *), "tree_copy");
 +      memset(new, '\0', hsize * sizeof(NODE *));
 +      newtree->nodes = new;
 +      newtree->array_base = tree->array_base;
 +      newtree->array_size = tree->array_size;
 +      newtree->table_size = tree->table_size;
 +      newtree->flags = tree->flags;
 +
 +      old = tree->nodes;
 +      for (j = 0; j < hsize; j++) {
 +              if (old[j] == NULL)
 +                      continue;
 +              if (old[j]->type == Node_array_tree) {
 +                      new[j] = make_node(Node_array_tree);
 +                      tree_copy(newsymb, old[j], new[j]);
 +              } else {
 +                      new[j] = make_node(Node_array_leaf);
 +                      leaf_copy(newsymb, old[j], new[j]);
 +              }
 +      }
 +}
 +
 +
 +/* tree_info --- print index, value info */
 +
 +static void
 +tree_info(NODE *tree, NODE *ndump, const char *aname)
 +{
 +      NODE *tn;
 +      size_t j, hsize;
 +
 +      hsize = tree->array_size;
 +      if ((tree->flags & HALFHAT) != 0)
 +              hsize /= 2;
 +
 +      for (j = 0; j < hsize; j++) {
 +              tn = tree->nodes[j];
 +              if (tn == NULL)
 +                      continue;
 +              if (tn->type == Node_array_tree)
 +                      tree_info(tn, ndump, aname);
 +              else
 +                      leaf_info(tn, ndump, aname);
 +      }
 +}
 +
 +
 +/* tree_kilobytes --- calculate memory consumption of a HAT */
 +
 +static size_t
 +tree_kilobytes(NODE *tree)
 +{
 +      NODE *tn;
 +      size_t j, hsize;
 +      size_t sz = 0;
 +
 +      hsize = tree->array_size;
 +      if ((tree->flags & HALFHAT) != 0)
 +              hsize /= 2;
 +      for (j = 0; j < hsize; j++) {
 +              tn = tree->nodes[j];
 +              if (tn == NULL)
 +                      continue;
 +              sz += sizeof(NODE);     /* Node_array_tree or Node_array_leaf */
 +              if (tn->type == Node_array_tree)
 +                      sz += tree_kilobytes(tn);
 +      }
 +      sz += hsize * sizeof(NODE *);   /* tree->nodes */
 +      return sz;
 +}
 +
 +#ifdef ARRAYDEBUG
 +
 +/* tree_print --- print the HAT structures */
 +
 +static void
 +tree_print(NODE *tree, size_t bi, int indent_level)
 +{
 +      NODE *tn;
 +      size_t j, hsize;
 +
 +      indent(indent_level);
 +
 +      hsize = tree->array_size;
 +      if ((tree->flags & HALFHAT) != 0)
 +              hsize /= 2;
 +      fprintf(output_fp, "%4lu:%s[%4lu:%-4lu]\n", bi,
 +                      (tree->flags & HALFHAT) ? "HH" : "H",
 +                      (unsigned long) hsize, (unsigned long) 
tree->table_size);
 +
 +      for (j = 0; j < hsize; j++) {
 +              tn = tree->nodes[j];
 +              if (tn == NULL)
 +                      continue;
 +              if (tn->type == Node_array_tree)
 +                      tree_print(tn, j, indent_level + 1);
 +              else
 +                      leaf_print(tn, j, indent_level + 1);
 +      }
 +}
 +#endif
 +
 +/*--------------------- leaf (linear 1-D) array --------------------*/
 +
 +/* leaf_lookup --- find an integer subscript in the array; Install it if
 +      it isn't there.
 +*/
 +
 +static inline NODE **
 +leaf_lookup(NODE *symbol, NODE *array, long k, long size, long base)
 +{
 +      NODE **lhs;
 +
 +      if (array->nodes == NULL) {
 +              array->table_size = 0;  /* sanity */
 +              array->array_size = size;
 +              array->array_base = base;
 +              emalloc(array->nodes, NODE **, size * sizeof(NODE *), 
"leaf_lookup");
 +              memset(array->nodes, '\0', size * sizeof(NODE *));
 +              symbol->array_capacity += size;
 +      }
 +
 +      lhs = array->nodes + (k - base); /* leaf element */
 +      if (*lhs == NULL) {
 +              array->table_size++;    /* one more element in leaf array */
 +              *lhs = dupnode(Nnull_string);
 +      }
 +      return lhs;
 +}
 +
 +
 +/* leaf_exists --- check if the array contains an integer subscript */ 
 +
 +static inline NODE **
 +leaf_exists(NODE *array, long k)
 +{
 +      NODE **lhs;
 +      lhs = array->nodes + (k - array->array_base); 
 +      return (*lhs != NULL) ? lhs : NULL;
 +}
 +
 +
 +/* leaf_clear --- flush all values in the array */
 +
 +static void
 +leaf_clear(NODE *array)
 +{
 +      long i, size = array->array_size;
 +      NODE *r;
 +
 +      for (i = 0; i < size; i++) {
 +              r = array->nodes[i];
 +              if (r == NULL)
 +                      continue;
 +              if (r->type == Node_var_array) {
 +                      assoc_clear(r);         /* recursively clear all 
sub-arrays */
 +                      efree(r->vname);                        
 +                      freenode(r);
 +              } else
 +                      unref(r);
 +      }
 +      efree(array->nodes);
 +      array->nodes = NULL;
 +      array->array_size = array->table_size = 0;
 +}
 +
 +
 +/* leaf_remove --- remove an integer subscript from the array */
 +
 +static int
 +leaf_remove(NODE *symbol, NODE *array, long k)
 +{
 +      NODE **lhs;
 +
 +      lhs = array->nodes + (k - array->array_base); 
 +      if (*lhs == NULL)
 +              return FALSE;
 +      *lhs = NULL;
 +      if (--array->table_size == 0) {
 +              efree(array->nodes);
 +              array->nodes = NULL;
 +              symbol->array_capacity -= array->array_size;
 +              array->array_size = 0;  /* sanity */
 +      }
 +      return TRUE;
 +}
 +
 +
 +/* leaf_copy --- duplicate a leaf array */
 +
 +static void
 +leaf_copy(NODE *newsymb, NODE *array, NODE *newarray)
 +{
 +      NODE **old, **new;
 +      long size, i;
 +
 +      size = array->array_size;
 +      emalloc(new, NODE **, size * sizeof(NODE *), "leaf_copy");
 +      memset(new, '\0', size * sizeof(NODE *));
 +      newarray->nodes = new;
 +      newarray->array_size = size;
 +      newarray->array_base = array->array_base;
 +      newarray->flags = array->flags;
 +      newarray->table_size = array->table_size;
 +
 +      old = array->nodes;
 +      for (i = 0; i < size; i++) {
 +              if (old[i] == NULL)
 +                      continue;
 +              if (old[i]->type == Node_val)
 +                      new[i] = dupnode(old[i]);
 +              else {
 +                      NODE *r;
 +                      r = make_array();
 +                      r->vname = estrdup(old[i]->vname, 
strlen(old[i]->vname));
 +                      r->parent_array = newsymb;
 +                      new[i] = assoc_copy(old[i], r);
 +              }
 +      }
 +}
 +
 +
 +/* leaf_list --- return a list of items */
 +
 +static long
 +leaf_list(NODE *array, NODE **list, unsigned int flags)
 +{
 +      NODE *r, *subs;
 +      long num, i, ci, k = 0;
 +      long size = array->array_size;
 +      static char buf[100];
 +
 +      for (i = 0; i < size; i++) {
 +              ci = (flags & ADESC) ? (size - 1 - i) : i;
 +              r = array->nodes[ci];
 +              if (r == NULL)
 +                      continue;
 +
 +              /* index */
 +              num = array->array_base + ci;
 +              if (flags & AISTR) {
 +                      sprintf(buf, "%ld", num); 
 +                      subs = make_string(buf, strlen(buf));
 +                      subs->numbr = num;
 +                      subs->flags |= (NUMCUR|NUMINT);
 +              } else {
 +                      subs = make_number((AWKNUM) num);
 +                      subs->flags |= (INTIND|NUMINT);
 +              }
 +              list[k++] = subs;
 +
 +              /* value */
 +              if (flags & AVALUE) {
 +                      if (r->type == Node_val) {
 +                              if ((flags & AVNUM) != 0)
 +                                      (void) force_number(r);
 +                              else if ((flags & AVSTR) != 0)
 +                                      r = force_string(r);
 +                      }
 +                      list[k++] = r;
 +              }
 +              if ((flags & ADELETE) != 0 && k >= 1)
 +                      return k;
 +      }
 +
 +      return k;
 +}
 +
 +
 +/* leaf_info --- print index, value info */
 +
 +static void
 +leaf_info(NODE *array, NODE *ndump, const char *aname)
 +{
 +      NODE *subs, *val;
 +      size_t i, size;
 +
 +      size = array->array_size;
 +
 +      subs = make_number((AWKNUM) 0.0);
 +      subs->flags |= (INTIND|NUMINT);
 +      for (i = 0; i < size; i++) {
 +              val = array->nodes[i];
 +              if (val == NULL)
 +                      continue;
 +              subs->numbr = array->array_base + i;
 +              assoc_info(subs, val, ndump, aname);
 +      }
 +      unref(subs);
 +}
 +
 +#ifdef ARRAYDEBUG
 +
 +/* leaf_print --- print the leaf-array structure */
 +
 +
 +static void
 +leaf_print(NODE *array, size_t bi, int indent_level)
 +{
 +      indent(indent_level);
 +      fprintf(output_fp, "%4lu:L[%4lu:%-4lu]\n", bi,
 +                      (unsigned long) array->array_size,
 +                      (unsigned long) array->table_size);
 +}
 +#endif
diff --cc debug.c
index 76c2dcb,1cfbac4..5fb8d9e
--- a/debug.c
+++ b/debug.c
@@@ -987,10 -989,12 +987,10 @@@ find_param(const char *name, long num, 
                int i, pcount;
  
                func = f->func_node;
 -              pnames = get_params(func);
 -              pcount = get_param_count(func);
 -
 +              pcount = func->param_cnt;
                for (i = 0; i < pcount; i++) {
 -                      if (strcmp(name, pnames[i]) == 0) {
 +                      fparam = func->fparms[i].param; 
-                       if (STREQ(name, fparam)) {
++                      if (strcmp(name, fparam) == 0) {
                                r = f->stack[i];
                                if (r->type == Node_array_ref)
                                        r = r->orig_array;
diff --cc int_array.c
index 6adb763,0000000..9dd20be
mode 100644,000000..100644
--- a/int_array.c
+++ b/int_array.c
@@@ -1,828 -1,0 +1,828 @@@
 +/*
 + * int_array.c - routines for arrays of integer indices.
 + */
 +
 +/* 
 + * Copyright (C) 1986, 1988, 1989, 1991-2011 the Free Software Foundation, 
Inc.
 + * 
 + * This file is part of GAWK, the GNU implementation of the
 + * AWK Programming Language.
 + * 
 + * GAWK is free software; you can redistribute it and/or modify
 + * it under the terms of the GNU General Public License as published by
 + * the Free Software Foundation; either version 3 of the License, or
 + * (at your option) any later version.
 + * 
 + * GAWK is distributed in the hope that it will be useful,
 + * but WITHOUT ANY WARRANTY; without even the implied warranty of
 + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 + * GNU General Public License for more details.
 + * 
 + * You should have received a copy of the GNU General Public License
 + * along with this program; if not, write to the Free Software
 + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, 
USA
 + */
 +
 +#include "awk.h"
 +
 +extern FILE *output_fp;
 +extern void indent(int indent_level);
 +extern NODE **is_integer(NODE *symbol, NODE *subs);
 +
 +static size_t INT_CHAIN_MAX = 2;
 +
 +static NODE **int_array_init(NODE *symbol, NODE *subs);
 +static NODE **int_lookup(NODE *symbol, NODE *subs);
 +static NODE **int_exists(NODE *symbol, NODE *subs);
 +static NODE **int_clear(NODE *symbol, NODE *subs);
 +static NODE **int_remove(NODE *symbol, NODE *subs);
 +static NODE **int_list(NODE *symbol, NODE *t);
 +static NODE **int_copy(NODE *symbol, NODE *newsymb);
 +static NODE **int_dump(NODE *symbol, NODE *ndump);
 +
 +#ifdef ARRAYDEBUG
 +static NODE **int_option(NODE *opt, NODE *val);
 +#endif
 +
 +static uint32_t int_hash(uint32_t k, uint32_t hsize);
 +static inline NODE **int_find(NODE *symbol, long k, uint32_t hash1);
 +static NODE **int_insert(NODE *symbol, long k, uint32_t hash1);
 +static void grow_int_table(NODE *symbol);
 +
 +array_ptr int_array_func[] = {
 +      int_array_init,
 +      is_integer,
 +      int_lookup,
 +      int_exists,
 +      int_clear,
 +      int_remove,
 +      int_list,
 +      int_copy,
 +      int_dump,
 +#ifdef ARRAYDEBUG
 +      int_option,
 +#endif
 +};
 +
 +
 +/* int_array_init --- check relevant environment variables */
 +
 +static NODE **
 +int_array_init(NODE *symbol ATTRIBUTE_UNUSED, NODE *subs ATTRIBUTE_UNUSED)
 +{
 +      long newval;
 +
 +      if ((newval = getenv_long("INT_CHAIN_MAX")) > 0)
 +              INT_CHAIN_MAX = newval;
 +      return (NODE **) ! NULL;
 +}
 +
 +
 +/* is_integer --- check if subscript is an integer */
 +
 +NODE **
 +is_integer(NODE *symbol, NODE *subs)
 +{
 +      long l;
 +      AWKNUM d;
 +
 +      if (subs == Nnull_string)
 +              return NULL;
 +
 +      if ((subs->flags & NUMINT) != 0)
 +              return (NODE **) ! NULL;
 +
 +      if ((subs->flags & NUMBER) != 0) {
 +              d = subs->numbr;
 +              if (d <= INT32_MAX && d >= INT32_MIN && d == (int32_t) d) {
 +                      subs->flags |= NUMINT;
 +                      return (NODE **) ! NULL;
 +              }
 +              return NULL;
 +      }
 +
 +      /* a[3]=1; print "3" in a    -- TRUE
 +       * a[3]=1; print "+3" in a   -- FALSE
 +       * a[3]=1; print "03" in a   -- FALSE
 +       * a[-3]=1; print "-3" in a  -- TRUE
 +       */
 +
 +      if ((subs->flags & (STRING|STRCUR)) != 0) {
 +              char *cp = subs->stptr, *cpend, *ptr;
 +              char save;
 +              size_t len = subs->stlen;
 +
 +              if (len == 0 || (! isdigit((unsigned char) *cp) && *cp != '-'))
 +                      return NULL;
 +              if (len > 1 && 
 +                      ((*cp == '0')           /* "00", "011" .. */
 +                              || (*cp == '-' && *(cp + 1) == '0')     /* 
"-0", "-011" .. */
 +                      )
 +              )
 +                      return NULL;
 +              if (len == 1 && *cp != '-') {   /* single digit */
 +                      subs->numbr = (long) (*cp - '0');
 +                      if ((subs->flags & MAYBE_NUM) != 0) {
 +                              subs->flags &= ~MAYBE_NUM;
 +                              subs->flags |= NUMBER;
 +                      }
 +                      subs->flags |= (NUMCUR|NUMINT);
 +                      return (NODE **) ! NULL;
 +              }
 +
 +              cpend = cp + len;
 +              save = *cpend;
 +              *cpend = '\0';
 +
 +              errno = 0;
 +              l = strtol(cp, & ptr, 10);
 +              *cpend = save;
 +              if (errno != 0 || ptr != cpend)
 +                      return NULL;
 +              subs->numbr = l;
 +              if ((subs->flags & MAYBE_NUM) != 0) {
 +                      subs->flags &= ~MAYBE_NUM;
 +                      subs->flags |= NUMBER;
 +              }
 +              subs->flags |= NUMCUR;
 +              if (l <= INT32_MAX && l >= INT32_MIN) {
 +                      subs->flags |= NUMINT;
 +                      return (NODE **) ! NULL;
 +              }
 +      }
 +      return NULL;
 +}
 +
 +
 +/* int_lookup --- Find SYMBOL[SUBS] in the assoc array.  Install it with 
value ""
 + * if it isn't there. Returns a pointer ala get_lhs to where its value is 
stored.
 + */
 +
 +static NODE **
 +int_lookup(NODE *symbol, NODE *subs)
 +{
 +      uint32_t hash1;
 +      long k;
 +      unsigned long size;
 +      NODE **lhs;
 +      NODE *xn;
 +
 +      /* N.B: symbol->table_size is the total # of non-integers 
(symbol->xarray)
 +       *      and integer elements. Also, symbol->xarray must have at least 
one
 +       *      item in it, and can not exist if there are no integer elements.
 +       *      In that case, symbol->xarray is promoted to 'symbol' (See 
int_remove).
 +       */
 +
 +
 +      if (! is_integer(symbol, subs)) {
 +              xn = symbol->xarray; 
 +              if (xn == NULL) {
 +                      xn = symbol->xarray = make_array();
 +                      xn->vname = symbol->vname;      /* shallow copy */
 +                      xn->flags |= XARRAY;
 +              } else if ((lhs = xn->aexists(xn, subs)) != NULL)
 +                      return lhs;
 +              symbol->table_size++;
 +              return assoc_lookup(xn, subs);
 +      }
 +
 +      k = subs->numbr;
 +      if (symbol->buckets == NULL)
 +              grow_int_table(symbol);
 +
 +      hash1 = int_hash(k, symbol->array_size);
 +      if ((lhs = int_find(symbol, k, hash1)) != NULL)
 +              return lhs;
 +      
 +      /* It's not there, install it */
 +      
 +      symbol->table_size++;
 +
 +      /* first see if we would need to grow the array, before installing */
 +      size = symbol->table_size;
 +      if ((xn = symbol->xarray) != NULL)
 +              size -= xn->table_size;
 +
 +      if ((symbol->flags & ARRAYMAXED) == 0
 +                  && (size / symbol->array_size) > INT_CHAIN_MAX) {
 +              grow_int_table(symbol);
 +              /* have to recompute hash value for new size */
 +              hash1 = int_hash(k, symbol->array_size);
 +      }
 +      
 +      return int_insert(symbol, k, hash1);
 +}
 +
 +
 +/* int_exists --- test whether the array element symbol[subs] exists or not,
 + *    return pointer to value if it does.
 + */
 +
 +static NODE **
 +int_exists(NODE *symbol, NODE *subs)
 +{
 +      long k;
 +      uint32_t hash1;
 +
 +      if (! is_integer(symbol, subs)) {
 +              NODE *xn = symbol->xarray;
 +              if (xn == NULL)
 +                      return NULL;
 +              return xn->aexists(xn, subs);
 +      }
 +      if (symbol->buckets == NULL)
 +              return NULL;
 +
 +      k = subs->numbr;
 +      hash1 = int_hash(k, symbol->array_size);
 +      return int_find(symbol, k, hash1);
 +}
 +
 +/* int_clear --- flush all the values in symbol[] */
 +
 +static NODE **
 +int_clear(NODE *symbol, NODE *subs ATTRIBUTE_UNUSED)
 +{
 +      unsigned long i;
 +      int j;
 +      BUCKET *b, *next;
 +      NODE *r;
 +
 +      if (symbol->xarray != NULL) {
 +              NODE *xn = symbol->xarray;
 +              assoc_clear(xn);
 +              freenode(xn);
 +              symbol->xarray = NULL;
 +      }
 +
 +      for (i = 0; i < symbol->array_size; i++) {
 +              for (b = symbol->buckets[i]; b != NULL; b = next) {
 +                      next = b->ainext;
 +                      for (j = 0; j < b->aicount; j++) {
 +                              r = b->aivalue[j];
 +                              if (r->type == Node_var_array) {
 +                                      assoc_clear(r); /* recursively clear 
all sub-arrays */
 +                                      efree(r->vname);                        
 +                                      freenode(r);
 +                              } else
 +                                      unref(r);
 +                      }
 +                      freebucket(b);
 +              }
 +              symbol->buckets[i] = NULL;
 +      }
 +      if (symbol->buckets != NULL)
 +              efree(symbol->buckets);
 +      init_array(symbol);     /* re-initialize symbol */
 +      symbol->flags &= ~ARRAYMAXED;
 +      return NULL;
 +}
 +
 +
 +/* int_remove --- If SUBS is already in the table, remove it. */
 +
 +static NODE **
 +int_remove(NODE *symbol, NODE *subs)
 +{
 +      uint32_t hash1;
 +      BUCKET *b, *prev = NULL;
 +      long k;
 +      int i;
 +      NODE *xn = symbol->xarray;
 +
 +      if (symbol->table_size == 0 || symbol->buckets == NULL)
 +              return NULL;
 +
 +      if (! is_integer(symbol, subs)) {
 +              if (xn == NULL || xn->aremove(xn, subs) == NULL)
 +                      return NULL;
 +              if (xn->table_size == 0) {
 +                      freenode(xn);
 +                      symbol->xarray = NULL;
 +              }
 +              symbol->table_size--;
 +              assert(symbol->table_size > 0);
 +              return (NODE **) ! NULL;
 +      }
 +
 +      k = subs->numbr;
 +      hash1 = int_hash(k, symbol->array_size);
 +
 +      for (b = symbol->buckets[hash1]; b != NULL; prev = b, b = b->ainext) {
 +              for (i = 0; i < b->aicount; i++) {
 +                      if (k != b->ainum[i])
 +                              continue;
 +
 +                      /* item found */
 +                      if (i == 0 && b->aicount == 2) {
 +                              /* removing the 1st item; move 2nd item from 
position 1 to 0 */
 +
 +                              b->ainum[0] = b->ainum[1];
 +                              b->aivalue[0] = b->aivalue[1];
 +                      } /* else
 +                              removing the only item or the 2nd item */
 +
 +                      goto removed;
 +              }
 +      }
 +
 +      if (b == NULL)  /* item not in array */
 +              return NULL;
 +
 +removed:
 +      b->aicount--;
 +
 +      if (b->aicount == 0) {
 +              /* detach bucket */
 +              if (prev != NULL)
 +                      prev->ainext = b->ainext;
 +              else
 +                      symbol->buckets[hash1] = b->ainext;
 +
 +              /* delete bucket */
 +              freebucket(b);
 +      } else if (b != symbol->buckets[hash1]) {
 +              BUCKET *head = symbol->buckets[hash1];
 +
 +              assert(b->aicount == 1);
 +              /* move the last element from head
 +               * to bucket to make it full.
 +               */
 +              i = --head->aicount;    /* head has one less element */
 +              b->ainum[1] = head->ainum[i];
 +              b->aivalue[1] = head->aivalue[i];
 +              b->aicount++;   /* bucket has one more element */
 +              if (i == 0) {
 +                      /* head is now empty; delete head */
 +                      symbol->buckets[hash1] = head->ainext;
 +                      freebucket(head);
 +              }
 +      } /* else
 +              do nothing */
 +
 +      symbol->table_size--;
 +      if (xn == NULL && symbol->table_size == 0) {
 +              efree(symbol->buckets);
 +              init_array(symbol);     /* re-initialize array 'symbol' */
 +              symbol->flags &= ~ARRAYMAXED;
 +      } else if (xn != NULL && symbol->table_size == xn->table_size) {
 +              /* promote xn (str_array) to symbol */
 +              xn->flags &= ~XARRAY;
 +              xn->parent_array = symbol->parent_array;
 +              efree(symbol->buckets);
 +              *symbol = *xn;
 +              freenode(xn);
 +      }
 +
 +      return (NODE **) ! NULL;        /* return success */
 +}
 +
 +
 +/* int_copy --- duplicate input array "symbol" */
 +
 +static NODE **
 +int_copy(NODE *symbol, NODE *newsymb)
 +{
 +      BUCKET **old, **new, **pnew;
 +      BUCKET *chain, *newchain;
 +      int j;
 +      unsigned long i, cursize;
 +
 +      assert(symbol->buckets != NULL);
 +
 +      /* find the current hash size */
 +      cursize = symbol->array_size;
 +      
 +      /* allocate new table */
 +      emalloc(new, BUCKET **, cursize * sizeof(BUCKET *), "int_copy");
 +      memset(new, '\0', cursize * sizeof(BUCKET *));
 +
 +      old = symbol->buckets;
 +
 +      for (i = 0; i < cursize; i++) {
 +              for (chain = old[i], pnew = & new[i]; chain != NULL;
 +                              chain = chain->ainext
 +              ) {
 +                      getbucket(newchain);
 +                      newchain->aicount = chain->aicount;
 +                      for (j = 0; j < chain->aicount; j++) {
 +                              NODE *oldval;
 +
 +                              /*
 +                               * copy the corresponding key and
 +                               * value from the original input list
 +                               */
 +                              newchain->ainum[j] = chain->ainum[j];
 +
 +                              oldval = chain->aivalue[j];
 +                              if (oldval->type == Node_val)
 +                                      newchain->aivalue[j] = dupnode(oldval);
 +                              else {
 +                                      NODE *r;
 +                                      r = make_array();
 +                                      r->vname = estrdup(oldval->vname, 
strlen(oldval->vname));
 +                                      r->parent_array = newsymb;
 +                                      newchain->aivalue[j] = 
assoc_copy(oldval, r);
 +                              }
 +                      }
 +
 +                      *pnew = newchain;
 +                      pnew = & newchain->ainext;
 +              }
 +      }       
 +
 +      if (symbol->xarray != NULL) {
 +              NODE *xn, *n;
 +              xn = symbol->xarray;
 +              n = make_array();
 +              n->vname = newsymb->vname;      /* shallow copy */
 +              (void) xn->acopy(xn, n);
 +              newsymb->xarray = n;
 +      } else
 +              newsymb->xarray = NULL;
 +
 +      newsymb->table_size = symbol->table_size;
 +      newsymb->buckets = new;
 +      newsymb->array_size = cursize;
 +      newsymb->flags = symbol->flags;
 +
 +      return NULL;
 +}
 +
 +
 +/* int_list --- return a list of array items */
 +
 +static NODE**
 +int_list(NODE *symbol, NODE *t)
 +{
 +      NODE **list = NULL;
 +      unsigned long num_elems, list_size, i, k = 0;
 +      BUCKET *b;
 +      NODE *r, *subs, *xn;
 +      int j, elem_size = 1;
 +      long num;
 +      static char buf[100];
 +
 +      if (symbol->table_size == 0)
 +              return NULL;
 +
 +      num_elems = symbol->table_size;
 +      if ((t->flags & (AINDEX|AVALUE|ADELETE)) == (AINDEX|ADELETE))
 +              num_elems = 1;
 +
 +      if ((t->flags & (AINDEX|AVALUE)) == (AINDEX|AVALUE))
 +              elem_size = 2;
 +      list_size = elem_size * num_elems;
 +      
 +      if (symbol->xarray != NULL) {
 +              xn = symbol->xarray;
 +              list = xn->alist(xn, t);
 +              assert(list != NULL);
 +              if (num_elems == 1 || num_elems == xn->table_size)
 +                      return list;
 +              erealloc(list, NODE **, list_size * sizeof(NODE *), "int_list");
 +              k = elem_size * xn->table_size;
 +      } else
 +              emalloc(list, NODE **, list_size * sizeof(NODE *), "int_list");
 +
 +      /* populate it */
 +
 +      for (i = 0; i < symbol->array_size; i++) {
 +              for (b = symbol->buckets[i]; b != NULL; b = b->ainext) {
 +                      for (j = 0; j < b->aicount; j++) {
 +                              /* index */
 +                              num = b->ainum[j];
 +                              if (t->flags & AISTR) {
 +                                      sprintf(buf, "%ld", num); 
 +                                      subs = make_string(buf, strlen(buf));
 +                                      subs->numbr = num;
 +                                      subs->flags |= (NUMCUR|NUMINT);
 +                              } else {
 +                                      subs = make_number((AWKNUM) num);
 +                                      subs->flags |= (INTIND|NUMINT);
 +                              }
 +                              list[k++] = subs;
 +
 +                              /* value */
 +                              if (t->flags & AVALUE) {
 +                                      r = b->aivalue[j];
 +                                      if (r->type == Node_val) {
 +                                              if ((t->flags & AVNUM) != 0)
 +                                                      (void) force_number(r);
 +                                              else if ((t->flags & AVSTR) != 
0)
 +                                                      r = force_string(r);
 +                                      }
 +                                      list[k++] = r;
 +                              }
 +
 +                              if (k >= list_size)
 +                                      return list;
 +                      }
 +              }
 +      }
 +      return list;
 +}
 +
 +
 +/* int_kilobytes --- calculate memory consumption of the assoc array */
 +
 +AWKNUM
 +int_kilobytes(NODE *symbol)
 +{
 +      unsigned long i, bucket_cnt = 0;
 +      BUCKET *b;
 +      AWKNUM kb;
 +      extern AWKNUM str_kilobytes(NODE *symbol);
 +
 +      for (i = 0; i < symbol->array_size; i++) {
 +              for (b = symbol->buckets[i]; b != NULL; b = b->ainext)
 +                      bucket_cnt++;
 +      }
 +      kb = (((AWKNUM) bucket_cnt) * sizeof (BUCKET) + 
 +                      ((AWKNUM) symbol->array_size) * sizeof (BUCKET *)) / 
1024.0;
 +
 +      if (symbol->xarray != NULL)
 +              kb += str_kilobytes(symbol->xarray);
 +
 +      return kb;
 +}
 +
 +
 +/* int_dump --- dump array info */
 +
 +static NODE **
 +int_dump(NODE *symbol, NODE *ndump)
 +{
 +#define HCNT  31
 +
 +      int indent_level;
 +      BUCKET *b;
 +      NODE *xn = NULL;
 +      unsigned long str_size = 0, int_size = 0;
 +      unsigned long i;
 +      size_t j, bucket_cnt;
 +      static size_t hash_dist[HCNT + 1];
 +
 +      indent_level = ndump->alevel;
 +
 +      if (symbol->xarray != NULL) {
 +              xn = symbol->xarray;
 +              str_size = xn->table_size;
 +      }
 +      int_size = symbol->table_size - str_size;
 +
 +      if ((symbol->flags & XARRAY) == 0)
 +              fprintf(output_fp, "%s `%s'\n",
 +                              (symbol->parent_array == NULL) ? "array" : 
"sub-array",
 +                              array_vname(symbol));
 +
 +      indent_level++;
 +      indent(indent_level);
 +      fprintf(output_fp, "array_func: int_array_func\n");
 +      if (symbol->flags != 0) {
 +              indent(indent_level);
 +              fprintf(output_fp, "flags: %s\n", flags2str(symbol->flags));
 +      }
 +      indent(indent_level);
 +      fprintf(output_fp, "INT_CHAIN_MAX: %lu\n", (unsigned long) 
INT_CHAIN_MAX);
 +      indent(indent_level);
 +      fprintf(output_fp, "array_size: %lu (int)\n", (unsigned long) 
symbol->array_size);
 +      indent(indent_level);
 +      fprintf(output_fp, "table_size: %lu (total), %lu (int), %lu (str)\n",
 +                      (unsigned long) symbol->table_size, int_size, str_size);
 +      indent(indent_level);
 +      fprintf(output_fp, "Avg # of items per chain (int): %.2g\n",
 +                      ((AWKNUM) int_size) / symbol->array_size);
 +
 +      indent(indent_level);
 +      fprintf(output_fp, "memory: %.2g kB (total)\n", int_kilobytes(symbol));
 +
 +      /* hash value distribution */
 +
 +      memset(hash_dist, '\0', (HCNT + 1) * sizeof(size_t));
 +      for (i = 0; i < symbol->array_size; i++) {
 +              bucket_cnt = 0;
 +              for (b = symbol->buckets[i]; b != NULL; b = b->ainext)
 +                      bucket_cnt += b->aicount;
 +              if (bucket_cnt >= HCNT)
 +                      bucket_cnt = HCNT;
 +              hash_dist[bucket_cnt]++;
 +      }
 +
 +      indent(indent_level);
 +      fprintf(output_fp, "Hash distribution:\n");
 +      indent_level++;
 +      for (j = 0; j <= HCNT; j++) {
 +              if (hash_dist[j] > 0) {
 +                      indent(indent_level);
 +                      if (j == HCNT)
 +                              fprintf(output_fp, "[>=%lu]:%lu\n",
 +                                      (unsigned long) HCNT, (unsigned long) 
hash_dist[j]);
 +                      else
 +                              fprintf(output_fp, "[%lu]:%lu\n",
 +                                      (unsigned long) j, (unsigned long) 
hash_dist[j]);
 +              }
 +      }
 +      indent_level--;
 +
 +      /* dump elements */
 +
 +      if (ndump->adepth >= 0) {
 +              NODE *subs;
 +              const char *aname;
 +
 +              fprintf(output_fp, "\n");
 +
 +              aname = make_aname(symbol);
 +              subs = make_number((AWKNUM) 0);
 +              subs->flags |= (INTIND|NUMINT);
 +
 +              for (i = 0; i < symbol->array_size; i++) {
 +                      for (b = symbol->buckets[i]; b != NULL; b = b->ainext) {
 +                              for (j = 0; j < b->aicount; j++) {
 +                                      subs->numbr = b->ainum[j];
 +                                      assoc_info(subs, b->aivalue[j], ndump, 
aname);
 +                              }
 +                      }
 +              }
 +              unref(subs);
 +      }
 +
 +      if (xn != NULL) {
 +              fprintf(output_fp, "\n");
 +              xn->adump(xn, ndump);
 +      }
 +
 +      return NULL;
 +
 +#undef HCNT
 +}
 +
 +
 +/* int_hash --- calculate the hash function of the integer subs */
 +
 +static uint32_t
 +int_hash(uint32_t k, uint32_t hsize)
 +{
 +
 +/* Code snippet copied from:
 + *    Hash functions (http://www.azillionmonkeys.com/qed/hash.html).
 + *    Copyright 2004-2008 by Paul Hsieh. Licenced under LGPL 2.1.
 + */
 +
 +      /* This is the final mixing function used by Paul Hsieh
 +       * in SuperFastHash.
 +       */
 + 
 +      k ^= k << 3;
 +      k += k >> 5;
 +      k ^= k << 4;
 +      k += k >> 17;
 +      k ^= k << 25;
 +      k += k >> 6;
 +
 +      if (k >= hsize)
 +              k %= hsize;
 +      return k;
 +}
 +
 +/* int_find --- locate symbol[subs] */
 +
 +static inline NODE **
 +int_find(NODE *symbol, long k, uint32_t hash1)
 +{
 +      BUCKET *b;
 +      int i;
 +
 +      assert(symbol->buckets != NULL);
 +      for (b = symbol->buckets[hash1]; b != NULL; b = b->ainext) {
 +              for (i = 0; i < b->aicount; i++) {
 +                      if (b->ainum[i] == k)
 +                              return (b->aivalue + i);
 +              }
 +      }
 +      return NULL;
 +}
 +
 +
 +/* int_insert --- install subs in the assoc array */
 +
 +static NODE **
 +int_insert(NODE *symbol, long k, uint32_t hash1)
 +{
 +      BUCKET *b;
 +      int i;
 +
 +      b = symbol->buckets[hash1];
 +
 +      /* Only the first bucket in the chain can be partially full,
 +       * but is never empty.
 +       */ 
 +
 +      if (b == NULL || (i = b->aicount) == 2) {
 +              getbucket(b);
 +              b->aicount = 0;
 +              b->ainext = symbol->buckets[hash1];
 +              symbol->buckets[hash1] = b;
 +              i = 0;
 +      }
 +
 +      b->ainum[i] = k;
 +      b->aivalue[i] = dupnode(Nnull_string);
 +      b->aicount++;
 +      return & b->aivalue[i];
 +}
 +
 +
 +/* grow_int_table --- grow the hash table */
 +
 +static void
 +grow_int_table(NODE *symbol)
 +{
 +      BUCKET **old, **new;
 +      BUCKET *chain, *next;
 +      int i, j;
 +      unsigned long oldsize, newsize, k;
 +
 +      /*
 +       * This is an array of primes. We grow the table by an order of
 +       * magnitude each time (not just doubling) so that growing is a
 +       * rare operation. We expect, on average, that it won't happen
 +       * more than twice.  The final size is also chosen to be small
 +       * enough so that MS-DOG mallocs can handle it. When things are
 +       * very large (> 8K), we just double more or less, instead of
 +       * just jumping from 8K to 64K.
 +       */
 +
 +      static const unsigned long sizes[] = {
 +              13, 127, 1021, 8191, 16381, 32749, 65497,
 +              131101, 262147, 524309, 1048583, 2097169,
 +              4194319, 8388617, 16777259, 33554467, 
 +              67108879, 134217757, 268435459, 536870923,
 +              1073741827
 +      };
 +
 +      /* find next biggest hash size */
 +      newsize = oldsize = symbol->array_size;
 +
 +      for (i = 0, j = sizeof(sizes)/sizeof(sizes[0]); i < j; i++) {
 +              if (oldsize < sizes[i]) {
 +                      newsize = sizes[i];
 +                      break;
 +              }
 +      }
 +      if (newsize == oldsize) {       /* table already at max (!) */
 +              symbol->flags |= ARRAYMAXED;
 +              return;
 +      }
 +
 +      /* allocate new table */
 +      emalloc(new, BUCKET **, newsize * sizeof(BUCKET *), "grow_int_table");
 +      memset(new, '\0', newsize * sizeof(BUCKET *));
 +
 +      old = symbol->buckets;
 +      symbol->buckets = new;
 +      symbol->array_size = newsize;
 +
 +      /* brand new hash table */
 +      if (old == NULL)
 +              return;         /* DO NOT initialize symbol->table_size */
 +
 +      /* old hash table there, move stuff to new, free old */
 +      /* note that symbol->table_size does not change if an old array. */
 +
 +      for (k = 0; k < oldsize; k++) {
 +              long num;
 +              for (chain = old[k]; chain != NULL; chain = next) {
 +                      for (i = 0; i < chain->aicount; i++) {
 +                              num = chain->ainum[i];
 +                              *int_insert(symbol, num, int_hash(num, 
newsize)) = chain->aivalue[i];
 +                      }
 +                      next = chain->ainext;
 +                      freebucket(chain);
 +              }
 +      }
 +      efree(old);
 +}
 +
 +
 +#ifdef ARRAYDEBUG
 +
 +static NODE **
 +int_option(NODE *opt, NODE *val)
 +{
 +      int newval;
 +      NODE *tmp;
 +      NODE **ret = (NODE **) ! NULL;
 +
 +      tmp = force_string(opt);
 +      (void) force_number(val);
-       if (STREQ(tmp->stptr, "INT_CHAIN_MAX")) {
++      if (strcmp(tmp->stptr, "INT_CHAIN_MAX") == 0) {
 +              newval = (int) val->numbr;
 +              if (newval > 0)         
 +                      INT_CHAIN_MAX = newval;
 +      } else
 +              ret = NULL;
 +      return ret;
 +}
 +#endif
diff --cc interpret.h
index 66f5b0d,0000000..67a702e
mode 100644,000000..100644
--- a/interpret.h
+++ b/interpret.h
@@@ -1,1187 -1,0 +1,1187 @@@
 +/*
 + * interpret:
 + *   code is a list of instructions to run. returns the exit value
 + *     from the awk code.
 + */
 + 
 + /* N.B.:
 + *   1) reference counting done for both number and string values.
 + *   2) Stack operations:
 + *       Use REPLACE[_XX] if last stack operation was TOP[_XX],
 + *       PUSH[_XX] if last operation was POP[_XX] instead. 
 + *   3) UPREF and DREF -- see awk.h 
 + */
 +
 +int
 +r_interpret(INSTRUCTION *code)
 +{
 +      INSTRUCTION *pc;   /* current instruction */
 +      NODE *r = NULL;
 +      NODE *m;
 +      INSTRUCTION *ni;
 +      NODE *t1, *t2;
 +      NODE *f;        /* function definition */
 +      NODE **lhs;
 +      AWKNUM x, x1, x2;
 +      int di;
 +      Regexp *rp;
 +      int stdio_problem = FALSE;
 +
 +/* array subscript */
 +#define mk_sub(n)     (n == 1 ? POP_SCALAR() : concat_exp(n, TRUE))
 +
 +#ifdef DEBUGGING
 +#define JUMPTO(x)     do { post_execute(pc); pc = (x); goto top; } while 
(FALSE)
 +#else
 +#define JUMPTO(x)     do { pc = (x); goto top; } while (FALSE)
 +#endif
 +
 +      pc = code;
 +
 +      /* N.B.: always use JUMPTO for next instruction, otherwise bad things
 +       * may happen. DO NOT add a real loop (for/while) below to
 +       * replace ' forever {'; this catches failure to use JUMPTO to execute
 +       * next instruction (e.g. continue statement).
 +       */
 +
 +      /* loop until hit Op_stop instruction */
 +
 +      /* forever {  */
 +top:
 +              if (pc->source_line > 0)
 +                      sourceline = pc->source_line;
 +
 +#ifdef DEBUGGING
 +              if (! pre_execute(&pc))
 +                      goto top;
 +#endif
 +
 +              switch (pc->opcode) {
 +              case Op_rule:
 +                      currule = pc->in_rule;   /* for sole use in Op_K_next, 
Op_K_nextfile, Op_K_getline */
 +                      /* fall through */
 +              case Op_func:
 +                      source = pc->source_file;
 +                      break;
 +
 +              case Op_atexit:
 +                      /* avoid false source indications */
 +                      source = NULL;
 +                      sourceline = 0;
 +                      (void) nextfile(& curfile, TRUE);       /* close input 
data file */ 
 +                      /*
 +                       * This used to be:
 +                       *
 +                       * if (close_io() != 0 && ! exiting && exit_val == 0)
 +                       *      exit_val = 1;
 +                       *
 +                       * Other awks don't care about problems closing open 
files
 +                       * and pipes, in that it doesn't affect their exit 
status.
 +                       * So we no longer do either.
 +                       */
 +                      (void) close_io(& stdio_problem);
 +                      /*
 +                       * However, we do want to exit non-zero if there was a 
problem
 +                       * with stdout/stderr, so we reinstate a slightly 
different
 +                       * version of the above:
 +                       */
 +                      if (stdio_problem && ! exiting && exit_val == 0)
 +                              exit_val = 1;
 +                      break;
 +
 +              case Op_stop:
 +                      return 0;
 +
 +              case Op_push_i:
 +                      m = pc->memory;
 +                      if (! do_traditional && (m->flags & INTLSTR) != 0) {
 +                              char *orig, *trans, save;
 +
 +                              save = m->stptr[m->stlen];
 +                              m->stptr[m->stlen] = '\0';
 +                              orig = m->stptr;
 +                              trans = dgettext(TEXTDOMAIN, orig);
 +                              m->stptr[m->stlen] = save;
 +                              m = make_string(trans, strlen(trans));
 +                      } else
 +                              UPREF(m);
 +                      PUSH(m);
 +                      break;
 +
 +              case Op_push:
 +              case Op_push_arg:
 +              {
 +                      NODE *save_symbol;
 +                      int isparam = FALSE;
 +
 +                      save_symbol = m = pc->memory;
 +                      if (m->type == Node_param_list) {
 +                              isparam = TRUE;
 +                              save_symbol = m = GET_PARAM(m->param_cnt);
 +                              if (m->type == Node_array_ref)
 +                                      m = m->orig_array;
 +                      }
 +                              
 +                      switch (m->type) {
 +                      case Node_var:
 +                              if (do_lint && var_uninitialized(m))
 +                                      lintwarn(isparam ?
 +                                              _("reference to uninitialized 
argument `%s'") :
 +                                              _("reference to uninitialized 
variable `%s'"),
 +                                                              
save_symbol->vname);
 +                              m = m->var_value;
 +                              UPREF(m);
 +                              PUSH(m);
 +                              break;
 +
 +                      case Node_var_new:
 +                              m->type = Node_var;
 +                              m->var_value = dupnode(Nnull_string);
 +                              if (do_lint)
 +                                      lintwarn(isparam ?
 +                                              _("reference to uninitialized 
argument `%s'") :
 +                                              _("reference to uninitialized 
variable `%s'"),
 +                                                              
save_symbol->vname);
 +                              m = dupnode(Nnull_string);
 +                              PUSH(m);
 +                              break;
 +
 +                      case Node_var_array:
 +                              if (pc->opcode == Op_push_arg)
 +                                      PUSH(m);
 +                              else
 +                                      fatal(_("attempt to use array `%s' in a 
scalar context"),
 +                                                      
array_vname(save_symbol));
 +                              break;
 +
 +                      default:
 +                              cant_happen();
 +                      }
 +              }
 +                      break;  
 +
 +              case Op_push_param:             /* function argument */
 +                      m = pc->memory;
 +                      if (m->type == Node_param_list)
 +                              m = GET_PARAM(m->param_cnt);
 +                      if (m->type == Node_var) {
 +                              m = m->var_value;
 +                              UPREF(m);
 +                              PUSH(m);
 +                              break;
 +                      }
 +                      /* else
 +                              fall through */
 +              case Op_push_array:
 +                      PUSH(pc->memory);
 +                      break;
 +
 +              case Op_push_lhs:
 +                      lhs = get_lhs(pc->memory, pc->do_reference);
 +                      PUSH_ADDRESS(lhs);
 +                      break;
 +
 +              case Op_subscript:
 +                      t2 = mk_sub(pc->sub_count);
 +                      t1 = POP_ARRAY();
 +
 +                      if (do_lint && in_array(t1, t2) == NULL) {
 +                              t2 = force_string(t2);
 +                              lintwarn(_("reference to uninitialized element 
`%s[\"%.*s\"]'"),
 +                                      array_vname(t1), (int) t2->stlen, 
t2->stptr);
 +                              if (t2->stlen == 0)
 +                                      lintwarn(_("subscript of array `%s' is 
null string"), array_vname(t1));
 +                      }
 +
 +                      r = *assoc_lookup(t1, t2);
 +                      DEREF(t2);
 +                      if (r->type == Node_val)
 +                              UPREF(r);
 +                      PUSH(r);
 +                      break;
 +
 +              case Op_sub_array:
 +                      t2 = mk_sub(pc->sub_count);
 +                      t1 = POP_ARRAY();
 +                      r = in_array(t1, t2);
 +                      if (r == NULL) {
 +                              r = make_array();
 +                              r->parent_array = t1;
 +                              *assoc_lookup(t1, t2) = r;
 +                              t2 = force_string(t2);
 +                              r->vname = estrdup(t2->stptr, t2->stlen);       
/* the subscript in parent array */
 +                      } else if (r->type != Node_var_array) {
 +                              t2 = force_string(t2);
 +                              fatal(_("attempt to use scalar `%s[\"%.*s\"]' 
as an array"),
 +                                              array_vname(t1), (int) 
t2->stlen, t2->stptr);
 +                      }
 +
 +                      DEREF(t2);
 +                      PUSH(r);
 +                      break;
 +
 +              case Op_subscript_lhs:
 +                      t2 = mk_sub(pc->sub_count);
 +                      t1 = POP_ARRAY();
 +                      if (do_lint && in_array(t1, t2) == NULL) {
 +                              t2 = force_string(t2);
 +                              if (pc->do_reference) 
 +                                      lintwarn(_("reference to uninitialized 
element `%s[\"%.*s\"]'"),
 +                                              array_vname(t1), (int) 
t2->stlen, t2->stptr);
 +                              if (t2->stlen == 0)
 +                                      lintwarn(_("subscript of array `%s' is 
null string"), array_vname(t1));
 +                      }
 +
 +                      lhs = assoc_lookup(t1, t2);
 +                      if ((*lhs)->type == Node_var_array) {
 +                              t2 = force_string(t2);
 +                              fatal(_("attempt to use array `%s[\"%.*s\"]' in 
a scalar context"),
 +                                              array_vname(t1), (int) 
t2->stlen, t2->stptr);
 +                      }
 +
 +                      DEREF(t2);
 +                      PUSH_ADDRESS(lhs);
 +                      break;
 +
 +              case Op_field_spec:
 +                      t1 = TOP_SCALAR();
 +                      lhs = r_get_field(t1, (Func_ptr *) 0, TRUE);
 +                      decr_sp();
 +                      DEREF(t1);
 +                      r = dupnode(*lhs);     /* can't use UPREF here */
 +                      PUSH(r);
 +                      break;
 +
 +              case Op_field_spec_lhs:
 +                      t1 = TOP_SCALAR();
 +                      lhs = r_get_field(t1, &pc->target_assign->field_assign, 
pc->do_reference);
 +                      decr_sp();
 +                      DEREF(t1);
 +                      PUSH_ADDRESS(lhs);
 +                      break;
 +
 +              case Op_lint:
 +                      if (do_lint) {
 +                              switch (pc->lint_type) {
 +                              case LINT_assign_in_cond:
 +                                      lintwarn(_("assignment used in 
conditional context"));
 +                                      break;
 +
 +                              case LINT_no_effect:
 +                                      lintwarn(_("statement has no effect"));
 +                                      break;
 +
 +                              default:
 +                                      cant_happen();
 +                              }
 +                      }
 +                      break;
 +
 +              case Op_K_break:
 +              case Op_K_continue:
 +              case Op_jmp:
 +                      JUMPTO(pc->target_jmp);
 +
 +              case Op_jmp_false:
 +                      r = POP_SCALAR();
 +                      di = eval_condition(r);
 +                      DEREF(r);
 +                      if (! di)
 +                              JUMPTO(pc->target_jmp);
 +                      break;
 +
 +              case Op_jmp_true:
 +                      r = POP_SCALAR();
 +                      di = eval_condition(r);
 +                      DEREF(r);                       
 +                      if (di)
 +                              JUMPTO(pc->target_jmp);
 +                      break;
 +
 +              case Op_and:
 +              case Op_or:
 +                      t1 = POP_SCALAR();
 +                      di = eval_condition(t1);
 +                      DEREF(t1);
 +                      if ((pc->opcode == Op_and && di)
 +                                      || (pc->opcode == Op_or && ! di))
 +                              break;
 +                      r = node_Boolean[di];
 +                      UPREF(r);
 +                      PUSH(r);
 +                      ni = pc->target_jmp;
 +                      JUMPTO(ni->nexti);
 +
 +              case Op_and_final:
 +              case Op_or_final:
 +                      t1 = TOP_SCALAR();
 +                      r = node_Boolean[eval_condition(t1)];
 +                      DEREF(t1);
 +                      UPREF(r);
 +                      REPLACE(r);
 +                      break;
 +
 +              case Op_not:
 +                      t1 = TOP_SCALAR();
 +                      r = node_Boolean[! eval_condition(t1)];
 +                      DEREF(t1);
 +                      UPREF(r);
 +                      REPLACE(r);
 +                      break;
 +
 +              case Op_equal:
 +                      r = node_Boolean[cmp_scalar() == 0];
 +                      UPREF(r);
 +                      REPLACE(r);
 +                      break;
 +
 +              case Op_notequal:
 +                      r = node_Boolean[cmp_scalar() != 0];
 +                      UPREF(r);
 +                      REPLACE(r);
 +                      break;
 +
 +              case Op_less:
 +                      r = node_Boolean[cmp_scalar() < 0];
 +                      UPREF(r);
 +                      REPLACE(r);
 +                      break;
 +
 +              case Op_greater:
 +                      r = node_Boolean[cmp_scalar() > 0];
 +                      UPREF(r);
 +                      REPLACE(r);
 +                      break;
 +
 +              case Op_leq:
 +                      r = node_Boolean[cmp_scalar() <= 0];
 +                      UPREF(r);
 +                      REPLACE(r);
 +                      break;
 +
 +              case Op_geq:
 +                      r = node_Boolean[cmp_scalar() >= 0];
 +                      UPREF(r);
 +                      REPLACE(r);
 +                      break;
 +
 +              case Op_plus_i:
 +                      x2 = force_number(pc->memory);
 +                      goto plus;
 +
 +              case Op_plus:
 +                      POP_NUMBER(x2);
 +plus:
 +                      TOP_NUMBER(x1);
 +                      r = make_number(x1 + x2);
 +                      REPLACE(r);
 +                      break;
 +
 +              case Op_minus_i:
 +                      x2 = force_number(pc->memory);
 +                      goto minus;
 +
 +              case Op_minus:
 +                      POP_NUMBER(x2);
 +minus:
 +                      TOP_NUMBER(x1);
 +                      r = make_number(x1 - x2);
 +                      REPLACE(r);
 +                      break;
 +
 +              case Op_times_i:
 +                      x2 = force_number(pc->memory);
 +                      goto times;
 +
 +              case Op_times:
 +                      POP_NUMBER(x2);
 +times:
 +                      TOP_NUMBER(x1);
 +                      r = make_number(x1 * x2);
 +                      REPLACE(r);
 +                      break;
 +
 +              case Op_exp_i:
 +                      x2 = force_number(pc->memory);
 +                      goto exponent;
 +
 +              case Op_exp:
 +                      POP_NUMBER(x2);
 +exponent:
 +                      TOP_NUMBER(x1);
 +                      x = calc_exp(x1, x2);
 +                      r = make_number(x);
 +                      REPLACE(r);
 +                      break;
 +
 +              case Op_quotient_i:
 +                      x2 = force_number(pc->memory);
 +                      goto quotient;
 +
 +              case Op_quotient:
 +                      POP_NUMBER(x2);
 +quotient:
 +                      if (x2 == 0)
 +                              fatal(_("division by zero attempted"));
 +
 +                      TOP_NUMBER(x1);
 +                      x = x1 / x2;
 +                      r = make_number(x);
 +                      REPLACE(r);
 +                      break;          
 +
 +              case Op_mod_i:
 +                      x2 = force_number(pc->memory);
 +                      goto mod;
 +
 +              case Op_mod:
 +                      POP_NUMBER(x2);
 +mod:
 +                      if (x2 == 0)
 +                              fatal(_("division by zero attempted in `%%'"));
 +
 +                      TOP_NUMBER(x1);
 +#ifdef HAVE_FMOD
 +                      x = fmod(x1, x2);
 +#else /* ! HAVE_FMOD */
 +                      (void) modf(x1 / x2, &x);
 +                      x = x1 - x * x2;
 +#endif        /* ! HAVE_FMOD */
 +                      r = make_number(x);
 +                      REPLACE(r);
 +                      break;          
 +
 +              case Op_preincrement:
 +              case Op_predecrement:
 +                      x2 = pc->opcode == Op_preincrement ? 1.0 : -1.0;
 +                      lhs = TOP_ADDRESS();
 +                      t1 = *lhs;
 +                      x1 = force_number(t1);
 +                      if (t1->valref == 1 && t1->flags == 
(MALLOC|NUMCUR|NUMBER)) {
 +                              /* optimization */
 +                              t1->numbr = x1 + x2;
 +                      } else {
 +                              unref(t1);
 +                              t1 = *lhs = make_number(x1 + x2);
 +                      }
 +                      UPREF(t1);
 +                      REPLACE(t1);
 +                      break;
 +
 +              case Op_postincrement:
 +              case Op_postdecrement:
 +                      x2 = pc->opcode == Op_postincrement ? 1.0 : -1.0;
 +                      lhs = TOP_ADDRESS();
 +                      t1 = *lhs;
 +                      x1 = force_number(t1);
 +                      if (t1->valref == 1 && t1->flags == 
(MALLOC|NUMCUR|NUMBER)) {
 +                              /* optimization */
 +                              t1->numbr = x1 + x2;
 +                      } else {
 +                              unref(t1);
 +                              *lhs = make_number(x1 + x2);
 +                      }
 +                      r = make_number(x1);
 +                      REPLACE(r);
 +                      break;
 +
 +              case Op_unary_minus:
 +                      TOP_NUMBER(x1);
 +                      r = make_number(-x1);
 +                      REPLACE(r);
 +                      break;
 +
 +              case Op_store_sub:
 +                      /* array[sub] assignment optimization,
 +                       * see awkgram.y (optimize_assignment)
 +                       */
 +                      t1 = get_array(pc->memory, TRUE);       /* array */
 +                      t2 = mk_sub(pc->expr_count);    /* subscript */
 +                      lhs = assoc_lookup(t1, t2);
 +                      if ((*lhs)->type == Node_var_array) {
 +                              t2 = force_string(t2);
 +                              fatal(_("attempt to use array `%s[\"%.*s\"]' in 
a scalar context"),
 +                                              array_vname(t1), (int) 
t2->stlen, t2->stptr);
 +                      }
 +                      DEREF(t2);
 +                      unref(*lhs);
 +                      *lhs = POP_SCALAR();
 +                      break;
 +
 +              case Op_store_var:
 +                      /* simple variable assignment optimization,
 +                       * see awkgram.y (optimize_assignment)
 +                       */
 +      
 +                      lhs = get_lhs(pc->memory, FALSE);
 +                      unref(*lhs);
 +                      r = pc->initval;        /* constant initializer */
 +                      if (r == NULL)
 +                              *lhs = POP_SCALAR();
 +                      else {
 +                              UPREF(r);
 +                              *lhs = r;
 +                      }
 +                      break;
 +
 +              case Op_store_field:
 +              {
 +                      /* field assignment optimization,
 +                       * see awkgram.y (optimize_assignment)
 +                       */
 +
 +                      Func_ptr assign;
 +                      t1 = TOP_SCALAR();
 +                      lhs = r_get_field(t1, &assign, FALSE);
 +                      decr_sp();
 +                      DEREF(t1);
 +                      unref(*lhs);
 +                      *lhs = POP_SCALAR();
 +                      assert(assign != NULL);
 +                      assign();
 +              }
 +                      break;
 +
 +              case Op_assign_concat:
 +                      /* x = x ... string concatenation optimization */
 +                      lhs = get_lhs(pc->memory, FALSE);
 +                      t1 = force_string(*lhs);
 +                      t2 = POP_STRING();
 +
 +                      free_wstr(*lhs);
 +
 +                      if (t1 != *lhs) {
 +                              unref(*lhs);
 +                              *lhs = dupnode(t1);
 +                      }
 +
 +                      if (t1 != t2 && t1->valref == 1) {
 +                              size_t nlen = t1->stlen + t2->stlen;
 +
 +                              erealloc(t1->stptr, char *, nlen + 2, 
"r_interpret");
 +                              memcpy(t1->stptr + t1->stlen, t2->stptr, 
t2->stlen);
 +                              t1->stlen = nlen;
 +                              t1->stptr[nlen] = '\0';
 +                              t1->flags &= ~(NUMCUR|NUMBER|NUMINT);
 +                      } else {
 +                              size_t nlen = t1->stlen + t2->stlen;  
 +                              char *p;
 +
 +                              emalloc(p, char *, nlen + 2, "r_interpret");
 +                              memcpy(p, t1->stptr, t1->stlen);
 +                              memcpy(p + t1->stlen, t2->stptr, t2->stlen);
 +                              unref(*lhs);
 +                              t1 = *lhs = make_str_node(p, nlen, 
ALREADY_MALLOCED); 
 +                      }
 +                      DEREF(t2);
 +                      break;
 +
 +              case Op_assign:
 +                      lhs = POP_ADDRESS();
 +                      r = TOP_SCALAR();
 +                      unref(*lhs);
 +                      *lhs = r;
 +                      UPREF(r);
 +                      REPLACE(r);
 +                      break;
 +
 +              /* numeric assignments */
 +              case Op_assign_plus:
 +              case Op_assign_minus:
 +              case Op_assign_times:
 +              case Op_assign_quotient:
 +              case Op_assign_mod:
 +              case Op_assign_exp:
 +                      op_assign(pc->opcode);
 +                      break;
 +
 +              case Op_var_update:        /* update value of NR, FNR or NF */
 +                      pc->update_var();
 +                      break;
 +
 +              case Op_var_assign:
 +              case Op_field_assign:
 +                      if (pc->assign_ctxt == Op_sub_builtin
 +                              && TOP()->numbr == 0.0  /* top of stack has a 
number == 0 */
 +                      ) {
 +                              /* There wasn't any substitutions. If the 
target is a FIELD,
 +                               * this means no field re-splitting or $0 
reconstruction.
 +                               * Skip the set_FOO routine if the target is a 
special variable.
 +                               */
 +
 +                              break;
 +                      } else if ((pc->assign_ctxt == Op_K_getline
 +                                      || pc->assign_ctxt == 
Op_K_getline_redir)
 +                              && TOP()->numbr <= 0.0  /* top of stack has a 
number <= 0 */
 +                      ) {
 +                              /* getline returned EOF or error */
 +
 +                              break;
 +                      }
 +
 +                      if (pc->opcode == Op_var_assign)
 +                              pc->assign_var();
 +                      else
 +                              pc->field_assign();
 +                      break;
 +
 +              case Op_concat:
 +                      r = concat_exp(pc->expr_count, pc->concat_flag & 
CSUBSEP);
 +                      PUSH(r);
 +                      break;
 +
 +              case Op_K_case:
 +                      if ((pc + 1)->match_exp) {
 +                              /* match a constant regex against switch 
expression instead of $0. */
 +
 +                              m = POP();      /* regex */
 +                              t2 = TOP_SCALAR();      /* switch expression */
 +                              t2 = force_string(t2);
 +                              rp = re_update(m);
 +                              di = (research(rp, t2->stptr, 0, t2->stlen,
 +                                                      avoid_dfa(m, t2->stptr, 
t2->stlen)) >= 0);
 +                      } else {
 +                              t1 = POP_SCALAR();      /* case value */
 +                              t2 = TOP_SCALAR();      /* switch expression */
 +                              di = (cmp_nodes(t2, t1) == 0);
 +                              DEREF(t1);
 +                      }
 +
 +                      if (di) {
 +                              /* match found */
 +
 +                              t2 = POP_SCALAR();
 +                              DEREF(t2);
 +                              JUMPTO(pc->target_jmp);
 +                      }
 +                      break;
 +
 +              case Op_K_delete:
 +                      t1 = POP_ARRAY();
 +                      do_delete(t1, pc->expr_count);
 +                      stack_adj(-pc->expr_count);
 +                      break;
 +
 +              case Op_K_delete_loop:
 +                      t1 = POP_ARRAY();
 +                      lhs = POP_ADDRESS();    /* item */
 +                      do_delete_loop(t1, lhs);
 +                      break;
 +
 +              case Op_in_array:
 +                      t1 = POP_ARRAY();
 +                      t2 = mk_sub(pc->expr_count);
 +                      di = (in_array(t1, t2) != NULL);
 +                      DEREF(t2);
 +                      PUSH(make_number((AWKNUM) di));
 +                      break;
 +
 +              case Op_arrayfor_init:
 +              {
 +                      NODE **list = NULL;
 +                      NODE *array, *sort_str;
 +                      size_t num_elems = 0;
 +                      static NODE *sorted_in = NULL;
 +                      const char *how_to_sort = "@unsorted";
 +
 +                      /* get the array */
 +                      array = POP_ARRAY();
 +
 +                      /* sanity: check if empty */
 +                      if (array_empty(array))
 +                              goto arrayfor;
 +
 +                      num_elems = array->table_size;
 +
 +                      if (sorted_in == NULL)          /* do this once */
 +                              sorted_in = make_string("sorted_in", 9);
 +
 +                      sort_str = NULL;
 +                      /*
 +                       * If posix, or if there's no PROCINFO[],
 +                       * there's no ["sorted_in"], so no sorting
 +                       */
 +                      if (! do_posix && PROCINFO_node != NULL)
 +                              sort_str = in_array(PROCINFO_node, sorted_in);
 +
 +                      if (sort_str != NULL) {
 +                              sort_str = force_string(sort_str);
 +                              if (sort_str->stlen > 0)
 +                                      how_to_sort = sort_str->stptr;
 +                      }
 +
 +                      list = assoc_list(array, how_to_sort, SORTED_IN);
 +
 +arrayfor:
 +                      getnode(r);
 +                      r->type = Node_arrayfor;
 +                      r->for_list = list;
 +                      r->for_list_size = num_elems;           /* # of 
elements in list */
 +                      r->cur_idx = -1;                        /* current 
index */
 +                      r->for_array = array;           /* array */
 +                      PUSH(r);
 +
 +                      if (num_elems == 0)
 +                              JUMPTO(pc->target_jmp);   /* Op_arrayfor_final 
*/
 +              }
 +                      break;
 +
 +              case Op_arrayfor_incr:
 +                      r = TOP();      /* Node_arrayfor */
 +                      if (++r->cur_idx == r->for_list_size) {
 +                              NODE *array;
 +                              array = r->for_array;   /* actual array */
 +                              if (do_lint && array->table_size != 
r->for_list_size)
 +                                      lintwarn(_("for loop: array `%s' 
changed size from %ld to %ld during loop execution"),
 +                                              array_vname(array), (long) 
r->for_list_size, (long) array->table_size);
 +                              JUMPTO(pc->target_jmp); /* Op_arrayfor_final */
 +                      }
 +
 +                      t1 = r->for_list[r->cur_idx];
 +                      lhs = get_lhs(pc->array_var, FALSE);
 +                      unref(*lhs);
 +                      *lhs = dupnode(t1);
 +                      break;
 +
 +              case Op_arrayfor_final:
 +                      r = POP();
 +                      assert(r->type == Node_arrayfor);
 +                      free_arrayfor(r);
 +                      break;
 +
 +              case Op_builtin:
 +                      r = pc->builtin(pc->expr_count);
 +                      PUSH(r);
 +                      break;
 +
 +              case Op_ext_builtin:
 +              {
 +                      int arg_count = pc->expr_count;
 +
 +                      PUSH_CODE(pc);
 +                      r = pc->builtin(arg_count);
 +                      (void) POP_CODE();
 +                      while (arg_count-- > 0) {
 +                              t1 = POP();
 +                              if (t1->type == Node_val)
 +                                      DEREF(t1);
 +                      }
 +                      PUSH(r);
 +              }
 +                      break;
 +
 +              case Op_sub_builtin:    /* sub, gsub and gensub */
 +                      r = do_sub(pc->expr_count, pc->sub_flags);
 +                      PUSH(r);
 +                      break;
 +
 +              case Op_K_print:
 +                      do_print(pc->expr_count, pc->redir_type);
 +                      break;
 +
 +              case Op_K_printf:
 +                      do_printf(pc->expr_count, pc->redir_type);
 +                      break;
 +
 +              case Op_K_print_rec:
 +                      do_print_rec(pc->expr_count, pc->redir_type);
 +                      break;
 +
 +              case Op_push_re:
 +                      m = pc->memory;
 +                      if (m->type == Node_dynregex) {
 +                              r = POP_STRING();
 +                              unref(m->re_exp);
 +                              m->re_exp = r;
 +                      }
 +                      PUSH(m);
 +                      break;
 +                      
 +              case Op_match_rec:
 +                      m = pc->memory;
 +                      t1 = *get_field(0, (Func_ptr *) 0);
 +match_re:
 +                      rp = re_update(m);
 +                      /*
 +                       * Any place where research() is called with a last 
parameter of
 +                       * zero, we need to use the avoid_dfa test. This 
appears here and
 +                       * in the code for Op_K_case.
 +                       *
 +                       * A new or improved dfa that distinguishes 
beginning/end of
 +                       * string from beginning/end of line will allow us to 
get rid of
 +                       * this hack.
 +                       *
 +                       * The avoid_dfa() function is in re.c; it is not very 
smart.
 +                       */
 +
 +                      di = research(rp, t1->stptr, 0, t1->stlen,
 +                                                              avoid_dfa(m, 
t1->stptr, t1->stlen));
 +                      di = (di == -1) ^ (pc->opcode != Op_nomatch);
 +                      if(pc->opcode != Op_match_rec) {
 +                              decr_sp();
 +                              DEREF(t1);
 +                      }
 +                      r = node_Boolean[di];
 +                      UPREF(r);
 +                      PUSH(r);
 +                      break;
 +
 +              case Op_nomatch:
 +                      /* fall through */
 +              case Op_match:
 +                      m = pc->memory;
 +                      t1 = TOP_STRING();
 +                      if (m->type == Node_dynregex) {
 +                              unref(m->re_exp);
 +                              m->re_exp = t1;
 +                              decr_sp();
 +                              t1 = TOP_STRING();
 +                      }
 +                      goto match_re;
 +                      break;
 +
 +              case Op_indirect_func_call:
 +              {
 +                      int arg_count;
 +
 +                      f = NULL;
 +                      arg_count = (pc + 1)->expr_count;
 +                      t1 = PEEK(arg_count);   /* indirect var */
 +                      assert(t1->type == Node_val);   /* @a[1](p) not allowed 
in grammar */
 +                      t1 = force_string(t1);
 +                      if (t1->stlen > 0) {
 +                              /* retrieve function definition node */
 +                              f = pc->func_body;
-                               if (f != NULL && STREQ(f->vname, t1->stptr)) {
++                              if (f != NULL && strcmp(f->vname, t1->stptr) == 
0) {
 +                                      /* indirect var hasn't been reassigned 
*/
 +
 +                                      goto func_call;
 +                              }
 +                              f = lookup(t1->stptr);
 +                      }
 +
 +                      if (f == NULL || f->type != Node_func)
 +                              fatal(_("function called indirectly through 
`%s' does not exist"),
 +                                              pc->func_name); 
 +                      pc->func_body = f;     /* save for next call */
 +
 +                      goto func_call;
 +              }
 +
 +              case Op_func_call:
 +                      /* retrieve function definition node */
 +                      f = pc->func_body;
 +                      if (f == NULL) {
 +                              f = lookup(pc->func_name);
 +                              if (f == NULL || (f->type != Node_func && 
f->type != Node_ext_func))
 +                                      fatal(_("function `%s' not defined"), 
pc->func_name);
 +                              pc->func_body = f;     /* save for next call */
 +                      }
 +
 +                      if (f->type == Node_ext_func) {
 +                              INSTRUCTION *bc;
 +                              char *fname = pc->func_name;
 +                              int arg_count = (pc + 1)->expr_count;
 +
 +                              bc = f->code_ptr;
 +                              assert(bc->opcode == Op_symbol);
 +                              pc->opcode = Op_ext_builtin;    /* self 
modifying code */
 +                              pc->builtin = bc->builtin;
 +                              pc->expr_count = arg_count;             /* 
actual argument count */
 +                              (pc + 1)->func_name = fname;    /* name of the 
builtin */
 +                              (pc + 1)->expr_count = bc->expr_count;  /* 
defined max # of arguments */
 +                              ni = pc; 
 +                              JUMPTO(ni);
 +                      }
 +
 +func_call:
 +                      ni = setup_frame(pc);
 +                                              
 +                      /* run the function instructions */
 +                      JUMPTO(ni);             /* Op_func */
 +
 +              case Op_K_return:
 +                      m = POP_SCALAR();       /* return value */
 +
 +                      ni = pop_fcall();
 +      
 +                      /* put the return value back on stack */
 +                      PUSH(m);
 +
 +                      JUMPTO(ni);
 +
 +              case Op_K_getline_redir:
 +                      if ((currule == BEGINFILE || currule == ENDFILE)
 +                                      && pc->into_var == FALSE
 +                                      && pc->redir_type == redirect_input)
 +                              fatal(_("`getline' invalid inside `%s' rule"), 
ruletab[currule]);
 +                      r = do_getline_redir(pc->into_var, pc->redir_type);
 +                      PUSH(r);
 +                      break;
 +
 +              case Op_K_getline:      /* no redirection */
 +                      if (! currule || currule == BEGINFILE || currule == 
ENDFILE)
 +                              fatal(_("non-redirected `getline' invalid 
inside `%s' rule"),
 +                                              ruletab[currule]);
 +
 +                      do {
 +                              int ret;
 +                              ret = nextfile(& curfile, FALSE);
 +                              if (ret <= 0)
 +                                      r = do_getline(pc->into_var, curfile);
 +                              else {
 +
 +                                      /* Save execution state so that we can 
return to it
 +                                       * from Op_after_beginfile or 
Op_after_endfile.
 +                                       */ 
 +
 +                                      push_exec_state(pc, currule, source, 
stack_ptr);
 +
 +                                      if (curfile == NULL)
 +                                              JUMPTO((pc + 
1)->target_endfile);
 +                                      else
 +                                              JUMPTO((pc + 
1)->target_beginfile);
 +                              }
 +                      } while (r == NULL);    /* EOF */
 +
 +                      PUSH(r);
 +                      break;
 +
 +              case Op_after_endfile:
 +                      /* Find the execution state to return to */
 +                      ni = pop_exec_state(& currule, & source, NULL);
 +
 +                      assert(ni->opcode == Op_newfile || ni->opcode == 
Op_K_getline);
 +                      JUMPTO(ni);
 +
 +              case Op_after_beginfile:
 +                      after_beginfile(& curfile);
 +
 +                      /* Find the execution state to return to */
 +                      ni = pop_exec_state(& currule, & source, NULL);
 +
 +                      assert(ni->opcode == Op_newfile || ni->opcode == 
Op_K_getline);
 +                      if (ni->opcode == Op_K_getline
 +                                      || curfile == NULL      /* skipping 
directory argument */
 +                      )
 +                              JUMPTO(ni);
 +
 +                      break;  /* read a record, Op_get_record */
 +
 +              case Op_newfile:
 +              {
 +                      int ret;
 +
 +                      ret = nextfile(& curfile, FALSE);
 +
 +                      if (ret < 0)    /* end of input */
 +                              JUMPTO(pc->target_jmp); /* end block or 
Op_atexit */
 +
 +                      if (ret == 0) /* read a record */
 +                              JUMPTO((pc + 1)->target_get_record);
 +
 +                      /* ret > 0 */
 +                      /* Save execution state for use in Op_after_beginfile 
or Op_after_endfile. */
 +
 +                      push_exec_state(pc, currule, source, stack_ptr);
 +
 +                      if (curfile == NULL)    /* EOF */
 +                              JUMPTO(pc->target_endfile);
 +                      /* else
 +                              execute beginfile block */
 +              }
 +                      break;
 +                      
 +              case Op_get_record:             
 +              {
 +                      int errcode = 0;
 +
 +                      ni = pc->target_newfile;
 +                      if (curfile == NULL) {
 +                              /* from non-redirected getline, e.g.:
 +                               *  {
 +                               *              while (getline > 0) ;
 +                               *  }
 +                               */
 +
 +                              ni = ni->target_jmp;    /* end_block or 
Op_atexit */
 +                              JUMPTO(ni);
 +                      }
 +
 +                      if (inrec(curfile, & errcode) != 0) {
 +                              if (errcode > 0 && (do_traditional || ! 
pc->has_endfile))
 +                                      fatal(_("error reading input file `%s': 
%s"),
 +                                              curfile->name, 
strerror(errcode));
 +
 +                              JUMPTO(ni);
 +                      } /* else
 +                              prog (rule) block */
 +              }
 +                      break;
 +
 +              case Op_K_nextfile:
 +              {
 +                      int ret;
 +
 +                      if (currule != Rule && currule != BEGINFILE)
 +                              fatal(_("`nextfile' cannot be called from a 
`%s' rule"),
 +                                      ruletab[currule]);
 +
 +                      ret = nextfile(& curfile, TRUE);        /* skip current 
file */
 +
 +                      if (currule == BEGINFILE) {
 +                              long stack_size;
 +
 +                              ni = pop_exec_state(& currule, & source, & 
stack_size);
 +
 +                              assert(ni->opcode == Op_K_getline || ni->opcode 
== Op_newfile);
 +
 +                              /* pop stack returning to the state of 
Op_K_getline or Op_newfile. */
 +                              unwind_stack(stack_size);
 +
 +                              if (ret == 0) {
 +                                      /* There was an error opening the file;
 +                                       * don't run ENDFILE block(s).
 +                                       */
 +
 +                                      JUMPTO(ni);
 +                              } else {
 +                                      /* do run ENDFILE block(s) first. */
 +                                      
 +                                      /* Execution state to return to in 
Op_after_endfile. */
 +                                      push_exec_state(ni, currule, source, 
stack_ptr);
 +
 +                                      JUMPTO(pc->target_endfile);
 +                              }                               
 +                      } /* else 
 +                              Start over with the first rule. */
 +
 +                      /* empty the run-time stack to avoid memory leak */
 +                      pop_stack();
 +
 +                      /* Push an execution state for Op_after_endfile to 
return to */
 +                      push_exec_state(pc->target_newfile, currule, source, 
stack_ptr);
 +
 +                      JUMPTO(pc->target_endfile);
 +              }
 +                      break;
 +
 +              case Op_K_exit:
 +                      /* exit not allowed in user-defined comparison 
functions for "sorted_in";
 +                       * This is done so that END blocks aren't executed more 
than once.
 +                       */
 +                      if (! currule)
 +                              fatal(_("`exit' cannot be called in the current 
context"));
 +
 +                      exiting = TRUE;
 +                      POP_NUMBER(x1);
 +                      exit_val = (int) x1;
 +#ifdef VMS
 +                      if (exit_val == 0)
 +                              exit_val = EXIT_SUCCESS;
 +                      else if (exit_val == 1)
 +                              exit_val = EXIT_FAILURE;
 +                      /* else
 +                              just pass anything else on through */
 +#endif
 +
 +                      if (currule == BEGINFILE || currule == ENDFILE) {
 +
 +                              /* Find the rule of the saved execution state 
(Op_K_getline/Op_newfile).
 +                               * This is needed to prevent multiple execution 
of any END rules:
 +                               *      gawk 'BEGINFILE { exit(1) } \
 +                               *         END { while (getline > 0); }' in1 in2
 +                               */
 +
 +                              (void) pop_exec_state(& currule, & source, 
NULL);
 +                      }
 +
 +                      pop_stack();    /* empty stack, don't leak memory */
 +
 +                      /* Jump to either the first END block instruction
 +                       * or to Op_atexit.
 +                       */
 +
 +                      if (currule == END)
 +                              ni = pc->target_atexit;
 +                      else
 +                              ni = pc->target_end;
 +                      JUMPTO(ni);
 +
 +              case Op_K_next:
 +                      if (currule != Rule)
 +                              fatal(_("`next' cannot be called from a `%s' 
rule"), ruletab[currule]);
 +
 +                      pop_stack();
 +                      JUMPTO(pc->target_jmp); /* Op_get_record, read next 
record */
 +
 +              case Op_pop:
 +                      r = POP_SCALAR();
 +                      DEREF(r);
 +                      break;
 +
 +              case Op_line_range:
 +                      if (pc->triggered)              /* evaluate right 
expression */
 +                              JUMPTO(pc->target_jmp);
 +                      /* else
 +                              evaluate left expression */
 +                      break;
 +
 +              case Op_cond_pair:
 +              {
 +                      int result;
 +                      INSTRUCTION *ip;
 +
 +                      t1 = TOP_SCALAR();   /* from right hand side expression 
*/
 +                      di = (eval_condition(t1) != 0);
 +                      DEREF(t1);
 +
 +                      ip = pc->line_range;            /* Op_line_range */
 +
 +                      if (! ip->triggered && di) {
 +                              /* not already triggered and left expression is 
TRUE */
 +                              decr_sp();
 +                              ip->triggered = TRUE;
 +                              JUMPTO(ip->target_jmp); /* evaluate right 
expression */ 
 +                      }
 +
 +                      result = ip->triggered || di;
 +                      ip->triggered ^= di;          /* update triggered flag 
*/
 +                      r = node_Boolean[result];      /* final value of 
condition pair */
 +                      UPREF(r);
 +                      REPLACE(r);
 +                      JUMPTO(pc->target_jmp);
 +              }
 +
 +              case Op_exec_count:
 +                      if (do_profile)
 +                              pc->exec_count++;
 +                      break;
 +
 +              case Op_no_op:
 +              case Op_K_do:
 +              case Op_K_while:
 +              case Op_K_for:
 +              case Op_K_arrayfor:
 +              case Op_K_switch:
 +              case Op_K_default:
 +              case Op_K_if:
 +              case Op_K_else:
 +              case Op_cond_exp:
 +                      break;
 +
 +              default:
 +                      fatal(_("Sorry, don't know how to interpret `%s'"), 
opcode2str(pc->opcode));
 +              }
 +
 +              JUMPTO(pc->nexti);
 +
 +/*    } forever */
 +
 +      /* not reached */
 +      return 0;
 +
 +#undef mk_sub
 +#undef JUMPTO
 +}
 +
diff --cc io.c
index e8c6d87,c57aef2..b6076eb
--- a/io.c
+++ b/io.c
@@@ -2404,8 -2373,7 +2403,8 @@@ do_find_source(const char *src, struct 
  
        emalloc(path, char *, max_pathlen + strlen(src) + 1, "do_find_source"); 
        for (i = 0; awkpath[i] != NULL; i++) {
-               if (STREQ(awkpath[i], "./") || STREQ(awkpath[i], ".")) {
+               if (strcmp(awkpath[i], "./") == 0 || strcmp(awkpath[i], ".") == 
0) {
 +                      /* FIXME: already tried CWD above; Why do it again ? */
                        *path = '\0';
                } else
                        strcpy(path, awkpath[i]);
diff --cc msg.c
index 84a4e5c,3ed0233..10f3837
--- a/msg.c
+++ b/msg.c
@@@ -46,8 -46,8 +46,6 @@@ err(const char *s, const char *emsg, va
  
        (void) fflush(output_fp);
        me = myname;
-       if (STREQN(me, "dgawk", 5))
-               me = &myname[1];
 -      if (strncmp(me, "dgawk", 5) == 0)
 -              me = &myname[1];
        (void) fprintf(stderr, "%s: ", me);
  #ifdef GAWKDEBUG
        if (srcfile != NULL) {
diff --cc str_array.c
index 0b17796,0000000..7ce617e
mode 100644,000000..100644
--- a/str_array.c
+++ b/str_array.c
@@@ -1,762 -1,0 +1,762 @@@
 +/*
 + * str_array.c - routines for associative arrays of string indices.
 + */
 +
 +/* 
 + * Copyright (C) 1986, 1988, 1989, 1991-2011 the Free Software Foundation, 
Inc.
 + * 
 + * This file is part of GAWK, the GNU implementation of the
 + * AWK Programming Language.
 + * 
 + * GAWK is free software; you can redistribute it and/or modify
 + * it under the terms of the GNU General Public License as published by
 + * the Free Software Foundation; either version 3 of the License, or
 + * (at your option) any later version.
 + * 
 + * GAWK is distributed in the hope that it will be useful,
 + * but WITHOUT ANY WARRANTY; without even the implied warranty of
 + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 + * GNU General Public License for more details.
 + * 
 + * You should have received a copy of the GNU General Public License
 + * along with this program; if not, write to the Free Software
 + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, 
USA
 + */
 +
 +#include "awk.h"
 +
 +/*
 + * Tree walks (``for (iggy in foo)'') and array deletions use expensive
 + * linear searching.  So what we do is start out with small arrays and
 + * grow them as needed, so that our arrays are hopefully small enough,
 + * most of the time, that they're pretty full and we're not looking at
 + * wasted space.
 + *
 + * The decision is made to grow the array if the average chain length is
 + * ``too big''. This is defined as the total number of entries in the table
 + * divided by the size of the array being greater than some constant.
 + *
 + * 11/2002: We make the constant a variable, so that it can be tweaked
 + * via environment variable.
 + * 11/2002: Modern machines are bigger, cut this down from 10.
 + */
 +
 +static size_t STR_CHAIN_MAX = 2;
 +
 +extern FILE *output_fp;
 +extern void indent(int indent_level);
 +
 +static NODE **str_array_init(NODE *symbol, NODE *subs);
 +static NODE **str_lookup(NODE *symbol, NODE *subs);
 +static NODE **str_exists(NODE *symbol, NODE *subs);
 +static NODE **str_clear(NODE *symbol, NODE *subs);
 +static NODE **str_remove(NODE *symbol, NODE *subs);
 +static NODE **str_list(NODE *symbol, NODE *subs);
 +static NODE **str_copy(NODE *symbol, NODE *newsymb);
 +static NODE **str_dump(NODE *symbol, NODE *ndump);
 +
 +#ifdef ARRAYDEBUG
 +static NODE **str_option(NODE *opt, NODE *val);
 +#endif
 +
 +
 +array_ptr str_array_func[] = {
 +      str_array_init,
 +      (array_ptr) 0,
 +      str_lookup,
 +      str_exists,
 +      str_clear,
 +      str_remove,
 +      str_list,
 +      str_copy,
 +      str_dump,
 +#ifdef ARRAYDEBUG
 +      str_option
 +#endif
 +};
 +
 +static inline NODE **str_find(NODE *symbol, NODE *s1, size_t code1, unsigned 
long hash1);
 +static void grow_table(NODE *symbol);
 +
 +static unsigned long gst_hash_string(const char *str, size_t len, unsigned 
long hsize, size_t *code);
 +static unsigned long scramble(unsigned long x);
 +static unsigned long awk_hash(const char *s, size_t len, unsigned long hsize, 
size_t *code);
 +
 +unsigned long (*hash)(const char *s, size_t len, unsigned long hsize, size_t 
*code) = awk_hash;
 +
 +
 +/* str_array_init --- check relevant environment variables */
 +
 +static NODE **
 +str_array_init(NODE *symbol ATTRIBUTE_UNUSED, NODE *subs ATTRIBUTE_UNUSED)
 +{
 +      long newval;
 +      const char *val;
 +
 +      if ((newval = getenv_long("STR_CHAIN_MAX")) > 0)
 +              STR_CHAIN_MAX = newval;
 +      if ((val = getenv("AWK_HASH")) != NULL && strcmp(val, "gst") == 0)
 +              hash = gst_hash_string;
 +      return (NODE **) ! NULL;
 +}
 +
 +
 +/*
 + * assoc_lookup:
 + * Find SYMBOL[SUBS] in the assoc array.  Install it with value "" if it
 + * isn't there. Returns a pointer ala get_lhs to where its value is stored.
 + *
 + * SYMBOL is the address of the node (or other pointer) being dereferenced.
 + * SUBS is a number or string used as the subscript. 
 + */
 +
 +static NODE **
 +str_lookup(NODE *symbol, NODE *subs)
 +{
 +      unsigned long hash1;
 +      NODE **lhs;
 +      BUCKET *b;
 +      size_t code1;
 +
 +      subs = force_string(subs);
 +
 +      if (symbol->buckets == NULL)
 +              grow_table(symbol);
 +      hash1 = hash(subs->stptr, subs->stlen,
 +                      (unsigned long) symbol->array_size, & code1);
 +      if ((lhs = str_find(symbol, subs, code1, hash1)) != NULL)
 +              return lhs;
 +
 +      /* It's not there, install it. */
 +      /* first see if we would need to grow the array, before installing */
 +
 +      symbol->table_size++;
 +      if ((symbol->flags & ARRAYMAXED) == 0
 +                      && (symbol->table_size / symbol->array_size) > 
STR_CHAIN_MAX) {
 +              grow_table(symbol);
 +              /* have to recompute hash value for new size */
 +              hash1 = code1 % (unsigned long) symbol->array_size;
 +      }
 +
 +      if (subs->stfmt != -1) {
 +              NODE *tmp;
 +
 +              /*
 +               * Need to freeze this string value --- it must never
 +               * change, no matter what happens to the value
 +               * that created it or to CONVFMT, etc.; So, get
 +               * a private copy.
 +               */
 +
 +              tmp = make_string(subs->stptr, subs->stlen);
 +
 +              /*
 +              * Set the numeric value for the index if it's  available. Useful
 +              * for numeric sorting by index.  Do this only if the numeric
 +              * value is available, instead of all the time, since doing it
 +              * all the time is a big performance hit for something that may
 +              * never be used.
 +              */
 +
 +              if (subs->flags & NUMCUR) {
 +                      tmp->numbr = subs->numbr;
 +                      tmp->flags |= NUMCUR;
 +              }
 +              subs = tmp;
 +      } else {
 +              /* string value already "frozen" */     
 +
 +              subs = dupnode(subs);
 +      }
 +
 +      getbucket(b);
 +      b->ahnext = symbol->buckets[hash1];
 +      symbol->buckets[hash1] = b;
 +      b->ahname = subs;
 +      b->ahname_str = subs->stptr;
 +      b->ahname_len = subs->stlen;
 +      b->ahvalue = dupnode(Nnull_string);
 +      b->ahcode = code1;
 +      return & (b->ahvalue);
 +}
 +
 +/* str_exists --- test whether the array element symbol[subs] exists or not,
 + *            return pointer to value if it does.
 + */
 +
 +static NODE **
 +str_exists(NODE *symbol, NODE *subs)
 +{
 +      NODE **lhs;
 +      unsigned long hash1;
 +      size_t code1;
 +
 +      if (symbol->table_size == 0)
 +              return NULL;
 +
 +      subs = force_string(subs);
 +      hash1 = hash(subs->stptr, subs->stlen, (unsigned long) 
symbol->array_size, & code1);
 +      lhs = str_find(symbol, subs, code1, hash1);
 +      return lhs;
 +}
 +
 +/* str_clear --- flush all the values in symbol[] */
 +
 +static NODE **
 +str_clear(NODE *symbol, NODE *subs ATTRIBUTE_UNUSED)
 +{
 +      unsigned long i;
 +      BUCKET *b, *next;
 +      NODE *r;
 +
 +      for (i = 0; i < symbol->array_size; i++) {
 +              for (b = symbol->buckets[i]; b != NULL; b = next) {
 +                      next = b->ahnext;
 +                      r = b->ahvalue;
 +                      if (r->type == Node_var_array) {
 +                              assoc_clear(r); /* recursively clear all 
sub-arrays */
 +                              efree(r->vname);                        
 +                              freenode(r);
 +                      } else
 +                              unref(r);
 +                      unref(b->ahname);
 +                      freebucket(b);
 +              }
 +              symbol->buckets[i] = NULL;
 +      }
 +
 +      if (symbol->buckets != NULL)
 +              efree(symbol->buckets);
 +      init_array(symbol);     /* re-initialize symbol */
 +      symbol->flags &= ~ARRAYMAXED;
 +      return NULL;
 +}
 +
 +
 +/* str_remove --- If SUBS is already in the table, remove it. */
 +
 +static NODE **
 +str_remove(NODE *symbol, NODE *subs)
 +{
 +      unsigned long hash1;
 +      BUCKET *b, *prev;
 +      NODE *s2;
 +      size_t s1_len;
 +
 +      if (symbol->table_size == 0)
 +              return NULL;
 +
 +      s2 = force_string(subs);
 +      hash1 = hash(s2->stptr, s2->stlen, (unsigned long) symbol->array_size, 
NULL);
 +
 +      for (b = symbol->buckets[hash1], prev = NULL; b != NULL;
 +                              prev = b, b = b->ahnext) {
 +
 +              /* Array indexes are strings; compare as such, always! */
 +              s1_len = b->ahname_len;
 +
 +              if (s1_len != s2->stlen)
 +                      continue;
 +              if (s1_len == 0         /* "" is a valid index */
 +                          || memcmp(b->ahname_str, s2->stptr, s1_len) == 0) {
 +                      /* item found */
 +
 +                      unref(b->ahname);
 +                      if (prev != NULL)
 +                              prev->ahnext = b->ahnext;
 +                      else
 +                              symbol->buckets[hash1] = b->ahnext;
 +
 +                      /* delete bucket */
 +                      freebucket(b);
 +
 +                      /* one less element in array */
 +                      if (--symbol->table_size == 0) {
 +                              if (symbol->buckets != NULL)
 +                                      efree(symbol->buckets);
 +                              init_array(symbol);     /* re-initialize symbol 
*/
 +                              symbol->flags &= ~ARRAYMAXED;
 +                      }
 +
 +                      return (NODE **) ! NULL;        /* return success */
 +              }
 +      }
 +
 +      return NULL;
 +}
 +
 +
 +/* str_copy --- duplicate input array "symbol" */
 +
 +static NODE **
 +str_copy(NODE *symbol, NODE *newsymb)
 +{
 +      BUCKET **old, **new, **pnew;
 +      BUCKET *chain, *newchain;
 +      unsigned long cursize, i;
 +      
 +      assert(symbol->table_size > 0);
 +
 +      /* find the current hash size */
 +      cursize = symbol->array_size;
 +
 +      /* allocate new table */
 +      emalloc(new, BUCKET **, cursize * sizeof(BUCKET *), "str_copy");
 +      memset(new, '\0', cursize * sizeof(BUCKET *));
 +
 +      old = symbol->buckets;
 +
 +      for (i = 0; i < cursize; i++) {
 +              for (chain = old[i], pnew = & new[i]; chain != NULL;
 +                              chain = chain->ahnext
 +              ) {
 +                      NODE *oldval, *newsubs;
 +
 +                      getbucket(newchain);
 +
 +                      /*
 +                       * copy the corresponding name and
 +                       * value from the original input list
 +                       */
 +
 +                      newsubs = newchain->ahname = dupnode(chain->ahname);
 +                      newchain->ahname_str = newsubs->stptr;
 +                      newchain->ahname_len = newsubs->stlen;
 +
 +                      oldval = chain->ahvalue;
 +                      if (oldval->type == Node_val)
 +                              newchain->ahvalue = dupnode(oldval);
 +                      else {
 +                              NODE *r;
 +
 +                              r = make_array();
 +                              r->vname = estrdup(oldval->vname, 
strlen(oldval->vname));
 +                              r->parent_array = newsymb;
 +                              newchain->ahvalue = assoc_copy(oldval, r);
 +                      }
 +                      newchain->ahcode = chain->ahcode;
 +
 +                      *pnew = newchain;
 +                      pnew = & newchain->ahnext;
 +              }
 +      }       
 +
 +      newsymb->table_size = symbol->table_size;
 +      newsymb->buckets = new;
 +      newsymb->array_size = cursize;
 +      newsymb->flags = symbol->flags;
 +      return NULL;
 +}
 +
 +
 +/* str_list --- return a list of array items */
 +
 +static NODE**
 +str_list(NODE *symbol, NODE *t)
 +{
 +      NODE **list;
 +      NODE *subs, *val;
 +      BUCKET *b;
 +      unsigned long num_elems, list_size, i, k = 0;
 +      int elem_size = 1;
 +
 +      if (symbol->table_size == 0)
 +              return NULL;
 +
 +      if ((t->flags & (AINDEX|AVALUE)) == (AINDEX|AVALUE))
 +              elem_size = 2;
 +
 +      /* allocate space for array */
 +      num_elems = symbol->table_size;
 +      if ((t->flags & (AINDEX|AVALUE|ADELETE)) == (AINDEX|ADELETE))
 +              num_elems = 1;
 +      list_size =  elem_size * num_elems;
 +      
 +      emalloc(list, NODE **, list_size * sizeof(NODE *), "str_list");
 + 
 +      /* populate it */
 +
 +      for (i = 0; i < symbol->array_size; i++) {
 +              for (b = symbol->buckets[i]; b != NULL; b = b->ahnext) {
 +                      /* index */
 +                      subs = b->ahname;
 +                      if (t->flags & AINUM)
 +                              (void) force_number(subs);
 +                      list[k++] = dupnode(subs);
 +
 +                      /* value */
 +                      if (t->flags & AVALUE) {
 +                              val = b->ahvalue;
 +                              if (val->type == Node_val) {
 +                                      if ((t->flags & AVNUM) != 0)
 +                                              (void) force_number(val);
 +                                      else if ((t->flags & AVSTR) != 0)
 +                                              val = force_string(val);
 +                              }
 +                              list[k++] = val;
 +                      }
 +                      if (k >= list_size)
 +                              return list;
 +              }                       
 +      }
 +      return list;
 +}
 +
 +
 +/* str_kilobytes --- calculate memory consumption of the assoc array */
 +
 +AWKNUM
 +str_kilobytes(NODE *symbol)
 +{
 +      unsigned long bucket_cnt;
 +      AWKNUM kb;
 +
 +      bucket_cnt = symbol->table_size;
 +
 +      /* This does not include extra memory for indices with stfmt != -1 */
 +      kb = (((AWKNUM) bucket_cnt) * sizeof (BUCKET) + 
 +              ((AWKNUM) symbol->array_size) * sizeof (BUCKET *)) / 1024.0;
 +      return kb;
 +}
 +
 +
 +/* str_dump --- dump array info */
 +
 +static NODE **
 +str_dump(NODE *symbol, NODE *ndump)
 +{
 +#define HCNT  31
 +
 +      int indent_level;
 +      unsigned long i, bucket_cnt;
 +      BUCKET *b;
 +      static size_t hash_dist[HCNT + 1];
 +
 +      indent_level = ndump->alevel;
 +
 +      if ((symbol->flags & XARRAY) == 0)
 +              fprintf(output_fp, "%s `%s'\n",
 +                              (symbol->parent_array == NULL) ? "array" : 
"sub-array",
 +                              array_vname(symbol));
 +      indent_level++;
 +      indent(indent_level);
 +      fprintf(output_fp, "array_func: str_array_func\n");
 +      if (symbol->flags != 0) {
 +              indent(indent_level);
 +              fprintf(output_fp, "flags: %s\n", flags2str(symbol->flags));
 +      }
 +      indent(indent_level);
 +      fprintf(output_fp, "STR_CHAIN_MAX: %lu\n", (unsigned long) 
STR_CHAIN_MAX);
 +      indent(indent_level);
 +      fprintf(output_fp, "array_size: %lu\n", (unsigned long) 
symbol->array_size);
 +      indent(indent_level);
 +      fprintf(output_fp, "table_size: %lu\n", (unsigned long) 
symbol->table_size);
 +      indent(indent_level);
 +      fprintf(output_fp, "Avg # of items per chain: %.2g\n",
 +                              ((AWKNUM) symbol->table_size) / 
symbol->array_size);
 +
 +      indent(indent_level);
 +      fprintf(output_fp, "memory: %.2g kB\n", str_kilobytes(symbol));
 +
 +      /* hash value distribution */
 +
 +      memset(hash_dist, '\0', (HCNT + 1) * sizeof(size_t));
 +      for (i = 0; i < symbol->array_size; i++) {
 +              bucket_cnt = 0;
 +              for (b = symbol->buckets[i]; b != NULL; b = b->ahnext)
 +                      bucket_cnt++;
 +              if (bucket_cnt >= HCNT)
 +                      bucket_cnt = HCNT;
 +              hash_dist[bucket_cnt]++;
 +      }
 +
 +      indent(indent_level);
 +      fprintf(output_fp, "Hash distribution:\n");
 +      indent_level++;
 +      for (i = 0; i <= HCNT; i++) {
 +              if (hash_dist[i] > 0) {
 +                      indent(indent_level);
 +                      if (i == HCNT)
 +                              fprintf(output_fp, "[>=%lu]:%lu\n",
 +                                      (unsigned long) HCNT, (unsigned long) 
hash_dist[i]);
 +                      else
 +                              fprintf(output_fp, "[%lu]:%lu\n",
 +                                      (unsigned long) i, (unsigned long) 
hash_dist[i]);
 +              }
 +      }
 +      indent_level--;
 +
 +      /* dump elements */
 +
 +      if (ndump->adepth >= 0) {
 +              const char *aname;
 +
 +              fprintf(output_fp, "\n");
 +              aname = make_aname(symbol);
 +              for (i = 0; i < symbol->array_size; i++) {
 +                      for (b = symbol->buckets[i]; b != NULL; b = b->ahnext)
 +                              assoc_info(b->ahname, b->ahvalue, ndump, aname);
 +              }
 +      }
 +
 +      return NULL;
 +
 +#undef HCNT
 +}     
 +
 +
 +/* awk_hash --- calculate the hash function of the string in subs */
 +
 +static unsigned long
 +awk_hash(const char *s, size_t len, unsigned long hsize, size_t *code)
 +{
 +      unsigned long h = 0;
 +      unsigned long htmp;
 +
 +      /*
 +       * Ozan Yigit's original sdbm hash, copied from Margo Seltzers
 +       * db package.
 +       *
 +       * This is INCREDIBLY ugly, but fast.  We break the string up into
 +       * 8 byte units.  On the first time through the loop we get the
 +       * "leftover bytes" (strlen % 8).  On every other iteration, we
 +       * perform 8 HASHC's so we handle all 8 bytes.  Essentially, this
 +       * saves us 7 cmp & branch instructions.  If this routine is
 +       * heavily used enough, it's worth the ugly coding.
 +       */
 +
 +      /*
 +       * Even more speed:
 +       * #define HASHC   h = *s++ + 65599 * h
 +       * Because 65599 = pow(2, 6) + pow(2, 16) - 1 we multiply by shifts
 +       *
 +       * 4/2011: Force the results to 32 bits, to get the same
 +       * result on both 32- and 64-bit systems. This may be a
 +       * bad idea.
 +       */
 +#define HASHC   htmp = (h << 6);  \
 +              h = *s++ + htmp + (htmp << 10) - h ; \
 +              htmp &= 0xFFFFFFFF; \
 +              h &= 0xFFFFFFFF
 +
 +      h = 0;
 +
 +      /* "Duff's Device" */
 +      if (len > 0) {
 +              size_t loop = (len + 8 - 1) >> 3;
 +
 +              switch (len & (8 - 1)) {
 +              case 0:
 +                      do {    /* All fall throughs */
 +                              HASHC;
 +              case 7:         HASHC;
 +              case 6:         HASHC;
 +              case 5:         HASHC;
 +              case 4:         HASHC;
 +              case 3:         HASHC;
 +              case 2:         HASHC;
 +              case 1:         HASHC;
 +                      } while (--loop);
 +              }
 +      }
 +
 +      if (code != NULL)
 +              *code = h;
 +
 +      if (h >= hsize)
 +              h %= hsize;
 +      return h;
 +}
 +
 +
 +/* str_find --- locate symbol[subs] */
 +
 +static inline NODE **
 +str_find(NODE *symbol, NODE *s1, size_t code1, unsigned long hash1)
 +{
 +      BUCKET *b;
 +      size_t s2_len;
 +
 +      for (b = symbol->buckets[hash1]; b != NULL; b = b->ahnext) {
 +              /*
 +               * This used to use cmp_nodes() here.  That's wrong.
 +               * Array indexes are strings; compare as such, always!
 +               */
 +              s2_len = b->ahname_len;
 +
 +              if (code1 == b->ahcode
 +                      && s1->stlen == s2_len
 +                      && (s2_len == 0         /* "" is a valid index */
 +                              || memcmp(s1->stptr, b->ahname_str, s2_len) == 
0)
 +              )
 +                      return & (b->ahvalue);
 +      }
 +      return NULL;
 +}
 +
 +
 +/* grow_table --- grow a hash table */
 +
 +static void
 +grow_table(NODE *symbol)
 +{
 +      BUCKET **old, **new;
 +      BUCKET *chain, *next;
 +      int i, j;
 +      unsigned long oldsize, newsize, k;
 +      unsigned long hash1;
 +
 +      /*
 +       * This is an array of primes. We grow the table by an order of
 +       * magnitude each time (not just doubling) so that growing is a
 +       * rare operation. We expect, on average, that it won't happen
 +       * more than twice.  The final size is also chosen to be small
 +       * enough so that MS-DOG mallocs can handle it. When things are
 +       * very large (> 8K), we just double more or less, instead of
 +       * just jumping from 8K to 64K.
 +       */
 + 
 +      static const unsigned long sizes[] = {
 +              13, 127, 1021, 8191, 16381, 32749, 65497,
 +              131101, 262147, 524309, 1048583, 2097169,
 +              4194319, 8388617, 16777259, 33554467, 
 +              67108879, 134217757, 268435459, 536870923,
 +              1073741827
 +      };
 +
 +      /* find next biggest hash size */
 +      newsize = oldsize = symbol->array_size;
 +
 +      for (i = 0, j = sizeof(sizes)/sizeof(sizes[0]); i < j; i++) {
 +              if (oldsize < sizes[i]) {
 +                      newsize = sizes[i];
 +                      break;
 +              }
 +      }
 +      if (newsize == oldsize) {       /* table already at max (!) */
 +              symbol->flags |= ARRAYMAXED;
 +              return;
 +      }
 +
 +      /* allocate new table */
 +      emalloc(new, BUCKET **, newsize * sizeof(BUCKET *), "grow_table");
 +      memset(new, '\0', newsize * sizeof(BUCKET *));
 +
 +      old = symbol->buckets;
 +      symbol->buckets = new;
 +      symbol->array_size = newsize;
 +
 +      /* brand new hash table, set things up and return */
 +      if (old == NULL) {
 +              symbol->table_size = 0;
 +              return;
 +      }
 +
 +      /* old hash table there, move stuff to new, free old */
 +
 +      /*
 +       * note that symbol->table_size does not change if an old array,
 +       * and is explicitly set to 0 if a new one.
 +       */
 +
 +      for (k = 0; k < oldsize; k++) {
 +              for (chain = old[k]; chain != NULL; chain = next) {
 +                      next = chain->ahnext;
 +                      hash1 = chain->ahcode % newsize;
 +
 +                      /* remove from old list, add to new */
 +                      chain->ahnext = new[hash1];
 +                      new[hash1] = chain;
 +              }
 +      }
 +      efree(old);
 +}
 +
 +
 +#ifdef ARRAYDEBUG
 +
 +static NODE **
 +str_option(NODE *opt, NODE *val)
 +{
 +      int newval;
 +      NODE *tmp;
 +      NODE **ret = (NODE **) ! NULL;
 +
 +      tmp = force_string(opt);
 +      (void) force_number(val);
-       if (STREQ(tmp->stptr, "STR_CHAIN_MAX")) {
++      if (strcmp(tmp->stptr, "STR_CHAIN_MAX") == 0) {
 +              newval = (int) val->numbr;
-               if (newval > 0)         
++              if (newval > 0)
 +                      STR_CHAIN_MAX = newval;
 +      } else
 +              ret = NULL;
 +      return ret;
 +}
 +#endif
 +
 +
 +/*
 +From address@hidden  Mon Oct 28 16:05:26 2002
 +Date: Mon, 28 Oct 2002 13:33:03 +0100
 +From: Paolo Bonzini <address@hidden>
 +To: address@hidden
 +Subject: Hash function
 +Message-ID: <address@hidden>
 +
 +Here is the hash function I'm using in GNU Smalltalk.  The scrambling is
 +needed if you use powers of two as the table sizes.  If you use primes it
 +is not needed.
 +
 +To use double-hashing with power-of-two size, you should use the
 +_gst_hash_string(str, len) as the primary hash and
 +scramble(_gst_hash_string (str, len)) | 1 as the secondary hash.
 +
 +Paolo
 +
 +*/
 +/*
 + * ADR: Slightly modified to work w/in the context of gawk.
 + */
 +
 +static unsigned long
 +gst_hash_string(const char *str, size_t len, unsigned long hsize, size_t 
*code)
 +{
 +      unsigned long hashVal = 1497032417;    /* arbitrary value */
 +      unsigned long ret;
 +
 +      while (len--) {
 +              hashVal += *str++;
 +              hashVal += (hashVal << 10);
 +              hashVal ^= (hashVal >> 6);
 +      }
 +
 +      ret = scramble(hashVal);
 +
 +      if (code != NULL)
 +              *code = ret;
 +
 +      if (ret >= hsize)
 +              ret %= hsize;
 +
 +      return ret;
 +}
 +
 +static unsigned long
 +scramble(unsigned long x)
 +{
 +      if (sizeof(long) == 4) {
 +              int y = ~x;
 +
 +              x += (y << 10) | (y >> 22);
 +              x += (x << 6)  | (x >> 26);
 +              x -= (x << 16) | (x >> 16);
 +      } else {
 +              x ^= (~x) >> 31;
 +              x += (x << 21) | (x >> 11);
 +              x += (x << 5) | (x >> 27);
 +              x += (x << 27) | (x >> 5);
 +              x += (x << 31);
 +      }
 +
 +      return x;
 +}

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

commit ccb220159bbbc45aac0572c7ca1d3f0f2247d1f5
Author: Arnold D. Robbins <address@hidden>
Date:   Sat Dec 31 20:53:17 2011 +0200

    Remove profile_p.c, no longer needed.

diff --git a/profile_p.c b/profile_p.c
deleted file mode 100644
index 97edd36..0000000
--- a/profile_p.c
+++ /dev/null
@@ -1,27 +0,0 @@
-/*
- * profile_p.c - compile profile.c with profiling turned on.
- */
-
-/* 
- * Copyright (C) 2001 the Free Software Foundation, Inc.
- * 
- * This file is part of GAWK, the GNU implementation of the
- * AWK Programming Language.
- * 
- * GAWK is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 3 of the License, or
- * (at your option) any later version.
- * 
- * GAWK is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- * GNU General Public License for more details.
- * 
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, 
USA
- */
-
-#define PROFILING 1
-#include "profile.c"

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

Summary of changes:
 ChangeLog      |   11 +
 awk.h          |    5 -
 awkgram.c      |    8 +-
 awkgram.y      |    8 +-
 builtin.c      |    4 +-
 cint_array.c   |    2 +-
 command.c      |  597 ++++++++++++++++++++++++++++++--------------------------
 command.y      |    2 +-
 debug.c        |   25 ++--
 int_array.c    |    2 +-
 interpret.h    |    2 +-
 io.c           |   31 ++--
 msg.c          |    2 -
 profile_p.c    |   27 ---
 str_array.c    |    4 +-
 vms/ChangeLog  |    5 +
 vms/vms_misc.c |    6 +-
 17 files changed, 384 insertions(+), 357 deletions(-)
 delete mode 100644 profile_p.c


hooks/post-receive
-- 
gawk



reply via email to

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