mailarchive of the ptxdist mailing list
 help / color / mirror / Atom feed
* [ptxdist] [PATCH v3] bash: Version bump. 4.3.30 (+patches-33) -> 5.1.8 (patches-12).
@ 2022-01-07  8:24 Christian Melki
  2022-01-21  7:19 ` [ptxdist] [APPLIED] " Michael Olbrich
  0 siblings, 1 reply; 2+ messages in thread
From: Christian Melki @ 2022-01-07  8:24 UTC (permalink / raw)
  To: ptxdist

Upgrade bash to 5.1.8 plus patches to patchlevel 12.
Roll up patches as in previous series.
Remove old patches.

Fixes CVE-2019-18276 and CVE-2019-9924 with a new baseline, without patches.
Fixes loads of bugs in bash.

Update licence file hash, general.c changed a copyright year.

Signed-off-by: Christian Melki <christian.melki@t2data.com>
---
 .../bash-4.3.30/0001-Bash-4.3-patch-31.patch  | 5467 -----------------
 .../bash-4.3.30/0002-Bash-4.3-patch-32.patch  | 5409 ----------------
 .../bash-4.3.30/0003-Bash-4.3-patch-33.patch  |  204 -
 patches/bash-4.3.30/series                    |    6 -
 .../bash-5.1.8/0001-Bash-5.1-patch-12.patch   |  262 +
 patches/bash-5.1.8/series                     |    1 +
 rules/bash.make                               |    6 +-
 7 files changed, 266 insertions(+), 11089 deletions(-)
 delete mode 100644 patches/bash-4.3.30/0001-Bash-4.3-patch-31.patch
 delete mode 100644 patches/bash-4.3.30/0002-Bash-4.3-patch-32.patch
 delete mode 100644 patches/bash-4.3.30/0003-Bash-4.3-patch-33.patch
 delete mode 100644 patches/bash-4.3.30/series
 create mode 100644 patches/bash-5.1.8/0001-Bash-5.1-patch-12.patch
 create mode 100644 patches/bash-5.1.8/series

diff --git a/patches/bash-4.3.30/0001-Bash-4.3-patch-31.patch b/patches/bash-4.3.30/0001-Bash-4.3-patch-31.patch
deleted file mode 100644
index d9a187dcb..000000000
--- a/patches/bash-4.3.30/0001-Bash-4.3-patch-31.patch
+++ /dev/null
@@ -1,5467 +0,0 @@
-From: Chet Ramey <chet.ramey@case.edu>
-Date: Thu, 15 Jan 2015 10:20:04 -0500
-Subject: [PATCH] Bash-4.3 patch 31
-
----
- patchlevel.h     |    2 +-
- subst.h          |    1 +
- variables.c      |   32 +-
- variables.c.orig | 5365 ++++++++++++++++++++++++++++++++++++++++++++++++++++++
- 4 files changed, 5397 insertions(+), 3 deletions(-)
- create mode 100644 variables.c.orig
-
-diff --git a/patchlevel.h b/patchlevel.h
-index e5dde5245275..0ad46aafbdd9 100644
---- a/patchlevel.h
-+++ b/patchlevel.h
-@@ -25,6 +25,6 @@
-    regexp `^#define[ 	]*PATCHLEVEL', since that's what support/mkversion.sh
-    looks for to find the patch level (for the sccs version string). */
- 
--#define PATCHLEVEL 30
-+#define PATCHLEVEL 31
- 
- #endif /* _PATCHLEVEL_H_ */
-diff --git a/subst.h b/subst.h
-index cedaf8b6b444..1c300ab96b04 100644
---- a/subst.h
-+++ b/subst.h
-@@ -47,6 +47,7 @@
- #define ASS_MKASSOC	0x0004
- #define ASS_MKGLOBAL	0x0008	/* force global assignment */
- #define ASS_NAMEREF	0x0010	/* assigning to nameref variable */
-+#define ASS_FROMREF	0x0020	/* assigning from value of nameref variable */
- 
- /* Flags for the string extraction functions. */
- #define SX_NOALLOC	0x0001	/* just skip; don't return substring */
-diff --git a/variables.c b/variables.c
-index 7c82710e0f0b..81b7877e32e8 100644
---- a/variables.c
-+++ b/variables.c
-@@ -2516,10 +2516,27 @@ bind_variable_internal (name, value, table, hflags, aflags)
-      HASH_TABLE *table;
-      int hflags, aflags;
- {
--  char *newval;
-+  char *newname, *newval;
-   SHELL_VAR *entry;
-+#if defined (ARRAY_VARS)
-+  arrayind_t ind;
-+  char *subp;
-+  int sublen;
-+#endif
- 
-+  newname = 0;
-+#if defined (ARRAY_VARS)
-+  if ((aflags & ASS_FROMREF) && (hflags & HASH_NOSRCH) == 0 && valid_array_reference (name))
-+    {
-+      newname = array_variable_name (name, &subp, &sublen);
-+      if (newname == 0)
-+	return (SHELL_VAR *)NULL;	/* XXX */
-+      entry = hash_lookup (newname, table);
-+    }
-+  else
-+#endif
-   entry = (hflags & HASH_NOSRCH) ? (SHELL_VAR *)NULL : hash_lookup (name, table);
-+
-   /* Follow the nameref chain here if this is the global variables table */
-   if (entry && nameref_p (entry) && (invisible_p (entry) == 0) && table == global_variables->table)
-     {
-@@ -2550,6 +2567,16 @@ bind_variable_internal (name, value, table, hflags, aflags)
-       var_setvalue (entry, make_variable_value (entry, value, 0));
-       }
-     }
-+#if defined (ARRAY_VARS)
-+  else if (entry == 0 && newname)
-+    {
-+      entry = make_new_array_variable (newname);	/* indexed array by default */
-+      if (entry == 0)
-+	return entry;
-+      ind = array_expand_index (name, subp, sublen);
-+      bind_array_element (entry, ind, value, aflags);
-+    }
-+#endif
-   else if (entry == 0)
-     {
-       entry = make_new_variable (name, table);
-@@ -2670,7 +2697,8 @@ bind_variable (name, value, flags)
- 			 normal. */
- 		      if (nameref_cell (nv) == 0)
- 			return (bind_variable_internal (nv->name, value, nvc->table, 0, flags));
--		      return (bind_variable_internal (nameref_cell (nv), value, nvc->table, 0, flags));
-+		      /* XXX - bug here with ref=array[index] */
-+		      return (bind_variable_internal (nameref_cell (nv), value, nvc->table, 0, flags|ASS_FROMREF));
- 		    }
- 		  else
- 		    v = nv;
-diff --git a/variables.c.orig b/variables.c.orig
-new file mode 100644
-index 000000000000..7c82710e0f0b
---- /dev/null
-+++ b/variables.c.orig
-@@ -0,0 +1,5365 @@
-+/* variables.c -- Functions for hacking shell variables. */
-+
-+/* Copyright (C) 1987-2013 Free Software Foundation, Inc.
-+
-+   This file is part of GNU Bash, the Bourne Again SHell.
-+
-+   Bash is free software: you can redistribute it and/or modify
-+   it under the terms of the GNU General Public License as published by
-+   the Free Software Foundation, either version 3 of the License, or
-+   (at your option) any later version.
-+
-+   Bash is distributed in the hope that it will be useful,
-+   but WITHOUT ANY WARRANTY; without even the implied warranty of
-+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
-+   GNU General Public License for more details.
-+
-+   You should have received a copy of the GNU General Public License
-+   along with Bash.  If not, see <http://www.gnu.org/licenses/>.
-+*/
-+
-+#include "config.h"
-+
-+#include "bashtypes.h"
-+#include "posixstat.h"
-+#include "posixtime.h"
-+
-+#if defined (__QNX__)
-+#  if defined (__QNXNTO__)
-+#    include <sys/netmgr.h>
-+#  else
-+#    include <sys/vc.h>
-+#  endif /* !__QNXNTO__ */
-+#endif /* __QNX__ */
-+
-+#if defined (HAVE_UNISTD_H)
-+#  include <unistd.h>
-+#endif
-+
-+#include <stdio.h>
-+#include "chartypes.h"
-+#if defined (HAVE_PWD_H)
-+#  include <pwd.h>
-+#endif
-+#include "bashansi.h"
-+#include "bashintl.h"
-+
-+#define NEED_XTRACE_SET_DECL
-+
-+#include "shell.h"
-+#include "flags.h"
-+#include "execute_cmd.h"
-+#include "findcmd.h"
-+#include "mailcheck.h"
-+#include "input.h"
-+#include "hashcmd.h"
-+#include "pathexp.h"
-+#include "alias.h"
-+#include "jobs.h"
-+
-+#include "version.h"
-+
-+#include "builtins/getopt.h"
-+#include "builtins/common.h"
-+#include "builtins/builtext.h"
-+
-+#if defined (READLINE)
-+#  include "bashline.h"
-+#  include <readline/readline.h>
-+#else
-+#  include <tilde/tilde.h>
-+#endif
-+
-+#if defined (HISTORY)
-+#  include "bashhist.h"
-+#  include <readline/history.h>
-+#endif /* HISTORY */
-+
-+#if defined (PROGRAMMABLE_COMPLETION)
-+#  include "pcomplete.h"
-+#endif
-+
-+#define TEMPENV_HASH_BUCKETS	4	/* must be power of two */
-+
-+#define ifsname(s)	((s)[0] == 'I' && (s)[1] == 'F' && (s)[2] == 'S' && (s)[3] == '\0')
-+
-+#define BASHFUNC_PREFIX		"BASH_FUNC_"
-+#define BASHFUNC_PREFLEN	10	/* == strlen(BASHFUNC_PREFIX */
-+#define BASHFUNC_SUFFIX		"%%"
-+#define BASHFUNC_SUFFLEN	2	/* == strlen(BASHFUNC_SUFFIX) */
-+
-+extern char **environ;
-+
-+/* Variables used here and defined in other files. */
-+extern int posixly_correct;
-+extern int line_number, line_number_base;
-+extern int subshell_environment, indirection_level, subshell_level;
-+extern int build_version, patch_level;
-+extern int expanding_redir;
-+extern int last_command_exit_value;
-+extern char *dist_version, *release_status;
-+extern char *shell_name;
-+extern char *primary_prompt, *secondary_prompt;
-+extern char *current_host_name;
-+extern sh_builtin_func_t *this_shell_builtin;
-+extern SHELL_VAR *this_shell_function;
-+extern char *the_printed_command_except_trap;
-+extern char *this_command_name;
-+extern char *command_execution_string;
-+extern time_t shell_start_time;
-+extern int assigning_in_environment;
-+extern int executing_builtin;
-+extern int funcnest_max;
-+
-+#if defined (READLINE)
-+extern int no_line_editing;
-+extern int perform_hostname_completion;
-+#endif
-+
-+/* The list of shell variables that the user has created at the global
-+   scope, or that came from the environment. */
-+VAR_CONTEXT *global_variables = (VAR_CONTEXT *)NULL;
-+
-+/* The current list of shell variables, including function scopes */
-+VAR_CONTEXT *shell_variables = (VAR_CONTEXT *)NULL;
-+
-+/* The list of shell functions that the user has created, or that came from
-+   the environment. */
-+HASH_TABLE *shell_functions = (HASH_TABLE *)NULL;
-+
-+#if defined (DEBUGGER)
-+/* The table of shell function definitions that the user defined or that
-+   came from the environment. */
-+HASH_TABLE *shell_function_defs = (HASH_TABLE *)NULL;
-+#endif
-+
-+/* The current variable context.  This is really a count of how deep into
-+   executing functions we are. */
-+int variable_context = 0;
-+
-+/* The set of shell assignments which are made only in the environment
-+   for a single command. */
-+HASH_TABLE *temporary_env = (HASH_TABLE *)NULL;
-+
-+/* Set to non-zero if an assignment error occurs while putting variables
-+   into the temporary environment. */
-+int tempenv_assign_error;
-+
-+/* Some funky variables which are known about specially.  Here is where
-+   "$*", "$1", and all the cruft is kept. */
-+char *dollar_vars[10];
-+WORD_LIST *rest_of_args = (WORD_LIST *)NULL;
-+
-+/* The value of $$. */
-+pid_t dollar_dollar_pid;
-+
-+/* Non-zero means that we have to remake EXPORT_ENV. */
-+int array_needs_making = 1;
-+
-+/* The number of times BASH has been executed.  This is set
-+   by initialize_variables (). */
-+int shell_level = 0;
-+
-+/* An array which is passed to commands as their environment.  It is
-+   manufactured from the union of the initial environment and the
-+   shell variables that are marked for export. */
-+char **export_env = (char **)NULL;
-+static int export_env_index;
-+static int export_env_size;
-+
-+#if defined (READLINE)
-+static int winsize_assignment;		/* currently assigning to LINES or COLUMNS */
-+#endif
-+
-+static HASH_TABLE *last_table_searched;	/* hash_lookup sets this */
-+
-+/* Some forward declarations. */
-+static void create_variable_tables __P((void));
-+
-+static void set_machine_vars __P((void));
-+static void set_home_var __P((void));
-+static void set_shell_var __P((void));
-+static char *get_bash_name __P((void));
-+static void initialize_shell_level __P((void));
-+static void uidset __P((void));
-+#if defined (ARRAY_VARS)
-+static void make_vers_array __P((void));
-+#endif
-+
-+static SHELL_VAR *null_assign __P((SHELL_VAR *, char *, arrayind_t, char *));
-+#if defined (ARRAY_VARS)
-+static SHELL_VAR *null_array_assign __P((SHELL_VAR *, char *, arrayind_t, char *));
-+#endif
-+static SHELL_VAR *get_self __P((SHELL_VAR *));
-+
-+#if defined (ARRAY_VARS)
-+static SHELL_VAR *init_dynamic_array_var __P((char *, sh_var_value_func_t *, sh_var_assign_func_t *, int));
-+static SHELL_VAR *init_dynamic_assoc_var __P((char *, sh_var_value_func_t *, sh_var_assign_func_t *, int));
-+#endif
-+
-+static SHELL_VAR *assign_seconds __P((SHELL_VAR *, char *, arrayind_t, char *));
-+static SHELL_VAR *get_seconds __P((SHELL_VAR *));
-+static SHELL_VAR *init_seconds_var __P((void));
-+
-+static int brand __P((void));
-+static void sbrand __P((unsigned long));		/* set bash random number generator. */
-+static void seedrand __P((void));			/* seed generator randomly */
-+static SHELL_VAR *assign_random __P((SHELL_VAR *, char *, arrayind_t, char *));
-+static SHELL_VAR *get_random __P((SHELL_VAR *));
-+
-+static SHELL_VAR *assign_lineno __P((SHELL_VAR *, char *, arrayind_t, char *));
-+static SHELL_VAR *get_lineno __P((SHELL_VAR *));
-+
-+static SHELL_VAR *assign_subshell __P((SHELL_VAR *, char *, arrayind_t, char *));
-+static SHELL_VAR *get_subshell __P((SHELL_VAR *));
-+
-+static SHELL_VAR *get_bashpid __P((SHELL_VAR *));
-+
-+#if defined (HISTORY)
-+static SHELL_VAR *get_histcmd __P((SHELL_VAR *));
-+#endif
-+
-+#if defined (READLINE)
-+static SHELL_VAR *get_comp_wordbreaks __P((SHELL_VAR *));
-+static SHELL_VAR *assign_comp_wordbreaks __P((SHELL_VAR *, char *, arrayind_t, char *));
-+#endif
-+
-+#if defined (PUSHD_AND_POPD) && defined (ARRAY_VARS)
-+static SHELL_VAR *assign_dirstack __P((SHELL_VAR *, char *, arrayind_t, char *));
-+static SHELL_VAR *get_dirstack __P((SHELL_VAR *));
-+#endif
-+
-+#if defined (ARRAY_VARS)
-+static SHELL_VAR *get_groupset __P((SHELL_VAR *));
-+
-+static SHELL_VAR *build_hashcmd __P((SHELL_VAR *));
-+static SHELL_VAR *get_hashcmd __P((SHELL_VAR *));
-+static SHELL_VAR *assign_hashcmd __P((SHELL_VAR *,  char *, arrayind_t, char *));
-+#  if defined (ALIAS)
-+static SHELL_VAR *build_aliasvar __P((SHELL_VAR *));
-+static SHELL_VAR *get_aliasvar __P((SHELL_VAR *));
-+static SHELL_VAR *assign_aliasvar __P((SHELL_VAR *,  char *, arrayind_t, char *));
-+#  endif
-+#endif
-+
-+static SHELL_VAR *get_funcname __P((SHELL_VAR *));
-+static SHELL_VAR *init_funcname_var __P((void));
-+
-+static void initialize_dynamic_variables __P((void));
-+
-+static SHELL_VAR *hash_lookup __P((const char *, HASH_TABLE *));
-+static SHELL_VAR *new_shell_variable __P((const char *));
-+static SHELL_VAR *make_new_variable __P((const char *, HASH_TABLE *));
-+static SHELL_VAR *bind_variable_internal __P((const char *, char *, HASH_TABLE *, int, int));
-+
-+static void dispose_variable_value __P((SHELL_VAR *));
-+static void free_variable_hash_data __P((PTR_T));
-+
-+static VARLIST *vlist_alloc __P((int));
-+static VARLIST *vlist_realloc __P((VARLIST *, int));
-+static void vlist_add __P((VARLIST *, SHELL_VAR *, int));
-+
-+static void flatten __P((HASH_TABLE *, sh_var_map_func_t *, VARLIST *, int));
-+
-+static int qsort_var_comp __P((SHELL_VAR **, SHELL_VAR **));
-+
-+static SHELL_VAR **vapply __P((sh_var_map_func_t *));
-+static SHELL_VAR **fapply __P((sh_var_map_func_t *));
-+
-+static int visible_var __P((SHELL_VAR *));
-+static int visible_and_exported __P((SHELL_VAR *));
-+static int export_environment_candidate __P((SHELL_VAR *));
-+static int local_and_exported __P((SHELL_VAR *));
-+static int variable_in_context __P((SHELL_VAR *));
-+#if defined (ARRAY_VARS)
-+static int visible_array_vars __P((SHELL_VAR *));
-+#endif
-+
-+static SHELL_VAR *find_nameref_at_context __P((SHELL_VAR *, VAR_CONTEXT *));
-+static SHELL_VAR *find_variable_nameref_context __P((SHELL_VAR *, VAR_CONTEXT *, VAR_CONTEXT **));
-+static SHELL_VAR *find_variable_last_nameref_context __P((SHELL_VAR *, VAR_CONTEXT *, VAR_CONTEXT **));
-+
-+static SHELL_VAR *bind_tempenv_variable __P((const char *, char *));
-+static void push_temp_var __P((PTR_T));
-+static void propagate_temp_var __P((PTR_T));
-+static void dispose_temporary_env __P((sh_free_func_t *));     
-+
-+static inline char *mk_env_string __P((const char *, const char *, int));
-+static char **make_env_array_from_var_list __P((SHELL_VAR **));
-+static char **make_var_export_array __P((VAR_CONTEXT *));
-+static char **make_func_export_array __P((void));
-+static void add_temp_array_to_env __P((char **, int, int));
-+
-+static int n_shell_variables __P((void));
-+static int set_context __P((SHELL_VAR *));
-+
-+static void push_func_var __P((PTR_T));
-+static void push_exported_var __P((PTR_T));
-+
-+static inline int find_special_var __P((const char *));
-+
-+static void
-+create_variable_tables ()
-+{
-+  if (shell_variables == 0)
-+    {
-+      shell_variables = global_variables = new_var_context ((char *)NULL, 0);
-+      shell_variables->scope = 0;
-+      shell_variables->table = hash_create (0);
-+    }
-+
-+  if (shell_functions == 0)
-+    shell_functions = hash_create (0);
-+
-+#if defined (DEBUGGER)
-+  if (shell_function_defs == 0)
-+    shell_function_defs = hash_create (0);
-+#endif
-+}
-+
-+/* Initialize the shell variables from the current environment.
-+   If PRIVMODE is nonzero, don't import functions from ENV or
-+   parse $SHELLOPTS. */
-+void
-+initialize_shell_variables (env, privmode)
-+     char **env;
-+     int privmode;
-+{
-+  char *name, *string, *temp_string;
-+  int c, char_index, string_index, string_length, ro;
-+  SHELL_VAR *temp_var;
-+
-+  create_variable_tables ();
-+
-+  for (string_index = 0; string = env[string_index++]; )
-+    {
-+      char_index = 0;
-+      name = string;
-+      while ((c = *string++) && c != '=')
-+	;
-+      if (string[-1] == '=')
-+	char_index = string - name - 1;
-+
-+      /* If there are weird things in the environment, like `=xxx' or a
-+	 string without an `=', just skip them. */
-+      if (char_index == 0)
-+	continue;
-+
-+      /* ASSERT(name[char_index] == '=') */
-+      name[char_index] = '\0';
-+      /* Now, name = env variable name, string = env variable value, and
-+	 char_index == strlen (name) */
-+
-+      temp_var = (SHELL_VAR *)NULL;
-+
-+      /* If exported function, define it now.  Don't import functions from
-+	 the environment in privileged mode. */
-+      if (privmode == 0 && read_but_dont_execute == 0 && 
-+          STREQN (BASHFUNC_PREFIX, name, BASHFUNC_PREFLEN) &&
-+          STREQ (BASHFUNC_SUFFIX, name + char_index - BASHFUNC_SUFFLEN) &&
-+	  STREQN ("() {", string, 4))
-+	{
-+	  size_t namelen;
-+	  char *tname;		/* desired imported function name */
-+
-+	  namelen = char_index - BASHFUNC_PREFLEN - BASHFUNC_SUFFLEN;
-+
-+	  tname = name + BASHFUNC_PREFLEN;	/* start of func name */
-+	  tname[namelen] = '\0';		/* now tname == func name */
-+
-+	  string_length = strlen (string);
-+	  temp_string = (char *)xmalloc (namelen + string_length + 2);
-+
-+	  memcpy (temp_string, tname, namelen);
-+	  temp_string[namelen] = ' ';
-+	  memcpy (temp_string + namelen + 1, string, string_length + 1);
-+
-+	  /* Don't import function names that are invalid identifiers from the
-+	     environment, though we still allow them to be defined as shell
-+	     variables. */
-+	  if (absolute_program (tname) == 0 && (posixly_correct == 0 || legal_identifier (tname)))
-+	    parse_and_execute (temp_string, tname, SEVAL_NONINT|SEVAL_NOHIST|SEVAL_FUNCDEF|SEVAL_ONECMD);
-+
-+	  if (temp_var = find_function (tname))
-+	    {
-+	      VSETATTR (temp_var, (att_exported|att_imported));
-+	      array_needs_making = 1;
-+	    }
-+	  else
-+	    {
-+	      if (temp_var = bind_variable (name, string, 0))
-+		{
-+		  VSETATTR (temp_var, (att_exported | att_imported | att_invisible));
-+		  array_needs_making = 1;
-+		}
-+	      last_command_exit_value = 1;
-+	      report_error (_("error importing function definition for `%s'"), tname);
-+	    }
-+
-+	  /* Restore original suffix */
-+	  tname[namelen] = BASHFUNC_SUFFIX[0];
-+	}
-+#if defined (ARRAY_VARS)
-+#  if ARRAY_EXPORT
-+      /* Array variables may not yet be exported. */
-+      else if (*string == '(' && string[1] == '[' && string[strlen (string) - 1] == ')')
-+	{
-+	  string_length = 1;
-+	  temp_string = extract_array_assignment_list (string, &string_length);
-+	  temp_var = assign_array_from_string (name, temp_string);
-+	  FREE (temp_string);
-+	  VSETATTR (temp_var, (att_exported | att_imported));
-+	  array_needs_making = 1;
-+	}
-+#  endif /* ARRAY_EXPORT */
-+#endif
-+#if 0
-+      else if (legal_identifier (name))
-+#else
-+      else
-+#endif
-+	{
-+	  ro = 0;
-+	  if (posixly_correct && STREQ (name, "SHELLOPTS"))
-+	    {
-+	      temp_var = find_variable ("SHELLOPTS");
-+	      ro = temp_var && readonly_p (temp_var);
-+	      if (temp_var)
-+		VUNSETATTR (temp_var, att_readonly);
-+	    }
-+	  temp_var = bind_variable (name, string, 0);
-+	  if (temp_var)
-+	    {
-+	      if (legal_identifier (name))
-+		VSETATTR (temp_var, (att_exported | att_imported));
-+	      else
-+		VSETATTR (temp_var, (att_exported | att_imported | att_invisible));
-+	      if (ro)
-+		VSETATTR (temp_var, att_readonly);
-+	      array_needs_making = 1;
-+	    }
-+	}
-+
-+      name[char_index] = '=';
-+      /* temp_var can be NULL if it was an exported function with a syntax
-+	 error (a different bug, but it still shouldn't dump core). */
-+      if (temp_var && function_p (temp_var) == 0)	/* XXX not yet */
-+	{
-+	  CACHE_IMPORTSTR (temp_var, name);
-+	}
-+    }
-+
-+  set_pwd ();
-+
-+  /* Set up initial value of $_ */
-+  temp_var = set_if_not ("_", dollar_vars[0]);
-+
-+  /* Remember this pid. */
-+  dollar_dollar_pid = getpid ();
-+
-+  /* Now make our own defaults in case the vars that we think are
-+     important are missing. */
-+  temp_var = set_if_not ("PATH", DEFAULT_PATH_VALUE);
-+#if 0
-+  set_auto_export (temp_var);	/* XXX */
-+#endif
-+
-+  temp_var = set_if_not ("TERM", "dumb");
-+#if 0
-+  set_auto_export (temp_var);	/* XXX */
-+#endif
-+
-+#if defined (__QNX__)
-+  /* set node id -- don't import it from the environment */
-+  {
-+    char node_name[22];
-+#  if defined (__QNXNTO__)
-+    netmgr_ndtostr(ND2S_LOCAL_STR, ND_LOCAL_NODE, node_name, sizeof(node_name));
-+#  else
-+    qnx_nidtostr (getnid (), node_name, sizeof (node_name));
-+#  endif
-+    temp_var = bind_variable ("NODE", node_name, 0);
-+    set_auto_export (temp_var);
-+  }
-+#endif
-+
-+  /* set up the prompts. */
-+  if (interactive_shell)
-+    {
-+#if defined (PROMPT_STRING_DECODE)
-+      set_if_not ("PS1", primary_prompt);
-+#else
-+      if (current_user.uid == -1)
-+	get_current_user_info ();
-+      set_if_not ("PS1", current_user.euid == 0 ? "# " : primary_prompt);
-+#endif
-+      set_if_not ("PS2", secondary_prompt);
-+    }
-+  set_if_not ("PS4", "+ ");
-+
-+  /* Don't allow IFS to be imported from the environment. */
-+  temp_var = bind_variable ("IFS", " \t\n", 0);
-+  setifs (temp_var);
-+
-+  /* Magic machine types.  Pretty convenient. */
-+  set_machine_vars ();
-+
-+  /* Default MAILCHECK for interactive shells.  Defer the creation of a
-+     default MAILPATH until the startup files are read, because MAIL
-+     names a mail file if MAILPATH is not set, and we should provide a
-+     default only if neither is set. */
-+  if (interactive_shell)
-+    {
-+      temp_var = set_if_not ("MAILCHECK", posixly_correct ? "600" : "60");
-+      VSETATTR (temp_var, att_integer);
-+    }
-+
-+  /* Do some things with shell level. */
-+  initialize_shell_level ();
-+
-+  set_ppid ();
-+
-+  /* Initialize the `getopts' stuff. */
-+  temp_var = bind_variable ("OPTIND", "1", 0);
-+  VSETATTR (temp_var, att_integer);
-+  getopts_reset (0);
-+  bind_variable ("OPTERR", "1", 0);
-+  sh_opterr = 1;
-+
-+  if (login_shell == 1 && posixly_correct == 0)
-+    set_home_var ();
-+
-+  /* Get the full pathname to THIS shell, and set the BASH variable
-+     to it. */
-+  name = get_bash_name ();
-+  temp_var = bind_variable ("BASH", name, 0);
-+  free (name);
-+
-+  /* Make the exported environment variable SHELL be the user's login
-+     shell.  Note that the `tset' command looks at this variable
-+     to determine what style of commands to output; if it ends in "csh",
-+     then C-shell commands are output, else Bourne shell commands. */
-+  set_shell_var ();
-+
-+  /* Make a variable called BASH_VERSION which contains the version info. */
-+  bind_variable ("BASH_VERSION", shell_version_string (), 0);
-+#if defined (ARRAY_VARS)
-+  make_vers_array ();
-+#endif
-+
-+  if (command_execution_string)
-+    bind_variable ("BASH_EXECUTION_STRING", command_execution_string, 0);
-+
-+  /* Find out if we're supposed to be in Posix.2 mode via an
-+     environment variable. */
-+  temp_var = find_variable ("POSIXLY_CORRECT");
-+  if (!temp_var)
-+    temp_var = find_variable ("POSIX_PEDANTIC");
-+  if (temp_var && imported_p (temp_var))
-+    sv_strict_posix (temp_var->name);
-+
-+#if defined (HISTORY)
-+  /* Set history variables to defaults, and then do whatever we would
-+     do if the variable had just been set.  Do this only in the case
-+     that we are remembering commands on the history list. */
-+  if (remember_on_history)
-+    {
-+      name = bash_tilde_expand (posixly_correct ? "~/.sh_history" : "~/.bash_history", 0);
-+
-+      set_if_not ("HISTFILE", name);
-+      free (name);
-+    }
-+#endif /* HISTORY */
-+
-+  /* Seed the random number generator. */
-+  seedrand ();
-+
-+  /* Handle some "special" variables that we may have inherited from a
-+     parent shell. */
-+  if (interactive_shell)
-+    {
-+      temp_var = find_variable ("IGNOREEOF");
-+      if (!temp_var)
-+	temp_var = find_variable ("ignoreeof");
-+      if (temp_var && imported_p (temp_var))
-+	sv_ignoreeof (temp_var->name);
-+    }
-+
-+#if defined (HISTORY)
-+  if (interactive_shell && remember_on_history)
-+    {
-+      sv_history_control ("HISTCONTROL");
-+      sv_histignore ("HISTIGNORE");
-+      sv_histtimefmt ("HISTTIMEFORMAT");
-+    }
-+#endif /* HISTORY */
-+
-+#if defined (READLINE) && defined (STRICT_POSIX)
-+  /* POSIXLY_CORRECT will only be 1 here if the shell was compiled
-+     -DSTRICT_POSIX */
-+  if (interactive_shell && posixly_correct && no_line_editing == 0)
-+    rl_prefer_env_winsize = 1;
-+#endif /* READLINE && STRICT_POSIX */
-+
-+     /*
-+      * 24 October 2001
-+      *
-+      * I'm tired of the arguing and bug reports.  Bash now leaves SSH_CLIENT
-+      * and SSH2_CLIENT alone.  I'm going to rely on the shell_level check in
-+      * isnetconn() to avoid running the startup files more often than wanted.
-+      * That will, of course, only work if the user's login shell is bash, so
-+      * I've made that behavior conditional on SSH_SOURCE_BASHRC being defined
-+      * in config-top.h.
-+      */
-+#if 0
-+  temp_var = find_variable ("SSH_CLIENT");
-+  if (temp_var && imported_p (temp_var))
-+    {
-+      VUNSETATTR (temp_var, att_exported);
-+      array_needs_making = 1;
-+    }
-+  temp_var = find_variable ("SSH2_CLIENT");
-+  if (temp_var && imported_p (temp_var))
-+    {
-+      VUNSETATTR (temp_var, att_exported);
-+      array_needs_making = 1;
-+    }
-+#endif
-+
-+  /* Get the user's real and effective user ids. */
-+  uidset ();
-+
-+  temp_var = find_variable ("BASH_XTRACEFD");
-+  if (temp_var && imported_p (temp_var))
-+    sv_xtracefd (temp_var->name);
-+
-+  /* Initialize the dynamic variables, and seed their values. */
-+  initialize_dynamic_variables ();
-+}
-+
-+/* **************************************************************** */
-+/*								    */
-+/*	     Setting values for special shell variables		    */
-+/*								    */
-+/* **************************************************************** */
-+
-+static void
-+set_machine_vars ()
-+{
-+  SHELL_VAR *temp_var;
-+
-+  temp_var = set_if_not ("HOSTTYPE", HOSTTYPE);
-+  temp_var = set_if_not ("OSTYPE", OSTYPE);
-+  temp_var = set_if_not ("MACHTYPE", MACHTYPE);
-+
-+  temp_var = set_if_not ("HOSTNAME", current_host_name);
-+}
-+
-+/* Set $HOME to the information in the password file if we didn't get
-+   it from the environment. */
-+
-+/* This function is not static so the tilde and readline libraries can
-+   use it. */
-+char *
-+sh_get_home_dir ()
-+{
-+  if (current_user.home_dir == 0)
-+    get_current_user_info ();
-+  return current_user.home_dir;
-+}
-+
-+static void
-+set_home_var ()
-+{
-+  SHELL_VAR *temp_var;
-+
-+  temp_var = find_variable ("HOME");
-+  if (temp_var == 0)
-+    temp_var = bind_variable ("HOME", sh_get_home_dir (), 0);
-+#if 0
-+  VSETATTR (temp_var, att_exported);
-+#endif
-+}
-+
-+/* Set $SHELL to the user's login shell if it is not already set.  Call
-+   get_current_user_info if we haven't already fetched the shell. */
-+static void
-+set_shell_var ()
-+{
-+  SHELL_VAR *temp_var;
-+
-+  temp_var = find_variable ("SHELL");
-+  if (temp_var == 0)
-+    {
-+      if (current_user.shell == 0)
-+	get_current_user_info ();
-+      temp_var = bind_variable ("SHELL", current_user.shell, 0);
-+    }
-+#if 0
-+  VSETATTR (temp_var, att_exported);
-+#endif
-+}
-+
-+static char *
-+get_bash_name ()
-+{
-+  char *name;
-+
-+  if ((login_shell == 1) && RELPATH(shell_name))
-+    {
-+      if (current_user.shell == 0)
-+	get_current_user_info ();
-+      name = savestring (current_user.shell);
-+    }
-+  else if (ABSPATH(shell_name))
-+    name = savestring (shell_name);
-+  else if (shell_name[0] == '.' && shell_name[1] == '/')
-+    {
-+      /* Fast path for common case. */
-+      char *cdir;
-+      int len;
-+
-+      cdir = get_string_value ("PWD");
-+      if (cdir)
-+	{
-+	  len = strlen (cdir);
-+	  name = (char *)xmalloc (len + strlen (shell_name) + 1);
-+	  strcpy (name, cdir);
-+	  strcpy (name + len, shell_name + 1);
-+	}
-+      else
-+	name = savestring (shell_name);
-+    }
-+  else
-+    {
-+      char *tname;
-+      int s;
-+
-+      tname = find_user_command (shell_name);
-+
-+      if (tname == 0)
-+	{
-+	  /* Try the current directory.  If there is not an executable
-+	     there, just punt and use the login shell. */
-+	  s = file_status (shell_name);
-+	  if (s & FS_EXECABLE)
-+	    {
-+	      tname = make_absolute (shell_name, get_string_value ("PWD"));
-+	      if (*shell_name == '.')
-+		{
-+		  name = sh_canonpath (tname, PATH_CHECKDOTDOT|PATH_CHECKEXISTS);
-+		  if (name == 0)
-+		    name = tname;
-+		  else
-+		    free (tname);
-+		}
-+	     else
-+		name = tname;
-+	    }
-+	  else
-+	    {
-+	      if (current_user.shell == 0)
-+		get_current_user_info ();
-+	      name = savestring (current_user.shell);
-+	    }
-+	}
-+      else
-+	{
-+	  name = full_pathname (tname);
-+	  free (tname);
-+	}
-+    }
-+
-+  return (name);
-+}
-+
-+void
-+adjust_shell_level (change)
-+     int change;
-+{
-+  char new_level[5], *old_SHLVL;
-+  intmax_t old_level;
-+  SHELL_VAR *temp_var;
-+
-+  old_SHLVL = get_string_value ("SHLVL");
-+  if (old_SHLVL == 0 || *old_SHLVL == '\0' || legal_number (old_SHLVL, &old_level) == 0)
-+    old_level = 0;
-+
-+  shell_level = old_level + change;
-+  if (shell_level < 0)
-+    shell_level = 0;
-+  else if (shell_level > 1000)
-+    {
-+      internal_warning (_("shell level (%d) too high, resetting to 1"), shell_level);
-+      shell_level = 1;
-+    }
-+
-+  /* We don't need the full generality of itos here. */
-+  if (shell_level < 10)
-+    {
-+      new_level[0] = shell_level + '0';
-+      new_level[1] = '\0';
-+    }
-+  else if (shell_level < 100)
-+    {
-+      new_level[0] = (shell_level / 10) + '0';
-+      new_level[1] = (shell_level % 10) + '0';
-+      new_level[2] = '\0';
-+    }
-+  else if (shell_level < 1000)
-+    {
-+      new_level[0] = (shell_level / 100) + '0';
-+      old_level = shell_level % 100;
-+      new_level[1] = (old_level / 10) + '0';
-+      new_level[2] = (old_level % 10) + '0';
-+      new_level[3] = '\0';
-+    }
-+
-+  temp_var = bind_variable ("SHLVL", new_level, 0);
-+  set_auto_export (temp_var);
-+}
-+
-+static void
-+initialize_shell_level ()
-+{
-+  adjust_shell_level (1);
-+}
-+
-+/* If we got PWD from the environment, update our idea of the current
-+   working directory.  In any case, make sure that PWD exists before
-+   checking it.  It is possible for getcwd () to fail on shell startup,
-+   and in that case, PWD would be undefined.  If this is an interactive
-+   login shell, see if $HOME is the current working directory, and if
-+   that's not the same string as $PWD, set PWD=$HOME. */
-+
-+void
-+set_pwd ()
-+{
-+  SHELL_VAR *temp_var, *home_var;
-+  char *temp_string, *home_string;
-+
-+  home_var = find_variable ("HOME");
-+  home_string = home_var ? value_cell (home_var) : (char *)NULL;
-+
-+  temp_var = find_variable ("PWD");
-+  if (temp_var && imported_p (temp_var) &&
-+      (temp_string = value_cell (temp_var)) &&
-+      same_file (temp_string, ".", (struct stat *)NULL, (struct stat *)NULL))
-+    set_working_directory (temp_string);
-+  else if (home_string && interactive_shell && login_shell &&
-+	   same_file (home_string, ".", (struct stat *)NULL, (struct stat *)NULL))
-+    {
-+      set_working_directory (home_string);
-+      temp_var = bind_variable ("PWD", home_string, 0);
-+      set_auto_export (temp_var);
-+    }
-+  else
-+    {
-+      temp_string = get_working_directory ("shell-init");
-+      if (temp_string)
-+	{
-+	  temp_var = bind_variable ("PWD", temp_string, 0);
-+	  set_auto_export (temp_var);
-+	  free (temp_string);
-+	}
-+    }
-+
-+  /* According to the Single Unix Specification, v2, $OLDPWD is an
-+     `environment variable' and therefore should be auto-exported.
-+     Make a dummy invisible variable for OLDPWD, and mark it as exported. */
-+  temp_var = bind_variable ("OLDPWD", (char *)NULL, 0);
-+  VSETATTR (temp_var, (att_exported | att_invisible));
-+}
-+
-+/* Make a variable $PPID, which holds the pid of the shell's parent.  */
-+void
-+set_ppid ()
-+{
-+  char namebuf[INT_STRLEN_BOUND(pid_t) + 1], *name;
-+  SHELL_VAR *temp_var;
-+
-+  name = inttostr (getppid (), namebuf, sizeof(namebuf));
-+  temp_var = find_variable ("PPID");
-+  if (temp_var)
-+    VUNSETATTR (temp_var, (att_readonly | att_exported));
-+  temp_var = bind_variable ("PPID", name, 0);
-+  VSETATTR (temp_var, (att_readonly | att_integer));
-+}
-+
-+static void
-+uidset ()
-+{
-+  char buff[INT_STRLEN_BOUND(uid_t) + 1], *b;
-+  register SHELL_VAR *v;
-+
-+  b = inttostr (current_user.uid, buff, sizeof (buff));
-+  v = find_variable ("UID");
-+  if (v == 0)
-+    {
-+      v = bind_variable ("UID", b, 0);
-+      VSETATTR (v, (att_readonly | att_integer));
-+    }
-+
-+  if (current_user.euid != current_user.uid)
-+    b = inttostr (current_user.euid, buff, sizeof (buff));
-+
-+  v = find_variable ("EUID");
-+  if (v == 0)
-+    {
-+      v = bind_variable ("EUID", b, 0);
-+      VSETATTR (v, (att_readonly | att_integer));
-+    }
-+}
-+
-+#if defined (ARRAY_VARS)
-+static void
-+make_vers_array ()
-+{
-+  SHELL_VAR *vv;
-+  ARRAY *av;
-+  char *s, d[32], b[INT_STRLEN_BOUND(int) + 1];
-+
-+  unbind_variable ("BASH_VERSINFO");
-+
-+  vv = make_new_array_variable ("BASH_VERSINFO");
-+  av = array_cell (vv);
-+  strcpy (d, dist_version);
-+  s = strchr (d, '.');
-+  if (s)
-+    *s++ = '\0';
-+  array_insert (av, 0, d);
-+  array_insert (av, 1, s);
-+  s = inttostr (patch_level, b, sizeof (b));
-+  array_insert (av, 2, s);
-+  s = inttostr (build_version, b, sizeof (b));
-+  array_insert (av, 3, s);
-+  array_insert (av, 4, release_status);
-+  array_insert (av, 5, MACHTYPE);
-+
-+  VSETATTR (vv, att_readonly);
-+}
-+#endif /* ARRAY_VARS */
-+
-+/* Set the environment variables $LINES and $COLUMNS in response to
-+   a window size change. */
-+void
-+sh_set_lines_and_columns (lines, cols)
-+     int lines, cols;
-+{
-+  char val[INT_STRLEN_BOUND(int) + 1], *v;
-+
-+#if defined (READLINE)
-+  /* If we are currently assigning to LINES or COLUMNS, don't do anything. */
-+  if (winsize_assignment)
-+    return;
-+#endif
-+
-+  v = inttostr (lines, val, sizeof (val));
-+  bind_variable ("LINES", v, 0);
-+
-+  v = inttostr (cols, val, sizeof (val));
-+  bind_variable ("COLUMNS", v, 0);
-+}
-+
-+/* **************************************************************** */
-+/*								    */
-+/*		   Printing variables and values		    */
-+/*								    */
-+/* **************************************************************** */
-+
-+/* Print LIST (a list of shell variables) to stdout in such a way that
-+   they can be read back in. */
-+void
-+print_var_list (list)
-+     register SHELL_VAR **list;
-+{
-+  register int i;
-+  register SHELL_VAR *var;
-+
-+  for (i = 0; list && (var = list[i]); i++)
-+    if (invisible_p (var) == 0)
-+      print_assignment (var);
-+}
-+
-+/* Print LIST (a list of shell functions) to stdout in such a way that
-+   they can be read back in. */
-+void
-+print_func_list (list)
-+     register SHELL_VAR **list;
-+{
-+  register int i;
-+  register SHELL_VAR *var;
-+
-+  for (i = 0; list && (var = list[i]); i++)
-+    {
-+      printf ("%s ", var->name);
-+      print_var_function (var);
-+      printf ("\n");
-+    }
-+}
-+      
-+/* Print the value of a single SHELL_VAR.  No newline is
-+   output, but the variable is printed in such a way that
-+   it can be read back in. */
-+void
-+print_assignment (var)
-+     SHELL_VAR *var;
-+{
-+  if (var_isset (var) == 0)
-+    return;
-+
-+  if (function_p (var))
-+    {
-+      printf ("%s", var->name);
-+      print_var_function (var);
-+      printf ("\n");
-+    }
-+#if defined (ARRAY_VARS)
-+  else if (array_p (var))
-+    print_array_assignment (var, 0);
-+  else if (assoc_p (var))
-+    print_assoc_assignment (var, 0);
-+#endif /* ARRAY_VARS */
-+  else
-+    {
-+      printf ("%s=", var->name);
-+      print_var_value (var, 1);
-+      printf ("\n");
-+    }
-+}
-+
-+/* Print the value cell of VAR, a shell variable.  Do not print
-+   the name, nor leading/trailing newline.  If QUOTE is non-zero,
-+   and the value contains shell metacharacters, quote the value
-+   in such a way that it can be read back in. */
-+void
-+print_var_value (var, quote)
-+     SHELL_VAR *var;
-+     int quote;
-+{
-+  char *t;
-+
-+  if (var_isset (var) == 0)
-+    return;
-+
-+  if (quote && posixly_correct == 0 && ansic_shouldquote (value_cell (var)))
-+    {
-+      t = ansic_quote (value_cell (var), 0, (int *)0);
-+      printf ("%s", t);
-+      free (t);
-+    }
-+  else if (quote && sh_contains_shell_metas (value_cell (var)))
-+    {
-+      t = sh_single_quote (value_cell (var));
-+      printf ("%s", t);
-+      free (t);
-+    }
-+  else
-+    printf ("%s", value_cell (var));
-+}
-+
-+/* Print the function cell of VAR, a shell variable.  Do not
-+   print the name, nor leading/trailing newline. */
-+void
-+print_var_function (var)
-+     SHELL_VAR *var;
-+{
-+  char *x;
-+
-+  if (function_p (var) && var_isset (var))
-+    {
-+      x = named_function_string ((char *)NULL, function_cell(var), FUNC_MULTILINE|FUNC_EXTERNAL);
-+      printf ("%s", x);
-+    }
-+}
-+
-+/* **************************************************************** */
-+/*								    */
-+/*		 	Dynamic Variables			    */
-+/*								    */
-+/* **************************************************************** */
-+
-+/* DYNAMIC VARIABLES
-+
-+   These are variables whose values are generated anew each time they are
-+   referenced.  These are implemented using a pair of function pointers
-+   in the struct variable: assign_func, which is called from bind_variable
-+   and, if arrays are compiled into the shell, some of the functions in
-+   arrayfunc.c, and dynamic_value, which is called from find_variable.
-+
-+   assign_func is called from bind_variable_internal, if
-+   bind_variable_internal discovers that the variable being assigned to
-+   has such a function.  The function is called as
-+	SHELL_VAR *temp = (*(entry->assign_func)) (entry, value, ind)
-+   and the (SHELL_VAR *)temp is returned as the value of bind_variable.  It
-+   is usually ENTRY (self).  IND is an index for an array variable, and
-+   unused otherwise.
-+
-+   dynamic_value is called from find_variable_internal to return a `new'
-+   value for the specified dynamic varible.  If this function is NULL,
-+   the variable is treated as a `normal' shell variable.  If it is not,
-+   however, then this function is called like this:
-+	tempvar = (*(var->dynamic_value)) (var);
-+
-+   Sometimes `tempvar' will replace the value of `var'.  Other times, the
-+   shell will simply use the string value.  Pretty object-oriented, huh?
-+
-+   Be warned, though: if you `unset' a special variable, it loses its
-+   special meaning, even if you subsequently set it.
-+
-+   The special assignment code would probably have been better put in
-+   subst.c: do_assignment_internal, in the same style as
-+   stupidly_hack_special_variables, but I wanted the changes as
-+   localized as possible.  */
-+
-+#define INIT_DYNAMIC_VAR(var, val, gfunc, afunc) \
-+  do \
-+    { \
-+      v = bind_variable (var, (val), 0); \
-+      v->dynamic_value = gfunc; \
-+      v->assign_func = afunc; \
-+    } \
-+  while (0)
-+
-+#define INIT_DYNAMIC_ARRAY_VAR(var, gfunc, afunc) \
-+  do \
-+    { \
-+      v = make_new_array_variable (var); \
-+      v->dynamic_value = gfunc; \
-+      v->assign_func = afunc; \
-+    } \
-+  while (0)
-+
-+#define INIT_DYNAMIC_ASSOC_VAR(var, gfunc, afunc) \
-+  do \
-+    { \
-+      v = make_new_assoc_variable (var); \
-+      v->dynamic_value = gfunc; \
-+      v->assign_func = afunc; \
-+    } \
-+  while (0)
-+
-+static SHELL_VAR *
-+null_assign (self, value, unused, key)
-+     SHELL_VAR *self;
-+     char *value;
-+     arrayind_t unused;
-+     char *key;
-+{
-+  return (self);
-+}
-+
-+#if defined (ARRAY_VARS)
-+static SHELL_VAR *
-+null_array_assign (self, value, ind, key)
-+     SHELL_VAR *self;
-+     char *value;
-+     arrayind_t ind;
-+     char *key;
-+{
-+  return (self);
-+}
-+#endif
-+
-+/* Degenerate `dynamic_value' function; just returns what's passed without
-+   manipulation. */
-+static SHELL_VAR *
-+get_self (self)
-+     SHELL_VAR *self;
-+{
-+  return (self);
-+}
-+
-+#if defined (ARRAY_VARS)
-+/* A generic dynamic array variable initializer.  Initialize array variable
-+   NAME with dynamic value function GETFUNC and assignment function SETFUNC. */
-+static SHELL_VAR *
-+init_dynamic_array_var (name, getfunc, setfunc, attrs)
-+     char *name;
-+     sh_var_value_func_t *getfunc;
-+     sh_var_assign_func_t *setfunc;
-+     int attrs;
-+{
-+  SHELL_VAR *v;
-+
-+  v = find_variable (name);
-+  if (v)
-+    return (v);
-+  INIT_DYNAMIC_ARRAY_VAR (name, getfunc, setfunc);
-+  if (attrs)
-+    VSETATTR (v, attrs);
-+  return v;
-+}
-+
-+static SHELL_VAR *
-+init_dynamic_assoc_var (name, getfunc, setfunc, attrs)
-+     char *name;
-+     sh_var_value_func_t *getfunc;
-+     sh_var_assign_func_t *setfunc;
-+     int attrs;
-+{
-+  SHELL_VAR *v;
-+
-+  v = find_variable (name);
-+  if (v)
-+    return (v);
-+  INIT_DYNAMIC_ASSOC_VAR (name, getfunc, setfunc);
-+  if (attrs)
-+    VSETATTR (v, attrs);
-+  return v;
-+}
-+#endif
-+
-+/* The value of $SECONDS.  This is the number of seconds since shell
-+   invocation, or, the number of seconds since the last assignment + the
-+   value of the last assignment. */
-+static intmax_t seconds_value_assigned;
-+
-+static SHELL_VAR *
-+assign_seconds (self, value, unused, key)
-+     SHELL_VAR *self;
-+     char *value;
-+     arrayind_t unused;
-+     char *key;
-+{
-+  if (legal_number (value, &seconds_value_assigned) == 0)
-+    seconds_value_assigned = 0;
-+  shell_start_time = NOW;
-+  return (self);
-+}
-+
-+static SHELL_VAR *
-+get_seconds (var)
-+     SHELL_VAR *var;
-+{
-+  time_t time_since_start;
-+  char *p;
-+
-+  time_since_start = NOW - shell_start_time;
-+  p = itos(seconds_value_assigned + time_since_start);
-+
-+  FREE (value_cell (var));
-+
-+  VSETATTR (var, att_integer);
-+  var_setvalue (var, p);
-+  return (var);
-+}
-+
-+static SHELL_VAR *
-+init_seconds_var ()
-+{
-+  SHELL_VAR *v;
-+
-+  v = find_variable ("SECONDS");
-+  if (v)
-+    {
-+      if (legal_number (value_cell(v), &seconds_value_assigned) == 0)
-+	seconds_value_assigned = 0;
-+    }
-+  INIT_DYNAMIC_VAR ("SECONDS", (v ? value_cell (v) : (char *)NULL), get_seconds, assign_seconds);
-+  return v;      
-+}
-+     
-+/* The random number seed.  You can change this by setting RANDOM. */
-+static unsigned long rseed = 1;
-+static int last_random_value;
-+static int seeded_subshell = 0;
-+
-+/* A linear congruential random number generator based on the example
-+   one in the ANSI C standard.  This one isn't very good, but a more
-+   complicated one is overkill. */
-+
-+/* Returns a pseudo-random number between 0 and 32767. */
-+static int
-+brand ()
-+{
-+  /* From "Random number generators: good ones are hard to find",
-+     Park and Miller, Communications of the ACM, vol. 31, no. 10,
-+     October 1988, p. 1195. filtered through FreeBSD */
-+  long h, l;
-+
-+  /* Can't seed with 0. */
-+  if (rseed == 0)
-+    rseed = 123459876;
-+  h = rseed / 127773;
-+  l = rseed % 127773;
-+  rseed = 16807 * l - 2836 * h;
-+#if 0
-+  if (rseed < 0)
-+    rseed += 0x7fffffff;
-+#endif
-+  return ((unsigned int)(rseed & 32767));	/* was % 32768 */
-+}
-+
-+/* Set the random number generator seed to SEED. */
-+static void
-+sbrand (seed)
-+     unsigned long seed;
-+{
-+  rseed = seed;
-+  last_random_value = 0;
-+}
-+
-+static void
-+seedrand ()
-+{
-+  struct timeval tv;
-+
-+  gettimeofday (&tv, NULL);
-+  sbrand (tv.tv_sec ^ tv.tv_usec ^ getpid ());
-+}
-+
-+static SHELL_VAR *
-+assign_random (self, value, unused, key)
-+     SHELL_VAR *self;
-+     char *value;
-+     arrayind_t unused;
-+     char *key;
-+{
-+  sbrand (strtoul (value, (char **)NULL, 10));
-+  if (subshell_environment)
-+    seeded_subshell = getpid ();
-+  return (self);
-+}
-+
-+int
-+get_random_number ()
-+{
-+  int rv, pid;
-+
-+  /* Reset for command and process substitution. */
-+  pid = getpid ();
-+  if (subshell_environment && seeded_subshell != pid)
-+    {
-+      seedrand ();
-+      seeded_subshell = pid;
-+    }
-+
-+  do
-+    rv = brand ();
-+  while (rv == last_random_value);
-+  return rv;
-+}
-+
-+static SHELL_VAR *
-+get_random (var)
-+     SHELL_VAR *var;
-+{
-+  int rv;
-+  char *p;
-+
-+  rv = get_random_number ();
-+  last_random_value = rv;
-+  p = itos (rv);
-+
-+  FREE (value_cell (var));
-+
-+  VSETATTR (var, att_integer);
-+  var_setvalue (var, p);
-+  return (var);
-+}
-+
-+static SHELL_VAR *
-+assign_lineno (var, value, unused, key)
-+     SHELL_VAR *var;
-+     char *value;
-+     arrayind_t unused;
-+     char *key;
-+{
-+  intmax_t new_value;
-+
-+  if (value == 0 || *value == '\0' || legal_number (value, &new_value) == 0)
-+    new_value = 0;
-+  line_number = line_number_base = new_value;
-+  return var;
-+}
-+
-+/* Function which returns the current line number. */
-+static SHELL_VAR *
-+get_lineno (var)
-+     SHELL_VAR *var;
-+{
-+  char *p;
-+  int ln;
-+
-+  ln = executing_line_number ();
-+  p = itos (ln);
-+  FREE (value_cell (var));
-+  var_setvalue (var, p);
-+  return (var);
-+}
-+
-+static SHELL_VAR *
-+assign_subshell (var, value, unused, key)
-+     SHELL_VAR *var;
-+     char *value;
-+     arrayind_t unused;
-+     char *key;
-+{
-+  intmax_t new_value;
-+
-+  if (value == 0 || *value == '\0' || legal_number (value, &new_value) == 0)
-+    new_value = 0;
-+  subshell_level = new_value;
-+  return var;
-+}
-+
-+static SHELL_VAR *
-+get_subshell (var)
-+     SHELL_VAR *var;
-+{
-+  char *p;
-+
-+  p = itos (subshell_level);
-+  FREE (value_cell (var));
-+  var_setvalue (var, p);
-+  return (var);
-+}
-+
-+static SHELL_VAR *
-+get_bashpid (var)
-+     SHELL_VAR *var;
-+{
-+  int pid;
-+  char *p;
-+
-+  pid = getpid ();
-+  p = itos (pid);
-+
-+  FREE (value_cell (var));
-+  VSETATTR (var, att_integer|att_readonly);
-+  var_setvalue (var, p);
-+  return (var);
-+}
-+
-+static SHELL_VAR *
-+get_bash_command (var)
-+     SHELL_VAR *var;
-+{
-+  char *p;
-+
-+  if (the_printed_command_except_trap)
-+    p = savestring (the_printed_command_except_trap);
-+  else
-+    {
-+      p = (char *)xmalloc (1);
-+      p[0] = '\0';
-+    }
-+  FREE (value_cell (var));
-+  var_setvalue (var, p);
-+  return (var);
-+}
-+
-+#if defined (HISTORY)
-+static SHELL_VAR *
-+get_histcmd (var)
-+     SHELL_VAR *var;
-+{
-+  char *p;
-+
-+  p = itos (history_number ());
-+  FREE (value_cell (var));
-+  var_setvalue (var, p);
-+  return (var);
-+}
-+#endif
-+
-+#if defined (READLINE)
-+/* When this function returns, VAR->value points to malloced memory. */
-+static SHELL_VAR *
-+get_comp_wordbreaks (var)
-+     SHELL_VAR *var;
-+{
-+  /* If we don't have anything yet, assign a default value. */
-+  if (rl_completer_word_break_characters == 0 && bash_readline_initialized == 0)
-+    enable_hostname_completion (perform_hostname_completion);
-+
-+  FREE (value_cell (var));
-+  var_setvalue (var, savestring (rl_completer_word_break_characters));
-+
-+  return (var);
-+}
-+
-+/* When this function returns, rl_completer_word_break_characters points to
-+   malloced memory. */
-+static SHELL_VAR *
-+assign_comp_wordbreaks (self, value, unused, key)
-+     SHELL_VAR *self;
-+     char *value;
-+     arrayind_t unused;
-+     char *key;
-+{
-+  if (rl_completer_word_break_characters &&
-+      rl_completer_word_break_characters != rl_basic_word_break_characters)
-+    free (rl_completer_word_break_characters);
-+
-+  rl_completer_word_break_characters = savestring (value);
-+  return self;
-+}
-+#endif /* READLINE */
-+
-+#if defined (PUSHD_AND_POPD) && defined (ARRAY_VARS)
-+static SHELL_VAR *
-+assign_dirstack (self, value, ind, key)
-+     SHELL_VAR *self;
-+     char *value;
-+     arrayind_t ind;
-+     char *key;
-+{
-+  set_dirstack_element (ind, 1, value);
-+  return self;
-+}
-+
-+static SHELL_VAR *
-+get_dirstack (self)
-+     SHELL_VAR *self;
-+{
-+  ARRAY *a;
-+  WORD_LIST *l;
-+
-+  l = get_directory_stack (0);
-+  a = array_from_word_list (l);
-+  array_dispose (array_cell (self));
-+  dispose_words (l);
-+  var_setarray (self, a);
-+  return self;
-+}
-+#endif /* PUSHD AND POPD && ARRAY_VARS */
-+
-+#if defined (ARRAY_VARS)
-+/* We don't want to initialize the group set with a call to getgroups()
-+   unless we're asked to, but we only want to do it once. */
-+static SHELL_VAR *
-+get_groupset (self)
-+     SHELL_VAR *self;
-+{
-+  register int i;
-+  int ng;
-+  ARRAY *a;
-+  static char **group_set = (char **)NULL;
-+
-+  if (group_set == 0)
-+    {
-+      group_set = get_group_list (&ng);
-+      a = array_cell (self);
-+      for (i = 0; i < ng; i++)
-+	array_insert (a, i, group_set[i]);
-+    }
-+  return (self);
-+}
-+
-+static SHELL_VAR *
-+build_hashcmd (self)
-+     SHELL_VAR *self;
-+{
-+  HASH_TABLE *h;
-+  int i;
-+  char *k, *v;
-+  BUCKET_CONTENTS *item;
-+
-+  h = assoc_cell (self);
-+  if (h)
-+    assoc_dispose (h);
-+
-+  if (hashed_filenames == 0 || HASH_ENTRIES (hashed_filenames) == 0)
-+    {
-+      var_setvalue (self, (char *)NULL);
-+      return self;
-+    }
-+
-+  h = assoc_create (hashed_filenames->nbuckets);
-+  for (i = 0; i < hashed_filenames->nbuckets; i++)
-+    {
-+      for (item = hash_items (i, hashed_filenames); item; item = item->next)
-+	{
-+	  k = savestring (item->key);
-+	  v = pathdata(item)->path;
-+	  assoc_insert (h, k, v);
-+	}
-+    }
-+
-+  var_setvalue (self, (char *)h);
-+  return self;
-+}
-+
-+static SHELL_VAR *
-+get_hashcmd (self)
-+     SHELL_VAR *self;
-+{
-+  build_hashcmd (self);
-+  return (self);
-+}
-+
-+static SHELL_VAR *
-+assign_hashcmd (self, value, ind, key)
-+     SHELL_VAR *self;
-+     char *value;
-+     arrayind_t ind;
-+     char *key;
-+{
-+  phash_insert (key, value, 0, 0);
-+  return (build_hashcmd (self));
-+}
-+
-+#if defined (ALIAS)
-+static SHELL_VAR *
-+build_aliasvar (self)
-+     SHELL_VAR *self;
-+{
-+  HASH_TABLE *h;
-+  int i;
-+  char *k, *v;
-+  BUCKET_CONTENTS *item;
-+
-+  h = assoc_cell (self);
-+  if (h)
-+    assoc_dispose (h);
-+
-+  if (aliases == 0 || HASH_ENTRIES (aliases) == 0)
-+    {
-+      var_setvalue (self, (char *)NULL);
-+      return self;
-+    }
-+
-+  h = assoc_create (aliases->nbuckets);
-+  for (i = 0; i < aliases->nbuckets; i++)
-+    {
-+      for (item = hash_items (i, aliases); item; item = item->next)
-+	{
-+	  k = savestring (item->key);
-+	  v = ((alias_t *)(item->data))->value;
-+	  assoc_insert (h, k, v);
-+	}
-+    }
-+
-+  var_setvalue (self, (char *)h);
-+  return self;
-+}
-+
-+static SHELL_VAR *
-+get_aliasvar (self)
-+     SHELL_VAR *self;
-+{
-+  build_aliasvar (self);
-+  return (self);
-+}
-+
-+static SHELL_VAR *
-+assign_aliasvar (self, value, ind, key)
-+     SHELL_VAR *self;
-+     char *value;
-+     arrayind_t ind;
-+     char *key;
-+{
-+  add_alias (key, value);
-+  return (build_aliasvar (self));
-+}
-+#endif /* ALIAS */
-+
-+#endif /* ARRAY_VARS */
-+
-+/* If ARRAY_VARS is not defined, this just returns the name of any
-+   currently-executing function.  If we have arrays, it's a call stack. */
-+static SHELL_VAR *
-+get_funcname (self)
-+     SHELL_VAR *self;
-+{
-+#if ! defined (ARRAY_VARS)
-+  char *t;
-+  if (variable_context && this_shell_function)
-+    {
-+      FREE (value_cell (self));
-+      t = savestring (this_shell_function->name);
-+      var_setvalue (self, t);
-+    }
-+#endif
-+  return (self);
-+}
-+
-+void
-+make_funcname_visible (on_or_off)
-+     int on_or_off;
-+{
-+  SHELL_VAR *v;
-+
-+  v = find_variable ("FUNCNAME");
-+  if (v == 0 || v->dynamic_value == 0)
-+    return;
-+
-+  if (on_or_off)
-+    VUNSETATTR (v, att_invisible);
-+  else
-+    VSETATTR (v, att_invisible);
-+}
-+
-+static SHELL_VAR *
-+init_funcname_var ()
-+{
-+  SHELL_VAR *v;
-+
-+  v = find_variable ("FUNCNAME");
-+  if (v)
-+    return v;
-+#if defined (ARRAY_VARS)
-+  INIT_DYNAMIC_ARRAY_VAR ("FUNCNAME", get_funcname, null_array_assign);
-+#else
-+  INIT_DYNAMIC_VAR ("FUNCNAME", (char *)NULL, get_funcname, null_assign);
-+#endif
-+  VSETATTR (v, att_invisible|att_noassign);
-+  return v;
-+}
-+
-+static void
-+initialize_dynamic_variables ()
-+{
-+  SHELL_VAR *v;
-+
-+  v = init_seconds_var ();
-+
-+  INIT_DYNAMIC_VAR ("BASH_COMMAND", (char *)NULL, get_bash_command, (sh_var_assign_func_t *)NULL);
-+  INIT_DYNAMIC_VAR ("BASH_SUBSHELL", (char *)NULL, get_subshell, assign_subshell);
-+
-+  INIT_DYNAMIC_VAR ("RANDOM", (char *)NULL, get_random, assign_random);
-+  VSETATTR (v, att_integer);
-+  INIT_DYNAMIC_VAR ("LINENO", (char *)NULL, get_lineno, assign_lineno);
-+  VSETATTR (v, att_integer);
-+
-+  INIT_DYNAMIC_VAR ("BASHPID", (char *)NULL, get_bashpid, null_assign);
-+  VSETATTR (v, att_integer|att_readonly);
-+
-+#if defined (HISTORY)
-+  INIT_DYNAMIC_VAR ("HISTCMD", (char *)NULL, get_histcmd, (sh_var_assign_func_t *)NULL);
-+  VSETATTR (v, att_integer);
-+#endif
-+
-+#if defined (READLINE)
-+  INIT_DYNAMIC_VAR ("COMP_WORDBREAKS", (char *)NULL, get_comp_wordbreaks, assign_comp_wordbreaks);
-+#endif
-+
-+#if defined (PUSHD_AND_POPD) && defined (ARRAY_VARS)
-+  v = init_dynamic_array_var ("DIRSTACK", get_dirstack, assign_dirstack, 0);
-+#endif /* PUSHD_AND_POPD && ARRAY_VARS */
-+
-+#if defined (ARRAY_VARS)
-+  v = init_dynamic_array_var ("GROUPS", get_groupset, null_array_assign, att_noassign);
-+
-+#  if defined (DEBUGGER)
-+  v = init_dynamic_array_var ("BASH_ARGC", get_self, null_array_assign, att_noassign|att_nounset);
-+  v = init_dynamic_array_var ("BASH_ARGV", get_self, null_array_assign, att_noassign|att_nounset);
-+#  endif /* DEBUGGER */
-+  v = init_dynamic_array_var ("BASH_SOURCE", get_self, null_array_assign, att_noassign|att_nounset);
-+  v = init_dynamic_array_var ("BASH_LINENO", get_self, null_array_assign, att_noassign|att_nounset);
-+
-+  v = init_dynamic_assoc_var ("BASH_CMDS", get_hashcmd, assign_hashcmd, att_nofree);
-+#  if defined (ALIAS)
-+  v = init_dynamic_assoc_var ("BASH_ALIASES", get_aliasvar, assign_aliasvar, att_nofree);
-+#  endif
-+#endif
-+
-+  v = init_funcname_var ();
-+}
-+
-+/* **************************************************************** */
-+/*								    */
-+/*		Retrieving variables and values			    */
-+/*								    */
-+/* **************************************************************** */
-+
-+/* How to get a pointer to the shell variable or function named NAME.
-+   HASHED_VARS is a pointer to the hash table containing the list
-+   of interest (either variables or functions). */
-+
-+static SHELL_VAR *
-+hash_lookup (name, hashed_vars)
-+     const char *name;
-+     HASH_TABLE *hashed_vars;
-+{
-+  BUCKET_CONTENTS *bucket;
-+
-+  bucket = hash_search (name, hashed_vars, 0);
-+  /* If we find the name in HASHED_VARS, set LAST_TABLE_SEARCHED to that
-+     table. */
-+  if (bucket)
-+    last_table_searched = hashed_vars;
-+  return (bucket ? (SHELL_VAR *)bucket->data : (SHELL_VAR *)NULL);
-+}
-+
-+SHELL_VAR *
-+var_lookup (name, vcontext)
-+     const char *name;
-+     VAR_CONTEXT *vcontext;
-+{
-+  VAR_CONTEXT *vc;
-+  SHELL_VAR *v;
-+
-+  v = (SHELL_VAR *)NULL;
-+  for (vc = vcontext; vc; vc = vc->down)
-+    if (v = hash_lookup (name, vc->table))
-+      break;
-+
-+  return v;
-+}
-+
-+/* Look up the variable entry named NAME.  If SEARCH_TEMPENV is non-zero,
-+   then also search the temporarily built list of exported variables.
-+   The lookup order is:
-+	temporary_env
-+	shell_variables list
-+*/
-+
-+SHELL_VAR *
-+find_variable_internal (name, force_tempenv)
-+     const char *name;
-+     int force_tempenv;
-+{
-+  SHELL_VAR *var;
-+  int search_tempenv;
-+  VAR_CONTEXT *vc;
-+
-+  var = (SHELL_VAR *)NULL;
-+
-+  /* If explicitly requested, first look in the temporary environment for
-+     the variable.  This allows constructs such as "foo=x eval 'echo $foo'"
-+     to get the `exported' value of $foo.  This happens if we are executing
-+     a function or builtin, or if we are looking up a variable in a
-+     "subshell environment". */
-+  search_tempenv = force_tempenv || (expanding_redir == 0 && subshell_environment);
-+
-+  if (search_tempenv && temporary_env)		
-+    var = hash_lookup (name, temporary_env);
-+
-+  vc = shell_variables;
-+#if 0
-+if (search_tempenv == 0 && /* (subshell_environment & SUBSHELL_COMSUB) && */
-+    expanding_redir &&
-+    (this_shell_builtin == eval_builtin || this_shell_builtin == command_builtin))
-+  {
-+  itrace("find_variable_internal: search_tempenv == 0: skipping VC_BLTNENV");
-+  while (vc && (vc->flags & VC_BLTNENV))
-+    vc = vc->down;
-+  if (vc == 0)
-+    vc = shell_variables;
-+  }
-+#endif
-+
-+  if (var == 0)
-+    var = var_lookup (name, vc);
-+
-+  if (var == 0)
-+    return ((SHELL_VAR *)NULL);
-+
-+  return (var->dynamic_value ? (*(var->dynamic_value)) (var) : var);
-+}
-+
-+/* Look up and resolve the chain of nameref variables starting at V all the
-+   way to NULL or non-nameref. */
-+SHELL_VAR *
-+find_variable_nameref (v)
-+     SHELL_VAR *v;
-+{
-+  int level;
-+  char *newname;
-+  SHELL_VAR *orig, *oldv;
-+
-+  level = 0;
-+  orig = v;
-+  while (v && nameref_p (v))
-+    {
-+      level++;
-+      if (level > NAMEREF_MAX)
-+	return ((SHELL_VAR *)0);	/* error message here? */
-+      newname = nameref_cell (v);
-+      if (newname == 0 || *newname == '\0')
-+	return ((SHELL_VAR *)0);
-+      oldv = v;
-+      v = find_variable_internal (newname, (expanding_redir == 0 && (assigning_in_environment || executing_builtin)));
-+      if (v == orig || v == oldv)
-+	{
-+	  internal_warning (_("%s: circular name reference"), orig->name);
-+	  return ((SHELL_VAR *)0);
-+	}
-+    }
-+  return v;
-+}
-+
-+/* Resolve the chain of nameref variables for NAME.  XXX - could change later */
-+SHELL_VAR *
-+find_variable_last_nameref (name)
-+     const char *name;
-+{
-+  SHELL_VAR *v, *nv;
-+  char *newname;
-+  int level;
-+
-+  nv = v = find_variable_noref (name);
-+  level = 0;
-+  while (v && nameref_p (v))
-+    {
-+      level++;
-+      if (level > NAMEREF_MAX)
-+        return ((SHELL_VAR *)0);	/* error message here? */
-+      newname = nameref_cell (v);
-+      if (newname == 0 || *newname == '\0')
-+	return ((SHELL_VAR *)0);
-+      nv = v;
-+      v = find_variable_internal (newname, (expanding_redir == 0 && (assigning_in_environment || executing_builtin)));
-+    }
-+  return nv;
-+}
-+
-+/* Resolve the chain of nameref variables for NAME.  XXX - could change later */
-+SHELL_VAR *
-+find_global_variable_last_nameref (name)
-+     const char *name;
-+{
-+  SHELL_VAR *v, *nv;
-+  char *newname;
-+  int level;
-+
-+  nv = v = find_global_variable_noref (name);
-+  level = 0;
-+  while (v && nameref_p (v))
-+    {
-+      level++;
-+      if (level > NAMEREF_MAX)
-+        return ((SHELL_VAR *)0);	/* error message here? */
-+      newname = nameref_cell (v);
-+      if (newname == 0 || *newname == '\0')
-+	return ((SHELL_VAR *)0);
-+      nv = v;
-+      v = find_global_variable_noref (newname);
-+    }
-+  return nv;
-+}
-+
-+static SHELL_VAR *
-+find_nameref_at_context (v, vc)
-+     SHELL_VAR *v;
-+     VAR_CONTEXT *vc;
-+{
-+  SHELL_VAR *nv, *nv2;
-+  VAR_CONTEXT *nvc;
-+  char *newname;
-+  int level;
-+
-+  nv = v;
-+  level = 1;
-+  while (nv && nameref_p (nv))
-+    {
-+      level++;
-+      if (level > NAMEREF_MAX)
-+        return ((SHELL_VAR *)NULL);
-+      newname = nameref_cell (nv);
-+      if (newname == 0 || *newname == '\0')
-+        return ((SHELL_VAR *)NULL);      
-+      nv2 = hash_lookup (newname, vc->table);
-+      if (nv2 == 0)
-+        break;
-+      nv = nv2;
-+    }
-+  return nv;
-+}
-+
-+/* Do nameref resolution from the VC, which is the local context for some
-+   function or builtin, `up' the chain to the global variables context.  If
-+   NVCP is not NULL, return the variable context where we finally ended the
-+   nameref resolution (so the bind_variable_internal can use the correct
-+   variable context and hash table). */
-+static SHELL_VAR *
-+find_variable_nameref_context (v, vc, nvcp)
-+     SHELL_VAR *v;
-+     VAR_CONTEXT *vc;
-+     VAR_CONTEXT **nvcp;
-+{
-+  SHELL_VAR *nv, *nv2;
-+  VAR_CONTEXT *nvc;
-+
-+  /* Look starting at the current context all the way `up' */
-+  for (nv = v, nvc = vc; nvc; nvc = nvc->down)
-+    {
-+      nv2 = find_nameref_at_context (nv, nvc);
-+      if (nv2 == 0)
-+        continue;
-+      nv = nv2;
-+      if (*nvcp)
-+        *nvcp = nvc;
-+      if (nameref_p (nv) == 0)
-+        break;
-+    }
-+  return (nameref_p (nv) ? (SHELL_VAR *)NULL : nv);
-+}
-+
-+/* Do nameref resolution from the VC, which is the local context for some
-+   function or builtin, `up' the chain to the global variables context.  If
-+   NVCP is not NULL, return the variable context where we finally ended the
-+   nameref resolution (so the bind_variable_internal can use the correct
-+   variable context and hash table). */
-+static SHELL_VAR *
-+find_variable_last_nameref_context (v, vc, nvcp)
-+     SHELL_VAR *v;
-+     VAR_CONTEXT *vc;
-+     VAR_CONTEXT **nvcp;
-+{
-+  SHELL_VAR *nv, *nv2;
-+  VAR_CONTEXT *nvc;
-+
-+  /* Look starting at the current context all the way `up' */
-+  for (nv = v, nvc = vc; nvc; nvc = nvc->down)
-+    {
-+      nv2 = find_nameref_at_context (nv, nvc);
-+      if (nv2 == 0)
-+	continue;
-+      nv = nv2;
-+      if (*nvcp)
-+        *nvcp = nvc;
-+    }
-+  return (nameref_p (nv) ? nv : (SHELL_VAR *)NULL);
-+}
-+
-+/* Find a variable, forcing a search of the temporary environment first */
-+SHELL_VAR *
-+find_variable_tempenv (name)
-+     const char *name;
-+{
-+  SHELL_VAR *var;
-+
-+  var = find_variable_internal (name, 1);
-+  if (var && nameref_p (var))
-+    var = find_variable_nameref (var);
-+  return (var);
-+}
-+
-+/* Find a variable, not forcing a search of the temporary environment first */
-+SHELL_VAR *
-+find_variable_notempenv (name)
-+     const char *name;
-+{
-+  SHELL_VAR *var;
-+
-+  var = find_variable_internal (name, 0);
-+  if (var && nameref_p (var))
-+    var = find_variable_nameref (var);
-+  return (var);
-+}
-+
-+SHELL_VAR *
-+find_global_variable (name)
-+     const char *name;
-+{
-+  SHELL_VAR *var;
-+
-+  var = var_lookup (name, global_variables);
-+  if (var && nameref_p (var))
-+    var = find_variable_nameref (var);
-+
-+  if (var == 0)
-+    return ((SHELL_VAR *)NULL);
-+
-+  return (var->dynamic_value ? (*(var->dynamic_value)) (var) : var);
-+}
-+
-+SHELL_VAR *
-+find_global_variable_noref (name)
-+     const char *name;
-+{
-+  SHELL_VAR *var;
-+
-+  var = var_lookup (name, global_variables);
-+
-+  if (var == 0)
-+    return ((SHELL_VAR *)NULL);
-+
-+  return (var->dynamic_value ? (*(var->dynamic_value)) (var) : var);
-+}
-+
-+SHELL_VAR *
-+find_shell_variable (name)
-+     const char *name;
-+{
-+  SHELL_VAR *var;
-+
-+  var = var_lookup (name, shell_variables);
-+  if (var && nameref_p (var))
-+    var = find_variable_nameref (var);
-+
-+  if (var == 0)
-+    return ((SHELL_VAR *)NULL);
-+
-+  return (var->dynamic_value ? (*(var->dynamic_value)) (var) : var);
-+}
-+
-+/* Look up the variable entry named NAME.  Returns the entry or NULL. */
-+SHELL_VAR *
-+find_variable (name)
-+     const char *name;
-+{
-+  SHELL_VAR *v;
-+
-+  last_table_searched = 0;
-+  v = find_variable_internal (name, (expanding_redir == 0 && (assigning_in_environment || executing_builtin)));
-+  if (v && nameref_p (v))
-+    v = find_variable_nameref (v);
-+  return v;
-+}
-+
-+SHELL_VAR *
-+find_variable_noref (name)
-+     const char *name;
-+{
-+  SHELL_VAR *v;
-+
-+  v = find_variable_internal (name, (expanding_redir == 0 && (assigning_in_environment || executing_builtin)));
-+  return v;
-+}
-+
-+/* Look up the function entry whose name matches STRING.
-+   Returns the entry or NULL. */
-+SHELL_VAR *
-+find_function (name)
-+     const char *name;
-+{
-+  return (hash_lookup (name, shell_functions));
-+}
-+
-+/* Find the function definition for the shell function named NAME.  Returns
-+   the entry or NULL. */
-+FUNCTION_DEF *
-+find_function_def (name)
-+     const char *name;
-+{
-+#if defined (DEBUGGER)
-+  return ((FUNCTION_DEF *)hash_lookup (name, shell_function_defs));
-+#else
-+  return ((FUNCTION_DEF *)0);
-+#endif
-+}
-+
-+/* Return the value of VAR.  VAR is assumed to have been the result of a
-+   lookup without any subscript, if arrays are compiled into the shell. */
-+char *
-+get_variable_value (var)
-+     SHELL_VAR *var;
-+{
-+  if (var == 0)
-+    return ((char *)NULL);
-+#if defined (ARRAY_VARS)
-+  else if (array_p (var))
-+    return (array_reference (array_cell (var), 0));
-+  else if (assoc_p (var))
-+    return (assoc_reference (assoc_cell (var), "0"));
-+#endif
-+  else
-+    return (value_cell (var));
-+}
-+
-+/* Return the string value of a variable.  Return NULL if the variable
-+   doesn't exist.  Don't cons a new string.  This is a potential memory
-+   leak if the variable is found in the temporary environment.  Since
-+   functions and variables have separate name spaces, returns NULL if
-+   var_name is a shell function only. */
-+char *
-+get_string_value (var_name)
-+     const char *var_name;
-+{
-+  SHELL_VAR *var;
-+
-+  var = find_variable (var_name);
-+  return ((var) ? get_variable_value (var) : (char *)NULL);
-+}
-+
-+/* This is present for use by the tilde and readline libraries. */
-+char *
-+sh_get_env_value (v)
-+     const char *v;
-+{
-+  return get_string_value (v);
-+}
-+
-+/* **************************************************************** */
-+/*								    */
-+/*		  Creating and setting variables		    */
-+/*								    */
-+/* **************************************************************** */
-+
-+/* Set NAME to VALUE if NAME has no value. */
-+SHELL_VAR *
-+set_if_not (name, value)
-+     char *name, *value;
-+{
-+  SHELL_VAR *v;
-+
-+  if (shell_variables == 0)
-+    create_variable_tables ();
-+
-+  v = find_variable (name);
-+  if (v == 0)
-+    v = bind_variable_internal (name, value, global_variables->table, HASH_NOSRCH, 0);
-+  return (v);
-+}
-+
-+/* Create a local variable referenced by NAME. */
-+SHELL_VAR *
-+make_local_variable (name)
-+     const char *name;
-+{
-+  SHELL_VAR *new_var, *old_var;
-+  VAR_CONTEXT *vc;
-+  int was_tmpvar;
-+  char *tmp_value;
-+
-+  /* local foo; local foo;  is a no-op. */
-+  old_var = find_variable (name);
-+  if (old_var && local_p (old_var) && old_var->context == variable_context)
-+    return (old_var);
-+
-+  was_tmpvar = old_var && tempvar_p (old_var);
-+  /* If we're making a local variable in a shell function, the temporary env
-+     has already been merged into the function's variable context stack.  We
-+     can assume that a temporary var in the same context appears in the same
-+     VAR_CONTEXT and can safely be returned without creating a new variable
-+     (which results in duplicate names in the same VAR_CONTEXT->table */
-+  /* We can't just test tmpvar_p because variables in the temporary env given
-+     to a shell function appear in the function's local variable VAR_CONTEXT
-+     but retain their tempvar attribute.  We want temporary variables that are
-+     found in temporary_env, hence the test for last_table_searched, which is
-+     set in hash_lookup and only (so far) checked here. */
-+  if (was_tmpvar && old_var->context == variable_context && last_table_searched != temporary_env)
-+    {
-+      VUNSETATTR (old_var, att_invisible);
-+      return (old_var);
-+    }
-+  if (was_tmpvar)
-+    tmp_value = value_cell (old_var);
-+
-+  for (vc = shell_variables; vc; vc = vc->down)
-+    if (vc_isfuncenv (vc) && vc->scope == variable_context)
-+      break;
-+
-+  if (vc == 0)
-+    {
-+      internal_error (_("make_local_variable: no function context at current scope"));
-+      return ((SHELL_VAR *)NULL);
-+    }
-+  else if (vc->table == 0)
-+    vc->table = hash_create (TEMPENV_HASH_BUCKETS);
-+
-+  /* Since this is called only from the local/declare/typeset code, we can
-+     call builtin_error here without worry (of course, it will also work
-+     for anything that sets this_command_name).  Variables with the `noassign'
-+     attribute may not be made local.  The test against old_var's context
-+     level is to disallow local copies of readonly global variables (since I
-+     believe that this could be a security hole).  Readonly copies of calling
-+     function local variables are OK. */
-+  if (old_var && (noassign_p (old_var) ||
-+		 (readonly_p (old_var) && old_var->context == 0)))
-+    {
-+      if (readonly_p (old_var))
-+	sh_readonly (name);
-+      else if (noassign_p (old_var))
-+	builtin_error (_("%s: variable may not be assigned value"), name);
-+#if 0
-+      /* Let noassign variables through with a warning */
-+      if (readonly_p (old_var))
-+#endif
-+	return ((SHELL_VAR *)NULL);
-+    }
-+
-+  if (old_var == 0)
-+    new_var = make_new_variable (name, vc->table);
-+  else
-+    {
-+      new_var = make_new_variable (name, vc->table);
-+
-+      /* If we found this variable in one of the temporary environments,
-+	 inherit its value.  Watch to see if this causes problems with
-+	 things like `x=4 local x'. XXX - see above for temporary env
-+	 variables with the same context level as variable_context */
-+      /* XXX - we should only do this if the variable is not an array. */
-+      if (was_tmpvar)
-+	var_setvalue (new_var, savestring (tmp_value));
-+
-+      new_var->attributes = exported_p (old_var) ? att_exported : 0;
-+    }
-+
-+  vc->flags |= VC_HASLOCAL;
-+
-+  new_var->context = variable_context;
-+  VSETATTR (new_var, att_local);
-+
-+  if (ifsname (name))
-+    setifs (new_var);
-+
-+  if (was_tmpvar == 0)
-+    VSETATTR (new_var, att_invisible);	/* XXX */
-+  return (new_var);
-+}
-+
-+/* Create a new shell variable with name NAME. */
-+static SHELL_VAR *
-+new_shell_variable (name)
-+     const char *name;
-+{
-+  SHELL_VAR *entry;
-+
-+  entry = (SHELL_VAR *)xmalloc (sizeof (SHELL_VAR));
-+
-+  entry->name = savestring (name);
-+  var_setvalue (entry, (char *)NULL);
-+  CLEAR_EXPORTSTR (entry);
-+
-+  entry->dynamic_value = (sh_var_value_func_t *)NULL;
-+  entry->assign_func = (sh_var_assign_func_t *)NULL;
-+
-+  entry->attributes = 0;
-+
-+  /* Always assume variables are to be made at toplevel!
-+     make_local_variable has the responsibility of changing the
-+     variable context. */
-+  entry->context = 0;
-+
-+  return (entry);
-+}
-+
-+/* Create a new shell variable with name NAME and add it to the hash table
-+   TABLE. */
-+static SHELL_VAR *
-+make_new_variable (name, table)
-+     const char *name;
-+     HASH_TABLE *table;
-+{
-+  SHELL_VAR *entry;
-+  BUCKET_CONTENTS *elt;
-+
-+  entry = new_shell_variable (name);
-+
-+  /* Make sure we have a shell_variables hash table to add to. */
-+  if (shell_variables == 0)
-+    create_variable_tables ();
-+
-+  elt = hash_insert (savestring (name), table, HASH_NOSRCH);
-+  elt->data = (PTR_T)entry;
-+
-+  return entry;
-+}
-+
-+#if defined (ARRAY_VARS)
-+SHELL_VAR *
-+make_new_array_variable (name)
-+     char *name;
-+{
-+  SHELL_VAR *entry;
-+  ARRAY *array;
-+
-+  entry = make_new_variable (name, global_variables->table);
-+  array = array_create ();
-+
-+  var_setarray (entry, array);
-+  VSETATTR (entry, att_array);
-+  return entry;
-+}
-+
-+SHELL_VAR *
-+make_local_array_variable (name, assoc_ok)
-+     char *name;
-+     int assoc_ok;
-+{
-+  SHELL_VAR *var;
-+  ARRAY *array;
-+
-+  var = make_local_variable (name);
-+  if (var == 0 || array_p (var) || (assoc_ok && assoc_p (var)))
-+    return var;
-+
-+  array = array_create ();
-+
-+  dispose_variable_value (var);
-+  var_setarray (var, array);
-+  VSETATTR (var, att_array);
-+  return var;
-+}
-+
-+SHELL_VAR *
-+make_new_assoc_variable (name)
-+     char *name;
-+{
-+  SHELL_VAR *entry;
-+  HASH_TABLE *hash;
-+
-+  entry = make_new_variable (name, global_variables->table);
-+  hash = assoc_create (0);
-+
-+  var_setassoc (entry, hash);
-+  VSETATTR (entry, att_assoc);
-+  return entry;
-+}
-+
-+SHELL_VAR *
-+make_local_assoc_variable (name)
-+     char *name;
-+{
-+  SHELL_VAR *var;
-+  HASH_TABLE *hash;
-+
-+  var = make_local_variable (name);
-+  if (var == 0 || assoc_p (var))
-+    return var;
-+
-+  dispose_variable_value (var);
-+  hash = assoc_create (0);
-+
-+  var_setassoc (var, hash);
-+  VSETATTR (var, att_assoc);
-+  return var;
-+}
-+#endif
-+
-+char *
-+make_variable_value (var, value, flags)
-+     SHELL_VAR *var;
-+     char *value;
-+     int flags;
-+{
-+  char *retval, *oval;
-+  intmax_t lval, rval;
-+  int expok, olen, op;
-+
-+  /* If this variable has had its type set to integer (via `declare -i'),
-+     then do expression evaluation on it and store the result.  The
-+     functions in expr.c (evalexp()) and bind_int_variable() are responsible
-+     for turning off the integer flag if they don't want further
-+     evaluation done. */
-+  if (integer_p (var))
-+    {
-+      if (flags & ASS_APPEND)
-+	{
-+	  oval = value_cell (var);
-+	  lval = evalexp (oval, &expok);	/* ksh93 seems to do this */
-+	  if (expok == 0)
-+	    {
-+	      top_level_cleanup ();
-+	      jump_to_top_level (DISCARD);
-+	    }
-+	}
-+      rval = evalexp (value, &expok);
-+      if (expok == 0)
-+	{
-+	  top_level_cleanup ();
-+	  jump_to_top_level (DISCARD);
-+	}
-+      /* This can be fooled if the variable's value changes while evaluating
-+	 `rval'.  We can change it if we move the evaluation of lval to here. */
-+      if (flags & ASS_APPEND)
-+	rval += lval;
-+      retval = itos (rval);
-+    }
-+#if defined (CASEMOD_ATTRS)
-+  else if (capcase_p (var) || uppercase_p (var) || lowercase_p (var))
-+    {
-+      if (flags & ASS_APPEND)
-+	{
-+	  oval = get_variable_value (var);
-+	  if (oval == 0)	/* paranoia */
-+	    oval = "";
-+	  olen = STRLEN (oval);
-+	  retval = (char *)xmalloc (olen + (value ? STRLEN (value) : 0) + 1);
-+	  strcpy (retval, oval);
-+	  if (value)
-+	    strcpy (retval+olen, value);
-+	}
-+      else if (*value)
-+	retval = savestring (value);
-+      else
-+	{
-+	  retval = (char *)xmalloc (1);
-+	  retval[0] = '\0';
-+	}
-+      op = capcase_p (var) ? CASE_CAPITALIZE
-+			 : (uppercase_p (var) ? CASE_UPPER : CASE_LOWER);
-+      oval = sh_modcase (retval, (char *)0, op);
-+      free (retval);
-+      retval = oval;
-+    }
-+#endif /* CASEMOD_ATTRS */
-+  else if (value)
-+    {
-+      if (flags & ASS_APPEND)
-+	{
-+	  oval = get_variable_value (var);
-+	  if (oval == 0)	/* paranoia */
-+	    oval = "";
-+	  olen = STRLEN (oval);
-+	  retval = (char *)xmalloc (olen + (value ? STRLEN (value) : 0) + 1);
-+	  strcpy (retval, oval);
-+	  if (value)
-+	    strcpy (retval+olen, value);
-+	}
-+      else if (*value)
-+	retval = savestring (value);
-+      else
-+	{
-+	  retval = (char *)xmalloc (1);
-+	  retval[0] = '\0';
-+	}
-+    }
-+  else
-+    retval = (char *)NULL;
-+
-+  return retval;
-+}
-+
-+/* Bind a variable NAME to VALUE in the HASH_TABLE TABLE, which may be the
-+   temporary environment (but usually is not). */
-+static SHELL_VAR *
-+bind_variable_internal (name, value, table, hflags, aflags)
-+     const char *name;
-+     char *value;
-+     HASH_TABLE *table;
-+     int hflags, aflags;
-+{
-+  char *newval;
-+  SHELL_VAR *entry;
-+
-+  entry = (hflags & HASH_NOSRCH) ? (SHELL_VAR *)NULL : hash_lookup (name, table);
-+  /* Follow the nameref chain here if this is the global variables table */
-+  if (entry && nameref_p (entry) && (invisible_p (entry) == 0) && table == global_variables->table)
-+    {
-+      entry = find_global_variable (entry->name);
-+      /* Let's see if we have a nameref referencing a variable that hasn't yet
-+	 been created. */
-+      if (entry == 0)
-+	entry = find_variable_last_nameref (name);	/* XXX */
-+      if (entry == 0)					/* just in case */
-+        return (entry);
-+    }
-+
-+  /* The first clause handles `declare -n ref; ref=x;' */
-+  if (entry && invisible_p (entry) && nameref_p (entry))
-+    goto assign_value;
-+  else if (entry && nameref_p (entry))
-+    {
-+      newval = nameref_cell (entry);
-+#if defined (ARRAY_VARS)
-+      /* declare -n foo=x[2] */
-+      if (valid_array_reference (newval))
-+        /* XXX - should it be aflags? */
-+	entry = assign_array_element (newval, make_variable_value (entry, value, 0), aflags);
-+      else
-+#endif
-+      {
-+      entry = make_new_variable (newval, table);
-+      var_setvalue (entry, make_variable_value (entry, value, 0));
-+      }
-+    }
-+  else if (entry == 0)
-+    {
-+      entry = make_new_variable (name, table);
-+      var_setvalue (entry, make_variable_value (entry, value, 0)); /* XXX */
-+    }
-+  else if (entry->assign_func)	/* array vars have assign functions now */
-+    {
-+      INVALIDATE_EXPORTSTR (entry);
-+      newval = (aflags & ASS_APPEND) ? make_variable_value (entry, value, aflags) : value;
-+      if (assoc_p (entry))
-+	entry = (*(entry->assign_func)) (entry, newval, -1, savestring ("0"));
-+      else if (array_p (entry))
-+	entry = (*(entry->assign_func)) (entry, newval, 0, 0);
-+      else
-+	entry = (*(entry->assign_func)) (entry, newval, -1, 0);
-+      if (newval != value)
-+	free (newval);
-+      return (entry);
-+    }
-+  else
-+    {
-+assign_value:
-+      if (readonly_p (entry) || noassign_p (entry))
-+	{
-+	  if (readonly_p (entry))
-+	    err_readonly (name);
-+	  return (entry);
-+	}
-+
-+      /* Variables which are bound are visible. */
-+      VUNSETATTR (entry, att_invisible);
-+
-+#if defined (ARRAY_VARS)
-+      if (assoc_p (entry) || array_p (entry))
-+        newval = make_array_variable_value (entry, 0, "0", value, aflags);
-+      else
-+#endif
-+
-+      newval = make_variable_value (entry, value, aflags);	/* XXX */
-+
-+      /* Invalidate any cached export string */
-+      INVALIDATE_EXPORTSTR (entry);
-+
-+#if defined (ARRAY_VARS)
-+      /* XXX -- this bears looking at again -- XXX */
-+      /* If an existing array variable x is being assigned to with x=b or
-+	 `read x' or something of that nature, silently convert it to
-+	 x[0]=b or `read x[0]'. */
-+      if (assoc_p (entry))
-+	{
-+	  assoc_insert (assoc_cell (entry), savestring ("0"), newval);
-+	  free (newval);
-+	}
-+      else if (array_p (entry))
-+	{
-+	  array_insert (array_cell (entry), 0, newval);
-+	  free (newval);
-+	}
-+      else
-+#endif
-+	{
-+	  FREE (value_cell (entry));
-+	  var_setvalue (entry, newval);
-+	}
-+    }
-+
-+  if (mark_modified_vars)
-+    VSETATTR (entry, att_exported);
-+
-+  if (exported_p (entry))
-+    array_needs_making = 1;
-+
-+  return (entry);
-+}
-+	
-+/* Bind a variable NAME to VALUE.  This conses up the name
-+   and value strings.  If we have a temporary environment, we bind there
-+   first, then we bind into shell_variables. */
-+
-+SHELL_VAR *
-+bind_variable (name, value, flags)
-+     const char *name;
-+     char *value;
-+     int flags;
-+{
-+  SHELL_VAR *v, *nv;
-+  VAR_CONTEXT *vc, *nvc;
-+  int level;
-+
-+  if (shell_variables == 0)
-+    create_variable_tables ();
-+
-+  /* If we have a temporary environment, look there first for the variable,
-+     and, if found, modify the value there before modifying it in the
-+     shell_variables table.  This allows sourced scripts to modify values
-+     given to them in a temporary environment while modifying the variable
-+     value that the caller sees. */
-+  if (temporary_env)
-+    bind_tempenv_variable (name, value);
-+
-+  /* XXX -- handle local variables here. */
-+  for (vc = shell_variables; vc; vc = vc->down)
-+    {
-+      if (vc_isfuncenv (vc) || vc_isbltnenv (vc))
-+	{
-+	  v = hash_lookup (name, vc->table);
-+	  nvc = vc;
-+	  if (v && nameref_p (v))
-+	    {
-+	      nv = find_variable_nameref_context (v, vc, &nvc);
-+	      if (nv == 0)
-+		{
-+		  nv = find_variable_last_nameref_context (v, vc, &nvc);
-+		  if (nv && nameref_p (nv))
-+		    {
-+		      /* If this nameref variable doesn't have a value yet,
-+			 set the value.  Otherwise, assign using the value as
-+			 normal. */
-+		      if (nameref_cell (nv) == 0)
-+			return (bind_variable_internal (nv->name, value, nvc->table, 0, flags));
-+		      return (bind_variable_internal (nameref_cell (nv), value, nvc->table, 0, flags));
-+		    }
-+		  else
-+		    v = nv;
-+		}
-+	      else
-+	        v = nv;
-+	    }
-+	  if (v)
-+	    return (bind_variable_internal (v->name, value, nvc->table, 0, flags));
-+	}
-+    }
-+  /* bind_variable_internal will handle nameref resolution in this case */
-+  return (bind_variable_internal (name, value, global_variables->table, 0, flags));
-+}
-+
-+SHELL_VAR *
-+bind_global_variable (name, value, flags)
-+     const char *name;
-+     char *value;
-+     int flags;
-+{
-+  SHELL_VAR *v, *nv;
-+  VAR_CONTEXT *vc, *nvc;
-+  int level;
-+
-+  if (shell_variables == 0)
-+    create_variable_tables ();
-+
-+  /* bind_variable_internal will handle nameref resolution in this case */
-+  return (bind_variable_internal (name, value, global_variables->table, 0, flags));
-+}
-+
-+/* Make VAR, a simple shell variable, have value VALUE.  Once assigned a
-+   value, variables are no longer invisible.  This is a duplicate of part
-+   of the internals of bind_variable.  If the variable is exported, or
-+   all modified variables should be exported, mark the variable for export
-+   and note that the export environment needs to be recreated. */
-+SHELL_VAR *
-+bind_variable_value (var, value, aflags)
-+     SHELL_VAR *var;
-+     char *value;
-+     int aflags;
-+{
-+  char *t;
-+  int invis;
-+
-+  invis = invisible_p (var);
-+  VUNSETATTR (var, att_invisible);
-+
-+  if (var->assign_func)
-+    {
-+      /* If we're appending, we need the old value, so use
-+	 make_variable_value */
-+      t = (aflags & ASS_APPEND) ? make_variable_value (var, value, aflags) : value;
-+      (*(var->assign_func)) (var, t, -1, 0);
-+      if (t != value && t)
-+	free (t);      
-+    }
-+  else
-+    {
-+      t = make_variable_value (var, value, aflags);
-+#if defined (ARRAY_VARS)
-+      if ((aflags & ASS_NAMEREF) && (t == 0 || *t == 0 || (legal_identifier (t) == 0 && valid_array_reference (t) == 0)))
-+#else
-+      if ((aflags & ASS_NAMEREF) && (t == 0 || *t == 0 || legal_identifier (t) == 0))
-+#endif
-+	{
-+	  free (t);
-+	  if (invis)
-+	    VSETATTR (var, att_invisible);	/* XXX */
-+	  return ((SHELL_VAR *)NULL);
-+	}
-+      FREE (value_cell (var));
-+      var_setvalue (var, t);
-+    }
-+
-+  INVALIDATE_EXPORTSTR (var);
-+
-+  if (mark_modified_vars)
-+    VSETATTR (var, att_exported);
-+
-+  if (exported_p (var))
-+    array_needs_making = 1;
-+
-+  return (var);
-+}
-+
-+/* Bind/create a shell variable with the name LHS to the RHS.
-+   This creates or modifies a variable such that it is an integer.
-+
-+   This used to be in expr.c, but it is here so that all of the
-+   variable binding stuff is localized.  Since we don't want any
-+   recursive evaluation from bind_variable() (possible without this code,
-+   since bind_variable() calls the evaluator for variables with the integer
-+   attribute set), we temporarily turn off the integer attribute for each
-+   variable we set here, then turn it back on after binding as necessary. */
-+
-+SHELL_VAR *
-+bind_int_variable (lhs, rhs)
-+     char *lhs, *rhs;
-+{
-+  register SHELL_VAR *v;
-+  int isint, isarr, implicitarray;
-+
-+  isint = isarr = implicitarray = 0;
-+#if defined (ARRAY_VARS)
-+  if (valid_array_reference (lhs))
-+    {
-+      isarr = 1;
-+      v = array_variable_part (lhs, (char **)0, (int *)0);
-+    }
-+  else
-+#endif
-+    v = find_variable (lhs);
-+
-+  if (v)
-+    {
-+      isint = integer_p (v);
-+      VUNSETATTR (v, att_integer);
-+#if defined (ARRAY_VARS)
-+      if (array_p (v) && isarr == 0)
-+	implicitarray = 1;
-+#endif
-+    }
-+
-+#if defined (ARRAY_VARS)
-+  if (isarr)
-+    v = assign_array_element (lhs, rhs, 0);
-+  else if (implicitarray)
-+    v = bind_array_variable (lhs, 0, rhs, 0);
-+  else
-+#endif
-+    v = bind_variable (lhs, rhs, 0);
-+
-+  if (v && isint)
-+    VSETATTR (v, att_integer);
-+
-+  VUNSETATTR (v, att_invisible);
-+
-+  return (v);
-+}
-+
-+SHELL_VAR *
-+bind_var_to_int (var, val)
-+     char *var;
-+     intmax_t val;
-+{
-+  char ibuf[INT_STRLEN_BOUND (intmax_t) + 1], *p;
-+
-+  p = fmtulong (val, 10, ibuf, sizeof (ibuf), 0);
-+  return (bind_int_variable (var, p));
-+}
-+
-+/* Do a function binding to a variable.  You pass the name and
-+   the command to bind to.  This conses the name and command. */
-+SHELL_VAR *
-+bind_function (name, value)
-+     const char *name;
-+     COMMAND *value;
-+{
-+  SHELL_VAR *entry;
-+
-+  entry = find_function (name);
-+  if (entry == 0)
-+    {
-+      BUCKET_CONTENTS *elt;
-+
-+      elt = hash_insert (savestring (name), shell_functions, HASH_NOSRCH);
-+      entry = new_shell_variable (name);
-+      elt->data = (PTR_T)entry;
-+    }
-+  else
-+    INVALIDATE_EXPORTSTR (entry);
-+
-+  if (var_isset (entry))
-+    dispose_command (function_cell (entry));
-+
-+  if (value)
-+    var_setfunc (entry, copy_command (value));
-+  else
-+    var_setfunc (entry, 0);
-+
-+  VSETATTR (entry, att_function);
-+
-+  if (mark_modified_vars)
-+    VSETATTR (entry, att_exported);
-+
-+  VUNSETATTR (entry, att_invisible);		/* Just to be sure */
-+
-+  if (exported_p (entry))
-+    array_needs_making = 1;
-+
-+#if defined (PROGRAMMABLE_COMPLETION)
-+  set_itemlist_dirty (&it_functions);
-+#endif
-+
-+  return (entry);
-+}
-+
-+#if defined (DEBUGGER)
-+/* Bind a function definition, which includes source file and line number
-+   information in addition to the command, into the FUNCTION_DEF hash table.*/
-+void
-+bind_function_def (name, value)
-+     const char *name;
-+     FUNCTION_DEF *value;
-+{
-+  FUNCTION_DEF *entry;
-+  BUCKET_CONTENTS *elt;
-+  COMMAND *cmd;
-+
-+  entry = find_function_def (name);
-+  if (entry)
-+    {
-+      dispose_function_def_contents (entry);
-+      entry = copy_function_def_contents (value, entry);
-+    }
-+  else
-+    {
-+      cmd = value->command;
-+      value->command = 0;
-+      entry = copy_function_def (value);
-+      value->command = cmd;
-+
-+      elt = hash_insert (savestring (name), shell_function_defs, HASH_NOSRCH);
-+      elt->data = (PTR_T *)entry;
-+    }
-+}
-+#endif /* DEBUGGER */
-+
-+/* Add STRING, which is of the form foo=bar, to the temporary environment
-+   HASH_TABLE (temporary_env).  The functions in execute_cmd.c are
-+   responsible for moving the main temporary env to one of the other
-+   temporary environments.  The expansion code in subst.c calls this. */
-+int
-+assign_in_env (word, flags)
-+     WORD_DESC *word;
-+     int flags;
-+{
-+  int offset, aflags;
-+  char *name, *temp, *value;
-+  SHELL_VAR *var;
-+  const char *string;
-+
-+  string = word->word;
-+
-+  aflags = 0;
-+  offset = assignment (string, 0);
-+  name = savestring (string);
-+  value = (char *)NULL;
-+
-+  if (name[offset] == '=')
-+    {
-+      name[offset] = 0;
-+
-+      /* don't ignore the `+' when assigning temporary environment */
-+      if (name[offset - 1] == '+')
-+	{
-+	  name[offset - 1] = '\0';
-+	  aflags |= ASS_APPEND;
-+	}
-+
-+      var = find_variable (name);
-+      if (var && (readonly_p (var) || noassign_p (var)))
-+	{
-+	  if (readonly_p (var))
-+	    err_readonly (name);
-+	  free (name);
-+  	  return (0);
-+	}
-+
-+      temp = name + offset + 1;
-+      value = expand_assignment_string_to_string (temp, 0);
-+
-+      if (var && (aflags & ASS_APPEND))
-+	{
-+	  temp = make_variable_value (var, value, aflags);
-+	  FREE (value);
-+	  value = temp;
-+	}
-+    }
-+
-+  if (temporary_env == 0)
-+    temporary_env = hash_create (TEMPENV_HASH_BUCKETS);
-+
-+  var = hash_lookup (name, temporary_env);
-+  if (var == 0)
-+    var = make_new_variable (name, temporary_env);
-+  else
-+    FREE (value_cell (var));
-+
-+  if (value == 0)
-+    {
-+      value = (char *)xmalloc (1);	/* like do_assignment_internal */
-+      value[0] = '\0';
-+    }
-+
-+  var_setvalue (var, value);
-+  var->attributes |= (att_exported|att_tempvar);
-+  var->context = variable_context;	/* XXX */
-+
-+  INVALIDATE_EXPORTSTR (var);
-+  var->exportstr = mk_env_string (name, value, 0);
-+
-+  array_needs_making = 1;
-+
-+  if (flags)
-+    stupidly_hack_special_variables (name);
-+
-+  if (echo_command_at_execute)
-+    /* The Korn shell prints the `+ ' in front of assignment statements,
-+	so we do too. */
-+    xtrace_print_assignment (name, value, 0, 1);
-+
-+  free (name);
-+  return 1;
-+}
-+
-+/* **************************************************************** */
-+/*								    */
-+/*			Copying variables			    */
-+/*								    */
-+/* **************************************************************** */
-+
-+#ifdef INCLUDE_UNUSED
-+/* Copy VAR to a new data structure and return that structure. */
-+SHELL_VAR *
-+copy_variable (var)
-+     SHELL_VAR *var;
-+{
-+  SHELL_VAR *copy = (SHELL_VAR *)NULL;
-+
-+  if (var)
-+    {
-+      copy = (SHELL_VAR *)xmalloc (sizeof (SHELL_VAR));
-+
-+      copy->attributes = var->attributes;
-+      copy->name = savestring (var->name);
-+
-+      if (function_p (var))
-+	var_setfunc (copy, copy_command (function_cell (var)));
-+#if defined (ARRAY_VARS)
-+      else if (array_p (var))
-+	var_setarray (copy, array_copy (array_cell (var)));
-+      else if (assoc_p (var))
-+	var_setassoc (copy, assoc_copy (assoc_cell (var)));
-+#endif
-+      else if (nameref_cell (var))	/* XXX - nameref */
-+	var_setref (copy, savestring (nameref_cell (var)));
-+      else if (value_cell (var))	/* XXX - nameref */
-+	var_setvalue (copy, savestring (value_cell (var)));
-+      else
-+	var_setvalue (copy, (char *)NULL);
-+
-+      copy->dynamic_value = var->dynamic_value;
-+      copy->assign_func = var->assign_func;
-+
-+      copy->exportstr = COPY_EXPORTSTR (var);
-+
-+      copy->context = var->context;
-+    }
-+  return (copy);
-+}
-+#endif
-+
-+/* **************************************************************** */
-+/*								    */
-+/*		  Deleting and unsetting variables		    */
-+/*								    */
-+/* **************************************************************** */
-+
-+/* Dispose of the information attached to VAR. */
-+static void
-+dispose_variable_value (var)
-+     SHELL_VAR *var;
-+{
-+  if (function_p (var))
-+    dispose_command (function_cell (var));
-+#if defined (ARRAY_VARS)
-+  else if (array_p (var))
-+    array_dispose (array_cell (var));
-+  else if (assoc_p (var))
-+    assoc_dispose (assoc_cell (var));
-+#endif
-+  else if (nameref_p (var))
-+    FREE (nameref_cell (var));
-+  else
-+    FREE (value_cell (var));
-+}
-+
-+void
-+dispose_variable (var)
-+     SHELL_VAR *var;
-+{
-+  if (var == 0)
-+    return;
-+
-+  if (nofree_p (var) == 0)
-+    dispose_variable_value (var);
-+
-+  FREE_EXPORTSTR (var);
-+
-+  free (var->name);
-+
-+  if (exported_p (var))
-+    array_needs_making = 1;
-+
-+  free (var);
-+}
-+
-+/* Unset the shell variable referenced by NAME.  Unsetting a nameref variable
-+   unsets the variable it resolves to but leaves the nameref alone. */
-+int
-+unbind_variable (name)
-+     const char *name;
-+{
-+  SHELL_VAR *v, *nv;
-+  int r;
-+
-+  v = var_lookup (name, shell_variables);
-+  nv = (v && nameref_p (v)) ? find_variable_nameref (v) : (SHELL_VAR *)NULL;
-+
-+  r = nv ? makunbound (nv->name, shell_variables) : makunbound (name, shell_variables);
-+  return r;
-+}
-+
-+/* Unbind NAME, where NAME is assumed to be a nameref variable */
-+int
-+unbind_nameref (name)
-+     const char *name;
-+{
-+  SHELL_VAR *v;
-+
-+  v = var_lookup (name, shell_variables);
-+  if (v && nameref_p (v))
-+    return makunbound (name, shell_variables);
-+  return 0;
-+}
-+
-+/* Unset the shell function named NAME. */
-+int
-+unbind_func (name)
-+     const char *name;
-+{
-+  BUCKET_CONTENTS *elt;
-+  SHELL_VAR *func;
-+
-+  elt = hash_remove (name, shell_functions, 0);
-+
-+  if (elt == 0)
-+    return -1;
-+
-+#if defined (PROGRAMMABLE_COMPLETION)
-+  set_itemlist_dirty (&it_functions);
-+#endif
-+
-+  func = (SHELL_VAR *)elt->data;
-+  if (func)
-+    {
-+      if (exported_p (func))
-+	array_needs_making++;
-+      dispose_variable (func);
-+    }
-+
-+  free (elt->key);
-+  free (elt);
-+
-+  return 0;  
-+}
-+
-+#if defined (DEBUGGER)
-+int
-+unbind_function_def (name)
-+     const char *name;
-+{
-+  BUCKET_CONTENTS *elt;
-+  FUNCTION_DEF *funcdef;
-+
-+  elt = hash_remove (name, shell_function_defs, 0);
-+
-+  if (elt == 0)
-+    return -1;
-+
-+  funcdef = (FUNCTION_DEF *)elt->data;
-+  if (funcdef)
-+    dispose_function_def (funcdef);
-+
-+  free (elt->key);
-+  free (elt);
-+
-+  return 0;  
-+}
-+#endif /* DEBUGGER */
-+
-+int
-+delete_var (name, vc)
-+     const char *name;
-+     VAR_CONTEXT *vc;
-+{
-+  BUCKET_CONTENTS *elt;
-+  SHELL_VAR *old_var;
-+  VAR_CONTEXT *v;
-+
-+  for (elt = (BUCKET_CONTENTS *)NULL, v = vc; v; v = v->down)
-+    if (elt = hash_remove (name, v->table, 0))
-+      break;
-+
-+  if (elt == 0)
-+    return (-1);
-+
-+  old_var = (SHELL_VAR *)elt->data;
-+  free (elt->key);
-+  free (elt);
-+
-+  dispose_variable (old_var);
-+  return (0);
-+}
-+
-+/* Make the variable associated with NAME go away.  HASH_LIST is the
-+   hash table from which this variable should be deleted (either
-+   shell_variables or shell_functions).
-+   Returns non-zero if the variable couldn't be found. */
-+int
-+makunbound (name, vc)
-+     const char *name;
-+     VAR_CONTEXT *vc;
-+{
-+  BUCKET_CONTENTS *elt, *new_elt;
-+  SHELL_VAR *old_var;
-+  VAR_CONTEXT *v;
-+  char *t;
-+
-+  for (elt = (BUCKET_CONTENTS *)NULL, v = vc; v; v = v->down)
-+    if (elt = hash_remove (name, v->table, 0))
-+      break;
-+
-+  if (elt == 0)
-+    return (-1);
-+
-+  old_var = (SHELL_VAR *)elt->data;
-+
-+  if (old_var && exported_p (old_var))
-+    array_needs_making++;
-+
-+  /* If we're unsetting a local variable and we're still executing inside
-+     the function, just mark the variable as invisible.  The function
-+     eventually called by pop_var_context() will clean it up later.  This
-+     must be done so that if the variable is subsequently assigned a new
-+     value inside the function, the `local' attribute is still present.
-+     We also need to add it back into the correct hash table. */
-+  if (old_var && local_p (old_var) && variable_context == old_var->context)
-+    {
-+      if (nofree_p (old_var))
-+	var_setvalue (old_var, (char *)NULL);
-+#if defined (ARRAY_VARS)
-+      else if (array_p (old_var))
-+	array_dispose (array_cell (old_var));
-+      else if (assoc_p (old_var))
-+	assoc_dispose (assoc_cell (old_var));
-+#endif
-+      else if (nameref_p (old_var))
-+	FREE (nameref_cell (old_var));
-+      else
-+	FREE (value_cell (old_var));
-+      /* Reset the attributes.  Preserve the export attribute if the variable
-+	 came from a temporary environment.  Make sure it stays local, and
-+	 make it invisible. */ 
-+      old_var->attributes = (exported_p (old_var) && tempvar_p (old_var)) ? att_exported : 0;
-+      VSETATTR (old_var, att_local);
-+      VSETATTR (old_var, att_invisible);
-+      var_setvalue (old_var, (char *)NULL);
-+      INVALIDATE_EXPORTSTR (old_var);
-+
-+      new_elt = hash_insert (savestring (old_var->name), v->table, 0);
-+      new_elt->data = (PTR_T)old_var;
-+      stupidly_hack_special_variables (old_var->name);
-+
-+      free (elt->key);
-+      free (elt);
-+      return (0);
-+    }
-+
-+  /* Have to save a copy of name here, because it might refer to
-+     old_var->name.  If so, stupidly_hack_special_variables will
-+     reference freed memory. */
-+  t = savestring (name);
-+
-+  free (elt->key);
-+  free (elt);
-+
-+  dispose_variable (old_var);
-+  stupidly_hack_special_variables (t);
-+  free (t);
-+
-+  return (0);
-+}
-+
-+/* Get rid of all of the variables in the current context. */
-+void
-+kill_all_local_variables ()
-+{
-+  VAR_CONTEXT *vc;
-+
-+  for (vc = shell_variables; vc; vc = vc->down)
-+    if (vc_isfuncenv (vc) && vc->scope == variable_context)
-+      break;
-+  if (vc == 0)
-+    return;		/* XXX */
-+
-+  if (vc->table && vc_haslocals (vc))
-+    {
-+      delete_all_variables (vc->table);
-+      hash_dispose (vc->table);
-+    }
-+  vc->table = (HASH_TABLE *)NULL;
-+}
-+
-+static void
-+free_variable_hash_data (data)
-+     PTR_T data;
-+{
-+  SHELL_VAR *var;
-+
-+  var = (SHELL_VAR *)data;
-+  dispose_variable (var);
-+}
-+
-+/* Delete the entire contents of the hash table. */
-+void
-+delete_all_variables (hashed_vars)
-+     HASH_TABLE *hashed_vars;
-+{
-+  hash_flush (hashed_vars, free_variable_hash_data);
-+}
-+
-+/* **************************************************************** */
-+/*								    */
-+/*		     Setting variable attributes		    */
-+/*								    */
-+/* **************************************************************** */
-+
-+#define FIND_OR_MAKE_VARIABLE(name, entry) \
-+  do \
-+    { \
-+      entry = find_variable (name); \
-+      if (!entry) \
-+	{ \
-+	  entry = bind_variable (name, "", 0); \
-+	  if (!no_invisible_vars && entry) entry->attributes |= att_invisible; \
-+	} \
-+    } \
-+  while (0)
-+
-+/* Make the variable associated with NAME be readonly.
-+   If NAME does not exist yet, create it. */
-+void
-+set_var_read_only (name)
-+     char *name;
-+{
-+  SHELL_VAR *entry;
-+
-+  FIND_OR_MAKE_VARIABLE (name, entry);
-+  VSETATTR (entry, att_readonly);
-+}
-+
-+#ifdef INCLUDE_UNUSED
-+/* Make the function associated with NAME be readonly.
-+   If NAME does not exist, we just punt, like auto_export code below. */
-+void
-+set_func_read_only (name)
-+     const char *name;
-+{
-+  SHELL_VAR *entry;
-+
-+  entry = find_function (name);
-+  if (entry)
-+    VSETATTR (entry, att_readonly);
-+}
-+
-+/* Make the variable associated with NAME be auto-exported.
-+   If NAME does not exist yet, create it. */
-+void
-+set_var_auto_export (name)
-+     char *name;
-+{
-+  SHELL_VAR *entry;
-+
-+  FIND_OR_MAKE_VARIABLE (name, entry);
-+  set_auto_export (entry);
-+}
-+
-+/* Make the function associated with NAME be auto-exported. */
-+void
-+set_func_auto_export (name)
-+     const char *name;
-+{
-+  SHELL_VAR *entry;
-+
-+  entry = find_function (name);
-+  if (entry)
-+    set_auto_export (entry);
-+}
-+#endif
-+
-+/* **************************************************************** */
-+/*								    */
-+/*		     Creating lists of variables		    */
-+/*								    */
-+/* **************************************************************** */
-+
-+static VARLIST *
-+vlist_alloc (nentries)
-+     int nentries;
-+{
-+  VARLIST  *vlist;
-+
-+  vlist = (VARLIST *)xmalloc (sizeof (VARLIST));
-+  vlist->list = (SHELL_VAR **)xmalloc ((nentries + 1) * sizeof (SHELL_VAR *));
-+  vlist->list_size = nentries;
-+  vlist->list_len = 0;
-+  vlist->list[0] = (SHELL_VAR *)NULL;
-+
-+  return vlist;
-+}
-+
-+static VARLIST *
-+vlist_realloc (vlist, n)
-+     VARLIST *vlist;
-+     int n;
-+{
-+  if (vlist == 0)
-+    return (vlist = vlist_alloc (n));
-+  if (n > vlist->list_size)
-+    {
-+      vlist->list_size = n;
-+      vlist->list = (SHELL_VAR **)xrealloc (vlist->list, (vlist->list_size + 1) * sizeof (SHELL_VAR *));
-+    }
-+  return vlist;
-+}
-+
-+static void
-+vlist_add (vlist, var, flags)
-+     VARLIST *vlist;
-+     SHELL_VAR *var;
-+     int flags;
-+{
-+  register int i;
-+
-+  for (i = 0; i < vlist->list_len; i++)
-+    if (STREQ (var->name, vlist->list[i]->name))
-+      break;
-+  if (i < vlist->list_len)
-+    return;
-+
-+  if (i >= vlist->list_size)
-+    vlist = vlist_realloc (vlist, vlist->list_size + 16);
-+
-+  vlist->list[vlist->list_len++] = var;
-+  vlist->list[vlist->list_len] = (SHELL_VAR *)NULL;
-+}
-+
-+/* Map FUNCTION over the variables in VAR_HASH_TABLE.  Return an array of the
-+   variables for which FUNCTION returns a non-zero value.  A NULL value
-+   for FUNCTION means to use all variables. */
-+SHELL_VAR **
-+map_over (function, vc)
-+     sh_var_map_func_t *function;
-+     VAR_CONTEXT *vc;
-+{
-+  VAR_CONTEXT *v;
-+  VARLIST *vlist;
-+  SHELL_VAR **ret;
-+  int nentries;
-+
-+  for (nentries = 0, v = vc; v; v = v->down)
-+    nentries += HASH_ENTRIES (v->table);
-+
-+  if (nentries == 0)
-+    return (SHELL_VAR **)NULL;
-+
-+  vlist = vlist_alloc (nentries);
-+
-+  for (v = vc; v; v = v->down)
-+    flatten (v->table, function, vlist, 0);
-+
-+  ret = vlist->list;
-+  free (vlist);
-+  return ret;
-+}
-+
-+SHELL_VAR **
-+map_over_funcs (function)
-+     sh_var_map_func_t *function;
-+{
-+  VARLIST *vlist;
-+  SHELL_VAR **ret;
-+
-+  if (shell_functions == 0 || HASH_ENTRIES (shell_functions) == 0)
-+    return ((SHELL_VAR **)NULL);
-+
-+  vlist = vlist_alloc (HASH_ENTRIES (shell_functions));
-+
-+  flatten (shell_functions, function, vlist, 0);
-+
-+  ret = vlist->list;
-+  free (vlist);
-+  return ret;
-+}
-+
-+/* Flatten VAR_HASH_TABLE, applying FUNC to each member and adding those
-+   elements for which FUNC succeeds to VLIST->list.  FLAGS is reserved
-+   for future use.  Only unique names are added to VLIST.  If FUNC is
-+   NULL, each variable in VAR_HASH_TABLE is added to VLIST.  If VLIST is
-+   NULL, FUNC is applied to each SHELL_VAR in VAR_HASH_TABLE.  If VLIST
-+   and FUNC are both NULL, nothing happens. */
-+static void
-+flatten (var_hash_table, func, vlist, flags)
-+     HASH_TABLE *var_hash_table;
-+     sh_var_map_func_t *func;
-+     VARLIST *vlist;
-+     int flags;
-+{
-+  register int i;
-+  register BUCKET_CONTENTS *tlist;
-+  int r;
-+  SHELL_VAR *var;
-+
-+  if (var_hash_table == 0 || (HASH_ENTRIES (var_hash_table) == 0) || (vlist == 0 && func == 0))
-+    return;
-+
-+  for (i = 0; i < var_hash_table->nbuckets; i++)
-+    {
-+      for (tlist = hash_items (i, var_hash_table); tlist; tlist = tlist->next)
-+	{
-+	  var = (SHELL_VAR *)tlist->data;
-+
-+	  r = func ? (*func) (var) : 1;
-+	  if (r && vlist)
-+	    vlist_add (vlist, var, flags);
-+	}
-+    }
-+}
-+
-+void
-+sort_variables (array)
-+     SHELL_VAR **array;
-+{
-+  qsort (array, strvec_len ((char **)array), sizeof (SHELL_VAR *), (QSFUNC *)qsort_var_comp);
-+}
-+
-+static int
-+qsort_var_comp (var1, var2)
-+     SHELL_VAR **var1, **var2;
-+{
-+  int result;
-+
-+  if ((result = (*var1)->name[0] - (*var2)->name[0]) == 0)
-+    result = strcmp ((*var1)->name, (*var2)->name);
-+
-+  return (result);
-+}
-+
-+/* Apply FUNC to each variable in SHELL_VARIABLES, adding each one for
-+   which FUNC succeeds to an array of SHELL_VAR *s.  Returns the array. */
-+static SHELL_VAR **
-+vapply (func)
-+     sh_var_map_func_t *func;
-+{
-+  SHELL_VAR **list;
-+
-+  list = map_over (func, shell_variables);
-+  if (list /* && posixly_correct */)
-+    sort_variables (list);
-+  return (list);
-+}
-+
-+/* Apply FUNC to each variable in SHELL_FUNCTIONS, adding each one for
-+   which FUNC succeeds to an array of SHELL_VAR *s.  Returns the array. */
-+static SHELL_VAR **
-+fapply (func)
-+     sh_var_map_func_t *func;
-+{
-+  SHELL_VAR **list;
-+
-+  list = map_over_funcs (func);
-+  if (list /* && posixly_correct */)
-+    sort_variables (list);
-+  return (list);
-+}
-+
-+/* Create a NULL terminated array of all the shell variables. */
-+SHELL_VAR **
-+all_shell_variables ()
-+{
-+  return (vapply ((sh_var_map_func_t *)NULL));
-+}
-+
-+/* Create a NULL terminated array of all the shell functions. */
-+SHELL_VAR **
-+all_shell_functions ()
-+{
-+  return (fapply ((sh_var_map_func_t *)NULL));
-+}
-+
-+static int
-+visible_var (var)
-+     SHELL_VAR *var;
-+{
-+  return (invisible_p (var) == 0);
-+}
-+
-+SHELL_VAR **
-+all_visible_functions ()
-+{
-+  return (fapply (visible_var));
-+}
-+
-+SHELL_VAR **
-+all_visible_variables ()
-+{
-+  return (vapply (visible_var));
-+}
-+
-+/* Return non-zero if the variable VAR is visible and exported.  Array
-+   variables cannot be exported. */
-+static int
-+visible_and_exported (var)
-+     SHELL_VAR *var;
-+{
-+  return (invisible_p (var) == 0 && exported_p (var));
-+}
-+
-+/* Candidate variables for the export environment are either valid variables
-+   with the export attribute or invalid variables inherited from the initial
-+   environment and simply passed through. */
-+static int
-+export_environment_candidate (var)
-+     SHELL_VAR *var;
-+{
-+  return (exported_p (var) && (invisible_p (var) == 0 || imported_p (var)));
-+}
-+
-+/* Return non-zero if VAR is a local variable in the current context and
-+   is exported. */
-+static int
-+local_and_exported (var)
-+     SHELL_VAR *var;
-+{
-+  return (invisible_p (var) == 0 && local_p (var) && var->context == variable_context && exported_p (var));
-+}
-+
-+SHELL_VAR **
-+all_exported_variables ()
-+{
-+  return (vapply (visible_and_exported));
-+}
-+
-+SHELL_VAR **
-+local_exported_variables ()
-+{
-+  return (vapply (local_and_exported));
-+}
-+
-+static int
-+variable_in_context (var)
-+     SHELL_VAR *var;
-+{
-+  return (invisible_p (var) == 0 && local_p (var) && var->context == variable_context);
-+}
-+
-+SHELL_VAR **
-+all_local_variables ()
-+{
-+  VARLIST *vlist;
-+  SHELL_VAR **ret;
-+  VAR_CONTEXT *vc;
-+
-+  vc = shell_variables;
-+  for (vc = shell_variables; vc; vc = vc->down)
-+    if (vc_isfuncenv (vc) && vc->scope == variable_context)
-+      break;
-+
-+  if (vc == 0)
-+    {
-+      internal_error (_("all_local_variables: no function context at current scope"));
-+      return (SHELL_VAR **)NULL;
-+    }
-+  if (vc->table == 0 || HASH_ENTRIES (vc->table) == 0 || vc_haslocals (vc) == 0)
-+    return (SHELL_VAR **)NULL;
-+    
-+  vlist = vlist_alloc (HASH_ENTRIES (vc->table));
-+
-+  flatten (vc->table, variable_in_context, vlist, 0);
-+
-+  ret = vlist->list;
-+  free (vlist);
-+  if (ret)
-+    sort_variables (ret);
-+  return ret;
-+}
-+
-+#if defined (ARRAY_VARS)
-+/* Return non-zero if the variable VAR is visible and an array. */
-+static int
-+visible_array_vars (var)
-+     SHELL_VAR *var;
-+{
-+  return (invisible_p (var) == 0 && array_p (var));
-+}
-+
-+SHELL_VAR **
-+all_array_variables ()
-+{
-+  return (vapply (visible_array_vars));
-+}
-+#endif /* ARRAY_VARS */
-+
-+char **
-+all_variables_matching_prefix (prefix)
-+     const char *prefix;
-+{
-+  SHELL_VAR **varlist;
-+  char **rlist;
-+  int vind, rind, plen;
-+
-+  plen = STRLEN (prefix);
-+  varlist = all_visible_variables ();
-+  for (vind = 0; varlist && varlist[vind]; vind++)
-+    ;
-+  if (varlist == 0 || vind == 0)
-+    return ((char **)NULL);
-+  rlist = strvec_create (vind + 1);
-+  for (vind = rind = 0; varlist[vind]; vind++)
-+    {
-+      if (plen == 0 || STREQN (prefix, varlist[vind]->name, plen))
-+	rlist[rind++] = savestring (varlist[vind]->name);
-+    }
-+  rlist[rind] = (char *)0;
-+  free (varlist);
-+
-+  return rlist;
-+}
-+
-+/* **************************************************************** */
-+/*								    */
-+/*		 Managing temporary variable scopes		    */
-+/*								    */
-+/* **************************************************************** */
-+
-+/* Make variable NAME have VALUE in the temporary environment. */
-+static SHELL_VAR *
-+bind_tempenv_variable (name, value)
-+     const char *name;
-+     char *value;
-+{
-+  SHELL_VAR *var;
-+
-+  var = temporary_env ? hash_lookup (name, temporary_env) : (SHELL_VAR *)NULL;
-+
-+  if (var)
-+    {
-+      FREE (value_cell (var));
-+      var_setvalue (var, savestring (value));
-+      INVALIDATE_EXPORTSTR (var);
-+    }
-+
-+  return (var);
-+}
-+
-+/* Find a variable in the temporary environment that is named NAME.
-+   Return the SHELL_VAR *, or NULL if not found. */
-+SHELL_VAR *
-+find_tempenv_variable (name)
-+     const char *name;
-+{
-+  return (temporary_env ? hash_lookup (name, temporary_env) : (SHELL_VAR *)NULL);
-+}
-+
-+char **tempvar_list;
-+int tvlist_ind;
-+
-+/* Push the variable described by (SHELL_VAR *)DATA down to the next
-+   variable context from the temporary environment. */
-+static void
-+push_temp_var (data)
-+     PTR_T data;
-+{
-+  SHELL_VAR *var, *v;
-+  HASH_TABLE *binding_table;
-+
-+  var = (SHELL_VAR *)data;
-+
-+  binding_table = shell_variables->table;
-+  if (binding_table == 0)
-+    {
-+      if (shell_variables == global_variables)
-+	/* shouldn't happen */
-+	binding_table = shell_variables->table = global_variables->table = hash_create (0);
-+      else
-+	binding_table = shell_variables->table = hash_create (TEMPENV_HASH_BUCKETS);
-+    }
-+
-+  v = bind_variable_internal (var->name, value_cell (var), binding_table, 0, 0);
-+
-+  /* XXX - should we set the context here?  It shouldn't matter because of how
-+     assign_in_env works, but might want to check. */
-+  if (binding_table == global_variables->table)		/* XXX */
-+    var->attributes &= ~(att_tempvar|att_propagate);
-+  else
-+    {
-+      var->attributes |= att_propagate;
-+      if  (binding_table == shell_variables->table)
-+	shell_variables->flags |= VC_HASTMPVAR;
-+    }
-+  v->attributes |= var->attributes;
-+
-+  if (find_special_var (var->name) >= 0)
-+    tempvar_list[tvlist_ind++] = savestring (var->name);
-+
-+  dispose_variable (var);
-+}
-+
-+static void
-+propagate_temp_var (data)
-+     PTR_T data;
-+{
-+  SHELL_VAR *var;
-+
-+  var = (SHELL_VAR *)data;
-+  if (tempvar_p (var) && (var->attributes & att_propagate))
-+    push_temp_var (data);
-+  else
-+    {
-+      if (find_special_var (var->name) >= 0)
-+	tempvar_list[tvlist_ind++] = savestring (var->name);
-+      dispose_variable (var);
-+    }
-+}
-+
-+/* Free the storage used in the hash table for temporary
-+   environment variables.  PUSHF is a function to be called
-+   to free each hash table entry.  It takes care of pushing variables
-+   to previous scopes if appropriate.  PUSHF stores names of variables
-+   that require special handling (e.g., IFS) on tempvar_list, so this
-+   function can call stupidly_hack_special_variables on all the
-+   variables in the list when the temporary hash table is destroyed. */
-+static void
-+dispose_temporary_env (pushf)
-+     sh_free_func_t *pushf;
-+{
-+  int i;
-+
-+  tempvar_list = strvec_create (HASH_ENTRIES (temporary_env) + 1);
-+  tempvar_list[tvlist_ind = 0] = 0;
-+    
-+  hash_flush (temporary_env, pushf);
-+  hash_dispose (temporary_env);
-+  temporary_env = (HASH_TABLE *)NULL;
-+
-+  tempvar_list[tvlist_ind] = 0;
-+
-+  array_needs_making = 1;
-+
-+#if 0
-+  sv_ifs ("IFS");		/* XXX here for now -- check setifs in assign_in_env */  
-+#endif
-+  for (i = 0; i < tvlist_ind; i++)
-+    stupidly_hack_special_variables (tempvar_list[i]);
-+
-+  strvec_dispose (tempvar_list);
-+  tempvar_list = 0;
-+  tvlist_ind = 0;
-+}
-+
-+void
-+dispose_used_env_vars ()
-+{
-+  if (temporary_env)
-+    {
-+      dispose_temporary_env (propagate_temp_var);
-+      maybe_make_export_env ();
-+    }
-+}
-+
-+/* Take all of the shell variables in the temporary environment HASH_TABLE
-+   and make shell variables from them at the current variable context. */
-+void
-+merge_temporary_env ()
-+{
-+  if (temporary_env)
-+    dispose_temporary_env (push_temp_var);
-+}
-+
-+/* **************************************************************** */
-+/*								    */
-+/*	     Creating and manipulating the environment		    */
-+/*								    */
-+/* **************************************************************** */
-+
-+static inline char *
-+mk_env_string (name, value, isfunc)
-+     const char *name, *value;
-+     int isfunc;
-+{
-+  size_t name_len, value_len;
-+  char	*p, *q;
-+
-+  name_len = strlen (name);
-+  value_len = STRLEN (value);
-+
-+  /* If we are exporting a shell function, construct the encoded function
-+     name. */
-+  if (isfunc && value)
-+    {
-+      p = (char *)xmalloc (BASHFUNC_PREFLEN + name_len + BASHFUNC_SUFFLEN + value_len + 2);
-+      q = p;
-+      memcpy (q, BASHFUNC_PREFIX, BASHFUNC_PREFLEN);
-+      q += BASHFUNC_PREFLEN;
-+      memcpy (q, name, name_len);
-+      q += name_len;
-+      memcpy (q, BASHFUNC_SUFFIX, BASHFUNC_SUFFLEN);
-+      q += BASHFUNC_SUFFLEN;
-+    }
-+  else
-+    {
-+      p = (char *)xmalloc (2 + name_len + value_len);
-+      memcpy (p, name, name_len);
-+      q = p + name_len;
-+    }
-+
-+  q[0] = '=';
-+  if (value && *value)
-+    memcpy (q + 1, value, value_len + 1);
-+  else
-+    q[1] = '\0';
-+
-+  return (p);
-+}
-+
-+#ifdef DEBUG
-+/* Debugging */
-+static int
-+valid_exportstr (v)
-+     SHELL_VAR *v;
-+{
-+  char *s;
-+
-+  s = v->exportstr;
-+  if (s == 0)
-+    {
-+      internal_error (_("%s has null exportstr"), v->name);
-+      return (0);
-+    }
-+  if (legal_variable_starter ((unsigned char)*s) == 0)
-+    {
-+      internal_error (_("invalid character %d in exportstr for %s"), *s, v->name);
-+      return (0);
-+    }
-+  for (s = v->exportstr + 1; s && *s; s++)
-+    {
-+      if (*s == '=')
-+	break;
-+      if (legal_variable_char ((unsigned char)*s) == 0)
-+	{
-+	  internal_error (_("invalid character %d in exportstr for %s"), *s, v->name);
-+	  return (0);
-+	}
-+    }
-+  if (*s != '=')
-+    {
-+      internal_error (_("no `=' in exportstr for %s"), v->name);
-+      return (0);
-+    }
-+  return (1);
-+}
-+#endif
-+
-+static char **
-+make_env_array_from_var_list (vars)
-+     SHELL_VAR **vars;
-+{
-+  register int i, list_index;
-+  register SHELL_VAR *var;
-+  char **list, *value;
-+
-+  list = strvec_create ((1 + strvec_len ((char **)vars)));
-+
-+#define USE_EXPORTSTR (value == var->exportstr)
-+
-+  for (i = 0, list_index = 0; var = vars[i]; i++)
-+    {
-+#if defined (__CYGWIN__)
-+      /* We don't use the exportstr stuff on Cygwin at all. */
-+      INVALIDATE_EXPORTSTR (var);
-+#endif
-+      if (var->exportstr)
-+	value = var->exportstr;
-+      else if (function_p (var))
-+	value = named_function_string ((char *)NULL, function_cell (var), 0);
-+#if defined (ARRAY_VARS)
-+      else if (array_p (var))
-+#  if ARRAY_EXPORT
-+	value = array_to_assignment_string (array_cell (var));
-+#  else
-+	continue;	/* XXX array vars cannot yet be exported */
-+#  endif /* ARRAY_EXPORT */
-+      else if (assoc_p (var))
-+#  if 0
-+	value = assoc_to_assignment_string (assoc_cell (var));
-+#  else
-+	continue;	/* XXX associative array vars cannot yet be exported */
-+#  endif
-+#endif
-+      else
-+	value = value_cell (var);
-+
-+      if (value)
-+	{
-+	  /* Gee, I'd like to get away with not using savestring() if we're
-+	     using the cached exportstr... */
-+	  list[list_index] = USE_EXPORTSTR ? savestring (value)
-+					   : mk_env_string (var->name, value, function_p (var));
-+
-+	  if (USE_EXPORTSTR == 0)
-+	    SAVE_EXPORTSTR (var, list[list_index]);
-+
-+	  list_index++;
-+#undef USE_EXPORTSTR
-+
-+#if 0	/* not yet */
-+#if defined (ARRAY_VARS)
-+	  if (array_p (var) || assoc_p (var))
-+	    free (value);
-+#endif
-+#endif
-+	}
-+    }
-+
-+  list[list_index] = (char *)NULL;
-+  return (list);
-+}
-+
-+/* Make an array of assignment statements from the hash table
-+   HASHED_VARS which contains SHELL_VARs.  Only visible, exported
-+   variables are eligible. */
-+static char **
-+make_var_export_array (vcxt)
-+     VAR_CONTEXT *vcxt;
-+{
-+  char **list;
-+  SHELL_VAR **vars;
-+
-+#if 0
-+  vars = map_over (visible_and_exported, vcxt);
-+#else
-+  vars = map_over (export_environment_candidate, vcxt);
-+#endif
-+
-+  if (vars == 0)
-+    return (char **)NULL;
-+
-+  list = make_env_array_from_var_list (vars);
-+
-+  free (vars);
-+  return (list);
-+}
-+
-+static char **
-+make_func_export_array ()
-+{
-+  char **list;
-+  SHELL_VAR **vars;
-+
-+  vars = map_over_funcs (visible_and_exported);
-+  if (vars == 0)
-+    return (char **)NULL;
-+
-+  list = make_env_array_from_var_list (vars);
-+
-+  free (vars);
-+  return (list);
-+}
-+
-+/* Add ENVSTR to the end of the exported environment, EXPORT_ENV. */
-+#define add_to_export_env(envstr,do_alloc) \
-+do \
-+  { \
-+    if (export_env_index >= (export_env_size - 1)) \
-+      { \
-+	export_env_size += 16; \
-+	export_env = strvec_resize (export_env, export_env_size); \
-+	environ = export_env; \
-+      } \
-+    export_env[export_env_index++] = (do_alloc) ? savestring (envstr) : envstr; \
-+    export_env[export_env_index] = (char *)NULL; \
-+  } while (0)
-+
-+/* Add ASSIGN to EXPORT_ENV, or supercede a previous assignment in the
-+   array with the same left-hand side.  Return the new EXPORT_ENV. */
-+char **
-+add_or_supercede_exported_var (assign, do_alloc)
-+     char *assign;
-+     int do_alloc;
-+{
-+  register int i;
-+  int equal_offset;
-+
-+  equal_offset = assignment (assign, 0);
-+  if (equal_offset == 0)
-+    return (export_env);
-+
-+  /* If this is a function, then only supersede the function definition.
-+     We do this by including the `=() {' in the comparison, like
-+     initialize_shell_variables does. */
-+  if (assign[equal_offset + 1] == '(' &&
-+     strncmp (assign + equal_offset + 2, ") {", 3) == 0)		/* } */
-+    equal_offset += 4;
-+
-+  for (i = 0; i < export_env_index; i++)
-+    {
-+      if (STREQN (assign, export_env[i], equal_offset + 1))
-+	{
-+	  free (export_env[i]);
-+	  export_env[i] = do_alloc ? savestring (assign) : assign;
-+	  return (export_env);
-+	}
-+    }
-+  add_to_export_env (assign, do_alloc);
-+  return (export_env);
-+}
-+
-+static void
-+add_temp_array_to_env (temp_array, do_alloc, do_supercede)
-+     char **temp_array;
-+     int do_alloc, do_supercede;
-+{
-+  register int i;
-+
-+  if (temp_array == 0)
-+    return;
-+
-+  for (i = 0; temp_array[i]; i++)
-+    {
-+      if (do_supercede)
-+	export_env = add_or_supercede_exported_var (temp_array[i], do_alloc);
-+      else
-+	add_to_export_env (temp_array[i], do_alloc);
-+    }
-+
-+  free (temp_array);
-+}
-+
-+/* Make the environment array for the command about to be executed, if the
-+   array needs making.  Otherwise, do nothing.  If a shell action could
-+   change the array that commands receive for their environment, then the
-+   code should `array_needs_making++'.
-+
-+   The order to add to the array is:
-+   	temporary_env
-+   	list of var contexts whose head is shell_variables
-+  	shell_functions
-+
-+  This is the shell variable lookup order.  We add only new variable
-+  names at each step, which allows local variables and variables in
-+  the temporary environments to shadow variables in the global (or
-+  any previous) scope.
-+*/
-+
-+static int
-+n_shell_variables ()
-+{
-+  VAR_CONTEXT *vc;
-+  int n;
-+
-+  for (n = 0, vc = shell_variables; vc; vc = vc->down)
-+    n += HASH_ENTRIES (vc->table);
-+  return n;
-+}
-+
-+int
-+chkexport (name)
-+     char *name;
-+{
-+  SHELL_VAR *v;
-+
-+  v = find_variable (name);
-+  if (v && exported_p (v))
-+    {
-+      array_needs_making = 1;
-+      maybe_make_export_env ();
-+      return 1;
-+    }
-+  return 0;
-+}
-+
-+void
-+maybe_make_export_env ()
-+{
-+  register char **temp_array;
-+  int new_size;
-+  VAR_CONTEXT *tcxt;
-+
-+  if (array_needs_making)
-+    {
-+      if (export_env)
-+	strvec_flush (export_env);
-+
-+      /* Make a guess based on how many shell variables and functions we
-+	 have.  Since there will always be array variables, and array
-+	 variables are not (yet) exported, this will always be big enough
-+	 for the exported variables and functions. */
-+      new_size = n_shell_variables () + HASH_ENTRIES (shell_functions) + 1 +
-+		 HASH_ENTRIES (temporary_env);
-+      if (new_size > export_env_size)
-+	{
-+	  export_env_size = new_size;
-+	  export_env = strvec_resize (export_env, export_env_size);
-+	  environ = export_env;
-+	}
-+      export_env[export_env_index = 0] = (char *)NULL;
-+
-+      /* Make a dummy variable context from the temporary_env, stick it on
-+	 the front of shell_variables, call make_var_export_array on the
-+	 whole thing to flatten it, and convert the list of SHELL_VAR *s
-+	 to the form needed by the environment. */
-+      if (temporary_env)
-+	{
-+	  tcxt = new_var_context ((char *)NULL, 0);
-+	  tcxt->table = temporary_env;
-+	  tcxt->down = shell_variables;
-+	}
-+      else
-+	tcxt = shell_variables;
-+      
-+      temp_array = make_var_export_array (tcxt);
-+      if (temp_array)
-+	add_temp_array_to_env (temp_array, 0, 0);
-+
-+      if (tcxt != shell_variables)
-+	free (tcxt);
-+
-+#if defined (RESTRICTED_SHELL)
-+      /* Restricted shells may not export shell functions. */
-+      temp_array = restricted ? (char **)0 : make_func_export_array ();
-+#else
-+      temp_array = make_func_export_array ();
-+#endif
-+      if (temp_array)
-+	add_temp_array_to_env (temp_array, 0, 0);
-+
-+      array_needs_making = 0;
-+    }
-+}
-+
-+/* This is an efficiency hack.  PWD and OLDPWD are auto-exported, so
-+   we will need to remake the exported environment every time we
-+   change directories.  `_' is always put into the environment for
-+   every external command, so without special treatment it will always
-+   cause the environment to be remade.
-+
-+   If there is no other reason to make the exported environment, we can
-+   just update the variables in place and mark the exported environment
-+   as no longer needing a remake. */
-+void
-+update_export_env_inplace (env_prefix, preflen, value)
-+     char *env_prefix;
-+     int preflen;
-+     char *value;
-+{
-+  char *evar;
-+
-+  evar = (char *)xmalloc (STRLEN (value) + preflen + 1);
-+  strcpy (evar, env_prefix);
-+  if (value)
-+    strcpy (evar + preflen, value);
-+  export_env = add_or_supercede_exported_var (evar, 0);
-+}
-+
-+/* We always put _ in the environment as the name of this command. */
-+void
-+put_command_name_into_env (command_name)
-+     char *command_name;
-+{
-+  update_export_env_inplace ("_=", 2, command_name);
-+}
-+
-+/* **************************************************************** */
-+/*								    */
-+/*		      Managing variable contexts		    */
-+/*								    */
-+/* **************************************************************** */
-+
-+/* Allocate and return a new variable context with NAME and FLAGS.
-+   NAME can be NULL. */
-+
-+VAR_CONTEXT *
-+new_var_context (name, flags)
-+     char *name;
-+     int flags;
-+{
-+  VAR_CONTEXT *vc;
-+
-+  vc = (VAR_CONTEXT *)xmalloc (sizeof (VAR_CONTEXT));
-+  vc->name = name ? savestring (name) : (char *)NULL;
-+  vc->scope = variable_context;
-+  vc->flags = flags;
-+
-+  vc->up = vc->down = (VAR_CONTEXT *)NULL;
-+  vc->table = (HASH_TABLE *)NULL;
-+
-+  return vc;
-+}
-+
-+/* Free a variable context and its data, including the hash table.  Dispose
-+   all of the variables. */
-+void
-+dispose_var_context (vc)
-+     VAR_CONTEXT *vc;
-+{
-+  FREE (vc->name);
-+
-+  if (vc->table)
-+    {
-+      delete_all_variables (vc->table);
-+      hash_dispose (vc->table);
-+    }
-+
-+  free (vc);
-+}
-+
-+/* Set VAR's scope level to the current variable context. */
-+static int
-+set_context (var)
-+     SHELL_VAR *var;
-+{
-+  return (var->context = variable_context);
-+}
-+
-+/* Make a new variable context with NAME and FLAGS and a HASH_TABLE of
-+   temporary variables, and push it onto shell_variables.  This is
-+   for shell functions. */
-+VAR_CONTEXT *
-+push_var_context (name, flags, tempvars)
-+     char *name;
-+     int flags;
-+     HASH_TABLE *tempvars;
-+{
-+  VAR_CONTEXT *vc;
-+
-+  vc = new_var_context (name, flags);
-+  vc->table = tempvars;
-+  if (tempvars)
-+    {
-+      /* Have to do this because the temp environment was created before
-+	 variable_context was incremented. */
-+      flatten (tempvars, set_context, (VARLIST *)NULL, 0);
-+      vc->flags |= VC_HASTMPVAR;
-+    }
-+  vc->down = shell_variables;
-+  shell_variables->up = vc;
-+
-+  return (shell_variables = vc);
-+}
-+
-+static void
-+push_func_var (data)
-+     PTR_T data;
-+{
-+  SHELL_VAR *var, *v;
-+
-+  var = (SHELL_VAR *)data;
-+
-+  if (tempvar_p (var) && (posixly_correct || (var->attributes & att_propagate)))
-+    {
-+      /* Make sure we have a hash table to store the variable in while it is
-+	 being propagated down to the global variables table.  Create one if
-+	 we have to */
-+      if ((vc_isfuncenv (shell_variables) || vc_istempenv (shell_variables)) && shell_variables->table == 0)
-+	shell_variables->table = hash_create (0);
-+      /* XXX - should we set v->context here? */
-+      v = bind_variable_internal (var->name, value_cell (var), shell_variables->table, 0, 0);
-+      if (shell_variables == global_variables)
-+	var->attributes &= ~(att_tempvar|att_propagate);
-+      else
-+	shell_variables->flags |= VC_HASTMPVAR;
-+      v->attributes |= var->attributes;
-+    }
-+  else
-+    stupidly_hack_special_variables (var->name);	/* XXX */
-+
-+  dispose_variable (var);
-+}
-+
-+/* Pop the top context off of VCXT and dispose of it, returning the rest of
-+   the stack. */
-+void
-+pop_var_context ()
-+{
-+  VAR_CONTEXT *ret, *vcxt;
-+
-+  vcxt = shell_variables;
-+  if (vc_isfuncenv (vcxt) == 0)
-+    {
-+      internal_error (_("pop_var_context: head of shell_variables not a function context"));
-+      return;
-+    }
-+
-+  if (ret = vcxt->down)
-+    {
-+      ret->up = (VAR_CONTEXT *)NULL;
-+      shell_variables = ret;
-+      if (vcxt->table)
-+	hash_flush (vcxt->table, push_func_var);
-+      dispose_var_context (vcxt);
-+    }
-+  else
-+    internal_error (_("pop_var_context: no global_variables context"));
-+}
-+
-+/* Delete the HASH_TABLEs for all variable contexts beginning at VCXT, and
-+   all of the VAR_CONTEXTs except GLOBAL_VARIABLES. */
-+void
-+delete_all_contexts (vcxt)
-+     VAR_CONTEXT *vcxt;
-+{
-+  VAR_CONTEXT *v, *t;
-+
-+  for (v = vcxt; v != global_variables; v = t)
-+    {
-+      t = v->down;
-+      dispose_var_context (v);
-+    }    
-+
-+  delete_all_variables (global_variables->table);
-+  shell_variables = global_variables;
-+}
-+
-+/* **************************************************************** */
-+/*								    */
-+/*	   Pushing and Popping temporary variable scopes	    */
-+/*								    */
-+/* **************************************************************** */
-+
-+VAR_CONTEXT *
-+push_scope (flags, tmpvars)
-+     int flags;
-+     HASH_TABLE *tmpvars;
-+{
-+  return (push_var_context ((char *)NULL, flags, tmpvars));
-+}
-+
-+static void
-+push_exported_var (data)
-+     PTR_T data;
-+{
-+  SHELL_VAR *var, *v;
-+
-+  var = (SHELL_VAR *)data;
-+
-+  /* If a temp var had its export attribute set, or it's marked to be
-+     propagated, bind it in the previous scope before disposing it. */
-+  /* XXX - This isn't exactly right, because all tempenv variables have the
-+    export attribute set. */
-+#if 0
-+  if (exported_p (var) || (var->attributes & att_propagate))
-+#else
-+  if (tempvar_p (var) && exported_p (var) && (var->attributes & att_propagate))
-+#endif
-+    {
-+      var->attributes &= ~att_tempvar;		/* XXX */
-+      v = bind_variable_internal (var->name, value_cell (var), shell_variables->table, 0, 0);
-+      if (shell_variables == global_variables)
-+	var->attributes &= ~att_propagate;
-+      v->attributes |= var->attributes;
-+    }
-+  else
-+    stupidly_hack_special_variables (var->name);	/* XXX */
-+
-+  dispose_variable (var);
-+}
-+
-+void
-+pop_scope (is_special)
-+     int is_special;
-+{
-+  VAR_CONTEXT *vcxt, *ret;
-+
-+  vcxt = shell_variables;
-+  if (vc_istempscope (vcxt) == 0)
-+    {
-+      internal_error (_("pop_scope: head of shell_variables not a temporary environment scope"));
-+      return;
-+    }
-+
-+  ret = vcxt->down;
-+  if (ret)
-+    ret->up = (VAR_CONTEXT *)NULL;
-+
-+  shell_variables = ret;
-+
-+  /* Now we can take care of merging variables in VCXT into set of scopes
-+     whose head is RET (shell_variables). */
-+  FREE (vcxt->name);
-+  if (vcxt->table)
-+    {
-+      if (is_special)
-+	hash_flush (vcxt->table, push_func_var);
-+      else
-+	hash_flush (vcxt->table, push_exported_var);
-+      hash_dispose (vcxt->table);
-+    }
-+  free (vcxt);
-+
-+  sv_ifs ("IFS");	/* XXX here for now */
-+}
-+
-+/* **************************************************************** */
-+/*								    */
-+/*		 Pushing and Popping function contexts		    */
-+/*								    */
-+/* **************************************************************** */
-+
-+static WORD_LIST **dollar_arg_stack = (WORD_LIST **)NULL;
-+static int dollar_arg_stack_slots;
-+static int dollar_arg_stack_index;
-+
-+/* XXX - we might want to consider pushing and popping the `getopts' state
-+   when we modify the positional parameters. */
-+void
-+push_context (name, is_subshell, tempvars)
-+     char *name;	/* function name */
-+     int is_subshell;
-+     HASH_TABLE *tempvars;
-+{
-+  if (is_subshell == 0)
-+    push_dollar_vars ();
-+  variable_context++;
-+  push_var_context (name, VC_FUNCENV, tempvars);
-+}
-+
-+/* Only called when subshell == 0, so we don't need to check, and can
-+   unconditionally pop the dollar vars off the stack. */
-+void
-+pop_context ()
-+{
-+  pop_dollar_vars ();
-+  variable_context--;
-+  pop_var_context ();
-+
-+  sv_ifs ("IFS");		/* XXX here for now */
-+}
-+
-+/* Save the existing positional parameters on a stack. */
-+void
-+push_dollar_vars ()
-+{
-+  if (dollar_arg_stack_index + 2 > dollar_arg_stack_slots)
-+    {
-+      dollar_arg_stack = (WORD_LIST **)
-+	xrealloc (dollar_arg_stack, (dollar_arg_stack_slots += 10)
-+		  * sizeof (WORD_LIST *));
-+    }
-+  dollar_arg_stack[dollar_arg_stack_index++] = list_rest_of_args ();
-+  dollar_arg_stack[dollar_arg_stack_index] = (WORD_LIST *)NULL;
-+}
-+
-+/* Restore the positional parameters from our stack. */
-+void
-+pop_dollar_vars ()
-+{
-+  if (!dollar_arg_stack || dollar_arg_stack_index == 0)
-+    return;
-+
-+  remember_args (dollar_arg_stack[--dollar_arg_stack_index], 1);
-+  dispose_words (dollar_arg_stack[dollar_arg_stack_index]);
-+  dollar_arg_stack[dollar_arg_stack_index] = (WORD_LIST *)NULL;
-+  set_dollar_vars_unchanged ();
-+}
-+
-+void
-+dispose_saved_dollar_vars ()
-+{
-+  if (!dollar_arg_stack || dollar_arg_stack_index == 0)
-+    return;
-+
-+  dispose_words (dollar_arg_stack[dollar_arg_stack_index]);
-+  dollar_arg_stack[dollar_arg_stack_index] = (WORD_LIST *)NULL;
-+}
-+
-+/* Manipulate the special BASH_ARGV and BASH_ARGC variables. */
-+
-+void
-+push_args (list)
-+     WORD_LIST *list;
-+{
-+#if defined (ARRAY_VARS) && defined (DEBUGGER)
-+  SHELL_VAR *bash_argv_v, *bash_argc_v;
-+  ARRAY *bash_argv_a, *bash_argc_a;
-+  WORD_LIST *l;
-+  arrayind_t i;
-+  char *t;
-+
-+  GET_ARRAY_FROM_VAR ("BASH_ARGV", bash_argv_v, bash_argv_a);
-+  GET_ARRAY_FROM_VAR ("BASH_ARGC", bash_argc_v, bash_argc_a);
-+
-+  for (l = list, i = 0; l; l = l->next, i++)
-+    array_push (bash_argv_a, l->word->word);
-+
-+  t = itos (i);
-+  array_push (bash_argc_a, t);
-+  free (t);
-+#endif /* ARRAY_VARS && DEBUGGER */
-+}
-+
-+/* Remove arguments from BASH_ARGV array.  Pop top element off BASH_ARGC
-+   array and use that value as the count of elements to remove from
-+   BASH_ARGV. */
-+void
-+pop_args ()
-+{
-+#if defined (ARRAY_VARS) && defined (DEBUGGER)
-+  SHELL_VAR *bash_argv_v, *bash_argc_v;
-+  ARRAY *bash_argv_a, *bash_argc_a;
-+  ARRAY_ELEMENT *ce;
-+  intmax_t i;
-+
-+  GET_ARRAY_FROM_VAR ("BASH_ARGV", bash_argv_v, bash_argv_a);
-+  GET_ARRAY_FROM_VAR ("BASH_ARGC", bash_argc_v, bash_argc_a);
-+
-+  ce = array_shift (bash_argc_a, 1, 0);
-+  if (ce == 0 || legal_number (element_value (ce), &i) == 0)
-+    i = 0;
-+
-+  for ( ; i > 0; i--)
-+    array_pop (bash_argv_a);
-+  array_dispose_element (ce);
-+#endif /* ARRAY_VARS && DEBUGGER */
-+}
-+
-+/*************************************************
-+ *						 *
-+ *	Functions to manage special variables	 *
-+ *						 *
-+ *************************************************/
-+
-+/* Extern declarations for variables this code has to manage. */
-+extern int eof_encountered, eof_encountered_limit, ignoreeof;
-+
-+#if defined (READLINE)
-+extern int hostname_list_initialized;
-+#endif
-+
-+/* An alist of name.function for each special variable.  Most of the
-+   functions don't do much, and in fact, this would be faster with a
-+   switch statement, but by the end of this file, I am sick of switch
-+   statements. */
-+
-+#define SET_INT_VAR(name, intvar)  intvar = find_variable (name) != 0
-+
-+/* This table will be sorted with qsort() the first time it's accessed. */
-+struct name_and_function {
-+  char *name;
-+  sh_sv_func_t *function;
-+};
-+
-+static struct name_and_function special_vars[] = {
-+  { "BASH_COMPAT", sv_shcompat },
-+  { "BASH_XTRACEFD", sv_xtracefd },
-+
-+#if defined (JOB_CONTROL)
-+  { "CHILD_MAX", sv_childmax },
-+#endif
-+
-+#if defined (READLINE)
-+#  if defined (STRICT_POSIX)
-+  { "COLUMNS", sv_winsize },
-+#  endif
-+  { "COMP_WORDBREAKS", sv_comp_wordbreaks },
-+#endif
-+
-+  { "FUNCNEST", sv_funcnest },
-+
-+  { "GLOBIGNORE", sv_globignore },
-+
-+#if defined (HISTORY)
-+  { "HISTCONTROL", sv_history_control },
-+  { "HISTFILESIZE", sv_histsize },
-+  { "HISTIGNORE", sv_histignore },
-+  { "HISTSIZE", sv_histsize },
-+  { "HISTTIMEFORMAT", sv_histtimefmt },
-+#endif
-+
-+#if defined (__CYGWIN__)
-+  { "HOME", sv_home },
-+#endif
-+
-+#if defined (READLINE)
-+  { "HOSTFILE", sv_hostfile },
-+#endif
-+
-+  { "IFS", sv_ifs },
-+  { "IGNOREEOF", sv_ignoreeof },
-+
-+  { "LANG", sv_locale },
-+  { "LC_ALL", sv_locale },
-+  { "LC_COLLATE", sv_locale },
-+  { "LC_CTYPE", sv_locale },
-+  { "LC_MESSAGES", sv_locale },
-+  { "LC_NUMERIC", sv_locale },
-+  { "LC_TIME", sv_locale },
-+
-+#if defined (READLINE) && defined (STRICT_POSIX)
-+  { "LINES", sv_winsize },
-+#endif
-+
-+  { "MAIL", sv_mail },
-+  { "MAILCHECK", sv_mail },
-+  { "MAILPATH", sv_mail },
-+
-+  { "OPTERR", sv_opterr },
-+  { "OPTIND", sv_optind },
-+
-+  { "PATH", sv_path },
-+  { "POSIXLY_CORRECT", sv_strict_posix },
-+
-+#if defined (READLINE)
-+  { "TERM", sv_terminal },
-+  { "TERMCAP", sv_terminal },
-+  { "TERMINFO", sv_terminal },
-+#endif /* READLINE */
-+
-+  { "TEXTDOMAIN", sv_locale },
-+  { "TEXTDOMAINDIR", sv_locale },
-+
-+#if defined (HAVE_TZSET)
-+  { "TZ", sv_tz },
-+#endif
-+
-+#if defined (HISTORY) && defined (BANG_HISTORY)
-+  { "histchars", sv_histchars },
-+#endif /* HISTORY && BANG_HISTORY */
-+
-+  { "ignoreeof", sv_ignoreeof },
-+
-+  { (char *)0, (sh_sv_func_t *)0 }
-+};
-+
-+#define N_SPECIAL_VARS	(sizeof (special_vars) / sizeof (special_vars[0]) - 1)
-+
-+static int
-+sv_compare (sv1, sv2)
-+     struct name_and_function *sv1, *sv2;
-+{
-+  int r;
-+
-+  if ((r = sv1->name[0] - sv2->name[0]) == 0)
-+    r = strcmp (sv1->name, sv2->name);
-+  return r;
-+}
-+
-+static inline int
-+find_special_var (name)
-+     const char *name;
-+{
-+  register int i, r;
-+
-+  for (i = 0; special_vars[i].name; i++)
-+    {
-+      r = special_vars[i].name[0] - name[0];
-+      if (r == 0)
-+	r = strcmp (special_vars[i].name, name);
-+      if (r == 0)
-+	return i;
-+      else if (r > 0)
-+	/* Can't match any of rest of elements in sorted list.  Take this out
-+	   if it causes problems in certain environments. */
-+	break;
-+    }
-+  return -1;
-+}
-+
-+/* The variable in NAME has just had its state changed.  Check to see if it
-+   is one of the special ones where something special happens. */
-+void
-+stupidly_hack_special_variables (name)
-+     char *name;
-+{
-+  static int sv_sorted = 0;
-+  int i;
-+
-+  if (sv_sorted == 0)	/* shouldn't need, but it's fairly cheap. */
-+    {
-+      qsort (special_vars, N_SPECIAL_VARS, sizeof (special_vars[0]),
-+		(QSFUNC *)sv_compare);
-+      sv_sorted = 1;
-+    }
-+
-+  i = find_special_var (name);
-+  if (i != -1)
-+    (*(special_vars[i].function)) (name);
-+}
-+
-+/* Special variables that need hooks to be run when they are unset as part
-+   of shell reinitialization should have their sv_ functions run here. */
-+void
-+reinit_special_variables ()
-+{
-+#if defined (READLINE)
-+  sv_comp_wordbreaks ("COMP_WORDBREAKS");
-+#endif
-+  sv_globignore ("GLOBIGNORE");
-+  sv_opterr ("OPTERR");
-+}
-+
-+void
-+sv_ifs (name)
-+     char *name;
-+{
-+  SHELL_VAR *v;
-+
-+  v = find_variable ("IFS");
-+  setifs (v);
-+}
-+
-+/* What to do just after the PATH variable has changed. */
-+void
-+sv_path (name)
-+     char *name;
-+{
-+  /* hash -r */
-+  phash_flush ();
-+}
-+
-+/* What to do just after one of the MAILxxxx variables has changed.  NAME
-+   is the name of the variable.  This is called with NAME set to one of
-+   MAIL, MAILCHECK, or MAILPATH.  */
-+void
-+sv_mail (name)
-+     char *name;
-+{
-+  /* If the time interval for checking the files has changed, then
-+     reset the mail timer.  Otherwise, one of the pathname vars
-+     to the users mailbox has changed, so rebuild the array of
-+     filenames. */
-+  if (name[4] == 'C')  /* if (strcmp (name, "MAILCHECK") == 0) */
-+    reset_mail_timer ();
-+  else
-+    {
-+      free_mail_files ();
-+      remember_mail_dates ();
-+    }
-+}
-+
-+void
-+sv_funcnest (name)
-+     char *name;
-+{
-+  SHELL_VAR *v;
-+  intmax_t num;
-+
-+  v = find_variable (name);
-+  if (v == 0)
-+    funcnest_max = 0;
-+  else if (legal_number (value_cell (v), &num) == 0)
-+    funcnest_max = 0;
-+  else
-+    funcnest_max = num;
-+}
-+
-+/* What to do when GLOBIGNORE changes. */
-+void
-+sv_globignore (name)
-+     char *name;
-+{
-+  if (privileged_mode == 0)
-+    setup_glob_ignore (name);
-+}
-+
-+#if defined (READLINE)
-+void
-+sv_comp_wordbreaks (name)
-+     char *name;
-+{
-+  SHELL_VAR *sv;
-+
-+  sv = find_variable (name);
-+  if (sv == 0)
-+    reset_completer_word_break_chars ();
-+}
-+
-+/* What to do just after one of the TERMxxx variables has changed.
-+   If we are an interactive shell, then try to reset the terminal
-+   information in readline. */
-+void
-+sv_terminal (name)
-+     char *name;
-+{
-+  if (interactive_shell && no_line_editing == 0)
-+    rl_reset_terminal (get_string_value ("TERM"));
-+}
-+
-+void
-+sv_hostfile (name)
-+     char *name;
-+{
-+  SHELL_VAR *v;
-+
-+  v = find_variable (name);
-+  if (v == 0)
-+    clear_hostname_list ();
-+  else
-+    hostname_list_initialized = 0;
-+}
-+
-+#if defined (STRICT_POSIX)
-+/* In strict posix mode, we allow assignments to LINES and COLUMNS (and values
-+   found in the initial environment) to override the terminal size reported by
-+   the kernel. */
-+void
-+sv_winsize (name)
-+     char *name;
-+{
-+  SHELL_VAR *v;
-+  intmax_t xd;
-+  int d;
-+
-+  if (posixly_correct == 0 || interactive_shell == 0 || no_line_editing)
-+    return;
-+
-+  v = find_variable (name);
-+  if (v == 0 || var_isnull (v))
-+    rl_reset_screen_size ();
-+  else
-+    {
-+      if (legal_number (value_cell (v), &xd) == 0)
-+	return;
-+      winsize_assignment = 1;
-+      d = xd;			/* truncate */
-+      if (name[0] == 'L')	/* LINES */
-+	rl_set_screen_size (d, -1);
-+      else			/* COLUMNS */
-+	rl_set_screen_size (-1, d);
-+      winsize_assignment = 0;
-+    }
-+}
-+#endif /* STRICT_POSIX */
-+#endif /* READLINE */
-+
-+/* Update the value of HOME in the export environment so tilde expansion will
-+   work on cygwin. */
-+#if defined (__CYGWIN__)
-+sv_home (name)
-+     char *name;
-+{
-+  array_needs_making = 1;
-+  maybe_make_export_env ();
-+}
-+#endif
-+
-+#if defined (HISTORY)
-+/* What to do after the HISTSIZE or HISTFILESIZE variables change.
-+   If there is a value for this HISTSIZE (and it is numeric), then stifle
-+   the history.  Otherwise, if there is NO value for this variable,
-+   unstifle the history.  If name is HISTFILESIZE, and its value is
-+   numeric, truncate the history file to hold no more than that many
-+   lines. */
-+void
-+sv_histsize (name)
-+     char *name;
-+{
-+  char *temp;
-+  intmax_t num;
-+  int hmax;
-+
-+  temp = get_string_value (name);
-+
-+  if (temp && *temp)
-+    {
-+      if (legal_number (temp, &num))
-+	{
-+	  hmax = num;
-+	  if (hmax < 0 && name[4] == 'S')
-+	    unstifle_history ();	/* unstifle history if HISTSIZE < 0 */
-+	  else if (name[4] == 'S')
-+	    {
-+	      stifle_history (hmax);
-+	      hmax = where_history ();
-+	      if (history_lines_this_session > hmax)
-+		history_lines_this_session = hmax;
-+	    }
-+	  else if (hmax >= 0)	/* truncate HISTFILE if HISTFILESIZE >= 0 */
-+	    {
-+	      history_truncate_file (get_string_value ("HISTFILE"), hmax);
-+	      if (hmax <= history_lines_in_file)
-+		history_lines_in_file = hmax;
-+	    }
-+	}
-+    }
-+  else if (name[4] == 'S')
-+    unstifle_history ();
-+}
-+
-+/* What to do after the HISTIGNORE variable changes. */
-+void
-+sv_histignore (name)
-+     char *name;
-+{
-+  setup_history_ignore (name);
-+}
-+
-+/* What to do after the HISTCONTROL variable changes. */
-+void
-+sv_history_control (name)
-+     char *name;
-+{
-+  char *temp;
-+  char *val;
-+  int tptr;
-+
-+  history_control = 0;
-+  temp = get_string_value (name);
-+
-+  if (temp == 0 || *temp == 0)
-+    return;
-+
-+  tptr = 0;
-+  while (val = extract_colon_unit (temp, &tptr))
-+    {
-+      if (STREQ (val, "ignorespace"))
-+	history_control |= HC_IGNSPACE;
-+      else if (STREQ (val, "ignoredups"))
-+	history_control |= HC_IGNDUPS;
-+      else if (STREQ (val, "ignoreboth"))
-+	history_control |= HC_IGNBOTH;
-+      else if (STREQ (val, "erasedups"))
-+	history_control |= HC_ERASEDUPS;
-+
-+      free (val);
-+    }
-+}
-+
-+#if defined (BANG_HISTORY)
-+/* Setting/unsetting of the history expansion character. */
-+void
-+sv_histchars (name)
-+     char *name;
-+{
-+  char *temp;
-+
-+  temp = get_string_value (name);
-+  if (temp)
-+    {
-+      history_expansion_char = *temp;
-+      if (temp[0] && temp[1])
-+	{
-+	  history_subst_char = temp[1];
-+	  if (temp[2])
-+	      history_comment_char = temp[2];
-+	}
-+    }
-+  else
-+    {
-+      history_expansion_char = '!';
-+      history_subst_char = '^';
-+      history_comment_char = '#';
-+    }
-+}
-+#endif /* BANG_HISTORY */
-+
-+void
-+sv_histtimefmt (name)
-+     char *name;
-+{
-+  SHELL_VAR *v;
-+
-+  if (v = find_variable (name))
-+    {
-+      if (history_comment_char == 0)
-+	history_comment_char = '#';
-+    }
-+  history_write_timestamps = (v != 0);
-+}
-+#endif /* HISTORY */
-+
-+#if defined (HAVE_TZSET)
-+void
-+sv_tz (name)
-+     char *name;
-+{
-+  if (chkexport (name))
-+    tzset ();
-+}
-+#endif
-+
-+/* If the variable exists, then the value of it can be the number
-+   of times we actually ignore the EOF.  The default is small,
-+   (smaller than csh, anyway). */
-+void
-+sv_ignoreeof (name)
-+     char *name;
-+{
-+  SHELL_VAR *tmp_var;
-+  char *temp;
-+
-+  eof_encountered = 0;
-+
-+  tmp_var = find_variable (name);
-+  ignoreeof = tmp_var != 0;
-+  temp = tmp_var ? value_cell (tmp_var) : (char *)NULL;
-+  if (temp)
-+    eof_encountered_limit = (*temp && all_digits (temp)) ? atoi (temp) : 10;
-+  set_shellopts ();	/* make sure `ignoreeof' is/is not in $SHELLOPTS */
-+}
-+
-+void
-+sv_optind (name)
-+     char *name;
-+{
-+  char *tt;
-+  int s;
-+
-+  tt = get_string_value ("OPTIND");
-+  if (tt && *tt)
-+    {
-+      s = atoi (tt);
-+
-+      /* According to POSIX, setting OPTIND=1 resets the internal state
-+	 of getopt (). */
-+      if (s < 0 || s == 1)
-+	s = 0;
-+    }
-+  else
-+    s = 0;
-+  getopts_reset (s);
-+}
-+
-+void
-+sv_opterr (name)
-+     char *name;
-+{
-+  char *tt;
-+
-+  tt = get_string_value ("OPTERR");
-+  sh_opterr = (tt && *tt) ? atoi (tt) : 1;
-+}
-+
-+void
-+sv_strict_posix (name)
-+     char *name;
-+{
-+  SET_INT_VAR (name, posixly_correct);
-+  posix_initialize (posixly_correct);
-+#if defined (READLINE)
-+  if (interactive_shell)
-+    posix_readline_initialize (posixly_correct);
-+#endif /* READLINE */
-+  set_shellopts ();	/* make sure `posix' is/is not in $SHELLOPTS */
-+}
-+
-+void
-+sv_locale (name)
-+     char *name;
-+{
-+  char *v;
-+  int r;
-+
-+  v = get_string_value (name);
-+  if (name[0] == 'L' && name[1] == 'A')	/* LANG */
-+    r = set_lang (name, v);
-+  else
-+    r = set_locale_var (name, v);		/* LC_*, TEXTDOMAIN* */
-+
-+#if 1
-+  if (r == 0 && posixly_correct)
-+    last_command_exit_value = 1;
-+#endif
-+}
-+
-+#if defined (ARRAY_VARS)
-+void
-+set_pipestatus_array (ps, nproc)
-+     int *ps;
-+     int nproc;
-+{
-+  SHELL_VAR *v;
-+  ARRAY *a;
-+  ARRAY_ELEMENT *ae;
-+  register int i;
-+  char *t, tbuf[INT_STRLEN_BOUND(int) + 1];
-+
-+  v = find_variable ("PIPESTATUS");
-+  if (v == 0)
-+    v = make_new_array_variable ("PIPESTATUS");
-+  if (array_p (v) == 0)
-+    return;		/* Do nothing if not an array variable. */
-+  a = array_cell (v);
-+
-+  if (a == 0 || array_num_elements (a) == 0)
-+    {
-+      for (i = 0; i < nproc; i++)	/* was ps[i] != -1, not i < nproc */
-+	{
-+	  t = inttostr (ps[i], tbuf, sizeof (tbuf));
-+	  array_insert (a, i, t);
-+	}
-+      return;
-+    }
-+
-+  /* Fast case */
-+  if (array_num_elements (a) == nproc && nproc == 1)
-+    {
-+      ae = element_forw (a->head);
-+      free (element_value (ae));
-+      ae->value = itos (ps[0]);
-+    }
-+  else if (array_num_elements (a) <= nproc)
-+    {
-+      /* modify in array_num_elements members in place, then add */
-+      ae = a->head;
-+      for (i = 0; i < array_num_elements (a); i++)
-+	{
-+	  ae = element_forw (ae);
-+	  free (element_value (ae));
-+	  ae->value = itos (ps[i]);
-+	}
-+      /* add any more */
-+      for ( ; i < nproc; i++)
-+	{
-+	  t = inttostr (ps[i], tbuf, sizeof (tbuf));
-+	  array_insert (a, i, t);
-+	}
-+    }
-+  else
-+    {
-+      /* deleting elements.  it's faster to rebuild the array. */	  
-+      array_flush (a);
-+      for (i = 0; ps[i] != -1; i++)
-+	{
-+	  t = inttostr (ps[i], tbuf, sizeof (tbuf));
-+	  array_insert (a, i, t);
-+	}
-+    }
-+}
-+
-+ARRAY *
-+save_pipestatus_array ()
-+{
-+  SHELL_VAR *v;
-+  ARRAY *a, *a2;
-+
-+  v = find_variable ("PIPESTATUS");
-+  if (v == 0 || array_p (v) == 0 || array_cell (v) == 0)
-+    return ((ARRAY *)NULL);
-+    
-+  a = array_cell (v);
-+  a2 = array_copy (array_cell (v));
-+
-+  return a2;
-+}
-+
-+void
-+restore_pipestatus_array (a)
-+     ARRAY *a;
-+{
-+  SHELL_VAR *v;
-+  ARRAY *a2;
-+
-+  v = find_variable ("PIPESTATUS");
-+  /* XXX - should we still assign even if existing value is NULL? */
-+  if (v == 0 || array_p (v) == 0 || array_cell (v) == 0)
-+    return;
-+
-+  a2 = array_cell (v);
-+  var_setarray (v, a); 
-+
-+  array_dispose (a2);
-+}
-+#endif
-+
-+void
-+set_pipestatus_from_exit (s)
-+     int s;
-+{
-+#if defined (ARRAY_VARS)
-+  static int v[2] = { 0, -1 };
-+
-+  v[0] = s;
-+  set_pipestatus_array (v, 1);
-+#endif
-+}
-+
-+void
-+sv_xtracefd (name)
-+     char *name;
-+{
-+  SHELL_VAR *v;
-+  char *t, *e;
-+  int fd;
-+  FILE *fp;
-+
-+  v = find_variable (name);
-+  if (v == 0)
-+    {
-+      xtrace_reset ();
-+      return;
-+    }
-+
-+  t = value_cell (v);
-+  if (t == 0 || *t == 0)
-+    xtrace_reset ();
-+  else
-+    {
-+      fd = (int)strtol (t, &e, 10);
-+      if (e != t && *e == '\0' && sh_validfd (fd))
-+	{
-+	  fp = fdopen (fd, "w");
-+	  if (fp == 0)
-+	    internal_error (_("%s: %s: cannot open as FILE"), name, value_cell (v));
-+	  else
-+	    xtrace_set (fd, fp);
-+	}
-+      else
-+	internal_error (_("%s: %s: invalid value for trace file descriptor"), name, value_cell (v));
-+    }
-+}
-+
-+#define MIN_COMPAT_LEVEL 31
-+
-+void
-+sv_shcompat (name)
-+     char *name;
-+{
-+  SHELL_VAR *v;
-+  char *val;
-+  int tens, ones, compatval;
-+
-+  v = find_variable (name);
-+  if (v == 0)
-+    {
-+      shell_compatibility_level = DEFAULT_COMPAT_LEVEL;
-+      set_compatibility_opts ();
-+      return;
-+    }
-+  val = value_cell (v);
-+  if (val == 0 || *val == '\0')
-+    {
-+      shell_compatibility_level = DEFAULT_COMPAT_LEVEL;
-+      set_compatibility_opts ();
-+      return;
-+    }
-+  /* Handle decimal-like compatibility version specifications: 4.2 */
-+  if (isdigit (val[0]) && val[1] == '.' && isdigit (val[2]) && val[3] == 0)
-+    {
-+      tens = val[0] - '0';
-+      ones = val[2] - '0';
-+      compatval = tens*10 + ones;
-+    }
-+  /* Handle integer-like compatibility version specifications: 42 */
-+  else if (isdigit (val[0]) && isdigit (val[1]) && val[2] == 0)
-+    {
-+      tens = val[0] - '0';
-+      ones = val[1] - '0';
-+      compatval = tens*10 + ones;
-+    }
-+  else
-+    {
-+compat_error:
-+      internal_error (_("%s: %s: compatibility value out of range"), name, val);
-+      shell_compatibility_level = DEFAULT_COMPAT_LEVEL;
-+      set_compatibility_opts ();
-+      return;
-+    }
-+
-+  if (compatval < MIN_COMPAT_LEVEL || compatval > DEFAULT_COMPAT_LEVEL)
-+    goto compat_error;
-+
-+  shell_compatibility_level = compatval;
-+  set_compatibility_opts ();
-+}
-+
-+#if defined (JOB_CONTROL)
-+void
-+sv_childmax (name)
-+     char *name;
-+{
-+  char *tt;
-+  int s;
-+
-+  tt = get_string_value (name);
-+  s = (tt && *tt) ? atoi (tt) : 0;
-+  set_maxchild (s);
-+}
-+#endif
diff --git a/patches/bash-4.3.30/0002-Bash-4.3-patch-32.patch b/patches/bash-4.3.30/0002-Bash-4.3-patch-32.patch
deleted file mode 100644
index 801b4a609..000000000
--- a/patches/bash-4.3.30/0002-Bash-4.3-patch-32.patch
+++ /dev/null
@@ -1,5409 +0,0 @@
-From: Chet Ramey <chet.ramey@case.edu>
-Date: Thu, 15 Jan 2015 10:20:45 -0500
-Subject: [PATCH] Bash-4.3 patch 32
-
----
- jobs.c           |    4 +-
- patchlevel.h     |    2 +-
- variables.c.orig | 5365 ------------------------------------------------------
- 3 files changed, 4 insertions(+), 5367 deletions(-)
- delete mode 100644 variables.c.orig
-
-diff --git a/jobs.c b/jobs.c
-index f38b0c3f4446..b6e59eba0de8 100644
---- a/jobs.c
-+++ b/jobs.c
-@@ -3339,7 +3339,9 @@ itrace("waitchld: waitpid returns %d block = %d", pid, block);
-       if (posixly_correct && this_shell_builtin && this_shell_builtin == wait_builtin)
- 	{
- 	  interrupt_immediately = 0;
--	  trap_handler (SIGCHLD);	/* set pending_traps[SIGCHLD] */
-+	  /* This was trap_handler (SIGCHLD) but that can lose traps if
-+	     children_exited > 1 */
-+	  queue_sigchld_trap (children_exited);
- 	  wait_signal_received = SIGCHLD;
- 	  /* If we're in a signal handler, let CHECK_WAIT_INTR pick it up;
- 	     run_pending_traps will call run_sigchld_trap later  */
-diff --git a/patchlevel.h b/patchlevel.h
-index 0ad46aafbdd9..b8bf38704ed2 100644
---- a/patchlevel.h
-+++ b/patchlevel.h
-@@ -25,6 +25,6 @@
-    regexp `^#define[ 	]*PATCHLEVEL', since that's what support/mkversion.sh
-    looks for to find the patch level (for the sccs version string). */
- 
--#define PATCHLEVEL 31
-+#define PATCHLEVEL 32
- 
- #endif /* _PATCHLEVEL_H_ */
-diff --git a/variables.c.orig b/variables.c.orig
-deleted file mode 100644
-index 7c82710e0f0b..000000000000
---- a/variables.c.orig
-+++ /dev/null
-@@ -1,5365 +0,0 @@
--/* variables.c -- Functions for hacking shell variables. */
--
--/* Copyright (C) 1987-2013 Free Software Foundation, Inc.
--
--   This file is part of GNU Bash, the Bourne Again SHell.
--
--   Bash is free software: you can redistribute it and/or modify
--   it under the terms of the GNU General Public License as published by
--   the Free Software Foundation, either version 3 of the License, or
--   (at your option) any later version.
--
--   Bash is distributed in the hope that it will be useful,
--   but WITHOUT ANY WARRANTY; without even the implied warranty of
--   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
--   GNU General Public License for more details.
--
--   You should have received a copy of the GNU General Public License
--   along with Bash.  If not, see <http://www.gnu.org/licenses/>.
--*/
--
--#include "config.h"
--
--#include "bashtypes.h"
--#include "posixstat.h"
--#include "posixtime.h"
--
--#if defined (__QNX__)
--#  if defined (__QNXNTO__)
--#    include <sys/netmgr.h>
--#  else
--#    include <sys/vc.h>
--#  endif /* !__QNXNTO__ */
--#endif /* __QNX__ */
--
--#if defined (HAVE_UNISTD_H)
--#  include <unistd.h>
--#endif
--
--#include <stdio.h>
--#include "chartypes.h"
--#if defined (HAVE_PWD_H)
--#  include <pwd.h>
--#endif
--#include "bashansi.h"
--#include "bashintl.h"
--
--#define NEED_XTRACE_SET_DECL
--
--#include "shell.h"
--#include "flags.h"
--#include "execute_cmd.h"
--#include "findcmd.h"
--#include "mailcheck.h"
--#include "input.h"
--#include "hashcmd.h"
--#include "pathexp.h"
--#include "alias.h"
--#include "jobs.h"
--
--#include "version.h"
--
--#include "builtins/getopt.h"
--#include "builtins/common.h"
--#include "builtins/builtext.h"
--
--#if defined (READLINE)
--#  include "bashline.h"
--#  include <readline/readline.h>
--#else
--#  include <tilde/tilde.h>
--#endif
--
--#if defined (HISTORY)
--#  include "bashhist.h"
--#  include <readline/history.h>
--#endif /* HISTORY */
--
--#if defined (PROGRAMMABLE_COMPLETION)
--#  include "pcomplete.h"
--#endif
--
--#define TEMPENV_HASH_BUCKETS	4	/* must be power of two */
--
--#define ifsname(s)	((s)[0] == 'I' && (s)[1] == 'F' && (s)[2] == 'S' && (s)[3] == '\0')
--
--#define BASHFUNC_PREFIX		"BASH_FUNC_"
--#define BASHFUNC_PREFLEN	10	/* == strlen(BASHFUNC_PREFIX */
--#define BASHFUNC_SUFFIX		"%%"
--#define BASHFUNC_SUFFLEN	2	/* == strlen(BASHFUNC_SUFFIX) */
--
--extern char **environ;
--
--/* Variables used here and defined in other files. */
--extern int posixly_correct;
--extern int line_number, line_number_base;
--extern int subshell_environment, indirection_level, subshell_level;
--extern int build_version, patch_level;
--extern int expanding_redir;
--extern int last_command_exit_value;
--extern char *dist_version, *release_status;
--extern char *shell_name;
--extern char *primary_prompt, *secondary_prompt;
--extern char *current_host_name;
--extern sh_builtin_func_t *this_shell_builtin;
--extern SHELL_VAR *this_shell_function;
--extern char *the_printed_command_except_trap;
--extern char *this_command_name;
--extern char *command_execution_string;
--extern time_t shell_start_time;
--extern int assigning_in_environment;
--extern int executing_builtin;
--extern int funcnest_max;
--
--#if defined (READLINE)
--extern int no_line_editing;
--extern int perform_hostname_completion;
--#endif
--
--/* The list of shell variables that the user has created at the global
--   scope, or that came from the environment. */
--VAR_CONTEXT *global_variables = (VAR_CONTEXT *)NULL;
--
--/* The current list of shell variables, including function scopes */
--VAR_CONTEXT *shell_variables = (VAR_CONTEXT *)NULL;
--
--/* The list of shell functions that the user has created, or that came from
--   the environment. */
--HASH_TABLE *shell_functions = (HASH_TABLE *)NULL;
--
--#if defined (DEBUGGER)
--/* The table of shell function definitions that the user defined or that
--   came from the environment. */
--HASH_TABLE *shell_function_defs = (HASH_TABLE *)NULL;
--#endif
--
--/* The current variable context.  This is really a count of how deep into
--   executing functions we are. */
--int variable_context = 0;
--
--/* The set of shell assignments which are made only in the environment
--   for a single command. */
--HASH_TABLE *temporary_env = (HASH_TABLE *)NULL;
--
--/* Set to non-zero if an assignment error occurs while putting variables
--   into the temporary environment. */
--int tempenv_assign_error;
--
--/* Some funky variables which are known about specially.  Here is where
--   "$*", "$1", and all the cruft is kept. */
--char *dollar_vars[10];
--WORD_LIST *rest_of_args = (WORD_LIST *)NULL;
--
--/* The value of $$. */
--pid_t dollar_dollar_pid;
--
--/* Non-zero means that we have to remake EXPORT_ENV. */
--int array_needs_making = 1;
--
--/* The number of times BASH has been executed.  This is set
--   by initialize_variables (). */
--int shell_level = 0;
--
--/* An array which is passed to commands as their environment.  It is
--   manufactured from the union of the initial environment and the
--   shell variables that are marked for export. */
--char **export_env = (char **)NULL;
--static int export_env_index;
--static int export_env_size;
--
--#if defined (READLINE)
--static int winsize_assignment;		/* currently assigning to LINES or COLUMNS */
--#endif
--
--static HASH_TABLE *last_table_searched;	/* hash_lookup sets this */
--
--/* Some forward declarations. */
--static void create_variable_tables __P((void));
--
--static void set_machine_vars __P((void));
--static void set_home_var __P((void));
--static void set_shell_var __P((void));
--static char *get_bash_name __P((void));
--static void initialize_shell_level __P((void));
--static void uidset __P((void));
--#if defined (ARRAY_VARS)
--static void make_vers_array __P((void));
--#endif
--
--static SHELL_VAR *null_assign __P((SHELL_VAR *, char *, arrayind_t, char *));
--#if defined (ARRAY_VARS)
--static SHELL_VAR *null_array_assign __P((SHELL_VAR *, char *, arrayind_t, char *));
--#endif
--static SHELL_VAR *get_self __P((SHELL_VAR *));
--
--#if defined (ARRAY_VARS)
--static SHELL_VAR *init_dynamic_array_var __P((char *, sh_var_value_func_t *, sh_var_assign_func_t *, int));
--static SHELL_VAR *init_dynamic_assoc_var __P((char *, sh_var_value_func_t *, sh_var_assign_func_t *, int));
--#endif
--
--static SHELL_VAR *assign_seconds __P((SHELL_VAR *, char *, arrayind_t, char *));
--static SHELL_VAR *get_seconds __P((SHELL_VAR *));
--static SHELL_VAR *init_seconds_var __P((void));
--
--static int brand __P((void));
--static void sbrand __P((unsigned long));		/* set bash random number generator. */
--static void seedrand __P((void));			/* seed generator randomly */
--static SHELL_VAR *assign_random __P((SHELL_VAR *, char *, arrayind_t, char *));
--static SHELL_VAR *get_random __P((SHELL_VAR *));
--
--static SHELL_VAR *assign_lineno __P((SHELL_VAR *, char *, arrayind_t, char *));
--static SHELL_VAR *get_lineno __P((SHELL_VAR *));
--
--static SHELL_VAR *assign_subshell __P((SHELL_VAR *, char *, arrayind_t, char *));
--static SHELL_VAR *get_subshell __P((SHELL_VAR *));
--
--static SHELL_VAR *get_bashpid __P((SHELL_VAR *));
--
--#if defined (HISTORY)
--static SHELL_VAR *get_histcmd __P((SHELL_VAR *));
--#endif
--
--#if defined (READLINE)
--static SHELL_VAR *get_comp_wordbreaks __P((SHELL_VAR *));
--static SHELL_VAR *assign_comp_wordbreaks __P((SHELL_VAR *, char *, arrayind_t, char *));
--#endif
--
--#if defined (PUSHD_AND_POPD) && defined (ARRAY_VARS)
--static SHELL_VAR *assign_dirstack __P((SHELL_VAR *, char *, arrayind_t, char *));
--static SHELL_VAR *get_dirstack __P((SHELL_VAR *));
--#endif
--
--#if defined (ARRAY_VARS)
--static SHELL_VAR *get_groupset __P((SHELL_VAR *));
--
--static SHELL_VAR *build_hashcmd __P((SHELL_VAR *));
--static SHELL_VAR *get_hashcmd __P((SHELL_VAR *));
--static SHELL_VAR *assign_hashcmd __P((SHELL_VAR *,  char *, arrayind_t, char *));
--#  if defined (ALIAS)
--static SHELL_VAR *build_aliasvar __P((SHELL_VAR *));
--static SHELL_VAR *get_aliasvar __P((SHELL_VAR *));
--static SHELL_VAR *assign_aliasvar __P((SHELL_VAR *,  char *, arrayind_t, char *));
--#  endif
--#endif
--
--static SHELL_VAR *get_funcname __P((SHELL_VAR *));
--static SHELL_VAR *init_funcname_var __P((void));
--
--static void initialize_dynamic_variables __P((void));
--
--static SHELL_VAR *hash_lookup __P((const char *, HASH_TABLE *));
--static SHELL_VAR *new_shell_variable __P((const char *));
--static SHELL_VAR *make_new_variable __P((const char *, HASH_TABLE *));
--static SHELL_VAR *bind_variable_internal __P((const char *, char *, HASH_TABLE *, int, int));
--
--static void dispose_variable_value __P((SHELL_VAR *));
--static void free_variable_hash_data __P((PTR_T));
--
--static VARLIST *vlist_alloc __P((int));
--static VARLIST *vlist_realloc __P((VARLIST *, int));
--static void vlist_add __P((VARLIST *, SHELL_VAR *, int));
--
--static void flatten __P((HASH_TABLE *, sh_var_map_func_t *, VARLIST *, int));
--
--static int qsort_var_comp __P((SHELL_VAR **, SHELL_VAR **));
--
--static SHELL_VAR **vapply __P((sh_var_map_func_t *));
--static SHELL_VAR **fapply __P((sh_var_map_func_t *));
--
--static int visible_var __P((SHELL_VAR *));
--static int visible_and_exported __P((SHELL_VAR *));
--static int export_environment_candidate __P((SHELL_VAR *));
--static int local_and_exported __P((SHELL_VAR *));
--static int variable_in_context __P((SHELL_VAR *));
--#if defined (ARRAY_VARS)
--static int visible_array_vars __P((SHELL_VAR *));
--#endif
--
--static SHELL_VAR *find_nameref_at_context __P((SHELL_VAR *, VAR_CONTEXT *));
--static SHELL_VAR *find_variable_nameref_context __P((SHELL_VAR *, VAR_CONTEXT *, VAR_CONTEXT **));
--static SHELL_VAR *find_variable_last_nameref_context __P((SHELL_VAR *, VAR_CONTEXT *, VAR_CONTEXT **));
--
--static SHELL_VAR *bind_tempenv_variable __P((const char *, char *));
--static void push_temp_var __P((PTR_T));
--static void propagate_temp_var __P((PTR_T));
--static void dispose_temporary_env __P((sh_free_func_t *));     
--
--static inline char *mk_env_string __P((const char *, const char *, int));
--static char **make_env_array_from_var_list __P((SHELL_VAR **));
--static char **make_var_export_array __P((VAR_CONTEXT *));
--static char **make_func_export_array __P((void));
--static void add_temp_array_to_env __P((char **, int, int));
--
--static int n_shell_variables __P((void));
--static int set_context __P((SHELL_VAR *));
--
--static void push_func_var __P((PTR_T));
--static void push_exported_var __P((PTR_T));
--
--static inline int find_special_var __P((const char *));
--
--static void
--create_variable_tables ()
--{
--  if (shell_variables == 0)
--    {
--      shell_variables = global_variables = new_var_context ((char *)NULL, 0);
--      shell_variables->scope = 0;
--      shell_variables->table = hash_create (0);
--    }
--
--  if (shell_functions == 0)
--    shell_functions = hash_create (0);
--
--#if defined (DEBUGGER)
--  if (shell_function_defs == 0)
--    shell_function_defs = hash_create (0);
--#endif
--}
--
--/* Initialize the shell variables from the current environment.
--   If PRIVMODE is nonzero, don't import functions from ENV or
--   parse $SHELLOPTS. */
--void
--initialize_shell_variables (env, privmode)
--     char **env;
--     int privmode;
--{
--  char *name, *string, *temp_string;
--  int c, char_index, string_index, string_length, ro;
--  SHELL_VAR *temp_var;
--
--  create_variable_tables ();
--
--  for (string_index = 0; string = env[string_index++]; )
--    {
--      char_index = 0;
--      name = string;
--      while ((c = *string++) && c != '=')
--	;
--      if (string[-1] == '=')
--	char_index = string - name - 1;
--
--      /* If there are weird things in the environment, like `=xxx' or a
--	 string without an `=', just skip them. */
--      if (char_index == 0)
--	continue;
--
--      /* ASSERT(name[char_index] == '=') */
--      name[char_index] = '\0';
--      /* Now, name = env variable name, string = env variable value, and
--	 char_index == strlen (name) */
--
--      temp_var = (SHELL_VAR *)NULL;
--
--      /* If exported function, define it now.  Don't import functions from
--	 the environment in privileged mode. */
--      if (privmode == 0 && read_but_dont_execute == 0 && 
--          STREQN (BASHFUNC_PREFIX, name, BASHFUNC_PREFLEN) &&
--          STREQ (BASHFUNC_SUFFIX, name + char_index - BASHFUNC_SUFFLEN) &&
--	  STREQN ("() {", string, 4))
--	{
--	  size_t namelen;
--	  char *tname;		/* desired imported function name */
--
--	  namelen = char_index - BASHFUNC_PREFLEN - BASHFUNC_SUFFLEN;
--
--	  tname = name + BASHFUNC_PREFLEN;	/* start of func name */
--	  tname[namelen] = '\0';		/* now tname == func name */
--
--	  string_length = strlen (string);
--	  temp_string = (char *)xmalloc (namelen + string_length + 2);
--
--	  memcpy (temp_string, tname, namelen);
--	  temp_string[namelen] = ' ';
--	  memcpy (temp_string + namelen + 1, string, string_length + 1);
--
--	  /* Don't import function names that are invalid identifiers from the
--	     environment, though we still allow them to be defined as shell
--	     variables. */
--	  if (absolute_program (tname) == 0 && (posixly_correct == 0 || legal_identifier (tname)))
--	    parse_and_execute (temp_string, tname, SEVAL_NONINT|SEVAL_NOHIST|SEVAL_FUNCDEF|SEVAL_ONECMD);
--
--	  if (temp_var = find_function (tname))
--	    {
--	      VSETATTR (temp_var, (att_exported|att_imported));
--	      array_needs_making = 1;
--	    }
--	  else
--	    {
--	      if (temp_var = bind_variable (name, string, 0))
--		{
--		  VSETATTR (temp_var, (att_exported | att_imported | att_invisible));
--		  array_needs_making = 1;
--		}
--	      last_command_exit_value = 1;
--	      report_error (_("error importing function definition for `%s'"), tname);
--	    }
--
--	  /* Restore original suffix */
--	  tname[namelen] = BASHFUNC_SUFFIX[0];
--	}
--#if defined (ARRAY_VARS)
--#  if ARRAY_EXPORT
--      /* Array variables may not yet be exported. */
--      else if (*string == '(' && string[1] == '[' && string[strlen (string) - 1] == ')')
--	{
--	  string_length = 1;
--	  temp_string = extract_array_assignment_list (string, &string_length);
--	  temp_var = assign_array_from_string (name, temp_string);
--	  FREE (temp_string);
--	  VSETATTR (temp_var, (att_exported | att_imported));
--	  array_needs_making = 1;
--	}
--#  endif /* ARRAY_EXPORT */
--#endif
--#if 0
--      else if (legal_identifier (name))
--#else
--      else
--#endif
--	{
--	  ro = 0;
--	  if (posixly_correct && STREQ (name, "SHELLOPTS"))
--	    {
--	      temp_var = find_variable ("SHELLOPTS");
--	      ro = temp_var && readonly_p (temp_var);
--	      if (temp_var)
--		VUNSETATTR (temp_var, att_readonly);
--	    }
--	  temp_var = bind_variable (name, string, 0);
--	  if (temp_var)
--	    {
--	      if (legal_identifier (name))
--		VSETATTR (temp_var, (att_exported | att_imported));
--	      else
--		VSETATTR (temp_var, (att_exported | att_imported | att_invisible));
--	      if (ro)
--		VSETATTR (temp_var, att_readonly);
--	      array_needs_making = 1;
--	    }
--	}
--
--      name[char_index] = '=';
--      /* temp_var can be NULL if it was an exported function with a syntax
--	 error (a different bug, but it still shouldn't dump core). */
--      if (temp_var && function_p (temp_var) == 0)	/* XXX not yet */
--	{
--	  CACHE_IMPORTSTR (temp_var, name);
--	}
--    }
--
--  set_pwd ();
--
--  /* Set up initial value of $_ */
--  temp_var = set_if_not ("_", dollar_vars[0]);
--
--  /* Remember this pid. */
--  dollar_dollar_pid = getpid ();
--
--  /* Now make our own defaults in case the vars that we think are
--     important are missing. */
--  temp_var = set_if_not ("PATH", DEFAULT_PATH_VALUE);
--#if 0
--  set_auto_export (temp_var);	/* XXX */
--#endif
--
--  temp_var = set_if_not ("TERM", "dumb");
--#if 0
--  set_auto_export (temp_var);	/* XXX */
--#endif
--
--#if defined (__QNX__)
--  /* set node id -- don't import it from the environment */
--  {
--    char node_name[22];
--#  if defined (__QNXNTO__)
--    netmgr_ndtostr(ND2S_LOCAL_STR, ND_LOCAL_NODE, node_name, sizeof(node_name));
--#  else
--    qnx_nidtostr (getnid (), node_name, sizeof (node_name));
--#  endif
--    temp_var = bind_variable ("NODE", node_name, 0);
--    set_auto_export (temp_var);
--  }
--#endif
--
--  /* set up the prompts. */
--  if (interactive_shell)
--    {
--#if defined (PROMPT_STRING_DECODE)
--      set_if_not ("PS1", primary_prompt);
--#else
--      if (current_user.uid == -1)
--	get_current_user_info ();
--      set_if_not ("PS1", current_user.euid == 0 ? "# " : primary_prompt);
--#endif
--      set_if_not ("PS2", secondary_prompt);
--    }
--  set_if_not ("PS4", "+ ");
--
--  /* Don't allow IFS to be imported from the environment. */
--  temp_var = bind_variable ("IFS", " \t\n", 0);
--  setifs (temp_var);
--
--  /* Magic machine types.  Pretty convenient. */
--  set_machine_vars ();
--
--  /* Default MAILCHECK for interactive shells.  Defer the creation of a
--     default MAILPATH until the startup files are read, because MAIL
--     names a mail file if MAILPATH is not set, and we should provide a
--     default only if neither is set. */
--  if (interactive_shell)
--    {
--      temp_var = set_if_not ("MAILCHECK", posixly_correct ? "600" : "60");
--      VSETATTR (temp_var, att_integer);
--    }
--
--  /* Do some things with shell level. */
--  initialize_shell_level ();
--
--  set_ppid ();
--
--  /* Initialize the `getopts' stuff. */
--  temp_var = bind_variable ("OPTIND", "1", 0);
--  VSETATTR (temp_var, att_integer);
--  getopts_reset (0);
--  bind_variable ("OPTERR", "1", 0);
--  sh_opterr = 1;
--
--  if (login_shell == 1 && posixly_correct == 0)
--    set_home_var ();
--
--  /* Get the full pathname to THIS shell, and set the BASH variable
--     to it. */
--  name = get_bash_name ();
--  temp_var = bind_variable ("BASH", name, 0);
--  free (name);
--
--  /* Make the exported environment variable SHELL be the user's login
--     shell.  Note that the `tset' command looks at this variable
--     to determine what style of commands to output; if it ends in "csh",
--     then C-shell commands are output, else Bourne shell commands. */
--  set_shell_var ();
--
--  /* Make a variable called BASH_VERSION which contains the version info. */
--  bind_variable ("BASH_VERSION", shell_version_string (), 0);
--#if defined (ARRAY_VARS)
--  make_vers_array ();
--#endif
--
--  if (command_execution_string)
--    bind_variable ("BASH_EXECUTION_STRING", command_execution_string, 0);
--
--  /* Find out if we're supposed to be in Posix.2 mode via an
--     environment variable. */
--  temp_var = find_variable ("POSIXLY_CORRECT");
--  if (!temp_var)
--    temp_var = find_variable ("POSIX_PEDANTIC");
--  if (temp_var && imported_p (temp_var))
--    sv_strict_posix (temp_var->name);
--
--#if defined (HISTORY)
--  /* Set history variables to defaults, and then do whatever we would
--     do if the variable had just been set.  Do this only in the case
--     that we are remembering commands on the history list. */
--  if (remember_on_history)
--    {
--      name = bash_tilde_expand (posixly_correct ? "~/.sh_history" : "~/.bash_history", 0);
--
--      set_if_not ("HISTFILE", name);
--      free (name);
--    }
--#endif /* HISTORY */
--
--  /* Seed the random number generator. */
--  seedrand ();
--
--  /* Handle some "special" variables that we may have inherited from a
--     parent shell. */
--  if (interactive_shell)
--    {
--      temp_var = find_variable ("IGNOREEOF");
--      if (!temp_var)
--	temp_var = find_variable ("ignoreeof");
--      if (temp_var && imported_p (temp_var))
--	sv_ignoreeof (temp_var->name);
--    }
--
--#if defined (HISTORY)
--  if (interactive_shell && remember_on_history)
--    {
--      sv_history_control ("HISTCONTROL");
--      sv_histignore ("HISTIGNORE");
--      sv_histtimefmt ("HISTTIMEFORMAT");
--    }
--#endif /* HISTORY */
--
--#if defined (READLINE) && defined (STRICT_POSIX)
--  /* POSIXLY_CORRECT will only be 1 here if the shell was compiled
--     -DSTRICT_POSIX */
--  if (interactive_shell && posixly_correct && no_line_editing == 0)
--    rl_prefer_env_winsize = 1;
--#endif /* READLINE && STRICT_POSIX */
--
--     /*
--      * 24 October 2001
--      *
--      * I'm tired of the arguing and bug reports.  Bash now leaves SSH_CLIENT
--      * and SSH2_CLIENT alone.  I'm going to rely on the shell_level check in
--      * isnetconn() to avoid running the startup files more often than wanted.
--      * That will, of course, only work if the user's login shell is bash, so
--      * I've made that behavior conditional on SSH_SOURCE_BASHRC being defined
--      * in config-top.h.
--      */
--#if 0
--  temp_var = find_variable ("SSH_CLIENT");
--  if (temp_var && imported_p (temp_var))
--    {
--      VUNSETATTR (temp_var, att_exported);
--      array_needs_making = 1;
--    }
--  temp_var = find_variable ("SSH2_CLIENT");
--  if (temp_var && imported_p (temp_var))
--    {
--      VUNSETATTR (temp_var, att_exported);
--      array_needs_making = 1;
--    }
--#endif
--
--  /* Get the user's real and effective user ids. */
--  uidset ();
--
--  temp_var = find_variable ("BASH_XTRACEFD");
--  if (temp_var && imported_p (temp_var))
--    sv_xtracefd (temp_var->name);
--
--  /* Initialize the dynamic variables, and seed their values. */
--  initialize_dynamic_variables ();
--}
--
--/* **************************************************************** */
--/*								    */
--/*	     Setting values for special shell variables		    */
--/*								    */
--/* **************************************************************** */
--
--static void
--set_machine_vars ()
--{
--  SHELL_VAR *temp_var;
--
--  temp_var = set_if_not ("HOSTTYPE", HOSTTYPE);
--  temp_var = set_if_not ("OSTYPE", OSTYPE);
--  temp_var = set_if_not ("MACHTYPE", MACHTYPE);
--
--  temp_var = set_if_not ("HOSTNAME", current_host_name);
--}
--
--/* Set $HOME to the information in the password file if we didn't get
--   it from the environment. */
--
--/* This function is not static so the tilde and readline libraries can
--   use it. */
--char *
--sh_get_home_dir ()
--{
--  if (current_user.home_dir == 0)
--    get_current_user_info ();
--  return current_user.home_dir;
--}
--
--static void
--set_home_var ()
--{
--  SHELL_VAR *temp_var;
--
--  temp_var = find_variable ("HOME");
--  if (temp_var == 0)
--    temp_var = bind_variable ("HOME", sh_get_home_dir (), 0);
--#if 0
--  VSETATTR (temp_var, att_exported);
--#endif
--}
--
--/* Set $SHELL to the user's login shell if it is not already set.  Call
--   get_current_user_info if we haven't already fetched the shell. */
--static void
--set_shell_var ()
--{
--  SHELL_VAR *temp_var;
--
--  temp_var = find_variable ("SHELL");
--  if (temp_var == 0)
--    {
--      if (current_user.shell == 0)
--	get_current_user_info ();
--      temp_var = bind_variable ("SHELL", current_user.shell, 0);
--    }
--#if 0
--  VSETATTR (temp_var, att_exported);
--#endif
--}
--
--static char *
--get_bash_name ()
--{
--  char *name;
--
--  if ((login_shell == 1) && RELPATH(shell_name))
--    {
--      if (current_user.shell == 0)
--	get_current_user_info ();
--      name = savestring (current_user.shell);
--    }
--  else if (ABSPATH(shell_name))
--    name = savestring (shell_name);
--  else if (shell_name[0] == '.' && shell_name[1] == '/')
--    {
--      /* Fast path for common case. */
--      char *cdir;
--      int len;
--
--      cdir = get_string_value ("PWD");
--      if (cdir)
--	{
--	  len = strlen (cdir);
--	  name = (char *)xmalloc (len + strlen (shell_name) + 1);
--	  strcpy (name, cdir);
--	  strcpy (name + len, shell_name + 1);
--	}
--      else
--	name = savestring (shell_name);
--    }
--  else
--    {
--      char *tname;
--      int s;
--
--      tname = find_user_command (shell_name);
--
--      if (tname == 0)
--	{
--	  /* Try the current directory.  If there is not an executable
--	     there, just punt and use the login shell. */
--	  s = file_status (shell_name);
--	  if (s & FS_EXECABLE)
--	    {
--	      tname = make_absolute (shell_name, get_string_value ("PWD"));
--	      if (*shell_name == '.')
--		{
--		  name = sh_canonpath (tname, PATH_CHECKDOTDOT|PATH_CHECKEXISTS);
--		  if (name == 0)
--		    name = tname;
--		  else
--		    free (tname);
--		}
--	     else
--		name = tname;
--	    }
--	  else
--	    {
--	      if (current_user.shell == 0)
--		get_current_user_info ();
--	      name = savestring (current_user.shell);
--	    }
--	}
--      else
--	{
--	  name = full_pathname (tname);
--	  free (tname);
--	}
--    }
--
--  return (name);
--}
--
--void
--adjust_shell_level (change)
--     int change;
--{
--  char new_level[5], *old_SHLVL;
--  intmax_t old_level;
--  SHELL_VAR *temp_var;
--
--  old_SHLVL = get_string_value ("SHLVL");
--  if (old_SHLVL == 0 || *old_SHLVL == '\0' || legal_number (old_SHLVL, &old_level) == 0)
--    old_level = 0;
--
--  shell_level = old_level + change;
--  if (shell_level < 0)
--    shell_level = 0;
--  else if (shell_level > 1000)
--    {
--      internal_warning (_("shell level (%d) too high, resetting to 1"), shell_level);
--      shell_level = 1;
--    }
--
--  /* We don't need the full generality of itos here. */
--  if (shell_level < 10)
--    {
--      new_level[0] = shell_level + '0';
--      new_level[1] = '\0';
--    }
--  else if (shell_level < 100)
--    {
--      new_level[0] = (shell_level / 10) + '0';
--      new_level[1] = (shell_level % 10) + '0';
--      new_level[2] = '\0';
--    }
--  else if (shell_level < 1000)
--    {
--      new_level[0] = (shell_level / 100) + '0';
--      old_level = shell_level % 100;
--      new_level[1] = (old_level / 10) + '0';
--      new_level[2] = (old_level % 10) + '0';
--      new_level[3] = '\0';
--    }
--
--  temp_var = bind_variable ("SHLVL", new_level, 0);
--  set_auto_export (temp_var);
--}
--
--static void
--initialize_shell_level ()
--{
--  adjust_shell_level (1);
--}
--
--/* If we got PWD from the environment, update our idea of the current
--   working directory.  In any case, make sure that PWD exists before
--   checking it.  It is possible for getcwd () to fail on shell startup,
--   and in that case, PWD would be undefined.  If this is an interactive
--   login shell, see if $HOME is the current working directory, and if
--   that's not the same string as $PWD, set PWD=$HOME. */
--
--void
--set_pwd ()
--{
--  SHELL_VAR *temp_var, *home_var;
--  char *temp_string, *home_string;
--
--  home_var = find_variable ("HOME");
--  home_string = home_var ? value_cell (home_var) : (char *)NULL;
--
--  temp_var = find_variable ("PWD");
--  if (temp_var && imported_p (temp_var) &&
--      (temp_string = value_cell (temp_var)) &&
--      same_file (temp_string, ".", (struct stat *)NULL, (struct stat *)NULL))
--    set_working_directory (temp_string);
--  else if (home_string && interactive_shell && login_shell &&
--	   same_file (home_string, ".", (struct stat *)NULL, (struct stat *)NULL))
--    {
--      set_working_directory (home_string);
--      temp_var = bind_variable ("PWD", home_string, 0);
--      set_auto_export (temp_var);
--    }
--  else
--    {
--      temp_string = get_working_directory ("shell-init");
--      if (temp_string)
--	{
--	  temp_var = bind_variable ("PWD", temp_string, 0);
--	  set_auto_export (temp_var);
--	  free (temp_string);
--	}
--    }
--
--  /* According to the Single Unix Specification, v2, $OLDPWD is an
--     `environment variable' and therefore should be auto-exported.
--     Make a dummy invisible variable for OLDPWD, and mark it as exported. */
--  temp_var = bind_variable ("OLDPWD", (char *)NULL, 0);
--  VSETATTR (temp_var, (att_exported | att_invisible));
--}
--
--/* Make a variable $PPID, which holds the pid of the shell's parent.  */
--void
--set_ppid ()
--{
--  char namebuf[INT_STRLEN_BOUND(pid_t) + 1], *name;
--  SHELL_VAR *temp_var;
--
--  name = inttostr (getppid (), namebuf, sizeof(namebuf));
--  temp_var = find_variable ("PPID");
--  if (temp_var)
--    VUNSETATTR (temp_var, (att_readonly | att_exported));
--  temp_var = bind_variable ("PPID", name, 0);
--  VSETATTR (temp_var, (att_readonly | att_integer));
--}
--
--static void
--uidset ()
--{
--  char buff[INT_STRLEN_BOUND(uid_t) + 1], *b;
--  register SHELL_VAR *v;
--
--  b = inttostr (current_user.uid, buff, sizeof (buff));
--  v = find_variable ("UID");
--  if (v == 0)
--    {
--      v = bind_variable ("UID", b, 0);
--      VSETATTR (v, (att_readonly | att_integer));
--    }
--
--  if (current_user.euid != current_user.uid)
--    b = inttostr (current_user.euid, buff, sizeof (buff));
--
--  v = find_variable ("EUID");
--  if (v == 0)
--    {
--      v = bind_variable ("EUID", b, 0);
--      VSETATTR (v, (att_readonly | att_integer));
--    }
--}
--
--#if defined (ARRAY_VARS)
--static void
--make_vers_array ()
--{
--  SHELL_VAR *vv;
--  ARRAY *av;
--  char *s, d[32], b[INT_STRLEN_BOUND(int) + 1];
--
--  unbind_variable ("BASH_VERSINFO");
--
--  vv = make_new_array_variable ("BASH_VERSINFO");
--  av = array_cell (vv);
--  strcpy (d, dist_version);
--  s = strchr (d, '.');
--  if (s)
--    *s++ = '\0';
--  array_insert (av, 0, d);
--  array_insert (av, 1, s);
--  s = inttostr (patch_level, b, sizeof (b));
--  array_insert (av, 2, s);
--  s = inttostr (build_version, b, sizeof (b));
--  array_insert (av, 3, s);
--  array_insert (av, 4, release_status);
--  array_insert (av, 5, MACHTYPE);
--
--  VSETATTR (vv, att_readonly);
--}
--#endif /* ARRAY_VARS */
--
--/* Set the environment variables $LINES and $COLUMNS in response to
--   a window size change. */
--void
--sh_set_lines_and_columns (lines, cols)
--     int lines, cols;
--{
--  char val[INT_STRLEN_BOUND(int) + 1], *v;
--
--#if defined (READLINE)
--  /* If we are currently assigning to LINES or COLUMNS, don't do anything. */
--  if (winsize_assignment)
--    return;
--#endif
--
--  v = inttostr (lines, val, sizeof (val));
--  bind_variable ("LINES", v, 0);
--
--  v = inttostr (cols, val, sizeof (val));
--  bind_variable ("COLUMNS", v, 0);
--}
--
--/* **************************************************************** */
--/*								    */
--/*		   Printing variables and values		    */
--/*								    */
--/* **************************************************************** */
--
--/* Print LIST (a list of shell variables) to stdout in such a way that
--   they can be read back in. */
--void
--print_var_list (list)
--     register SHELL_VAR **list;
--{
--  register int i;
--  register SHELL_VAR *var;
--
--  for (i = 0; list && (var = list[i]); i++)
--    if (invisible_p (var) == 0)
--      print_assignment (var);
--}
--
--/* Print LIST (a list of shell functions) to stdout in such a way that
--   they can be read back in. */
--void
--print_func_list (list)
--     register SHELL_VAR **list;
--{
--  register int i;
--  register SHELL_VAR *var;
--
--  for (i = 0; list && (var = list[i]); i++)
--    {
--      printf ("%s ", var->name);
--      print_var_function (var);
--      printf ("\n");
--    }
--}
--      
--/* Print the value of a single SHELL_VAR.  No newline is
--   output, but the variable is printed in such a way that
--   it can be read back in. */
--void
--print_assignment (var)
--     SHELL_VAR *var;
--{
--  if (var_isset (var) == 0)
--    return;
--
--  if (function_p (var))
--    {
--      printf ("%s", var->name);
--      print_var_function (var);
--      printf ("\n");
--    }
--#if defined (ARRAY_VARS)
--  else if (array_p (var))
--    print_array_assignment (var, 0);
--  else if (assoc_p (var))
--    print_assoc_assignment (var, 0);
--#endif /* ARRAY_VARS */
--  else
--    {
--      printf ("%s=", var->name);
--      print_var_value (var, 1);
--      printf ("\n");
--    }
--}
--
--/* Print the value cell of VAR, a shell variable.  Do not print
--   the name, nor leading/trailing newline.  If QUOTE is non-zero,
--   and the value contains shell metacharacters, quote the value
--   in such a way that it can be read back in. */
--void
--print_var_value (var, quote)
--     SHELL_VAR *var;
--     int quote;
--{
--  char *t;
--
--  if (var_isset (var) == 0)
--    return;
--
--  if (quote && posixly_correct == 0 && ansic_shouldquote (value_cell (var)))
--    {
--      t = ansic_quote (value_cell (var), 0, (int *)0);
--      printf ("%s", t);
--      free (t);
--    }
--  else if (quote && sh_contains_shell_metas (value_cell (var)))
--    {
--      t = sh_single_quote (value_cell (var));
--      printf ("%s", t);
--      free (t);
--    }
--  else
--    printf ("%s", value_cell (var));
--}
--
--/* Print the function cell of VAR, a shell variable.  Do not
--   print the name, nor leading/trailing newline. */
--void
--print_var_function (var)
--     SHELL_VAR *var;
--{
--  char *x;
--
--  if (function_p (var) && var_isset (var))
--    {
--      x = named_function_string ((char *)NULL, function_cell(var), FUNC_MULTILINE|FUNC_EXTERNAL);
--      printf ("%s", x);
--    }
--}
--
--/* **************************************************************** */
--/*								    */
--/*		 	Dynamic Variables			    */
--/*								    */
--/* **************************************************************** */
--
--/* DYNAMIC VARIABLES
--
--   These are variables whose values are generated anew each time they are
--   referenced.  These are implemented using a pair of function pointers
--   in the struct variable: assign_func, which is called from bind_variable
--   and, if arrays are compiled into the shell, some of the functions in
--   arrayfunc.c, and dynamic_value, which is called from find_variable.
--
--   assign_func is called from bind_variable_internal, if
--   bind_variable_internal discovers that the variable being assigned to
--   has such a function.  The function is called as
--	SHELL_VAR *temp = (*(entry->assign_func)) (entry, value, ind)
--   and the (SHELL_VAR *)temp is returned as the value of bind_variable.  It
--   is usually ENTRY (self).  IND is an index for an array variable, and
--   unused otherwise.
--
--   dynamic_value is called from find_variable_internal to return a `new'
--   value for the specified dynamic varible.  If this function is NULL,
--   the variable is treated as a `normal' shell variable.  If it is not,
--   however, then this function is called like this:
--	tempvar = (*(var->dynamic_value)) (var);
--
--   Sometimes `tempvar' will replace the value of `var'.  Other times, the
--   shell will simply use the string value.  Pretty object-oriented, huh?
--
--   Be warned, though: if you `unset' a special variable, it loses its
--   special meaning, even if you subsequently set it.
--
--   The special assignment code would probably have been better put in
--   subst.c: do_assignment_internal, in the same style as
--   stupidly_hack_special_variables, but I wanted the changes as
--   localized as possible.  */
--
--#define INIT_DYNAMIC_VAR(var, val, gfunc, afunc) \
--  do \
--    { \
--      v = bind_variable (var, (val), 0); \
--      v->dynamic_value = gfunc; \
--      v->assign_func = afunc; \
--    } \
--  while (0)
--
--#define INIT_DYNAMIC_ARRAY_VAR(var, gfunc, afunc) \
--  do \
--    { \
--      v = make_new_array_variable (var); \
--      v->dynamic_value = gfunc; \
--      v->assign_func = afunc; \
--    } \
--  while (0)
--
--#define INIT_DYNAMIC_ASSOC_VAR(var, gfunc, afunc) \
--  do \
--    { \
--      v = make_new_assoc_variable (var); \
--      v->dynamic_value = gfunc; \
--      v->assign_func = afunc; \
--    } \
--  while (0)
--
--static SHELL_VAR *
--null_assign (self, value, unused, key)
--     SHELL_VAR *self;
--     char *value;
--     arrayind_t unused;
--     char *key;
--{
--  return (self);
--}
--
--#if defined (ARRAY_VARS)
--static SHELL_VAR *
--null_array_assign (self, value, ind, key)
--     SHELL_VAR *self;
--     char *value;
--     arrayind_t ind;
--     char *key;
--{
--  return (self);
--}
--#endif
--
--/* Degenerate `dynamic_value' function; just returns what's passed without
--   manipulation. */
--static SHELL_VAR *
--get_self (self)
--     SHELL_VAR *self;
--{
--  return (self);
--}
--
--#if defined (ARRAY_VARS)
--/* A generic dynamic array variable initializer.  Initialize array variable
--   NAME with dynamic value function GETFUNC and assignment function SETFUNC. */
--static SHELL_VAR *
--init_dynamic_array_var (name, getfunc, setfunc, attrs)
--     char *name;
--     sh_var_value_func_t *getfunc;
--     sh_var_assign_func_t *setfunc;
--     int attrs;
--{
--  SHELL_VAR *v;
--
--  v = find_variable (name);
--  if (v)
--    return (v);
--  INIT_DYNAMIC_ARRAY_VAR (name, getfunc, setfunc);
--  if (attrs)
--    VSETATTR (v, attrs);
--  return v;
--}
--
--static SHELL_VAR *
--init_dynamic_assoc_var (name, getfunc, setfunc, attrs)
--     char *name;
--     sh_var_value_func_t *getfunc;
--     sh_var_assign_func_t *setfunc;
--     int attrs;
--{
--  SHELL_VAR *v;
--
--  v = find_variable (name);
--  if (v)
--    return (v);
--  INIT_DYNAMIC_ASSOC_VAR (name, getfunc, setfunc);
--  if (attrs)
--    VSETATTR (v, attrs);
--  return v;
--}
--#endif
--
--/* The value of $SECONDS.  This is the number of seconds since shell
--   invocation, or, the number of seconds since the last assignment + the
--   value of the last assignment. */
--static intmax_t seconds_value_assigned;
--
--static SHELL_VAR *
--assign_seconds (self, value, unused, key)
--     SHELL_VAR *self;
--     char *value;
--     arrayind_t unused;
--     char *key;
--{
--  if (legal_number (value, &seconds_value_assigned) == 0)
--    seconds_value_assigned = 0;
--  shell_start_time = NOW;
--  return (self);
--}
--
--static SHELL_VAR *
--get_seconds (var)
--     SHELL_VAR *var;
--{
--  time_t time_since_start;
--  char *p;
--
--  time_since_start = NOW - shell_start_time;
--  p = itos(seconds_value_assigned + time_since_start);
--
--  FREE (value_cell (var));
--
--  VSETATTR (var, att_integer);
--  var_setvalue (var, p);
--  return (var);
--}
--
--static SHELL_VAR *
--init_seconds_var ()
--{
--  SHELL_VAR *v;
--
--  v = find_variable ("SECONDS");
--  if (v)
--    {
--      if (legal_number (value_cell(v), &seconds_value_assigned) == 0)
--	seconds_value_assigned = 0;
--    }
--  INIT_DYNAMIC_VAR ("SECONDS", (v ? value_cell (v) : (char *)NULL), get_seconds, assign_seconds);
--  return v;      
--}
--     
--/* The random number seed.  You can change this by setting RANDOM. */
--static unsigned long rseed = 1;
--static int last_random_value;
--static int seeded_subshell = 0;
--
--/* A linear congruential random number generator based on the example
--   one in the ANSI C standard.  This one isn't very good, but a more
--   complicated one is overkill. */
--
--/* Returns a pseudo-random number between 0 and 32767. */
--static int
--brand ()
--{
--  /* From "Random number generators: good ones are hard to find",
--     Park and Miller, Communications of the ACM, vol. 31, no. 10,
--     October 1988, p. 1195. filtered through FreeBSD */
--  long h, l;
--
--  /* Can't seed with 0. */
--  if (rseed == 0)
--    rseed = 123459876;
--  h = rseed / 127773;
--  l = rseed % 127773;
--  rseed = 16807 * l - 2836 * h;
--#if 0
--  if (rseed < 0)
--    rseed += 0x7fffffff;
--#endif
--  return ((unsigned int)(rseed & 32767));	/* was % 32768 */
--}
--
--/* Set the random number generator seed to SEED. */
--static void
--sbrand (seed)
--     unsigned long seed;
--{
--  rseed = seed;
--  last_random_value = 0;
--}
--
--static void
--seedrand ()
--{
--  struct timeval tv;
--
--  gettimeofday (&tv, NULL);
--  sbrand (tv.tv_sec ^ tv.tv_usec ^ getpid ());
--}
--
--static SHELL_VAR *
--assign_random (self, value, unused, key)
--     SHELL_VAR *self;
--     char *value;
--     arrayind_t unused;
--     char *key;
--{
--  sbrand (strtoul (value, (char **)NULL, 10));
--  if (subshell_environment)
--    seeded_subshell = getpid ();
--  return (self);
--}
--
--int
--get_random_number ()
--{
--  int rv, pid;
--
--  /* Reset for command and process substitution. */
--  pid = getpid ();
--  if (subshell_environment && seeded_subshell != pid)
--    {
--      seedrand ();
--      seeded_subshell = pid;
--    }
--
--  do
--    rv = brand ();
--  while (rv == last_random_value);
--  return rv;
--}
--
--static SHELL_VAR *
--get_random (var)
--     SHELL_VAR *var;
--{
--  int rv;
--  char *p;
--
--  rv = get_random_number ();
--  last_random_value = rv;
--  p = itos (rv);
--
--  FREE (value_cell (var));
--
--  VSETATTR (var, att_integer);
--  var_setvalue (var, p);
--  return (var);
--}
--
--static SHELL_VAR *
--assign_lineno (var, value, unused, key)
--     SHELL_VAR *var;
--     char *value;
--     arrayind_t unused;
--     char *key;
--{
--  intmax_t new_value;
--
--  if (value == 0 || *value == '\0' || legal_number (value, &new_value) == 0)
--    new_value = 0;
--  line_number = line_number_base = new_value;
--  return var;
--}
--
--/* Function which returns the current line number. */
--static SHELL_VAR *
--get_lineno (var)
--     SHELL_VAR *var;
--{
--  char *p;
--  int ln;
--
--  ln = executing_line_number ();
--  p = itos (ln);
--  FREE (value_cell (var));
--  var_setvalue (var, p);
--  return (var);
--}
--
--static SHELL_VAR *
--assign_subshell (var, value, unused, key)
--     SHELL_VAR *var;
--     char *value;
--     arrayind_t unused;
--     char *key;
--{
--  intmax_t new_value;
--
--  if (value == 0 || *value == '\0' || legal_number (value, &new_value) == 0)
--    new_value = 0;
--  subshell_level = new_value;
--  return var;
--}
--
--static SHELL_VAR *
--get_subshell (var)
--     SHELL_VAR *var;
--{
--  char *p;
--
--  p = itos (subshell_level);
--  FREE (value_cell (var));
--  var_setvalue (var, p);
--  return (var);
--}
--
--static SHELL_VAR *
--get_bashpid (var)
--     SHELL_VAR *var;
--{
--  int pid;
--  char *p;
--
--  pid = getpid ();
--  p = itos (pid);
--
--  FREE (value_cell (var));
--  VSETATTR (var, att_integer|att_readonly);
--  var_setvalue (var, p);
--  return (var);
--}
--
--static SHELL_VAR *
--get_bash_command (var)
--     SHELL_VAR *var;
--{
--  char *p;
--
--  if (the_printed_command_except_trap)
--    p = savestring (the_printed_command_except_trap);
--  else
--    {
--      p = (char *)xmalloc (1);
--      p[0] = '\0';
--    }
--  FREE (value_cell (var));
--  var_setvalue (var, p);
--  return (var);
--}
--
--#if defined (HISTORY)
--static SHELL_VAR *
--get_histcmd (var)
--     SHELL_VAR *var;
--{
--  char *p;
--
--  p = itos (history_number ());
--  FREE (value_cell (var));
--  var_setvalue (var, p);
--  return (var);
--}
--#endif
--
--#if defined (READLINE)
--/* When this function returns, VAR->value points to malloced memory. */
--static SHELL_VAR *
--get_comp_wordbreaks (var)
--     SHELL_VAR *var;
--{
--  /* If we don't have anything yet, assign a default value. */
--  if (rl_completer_word_break_characters == 0 && bash_readline_initialized == 0)
--    enable_hostname_completion (perform_hostname_completion);
--
--  FREE (value_cell (var));
--  var_setvalue (var, savestring (rl_completer_word_break_characters));
--
--  return (var);
--}
--
--/* When this function returns, rl_completer_word_break_characters points to
--   malloced memory. */
--static SHELL_VAR *
--assign_comp_wordbreaks (self, value, unused, key)
--     SHELL_VAR *self;
--     char *value;
--     arrayind_t unused;
--     char *key;
--{
--  if (rl_completer_word_break_characters &&
--      rl_completer_word_break_characters != rl_basic_word_break_characters)
--    free (rl_completer_word_break_characters);
--
--  rl_completer_word_break_characters = savestring (value);
--  return self;
--}
--#endif /* READLINE */
--
--#if defined (PUSHD_AND_POPD) && defined (ARRAY_VARS)
--static SHELL_VAR *
--assign_dirstack (self, value, ind, key)
--     SHELL_VAR *self;
--     char *value;
--     arrayind_t ind;
--     char *key;
--{
--  set_dirstack_element (ind, 1, value);
--  return self;
--}
--
--static SHELL_VAR *
--get_dirstack (self)
--     SHELL_VAR *self;
--{
--  ARRAY *a;
--  WORD_LIST *l;
--
--  l = get_directory_stack (0);
--  a = array_from_word_list (l);
--  array_dispose (array_cell (self));
--  dispose_words (l);
--  var_setarray (self, a);
--  return self;
--}
--#endif /* PUSHD AND POPD && ARRAY_VARS */
--
--#if defined (ARRAY_VARS)
--/* We don't want to initialize the group set with a call to getgroups()
--   unless we're asked to, but we only want to do it once. */
--static SHELL_VAR *
--get_groupset (self)
--     SHELL_VAR *self;
--{
--  register int i;
--  int ng;
--  ARRAY *a;
--  static char **group_set = (char **)NULL;
--
--  if (group_set == 0)
--    {
--      group_set = get_group_list (&ng);
--      a = array_cell (self);
--      for (i = 0; i < ng; i++)
--	array_insert (a, i, group_set[i]);
--    }
--  return (self);
--}
--
--static SHELL_VAR *
--build_hashcmd (self)
--     SHELL_VAR *self;
--{
--  HASH_TABLE *h;
--  int i;
--  char *k, *v;
--  BUCKET_CONTENTS *item;
--
--  h = assoc_cell (self);
--  if (h)
--    assoc_dispose (h);
--
--  if (hashed_filenames == 0 || HASH_ENTRIES (hashed_filenames) == 0)
--    {
--      var_setvalue (self, (char *)NULL);
--      return self;
--    }
--
--  h = assoc_create (hashed_filenames->nbuckets);
--  for (i = 0; i < hashed_filenames->nbuckets; i++)
--    {
--      for (item = hash_items (i, hashed_filenames); item; item = item->next)
--	{
--	  k = savestring (item->key);
--	  v = pathdata(item)->path;
--	  assoc_insert (h, k, v);
--	}
--    }
--
--  var_setvalue (self, (char *)h);
--  return self;
--}
--
--static SHELL_VAR *
--get_hashcmd (self)
--     SHELL_VAR *self;
--{
--  build_hashcmd (self);
--  return (self);
--}
--
--static SHELL_VAR *
--assign_hashcmd (self, value, ind, key)
--     SHELL_VAR *self;
--     char *value;
--     arrayind_t ind;
--     char *key;
--{
--  phash_insert (key, value, 0, 0);
--  return (build_hashcmd (self));
--}
--
--#if defined (ALIAS)
--static SHELL_VAR *
--build_aliasvar (self)
--     SHELL_VAR *self;
--{
--  HASH_TABLE *h;
--  int i;
--  char *k, *v;
--  BUCKET_CONTENTS *item;
--
--  h = assoc_cell (self);
--  if (h)
--    assoc_dispose (h);
--
--  if (aliases == 0 || HASH_ENTRIES (aliases) == 0)
--    {
--      var_setvalue (self, (char *)NULL);
--      return self;
--    }
--
--  h = assoc_create (aliases->nbuckets);
--  for (i = 0; i < aliases->nbuckets; i++)
--    {
--      for (item = hash_items (i, aliases); item; item = item->next)
--	{
--	  k = savestring (item->key);
--	  v = ((alias_t *)(item->data))->value;
--	  assoc_insert (h, k, v);
--	}
--    }
--
--  var_setvalue (self, (char *)h);
--  return self;
--}
--
--static SHELL_VAR *
--get_aliasvar (self)
--     SHELL_VAR *self;
--{
--  build_aliasvar (self);
--  return (self);
--}
--
--static SHELL_VAR *
--assign_aliasvar (self, value, ind, key)
--     SHELL_VAR *self;
--     char *value;
--     arrayind_t ind;
--     char *key;
--{
--  add_alias (key, value);
--  return (build_aliasvar (self));
--}
--#endif /* ALIAS */
--
--#endif /* ARRAY_VARS */
--
--/* If ARRAY_VARS is not defined, this just returns the name of any
--   currently-executing function.  If we have arrays, it's a call stack. */
--static SHELL_VAR *
--get_funcname (self)
--     SHELL_VAR *self;
--{
--#if ! defined (ARRAY_VARS)
--  char *t;
--  if (variable_context && this_shell_function)
--    {
--      FREE (value_cell (self));
--      t = savestring (this_shell_function->name);
--      var_setvalue (self, t);
--    }
--#endif
--  return (self);
--}
--
--void
--make_funcname_visible (on_or_off)
--     int on_or_off;
--{
--  SHELL_VAR *v;
--
--  v = find_variable ("FUNCNAME");
--  if (v == 0 || v->dynamic_value == 0)
--    return;
--
--  if (on_or_off)
--    VUNSETATTR (v, att_invisible);
--  else
--    VSETATTR (v, att_invisible);
--}
--
--static SHELL_VAR *
--init_funcname_var ()
--{
--  SHELL_VAR *v;
--
--  v = find_variable ("FUNCNAME");
--  if (v)
--    return v;
--#if defined (ARRAY_VARS)
--  INIT_DYNAMIC_ARRAY_VAR ("FUNCNAME", get_funcname, null_array_assign);
--#else
--  INIT_DYNAMIC_VAR ("FUNCNAME", (char *)NULL, get_funcname, null_assign);
--#endif
--  VSETATTR (v, att_invisible|att_noassign);
--  return v;
--}
--
--static void
--initialize_dynamic_variables ()
--{
--  SHELL_VAR *v;
--
--  v = init_seconds_var ();
--
--  INIT_DYNAMIC_VAR ("BASH_COMMAND", (char *)NULL, get_bash_command, (sh_var_assign_func_t *)NULL);
--  INIT_DYNAMIC_VAR ("BASH_SUBSHELL", (char *)NULL, get_subshell, assign_subshell);
--
--  INIT_DYNAMIC_VAR ("RANDOM", (char *)NULL, get_random, assign_random);
--  VSETATTR (v, att_integer);
--  INIT_DYNAMIC_VAR ("LINENO", (char *)NULL, get_lineno, assign_lineno);
--  VSETATTR (v, att_integer);
--
--  INIT_DYNAMIC_VAR ("BASHPID", (char *)NULL, get_bashpid, null_assign);
--  VSETATTR (v, att_integer|att_readonly);
--
--#if defined (HISTORY)
--  INIT_DYNAMIC_VAR ("HISTCMD", (char *)NULL, get_histcmd, (sh_var_assign_func_t *)NULL);
--  VSETATTR (v, att_integer);
--#endif
--
--#if defined (READLINE)
--  INIT_DYNAMIC_VAR ("COMP_WORDBREAKS", (char *)NULL, get_comp_wordbreaks, assign_comp_wordbreaks);
--#endif
--
--#if defined (PUSHD_AND_POPD) && defined (ARRAY_VARS)
--  v = init_dynamic_array_var ("DIRSTACK", get_dirstack, assign_dirstack, 0);
--#endif /* PUSHD_AND_POPD && ARRAY_VARS */
--
--#if defined (ARRAY_VARS)
--  v = init_dynamic_array_var ("GROUPS", get_groupset, null_array_assign, att_noassign);
--
--#  if defined (DEBUGGER)
--  v = init_dynamic_array_var ("BASH_ARGC", get_self, null_array_assign, att_noassign|att_nounset);
--  v = init_dynamic_array_var ("BASH_ARGV", get_self, null_array_assign, att_noassign|att_nounset);
--#  endif /* DEBUGGER */
--  v = init_dynamic_array_var ("BASH_SOURCE", get_self, null_array_assign, att_noassign|att_nounset);
--  v = init_dynamic_array_var ("BASH_LINENO", get_self, null_array_assign, att_noassign|att_nounset);
--
--  v = init_dynamic_assoc_var ("BASH_CMDS", get_hashcmd, assign_hashcmd, att_nofree);
--#  if defined (ALIAS)
--  v = init_dynamic_assoc_var ("BASH_ALIASES", get_aliasvar, assign_aliasvar, att_nofree);
--#  endif
--#endif
--
--  v = init_funcname_var ();
--}
--
--/* **************************************************************** */
--/*								    */
--/*		Retrieving variables and values			    */
--/*								    */
--/* **************************************************************** */
--
--/* How to get a pointer to the shell variable or function named NAME.
--   HASHED_VARS is a pointer to the hash table containing the list
--   of interest (either variables or functions). */
--
--static SHELL_VAR *
--hash_lookup (name, hashed_vars)
--     const char *name;
--     HASH_TABLE *hashed_vars;
--{
--  BUCKET_CONTENTS *bucket;
--
--  bucket = hash_search (name, hashed_vars, 0);
--  /* If we find the name in HASHED_VARS, set LAST_TABLE_SEARCHED to that
--     table. */
--  if (bucket)
--    last_table_searched = hashed_vars;
--  return (bucket ? (SHELL_VAR *)bucket->data : (SHELL_VAR *)NULL);
--}
--
--SHELL_VAR *
--var_lookup (name, vcontext)
--     const char *name;
--     VAR_CONTEXT *vcontext;
--{
--  VAR_CONTEXT *vc;
--  SHELL_VAR *v;
--
--  v = (SHELL_VAR *)NULL;
--  for (vc = vcontext; vc; vc = vc->down)
--    if (v = hash_lookup (name, vc->table))
--      break;
--
--  return v;
--}
--
--/* Look up the variable entry named NAME.  If SEARCH_TEMPENV is non-zero,
--   then also search the temporarily built list of exported variables.
--   The lookup order is:
--	temporary_env
--	shell_variables list
--*/
--
--SHELL_VAR *
--find_variable_internal (name, force_tempenv)
--     const char *name;
--     int force_tempenv;
--{
--  SHELL_VAR *var;
--  int search_tempenv;
--  VAR_CONTEXT *vc;
--
--  var = (SHELL_VAR *)NULL;
--
--  /* If explicitly requested, first look in the temporary environment for
--     the variable.  This allows constructs such as "foo=x eval 'echo $foo'"
--     to get the `exported' value of $foo.  This happens if we are executing
--     a function or builtin, or if we are looking up a variable in a
--     "subshell environment". */
--  search_tempenv = force_tempenv || (expanding_redir == 0 && subshell_environment);
--
--  if (search_tempenv && temporary_env)		
--    var = hash_lookup (name, temporary_env);
--
--  vc = shell_variables;
--#if 0
--if (search_tempenv == 0 && /* (subshell_environment & SUBSHELL_COMSUB) && */
--    expanding_redir &&
--    (this_shell_builtin == eval_builtin || this_shell_builtin == command_builtin))
--  {
--  itrace("find_variable_internal: search_tempenv == 0: skipping VC_BLTNENV");
--  while (vc && (vc->flags & VC_BLTNENV))
--    vc = vc->down;
--  if (vc == 0)
--    vc = shell_variables;
--  }
--#endif
--
--  if (var == 0)
--    var = var_lookup (name, vc);
--
--  if (var == 0)
--    return ((SHELL_VAR *)NULL);
--
--  return (var->dynamic_value ? (*(var->dynamic_value)) (var) : var);
--}
--
--/* Look up and resolve the chain of nameref variables starting at V all the
--   way to NULL or non-nameref. */
--SHELL_VAR *
--find_variable_nameref (v)
--     SHELL_VAR *v;
--{
--  int level;
--  char *newname;
--  SHELL_VAR *orig, *oldv;
--
--  level = 0;
--  orig = v;
--  while (v && nameref_p (v))
--    {
--      level++;
--      if (level > NAMEREF_MAX)
--	return ((SHELL_VAR *)0);	/* error message here? */
--      newname = nameref_cell (v);
--      if (newname == 0 || *newname == '\0')
--	return ((SHELL_VAR *)0);
--      oldv = v;
--      v = find_variable_internal (newname, (expanding_redir == 0 && (assigning_in_environment || executing_builtin)));
--      if (v == orig || v == oldv)
--	{
--	  internal_warning (_("%s: circular name reference"), orig->name);
--	  return ((SHELL_VAR *)0);
--	}
--    }
--  return v;
--}
--
--/* Resolve the chain of nameref variables for NAME.  XXX - could change later */
--SHELL_VAR *
--find_variable_last_nameref (name)
--     const char *name;
--{
--  SHELL_VAR *v, *nv;
--  char *newname;
--  int level;
--
--  nv = v = find_variable_noref (name);
--  level = 0;
--  while (v && nameref_p (v))
--    {
--      level++;
--      if (level > NAMEREF_MAX)
--        return ((SHELL_VAR *)0);	/* error message here? */
--      newname = nameref_cell (v);
--      if (newname == 0 || *newname == '\0')
--	return ((SHELL_VAR *)0);
--      nv = v;
--      v = find_variable_internal (newname, (expanding_redir == 0 && (assigning_in_environment || executing_builtin)));
--    }
--  return nv;
--}
--
--/* Resolve the chain of nameref variables for NAME.  XXX - could change later */
--SHELL_VAR *
--find_global_variable_last_nameref (name)
--     const char *name;
--{
--  SHELL_VAR *v, *nv;
--  char *newname;
--  int level;
--
--  nv = v = find_global_variable_noref (name);
--  level = 0;
--  while (v && nameref_p (v))
--    {
--      level++;
--      if (level > NAMEREF_MAX)
--        return ((SHELL_VAR *)0);	/* error message here? */
--      newname = nameref_cell (v);
--      if (newname == 0 || *newname == '\0')
--	return ((SHELL_VAR *)0);
--      nv = v;
--      v = find_global_variable_noref (newname);
--    }
--  return nv;
--}
--
--static SHELL_VAR *
--find_nameref_at_context (v, vc)
--     SHELL_VAR *v;
--     VAR_CONTEXT *vc;
--{
--  SHELL_VAR *nv, *nv2;
--  VAR_CONTEXT *nvc;
--  char *newname;
--  int level;
--
--  nv = v;
--  level = 1;
--  while (nv && nameref_p (nv))
--    {
--      level++;
--      if (level > NAMEREF_MAX)
--        return ((SHELL_VAR *)NULL);
--      newname = nameref_cell (nv);
--      if (newname == 0 || *newname == '\0')
--        return ((SHELL_VAR *)NULL);      
--      nv2 = hash_lookup (newname, vc->table);
--      if (nv2 == 0)
--        break;
--      nv = nv2;
--    }
--  return nv;
--}
--
--/* Do nameref resolution from the VC, which is the local context for some
--   function or builtin, `up' the chain to the global variables context.  If
--   NVCP is not NULL, return the variable context where we finally ended the
--   nameref resolution (so the bind_variable_internal can use the correct
--   variable context and hash table). */
--static SHELL_VAR *
--find_variable_nameref_context (v, vc, nvcp)
--     SHELL_VAR *v;
--     VAR_CONTEXT *vc;
--     VAR_CONTEXT **nvcp;
--{
--  SHELL_VAR *nv, *nv2;
--  VAR_CONTEXT *nvc;
--
--  /* Look starting at the current context all the way `up' */
--  for (nv = v, nvc = vc; nvc; nvc = nvc->down)
--    {
--      nv2 = find_nameref_at_context (nv, nvc);
--      if (nv2 == 0)
--        continue;
--      nv = nv2;
--      if (*nvcp)
--        *nvcp = nvc;
--      if (nameref_p (nv) == 0)
--        break;
--    }
--  return (nameref_p (nv) ? (SHELL_VAR *)NULL : nv);
--}
--
--/* Do nameref resolution from the VC, which is the local context for some
--   function or builtin, `up' the chain to the global variables context.  If
--   NVCP is not NULL, return the variable context where we finally ended the
--   nameref resolution (so the bind_variable_internal can use the correct
--   variable context and hash table). */
--static SHELL_VAR *
--find_variable_last_nameref_context (v, vc, nvcp)
--     SHELL_VAR *v;
--     VAR_CONTEXT *vc;
--     VAR_CONTEXT **nvcp;
--{
--  SHELL_VAR *nv, *nv2;
--  VAR_CONTEXT *nvc;
--
--  /* Look starting at the current context all the way `up' */
--  for (nv = v, nvc = vc; nvc; nvc = nvc->down)
--    {
--      nv2 = find_nameref_at_context (nv, nvc);
--      if (nv2 == 0)
--	continue;
--      nv = nv2;
--      if (*nvcp)
--        *nvcp = nvc;
--    }
--  return (nameref_p (nv) ? nv : (SHELL_VAR *)NULL);
--}
--
--/* Find a variable, forcing a search of the temporary environment first */
--SHELL_VAR *
--find_variable_tempenv (name)
--     const char *name;
--{
--  SHELL_VAR *var;
--
--  var = find_variable_internal (name, 1);
--  if (var && nameref_p (var))
--    var = find_variable_nameref (var);
--  return (var);
--}
--
--/* Find a variable, not forcing a search of the temporary environment first */
--SHELL_VAR *
--find_variable_notempenv (name)
--     const char *name;
--{
--  SHELL_VAR *var;
--
--  var = find_variable_internal (name, 0);
--  if (var && nameref_p (var))
--    var = find_variable_nameref (var);
--  return (var);
--}
--
--SHELL_VAR *
--find_global_variable (name)
--     const char *name;
--{
--  SHELL_VAR *var;
--
--  var = var_lookup (name, global_variables);
--  if (var && nameref_p (var))
--    var = find_variable_nameref (var);
--
--  if (var == 0)
--    return ((SHELL_VAR *)NULL);
--
--  return (var->dynamic_value ? (*(var->dynamic_value)) (var) : var);
--}
--
--SHELL_VAR *
--find_global_variable_noref (name)
--     const char *name;
--{
--  SHELL_VAR *var;
--
--  var = var_lookup (name, global_variables);
--
--  if (var == 0)
--    return ((SHELL_VAR *)NULL);
--
--  return (var->dynamic_value ? (*(var->dynamic_value)) (var) : var);
--}
--
--SHELL_VAR *
--find_shell_variable (name)
--     const char *name;
--{
--  SHELL_VAR *var;
--
--  var = var_lookup (name, shell_variables);
--  if (var && nameref_p (var))
--    var = find_variable_nameref (var);
--
--  if (var == 0)
--    return ((SHELL_VAR *)NULL);
--
--  return (var->dynamic_value ? (*(var->dynamic_value)) (var) : var);
--}
--
--/* Look up the variable entry named NAME.  Returns the entry or NULL. */
--SHELL_VAR *
--find_variable (name)
--     const char *name;
--{
--  SHELL_VAR *v;
--
--  last_table_searched = 0;
--  v = find_variable_internal (name, (expanding_redir == 0 && (assigning_in_environment || executing_builtin)));
--  if (v && nameref_p (v))
--    v = find_variable_nameref (v);
--  return v;
--}
--
--SHELL_VAR *
--find_variable_noref (name)
--     const char *name;
--{
--  SHELL_VAR *v;
--
--  v = find_variable_internal (name, (expanding_redir == 0 && (assigning_in_environment || executing_builtin)));
--  return v;
--}
--
--/* Look up the function entry whose name matches STRING.
--   Returns the entry or NULL. */
--SHELL_VAR *
--find_function (name)
--     const char *name;
--{
--  return (hash_lookup (name, shell_functions));
--}
--
--/* Find the function definition for the shell function named NAME.  Returns
--   the entry or NULL. */
--FUNCTION_DEF *
--find_function_def (name)
--     const char *name;
--{
--#if defined (DEBUGGER)
--  return ((FUNCTION_DEF *)hash_lookup (name, shell_function_defs));
--#else
--  return ((FUNCTION_DEF *)0);
--#endif
--}
--
--/* Return the value of VAR.  VAR is assumed to have been the result of a
--   lookup without any subscript, if arrays are compiled into the shell. */
--char *
--get_variable_value (var)
--     SHELL_VAR *var;
--{
--  if (var == 0)
--    return ((char *)NULL);
--#if defined (ARRAY_VARS)
--  else if (array_p (var))
--    return (array_reference (array_cell (var), 0));
--  else if (assoc_p (var))
--    return (assoc_reference (assoc_cell (var), "0"));
--#endif
--  else
--    return (value_cell (var));
--}
--
--/* Return the string value of a variable.  Return NULL if the variable
--   doesn't exist.  Don't cons a new string.  This is a potential memory
--   leak if the variable is found in the temporary environment.  Since
--   functions and variables have separate name spaces, returns NULL if
--   var_name is a shell function only. */
--char *
--get_string_value (var_name)
--     const char *var_name;
--{
--  SHELL_VAR *var;
--
--  var = find_variable (var_name);
--  return ((var) ? get_variable_value (var) : (char *)NULL);
--}
--
--/* This is present for use by the tilde and readline libraries. */
--char *
--sh_get_env_value (v)
--     const char *v;
--{
--  return get_string_value (v);
--}
--
--/* **************************************************************** */
--/*								    */
--/*		  Creating and setting variables		    */
--/*								    */
--/* **************************************************************** */
--
--/* Set NAME to VALUE if NAME has no value. */
--SHELL_VAR *
--set_if_not (name, value)
--     char *name, *value;
--{
--  SHELL_VAR *v;
--
--  if (shell_variables == 0)
--    create_variable_tables ();
--
--  v = find_variable (name);
--  if (v == 0)
--    v = bind_variable_internal (name, value, global_variables->table, HASH_NOSRCH, 0);
--  return (v);
--}
--
--/* Create a local variable referenced by NAME. */
--SHELL_VAR *
--make_local_variable (name)
--     const char *name;
--{
--  SHELL_VAR *new_var, *old_var;
--  VAR_CONTEXT *vc;
--  int was_tmpvar;
--  char *tmp_value;
--
--  /* local foo; local foo;  is a no-op. */
--  old_var = find_variable (name);
--  if (old_var && local_p (old_var) && old_var->context == variable_context)
--    return (old_var);
--
--  was_tmpvar = old_var && tempvar_p (old_var);
--  /* If we're making a local variable in a shell function, the temporary env
--     has already been merged into the function's variable context stack.  We
--     can assume that a temporary var in the same context appears in the same
--     VAR_CONTEXT and can safely be returned without creating a new variable
--     (which results in duplicate names in the same VAR_CONTEXT->table */
--  /* We can't just test tmpvar_p because variables in the temporary env given
--     to a shell function appear in the function's local variable VAR_CONTEXT
--     but retain their tempvar attribute.  We want temporary variables that are
--     found in temporary_env, hence the test for last_table_searched, which is
--     set in hash_lookup and only (so far) checked here. */
--  if (was_tmpvar && old_var->context == variable_context && last_table_searched != temporary_env)
--    {
--      VUNSETATTR (old_var, att_invisible);
--      return (old_var);
--    }
--  if (was_tmpvar)
--    tmp_value = value_cell (old_var);
--
--  for (vc = shell_variables; vc; vc = vc->down)
--    if (vc_isfuncenv (vc) && vc->scope == variable_context)
--      break;
--
--  if (vc == 0)
--    {
--      internal_error (_("make_local_variable: no function context at current scope"));
--      return ((SHELL_VAR *)NULL);
--    }
--  else if (vc->table == 0)
--    vc->table = hash_create (TEMPENV_HASH_BUCKETS);
--
--  /* Since this is called only from the local/declare/typeset code, we can
--     call builtin_error here without worry (of course, it will also work
--     for anything that sets this_command_name).  Variables with the `noassign'
--     attribute may not be made local.  The test against old_var's context
--     level is to disallow local copies of readonly global variables (since I
--     believe that this could be a security hole).  Readonly copies of calling
--     function local variables are OK. */
--  if (old_var && (noassign_p (old_var) ||
--		 (readonly_p (old_var) && old_var->context == 0)))
--    {
--      if (readonly_p (old_var))
--	sh_readonly (name);
--      else if (noassign_p (old_var))
--	builtin_error (_("%s: variable may not be assigned value"), name);
--#if 0
--      /* Let noassign variables through with a warning */
--      if (readonly_p (old_var))
--#endif
--	return ((SHELL_VAR *)NULL);
--    }
--
--  if (old_var == 0)
--    new_var = make_new_variable (name, vc->table);
--  else
--    {
--      new_var = make_new_variable (name, vc->table);
--
--      /* If we found this variable in one of the temporary environments,
--	 inherit its value.  Watch to see if this causes problems with
--	 things like `x=4 local x'. XXX - see above for temporary env
--	 variables with the same context level as variable_context */
--      /* XXX - we should only do this if the variable is not an array. */
--      if (was_tmpvar)
--	var_setvalue (new_var, savestring (tmp_value));
--
--      new_var->attributes = exported_p (old_var) ? att_exported : 0;
--    }
--
--  vc->flags |= VC_HASLOCAL;
--
--  new_var->context = variable_context;
--  VSETATTR (new_var, att_local);
--
--  if (ifsname (name))
--    setifs (new_var);
--
--  if (was_tmpvar == 0)
--    VSETATTR (new_var, att_invisible);	/* XXX */
--  return (new_var);
--}
--
--/* Create a new shell variable with name NAME. */
--static SHELL_VAR *
--new_shell_variable (name)
--     const char *name;
--{
--  SHELL_VAR *entry;
--
--  entry = (SHELL_VAR *)xmalloc (sizeof (SHELL_VAR));
--
--  entry->name = savestring (name);
--  var_setvalue (entry, (char *)NULL);
--  CLEAR_EXPORTSTR (entry);
--
--  entry->dynamic_value = (sh_var_value_func_t *)NULL;
--  entry->assign_func = (sh_var_assign_func_t *)NULL;
--
--  entry->attributes = 0;
--
--  /* Always assume variables are to be made at toplevel!
--     make_local_variable has the responsibility of changing the
--     variable context. */
--  entry->context = 0;
--
--  return (entry);
--}
--
--/* Create a new shell variable with name NAME and add it to the hash table
--   TABLE. */
--static SHELL_VAR *
--make_new_variable (name, table)
--     const char *name;
--     HASH_TABLE *table;
--{
--  SHELL_VAR *entry;
--  BUCKET_CONTENTS *elt;
--
--  entry = new_shell_variable (name);
--
--  /* Make sure we have a shell_variables hash table to add to. */
--  if (shell_variables == 0)
--    create_variable_tables ();
--
--  elt = hash_insert (savestring (name), table, HASH_NOSRCH);
--  elt->data = (PTR_T)entry;
--
--  return entry;
--}
--
--#if defined (ARRAY_VARS)
--SHELL_VAR *
--make_new_array_variable (name)
--     char *name;
--{
--  SHELL_VAR *entry;
--  ARRAY *array;
--
--  entry = make_new_variable (name, global_variables->table);
--  array = array_create ();
--
--  var_setarray (entry, array);
--  VSETATTR (entry, att_array);
--  return entry;
--}
--
--SHELL_VAR *
--make_local_array_variable (name, assoc_ok)
--     char *name;
--     int assoc_ok;
--{
--  SHELL_VAR *var;
--  ARRAY *array;
--
--  var = make_local_variable (name);
--  if (var == 0 || array_p (var) || (assoc_ok && assoc_p (var)))
--    return var;
--
--  array = array_create ();
--
--  dispose_variable_value (var);
--  var_setarray (var, array);
--  VSETATTR (var, att_array);
--  return var;
--}
--
--SHELL_VAR *
--make_new_assoc_variable (name)
--     char *name;
--{
--  SHELL_VAR *entry;
--  HASH_TABLE *hash;
--
--  entry = make_new_variable (name, global_variables->table);
--  hash = assoc_create (0);
--
--  var_setassoc (entry, hash);
--  VSETATTR (entry, att_assoc);
--  return entry;
--}
--
--SHELL_VAR *
--make_local_assoc_variable (name)
--     char *name;
--{
--  SHELL_VAR *var;
--  HASH_TABLE *hash;
--
--  var = make_local_variable (name);
--  if (var == 0 || assoc_p (var))
--    return var;
--
--  dispose_variable_value (var);
--  hash = assoc_create (0);
--
--  var_setassoc (var, hash);
--  VSETATTR (var, att_assoc);
--  return var;
--}
--#endif
--
--char *
--make_variable_value (var, value, flags)
--     SHELL_VAR *var;
--     char *value;
--     int flags;
--{
--  char *retval, *oval;
--  intmax_t lval, rval;
--  int expok, olen, op;
--
--  /* If this variable has had its type set to integer (via `declare -i'),
--     then do expression evaluation on it and store the result.  The
--     functions in expr.c (evalexp()) and bind_int_variable() are responsible
--     for turning off the integer flag if they don't want further
--     evaluation done. */
--  if (integer_p (var))
--    {
--      if (flags & ASS_APPEND)
--	{
--	  oval = value_cell (var);
--	  lval = evalexp (oval, &expok);	/* ksh93 seems to do this */
--	  if (expok == 0)
--	    {
--	      top_level_cleanup ();
--	      jump_to_top_level (DISCARD);
--	    }
--	}
--      rval = evalexp (value, &expok);
--      if (expok == 0)
--	{
--	  top_level_cleanup ();
--	  jump_to_top_level (DISCARD);
--	}
--      /* This can be fooled if the variable's value changes while evaluating
--	 `rval'.  We can change it if we move the evaluation of lval to here. */
--      if (flags & ASS_APPEND)
--	rval += lval;
--      retval = itos (rval);
--    }
--#if defined (CASEMOD_ATTRS)
--  else if (capcase_p (var) || uppercase_p (var) || lowercase_p (var))
--    {
--      if (flags & ASS_APPEND)
--	{
--	  oval = get_variable_value (var);
--	  if (oval == 0)	/* paranoia */
--	    oval = "";
--	  olen = STRLEN (oval);
--	  retval = (char *)xmalloc (olen + (value ? STRLEN (value) : 0) + 1);
--	  strcpy (retval, oval);
--	  if (value)
--	    strcpy (retval+olen, value);
--	}
--      else if (*value)
--	retval = savestring (value);
--      else
--	{
--	  retval = (char *)xmalloc (1);
--	  retval[0] = '\0';
--	}
--      op = capcase_p (var) ? CASE_CAPITALIZE
--			 : (uppercase_p (var) ? CASE_UPPER : CASE_LOWER);
--      oval = sh_modcase (retval, (char *)0, op);
--      free (retval);
--      retval = oval;
--    }
--#endif /* CASEMOD_ATTRS */
--  else if (value)
--    {
--      if (flags & ASS_APPEND)
--	{
--	  oval = get_variable_value (var);
--	  if (oval == 0)	/* paranoia */
--	    oval = "";
--	  olen = STRLEN (oval);
--	  retval = (char *)xmalloc (olen + (value ? STRLEN (value) : 0) + 1);
--	  strcpy (retval, oval);
--	  if (value)
--	    strcpy (retval+olen, value);
--	}
--      else if (*value)
--	retval = savestring (value);
--      else
--	{
--	  retval = (char *)xmalloc (1);
--	  retval[0] = '\0';
--	}
--    }
--  else
--    retval = (char *)NULL;
--
--  return retval;
--}
--
--/* Bind a variable NAME to VALUE in the HASH_TABLE TABLE, which may be the
--   temporary environment (but usually is not). */
--static SHELL_VAR *
--bind_variable_internal (name, value, table, hflags, aflags)
--     const char *name;
--     char *value;
--     HASH_TABLE *table;
--     int hflags, aflags;
--{
--  char *newval;
--  SHELL_VAR *entry;
--
--  entry = (hflags & HASH_NOSRCH) ? (SHELL_VAR *)NULL : hash_lookup (name, table);
--  /* Follow the nameref chain here if this is the global variables table */
--  if (entry && nameref_p (entry) && (invisible_p (entry) == 0) && table == global_variables->table)
--    {
--      entry = find_global_variable (entry->name);
--      /* Let's see if we have a nameref referencing a variable that hasn't yet
--	 been created. */
--      if (entry == 0)
--	entry = find_variable_last_nameref (name);	/* XXX */
--      if (entry == 0)					/* just in case */
--        return (entry);
--    }
--
--  /* The first clause handles `declare -n ref; ref=x;' */
--  if (entry && invisible_p (entry) && nameref_p (entry))
--    goto assign_value;
--  else if (entry && nameref_p (entry))
--    {
--      newval = nameref_cell (entry);
--#if defined (ARRAY_VARS)
--      /* declare -n foo=x[2] */
--      if (valid_array_reference (newval))
--        /* XXX - should it be aflags? */
--	entry = assign_array_element (newval, make_variable_value (entry, value, 0), aflags);
--      else
--#endif
--      {
--      entry = make_new_variable (newval, table);
--      var_setvalue (entry, make_variable_value (entry, value, 0));
--      }
--    }
--  else if (entry == 0)
--    {
--      entry = make_new_variable (name, table);
--      var_setvalue (entry, make_variable_value (entry, value, 0)); /* XXX */
--    }
--  else if (entry->assign_func)	/* array vars have assign functions now */
--    {
--      INVALIDATE_EXPORTSTR (entry);
--      newval = (aflags & ASS_APPEND) ? make_variable_value (entry, value, aflags) : value;
--      if (assoc_p (entry))
--	entry = (*(entry->assign_func)) (entry, newval, -1, savestring ("0"));
--      else if (array_p (entry))
--	entry = (*(entry->assign_func)) (entry, newval, 0, 0);
--      else
--	entry = (*(entry->assign_func)) (entry, newval, -1, 0);
--      if (newval != value)
--	free (newval);
--      return (entry);
--    }
--  else
--    {
--assign_value:
--      if (readonly_p (entry) || noassign_p (entry))
--	{
--	  if (readonly_p (entry))
--	    err_readonly (name);
--	  return (entry);
--	}
--
--      /* Variables which are bound are visible. */
--      VUNSETATTR (entry, att_invisible);
--
--#if defined (ARRAY_VARS)
--      if (assoc_p (entry) || array_p (entry))
--        newval = make_array_variable_value (entry, 0, "0", value, aflags);
--      else
--#endif
--
--      newval = make_variable_value (entry, value, aflags);	/* XXX */
--
--      /* Invalidate any cached export string */
--      INVALIDATE_EXPORTSTR (entry);
--
--#if defined (ARRAY_VARS)
--      /* XXX -- this bears looking at again -- XXX */
--      /* If an existing array variable x is being assigned to with x=b or
--	 `read x' or something of that nature, silently convert it to
--	 x[0]=b or `read x[0]'. */
--      if (assoc_p (entry))
--	{
--	  assoc_insert (assoc_cell (entry), savestring ("0"), newval);
--	  free (newval);
--	}
--      else if (array_p (entry))
--	{
--	  array_insert (array_cell (entry), 0, newval);
--	  free (newval);
--	}
--      else
--#endif
--	{
--	  FREE (value_cell (entry));
--	  var_setvalue (entry, newval);
--	}
--    }
--
--  if (mark_modified_vars)
--    VSETATTR (entry, att_exported);
--
--  if (exported_p (entry))
--    array_needs_making = 1;
--
--  return (entry);
--}
--	
--/* Bind a variable NAME to VALUE.  This conses up the name
--   and value strings.  If we have a temporary environment, we bind there
--   first, then we bind into shell_variables. */
--
--SHELL_VAR *
--bind_variable (name, value, flags)
--     const char *name;
--     char *value;
--     int flags;
--{
--  SHELL_VAR *v, *nv;
--  VAR_CONTEXT *vc, *nvc;
--  int level;
--
--  if (shell_variables == 0)
--    create_variable_tables ();
--
--  /* If we have a temporary environment, look there first for the variable,
--     and, if found, modify the value there before modifying it in the
--     shell_variables table.  This allows sourced scripts to modify values
--     given to them in a temporary environment while modifying the variable
--     value that the caller sees. */
--  if (temporary_env)
--    bind_tempenv_variable (name, value);
--
--  /* XXX -- handle local variables here. */
--  for (vc = shell_variables; vc; vc = vc->down)
--    {
--      if (vc_isfuncenv (vc) || vc_isbltnenv (vc))
--	{
--	  v = hash_lookup (name, vc->table);
--	  nvc = vc;
--	  if (v && nameref_p (v))
--	    {
--	      nv = find_variable_nameref_context (v, vc, &nvc);
--	      if (nv == 0)
--		{
--		  nv = find_variable_last_nameref_context (v, vc, &nvc);
--		  if (nv && nameref_p (nv))
--		    {
--		      /* If this nameref variable doesn't have a value yet,
--			 set the value.  Otherwise, assign using the value as
--			 normal. */
--		      if (nameref_cell (nv) == 0)
--			return (bind_variable_internal (nv->name, value, nvc->table, 0, flags));
--		      return (bind_variable_internal (nameref_cell (nv), value, nvc->table, 0, flags));
--		    }
--		  else
--		    v = nv;
--		}
--	      else
--	        v = nv;
--	    }
--	  if (v)
--	    return (bind_variable_internal (v->name, value, nvc->table, 0, flags));
--	}
--    }
--  /* bind_variable_internal will handle nameref resolution in this case */
--  return (bind_variable_internal (name, value, global_variables->table, 0, flags));
--}
--
--SHELL_VAR *
--bind_global_variable (name, value, flags)
--     const char *name;
--     char *value;
--     int flags;
--{
--  SHELL_VAR *v, *nv;
--  VAR_CONTEXT *vc, *nvc;
--  int level;
--
--  if (shell_variables == 0)
--    create_variable_tables ();
--
--  /* bind_variable_internal will handle nameref resolution in this case */
--  return (bind_variable_internal (name, value, global_variables->table, 0, flags));
--}
--
--/* Make VAR, a simple shell variable, have value VALUE.  Once assigned a
--   value, variables are no longer invisible.  This is a duplicate of part
--   of the internals of bind_variable.  If the variable is exported, or
--   all modified variables should be exported, mark the variable for export
--   and note that the export environment needs to be recreated. */
--SHELL_VAR *
--bind_variable_value (var, value, aflags)
--     SHELL_VAR *var;
--     char *value;
--     int aflags;
--{
--  char *t;
--  int invis;
--
--  invis = invisible_p (var);
--  VUNSETATTR (var, att_invisible);
--
--  if (var->assign_func)
--    {
--      /* If we're appending, we need the old value, so use
--	 make_variable_value */
--      t = (aflags & ASS_APPEND) ? make_variable_value (var, value, aflags) : value;
--      (*(var->assign_func)) (var, t, -1, 0);
--      if (t != value && t)
--	free (t);      
--    }
--  else
--    {
--      t = make_variable_value (var, value, aflags);
--#if defined (ARRAY_VARS)
--      if ((aflags & ASS_NAMEREF) && (t == 0 || *t == 0 || (legal_identifier (t) == 0 && valid_array_reference (t) == 0)))
--#else
--      if ((aflags & ASS_NAMEREF) && (t == 0 || *t == 0 || legal_identifier (t) == 0))
--#endif
--	{
--	  free (t);
--	  if (invis)
--	    VSETATTR (var, att_invisible);	/* XXX */
--	  return ((SHELL_VAR *)NULL);
--	}
--      FREE (value_cell (var));
--      var_setvalue (var, t);
--    }
--
--  INVALIDATE_EXPORTSTR (var);
--
--  if (mark_modified_vars)
--    VSETATTR (var, att_exported);
--
--  if (exported_p (var))
--    array_needs_making = 1;
--
--  return (var);
--}
--
--/* Bind/create a shell variable with the name LHS to the RHS.
--   This creates or modifies a variable such that it is an integer.
--
--   This used to be in expr.c, but it is here so that all of the
--   variable binding stuff is localized.  Since we don't want any
--   recursive evaluation from bind_variable() (possible without this code,
--   since bind_variable() calls the evaluator for variables with the integer
--   attribute set), we temporarily turn off the integer attribute for each
--   variable we set here, then turn it back on after binding as necessary. */
--
--SHELL_VAR *
--bind_int_variable (lhs, rhs)
--     char *lhs, *rhs;
--{
--  register SHELL_VAR *v;
--  int isint, isarr, implicitarray;
--
--  isint = isarr = implicitarray = 0;
--#if defined (ARRAY_VARS)
--  if (valid_array_reference (lhs))
--    {
--      isarr = 1;
--      v = array_variable_part (lhs, (char **)0, (int *)0);
--    }
--  else
--#endif
--    v = find_variable (lhs);
--
--  if (v)
--    {
--      isint = integer_p (v);
--      VUNSETATTR (v, att_integer);
--#if defined (ARRAY_VARS)
--      if (array_p (v) && isarr == 0)
--	implicitarray = 1;
--#endif
--    }
--
--#if defined (ARRAY_VARS)
--  if (isarr)
--    v = assign_array_element (lhs, rhs, 0);
--  else if (implicitarray)
--    v = bind_array_variable (lhs, 0, rhs, 0);
--  else
--#endif
--    v = bind_variable (lhs, rhs, 0);
--
--  if (v && isint)
--    VSETATTR (v, att_integer);
--
--  VUNSETATTR (v, att_invisible);
--
--  return (v);
--}
--
--SHELL_VAR *
--bind_var_to_int (var, val)
--     char *var;
--     intmax_t val;
--{
--  char ibuf[INT_STRLEN_BOUND (intmax_t) + 1], *p;
--
--  p = fmtulong (val, 10, ibuf, sizeof (ibuf), 0);
--  return (bind_int_variable (var, p));
--}
--
--/* Do a function binding to a variable.  You pass the name and
--   the command to bind to.  This conses the name and command. */
--SHELL_VAR *
--bind_function (name, value)
--     const char *name;
--     COMMAND *value;
--{
--  SHELL_VAR *entry;
--
--  entry = find_function (name);
--  if (entry == 0)
--    {
--      BUCKET_CONTENTS *elt;
--
--      elt = hash_insert (savestring (name), shell_functions, HASH_NOSRCH);
--      entry = new_shell_variable (name);
--      elt->data = (PTR_T)entry;
--    }
--  else
--    INVALIDATE_EXPORTSTR (entry);
--
--  if (var_isset (entry))
--    dispose_command (function_cell (entry));
--
--  if (value)
--    var_setfunc (entry, copy_command (value));
--  else
--    var_setfunc (entry, 0);
--
--  VSETATTR (entry, att_function);
--
--  if (mark_modified_vars)
--    VSETATTR (entry, att_exported);
--
--  VUNSETATTR (entry, att_invisible);		/* Just to be sure */
--
--  if (exported_p (entry))
--    array_needs_making = 1;
--
--#if defined (PROGRAMMABLE_COMPLETION)
--  set_itemlist_dirty (&it_functions);
--#endif
--
--  return (entry);
--}
--
--#if defined (DEBUGGER)
--/* Bind a function definition, which includes source file and line number
--   information in addition to the command, into the FUNCTION_DEF hash table.*/
--void
--bind_function_def (name, value)
--     const char *name;
--     FUNCTION_DEF *value;
--{
--  FUNCTION_DEF *entry;
--  BUCKET_CONTENTS *elt;
--  COMMAND *cmd;
--
--  entry = find_function_def (name);
--  if (entry)
--    {
--      dispose_function_def_contents (entry);
--      entry = copy_function_def_contents (value, entry);
--    }
--  else
--    {
--      cmd = value->command;
--      value->command = 0;
--      entry = copy_function_def (value);
--      value->command = cmd;
--
--      elt = hash_insert (savestring (name), shell_function_defs, HASH_NOSRCH);
--      elt->data = (PTR_T *)entry;
--    }
--}
--#endif /* DEBUGGER */
--
--/* Add STRING, which is of the form foo=bar, to the temporary environment
--   HASH_TABLE (temporary_env).  The functions in execute_cmd.c are
--   responsible for moving the main temporary env to one of the other
--   temporary environments.  The expansion code in subst.c calls this. */
--int
--assign_in_env (word, flags)
--     WORD_DESC *word;
--     int flags;
--{
--  int offset, aflags;
--  char *name, *temp, *value;
--  SHELL_VAR *var;
--  const char *string;
--
--  string = word->word;
--
--  aflags = 0;
--  offset = assignment (string, 0);
--  name = savestring (string);
--  value = (char *)NULL;
--
--  if (name[offset] == '=')
--    {
--      name[offset] = 0;
--
--      /* don't ignore the `+' when assigning temporary environment */
--      if (name[offset - 1] == '+')
--	{
--	  name[offset - 1] = '\0';
--	  aflags |= ASS_APPEND;
--	}
--
--      var = find_variable (name);
--      if (var && (readonly_p (var) || noassign_p (var)))
--	{
--	  if (readonly_p (var))
--	    err_readonly (name);
--	  free (name);
--  	  return (0);
--	}
--
--      temp = name + offset + 1;
--      value = expand_assignment_string_to_string (temp, 0);
--
--      if (var && (aflags & ASS_APPEND))
--	{
--	  temp = make_variable_value (var, value, aflags);
--	  FREE (value);
--	  value = temp;
--	}
--    }
--
--  if (temporary_env == 0)
--    temporary_env = hash_create (TEMPENV_HASH_BUCKETS);
--
--  var = hash_lookup (name, temporary_env);
--  if (var == 0)
--    var = make_new_variable (name, temporary_env);
--  else
--    FREE (value_cell (var));
--
--  if (value == 0)
--    {
--      value = (char *)xmalloc (1);	/* like do_assignment_internal */
--      value[0] = '\0';
--    }
--
--  var_setvalue (var, value);
--  var->attributes |= (att_exported|att_tempvar);
--  var->context = variable_context;	/* XXX */
--
--  INVALIDATE_EXPORTSTR (var);
--  var->exportstr = mk_env_string (name, value, 0);
--
--  array_needs_making = 1;
--
--  if (flags)
--    stupidly_hack_special_variables (name);
--
--  if (echo_command_at_execute)
--    /* The Korn shell prints the `+ ' in front of assignment statements,
--	so we do too. */
--    xtrace_print_assignment (name, value, 0, 1);
--
--  free (name);
--  return 1;
--}
--
--/* **************************************************************** */
--/*								    */
--/*			Copying variables			    */
--/*								    */
--/* **************************************************************** */
--
--#ifdef INCLUDE_UNUSED
--/* Copy VAR to a new data structure and return that structure. */
--SHELL_VAR *
--copy_variable (var)
--     SHELL_VAR *var;
--{
--  SHELL_VAR *copy = (SHELL_VAR *)NULL;
--
--  if (var)
--    {
--      copy = (SHELL_VAR *)xmalloc (sizeof (SHELL_VAR));
--
--      copy->attributes = var->attributes;
--      copy->name = savestring (var->name);
--
--      if (function_p (var))
--	var_setfunc (copy, copy_command (function_cell (var)));
--#if defined (ARRAY_VARS)
--      else if (array_p (var))
--	var_setarray (copy, array_copy (array_cell (var)));
--      else if (assoc_p (var))
--	var_setassoc (copy, assoc_copy (assoc_cell (var)));
--#endif
--      else if (nameref_cell (var))	/* XXX - nameref */
--	var_setref (copy, savestring (nameref_cell (var)));
--      else if (value_cell (var))	/* XXX - nameref */
--	var_setvalue (copy, savestring (value_cell (var)));
--      else
--	var_setvalue (copy, (char *)NULL);
--
--      copy->dynamic_value = var->dynamic_value;
--      copy->assign_func = var->assign_func;
--
--      copy->exportstr = COPY_EXPORTSTR (var);
--
--      copy->context = var->context;
--    }
--  return (copy);
--}
--#endif
--
--/* **************************************************************** */
--/*								    */
--/*		  Deleting and unsetting variables		    */
--/*								    */
--/* **************************************************************** */
--
--/* Dispose of the information attached to VAR. */
--static void
--dispose_variable_value (var)
--     SHELL_VAR *var;
--{
--  if (function_p (var))
--    dispose_command (function_cell (var));
--#if defined (ARRAY_VARS)
--  else if (array_p (var))
--    array_dispose (array_cell (var));
--  else if (assoc_p (var))
--    assoc_dispose (assoc_cell (var));
--#endif
--  else if (nameref_p (var))
--    FREE (nameref_cell (var));
--  else
--    FREE (value_cell (var));
--}
--
--void
--dispose_variable (var)
--     SHELL_VAR *var;
--{
--  if (var == 0)
--    return;
--
--  if (nofree_p (var) == 0)
--    dispose_variable_value (var);
--
--  FREE_EXPORTSTR (var);
--
--  free (var->name);
--
--  if (exported_p (var))
--    array_needs_making = 1;
--
--  free (var);
--}
--
--/* Unset the shell variable referenced by NAME.  Unsetting a nameref variable
--   unsets the variable it resolves to but leaves the nameref alone. */
--int
--unbind_variable (name)
--     const char *name;
--{
--  SHELL_VAR *v, *nv;
--  int r;
--
--  v = var_lookup (name, shell_variables);
--  nv = (v && nameref_p (v)) ? find_variable_nameref (v) : (SHELL_VAR *)NULL;
--
--  r = nv ? makunbound (nv->name, shell_variables) : makunbound (name, shell_variables);
--  return r;
--}
--
--/* Unbind NAME, where NAME is assumed to be a nameref variable */
--int
--unbind_nameref (name)
--     const char *name;
--{
--  SHELL_VAR *v;
--
--  v = var_lookup (name, shell_variables);
--  if (v && nameref_p (v))
--    return makunbound (name, shell_variables);
--  return 0;
--}
--
--/* Unset the shell function named NAME. */
--int
--unbind_func (name)
--     const char *name;
--{
--  BUCKET_CONTENTS *elt;
--  SHELL_VAR *func;
--
--  elt = hash_remove (name, shell_functions, 0);
--
--  if (elt == 0)
--    return -1;
--
--#if defined (PROGRAMMABLE_COMPLETION)
--  set_itemlist_dirty (&it_functions);
--#endif
--
--  func = (SHELL_VAR *)elt->data;
--  if (func)
--    {
--      if (exported_p (func))
--	array_needs_making++;
--      dispose_variable (func);
--    }
--
--  free (elt->key);
--  free (elt);
--
--  return 0;  
--}
--
--#if defined (DEBUGGER)
--int
--unbind_function_def (name)
--     const char *name;
--{
--  BUCKET_CONTENTS *elt;
--  FUNCTION_DEF *funcdef;
--
--  elt = hash_remove (name, shell_function_defs, 0);
--
--  if (elt == 0)
--    return -1;
--
--  funcdef = (FUNCTION_DEF *)elt->data;
--  if (funcdef)
--    dispose_function_def (funcdef);
--
--  free (elt->key);
--  free (elt);
--
--  return 0;  
--}
--#endif /* DEBUGGER */
--
--int
--delete_var (name, vc)
--     const char *name;
--     VAR_CONTEXT *vc;
--{
--  BUCKET_CONTENTS *elt;
--  SHELL_VAR *old_var;
--  VAR_CONTEXT *v;
--
--  for (elt = (BUCKET_CONTENTS *)NULL, v = vc; v; v = v->down)
--    if (elt = hash_remove (name, v->table, 0))
--      break;
--
--  if (elt == 0)
--    return (-1);
--
--  old_var = (SHELL_VAR *)elt->data;
--  free (elt->key);
--  free (elt);
--
--  dispose_variable (old_var);
--  return (0);
--}
--
--/* Make the variable associated with NAME go away.  HASH_LIST is the
--   hash table from which this variable should be deleted (either
--   shell_variables or shell_functions).
--   Returns non-zero if the variable couldn't be found. */
--int
--makunbound (name, vc)
--     const char *name;
--     VAR_CONTEXT *vc;
--{
--  BUCKET_CONTENTS *elt, *new_elt;
--  SHELL_VAR *old_var;
--  VAR_CONTEXT *v;
--  char *t;
--
--  for (elt = (BUCKET_CONTENTS *)NULL, v = vc; v; v = v->down)
--    if (elt = hash_remove (name, v->table, 0))
--      break;
--
--  if (elt == 0)
--    return (-1);
--
--  old_var = (SHELL_VAR *)elt->data;
--
--  if (old_var && exported_p (old_var))
--    array_needs_making++;
--
--  /* If we're unsetting a local variable and we're still executing inside
--     the function, just mark the variable as invisible.  The function
--     eventually called by pop_var_context() will clean it up later.  This
--     must be done so that if the variable is subsequently assigned a new
--     value inside the function, the `local' attribute is still present.
--     We also need to add it back into the correct hash table. */
--  if (old_var && local_p (old_var) && variable_context == old_var->context)
--    {
--      if (nofree_p (old_var))
--	var_setvalue (old_var, (char *)NULL);
--#if defined (ARRAY_VARS)
--      else if (array_p (old_var))
--	array_dispose (array_cell (old_var));
--      else if (assoc_p (old_var))
--	assoc_dispose (assoc_cell (old_var));
--#endif
--      else if (nameref_p (old_var))
--	FREE (nameref_cell (old_var));
--      else
--	FREE (value_cell (old_var));
--      /* Reset the attributes.  Preserve the export attribute if the variable
--	 came from a temporary environment.  Make sure it stays local, and
--	 make it invisible. */ 
--      old_var->attributes = (exported_p (old_var) && tempvar_p (old_var)) ? att_exported : 0;
--      VSETATTR (old_var, att_local);
--      VSETATTR (old_var, att_invisible);
--      var_setvalue (old_var, (char *)NULL);
--      INVALIDATE_EXPORTSTR (old_var);
--
--      new_elt = hash_insert (savestring (old_var->name), v->table, 0);
--      new_elt->data = (PTR_T)old_var;
--      stupidly_hack_special_variables (old_var->name);
--
--      free (elt->key);
--      free (elt);
--      return (0);
--    }
--
--  /* Have to save a copy of name here, because it might refer to
--     old_var->name.  If so, stupidly_hack_special_variables will
--     reference freed memory. */
--  t = savestring (name);
--
--  free (elt->key);
--  free (elt);
--
--  dispose_variable (old_var);
--  stupidly_hack_special_variables (t);
--  free (t);
--
--  return (0);
--}
--
--/* Get rid of all of the variables in the current context. */
--void
--kill_all_local_variables ()
--{
--  VAR_CONTEXT *vc;
--
--  for (vc = shell_variables; vc; vc = vc->down)
--    if (vc_isfuncenv (vc) && vc->scope == variable_context)
--      break;
--  if (vc == 0)
--    return;		/* XXX */
--
--  if (vc->table && vc_haslocals (vc))
--    {
--      delete_all_variables (vc->table);
--      hash_dispose (vc->table);
--    }
--  vc->table = (HASH_TABLE *)NULL;
--}
--
--static void
--free_variable_hash_data (data)
--     PTR_T data;
--{
--  SHELL_VAR *var;
--
--  var = (SHELL_VAR *)data;
--  dispose_variable (var);
--}
--
--/* Delete the entire contents of the hash table. */
--void
--delete_all_variables (hashed_vars)
--     HASH_TABLE *hashed_vars;
--{
--  hash_flush (hashed_vars, free_variable_hash_data);
--}
--
--/* **************************************************************** */
--/*								    */
--/*		     Setting variable attributes		    */
--/*								    */
--/* **************************************************************** */
--
--#define FIND_OR_MAKE_VARIABLE(name, entry) \
--  do \
--    { \
--      entry = find_variable (name); \
--      if (!entry) \
--	{ \
--	  entry = bind_variable (name, "", 0); \
--	  if (!no_invisible_vars && entry) entry->attributes |= att_invisible; \
--	} \
--    } \
--  while (0)
--
--/* Make the variable associated with NAME be readonly.
--   If NAME does not exist yet, create it. */
--void
--set_var_read_only (name)
--     char *name;
--{
--  SHELL_VAR *entry;
--
--  FIND_OR_MAKE_VARIABLE (name, entry);
--  VSETATTR (entry, att_readonly);
--}
--
--#ifdef INCLUDE_UNUSED
--/* Make the function associated with NAME be readonly.
--   If NAME does not exist, we just punt, like auto_export code below. */
--void
--set_func_read_only (name)
--     const char *name;
--{
--  SHELL_VAR *entry;
--
--  entry = find_function (name);
--  if (entry)
--    VSETATTR (entry, att_readonly);
--}
--
--/* Make the variable associated with NAME be auto-exported.
--   If NAME does not exist yet, create it. */
--void
--set_var_auto_export (name)
--     char *name;
--{
--  SHELL_VAR *entry;
--
--  FIND_OR_MAKE_VARIABLE (name, entry);
--  set_auto_export (entry);
--}
--
--/* Make the function associated with NAME be auto-exported. */
--void
--set_func_auto_export (name)
--     const char *name;
--{
--  SHELL_VAR *entry;
--
--  entry = find_function (name);
--  if (entry)
--    set_auto_export (entry);
--}
--#endif
--
--/* **************************************************************** */
--/*								    */
--/*		     Creating lists of variables		    */
--/*								    */
--/* **************************************************************** */
--
--static VARLIST *
--vlist_alloc (nentries)
--     int nentries;
--{
--  VARLIST  *vlist;
--
--  vlist = (VARLIST *)xmalloc (sizeof (VARLIST));
--  vlist->list = (SHELL_VAR **)xmalloc ((nentries + 1) * sizeof (SHELL_VAR *));
--  vlist->list_size = nentries;
--  vlist->list_len = 0;
--  vlist->list[0] = (SHELL_VAR *)NULL;
--
--  return vlist;
--}
--
--static VARLIST *
--vlist_realloc (vlist, n)
--     VARLIST *vlist;
--     int n;
--{
--  if (vlist == 0)
--    return (vlist = vlist_alloc (n));
--  if (n > vlist->list_size)
--    {
--      vlist->list_size = n;
--      vlist->list = (SHELL_VAR **)xrealloc (vlist->list, (vlist->list_size + 1) * sizeof (SHELL_VAR *));
--    }
--  return vlist;
--}
--
--static void
--vlist_add (vlist, var, flags)
--     VARLIST *vlist;
--     SHELL_VAR *var;
--     int flags;
--{
--  register int i;
--
--  for (i = 0; i < vlist->list_len; i++)
--    if (STREQ (var->name, vlist->list[i]->name))
--      break;
--  if (i < vlist->list_len)
--    return;
--
--  if (i >= vlist->list_size)
--    vlist = vlist_realloc (vlist, vlist->list_size + 16);
--
--  vlist->list[vlist->list_len++] = var;
--  vlist->list[vlist->list_len] = (SHELL_VAR *)NULL;
--}
--
--/* Map FUNCTION over the variables in VAR_HASH_TABLE.  Return an array of the
--   variables for which FUNCTION returns a non-zero value.  A NULL value
--   for FUNCTION means to use all variables. */
--SHELL_VAR **
--map_over (function, vc)
--     sh_var_map_func_t *function;
--     VAR_CONTEXT *vc;
--{
--  VAR_CONTEXT *v;
--  VARLIST *vlist;
--  SHELL_VAR **ret;
--  int nentries;
--
--  for (nentries = 0, v = vc; v; v = v->down)
--    nentries += HASH_ENTRIES (v->table);
--
--  if (nentries == 0)
--    return (SHELL_VAR **)NULL;
--
--  vlist = vlist_alloc (nentries);
--
--  for (v = vc; v; v = v->down)
--    flatten (v->table, function, vlist, 0);
--
--  ret = vlist->list;
--  free (vlist);
--  return ret;
--}
--
--SHELL_VAR **
--map_over_funcs (function)
--     sh_var_map_func_t *function;
--{
--  VARLIST *vlist;
--  SHELL_VAR **ret;
--
--  if (shell_functions == 0 || HASH_ENTRIES (shell_functions) == 0)
--    return ((SHELL_VAR **)NULL);
--
--  vlist = vlist_alloc (HASH_ENTRIES (shell_functions));
--
--  flatten (shell_functions, function, vlist, 0);
--
--  ret = vlist->list;
--  free (vlist);
--  return ret;
--}
--
--/* Flatten VAR_HASH_TABLE, applying FUNC to each member and adding those
--   elements for which FUNC succeeds to VLIST->list.  FLAGS is reserved
--   for future use.  Only unique names are added to VLIST.  If FUNC is
--   NULL, each variable in VAR_HASH_TABLE is added to VLIST.  If VLIST is
--   NULL, FUNC is applied to each SHELL_VAR in VAR_HASH_TABLE.  If VLIST
--   and FUNC are both NULL, nothing happens. */
--static void
--flatten (var_hash_table, func, vlist, flags)
--     HASH_TABLE *var_hash_table;
--     sh_var_map_func_t *func;
--     VARLIST *vlist;
--     int flags;
--{
--  register int i;
--  register BUCKET_CONTENTS *tlist;
--  int r;
--  SHELL_VAR *var;
--
--  if (var_hash_table == 0 || (HASH_ENTRIES (var_hash_table) == 0) || (vlist == 0 && func == 0))
--    return;
--
--  for (i = 0; i < var_hash_table->nbuckets; i++)
--    {
--      for (tlist = hash_items (i, var_hash_table); tlist; tlist = tlist->next)
--	{
--	  var = (SHELL_VAR *)tlist->data;
--
--	  r = func ? (*func) (var) : 1;
--	  if (r && vlist)
--	    vlist_add (vlist, var, flags);
--	}
--    }
--}
--
--void
--sort_variables (array)
--     SHELL_VAR **array;
--{
--  qsort (array, strvec_len ((char **)array), sizeof (SHELL_VAR *), (QSFUNC *)qsort_var_comp);
--}
--
--static int
--qsort_var_comp (var1, var2)
--     SHELL_VAR **var1, **var2;
--{
--  int result;
--
--  if ((result = (*var1)->name[0] - (*var2)->name[0]) == 0)
--    result = strcmp ((*var1)->name, (*var2)->name);
--
--  return (result);
--}
--
--/* Apply FUNC to each variable in SHELL_VARIABLES, adding each one for
--   which FUNC succeeds to an array of SHELL_VAR *s.  Returns the array. */
--static SHELL_VAR **
--vapply (func)
--     sh_var_map_func_t *func;
--{
--  SHELL_VAR **list;
--
--  list = map_over (func, shell_variables);
--  if (list /* && posixly_correct */)
--    sort_variables (list);
--  return (list);
--}
--
--/* Apply FUNC to each variable in SHELL_FUNCTIONS, adding each one for
--   which FUNC succeeds to an array of SHELL_VAR *s.  Returns the array. */
--static SHELL_VAR **
--fapply (func)
--     sh_var_map_func_t *func;
--{
--  SHELL_VAR **list;
--
--  list = map_over_funcs (func);
--  if (list /* && posixly_correct */)
--    sort_variables (list);
--  return (list);
--}
--
--/* Create a NULL terminated array of all the shell variables. */
--SHELL_VAR **
--all_shell_variables ()
--{
--  return (vapply ((sh_var_map_func_t *)NULL));
--}
--
--/* Create a NULL terminated array of all the shell functions. */
--SHELL_VAR **
--all_shell_functions ()
--{
--  return (fapply ((sh_var_map_func_t *)NULL));
--}
--
--static int
--visible_var (var)
--     SHELL_VAR *var;
--{
--  return (invisible_p (var) == 0);
--}
--
--SHELL_VAR **
--all_visible_functions ()
--{
--  return (fapply (visible_var));
--}
--
--SHELL_VAR **
--all_visible_variables ()
--{
--  return (vapply (visible_var));
--}
--
--/* Return non-zero if the variable VAR is visible and exported.  Array
--   variables cannot be exported. */
--static int
--visible_and_exported (var)
--     SHELL_VAR *var;
--{
--  return (invisible_p (var) == 0 && exported_p (var));
--}
--
--/* Candidate variables for the export environment are either valid variables
--   with the export attribute or invalid variables inherited from the initial
--   environment and simply passed through. */
--static int
--export_environment_candidate (var)
--     SHELL_VAR *var;
--{
--  return (exported_p (var) && (invisible_p (var) == 0 || imported_p (var)));
--}
--
--/* Return non-zero if VAR is a local variable in the current context and
--   is exported. */
--static int
--local_and_exported (var)
--     SHELL_VAR *var;
--{
--  return (invisible_p (var) == 0 && local_p (var) && var->context == variable_context && exported_p (var));
--}
--
--SHELL_VAR **
--all_exported_variables ()
--{
--  return (vapply (visible_and_exported));
--}
--
--SHELL_VAR **
--local_exported_variables ()
--{
--  return (vapply (local_and_exported));
--}
--
--static int
--variable_in_context (var)
--     SHELL_VAR *var;
--{
--  return (invisible_p (var) == 0 && local_p (var) && var->context == variable_context);
--}
--
--SHELL_VAR **
--all_local_variables ()
--{
--  VARLIST *vlist;
--  SHELL_VAR **ret;
--  VAR_CONTEXT *vc;
--
--  vc = shell_variables;
--  for (vc = shell_variables; vc; vc = vc->down)
--    if (vc_isfuncenv (vc) && vc->scope == variable_context)
--      break;
--
--  if (vc == 0)
--    {
--      internal_error (_("all_local_variables: no function context at current scope"));
--      return (SHELL_VAR **)NULL;
--    }
--  if (vc->table == 0 || HASH_ENTRIES (vc->table) == 0 || vc_haslocals (vc) == 0)
--    return (SHELL_VAR **)NULL;
--    
--  vlist = vlist_alloc (HASH_ENTRIES (vc->table));
--
--  flatten (vc->table, variable_in_context, vlist, 0);
--
--  ret = vlist->list;
--  free (vlist);
--  if (ret)
--    sort_variables (ret);
--  return ret;
--}
--
--#if defined (ARRAY_VARS)
--/* Return non-zero if the variable VAR is visible and an array. */
--static int
--visible_array_vars (var)
--     SHELL_VAR *var;
--{
--  return (invisible_p (var) == 0 && array_p (var));
--}
--
--SHELL_VAR **
--all_array_variables ()
--{
--  return (vapply (visible_array_vars));
--}
--#endif /* ARRAY_VARS */
--
--char **
--all_variables_matching_prefix (prefix)
--     const char *prefix;
--{
--  SHELL_VAR **varlist;
--  char **rlist;
--  int vind, rind, plen;
--
--  plen = STRLEN (prefix);
--  varlist = all_visible_variables ();
--  for (vind = 0; varlist && varlist[vind]; vind++)
--    ;
--  if (varlist == 0 || vind == 0)
--    return ((char **)NULL);
--  rlist = strvec_create (vind + 1);
--  for (vind = rind = 0; varlist[vind]; vind++)
--    {
--      if (plen == 0 || STREQN (prefix, varlist[vind]->name, plen))
--	rlist[rind++] = savestring (varlist[vind]->name);
--    }
--  rlist[rind] = (char *)0;
--  free (varlist);
--
--  return rlist;
--}
--
--/* **************************************************************** */
--/*								    */
--/*		 Managing temporary variable scopes		    */
--/*								    */
--/* **************************************************************** */
--
--/* Make variable NAME have VALUE in the temporary environment. */
--static SHELL_VAR *
--bind_tempenv_variable (name, value)
--     const char *name;
--     char *value;
--{
--  SHELL_VAR *var;
--
--  var = temporary_env ? hash_lookup (name, temporary_env) : (SHELL_VAR *)NULL;
--
--  if (var)
--    {
--      FREE (value_cell (var));
--      var_setvalue (var, savestring (value));
--      INVALIDATE_EXPORTSTR (var);
--    }
--
--  return (var);
--}
--
--/* Find a variable in the temporary environment that is named NAME.
--   Return the SHELL_VAR *, or NULL if not found. */
--SHELL_VAR *
--find_tempenv_variable (name)
--     const char *name;
--{
--  return (temporary_env ? hash_lookup (name, temporary_env) : (SHELL_VAR *)NULL);
--}
--
--char **tempvar_list;
--int tvlist_ind;
--
--/* Push the variable described by (SHELL_VAR *)DATA down to the next
--   variable context from the temporary environment. */
--static void
--push_temp_var (data)
--     PTR_T data;
--{
--  SHELL_VAR *var, *v;
--  HASH_TABLE *binding_table;
--
--  var = (SHELL_VAR *)data;
--
--  binding_table = shell_variables->table;
--  if (binding_table == 0)
--    {
--      if (shell_variables == global_variables)
--	/* shouldn't happen */
--	binding_table = shell_variables->table = global_variables->table = hash_create (0);
--      else
--	binding_table = shell_variables->table = hash_create (TEMPENV_HASH_BUCKETS);
--    }
--
--  v = bind_variable_internal (var->name, value_cell (var), binding_table, 0, 0);
--
--  /* XXX - should we set the context here?  It shouldn't matter because of how
--     assign_in_env works, but might want to check. */
--  if (binding_table == global_variables->table)		/* XXX */
--    var->attributes &= ~(att_tempvar|att_propagate);
--  else
--    {
--      var->attributes |= att_propagate;
--      if  (binding_table == shell_variables->table)
--	shell_variables->flags |= VC_HASTMPVAR;
--    }
--  v->attributes |= var->attributes;
--
--  if (find_special_var (var->name) >= 0)
--    tempvar_list[tvlist_ind++] = savestring (var->name);
--
--  dispose_variable (var);
--}
--
--static void
--propagate_temp_var (data)
--     PTR_T data;
--{
--  SHELL_VAR *var;
--
--  var = (SHELL_VAR *)data;
--  if (tempvar_p (var) && (var->attributes & att_propagate))
--    push_temp_var (data);
--  else
--    {
--      if (find_special_var (var->name) >= 0)
--	tempvar_list[tvlist_ind++] = savestring (var->name);
--      dispose_variable (var);
--    }
--}
--
--/* Free the storage used in the hash table for temporary
--   environment variables.  PUSHF is a function to be called
--   to free each hash table entry.  It takes care of pushing variables
--   to previous scopes if appropriate.  PUSHF stores names of variables
--   that require special handling (e.g., IFS) on tempvar_list, so this
--   function can call stupidly_hack_special_variables on all the
--   variables in the list when the temporary hash table is destroyed. */
--static void
--dispose_temporary_env (pushf)
--     sh_free_func_t *pushf;
--{
--  int i;
--
--  tempvar_list = strvec_create (HASH_ENTRIES (temporary_env) + 1);
--  tempvar_list[tvlist_ind = 0] = 0;
--    
--  hash_flush (temporary_env, pushf);
--  hash_dispose (temporary_env);
--  temporary_env = (HASH_TABLE *)NULL;
--
--  tempvar_list[tvlist_ind] = 0;
--
--  array_needs_making = 1;
--
--#if 0
--  sv_ifs ("IFS");		/* XXX here for now -- check setifs in assign_in_env */  
--#endif
--  for (i = 0; i < tvlist_ind; i++)
--    stupidly_hack_special_variables (tempvar_list[i]);
--
--  strvec_dispose (tempvar_list);
--  tempvar_list = 0;
--  tvlist_ind = 0;
--}
--
--void
--dispose_used_env_vars ()
--{
--  if (temporary_env)
--    {
--      dispose_temporary_env (propagate_temp_var);
--      maybe_make_export_env ();
--    }
--}
--
--/* Take all of the shell variables in the temporary environment HASH_TABLE
--   and make shell variables from them at the current variable context. */
--void
--merge_temporary_env ()
--{
--  if (temporary_env)
--    dispose_temporary_env (push_temp_var);
--}
--
--/* **************************************************************** */
--/*								    */
--/*	     Creating and manipulating the environment		    */
--/*								    */
--/* **************************************************************** */
--
--static inline char *
--mk_env_string (name, value, isfunc)
--     const char *name, *value;
--     int isfunc;
--{
--  size_t name_len, value_len;
--  char	*p, *q;
--
--  name_len = strlen (name);
--  value_len = STRLEN (value);
--
--  /* If we are exporting a shell function, construct the encoded function
--     name. */
--  if (isfunc && value)
--    {
--      p = (char *)xmalloc (BASHFUNC_PREFLEN + name_len + BASHFUNC_SUFFLEN + value_len + 2);
--      q = p;
--      memcpy (q, BASHFUNC_PREFIX, BASHFUNC_PREFLEN);
--      q += BASHFUNC_PREFLEN;
--      memcpy (q, name, name_len);
--      q += name_len;
--      memcpy (q, BASHFUNC_SUFFIX, BASHFUNC_SUFFLEN);
--      q += BASHFUNC_SUFFLEN;
--    }
--  else
--    {
--      p = (char *)xmalloc (2 + name_len + value_len);
--      memcpy (p, name, name_len);
--      q = p + name_len;
--    }
--
--  q[0] = '=';
--  if (value && *value)
--    memcpy (q + 1, value, value_len + 1);
--  else
--    q[1] = '\0';
--
--  return (p);
--}
--
--#ifdef DEBUG
--/* Debugging */
--static int
--valid_exportstr (v)
--     SHELL_VAR *v;
--{
--  char *s;
--
--  s = v->exportstr;
--  if (s == 0)
--    {
--      internal_error (_("%s has null exportstr"), v->name);
--      return (0);
--    }
--  if (legal_variable_starter ((unsigned char)*s) == 0)
--    {
--      internal_error (_("invalid character %d in exportstr for %s"), *s, v->name);
--      return (0);
--    }
--  for (s = v->exportstr + 1; s && *s; s++)
--    {
--      if (*s == '=')
--	break;
--      if (legal_variable_char ((unsigned char)*s) == 0)
--	{
--	  internal_error (_("invalid character %d in exportstr for %s"), *s, v->name);
--	  return (0);
--	}
--    }
--  if (*s != '=')
--    {
--      internal_error (_("no `=' in exportstr for %s"), v->name);
--      return (0);
--    }
--  return (1);
--}
--#endif
--
--static char **
--make_env_array_from_var_list (vars)
--     SHELL_VAR **vars;
--{
--  register int i, list_index;
--  register SHELL_VAR *var;
--  char **list, *value;
--
--  list = strvec_create ((1 + strvec_len ((char **)vars)));
--
--#define USE_EXPORTSTR (value == var->exportstr)
--
--  for (i = 0, list_index = 0; var = vars[i]; i++)
--    {
--#if defined (__CYGWIN__)
--      /* We don't use the exportstr stuff on Cygwin at all. */
--      INVALIDATE_EXPORTSTR (var);
--#endif
--      if (var->exportstr)
--	value = var->exportstr;
--      else if (function_p (var))
--	value = named_function_string ((char *)NULL, function_cell (var), 0);
--#if defined (ARRAY_VARS)
--      else if (array_p (var))
--#  if ARRAY_EXPORT
--	value = array_to_assignment_string (array_cell (var));
--#  else
--	continue;	/* XXX array vars cannot yet be exported */
--#  endif /* ARRAY_EXPORT */
--      else if (assoc_p (var))
--#  if 0
--	value = assoc_to_assignment_string (assoc_cell (var));
--#  else
--	continue;	/* XXX associative array vars cannot yet be exported */
--#  endif
--#endif
--      else
--	value = value_cell (var);
--
--      if (value)
--	{
--	  /* Gee, I'd like to get away with not using savestring() if we're
--	     using the cached exportstr... */
--	  list[list_index] = USE_EXPORTSTR ? savestring (value)
--					   : mk_env_string (var->name, value, function_p (var));
--
--	  if (USE_EXPORTSTR == 0)
--	    SAVE_EXPORTSTR (var, list[list_index]);
--
--	  list_index++;
--#undef USE_EXPORTSTR
--
--#if 0	/* not yet */
--#if defined (ARRAY_VARS)
--	  if (array_p (var) || assoc_p (var))
--	    free (value);
--#endif
--#endif
--	}
--    }
--
--  list[list_index] = (char *)NULL;
--  return (list);
--}
--
--/* Make an array of assignment statements from the hash table
--   HASHED_VARS which contains SHELL_VARs.  Only visible, exported
--   variables are eligible. */
--static char **
--make_var_export_array (vcxt)
--     VAR_CONTEXT *vcxt;
--{
--  char **list;
--  SHELL_VAR **vars;
--
--#if 0
--  vars = map_over (visible_and_exported, vcxt);
--#else
--  vars = map_over (export_environment_candidate, vcxt);
--#endif
--
--  if (vars == 0)
--    return (char **)NULL;
--
--  list = make_env_array_from_var_list (vars);
--
--  free (vars);
--  return (list);
--}
--
--static char **
--make_func_export_array ()
--{
--  char **list;
--  SHELL_VAR **vars;
--
--  vars = map_over_funcs (visible_and_exported);
--  if (vars == 0)
--    return (char **)NULL;
--
--  list = make_env_array_from_var_list (vars);
--
--  free (vars);
--  return (list);
--}
--
--/* Add ENVSTR to the end of the exported environment, EXPORT_ENV. */
--#define add_to_export_env(envstr,do_alloc) \
--do \
--  { \
--    if (export_env_index >= (export_env_size - 1)) \
--      { \
--	export_env_size += 16; \
--	export_env = strvec_resize (export_env, export_env_size); \
--	environ = export_env; \
--      } \
--    export_env[export_env_index++] = (do_alloc) ? savestring (envstr) : envstr; \
--    export_env[export_env_index] = (char *)NULL; \
--  } while (0)
--
--/* Add ASSIGN to EXPORT_ENV, or supercede a previous assignment in the
--   array with the same left-hand side.  Return the new EXPORT_ENV. */
--char **
--add_or_supercede_exported_var (assign, do_alloc)
--     char *assign;
--     int do_alloc;
--{
--  register int i;
--  int equal_offset;
--
--  equal_offset = assignment (assign, 0);
--  if (equal_offset == 0)
--    return (export_env);
--
--  /* If this is a function, then only supersede the function definition.
--     We do this by including the `=() {' in the comparison, like
--     initialize_shell_variables does. */
--  if (assign[equal_offset + 1] == '(' &&
--     strncmp (assign + equal_offset + 2, ") {", 3) == 0)		/* } */
--    equal_offset += 4;
--
--  for (i = 0; i < export_env_index; i++)
--    {
--      if (STREQN (assign, export_env[i], equal_offset + 1))
--	{
--	  free (export_env[i]);
--	  export_env[i] = do_alloc ? savestring (assign) : assign;
--	  return (export_env);
--	}
--    }
--  add_to_export_env (assign, do_alloc);
--  return (export_env);
--}
--
--static void
--add_temp_array_to_env (temp_array, do_alloc, do_supercede)
--     char **temp_array;
--     int do_alloc, do_supercede;
--{
--  register int i;
--
--  if (temp_array == 0)
--    return;
--
--  for (i = 0; temp_array[i]; i++)
--    {
--      if (do_supercede)
--	export_env = add_or_supercede_exported_var (temp_array[i], do_alloc);
--      else
--	add_to_export_env (temp_array[i], do_alloc);
--    }
--
--  free (temp_array);
--}
--
--/* Make the environment array for the command about to be executed, if the
--   array needs making.  Otherwise, do nothing.  If a shell action could
--   change the array that commands receive for their environment, then the
--   code should `array_needs_making++'.
--
--   The order to add to the array is:
--   	temporary_env
--   	list of var contexts whose head is shell_variables
--  	shell_functions
--
--  This is the shell variable lookup order.  We add only new variable
--  names at each step, which allows local variables and variables in
--  the temporary environments to shadow variables in the global (or
--  any previous) scope.
--*/
--
--static int
--n_shell_variables ()
--{
--  VAR_CONTEXT *vc;
--  int n;
--
--  for (n = 0, vc = shell_variables; vc; vc = vc->down)
--    n += HASH_ENTRIES (vc->table);
--  return n;
--}
--
--int
--chkexport (name)
--     char *name;
--{
--  SHELL_VAR *v;
--
--  v = find_variable (name);
--  if (v && exported_p (v))
--    {
--      array_needs_making = 1;
--      maybe_make_export_env ();
--      return 1;
--    }
--  return 0;
--}
--
--void
--maybe_make_export_env ()
--{
--  register char **temp_array;
--  int new_size;
--  VAR_CONTEXT *tcxt;
--
--  if (array_needs_making)
--    {
--      if (export_env)
--	strvec_flush (export_env);
--
--      /* Make a guess based on how many shell variables and functions we
--	 have.  Since there will always be array variables, and array
--	 variables are not (yet) exported, this will always be big enough
--	 for the exported variables and functions. */
--      new_size = n_shell_variables () + HASH_ENTRIES (shell_functions) + 1 +
--		 HASH_ENTRIES (temporary_env);
--      if (new_size > export_env_size)
--	{
--	  export_env_size = new_size;
--	  export_env = strvec_resize (export_env, export_env_size);
--	  environ = export_env;
--	}
--      export_env[export_env_index = 0] = (char *)NULL;
--
--      /* Make a dummy variable context from the temporary_env, stick it on
--	 the front of shell_variables, call make_var_export_array on the
--	 whole thing to flatten it, and convert the list of SHELL_VAR *s
--	 to the form needed by the environment. */
--      if (temporary_env)
--	{
--	  tcxt = new_var_context ((char *)NULL, 0);
--	  tcxt->table = temporary_env;
--	  tcxt->down = shell_variables;
--	}
--      else
--	tcxt = shell_variables;
--      
--      temp_array = make_var_export_array (tcxt);
--      if (temp_array)
--	add_temp_array_to_env (temp_array, 0, 0);
--
--      if (tcxt != shell_variables)
--	free (tcxt);
--
--#if defined (RESTRICTED_SHELL)
--      /* Restricted shells may not export shell functions. */
--      temp_array = restricted ? (char **)0 : make_func_export_array ();
--#else
--      temp_array = make_func_export_array ();
--#endif
--      if (temp_array)
--	add_temp_array_to_env (temp_array, 0, 0);
--
--      array_needs_making = 0;
--    }
--}
--
--/* This is an efficiency hack.  PWD and OLDPWD are auto-exported, so
--   we will need to remake the exported environment every time we
--   change directories.  `_' is always put into the environment for
--   every external command, so without special treatment it will always
--   cause the environment to be remade.
--
--   If there is no other reason to make the exported environment, we can
--   just update the variables in place and mark the exported environment
--   as no longer needing a remake. */
--void
--update_export_env_inplace (env_prefix, preflen, value)
--     char *env_prefix;
--     int preflen;
--     char *value;
--{
--  char *evar;
--
--  evar = (char *)xmalloc (STRLEN (value) + preflen + 1);
--  strcpy (evar, env_prefix);
--  if (value)
--    strcpy (evar + preflen, value);
--  export_env = add_or_supercede_exported_var (evar, 0);
--}
--
--/* We always put _ in the environment as the name of this command. */
--void
--put_command_name_into_env (command_name)
--     char *command_name;
--{
--  update_export_env_inplace ("_=", 2, command_name);
--}
--
--/* **************************************************************** */
--/*								    */
--/*		      Managing variable contexts		    */
--/*								    */
--/* **************************************************************** */
--
--/* Allocate and return a new variable context with NAME and FLAGS.
--   NAME can be NULL. */
--
--VAR_CONTEXT *
--new_var_context (name, flags)
--     char *name;
--     int flags;
--{
--  VAR_CONTEXT *vc;
--
--  vc = (VAR_CONTEXT *)xmalloc (sizeof (VAR_CONTEXT));
--  vc->name = name ? savestring (name) : (char *)NULL;
--  vc->scope = variable_context;
--  vc->flags = flags;
--
--  vc->up = vc->down = (VAR_CONTEXT *)NULL;
--  vc->table = (HASH_TABLE *)NULL;
--
--  return vc;
--}
--
--/* Free a variable context and its data, including the hash table.  Dispose
--   all of the variables. */
--void
--dispose_var_context (vc)
--     VAR_CONTEXT *vc;
--{
--  FREE (vc->name);
--
--  if (vc->table)
--    {
--      delete_all_variables (vc->table);
--      hash_dispose (vc->table);
--    }
--
--  free (vc);
--}
--
--/* Set VAR's scope level to the current variable context. */
--static int
--set_context (var)
--     SHELL_VAR *var;
--{
--  return (var->context = variable_context);
--}
--
--/* Make a new variable context with NAME and FLAGS and a HASH_TABLE of
--   temporary variables, and push it onto shell_variables.  This is
--   for shell functions. */
--VAR_CONTEXT *
--push_var_context (name, flags, tempvars)
--     char *name;
--     int flags;
--     HASH_TABLE *tempvars;
--{
--  VAR_CONTEXT *vc;
--
--  vc = new_var_context (name, flags);
--  vc->table = tempvars;
--  if (tempvars)
--    {
--      /* Have to do this because the temp environment was created before
--	 variable_context was incremented. */
--      flatten (tempvars, set_context, (VARLIST *)NULL, 0);
--      vc->flags |= VC_HASTMPVAR;
--    }
--  vc->down = shell_variables;
--  shell_variables->up = vc;
--
--  return (shell_variables = vc);
--}
--
--static void
--push_func_var (data)
--     PTR_T data;
--{
--  SHELL_VAR *var, *v;
--
--  var = (SHELL_VAR *)data;
--
--  if (tempvar_p (var) && (posixly_correct || (var->attributes & att_propagate)))
--    {
--      /* Make sure we have a hash table to store the variable in while it is
--	 being propagated down to the global variables table.  Create one if
--	 we have to */
--      if ((vc_isfuncenv (shell_variables) || vc_istempenv (shell_variables)) && shell_variables->table == 0)
--	shell_variables->table = hash_create (0);
--      /* XXX - should we set v->context here? */
--      v = bind_variable_internal (var->name, value_cell (var), shell_variables->table, 0, 0);
--      if (shell_variables == global_variables)
--	var->attributes &= ~(att_tempvar|att_propagate);
--      else
--	shell_variables->flags |= VC_HASTMPVAR;
--      v->attributes |= var->attributes;
--    }
--  else
--    stupidly_hack_special_variables (var->name);	/* XXX */
--
--  dispose_variable (var);
--}
--
--/* Pop the top context off of VCXT and dispose of it, returning the rest of
--   the stack. */
--void
--pop_var_context ()
--{
--  VAR_CONTEXT *ret, *vcxt;
--
--  vcxt = shell_variables;
--  if (vc_isfuncenv (vcxt) == 0)
--    {
--      internal_error (_("pop_var_context: head of shell_variables not a function context"));
--      return;
--    }
--
--  if (ret = vcxt->down)
--    {
--      ret->up = (VAR_CONTEXT *)NULL;
--      shell_variables = ret;
--      if (vcxt->table)
--	hash_flush (vcxt->table, push_func_var);
--      dispose_var_context (vcxt);
--    }
--  else
--    internal_error (_("pop_var_context: no global_variables context"));
--}
--
--/* Delete the HASH_TABLEs for all variable contexts beginning at VCXT, and
--   all of the VAR_CONTEXTs except GLOBAL_VARIABLES. */
--void
--delete_all_contexts (vcxt)
--     VAR_CONTEXT *vcxt;
--{
--  VAR_CONTEXT *v, *t;
--
--  for (v = vcxt; v != global_variables; v = t)
--    {
--      t = v->down;
--      dispose_var_context (v);
--    }    
--
--  delete_all_variables (global_variables->table);
--  shell_variables = global_variables;
--}
--
--/* **************************************************************** */
--/*								    */
--/*	   Pushing and Popping temporary variable scopes	    */
--/*								    */
--/* **************************************************************** */
--
--VAR_CONTEXT *
--push_scope (flags, tmpvars)
--     int flags;
--     HASH_TABLE *tmpvars;
--{
--  return (push_var_context ((char *)NULL, flags, tmpvars));
--}
--
--static void
--push_exported_var (data)
--     PTR_T data;
--{
--  SHELL_VAR *var, *v;
--
--  var = (SHELL_VAR *)data;
--
--  /* If a temp var had its export attribute set, or it's marked to be
--     propagated, bind it in the previous scope before disposing it. */
--  /* XXX - This isn't exactly right, because all tempenv variables have the
--    export attribute set. */
--#if 0
--  if (exported_p (var) || (var->attributes & att_propagate))
--#else
--  if (tempvar_p (var) && exported_p (var) && (var->attributes & att_propagate))
--#endif
--    {
--      var->attributes &= ~att_tempvar;		/* XXX */
--      v = bind_variable_internal (var->name, value_cell (var), shell_variables->table, 0, 0);
--      if (shell_variables == global_variables)
--	var->attributes &= ~att_propagate;
--      v->attributes |= var->attributes;
--    }
--  else
--    stupidly_hack_special_variables (var->name);	/* XXX */
--
--  dispose_variable (var);
--}
--
--void
--pop_scope (is_special)
--     int is_special;
--{
--  VAR_CONTEXT *vcxt, *ret;
--
--  vcxt = shell_variables;
--  if (vc_istempscope (vcxt) == 0)
--    {
--      internal_error (_("pop_scope: head of shell_variables not a temporary environment scope"));
--      return;
--    }
--
--  ret = vcxt->down;
--  if (ret)
--    ret->up = (VAR_CONTEXT *)NULL;
--
--  shell_variables = ret;
--
--  /* Now we can take care of merging variables in VCXT into set of scopes
--     whose head is RET (shell_variables). */
--  FREE (vcxt->name);
--  if (vcxt->table)
--    {
--      if (is_special)
--	hash_flush (vcxt->table, push_func_var);
--      else
--	hash_flush (vcxt->table, push_exported_var);
--      hash_dispose (vcxt->table);
--    }
--  free (vcxt);
--
--  sv_ifs ("IFS");	/* XXX here for now */
--}
--
--/* **************************************************************** */
--/*								    */
--/*		 Pushing and Popping function contexts		    */
--/*								    */
--/* **************************************************************** */
--
--static WORD_LIST **dollar_arg_stack = (WORD_LIST **)NULL;
--static int dollar_arg_stack_slots;
--static int dollar_arg_stack_index;
--
--/* XXX - we might want to consider pushing and popping the `getopts' state
--   when we modify the positional parameters. */
--void
--push_context (name, is_subshell, tempvars)
--     char *name;	/* function name */
--     int is_subshell;
--     HASH_TABLE *tempvars;
--{
--  if (is_subshell == 0)
--    push_dollar_vars ();
--  variable_context++;
--  push_var_context (name, VC_FUNCENV, tempvars);
--}
--
--/* Only called when subshell == 0, so we don't need to check, and can
--   unconditionally pop the dollar vars off the stack. */
--void
--pop_context ()
--{
--  pop_dollar_vars ();
--  variable_context--;
--  pop_var_context ();
--
--  sv_ifs ("IFS");		/* XXX here for now */
--}
--
--/* Save the existing positional parameters on a stack. */
--void
--push_dollar_vars ()
--{
--  if (dollar_arg_stack_index + 2 > dollar_arg_stack_slots)
--    {
--      dollar_arg_stack = (WORD_LIST **)
--	xrealloc (dollar_arg_stack, (dollar_arg_stack_slots += 10)
--		  * sizeof (WORD_LIST *));
--    }
--  dollar_arg_stack[dollar_arg_stack_index++] = list_rest_of_args ();
--  dollar_arg_stack[dollar_arg_stack_index] = (WORD_LIST *)NULL;
--}
--
--/* Restore the positional parameters from our stack. */
--void
--pop_dollar_vars ()
--{
--  if (!dollar_arg_stack || dollar_arg_stack_index == 0)
--    return;
--
--  remember_args (dollar_arg_stack[--dollar_arg_stack_index], 1);
--  dispose_words (dollar_arg_stack[dollar_arg_stack_index]);
--  dollar_arg_stack[dollar_arg_stack_index] = (WORD_LIST *)NULL;
--  set_dollar_vars_unchanged ();
--}
--
--void
--dispose_saved_dollar_vars ()
--{
--  if (!dollar_arg_stack || dollar_arg_stack_index == 0)
--    return;
--
--  dispose_words (dollar_arg_stack[dollar_arg_stack_index]);
--  dollar_arg_stack[dollar_arg_stack_index] = (WORD_LIST *)NULL;
--}
--
--/* Manipulate the special BASH_ARGV and BASH_ARGC variables. */
--
--void
--push_args (list)
--     WORD_LIST *list;
--{
--#if defined (ARRAY_VARS) && defined (DEBUGGER)
--  SHELL_VAR *bash_argv_v, *bash_argc_v;
--  ARRAY *bash_argv_a, *bash_argc_a;
--  WORD_LIST *l;
--  arrayind_t i;
--  char *t;
--
--  GET_ARRAY_FROM_VAR ("BASH_ARGV", bash_argv_v, bash_argv_a);
--  GET_ARRAY_FROM_VAR ("BASH_ARGC", bash_argc_v, bash_argc_a);
--
--  for (l = list, i = 0; l; l = l->next, i++)
--    array_push (bash_argv_a, l->word->word);
--
--  t = itos (i);
--  array_push (bash_argc_a, t);
--  free (t);
--#endif /* ARRAY_VARS && DEBUGGER */
--}
--
--/* Remove arguments from BASH_ARGV array.  Pop top element off BASH_ARGC
--   array and use that value as the count of elements to remove from
--   BASH_ARGV. */
--void
--pop_args ()
--{
--#if defined (ARRAY_VARS) && defined (DEBUGGER)
--  SHELL_VAR *bash_argv_v, *bash_argc_v;
--  ARRAY *bash_argv_a, *bash_argc_a;
--  ARRAY_ELEMENT *ce;
--  intmax_t i;
--
--  GET_ARRAY_FROM_VAR ("BASH_ARGV", bash_argv_v, bash_argv_a);
--  GET_ARRAY_FROM_VAR ("BASH_ARGC", bash_argc_v, bash_argc_a);
--
--  ce = array_shift (bash_argc_a, 1, 0);
--  if (ce == 0 || legal_number (element_value (ce), &i) == 0)
--    i = 0;
--
--  for ( ; i > 0; i--)
--    array_pop (bash_argv_a);
--  array_dispose_element (ce);
--#endif /* ARRAY_VARS && DEBUGGER */
--}
--
--/*************************************************
-- *						 *
-- *	Functions to manage special variables	 *
-- *						 *
-- *************************************************/
--
--/* Extern declarations for variables this code has to manage. */
--extern int eof_encountered, eof_encountered_limit, ignoreeof;
--
--#if defined (READLINE)
--extern int hostname_list_initialized;
--#endif
--
--/* An alist of name.function for each special variable.  Most of the
--   functions don't do much, and in fact, this would be faster with a
--   switch statement, but by the end of this file, I am sick of switch
--   statements. */
--
--#define SET_INT_VAR(name, intvar)  intvar = find_variable (name) != 0
--
--/* This table will be sorted with qsort() the first time it's accessed. */
--struct name_and_function {
--  char *name;
--  sh_sv_func_t *function;
--};
--
--static struct name_and_function special_vars[] = {
--  { "BASH_COMPAT", sv_shcompat },
--  { "BASH_XTRACEFD", sv_xtracefd },
--
--#if defined (JOB_CONTROL)
--  { "CHILD_MAX", sv_childmax },
--#endif
--
--#if defined (READLINE)
--#  if defined (STRICT_POSIX)
--  { "COLUMNS", sv_winsize },
--#  endif
--  { "COMP_WORDBREAKS", sv_comp_wordbreaks },
--#endif
--
--  { "FUNCNEST", sv_funcnest },
--
--  { "GLOBIGNORE", sv_globignore },
--
--#if defined (HISTORY)
--  { "HISTCONTROL", sv_history_control },
--  { "HISTFILESIZE", sv_histsize },
--  { "HISTIGNORE", sv_histignore },
--  { "HISTSIZE", sv_histsize },
--  { "HISTTIMEFORMAT", sv_histtimefmt },
--#endif
--
--#if defined (__CYGWIN__)
--  { "HOME", sv_home },
--#endif
--
--#if defined (READLINE)
--  { "HOSTFILE", sv_hostfile },
--#endif
--
--  { "IFS", sv_ifs },
--  { "IGNOREEOF", sv_ignoreeof },
--
--  { "LANG", sv_locale },
--  { "LC_ALL", sv_locale },
--  { "LC_COLLATE", sv_locale },
--  { "LC_CTYPE", sv_locale },
--  { "LC_MESSAGES", sv_locale },
--  { "LC_NUMERIC", sv_locale },
--  { "LC_TIME", sv_locale },
--
--#if defined (READLINE) && defined (STRICT_POSIX)
--  { "LINES", sv_winsize },
--#endif
--
--  { "MAIL", sv_mail },
--  { "MAILCHECK", sv_mail },
--  { "MAILPATH", sv_mail },
--
--  { "OPTERR", sv_opterr },
--  { "OPTIND", sv_optind },
--
--  { "PATH", sv_path },
--  { "POSIXLY_CORRECT", sv_strict_posix },
--
--#if defined (READLINE)
--  { "TERM", sv_terminal },
--  { "TERMCAP", sv_terminal },
--  { "TERMINFO", sv_terminal },
--#endif /* READLINE */
--
--  { "TEXTDOMAIN", sv_locale },
--  { "TEXTDOMAINDIR", sv_locale },
--
--#if defined (HAVE_TZSET)
--  { "TZ", sv_tz },
--#endif
--
--#if defined (HISTORY) && defined (BANG_HISTORY)
--  { "histchars", sv_histchars },
--#endif /* HISTORY && BANG_HISTORY */
--
--  { "ignoreeof", sv_ignoreeof },
--
--  { (char *)0, (sh_sv_func_t *)0 }
--};
--
--#define N_SPECIAL_VARS	(sizeof (special_vars) / sizeof (special_vars[0]) - 1)
--
--static int
--sv_compare (sv1, sv2)
--     struct name_and_function *sv1, *sv2;
--{
--  int r;
--
--  if ((r = sv1->name[0] - sv2->name[0]) == 0)
--    r = strcmp (sv1->name, sv2->name);
--  return r;
--}
--
--static inline int
--find_special_var (name)
--     const char *name;
--{
--  register int i, r;
--
--  for (i = 0; special_vars[i].name; i++)
--    {
--      r = special_vars[i].name[0] - name[0];
--      if (r == 0)
--	r = strcmp (special_vars[i].name, name);
--      if (r == 0)
--	return i;
--      else if (r > 0)
--	/* Can't match any of rest of elements in sorted list.  Take this out
--	   if it causes problems in certain environments. */
--	break;
--    }
--  return -1;
--}
--
--/* The variable in NAME has just had its state changed.  Check to see if it
--   is one of the special ones where something special happens. */
--void
--stupidly_hack_special_variables (name)
--     char *name;
--{
--  static int sv_sorted = 0;
--  int i;
--
--  if (sv_sorted == 0)	/* shouldn't need, but it's fairly cheap. */
--    {
--      qsort (special_vars, N_SPECIAL_VARS, sizeof (special_vars[0]),
--		(QSFUNC *)sv_compare);
--      sv_sorted = 1;
--    }
--
--  i = find_special_var (name);
--  if (i != -1)
--    (*(special_vars[i].function)) (name);
--}
--
--/* Special variables that need hooks to be run when they are unset as part
--   of shell reinitialization should have their sv_ functions run here. */
--void
--reinit_special_variables ()
--{
--#if defined (READLINE)
--  sv_comp_wordbreaks ("COMP_WORDBREAKS");
--#endif
--  sv_globignore ("GLOBIGNORE");
--  sv_opterr ("OPTERR");
--}
--
--void
--sv_ifs (name)
--     char *name;
--{
--  SHELL_VAR *v;
--
--  v = find_variable ("IFS");
--  setifs (v);
--}
--
--/* What to do just after the PATH variable has changed. */
--void
--sv_path (name)
--     char *name;
--{
--  /* hash -r */
--  phash_flush ();
--}
--
--/* What to do just after one of the MAILxxxx variables has changed.  NAME
--   is the name of the variable.  This is called with NAME set to one of
--   MAIL, MAILCHECK, or MAILPATH.  */
--void
--sv_mail (name)
--     char *name;
--{
--  /* If the time interval for checking the files has changed, then
--     reset the mail timer.  Otherwise, one of the pathname vars
--     to the users mailbox has changed, so rebuild the array of
--     filenames. */
--  if (name[4] == 'C')  /* if (strcmp (name, "MAILCHECK") == 0) */
--    reset_mail_timer ();
--  else
--    {
--      free_mail_files ();
--      remember_mail_dates ();
--    }
--}
--
--void
--sv_funcnest (name)
--     char *name;
--{
--  SHELL_VAR *v;
--  intmax_t num;
--
--  v = find_variable (name);
--  if (v == 0)
--    funcnest_max = 0;
--  else if (legal_number (value_cell (v), &num) == 0)
--    funcnest_max = 0;
--  else
--    funcnest_max = num;
--}
--
--/* What to do when GLOBIGNORE changes. */
--void
--sv_globignore (name)
--     char *name;
--{
--  if (privileged_mode == 0)
--    setup_glob_ignore (name);
--}
--
--#if defined (READLINE)
--void
--sv_comp_wordbreaks (name)
--     char *name;
--{
--  SHELL_VAR *sv;
--
--  sv = find_variable (name);
--  if (sv == 0)
--    reset_completer_word_break_chars ();
--}
--
--/* What to do just after one of the TERMxxx variables has changed.
--   If we are an interactive shell, then try to reset the terminal
--   information in readline. */
--void
--sv_terminal (name)
--     char *name;
--{
--  if (interactive_shell && no_line_editing == 0)
--    rl_reset_terminal (get_string_value ("TERM"));
--}
--
--void
--sv_hostfile (name)
--     char *name;
--{
--  SHELL_VAR *v;
--
--  v = find_variable (name);
--  if (v == 0)
--    clear_hostname_list ();
--  else
--    hostname_list_initialized = 0;
--}
--
--#if defined (STRICT_POSIX)
--/* In strict posix mode, we allow assignments to LINES and COLUMNS (and values
--   found in the initial environment) to override the terminal size reported by
--   the kernel. */
--void
--sv_winsize (name)
--     char *name;
--{
--  SHELL_VAR *v;
--  intmax_t xd;
--  int d;
--
--  if (posixly_correct == 0 || interactive_shell == 0 || no_line_editing)
--    return;
--
--  v = find_variable (name);
--  if (v == 0 || var_isnull (v))
--    rl_reset_screen_size ();
--  else
--    {
--      if (legal_number (value_cell (v), &xd) == 0)
--	return;
--      winsize_assignment = 1;
--      d = xd;			/* truncate */
--      if (name[0] == 'L')	/* LINES */
--	rl_set_screen_size (d, -1);
--      else			/* COLUMNS */
--	rl_set_screen_size (-1, d);
--      winsize_assignment = 0;
--    }
--}
--#endif /* STRICT_POSIX */
--#endif /* READLINE */
--
--/* Update the value of HOME in the export environment so tilde expansion will
--   work on cygwin. */
--#if defined (__CYGWIN__)
--sv_home (name)
--     char *name;
--{
--  array_needs_making = 1;
--  maybe_make_export_env ();
--}
--#endif
--
--#if defined (HISTORY)
--/* What to do after the HISTSIZE or HISTFILESIZE variables change.
--   If there is a value for this HISTSIZE (and it is numeric), then stifle
--   the history.  Otherwise, if there is NO value for this variable,
--   unstifle the history.  If name is HISTFILESIZE, and its value is
--   numeric, truncate the history file to hold no more than that many
--   lines. */
--void
--sv_histsize (name)
--     char *name;
--{
--  char *temp;
--  intmax_t num;
--  int hmax;
--
--  temp = get_string_value (name);
--
--  if (temp && *temp)
--    {
--      if (legal_number (temp, &num))
--	{
--	  hmax = num;
--	  if (hmax < 0 && name[4] == 'S')
--	    unstifle_history ();	/* unstifle history if HISTSIZE < 0 */
--	  else if (name[4] == 'S')
--	    {
--	      stifle_history (hmax);
--	      hmax = where_history ();
--	      if (history_lines_this_session > hmax)
--		history_lines_this_session = hmax;
--	    }
--	  else if (hmax >= 0)	/* truncate HISTFILE if HISTFILESIZE >= 0 */
--	    {
--	      history_truncate_file (get_string_value ("HISTFILE"), hmax);
--	      if (hmax <= history_lines_in_file)
--		history_lines_in_file = hmax;
--	    }
--	}
--    }
--  else if (name[4] == 'S')
--    unstifle_history ();
--}
--
--/* What to do after the HISTIGNORE variable changes. */
--void
--sv_histignore (name)
--     char *name;
--{
--  setup_history_ignore (name);
--}
--
--/* What to do after the HISTCONTROL variable changes. */
--void
--sv_history_control (name)
--     char *name;
--{
--  char *temp;
--  char *val;
--  int tptr;
--
--  history_control = 0;
--  temp = get_string_value (name);
--
--  if (temp == 0 || *temp == 0)
--    return;
--
--  tptr = 0;
--  while (val = extract_colon_unit (temp, &tptr))
--    {
--      if (STREQ (val, "ignorespace"))
--	history_control |= HC_IGNSPACE;
--      else if (STREQ (val, "ignoredups"))
--	history_control |= HC_IGNDUPS;
--      else if (STREQ (val, "ignoreboth"))
--	history_control |= HC_IGNBOTH;
--      else if (STREQ (val, "erasedups"))
--	history_control |= HC_ERASEDUPS;
--
--      free (val);
--    }
--}
--
--#if defined (BANG_HISTORY)
--/* Setting/unsetting of the history expansion character. */
--void
--sv_histchars (name)
--     char *name;
--{
--  char *temp;
--
--  temp = get_string_value (name);
--  if (temp)
--    {
--      history_expansion_char = *temp;
--      if (temp[0] && temp[1])
--	{
--	  history_subst_char = temp[1];
--	  if (temp[2])
--	      history_comment_char = temp[2];
--	}
--    }
--  else
--    {
--      history_expansion_char = '!';
--      history_subst_char = '^';
--      history_comment_char = '#';
--    }
--}
--#endif /* BANG_HISTORY */
--
--void
--sv_histtimefmt (name)
--     char *name;
--{
--  SHELL_VAR *v;
--
--  if (v = find_variable (name))
--    {
--      if (history_comment_char == 0)
--	history_comment_char = '#';
--    }
--  history_write_timestamps = (v != 0);
--}
--#endif /* HISTORY */
--
--#if defined (HAVE_TZSET)
--void
--sv_tz (name)
--     char *name;
--{
--  if (chkexport (name))
--    tzset ();
--}
--#endif
--
--/* If the variable exists, then the value of it can be the number
--   of times we actually ignore the EOF.  The default is small,
--   (smaller than csh, anyway). */
--void
--sv_ignoreeof (name)
--     char *name;
--{
--  SHELL_VAR *tmp_var;
--  char *temp;
--
--  eof_encountered = 0;
--
--  tmp_var = find_variable (name);
--  ignoreeof = tmp_var != 0;
--  temp = tmp_var ? value_cell (tmp_var) : (char *)NULL;
--  if (temp)
--    eof_encountered_limit = (*temp && all_digits (temp)) ? atoi (temp) : 10;
--  set_shellopts ();	/* make sure `ignoreeof' is/is not in $SHELLOPTS */
--}
--
--void
--sv_optind (name)
--     char *name;
--{
--  char *tt;
--  int s;
--
--  tt = get_string_value ("OPTIND");
--  if (tt && *tt)
--    {
--      s = atoi (tt);
--
--      /* According to POSIX, setting OPTIND=1 resets the internal state
--	 of getopt (). */
--      if (s < 0 || s == 1)
--	s = 0;
--    }
--  else
--    s = 0;
--  getopts_reset (s);
--}
--
--void
--sv_opterr (name)
--     char *name;
--{
--  char *tt;
--
--  tt = get_string_value ("OPTERR");
--  sh_opterr = (tt && *tt) ? atoi (tt) : 1;
--}
--
--void
--sv_strict_posix (name)
--     char *name;
--{
--  SET_INT_VAR (name, posixly_correct);
--  posix_initialize (posixly_correct);
--#if defined (READLINE)
--  if (interactive_shell)
--    posix_readline_initialize (posixly_correct);
--#endif /* READLINE */
--  set_shellopts ();	/* make sure `posix' is/is not in $SHELLOPTS */
--}
--
--void
--sv_locale (name)
--     char *name;
--{
--  char *v;
--  int r;
--
--  v = get_string_value (name);
--  if (name[0] == 'L' && name[1] == 'A')	/* LANG */
--    r = set_lang (name, v);
--  else
--    r = set_locale_var (name, v);		/* LC_*, TEXTDOMAIN* */
--
--#if 1
--  if (r == 0 && posixly_correct)
--    last_command_exit_value = 1;
--#endif
--}
--
--#if defined (ARRAY_VARS)
--void
--set_pipestatus_array (ps, nproc)
--     int *ps;
--     int nproc;
--{
--  SHELL_VAR *v;
--  ARRAY *a;
--  ARRAY_ELEMENT *ae;
--  register int i;
--  char *t, tbuf[INT_STRLEN_BOUND(int) + 1];
--
--  v = find_variable ("PIPESTATUS");
--  if (v == 0)
--    v = make_new_array_variable ("PIPESTATUS");
--  if (array_p (v) == 0)
--    return;		/* Do nothing if not an array variable. */
--  a = array_cell (v);
--
--  if (a == 0 || array_num_elements (a) == 0)
--    {
--      for (i = 0; i < nproc; i++)	/* was ps[i] != -1, not i < nproc */
--	{
--	  t = inttostr (ps[i], tbuf, sizeof (tbuf));
--	  array_insert (a, i, t);
--	}
--      return;
--    }
--
--  /* Fast case */
--  if (array_num_elements (a) == nproc && nproc == 1)
--    {
--      ae = element_forw (a->head);
--      free (element_value (ae));
--      ae->value = itos (ps[0]);
--    }
--  else if (array_num_elements (a) <= nproc)
--    {
--      /* modify in array_num_elements members in place, then add */
--      ae = a->head;
--      for (i = 0; i < array_num_elements (a); i++)
--	{
--	  ae = element_forw (ae);
--	  free (element_value (ae));
--	  ae->value = itos (ps[i]);
--	}
--      /* add any more */
--      for ( ; i < nproc; i++)
--	{
--	  t = inttostr (ps[i], tbuf, sizeof (tbuf));
--	  array_insert (a, i, t);
--	}
--    }
--  else
--    {
--      /* deleting elements.  it's faster to rebuild the array. */	  
--      array_flush (a);
--      for (i = 0; ps[i] != -1; i++)
--	{
--	  t = inttostr (ps[i], tbuf, sizeof (tbuf));
--	  array_insert (a, i, t);
--	}
--    }
--}
--
--ARRAY *
--save_pipestatus_array ()
--{
--  SHELL_VAR *v;
--  ARRAY *a, *a2;
--
--  v = find_variable ("PIPESTATUS");
--  if (v == 0 || array_p (v) == 0 || array_cell (v) == 0)
--    return ((ARRAY *)NULL);
--    
--  a = array_cell (v);
--  a2 = array_copy (array_cell (v));
--
--  return a2;
--}
--
--void
--restore_pipestatus_array (a)
--     ARRAY *a;
--{
--  SHELL_VAR *v;
--  ARRAY *a2;
--
--  v = find_variable ("PIPESTATUS");
--  /* XXX - should we still assign even if existing value is NULL? */
--  if (v == 0 || array_p (v) == 0 || array_cell (v) == 0)
--    return;
--
--  a2 = array_cell (v);
--  var_setarray (v, a); 
--
--  array_dispose (a2);
--}
--#endif
--
--void
--set_pipestatus_from_exit (s)
--     int s;
--{
--#if defined (ARRAY_VARS)
--  static int v[2] = { 0, -1 };
--
--  v[0] = s;
--  set_pipestatus_array (v, 1);
--#endif
--}
--
--void
--sv_xtracefd (name)
--     char *name;
--{
--  SHELL_VAR *v;
--  char *t, *e;
--  int fd;
--  FILE *fp;
--
--  v = find_variable (name);
--  if (v == 0)
--    {
--      xtrace_reset ();
--      return;
--    }
--
--  t = value_cell (v);
--  if (t == 0 || *t == 0)
--    xtrace_reset ();
--  else
--    {
--      fd = (int)strtol (t, &e, 10);
--      if (e != t && *e == '\0' && sh_validfd (fd))
--	{
--	  fp = fdopen (fd, "w");
--	  if (fp == 0)
--	    internal_error (_("%s: %s: cannot open as FILE"), name, value_cell (v));
--	  else
--	    xtrace_set (fd, fp);
--	}
--      else
--	internal_error (_("%s: %s: invalid value for trace file descriptor"), name, value_cell (v));
--    }
--}
--
--#define MIN_COMPAT_LEVEL 31
--
--void
--sv_shcompat (name)
--     char *name;
--{
--  SHELL_VAR *v;
--  char *val;
--  int tens, ones, compatval;
--
--  v = find_variable (name);
--  if (v == 0)
--    {
--      shell_compatibility_level = DEFAULT_COMPAT_LEVEL;
--      set_compatibility_opts ();
--      return;
--    }
--  val = value_cell (v);
--  if (val == 0 || *val == '\0')
--    {
--      shell_compatibility_level = DEFAULT_COMPAT_LEVEL;
--      set_compatibility_opts ();
--      return;
--    }
--  /* Handle decimal-like compatibility version specifications: 4.2 */
--  if (isdigit (val[0]) && val[1] == '.' && isdigit (val[2]) && val[3] == 0)
--    {
--      tens = val[0] - '0';
--      ones = val[2] - '0';
--      compatval = tens*10 + ones;
--    }
--  /* Handle integer-like compatibility version specifications: 42 */
--  else if (isdigit (val[0]) && isdigit (val[1]) && val[2] == 0)
--    {
--      tens = val[0] - '0';
--      ones = val[1] - '0';
--      compatval = tens*10 + ones;
--    }
--  else
--    {
--compat_error:
--      internal_error (_("%s: %s: compatibility value out of range"), name, val);
--      shell_compatibility_level = DEFAULT_COMPAT_LEVEL;
--      set_compatibility_opts ();
--      return;
--    }
--
--  if (compatval < MIN_COMPAT_LEVEL || compatval > DEFAULT_COMPAT_LEVEL)
--    goto compat_error;
--
--  shell_compatibility_level = compatval;
--  set_compatibility_opts ();
--}
--
--#if defined (JOB_CONTROL)
--void
--sv_childmax (name)
--     char *name;
--{
--  char *tt;
--  int s;
--
--  tt = get_string_value (name);
--  s = (tt && *tt) ? atoi (tt) : 0;
--  set_maxchild (s);
--}
--#endif
diff --git a/patches/bash-4.3.30/0003-Bash-4.3-patch-33.patch b/patches/bash-4.3.30/0003-Bash-4.3-patch-33.patch
deleted file mode 100644
index cda179735..000000000
--- a/patches/bash-4.3.30/0003-Bash-4.3-patch-33.patch
+++ /dev/null
@@ -1,204 +0,0 @@
-From: Chet Ramey <chet.ramey@case.edu>
-Date: Thu, 15 Jan 2015 10:21:08 -0500
-Subject: [PATCH] Bash-4.3 patch 33
-
----
- bashline.c        |  6 ++++--
- builtins/common.h |  4 ++++
- builtins/read.def | 31 ++++++++++++++++++++++++++++---
- patchlevel.h      |  2 +-
- shell.c           |  9 +++++++++
- sig.c             |  6 ++++--
- 6 files changed, 50 insertions(+), 8 deletions(-)
-
-diff --git a/bashline.c b/bashline.c
-index 77ca033f2cc8..c87415171a4a 100644
---- a/bashline.c
-+++ b/bashline.c
-@@ -202,6 +202,7 @@ extern int current_command_line_count, saved_command_line_count;
- extern int last_command_exit_value;
- extern int array_needs_making;
- extern int posixly_correct, no_symbolic_links;
-+extern int sigalrm_seen;
- extern char *current_prompt_string, *ps1_prompt;
- extern STRING_INT_ALIST word_token_alist[];
- extern sh_builtin_func_t *last_shell_builtin, *this_shell_builtin;
-@@ -4208,8 +4209,9 @@ bash_event_hook ()
- {
-   /* If we're going to longjmp to top_level, make sure we clean up readline.
-      check_signals will call QUIT, which will eventually longjmp to top_level,
--     calling run_interrupt_trap along the way. */
--  if (interrupt_state)
-+     calling run_interrupt_trap along the way.  The check for sigalrm_seen is
-+     to clean up the read builtin's state. */
-+  if (terminating_signal || interrupt_state || sigalrm_seen)
-     rl_cleanup_after_signal ();
-   bashline_reset_event_hook ();
-   check_signals_and_traps ();	/* XXX */
-diff --git a/builtins/common.h b/builtins/common.h
-index cae16b10fb65..a1298cb9c84a 100644
---- a/builtins/common.h
-+++ b/builtins/common.h
-@@ -122,6 +122,10 @@ extern void bash_logout __P((void));
- /* Functions from getopts.def */
- extern void getopts_reset __P((int));
- 
-+/* Functions from read.def */
-+extern void read_tty_cleanup __P((void));
-+extern int read_tty_modified __P((void));
-+
- /* Functions from set.def */
- extern int minus_o_option_value __P((char *));
- extern void list_minus_o_opts __P((int, int));
-diff --git a/builtins/read.def b/builtins/read.def
-index 43971544d081..56c23010bbe8 100644
---- a/builtins/read.def
-+++ b/builtins/read.def
-@@ -140,10 +140,12 @@ static void reset_alarm __P((void));
- procenv_t alrmbuf;
- int sigalrm_seen;
- 
--static int reading;
-+static int reading, tty_modified;
- static SigHandler *old_alrm;
- static unsigned char delim;
- 
-+static struct ttsave termsave;
-+
- /* In all cases, SIGALRM just sets a flag that we check periodically.  This
-    avoids problems with the semi-tricky stuff we do with the xfree of
-    input_string at the top of the unwind-protect list (see below). */
-@@ -188,7 +190,6 @@ read_builtin (list)
-   struct stat tsb;
-   SHELL_VAR *var;
-   TTYSTRUCT ttattrs, ttset;
--  struct ttsave termsave;
- #if defined (ARRAY_VARS)
-   WORD_LIST *alist;
- #endif
-@@ -221,7 +222,7 @@ read_builtin (list)
-   USE_VAR(ps2);
-   USE_VAR(lastsig);
- 
--  sigalrm_seen = reading = 0;
-+  sigalrm_seen = reading = tty_modified = 0;
- 
-   i = 0;		/* Index into the string that we are reading. */
-   raw = edit = 0;	/* Not reading raw input by default. */
-@@ -438,6 +439,8 @@ read_builtin (list)
- 	  retval = 128+SIGALRM;
- 	  goto assign_vars;
- 	}
-+      if (interactive_shell == 0)
-+	initialize_terminating_signals ();
-       old_alrm = set_signal_handler (SIGALRM, sigalrm);
-       add_unwind_protect (reset_alarm, (char *)NULL);
- #if defined (READLINE)
-@@ -482,7 +485,10 @@ read_builtin (list)
- 	  i = silent ? ttfd_cbreak (fd, &ttset) : ttfd_onechar (fd, &ttset);
- 	  if (i < 0)
- 	    sh_ttyerror (1);
-+	  tty_modified = 1;
- 	  add_unwind_protect ((Function *)ttyrestore, (char *)&termsave);
-+	  if (interactive_shell == 0)
-+	    initialize_terminating_signals ();
- 	}
-     }
-   else if (silent)	/* turn off echo but leave term in canonical mode */
-@@ -497,7 +503,10 @@ read_builtin (list)
-       if (i < 0)
- 	sh_ttyerror (1);
- 
-+      tty_modified = 1;
-       add_unwind_protect ((Function *)ttyrestore, (char *)&termsave);
-+      if (interactive_shell == 0)
-+	initialize_terminating_signals ();
-     }
- 
-   /* This *must* be the top unwind-protect on the stack, so the manipulation
-@@ -588,6 +597,8 @@ read_builtin (list)
- 	    }
- 	  else
- 	    lastsig = 0;
-+	  if (terminating_signal && tty_modified)
-+	    ttyrestore (&termsave);	/* fix terminal before exiting */
- 	  CHECK_TERMSIG;
- 	  eof = 1;
- 	  break;
-@@ -978,6 +989,20 @@ ttyrestore (ttp)
-      struct ttsave *ttp;
- {
-   ttsetattr (ttp->fd, ttp->attrs);
-+  tty_modified = 0;
-+}
-+
-+void
-+read_tty_cleanup ()
-+{
-+  if (tty_modified)
-+    ttyrestore (&termsave);
-+}
-+
-+int
-+read_tty_modified ()
-+{
-+  return (tty_modified);
- }
- 
- #if defined (READLINE)
-diff --git a/patchlevel.h b/patchlevel.h
-index b8bf38704ed2..cefe6bdd3a13 100644
---- a/patchlevel.h
-+++ b/patchlevel.h
-@@ -25,6 +25,6 @@
-    regexp `^#define[ 	]*PATCHLEVEL', since that's what support/mkversion.sh
-    looks for to find the patch level (for the sccs version string). */
- 
--#define PATCHLEVEL 32
-+#define PATCHLEVEL 33
- 
- #endif /* _PATCHLEVEL_H_ */
-diff --git a/shell.c b/shell.c
-index bbc8a66cc2eb..2fd8179ba10d 100644
---- a/shell.c
-+++ b/shell.c
-@@ -73,6 +73,7 @@
- #endif
- 
- #if defined (READLINE)
-+#  include <readline/readline.h>
- #  include "bashline.h"
- #endif
- 
-@@ -909,6 +910,14 @@ exit_shell (s)
-   fflush (stdout);		/* XXX */
-   fflush (stderr);
- 
-+  /* Clean up the terminal if we are in a state where it's been modified. */
-+#if defined (READLINE)
-+  if (RL_ISSTATE (RL_STATE_TERMPREPPED) && rl_deprep_term_function)
-+    (*rl_deprep_term_function) ();
-+#endif
-+  if (read_tty_modified ())
-+    read_tty_cleanup ();
-+
-   /* Do trap[0] if defined.  Allow it to override the exit status
-      passed to us. */
-   if (signal_is_trapped (0))
-diff --git a/sig.c b/sig.c
-index 3b62ea5d7c5d..8bc45c17f478 100644
---- a/sig.c
-+++ b/sig.c
-@@ -532,8 +532,10 @@ termsig_sighandler (sig)
- #if defined (READLINE)
-   /* Set the event hook so readline will call it after the signal handlers
-      finish executing, so if this interrupted character input we can get
--     quick response. */
--  if (interactive_shell && interactive && no_line_editing == 0)
-+     quick response.  If readline is active or has modified the terminal we
-+     need to set this no matter what the signal is, though the check for
-+     RL_STATE_TERMPREPPED is possibly redundant. */
-+  if (RL_ISSTATE (RL_STATE_SIGHANDLER) || RL_ISSTATE (RL_STATE_TERMPREPPED))
-     bashline_set_event_hook ();
- #endif
- 
diff --git a/patches/bash-4.3.30/series b/patches/bash-4.3.30/series
deleted file mode 100644
index 2e1fdf17f..000000000
--- a/patches/bash-4.3.30/series
+++ /dev/null
@@ -1,6 +0,0 @@
-# generated by git-ptx-patches
-#tag:base --start-number 1
-0001-Bash-4.3-patch-31.patch
-0002-Bash-4.3-patch-32.patch
-0003-Bash-4.3-patch-33.patch
-# 602897f584d96d29536a2fa60f8d5e23  - git-ptx-patches magic
diff --git a/patches/bash-5.1.8/0001-Bash-5.1-patch-12.patch b/patches/bash-5.1.8/0001-Bash-5.1-patch-12.patch
new file mode 100644
index 000000000..ef8b6ae7f
--- /dev/null
+++ b/patches/bash-5.1.8/0001-Bash-5.1-patch-12.patch
@@ -0,0 +1,262 @@
+diff -urN bash-5.1.8.orig/builtins/wait.def bash-5.1.8/builtins/wait.def
+--- bash-5.1.8.orig/builtins/wait.def	2021-12-21 12:57:08.083139502 +0100
++++ bash-5.1.8/builtins/wait.def	2021-12-21 12:58:24.304849937 +0100
+@@ -111,7 +111,8 @@
+ wait_builtin (list)
+      WORD_LIST *list;
+ {
+-  int status, code, opt, nflag, wflags;
++  int status, code, opt, nflag;
++  volatile int wflags;
+   char *vname;
+   SHELL_VAR *pidvar;
+   struct procstat pstat;
+@@ -180,6 +181,8 @@
+       last_command_exit_signal = wait_signal_received;
+       status = 128 + wait_signal_received;
+       wait_sigint_cleanup ();
++      if (wflags & JWAIT_WAITING)
++	unset_waitlist ();
+       WAIT_RETURN (status);
+     }
+ 
+diff -urN bash-5.1.8.orig/command.h bash-5.1.8/command.h
+--- bash-5.1.8.orig/command.h	2021-12-21 12:57:08.099139862 +0100
++++ bash-5.1.8/command.h	2021-12-21 12:58:27.760927380 +0100
+@@ -124,6 +124,7 @@
+ #define SUBSHELL_PROCSUB 0x20	/* subshell caused by <(command) or >(command) */
+ #define SUBSHELL_COPROC	0x40	/* subshell from a coproc pipeline */
+ #define SUBSHELL_RESETTRAP 0x80	/* subshell needs to reset trap strings on first call to trap */
++#define SUBSHELL_IGNTRAP 0x100  /* subshell should reset trapped signals from trap_handler */
+ 
+ /* A structure which represents a word. */
+ typedef struct word_desc {
+diff -urN bash-5.1.8.orig/execute_cmd.c bash-5.1.8/execute_cmd.c
+--- bash-5.1.8.orig/execute_cmd.c	2021-12-21 12:57:08.091139683 +0100
++++ bash-5.1.8/execute_cmd.c	2021-12-21 12:58:27.764927470 +0100
+@@ -1547,6 +1547,9 @@
+   clear_pending_traps ();
+   reset_signal_handlers ();
+   subshell_environment |= SUBSHELL_RESETTRAP;
++  /* Note that signal handlers have been reset, so we should no longer
++    reset the handler and resend trapped signals to ourselves. */
++  subshell_environment &= ~SUBSHELL_IGNTRAP;
+ 
+   /* We are in a subshell, so forget that we are running a trap handler or
+      that the signal handler has changed (we haven't changed it!) */
+@@ -4320,7 +4323,8 @@
+ 	  already_forked = 1;
+ 	  cmdflags |= CMD_NO_FORK;
+ 
+-	  subshell_environment = SUBSHELL_FORK;		/* XXX */
++	  /* We redo some of what make_child() does with SUBSHELL_IGNTRAP */
++	  subshell_environment = SUBSHELL_FORK|SUBSHELL_IGNTRAP;	/* XXX */
+ 	  if (pipe_in != NO_PIPE || pipe_out != NO_PIPE)
+ 	    subshell_environment |= SUBSHELL_PIPE;
+ 	  if (async)
+@@ -4574,6 +4578,7 @@
+ 	     trap strings if we run trap to change a signal disposition. */
+ 	  reset_signal_handlers ();
+ 	  subshell_environment |= SUBSHELL_RESETTRAP;
++	  subshell_environment &= ~SUBSHELL_IGNTRAP;
+ 
+ 	  if (async)
+ 	    {
+@@ -5514,6 +5519,7 @@
+       reset_terminating_signals ();	/* XXX */
+       /* Cancel traps, in trap.c. */
+       restore_original_signals ();
++      subshell_environment &= ~SUBSHELL_IGNTRAP;
+ 
+ #if defined (JOB_CONTROL)
+       FREE (p);
+diff -urN bash-5.1.8.orig/jobs.c bash-5.1.8/jobs.c
+--- bash-5.1.8.orig/jobs.c	2021-12-21 12:57:08.091139683 +0100
++++ bash-5.1.8/jobs.c	2021-12-21 12:58:27.764927470 +0100
+@@ -2217,6 +2217,8 @@
+ 	 signals to the default state for a new process. */
+       pid_t mypid;
+ 
++      subshell_environment |= SUBSHELL_IGNTRAP;
++
+       /* If this ends up being changed to modify or use `command' in the
+ 	 child process, go back and change callers who free `command' in
+ 	 the child process when this returns. */
+diff -urN bash-5.1.8.orig/lib/malloc/malloc.c bash-5.1.8/lib/malloc/malloc.c
+--- bash-5.1.8.orig/lib/malloc/malloc.c	2021-12-21 12:57:08.095139773 +0100
++++ bash-5.1.8/lib/malloc/malloc.c	2021-12-21 12:58:22.200802784 +0100
+@@ -1286,13 +1286,12 @@
+       p = (union mhead *) ap - 1;
+     }
+ 
+-  /* XXX - should we return 0 if ISFREE? */
+-  maxbytes = binsize(p->mh_index);
+-
+-  /* So the usable size is the maximum number of bytes in the bin less the
+-     malloc overhead */
+-  maxbytes -= MOVERHEAD + MSLOP;
+-  return (maxbytes);
++  /* return 0 if ISFREE */
++  if (p->mh_alloc == ISFREE)
++    return 0;
++  
++  /* Since we use bounds checking, the usable size is the last requested size. */
++  return (p->mh_nbytes);
+ }
+ 
+ #if !defined (NO_VALLOC)
+diff -urN bash-5.1.8.orig/nojobs.c bash-5.1.8/nojobs.c
+--- bash-5.1.8.orig/nojobs.c	2021-12-21 12:57:08.091139683 +0100
++++ bash-5.1.8/nojobs.c	2021-12-21 12:58:27.764927470 +0100
+@@ -575,6 +575,8 @@
+ 	last_asynchronous_pid = getpid ();
+ #endif
+ 
++      subshell_environment |= SUBSHELL_IGNTRAP;
++
+       default_tty_job_signals ();
+     }
+   else
+diff -urN bash-5.1.8.orig/parse.y bash-5.1.8/parse.y
+--- bash-5.1.8.orig/parse.y	2021-12-21 12:57:08.099139862 +0100
++++ bash-5.1.8/parse.y	2021-12-21 12:58:26.112890455 +0100
+@@ -6493,10 +6493,8 @@
+   old_expand_aliases = expand_aliases;
+ 
+   push_stream (1);
+-#if 0 /* TAG: bash-5.2 Alex fxmbsw7 Ratchev <fxmbsw7@gmail.com> 11/17/2020 */
+   if (ea = expanding_alias ())
+     parser_save_alias ();
+-#endif
+   last_read_token = WORD;		/* WORD to allow reserved words here */
+   current_command_line_count = 0;
+   echo_input_at_read = expand_aliases = 0;
+@@ -6531,10 +6529,8 @@
+   last_read_token = '\n';
+   pop_stream ();
+ 
+-#if 0 /* TAG: bash-5.2 */
+   if (ea)
+     parser_restore_alias ();
+-#endif
+ 
+ #if defined (HISTORY)
+   remember_on_history = old_remember_on_history;
+diff -urN bash-5.1.8.orig/patchlevel.h bash-5.1.8/patchlevel.h
+--- bash-5.1.8.orig/patchlevel.h	2021-12-21 12:57:08.075139321 +0100
++++ bash-5.1.8/patchlevel.h	2021-12-21 12:58:27.764927470 +0100
+@@ -25,6 +25,6 @@
+    regexp `^#define[ 	]*PATCHLEVEL', since that's what support/mkversion.sh
+    looks for to find the patch level (for the sccs version string). */
+ 
+-#define PATCHLEVEL 8
++#define PATCHLEVEL 12
+ 
+ #endif /* _PATCHLEVEL_H_ */
+diff -urN bash-5.1.8.orig/sig.c bash-5.1.8/sig.c
+--- bash-5.1.8.orig/sig.c	2021-12-21 12:57:08.071139231 +0100
++++ bash-5.1.8/sig.c	2021-12-21 12:58:27.764927470 +0100
+@@ -55,7 +55,8 @@
+ #  include "bashhist.h"
+ #endif
+ 
+-extern void initialize_siglist ();
++extern void initialize_siglist PARAMS((void));
++extern void set_original_signal PARAMS((int, SigHandler *));
+ 
+ #if !defined (JOB_CONTROL)
+ extern void initialize_job_signals PARAMS((void));
+@@ -255,6 +256,13 @@
+       sigaction (XSIG (i), &act, &oact);
+       XHANDLER(i) = oact.sa_handler;
+       XSAFLAGS(i) = oact.sa_flags;
++
++#if 0
++      set_original_signal (XSIG(i), XHANDLER(i));	/* optimization */
++#else
++      set_original_signal (XSIG(i), act.sa_handler);	/* optimization */
++#endif
++
+       /* Don't do anything with signals that are ignored at shell entry
+ 	 if the shell is not interactive. */
+       /* XXX - should we do this for interactive shells, too? */
+diff -urN bash-5.1.8.orig/subst.c bash-5.1.8/subst.c
+--- bash-5.1.8.orig/subst.c	2021-12-21 12:57:08.099139862 +0100
++++ bash-5.1.8/subst.c	2021-12-21 12:58:27.764927470 +0100
+@@ -5951,6 +5951,7 @@
+       free_pushed_string_input ();
+       /* Cancel traps, in trap.c. */
+       restore_original_signals ();	/* XXX - what about special builtins? bash-4.2 */
++      subshell_environment &= ~SUBSHELL_IGNTRAP;
+       QUIT;	/* catch any interrupts we got post-fork */
+       setup_async_signals ();
+ #if 0
+@@ -6382,6 +6383,7 @@
+ 	}	
+       QUIT;	/* catch any interrupts we got post-fork */
+       subshell_environment |= SUBSHELL_RESETTRAP;
++      subshell_environment &= ~SUBSHELL_IGNTRAP;
+     }
+ 
+ #if defined (JOB_CONTROL)
+diff -urN bash-5.1.8.orig/trap.c bash-5.1.8/trap.c
+--- bash-5.1.8.orig/trap.c	2021-12-21 12:57:08.083139502 +0100
++++ bash-5.1.8/trap.c	2021-12-21 12:58:27.764927470 +0100
+@@ -481,6 +481,32 @@
+       SIGRETURN (0);
+     }
+ 
++  /* This means we're in a subshell, but have not yet reset the handler for
++     trapped signals. We're not supposed to execute the trap in this situation;
++     we should restore the original signal and resend the signal to ourselves
++     to preserve the Posix "signal traps that are not being ignored shall be
++     set to the default action" semantics. */
++  if ((subshell_environment & SUBSHELL_IGNTRAP) && trap_list[sig] != (char *)IGNORE_SIG)
++    {
++      sigset_t mask;
++
++      /* Paranoia */
++      if (original_signals[sig] == IMPOSSIBLE_TRAP_HANDLER)
++	original_signals[sig] = SIG_DFL;
++
++      restore_signal (sig);
++
++      /* Make sure we let the signal we just caught through */
++      sigemptyset (&mask);
++      sigprocmask (SIG_SETMASK, (sigset_t *)NULL, &mask);
++      sigdelset (&mask, sig);
++      sigprocmask (SIG_SETMASK, &mask, (sigset_t *)NULL);
++
++      kill (getpid (), sig);
++
++      SIGRETURN (0);
++    }
++
+   if ((sig >= NSIG) ||
+       (trap_list[sig] == (char *)DEFAULT_SIG) ||
+       (trap_list[sig] == (char *)IGNORE_SIG))
+diff -urN bash-5.1.8.orig/y.tab.c bash-5.1.8/y.tab.c
+--- bash-5.1.8.orig/y.tab.c	2021-12-21 12:57:08.075139321 +0100
++++ bash-5.1.8/y.tab.c	2021-12-21 12:58:26.116890545 +0100
+@@ -8787,10 +8787,8 @@
+   old_expand_aliases = expand_aliases;
+ 
+   push_stream (1);
+-#if 0 /* TAG: bash-5.2 Alex fxmbsw7 Ratchev <fxmbsw7@gmail.com> 11/17/2020 */
+   if (ea = expanding_alias ())
+     parser_save_alias ();
+-#endif
+   last_read_token = WORD;		/* WORD to allow reserved words here */
+   current_command_line_count = 0;
+   echo_input_at_read = expand_aliases = 0;
+@@ -8825,10 +8823,8 @@
+   last_read_token = '\n';
+   pop_stream ();
+ 
+-#if 0 /* TAG: bash-5.2 */
+   if (ea)
+     parser_restore_alias ();
+-#endif
+ 
+ #if defined (HISTORY)
+   remember_on_history = old_remember_on_history;
diff --git a/patches/bash-5.1.8/series b/patches/bash-5.1.8/series
new file mode 100644
index 000000000..0e53e492a
--- /dev/null
+++ b/patches/bash-5.1.8/series
@@ -0,0 +1 @@
+0001-Bash-5.1-patch-12.patch
diff --git a/rules/bash.make b/rules/bash.make
index bed121586..881ba55fd 100644
--- a/rules/bash.make
+++ b/rules/bash.make
@@ -13,8 +13,8 @@ PACKAGES-$(PTXCONF_BASH) += bash
 #
 # Paths and names
 #
-BASH_VERSION	:= 4.3.30
-BASH_MD5	:= a27b3ee9be83bd3ba448c0ff52b28447
+BASH_VERSION	:= 5.1.8
+BASH_MD5	:= 23eee6195b47318b9fd878e590ccb38c
 BASH		:= bash-$(BASH_VERSION)
 BASH_SUFFIX	:= tar.gz
 BASH_URL	:= $(call ptx/mirror, GNU, bash/$(BASH).$(BASH_SUFFIX))
@@ -24,7 +24,7 @@ BASH_MAKE_PAR	:= NO
 BASH_LICENSE	:= GPL-3.0-or-later
 BASH_LICENSE_FILES	:= \
 	file://COPYING;md5=d32239bcb673463ab874e80d47fae504 \
-	file://general.c;startline=1;endline=19;md5=c018785d21f8c206ca7a13fa7d027568
+	file://general.c;startline=1;endline=19;md5=94a266185749191808da241ccc2af60a
 
 # ----------------------------------------------------------------------------
 # Prepare
-- 
2.30.2


_______________________________________________
ptxdist mailing list
ptxdist@pengutronix.de
To unsubscribe, send a mail with subject "unsubscribe" to ptxdist-request@pengutronix.de


^ permalink raw reply	[flat|nested] 2+ messages in thread

* Re: [ptxdist] [APPLIED] bash: Version bump. 4.3.30 (+patches-33) -> 5.1.8 (patches-12).
  2022-01-07  8:24 [ptxdist] [PATCH v3] bash: Version bump. 4.3.30 (+patches-33) -> 5.1.8 (patches-12) Christian Melki
@ 2022-01-21  7:19 ` Michael Olbrich
  0 siblings, 0 replies; 2+ messages in thread
From: Michael Olbrich @ 2022-01-21  7:19 UTC (permalink / raw)
  To: ptxdist; +Cc: Christian Melki

Thanks, applied as faab202f74b433238d558ea184788f11fb6c0dea.

Michael

[sent from post-receive hook]

On Fri, 21 Jan 2022 08:19:21 +0100, Christian Melki <christian.melki@t2data.com> wrote:
> Upgrade bash to 5.1.8 plus patches to patchlevel 12.
> Roll up patches as in previous series.
> Remove old patches.
> 
> Fixes CVE-2019-18276 and CVE-2019-9924 with a new baseline, without patches.
> Fixes loads of bugs in bash.
> 
> Update licence file hash, general.c changed a copyright year.
> 
> Signed-off-by: Christian Melki <christian.melki@t2data.com>
> Message-Id: <20220107082454.4188431-1-christian.melki@t2data.com>
> Signed-off-by: Michael Olbrich <m.olbrich@pengutronix.de>
> 
> diff --git a/patches/bash-4.3.30/0001-Bash-4.3-patch-31.patch b/patches/bash-4.3.30/0001-Bash-4.3-patch-31.patch
> deleted file mode 100644
> index d9a187dcb461..000000000000
> --- a/patches/bash-4.3.30/0001-Bash-4.3-patch-31.patch
> +++ /dev/null
> @@ -1,5467 +0,0 @@
> -From: Chet Ramey <chet.ramey@case.edu>
> -Date: Thu, 15 Jan 2015 10:20:04 -0500
> -Subject: [PATCH] Bash-4.3 patch 31
> -
> ----
> - patchlevel.h     |    2 +-
> - subst.h          |    1 +
> - variables.c      |   32 +-
> - variables.c.orig | 5365 ++++++++++++++++++++++++++++++++++++++++++++++++++++++
> - 4 files changed, 5397 insertions(+), 3 deletions(-)
> - create mode 100644 variables.c.orig
> -
> -diff --git a/patchlevel.h b/patchlevel.h
> -index e5dde5245275..0ad46aafbdd9 100644
> ---- a/patchlevel.h
> -+++ b/patchlevel.h
> -@@ -25,6 +25,6 @@
> -    regexp `^#define[ 	]*PATCHLEVEL', since that's what support/mkversion.sh
> -    looks for to find the patch level (for the sccs version string). */
> - 
> --#define PATCHLEVEL 30
> -+#define PATCHLEVEL 31
> - 
> - #endif /* _PATCHLEVEL_H_ */
> -diff --git a/subst.h b/subst.h
> -index cedaf8b6b444..1c300ab96b04 100644
> ---- a/subst.h
> -+++ b/subst.h
> -@@ -47,6 +47,7 @@
> - #define ASS_MKASSOC	0x0004
> - #define ASS_MKGLOBAL	0x0008	/* force global assignment */
> - #define ASS_NAMEREF	0x0010	/* assigning to nameref variable */
> -+#define ASS_FROMREF	0x0020	/* assigning from value of nameref variable */
> - 
> - /* Flags for the string extraction functions. */
> - #define SX_NOALLOC	0x0001	/* just skip; don't return substring */
> -diff --git a/variables.c b/variables.c
> -index 7c82710e0f0b..81b7877e32e8 100644
> ---- a/variables.c
> -+++ b/variables.c
> -@@ -2516,10 +2516,27 @@ bind_variable_internal (name, value, table, hflags, aflags)
> -      HASH_TABLE *table;
> -      int hflags, aflags;
> - {
> --  char *newval;
> -+  char *newname, *newval;
> -   SHELL_VAR *entry;
> -+#if defined (ARRAY_VARS)
> -+  arrayind_t ind;
> -+  char *subp;
> -+  int sublen;
> -+#endif
> - 
> -+  newname = 0;
> -+#if defined (ARRAY_VARS)
> -+  if ((aflags & ASS_FROMREF) && (hflags & HASH_NOSRCH) == 0 && valid_array_reference (name))
> -+    {
> -+      newname = array_variable_name (name, &subp, &sublen);
> -+      if (newname == 0)
> -+	return (SHELL_VAR *)NULL;	/* XXX */
> -+      entry = hash_lookup (newname, table);
> -+    }
> -+  else
> -+#endif
> -   entry = (hflags & HASH_NOSRCH) ? (SHELL_VAR *)NULL : hash_lookup (name, table);
> -+
> -   /* Follow the nameref chain here if this is the global variables table */
> -   if (entry && nameref_p (entry) && (invisible_p (entry) == 0) && table == global_variables->table)
> -     {
> -@@ -2550,6 +2567,16 @@ bind_variable_internal (name, value, table, hflags, aflags)
> -       var_setvalue (entry, make_variable_value (entry, value, 0));
> -       }
> -     }
> -+#if defined (ARRAY_VARS)
> -+  else if (entry == 0 && newname)
> -+    {
> -+      entry = make_new_array_variable (newname);	/* indexed array by default */
> -+      if (entry == 0)
> -+	return entry;
> -+      ind = array_expand_index (name, subp, sublen);
> -+      bind_array_element (entry, ind, value, aflags);
> -+    }
> -+#endif
> -   else if (entry == 0)
> -     {
> -       entry = make_new_variable (name, table);
> -@@ -2670,7 +2697,8 @@ bind_variable (name, value, flags)
> - 			 normal. */
> - 		      if (nameref_cell (nv) == 0)
> - 			return (bind_variable_internal (nv->name, value, nvc->table, 0, flags));
> --		      return (bind_variable_internal (nameref_cell (nv), value, nvc->table, 0, flags));
> -+		      /* XXX - bug here with ref=array[index] */
> -+		      return (bind_variable_internal (nameref_cell (nv), value, nvc->table, 0, flags|ASS_FROMREF));
> - 		    }
> - 		  else
> - 		    v = nv;
> -diff --git a/variables.c.orig b/variables.c.orig
> -new file mode 100644
> -index 000000000000..7c82710e0f0b
> ---- /dev/null
> -+++ b/variables.c.orig
> -@@ -0,0 +1,5365 @@
> -+/* variables.c -- Functions for hacking shell variables. */
> -+
> -+/* Copyright (C) 1987-2013 Free Software Foundation, Inc.
> -+
> -+   This file is part of GNU Bash, the Bourne Again SHell.
> -+
> -+   Bash is free software: you can redistribute it and/or modify
> -+   it under the terms of the GNU General Public License as published by
> -+   the Free Software Foundation, either version 3 of the License, or
> -+   (at your option) any later version.
> -+
> -+   Bash is distributed in the hope that it will be useful,
> -+   but WITHOUT ANY WARRANTY; without even the implied warranty of
> -+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
> -+   GNU General Public License for more details.
> -+
> -+   You should have received a copy of the GNU General Public License
> -+   along with Bash.  If not, see <http://www.gnu.org/licenses/>.
> -+*/
> -+
> -+#include "config.h"
> -+
> -+#include "bashtypes.h"
> -+#include "posixstat.h"
> -+#include "posixtime.h"
> -+
> -+#if defined (__QNX__)
> -+#  if defined (__QNXNTO__)
> -+#    include <sys/netmgr.h>
> -+#  else
> -+#    include <sys/vc.h>
> -+#  endif /* !__QNXNTO__ */
> -+#endif /* __QNX__ */
> -+
> -+#if defined (HAVE_UNISTD_H)
> -+#  include <unistd.h>
> -+#endif
> -+
> -+#include <stdio.h>
> -+#include "chartypes.h"
> -+#if defined (HAVE_PWD_H)
> -+#  include <pwd.h>
> -+#endif
> -+#include "bashansi.h"
> -+#include "bashintl.h"
> -+
> -+#define NEED_XTRACE_SET_DECL
> -+
> -+#include "shell.h"
> -+#include "flags.h"
> -+#include "execute_cmd.h"
> -+#include "findcmd.h"
> -+#include "mailcheck.h"
> -+#include "input.h"
> -+#include "hashcmd.h"
> -+#include "pathexp.h"
> -+#include "alias.h"
> -+#include "jobs.h"
> -+
> -+#include "version.h"
> -+
> -+#include "builtins/getopt.h"
> -+#include "builtins/common.h"
> -+#include "builtins/builtext.h"
> -+
> -+#if defined (READLINE)
> -+#  include "bashline.h"
> -+#  include <readline/readline.h>
> -+#else
> -+#  include <tilde/tilde.h>
> -+#endif
> -+
> -+#if defined (HISTORY)
> -+#  include "bashhist.h"
> -+#  include <readline/history.h>
> -+#endif /* HISTORY */
> -+
> -+#if defined (PROGRAMMABLE_COMPLETION)
> -+#  include "pcomplete.h"
> -+#endif
> -+
> -+#define TEMPENV_HASH_BUCKETS	4	/* must be power of two */
> -+
> -+#define ifsname(s)	((s)[0] == 'I' && (s)[1] == 'F' && (s)[2] == 'S' && (s)[3] == '\0')
> -+
> -+#define BASHFUNC_PREFIX		"BASH_FUNC_"
> -+#define BASHFUNC_PREFLEN	10	/* == strlen(BASHFUNC_PREFIX */
> -+#define BASHFUNC_SUFFIX		"%%"
> -+#define BASHFUNC_SUFFLEN	2	/* == strlen(BASHFUNC_SUFFIX) */
> -+
> -+extern char **environ;
> -+
> -+/* Variables used here and defined in other files. */
> -+extern int posixly_correct;
> -+extern int line_number, line_number_base;
> -+extern int subshell_environment, indirection_level, subshell_level;
> -+extern int build_version, patch_level;
> -+extern int expanding_redir;
> -+extern int last_command_exit_value;
> -+extern char *dist_version, *release_status;
> -+extern char *shell_name;
> -+extern char *primary_prompt, *secondary_prompt;
> -+extern char *current_host_name;
> -+extern sh_builtin_func_t *this_shell_builtin;
> -+extern SHELL_VAR *this_shell_function;
> -+extern char *the_printed_command_except_trap;
> -+extern char *this_command_name;
> -+extern char *command_execution_string;
> -+extern time_t shell_start_time;
> -+extern int assigning_in_environment;
> -+extern int executing_builtin;
> -+extern int funcnest_max;
> -+
> -+#if defined (READLINE)
> -+extern int no_line_editing;
> -+extern int perform_hostname_completion;
> -+#endif
> -+
> -+/* The list of shell variables that the user has created at the global
> -+   scope, or that came from the environment. */
> -+VAR_CONTEXT *global_variables = (VAR_CONTEXT *)NULL;
> -+
> -+/* The current list of shell variables, including function scopes */
> -+VAR_CONTEXT *shell_variables = (VAR_CONTEXT *)NULL;
> -+
> -+/* The list of shell functions that the user has created, or that came from
> -+   the environment. */
> -+HASH_TABLE *shell_functions = (HASH_TABLE *)NULL;
> -+
> -+#if defined (DEBUGGER)
> -+/* The table of shell function definitions that the user defined or that
> -+   came from the environment. */
> -+HASH_TABLE *shell_function_defs = (HASH_TABLE *)NULL;
> -+#endif
> -+
> -+/* The current variable context.  This is really a count of how deep into
> -+   executing functions we are. */
> -+int variable_context = 0;
> -+
> -+/* The set of shell assignments which are made only in the environment
> -+   for a single command. */
> -+HASH_TABLE *temporary_env = (HASH_TABLE *)NULL;
> -+
> -+/* Set to non-zero if an assignment error occurs while putting variables
> -+   into the temporary environment. */
> -+int tempenv_assign_error;
> -+
> -+/* Some funky variables which are known about specially.  Here is where
> -+   "$*", "$1", and all the cruft is kept. */
> -+char *dollar_vars[10];
> -+WORD_LIST *rest_of_args = (WORD_LIST *)NULL;
> -+
> -+/* The value of $$. */
> -+pid_t dollar_dollar_pid;
> -+
> -+/* Non-zero means that we have to remake EXPORT_ENV. */
> -+int array_needs_making = 1;
> -+
> -+/* The number of times BASH has been executed.  This is set
> -+   by initialize_variables (). */
> -+int shell_level = 0;
> -+
> -+/* An array which is passed to commands as their environment.  It is
> -+   manufactured from the union of the initial environment and the
> -+   shell variables that are marked for export. */
> -+char **export_env = (char **)NULL;
> -+static int export_env_index;
> -+static int export_env_size;
> -+
> -+#if defined (READLINE)
> -+static int winsize_assignment;		/* currently assigning to LINES or COLUMNS */
> -+#endif
> -+
> -+static HASH_TABLE *last_table_searched;	/* hash_lookup sets this */
> -+
> -+/* Some forward declarations. */
> -+static void create_variable_tables __P((void));
> -+
> -+static void set_machine_vars __P((void));
> -+static void set_home_var __P((void));
> -+static void set_shell_var __P((void));
> -+static char *get_bash_name __P((void));
> -+static void initialize_shell_level __P((void));
> -+static void uidset __P((void));
> -+#if defined (ARRAY_VARS)
> -+static void make_vers_array __P((void));
> -+#endif
> -+
> -+static SHELL_VAR *null_assign __P((SHELL_VAR *, char *, arrayind_t, char *));
> -+#if defined (ARRAY_VARS)
> -+static SHELL_VAR *null_array_assign __P((SHELL_VAR *, char *, arrayind_t, char *));
> -+#endif
> -+static SHELL_VAR *get_self __P((SHELL_VAR *));
> -+
> -+#if defined (ARRAY_VARS)
> -+static SHELL_VAR *init_dynamic_array_var __P((char *, sh_var_value_func_t *, sh_var_assign_func_t *, int));
> -+static SHELL_VAR *init_dynamic_assoc_var __P((char *, sh_var_value_func_t *, sh_var_assign_func_t *, int));
> -+#endif
> -+
> -+static SHELL_VAR *assign_seconds __P((SHELL_VAR *, char *, arrayind_t, char *));
> -+static SHELL_VAR *get_seconds __P((SHELL_VAR *));
> -+static SHELL_VAR *init_seconds_var __P((void));
> -+
> -+static int brand __P((void));
> -+static void sbrand __P((unsigned long));		/* set bash random number generator. */
> -+static void seedrand __P((void));			/* seed generator randomly */
> -+static SHELL_VAR *assign_random __P((SHELL_VAR *, char *, arrayind_t, char *));
> -+static SHELL_VAR *get_random __P((SHELL_VAR *));
> -+
> -+static SHELL_VAR *assign_lineno __P((SHELL_VAR *, char *, arrayind_t, char *));
> -+static SHELL_VAR *get_lineno __P((SHELL_VAR *));
> -+
> -+static SHELL_VAR *assign_subshell __P((SHELL_VAR *, char *, arrayind_t, char *));
> -+static SHELL_VAR *get_subshell __P((SHELL_VAR *));
> -+
> -+static SHELL_VAR *get_bashpid __P((SHELL_VAR *));
> -+
> -+#if defined (HISTORY)
> -+static SHELL_VAR *get_histcmd __P((SHELL_VAR *));
> -+#endif
> -+
> -+#if defined (READLINE)
> -+static SHELL_VAR *get_comp_wordbreaks __P((SHELL_VAR *));
> -+static SHELL_VAR *assign_comp_wordbreaks __P((SHELL_VAR *, char *, arrayind_t, char *));
> -+#endif
> -+
> -+#if defined (PUSHD_AND_POPD) && defined (ARRAY_VARS)
> -+static SHELL_VAR *assign_dirstack __P((SHELL_VAR *, char *, arrayind_t, char *));
> -+static SHELL_VAR *get_dirstack __P((SHELL_VAR *));
> -+#endif
> -+
> -+#if defined (ARRAY_VARS)
> -+static SHELL_VAR *get_groupset __P((SHELL_VAR *));
> -+
> -+static SHELL_VAR *build_hashcmd __P((SHELL_VAR *));
> -+static SHELL_VAR *get_hashcmd __P((SHELL_VAR *));
> -+static SHELL_VAR *assign_hashcmd __P((SHELL_VAR *,  char *, arrayind_t, char *));
> -+#  if defined (ALIAS)
> -+static SHELL_VAR *build_aliasvar __P((SHELL_VAR *));
> -+static SHELL_VAR *get_aliasvar __P((SHELL_VAR *));
> -+static SHELL_VAR *assign_aliasvar __P((SHELL_VAR *,  char *, arrayind_t, char *));
> -+#  endif
> -+#endif
> -+
> -+static SHELL_VAR *get_funcname __P((SHELL_VAR *));
> -+static SHELL_VAR *init_funcname_var __P((void));
> -+
> -+static void initialize_dynamic_variables __P((void));
> -+
> -+static SHELL_VAR *hash_lookup __P((const char *, HASH_TABLE *));
> -+static SHELL_VAR *new_shell_variable __P((const char *));
> -+static SHELL_VAR *make_new_variable __P((const char *, HASH_TABLE *));
> -+static SHELL_VAR *bind_variable_internal __P((const char *, char *, HASH_TABLE *, int, int));
> -+
> -+static void dispose_variable_value __P((SHELL_VAR *));
> -+static void free_variable_hash_data __P((PTR_T));
> -+
> -+static VARLIST *vlist_alloc __P((int));
> -+static VARLIST *vlist_realloc __P((VARLIST *, int));
> -+static void vlist_add __P((VARLIST *, SHELL_VAR *, int));
> -+
> -+static void flatten __P((HASH_TABLE *, sh_var_map_func_t *, VARLIST *, int));
> -+
> -+static int qsort_var_comp __P((SHELL_VAR **, SHELL_VAR **));
> -+
> -+static SHELL_VAR **vapply __P((sh_var_map_func_t *));
> -+static SHELL_VAR **fapply __P((sh_var_map_func_t *));
> -+
> -+static int visible_var __P((SHELL_VAR *));
> -+static int visible_and_exported __P((SHELL_VAR *));
> -+static int export_environment_candidate __P((SHELL_VAR *));
> -+static int local_and_exported __P((SHELL_VAR *));
> -+static int variable_in_context __P((SHELL_VAR *));
> -+#if defined (ARRAY_VARS)
> -+static int visible_array_vars __P((SHELL_VAR *));
> -+#endif
> -+
> -+static SHELL_VAR *find_nameref_at_context __P((SHELL_VAR *, VAR_CONTEXT *));
> -+static SHELL_VAR *find_variable_nameref_context __P((SHELL_VAR *, VAR_CONTEXT *, VAR_CONTEXT **));
> -+static SHELL_VAR *find_variable_last_nameref_context __P((SHELL_VAR *, VAR_CONTEXT *, VAR_CONTEXT **));
> -+
> -+static SHELL_VAR *bind_tempenv_variable __P((const char *, char *));
> -+static void push_temp_var __P((PTR_T));
> -+static void propagate_temp_var __P((PTR_T));
> -+static void dispose_temporary_env __P((sh_free_func_t *));     
> -+
> -+static inline char *mk_env_string __P((const char *, const char *, int));
> -+static char **make_env_array_from_var_list __P((SHELL_VAR **));
> -+static char **make_var_export_array __P((VAR_CONTEXT *));
> -+static char **make_func_export_array __P((void));
> -+static void add_temp_array_to_env __P((char **, int, int));
> -+
> -+static int n_shell_variables __P((void));
> -+static int set_context __P((SHELL_VAR *));
> -+
> -+static void push_func_var __P((PTR_T));
> -+static void push_exported_var __P((PTR_T));
> -+
> -+static inline int find_special_var __P((const char *));
> -+
> -+static void
> -+create_variable_tables ()
> -+{
> -+  if (shell_variables == 0)
> -+    {
> -+      shell_variables = global_variables = new_var_context ((char *)NULL, 0);
> -+      shell_variables->scope = 0;
> -+      shell_variables->table = hash_create (0);
> -+    }
> -+
> -+  if (shell_functions == 0)
> -+    shell_functions = hash_create (0);
> -+
> -+#if defined (DEBUGGER)
> -+  if (shell_function_defs == 0)
> -+    shell_function_defs = hash_create (0);
> -+#endif
> -+}
> -+
> -+/* Initialize the shell variables from the current environment.
> -+   If PRIVMODE is nonzero, don't import functions from ENV or
> -+   parse $SHELLOPTS. */
> -+void
> -+initialize_shell_variables (env, privmode)
> -+     char **env;
> -+     int privmode;
> -+{
> -+  char *name, *string, *temp_string;
> -+  int c, char_index, string_index, string_length, ro;
> -+  SHELL_VAR *temp_var;
> -+
> -+  create_variable_tables ();
> -+
> -+  for (string_index = 0; string = env[string_index++]; )
> -+    {
> -+      char_index = 0;
> -+      name = string;
> -+      while ((c = *string++) && c != '=')
> -+	;
> -+      if (string[-1] == '=')
> -+	char_index = string - name - 1;
> -+
> -+      /* If there are weird things in the environment, like `=xxx' or a
> -+	 string without an `=', just skip them. */
> -+      if (char_index == 0)
> -+	continue;
> -+
> -+      /* ASSERT(name[char_index] == '=') */
> -+      name[char_index] = '\0';
> -+      /* Now, name = env variable name, string = env variable value, and
> -+	 char_index == strlen (name) */
> -+
> -+      temp_var = (SHELL_VAR *)NULL;
> -+
> -+      /* If exported function, define it now.  Don't import functions from
> -+	 the environment in privileged mode. */
> -+      if (privmode == 0 && read_but_dont_execute == 0 && 
> -+          STREQN (BASHFUNC_PREFIX, name, BASHFUNC_PREFLEN) &&
> -+          STREQ (BASHFUNC_SUFFIX, name + char_index - BASHFUNC_SUFFLEN) &&
> -+	  STREQN ("() {", string, 4))
> -+	{
> -+	  size_t namelen;
> -+	  char *tname;		/* desired imported function name */
> -+
> -+	  namelen = char_index - BASHFUNC_PREFLEN - BASHFUNC_SUFFLEN;
> -+
> -+	  tname = name + BASHFUNC_PREFLEN;	/* start of func name */
> -+	  tname[namelen] = '\0';		/* now tname == func name */
> -+
> -+	  string_length = strlen (string);
> -+	  temp_string = (char *)xmalloc (namelen + string_length + 2);
> -+
> -+	  memcpy (temp_string, tname, namelen);
> -+	  temp_string[namelen] = ' ';
> -+	  memcpy (temp_string + namelen + 1, string, string_length + 1);
> -+
> -+	  /* Don't import function names that are invalid identifiers from the
> -+	     environment, though we still allow them to be defined as shell
> -+	     variables. */
> -+	  if (absolute_program (tname) == 0 && (posixly_correct == 0 || legal_identifier (tname)))
> -+	    parse_and_execute (temp_string, tname, SEVAL_NONINT|SEVAL_NOHIST|SEVAL_FUNCDEF|SEVAL_ONECMD);
> -+
> -+	  if (temp_var = find_function (tname))
> -+	    {
> -+	      VSETATTR (temp_var, (att_exported|att_imported));
> -+	      array_needs_making = 1;
> -+	    }
> -+	  else
> -+	    {
> -+	      if (temp_var = bind_variable (name, string, 0))
> -+		{
> -+		  VSETATTR (temp_var, (att_exported | att_imported | att_invisible));
> -+		  array_needs_making = 1;
> -+		}
> -+	      last_command_exit_value = 1;
> -+	      report_error (_("error importing function definition for `%s'"), tname);
> -+	    }
> -+
> -+	  /* Restore original suffix */
> -+	  tname[namelen] = BASHFUNC_SUFFIX[0];
> -+	}
> -+#if defined (ARRAY_VARS)
> -+#  if ARRAY_EXPORT
> -+      /* Array variables may not yet be exported. */
> -+      else if (*string == '(' && string[1] == '[' && string[strlen (string) - 1] == ')')
> -+	{
> -+	  string_length = 1;
> -+	  temp_string = extract_array_assignment_list (string, &string_length);
> -+	  temp_var = assign_array_from_string (name, temp_string);
> -+	  FREE (temp_string);
> -+	  VSETATTR (temp_var, (att_exported | att_imported));
> -+	  array_needs_making = 1;
> -+	}
> -+#  endif /* ARRAY_EXPORT */
> -+#endif
> -+#if 0
> -+      else if (legal_identifier (name))
> -+#else
> -+      else
> -+#endif
> -+	{
> -+	  ro = 0;
> -+	  if (posixly_correct && STREQ (name, "SHELLOPTS"))
> -+	    {
> -+	      temp_var = find_variable ("SHELLOPTS");
> -+	      ro = temp_var && readonly_p (temp_var);
> -+	      if (temp_var)
> -+		VUNSETATTR (temp_var, att_readonly);
> -+	    }
> -+	  temp_var = bind_variable (name, string, 0);
> -+	  if (temp_var)
> -+	    {
> -+	      if (legal_identifier (name))
> -+		VSETATTR (temp_var, (att_exported | att_imported));
> -+	      else
> -+		VSETATTR (temp_var, (att_exported | att_imported | att_invisible));
> -+	      if (ro)
> -+		VSETATTR (temp_var, att_readonly);
> -+	      array_needs_making = 1;
> -+	    }
> -+	}
> -+
> -+      name[char_index] = '=';
> -+      /* temp_var can be NULL if it was an exported function with a syntax
> -+	 error (a different bug, but it still shouldn't dump core). */
> -+      if (temp_var && function_p (temp_var) == 0)	/* XXX not yet */
> -+	{
> -+	  CACHE_IMPORTSTR (temp_var, name);
> -+	}
> -+    }
> -+
> -+  set_pwd ();
> -+
> -+  /* Set up initial value of $_ */
> -+  temp_var = set_if_not ("_", dollar_vars[0]);
> -+
> -+  /* Remember this pid. */
> -+  dollar_dollar_pid = getpid ();
> -+
> -+  /* Now make our own defaults in case the vars that we think are
> -+     important are missing. */
> -+  temp_var = set_if_not ("PATH", DEFAULT_PATH_VALUE);
> -+#if 0
> -+  set_auto_export (temp_var);	/* XXX */
> -+#endif
> -+
> -+  temp_var = set_if_not ("TERM", "dumb");
> -+#if 0
> -+  set_auto_export (temp_var);	/* XXX */
> -+#endif
> -+
> -+#if defined (__QNX__)
> -+  /* set node id -- don't import it from the environment */
> -+  {
> -+    char node_name[22];
> -+#  if defined (__QNXNTO__)
> -+    netmgr_ndtostr(ND2S_LOCAL_STR, ND_LOCAL_NODE, node_name, sizeof(node_name));
> -+#  else
> -+    qnx_nidtostr (getnid (), node_name, sizeof (node_name));
> -+#  endif
> -+    temp_var = bind_variable ("NODE", node_name, 0);
> -+    set_auto_export (temp_var);
> -+  }
> -+#endif
> -+
> -+  /* set up the prompts. */
> -+  if (interactive_shell)
> -+    {
> -+#if defined (PROMPT_STRING_DECODE)
> -+      set_if_not ("PS1", primary_prompt);
> -+#else
> -+      if (current_user.uid == -1)
> -+	get_current_user_info ();
> -+      set_if_not ("PS1", current_user.euid == 0 ? "# " : primary_prompt);
> -+#endif
> -+      set_if_not ("PS2", secondary_prompt);
> -+    }
> -+  set_if_not ("PS4", "+ ");
> -+
> -+  /* Don't allow IFS to be imported from the environment. */
> -+  temp_var = bind_variable ("IFS", " \t\n", 0);
> -+  setifs (temp_var);
> -+
> -+  /* Magic machine types.  Pretty convenient. */
> -+  set_machine_vars ();
> -+
> -+  /* Default MAILCHECK for interactive shells.  Defer the creation of a
> -+     default MAILPATH until the startup files are read, because MAIL
> -+     names a mail file if MAILPATH is not set, and we should provide a
> -+     default only if neither is set. */
> -+  if (interactive_shell)
> -+    {
> -+      temp_var = set_if_not ("MAILCHECK", posixly_correct ? "600" : "60");
> -+      VSETATTR (temp_var, att_integer);
> -+    }
> -+
> -+  /* Do some things with shell level. */
> -+  initialize_shell_level ();
> -+
> -+  set_ppid ();
> -+
> -+  /* Initialize the `getopts' stuff. */
> -+  temp_var = bind_variable ("OPTIND", "1", 0);
> -+  VSETATTR (temp_var, att_integer);
> -+  getopts_reset (0);
> -+  bind_variable ("OPTERR", "1", 0);
> -+  sh_opterr = 1;
> -+
> -+  if (login_shell == 1 && posixly_correct == 0)
> -+    set_home_var ();
> -+
> -+  /* Get the full pathname to THIS shell, and set the BASH variable
> -+     to it. */
> -+  name = get_bash_name ();
> -+  temp_var = bind_variable ("BASH", name, 0);
> -+  free (name);
> -+
> -+  /* Make the exported environment variable SHELL be the user's login
> -+     shell.  Note that the `tset' command looks at this variable
> -+     to determine what style of commands to output; if it ends in "csh",
> -+     then C-shell commands are output, else Bourne shell commands. */
> -+  set_shell_var ();
> -+
> -+  /* Make a variable called BASH_VERSION which contains the version info. */
> -+  bind_variable ("BASH_VERSION", shell_version_string (), 0);
> -+#if defined (ARRAY_VARS)
> -+  make_vers_array ();
> -+#endif
> -+
> -+  if (command_execution_string)
> -+    bind_variable ("BASH_EXECUTION_STRING", command_execution_string, 0);
> -+
> -+  /* Find out if we're supposed to be in Posix.2 mode via an
> -+     environment variable. */
> -+  temp_var = find_variable ("POSIXLY_CORRECT");
> -+  if (!temp_var)
> -+    temp_var = find_variable ("POSIX_PEDANTIC");
> -+  if (temp_var && imported_p (temp_var))
> -+    sv_strict_posix (temp_var->name);
> -+
> -+#if defined (HISTORY)
> -+  /* Set history variables to defaults, and then do whatever we would
> -+     do if the variable had just been set.  Do this only in the case
> -+     that we are remembering commands on the history list. */
> -+  if (remember_on_history)
> -+    {
> -+      name = bash_tilde_expand (posixly_correct ? "~/.sh_history" : "~/.bash_history", 0);
> -+
> -+      set_if_not ("HISTFILE", name);
> -+      free (name);
> -+    }
> -+#endif /* HISTORY */
> -+
> -+  /* Seed the random number generator. */
> -+  seedrand ();
> -+
> -+  /* Handle some "special" variables that we may have inherited from a
> -+     parent shell. */
> -+  if (interactive_shell)
> -+    {
> -+      temp_var = find_variable ("IGNOREEOF");
> -+      if (!temp_var)
> -+	temp_var = find_variable ("ignoreeof");
> -+      if (temp_var && imported_p (temp_var))
> -+	sv_ignoreeof (temp_var->name);
> -+    }
> -+
> -+#if defined (HISTORY)
> -+  if (interactive_shell && remember_on_history)
> -+    {
> -+      sv_history_control ("HISTCONTROL");
> -+      sv_histignore ("HISTIGNORE");
> -+      sv_histtimefmt ("HISTTIMEFORMAT");
> -+    }
> -+#endif /* HISTORY */
> -+
> -+#if defined (READLINE) && defined (STRICT_POSIX)
> -+  /* POSIXLY_CORRECT will only be 1 here if the shell was compiled
> -+     -DSTRICT_POSIX */
> -+  if (interactive_shell && posixly_correct && no_line_editing == 0)
> -+    rl_prefer_env_winsize = 1;
> -+#endif /* READLINE && STRICT_POSIX */
> -+
> -+     /*
> -+      * 24 October 2001
> -+      *
> -+      * I'm tired of the arguing and bug reports.  Bash now leaves SSH_CLIENT
> -+      * and SSH2_CLIENT alone.  I'm going to rely on the shell_level check in
> -+      * isnetconn() to avoid running the startup files more often than wanted.
> -+      * That will, of course, only work if the user's login shell is bash, so
> -+      * I've made that behavior conditional on SSH_SOURCE_BASHRC being defined
> -+      * in config-top.h.
> -+      */
> -+#if 0
> -+  temp_var = find_variable ("SSH_CLIENT");
> -+  if (temp_var && imported_p (temp_var))
> -+    {
> -+      VUNSETATTR (temp_var, att_exported);
> -+      array_needs_making = 1;
> -+    }
> -+  temp_var = find_variable ("SSH2_CLIENT");
> -+  if (temp_var && imported_p (temp_var))
> -+    {
> -+      VUNSETATTR (temp_var, att_exported);
> -+      array_needs_making = 1;
> -+    }
> -+#endif
> -+
> -+  /* Get the user's real and effective user ids. */
> -+  uidset ();
> -+
> -+  temp_var = find_variable ("BASH_XTRACEFD");
> -+  if (temp_var && imported_p (temp_var))
> -+    sv_xtracefd (temp_var->name);
> -+
> -+  /* Initialize the dynamic variables, and seed their values. */
> -+  initialize_dynamic_variables ();
> -+}
> -+
> -+/* **************************************************************** */
> -+/*								    */
> -+/*	     Setting values for special shell variables		    */
> -+/*								    */
> -+/* **************************************************************** */
> -+
> -+static void
> -+set_machine_vars ()
> -+{
> -+  SHELL_VAR *temp_var;
> -+
> -+  temp_var = set_if_not ("HOSTTYPE", HOSTTYPE);
> -+  temp_var = set_if_not ("OSTYPE", OSTYPE);
> -+  temp_var = set_if_not ("MACHTYPE", MACHTYPE);
> -+
> -+  temp_var = set_if_not ("HOSTNAME", current_host_name);
> -+}
> -+
> -+/* Set $HOME to the information in the password file if we didn't get
> -+   it from the environment. */
> -+
> -+/* This function is not static so the tilde and readline libraries can
> -+   use it. */
> -+char *
> -+sh_get_home_dir ()
> -+{
> -+  if (current_user.home_dir == 0)
> -+    get_current_user_info ();
> -+  return current_user.home_dir;
> -+}
> -+
> -+static void
> -+set_home_var ()
> -+{
> -+  SHELL_VAR *temp_var;
> -+
> -+  temp_var = find_variable ("HOME");
> -+  if (temp_var == 0)
> -+    temp_var = bind_variable ("HOME", sh_get_home_dir (), 0);
> -+#if 0
> -+  VSETATTR (temp_var, att_exported);
> -+#endif
> -+}
> -+
> -+/* Set $SHELL to the user's login shell if it is not already set.  Call
> -+   get_current_user_info if we haven't already fetched the shell. */
> -+static void
> -+set_shell_var ()
> -+{
> -+  SHELL_VAR *temp_var;
> -+
> -+  temp_var = find_variable ("SHELL");
> -+  if (temp_var == 0)
> -+    {
> -+      if (current_user.shell == 0)
> -+	get_current_user_info ();
> -+      temp_var = bind_variable ("SHELL", current_user.shell, 0);
> -+    }
> -+#if 0
> -+  VSETATTR (temp_var, att_exported);
> -+#endif
> -+}
> -+
> -+static char *
> -+get_bash_name ()
> -+{
> -+  char *name;
> -+
> -+  if ((login_shell == 1) && RELPATH(shell_name))
> -+    {
> -+      if (current_user.shell == 0)
> -+	get_current_user_info ();
> -+      name = savestring (current_user.shell);
> -+    }
> -+  else if (ABSPATH(shell_name))
> -+    name = savestring (shell_name);
> -+  else if (shell_name[0] == '.' && shell_name[1] == '/')
> -+    {
> -+      /* Fast path for common case. */
> -+      char *cdir;
> -+      int len;
> -+
> -+      cdir = get_string_value ("PWD");
> -+      if (cdir)
> -+	{
> -+	  len = strlen (cdir);
> -+	  name = (char *)xmalloc (len + strlen (shell_name) + 1);
> -+	  strcpy (name, cdir);
> -+	  strcpy (name + len, shell_name + 1);
> -+	}
> -+      else
> -+	name = savestring (shell_name);
> -+    }
> -+  else
> -+    {
> -+      char *tname;
> -+      int s;
> -+
> -+      tname = find_user_command (shell_name);
> -+
> -+      if (tname == 0)
> -+	{
> -+	  /* Try the current directory.  If there is not an executable
> -+	     there, just punt and use the login shell. */
> -+	  s = file_status (shell_name);
> -+	  if (s & FS_EXECABLE)
> -+	    {
> -+	      tname = make_absolute (shell_name, get_string_value ("PWD"));
> -+	      if (*shell_name == '.')
> -+		{
> -+		  name = sh_canonpath (tname, PATH_CHECKDOTDOT|PATH_CHECKEXISTS);
> -+		  if (name == 0)
> -+		    name = tname;
> -+		  else
> -+		    free (tname);
> -+		}
> -+	     else
> -+		name = tname;
> -+	    }
> -+	  else
> -+	    {
> -+	      if (current_user.shell == 0)
> -+		get_current_user_info ();
> -+	      name = savestring (current_user.shell);
> -+	    }
> -+	}
> -+      else
> -+	{
> -+	  name = full_pathname (tname);
> -+	  free (tname);
> -+	}
> -+    }
> -+
> -+  return (name);
> -+}
> -+
> -+void
> -+adjust_shell_level (change)
> -+     int change;
> -+{
> -+  char new_level[5], *old_SHLVL;
> -+  intmax_t old_level;
> -+  SHELL_VAR *temp_var;
> -+
> -+  old_SHLVL = get_string_value ("SHLVL");
> -+  if (old_SHLVL == 0 || *old_SHLVL == '\0' || legal_number (old_SHLVL, &old_level) == 0)
> -+    old_level = 0;
> -+
> -+  shell_level = old_level + change;
> -+  if (shell_level < 0)
> -+    shell_level = 0;
> -+  else if (shell_level > 1000)
> -+    {
> -+      internal_warning (_("shell level (%d) too high, resetting to 1"), shell_level);
> -+      shell_level = 1;
> -+    }
> -+
> -+  /* We don't need the full generality of itos here. */
> -+  if (shell_level < 10)
> -+    {
> -+      new_level[0] = shell_level + '0';
> -+      new_level[1] = '\0';
> -+    }
> -+  else if (shell_level < 100)
> -+    {
> -+      new_level[0] = (shell_level / 10) + '0';
> -+      new_level[1] = (shell_level % 10) + '0';
> -+      new_level[2] = '\0';
> -+    }
> -+  else if (shell_level < 1000)
> -+    {
> -+      new_level[0] = (shell_level / 100) + '0';
> -+      old_level = shell_level % 100;
> -+      new_level[1] = (old_level / 10) + '0';
> -+      new_level[2] = (old_level % 10) + '0';
> -+      new_level[3] = '\0';
> -+    }
> -+
> -+  temp_var = bind_variable ("SHLVL", new_level, 0);
> -+  set_auto_export (temp_var);
> -+}
> -+
> -+static void
> -+initialize_shell_level ()
> -+{
> -+  adjust_shell_level (1);
> -+}
> -+
> -+/* If we got PWD from the environment, update our idea of the current
> -+   working directory.  In any case, make sure that PWD exists before
> -+   checking it.  It is possible for getcwd () to fail on shell startup,
> -+   and in that case, PWD would be undefined.  If this is an interactive
> -+   login shell, see if $HOME is the current working directory, and if
> -+   that's not the same string as $PWD, set PWD=$HOME. */
> -+
> -+void
> -+set_pwd ()
> -+{
> -+  SHELL_VAR *temp_var, *home_var;
> -+  char *temp_string, *home_string;
> -+
> -+  home_var = find_variable ("HOME");
> -+  home_string = home_var ? value_cell (home_var) : (char *)NULL;
> -+
> -+  temp_var = find_variable ("PWD");
> -+  if (temp_var && imported_p (temp_var) &&
> -+      (temp_string = value_cell (temp_var)) &&
> -+      same_file (temp_string, ".", (struct stat *)NULL, (struct stat *)NULL))
> -+    set_working_directory (temp_string);
> -+  else if (home_string && interactive_shell && login_shell &&
> -+	   same_file (home_string, ".", (struct stat *)NULL, (struct stat *)NULL))
> -+    {
> -+      set_working_directory (home_string);
> -+      temp_var = bind_variable ("PWD", home_string, 0);
> -+      set_auto_export (temp_var);
> -+    }
> -+  else
> -+    {
> -+      temp_string = get_working_directory ("shell-init");
> -+      if (temp_string)
> -+	{
> -+	  temp_var = bind_variable ("PWD", temp_string, 0);
> -+	  set_auto_export (temp_var);
> -+	  free (temp_string);
> -+	}
> -+    }
> -+
> -+  /* According to the Single Unix Specification, v2, $OLDPWD is an
> -+     `environment variable' and therefore should be auto-exported.
> -+     Make a dummy invisible variable for OLDPWD, and mark it as exported. */
> -+  temp_var = bind_variable ("OLDPWD", (char *)NULL, 0);
> -+  VSETATTR (temp_var, (att_exported | att_invisible));
> -+}
> -+
> -+/* Make a variable $PPID, which holds the pid of the shell's parent.  */
> -+void
> -+set_ppid ()
> -+{
> -+  char namebuf[INT_STRLEN_BOUND(pid_t) + 1], *name;
> -+  SHELL_VAR *temp_var;
> -+
> -+  name = inttostr (getppid (), namebuf, sizeof(namebuf));
> -+  temp_var = find_variable ("PPID");
> -+  if (temp_var)
> -+    VUNSETATTR (temp_var, (att_readonly | att_exported));
> -+  temp_var = bind_variable ("PPID", name, 0);
> -+  VSETATTR (temp_var, (att_readonly | att_integer));
> -+}
> -+
> -+static void
> -+uidset ()
> -+{
> -+  char buff[INT_STRLEN_BOUND(uid_t) + 1], *b;
> -+  register SHELL_VAR *v;
> -+
> -+  b = inttostr (current_user.uid, buff, sizeof (buff));
> -+  v = find_variable ("UID");
> -+  if (v == 0)
> -+    {
> -+      v = bind_variable ("UID", b, 0);
> -+      VSETATTR (v, (att_readonly | att_integer));
> -+    }
> -+
> -+  if (current_user.euid != current_user.uid)
> -+    b = inttostr (current_user.euid, buff, sizeof (buff));
> -+
> -+  v = find_variable ("EUID");
> -+  if (v == 0)
> -+    {
> -+      v = bind_variable ("EUID", b, 0);
> -+      VSETATTR (v, (att_readonly | att_integer));
> -+    }
> -+}
> -+
> -+#if defined (ARRAY_VARS)
> -+static void
> -+make_vers_array ()
> -+{
> -+  SHELL_VAR *vv;
> -+  ARRAY *av;
> -+  char *s, d[32], b[INT_STRLEN_BOUND(int) + 1];
> -+
> -+  unbind_variable ("BASH_VERSINFO");
> -+
> -+  vv = make_new_array_variable ("BASH_VERSINFO");
> -+  av = array_cell (vv);
> -+  strcpy (d, dist_version);
> -+  s = strchr (d, '.');
> -+  if (s)
> -+    *s++ = '\0';
> -+  array_insert (av, 0, d);
> -+  array_insert (av, 1, s);
> -+  s = inttostr (patch_level, b, sizeof (b));
> -+  array_insert (av, 2, s);
> -+  s = inttostr (build_version, b, sizeof (b));
> -+  array_insert (av, 3, s);
> -+  array_insert (av, 4, release_status);
> -+  array_insert (av, 5, MACHTYPE);
> -+
> -+  VSETATTR (vv, att_readonly);
> -+}
> -+#endif /* ARRAY_VARS */
> -+
> -+/* Set the environment variables $LINES and $COLUMNS in response to
> -+   a window size change. */
> -+void
> -+sh_set_lines_and_columns (lines, cols)
> -+     int lines, cols;
> -+{
> -+  char val[INT_STRLEN_BOUND(int) + 1], *v;
> -+
> -+#if defined (READLINE)
> -+  /* If we are currently assigning to LINES or COLUMNS, don't do anything. */
> -+  if (winsize_assignment)
> -+    return;
> -+#endif
> -+
> -+  v = inttostr (lines, val, sizeof (val));
> -+  bind_variable ("LINES", v, 0);
> -+
> -+  v = inttostr (cols, val, sizeof (val));
> -+  bind_variable ("COLUMNS", v, 0);
> -+}
> -+
> -+/* **************************************************************** */
> -+/*								    */
> -+/*		   Printing variables and values		    */
> -+/*								    */
> -+/* **************************************************************** */
> -+
> -+/* Print LIST (a list of shell variables) to stdout in such a way that
> -+   they can be read back in. */
> -+void
> -+print_var_list (list)
> -+     register SHELL_VAR **list;
> -+{
> -+  register int i;
> -+  register SHELL_VAR *var;
> -+
> -+  for (i = 0; list && (var = list[i]); i++)
> -+    if (invisible_p (var) == 0)
> -+      print_assignment (var);
> -+}
> -+
> -+/* Print LIST (a list of shell functions) to stdout in such a way that
> -+   they can be read back in. */
> -+void
> -+print_func_list (list)
> -+     register SHELL_VAR **list;
> -+{
> -+  register int i;
> -+  register SHELL_VAR *var;
> -+
> -+  for (i = 0; list && (var = list[i]); i++)
> -+    {
> -+      printf ("%s ", var->name);
> -+      print_var_function (var);
> -+      printf ("\n");
> -+    }
> -+}
> -+      
> -+/* Print the value of a single SHELL_VAR.  No newline is
> -+   output, but the variable is printed in such a way that
> -+   it can be read back in. */
> -+void
> -+print_assignment (var)
> -+     SHELL_VAR *var;
> -+{
> -+  if (var_isset (var) == 0)
> -+    return;
> -+
> -+  if (function_p (var))
> -+    {
> -+      printf ("%s", var->name);
> -+      print_var_function (var);
> -+      printf ("\n");
> -+    }
> -+#if defined (ARRAY_VARS)
> -+  else if (array_p (var))
> -+    print_array_assignment (var, 0);
> -+  else if (assoc_p (var))
> -+    print_assoc_assignment (var, 0);
> -+#endif /* ARRAY_VARS */
> -+  else
> -+    {
> -+      printf ("%s=", var->name);
> -+      print_var_value (var, 1);
> -+      printf ("\n");
> -+    }
> -+}
> -+
> -+/* Print the value cell of VAR, a shell variable.  Do not print
> -+   the name, nor leading/trailing newline.  If QUOTE is non-zero,
> -+   and the value contains shell metacharacters, quote the value
> -+   in such a way that it can be read back in. */
> -+void
> -+print_var_value (var, quote)
> -+     SHELL_VAR *var;
> -+     int quote;
> -+{
> -+  char *t;
> -+
> -+  if (var_isset (var) == 0)
> -+    return;
> -+
> -+  if (quote && posixly_correct == 0 && ansic_shouldquote (value_cell (var)))
> -+    {
> -+      t = ansic_quote (value_cell (var), 0, (int *)0);
> -+      printf ("%s", t);
> -+      free (t);
> -+    }
> -+  else if (quote && sh_contains_shell_metas (value_cell (var)))
> -+    {
> -+      t = sh_single_quote (value_cell (var));
> -+      printf ("%s", t);
> -+      free (t);
> -+    }
> -+  else
> -+    printf ("%s", value_cell (var));
> -+}
> -+
> -+/* Print the function cell of VAR, a shell variable.  Do not
> -+   print the name, nor leading/trailing newline. */
> -+void
> -+print_var_function (var)
> -+     SHELL_VAR *var;
> -+{
> -+  char *x;
> -+
> -+  if (function_p (var) && var_isset (var))
> -+    {
> -+      x = named_function_string ((char *)NULL, function_cell(var), FUNC_MULTILINE|FUNC_EXTERNAL);
> -+      printf ("%s", x);
> -+    }
> -+}
> -+
> -+/* **************************************************************** */
> -+/*								    */
> -+/*		 	Dynamic Variables			    */
> -+/*								    */
> -+/* **************************************************************** */
> -+
> -+/* DYNAMIC VARIABLES
> -+
> -+   These are variables whose values are generated anew each time they are
> -+   referenced.  These are implemented using a pair of function pointers
> -+   in the struct variable: assign_func, which is called from bind_variable
> -+   and, if arrays are compiled into the shell, some of the functions in
> -+   arrayfunc.c, and dynamic_value, which is called from find_variable.
> -+
> -+   assign_func is called from bind_variable_internal, if
> -+   bind_variable_internal discovers that the variable being assigned to
> -+   has such a function.  The function is called as
> -+	SHELL_VAR *temp = (*(entry->assign_func)) (entry, value, ind)
> -+   and the (SHELL_VAR *)temp is returned as the value of bind_variable.  It
> -+   is usually ENTRY (self).  IND is an index for an array variable, and
> -+   unused otherwise.
> -+
> -+   dynamic_value is called from find_variable_internal to return a `new'
> -+   value for the specified dynamic varible.  If this function is NULL,
> -+   the variable is treated as a `normal' shell variable.  If it is not,
> -+   however, then this function is called like this:
> -+	tempvar = (*(var->dynamic_value)) (var);
> -+
> -+   Sometimes `tempvar' will replace the value of `var'.  Other times, the
> -+   shell will simply use the string value.  Pretty object-oriented, huh?
> -+
> -+   Be warned, though: if you `unset' a special variable, it loses its
> -+   special meaning, even if you subsequently set it.
> -+
> -+   The special assignment code would probably have been better put in
> -+   subst.c: do_assignment_internal, in the same style as
> -+   stupidly_hack_special_variables, but I wanted the changes as
> -+   localized as possible.  */
> -+
> -+#define INIT_DYNAMIC_VAR(var, val, gfunc, afunc) \
> -+  do \
> -+    { \
> -+      v = bind_variable (var, (val), 0); \
> -+      v->dynamic_value = gfunc; \
> -+      v->assign_func = afunc; \
> -+    } \
> -+  while (0)
> -+
> -+#define INIT_DYNAMIC_ARRAY_VAR(var, gfunc, afunc) \
> -+  do \
> -+    { \
> -+      v = make_new_array_variable (var); \
> -+      v->dynamic_value = gfunc; \
> -+      v->assign_func = afunc; \
> -+    } \
> -+  while (0)
> -+
> -+#define INIT_DYNAMIC_ASSOC_VAR(var, gfunc, afunc) \
> -+  do \
> -+    { \
> -+      v = make_new_assoc_variable (var); \
> -+      v->dynamic_value = gfunc; \
> -+      v->assign_func = afunc; \
> -+    } \
> -+  while (0)
> -+
> -+static SHELL_VAR *
> -+null_assign (self, value, unused, key)
> -+     SHELL_VAR *self;
> -+     char *value;
> -+     arrayind_t unused;
> -+     char *key;
> -+{
> -+  return (self);
> -+}
> -+
> -+#if defined (ARRAY_VARS)
> -+static SHELL_VAR *
> -+null_array_assign (self, value, ind, key)
> -+     SHELL_VAR *self;
> -+     char *value;
> -+     arrayind_t ind;
> -+     char *key;
> -+{
> -+  return (self);
> -+}
> -+#endif
> -+
> -+/* Degenerate `dynamic_value' function; just returns what's passed without
> -+   manipulation. */
> -+static SHELL_VAR *
> -+get_self (self)
> -+     SHELL_VAR *self;
> -+{
> -+  return (self);
> -+}
> -+
> -+#if defined (ARRAY_VARS)
> -+/* A generic dynamic array variable initializer.  Initialize array variable
> -+   NAME with dynamic value function GETFUNC and assignment function SETFUNC. */
> -+static SHELL_VAR *
> -+init_dynamic_array_var (name, getfunc, setfunc, attrs)
> -+     char *name;
> -+     sh_var_value_func_t *getfunc;
> -+     sh_var_assign_func_t *setfunc;
> -+     int attrs;
> -+{
> -+  SHELL_VAR *v;
> -+
> -+  v = find_variable (name);
> -+  if (v)
> -+    return (v);
> -+  INIT_DYNAMIC_ARRAY_VAR (name, getfunc, setfunc);
> -+  if (attrs)
> -+    VSETATTR (v, attrs);
> -+  return v;
> -+}
> -+
> -+static SHELL_VAR *
> -+init_dynamic_assoc_var (name, getfunc, setfunc, attrs)
> -+     char *name;
> -+     sh_var_value_func_t *getfunc;
> -+     sh_var_assign_func_t *setfunc;
> -+     int attrs;
> -+{
> -+  SHELL_VAR *v;
> -+
> -+  v = find_variable (name);
> -+  if (v)
> -+    return (v);
> -+  INIT_DYNAMIC_ASSOC_VAR (name, getfunc, setfunc);
> -+  if (attrs)
> -+    VSETATTR (v, attrs);
> -+  return v;
> -+}
> -+#endif
> -+
> -+/* The value of $SECONDS.  This is the number of seconds since shell
> -+   invocation, or, the number of seconds since the last assignment + the
> -+   value of the last assignment. */
> -+static intmax_t seconds_value_assigned;
> -+
> -+static SHELL_VAR *
> -+assign_seconds (self, value, unused, key)
> -+     SHELL_VAR *self;
> -+     char *value;
> -+     arrayind_t unused;
> -+     char *key;
> -+{
> -+  if (legal_number (value, &seconds_value_assigned) == 0)
> -+    seconds_value_assigned = 0;
> -+  shell_start_time = NOW;
> -+  return (self);
> -+}
> -+
> -+static SHELL_VAR *
> -+get_seconds (var)
> -+     SHELL_VAR *var;
> -+{
> -+  time_t time_since_start;
> -+  char *p;
> -+
> -+  time_since_start = NOW - shell_start_time;
> -+  p = itos(seconds_value_assigned + time_since_start);
> -+
> -+  FREE (value_cell (var));
> -+
> -+  VSETATTR (var, att_integer);
> -+  var_setvalue (var, p);
> -+  return (var);
> -+}
> -+
> -+static SHELL_VAR *
> -+init_seconds_var ()
> -+{
> -+  SHELL_VAR *v;
> -+
> -+  v = find_variable ("SECONDS");
> -+  if (v)
> -+    {
> -+      if (legal_number (value_cell(v), &seconds_value_assigned) == 0)
> -+	seconds_value_assigned = 0;
> -+    }
> -+  INIT_DYNAMIC_VAR ("SECONDS", (v ? value_cell (v) : (char *)NULL), get_seconds, assign_seconds);
> -+  return v;      
> -+}
> -+     
> -+/* The random number seed.  You can change this by setting RANDOM. */
> -+static unsigned long rseed = 1;
> -+static int last_random_value;
> -+static int seeded_subshell = 0;
> -+
> -+/* A linear congruential random number generator based on the example
> -+   one in the ANSI C standard.  This one isn't very good, but a more
> -+   complicated one is overkill. */
> -+
> -+/* Returns a pseudo-random number between 0 and 32767. */
> -+static int
> -+brand ()
> -+{
> -+  /* From "Random number generators: good ones are hard to find",
> -+     Park and Miller, Communications of the ACM, vol. 31, no. 10,
> -+     October 1988, p. 1195. filtered through FreeBSD */
> -+  long h, l;
> -+
> -+  /* Can't seed with 0. */
> -+  if (rseed == 0)
> -+    rseed = 123459876;
> -+  h = rseed / 127773;
> -+  l = rseed % 127773;
> -+  rseed = 16807 * l - 2836 * h;
> -+#if 0
> -+  if (rseed < 0)
> -+    rseed += 0x7fffffff;
> -+#endif
> -+  return ((unsigned int)(rseed & 32767));	/* was % 32768 */
> -+}
> -+
> -+/* Set the random number generator seed to SEED. */
> -+static void
> -+sbrand (seed)
> -+     unsigned long seed;
> -+{
> -+  rseed = seed;
> -+  last_random_value = 0;
> -+}
> -+
> -+static void
> -+seedrand ()
> -+{
> -+  struct timeval tv;
> -+
> -+  gettimeofday (&tv, NULL);
> -+  sbrand (tv.tv_sec ^ tv.tv_usec ^ getpid ());
> -+}
> -+
> -+static SHELL_VAR *
> -+assign_random (self, value, unused, key)
> -+     SHELL_VAR *self;
> -+     char *value;
> -+     arrayind_t unused;
> -+     char *key;
> -+{
> -+  sbrand (strtoul (value, (char **)NULL, 10));
> -+  if (subshell_environment)
> -+    seeded_subshell = getpid ();
> -+  return (self);
> -+}
> -+
> -+int
> -+get_random_number ()
> -+{
> -+  int rv, pid;
> -+
> -+  /* Reset for command and process substitution. */
> -+  pid = getpid ();
> -+  if (subshell_environment && seeded_subshell != pid)
> -+    {
> -+      seedrand ();
> -+      seeded_subshell = pid;
> -+    }
> -+
> -+  do
> -+    rv = brand ();
> -+  while (rv == last_random_value);
> -+  return rv;
> -+}
> -+
> -+static SHELL_VAR *
> -+get_random (var)
> -+     SHELL_VAR *var;
> -+{
> -+  int rv;
> -+  char *p;
> -+
> -+  rv = get_random_number ();
> -+  last_random_value = rv;
> -+  p = itos (rv);
> -+
> -+  FREE (value_cell (var));
> -+
> -+  VSETATTR (var, att_integer);
> -+  var_setvalue (var, p);
> -+  return (var);
> -+}
> -+
> -+static SHELL_VAR *
> -+assign_lineno (var, value, unused, key)
> -+     SHELL_VAR *var;
> -+     char *value;
> -+     arrayind_t unused;
> -+     char *key;
> -+{
> -+  intmax_t new_value;
> -+
> -+  if (value == 0 || *value == '\0' || legal_number (value, &new_value) == 0)
> -+    new_value = 0;
> -+  line_number = line_number_base = new_value;
> -+  return var;
> -+}
> -+
> -+/* Function which returns the current line number. */
> -+static SHELL_VAR *
> -+get_lineno (var)
> -+     SHELL_VAR *var;
> -+{
> -+  char *p;
> -+  int ln;
> -+
> -+  ln = executing_line_number ();
> -+  p = itos (ln);
> -+  FREE (value_cell (var));
> -+  var_setvalue (var, p);
> -+  return (var);
> -+}
> -+
> -+static SHELL_VAR *
> -+assign_subshell (var, value, unused, key)
> -+     SHELL_VAR *var;
> -+     char *value;
> -+     arrayind_t unused;
> -+     char *key;
> -+{
> -+  intmax_t new_value;
> -+
> -+  if (value == 0 || *value == '\0' || legal_number (value, &new_value) == 0)
> -+    new_value = 0;
> -+  subshell_level = new_value;
> -+  return var;
> -+}
> -+
> -+static SHELL_VAR *
> -+get_subshell (var)
> -+     SHELL_VAR *var;
> -+{
> -+  char *p;
> -+
> -+  p = itos (subshell_level);
> -+  FREE (value_cell (var));
> -+  var_setvalue (var, p);
> -+  return (var);
> -+}
> -+
> -+static SHELL_VAR *
> -+get_bashpid (var)
> -+     SHELL_VAR *var;
> -+{
> -+  int pid;
> -+  char *p;
> -+
> -+  pid = getpid ();
> -+  p = itos (pid);
> -+
> -+  FREE (value_cell (var));
> -+  VSETATTR (var, att_integer|att_readonly);
> -+  var_setvalue (var, p);
> -+  return (var);
> -+}
> -+
> -+static SHELL_VAR *
> -+get_bash_command (var)
> -+     SHELL_VAR *var;
> -+{
> -+  char *p;
> -+
> -+  if (the_printed_command_except_trap)
> -+    p = savestring (the_printed_command_except_trap);
> -+  else
> -+    {
> -+      p = (char *)xmalloc (1);
> -+      p[0] = '\0';
> -+    }
> -+  FREE (value_cell (var));
> -+  var_setvalue (var, p);
> -+  return (var);
> -+}
> -+
> -+#if defined (HISTORY)
> -+static SHELL_VAR *
> -+get_histcmd (var)
> -+     SHELL_VAR *var;
> -+{
> -+  char *p;
> -+
> -+  p = itos (history_number ());
> -+  FREE (value_cell (var));
> -+  var_setvalue (var, p);
> -+  return (var);
> -+}
> -+#endif
> -+
> -+#if defined (READLINE)
> -+/* When this function returns, VAR->value points to malloced memory. */
> -+static SHELL_VAR *
> -+get_comp_wordbreaks (var)
> -+     SHELL_VAR *var;
> -+{
> -+  /* If we don't have anything yet, assign a default value. */
> -+  if (rl_completer_word_break_characters == 0 && bash_readline_initialized == 0)
> -+    enable_hostname_completion (perform_hostname_completion);
> -+
> -+  FREE (value_cell (var));
> -+  var_setvalue (var, savestring (rl_completer_word_break_characters));
> -+
> -+  return (var);
> -+}
> -+
> -+/* When this function returns, rl_completer_word_break_characters points to
> -+   malloced memory. */
> -+static SHELL_VAR *
> -+assign_comp_wordbreaks (self, value, unused, key)
> -+     SHELL_VAR *self;
> -+     char *value;
> -+     arrayind_t unused;
> -+     char *key;
> -+{
> -+  if (rl_completer_word_break_characters &&
> -+      rl_completer_word_break_characters != rl_basic_word_break_characters)
> -+    free (rl_completer_word_break_characters);
> -+
> -+  rl_completer_word_break_characters = savestring (value);
> -+  return self;
> -+}
> -+#endif /* READLINE */
> -+
> -+#if defined (PUSHD_AND_POPD) && defined (ARRAY_VARS)
> -+static SHELL_VAR *
> -+assign_dirstack (self, value, ind, key)
> -+     SHELL_VAR *self;
> -+     char *value;
> -+     arrayind_t ind;
> -+     char *key;
> -+{
> -+  set_dirstack_element (ind, 1, value);
> -+  return self;
> -+}
> -+
> -+static SHELL_VAR *
> -+get_dirstack (self)
> -+     SHELL_VAR *self;
> -+{
> -+  ARRAY *a;
> -+  WORD_LIST *l;
> -+
> -+  l = get_directory_stack (0);
> -+  a = array_from_word_list (l);
> -+  array_dispose (array_cell (self));
> -+  dispose_words (l);
> -+  var_setarray (self, a);
> -+  return self;
> -+}
> -+#endif /* PUSHD AND POPD && ARRAY_VARS */
> -+
> -+#if defined (ARRAY_VARS)
> -+/* We don't want to initialize the group set with a call to getgroups()
> -+   unless we're asked to, but we only want to do it once. */
> -+static SHELL_VAR *
> -+get_groupset (self)
> -+     SHELL_VAR *self;
> -+{
> -+  register int i;
> -+  int ng;
> -+  ARRAY *a;
> -+  static char **group_set = (char **)NULL;
> -+
> -+  if (group_set == 0)
> -+    {
> -+      group_set = get_group_list (&ng);
> -+      a = array_cell (self);
> -+      for (i = 0; i < ng; i++)
> -+	array_insert (a, i, group_set[i]);
> -+    }
> -+  return (self);
> -+}
> -+
> -+static SHELL_VAR *
> -+build_hashcmd (self)
> -+     SHELL_VAR *self;
> -+{
> -+  HASH_TABLE *h;
> -+  int i;
> -+  char *k, *v;
> -+  BUCKET_CONTENTS *item;
> -+
> -+  h = assoc_cell (self);
> -+  if (h)
> -+    assoc_dispose (h);
> -+
> -+  if (hashed_filenames == 0 || HASH_ENTRIES (hashed_filenames) == 0)
> -+    {
> -+      var_setvalue (self, (char *)NULL);
> -+      return self;
> -+    }
> -+
> -+  h = assoc_create (hashed_filenames->nbuckets);
> -+  for (i = 0; i < hashed_filenames->nbuckets; i++)
> -+    {
> -+      for (item = hash_items (i, hashed_filenames); item; item = item->next)
> -+	{
> -+	  k = savestring (item->key);
> -+	  v = pathdata(item)->path;
> -+	  assoc_insert (h, k, v);
> -+	}
> -+    }
> -+
> -+  var_setvalue (self, (char *)h);
> -+  return self;
> -+}
> -+
> -+static SHELL_VAR *
> -+get_hashcmd (self)
> -+     SHELL_VAR *self;
> -+{
> -+  build_hashcmd (self);
> -+  return (self);
> -+}
> -+
> -+static SHELL_VAR *
> -+assign_hashcmd (self, value, ind, key)
> -+     SHELL_VAR *self;
> -+     char *value;
> -+     arrayind_t ind;
> -+     char *key;
> -+{
> -+  phash_insert (key, value, 0, 0);
> -+  return (build_hashcmd (self));
> -+}
> -+
> -+#if defined (ALIAS)
> -+static SHELL_VAR *
> -+build_aliasvar (self)
> -+     SHELL_VAR *self;
> -+{
> -+  HASH_TABLE *h;
> -+  int i;
> -+  char *k, *v;
> -+  BUCKET_CONTENTS *item;
> -+
> -+  h = assoc_cell (self);
> -+  if (h)
> -+    assoc_dispose (h);
> -+
> -+  if (aliases == 0 || HASH_ENTRIES (aliases) == 0)
> -+    {
> -+      var_setvalue (self, (char *)NULL);
> -+      return self;
> -+    }
> -+
> -+  h = assoc_create (aliases->nbuckets);
> -+  for (i = 0; i < aliases->nbuckets; i++)
> -+    {
> -+      for (item = hash_items (i, aliases); item; item = item->next)
> -+	{
> -+	  k = savestring (item->key);
> -+	  v = ((alias_t *)(item->data))->value;
> -+	  assoc_insert (h, k, v);
> -+	}
> -+    }
> -+
> -+  var_setvalue (self, (char *)h);
> -+  return self;
> -+}
> -+
> -+static SHELL_VAR *
> -+get_aliasvar (self)
> -+     SHELL_VAR *self;
> -+{
> -+  build_aliasvar (self);
> -+  return (self);
> -+}
> -+
> -+static SHELL_VAR *
> -+assign_aliasvar (self, value, ind, key)
> -+     SHELL_VAR *self;
> -+     char *value;
> -+     arrayind_t ind;
> -+     char *key;
> -+{
> -+  add_alias (key, value);
> -+  return (build_aliasvar (self));
> -+}
> -+#endif /* ALIAS */
> -+
> -+#endif /* ARRAY_VARS */
> -+
> -+/* If ARRAY_VARS is not defined, this just returns the name of any
> -+   currently-executing function.  If we have arrays, it's a call stack. */
> -+static SHELL_VAR *
> -+get_funcname (self)
> -+     SHELL_VAR *self;
> -+{
> -+#if ! defined (ARRAY_VARS)
> -+  char *t;
> -+  if (variable_context && this_shell_function)
> -+    {
> -+      FREE (value_cell (self));
> -+      t = savestring (this_shell_function->name);
> -+      var_setvalue (self, t);
> -+    }
> -+#endif
> -+  return (self);
> -+}
> -+
> -+void
> -+make_funcname_visible (on_or_off)
> -+     int on_or_off;
> -+{
> -+  SHELL_VAR *v;
> -+
> -+  v = find_variable ("FUNCNAME");
> -+  if (v == 0 || v->dynamic_value == 0)
> -+    return;
> -+
> -+  if (on_or_off)
> -+    VUNSETATTR (v, att_invisible);
> -+  else
> -+    VSETATTR (v, att_invisible);
> -+}
> -+
> -+static SHELL_VAR *
> -+init_funcname_var ()
> -+{
> -+  SHELL_VAR *v;
> -+
> -+  v = find_variable ("FUNCNAME");
> -+  if (v)
> -+    return v;
> -+#if defined (ARRAY_VARS)
> -+  INIT_DYNAMIC_ARRAY_VAR ("FUNCNAME", get_funcname, null_array_assign);
> -+#else
> -+  INIT_DYNAMIC_VAR ("FUNCNAME", (char *)NULL, get_funcname, null_assign);
> -+#endif
> -+  VSETATTR (v, att_invisible|att_noassign);
> -+  return v;
> -+}
> -+
> -+static void
> -+initialize_dynamic_variables ()
> -+{
> -+  SHELL_VAR *v;
> -+
> -+  v = init_seconds_var ();
> -+
> -+  INIT_DYNAMIC_VAR ("BASH_COMMAND", (char *)NULL, get_bash_command, (sh_var_assign_func_t *)NULL);
> -+  INIT_DYNAMIC_VAR ("BASH_SUBSHELL", (char *)NULL, get_subshell, assign_subshell);
> -+
> -+  INIT_DYNAMIC_VAR ("RANDOM", (char *)NULL, get_random, assign_random);
> -+  VSETATTR (v, att_integer);
> -+  INIT_DYNAMIC_VAR ("LINENO", (char *)NULL, get_lineno, assign_lineno);
> -+  VSETATTR (v, att_integer);
> -+
> -+  INIT_DYNAMIC_VAR ("BASHPID", (char *)NULL, get_bashpid, null_assign);
> -+  VSETATTR (v, att_integer|att_readonly);
> -+
> -+#if defined (HISTORY)
> -+  INIT_DYNAMIC_VAR ("HISTCMD", (char *)NULL, get_histcmd, (sh_var_assign_func_t *)NULL);
> -+  VSETATTR (v, att_integer);
> -+#endif
> -+
> -+#if defined (READLINE)
> -+  INIT_DYNAMIC_VAR ("COMP_WORDBREAKS", (char *)NULL, get_comp_wordbreaks, assign_comp_wordbreaks);
> -+#endif
> -+
> -+#if defined (PUSHD_AND_POPD) && defined (ARRAY_VARS)
> -+  v = init_dynamic_array_var ("DIRSTACK", get_dirstack, assign_dirstack, 0);
> -+#endif /* PUSHD_AND_POPD && ARRAY_VARS */
> -+
> -+#if defined (ARRAY_VARS)
> -+  v = init_dynamic_array_var ("GROUPS", get_groupset, null_array_assign, att_noassign);
> -+
> -+#  if defined (DEBUGGER)
> -+  v = init_dynamic_array_var ("BASH_ARGC", get_self, null_array_assign, att_noassign|att_nounset);
> -+  v = init_dynamic_array_var ("BASH_ARGV", get_self, null_array_assign, att_noassign|att_nounset);
> -+#  endif /* DEBUGGER */
> -+  v = init_dynamic_array_var ("BASH_SOURCE", get_self, null_array_assign, att_noassign|att_nounset);
> -+  v = init_dynamic_array_var ("BASH_LINENO", get_self, null_array_assign, att_noassign|att_nounset);
> -+
> -+  v = init_dynamic_assoc_var ("BASH_CMDS", get_hashcmd, assign_hashcmd, att_nofree);
> -+#  if defined (ALIAS)
> -+  v = init_dynamic_assoc_var ("BASH_ALIASES", get_aliasvar, assign_aliasvar, att_nofree);
> -+#  endif
> -+#endif
> -+
> -+  v = init_funcname_var ();
> -+}
> -+
> -+/* **************************************************************** */
> -+/*								    */
> -+/*		Retrieving variables and values			    */
> -+/*								    */
> -+/* **************************************************************** */
> -+
> -+/* How to get a pointer to the shell variable or function named NAME.
> -+   HASHED_VARS is a pointer to the hash table containing the list
> -+   of interest (either variables or functions). */
> -+
> -+static SHELL_VAR *
> -+hash_lookup (name, hashed_vars)
> -+     const char *name;
> -+     HASH_TABLE *hashed_vars;
> -+{
> -+  BUCKET_CONTENTS *bucket;
> -+
> -+  bucket = hash_search (name, hashed_vars, 0);
> -+  /* If we find the name in HASHED_VARS, set LAST_TABLE_SEARCHED to that
> -+     table. */
> -+  if (bucket)
> -+    last_table_searched = hashed_vars;
> -+  return (bucket ? (SHELL_VAR *)bucket->data : (SHELL_VAR *)NULL);
> -+}
> -+
> -+SHELL_VAR *
> -+var_lookup (name, vcontext)
> -+     const char *name;
> -+     VAR_CONTEXT *vcontext;
> -+{
> -+  VAR_CONTEXT *vc;
> -+  SHELL_VAR *v;
> -+
> -+  v = (SHELL_VAR *)NULL;
> -+  for (vc = vcontext; vc; vc = vc->down)
> -+    if (v = hash_lookup (name, vc->table))
> -+      break;
> -+
> -+  return v;
> -+}
> -+
> -+/* Look up the variable entry named NAME.  If SEARCH_TEMPENV is non-zero,
> -+   then also search the temporarily built list of exported variables.
> -+   The lookup order is:
> -+	temporary_env
> -+	shell_variables list
> -+*/
> -+
> -+SHELL_VAR *
> -+find_variable_internal (name, force_tempenv)
> -+     const char *name;
> -+     int force_tempenv;
> -+{
> -+  SHELL_VAR *var;
> -+  int search_tempenv;
> -+  VAR_CONTEXT *vc;
> -+
> -+  var = (SHELL_VAR *)NULL;
> -+
> -+  /* If explicitly requested, first look in the temporary environment for
> -+     the variable.  This allows constructs such as "foo=x eval 'echo $foo'"
> -+     to get the `exported' value of $foo.  This happens if we are executing
> -+     a function or builtin, or if we are looking up a variable in a
> -+     "subshell environment". */
> -+  search_tempenv = force_tempenv || (expanding_redir == 0 && subshell_environment);
> -+
> -+  if (search_tempenv && temporary_env)		
> -+    var = hash_lookup (name, temporary_env);
> -+
> -+  vc = shell_variables;
> -+#if 0
> -+if (search_tempenv == 0 && /* (subshell_environment & SUBSHELL_COMSUB) && */
> -+    expanding_redir &&
> -+    (this_shell_builtin == eval_builtin || this_shell_builtin == command_builtin))
> -+  {
> -+  itrace("find_variable_internal: search_tempenv == 0: skipping VC_BLTNENV");
> -+  while (vc && (vc->flags & VC_BLTNENV))
> -+    vc = vc->down;
> -+  if (vc == 0)
> -+    vc = shell_variables;
> -+  }
> -+#endif
> -+
> -+  if (var == 0)
> -+    var = var_lookup (name, vc);
> -+
> -+  if (var == 0)
> -+    return ((SHELL_VAR *)NULL);
> -+
> -+  return (var->dynamic_value ? (*(var->dynamic_value)) (var) : var);
> -+}
> -+
> -+/* Look up and resolve the chain of nameref variables starting at V all the
> -+   way to NULL or non-nameref. */
> -+SHELL_VAR *
> -+find_variable_nameref (v)
> -+     SHELL_VAR *v;
> -+{
> -+  int level;
> -+  char *newname;
> -+  SHELL_VAR *orig, *oldv;
> -+
> -+  level = 0;
> -+  orig = v;
> -+  while (v && nameref_p (v))
> -+    {
> -+      level++;
> -+      if (level > NAMEREF_MAX)
> -+	return ((SHELL_VAR *)0);	/* error message here? */
> -+      newname = nameref_cell (v);
> -+      if (newname == 0 || *newname == '\0')
> -+	return ((SHELL_VAR *)0);
> -+      oldv = v;
> -+      v = find_variable_internal (newname, (expanding_redir == 0 && (assigning_in_environment || executing_builtin)));
> -+      if (v == orig || v == oldv)
> -+	{
> -+	  internal_warning (_("%s: circular name reference"), orig->name);
> -+	  return ((SHELL_VAR *)0);
> -+	}
> -+    }
> -+  return v;
> -+}
> -+
> -+/* Resolve the chain of nameref variables for NAME.  XXX - could change later */
> -+SHELL_VAR *
> -+find_variable_last_nameref (name)
> -+     const char *name;
> -+{
> -+  SHELL_VAR *v, *nv;
> -+  char *newname;
> -+  int level;
> -+
> -+  nv = v = find_variable_noref (name);
> -+  level = 0;
> -+  while (v && nameref_p (v))
> -+    {
> -+      level++;
> -+      if (level > NAMEREF_MAX)
> -+        return ((SHELL_VAR *)0);	/* error message here? */
> -+      newname = nameref_cell (v);
> -+      if (newname == 0 || *newname == '\0')
> -+	return ((SHELL_VAR *)0);
> -+      nv = v;
> -+      v = find_variable_internal (newname, (expanding_redir == 0 && (assigning_in_environment || executing_builtin)));
> -+    }
> -+  return nv;
> -+}
> -+
> -+/* Resolve the chain of nameref variables for NAME.  XXX - could change later */
> -+SHELL_VAR *
> -+find_global_variable_last_nameref (name)
> -+     const char *name;
> -+{
> -+  SHELL_VAR *v, *nv;
> -+  char *newname;
> -+  int level;
> -+
> -+  nv = v = find_global_variable_noref (name);
> -+  level = 0;
> -+  while (v && nameref_p (v))
> -+    {
> -+      level++;
> -+      if (level > NAMEREF_MAX)
> -+        return ((SHELL_VAR *)0);	/* error message here? */
> -+      newname = nameref_cell (v);
> -+      if (newname == 0 || *newname == '\0')
> -+	return ((SHELL_VAR *)0);
> -+      nv = v;
> -+      v = find_global_variable_noref (newname);
> -+    }
> -+  return nv;
> -+}
> -+
> -+static SHELL_VAR *
> -+find_nameref_at_context (v, vc)
> -+     SHELL_VAR *v;
> -+     VAR_CONTEXT *vc;
> -+{
> -+  SHELL_VAR *nv, *nv2;
> -+  VAR_CONTEXT *nvc;
> -+  char *newname;
> -+  int level;
> -+
> -+  nv = v;
> -+  level = 1;
> -+  while (nv && nameref_p (nv))
> -+    {
> -+      level++;
> -+      if (level > NAMEREF_MAX)
> -+        return ((SHELL_VAR *)NULL);
> -+      newname = nameref_cell (nv);
> -+      if (newname == 0 || *newname == '\0')
> -+        return ((SHELL_VAR *)NULL);      
> -+      nv2 = hash_lookup (newname, vc->table);
> -+      if (nv2 == 0)
> -+        break;
> -+      nv = nv2;
> -+    }
> -+  return nv;
> -+}
> -+
> -+/* Do nameref resolution from the VC, which is the local context for some
> -+   function or builtin, `up' the chain to the global variables context.  If
> -+   NVCP is not NULL, return the variable context where we finally ended the
> -+   nameref resolution (so the bind_variable_internal can use the correct
> -+   variable context and hash table). */
> -+static SHELL_VAR *
> -+find_variable_nameref_context (v, vc, nvcp)
> -+     SHELL_VAR *v;
> -+     VAR_CONTEXT *vc;
> -+     VAR_CONTEXT **nvcp;
> -+{
> -+  SHELL_VAR *nv, *nv2;
> -+  VAR_CONTEXT *nvc;
> -+
> -+  /* Look starting at the current context all the way `up' */
> -+  for (nv = v, nvc = vc; nvc; nvc = nvc->down)
> -+    {
> -+      nv2 = find_nameref_at_context (nv, nvc);
> -+      if (nv2 == 0)
> -+        continue;
> -+      nv = nv2;
> -+      if (*nvcp)
> -+        *nvcp = nvc;
> -+      if (nameref_p (nv) == 0)
> -+        break;
> -+    }
> -+  return (nameref_p (nv) ? (SHELL_VAR *)NULL : nv);
> -+}
> -+
> -+/* Do nameref resolution from the VC, which is the local context for some
> -+   function or builtin, `up' the chain to the global variables context.  If
> -+   NVCP is not NULL, return the variable context where we finally ended the
> -+   nameref resolution (so the bind_variable_internal can use the correct
> -+   variable context and hash table). */
> -+static SHELL_VAR *
> -+find_variable_last_nameref_context (v, vc, nvcp)
> -+     SHELL_VAR *v;
> -+     VAR_CONTEXT *vc;
> -+     VAR_CONTEXT **nvcp;
> -+{
> -+  SHELL_VAR *nv, *nv2;
> -+  VAR_CONTEXT *nvc;
> -+
> -+  /* Look starting at the current context all the way `up' */
> -+  for (nv = v, nvc = vc; nvc; nvc = nvc->down)
> -+    {
> -+      nv2 = find_nameref_at_context (nv, nvc);
> -+      if (nv2 == 0)
> -+	continue;
> -+      nv = nv2;
> -+      if (*nvcp)
> -+        *nvcp = nvc;
> -+    }
> -+  return (nameref_p (nv) ? nv : (SHELL_VAR *)NULL);
> -+}
> -+
> -+/* Find a variable, forcing a search of the temporary environment first */
> -+SHELL_VAR *
> -+find_variable_tempenv (name)
> -+     const char *name;
> -+{
> -+  SHELL_VAR *var;
> -+
> -+  var = find_variable_internal (name, 1);
> -+  if (var && nameref_p (var))
> -+    var = find_variable_nameref (var);
> -+  return (var);
> -+}
> -+
> -+/* Find a variable, not forcing a search of the temporary environment first */
> -+SHELL_VAR *
> -+find_variable_notempenv (name)
> -+     const char *name;
> -+{
> -+  SHELL_VAR *var;
> -+
> -+  var = find_variable_internal (name, 0);
> -+  if (var && nameref_p (var))
> -+    var = find_variable_nameref (var);
> -+  return (var);
> -+}
> -+
> -+SHELL_VAR *
> -+find_global_variable (name)
> -+     const char *name;
> -+{
> -+  SHELL_VAR *var;
> -+
> -+  var = var_lookup (name, global_variables);
> -+  if (var && nameref_p (var))
> -+    var = find_variable_nameref (var);
> -+
> -+  if (var == 0)
> -+    return ((SHELL_VAR *)NULL);
> -+
> -+  return (var->dynamic_value ? (*(var->dynamic_value)) (var) : var);
> -+}
> -+
> -+SHELL_VAR *
> -+find_global_variable_noref (name)
> -+     const char *name;
> -+{
> -+  SHELL_VAR *var;
> -+
> -+  var = var_lookup (name, global_variables);
> -+
> -+  if (var == 0)
> -+    return ((SHELL_VAR *)NULL);
> -+
> -+  return (var->dynamic_value ? (*(var->dynamic_value)) (var) : var);
> -+}
> -+
> -+SHELL_VAR *
> -+find_shell_variable (name)
> -+     const char *name;
> -+{
> -+  SHELL_VAR *var;
> -+
> -+  var = var_lookup (name, shell_variables);
> -+  if (var && nameref_p (var))
> -+    var = find_variable_nameref (var);
> -+
> -+  if (var == 0)
> -+    return ((SHELL_VAR *)NULL);
> -+
> -+  return (var->dynamic_value ? (*(var->dynamic_value)) (var) : var);
> -+}
> -+
> -+/* Look up the variable entry named NAME.  Returns the entry or NULL. */
> -+SHELL_VAR *
> -+find_variable (name)
> -+     const char *name;
> -+{
> -+  SHELL_VAR *v;
> -+
> -+  last_table_searched = 0;
> -+  v = find_variable_internal (name, (expanding_redir == 0 && (assigning_in_environment || executing_builtin)));
> -+  if (v && nameref_p (v))
> -+    v = find_variable_nameref (v);
> -+  return v;
> -+}
> -+
> -+SHELL_VAR *
> -+find_variable_noref (name)
> -+     const char *name;
> -+{
> -+  SHELL_VAR *v;
> -+
> -+  v = find_variable_internal (name, (expanding_redir == 0 && (assigning_in_environment || executing_builtin)));
> -+  return v;
> -+}
> -+
> -+/* Look up the function entry whose name matches STRING.
> -+   Returns the entry or NULL. */
> -+SHELL_VAR *
> -+find_function (name)
> -+     const char *name;
> -+{
> -+  return (hash_lookup (name, shell_functions));
> -+}
> -+
> -+/* Find the function definition for the shell function named NAME.  Returns
> -+   the entry or NULL. */
> -+FUNCTION_DEF *
> -+find_function_def (name)
> -+     const char *name;
> -+{
> -+#if defined (DEBUGGER)
> -+  return ((FUNCTION_DEF *)hash_lookup (name, shell_function_defs));
> -+#else
> -+  return ((FUNCTION_DEF *)0);
> -+#endif
> -+}
> -+
> -+/* Return the value of VAR.  VAR is assumed to have been the result of a
> -+   lookup without any subscript, if arrays are compiled into the shell. */
> -+char *
> -+get_variable_value (var)
> -+     SHELL_VAR *var;
> -+{
> -+  if (var == 0)
> -+    return ((char *)NULL);
> -+#if defined (ARRAY_VARS)
> -+  else if (array_p (var))
> -+    return (array_reference (array_cell (var), 0));
> -+  else if (assoc_p (var))
> -+    return (assoc_reference (assoc_cell (var), "0"));
> -+#endif
> -+  else
> -+    return (value_cell (var));
> -+}
> -+
> -+/* Return the string value of a variable.  Return NULL if the variable
> -+   doesn't exist.  Don't cons a new string.  This is a potential memory
> -+   leak if the variable is found in the temporary environment.  Since
> -+   functions and variables have separate name spaces, returns NULL if
> -+   var_name is a shell function only. */
> -+char *
> -+get_string_value (var_name)
> -+     const char *var_name;
> -+{
> -+  SHELL_VAR *var;
> -+
> -+  var = find_variable (var_name);
> -+  return ((var) ? get_variable_value (var) : (char *)NULL);
> -+}
> -+
> -+/* This is present for use by the tilde and readline libraries. */
> -+char *
> -+sh_get_env_value (v)
> -+     const char *v;
> -+{
> -+  return get_string_value (v);
> -+}
> -+
> -+/* **************************************************************** */
> -+/*								    */
> -+/*		  Creating and setting variables		    */
> -+/*								    */
> -+/* **************************************************************** */
> -+
> -+/* Set NAME to VALUE if NAME has no value. */
> -+SHELL_VAR *
> -+set_if_not (name, value)
> -+     char *name, *value;
> -+{
> -+  SHELL_VAR *v;
> -+
> -+  if (shell_variables == 0)
> -+    create_variable_tables ();
> -+
> -+  v = find_variable (name);
> -+  if (v == 0)
> -+    v = bind_variable_internal (name, value, global_variables->table, HASH_NOSRCH, 0);
> -+  return (v);
> -+}
> -+
> -+/* Create a local variable referenced by NAME. */
> -+SHELL_VAR *
> -+make_local_variable (name)
> -+     const char *name;
> -+{
> -+  SHELL_VAR *new_var, *old_var;
> -+  VAR_CONTEXT *vc;
> -+  int was_tmpvar;
> -+  char *tmp_value;
> -+
> -+  /* local foo; local foo;  is a no-op. */
> -+  old_var = find_variable (name);
> -+  if (old_var && local_p (old_var) && old_var->context == variable_context)
> -+    return (old_var);
> -+
> -+  was_tmpvar = old_var && tempvar_p (old_var);
> -+  /* If we're making a local variable in a shell function, the temporary env
> -+     has already been merged into the function's variable context stack.  We
> -+     can assume that a temporary var in the same context appears in the same
> -+     VAR_CONTEXT and can safely be returned without creating a new variable
> -+     (which results in duplicate names in the same VAR_CONTEXT->table */
> -+  /* We can't just test tmpvar_p because variables in the temporary env given
> -+     to a shell function appear in the function's local variable VAR_CONTEXT
> -+     but retain their tempvar attribute.  We want temporary variables that are
> -+     found in temporary_env, hence the test for last_table_searched, which is
> -+     set in hash_lookup and only (so far) checked here. */
> -+  if (was_tmpvar && old_var->context == variable_context && last_table_searched != temporary_env)
> -+    {
> -+      VUNSETATTR (old_var, att_invisible);
> -+      return (old_var);
> -+    }
> -+  if (was_tmpvar)
> -+    tmp_value = value_cell (old_var);
> -+
> -+  for (vc = shell_variables; vc; vc = vc->down)
> -+    if (vc_isfuncenv (vc) && vc->scope == variable_context)
> -+      break;
> -+
> -+  if (vc == 0)
> -+    {
> -+      internal_error (_("make_local_variable: no function context at current scope"));
> -+      return ((SHELL_VAR *)NULL);
> -+    }
> -+  else if (vc->table == 0)
> -+    vc->table = hash_create (TEMPENV_HASH_BUCKETS);
> -+
> -+  /* Since this is called only from the local/declare/typeset code, we can
> -+     call builtin_error here without worry (of course, it will also work
> -+     for anything that sets this_command_name).  Variables with the `noassign'
> -+     attribute may not be made local.  The test against old_var's context
> -+     level is to disallow local copies of readonly global variables (since I
> -+     believe that this could be a security hole).  Readonly copies of calling
> -+     function local variables are OK. */
> -+  if (old_var && (noassign_p (old_var) ||
> -+		 (readonly_p (old_var) && old_var->context == 0)))
> -+    {
> -+      if (readonly_p (old_var))
> -+	sh_readonly (name);
> -+      else if (noassign_p (old_var))
> -+	builtin_error (_("%s: variable may not be assigned value"), name);
> -+#if 0
> -+      /* Let noassign variables through with a warning */
> -+      if (readonly_p (old_var))
> -+#endif
> -+	return ((SHELL_VAR *)NULL);
> -+    }
> -+
> -+  if (old_var == 0)
> -+    new_var = make_new_variable (name, vc->table);
> -+  else
> -+    {
> -+      new_var = make_new_variable (name, vc->table);
> -+
> -+      /* If we found this variable in one of the temporary environments,
> -+	 inherit its value.  Watch to see if this causes problems with
> -+	 things like `x=4 local x'. XXX - see above for temporary env
> -+	 variables with the same context level as variable_context */
> -+      /* XXX - we should only do this if the variable is not an array. */
> -+      if (was_tmpvar)
> -+	var_setvalue (new_var, savestring (tmp_value));
> -+
> -+      new_var->attributes = exported_p (old_var) ? att_exported : 0;
> -+    }
> -+
> -+  vc->flags |= VC_HASLOCAL;
> -+
> -+  new_var->context = variable_context;
> -+  VSETATTR (new_var, att_local);
> -+
> -+  if (ifsname (name))
> -+    setifs (new_var);
> -+
> -+  if (was_tmpvar == 0)
> -+    VSETATTR (new_var, att_invisible);	/* XXX */
> -+  return (new_var);
> -+}
> -+
> -+/* Create a new shell variable with name NAME. */
> -+static SHELL_VAR *
> -+new_shell_variable (name)
> -+     const char *name;
> -+{
> -+  SHELL_VAR *entry;
> -+
> -+  entry = (SHELL_VAR *)xmalloc (sizeof (SHELL_VAR));
> -+
> -+  entry->name = savestring (name);
> -+  var_setvalue (entry, (char *)NULL);
> -+  CLEAR_EXPORTSTR (entry);
> -+
> -+  entry->dynamic_value = (sh_var_value_func_t *)NULL;
> -+  entry->assign_func = (sh_var_assign_func_t *)NULL;
> -+
> -+  entry->attributes = 0;
> -+
> -+  /* Always assume variables are to be made at toplevel!
> -+     make_local_variable has the responsibility of changing the
> -+     variable context. */
> -+  entry->context = 0;
> -+
> -+  return (entry);
> -+}
> -+
> -+/* Create a new shell variable with name NAME and add it to the hash table
> -+   TABLE. */
> -+static SHELL_VAR *
> -+make_new_variable (name, table)
> -+     const char *name;
> -+     HASH_TABLE *table;
> -+{
> -+  SHELL_VAR *entry;
> -+  BUCKET_CONTENTS *elt;
> -+
> -+  entry = new_shell_variable (name);
> -+
> -+  /* Make sure we have a shell_variables hash table to add to. */
> -+  if (shell_variables == 0)
> -+    create_variable_tables ();
> -+
> -+  elt = hash_insert (savestring (name), table, HASH_NOSRCH);
> -+  elt->data = (PTR_T)entry;
> -+
> -+  return entry;
> -+}
> -+
> -+#if defined (ARRAY_VARS)
> -+SHELL_VAR *
> -+make_new_array_variable (name)
> -+     char *name;
> -+{
> -+  SHELL_VAR *entry;
> -+  ARRAY *array;
> -+
> -+  entry = make_new_variable (name, global_variables->table);
> -+  array = array_create ();
> -+
> -+  var_setarray (entry, array);
> -+  VSETATTR (entry, att_array);
> -+  return entry;
> -+}
> -+
> -+SHELL_VAR *
> -+make_local_array_variable (name, assoc_ok)
> -+     char *name;
> -+     int assoc_ok;
> -+{
> -+  SHELL_VAR *var;
> -+  ARRAY *array;
> -+
> -+  var = make_local_variable (name);
> -+  if (var == 0 || array_p (var) || (assoc_ok && assoc_p (var)))
> -+    return var;
> -+
> -+  array = array_create ();
> -+
> -+  dispose_variable_value (var);
> -+  var_setarray (var, array);
> -+  VSETATTR (var, att_array);
> -+  return var;
> -+}
> -+
> -+SHELL_VAR *
> -+make_new_assoc_variable (name)
> -+     char *name;
> -+{
> -+  SHELL_VAR *entry;
> -+  HASH_TABLE *hash;
> -+
> -+  entry = make_new_variable (name, global_variables->table);
> -+  hash = assoc_create (0);
> -+
> -+  var_setassoc (entry, hash);
> -+  VSETATTR (entry, att_assoc);
> -+  return entry;
> -+}
> -+
> -+SHELL_VAR *
> -+make_local_assoc_variable (name)
> -+     char *name;
> -+{
> -+  SHELL_VAR *var;
> -+  HASH_TABLE *hash;
> -+
> -+  var = make_local_variable (name);
> -+  if (var == 0 || assoc_p (var))
> -+    return var;
> -+
> -+  dispose_variable_value (var);
> -+  hash = assoc_create (0);
> -+
> -+  var_setassoc (var, hash);
> -+  VSETATTR (var, att_assoc);
> -+  return var;
> -+}
> -+#endif
> -+
> -+char *
> -+make_variable_value (var, value, flags)
> -+     SHELL_VAR *var;
> -+     char *value;
> -+     int flags;
> -+{
> -+  char *retval, *oval;
> -+  intmax_t lval, rval;
> -+  int expok, olen, op;
> -+
> -+  /* If this variable has had its type set to integer (via `declare -i'),
> -+     then do expression evaluation on it and store the result.  The
> -+     functions in expr.c (evalexp()) and bind_int_variable() are responsible
> -+     for turning off the integer flag if they don't want further
> -+     evaluation done. */
> -+  if (integer_p (var))
> -+    {
> -+      if (flags & ASS_APPEND)
> -+	{
> -+	  oval = value_cell (var);
> -+	  lval = evalexp (oval, &expok);	/* ksh93 seems to do this */
> -+	  if (expok == 0)
> -+	    {
> -+	      top_level_cleanup ();
> -+	      jump_to_top_level (DISCARD);
> -+	    }
> -+	}
> -+      rval = evalexp (value, &expok);
> -+      if (expok == 0)
> -+	{
> -+	  top_level_cleanup ();
> -+	  jump_to_top_level (DISCARD);
> -+	}
> -+      /* This can be fooled if the variable's value changes while evaluating
> -+	 `rval'.  We can change it if we move the evaluation of lval to here. */
> -+      if (flags & ASS_APPEND)
> -+	rval += lval;
> -+      retval = itos (rval);
> -+    }
> -+#if defined (CASEMOD_ATTRS)
> -+  else if (capcase_p (var) || uppercase_p (var) || lowercase_p (var))
> -+    {
> -+      if (flags & ASS_APPEND)
> -+	{
> -+	  oval = get_variable_value (var);
> -+	  if (oval == 0)	/* paranoia */
> -+	    oval = "";
> -+	  olen = STRLEN (oval);
> -+	  retval = (char *)xmalloc (olen + (value ? STRLEN (value) : 0) + 1);
> -+	  strcpy (retval, oval);
> -+	  if (value)
> -+	    strcpy (retval+olen, value);
> -+	}
> -+      else if (*value)
> -+	retval = savestring (value);
> -+      else
> -+	{
> -+	  retval = (char *)xmalloc (1);
> -+	  retval[0] = '\0';
> -+	}
> -+      op = capcase_p (var) ? CASE_CAPITALIZE
> -+			 : (uppercase_p (var) ? CASE_UPPER : CASE_LOWER);
> -+      oval = sh_modcase (retval, (char *)0, op);
> -+      free (retval);
> -+      retval = oval;
> -+    }
> -+#endif /* CASEMOD_ATTRS */
> -+  else if (value)
> -+    {
> -+      if (flags & ASS_APPEND)
> -+	{
> -+	  oval = get_variable_value (var);
> -+	  if (oval == 0)	/* paranoia */
> -+	    oval = "";
> -+	  olen = STRLEN (oval);
> -+	  retval = (char *)xmalloc (olen + (value ? STRLEN (value) : 0) + 1);
> -+	  strcpy (retval, oval);
> -+	  if (value)
> -+	    strcpy (retval+olen, value);
> -+	}
> -+      else if (*value)
> -+	retval = savestring (value);
> -+      else
> -+	{
> -+	  retval = (char *)xmalloc (1);
> -+	  retval[0] = '\0';
> -+	}
> -+    }
> -+  else
> -+    retval = (char *)NULL;
> -+
> -+  return retval;
> -+}
> -+
> -+/* Bind a variable NAME to VALUE in the HASH_TABLE TABLE, which may be the
> -+   temporary environment (but usually is not). */
> -+static SHELL_VAR *
> -+bind_variable_internal (name, value, table, hflags, aflags)
> -+     const char *name;
> -+     char *value;
> -+     HASH_TABLE *table;
> -+     int hflags, aflags;
> -+{
> -+  char *newval;
> -+  SHELL_VAR *entry;
> -+
> -+  entry = (hflags & HASH_NOSRCH) ? (SHELL_VAR *)NULL : hash_lookup (name, table);
> -+  /* Follow the nameref chain here if this is the global variables table */
> -+  if (entry && nameref_p (entry) && (invisible_p (entry) == 0) && table == global_variables->table)
> -+    {
> -+      entry = find_global_variable (entry->name);
> -+      /* Let's see if we have a nameref referencing a variable that hasn't yet
> -+	 been created. */
> -+      if (entry == 0)
> -+	entry = find_variable_last_nameref (name);	/* XXX */
> -+      if (entry == 0)					/* just in case */
> -+        return (entry);
> -+    }
> -+
> -+  /* The first clause handles `declare -n ref; ref=x;' */
> -+  if (entry && invisible_p (entry) && nameref_p (entry))
> -+    goto assign_value;
> -+  else if (entry && nameref_p (entry))
> -+    {
> -+      newval = nameref_cell (entry);
> -+#if defined (ARRAY_VARS)
> -+      /* declare -n foo=x[2] */
> -+      if (valid_array_reference (newval))
> -+        /* XXX - should it be aflags? */
> -+	entry = assign_array_element (newval, make_variable_value (entry, value, 0), aflags);
> -+      else
> -+#endif
> -+      {
> -+      entry = make_new_variable (newval, table);
> -+      var_setvalue (entry, make_variable_value (entry, value, 0));
> -+      }
> -+    }
> -+  else if (entry == 0)
> -+    {
> -+      entry = make_new_variable (name, table);
> -+      var_setvalue (entry, make_variable_value (entry, value, 0)); /* XXX */
> -+    }
> -+  else if (entry->assign_func)	/* array vars have assign functions now */
> -+    {
> -+      INVALIDATE_EXPORTSTR (entry);
> -+      newval = (aflags & ASS_APPEND) ? make_variable_value (entry, value, aflags) : value;
> -+      if (assoc_p (entry))
> -+	entry = (*(entry->assign_func)) (entry, newval, -1, savestring ("0"));
> -+      else if (array_p (entry))
> -+	entry = (*(entry->assign_func)) (entry, newval, 0, 0);
> -+      else
> -+	entry = (*(entry->assign_func)) (entry, newval, -1, 0);
> -+      if (newval != value)
> -+	free (newval);
> -+      return (entry);
> -+    }
> -+  else
> -+    {
> -+assign_value:
> -+      if (readonly_p (entry) || noassign_p (entry))
> -+	{
> -+	  if (readonly_p (entry))
> -+	    err_readonly (name);
> -+	  return (entry);
> -+	}
> -+
> -+      /* Variables which are bound are visible. */
> -+      VUNSETATTR (entry, att_invisible);
> -+
> -+#if defined (ARRAY_VARS)
> -+      if (assoc_p (entry) || array_p (entry))
> -+        newval = make_array_variable_value (entry, 0, "0", value, aflags);
> -+      else
> -+#endif
> -+
> -+      newval = make_variable_value (entry, value, aflags);	/* XXX */
> -+
> -+      /* Invalidate any cached export string */
> -+      INVALIDATE_EXPORTSTR (entry);
> -+
> -+#if defined (ARRAY_VARS)
> -+      /* XXX -- this bears looking at again -- XXX */
> -+      /* If an existing array variable x is being assigned to with x=b or
> -+	 `read x' or something of that nature, silently convert it to
> -+	 x[0]=b or `read x[0]'. */
> -+      if (assoc_p (entry))
> -+	{
> -+	  assoc_insert (assoc_cell (entry), savestring ("0"), newval);
> -+	  free (newval);
> -+	}
> -+      else if (array_p (entry))
> -+	{
> -+	  array_insert (array_cell (entry), 0, newval);
> -+	  free (newval);
> -+	}
> -+      else
> -+#endif
> -+	{
> -+	  FREE (value_cell (entry));
> -+	  var_setvalue (entry, newval);
> -+	}
> -+    }
> -+
> -+  if (mark_modified_vars)
> -+    VSETATTR (entry, att_exported);
> -+
> -+  if (exported_p (entry))
> -+    array_needs_making = 1;
> -+
> -+  return (entry);
> -+}
> -+	
> -+/* Bind a variable NAME to VALUE.  This conses up the name
> -+   and value strings.  If we have a temporary environment, we bind there
> -+   first, then we bind into shell_variables. */
> -+
> -+SHELL_VAR *
> -+bind_variable (name, value, flags)
> -+     const char *name;
> -+     char *value;
> -+     int flags;
> -+{
> -+  SHELL_VAR *v, *nv;
> -+  VAR_CONTEXT *vc, *nvc;
> -+  int level;
> -+
> -+  if (shell_variables == 0)
> -+    create_variable_tables ();
> -+
> -+  /* If we have a temporary environment, look there first for the variable,
> -+     and, if found, modify the value there before modifying it in the
> -+     shell_variables table.  This allows sourced scripts to modify values
> -+     given to them in a temporary environment while modifying the variable
> -+     value that the caller sees. */
> -+  if (temporary_env)
> -+    bind_tempenv_variable (name, value);
> -+
> -+  /* XXX -- handle local variables here. */
> -+  for (vc = shell_variables; vc; vc = vc->down)
> -+    {
> -+      if (vc_isfuncenv (vc) || vc_isbltnenv (vc))
> -+	{
> -+	  v = hash_lookup (name, vc->table);
> -+	  nvc = vc;
> -+	  if (v && nameref_p (v))
> -+	    {
> -+	      nv = find_variable_nameref_context (v, vc, &nvc);
> -+	      if (nv == 0)
> -+		{
> -+		  nv = find_variable_last_nameref_context (v, vc, &nvc);
> -+		  if (nv && nameref_p (nv))
> -+		    {
> -+		      /* If this nameref variable doesn't have a value yet,
> -+			 set the value.  Otherwise, assign using the value as
> -+			 normal. */
> -+		      if (nameref_cell (nv) == 0)
> -+			return (bind_variable_internal (nv->name, value, nvc->table, 0, flags));
> -+		      return (bind_variable_internal (nameref_cell (nv), value, nvc->table, 0, flags));
> -+		    }
> -+		  else
> -+		    v = nv;
> -+		}
> -+	      else
> -+	        v = nv;
> -+	    }
> -+	  if (v)
> -+	    return (bind_variable_internal (v->name, value, nvc->table, 0, flags));
> -+	}
> -+    }
> -+  /* bind_variable_internal will handle nameref resolution in this case */
> -+  return (bind_variable_internal (name, value, global_variables->table, 0, flags));
> -+}
> -+
> -+SHELL_VAR *
> -+bind_global_variable (name, value, flags)
> -+     const char *name;
> -+     char *value;
> -+     int flags;
> -+{
> -+  SHELL_VAR *v, *nv;
> -+  VAR_CONTEXT *vc, *nvc;
> -+  int level;
> -+
> -+  if (shell_variables == 0)
> -+    create_variable_tables ();
> -+
> -+  /* bind_variable_internal will handle nameref resolution in this case */
> -+  return (bind_variable_internal (name, value, global_variables->table, 0, flags));
> -+}
> -+
> -+/* Make VAR, a simple shell variable, have value VALUE.  Once assigned a
> -+   value, variables are no longer invisible.  This is a duplicate of part
> -+   of the internals of bind_variable.  If the variable is exported, or
> -+   all modified variables should be exported, mark the variable for export
> -+   and note that the export environment needs to be recreated. */
> -+SHELL_VAR *
> -+bind_variable_value (var, value, aflags)
> -+     SHELL_VAR *var;
> -+     char *value;
> -+     int aflags;
> -+{
> -+  char *t;
> -+  int invis;
> -+
> -+  invis = invisible_p (var);
> -+  VUNSETATTR (var, att_invisible);
> -+
> -+  if (var->assign_func)
> -+    {
> -+      /* If we're appending, we need the old value, so use
> -+	 make_variable_value */
> -+      t = (aflags & ASS_APPEND) ? make_variable_value (var, value, aflags) : value;
> -+      (*(var->assign_func)) (var, t, -1, 0);
> -+      if (t != value && t)
> -+	free (t);      
> -+    }
> -+  else
> -+    {
> -+      t = make_variable_value (var, value, aflags);
> -+#if defined (ARRAY_VARS)
> -+      if ((aflags & ASS_NAMEREF) && (t == 0 || *t == 0 || (legal_identifier (t) == 0 && valid_array_reference (t) == 0)))
> -+#else
> -+      if ((aflags & ASS_NAMEREF) && (t == 0 || *t == 0 || legal_identifier (t) == 0))
> -+#endif
> -+	{
> -+	  free (t);
> -+	  if (invis)
> -+	    VSETATTR (var, att_invisible);	/* XXX */
> -+	  return ((SHELL_VAR *)NULL);
> -+	}
> -+      FREE (value_cell (var));
> -+      var_setvalue (var, t);
> -+    }
> -+
> -+  INVALIDATE_EXPORTSTR (var);
> -+
> -+  if (mark_modified_vars)
> -+    VSETATTR (var, att_exported);
> -+
> -+  if (exported_p (var))
> -+    array_needs_making = 1;
> -+
> -+  return (var);
> -+}
> -+
> -+/* Bind/create a shell variable with the name LHS to the RHS.
> -+   This creates or modifies a variable such that it is an integer.
> -+
> -+   This used to be in expr.c, but it is here so that all of the
> -+   variable binding stuff is localized.  Since we don't want any
> -+   recursive evaluation from bind_variable() (possible without this code,
> -+   since bind_variable() calls the evaluator for variables with the integer
> -+   attribute set), we temporarily turn off the integer attribute for each
> -+   variable we set here, then turn it back on after binding as necessary. */
> -+
> -+SHELL_VAR *
> -+bind_int_variable (lhs, rhs)
> -+     char *lhs, *rhs;
> -+{
> -+  register SHELL_VAR *v;
> -+  int isint, isarr, implicitarray;
> -+
> -+  isint = isarr = implicitarray = 0;
> -+#if defined (ARRAY_VARS)
> -+  if (valid_array_reference (lhs))
> -+    {
> -+      isarr = 1;
> -+      v = array_variable_part (lhs, (char **)0, (int *)0);
> -+    }
> -+  else
> -+#endif
> -+    v = find_variable (lhs);
> -+
> -+  if (v)
> -+    {
> -+      isint = integer_p (v);
> -+      VUNSETATTR (v, att_integer);
> -+#if defined (ARRAY_VARS)
> -+      if (array_p (v) && isarr == 0)
> -+	implicitarray = 1;
> -+#endif
> -+    }
> -+
> -+#if defined (ARRAY_VARS)
> -+  if (isarr)
> -+    v = assign_array_element (lhs, rhs, 0);
> -+  else if (implicitarray)
> -+    v = bind_array_variable (lhs, 0, rhs, 0);
> -+  else
> -+#endif
> -+    v = bind_variable (lhs, rhs, 0);
> -+
> -+  if (v && isint)
> -+    VSETATTR (v, att_integer);
> -+
> -+  VUNSETATTR (v, att_invisible);
> -+
> -+  return (v);
> -+}
> -+
> -+SHELL_VAR *
> -+bind_var_to_int (var, val)
> -+     char *var;
> -+     intmax_t val;
> -+{
> -+  char ibuf[INT_STRLEN_BOUND (intmax_t) + 1], *p;
> -+
> -+  p = fmtulong (val, 10, ibuf, sizeof (ibuf), 0);
> -+  return (bind_int_variable (var, p));
> -+}
> -+
> -+/* Do a function binding to a variable.  You pass the name and
> -+   the command to bind to.  This conses the name and command. */
> -+SHELL_VAR *
> -+bind_function (name, value)
> -+     const char *name;
> -+     COMMAND *value;
> -+{
> -+  SHELL_VAR *entry;
> -+
> -+  entry = find_function (name);
> -+  if (entry == 0)
> -+    {
> -+      BUCKET_CONTENTS *elt;
> -+
> -+      elt = hash_insert (savestring (name), shell_functions, HASH_NOSRCH);
> -+      entry = new_shell_variable (name);
> -+      elt->data = (PTR_T)entry;
> -+    }
> -+  else
> -+    INVALIDATE_EXPORTSTR (entry);
> -+
> -+  if (var_isset (entry))
> -+    dispose_command (function_cell (entry));
> -+
> -+  if (value)
> -+    var_setfunc (entry, copy_command (value));
> -+  else
> -+    var_setfunc (entry, 0);
> -+
> -+  VSETATTR (entry, att_function);
> -+
> -+  if (mark_modified_vars)
> -+    VSETATTR (entry, att_exported);
> -+
> -+  VUNSETATTR (entry, att_invisible);		/* Just to be sure */
> -+
> -+  if (exported_p (entry))
> -+    array_needs_making = 1;
> -+
> -+#if defined (PROGRAMMABLE_COMPLETION)
> -+  set_itemlist_dirty (&it_functions);
> -+#endif
> -+
> -+  return (entry);
> -+}
> -+
> -+#if defined (DEBUGGER)
> -+/* Bind a function definition, which includes source file and line number
> -+   information in addition to the command, into the FUNCTION_DEF hash table.*/
> -+void
> -+bind_function_def (name, value)
> -+     const char *name;
> -+     FUNCTION_DEF *value;
> -+{
> -+  FUNCTION_DEF *entry;
> -+  BUCKET_CONTENTS *elt;
> -+  COMMAND *cmd;
> -+
> -+  entry = find_function_def (name);
> -+  if (entry)
> -+    {
> -+      dispose_function_def_contents (entry);
> -+      entry = copy_function_def_contents (value, entry);
> -+    }
> -+  else
> -+    {
> -+      cmd = value->command;
> -+      value->command = 0;
> -+      entry = copy_function_def (value);
> -+      value->command = cmd;
> -+
> -+      elt = hash_insert (savestring (name), shell_function_defs, HASH_NOSRCH);
> -+      elt->data = (PTR_T *)entry;
> -+    }
> -+}
> -+#endif /* DEBUGGER */
> -+
> -+/* Add STRING, which is of the form foo=bar, to the temporary environment
> -+   HASH_TABLE (temporary_env).  The functions in execute_cmd.c are
> -+   responsible for moving the main temporary env to one of the other
> -+   temporary environments.  The expansion code in subst.c calls this. */
> -+int
> -+assign_in_env (word, flags)
> -+     WORD_DESC *word;
> -+     int flags;
> -+{
> -+  int offset, aflags;
> -+  char *name, *temp, *value;
> -+  SHELL_VAR *var;
> -+  const char *string;
> -+
> -+  string = word->word;
> -+
> -+  aflags = 0;
> -+  offset = assignment (string, 0);
> -+  name = savestring (string);
> -+  value = (char *)NULL;
> -+
> -+  if (name[offset] == '=')
> -+    {
> -+      name[offset] = 0;
> -+
> -+      /* don't ignore the `+' when assigning temporary environment */
> -+      if (name[offset - 1] == '+')
> -+	{
> -+	  name[offset - 1] = '\0';
> -+	  aflags |= ASS_APPEND;
> -+	}
> -+
> -+      var = find_variable (name);
> -+      if (var && (readonly_p (var) || noassign_p (var)))
> -+	{
> -+	  if (readonly_p (var))
> -+	    err_readonly (name);
> -+	  free (name);
> -+  	  return (0);
> -+	}
> -+
> -+      temp = name + offset + 1;
> -+      value = expand_assignment_string_to_string (temp, 0);
> -+
> -+      if (var && (aflags & ASS_APPEND))
> -+	{
> -+	  temp = make_variable_value (var, value, aflags);
> -+	  FREE (value);
> -+	  value = temp;
> -+	}
> -+    }
> -+
> -+  if (temporary_env == 0)
> -+    temporary_env = hash_create (TEMPENV_HASH_BUCKETS);
> -+
> -+  var = hash_lookup (name, temporary_env);
> -+  if (var == 0)
> -+    var = make_new_variable (name, temporary_env);
> -+  else
> -+    FREE (value_cell (var));
> -+
> -+  if (value == 0)
> -+    {
> -+      value = (char *)xmalloc (1);	/* like do_assignment_internal */
> -+      value[0] = '\0';
> -+    }
> -+
> -+  var_setvalue (var, value);
> -+  var->attributes |= (att_exported|att_tempvar);
> -+  var->context = variable_context;	/* XXX */
> -+
> -+  INVALIDATE_EXPORTSTR (var);
> -+  var->exportstr = mk_env_string (name, value, 0);
> -+
> -+  array_needs_making = 1;
> -+
> -+  if (flags)
> -+    stupidly_hack_special_variables (name);
> -+
> -+  if (echo_command_at_execute)
> -+    /* The Korn shell prints the `+ ' in front of assignment statements,
> -+	so we do too. */
> -+    xtrace_print_assignment (name, value, 0, 1);
> -+
> -+  free (name);
> -+  return 1;
> -+}
> -+
> -+/* **************************************************************** */
> -+/*								    */
> -+/*			Copying variables			    */
> -+/*								    */
> -+/* **************************************************************** */
> -+
> -+#ifdef INCLUDE_UNUSED
> -+/* Copy VAR to a new data structure and return that structure. */
> -+SHELL_VAR *
> -+copy_variable (var)
> -+     SHELL_VAR *var;
> -+{
> -+  SHELL_VAR *copy = (SHELL_VAR *)NULL;
> -+
> -+  if (var)
> -+    {
> -+      copy = (SHELL_VAR *)xmalloc (sizeof (SHELL_VAR));
> -+
> -+      copy->attributes = var->attributes;
> -+      copy->name = savestring (var->name);
> -+
> -+      if (function_p (var))
> -+	var_setfunc (copy, copy_command (function_cell (var)));
> -+#if defined (ARRAY_VARS)
> -+      else if (array_p (var))
> -+	var_setarray (copy, array_copy (array_cell (var)));
> -+      else if (assoc_p (var))
> -+	var_setassoc (copy, assoc_copy (assoc_cell (var)));
> -+#endif
> -+      else if (nameref_cell (var))	/* XXX - nameref */
> -+	var_setref (copy, savestring (nameref_cell (var)));
> -+      else if (value_cell (var))	/* XXX - nameref */
> -+	var_setvalue (copy, savestring (value_cell (var)));
> -+      else
> -+	var_setvalue (copy, (char *)NULL);
> -+
> -+      copy->dynamic_value = var->dynamic_value;
> -+      copy->assign_func = var->assign_func;
> -+
> -+      copy->exportstr = COPY_EXPORTSTR (var);
> -+
> -+      copy->context = var->context;
> -+    }
> -+  return (copy);
> -+}
> -+#endif
> -+
> -+/* **************************************************************** */
> -+/*								    */
> -+/*		  Deleting and unsetting variables		    */
> -+/*								    */
> -+/* **************************************************************** */
> -+
> -+/* Dispose of the information attached to VAR. */
> -+static void
> -+dispose_variable_value (var)
> -+     SHELL_VAR *var;
> -+{
> -+  if (function_p (var))
> -+    dispose_command (function_cell (var));
> -+#if defined (ARRAY_VARS)
> -+  else if (array_p (var))
> -+    array_dispose (array_cell (var));
> -+  else if (assoc_p (var))
> -+    assoc_dispose (assoc_cell (var));
> -+#endif
> -+  else if (nameref_p (var))
> -+    FREE (nameref_cell (var));
> -+  else
> -+    FREE (value_cell (var));
> -+}
> -+
> -+void
> -+dispose_variable (var)
> -+     SHELL_VAR *var;
> -+{
> -+  if (var == 0)
> -+    return;
> -+
> -+  if (nofree_p (var) == 0)
> -+    dispose_variable_value (var);
> -+
> -+  FREE_EXPORTSTR (var);
> -+
> -+  free (var->name);
> -+
> -+  if (exported_p (var))
> -+    array_needs_making = 1;
> -+
> -+  free (var);
> -+}
> -+
> -+/* Unset the shell variable referenced by NAME.  Unsetting a nameref variable
> -+   unsets the variable it resolves to but leaves the nameref alone. */
> -+int
> -+unbind_variable (name)
> -+     const char *name;
> -+{
> -+  SHELL_VAR *v, *nv;
> -+  int r;
> -+
> -+  v = var_lookup (name, shell_variables);
> -+  nv = (v && nameref_p (v)) ? find_variable_nameref (v) : (SHELL_VAR *)NULL;
> -+
> -+  r = nv ? makunbound (nv->name, shell_variables) : makunbound (name, shell_variables);
> -+  return r;
> -+}
> -+
> -+/* Unbind NAME, where NAME is assumed to be a nameref variable */
> -+int
> -+unbind_nameref (name)
> -+     const char *name;
> -+{
> -+  SHELL_VAR *v;
> -+
> -+  v = var_lookup (name, shell_variables);
> -+  if (v && nameref_p (v))
> -+    return makunbound (name, shell_variables);
> -+  return 0;
> -+}
> -+
> -+/* Unset the shell function named NAME. */
> -+int
> -+unbind_func (name)
> -+     const char *name;
> -+{
> -+  BUCKET_CONTENTS *elt;
> -+  SHELL_VAR *func;
> -+
> -+  elt = hash_remove (name, shell_functions, 0);
> -+
> -+  if (elt == 0)
> -+    return -1;
> -+
> -+#if defined (PROGRAMMABLE_COMPLETION)
> -+  set_itemlist_dirty (&it_functions);
> -+#endif
> -+
> -+  func = (SHELL_VAR *)elt->data;
> -+  if (func)
> -+    {
> -+      if (exported_p (func))
> -+	array_needs_making++;
> -+      dispose_variable (func);
> -+    }
> -+
> -+  free (elt->key);
> -+  free (elt);
> -+
> -+  return 0;  
> -+}
> -+
> -+#if defined (DEBUGGER)
> -+int
> -+unbind_function_def (name)
> -+     const char *name;
> -+{
> -+  BUCKET_CONTENTS *elt;
> -+  FUNCTION_DEF *funcdef;
> -+
> -+  elt = hash_remove (name, shell_function_defs, 0);
> -+
> -+  if (elt == 0)
> -+    return -1;
> -+
> -+  funcdef = (FUNCTION_DEF *)elt->data;
> -+  if (funcdef)
> -+    dispose_function_def (funcdef);
> -+
> -+  free (elt->key);
> -+  free (elt);
> -+
> -+  return 0;  
> -+}
> -+#endif /* DEBUGGER */
> -+
> -+int
> -+delete_var (name, vc)
> -+     const char *name;
> -+     VAR_CONTEXT *vc;
> -+{
> -+  BUCKET_CONTENTS *elt;
> -+  SHELL_VAR *old_var;
> -+  VAR_CONTEXT *v;
> -+
> -+  for (elt = (BUCKET_CONTENTS *)NULL, v = vc; v; v = v->down)
> -+    if (elt = hash_remove (name, v->table, 0))
> -+      break;
> -+
> -+  if (elt == 0)
> -+    return (-1);
> -+
> -+  old_var = (SHELL_VAR *)elt->data;
> -+  free (elt->key);
> -+  free (elt);
> -+
> -+  dispose_variable (old_var);
> -+  return (0);
> -+}
> -+
> -+/* Make the variable associated with NAME go away.  HASH_LIST is the
> -+   hash table from which this variable should be deleted (either
> -+   shell_variables or shell_functions).
> -+   Returns non-zero if the variable couldn't be found. */
> -+int
> -+makunbound (name, vc)
> -+     const char *name;
> -+     VAR_CONTEXT *vc;
> -+{
> -+  BUCKET_CONTENTS *elt, *new_elt;
> -+  SHELL_VAR *old_var;
> -+  VAR_CONTEXT *v;
> -+  char *t;
> -+
> -+  for (elt = (BUCKET_CONTENTS *)NULL, v = vc; v; v = v->down)
> -+    if (elt = hash_remove (name, v->table, 0))
> -+      break;
> -+
> -+  if (elt == 0)
> -+    return (-1);
> -+
> -+  old_var = (SHELL_VAR *)elt->data;
> -+
> -+  if (old_var && exported_p (old_var))
> -+    array_needs_making++;
> -+
> -+  /* If we're unsetting a local variable and we're still executing inside
> -+     the function, just mark the variable as invisible.  The function
> -+     eventually called by pop_var_context() will clean it up later.  This
> -+     must be done so that if the variable is subsequently assigned a new
> -+     value inside the function, the `local' attribute is still present.
> -+     We also need to add it back into the correct hash table. */
> -+  if (old_var && local_p (old_var) && variable_context == old_var->context)
> -+    {
> -+      if (nofree_p (old_var))
> -+	var_setvalue (old_var, (char *)NULL);
> -+#if defined (ARRAY_VARS)
> -+      else if (array_p (old_var))
> -+	array_dispose (array_cell (old_var));
> -+      else if (assoc_p (old_var))
> -+	assoc_dispose (assoc_cell (old_var));
> -+#endif
> -+      else if (nameref_p (old_var))
> -+	FREE (nameref_cell (old_var));
> -+      else
> -+	FREE (value_cell (old_var));
> -+      /* Reset the attributes.  Preserve the export attribute if the variable
> -+	 came from a temporary environment.  Make sure it stays local, and
> -+	 make it invisible. */ 
> -+      old_var->attributes = (exported_p (old_var) && tempvar_p (old_var)) ? att_exported : 0;
> -+      VSETATTR (old_var, att_local);
> -+      VSETATTR (old_var, att_invisible);
> -+      var_setvalue (old_var, (char *)NULL);
> -+      INVALIDATE_EXPORTSTR (old_var);
> -+
> -+      new_elt = hash_insert (savestring (old_var->name), v->table, 0);
> -+      new_elt->data = (PTR_T)old_var;
> -+      stupidly_hack_special_variables (old_var->name);
> -+
> -+      free (elt->key);
> -+      free (elt);
> -+      return (0);
> -+    }
> -+
> -+  /* Have to save a copy of name here, because it might refer to
> -+     old_var->name.  If so, stupidly_hack_special_variables will
> -+     reference freed memory. */
> -+  t = savestring (name);
> -+
> -+  free (elt->key);
> -+  free (elt);
> -+
> -+  dispose_variable (old_var);
> -+  stupidly_hack_special_variables (t);
> -+  free (t);
> -+
> -+  return (0);
> -+}
> -+
> -+/* Get rid of all of the variables in the current context. */
> -+void
> -+kill_all_local_variables ()
> -+{
> -+  VAR_CONTEXT *vc;
> -+
> -+  for (vc = shell_variables; vc; vc = vc->down)
> -+    if (vc_isfuncenv (vc) && vc->scope == variable_context)
> -+      break;
> -+  if (vc == 0)
> -+    return;		/* XXX */
> -+
> -+  if (vc->table && vc_haslocals (vc))
> -+    {
> -+      delete_all_variables (vc->table);
> -+      hash_dispose (vc->table);
> -+    }
> -+  vc->table = (HASH_TABLE *)NULL;
> -+}
> -+
> -+static void
> -+free_variable_hash_data (data)
> -+     PTR_T data;
> -+{
> -+  SHELL_VAR *var;
> -+
> -+  var = (SHELL_VAR *)data;
> -+  dispose_variable (var);
> -+}
> -+
> -+/* Delete the entire contents of the hash table. */
> -+void
> -+delete_all_variables (hashed_vars)
> -+     HASH_TABLE *hashed_vars;
> -+{
> -+  hash_flush (hashed_vars, free_variable_hash_data);
> -+}
> -+
> -+/* **************************************************************** */
> -+/*								    */
> -+/*		     Setting variable attributes		    */
> -+/*								    */
> -+/* **************************************************************** */
> -+
> -+#define FIND_OR_MAKE_VARIABLE(name, entry) \
> -+  do \
> -+    { \
> -+      entry = find_variable (name); \
> -+      if (!entry) \
> -+	{ \
> -+	  entry = bind_variable (name, "", 0); \
> -+	  if (!no_invisible_vars && entry) entry->attributes |= att_invisible; \
> -+	} \
> -+    } \
> -+  while (0)
> -+
> -+/* Make the variable associated with NAME be readonly.
> -+   If NAME does not exist yet, create it. */
> -+void
> -+set_var_read_only (name)
> -+     char *name;
> -+{
> -+  SHELL_VAR *entry;
> -+
> -+  FIND_OR_MAKE_VARIABLE (name, entry);
> -+  VSETATTR (entry, att_readonly);
> -+}
> -+
> -+#ifdef INCLUDE_UNUSED
> -+/* Make the function associated with NAME be readonly.
> -+   If NAME does not exist, we just punt, like auto_export code below. */
> -+void
> -+set_func_read_only (name)
> -+     const char *name;
> -+{
> -+  SHELL_VAR *entry;
> -+
> -+  entry = find_function (name);
> -+  if (entry)
> -+    VSETATTR (entry, att_readonly);
> -+}
> -+
> -+/* Make the variable associated with NAME be auto-exported.
> -+   If NAME does not exist yet, create it. */
> -+void
> -+set_var_auto_export (name)
> -+     char *name;
> -+{
> -+  SHELL_VAR *entry;
> -+
> -+  FIND_OR_MAKE_VARIABLE (name, entry);
> -+  set_auto_export (entry);
> -+}
> -+
> -+/* Make the function associated with NAME be auto-exported. */
> -+void
> -+set_func_auto_export (name)
> -+     const char *name;
> -+{
> -+  SHELL_VAR *entry;
> -+
> -+  entry = find_function (name);
> -+  if (entry)
> -+    set_auto_export (entry);
> -+}
> -+#endif
> -+
> -+/* **************************************************************** */
> -+/*								    */
> -+/*		     Creating lists of variables		    */
> -+/*								    */
> -+/* **************************************************************** */
> -+
> -+static VARLIST *
> -+vlist_alloc (nentries)
> -+     int nentries;
> -+{
> -+  VARLIST  *vlist;
> -+
> -+  vlist = (VARLIST *)xmalloc (sizeof (VARLIST));
> -+  vlist->list = (SHELL_VAR **)xmalloc ((nentries + 1) * sizeof (SHELL_VAR *));
> -+  vlist->list_size = nentries;
> -+  vlist->list_len = 0;
> -+  vlist->list[0] = (SHELL_VAR *)NULL;
> -+
> -+  return vlist;
> -+}
> -+
> -+static VARLIST *
> -+vlist_realloc (vlist, n)
> -+     VARLIST *vlist;
> -+     int n;
> -+{
> -+  if (vlist == 0)
> -+    return (vlist = vlist_alloc (n));
> -+  if (n > vlist->list_size)
> -+    {
> -+      vlist->list_size = n;
> -+      vlist->list = (SHELL_VAR **)xrealloc (vlist->list, (vlist->list_size + 1) * sizeof (SHELL_VAR *));
> -+    }
> -+  return vlist;
> -+}
> -+
> -+static void
> -+vlist_add (vlist, var, flags)
> -+     VARLIST *vlist;
> -+     SHELL_VAR *var;
> -+     int flags;
> -+{
> -+  register int i;
> -+
> -+  for (i = 0; i < vlist->list_len; i++)
> -+    if (STREQ (var->name, vlist->list[i]->name))
> -+      break;
> -+  if (i < vlist->list_len)
> -+    return;
> -+
> -+  if (i >= vlist->list_size)
> -+    vlist = vlist_realloc (vlist, vlist->list_size + 16);
> -+
> -+  vlist->list[vlist->list_len++] = var;
> -+  vlist->list[vlist->list_len] = (SHELL_VAR *)NULL;
> -+}
> -+
> -+/* Map FUNCTION over the variables in VAR_HASH_TABLE.  Return an array of the
> -+   variables for which FUNCTION returns a non-zero value.  A NULL value
> -+   for FUNCTION means to use all variables. */
> -+SHELL_VAR **
> -+map_over (function, vc)
> -+     sh_var_map_func_t *function;
> -+     VAR_CONTEXT *vc;
> -+{
> -+  VAR_CONTEXT *v;
> -+  VARLIST *vlist;
> -+  SHELL_VAR **ret;
> -+  int nentries;
> -+
> -+  for (nentries = 0, v = vc; v; v = v->down)
> -+    nentries += HASH_ENTRIES (v->table);
> -+
> -+  if (nentries == 0)
> -+    return (SHELL_VAR **)NULL;
> -+
> -+  vlist = vlist_alloc (nentries);
> -+
> -+  for (v = vc; v; v = v->down)
> -+    flatten (v->table, function, vlist, 0);
> -+
> -+  ret = vlist->list;
> -+  free (vlist);
> -+  return ret;
> -+}
> -+
> -+SHELL_VAR **
> -+map_over_funcs (function)
> -+     sh_var_map_func_t *function;
> -+{
> -+  VARLIST *vlist;
> -+  SHELL_VAR **ret;
> -+
> -+  if (shell_functions == 0 || HASH_ENTRIES (shell_functions) == 0)
> -+    return ((SHELL_VAR **)NULL);
> -+
> -+  vlist = vlist_alloc (HASH_ENTRIES (shell_functions));
> -+
> -+  flatten (shell_functions, function, vlist, 0);
> -+
> -+  ret = vlist->list;
> -+  free (vlist);
> -+  return ret;
> -+}
> -+
> -+/* Flatten VAR_HASH_TABLE, applying FUNC to each member and adding those
> -+   elements for which FUNC succeeds to VLIST->list.  FLAGS is reserved
> -+   for future use.  Only unique names are added to VLIST.  If FUNC is
> -+   NULL, each variable in VAR_HASH_TABLE is added to VLIST.  If VLIST is
> -+   NULL, FUNC is applied to each SHELL_VAR in VAR_HASH_TABLE.  If VLIST
> -+   and FUNC are both NULL, nothing happens. */
> -+static void
> -+flatten (var_hash_table, func, vlist, flags)
> -+     HASH_TABLE *var_hash_table;
> -+     sh_var_map_func_t *func;
> -+     VARLIST *vlist;
> -+     int flags;
> -+{
> -+  register int i;
> -+  register BUCKET_CONTENTS *tlist;
> -+  int r;
> -+  SHELL_VAR *var;
> -+
> -+  if (var_hash_table == 0 || (HASH_ENTRIES (var_hash_table) == 0) || (vlist == 0 && func == 0))
> -+    return;
> -+
> -+  for (i = 0; i < var_hash_table->nbuckets; i++)
> -+    {
> -+      for (tlist = hash_items (i, var_hash_table); tlist; tlist = tlist->next)
> -+	{
> -+	  var = (SHELL_VAR *)tlist->data;
> -+
> -+	  r = func ? (*func) (var) : 1;
> -+	  if (r && vlist)
> -+	    vlist_add (vlist, var, flags);
> -+	}
> -+    }
> -+}
> -+
> -+void
> -+sort_variables (array)
> -+     SHELL_VAR **array;
> -+{
> -+  qsort (array, strvec_len ((char **)array), sizeof (SHELL_VAR *), (QSFUNC *)qsort_var_comp);
> -+}
> -+
> -+static int
> -+qsort_var_comp (var1, var2)
> -+     SHELL_VAR **var1, **var2;
> -+{
> -+  int result;
> -+
> -+  if ((result = (*var1)->name[0] - (*var2)->name[0]) == 0)
> -+    result = strcmp ((*var1)->name, (*var2)->name);
> -+
> -+  return (result);
> -+}
> -+
> -+/* Apply FUNC to each variable in SHELL_VARIABLES, adding each one for
> -+   which FUNC succeeds to an array of SHELL_VAR *s.  Returns the array. */
> -+static SHELL_VAR **
> -+vapply (func)
> -+     sh_var_map_func_t *func;
> -+{
> -+  SHELL_VAR **list;
> -+
> -+  list = map_over (func, shell_variables);
> -+  if (list /* && posixly_correct */)
> -+    sort_variables (list);
> -+  return (list);
> -+}
> -+
> -+/* Apply FUNC to each variable in SHELL_FUNCTIONS, adding each one for
> -+   which FUNC succeeds to an array of SHELL_VAR *s.  Returns the array. */
> -+static SHELL_VAR **
> -+fapply (func)
> -+     sh_var_map_func_t *func;
> -+{
> -+  SHELL_VAR **list;
> -+
> -+  list = map_over_funcs (func);
> -+  if (list /* && posixly_correct */)
> -+    sort_variables (list);
> -+  return (list);
> -+}
> -+
> -+/* Create a NULL terminated array of all the shell variables. */
> -+SHELL_VAR **
> -+all_shell_variables ()
> -+{
> -+  return (vapply ((sh_var_map_func_t *)NULL));
> -+}
> -+
> -+/* Create a NULL terminated array of all the shell functions. */
> -+SHELL_VAR **
> -+all_shell_functions ()
> -+{
> -+  return (fapply ((sh_var_map_func_t *)NULL));
> -+}
> -+
> -+static int
> -+visible_var (var)
> -+     SHELL_VAR *var;
> -+{
> -+  return (invisible_p (var) == 0);
> -+}
> -+
> -+SHELL_VAR **
> -+all_visible_functions ()
> -+{
> -+  return (fapply (visible_var));
> -+}
> -+
> -+SHELL_VAR **
> -+all_visible_variables ()
> -+{
> -+  return (vapply (visible_var));
> -+}
> -+
> -+/* Return non-zero if the variable VAR is visible and exported.  Array
> -+   variables cannot be exported. */
> -+static int
> -+visible_and_exported (var)
> -+     SHELL_VAR *var;
> -+{
> -+  return (invisible_p (var) == 0 && exported_p (var));
> -+}
> -+
> -+/* Candidate variables for the export environment are either valid variables
> -+   with the export attribute or invalid variables inherited from the initial
> -+   environment and simply passed through. */
> -+static int
> -+export_environment_candidate (var)
> -+     SHELL_VAR *var;
> -+{
> -+  return (exported_p (var) && (invisible_p (var) == 0 || imported_p (var)));
> -+}
> -+
> -+/* Return non-zero if VAR is a local variable in the current context and
> -+   is exported. */
> -+static int
> -+local_and_exported (var)
> -+     SHELL_VAR *var;
> -+{
> -+  return (invisible_p (var) == 0 && local_p (var) && var->context == variable_context && exported_p (var));
> -+}
> -+
> -+SHELL_VAR **
> -+all_exported_variables ()
> -+{
> -+  return (vapply (visible_and_exported));
> -+}
> -+
> -+SHELL_VAR **
> -+local_exported_variables ()
> -+{
> -+  return (vapply (local_and_exported));
> -+}
> -+
> -+static int
> -+variable_in_context (var)
> -+     SHELL_VAR *var;
> -+{
> -+  return (invisible_p (var) == 0 && local_p (var) && var->context == variable_context);
> -+}
> -+
> -+SHELL_VAR **
> -+all_local_variables ()
> -+{
> -+  VARLIST *vlist;
> -+  SHELL_VAR **ret;
> -+  VAR_CONTEXT *vc;
> -+
> -+  vc = shell_variables;
> -+  for (vc = shell_variables; vc; vc = vc->down)
> -+    if (vc_isfuncenv (vc) && vc->scope == variable_context)
> -+      break;
> -+
> -+  if (vc == 0)
> -+    {
> -+      internal_error (_("all_local_variables: no function context at current scope"));
> -+      return (SHELL_VAR **)NULL;
> -+    }
> -+  if (vc->table == 0 || HASH_ENTRIES (vc->table) == 0 || vc_haslocals (vc) == 0)
> -+    return (SHELL_VAR **)NULL;
> -+    
> -+  vlist = vlist_alloc (HASH_ENTRIES (vc->table));
> -+
> -+  flatten (vc->table, variable_in_context, vlist, 0);
> -+
> -+  ret = vlist->list;
> -+  free (vlist);
> -+  if (ret)
> -+    sort_variables (ret);
> -+  return ret;
> -+}
> -+
> -+#if defined (ARRAY_VARS)
> -+/* Return non-zero if the variable VAR is visible and an array. */
> -+static int
> -+visible_array_vars (var)
> -+     SHELL_VAR *var;
> -+{
> -+  return (invisible_p (var) == 0 && array_p (var));
> -+}
> -+
> -+SHELL_VAR **
> -+all_array_variables ()
> -+{
> -+  return (vapply (visible_array_vars));
> -+}
> -+#endif /* ARRAY_VARS */
> -+
> -+char **
> -+all_variables_matching_prefix (prefix)
> -+     const char *prefix;
> -+{
> -+  SHELL_VAR **varlist;
> -+  char **rlist;
> -+  int vind, rind, plen;
> -+
> -+  plen = STRLEN (prefix);
> -+  varlist = all_visible_variables ();
> -+  for (vind = 0; varlist && varlist[vind]; vind++)
> -+    ;
> -+  if (varlist == 0 || vind == 0)
> -+    return ((char **)NULL);
> -+  rlist = strvec_create (vind + 1);
> -+  for (vind = rind = 0; varlist[vind]; vind++)
> -+    {
> -+      if (plen == 0 || STREQN (prefix, varlist[vind]->name, plen))
> -+	rlist[rind++] = savestring (varlist[vind]->name);
> -+    }
> -+  rlist[rind] = (char *)0;
> -+  free (varlist);
> -+
> -+  return rlist;
> -+}
> -+
> -+/* **************************************************************** */
> -+/*								    */
> -+/*		 Managing temporary variable scopes		    */
> -+/*								    */
> -+/* **************************************************************** */
> -+
> -+/* Make variable NAME have VALUE in the temporary environment. */
> -+static SHELL_VAR *
> -+bind_tempenv_variable (name, value)
> -+     const char *name;
> -+     char *value;
> -+{
> -+  SHELL_VAR *var;
> -+
> -+  var = temporary_env ? hash_lookup (name, temporary_env) : (SHELL_VAR *)NULL;
> -+
> -+  if (var)
> -+    {
> -+      FREE (value_cell (var));
> -+      var_setvalue (var, savestring (value));
> -+      INVALIDATE_EXPORTSTR (var);
> -+    }
> -+
> -+  return (var);
> -+}
> -+
> -+/* Find a variable in the temporary environment that is named NAME.
> -+   Return the SHELL_VAR *, or NULL if not found. */
> -+SHELL_VAR *
> -+find_tempenv_variable (name)
> -+     const char *name;
> -+{
> -+  return (temporary_env ? hash_lookup (name, temporary_env) : (SHELL_VAR *)NULL);
> -+}
> -+
> -+char **tempvar_list;
> -+int tvlist_ind;
> -+
> -+/* Push the variable described by (SHELL_VAR *)DATA down to the next
> -+   variable context from the temporary environment. */
> -+static void
> -+push_temp_var (data)
> -+     PTR_T data;
> -+{
> -+  SHELL_VAR *var, *v;
> -+  HASH_TABLE *binding_table;
> -+
> -+  var = (SHELL_VAR *)data;
> -+
> -+  binding_table = shell_variables->table;
> -+  if (binding_table == 0)
> -+    {
> -+      if (shell_variables == global_variables)
> -+	/* shouldn't happen */
> -+	binding_table = shell_variables->table = global_variables->table = hash_create (0);
> -+      else
> -+	binding_table = shell_variables->table = hash_create (TEMPENV_HASH_BUCKETS);
> -+    }
> -+
> -+  v = bind_variable_internal (var->name, value_cell (var), binding_table, 0, 0);
> -+
> -+  /* XXX - should we set the context here?  It shouldn't matter because of how
> -+     assign_in_env works, but might want to check. */
> -+  if (binding_table == global_variables->table)		/* XXX */
> -+    var->attributes &= ~(att_tempvar|att_propagate);
> -+  else
> -+    {
> -+      var->attributes |= att_propagate;
> -+      if  (binding_table == shell_variables->table)
> -+	shell_variables->flags |= VC_HASTMPVAR;
> -+    }
> -+  v->attributes |= var->attributes;
> -+
> -+  if (find_special_var (var->name) >= 0)
> -+    tempvar_list[tvlist_ind++] = savestring (var->name);
> -+
> -+  dispose_variable (var);
> -+}
> -+
> -+static void
> -+propagate_temp_var (data)
> -+     PTR_T data;
> -+{
> -+  SHELL_VAR *var;
> -+
> -+  var = (SHELL_VAR *)data;
> -+  if (tempvar_p (var) && (var->attributes & att_propagate))
> -+    push_temp_var (data);
> -+  else
> -+    {
> -+      if (find_special_var (var->name) >= 0)
> -+	tempvar_list[tvlist_ind++] = savestring (var->name);
> -+      dispose_variable (var);
> -+    }
> -+}
> -+
> -+/* Free the storage used in the hash table for temporary
> -+   environment variables.  PUSHF is a function to be called
> -+   to free each hash table entry.  It takes care of pushing variables
> -+   to previous scopes if appropriate.  PUSHF stores names of variables
> -+   that require special handling (e.g., IFS) on tempvar_list, so this
> -+   function can call stupidly_hack_special_variables on all the
> -+   variables in the list when the temporary hash table is destroyed. */
> -+static void
> -+dispose_temporary_env (pushf)
> -+     sh_free_func_t *pushf;
> -+{
> -+  int i;
> -+
> -+  tempvar_list = strvec_create (HASH_ENTRIES (temporary_env) + 1);
> -+  tempvar_list[tvlist_ind = 0] = 0;
> -+    
> -+  hash_flush (temporary_env, pushf);
> -+  hash_dispose (temporary_env);
> -+  temporary_env = (HASH_TABLE *)NULL;
> -+
> -+  tempvar_list[tvlist_ind] = 0;
> -+
> -+  array_needs_making = 1;
> -+
> -+#if 0
> -+  sv_ifs ("IFS");		/* XXX here for now -- check setifs in assign_in_env */  
> -+#endif
> -+  for (i = 0; i < tvlist_ind; i++)
> -+    stupidly_hack_special_variables (tempvar_list[i]);
> -+
> -+  strvec_dispose (tempvar_list);
> -+  tempvar_list = 0;
> -+  tvlist_ind = 0;
> -+}
> -+
> -+void
> -+dispose_used_env_vars ()
> -+{
> -+  if (temporary_env)
> -+    {
> -+      dispose_temporary_env (propagate_temp_var);
> -+      maybe_make_export_env ();
> -+    }
> -+}
> -+
> -+/* Take all of the shell variables in the temporary environment HASH_TABLE
> -+   and make shell variables from them at the current variable context. */
> -+void
> -+merge_temporary_env ()
> -+{
> -+  if (temporary_env)
> -+    dispose_temporary_env (push_temp_var);
> -+}
> -+
> -+/* **************************************************************** */
> -+/*								    */
> -+/*	     Creating and manipulating the environment		    */
> -+/*								    */
> -+/* **************************************************************** */
> -+
> -+static inline char *
> -+mk_env_string (name, value, isfunc)
> -+     const char *name, *value;
> -+     int isfunc;
> -+{
> -+  size_t name_len, value_len;
> -+  char	*p, *q;
> -+
> -+  name_len = strlen (name);
> -+  value_len = STRLEN (value);
> -+
> -+  /* If we are exporting a shell function, construct the encoded function
> -+     name. */
> -+  if (isfunc && value)
> -+    {
> -+      p = (char *)xmalloc (BASHFUNC_PREFLEN + name_len + BASHFUNC_SUFFLEN + value_len + 2);
> -+      q = p;
> -+      memcpy (q, BASHFUNC_PREFIX, BASHFUNC_PREFLEN);
> -+      q += BASHFUNC_PREFLEN;
> -+      memcpy (q, name, name_len);
> -+      q += name_len;
> -+      memcpy (q, BASHFUNC_SUFFIX, BASHFUNC_SUFFLEN);
> -+      q += BASHFUNC_SUFFLEN;
> -+    }
> -+  else
> -+    {
> -+      p = (char *)xmalloc (2 + name_len + value_len);
> -+      memcpy (p, name, name_len);
> -+      q = p + name_len;
> -+    }
> -+
> -+  q[0] = '=';
> -+  if (value && *value)
> -+    memcpy (q + 1, value, value_len + 1);
> -+  else
> -+    q[1] = '\0';
> -+
> -+  return (p);
> -+}
> -+
> -+#ifdef DEBUG
> -+/* Debugging */
> -+static int
> -+valid_exportstr (v)
> -+     SHELL_VAR *v;
> -+{
> -+  char *s;
> -+
> -+  s = v->exportstr;
> -+  if (s == 0)
> -+    {
> -+      internal_error (_("%s has null exportstr"), v->name);
> -+      return (0);
> -+    }
> -+  if (legal_variable_starter ((unsigned char)*s) == 0)
> -+    {
> -+      internal_error (_("invalid character %d in exportstr for %s"), *s, v->name);
> -+      return (0);
> -+    }
> -+  for (s = v->exportstr + 1; s && *s; s++)
> -+    {
> -+      if (*s == '=')
> -+	break;
> -+      if (legal_variable_char ((unsigned char)*s) == 0)
> -+	{
> -+	  internal_error (_("invalid character %d in exportstr for %s"), *s, v->name);
> -+	  return (0);
> -+	}
> -+    }
> -+  if (*s != '=')
> -+    {
> -+      internal_error (_("no `=' in exportstr for %s"), v->name);
> -+      return (0);
> -+    }
> -+  return (1);
> -+}
> -+#endif
> -+
> -+static char **
> -+make_env_array_from_var_list (vars)
> -+     SHELL_VAR **vars;
> -+{
> -+  register int i, list_index;
> -+  register SHELL_VAR *var;
> -+  char **list, *value;
> -+
> -+  list = strvec_create ((1 + strvec_len ((char **)vars)));
> -+
> -+#define USE_EXPORTSTR (value == var->exportstr)
> -+
> -+  for (i = 0, list_index = 0; var = vars[i]; i++)
> -+    {
> -+#if defined (__CYGWIN__)
> -+      /* We don't use the exportstr stuff on Cygwin at all. */
> -+      INVALIDATE_EXPORTSTR (var);
> -+#endif
> -+      if (var->exportstr)
> -+	value = var->exportstr;
> -+      else if (function_p (var))
> -+	value = named_function_string ((char *)NULL, function_cell (var), 0);
> -+#if defined (ARRAY_VARS)
> -+      else if (array_p (var))
> -+#  if ARRAY_EXPORT
> -+	value = array_to_assignment_string (array_cell (var));
> -+#  else
> -+	continue;	/* XXX array vars cannot yet be exported */
> -+#  endif /* ARRAY_EXPORT */
> -+      else if (assoc_p (var))
> -+#  if 0
> -+	value = assoc_to_assignment_string (assoc_cell (var));
> -+#  else
> -+	continue;	/* XXX associative array vars cannot yet be exported */
> -+#  endif
> -+#endif
> -+      else
> -+	value = value_cell (var);
> -+
> -+      if (value)
> -+	{
> -+	  /* Gee, I'd like to get away with not using savestring() if we're
> -+	     using the cached exportstr... */
> -+	  list[list_index] = USE_EXPORTSTR ? savestring (value)
> -+					   : mk_env_string (var->name, value, function_p (var));
> -+
> -+	  if (USE_EXPORTSTR == 0)
> -+	    SAVE_EXPORTSTR (var, list[list_index]);
> -+
> -+	  list_index++;
> -+#undef USE_EXPORTSTR
> -+
> -+#if 0	/* not yet */
> -+#if defined (ARRAY_VARS)
> -+	  if (array_p (var) || assoc_p (var))
> -+	    free (value);
> -+#endif
> -+#endif
> -+	}
> -+    }
> -+
> -+  list[list_index] = (char *)NULL;
> -+  return (list);
> -+}
> -+
> -+/* Make an array of assignment statements from the hash table
> -+   HASHED_VARS which contains SHELL_VARs.  Only visible, exported
> -+   variables are eligible. */
> -+static char **
> -+make_var_export_array (vcxt)
> -+     VAR_CONTEXT *vcxt;
> -+{
> -+  char **list;
> -+  SHELL_VAR **vars;
> -+
> -+#if 0
> -+  vars = map_over (visible_and_exported, vcxt);
> -+#else
> -+  vars = map_over (export_environment_candidate, vcxt);
> -+#endif
> -+
> -+  if (vars == 0)
> -+    return (char **)NULL;
> -+
> -+  list = make_env_array_from_var_list (vars);
> -+
> -+  free (vars);
> -+  return (list);
> -+}
> -+
> -+static char **
> -+make_func_export_array ()
> -+{
> -+  char **list;
> -+  SHELL_VAR **vars;
> -+
> -+  vars = map_over_funcs (visible_and_exported);
> -+  if (vars == 0)
> -+    return (char **)NULL;
> -+
> -+  list = make_env_array_from_var_list (vars);
> -+
> -+  free (vars);
> -+  return (list);
> -+}
> -+
> -+/* Add ENVSTR to the end of the exported environment, EXPORT_ENV. */
> -+#define add_to_export_env(envstr,do_alloc) \
> -+do \
> -+  { \
> -+    if (export_env_index >= (export_env_size - 1)) \
> -+      { \
> -+	export_env_size += 16; \
> -+	export_env = strvec_resize (export_env, export_env_size); \
> -+	environ = export_env; \
> -+      } \
> -+    export_env[export_env_index++] = (do_alloc) ? savestring (envstr) : envstr; \
> -+    export_env[export_env_index] = (char *)NULL; \
> -+  } while (0)
> -+
> -+/* Add ASSIGN to EXPORT_ENV, or supercede a previous assignment in the
> -+   array with the same left-hand side.  Return the new EXPORT_ENV. */
> -+char **
> -+add_or_supercede_exported_var (assign, do_alloc)
> -+     char *assign;
> -+     int do_alloc;
> -+{
> -+  register int i;
> -+  int equal_offset;
> -+
> -+  equal_offset = assignment (assign, 0);
> -+  if (equal_offset == 0)
> -+    return (export_env);
> -+
> -+  /* If this is a function, then only supersede the function definition.
> -+     We do this by including the `=() {' in the comparison, like
> -+     initialize_shell_variables does. */
> -+  if (assign[equal_offset + 1] == '(' &&
> -+     strncmp (assign + equal_offset + 2, ") {", 3) == 0)		/* } */
> -+    equal_offset += 4;
> -+
> -+  for (i = 0; i < export_env_index; i++)
> -+    {
> -+      if (STREQN (assign, export_env[i], equal_offset + 1))
> -+	{
> -+	  free (export_env[i]);
> -+	  export_env[i] = do_alloc ? savestring (assign) : assign;
> -+	  return (export_env);
> -+	}
> -+    }
> -+  add_to_export_env (assign, do_alloc);
> -+  return (export_env);
> -+}
> -+
> -+static void
> -+add_temp_array_to_env (temp_array, do_alloc, do_supercede)
> -+     char **temp_array;
> -+     int do_alloc, do_supercede;
> -+{
> -+  register int i;
> -+
> -+  if (temp_array == 0)
> -+    return;
> -+
> -+  for (i = 0; temp_array[i]; i++)
> -+    {
> -+      if (do_supercede)
> -+	export_env = add_or_supercede_exported_var (temp_array[i], do_alloc);
> -+      else
> -+	add_to_export_env (temp_array[i], do_alloc);
> -+    }
> -+
> -+  free (temp_array);
> -+}
> -+
> -+/* Make the environment array for the command about to be executed, if the
> -+   array needs making.  Otherwise, do nothing.  If a shell action could
> -+   change the array that commands receive for their environment, then the
> -+   code should `array_needs_making++'.
> -+
> -+   The order to add to the array is:
> -+   	temporary_env
> -+   	list of var contexts whose head is shell_variables
> -+  	shell_functions
> -+
> -+  This is the shell variable lookup order.  We add only new variable
> -+  names at each step, which allows local variables and variables in
> -+  the temporary environments to shadow variables in the global (or
> -+  any previous) scope.
> -+*/
> -+
> -+static int
> -+n_shell_variables ()
> -+{
> -+  VAR_CONTEXT *vc;
> -+  int n;
> -+
> -+  for (n = 0, vc = shell_variables; vc; vc = vc->down)
> -+    n += HASH_ENTRIES (vc->table);
> -+  return n;
> -+}
> -+
> -+int
> -+chkexport (name)
> -+     char *name;
> -+{
> -+  SHELL_VAR *v;
> -+
> -+  v = find_variable (name);
> -+  if (v && exported_p (v))
> -+    {
> -+      array_needs_making = 1;
> -+      maybe_make_export_env ();
> -+      return 1;
> -+    }
> -+  return 0;
> -+}
> -+
> -+void
> -+maybe_make_export_env ()
> -+{
> -+  register char **temp_array;
> -+  int new_size;
> -+  VAR_CONTEXT *tcxt;
> -+
> -+  if (array_needs_making)
> -+    {
> -+      if (export_env)
> -+	strvec_flush (export_env);
> -+
> -+      /* Make a guess based on how many shell variables and functions we
> -+	 have.  Since there will always be array variables, and array
> -+	 variables are not (yet) exported, this will always be big enough
> -+	 for the exported variables and functions. */
> -+      new_size = n_shell_variables () + HASH_ENTRIES (shell_functions) + 1 +
> -+		 HASH_ENTRIES (temporary_env);
> -+      if (new_size > export_env_size)
> -+	{
> -+	  export_env_size = new_size;
> -+	  export_env = strvec_resize (export_env, export_env_size);
> -+	  environ = export_env;
> -+	}
> -+      export_env[export_env_index = 0] = (char *)NULL;
> -+
> -+      /* Make a dummy variable context from the temporary_env, stick it on
> -+	 the front of shell_variables, call make_var_export_array on the
> -+	 whole thing to flatten it, and convert the list of SHELL_VAR *s
> -+	 to the form needed by the environment. */
> -+      if (temporary_env)
> -+	{
> -+	  tcxt = new_var_context ((char *)NULL, 0);
> -+	  tcxt->table = temporary_env;
> -+	  tcxt->down = shell_variables;
> -+	}
> -+      else
> -+	tcxt = shell_variables;
> -+      
> -+      temp_array = make_var_export_array (tcxt);
> -+      if (temp_array)
> -+	add_temp_array_to_env (temp_array, 0, 0);
> -+
> -+      if (tcxt != shell_variables)
> -+	free (tcxt);
> -+
> -+#if defined (RESTRICTED_SHELL)
> -+      /* Restricted shells may not export shell functions. */
> -+      temp_array = restricted ? (char **)0 : make_func_export_array ();
> -+#else
> -+      temp_array = make_func_export_array ();
> -+#endif
> -+      if (temp_array)
> -+	add_temp_array_to_env (temp_array, 0, 0);
> -+
> -+      array_needs_making = 0;
> -+    }
> -+}
> -+
> -+/* This is an efficiency hack.  PWD and OLDPWD are auto-exported, so
> -+   we will need to remake the exported environment every time we
> -+   change directories.  `_' is always put into the environment for
> -+   every external command, so without special treatment it will always
> -+   cause the environment to be remade.
> -+
> -+   If there is no other reason to make the exported environment, we can
> -+   just update the variables in place and mark the exported environment
> -+   as no longer needing a remake. */
> -+void
> -+update_export_env_inplace (env_prefix, preflen, value)
> -+     char *env_prefix;
> -+     int preflen;
> -+     char *value;
> -+{
> -+  char *evar;
> -+
> -+  evar = (char *)xmalloc (STRLEN (value) + preflen + 1);
> -+  strcpy (evar, env_prefix);
> -+  if (value)
> -+    strcpy (evar + preflen, value);
> -+  export_env = add_or_supercede_exported_var (evar, 0);
> -+}
> -+
> -+/* We always put _ in the environment as the name of this command. */
> -+void
> -+put_command_name_into_env (command_name)
> -+     char *command_name;
> -+{
> -+  update_export_env_inplace ("_=", 2, command_name);
> -+}
> -+
> -+/* **************************************************************** */
> -+/*								    */
> -+/*		      Managing variable contexts		    */
> -+/*								    */
> -+/* **************************************************************** */
> -+
> -+/* Allocate and return a new variable context with NAME and FLAGS.
> -+   NAME can be NULL. */
> -+
> -+VAR_CONTEXT *
> -+new_var_context (name, flags)
> -+     char *name;
> -+     int flags;
> -+{
> -+  VAR_CONTEXT *vc;
> -+
> -+  vc = (VAR_CONTEXT *)xmalloc (sizeof (VAR_CONTEXT));
> -+  vc->name = name ? savestring (name) : (char *)NULL;
> -+  vc->scope = variable_context;
> -+  vc->flags = flags;
> -+
> -+  vc->up = vc->down = (VAR_CONTEXT *)NULL;
> -+  vc->table = (HASH_TABLE *)NULL;
> -+
> -+  return vc;
> -+}
> -+
> -+/* Free a variable context and its data, including the hash table.  Dispose
> -+   all of the variables. */
> -+void
> -+dispose_var_context (vc)
> -+     VAR_CONTEXT *vc;
> -+{
> -+  FREE (vc->name);
> -+
> -+  if (vc->table)
> -+    {
> -+      delete_all_variables (vc->table);
> -+      hash_dispose (vc->table);
> -+    }
> -+
> -+  free (vc);
> -+}
> -+
> -+/* Set VAR's scope level to the current variable context. */
> -+static int
> -+set_context (var)
> -+     SHELL_VAR *var;
> -+{
> -+  return (var->context = variable_context);
> -+}
> -+
> -+/* Make a new variable context with NAME and FLAGS and a HASH_TABLE of
> -+   temporary variables, and push it onto shell_variables.  This is
> -+   for shell functions. */
> -+VAR_CONTEXT *
> -+push_var_context (name, flags, tempvars)
> -+     char *name;
> -+     int flags;
> -+     HASH_TABLE *tempvars;
> -+{
> -+  VAR_CONTEXT *vc;
> -+
> -+  vc = new_var_context (name, flags);
> -+  vc->table = tempvars;
> -+  if (tempvars)
> -+    {
> -+      /* Have to do this because the temp environment was created before
> -+	 variable_context was incremented. */
> -+      flatten (tempvars, set_context, (VARLIST *)NULL, 0);
> -+      vc->flags |= VC_HASTMPVAR;
> -+    }
> -+  vc->down = shell_variables;
> -+  shell_variables->up = vc;
> -+
> -+  return (shell_variables = vc);
> -+}
> -+
> -+static void
> -+push_func_var (data)
> -+     PTR_T data;
> -+{
> -+  SHELL_VAR *var, *v;
> -+
> -+  var = (SHELL_VAR *)data;
> -+
> -+  if (tempvar_p (var) && (posixly_correct || (var->attributes & att_propagate)))
> -+    {
> -+      /* Make sure we have a hash table to store the variable in while it is
> -+	 being propagated down to the global variables table.  Create one if
> -+	 we have to */
> -+      if ((vc_isfuncenv (shell_variables) || vc_istempenv (shell_variables)) && shell_variables->table == 0)
> -+	shell_variables->table = hash_create (0);
> -+      /* XXX - should we set v->context here? */
> -+      v = bind_variable_internal (var->name, value_cell (var), shell_variables->table, 0, 0);
> -+      if (shell_variables == global_variables)
> -+	var->attributes &= ~(att_tempvar|att_propagate);
> -+      else
> -+	shell_variables->flags |= VC_HASTMPVAR;
> -+      v->attributes |= var->attributes;
> -+    }
> -+  else
> -+    stupidly_hack_special_variables (var->name);	/* XXX */
> -+
> -+  dispose_variable (var);
> -+}
> -+
> -+/* Pop the top context off of VCXT and dispose of it, returning the rest of
> -+   the stack. */
> -+void
> -+pop_var_context ()
> -+{
> -+  VAR_CONTEXT *ret, *vcxt;
> -+
> -+  vcxt = shell_variables;
> -+  if (vc_isfuncenv (vcxt) == 0)
> -+    {
> -+      internal_error (_("pop_var_context: head of shell_variables not a function context"));
> -+      return;
> -+    }
> -+
> -+  if (ret = vcxt->down)
> -+    {
> -+      ret->up = (VAR_CONTEXT *)NULL;
> -+      shell_variables = ret;
> -+      if (vcxt->table)
> -+	hash_flush (vcxt->table, push_func_var);
> -+      dispose_var_context (vcxt);
> -+    }
> -+  else
> -+    internal_error (_("pop_var_context: no global_variables context"));
> -+}
> -+
> -+/* Delete the HASH_TABLEs for all variable contexts beginning at VCXT, and
> -+   all of the VAR_CONTEXTs except GLOBAL_VARIABLES. */
> -+void
> -+delete_all_contexts (vcxt)
> -+     VAR_CONTEXT *vcxt;
> -+{
> -+  VAR_CONTEXT *v, *t;
> -+
> -+  for (v = vcxt; v != global_variables; v = t)
> -+    {
> -+      t = v->down;
> -+      dispose_var_context (v);
> -+    }    
> -+
> -+  delete_all_variables (global_variables->table);
> -+  shell_variables = global_variables;
> -+}
> -+
> -+/* **************************************************************** */
> -+/*								    */
> -+/*	   Pushing and Popping temporary variable scopes	    */
> -+/*								    */
> -+/* **************************************************************** */
> -+
> -+VAR_CONTEXT *
> -+push_scope (flags, tmpvars)
> -+     int flags;
> -+     HASH_TABLE *tmpvars;
> -+{
> -+  return (push_var_context ((char *)NULL, flags, tmpvars));
> -+}
> -+
> -+static void
> -+push_exported_var (data)
> -+     PTR_T data;
> -+{
> -+  SHELL_VAR *var, *v;
> -+
> -+  var = (SHELL_VAR *)data;
> -+
> -+  /* If a temp var had its export attribute set, or it's marked to be
> -+     propagated, bind it in the previous scope before disposing it. */
> -+  /* XXX - This isn't exactly right, because all tempenv variables have the
> -+    export attribute set. */
> -+#if 0
> -+  if (exported_p (var) || (var->attributes & att_propagate))
> -+#else
> -+  if (tempvar_p (var) && exported_p (var) && (var->attributes & att_propagate))
> -+#endif
> -+    {
> -+      var->attributes &= ~att_tempvar;		/* XXX */
> -+      v = bind_variable_internal (var->name, value_cell (var), shell_variables->table, 0, 0);
> -+      if (shell_variables == global_variables)
> -+	var->attributes &= ~att_propagate;
> -+      v->attributes |= var->attributes;
> -+    }
> -+  else
> -+    stupidly_hack_special_variables (var->name);	/* XXX */
> -+
> -+  dispose_variable (var);
> -+}
> -+
> -+void
> -+pop_scope (is_special)
> -+     int is_special;
> -+{
> -+  VAR_CONTEXT *vcxt, *ret;
> -+
> -+  vcxt = shell_variables;
> -+  if (vc_istempscope (vcxt) == 0)
> -+    {
> -+      internal_error (_("pop_scope: head of shell_variables not a temporary environment scope"));
> -+      return;
> -+    }
> -+
> -+  ret = vcxt->down;
> -+  if (ret)
> -+    ret->up = (VAR_CONTEXT *)NULL;
> -+
> -+  shell_variables = ret;
> -+
> -+  /* Now we can take care of merging variables in VCXT into set of scopes
> -+     whose head is RET (shell_variables). */
> -+  FREE (vcxt->name);
> -+  if (vcxt->table)
> -+    {
> -+      if (is_special)
> -+	hash_flush (vcxt->table, push_func_var);
> -+      else
> -+	hash_flush (vcxt->table, push_exported_var);
> -+      hash_dispose (vcxt->table);
> -+    }
> -+  free (vcxt);
> -+
> -+  sv_ifs ("IFS");	/* XXX here for now */
> -+}
> -+
> -+/* **************************************************************** */
> -+/*								    */
> -+/*		 Pushing and Popping function contexts		    */
> -+/*								    */
> -+/* **************************************************************** */
> -+
> -+static WORD_LIST **dollar_arg_stack = (WORD_LIST **)NULL;
> -+static int dollar_arg_stack_slots;
> -+static int dollar_arg_stack_index;
> -+
> -+/* XXX - we might want to consider pushing and popping the `getopts' state
> -+   when we modify the positional parameters. */
> -+void
> -+push_context (name, is_subshell, tempvars)
> -+     char *name;	/* function name */
> -+     int is_subshell;
> -+     HASH_TABLE *tempvars;
> -+{
> -+  if (is_subshell == 0)
> -+    push_dollar_vars ();
> -+  variable_context++;
> -+  push_var_context (name, VC_FUNCENV, tempvars);
> -+}
> -+
> -+/* Only called when subshell == 0, so we don't need to check, and can
> -+   unconditionally pop the dollar vars off the stack. */
> -+void
> -+pop_context ()
> -+{
> -+  pop_dollar_vars ();
> -+  variable_context--;
> -+  pop_var_context ();
> -+
> -+  sv_ifs ("IFS");		/* XXX here for now */
> -+}
> -+
> -+/* Save the existing positional parameters on a stack. */
> -+void
> -+push_dollar_vars ()
> -+{
> -+  if (dollar_arg_stack_index + 2 > dollar_arg_stack_slots)
> -+    {
> -+      dollar_arg_stack = (WORD_LIST **)
> -+	xrealloc (dollar_arg_stack, (dollar_arg_stack_slots += 10)
> -+		  * sizeof (WORD_LIST *));
> -+    }
> -+  dollar_arg_stack[dollar_arg_stack_index++] = list_rest_of_args ();
> -+  dollar_arg_stack[dollar_arg_stack_index] = (WORD_LIST *)NULL;
> -+}
> -+
> -+/* Restore the positional parameters from our stack. */
> -+void
> -+pop_dollar_vars ()
> -+{
> -+  if (!dollar_arg_stack || dollar_arg_stack_index == 0)
> -+    return;
> -+
> -+  remember_args (dollar_arg_stack[--dollar_arg_stack_index], 1);
> -+  dispose_words (dollar_arg_stack[dollar_arg_stack_index]);
> -+  dollar_arg_stack[dollar_arg_stack_index] = (WORD_LIST *)NULL;
> -+  set_dollar_vars_unchanged ();
> -+}
> -+
> -+void
> -+dispose_saved_dollar_vars ()
> -+{
> -+  if (!dollar_arg_stack || dollar_arg_stack_index == 0)
> -+    return;
> -+
> -+  dispose_words (dollar_arg_stack[dollar_arg_stack_index]);
> -+  dollar_arg_stack[dollar_arg_stack_index] = (WORD_LIST *)NULL;
> -+}
> -+
> -+/* Manipulate the special BASH_ARGV and BASH_ARGC variables. */
> -+
> -+void
> -+push_args (list)
> -+     WORD_LIST *list;
> -+{
> -+#if defined (ARRAY_VARS) && defined (DEBUGGER)
> -+  SHELL_VAR *bash_argv_v, *bash_argc_v;
> -+  ARRAY *bash_argv_a, *bash_argc_a;
> -+  WORD_LIST *l;
> -+  arrayind_t i;
> -+  char *t;
> -+
> -+  GET_ARRAY_FROM_VAR ("BASH_ARGV", bash_argv_v, bash_argv_a);
> -+  GET_ARRAY_FROM_VAR ("BASH_ARGC", bash_argc_v, bash_argc_a);
> -+
> -+  for (l = list, i = 0; l; l = l->next, i++)
> -+    array_push (bash_argv_a, l->word->word);
> -+
> -+  t = itos (i);
> -+  array_push (bash_argc_a, t);
> -+  free (t);
> -+#endif /* ARRAY_VARS && DEBUGGER */
> -+}
> -+
> -+/* Remove arguments from BASH_ARGV array.  Pop top element off BASH_ARGC
> -+   array and use that value as the count of elements to remove from
> -+   BASH_ARGV. */
> -+void
> -+pop_args ()
> -+{
> -+#if defined (ARRAY_VARS) && defined (DEBUGGER)
> -+  SHELL_VAR *bash_argv_v, *bash_argc_v;
> -+  ARRAY *bash_argv_a, *bash_argc_a;
> -+  ARRAY_ELEMENT *ce;
> -+  intmax_t i;
> -+
> -+  GET_ARRAY_FROM_VAR ("BASH_ARGV", bash_argv_v, bash_argv_a);
> -+  GET_ARRAY_FROM_VAR ("BASH_ARGC", bash_argc_v, bash_argc_a);
> -+
> -+  ce = array_shift (bash_argc_a, 1, 0);
> -+  if (ce == 0 || legal_number (element_value (ce), &i) == 0)
> -+    i = 0;
> -+
> -+  for ( ; i > 0; i--)
> -+    array_pop (bash_argv_a);
> -+  array_dispose_element (ce);
> -+#endif /* ARRAY_VARS && DEBUGGER */
> -+}
> -+
> -+/*************************************************
> -+ *						 *
> -+ *	Functions to manage special variables	 *
> -+ *						 *
> -+ *************************************************/
> -+
> -+/* Extern declarations for variables this code has to manage. */
> -+extern int eof_encountered, eof_encountered_limit, ignoreeof;
> -+
> -+#if defined (READLINE)
> -+extern int hostname_list_initialized;
> -+#endif
> -+
> -+/* An alist of name.function for each special variable.  Most of the
> -+   functions don't do much, and in fact, this would be faster with a
> -+   switch statement, but by the end of this file, I am sick of switch
> -+   statements. */
> -+
> -+#define SET_INT_VAR(name, intvar)  intvar = find_variable (name) != 0
> -+
> -+/* This table will be sorted with qsort() the first time it's accessed. */
> -+struct name_and_function {
> -+  char *name;
> -+  sh_sv_func_t *function;
> -+};
> -+
> -+static struct name_and_function special_vars[] = {
> -+  { "BASH_COMPAT", sv_shcompat },
> -+  { "BASH_XTRACEFD", sv_xtracefd },
> -+
> -+#if defined (JOB_CONTROL)
> -+  { "CHILD_MAX", sv_childmax },
> -+#endif
> -+
> -+#if defined (READLINE)
> -+#  if defined (STRICT_POSIX)
> -+  { "COLUMNS", sv_winsize },
> -+#  endif
> -+  { "COMP_WORDBREAKS", sv_comp_wordbreaks },
> -+#endif
> -+
> -+  { "FUNCNEST", sv_funcnest },
> -+
> -+  { "GLOBIGNORE", sv_globignore },
> -+
> -+#if defined (HISTORY)
> -+  { "HISTCONTROL", sv_history_control },
> -+  { "HISTFILESIZE", sv_histsize },
> -+  { "HISTIGNORE", sv_histignore },
> -+  { "HISTSIZE", sv_histsize },
> -+  { "HISTTIMEFORMAT", sv_histtimefmt },
> -+#endif
> -+
> -+#if defined (__CYGWIN__)
> -+  { "HOME", sv_home },
> -+#endif
> -+
> -+#if defined (READLINE)
> -+  { "HOSTFILE", sv_hostfile },
> -+#endif
> -+
> -+  { "IFS", sv_ifs },
> -+  { "IGNOREEOF", sv_ignoreeof },
> -+
> -+  { "LANG", sv_locale },
> -+  { "LC_ALL", sv_locale },
> -+  { "LC_COLLATE", sv_locale },
> -+  { "LC_CTYPE", sv_locale },
> -+  { "LC_MESSAGES", sv_locale },
> -+  { "LC_NUMERIC", sv_locale },
> -+  { "LC_TIME", sv_locale },
> -+
> -+#if defined (READLINE) && defined (STRICT_POSIX)
> -+  { "LINES", sv_winsize },
> -+#endif
> -+
> -+  { "MAIL", sv_mail },
> -+  { "MAILCHECK", sv_mail },
> -+  { "MAILPATH", sv_mail },
> -+
> -+  { "OPTERR", sv_opterr },
> -+  { "OPTIND", sv_optind },
> -+
> -+  { "PATH", sv_path },
> -+  { "POSIXLY_CORRECT", sv_strict_posix },
> -+
> -+#if defined (READLINE)
> -+  { "TERM", sv_terminal },
> -+  { "TERMCAP", sv_terminal },
> -+  { "TERMINFO", sv_terminal },
> -+#endif /* READLINE */
> -+
> -+  { "TEXTDOMAIN", sv_locale },
> -+  { "TEXTDOMAINDIR", sv_locale },
> -+
> -+#if defined (HAVE_TZSET)
> -+  { "TZ", sv_tz },
> -+#endif
> -+
> -+#if defined (HISTORY) && defined (BANG_HISTORY)
> -+  { "histchars", sv_histchars },
> -+#endif /* HISTORY && BANG_HISTORY */
> -+
> -+  { "ignoreeof", sv_ignoreeof },
> -+
> -+  { (char *)0, (sh_sv_func_t *)0 }
> -+};
> -+
> -+#define N_SPECIAL_VARS	(sizeof (special_vars) / sizeof (special_vars[0]) - 1)
> -+
> -+static int
> -+sv_compare (sv1, sv2)
> -+     struct name_and_function *sv1, *sv2;
> -+{
> -+  int r;
> -+
> -+  if ((r = sv1->name[0] - sv2->name[0]) == 0)
> -+    r = strcmp (sv1->name, sv2->name);
> -+  return r;
> -+}
> -+
> -+static inline int
> -+find_special_var (name)
> -+     const char *name;
> -+{
> -+  register int i, r;
> -+
> -+  for (i = 0; special_vars[i].name; i++)
> -+    {
> -+      r = special_vars[i].name[0] - name[0];
> -+      if (r == 0)
> -+	r = strcmp (special_vars[i].name, name);
> -+      if (r == 0)
> -+	return i;
> -+      else if (r > 0)
> -+	/* Can't match any of rest of elements in sorted list.  Take this out
> -+	   if it causes problems in certain environments. */
> -+	break;
> -+    }
> -+  return -1;
> -+}
> -+
> -+/* The variable in NAME has just had its state changed.  Check to see if it
> -+   is one of the special ones where something special happens. */
> -+void
> -+stupidly_hack_special_variables (name)
> -+     char *name;
> -+{
> -+  static int sv_sorted = 0;
> -+  int i;
> -+
> -+  if (sv_sorted == 0)	/* shouldn't need, but it's fairly cheap. */
> -+    {
> -+      qsort (special_vars, N_SPECIAL_VARS, sizeof (special_vars[0]),
> -+		(QSFUNC *)sv_compare);
> -+      sv_sorted = 1;
> -+    }
> -+
> -+  i = find_special_var (name);
> -+  if (i != -1)
> -+    (*(special_vars[i].function)) (name);
> -+}
> -+
> -+/* Special variables that need hooks to be run when they are unset as part
> -+   of shell reinitialization should have their sv_ functions run here. */
> -+void
> -+reinit_special_variables ()
> -+{
> -+#if defined (READLINE)
> -+  sv_comp_wordbreaks ("COMP_WORDBREAKS");
> -+#endif
> -+  sv_globignore ("GLOBIGNORE");
> -+  sv_opterr ("OPTERR");
> -+}
> -+
> -+void
> -+sv_ifs (name)
> -+     char *name;
> -+{
> -+  SHELL_VAR *v;
> -+
> -+  v = find_variable ("IFS");
> -+  setifs (v);
> -+}
> -+
> -+/* What to do just after the PATH variable has changed. */
> -+void
> -+sv_path (name)
> -+     char *name;
> -+{
> -+  /* hash -r */
> -+  phash_flush ();
> -+}
> -+
> -+/* What to do just after one of the MAILxxxx variables has changed.  NAME
> -+   is the name of the variable.  This is called with NAME set to one of
> -+   MAIL, MAILCHECK, or MAILPATH.  */
> -+void
> -+sv_mail (name)
> -+     char *name;
> -+{
> -+  /* If the time interval for checking the files has changed, then
> -+     reset the mail timer.  Otherwise, one of the pathname vars
> -+     to the users mailbox has changed, so rebuild the array of
> -+     filenames. */
> -+  if (name[4] == 'C')  /* if (strcmp (name, "MAILCHECK") == 0) */
> -+    reset_mail_timer ();
> -+  else
> -+    {
> -+      free_mail_files ();
> -+      remember_mail_dates ();
> -+    }
> -+}
> -+
> -+void
> -+sv_funcnest (name)
> -+     char *name;
> -+{
> -+  SHELL_VAR *v;
> -+  intmax_t num;
> -+
> -+  v = find_variable (name);
> -+  if (v == 0)
> -+    funcnest_max = 0;
> -+  else if (legal_number (value_cell (v), &num) == 0)
> -+    funcnest_max = 0;
> -+  else
> -+    funcnest_max = num;
> -+}
> -+
> -+/* What to do when GLOBIGNORE changes. */
> -+void
> -+sv_globignore (name)
> -+     char *name;
> -+{
> -+  if (privileged_mode == 0)
> -+    setup_glob_ignore (name);
> -+}
> -+
> -+#if defined (READLINE)
> -+void
> -+sv_comp_wordbreaks (name)
> -+     char *name;
> -+{
> -+  SHELL_VAR *sv;
> -+
> -+  sv = find_variable (name);
> -+  if (sv == 0)
> -+    reset_completer_word_break_chars ();
> -+}
> -+
> -+/* What to do just after one of the TERMxxx variables has changed.
> -+   If we are an interactive shell, then try to reset the terminal
> -+   information in readline. */
> -+void
> -+sv_terminal (name)
> -+     char *name;
> -+{
> -+  if (interactive_shell && no_line_editing == 0)
> -+    rl_reset_terminal (get_string_value ("TERM"));
> -+}
> -+
> -+void
> -+sv_hostfile (name)
> -+     char *name;
> -+{
> -+  SHELL_VAR *v;
> -+
> -+  v = find_variable (name);
> -+  if (v == 0)
> -+    clear_hostname_list ();
> -+  else
> -+    hostname_list_initialized = 0;
> -+}
> -+
> -+#if defined (STRICT_POSIX)
> -+/* In strict posix mode, we allow assignments to LINES and COLUMNS (and values
> -+   found in the initial environment) to override the terminal size reported by
> -+   the kernel. */
> -+void
> -+sv_winsize (name)
> -+     char *name;
> -+{
> -+  SHELL_VAR *v;
> -+  intmax_t xd;
> -+  int d;
> -+
> -+  if (posixly_correct == 0 || interactive_shell == 0 || no_line_editing)
> -+    return;
> -+
> -+  v = find_variable (name);
> -+  if (v == 0 || var_isnull (v))
> -+    rl_reset_screen_size ();
> -+  else
> -+    {
> -+      if (legal_number (value_cell (v), &xd) == 0)
> -+	return;
> -+      winsize_assignment = 1;
> -+      d = xd;			/* truncate */
> -+      if (name[0] == 'L')	/* LINES */
> -+	rl_set_screen_size (d, -1);
> -+      else			/* COLUMNS */
> -+	rl_set_screen_size (-1, d);
> -+      winsize_assignment = 0;
> -+    }
> -+}
> -+#endif /* STRICT_POSIX */
> -+#endif /* READLINE */
> -+
> -+/* Update the value of HOME in the export environment so tilde expansion will
> -+   work on cygwin. */
> -+#if defined (__CYGWIN__)
> -+sv_home (name)
> -+     char *name;
> -+{
> -+  array_needs_making = 1;
> -+  maybe_make_export_env ();
> -+}
> -+#endif
> -+
> -+#if defined (HISTORY)
> -+/* What to do after the HISTSIZE or HISTFILESIZE variables change.
> -+   If there is a value for this HISTSIZE (and it is numeric), then stifle
> -+   the history.  Otherwise, if there is NO value for this variable,
> -+   unstifle the history.  If name is HISTFILESIZE, and its value is
> -+   numeric, truncate the history file to hold no more than that many
> -+   lines. */
> -+void
> -+sv_histsize (name)
> -+     char *name;
> -+{
> -+  char *temp;
> -+  intmax_t num;
> -+  int hmax;
> -+
> -+  temp = get_string_value (name);
> -+
> -+  if (temp && *temp)
> -+    {
> -+      if (legal_number (temp, &num))
> -+	{
> -+	  hmax = num;
> -+	  if (hmax < 0 && name[4] == 'S')
> -+	    unstifle_history ();	/* unstifle history if HISTSIZE < 0 */
> -+	  else if (name[4] == 'S')
> -+	    {
> -+	      stifle_history (hmax);
> -+	      hmax = where_history ();
> -+	      if (history_lines_this_session > hmax)
> -+		history_lines_this_session = hmax;
> -+	    }
> -+	  else if (hmax >= 0)	/* truncate HISTFILE if HISTFILESIZE >= 0 */
> -+	    {
> -+	      history_truncate_file (get_string_value ("HISTFILE"), hmax);
> -+	      if (hmax <= history_lines_in_file)
> -+		history_lines_in_file = hmax;
> -+	    }
> -+	}
> -+    }
> -+  else if (name[4] == 'S')
> -+    unstifle_history ();
> -+}
> -+
> -+/* What to do after the HISTIGNORE variable changes. */
> -+void
> -+sv_histignore (name)
> -+     char *name;
> -+{
> -+  setup_history_ignore (name);
> -+}
> -+
> -+/* What to do after the HISTCONTROL variable changes. */
> -+void
> -+sv_history_control (name)
> -+     char *name;
> -+{
> -+  char *temp;
> -+  char *val;
> -+  int tptr;
> -+
> -+  history_control = 0;
> -+  temp = get_string_value (name);
> -+
> -+  if (temp == 0 || *temp == 0)
> -+    return;
> -+
> -+  tptr = 0;
> -+  while (val = extract_colon_unit (temp, &tptr))
> -+    {
> -+      if (STREQ (val, "ignorespace"))
> -+	history_control |= HC_IGNSPACE;
> -+      else if (STREQ (val, "ignoredups"))
> -+	history_control |= HC_IGNDUPS;
> -+      else if (STREQ (val, "ignoreboth"))
> -+	history_control |= HC_IGNBOTH;
> -+      else if (STREQ (val, "erasedups"))
> -+	history_control |= HC_ERASEDUPS;
> -+
> -+      free (val);
> -+    }
> -+}
> -+
> -+#if defined (BANG_HISTORY)
> -+/* Setting/unsetting of the history expansion character. */
> -+void
> -+sv_histchars (name)
> -+     char *name;
> -+{
> -+  char *temp;
> -+
> -+  temp = get_string_value (name);
> -+  if (temp)
> -+    {
> -+      history_expansion_char = *temp;
> -+      if (temp[0] && temp[1])
> -+	{
> -+	  history_subst_char = temp[1];
> -+	  if (temp[2])
> -+	      history_comment_char = temp[2];
> -+	}
> -+    }
> -+  else
> -+    {
> -+      history_expansion_char = '!';
> -+      history_subst_char = '^';
> -+      history_comment_char = '#';
> -+    }
> -+}
> -+#endif /* BANG_HISTORY */
> -+
> -+void
> -+sv_histtimefmt (name)
> -+     char *name;
> -+{
> -+  SHELL_VAR *v;
> -+
> -+  if (v = find_variable (name))
> -+    {
> -+      if (history_comment_char == 0)
> -+	history_comment_char = '#';
> -+    }
> -+  history_write_timestamps = (v != 0);
> -+}
> -+#endif /* HISTORY */
> -+
> -+#if defined (HAVE_TZSET)
> -+void
> -+sv_tz (name)
> -+     char *name;
> -+{
> -+  if (chkexport (name))
> -+    tzset ();
> -+}
> -+#endif
> -+
> -+/* If the variable exists, then the value of it can be the number
> -+   of times we actually ignore the EOF.  The default is small,
> -+   (smaller than csh, anyway). */
> -+void
> -+sv_ignoreeof (name)
> -+     char *name;
> -+{
> -+  SHELL_VAR *tmp_var;
> -+  char *temp;
> -+
> -+  eof_encountered = 0;
> -+
> -+  tmp_var = find_variable (name);
> -+  ignoreeof = tmp_var != 0;
> -+  temp = tmp_var ? value_cell (tmp_var) : (char *)NULL;
> -+  if (temp)
> -+    eof_encountered_limit = (*temp && all_digits (temp)) ? atoi (temp) : 10;
> -+  set_shellopts ();	/* make sure `ignoreeof' is/is not in $SHELLOPTS */
> -+}
> -+
> -+void
> -+sv_optind (name)
> -+     char *name;
> -+{
> -+  char *tt;
> -+  int s;
> -+
> -+  tt = get_string_value ("OPTIND");
> -+  if (tt && *tt)
> -+    {
> -+      s = atoi (tt);
> -+
> -+      /* According to POSIX, setting OPTIND=1 resets the internal state
> -+	 of getopt (). */
> -+      if (s < 0 || s == 1)
> -+	s = 0;
> -+    }
> -+  else
> -+    s = 0;
> -+  getopts_reset (s);
> -+}
> -+
> -+void
> -+sv_opterr (name)
> -+     char *name;
> -+{
> -+  char *tt;
> -+
> -+  tt = get_string_value ("OPTERR");
> -+  sh_opterr = (tt && *tt) ? atoi (tt) : 1;
> -+}
> -+
> -+void
> -+sv_strict_posix (name)
> -+     char *name;
> -+{
> -+  SET_INT_VAR (name, posixly_correct);
> -+  posix_initialize (posixly_correct);
> -+#if defined (READLINE)
> -+  if (interactive_shell)
> -+    posix_readline_initialize (posixly_correct);
> -+#endif /* READLINE */
> -+  set_shellopts ();	/* make sure `posix' is/is not in $SHELLOPTS */
> -+}
> -+
> -+void
> -+sv_locale (name)
> -+     char *name;
> -+{
> -+  char *v;
> -+  int r;
> -+
> -+  v = get_string_value (name);
> -+  if (name[0] == 'L' && name[1] == 'A')	/* LANG */
> -+    r = set_lang (name, v);
> -+  else
> -+    r = set_locale_var (name, v);		/* LC_*, TEXTDOMAIN* */
> -+
> -+#if 1
> -+  if (r == 0 && posixly_correct)
> -+    last_command_exit_value = 1;
> -+#endif
> -+}
> -+
> -+#if defined (ARRAY_VARS)
> -+void
> -+set_pipestatus_array (ps, nproc)
> -+     int *ps;
> -+     int nproc;
> -+{
> -+  SHELL_VAR *v;
> -+  ARRAY *a;
> -+  ARRAY_ELEMENT *ae;
> -+  register int i;
> -+  char *t, tbuf[INT_STRLEN_BOUND(int) + 1];
> -+
> -+  v = find_variable ("PIPESTATUS");
> -+  if (v == 0)
> -+    v = make_new_array_variable ("PIPESTATUS");
> -+  if (array_p (v) == 0)
> -+    return;		/* Do nothing if not an array variable. */
> -+  a = array_cell (v);
> -+
> -+  if (a == 0 || array_num_elements (a) == 0)
> -+    {
> -+      for (i = 0; i < nproc; i++)	/* was ps[i] != -1, not i < nproc */
> -+	{
> -+	  t = inttostr (ps[i], tbuf, sizeof (tbuf));
> -+	  array_insert (a, i, t);
> -+	}
> -+      return;
> -+    }
> -+
> -+  /* Fast case */
> -+  if (array_num_elements (a) == nproc && nproc == 1)
> -+    {
> -+      ae = element_forw (a->head);
> -+      free (element_value (ae));
> -+      ae->value = itos (ps[0]);
> -+    }
> -+  else if (array_num_elements (a) <= nproc)
> -+    {
> -+      /* modify in array_num_elements members in place, then add */
> -+      ae = a->head;
> -+      for (i = 0; i < array_num_elements (a); i++)
> -+	{
> -+	  ae = element_forw (ae);
> -+	  free (element_value (ae));
> -+	  ae->value = itos (ps[i]);
> -+	}
> -+      /* add any more */
> -+      for ( ; i < nproc; i++)
> -+	{
> -+	  t = inttostr (ps[i], tbuf, sizeof (tbuf));
> -+	  array_insert (a, i, t);
> -+	}
> -+    }
> -+  else
> -+    {
> -+      /* deleting elements.  it's faster to rebuild the array. */	  
> -+      array_flush (a);
> -+      for (i = 0; ps[i] != -1; i++)
> -+	{
> -+	  t = inttostr (ps[i], tbuf, sizeof (tbuf));
> -+	  array_insert (a, i, t);
> -+	}
> -+    }
> -+}
> -+
> -+ARRAY *
> -+save_pipestatus_array ()
> -+{
> -+  SHELL_VAR *v;
> -+  ARRAY *a, *a2;
> -+
> -+  v = find_variable ("PIPESTATUS");
> -+  if (v == 0 || array_p (v) == 0 || array_cell (v) == 0)
> -+    return ((ARRAY *)NULL);
> -+    
> -+  a = array_cell (v);
> -+  a2 = array_copy (array_cell (v));
> -+
> -+  return a2;
> -+}
> -+
> -+void
> -+restore_pipestatus_array (a)
> -+     ARRAY *a;
> -+{
> -+  SHELL_VAR *v;
> -+  ARRAY *a2;
> -+
> -+  v = find_variable ("PIPESTATUS");
> -+  /* XXX - should we still assign even if existing value is NULL? */
> -+  if (v == 0 || array_p (v) == 0 || array_cell (v) == 0)
> -+    return;
> -+
> -+  a2 = array_cell (v);
> -+  var_setarray (v, a); 
> -+
> -+  array_dispose (a2);
> -+}
> -+#endif
> -+
> -+void
> -+set_pipestatus_from_exit (s)
> -+     int s;
> -+{
> -+#if defined (ARRAY_VARS)
> -+  static int v[2] = { 0, -1 };
> -+
> -+  v[0] = s;
> -+  set_pipestatus_array (v, 1);
> -+#endif
> -+}
> -+
> -+void
> -+sv_xtracefd (name)
> -+     char *name;
> -+{
> -+  SHELL_VAR *v;
> -+  char *t, *e;
> -+  int fd;
> -+  FILE *fp;
> -+
> -+  v = find_variable (name);
> -+  if (v == 0)
> -+    {
> -+      xtrace_reset ();
> -+      return;
> -+    }
> -+
> -+  t = value_cell (v);
> -+  if (t == 0 || *t == 0)
> -+    xtrace_reset ();
> -+  else
> -+    {
> -+      fd = (int)strtol (t, &e, 10);
> -+      if (e != t && *e == '\0' && sh_validfd (fd))
> -+	{
> -+	  fp = fdopen (fd, "w");
> -+	  if (fp == 0)
> -+	    internal_error (_("%s: %s: cannot open as FILE"), name, value_cell (v));
> -+	  else
> -+	    xtrace_set (fd, fp);
> -+	}
> -+      else
> -+	internal_error (_("%s: %s: invalid value for trace file descriptor"), name, value_cell (v));
> -+    }
> -+}
> -+
> -+#define MIN_COMPAT_LEVEL 31
> -+
> -+void
> -+sv_shcompat (name)
> -+     char *name;
> -+{
> -+  SHELL_VAR *v;
> -+  char *val;
> -+  int tens, ones, compatval;
> -+
> -+  v = find_variable (name);
> -+  if (v == 0)
> -+    {
> -+      shell_compatibility_level = DEFAULT_COMPAT_LEVEL;
> -+      set_compatibility_opts ();
> -+      return;
> -+    }
> -+  val = value_cell (v);
> -+  if (val == 0 || *val == '\0')
> -+    {
> -+      shell_compatibility_level = DEFAULT_COMPAT_LEVEL;
> -+      set_compatibility_opts ();
> -+      return;
> -+    }
> -+  /* Handle decimal-like compatibility version specifications: 4.2 */
> -+  if (isdigit (val[0]) && val[1] == '.' && isdigit (val[2]) && val[3] == 0)
> -+    {
> -+      tens = val[0] - '0';
> -+      ones = val[2] - '0';
> -+      compatval = tens*10 + ones;
> -+    }
> -+  /* Handle integer-like compatibility version specifications: 42 */
> -+  else if (isdigit (val[0]) && isdigit (val[1]) && val[2] == 0)
> -+    {
> -+      tens = val[0] - '0';
> -+      ones = val[1] - '0';
> -+      compatval = tens*10 + ones;
> -+    }
> -+  else
> -+    {
> -+compat_error:
> -+      internal_error (_("%s: %s: compatibility value out of range"), name, val);
> -+      shell_compatibility_level = DEFAULT_COMPAT_LEVEL;
> -+      set_compatibility_opts ();
> -+      return;
> -+    }
> -+
> -+  if (compatval < MIN_COMPAT_LEVEL || compatval > DEFAULT_COMPAT_LEVEL)
> -+    goto compat_error;
> -+
> -+  shell_compatibility_level = compatval;
> -+  set_compatibility_opts ();
> -+}
> -+
> -+#if defined (JOB_CONTROL)
> -+void
> -+sv_childmax (name)
> -+     char *name;
> -+{
> -+  char *tt;
> -+  int s;
> -+
> -+  tt = get_string_value (name);
> -+  s = (tt && *tt) ? atoi (tt) : 0;
> -+  set_maxchild (s);
> -+}
> -+#endif
> diff --git a/patches/bash-4.3.30/0002-Bash-4.3-patch-32.patch b/patches/bash-4.3.30/0002-Bash-4.3-patch-32.patch
> deleted file mode 100644
> index 801b4a609411..000000000000
> --- a/patches/bash-4.3.30/0002-Bash-4.3-patch-32.patch
> +++ /dev/null
> @@ -1,5409 +0,0 @@
> -From: Chet Ramey <chet.ramey@case.edu>
> -Date: Thu, 15 Jan 2015 10:20:45 -0500
> -Subject: [PATCH] Bash-4.3 patch 32
> -
> ----
> - jobs.c           |    4 +-
> - patchlevel.h     |    2 +-
> - variables.c.orig | 5365 ------------------------------------------------------
> - 3 files changed, 4 insertions(+), 5367 deletions(-)
> - delete mode 100644 variables.c.orig
> -
> -diff --git a/jobs.c b/jobs.c
> -index f38b0c3f4446..b6e59eba0de8 100644
> ---- a/jobs.c
> -+++ b/jobs.c
> -@@ -3339,7 +3339,9 @@ itrace("waitchld: waitpid returns %d block = %d", pid, block);
> -       if (posixly_correct && this_shell_builtin && this_shell_builtin == wait_builtin)
> - 	{
> - 	  interrupt_immediately = 0;
> --	  trap_handler (SIGCHLD);	/* set pending_traps[SIGCHLD] */
> -+	  /* This was trap_handler (SIGCHLD) but that can lose traps if
> -+	     children_exited > 1 */
> -+	  queue_sigchld_trap (children_exited);
> - 	  wait_signal_received = SIGCHLD;
> - 	  /* If we're in a signal handler, let CHECK_WAIT_INTR pick it up;
> - 	     run_pending_traps will call run_sigchld_trap later  */
> -diff --git a/patchlevel.h b/patchlevel.h
> -index 0ad46aafbdd9..b8bf38704ed2 100644
> ---- a/patchlevel.h
> -+++ b/patchlevel.h
> -@@ -25,6 +25,6 @@
> -    regexp `^#define[ 	]*PATCHLEVEL', since that's what support/mkversion.sh
> -    looks for to find the patch level (for the sccs version string). */
> - 
> --#define PATCHLEVEL 31
> -+#define PATCHLEVEL 32
> - 
> - #endif /* _PATCHLEVEL_H_ */
> -diff --git a/variables.c.orig b/variables.c.orig
> -deleted file mode 100644
> -index 7c82710e0f0b..000000000000
> ---- a/variables.c.orig
> -+++ /dev/null
> -@@ -1,5365 +0,0 @@
> --/* variables.c -- Functions for hacking shell variables. */
> --
> --/* Copyright (C) 1987-2013 Free Software Foundation, Inc.
> --
> --   This file is part of GNU Bash, the Bourne Again SHell.
> --
> --   Bash is free software: you can redistribute it and/or modify
> --   it under the terms of the GNU General Public License as published by
> --   the Free Software Foundation, either version 3 of the License, or
> --   (at your option) any later version.
> --
> --   Bash is distributed in the hope that it will be useful,
> --   but WITHOUT ANY WARRANTY; without even the implied warranty of
> --   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
> --   GNU General Public License for more details.
> --
> --   You should have received a copy of the GNU General Public License
> --   along with Bash.  If not, see <http://www.gnu.org/licenses/>.
> --*/
> --
> --#include "config.h"
> --
> --#include "bashtypes.h"
> --#include "posixstat.h"
> --#include "posixtime.h"
> --
> --#if defined (__QNX__)
> --#  if defined (__QNXNTO__)
> --#    include <sys/netmgr.h>
> --#  else
> --#    include <sys/vc.h>
> --#  endif /* !__QNXNTO__ */
> --#endif /* __QNX__ */
> --
> --#if defined (HAVE_UNISTD_H)
> --#  include <unistd.h>
> --#endif
> --
> --#include <stdio.h>
> --#include "chartypes.h"
> --#if defined (HAVE_PWD_H)
> --#  include <pwd.h>
> --#endif
> --#include "bashansi.h"
> --#include "bashintl.h"
> --
> --#define NEED_XTRACE_SET_DECL
> --
> --#include "shell.h"
> --#include "flags.h"
> --#include "execute_cmd.h"
> --#include "findcmd.h"
> --#include "mailcheck.h"
> --#include "input.h"
> --#include "hashcmd.h"
> --#include "pathexp.h"
> --#include "alias.h"
> --#include "jobs.h"
> --
> --#include "version.h"
> --
> --#include "builtins/getopt.h"
> --#include "builtins/common.h"
> --#include "builtins/builtext.h"
> --
> --#if defined (READLINE)
> --#  include "bashline.h"
> --#  include <readline/readline.h>
> --#else
> --#  include <tilde/tilde.h>
> --#endif
> --
> --#if defined (HISTORY)
> --#  include "bashhist.h"
> --#  include <readline/history.h>
> --#endif /* HISTORY */
> --
> --#if defined (PROGRAMMABLE_COMPLETION)
> --#  include "pcomplete.h"
> --#endif
> --
> --#define TEMPENV_HASH_BUCKETS	4	/* must be power of two */
> --
> --#define ifsname(s)	((s)[0] == 'I' && (s)[1] == 'F' && (s)[2] == 'S' && (s)[3] == '\0')
> --
> --#define BASHFUNC_PREFIX		"BASH_FUNC_"
> --#define BASHFUNC_PREFLEN	10	/* == strlen(BASHFUNC_PREFIX */
> --#define BASHFUNC_SUFFIX		"%%"
> --#define BASHFUNC_SUFFLEN	2	/* == strlen(BASHFUNC_SUFFIX) */
> --
> --extern char **environ;
> --
> --/* Variables used here and defined in other files. */
> --extern int posixly_correct;
> --extern int line_number, line_number_base;
> --extern int subshell_environment, indirection_level, subshell_level;
> --extern int build_version, patch_level;
> --extern int expanding_redir;
> --extern int last_command_exit_value;
> --extern char *dist_version, *release_status;
> --extern char *shell_name;
> --extern char *primary_prompt, *secondary_prompt;
> --extern char *current_host_name;
> --extern sh_builtin_func_t *this_shell_builtin;
> --extern SHELL_VAR *this_shell_function;
> --extern char *the_printed_command_except_trap;
> --extern char *this_command_name;
> --extern char *command_execution_string;
> --extern time_t shell_start_time;
> --extern int assigning_in_environment;
> --extern int executing_builtin;
> --extern int funcnest_max;
> --
> --#if defined (READLINE)
> --extern int no_line_editing;
> --extern int perform_hostname_completion;
> --#endif
> --
> --/* The list of shell variables that the user has created at the global
> --   scope, or that came from the environment. */
> --VAR_CONTEXT *global_variables = (VAR_CONTEXT *)NULL;
> --
> --/* The current list of shell variables, including function scopes */
> --VAR_CONTEXT *shell_variables = (VAR_CONTEXT *)NULL;
> --
> --/* The list of shell functions that the user has created, or that came from
> --   the environment. */
> --HASH_TABLE *shell_functions = (HASH_TABLE *)NULL;
> --
> --#if defined (DEBUGGER)
> --/* The table of shell function definitions that the user defined or that
> --   came from the environment. */
> --HASH_TABLE *shell_function_defs = (HASH_TABLE *)NULL;
> --#endif
> --
> --/* The current variable context.  This is really a count of how deep into
> --   executing functions we are. */
> --int variable_context = 0;
> --
> --/* The set of shell assignments which are made only in the environment
> --   for a single command. */
> --HASH_TABLE *temporary_env = (HASH_TABLE *)NULL;
> --
> --/* Set to non-zero if an assignment error occurs while putting variables
> --   into the temporary environment. */
> --int tempenv_assign_error;
> --
> --/* Some funky variables which are known about specially.  Here is where
> --   "$*", "$1", and all the cruft is kept. */
> --char *dollar_vars[10];
> --WORD_LIST *rest_of_args = (WORD_LIST *)NULL;
> --
> --/* The value of $$. */
> --pid_t dollar_dollar_pid;
> --
> --/* Non-zero means that we have to remake EXPORT_ENV. */
> --int array_needs_making = 1;
> --
> --/* The number of times BASH has been executed.  This is set
> --   by initialize_variables (). */
> --int shell_level = 0;
> --
> --/* An array which is passed to commands as their environment.  It is
> --   manufactured from the union of the initial environment and the
> --   shell variables that are marked for export. */
> --char **export_env = (char **)NULL;
> --static int export_env_index;
> --static int export_env_size;
> --
> --#if defined (READLINE)
> --static int winsize_assignment;		/* currently assigning to LINES or COLUMNS */
> --#endif
> --
> --static HASH_TABLE *last_table_searched;	/* hash_lookup sets this */
> --
> --/* Some forward declarations. */
> --static void create_variable_tables __P((void));
> --
> --static void set_machine_vars __P((void));
> --static void set_home_var __P((void));
> --static void set_shell_var __P((void));
> --static char *get_bash_name __P((void));
> --static void initialize_shell_level __P((void));
> --static void uidset __P((void));
> --#if defined (ARRAY_VARS)
> --static void make_vers_array __P((void));
> --#endif
> --
> --static SHELL_VAR *null_assign __P((SHELL_VAR *, char *, arrayind_t, char *));
> --#if defined (ARRAY_VARS)
> --static SHELL_VAR *null_array_assign __P((SHELL_VAR *, char *, arrayind_t, char *));
> --#endif
> --static SHELL_VAR *get_self __P((SHELL_VAR *));
> --
> --#if defined (ARRAY_VARS)
> --static SHELL_VAR *init_dynamic_array_var __P((char *, sh_var_value_func_t *, sh_var_assign_func_t *, int));
> --static SHELL_VAR *init_dynamic_assoc_var __P((char *, sh_var_value_func_t *, sh_var_assign_func_t *, int));
> --#endif
> --
> --static SHELL_VAR *assign_seconds __P((SHELL_VAR *, char *, arrayind_t, char *));
> --static SHELL_VAR *get_seconds __P((SHELL_VAR *));
> --static SHELL_VAR *init_seconds_var __P((void));
> --
> --static int brand __P((void));
> --static void sbrand __P((unsigned long));		/* set bash random number generator. */
> --static void seedrand __P((void));			/* seed generator randomly */
> --static SHELL_VAR *assign_random __P((SHELL_VAR *, char *, arrayind_t, char *));
> --static SHELL_VAR *get_random __P((SHELL_VAR *));
> --
> --static SHELL_VAR *assign_lineno __P((SHELL_VAR *, char *, arrayind_t, char *));
> --static SHELL_VAR *get_lineno __P((SHELL_VAR *));
> --
> --static SHELL_VAR *assign_subshell __P((SHELL_VAR *, char *, arrayind_t, char *));
> --static SHELL_VAR *get_subshell __P((SHELL_VAR *));
> --
> --static SHELL_VAR *get_bashpid __P((SHELL_VAR *));
> --
> --#if defined (HISTORY)
> --static SHELL_VAR *get_histcmd __P((SHELL_VAR *));
> --#endif
> --
> --#if defined (READLINE)
> --static SHELL_VAR *get_comp_wordbreaks __P((SHELL_VAR *));
> --static SHELL_VAR *assign_comp_wordbreaks __P((SHELL_VAR *, char *, arrayind_t, char *));
> --#endif
> --
> --#if defined (PUSHD_AND_POPD) && defined (ARRAY_VARS)
> --static SHELL_VAR *assign_dirstack __P((SHELL_VAR *, char *, arrayind_t, char *));
> --static SHELL_VAR *get_dirstack __P((SHELL_VAR *));
> --#endif
> --
> --#if defined (ARRAY_VARS)
> --static SHELL_VAR *get_groupset __P((SHELL_VAR *));
> --
> --static SHELL_VAR *build_hashcmd __P((SHELL_VAR *));
> --static SHELL_VAR *get_hashcmd __P((SHELL_VAR *));
> --static SHELL_VAR *assign_hashcmd __P((SHELL_VAR *,  char *, arrayind_t, char *));
> --#  if defined (ALIAS)
> --static SHELL_VAR *build_aliasvar __P((SHELL_VAR *));
> --static SHELL_VAR *get_aliasvar __P((SHELL_VAR *));
> --static SHELL_VAR *assign_aliasvar __P((SHELL_VAR *,  char *, arrayind_t, char *));
> --#  endif
> --#endif
> --
> --static SHELL_VAR *get_funcname __P((SHELL_VAR *));
> --static SHELL_VAR *init_funcname_var __P((void));
> --
> --static void initialize_dynamic_variables __P((void));
> --
> --static SHELL_VAR *hash_lookup __P((const char *, HASH_TABLE *));
> --static SHELL_VAR *new_shell_variable __P((const char *));
> --static SHELL_VAR *make_new_variable __P((const char *, HASH_TABLE *));
> --static SHELL_VAR *bind_variable_internal __P((const char *, char *, HASH_TABLE *, int, int));
> --
> --static void dispose_variable_value __P((SHELL_VAR *));
> --static void free_variable_hash_data __P((PTR_T));
> --
> --static VARLIST *vlist_alloc __P((int));
> --static VARLIST *vlist_realloc __P((VARLIST *, int));
> --static void vlist_add __P((VARLIST *, SHELL_VAR *, int));
> --
> --static void flatten __P((HASH_TABLE *, sh_var_map_func_t *, VARLIST *, int));
> --
> --static int qsort_var_comp __P((SHELL_VAR **, SHELL_VAR **));
> --
> --static SHELL_VAR **vapply __P((sh_var_map_func_t *));
> --static SHELL_VAR **fapply __P((sh_var_map_func_t *));
> --
> --static int visible_var __P((SHELL_VAR *));
> --static int visible_and_exported __P((SHELL_VAR *));
> --static int export_environment_candidate __P((SHELL_VAR *));
> --static int local_and_exported __P((SHELL_VAR *));
> --static int variable_in_context __P((SHELL_VAR *));
> --#if defined (ARRAY_VARS)
> --static int visible_array_vars __P((SHELL_VAR *));
> --#endif
> --
> --static SHELL_VAR *find_nameref_at_context __P((SHELL_VAR *, VAR_CONTEXT *));
> --static SHELL_VAR *find_variable_nameref_context __P((SHELL_VAR *, VAR_CONTEXT *, VAR_CONTEXT **));
> --static SHELL_VAR *find_variable_last_nameref_context __P((SHELL_VAR *, VAR_CONTEXT *, VAR_CONTEXT **));
> --
> --static SHELL_VAR *bind_tempenv_variable __P((const char *, char *));
> --static void push_temp_var __P((PTR_T));
> --static void propagate_temp_var __P((PTR_T));
> --static void dispose_temporary_env __P((sh_free_func_t *));     
> --
> --static inline char *mk_env_string __P((const char *, const char *, int));
> --static char **make_env_array_from_var_list __P((SHELL_VAR **));
> --static char **make_var_export_array __P((VAR_CONTEXT *));
> --static char **make_func_export_array __P((void));
> --static void add_temp_array_to_env __P((char **, int, int));
> --
> --static int n_shell_variables __P((void));
> --static int set_context __P((SHELL_VAR *));
> --
> --static void push_func_var __P((PTR_T));
> --static void push_exported_var __P((PTR_T));
> --
> --static inline int find_special_var __P((const char *));
> --
> --static void
> --create_variable_tables ()
> --{
> --  if (shell_variables == 0)
> --    {
> --      shell_variables = global_variables = new_var_context ((char *)NULL, 0);
> --      shell_variables->scope = 0;
> --      shell_variables->table = hash_create (0);
> --    }
> --
> --  if (shell_functions == 0)
> --    shell_functions = hash_create (0);
> --
> --#if defined (DEBUGGER)
> --  if (shell_function_defs == 0)
> --    shell_function_defs = hash_create (0);
> --#endif
> --}
> --
> --/* Initialize the shell variables from the current environment.
> --   If PRIVMODE is nonzero, don't import functions from ENV or
> --   parse $SHELLOPTS. */
> --void
> --initialize_shell_variables (env, privmode)
> --     char **env;
> --     int privmode;
> --{
> --  char *name, *string, *temp_string;
> --  int c, char_index, string_index, string_length, ro;
> --  SHELL_VAR *temp_var;
> --
> --  create_variable_tables ();
> --
> --  for (string_index = 0; string = env[string_index++]; )
> --    {
> --      char_index = 0;
> --      name = string;
> --      while ((c = *string++) && c != '=')
> --	;
> --      if (string[-1] == '=')
> --	char_index = string - name - 1;
> --
> --      /* If there are weird things in the environment, like `=xxx' or a
> --	 string without an `=', just skip them. */
> --      if (char_index == 0)
> --	continue;
> --
> --      /* ASSERT(name[char_index] == '=') */
> --      name[char_index] = '\0';
> --      /* Now, name = env variable name, string = env variable value, and
> --	 char_index == strlen (name) */
> --
> --      temp_var = (SHELL_VAR *)NULL;
> --
> --      /* If exported function, define it now.  Don't import functions from
> --	 the environment in privileged mode. */
> --      if (privmode == 0 && read_but_dont_execute == 0 && 
> --          STREQN (BASHFUNC_PREFIX, name, BASHFUNC_PREFLEN) &&
> --          STREQ (BASHFUNC_SUFFIX, name + char_index - BASHFUNC_SUFFLEN) &&
> --	  STREQN ("() {", string, 4))
> --	{
> --	  size_t namelen;
> --	  char *tname;		/* desired imported function name */
> --
> --	  namelen = char_index - BASHFUNC_PREFLEN - BASHFUNC_SUFFLEN;
> --
> --	  tname = name + BASHFUNC_PREFLEN;	/* start of func name */
> --	  tname[namelen] = '\0';		/* now tname == func name */
> --
> --	  string_length = strlen (string);
> --	  temp_string = (char *)xmalloc (namelen + string_length + 2);
> --
> --	  memcpy (temp_string, tname, namelen);
> --	  temp_string[namelen] = ' ';
> --	  memcpy (temp_string + namelen + 1, string, string_length + 1);
> --
> --	  /* Don't import function names that are invalid identifiers from the
> --	     environment, though we still allow them to be defined as shell
> --	     variables. */
> --	  if (absolute_program (tname) == 0 && (posixly_correct == 0 || legal_identifier (tname)))
> --	    parse_and_execute (temp_string, tname, SEVAL_NONINT|SEVAL_NOHIST|SEVAL_FUNCDEF|SEVAL_ONECMD);
> --
> --	  if (temp_var = find_function (tname))
> --	    {
> --	      VSETATTR (temp_var, (att_exported|att_imported));
> --	      array_needs_making = 1;
> --	    }
> --	  else
> --	    {
> --	      if (temp_var = bind_variable (name, string, 0))
> --		{
> --		  VSETATTR (temp_var, (att_exported | att_imported | att_invisible));
> --		  array_needs_making = 1;
> --		}
> --	      last_command_exit_value = 1;
> --	      report_error (_("error importing function definition for `%s'"), tname);
> --	    }
> --
> --	  /* Restore original suffix */
> --	  tname[namelen] = BASHFUNC_SUFFIX[0];
> --	}
> --#if defined (ARRAY_VARS)
> --#  if ARRAY_EXPORT
> --      /* Array variables may not yet be exported. */
> --      else if (*string == '(' && string[1] == '[' && string[strlen (string) - 1] == ')')
> --	{
> --	  string_length = 1;
> --	  temp_string = extract_array_assignment_list (string, &string_length);
> --	  temp_var = assign_array_from_string (name, temp_string);
> --	  FREE (temp_string);
> --	  VSETATTR (temp_var, (att_exported | att_imported));
> --	  array_needs_making = 1;
> --	}
> --#  endif /* ARRAY_EXPORT */
> --#endif
> --#if 0
> --      else if (legal_identifier (name))
> --#else
> --      else
> --#endif
> --	{
> --	  ro = 0;
> --	  if (posixly_correct && STREQ (name, "SHELLOPTS"))
> --	    {
> --	      temp_var = find_variable ("SHELLOPTS");
> --	      ro = temp_var && readonly_p (temp_var);
> --	      if (temp_var)
> --		VUNSETATTR (temp_var, att_readonly);
> --	    }
> --	  temp_var = bind_variable (name, string, 0);
> --	  if (temp_var)
> --	    {
> --	      if (legal_identifier (name))
> --		VSETATTR (temp_var, (att_exported | att_imported));
> --	      else
> --		VSETATTR (temp_var, (att_exported | att_imported | att_invisible));
> --	      if (ro)
> --		VSETATTR (temp_var, att_readonly);
> --	      array_needs_making = 1;
> --	    }
> --	}
> --
> --      name[char_index] = '=';
> --      /* temp_var can be NULL if it was an exported function with a syntax
> --	 error (a different bug, but it still shouldn't dump core). */
> --      if (temp_var && function_p (temp_var) == 0)	/* XXX not yet */
> --	{
> --	  CACHE_IMPORTSTR (temp_var, name);
> --	}
> --    }
> --
> --  set_pwd ();
> --
> --  /* Set up initial value of $_ */
> --  temp_var = set_if_not ("_", dollar_vars[0]);
> --
> --  /* Remember this pid. */
> --  dollar_dollar_pid = getpid ();
> --
> --  /* Now make our own defaults in case the vars that we think are
> --     important are missing. */
> --  temp_var = set_if_not ("PATH", DEFAULT_PATH_VALUE);
> --#if 0
> --  set_auto_export (temp_var);	/* XXX */
> --#endif
> --
> --  temp_var = set_if_not ("TERM", "dumb");
> --#if 0
> --  set_auto_export (temp_var);	/* XXX */
> --#endif
> --
> --#if defined (__QNX__)
> --  /* set node id -- don't import it from the environment */
> --  {
> --    char node_name[22];
> --#  if defined (__QNXNTO__)
> --    netmgr_ndtostr(ND2S_LOCAL_STR, ND_LOCAL_NODE, node_name, sizeof(node_name));
> --#  else
> --    qnx_nidtostr (getnid (), node_name, sizeof (node_name));
> --#  endif
> --    temp_var = bind_variable ("NODE", node_name, 0);
> --    set_auto_export (temp_var);
> --  }
> --#endif
> --
> --  /* set up the prompts. */
> --  if (interactive_shell)
> --    {
> --#if defined (PROMPT_STRING_DECODE)
> --      set_if_not ("PS1", primary_prompt);
> --#else
> --      if (current_user.uid == -1)
> --	get_current_user_info ();
> --      set_if_not ("PS1", current_user.euid == 0 ? "# " : primary_prompt);
> --#endif
> --      set_if_not ("PS2", secondary_prompt);
> --    }
> --  set_if_not ("PS4", "+ ");
> --
> --  /* Don't allow IFS to be imported from the environment. */
> --  temp_var = bind_variable ("IFS", " \t\n", 0);
> --  setifs (temp_var);
> --
> --  /* Magic machine types.  Pretty convenient. */
> --  set_machine_vars ();
> --
> --  /* Default MAILCHECK for interactive shells.  Defer the creation of a
> --     default MAILPATH until the startup files are read, because MAIL
> --     names a mail file if MAILPATH is not set, and we should provide a
> --     default only if neither is set. */
> --  if (interactive_shell)
> --    {
> --      temp_var = set_if_not ("MAILCHECK", posixly_correct ? "600" : "60");
> --      VSETATTR (temp_var, att_integer);
> --    }
> --
> --  /* Do some things with shell level. */
> --  initialize_shell_level ();
> --
> --  set_ppid ();
> --
> --  /* Initialize the `getopts' stuff. */
> --  temp_var = bind_variable ("OPTIND", "1", 0);
> --  VSETATTR (temp_var, att_integer);
> --  getopts_reset (0);
> --  bind_variable ("OPTERR", "1", 0);
> --  sh_opterr = 1;
> --
> --  if (login_shell == 1 && posixly_correct == 0)
> --    set_home_var ();
> --
> --  /* Get the full pathname to THIS shell, and set the BASH variable
> --     to it. */
> --  name = get_bash_name ();
> --  temp_var = bind_variable ("BASH", name, 0);
> --  free (name);
> --
> --  /* Make the exported environment variable SHELL be the user's login
> --     shell.  Note that the `tset' command looks at this variable
> --     to determine what style of commands to output; if it ends in "csh",
> --     then C-shell commands are output, else Bourne shell commands. */
> --  set_shell_var ();
> --
> --  /* Make a variable called BASH_VERSION which contains the version info. */
> --  bind_variable ("BASH_VERSION", shell_version_string (), 0);
> --#if defined (ARRAY_VARS)
> --  make_vers_array ();
> --#endif
> --
> --  if (command_execution_string)
> --    bind_variable ("BASH_EXECUTION_STRING", command_execution_string, 0);
> --
> --  /* Find out if we're supposed to be in Posix.2 mode via an
> --     environment variable. */
> --  temp_var = find_variable ("POSIXLY_CORRECT");
> --  if (!temp_var)
> --    temp_var = find_variable ("POSIX_PEDANTIC");
> --  if (temp_var && imported_p (temp_var))
> --    sv_strict_posix (temp_var->name);
> --
> --#if defined (HISTORY)
> --  /* Set history variables to defaults, and then do whatever we would
> --     do if the variable had just been set.  Do this only in the case
> --     that we are remembering commands on the history list. */
> --  if (remember_on_history)
> --    {
> --      name = bash_tilde_expand (posixly_correct ? "~/.sh_history" : "~/.bash_history", 0);
> --
> --      set_if_not ("HISTFILE", name);
> --      free (name);
> --    }
> --#endif /* HISTORY */
> --
> --  /* Seed the random number generator. */
> --  seedrand ();
> --
> --  /* Handle some "special" variables that we may have inherited from a
> --     parent shell. */
> --  if (interactive_shell)
> --    {
> --      temp_var = find_variable ("IGNOREEOF");
> --      if (!temp_var)
> --	temp_var = find_variable ("ignoreeof");
> --      if (temp_var && imported_p (temp_var))
> --	sv_ignoreeof (temp_var->name);
> --    }
> --
> --#if defined (HISTORY)
> --  if (interactive_shell && remember_on_history)
> --    {
> --      sv_history_control ("HISTCONTROL");
> --      sv_histignore ("HISTIGNORE");
> --      sv_histtimefmt ("HISTTIMEFORMAT");
> --    }
> --#endif /* HISTORY */
> --
> --#if defined (READLINE) && defined (STRICT_POSIX)
> --  /* POSIXLY_CORRECT will only be 1 here if the shell was compiled
> --     -DSTRICT_POSIX */
> --  if (interactive_shell && posixly_correct && no_line_editing == 0)
> --    rl_prefer_env_winsize = 1;
> --#endif /* READLINE && STRICT_POSIX */
> --
> --     /*
> --      * 24 October 2001
> --      *
> --      * I'm tired of the arguing and bug reports.  Bash now leaves SSH_CLIENT
> --      * and SSH2_CLIENT alone.  I'm going to rely on the shell_level check in
> --      * isnetconn() to avoid running the startup files more often than wanted.
> --      * That will, of course, only work if the user's login shell is bash, so
> --      * I've made that behavior conditional on SSH_SOURCE_BASHRC being defined
> --      * in config-top.h.
> --      */
> --#if 0
> --  temp_var = find_variable ("SSH_CLIENT");
> --  if (temp_var && imported_p (temp_var))
> --    {
> --      VUNSETATTR (temp_var, att_exported);
> --      array_needs_making = 1;
> --    }
> --  temp_var = find_variable ("SSH2_CLIENT");
> --  if (temp_var && imported_p (temp_var))
> --    {
> --      VUNSETATTR (temp_var, att_exported);
> --      array_needs_making = 1;
> --    }
> --#endif
> --
> --  /* Get the user's real and effective user ids. */
> --  uidset ();
> --
> --  temp_var = find_variable ("BASH_XTRACEFD");
> --  if (temp_var && imported_p (temp_var))
> --    sv_xtracefd (temp_var->name);
> --
> --  /* Initialize the dynamic variables, and seed their values. */
> --  initialize_dynamic_variables ();
> --}
> --
> --/* **************************************************************** */
> --/*								    */
> --/*	     Setting values for special shell variables		    */
> --/*								    */
> --/* **************************************************************** */
> --
> --static void
> --set_machine_vars ()
> --{
> --  SHELL_VAR *temp_var;
> --
> --  temp_var = set_if_not ("HOSTTYPE", HOSTTYPE);
> --  temp_var = set_if_not ("OSTYPE", OSTYPE);
> --  temp_var = set_if_not ("MACHTYPE", MACHTYPE);
> --
> --  temp_var = set_if_not ("HOSTNAME", current_host_name);
> --}
> --
> --/* Set $HOME to the information in the password file if we didn't get
> --   it from the environment. */
> --
> --/* This function is not static so the tilde and readline libraries can
> --   use it. */
> --char *
> --sh_get_home_dir ()
> --{
> --  if (current_user.home_dir == 0)
> --    get_current_user_info ();
> --  return current_user.home_dir;
> --}
> --
> --static void
> --set_home_var ()
> --{
> --  SHELL_VAR *temp_var;
> --
> --  temp_var = find_variable ("HOME");
> --  if (temp_var == 0)
> --    temp_var = bind_variable ("HOME", sh_get_home_dir (), 0);
> --#if 0
> --  VSETATTR (temp_var, att_exported);
> --#endif
> --}
> --
> --/* Set $SHELL to the user's login shell if it is not already set.  Call
> --   get_current_user_info if we haven't already fetched the shell. */
> --static void
> --set_shell_var ()
> --{
> --  SHELL_VAR *temp_var;
> --
> --  temp_var = find_variable ("SHELL");
> --  if (temp_var == 0)
> --    {
> --      if (current_user.shell == 0)
> --	get_current_user_info ();
> --      temp_var = bind_variable ("SHELL", current_user.shell, 0);
> --    }
> --#if 0
> --  VSETATTR (temp_var, att_exported);
> --#endif
> --}
> --
> --static char *
> --get_bash_name ()
> --{
> --  char *name;
> --
> --  if ((login_shell == 1) && RELPATH(shell_name))
> --    {
> --      if (current_user.shell == 0)
> --	get_current_user_info ();
> --      name = savestring (current_user.shell);
> --    }
> --  else if (ABSPATH(shell_name))
> --    name = savestring (shell_name);
> --  else if (shell_name[0] == '.' && shell_name[1] == '/')
> --    {
> --      /* Fast path for common case. */
> --      char *cdir;
> --      int len;
> --
> --      cdir = get_string_value ("PWD");
> --      if (cdir)
> --	{
> --	  len = strlen (cdir);
> --	  name = (char *)xmalloc (len + strlen (shell_name) + 1);
> --	  strcpy (name, cdir);
> --	  strcpy (name + len, shell_name + 1);
> --	}
> --      else
> --	name = savestring (shell_name);
> --    }
> --  else
> --    {
> --      char *tname;
> --      int s;
> --
> --      tname = find_user_command (shell_name);
> --
> --      if (tname == 0)
> --	{
> --	  /* Try the current directory.  If there is not an executable
> --	     there, just punt and use the login shell. */
> --	  s = file_status (shell_name);
> --	  if (s & FS_EXECABLE)
> --	    {
> --	      tname = make_absolute (shell_name, get_string_value ("PWD"));
> --	      if (*shell_name == '.')
> --		{
> --		  name = sh_canonpath (tname, PATH_CHECKDOTDOT|PATH_CHECKEXISTS);
> --		  if (name == 0)
> --		    name = tname;
> --		  else
> --		    free (tname);
> --		}
> --	     else
> --		name = tname;
> --	    }
> --	  else
> --	    {
> --	      if (current_user.shell == 0)
> --		get_current_user_info ();
> --	      name = savestring (current_user.shell);
> --	    }
> --	}
> --      else
> --	{
> --	  name = full_pathname (tname);
> --	  free (tname);
> --	}
> --    }
> --
> --  return (name);
> --}
> --
> --void
> --adjust_shell_level (change)
> --     int change;
> --{
> --  char new_level[5], *old_SHLVL;
> --  intmax_t old_level;
> --  SHELL_VAR *temp_var;
> --
> --  old_SHLVL = get_string_value ("SHLVL");
> --  if (old_SHLVL == 0 || *old_SHLVL == '\0' || legal_number (old_SHLVL, &old_level) == 0)
> --    old_level = 0;
> --
> --  shell_level = old_level + change;
> --  if (shell_level < 0)
> --    shell_level = 0;
> --  else if (shell_level > 1000)
> --    {
> --      internal_warning (_("shell level (%d) too high, resetting to 1"), shell_level);
> --      shell_level = 1;
> --    }
> --
> --  /* We don't need the full generality of itos here. */
> --  if (shell_level < 10)
> --    {
> --      new_level[0] = shell_level + '0';
> --      new_level[1] = '\0';
> --    }
> --  else if (shell_level < 100)
> --    {
> --      new_level[0] = (shell_level / 10) + '0';
> --      new_level[1] = (shell_level % 10) + '0';
> --      new_level[2] = '\0';
> --    }
> --  else if (shell_level < 1000)
> --    {
> --      new_level[0] = (shell_level / 100) + '0';
> --      old_level = shell_level % 100;
> --      new_level[1] = (old_level / 10) + '0';
> --      new_level[2] = (old_level % 10) + '0';
> --      new_level[3] = '\0';
> --    }
> --
> --  temp_var = bind_variable ("SHLVL", new_level, 0);
> --  set_auto_export (temp_var);
> --}
> --
> --static void
> --initialize_shell_level ()
> --{
> --  adjust_shell_level (1);
> --}
> --
> --/* If we got PWD from the environment, update our idea of the current
> --   working directory.  In any case, make sure that PWD exists before
> --   checking it.  It is possible for getcwd () to fail on shell startup,
> --   and in that case, PWD would be undefined.  If this is an interactive
> --   login shell, see if $HOME is the current working directory, and if
> --   that's not the same string as $PWD, set PWD=$HOME. */
> --
> --void
> --set_pwd ()
> --{
> --  SHELL_VAR *temp_var, *home_var;
> --  char *temp_string, *home_string;
> --
> --  home_var = find_variable ("HOME");
> --  home_string = home_var ? value_cell (home_var) : (char *)NULL;
> --
> --  temp_var = find_variable ("PWD");
> --  if (temp_var && imported_p (temp_var) &&
> --      (temp_string = value_cell (temp_var)) &&
> --      same_file (temp_string, ".", (struct stat *)NULL, (struct stat *)NULL))
> --    set_working_directory (temp_string);
> --  else if (home_string && interactive_shell && login_shell &&
> --	   same_file (home_string, ".", (struct stat *)NULL, (struct stat *)NULL))
> --    {
> --      set_working_directory (home_string);
> --      temp_var = bind_variable ("PWD", home_string, 0);
> --      set_auto_export (temp_var);
> --    }
> --  else
> --    {
> --      temp_string = get_working_directory ("shell-init");
> --      if (temp_string)
> --	{
> --	  temp_var = bind_variable ("PWD", temp_string, 0);
> --	  set_auto_export (temp_var);
> --	  free (temp_string);
> --	}
> --    }
> --
> --  /* According to the Single Unix Specification, v2, $OLDPWD is an
> --     `environment variable' and therefore should be auto-exported.
> --     Make a dummy invisible variable for OLDPWD, and mark it as exported. */
> --  temp_var = bind_variable ("OLDPWD", (char *)NULL, 0);
> --  VSETATTR (temp_var, (att_exported | att_invisible));
> --}
> --
> --/* Make a variable $PPID, which holds the pid of the shell's parent.  */
> --void
> --set_ppid ()
> --{
> --  char namebuf[INT_STRLEN_BOUND(pid_t) + 1], *name;
> --  SHELL_VAR *temp_var;
> --
> --  name = inttostr (getppid (), namebuf, sizeof(namebuf));
> --  temp_var = find_variable ("PPID");
> --  if (temp_var)
> --    VUNSETATTR (temp_var, (att_readonly | att_exported));
> --  temp_var = bind_variable ("PPID", name, 0);
> --  VSETATTR (temp_var, (att_readonly | att_integer));
> --}
> --
> --static void
> --uidset ()
> --{
> --  char buff[INT_STRLEN_BOUND(uid_t) + 1], *b;
> --  register SHELL_VAR *v;
> --
> --  b = inttostr (current_user.uid, buff, sizeof (buff));
> --  v = find_variable ("UID");
> --  if (v == 0)
> --    {
> --      v = bind_variable ("UID", b, 0);
> --      VSETATTR (v, (att_readonly | att_integer));
> --    }
> --
> --  if (current_user.euid != current_user.uid)
> --    b = inttostr (current_user.euid, buff, sizeof (buff));
> --
> --  v = find_variable ("EUID");
> --  if (v == 0)
> --    {
> --      v = bind_variable ("EUID", b, 0);
> --      VSETATTR (v, (att_readonly | att_integer));
> --    }
> --}
> --
> --#if defined (ARRAY_VARS)
> --static void
> --make_vers_array ()
> --{
> --  SHELL_VAR *vv;
> --  ARRAY *av;
> --  char *s, d[32], b[INT_STRLEN_BOUND(int) + 1];
> --
> --  unbind_variable ("BASH_VERSINFO");
> --
> --  vv = make_new_array_variable ("BASH_VERSINFO");
> --  av = array_cell (vv);
> --  strcpy (d, dist_version);
> --  s = strchr (d, '.');
> --  if (s)
> --    *s++ = '\0';
> --  array_insert (av, 0, d);
> --  array_insert (av, 1, s);
> --  s = inttostr (patch_level, b, sizeof (b));
> --  array_insert (av, 2, s);
> --  s = inttostr (build_version, b, sizeof (b));
> --  array_insert (av, 3, s);
> --  array_insert (av, 4, release_status);
> --  array_insert (av, 5, MACHTYPE);
> --
> --  VSETATTR (vv, att_readonly);
> --}
> --#endif /* ARRAY_VARS */
> --
> --/* Set the environment variables $LINES and $COLUMNS in response to
> --   a window size change. */
> --void
> --sh_set_lines_and_columns (lines, cols)
> --     int lines, cols;
> --{
> --  char val[INT_STRLEN_BOUND(int) + 1], *v;
> --
> --#if defined (READLINE)
> --  /* If we are currently assigning to LINES or COLUMNS, don't do anything. */
> --  if (winsize_assignment)
> --    return;
> --#endif
> --
> --  v = inttostr (lines, val, sizeof (val));
> --  bind_variable ("LINES", v, 0);
> --
> --  v = inttostr (cols, val, sizeof (val));
> --  bind_variable ("COLUMNS", v, 0);
> --}
> --
> --/* **************************************************************** */
> --/*								    */
> --/*		   Printing variables and values		    */
> --/*								    */
> --/* **************************************************************** */
> --
> --/* Print LIST (a list of shell variables) to stdout in such a way that
> --   they can be read back in. */
> --void
> --print_var_list (list)
> --     register SHELL_VAR **list;
> --{
> --  register int i;
> --  register SHELL_VAR *var;
> --
> --  for (i = 0; list && (var = list[i]); i++)
> --    if (invisible_p (var) == 0)
> --      print_assignment (var);
> --}
> --
> --/* Print LIST (a list of shell functions) to stdout in such a way that
> --   they can be read back in. */
> --void
> --print_func_list (list)
> --     register SHELL_VAR **list;
> --{
> --  register int i;
> --  register SHELL_VAR *var;
> --
> --  for (i = 0; list && (var = list[i]); i++)
> --    {
> --      printf ("%s ", var->name);
> --      print_var_function (var);
> --      printf ("\n");
> --    }
> --}
> --      
> --/* Print the value of a single SHELL_VAR.  No newline is
> --   output, but the variable is printed in such a way that
> --   it can be read back in. */
> --void
> --print_assignment (var)
> --     SHELL_VAR *var;
> --{
> --  if (var_isset (var) == 0)
> --    return;
> --
> --  if (function_p (var))
> --    {
> --      printf ("%s", var->name);
> --      print_var_function (var);
> --      printf ("\n");
> --    }
> --#if defined (ARRAY_VARS)
> --  else if (array_p (var))
> --    print_array_assignment (var, 0);
> --  else if (assoc_p (var))
> --    print_assoc_assignment (var, 0);
> --#endif /* ARRAY_VARS */
> --  else
> --    {
> --      printf ("%s=", var->name);
> --      print_var_value (var, 1);
> --      printf ("\n");
> --    }
> --}
> --
> --/* Print the value cell of VAR, a shell variable.  Do not print
> --   the name, nor leading/trailing newline.  If QUOTE is non-zero,
> --   and the value contains shell metacharacters, quote the value
> --   in such a way that it can be read back in. */
> --void
> --print_var_value (var, quote)
> --     SHELL_VAR *var;
> --     int quote;
> --{
> --  char *t;
> --
> --  if (var_isset (var) == 0)
> --    return;
> --
> --  if (quote && posixly_correct == 0 && ansic_shouldquote (value_cell (var)))
> --    {
> --      t = ansic_quote (value_cell (var), 0, (int *)0);
> --      printf ("%s", t);
> --      free (t);
> --    }
> --  else if (quote && sh_contains_shell_metas (value_cell (var)))
> --    {
> --      t = sh_single_quote (value_cell (var));
> --      printf ("%s", t);
> --      free (t);
> --    }
> --  else
> --    printf ("%s", value_cell (var));
> --}
> --
> --/* Print the function cell of VAR, a shell variable.  Do not
> --   print the name, nor leading/trailing newline. */
> --void
> --print_var_function (var)
> --     SHELL_VAR *var;
> --{
> --  char *x;
> --
> --  if (function_p (var) && var_isset (var))
> --    {
> --      x = named_function_string ((char *)NULL, function_cell(var), FUNC_MULTILINE|FUNC_EXTERNAL);
> --      printf ("%s", x);
> --    }
> --}
> --
> --/* **************************************************************** */
> --/*								    */
> --/*		 	Dynamic Variables			    */
> --/*								    */
> --/* **************************************************************** */
> --
> --/* DYNAMIC VARIABLES
> --
> --   These are variables whose values are generated anew each time they are
> --   referenced.  These are implemented using a pair of function pointers
> --   in the struct variable: assign_func, which is called from bind_variable
> --   and, if arrays are compiled into the shell, some of the functions in
> --   arrayfunc.c, and dynamic_value, which is called from find_variable.
> --
> --   assign_func is called from bind_variable_internal, if
> --   bind_variable_internal discovers that the variable being assigned to
> --   has such a function.  The function is called as
> --	SHELL_VAR *temp = (*(entry->assign_func)) (entry, value, ind)
> --   and the (SHELL_VAR *)temp is returned as the value of bind_variable.  It
> --   is usually ENTRY (self).  IND is an index for an array variable, and
> --   unused otherwise.
> --
> --   dynamic_value is called from find_variable_internal to return a `new'
> --   value for the specified dynamic varible.  If this function is NULL,
> --   the variable is treated as a `normal' shell variable.  If it is not,
> --   however, then this function is called like this:
> --	tempvar = (*(var->dynamic_value)) (var);
> --
> --   Sometimes `tempvar' will replace the value of `var'.  Other times, the
> --   shell will simply use the string value.  Pretty object-oriented, huh?
> --
> --   Be warned, though: if you `unset' a special variable, it loses its
> --   special meaning, even if you subsequently set it.
> --
> --   The special assignment code would probably have been better put in
> --   subst.c: do_assignment_internal, in the same style as
> --   stupidly_hack_special_variables, but I wanted the changes as
> --   localized as possible.  */
> --
> --#define INIT_DYNAMIC_VAR(var, val, gfunc, afunc) \
> --  do \
> --    { \
> --      v = bind_variable (var, (val), 0); \
> --      v->dynamic_value = gfunc; \
> --      v->assign_func = afunc; \
> --    } \
> --  while (0)
> --
> --#define INIT_DYNAMIC_ARRAY_VAR(var, gfunc, afunc) \
> --  do \
> --    { \
> --      v = make_new_array_variable (var); \
> --      v->dynamic_value = gfunc; \
> --      v->assign_func = afunc; \
> --    } \
> --  while (0)
> --
> --#define INIT_DYNAMIC_ASSOC_VAR(var, gfunc, afunc) \
> --  do \
> --    { \
> --      v = make_new_assoc_variable (var); \
> --      v->dynamic_value = gfunc; \
> --      v->assign_func = afunc; \
> --    } \
> --  while (0)
> --
> --static SHELL_VAR *
> --null_assign (self, value, unused, key)
> --     SHELL_VAR *self;
> --     char *value;
> --     arrayind_t unused;
> --     char *key;
> --{
> --  return (self);
> --}
> --
> --#if defined (ARRAY_VARS)
> --static SHELL_VAR *
> --null_array_assign (self, value, ind, key)
> --     SHELL_VAR *self;
> --     char *value;
> --     arrayind_t ind;
> --     char *key;
> --{
> --  return (self);
> --}
> --#endif
> --
> --/* Degenerate `dynamic_value' function; just returns what's passed without
> --   manipulation. */
> --static SHELL_VAR *
> --get_self (self)
> --     SHELL_VAR *self;
> --{
> --  return (self);
> --}
> --
> --#if defined (ARRAY_VARS)
> --/* A generic dynamic array variable initializer.  Initialize array variable
> --   NAME with dynamic value function GETFUNC and assignment function SETFUNC. */
> --static SHELL_VAR *
> --init_dynamic_array_var (name, getfunc, setfunc, attrs)
> --     char *name;
> --     sh_var_value_func_t *getfunc;
> --     sh_var_assign_func_t *setfunc;
> --     int attrs;
> --{
> --  SHELL_VAR *v;
> --
> --  v = find_variable (name);
> --  if (v)
> --    return (v);
> --  INIT_DYNAMIC_ARRAY_VAR (name, getfunc, setfunc);
> --  if (attrs)
> --    VSETATTR (v, attrs);
> --  return v;
> --}
> --
> --static SHELL_VAR *
> --init_dynamic_assoc_var (name, getfunc, setfunc, attrs)
> --     char *name;
> --     sh_var_value_func_t *getfunc;
> --     sh_var_assign_func_t *setfunc;
> --     int attrs;
> --{
> --  SHELL_VAR *v;
> --
> --  v = find_variable (name);
> --  if (v)
> --    return (v);
> --  INIT_DYNAMIC_ASSOC_VAR (name, getfunc, setfunc);
> --  if (attrs)
> --    VSETATTR (v, attrs);
> --  return v;
> --}
> --#endif
> --
> --/* The value of $SECONDS.  This is the number of seconds since shell
> --   invocation, or, the number of seconds since the last assignment + the
> --   value of the last assignment. */
> --static intmax_t seconds_value_assigned;
> --
> --static SHELL_VAR *
> --assign_seconds (self, value, unused, key)
> --     SHELL_VAR *self;
> --     char *value;
> --     arrayind_t unused;
> --     char *key;
> --{
> --  if (legal_number (value, &seconds_value_assigned) == 0)
> --    seconds_value_assigned = 0;
> --  shell_start_time = NOW;
> --  return (self);
> --}
> --
> --static SHELL_VAR *
> --get_seconds (var)
> --     SHELL_VAR *var;
> --{
> --  time_t time_since_start;
> --  char *p;
> --
> --  time_since_start = NOW - shell_start_time;
> --  p = itos(seconds_value_assigned + time_since_start);
> --
> --  FREE (value_cell (var));
> --
> --  VSETATTR (var, att_integer);
> --  var_setvalue (var, p);
> --  return (var);
> --}
> --
> --static SHELL_VAR *
> --init_seconds_var ()
> --{
> --  SHELL_VAR *v;
> --
> --  v = find_variable ("SECONDS");
> --  if (v)
> --    {
> --      if (legal_number (value_cell(v), &seconds_value_assigned) == 0)
> --	seconds_value_assigned = 0;
> --    }
> --  INIT_DYNAMIC_VAR ("SECONDS", (v ? value_cell (v) : (char *)NULL), get_seconds, assign_seconds);
> --  return v;      
> --}
> --     
> --/* The random number seed.  You can change this by setting RANDOM. */
> --static unsigned long rseed = 1;
> --static int last_random_value;
> --static int seeded_subshell = 0;
> --
> --/* A linear congruential random number generator based on the example
> --   one in the ANSI C standard.  This one isn't very good, but a more
> --   complicated one is overkill. */
> --
> --/* Returns a pseudo-random number between 0 and 32767. */
> --static int
> --brand ()
> --{
> --  /* From "Random number generators: good ones are hard to find",
> --     Park and Miller, Communications of the ACM, vol. 31, no. 10,
> --     October 1988, p. 1195. filtered through FreeBSD */
> --  long h, l;
> --
> --  /* Can't seed with 0. */
> --  if (rseed == 0)
> --    rseed = 123459876;
> --  h = rseed / 127773;
> --  l = rseed % 127773;
> --  rseed = 16807 * l - 2836 * h;
> --#if 0
> --  if (rseed < 0)
> --    rseed += 0x7fffffff;
> --#endif
> --  return ((unsigned int)(rseed & 32767));	/* was % 32768 */
> --}
> --
> --/* Set the random number generator seed to SEED. */
> --static void
> --sbrand (seed)
> --     unsigned long seed;
> --{
> --  rseed = seed;
> --  last_random_value = 0;
> --}
> --
> --static void
> --seedrand ()
> --{
> --  struct timeval tv;
> --
> --  gettimeofday (&tv, NULL);
> --  sbrand (tv.tv_sec ^ tv.tv_usec ^ getpid ());
> --}
> --
> --static SHELL_VAR *
> --assign_random (self, value, unused, key)
> --     SHELL_VAR *self;
> --     char *value;
> --     arrayind_t unused;
> --     char *key;
> --{
> --  sbrand (strtoul (value, (char **)NULL, 10));
> --  if (subshell_environment)
> --    seeded_subshell = getpid ();
> --  return (self);
> --}
> --
> --int
> --get_random_number ()
> --{
> --  int rv, pid;
> --
> --  /* Reset for command and process substitution. */
> --  pid = getpid ();
> --  if (subshell_environment && seeded_subshell != pid)
> --    {
> --      seedrand ();
> --      seeded_subshell = pid;
> --    }
> --
> --  do
> --    rv = brand ();
> --  while (rv == last_random_value);
> --  return rv;
> --}
> --
> --static SHELL_VAR *
> --get_random (var)
> --     SHELL_VAR *var;
> --{
> --  int rv;
> --  char *p;
> --
> --  rv = get_random_number ();
> --  last_random_value = rv;
> --  p = itos (rv);
> --
> --  FREE (value_cell (var));
> --
> --  VSETATTR (var, att_integer);
> --  var_setvalue (var, p);
> --  return (var);
> --}
> --
> --static SHELL_VAR *
> --assign_lineno (var, value, unused, key)
> --     SHELL_VAR *var;
> --     char *value;
> --     arrayind_t unused;
> --     char *key;
> --{
> --  intmax_t new_value;
> --
> --  if (value == 0 || *value == '\0' || legal_number (value, &new_value) == 0)
> --    new_value = 0;
> --  line_number = line_number_base = new_value;
> --  return var;
> --}
> --
> --/* Function which returns the current line number. */
> --static SHELL_VAR *
> --get_lineno (var)
> --     SHELL_VAR *var;
> --{
> --  char *p;
> --  int ln;
> --
> --  ln = executing_line_number ();
> --  p = itos (ln);
> --  FREE (value_cell (var));
> --  var_setvalue (var, p);
> --  return (var);
> --}
> --
> --static SHELL_VAR *
> --assign_subshell (var, value, unused, key)
> --     SHELL_VAR *var;
> --     char *value;
> --     arrayind_t unused;
> --     char *key;
> --{
> --  intmax_t new_value;
> --
> --  if (value == 0 || *value == '\0' || legal_number (value, &new_value) == 0)
> --    new_value = 0;
> --  subshell_level = new_value;
> --  return var;
> --}
> --
> --static SHELL_VAR *
> --get_subshell (var)
> --     SHELL_VAR *var;
> --{
> --  char *p;
> --
> --  p = itos (subshell_level);
> --  FREE (value_cell (var));
> --  var_setvalue (var, p);
> --  return (var);
> --}
> --
> --static SHELL_VAR *
> --get_bashpid (var)
> --     SHELL_VAR *var;
> --{
> --  int pid;
> --  char *p;
> --
> --  pid = getpid ();
> --  p = itos (pid);
> --
> --  FREE (value_cell (var));
> --  VSETATTR (var, att_integer|att_readonly);
> --  var_setvalue (var, p);
> --  return (var);
> --}
> --
> --static SHELL_VAR *
> --get_bash_command (var)
> --     SHELL_VAR *var;
> --{
> --  char *p;
> --
> --  if (the_printed_command_except_trap)
> --    p = savestring (the_printed_command_except_trap);
> --  else
> --    {
> --      p = (char *)xmalloc (1);
> --      p[0] = '\0';
> --    }
> --  FREE (value_cell (var));
> --  var_setvalue (var, p);
> --  return (var);
> --}
> --
> --#if defined (HISTORY)
> --static SHELL_VAR *
> --get_histcmd (var)
> --     SHELL_VAR *var;
> --{
> --  char *p;
> --
> --  p = itos (history_number ());
> --  FREE (value_cell (var));
> --  var_setvalue (var, p);
> --  return (var);
> --}
> --#endif
> --
> --#if defined (READLINE)
> --/* When this function returns, VAR->value points to malloced memory. */
> --static SHELL_VAR *
> --get_comp_wordbreaks (var)
> --     SHELL_VAR *var;
> --{
> --  /* If we don't have anything yet, assign a default value. */
> --  if (rl_completer_word_break_characters == 0 && bash_readline_initialized == 0)
> --    enable_hostname_completion (perform_hostname_completion);
> --
> --  FREE (value_cell (var));
> --  var_setvalue (var, savestring (rl_completer_word_break_characters));
> --
> --  return (var);
> --}
> --
> --/* When this function returns, rl_completer_word_break_characters points to
> --   malloced memory. */
> --static SHELL_VAR *
> --assign_comp_wordbreaks (self, value, unused, key)
> --     SHELL_VAR *self;
> --     char *value;
> --     arrayind_t unused;
> --     char *key;
> --{
> --  if (rl_completer_word_break_characters &&
> --      rl_completer_word_break_characters != rl_basic_word_break_characters)
> --    free (rl_completer_word_break_characters);
> --
> --  rl_completer_word_break_characters = savestring (value);
> --  return self;
> --}
> --#endif /* READLINE */
> --
> --#if defined (PUSHD_AND_POPD) && defined (ARRAY_VARS)
> --static SHELL_VAR *
> --assign_dirstack (self, value, ind, key)
> --     SHELL_VAR *self;
> --     char *value;
> --     arrayind_t ind;
> --     char *key;
> --{
> --  set_dirstack_element (ind, 1, value);
> --  return self;
> --}
> --
> --static SHELL_VAR *
> --get_dirstack (self)
> --     SHELL_VAR *self;
> --{
> --  ARRAY *a;
> --  WORD_LIST *l;
> --
> --  l = get_directory_stack (0);
> --  a = array_from_word_list (l);
> --  array_dispose (array_cell (self));
> --  dispose_words (l);
> --  var_setarray (self, a);
> --  return self;
> --}
> --#endif /* PUSHD AND POPD && ARRAY_VARS */
> --
> --#if defined (ARRAY_VARS)
> --/* We don't want to initialize the group set with a call to getgroups()
> --   unless we're asked to, but we only want to do it once. */
> --static SHELL_VAR *
> --get_groupset (self)
> --     SHELL_VAR *self;
> --{
> --  register int i;
> --  int ng;
> --  ARRAY *a;
> --  static char **group_set = (char **)NULL;
> --
> --  if (group_set == 0)
> --    {
> --      group_set = get_group_list (&ng);
> --      a = array_cell (self);
> --      for (i = 0; i < ng; i++)
> --	array_insert (a, i, group_set[i]);
> --    }
> --  return (self);
> --}
> --
> --static SHELL_VAR *
> --build_hashcmd (self)
> --     SHELL_VAR *self;
> --{
> --  HASH_TABLE *h;
> --  int i;
> --  char *k, *v;
> --  BUCKET_CONTENTS *item;
> --
> --  h = assoc_cell (self);
> --  if (h)
> --    assoc_dispose (h);
> --
> --  if (hashed_filenames == 0 || HASH_ENTRIES (hashed_filenames) == 0)
> --    {
> --      var_setvalue (self, (char *)NULL);
> --      return self;
> --    }
> --
> --  h = assoc_create (hashed_filenames->nbuckets);
> --  for (i = 0; i < hashed_filenames->nbuckets; i++)
> --    {
> --      for (item = hash_items (i, hashed_filenames); item; item = item->next)
> --	{
> --	  k = savestring (item->key);
> --	  v = pathdata(item)->path;
> --	  assoc_insert (h, k, v);
> --	}
> --    }
> --
> --  var_setvalue (self, (char *)h);
> --  return self;
> --}
> --
> --static SHELL_VAR *
> --get_hashcmd (self)
> --     SHELL_VAR *self;
> --{
> --  build_hashcmd (self);
> --  return (self);
> --}
> --
> --static SHELL_VAR *
> --assign_hashcmd (self, value, ind, key)
> --     SHELL_VAR *self;
> --     char *value;
> --     arrayind_t ind;
> --     char *key;
> --{
> --  phash_insert (key, value, 0, 0);
> --  return (build_hashcmd (self));
> --}
> --
> --#if defined (ALIAS)
> --static SHELL_VAR *
> --build_aliasvar (self)
> --     SHELL_VAR *self;
> --{
> --  HASH_TABLE *h;
> --  int i;
> --  char *k, *v;
> --  BUCKET_CONTENTS *item;
> --
> --  h = assoc_cell (self);
> --  if (h)
> --    assoc_dispose (h);
> --
> --  if (aliases == 0 || HASH_ENTRIES (aliases) == 0)
> --    {
> --      var_setvalue (self, (char *)NULL);
> --      return self;
> --    }
> --
> --  h = assoc_create (aliases->nbuckets);
> --  for (i = 0; i < aliases->nbuckets; i++)
> --    {
> --      for (item = hash_items (i, aliases); item; item = item->next)
> --	{
> --	  k = savestring (item->key);
> --	  v = ((alias_t *)(item->data))->value;
> --	  assoc_insert (h, k, v);
> --	}
> --    }
> --
> --  var_setvalue (self, (char *)h);
> --  return self;
> --}
> --
> --static SHELL_VAR *
> --get_aliasvar (self)
> --     SHELL_VAR *self;
> --{
> --  build_aliasvar (self);
> --  return (self);
> --}
> --
> --static SHELL_VAR *
> --assign_aliasvar (self, value, ind, key)
> --     SHELL_VAR *self;
> --     char *value;
> --     arrayind_t ind;
> --     char *key;
> --{
> --  add_alias (key, value);
> --  return (build_aliasvar (self));
> --}
> --#endif /* ALIAS */
> --
> --#endif /* ARRAY_VARS */
> --
> --/* If ARRAY_VARS is not defined, this just returns the name of any
> --   currently-executing function.  If we have arrays, it's a call stack. */
> --static SHELL_VAR *
> --get_funcname (self)
> --     SHELL_VAR *self;
> --{
> --#if ! defined (ARRAY_VARS)
> --  char *t;
> --  if (variable_context && this_shell_function)
> --    {
> --      FREE (value_cell (self));
> --      t = savestring (this_shell_function->name);
> --      var_setvalue (self, t);
> --    }
> --#endif
> --  return (self);
> --}
> --
> --void
> --make_funcname_visible (on_or_off)
> --     int on_or_off;
> --{
> --  SHELL_VAR *v;
> --
> --  v = find_variable ("FUNCNAME");
> --  if (v == 0 || v->dynamic_value == 0)
> --    return;
> --
> --  if (on_or_off)
> --    VUNSETATTR (v, att_invisible);
> --  else
> --    VSETATTR (v, att_invisible);
> --}
> --
> --static SHELL_VAR *
> --init_funcname_var ()
> --{
> --  SHELL_VAR *v;
> --
> --  v = find_variable ("FUNCNAME");
> --  if (v)
> --    return v;
> --#if defined (ARRAY_VARS)
> --  INIT_DYNAMIC_ARRAY_VAR ("FUNCNAME", get_funcname, null_array_assign);
> --#else
> --  INIT_DYNAMIC_VAR ("FUNCNAME", (char *)NULL, get_funcname, null_assign);
> --#endif
> --  VSETATTR (v, att_invisible|att_noassign);
> --  return v;
> --}
> --
> --static void
> --initialize_dynamic_variables ()
> --{
> --  SHELL_VAR *v;
> --
> --  v = init_seconds_var ();
> --
> --  INIT_DYNAMIC_VAR ("BASH_COMMAND", (char *)NULL, get_bash_command, (sh_var_assign_func_t *)NULL);
> --  INIT_DYNAMIC_VAR ("BASH_SUBSHELL", (char *)NULL, get_subshell, assign_subshell);
> --
> --  INIT_DYNAMIC_VAR ("RANDOM", (char *)NULL, get_random, assign_random);
> --  VSETATTR (v, att_integer);
> --  INIT_DYNAMIC_VAR ("LINENO", (char *)NULL, get_lineno, assign_lineno);
> --  VSETATTR (v, att_integer);
> --
> --  INIT_DYNAMIC_VAR ("BASHPID", (char *)NULL, get_bashpid, null_assign);
> --  VSETATTR (v, att_integer|att_readonly);
> --
> --#if defined (HISTORY)
> --  INIT_DYNAMIC_VAR ("HISTCMD", (char *)NULL, get_histcmd, (sh_var_assign_func_t *)NULL);
> --  VSETATTR (v, att_integer);
> --#endif
> --
> --#if defined (READLINE)
> --  INIT_DYNAMIC_VAR ("COMP_WORDBREAKS", (char *)NULL, get_comp_wordbreaks, assign_comp_wordbreaks);
> --#endif
> --
> --#if defined (PUSHD_AND_POPD) && defined (ARRAY_VARS)
> --  v = init_dynamic_array_var ("DIRSTACK", get_dirstack, assign_dirstack, 0);
> --#endif /* PUSHD_AND_POPD && ARRAY_VARS */
> --
> --#if defined (ARRAY_VARS)
> --  v = init_dynamic_array_var ("GROUPS", get_groupset, null_array_assign, att_noassign);
> --
> --#  if defined (DEBUGGER)
> --  v = init_dynamic_array_var ("BASH_ARGC", get_self, null_array_assign, att_noassign|att_nounset);
> --  v = init_dynamic_array_var ("BASH_ARGV", get_self, null_array_assign, att_noassign|att_nounset);
> --#  endif /* DEBUGGER */
> --  v = init_dynamic_array_var ("BASH_SOURCE", get_self, null_array_assign, att_noassign|att_nounset);
> --  v = init_dynamic_array_var ("BASH_LINENO", get_self, null_array_assign, att_noassign|att_nounset);
> --
> --  v = init_dynamic_assoc_var ("BASH_CMDS", get_hashcmd, assign_hashcmd, att_nofree);
> --#  if defined (ALIAS)
> --  v = init_dynamic_assoc_var ("BASH_ALIASES", get_aliasvar, assign_aliasvar, att_nofree);
> --#  endif
> --#endif
> --
> --  v = init_funcname_var ();
> --}
> --
> --/* **************************************************************** */
> --/*								    */
> --/*		Retrieving variables and values			    */
> --/*								    */
> --/* **************************************************************** */
> --
> --/* How to get a pointer to the shell variable or function named NAME.
> --   HASHED_VARS is a pointer to the hash table containing the list
> --   of interest (either variables or functions). */
> --
> --static SHELL_VAR *
> --hash_lookup (name, hashed_vars)
> --     const char *name;
> --     HASH_TABLE *hashed_vars;
> --{
> --  BUCKET_CONTENTS *bucket;
> --
> --  bucket = hash_search (name, hashed_vars, 0);
> --  /* If we find the name in HASHED_VARS, set LAST_TABLE_SEARCHED to that
> --     table. */
> --  if (bucket)
> --    last_table_searched = hashed_vars;
> --  return (bucket ? (SHELL_VAR *)bucket->data : (SHELL_VAR *)NULL);
> --}
> --
> --SHELL_VAR *
> --var_lookup (name, vcontext)
> --     const char *name;
> --     VAR_CONTEXT *vcontext;
> --{
> --  VAR_CONTEXT *vc;
> --  SHELL_VAR *v;
> --
> --  v = (SHELL_VAR *)NULL;
> --  for (vc = vcontext; vc; vc = vc->down)
> --    if (v = hash_lookup (name, vc->table))
> --      break;
> --
> --  return v;
> --}
> --
> --/* Look up the variable entry named NAME.  If SEARCH_TEMPENV is non-zero,
> --   then also search the temporarily built list of exported variables.
> --   The lookup order is:
> --	temporary_env
> --	shell_variables list
> --*/
> --
> --SHELL_VAR *
> --find_variable_internal (name, force_tempenv)
> --     const char *name;
> --     int force_tempenv;
> --{
> --  SHELL_VAR *var;
> --  int search_tempenv;
> --  VAR_CONTEXT *vc;
> --
> --  var = (SHELL_VAR *)NULL;
> --
> --  /* If explicitly requested, first look in the temporary environment for
> --     the variable.  This allows constructs such as "foo=x eval 'echo $foo'"
> --     to get the `exported' value of $foo.  This happens if we are executing
> --     a function or builtin, or if we are looking up a variable in a
> --     "subshell environment". */
> --  search_tempenv = force_tempenv || (expanding_redir == 0 && subshell_environment);
> --
> --  if (search_tempenv && temporary_env)		
> --    var = hash_lookup (name, temporary_env);
> --
> --  vc = shell_variables;
> --#if 0
> --if (search_tempenv == 0 && /* (subshell_environment & SUBSHELL_COMSUB) && */
> --    expanding_redir &&
> --    (this_shell_builtin == eval_builtin || this_shell_builtin == command_builtin))
> --  {
> --  itrace("find_variable_internal: search_tempenv == 0: skipping VC_BLTNENV");
> --  while (vc && (vc->flags & VC_BLTNENV))
> --    vc = vc->down;
> --  if (vc == 0)
> --    vc = shell_variables;
> --  }
> --#endif
> --
> --  if (var == 0)
> --    var = var_lookup (name, vc);
> --
> --  if (var == 0)
> --    return ((SHELL_VAR *)NULL);
> --
> --  return (var->dynamic_value ? (*(var->dynamic_value)) (var) : var);
> --}
> --
> --/* Look up and resolve the chain of nameref variables starting at V all the
> --   way to NULL or non-nameref. */
> --SHELL_VAR *
> --find_variable_nameref (v)
> --     SHELL_VAR *v;
> --{
> --  int level;
> --  char *newname;
> --  SHELL_VAR *orig, *oldv;
> --
> --  level = 0;
> --  orig = v;
> --  while (v && nameref_p (v))
> --    {
> --      level++;
> --      if (level > NAMEREF_MAX)
> --	return ((SHELL_VAR *)0);	/* error message here? */
> --      newname = nameref_cell (v);
> --      if (newname == 0 || *newname == '\0')
> --	return ((SHELL_VAR *)0);
> --      oldv = v;
> --      v = find_variable_internal (newname, (expanding_redir == 0 && (assigning_in_environment || executing_builtin)));
> --      if (v == orig || v == oldv)
> --	{
> --	  internal_warning (_("%s: circular name reference"), orig->name);
> --	  return ((SHELL_VAR *)0);
> --	}
> --    }
> --  return v;
> --}
> --
> --/* Resolve the chain of nameref variables for NAME.  XXX - could change later */
> --SHELL_VAR *
> --find_variable_last_nameref (name)
> --     const char *name;
> --{
> --  SHELL_VAR *v, *nv;
> --  char *newname;
> --  int level;
> --
> --  nv = v = find_variable_noref (name);
> --  level = 0;
> --  while (v && nameref_p (v))
> --    {
> --      level++;
> --      if (level > NAMEREF_MAX)
> --        return ((SHELL_VAR *)0);	/* error message here? */
> --      newname = nameref_cell (v);
> --      if (newname == 0 || *newname == '\0')
> --	return ((SHELL_VAR *)0);
> --      nv = v;
> --      v = find_variable_internal (newname, (expanding_redir == 0 && (assigning_in_environment || executing_builtin)));
> --    }
> --  return nv;
> --}
> --
> --/* Resolve the chain of nameref variables for NAME.  XXX - could change later */
> --SHELL_VAR *
> --find_global_variable_last_nameref (name)
> --     const char *name;
> --{
> --  SHELL_VAR *v, *nv;
> --  char *newname;
> --  int level;
> --
> --  nv = v = find_global_variable_noref (name);
> --  level = 0;
> --  while (v && nameref_p (v))
> --    {
> --      level++;
> --      if (level > NAMEREF_MAX)
> --        return ((SHELL_VAR *)0);	/* error message here? */
> --      newname = nameref_cell (v);
> --      if (newname == 0 || *newname == '\0')
> --	return ((SHELL_VAR *)0);
> --      nv = v;
> --      v = find_global_variable_noref (newname);
> --    }
> --  return nv;
> --}
> --
> --static SHELL_VAR *
> --find_nameref_at_context (v, vc)
> --     SHELL_VAR *v;
> --     VAR_CONTEXT *vc;
> --{
> --  SHELL_VAR *nv, *nv2;
> --  VAR_CONTEXT *nvc;
> --  char *newname;
> --  int level;
> --
> --  nv = v;
> --  level = 1;
> --  while (nv && nameref_p (nv))
> --    {
> --      level++;
> --      if (level > NAMEREF_MAX)
> --        return ((SHELL_VAR *)NULL);
> --      newname = nameref_cell (nv);
> --      if (newname == 0 || *newname == '\0')
> --        return ((SHELL_VAR *)NULL);      
> --      nv2 = hash_lookup (newname, vc->table);
> --      if (nv2 == 0)
> --        break;
> --      nv = nv2;
> --    }
> --  return nv;
> --}
> --
> --/* Do nameref resolution from the VC, which is the local context for some
> --   function or builtin, `up' the chain to the global variables context.  If
> --   NVCP is not NULL, return the variable context where we finally ended the
> --   nameref resolution (so the bind_variable_internal can use the correct
> --   variable context and hash table). */
> --static SHELL_VAR *
> --find_variable_nameref_context (v, vc, nvcp)
> --     SHELL_VAR *v;
> --     VAR_CONTEXT *vc;
> --     VAR_CONTEXT **nvcp;
> --{
> --  SHELL_VAR *nv, *nv2;
> --  VAR_CONTEXT *nvc;
> --
> --  /* Look starting at the current context all the way `up' */
> --  for (nv = v, nvc = vc; nvc; nvc = nvc->down)
> --    {
> --      nv2 = find_nameref_at_context (nv, nvc);
> --      if (nv2 == 0)
> --        continue;
> --      nv = nv2;
> --      if (*nvcp)
> --        *nvcp = nvc;
> --      if (nameref_p (nv) == 0)
> --        break;
> --    }
> --  return (nameref_p (nv) ? (SHELL_VAR *)NULL : nv);
> --}
> --
> --/* Do nameref resolution from the VC, which is the local context for some
> --   function or builtin, `up' the chain to the global variables context.  If
> --   NVCP is not NULL, return the variable context where we finally ended the
> --   nameref resolution (so the bind_variable_internal can use the correct
> --   variable context and hash table). */
> --static SHELL_VAR *
> --find_variable_last_nameref_context (v, vc, nvcp)
> --     SHELL_VAR *v;
> --     VAR_CONTEXT *vc;
> --     VAR_CONTEXT **nvcp;
> --{
> --  SHELL_VAR *nv, *nv2;
> --  VAR_CONTEXT *nvc;
> --
> --  /* Look starting at the current context all the way `up' */
> --  for (nv = v, nvc = vc; nvc; nvc = nvc->down)
> --    {
> --      nv2 = find_nameref_at_context (nv, nvc);
> --      if (nv2 == 0)
> --	continue;
> --      nv = nv2;
> --      if (*nvcp)
> --        *nvcp = nvc;
> --    }
> --  return (nameref_p (nv) ? nv : (SHELL_VAR *)NULL);
> --}
> --
> --/* Find a variable, forcing a search of the temporary environment first */
> --SHELL_VAR *
> --find_variable_tempenv (name)
> --     const char *name;
> --{
> --  SHELL_VAR *var;
> --
> --  var = find_variable_internal (name, 1);
> --  if (var && nameref_p (var))
> --    var = find_variable_nameref (var);
> --  return (var);
> --}
> --
> --/* Find a variable, not forcing a search of the temporary environment first */
> --SHELL_VAR *
> --find_variable_notempenv (name)
> --     const char *name;
> --{
> --  SHELL_VAR *var;
> --
> --  var = find_variable_internal (name, 0);
> --  if (var && nameref_p (var))
> --    var = find_variable_nameref (var);
> --  return (var);
> --}
> --
> --SHELL_VAR *
> --find_global_variable (name)
> --     const char *name;
> --{
> --  SHELL_VAR *var;
> --
> --  var = var_lookup (name, global_variables);
> --  if (var && nameref_p (var))
> --    var = find_variable_nameref (var);
> --
> --  if (var == 0)
> --    return ((SHELL_VAR *)NULL);
> --
> --  return (var->dynamic_value ? (*(var->dynamic_value)) (var) : var);
> --}
> --
> --SHELL_VAR *
> --find_global_variable_noref (name)
> --     const char *name;
> --{
> --  SHELL_VAR *var;
> --
> --  var = var_lookup (name, global_variables);
> --
> --  if (var == 0)
> --    return ((SHELL_VAR *)NULL);
> --
> --  return (var->dynamic_value ? (*(var->dynamic_value)) (var) : var);
> --}
> --
> --SHELL_VAR *
> --find_shell_variable (name)
> --     const char *name;
> --{
> --  SHELL_VAR *var;
> --
> --  var = var_lookup (name, shell_variables);
> --  if (var && nameref_p (var))
> --    var = find_variable_nameref (var);
> --
> --  if (var == 0)
> --    return ((SHELL_VAR *)NULL);
> --
> --  return (var->dynamic_value ? (*(var->dynamic_value)) (var) : var);
> --}
> --
> --/* Look up the variable entry named NAME.  Returns the entry or NULL. */
> --SHELL_VAR *
> --find_variable (name)
> --     const char *name;
> --{
> --  SHELL_VAR *v;
> --
> --  last_table_searched = 0;
> --  v = find_variable_internal (name, (expanding_redir == 0 && (assigning_in_environment || executing_builtin)));
> --  if (v && nameref_p (v))
> --    v = find_variable_nameref (v);
> --  return v;
> --}
> --
> --SHELL_VAR *
> --find_variable_noref (name)
> --     const char *name;
> --{
> --  SHELL_VAR *v;
> --
> --  v = find_variable_internal (name, (expanding_redir == 0 && (assigning_in_environment || executing_builtin)));
> --  return v;
> --}
> --
> --/* Look up the function entry whose name matches STRING.
> --   Returns the entry or NULL. */
> --SHELL_VAR *
> --find_function (name)
> --     const char *name;
> --{
> --  return (hash_lookup (name, shell_functions));
> --}
> --
> --/* Find the function definition for the shell function named NAME.  Returns
> --   the entry or NULL. */
> --FUNCTION_DEF *
> --find_function_def (name)
> --     const char *name;
> --{
> --#if defined (DEBUGGER)
> --  return ((FUNCTION_DEF *)hash_lookup (name, shell_function_defs));
> --#else
> --  return ((FUNCTION_DEF *)0);
> --#endif
> --}
> --
> --/* Return the value of VAR.  VAR is assumed to have been the result of a
> --   lookup without any subscript, if arrays are compiled into the shell. */
> --char *
> --get_variable_value (var)
> --     SHELL_VAR *var;
> --{
> --  if (var == 0)
> --    return ((char *)NULL);
> --#if defined (ARRAY_VARS)
> --  else if (array_p (var))
> --    return (array_reference (array_cell (var), 0));
> --  else if (assoc_p (var))
> --    return (assoc_reference (assoc_cell (var), "0"));
> --#endif
> --  else
> --    return (value_cell (var));
> --}
> --
> --/* Return the string value of a variable.  Return NULL if the variable
> --   doesn't exist.  Don't cons a new string.  This is a potential memory
> --   leak if the variable is found in the temporary environment.  Since
> --   functions and variables have separate name spaces, returns NULL if
> --   var_name is a shell function only. */
> --char *
> --get_string_value (var_name)
> --     const char *var_name;
> --{
> --  SHELL_VAR *var;
> --
> --  var = find_variable (var_name);
> --  return ((var) ? get_variable_value (var) : (char *)NULL);
> --}
> --
> --/* This is present for use by the tilde and readline libraries. */
> --char *
> --sh_get_env_value (v)
> --     const char *v;
> --{
> --  return get_string_value (v);
> --}
> --
> --/* **************************************************************** */
> --/*								    */
> --/*		  Creating and setting variables		    */
> --/*								    */
> --/* **************************************************************** */
> --
> --/* Set NAME to VALUE if NAME has no value. */
> --SHELL_VAR *
> --set_if_not (name, value)
> --     char *name, *value;
> --{
> --  SHELL_VAR *v;
> --
> --  if (shell_variables == 0)
> --    create_variable_tables ();
> --
> --  v = find_variable (name);
> --  if (v == 0)
> --    v = bind_variable_internal (name, value, global_variables->table, HASH_NOSRCH, 0);
> --  return (v);
> --}
> --
> --/* Create a local variable referenced by NAME. */
> --SHELL_VAR *
> --make_local_variable (name)
> --     const char *name;
> --{
> --  SHELL_VAR *new_var, *old_var;
> --  VAR_CONTEXT *vc;
> --  int was_tmpvar;
> --  char *tmp_value;
> --
> --  /* local foo; local foo;  is a no-op. */
> --  old_var = find_variable (name);
> --  if (old_var && local_p (old_var) && old_var->context == variable_context)
> --    return (old_var);
> --
> --  was_tmpvar = old_var && tempvar_p (old_var);
> --  /* If we're making a local variable in a shell function, the temporary env
> --     has already been merged into the function's variable context stack.  We
> --     can assume that a temporary var in the same context appears in the same
> --     VAR_CONTEXT and can safely be returned without creating a new variable
> --     (which results in duplicate names in the same VAR_CONTEXT->table */
> --  /* We can't just test tmpvar_p because variables in the temporary env given
> --     to a shell function appear in the function's local variable VAR_CONTEXT
> --     but retain their tempvar attribute.  We want temporary variables that are
> --     found in temporary_env, hence the test for last_table_searched, which is
> --     set in hash_lookup and only (so far) checked here. */
> --  if (was_tmpvar && old_var->context == variable_context && last_table_searched != temporary_env)
> --    {
> --      VUNSETATTR (old_var, att_invisible);
> --      return (old_var);
> --    }
> --  if (was_tmpvar)
> --    tmp_value = value_cell (old_var);
> --
> --  for (vc = shell_variables; vc; vc = vc->down)
> --    if (vc_isfuncenv (vc) && vc->scope == variable_context)
> --      break;
> --
> --  if (vc == 0)
> --    {
> --      internal_error (_("make_local_variable: no function context at current scope"));
> --      return ((SHELL_VAR *)NULL);
> --    }
> --  else if (vc->table == 0)
> --    vc->table = hash_create (TEMPENV_HASH_BUCKETS);
> --
> --  /* Since this is called only from the local/declare/typeset code, we can
> --     call builtin_error here without worry (of course, it will also work
> --     for anything that sets this_command_name).  Variables with the `noassign'
> --     attribute may not be made local.  The test against old_var's context
> --     level is to disallow local copies of readonly global variables (since I
> --     believe that this could be a security hole).  Readonly copies of calling
> --     function local variables are OK. */
> --  if (old_var && (noassign_p (old_var) ||
> --		 (readonly_p (old_var) && old_var->context == 0)))
> --    {
> --      if (readonly_p (old_var))
> --	sh_readonly (name);
> --      else if (noassign_p (old_var))
> --	builtin_error (_("%s: variable may not be assigned value"), name);
> --#if 0
> --      /* Let noassign variables through with a warning */
> --      if (readonly_p (old_var))
> --#endif
> --	return ((SHELL_VAR *)NULL);
> --    }
> --
> --  if (old_var == 0)
> --    new_var = make_new_variable (name, vc->table);
> --  else
> --    {
> --      new_var = make_new_variable (name, vc->table);
> --
> --      /* If we found this variable in one of the temporary environments,
> --	 inherit its value.  Watch to see if this causes problems with
> --	 things like `x=4 local x'. XXX - see above for temporary env
> --	 variables with the same context level as variable_context */
> --      /* XXX - we should only do this if the variable is not an array. */
> --      if (was_tmpvar)
> --	var_setvalue (new_var, savestring (tmp_value));
> --
> --      new_var->attributes = exported_p (old_var) ? att_exported : 0;
> --    }
> --
> --  vc->flags |= VC_HASLOCAL;
> --
> --  new_var->context = variable_context;
> --  VSETATTR (new_var, att_local);
> --
> --  if (ifsname (name))
> --    setifs (new_var);
> --
> --  if (was_tmpvar == 0)
> --    VSETATTR (new_var, att_invisible);	/* XXX */
> --  return (new_var);
> --}
> --
> --/* Create a new shell variable with name NAME. */
> --static SHELL_VAR *
> --new_shell_variable (name)
> --     const char *name;
> --{
> --  SHELL_VAR *entry;
> --
> --  entry = (SHELL_VAR *)xmalloc (sizeof (SHELL_VAR));
> --
> --  entry->name = savestring (name);
> --  var_setvalue (entry, (char *)NULL);
> --  CLEAR_EXPORTSTR (entry);
> --
> --  entry->dynamic_value = (sh_var_value_func_t *)NULL;
> --  entry->assign_func = (sh_var_assign_func_t *)NULL;
> --
> --  entry->attributes = 0;
> --
> --  /* Always assume variables are to be made at toplevel!
> --     make_local_variable has the responsibility of changing the
> --     variable context. */
> --  entry->context = 0;
> --
> --  return (entry);
> --}
> --
> --/* Create a new shell variable with name NAME and add it to the hash table
> --   TABLE. */
> --static SHELL_VAR *
> --make_new_variable (name, table)
> --     const char *name;
> --     HASH_TABLE *table;
> --{
> --  SHELL_VAR *entry;
> --  BUCKET_CONTENTS *elt;
> --
> --  entry = new_shell_variable (name);
> --
> --  /* Make sure we have a shell_variables hash table to add to. */
> --  if (shell_variables == 0)
> --    create_variable_tables ();
> --
> --  elt = hash_insert (savestring (name), table, HASH_NOSRCH);
> --  elt->data = (PTR_T)entry;
> --
> --  return entry;
> --}
> --
> --#if defined (ARRAY_VARS)
> --SHELL_VAR *
> --make_new_array_variable (name)
> --     char *name;
> --{
> --  SHELL_VAR *entry;
> --  ARRAY *array;
> --
> --  entry = make_new_variable (name, global_variables->table);
> --  array = array_create ();
> --
> --  var_setarray (entry, array);
> --  VSETATTR (entry, att_array);
> --  return entry;
> --}
> --
> --SHELL_VAR *
> --make_local_array_variable (name, assoc_ok)
> --     char *name;
> --     int assoc_ok;
> --{
> --  SHELL_VAR *var;
> --  ARRAY *array;
> --
> --  var = make_local_variable (name);
> --  if (var == 0 || array_p (var) || (assoc_ok && assoc_p (var)))
> --    return var;
> --
> --  array = array_create ();
> --
> --  dispose_variable_value (var);
> --  var_setarray (var, array);
> --  VSETATTR (var, att_array);
> --  return var;
> --}
> --
> --SHELL_VAR *
> --make_new_assoc_variable (name)
> --     char *name;
> --{
> --  SHELL_VAR *entry;
> --  HASH_TABLE *hash;
> --
> --  entry = make_new_variable (name, global_variables->table);
> --  hash = assoc_create (0);
> --
> --  var_setassoc (entry, hash);
> --  VSETATTR (entry, att_assoc);
> --  return entry;
> --}
> --
> --SHELL_VAR *
> --make_local_assoc_variable (name)
> --     char *name;
> --{
> --  SHELL_VAR *var;
> --  HASH_TABLE *hash;
> --
> --  var = make_local_variable (name);
> --  if (var == 0 || assoc_p (var))
> --    return var;
> --
> --  dispose_variable_value (var);
> --  hash = assoc_create (0);
> --
> --  var_setassoc (var, hash);
> --  VSETATTR (var, att_assoc);
> --  return var;
> --}
> --#endif
> --
> --char *
> --make_variable_value (var, value, flags)
> --     SHELL_VAR *var;
> --     char *value;
> --     int flags;
> --{
> --  char *retval, *oval;
> --  intmax_t lval, rval;
> --  int expok, olen, op;
> --
> --  /* If this variable has had its type set to integer (via `declare -i'),
> --     then do expression evaluation on it and store the result.  The
> --     functions in expr.c (evalexp()) and bind_int_variable() are responsible
> --     for turning off the integer flag if they don't want further
> --     evaluation done. */
> --  if (integer_p (var))
> --    {
> --      if (flags & ASS_APPEND)
> --	{
> --	  oval = value_cell (var);
> --	  lval = evalexp (oval, &expok);	/* ksh93 seems to do this */
> --	  if (expok == 0)
> --	    {
> --	      top_level_cleanup ();
> --	      jump_to_top_level (DISCARD);
> --	    }
> --	}
> --      rval = evalexp (value, &expok);
> --      if (expok == 0)
> --	{
> --	  top_level_cleanup ();
> --	  jump_to_top_level (DISCARD);
> --	}
> --      /* This can be fooled if the variable's value changes while evaluating
> --	 `rval'.  We can change it if we move the evaluation of lval to here. */
> --      if (flags & ASS_APPEND)
> --	rval += lval;
> --      retval = itos (rval);
> --    }
> --#if defined (CASEMOD_ATTRS)
> --  else if (capcase_p (var) || uppercase_p (var) || lowercase_p (var))
> --    {
> --      if (flags & ASS_APPEND)
> --	{
> --	  oval = get_variable_value (var);
> --	  if (oval == 0)	/* paranoia */
> --	    oval = "";
> --	  olen = STRLEN (oval);
> --	  retval = (char *)xmalloc (olen + (value ? STRLEN (value) : 0) + 1);
> --	  strcpy (retval, oval);
> --	  if (value)
> --	    strcpy (retval+olen, value);
> --	}
> --      else if (*value)
> --	retval = savestring (value);
> --      else
> --	{
> --	  retval = (char *)xmalloc (1);
> --	  retval[0] = '\0';
> --	}
> --      op = capcase_p (var) ? CASE_CAPITALIZE
> --			 : (uppercase_p (var) ? CASE_UPPER : CASE_LOWER);
> --      oval = sh_modcase (retval, (char *)0, op);
> --      free (retval);
> --      retval = oval;
> --    }
> --#endif /* CASEMOD_ATTRS */
> --  else if (value)
> --    {
> --      if (flags & ASS_APPEND)
> --	{
> --	  oval = get_variable_value (var);
> --	  if (oval == 0)	/* paranoia */
> --	    oval = "";
> --	  olen = STRLEN (oval);
> --	  retval = (char *)xmalloc (olen + (value ? STRLEN (value) : 0) + 1);
> --	  strcpy (retval, oval);
> --	  if (value)
> --	    strcpy (retval+olen, value);
> --	}
> --      else if (*value)
> --	retval = savestring (value);
> --      else
> --	{
> --	  retval = (char *)xmalloc (1);
> --	  retval[0] = '\0';
> --	}
> --    }
> --  else
> --    retval = (char *)NULL;
> --
> --  return retval;
> --}
> --
> --/* Bind a variable NAME to VALUE in the HASH_TABLE TABLE, which may be the
> --   temporary environment (but usually is not). */
> --static SHELL_VAR *
> --bind_variable_internal (name, value, table, hflags, aflags)
> --     const char *name;
> --     char *value;
> --     HASH_TABLE *table;
> --     int hflags, aflags;
> --{
> --  char *newval;
> --  SHELL_VAR *entry;
> --
> --  entry = (hflags & HASH_NOSRCH) ? (SHELL_VAR *)NULL : hash_lookup (name, table);
> --  /* Follow the nameref chain here if this is the global variables table */
> --  if (entry && nameref_p (entry) && (invisible_p (entry) == 0) && table == global_variables->table)
> --    {
> --      entry = find_global_variable (entry->name);
> --      /* Let's see if we have a nameref referencing a variable that hasn't yet
> --	 been created. */
> --      if (entry == 0)
> --	entry = find_variable_last_nameref (name);	/* XXX */
> --      if (entry == 0)					/* just in case */
> --        return (entry);
> --    }
> --
> --  /* The first clause handles `declare -n ref; ref=x;' */
> --  if (entry && invisible_p (entry) && nameref_p (entry))
> --    goto assign_value;
> --  else if (entry && nameref_p (entry))
> --    {
> --      newval = nameref_cell (entry);
> --#if defined (ARRAY_VARS)
> --      /* declare -n foo=x[2] */
> --      if (valid_array_reference (newval))
> --        /* XXX - should it be aflags? */
> --	entry = assign_array_element (newval, make_variable_value (entry, value, 0), aflags);
> --      else
> --#endif
> --      {
> --      entry = make_new_variable (newval, table);
> --      var_setvalue (entry, make_variable_value (entry, value, 0));
> --      }
> --    }
> --  else if (entry == 0)
> --    {
> --      entry = make_new_variable (name, table);
> --      var_setvalue (entry, make_variable_value (entry, value, 0)); /* XXX */
> --    }
> --  else if (entry->assign_func)	/* array vars have assign functions now */
> --    {
> --      INVALIDATE_EXPORTSTR (entry);
> --      newval = (aflags & ASS_APPEND) ? make_variable_value (entry, value, aflags) : value;
> --      if (assoc_p (entry))
> --	entry = (*(entry->assign_func)) (entry, newval, -1, savestring ("0"));
> --      else if (array_p (entry))
> --	entry = (*(entry->assign_func)) (entry, newval, 0, 0);
> --      else
> --	entry = (*(entry->assign_func)) (entry, newval, -1, 0);
> --      if (newval != value)
> --	free (newval);
> --      return (entry);
> --    }
> --  else
> --    {
> --assign_value:
> --      if (readonly_p (entry) || noassign_p (entry))
> --	{
> --	  if (readonly_p (entry))
> --	    err_readonly (name);
> --	  return (entry);
> --	}
> --
> --      /* Variables which are bound are visible. */
> --      VUNSETATTR (entry, att_invisible);
> --
> --#if defined (ARRAY_VARS)
> --      if (assoc_p (entry) || array_p (entry))
> --        newval = make_array_variable_value (entry, 0, "0", value, aflags);
> --      else
> --#endif
> --
> --      newval = make_variable_value (entry, value, aflags);	/* XXX */
> --
> --      /* Invalidate any cached export string */
> --      INVALIDATE_EXPORTSTR (entry);
> --
> --#if defined (ARRAY_VARS)
> --      /* XXX -- this bears looking at again -- XXX */
> --      /* If an existing array variable x is being assigned to with x=b or
> --	 `read x' or something of that nature, silently convert it to
> --	 x[0]=b or `read x[0]'. */
> --      if (assoc_p (entry))
> --	{
> --	  assoc_insert (assoc_cell (entry), savestring ("0"), newval);
> --	  free (newval);
> --	}
> --      else if (array_p (entry))
> --	{
> --	  array_insert (array_cell (entry), 0, newval);
> --	  free (newval);
> --	}
> --      else
> --#endif
> --	{
> --	  FREE (value_cell (entry));
> --	  var_setvalue (entry, newval);
> --	}
> --    }
> --
> --  if (mark_modified_vars)
> --    VSETATTR (entry, att_exported);
> --
> --  if (exported_p (entry))
> --    array_needs_making = 1;
> --
> --  return (entry);
> --}
> --	
> --/* Bind a variable NAME to VALUE.  This conses up the name
> --   and value strings.  If we have a temporary environment, we bind there
> --   first, then we bind into shell_variables. */
> --
> --SHELL_VAR *
> --bind_variable (name, value, flags)
> --     const char *name;
> --     char *value;
> --     int flags;
> --{
> --  SHELL_VAR *v, *nv;
> --  VAR_CONTEXT *vc, *nvc;
> --  int level;
> --
> --  if (shell_variables == 0)
> --    create_variable_tables ();
> --
> --  /* If we have a temporary environment, look there first for the variable,
> --     and, if found, modify the value there before modifying it in the
> --     shell_variables table.  This allows sourced scripts to modify values
> --     given to them in a temporary environment while modifying the variable
> --     value that the caller sees. */
> --  if (temporary_env)
> --    bind_tempenv_variable (name, value);
> --
> --  /* XXX -- handle local variables here. */
> --  for (vc = shell_variables; vc; vc = vc->down)
> --    {
> --      if (vc_isfuncenv (vc) || vc_isbltnenv (vc))
> --	{
> --	  v = hash_lookup (name, vc->table);
> --	  nvc = vc;
> --	  if (v && nameref_p (v))
> --	    {
> --	      nv = find_variable_nameref_context (v, vc, &nvc);
> --	      if (nv == 0)
> --		{
> --		  nv = find_variable_last_nameref_context (v, vc, &nvc);
> --		  if (nv && nameref_p (nv))
> --		    {
> --		      /* If this nameref variable doesn't have a value yet,
> --			 set the value.  Otherwise, assign using the value as
> --			 normal. */
> --		      if (nameref_cell (nv) == 0)
> --			return (bind_variable_internal (nv->name, value, nvc->table, 0, flags));
> --		      return (bind_variable_internal (nameref_cell (nv), value, nvc->table, 0, flags));
> --		    }
> --		  else
> --		    v = nv;
> --		}
> --	      else
> --	        v = nv;
> --	    }
> --	  if (v)
> --	    return (bind_variable_internal (v->name, value, nvc->table, 0, flags));
> --	}
> --    }
> --  /* bind_variable_internal will handle nameref resolution in this case */
> --  return (bind_variable_internal (name, value, global_variables->table, 0, flags));
> --}
> --
> --SHELL_VAR *
> --bind_global_variable (name, value, flags)
> --     const char *name;
> --     char *value;
> --     int flags;
> --{
> --  SHELL_VAR *v, *nv;
> --  VAR_CONTEXT *vc, *nvc;
> --  int level;
> --
> --  if (shell_variables == 0)
> --    create_variable_tables ();
> --
> --  /* bind_variable_internal will handle nameref resolution in this case */
> --  return (bind_variable_internal (name, value, global_variables->table, 0, flags));
> --}
> --
> --/* Make VAR, a simple shell variable, have value VALUE.  Once assigned a
> --   value, variables are no longer invisible.  This is a duplicate of part
> --   of the internals of bind_variable.  If the variable is exported, or
> --   all modified variables should be exported, mark the variable for export
> --   and note that the export environment needs to be recreated. */
> --SHELL_VAR *
> --bind_variable_value (var, value, aflags)
> --     SHELL_VAR *var;
> --     char *value;
> --     int aflags;
> --{
> --  char *t;
> --  int invis;
> --
> --  invis = invisible_p (var);
> --  VUNSETATTR (var, att_invisible);
> --
> --  if (var->assign_func)
> --    {
> --      /* If we're appending, we need the old value, so use
> --	 make_variable_value */
> --      t = (aflags & ASS_APPEND) ? make_variable_value (var, value, aflags) : value;
> --      (*(var->assign_func)) (var, t, -1, 0);
> --      if (t != value && t)
> --	free (t);      
> --    }
> --  else
> --    {
> --      t = make_variable_value (var, value, aflags);
> --#if defined (ARRAY_VARS)
> --      if ((aflags & ASS_NAMEREF) && (t == 0 || *t == 0 || (legal_identifier (t) == 0 && valid_array_reference (t) == 0)))
> --#else
> --      if ((aflags & ASS_NAMEREF) && (t == 0 || *t == 0 || legal_identifier (t) == 0))
> --#endif
> --	{
> --	  free (t);
> --	  if (invis)
> --	    VSETATTR (var, att_invisible);	/* XXX */
> --	  return ((SHELL_VAR *)NULL);
> --	}
> --      FREE (value_cell (var));
> --      var_setvalue (var, t);
> --    }
> --
> --  INVALIDATE_EXPORTSTR (var);
> --
> --  if (mark_modified_vars)
> --    VSETATTR (var, att_exported);
> --
> --  if (exported_p (var))
> --    array_needs_making = 1;
> --
> --  return (var);
> --}
> --
> --/* Bind/create a shell variable with the name LHS to the RHS.
> --   This creates or modifies a variable such that it is an integer.
> --
> --   This used to be in expr.c, but it is here so that all of the
> --   variable binding stuff is localized.  Since we don't want any
> --   recursive evaluation from bind_variable() (possible without this code,
> --   since bind_variable() calls the evaluator for variables with the integer
> --   attribute set), we temporarily turn off the integer attribute for each
> --   variable we set here, then turn it back on after binding as necessary. */
> --
> --SHELL_VAR *
> --bind_int_variable (lhs, rhs)
> --     char *lhs, *rhs;
> --{
> --  register SHELL_VAR *v;
> --  int isint, isarr, implicitarray;
> --
> --  isint = isarr = implicitarray = 0;
> --#if defined (ARRAY_VARS)
> --  if (valid_array_reference (lhs))
> --    {
> --      isarr = 1;
> --      v = array_variable_part (lhs, (char **)0, (int *)0);
> --    }
> --  else
> --#endif
> --    v = find_variable (lhs);
> --
> --  if (v)
> --    {
> --      isint = integer_p (v);
> --      VUNSETATTR (v, att_integer);
> --#if defined (ARRAY_VARS)
> --      if (array_p (v) && isarr == 0)
> --	implicitarray = 1;
> --#endif
> --    }
> --
> --#if defined (ARRAY_VARS)
> --  if (isarr)
> --    v = assign_array_element (lhs, rhs, 0);
> --  else if (implicitarray)
> --    v = bind_array_variable (lhs, 0, rhs, 0);
> --  else
> --#endif
> --    v = bind_variable (lhs, rhs, 0);
> --
> --  if (v && isint)
> --    VSETATTR (v, att_integer);
> --
> --  VUNSETATTR (v, att_invisible);
> --
> --  return (v);
> --}
> --
> --SHELL_VAR *
> --bind_var_to_int (var, val)
> --     char *var;
> --     intmax_t val;
> --{
> --  char ibuf[INT_STRLEN_BOUND (intmax_t) + 1], *p;
> --
> --  p = fmtulong (val, 10, ibuf, sizeof (ibuf), 0);
> --  return (bind_int_variable (var, p));
> --}
> --
> --/* Do a function binding to a variable.  You pass the name and
> --   the command to bind to.  This conses the name and command. */
> --SHELL_VAR *
> --bind_function (name, value)
> --     const char *name;
> --     COMMAND *value;
> --{
> --  SHELL_VAR *entry;
> --
> --  entry = find_function (name);
> --  if (entry == 0)
> --    {
> --      BUCKET_CONTENTS *elt;
> --
> --      elt = hash_insert (savestring (name), shell_functions, HASH_NOSRCH);
> --      entry = new_shell_variable (name);
> --      elt->data = (PTR_T)entry;
> --    }
> --  else
> --    INVALIDATE_EXPORTSTR (entry);
> --
> --  if (var_isset (entry))
> --    dispose_command (function_cell (entry));
> --
> --  if (value)
> --    var_setfunc (entry, copy_command (value));
> --  else
> --    var_setfunc (entry, 0);
> --
> --  VSETATTR (entry, att_function);
> --
> --  if (mark_modified_vars)
> --    VSETATTR (entry, att_exported);
> --
> --  VUNSETATTR (entry, att_invisible);		/* Just to be sure */
> --
> --  if (exported_p (entry))
> --    array_needs_making = 1;
> --
> --#if defined (PROGRAMMABLE_COMPLETION)
> --  set_itemlist_dirty (&it_functions);
> --#endif
> --
> --  return (entry);
> --}
> --
> --#if defined (DEBUGGER)
> --/* Bind a function definition, which includes source file and line number
> --   information in addition to the command, into the FUNCTION_DEF hash table.*/
> --void
> --bind_function_def (name, value)
> --     const char *name;
> --     FUNCTION_DEF *value;
> --{
> --  FUNCTION_DEF *entry;
> --  BUCKET_CONTENTS *elt;
> --  COMMAND *cmd;
> --
> --  entry = find_function_def (name);
> --  if (entry)
> --    {
> --      dispose_function_def_contents (entry);
> --      entry = copy_function_def_contents (value, entry);
> --    }
> --  else
> --    {
> --      cmd = value->command;
> --      value->command = 0;
> --      entry = copy_function_def (value);
> --      value->command = cmd;
> --
> --      elt = hash_insert (savestring (name), shell_function_defs, HASH_NOSRCH);
> --      elt->data = (PTR_T *)entry;
> --    }
> --}
> --#endif /* DEBUGGER */
> --
> --/* Add STRING, which is of the form foo=bar, to the temporary environment
> --   HASH_TABLE (temporary_env).  The functions in execute_cmd.c are
> --   responsible for moving the main temporary env to one of the other
> --   temporary environments.  The expansion code in subst.c calls this. */
> --int
> --assign_in_env (word, flags)
> --     WORD_DESC *word;
> --     int flags;
> --{
> --  int offset, aflags;
> --  char *name, *temp, *value;
> --  SHELL_VAR *var;
> --  const char *string;
> --
> --  string = word->word;
> --
> --  aflags = 0;
> --  offset = assignment (string, 0);
> --  name = savestring (string);
> --  value = (char *)NULL;
> --
> --  if (name[offset] == '=')
> --    {
> --      name[offset] = 0;
> --
> --      /* don't ignore the `+' when assigning temporary environment */
> --      if (name[offset - 1] == '+')
> --	{
> --	  name[offset - 1] = '\0';
> --	  aflags |= ASS_APPEND;
> --	}
> --
> --      var = find_variable (name);
> --      if (var && (readonly_p (var) || noassign_p (var)))
> --	{
> --	  if (readonly_p (var))
> --	    err_readonly (name);
> --	  free (name);
> --  	  return (0);
> --	}
> --
> --      temp = name + offset + 1;
> --      value = expand_assignment_string_to_string (temp, 0);
> --
> --      if (var && (aflags & ASS_APPEND))
> --	{
> --	  temp = make_variable_value (var, value, aflags);
> --	  FREE (value);
> --	  value = temp;
> --	}
> --    }
> --
> --  if (temporary_env == 0)
> --    temporary_env = hash_create (TEMPENV_HASH_BUCKETS);
> --
> --  var = hash_lookup (name, temporary_env);
> --  if (var == 0)
> --    var = make_new_variable (name, temporary_env);
> --  else
> --    FREE (value_cell (var));
> --
> --  if (value == 0)
> --    {
> --      value = (char *)xmalloc (1);	/* like do_assignment_internal */
> --      value[0] = '\0';
> --    }
> --
> --  var_setvalue (var, value);
> --  var->attributes |= (att_exported|att_tempvar);
> --  var->context = variable_context;	/* XXX */
> --
> --  INVALIDATE_EXPORTSTR (var);
> --  var->exportstr = mk_env_string (name, value, 0);
> --
> --  array_needs_making = 1;
> --
> --  if (flags)
> --    stupidly_hack_special_variables (name);
> --
> --  if (echo_command_at_execute)
> --    /* The Korn shell prints the `+ ' in front of assignment statements,
> --	so we do too. */
> --    xtrace_print_assignment (name, value, 0, 1);
> --
> --  free (name);
> --  return 1;
> --}
> --
> --/* **************************************************************** */
> --/*								    */
> --/*			Copying variables			    */
> --/*								    */
> --/* **************************************************************** */
> --
> --#ifdef INCLUDE_UNUSED
> --/* Copy VAR to a new data structure and return that structure. */
> --SHELL_VAR *
> --copy_variable (var)
> --     SHELL_VAR *var;
> --{
> --  SHELL_VAR *copy = (SHELL_VAR *)NULL;
> --
> --  if (var)
> --    {
> --      copy = (SHELL_VAR *)xmalloc (sizeof (SHELL_VAR));
> --
> --      copy->attributes = var->attributes;
> --      copy->name = savestring (var->name);
> --
> --      if (function_p (var))
> --	var_setfunc (copy, copy_command (function_cell (var)));
> --#if defined (ARRAY_VARS)
> --      else if (array_p (var))
> --	var_setarray (copy, array_copy (array_cell (var)));
> --      else if (assoc_p (var))
> --	var_setassoc (copy, assoc_copy (assoc_cell (var)));
> --#endif
> --      else if (nameref_cell (var))	/* XXX - nameref */
> --	var_setref (copy, savestring (nameref_cell (var)));
> --      else if (value_cell (var))	/* XXX - nameref */
> --	var_setvalue (copy, savestring (value_cell (var)));
> --      else
> --	var_setvalue (copy, (char *)NULL);
> --
> --      copy->dynamic_value = var->dynamic_value;
> --      copy->assign_func = var->assign_func;
> --
> --      copy->exportstr = COPY_EXPORTSTR (var);
> --
> --      copy->context = var->context;
> --    }
> --  return (copy);
> --}
> --#endif
> --
> --/* **************************************************************** */
> --/*								    */
> --/*		  Deleting and unsetting variables		    */
> --/*								    */
> --/* **************************************************************** */
> --
> --/* Dispose of the information attached to VAR. */
> --static void
> --dispose_variable_value (var)
> --     SHELL_VAR *var;
> --{
> --  if (function_p (var))
> --    dispose_command (function_cell (var));
> --#if defined (ARRAY_VARS)
> --  else if (array_p (var))
> --    array_dispose (array_cell (var));
> --  else if (assoc_p (var))
> --    assoc_dispose (assoc_cell (var));
> --#endif
> --  else if (nameref_p (var))
> --    FREE (nameref_cell (var));
> --  else
> --    FREE (value_cell (var));
> --}
> --
> --void
> --dispose_variable (var)
> --     SHELL_VAR *var;
> --{
> --  if (var == 0)
> --    return;
> --
> --  if (nofree_p (var) == 0)
> --    dispose_variable_value (var);
> --
> --  FREE_EXPORTSTR (var);
> --
> --  free (var->name);
> --
> --  if (exported_p (var))
> --    array_needs_making = 1;
> --
> --  free (var);
> --}
> --
> --/* Unset the shell variable referenced by NAME.  Unsetting a nameref variable
> --   unsets the variable it resolves to but leaves the nameref alone. */
> --int
> --unbind_variable (name)
> --     const char *name;
> --{
> --  SHELL_VAR *v, *nv;
> --  int r;
> --
> --  v = var_lookup (name, shell_variables);
> --  nv = (v && nameref_p (v)) ? find_variable_nameref (v) : (SHELL_VAR *)NULL;
> --
> --  r = nv ? makunbound (nv->name, shell_variables) : makunbound (name, shell_variables);
> --  return r;
> --}
> --
> --/* Unbind NAME, where NAME is assumed to be a nameref variable */
> --int
> --unbind_nameref (name)
> --     const char *name;
> --{
> --  SHELL_VAR *v;
> --
> --  v = var_lookup (name, shell_variables);
> --  if (v && nameref_p (v))
> --    return makunbound (name, shell_variables);
> --  return 0;
> --}
> --
> --/* Unset the shell function named NAME. */
> --int
> --unbind_func (name)
> --     const char *name;
> --{
> --  BUCKET_CONTENTS *elt;
> --  SHELL_VAR *func;
> --
> --  elt = hash_remove (name, shell_functions, 0);
> --
> --  if (elt == 0)
> --    return -1;
> --
> --#if defined (PROGRAMMABLE_COMPLETION)
> --  set_itemlist_dirty (&it_functions);
> --#endif
> --
> --  func = (SHELL_VAR *)elt->data;
> --  if (func)
> --    {
> --      if (exported_p (func))
> --	array_needs_making++;
> --      dispose_variable (func);
> --    }
> --
> --  free (elt->key);
> --  free (elt);
> --
> --  return 0;  
> --}
> --
> --#if defined (DEBUGGER)
> --int
> --unbind_function_def (name)
> --     const char *name;
> --{
> --  BUCKET_CONTENTS *elt;
> --  FUNCTION_DEF *funcdef;
> --
> --  elt = hash_remove (name, shell_function_defs, 0);
> --
> --  if (elt == 0)
> --    return -1;
> --
> --  funcdef = (FUNCTION_DEF *)elt->data;
> --  if (funcdef)
> --    dispose_function_def (funcdef);
> --
> --  free (elt->key);
> --  free (elt);
> --
> --  return 0;  
> --}
> --#endif /* DEBUGGER */
> --
> --int
> --delete_var (name, vc)
> --     const char *name;
> --     VAR_CONTEXT *vc;
> --{
> --  BUCKET_CONTENTS *elt;
> --  SHELL_VAR *old_var;
> --  VAR_CONTEXT *v;
> --
> --  for (elt = (BUCKET_CONTENTS *)NULL, v = vc; v; v = v->down)
> --    if (elt = hash_remove (name, v->table, 0))
> --      break;
> --
> --  if (elt == 0)
> --    return (-1);
> --
> --  old_var = (SHELL_VAR *)elt->data;
> --  free (elt->key);
> --  free (elt);
> --
> --  dispose_variable (old_var);
> --  return (0);
> --}
> --
> --/* Make the variable associated with NAME go away.  HASH_LIST is the
> --   hash table from which this variable should be deleted (either
> --   shell_variables or shell_functions).
> --   Returns non-zero if the variable couldn't be found. */
> --int
> --makunbound (name, vc)
> --     const char *name;
> --     VAR_CONTEXT *vc;
> --{
> --  BUCKET_CONTENTS *elt, *new_elt;
> --  SHELL_VAR *old_var;
> --  VAR_CONTEXT *v;
> --  char *t;
> --
> --  for (elt = (BUCKET_CONTENTS *)NULL, v = vc; v; v = v->down)
> --    if (elt = hash_remove (name, v->table, 0))
> --      break;
> --
> --  if (elt == 0)
> --    return (-1);
> --
> --  old_var = (SHELL_VAR *)elt->data;
> --
> --  if (old_var && exported_p (old_var))
> --    array_needs_making++;
> --
> --  /* If we're unsetting a local variable and we're still executing inside
> --     the function, just mark the variable as invisible.  The function
> --     eventually called by pop_var_context() will clean it up later.  This
> --     must be done so that if the variable is subsequently assigned a new
> --     value inside the function, the `local' attribute is still present.
> --     We also need to add it back into the correct hash table. */
> --  if (old_var && local_p (old_var) && variable_context == old_var->context)
> --    {
> --      if (nofree_p (old_var))
> --	var_setvalue (old_var, (char *)NULL);
> --#if defined (ARRAY_VARS)
> --      else if (array_p (old_var))
> --	array_dispose (array_cell (old_var));
> --      else if (assoc_p (old_var))
> --	assoc_dispose (assoc_cell (old_var));
> --#endif
> --      else if (nameref_p (old_var))
> --	FREE (nameref_cell (old_var));
> --      else
> --	FREE (value_cell (old_var));
> --      /* Reset the attributes.  Preserve the export attribute if the variable
> --	 came from a temporary environment.  Make sure it stays local, and
> --	 make it invisible. */ 
> --      old_var->attributes = (exported_p (old_var) && tempvar_p (old_var)) ? att_exported : 0;
> --      VSETATTR (old_var, att_local);
> --      VSETATTR (old_var, att_invisible);
> --      var_setvalue (old_var, (char *)NULL);
> --      INVALIDATE_EXPORTSTR (old_var);
> --
> --      new_elt = hash_insert (savestring (old_var->name), v->table, 0);
> --      new_elt->data = (PTR_T)old_var;
> --      stupidly_hack_special_variables (old_var->name);
> --
> --      free (elt->key);
> --      free (elt);
> --      return (0);
> --    }
> --
> --  /* Have to save a copy of name here, because it might refer to
> --     old_var->name.  If so, stupidly_hack_special_variables will
> --     reference freed memory. */
> --  t = savestring (name);
> --
> --  free (elt->key);
> --  free (elt);
> --
> --  dispose_variable (old_var);
> --  stupidly_hack_special_variables (t);
> --  free (t);
> --
> --  return (0);
> --}
> --
> --/* Get rid of all of the variables in the current context. */
> --void
> --kill_all_local_variables ()
> --{
> --  VAR_CONTEXT *vc;
> --
> --  for (vc = shell_variables; vc; vc = vc->down)
> --    if (vc_isfuncenv (vc) && vc->scope == variable_context)
> --      break;
> --  if (vc == 0)
> --    return;		/* XXX */
> --
> --  if (vc->table && vc_haslocals (vc))
> --    {
> --      delete_all_variables (vc->table);
> --      hash_dispose (vc->table);
> --    }
> --  vc->table = (HASH_TABLE *)NULL;
> --}
> --
> --static void
> --free_variable_hash_data (data)
> --     PTR_T data;
> --{
> --  SHELL_VAR *var;
> --
> --  var = (SHELL_VAR *)data;
> --  dispose_variable (var);
> --}
> --
> --/* Delete the entire contents of the hash table. */
> --void
> --delete_all_variables (hashed_vars)
> --     HASH_TABLE *hashed_vars;
> --{
> --  hash_flush (hashed_vars, free_variable_hash_data);
> --}
> --
> --/* **************************************************************** */
> --/*								    */
> --/*		     Setting variable attributes		    */
> --/*								    */
> --/* **************************************************************** */
> --
> --#define FIND_OR_MAKE_VARIABLE(name, entry) \
> --  do \
> --    { \
> --      entry = find_variable (name); \
> --      if (!entry) \
> --	{ \
> --	  entry = bind_variable (name, "", 0); \
> --	  if (!no_invisible_vars && entry) entry->attributes |= att_invisible; \
> --	} \
> --    } \
> --  while (0)
> --
> --/* Make the variable associated with NAME be readonly.
> --   If NAME does not exist yet, create it. */
> --void
> --set_var_read_only (name)
> --     char *name;
> --{
> --  SHELL_VAR *entry;
> --
> --  FIND_OR_MAKE_VARIABLE (name, entry);
> --  VSETATTR (entry, att_readonly);
> --}
> --
> --#ifdef INCLUDE_UNUSED
> --/* Make the function associated with NAME be readonly.
> --   If NAME does not exist, we just punt, like auto_export code below. */
> --void
> --set_func_read_only (name)
> --     const char *name;
> --{
> --  SHELL_VAR *entry;
> --
> --  entry = find_function (name);
> --  if (entry)
> --    VSETATTR (entry, att_readonly);
> --}
> --
> --/* Make the variable associated with NAME be auto-exported.
> --   If NAME does not exist yet, create it. */
> --void
> --set_var_auto_export (name)
> --     char *name;
> --{
> --  SHELL_VAR *entry;
> --
> --  FIND_OR_MAKE_VARIABLE (name, entry);
> --  set_auto_export (entry);
> --}
> --
> --/* Make the function associated with NAME be auto-exported. */
> --void
> --set_func_auto_export (name)
> --     const char *name;
> --{
> --  SHELL_VAR *entry;
> --
> --  entry = find_function (name);
> --  if (entry)
> --    set_auto_export (entry);
> --}
> --#endif
> --
> --/* **************************************************************** */
> --/*								    */
> --/*		     Creating lists of variables		    */
> --/*								    */
> --/* **************************************************************** */
> --
> --static VARLIST *
> --vlist_alloc (nentries)
> --     int nentries;
> --{
> --  VARLIST  *vlist;
> --
> --  vlist = (VARLIST *)xmalloc (sizeof (VARLIST));
> --  vlist->list = (SHELL_VAR **)xmalloc ((nentries + 1) * sizeof (SHELL_VAR *));
> --  vlist->list_size = nentries;
> --  vlist->list_len = 0;
> --  vlist->list[0] = (SHELL_VAR *)NULL;
> --
> --  return vlist;
> --}
> --
> --static VARLIST *
> --vlist_realloc (vlist, n)
> --     VARLIST *vlist;
> --     int n;
> --{
> --  if (vlist == 0)
> --    return (vlist = vlist_alloc (n));
> --  if (n > vlist->list_size)
> --    {
> --      vlist->list_size = n;
> --      vlist->list = (SHELL_VAR **)xrealloc (vlist->list, (vlist->list_size + 1) * sizeof (SHELL_VAR *));
> --    }
> --  return vlist;
> --}
> --
> --static void
> --vlist_add (vlist, var, flags)
> --     VARLIST *vlist;
> --     SHELL_VAR *var;
> --     int flags;
> --{
> --  register int i;
> --
> --  for (i = 0; i < vlist->list_len; i++)
> --    if (STREQ (var->name, vlist->list[i]->name))
> --      break;
> --  if (i < vlist->list_len)
> --    return;
> --
> --  if (i >= vlist->list_size)
> --    vlist = vlist_realloc (vlist, vlist->list_size + 16);
> --
> --  vlist->list[vlist->list_len++] = var;
> --  vlist->list[vlist->list_len] = (SHELL_VAR *)NULL;
> --}
> --
> --/* Map FUNCTION over the variables in VAR_HASH_TABLE.  Return an array of the
> --   variables for which FUNCTION returns a non-zero value.  A NULL value
> --   for FUNCTION means to use all variables. */
> --SHELL_VAR **
> --map_over (function, vc)
> --     sh_var_map_func_t *function;
> --     VAR_CONTEXT *vc;
> --{
> --  VAR_CONTEXT *v;
> --  VARLIST *vlist;
> --  SHELL_VAR **ret;
> --  int nentries;
> --
> --  for (nentries = 0, v = vc; v; v = v->down)
> --    nentries += HASH_ENTRIES (v->table);
> --
> --  if (nentries == 0)
> --    return (SHELL_VAR **)NULL;
> --
> --  vlist = vlist_alloc (nentries);
> --
> --  for (v = vc; v; v = v->down)
> --    flatten (v->table, function, vlist, 0);
> --
> --  ret = vlist->list;
> --  free (vlist);
> --  return ret;
> --}
> --
> --SHELL_VAR **
> --map_over_funcs (function)
> --     sh_var_map_func_t *function;
> --{
> --  VARLIST *vlist;
> --  SHELL_VAR **ret;
> --
> --  if (shell_functions == 0 || HASH_ENTRIES (shell_functions) == 0)
> --    return ((SHELL_VAR **)NULL);
> --
> --  vlist = vlist_alloc (HASH_ENTRIES (shell_functions));
> --
> --  flatten (shell_functions, function, vlist, 0);
> --
> --  ret = vlist->list;
> --  free (vlist);
> --  return ret;
> --}
> --
> --/* Flatten VAR_HASH_TABLE, applying FUNC to each member and adding those
> --   elements for which FUNC succeeds to VLIST->list.  FLAGS is reserved
> --   for future use.  Only unique names are added to VLIST.  If FUNC is
> --   NULL, each variable in VAR_HASH_TABLE is added to VLIST.  If VLIST is
> --   NULL, FUNC is applied to each SHELL_VAR in VAR_HASH_TABLE.  If VLIST
> --   and FUNC are both NULL, nothing happens. */
> --static void
> --flatten (var_hash_table, func, vlist, flags)
> --     HASH_TABLE *var_hash_table;
> --     sh_var_map_func_t *func;
> --     VARLIST *vlist;
> --     int flags;
> --{
> --  register int i;
> --  register BUCKET_CONTENTS *tlist;
> --  int r;
> --  SHELL_VAR *var;
> --
> --  if (var_hash_table == 0 || (HASH_ENTRIES (var_hash_table) == 0) || (vlist == 0 && func == 0))
> --    return;
> --
> --  for (i = 0; i < var_hash_table->nbuckets; i++)
> --    {
> --      for (tlist = hash_items (i, var_hash_table); tlist; tlist = tlist->next)
> --	{
> --	  var = (SHELL_VAR *)tlist->data;
> --
> --	  r = func ? (*func) (var) : 1;
> --	  if (r && vlist)
> --	    vlist_add (vlist, var, flags);
> --	}
> --    }
> --}
> --
> --void
> --sort_variables (array)
> --     SHELL_VAR **array;
> --{
> --  qsort (array, strvec_len ((char **)array), sizeof (SHELL_VAR *), (QSFUNC *)qsort_var_comp);
> --}
> --
> --static int
> --qsort_var_comp (var1, var2)
> --     SHELL_VAR **var1, **var2;
> --{
> --  int result;
> --
> --  if ((result = (*var1)->name[0] - (*var2)->name[0]) == 0)
> --    result = strcmp ((*var1)->name, (*var2)->name);
> --
> --  return (result);
> --}
> --
> --/* Apply FUNC to each variable in SHELL_VARIABLES, adding each one for
> --   which FUNC succeeds to an array of SHELL_VAR *s.  Returns the array. */
> --static SHELL_VAR **
> --vapply (func)
> --     sh_var_map_func_t *func;
> --{
> --  SHELL_VAR **list;
> --
> --  list = map_over (func, shell_variables);
> --  if (list /* && posixly_correct */)
> --    sort_variables (list);
> --  return (list);
> --}
> --
> --/* Apply FUNC to each variable in SHELL_FUNCTIONS, adding each one for
> --   which FUNC succeeds to an array of SHELL_VAR *s.  Returns the array. */
> --static SHELL_VAR **
> --fapply (func)
> --     sh_var_map_func_t *func;
> --{
> --  SHELL_VAR **list;
> --
> --  list = map_over_funcs (func);
> --  if (list /* && posixly_correct */)
> --    sort_variables (list);
> --  return (list);
> --}
> --
> --/* Create a NULL terminated array of all the shell variables. */
> --SHELL_VAR **
> --all_shell_variables ()
> --{
> --  return (vapply ((sh_var_map_func_t *)NULL));
> --}
> --
> --/* Create a NULL terminated array of all the shell functions. */
> --SHELL_VAR **
> --all_shell_functions ()
> --{
> --  return (fapply ((sh_var_map_func_t *)NULL));
> --}
> --
> --static int
> --visible_var (var)
> --     SHELL_VAR *var;
> --{
> --  return (invisible_p (var) == 0);
> --}
> --
> --SHELL_VAR **
> --all_visible_functions ()
> --{
> --  return (fapply (visible_var));
> --}
> --
> --SHELL_VAR **
> --all_visible_variables ()
> --{
> --  return (vapply (visible_var));
> --}
> --
> --/* Return non-zero if the variable VAR is visible and exported.  Array
> --   variables cannot be exported. */
> --static int
> --visible_and_exported (var)
> --     SHELL_VAR *var;
> --{
> --  return (invisible_p (var) == 0 && exported_p (var));
> --}
> --
> --/* Candidate variables for the export environment are either valid variables
> --   with the export attribute or invalid variables inherited from the initial
> --   environment and simply passed through. */
> --static int
> --export_environment_candidate (var)
> --     SHELL_VAR *var;
> --{
> --  return (exported_p (var) && (invisible_p (var) == 0 || imported_p (var)));
> --}
> --
> --/* Return non-zero if VAR is a local variable in the current context and
> --   is exported. */
> --static int
> --local_and_exported (var)
> --     SHELL_VAR *var;
> --{
> --  return (invisible_p (var) == 0 && local_p (var) && var->context == variable_context && exported_p (var));
> --}
> --
> --SHELL_VAR **
> --all_exported_variables ()
> --{
> --  return (vapply (visible_and_exported));
> --}
> --
> --SHELL_VAR **
> --local_exported_variables ()
> --{
> --  return (vapply (local_and_exported));
> --}
> --
> --static int
> --variable_in_context (var)
> --     SHELL_VAR *var;
> --{
> --  return (invisible_p (var) == 0 && local_p (var) && var->context == variable_context);
> --}
> --
> --SHELL_VAR **
> --all_local_variables ()
> --{
> --  VARLIST *vlist;
> --  SHELL_VAR **ret;
> --  VAR_CONTEXT *vc;
> --
> --  vc = shell_variables;
> --  for (vc = shell_variables; vc; vc = vc->down)
> --    if (vc_isfuncenv (vc) && vc->scope == variable_context)
> --      break;
> --
> --  if (vc == 0)
> --    {
> --      internal_error (_("all_local_variables: no function context at current scope"));
> --      return (SHELL_VAR **)NULL;
> --    }
> --  if (vc->table == 0 || HASH_ENTRIES (vc->table) == 0 || vc_haslocals (vc) == 0)
> --    return (SHELL_VAR **)NULL;
> --    
> --  vlist = vlist_alloc (HASH_ENTRIES (vc->table));
> --
> --  flatten (vc->table, variable_in_context, vlist, 0);
> --
> --  ret = vlist->list;
> --  free (vlist);
> --  if (ret)
> --    sort_variables (ret);
> --  return ret;
> --}
> --
> --#if defined (ARRAY_VARS)
> --/* Return non-zero if the variable VAR is visible and an array. */
> --static int
> --visible_array_vars (var)
> --     SHELL_VAR *var;
> --{
> --  return (invisible_p (var) == 0 && array_p (var));
> --}
> --
> --SHELL_VAR **
> --all_array_variables ()
> --{
> --  return (vapply (visible_array_vars));
> --}
> --#endif /* ARRAY_VARS */
> --
> --char **
> --all_variables_matching_prefix (prefix)
> --     const char *prefix;
> --{
> --  SHELL_VAR **varlist;
> --  char **rlist;
> --  int vind, rind, plen;
> --
> --  plen = STRLEN (prefix);
> --  varlist = all_visible_variables ();
> --  for (vind = 0; varlist && varlist[vind]; vind++)
> --    ;
> --  if (varlist == 0 || vind == 0)
> --    return ((char **)NULL);
> --  rlist = strvec_create (vind + 1);
> --  for (vind = rind = 0; varlist[vind]; vind++)
> --    {
> --      if (plen == 0 || STREQN (prefix, varlist[vind]->name, plen))
> --	rlist[rind++] = savestring (varlist[vind]->name);
> --    }
> --  rlist[rind] = (char *)0;
> --  free (varlist);
> --
> --  return rlist;
> --}
> --
> --/* **************************************************************** */
> --/*								    */
> --/*		 Managing temporary variable scopes		    */
> --/*								    */
> --/* **************************************************************** */
> --
> --/* Make variable NAME have VALUE in the temporary environment. */
> --static SHELL_VAR *
> --bind_tempenv_variable (name, value)
> --     const char *name;
> --     char *value;
> --{
> --  SHELL_VAR *var;
> --
> --  var = temporary_env ? hash_lookup (name, temporary_env) : (SHELL_VAR *)NULL;
> --
> --  if (var)
> --    {
> --      FREE (value_cell (var));
> --      var_setvalue (var, savestring (value));
> --      INVALIDATE_EXPORTSTR (var);
> --    }
> --
> --  return (var);
> --}
> --
> --/* Find a variable in the temporary environment that is named NAME.
> --   Return the SHELL_VAR *, or NULL if not found. */
> --SHELL_VAR *
> --find_tempenv_variable (name)
> --     const char *name;
> --{
> --  return (temporary_env ? hash_lookup (name, temporary_env) : (SHELL_VAR *)NULL);
> --}
> --
> --char **tempvar_list;
> --int tvlist_ind;
> --
> --/* Push the variable described by (SHELL_VAR *)DATA down to the next
> --   variable context from the temporary environment. */
> --static void
> --push_temp_var (data)
> --     PTR_T data;
> --{
> --  SHELL_VAR *var, *v;
> --  HASH_TABLE *binding_table;
> --
> --  var = (SHELL_VAR *)data;
> --
> --  binding_table = shell_variables->table;
> --  if (binding_table == 0)
> --    {
> --      if (shell_variables == global_variables)
> --	/* shouldn't happen */
> --	binding_table = shell_variables->table = global_variables->table = hash_create (0);
> --      else
> --	binding_table = shell_variables->table = hash_create (TEMPENV_HASH_BUCKETS);
> --    }
> --
> --  v = bind_variable_internal (var->name, value_cell (var), binding_table, 0, 0);
> --
> --  /* XXX - should we set the context here?  It shouldn't matter because of how
> --     assign_in_env works, but might want to check. */
> --  if (binding_table == global_variables->table)		/* XXX */
> --    var->attributes &= ~(att_tempvar|att_propagate);
> --  else
> --    {
> --      var->attributes |= att_propagate;
> --      if  (binding_table == shell_variables->table)
> --	shell_variables->flags |= VC_HASTMPVAR;
> --    }
> --  v->attributes |= var->attributes;
> --
> --  if (find_special_var (var->name) >= 0)
> --    tempvar_list[tvlist_ind++] = savestring (var->name);
> --
> --  dispose_variable (var);
> --}
> --
> --static void
> --propagate_temp_var (data)
> --     PTR_T data;
> --{
> --  SHELL_VAR *var;
> --
> --  var = (SHELL_VAR *)data;
> --  if (tempvar_p (var) && (var->attributes & att_propagate))
> --    push_temp_var (data);
> --  else
> --    {
> --      if (find_special_var (var->name) >= 0)
> --	tempvar_list[tvlist_ind++] = savestring (var->name);
> --      dispose_variable (var);
> --    }
> --}
> --
> --/* Free the storage used in the hash table for temporary
> --   environment variables.  PUSHF is a function to be called
> --   to free each hash table entry.  It takes care of pushing variables
> --   to previous scopes if appropriate.  PUSHF stores names of variables
> --   that require special handling (e.g., IFS) on tempvar_list, so this
> --   function can call stupidly_hack_special_variables on all the
> --   variables in the list when the temporary hash table is destroyed. */
> --static void
> --dispose_temporary_env (pushf)
> --     sh_free_func_t *pushf;
> --{
> --  int i;
> --
> --  tempvar_list = strvec_create (HASH_ENTRIES (temporary_env) + 1);
> --  tempvar_list[tvlist_ind = 0] = 0;
> --    
> --  hash_flush (temporary_env, pushf);
> --  hash_dispose (temporary_env);
> --  temporary_env = (HASH_TABLE *)NULL;
> --
> --  tempvar_list[tvlist_ind] = 0;
> --
> --  array_needs_making = 1;
> --
> --#if 0
> --  sv_ifs ("IFS");		/* XXX here for now -- check setifs in assign_in_env */  
> --#endif
> --  for (i = 0; i < tvlist_ind; i++)
> --    stupidly_hack_special_variables (tempvar_list[i]);
> --
> --  strvec_dispose (tempvar_list);
> --  tempvar_list = 0;
> --  tvlist_ind = 0;
> --}
> --
> --void
> --dispose_used_env_vars ()
> --{
> --  if (temporary_env)
> --    {
> --      dispose_temporary_env (propagate_temp_var);
> --      maybe_make_export_env ();
> --    }
> --}
> --
> --/* Take all of the shell variables in the temporary environment HASH_TABLE
> --   and make shell variables from them at the current variable context. */
> --void
> --merge_temporary_env ()
> --{
> --  if (temporary_env)
> --    dispose_temporary_env (push_temp_var);
> --}
> --
> --/* **************************************************************** */
> --/*								    */
> --/*	     Creating and manipulating the environment		    */
> --/*								    */
> --/* **************************************************************** */
> --
> --static inline char *
> --mk_env_string (name, value, isfunc)
> --     const char *name, *value;
> --     int isfunc;
> --{
> --  size_t name_len, value_len;
> --  char	*p, *q;
> --
> --  name_len = strlen (name);
> --  value_len = STRLEN (value);
> --
> --  /* If we are exporting a shell function, construct the encoded function
> --     name. */
> --  if (isfunc && value)
> --    {
> --      p = (char *)xmalloc (BASHFUNC_PREFLEN + name_len + BASHFUNC_SUFFLEN + value_len + 2);
> --      q = p;
> --      memcpy (q, BASHFUNC_PREFIX, BASHFUNC_PREFLEN);
> --      q += BASHFUNC_PREFLEN;
> --      memcpy (q, name, name_len);
> --      q += name_len;
> --      memcpy (q, BASHFUNC_SUFFIX, BASHFUNC_SUFFLEN);
> --      q += BASHFUNC_SUFFLEN;
> --    }
> --  else
> --    {
> --      p = (char *)xmalloc (2 + name_len + value_len);
> --      memcpy (p, name, name_len);
> --      q = p + name_len;
> --    }
> --
> --  q[0] = '=';
> --  if (value && *value)
> --    memcpy (q + 1, value, value_len + 1);
> --  else
> --    q[1] = '\0';
> --
> --  return (p);
> --}
> --
> --#ifdef DEBUG
> --/* Debugging */
> --static int
> --valid_exportstr (v)
> --     SHELL_VAR *v;
> --{
> --  char *s;
> --
> --  s = v->exportstr;
> --  if (s == 0)
> --    {
> --      internal_error (_("%s has null exportstr"), v->name);
> --      return (0);
> --    }
> --  if (legal_variable_starter ((unsigned char)*s) == 0)
> --    {
> --      internal_error (_("invalid character %d in exportstr for %s"), *s, v->name);
> --      return (0);
> --    }
> --  for (s = v->exportstr + 1; s && *s; s++)
> --    {
> --      if (*s == '=')
> --	break;
> --      if (legal_variable_char ((unsigned char)*s) == 0)
> --	{
> --	  internal_error (_("invalid character %d in exportstr for %s"), *s, v->name);
> --	  return (0);
> --	}
> --    }
> --  if (*s != '=')
> --    {
> --      internal_error (_("no `=' in exportstr for %s"), v->name);
> --      return (0);
> --    }
> --  return (1);
> --}
> --#endif
> --
> --static char **
> --make_env_array_from_var_list (vars)
> --     SHELL_VAR **vars;
> --{
> --  register int i, list_index;
> --  register SHELL_VAR *var;
> --  char **list, *value;
> --
> --  list = strvec_create ((1 + strvec_len ((char **)vars)));
> --
> --#define USE_EXPORTSTR (value == var->exportstr)
> --
> --  for (i = 0, list_index = 0; var = vars[i]; i++)
> --    {
> --#if defined (__CYGWIN__)
> --      /* We don't use the exportstr stuff on Cygwin at all. */
> --      INVALIDATE_EXPORTSTR (var);
> --#endif
> --      if (var->exportstr)
> --	value = var->exportstr;
> --      else if (function_p (var))
> --	value = named_function_string ((char *)NULL, function_cell (var), 0);
> --#if defined (ARRAY_VARS)
> --      else if (array_p (var))
> --#  if ARRAY_EXPORT
> --	value = array_to_assignment_string (array_cell (var));
> --#  else
> --	continue;	/* XXX array vars cannot yet be exported */
> --#  endif /* ARRAY_EXPORT */
> --      else if (assoc_p (var))
> --#  if 0
> --	value = assoc_to_assignment_string (assoc_cell (var));
> --#  else
> --	continue;	/* XXX associative array vars cannot yet be exported */
> --#  endif
> --#endif
> --      else
> --	value = value_cell (var);
> --
> --      if (value)
> --	{
> --	  /* Gee, I'd like to get away with not using savestring() if we're
> --	     using the cached exportstr... */
> --	  list[list_index] = USE_EXPORTSTR ? savestring (value)
> --					   : mk_env_string (var->name, value, function_p (var));
> --
> --	  if (USE_EXPORTSTR == 0)
> --	    SAVE_EXPORTSTR (var, list[list_index]);
> --
> --	  list_index++;
> --#undef USE_EXPORTSTR
> --
> --#if 0	/* not yet */
> --#if defined (ARRAY_VARS)
> --	  if (array_p (var) || assoc_p (var))
> --	    free (value);
> --#endif
> --#endif
> --	}
> --    }
> --
> --  list[list_index] = (char *)NULL;
> --  return (list);
> --}
> --
> --/* Make an array of assignment statements from the hash table
> --   HASHED_VARS which contains SHELL_VARs.  Only visible, exported
> --   variables are eligible. */
> --static char **
> --make_var_export_array (vcxt)
> --     VAR_CONTEXT *vcxt;
> --{
> --  char **list;
> --  SHELL_VAR **vars;
> --
> --#if 0
> --  vars = map_over (visible_and_exported, vcxt);
> --#else
> --  vars = map_over (export_environment_candidate, vcxt);
> --#endif
> --
> --  if (vars == 0)
> --    return (char **)NULL;
> --
> --  list = make_env_array_from_var_list (vars);
> --
> --  free (vars);
> --  return (list);
> --}
> --
> --static char **
> --make_func_export_array ()
> --{
> --  char **list;
> --  SHELL_VAR **vars;
> --
> --  vars = map_over_funcs (visible_and_exported);
> --  if (vars == 0)
> --    return (char **)NULL;
> --
> --  list = make_env_array_from_var_list (vars);
> --
> --  free (vars);
> --  return (list);
> --}
> --
> --/* Add ENVSTR to the end of the exported environment, EXPORT_ENV. */
> --#define add_to_export_env(envstr,do_alloc) \
> --do \
> --  { \
> --    if (export_env_index >= (export_env_size - 1)) \
> --      { \
> --	export_env_size += 16; \
> --	export_env = strvec_resize (export_env, export_env_size); \
> --	environ = export_env; \
> --      } \
> --    export_env[export_env_index++] = (do_alloc) ? savestring (envstr) : envstr; \
> --    export_env[export_env_index] = (char *)NULL; \
> --  } while (0)
> --
> --/* Add ASSIGN to EXPORT_ENV, or supercede a previous assignment in the
> --   array with the same left-hand side.  Return the new EXPORT_ENV. */
> --char **
> --add_or_supercede_exported_var (assign, do_alloc)
> --     char *assign;
> --     int do_alloc;
> --{
> --  register int i;
> --  int equal_offset;
> --
> --  equal_offset = assignment (assign, 0);
> --  if (equal_offset == 0)
> --    return (export_env);
> --
> --  /* If this is a function, then only supersede the function definition.
> --     We do this by including the `=() {' in the comparison, like
> --     initialize_shell_variables does. */
> --  if (assign[equal_offset + 1] == '(' &&
> --     strncmp (assign + equal_offset + 2, ") {", 3) == 0)		/* } */
> --    equal_offset += 4;
> --
> --  for (i = 0; i < export_env_index; i++)
> --    {
> --      if (STREQN (assign, export_env[i], equal_offset + 1))
> --	{
> --	  free (export_env[i]);
> --	  export_env[i] = do_alloc ? savestring (assign) : assign;
> --	  return (export_env);
> --	}
> --    }
> --  add_to_export_env (assign, do_alloc);
> --  return (export_env);
> --}
> --
> --static void
> --add_temp_array_to_env (temp_array, do_alloc, do_supercede)
> --     char **temp_array;
> --     int do_alloc, do_supercede;
> --{
> --  register int i;
> --
> --  if (temp_array == 0)
> --    return;
> --
> --  for (i = 0; temp_array[i]; i++)
> --    {
> --      if (do_supercede)
> --	export_env = add_or_supercede_exported_var (temp_array[i], do_alloc);
> --      else
> --	add_to_export_env (temp_array[i], do_alloc);
> --    }
> --
> --  free (temp_array);
> --}
> --
> --/* Make the environment array for the command about to be executed, if the
> --   array needs making.  Otherwise, do nothing.  If a shell action could
> --   change the array that commands receive for their environment, then the
> --   code should `array_needs_making++'.
> --
> --   The order to add to the array is:
> --   	temporary_env
> --   	list of var contexts whose head is shell_variables
> --  	shell_functions
> --
> --  This is the shell variable lookup order.  We add only new variable
> --  names at each step, which allows local variables and variables in
> --  the temporary environments to shadow variables in the global (or
> --  any previous) scope.
> --*/
> --
> --static int
> --n_shell_variables ()
> --{
> --  VAR_CONTEXT *vc;
> --  int n;
> --
> --  for (n = 0, vc = shell_variables; vc; vc = vc->down)
> --    n += HASH_ENTRIES (vc->table);
> --  return n;
> --}
> --
> --int
> --chkexport (name)
> --     char *name;
> --{
> --  SHELL_VAR *v;
> --
> --  v = find_variable (name);
> --  if (v && exported_p (v))
> --    {
> --      array_needs_making = 1;
> --      maybe_make_export_env ();
> --      return 1;
> --    }
> --  return 0;
> --}
> --
> --void
> --maybe_make_export_env ()
> --{
> --  register char **temp_array;
> --  int new_size;
> --  VAR_CONTEXT *tcxt;
> --
> --  if (array_needs_making)
> --    {
> --      if (export_env)
> --	strvec_flush (export_env);
> --
> --      /* Make a guess based on how many shell variables and functions we
> --	 have.  Since there will always be array variables, and array
> --	 variables are not (yet) exported, this will always be big enough
> --	 for the exported variables and functions. */
> --      new_size = n_shell_variables () + HASH_ENTRIES (shell_functions) + 1 +
> --		 HASH_ENTRIES (temporary_env);
> --      if (new_size > export_env_size)
> --	{
> --	  export_env_size = new_size;
> --	  export_env = strvec_resize (export_env, export_env_size);
> --	  environ = export_env;
> --	}
> --      export_env[export_env_index = 0] = (char *)NULL;
> --
> --      /* Make a dummy variable context from the temporary_env, stick it on
> --	 the front of shell_variables, call make_var_export_array on the
> --	 whole thing to flatten it, and convert the list of SHELL_VAR *s
> --	 to the form needed by the environment. */
> --      if (temporary_env)
> --	{
> --	  tcxt = new_var_context ((char *)NULL, 0);
> --	  tcxt->table = temporary_env;
> --	  tcxt->down = shell_variables;
> --	}
> --      else
> --	tcxt = shell_variables;
> --      
> --      temp_array = make_var_export_array (tcxt);
> --      if (temp_array)
> --	add_temp_array_to_env (temp_array, 0, 0);
> --
> --      if (tcxt != shell_variables)
> --	free (tcxt);
> --
> --#if defined (RESTRICTED_SHELL)
> --      /* Restricted shells may not export shell functions. */
> --      temp_array = restricted ? (char **)0 : make_func_export_array ();
> --#else
> --      temp_array = make_func_export_array ();
> --#endif
> --      if (temp_array)
> --	add_temp_array_to_env (temp_array, 0, 0);
> --
> --      array_needs_making = 0;
> --    }
> --}
> --
> --/* This is an efficiency hack.  PWD and OLDPWD are auto-exported, so
> --   we will need to remake the exported environment every time we
> --   change directories.  `_' is always put into the environment for
> --   every external command, so without special treatment it will always
> --   cause the environment to be remade.
> --
> --   If there is no other reason to make the exported environment, we can
> --   just update the variables in place and mark the exported environment
> --   as no longer needing a remake. */
> --void
> --update_export_env_inplace (env_prefix, preflen, value)
> --     char *env_prefix;
> --     int preflen;
> --     char *value;
> --{
> --  char *evar;
> --
> --  evar = (char *)xmalloc (STRLEN (value) + preflen + 1);
> --  strcpy (evar, env_prefix);
> --  if (value)
> --    strcpy (evar + preflen, value);
> --  export_env = add_or_supercede_exported_var (evar, 0);
> --}
> --
> --/* We always put _ in the environment as the name of this command. */
> --void
> --put_command_name_into_env (command_name)
> --     char *command_name;
> --{
> --  update_export_env_inplace ("_=", 2, command_name);
> --}
> --
> --/* **************************************************************** */
> --/*								    */
> --/*		      Managing variable contexts		    */
> --/*								    */
> --/* **************************************************************** */
> --
> --/* Allocate and return a new variable context with NAME and FLAGS.
> --   NAME can be NULL. */
> --
> --VAR_CONTEXT *
> --new_var_context (name, flags)
> --     char *name;
> --     int flags;
> --{
> --  VAR_CONTEXT *vc;
> --
> --  vc = (VAR_CONTEXT *)xmalloc (sizeof (VAR_CONTEXT));
> --  vc->name = name ? savestring (name) : (char *)NULL;
> --  vc->scope = variable_context;
> --  vc->flags = flags;
> --
> --  vc->up = vc->down = (VAR_CONTEXT *)NULL;
> --  vc->table = (HASH_TABLE *)NULL;
> --
> --  return vc;
> --}
> --
> --/* Free a variable context and its data, including the hash table.  Dispose
> --   all of the variables. */
> --void
> --dispose_var_context (vc)
> --     VAR_CONTEXT *vc;
> --{
> --  FREE (vc->name);
> --
> --  if (vc->table)
> --    {
> --      delete_all_variables (vc->table);
> --      hash_dispose (vc->table);
> --    }
> --
> --  free (vc);
> --}
> --
> --/* Set VAR's scope level to the current variable context. */
> --static int
> --set_context (var)
> --     SHELL_VAR *var;
> --{
> --  return (var->context = variable_context);
> --}
> --
> --/* Make a new variable context with NAME and FLAGS and a HASH_TABLE of
> --   temporary variables, and push it onto shell_variables.  This is
> --   for shell functions. */
> --VAR_CONTEXT *
> --push_var_context (name, flags, tempvars)
> --     char *name;
> --     int flags;
> --     HASH_TABLE *tempvars;
> --{
> --  VAR_CONTEXT *vc;
> --
> --  vc = new_var_context (name, flags);
> --  vc->table = tempvars;
> --  if (tempvars)
> --    {
> --      /* Have to do this because the temp environment was created before
> --	 variable_context was incremented. */
> --      flatten (tempvars, set_context, (VARLIST *)NULL, 0);
> --      vc->flags |= VC_HASTMPVAR;
> --    }
> --  vc->down = shell_variables;
> --  shell_variables->up = vc;
> --
> --  return (shell_variables = vc);
> --}
> --
> --static void
> --push_func_var (data)
> --     PTR_T data;
> --{
> --  SHELL_VAR *var, *v;
> --
> --  var = (SHELL_VAR *)data;
> --
> --  if (tempvar_p (var) && (posixly_correct || (var->attributes & att_propagate)))
> --    {
> --      /* Make sure we have a hash table to store the variable in while it is
> --	 being propagated down to the global variables table.  Create one if
> --	 we have to */
> --      if ((vc_isfuncenv (shell_variables) || vc_istempenv (shell_variables)) && shell_variables->table == 0)
> --	shell_variables->table = hash_create (0);
> --      /* XXX - should we set v->context here? */
> --      v = bind_variable_internal (var->name, value_cell (var), shell_variables->table, 0, 0);
> --      if (shell_variables == global_variables)
> --	var->attributes &= ~(att_tempvar|att_propagate);
> --      else
> --	shell_variables->flags |= VC_HASTMPVAR;
> --      v->attributes |= var->attributes;
> --    }
> --  else
> --    stupidly_hack_special_variables (var->name);	/* XXX */
> --
> --  dispose_variable (var);
> --}
> --
> --/* Pop the top context off of VCXT and dispose of it, returning the rest of
> --   the stack. */
> --void
> --pop_var_context ()
> --{
> --  VAR_CONTEXT *ret, *vcxt;
> --
> --  vcxt = shell_variables;
> --  if (vc_isfuncenv (vcxt) == 0)
> --    {
> --      internal_error (_("pop_var_context: head of shell_variables not a function context"));
> --      return;
> --    }
> --
> --  if (ret = vcxt->down)
> --    {
> --      ret->up = (VAR_CONTEXT *)NULL;
> --      shell_variables = ret;
> --      if (vcxt->table)
> --	hash_flush (vcxt->table, push_func_var);
> --      dispose_var_context (vcxt);
> --    }
> --  else
> --    internal_error (_("pop_var_context: no global_variables context"));
> --}
> --
> --/* Delete the HASH_TABLEs for all variable contexts beginning at VCXT, and
> --   all of the VAR_CONTEXTs except GLOBAL_VARIABLES. */
> --void
> --delete_all_contexts (vcxt)
> --     VAR_CONTEXT *vcxt;
> --{
> --  VAR_CONTEXT *v, *t;
> --
> --  for (v = vcxt; v != global_variables; v = t)
> --    {
> --      t = v->down;
> --      dispose_var_context (v);
> --    }    
> --
> --  delete_all_variables (global_variables->table);
> --  shell_variables = global_variables;
> --}
> --
> --/* **************************************************************** */
> --/*								    */
> --/*	   Pushing and Popping temporary variable scopes	    */
> --/*								    */
> --/* **************************************************************** */
> --
> --VAR_CONTEXT *
> --push_scope (flags, tmpvars)
> --     int flags;
> --     HASH_TABLE *tmpvars;
> --{
> --  return (push_var_context ((char *)NULL, flags, tmpvars));
> --}
> --
> --static void
> --push_exported_var (data)
> --     PTR_T data;
> --{
> --  SHELL_VAR *var, *v;
> --
> --  var = (SHELL_VAR *)data;
> --
> --  /* If a temp var had its export attribute set, or it's marked to be
> --     propagated, bind it in the previous scope before disposing it. */
> --  /* XXX - This isn't exactly right, because all tempenv variables have the
> --    export attribute set. */
> --#if 0
> --  if (exported_p (var) || (var->attributes & att_propagate))
> --#else
> --  if (tempvar_p (var) && exported_p (var) && (var->attributes & att_propagate))
> --#endif
> --    {
> --      var->attributes &= ~att_tempvar;		/* XXX */
> --      v = bind_variable_internal (var->name, value_cell (var), shell_variables->table, 0, 0);
> --      if (shell_variables == global_variables)
> --	var->attributes &= ~att_propagate;
> --      v->attributes |= var->attributes;
> --    }
> --  else
> --    stupidly_hack_special_variables (var->name);	/* XXX */
> --
> --  dispose_variable (var);
> --}
> --
> --void
> --pop_scope (is_special)
> --     int is_special;
> --{
> --  VAR_CONTEXT *vcxt, *ret;
> --
> --  vcxt = shell_variables;
> --  if (vc_istempscope (vcxt) == 0)
> --    {
> --      internal_error (_("pop_scope: head of shell_variables not a temporary environment scope"));
> --      return;
> --    }
> --
> --  ret = vcxt->down;
> --  if (ret)
> --    ret->up = (VAR_CONTEXT *)NULL;
> --
> --  shell_variables = ret;
> --
> --  /* Now we can take care of merging variables in VCXT into set of scopes
> --     whose head is RET (shell_variables). */
> --  FREE (vcxt->name);
> --  if (vcxt->table)
> --    {
> --      if (is_special)
> --	hash_flush (vcxt->table, push_func_var);
> --      else
> --	hash_flush (vcxt->table, push_exported_var);
> --      hash_dispose (vcxt->table);
> --    }
> --  free (vcxt);
> --
> --  sv_ifs ("IFS");	/* XXX here for now */
> --}
> --
> --/* **************************************************************** */
> --/*								    */
> --/*		 Pushing and Popping function contexts		    */
> --/*								    */
> --/* **************************************************************** */
> --
> --static WORD_LIST **dollar_arg_stack = (WORD_LIST **)NULL;
> --static int dollar_arg_stack_slots;
> --static int dollar_arg_stack_index;
> --
> --/* XXX - we might want to consider pushing and popping the `getopts' state
> --   when we modify the positional parameters. */
> --void
> --push_context (name, is_subshell, tempvars)
> --     char *name;	/* function name */
> --     int is_subshell;
> --     HASH_TABLE *tempvars;
> --{
> --  if (is_subshell == 0)
> --    push_dollar_vars ();
> --  variable_context++;
> --  push_var_context (name, VC_FUNCENV, tempvars);
> --}
> --
> --/* Only called when subshell == 0, so we don't need to check, and can
> --   unconditionally pop the dollar vars off the stack. */
> --void
> --pop_context ()
> --{
> --  pop_dollar_vars ();
> --  variable_context--;
> --  pop_var_context ();
> --
> --  sv_ifs ("IFS");		/* XXX here for now */
> --}
> --
> --/* Save the existing positional parameters on a stack. */
> --void
> --push_dollar_vars ()
> --{
> --  if (dollar_arg_stack_index + 2 > dollar_arg_stack_slots)
> --    {
> --      dollar_arg_stack = (WORD_LIST **)
> --	xrealloc (dollar_arg_stack, (dollar_arg_stack_slots += 10)
> --		  * sizeof (WORD_LIST *));
> --    }
> --  dollar_arg_stack[dollar_arg_stack_index++] = list_rest_of_args ();
> --  dollar_arg_stack[dollar_arg_stack_index] = (WORD_LIST *)NULL;
> --}
> --
> --/* Restore the positional parameters from our stack. */
> --void
> --pop_dollar_vars ()
> --{
> --  if (!dollar_arg_stack || dollar_arg_stack_index == 0)
> --    return;
> --
> --  remember_args (dollar_arg_stack[--dollar_arg_stack_index], 1);
> --  dispose_words (dollar_arg_stack[dollar_arg_stack_index]);
> --  dollar_arg_stack[dollar_arg_stack_index] = (WORD_LIST *)NULL;
> --  set_dollar_vars_unchanged ();
> --}
> --
> --void
> --dispose_saved_dollar_vars ()
> --{
> --  if (!dollar_arg_stack || dollar_arg_stack_index == 0)
> --    return;
> --
> --  dispose_words (dollar_arg_stack[dollar_arg_stack_index]);
> --  dollar_arg_stack[dollar_arg_stack_index] = (WORD_LIST *)NULL;
> --}
> --
> --/* Manipulate the special BASH_ARGV and BASH_ARGC variables. */
> --
> --void
> --push_args (list)
> --     WORD_LIST *list;
> --{
> --#if defined (ARRAY_VARS) && defined (DEBUGGER)
> --  SHELL_VAR *bash_argv_v, *bash_argc_v;
> --  ARRAY *bash_argv_a, *bash_argc_a;
> --  WORD_LIST *l;
> --  arrayind_t i;
> --  char *t;
> --
> --  GET_ARRAY_FROM_VAR ("BASH_ARGV", bash_argv_v, bash_argv_a);
> --  GET_ARRAY_FROM_VAR ("BASH_ARGC", bash_argc_v, bash_argc_a);
> --
> --  for (l = list, i = 0; l; l = l->next, i++)
> --    array_push (bash_argv_a, l->word->word);
> --
> --  t = itos (i);
> --  array_push (bash_argc_a, t);
> --  free (t);
> --#endif /* ARRAY_VARS && DEBUGGER */
> --}
> --
> --/* Remove arguments from BASH_ARGV array.  Pop top element off BASH_ARGC
> --   array and use that value as the count of elements to remove from
> --   BASH_ARGV. */
> --void
> --pop_args ()
> --{
> --#if defined (ARRAY_VARS) && defined (DEBUGGER)
> --  SHELL_VAR *bash_argv_v, *bash_argc_v;
> --  ARRAY *bash_argv_a, *bash_argc_a;
> --  ARRAY_ELEMENT *ce;
> --  intmax_t i;
> --
> --  GET_ARRAY_FROM_VAR ("BASH_ARGV", bash_argv_v, bash_argv_a);
> --  GET_ARRAY_FROM_VAR ("BASH_ARGC", bash_argc_v, bash_argc_a);
> --
> --  ce = array_shift (bash_argc_a, 1, 0);
> --  if (ce == 0 || legal_number (element_value (ce), &i) == 0)
> --    i = 0;
> --
> --  for ( ; i > 0; i--)
> --    array_pop (bash_argv_a);
> --  array_dispose_element (ce);
> --#endif /* ARRAY_VARS && DEBUGGER */
> --}
> --
> --/*************************************************
> -- *						 *
> -- *	Functions to manage special variables	 *
> -- *						 *
> -- *************************************************/
> --
> --/* Extern declarations for variables this code has to manage. */
> --extern int eof_encountered, eof_encountered_limit, ignoreeof;
> --
> --#if defined (READLINE)
> --extern int hostname_list_initialized;
> --#endif
> --
> --/* An alist of name.function for each special variable.  Most of the
> --   functions don't do much, and in fact, this would be faster with a
> --   switch statement, but by the end of this file, I am sick of switch
> --   statements. */
> --
> --#define SET_INT_VAR(name, intvar)  intvar = find_variable (name) != 0
> --
> --/* This table will be sorted with qsort() the first time it's accessed. */
> --struct name_and_function {
> --  char *name;
> --  sh_sv_func_t *function;
> --};
> --
> --static struct name_and_function special_vars[] = {
> --  { "BASH_COMPAT", sv_shcompat },
> --  { "BASH_XTRACEFD", sv_xtracefd },
> --
> --#if defined (JOB_CONTROL)
> --  { "CHILD_MAX", sv_childmax },
> --#endif
> --
> --#if defined (READLINE)
> --#  if defined (STRICT_POSIX)
> --  { "COLUMNS", sv_winsize },
> --#  endif
> --  { "COMP_WORDBREAKS", sv_comp_wordbreaks },
> --#endif
> --
> --  { "FUNCNEST", sv_funcnest },
> --
> --  { "GLOBIGNORE", sv_globignore },
> --
> --#if defined (HISTORY)
> --  { "HISTCONTROL", sv_history_control },
> --  { "HISTFILESIZE", sv_histsize },
> --  { "HISTIGNORE", sv_histignore },
> --  { "HISTSIZE", sv_histsize },
> --  { "HISTTIMEFORMAT", sv_histtimefmt },
> --#endif
> --
> --#if defined (__CYGWIN__)
> --  { "HOME", sv_home },
> --#endif
> --
> --#if defined (READLINE)
> --  { "HOSTFILE", sv_hostfile },
> --#endif
> --
> --  { "IFS", sv_ifs },
> --  { "IGNOREEOF", sv_ignoreeof },
> --
> --  { "LANG", sv_locale },
> --  { "LC_ALL", sv_locale },
> --  { "LC_COLLATE", sv_locale },
> --  { "LC_CTYPE", sv_locale },
> --  { "LC_MESSAGES", sv_locale },
> --  { "LC_NUMERIC", sv_locale },
> --  { "LC_TIME", sv_locale },
> --
> --#if defined (READLINE) && defined (STRICT_POSIX)
> --  { "LINES", sv_winsize },
> --#endif
> --
> --  { "MAIL", sv_mail },
> --  { "MAILCHECK", sv_mail },
> --  { "MAILPATH", sv_mail },
> --
> --  { "OPTERR", sv_opterr },
> --  { "OPTIND", sv_optind },
> --
> --  { "PATH", sv_path },
> --  { "POSIXLY_CORRECT", sv_strict_posix },
> --
> --#if defined (READLINE)
> --  { "TERM", sv_terminal },
> --  { "TERMCAP", sv_terminal },
> --  { "TERMINFO", sv_terminal },
> --#endif /* READLINE */
> --
> --  { "TEXTDOMAIN", sv_locale },
> --  { "TEXTDOMAINDIR", sv_locale },
> --
> --#if defined (HAVE_TZSET)
> --  { "TZ", sv_tz },
> --#endif
> --
> --#if defined (HISTORY) && defined (BANG_HISTORY)
> --  { "histchars", sv_histchars },
> --#endif /* HISTORY && BANG_HISTORY */
> --
> --  { "ignoreeof", sv_ignoreeof },
> --
> --  { (char *)0, (sh_sv_func_t *)0 }
> --};
> --
> --#define N_SPECIAL_VARS	(sizeof (special_vars) / sizeof (special_vars[0]) - 1)
> --
> --static int
> --sv_compare (sv1, sv2)
> --     struct name_and_function *sv1, *sv2;
> --{
> --  int r;
> --
> --  if ((r = sv1->name[0] - sv2->name[0]) == 0)
> --    r = strcmp (sv1->name, sv2->name);
> --  return r;
> --}
> --
> --static inline int
> --find_special_var (name)
> --     const char *name;
> --{
> --  register int i, r;
> --
> --  for (i = 0; special_vars[i].name; i++)
> --    {
> --      r = special_vars[i].name[0] - name[0];
> --      if (r == 0)
> --	r = strcmp (special_vars[i].name, name);
> --      if (r == 0)
> --	return i;
> --      else if (r > 0)
> --	/* Can't match any of rest of elements in sorted list.  Take this out
> --	   if it causes problems in certain environments. */
> --	break;
> --    }
> --  return -1;
> --}
> --
> --/* The variable in NAME has just had its state changed.  Check to see if it
> --   is one of the special ones where something special happens. */
> --void
> --stupidly_hack_special_variables (name)
> --     char *name;
> --{
> --  static int sv_sorted = 0;
> --  int i;
> --
> --  if (sv_sorted == 0)	/* shouldn't need, but it's fairly cheap. */
> --    {
> --      qsort (special_vars, N_SPECIAL_VARS, sizeof (special_vars[0]),
> --		(QSFUNC *)sv_compare);
> --      sv_sorted = 1;
> --    }
> --
> --  i = find_special_var (name);
> --  if (i != -1)
> --    (*(special_vars[i].function)) (name);
> --}
> --
> --/* Special variables that need hooks to be run when they are unset as part
> --   of shell reinitialization should have their sv_ functions run here. */
> --void
> --reinit_special_variables ()
> --{
> --#if defined (READLINE)
> --  sv_comp_wordbreaks ("COMP_WORDBREAKS");
> --#endif
> --  sv_globignore ("GLOBIGNORE");
> --  sv_opterr ("OPTERR");
> --}
> --
> --void
> --sv_ifs (name)
> --     char *name;
> --{
> --  SHELL_VAR *v;
> --
> --  v = find_variable ("IFS");
> --  setifs (v);
> --}
> --
> --/* What to do just after the PATH variable has changed. */
> --void
> --sv_path (name)
> --     char *name;
> --{
> --  /* hash -r */
> --  phash_flush ();
> --}
> --
> --/* What to do just after one of the MAILxxxx variables has changed.  NAME
> --   is the name of the variable.  This is called with NAME set to one of
> --   MAIL, MAILCHECK, or MAILPATH.  */
> --void
> --sv_mail (name)
> --     char *name;
> --{
> --  /* If the time interval for checking the files has changed, then
> --     reset the mail timer.  Otherwise, one of the pathname vars
> --     to the users mailbox has changed, so rebuild the array of
> --     filenames. */
> --  if (name[4] == 'C')  /* if (strcmp (name, "MAILCHECK") == 0) */
> --    reset_mail_timer ();
> --  else
> --    {
> --      free_mail_files ();
> --      remember_mail_dates ();
> --    }
> --}
> --
> --void
> --sv_funcnest (name)
> --     char *name;
> --{
> --  SHELL_VAR *v;
> --  intmax_t num;
> --
> --  v = find_variable (name);
> --  if (v == 0)
> --    funcnest_max = 0;
> --  else if (legal_number (value_cell (v), &num) == 0)
> --    funcnest_max = 0;
> --  else
> --    funcnest_max = num;
> --}
> --
> --/* What to do when GLOBIGNORE changes. */
> --void
> --sv_globignore (name)
> --     char *name;
> --{
> --  if (privileged_mode == 0)
> --    setup_glob_ignore (name);
> --}
> --
> --#if defined (READLINE)
> --void
> --sv_comp_wordbreaks (name)
> --     char *name;
> --{
> --  SHELL_VAR *sv;
> --
> --  sv = find_variable (name);
> --  if (sv == 0)
> --    reset_completer_word_break_chars ();
> --}
> --
> --/* What to do just after one of the TERMxxx variables has changed.
> --   If we are an interactive shell, then try to reset the terminal
> --   information in readline. */
> --void
> --sv_terminal (name)
> --     char *name;
> --{
> --  if (interactive_shell && no_line_editing == 0)
> --    rl_reset_terminal (get_string_value ("TERM"));
> --}
> --
> --void
> --sv_hostfile (name)
> --     char *name;
> --{
> --  SHELL_VAR *v;
> --
> --  v = find_variable (name);
> --  if (v == 0)
> --    clear_hostname_list ();
> --  else
> --    hostname_list_initialized = 0;
> --}
> --
> --#if defined (STRICT_POSIX)
> --/* In strict posix mode, we allow assignments to LINES and COLUMNS (and values
> --   found in the initial environment) to override the terminal size reported by
> --   the kernel. */
> --void
> --sv_winsize (name)
> --     char *name;
> --{
> --  SHELL_VAR *v;
> --  intmax_t xd;
> --  int d;
> --
> --  if (posixly_correct == 0 || interactive_shell == 0 || no_line_editing)
> --    return;
> --
> --  v = find_variable (name);
> --  if (v == 0 || var_isnull (v))
> --    rl_reset_screen_size ();
> --  else
> --    {
> --      if (legal_number (value_cell (v), &xd) == 0)
> --	return;
> --      winsize_assignment = 1;
> --      d = xd;			/* truncate */
> --      if (name[0] == 'L')	/* LINES */
> --	rl_set_screen_size (d, -1);
> --      else			/* COLUMNS */
> --	rl_set_screen_size (-1, d);
> --      winsize_assignment = 0;
> --    }
> --}
> --#endif /* STRICT_POSIX */
> --#endif /* READLINE */
> --
> --/* Update the value of HOME in the export environment so tilde expansion will
> --   work on cygwin. */
> --#if defined (__CYGWIN__)
> --sv_home (name)
> --     char *name;
> --{
> --  array_needs_making = 1;
> --  maybe_make_export_env ();
> --}
> --#endif
> --
> --#if defined (HISTORY)
> --/* What to do after the HISTSIZE or HISTFILESIZE variables change.
> --   If there is a value for this HISTSIZE (and it is numeric), then stifle
> --   the history.  Otherwise, if there is NO value for this variable,
> --   unstifle the history.  If name is HISTFILESIZE, and its value is
> --   numeric, truncate the history file to hold no more than that many
> --   lines. */
> --void
> --sv_histsize (name)
> --     char *name;
> --{
> --  char *temp;
> --  intmax_t num;
> --  int hmax;
> --
> --  temp = get_string_value (name);
> --
> --  if (temp && *temp)
> --    {
> --      if (legal_number (temp, &num))
> --	{
> --	  hmax = num;
> --	  if (hmax < 0 && name[4] == 'S')
> --	    unstifle_history ();	/* unstifle history if HISTSIZE < 0 */
> --	  else if (name[4] == 'S')
> --	    {
> --	      stifle_history (hmax);
> --	      hmax = where_history ();
> --	      if (history_lines_this_session > hmax)
> --		history_lines_this_session = hmax;
> --	    }
> --	  else if (hmax >= 0)	/* truncate HISTFILE if HISTFILESIZE >= 0 */
> --	    {
> --	      history_truncate_file (get_string_value ("HISTFILE"), hmax);
> --	      if (hmax <= history_lines_in_file)
> --		history_lines_in_file = hmax;
> --	    }
> --	}
> --    }
> --  else if (name[4] == 'S')
> --    unstifle_history ();
> --}
> --
> --/* What to do after the HISTIGNORE variable changes. */
> --void
> --sv_histignore (name)
> --     char *name;
> --{
> --  setup_history_ignore (name);
> --}
> --
> --/* What to do after the HISTCONTROL variable changes. */
> --void
> --sv_history_control (name)
> --     char *name;
> --{
> --  char *temp;
> --  char *val;
> --  int tptr;
> --
> --  history_control = 0;
> --  temp = get_string_value (name);
> --
> --  if (temp == 0 || *temp == 0)
> --    return;
> --
> --  tptr = 0;
> --  while (val = extract_colon_unit (temp, &tptr))
> --    {
> --      if (STREQ (val, "ignorespace"))
> --	history_control |= HC_IGNSPACE;
> --      else if (STREQ (val, "ignoredups"))
> --	history_control |= HC_IGNDUPS;
> --      else if (STREQ (val, "ignoreboth"))
> --	history_control |= HC_IGNBOTH;
> --      else if (STREQ (val, "erasedups"))
> --	history_control |= HC_ERASEDUPS;
> --
> --      free (val);
> --    }
> --}
> --
> --#if defined (BANG_HISTORY)
> --/* Setting/unsetting of the history expansion character. */
> --void
> --sv_histchars (name)
> --     char *name;
> --{
> --  char *temp;
> --
> --  temp = get_string_value (name);
> --  if (temp)
> --    {
> --      history_expansion_char = *temp;
> --      if (temp[0] && temp[1])
> --	{
> --	  history_subst_char = temp[1];
> --	  if (temp[2])
> --	      history_comment_char = temp[2];
> --	}
> --    }
> --  else
> --    {
> --      history_expansion_char = '!';
> --      history_subst_char = '^';
> --      history_comment_char = '#';
> --    }
> --}
> --#endif /* BANG_HISTORY */
> --
> --void
> --sv_histtimefmt (name)
> --     char *name;
> --{
> --  SHELL_VAR *v;
> --
> --  if (v = find_variable (name))
> --    {
> --      if (history_comment_char == 0)
> --	history_comment_char = '#';
> --    }
> --  history_write_timestamps = (v != 0);
> --}
> --#endif /* HISTORY */
> --
> --#if defined (HAVE_TZSET)
> --void
> --sv_tz (name)
> --     char *name;
> --{
> --  if (chkexport (name))
> --    tzset ();
> --}
> --#endif
> --
> --/* If the variable exists, then the value of it can be the number
> --   of times we actually ignore the EOF.  The default is small,
> --   (smaller than csh, anyway). */
> --void
> --sv_ignoreeof (name)
> --     char *name;
> --{
> --  SHELL_VAR *tmp_var;
> --  char *temp;
> --
> --  eof_encountered = 0;
> --
> --  tmp_var = find_variable (name);
> --  ignoreeof = tmp_var != 0;
> --  temp = tmp_var ? value_cell (tmp_var) : (char *)NULL;
> --  if (temp)
> --    eof_encountered_limit = (*temp && all_digits (temp)) ? atoi (temp) : 10;
> --  set_shellopts ();	/* make sure `ignoreeof' is/is not in $SHELLOPTS */
> --}
> --
> --void
> --sv_optind (name)
> --     char *name;
> --{
> --  char *tt;
> --  int s;
> --
> --  tt = get_string_value ("OPTIND");
> --  if (tt && *tt)
> --    {
> --      s = atoi (tt);
> --
> --      /* According to POSIX, setting OPTIND=1 resets the internal state
> --	 of getopt (). */
> --      if (s < 0 || s == 1)
> --	s = 0;
> --    }
> --  else
> --    s = 0;
> --  getopts_reset (s);
> --}
> --
> --void
> --sv_opterr (name)
> --     char *name;
> --{
> --  char *tt;
> --
> --  tt = get_string_value ("OPTERR");
> --  sh_opterr = (tt && *tt) ? atoi (tt) : 1;
> --}
> --
> --void
> --sv_strict_posix (name)
> --     char *name;
> --{
> --  SET_INT_VAR (name, posixly_correct);
> --  posix_initialize (posixly_correct);
> --#if defined (READLINE)
> --  if (interactive_shell)
> --    posix_readline_initialize (posixly_correct);
> --#endif /* READLINE */
> --  set_shellopts ();	/* make sure `posix' is/is not in $SHELLOPTS */
> --}
> --
> --void
> --sv_locale (name)
> --     char *name;
> --{
> --  char *v;
> --  int r;
> --
> --  v = get_string_value (name);
> --  if (name[0] == 'L' && name[1] == 'A')	/* LANG */
> --    r = set_lang (name, v);
> --  else
> --    r = set_locale_var (name, v);		/* LC_*, TEXTDOMAIN* */
> --
> --#if 1
> --  if (r == 0 && posixly_correct)
> --    last_command_exit_value = 1;
> --#endif
> --}
> --
> --#if defined (ARRAY_VARS)
> --void
> --set_pipestatus_array (ps, nproc)
> --     int *ps;
> --     int nproc;
> --{
> --  SHELL_VAR *v;
> --  ARRAY *a;
> --  ARRAY_ELEMENT *ae;
> --  register int i;
> --  char *t, tbuf[INT_STRLEN_BOUND(int) + 1];
> --
> --  v = find_variable ("PIPESTATUS");
> --  if (v == 0)
> --    v = make_new_array_variable ("PIPESTATUS");
> --  if (array_p (v) == 0)
> --    return;		/* Do nothing if not an array variable. */
> --  a = array_cell (v);
> --
> --  if (a == 0 || array_num_elements (a) == 0)
> --    {
> --      for (i = 0; i < nproc; i++)	/* was ps[i] != -1, not i < nproc */
> --	{
> --	  t = inttostr (ps[i], tbuf, sizeof (tbuf));
> --	  array_insert (a, i, t);
> --	}
> --      return;
> --    }
> --
> --  /* Fast case */
> --  if (array_num_elements (a) == nproc && nproc == 1)
> --    {
> --      ae = element_forw (a->head);
> --      free (element_value (ae));
> --      ae->value = itos (ps[0]);
> --    }
> --  else if (array_num_elements (a) <= nproc)
> --    {
> --      /* modify in array_num_elements members in place, then add */
> --      ae = a->head;
> --      for (i = 0; i < array_num_elements (a); i++)
> --	{
> --	  ae = element_forw (ae);
> --	  free (element_value (ae));
> --	  ae->value = itos (ps[i]);
> --	}
> --      /* add any more */
> --      for ( ; i < nproc; i++)
> --	{
> --	  t = inttostr (ps[i], tbuf, sizeof (tbuf));
> --	  array_insert (a, i, t);
> --	}
> --    }
> --  else
> --    {
> --      /* deleting elements.  it's faster to rebuild the array. */	  
> --      array_flush (a);
> --      for (i = 0; ps[i] != -1; i++)
> --	{
> --	  t = inttostr (ps[i], tbuf, sizeof (tbuf));
> --	  array_insert (a, i, t);
> --	}
> --    }
> --}
> --
> --ARRAY *
> --save_pipestatus_array ()
> --{
> --  SHELL_VAR *v;
> --  ARRAY *a, *a2;
> --
> --  v = find_variable ("PIPESTATUS");
> --  if (v == 0 || array_p (v) == 0 || array_cell (v) == 0)
> --    return ((ARRAY *)NULL);
> --    
> --  a = array_cell (v);
> --  a2 = array_copy (array_cell (v));
> --
> --  return a2;
> --}
> --
> --void
> --restore_pipestatus_array (a)
> --     ARRAY *a;
> --{
> --  SHELL_VAR *v;
> --  ARRAY *a2;
> --
> --  v = find_variable ("PIPESTATUS");
> --  /* XXX - should we still assign even if existing value is NULL? */
> --  if (v == 0 || array_p (v) == 0 || array_cell (v) == 0)
> --    return;
> --
> --  a2 = array_cell (v);
> --  var_setarray (v, a); 
> --
> --  array_dispose (a2);
> --}
> --#endif
> --
> --void
> --set_pipestatus_from_exit (s)
> --     int s;
> --{
> --#if defined (ARRAY_VARS)
> --  static int v[2] = { 0, -1 };
> --
> --  v[0] = s;
> --  set_pipestatus_array (v, 1);
> --#endif
> --}
> --
> --void
> --sv_xtracefd (name)
> --     char *name;
> --{
> --  SHELL_VAR *v;
> --  char *t, *e;
> --  int fd;
> --  FILE *fp;
> --
> --  v = find_variable (name);
> --  if (v == 0)
> --    {
> --      xtrace_reset ();
> --      return;
> --    }
> --
> --  t = value_cell (v);
> --  if (t == 0 || *t == 0)
> --    xtrace_reset ();
> --  else
> --    {
> --      fd = (int)strtol (t, &e, 10);
> --      if (e != t && *e == '\0' && sh_validfd (fd))
> --	{
> --	  fp = fdopen (fd, "w");
> --	  if (fp == 0)
> --	    internal_error (_("%s: %s: cannot open as FILE"), name, value_cell (v));
> --	  else
> --	    xtrace_set (fd, fp);
> --	}
> --      else
> --	internal_error (_("%s: %s: invalid value for trace file descriptor"), name, value_cell (v));
> --    }
> --}
> --
> --#define MIN_COMPAT_LEVEL 31
> --
> --void
> --sv_shcompat (name)
> --     char *name;
> --{
> --  SHELL_VAR *v;
> --  char *val;
> --  int tens, ones, compatval;
> --
> --  v = find_variable (name);
> --  if (v == 0)
> --    {
> --      shell_compatibility_level = DEFAULT_COMPAT_LEVEL;
> --      set_compatibility_opts ();
> --      return;
> --    }
> --  val = value_cell (v);
> --  if (val == 0 || *val == '\0')
> --    {
> --      shell_compatibility_level = DEFAULT_COMPAT_LEVEL;
> --      set_compatibility_opts ();
> --      return;
> --    }
> --  /* Handle decimal-like compatibility version specifications: 4.2 */
> --  if (isdigit (val[0]) && val[1] == '.' && isdigit (val[2]) && val[3] == 0)
> --    {
> --      tens = val[0] - '0';
> --      ones = val[2] - '0';
> --      compatval = tens*10 + ones;
> --    }
> --  /* Handle integer-like compatibility version specifications: 42 */
> --  else if (isdigit (val[0]) && isdigit (val[1]) && val[2] == 0)
> --    {
> --      tens = val[0] - '0';
> --      ones = val[1] - '0';
> --      compatval = tens*10 + ones;
> --    }
> --  else
> --    {
> --compat_error:
> --      internal_error (_("%s: %s: compatibility value out of range"), name, val);
> --      shell_compatibility_level = DEFAULT_COMPAT_LEVEL;
> --      set_compatibility_opts ();
> --      return;
> --    }
> --
> --  if (compatval < MIN_COMPAT_LEVEL || compatval > DEFAULT_COMPAT_LEVEL)
> --    goto compat_error;
> --
> --  shell_compatibility_level = compatval;
> --  set_compatibility_opts ();
> --}
> --
> --#if defined (JOB_CONTROL)
> --void
> --sv_childmax (name)
> --     char *name;
> --{
> --  char *tt;
> --  int s;
> --
> --  tt = get_string_value (name);
> --  s = (tt && *tt) ? atoi (tt) : 0;
> --  set_maxchild (s);
> --}
> --#endif
> diff --git a/patches/bash-4.3.30/0003-Bash-4.3-patch-33.patch b/patches/bash-4.3.30/0003-Bash-4.3-patch-33.patch
> deleted file mode 100644
> index cda1797354c7..000000000000
> --- a/patches/bash-4.3.30/0003-Bash-4.3-patch-33.patch
> +++ /dev/null
> @@ -1,204 +0,0 @@
> -From: Chet Ramey <chet.ramey@case.edu>
> -Date: Thu, 15 Jan 2015 10:21:08 -0500
> -Subject: [PATCH] Bash-4.3 patch 33
> -
> ----
> - bashline.c        |  6 ++++--
> - builtins/common.h |  4 ++++
> - builtins/read.def | 31 ++++++++++++++++++++++++++++---
> - patchlevel.h      |  2 +-
> - shell.c           |  9 +++++++++
> - sig.c             |  6 ++++--
> - 6 files changed, 50 insertions(+), 8 deletions(-)
> -
> -diff --git a/bashline.c b/bashline.c
> -index 77ca033f2cc8..c87415171a4a 100644
> ---- a/bashline.c
> -+++ b/bashline.c
> -@@ -202,6 +202,7 @@ extern int current_command_line_count, saved_command_line_count;
> - extern int last_command_exit_value;
> - extern int array_needs_making;
> - extern int posixly_correct, no_symbolic_links;
> -+extern int sigalrm_seen;
> - extern char *current_prompt_string, *ps1_prompt;
> - extern STRING_INT_ALIST word_token_alist[];
> - extern sh_builtin_func_t *last_shell_builtin, *this_shell_builtin;
> -@@ -4208,8 +4209,9 @@ bash_event_hook ()
> - {
> -   /* If we're going to longjmp to top_level, make sure we clean up readline.
> -      check_signals will call QUIT, which will eventually longjmp to top_level,
> --     calling run_interrupt_trap along the way. */
> --  if (interrupt_state)
> -+     calling run_interrupt_trap along the way.  The check for sigalrm_seen is
> -+     to clean up the read builtin's state. */
> -+  if (terminating_signal || interrupt_state || sigalrm_seen)
> -     rl_cleanup_after_signal ();
> -   bashline_reset_event_hook ();
> -   check_signals_and_traps ();	/* XXX */
> -diff --git a/builtins/common.h b/builtins/common.h
> -index cae16b10fb65..a1298cb9c84a 100644
> ---- a/builtins/common.h
> -+++ b/builtins/common.h
> -@@ -122,6 +122,10 @@ extern void bash_logout __P((void));
> - /* Functions from getopts.def */
> - extern void getopts_reset __P((int));
> - 
> -+/* Functions from read.def */
> -+extern void read_tty_cleanup __P((void));
> -+extern int read_tty_modified __P((void));
> -+
> - /* Functions from set.def */
> - extern int minus_o_option_value __P((char *));
> - extern void list_minus_o_opts __P((int, int));
> -diff --git a/builtins/read.def b/builtins/read.def
> -index 43971544d081..56c23010bbe8 100644
> ---- a/builtins/read.def
> -+++ b/builtins/read.def
> -@@ -140,10 +140,12 @@ static void reset_alarm __P((void));
> - procenv_t alrmbuf;
> - int sigalrm_seen;
> - 
> --static int reading;
> -+static int reading, tty_modified;
> - static SigHandler *old_alrm;
> - static unsigned char delim;
> - 
> -+static struct ttsave termsave;
> -+
> - /* In all cases, SIGALRM just sets a flag that we check periodically.  This
> -    avoids problems with the semi-tricky stuff we do with the xfree of
> -    input_string at the top of the unwind-protect list (see below). */
> -@@ -188,7 +190,6 @@ read_builtin (list)
> -   struct stat tsb;
> -   SHELL_VAR *var;
> -   TTYSTRUCT ttattrs, ttset;
> --  struct ttsave termsave;
> - #if defined (ARRAY_VARS)
> -   WORD_LIST *alist;
> - #endif
> -@@ -221,7 +222,7 @@ read_builtin (list)
> -   USE_VAR(ps2);
> -   USE_VAR(lastsig);
> - 
> --  sigalrm_seen = reading = 0;
> -+  sigalrm_seen = reading = tty_modified = 0;
> - 
> -   i = 0;		/* Index into the string that we are reading. */
> -   raw = edit = 0;	/* Not reading raw input by default. */
> -@@ -438,6 +439,8 @@ read_builtin (list)
> - 	  retval = 128+SIGALRM;
> - 	  goto assign_vars;
> - 	}
> -+      if (interactive_shell == 0)
> -+	initialize_terminating_signals ();
> -       old_alrm = set_signal_handler (SIGALRM, sigalrm);
> -       add_unwind_protect (reset_alarm, (char *)NULL);
> - #if defined (READLINE)
> -@@ -482,7 +485,10 @@ read_builtin (list)
> - 	  i = silent ? ttfd_cbreak (fd, &ttset) : ttfd_onechar (fd, &ttset);
> - 	  if (i < 0)
> - 	    sh_ttyerror (1);
> -+	  tty_modified = 1;
> - 	  add_unwind_protect ((Function *)ttyrestore, (char *)&termsave);
> -+	  if (interactive_shell == 0)
> -+	    initialize_terminating_signals ();
> - 	}
> -     }
> -   else if (silent)	/* turn off echo but leave term in canonical mode */
> -@@ -497,7 +503,10 @@ read_builtin (list)
> -       if (i < 0)
> - 	sh_ttyerror (1);
> - 
> -+      tty_modified = 1;
> -       add_unwind_protect ((Function *)ttyrestore, (char *)&termsave);
> -+      if (interactive_shell == 0)
> -+	initialize_terminating_signals ();
> -     }
> - 
> -   /* This *must* be the top unwind-protect on the stack, so the manipulation
> -@@ -588,6 +597,8 @@ read_builtin (list)
> - 	    }
> - 	  else
> - 	    lastsig = 0;
> -+	  if (terminating_signal && tty_modified)
> -+	    ttyrestore (&termsave);	/* fix terminal before exiting */
> - 	  CHECK_TERMSIG;
> - 	  eof = 1;
> - 	  break;
> -@@ -978,6 +989,20 @@ ttyrestore (ttp)
> -      struct ttsave *ttp;
> - {
> -   ttsetattr (ttp->fd, ttp->attrs);
> -+  tty_modified = 0;
> -+}
> -+
> -+void
> -+read_tty_cleanup ()
> -+{
> -+  if (tty_modified)
> -+    ttyrestore (&termsave);
> -+}
> -+
> -+int
> -+read_tty_modified ()
> -+{
> -+  return (tty_modified);
> - }
> - 
> - #if defined (READLINE)
> -diff --git a/patchlevel.h b/patchlevel.h
> -index b8bf38704ed2..cefe6bdd3a13 100644
> ---- a/patchlevel.h
> -+++ b/patchlevel.h
> -@@ -25,6 +25,6 @@
> -    regexp `^#define[ 	]*PATCHLEVEL', since that's what support/mkversion.sh
> -    looks for to find the patch level (for the sccs version string). */
> - 
> --#define PATCHLEVEL 32
> -+#define PATCHLEVEL 33
> - 
> - #endif /* _PATCHLEVEL_H_ */
> -diff --git a/shell.c b/shell.c
> -index bbc8a66cc2eb..2fd8179ba10d 100644
> ---- a/shell.c
> -+++ b/shell.c
> -@@ -73,6 +73,7 @@
> - #endif
> - 
> - #if defined (READLINE)
> -+#  include <readline/readline.h>
> - #  include "bashline.h"
> - #endif
> - 
> -@@ -909,6 +910,14 @@ exit_shell (s)
> -   fflush (stdout);		/* XXX */
> -   fflush (stderr);
> - 
> -+  /* Clean up the terminal if we are in a state where it's been modified. */
> -+#if defined (READLINE)
> -+  if (RL_ISSTATE (RL_STATE_TERMPREPPED) && rl_deprep_term_function)
> -+    (*rl_deprep_term_function) ();
> -+#endif
> -+  if (read_tty_modified ())
> -+    read_tty_cleanup ();
> -+
> -   /* Do trap[0] if defined.  Allow it to override the exit status
> -      passed to us. */
> -   if (signal_is_trapped (0))
> -diff --git a/sig.c b/sig.c
> -index 3b62ea5d7c5d..8bc45c17f478 100644
> ---- a/sig.c
> -+++ b/sig.c
> -@@ -532,8 +532,10 @@ termsig_sighandler (sig)
> - #if defined (READLINE)
> -   /* Set the event hook so readline will call it after the signal handlers
> -      finish executing, so if this interrupted character input we can get
> --     quick response. */
> --  if (interactive_shell && interactive && no_line_editing == 0)
> -+     quick response.  If readline is active or has modified the terminal we
> -+     need to set this no matter what the signal is, though the check for
> -+     RL_STATE_TERMPREPPED is possibly redundant. */
> -+  if (RL_ISSTATE (RL_STATE_SIGHANDLER) || RL_ISSTATE (RL_STATE_TERMPREPPED))
> -     bashline_set_event_hook ();
> - #endif
> - 
> diff --git a/patches/bash-4.3.30/series b/patches/bash-4.3.30/series
> deleted file mode 100644
> index 2e1fdf17fa87..000000000000
> --- a/patches/bash-4.3.30/series
> +++ /dev/null
> @@ -1,6 +0,0 @@
> -# generated by git-ptx-patches
> -#tag:base --start-number 1
> -0001-Bash-4.3-patch-31.patch
> -0002-Bash-4.3-patch-32.patch
> -0003-Bash-4.3-patch-33.patch
> -# 602897f584d96d29536a2fa60f8d5e23  - git-ptx-patches magic
> diff --git a/patches/bash-5.1.8/0001-Bash-5.1-patch-12.patch b/patches/bash-5.1.8/0001-Bash-5.1-patch-12.patch
> new file mode 100644
> index 000000000000..ef8b6ae7f90d
> --- /dev/null
> +++ b/patches/bash-5.1.8/0001-Bash-5.1-patch-12.patch
> @@ -0,0 +1,262 @@
> +diff -urN bash-5.1.8.orig/builtins/wait.def bash-5.1.8/builtins/wait.def
> +--- bash-5.1.8.orig/builtins/wait.def	2021-12-21 12:57:08.083139502 +0100
> ++++ bash-5.1.8/builtins/wait.def	2021-12-21 12:58:24.304849937 +0100
> +@@ -111,7 +111,8 @@
> + wait_builtin (list)
> +      WORD_LIST *list;
> + {
> +-  int status, code, opt, nflag, wflags;
> ++  int status, code, opt, nflag;
> ++  volatile int wflags;
> +   char *vname;
> +   SHELL_VAR *pidvar;
> +   struct procstat pstat;
> +@@ -180,6 +181,8 @@
> +       last_command_exit_signal = wait_signal_received;
> +       status = 128 + wait_signal_received;
> +       wait_sigint_cleanup ();
> ++      if (wflags & JWAIT_WAITING)
> ++	unset_waitlist ();
> +       WAIT_RETURN (status);
> +     }
> + 
> +diff -urN bash-5.1.8.orig/command.h bash-5.1.8/command.h
> +--- bash-5.1.8.orig/command.h	2021-12-21 12:57:08.099139862 +0100
> ++++ bash-5.1.8/command.h	2021-12-21 12:58:27.760927380 +0100
> +@@ -124,6 +124,7 @@
> + #define SUBSHELL_PROCSUB 0x20	/* subshell caused by <(command) or >(command) */
> + #define SUBSHELL_COPROC	0x40	/* subshell from a coproc pipeline */
> + #define SUBSHELL_RESETTRAP 0x80	/* subshell needs to reset trap strings on first call to trap */
> ++#define SUBSHELL_IGNTRAP 0x100  /* subshell should reset trapped signals from trap_handler */
> + 
> + /* A structure which represents a word. */
> + typedef struct word_desc {
> +diff -urN bash-5.1.8.orig/execute_cmd.c bash-5.1.8/execute_cmd.c
> +--- bash-5.1.8.orig/execute_cmd.c	2021-12-21 12:57:08.091139683 +0100
> ++++ bash-5.1.8/execute_cmd.c	2021-12-21 12:58:27.764927470 +0100
> +@@ -1547,6 +1547,9 @@
> +   clear_pending_traps ();
> +   reset_signal_handlers ();
> +   subshell_environment |= SUBSHELL_RESETTRAP;
> ++  /* Note that signal handlers have been reset, so we should no longer
> ++    reset the handler and resend trapped signals to ourselves. */
> ++  subshell_environment &= ~SUBSHELL_IGNTRAP;
> + 
> +   /* We are in a subshell, so forget that we are running a trap handler or
> +      that the signal handler has changed (we haven't changed it!) */
> +@@ -4320,7 +4323,8 @@
> + 	  already_forked = 1;
> + 	  cmdflags |= CMD_NO_FORK;
> + 
> +-	  subshell_environment = SUBSHELL_FORK;		/* XXX */
> ++	  /* We redo some of what make_child() does with SUBSHELL_IGNTRAP */
> ++	  subshell_environment = SUBSHELL_FORK|SUBSHELL_IGNTRAP;	/* XXX */
> + 	  if (pipe_in != NO_PIPE || pipe_out != NO_PIPE)
> + 	    subshell_environment |= SUBSHELL_PIPE;
> + 	  if (async)
> +@@ -4574,6 +4578,7 @@
> + 	     trap strings if we run trap to change a signal disposition. */
> + 	  reset_signal_handlers ();
> + 	  subshell_environment |= SUBSHELL_RESETTRAP;
> ++	  subshell_environment &= ~SUBSHELL_IGNTRAP;
> + 
> + 	  if (async)
> + 	    {
> +@@ -5514,6 +5519,7 @@
> +       reset_terminating_signals ();	/* XXX */
> +       /* Cancel traps, in trap.c. */
> +       restore_original_signals ();
> ++      subshell_environment &= ~SUBSHELL_IGNTRAP;
> + 
> + #if defined (JOB_CONTROL)
> +       FREE (p);
> +diff -urN bash-5.1.8.orig/jobs.c bash-5.1.8/jobs.c
> +--- bash-5.1.8.orig/jobs.c	2021-12-21 12:57:08.091139683 +0100
> ++++ bash-5.1.8/jobs.c	2021-12-21 12:58:27.764927470 +0100
> +@@ -2217,6 +2217,8 @@
> + 	 signals to the default state for a new process. */
> +       pid_t mypid;
> + 
> ++      subshell_environment |= SUBSHELL_IGNTRAP;
> ++
> +       /* If this ends up being changed to modify or use `command' in the
> + 	 child process, go back and change callers who free `command' in
> + 	 the child process when this returns. */
> +diff -urN bash-5.1.8.orig/lib/malloc/malloc.c bash-5.1.8/lib/malloc/malloc.c
> +--- bash-5.1.8.orig/lib/malloc/malloc.c	2021-12-21 12:57:08.095139773 +0100
> ++++ bash-5.1.8/lib/malloc/malloc.c	2021-12-21 12:58:22.200802784 +0100
> +@@ -1286,13 +1286,12 @@
> +       p = (union mhead *) ap - 1;
> +     }
> + 
> +-  /* XXX - should we return 0 if ISFREE? */
> +-  maxbytes = binsize(p->mh_index);
> +-
> +-  /* So the usable size is the maximum number of bytes in the bin less the
> +-     malloc overhead */
> +-  maxbytes -= MOVERHEAD + MSLOP;
> +-  return (maxbytes);
> ++  /* return 0 if ISFREE */
> ++  if (p->mh_alloc == ISFREE)
> ++    return 0;
> ++  
> ++  /* Since we use bounds checking, the usable size is the last requested size. */
> ++  return (p->mh_nbytes);
> + }
> + 
> + #if !defined (NO_VALLOC)
> +diff -urN bash-5.1.8.orig/nojobs.c bash-5.1.8/nojobs.c
> +--- bash-5.1.8.orig/nojobs.c	2021-12-21 12:57:08.091139683 +0100
> ++++ bash-5.1.8/nojobs.c	2021-12-21 12:58:27.764927470 +0100
> +@@ -575,6 +575,8 @@
> + 	last_asynchronous_pid = getpid ();
> + #endif
> + 
> ++      subshell_environment |= SUBSHELL_IGNTRAP;
> ++
> +       default_tty_job_signals ();
> +     }
> +   else
> +diff -urN bash-5.1.8.orig/parse.y bash-5.1.8/parse.y
> +--- bash-5.1.8.orig/parse.y	2021-12-21 12:57:08.099139862 +0100
> ++++ bash-5.1.8/parse.y	2021-12-21 12:58:26.112890455 +0100
> +@@ -6493,10 +6493,8 @@
> +   old_expand_aliases = expand_aliases;
> + 
> +   push_stream (1);
> +-#if 0 /* TAG: bash-5.2 Alex fxmbsw7 Ratchev <fxmbsw7@gmail.com> 11/17/2020 */
> +   if (ea = expanding_alias ())
> +     parser_save_alias ();
> +-#endif
> +   last_read_token = WORD;		/* WORD to allow reserved words here */
> +   current_command_line_count = 0;
> +   echo_input_at_read = expand_aliases = 0;
> +@@ -6531,10 +6529,8 @@
> +   last_read_token = '\n';
> +   pop_stream ();
> + 
> +-#if 0 /* TAG: bash-5.2 */
> +   if (ea)
> +     parser_restore_alias ();
> +-#endif
> + 
> + #if defined (HISTORY)
> +   remember_on_history = old_remember_on_history;
> +diff -urN bash-5.1.8.orig/patchlevel.h bash-5.1.8/patchlevel.h
> +--- bash-5.1.8.orig/patchlevel.h	2021-12-21 12:57:08.075139321 +0100
> ++++ bash-5.1.8/patchlevel.h	2021-12-21 12:58:27.764927470 +0100
> +@@ -25,6 +25,6 @@
> +    regexp `^#define[ 	]*PATCHLEVEL', since that's what support/mkversion.sh
> +    looks for to find the patch level (for the sccs version string). */
> + 
> +-#define PATCHLEVEL 8
> ++#define PATCHLEVEL 12
> + 
> + #endif /* _PATCHLEVEL_H_ */
> +diff -urN bash-5.1.8.orig/sig.c bash-5.1.8/sig.c
> +--- bash-5.1.8.orig/sig.c	2021-12-21 12:57:08.071139231 +0100
> ++++ bash-5.1.8/sig.c	2021-12-21 12:58:27.764927470 +0100
> +@@ -55,7 +55,8 @@
> + #  include "bashhist.h"
> + #endif
> + 
> +-extern void initialize_siglist ();
> ++extern void initialize_siglist PARAMS((void));
> ++extern void set_original_signal PARAMS((int, SigHandler *));
> + 
> + #if !defined (JOB_CONTROL)
> + extern void initialize_job_signals PARAMS((void));
> +@@ -255,6 +256,13 @@
> +       sigaction (XSIG (i), &act, &oact);
> +       XHANDLER(i) = oact.sa_handler;
> +       XSAFLAGS(i) = oact.sa_flags;
> ++
> ++#if 0
> ++      set_original_signal (XSIG(i), XHANDLER(i));	/* optimization */
> ++#else
> ++      set_original_signal (XSIG(i), act.sa_handler);	/* optimization */
> ++#endif
> ++
> +       /* Don't do anything with signals that are ignored at shell entry
> + 	 if the shell is not interactive. */
> +       /* XXX - should we do this for interactive shells, too? */
> +diff -urN bash-5.1.8.orig/subst.c bash-5.1.8/subst.c
> +--- bash-5.1.8.orig/subst.c	2021-12-21 12:57:08.099139862 +0100
> ++++ bash-5.1.8/subst.c	2021-12-21 12:58:27.764927470 +0100
> +@@ -5951,6 +5951,7 @@
> +       free_pushed_string_input ();
> +       /* Cancel traps, in trap.c. */
> +       restore_original_signals ();	/* XXX - what about special builtins? bash-4.2 */
> ++      subshell_environment &= ~SUBSHELL_IGNTRAP;
> +       QUIT;	/* catch any interrupts we got post-fork */
> +       setup_async_signals ();
> + #if 0
> +@@ -6382,6 +6383,7 @@
> + 	}	
> +       QUIT;	/* catch any interrupts we got post-fork */
> +       subshell_environment |= SUBSHELL_RESETTRAP;
> ++      subshell_environment &= ~SUBSHELL_IGNTRAP;
> +     }
> + 
> + #if defined (JOB_CONTROL)
> +diff -urN bash-5.1.8.orig/trap.c bash-5.1.8/trap.c
> +--- bash-5.1.8.orig/trap.c	2021-12-21 12:57:08.083139502 +0100
> ++++ bash-5.1.8/trap.c	2021-12-21 12:58:27.764927470 +0100
> +@@ -481,6 +481,32 @@
> +       SIGRETURN (0);
> +     }
> + 
> ++  /* This means we're in a subshell, but have not yet reset the handler for
> ++     trapped signals. We're not supposed to execute the trap in this situation;
> ++     we should restore the original signal and resend the signal to ourselves
> ++     to preserve the Posix "signal traps that are not being ignored shall be
> ++     set to the default action" semantics. */
> ++  if ((subshell_environment & SUBSHELL_IGNTRAP) && trap_list[sig] != (char *)IGNORE_SIG)
> ++    {
> ++      sigset_t mask;
> ++
> ++      /* Paranoia */
> ++      if (original_signals[sig] == IMPOSSIBLE_TRAP_HANDLER)
> ++	original_signals[sig] = SIG_DFL;
> ++
> ++      restore_signal (sig);
> ++
> ++      /* Make sure we let the signal we just caught through */
> ++      sigemptyset (&mask);
> ++      sigprocmask (SIG_SETMASK, (sigset_t *)NULL, &mask);
> ++      sigdelset (&mask, sig);
> ++      sigprocmask (SIG_SETMASK, &mask, (sigset_t *)NULL);
> ++
> ++      kill (getpid (), sig);
> ++
> ++      SIGRETURN (0);
> ++    }
> ++
> +   if ((sig >= NSIG) ||
> +       (trap_list[sig] == (char *)DEFAULT_SIG) ||
> +       (trap_list[sig] == (char *)IGNORE_SIG))
> +diff -urN bash-5.1.8.orig/y.tab.c bash-5.1.8/y.tab.c
> +--- bash-5.1.8.orig/y.tab.c	2021-12-21 12:57:08.075139321 +0100
> ++++ bash-5.1.8/y.tab.c	2021-12-21 12:58:26.116890545 +0100
> +@@ -8787,10 +8787,8 @@
> +   old_expand_aliases = expand_aliases;
> + 
> +   push_stream (1);
> +-#if 0 /* TAG: bash-5.2 Alex fxmbsw7 Ratchev <fxmbsw7@gmail.com> 11/17/2020 */
> +   if (ea = expanding_alias ())
> +     parser_save_alias ();
> +-#endif
> +   last_read_token = WORD;		/* WORD to allow reserved words here */
> +   current_command_line_count = 0;
> +   echo_input_at_read = expand_aliases = 0;
> +@@ -8825,10 +8823,8 @@
> +   last_read_token = '\n';
> +   pop_stream ();
> + 
> +-#if 0 /* TAG: bash-5.2 */
> +   if (ea)
> +     parser_restore_alias ();
> +-#endif
> + 
> + #if defined (HISTORY)
> +   remember_on_history = old_remember_on_history;
> diff --git a/patches/bash-5.1.8/series b/patches/bash-5.1.8/series
> new file mode 100644
> index 000000000000..0e53e492ad1d
> --- /dev/null
> +++ b/patches/bash-5.1.8/series
> @@ -0,0 +1 @@
> +0001-Bash-5.1-patch-12.patch
> diff --git a/rules/bash.make b/rules/bash.make
> index bed121586475..881ba55fd9b7 100644
> --- a/rules/bash.make
> +++ b/rules/bash.make
> @@ -13,8 +13,8 @@ PACKAGES-$(PTXCONF_BASH) += bash
>  #
>  # Paths and names
>  #
> -BASH_VERSION	:= 4.3.30
> -BASH_MD5	:= a27b3ee9be83bd3ba448c0ff52b28447
> +BASH_VERSION	:= 5.1.8
> +BASH_MD5	:= 23eee6195b47318b9fd878e590ccb38c
>  BASH		:= bash-$(BASH_VERSION)
>  BASH_SUFFIX	:= tar.gz
>  BASH_URL	:= $(call ptx/mirror, GNU, bash/$(BASH).$(BASH_SUFFIX))
> @@ -24,7 +24,7 @@ BASH_MAKE_PAR	:= NO
>  BASH_LICENSE	:= GPL-3.0-or-later
>  BASH_LICENSE_FILES	:= \
>  	file://COPYING;md5=d32239bcb673463ab874e80d47fae504 \
> -	file://general.c;startline=1;endline=19;md5=c018785d21f8c206ca7a13fa7d027568
> +	file://general.c;startline=1;endline=19;md5=94a266185749191808da241ccc2af60a
>  
>  # ----------------------------------------------------------------------------
>  # Prepare

_______________________________________________
ptxdist mailing list
ptxdist@pengutronix.de
To unsubscribe, send a mail with subject "unsubscribe" to ptxdist-request@pengutronix.de


^ permalink raw reply	[flat|nested] 2+ messages in thread

end of thread, other threads:[~2022-01-21  7:22 UTC | newest]

Thread overview: 2+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2022-01-07  8:24 [ptxdist] [PATCH v3] bash: Version bump. 4.3.30 (+patches-33) -> 5.1.8 (patches-12) Christian Melki
2022-01-21  7:19 ` [ptxdist] [APPLIED] " Michael Olbrich

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox