mailarchive of the ptxdist mailing list
 help / color / mirror / Atom feed
From: Michael Olbrich <m.olbrich@pengutronix.de>
To: Christian Melki <christian.melki@t2data.com>
Cc: ptxdist@pengutronix.de
Subject: Re: [ptxdist] [PATCH] bash: Version bump. 4.3.30 (+patches-33) -> 5.1.8 (patches-12).
Date: Wed, 5 Jan 2022 14:00:25 +0100	[thread overview]
Message-ID: <YdWWaWu6hllAgDnA@pengutronix.de> (raw)
In-Reply-To: <20211222130304.2549154-1-christian.melki@t2data.com>

On Wed, Dec 22, 2021 at 02:02:42PM +0100, Christian Melki wrote:
> Upgrade bash to 5.1.8 plus patches to patchlevel 12.
> Roll up patches as in previous series.
> 
> Fixes CVE-2019-18276 and CVE-2019-9924 with a new baseline, without patches.
> Fixes loads of bugs in bash.

License file changed. Please check & update.

"ptxdist licensecheck <package>" is your friend :-).

Michael

> 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                               |    4 +-
>  7 files changed, 265 insertions(+), 11088 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..c46196f7f 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))
> -- 
> 2.30.2
> 
> 
> _______________________________________________
> ptxdist mailing list
> ptxdist@pengutronix.de
> To unsubscribe, send a mail with subject "unsubscribe" to ptxdist-request@pengutronix.de
> 

-- 
Pengutronix e.K.                           |                             |
Steuerwalder Str. 21                       | http://www.pengutronix.de/  |
31137 Hildesheim, Germany                  | Phone: +49-5121-206917-0    |
Amtsgericht Hildesheim, HRA 2686           | Fax:   +49-5121-206917-5555 |

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


      parent reply	other threads:[~2022-01-05 13:00 UTC|newest]

Thread overview: 54+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2021-12-22 13:02 Christian Melki
2021-12-22 13:02 ` [ptxdist] [PATCH] bridge-utils: Version bump. 1.6 -> 1.7.1 Christian Melki
2022-01-21  7:18   ` [ptxdist] [APPLIED] " Michael Olbrich
2021-12-22 13:02 ` [ptxdist] [PATCH] curl: Version bump 7.77.0 -> 7.80.0 Christian Melki
2022-01-21  7:19   ` [ptxdist] [APPLIED] " Michael Olbrich
2021-12-22 13:02 ` [ptxdist] [PATCH] e2fsprogs: Version bump 1.46.2 -> 1.46.4 Christian Melki
2022-01-21  7:19   ` [ptxdist] [APPLIED] " Michael Olbrich
2021-12-22 13:02 ` [ptxdist] [PATCH] ethtool: Version bump. 5.13 -> 5.15 Christian Melki
2022-01-21  7:19   ` [ptxdist] [APPLIED] " Michael Olbrich
2021-12-22 13:02 ` [ptxdist] [PATCH] expat: Version bump 2.4.1 -> 2.4.2 Christian Melki
2022-01-21  7:19   ` [ptxdist] [APPLIED] " Michael Olbrich
2021-12-22 13:02 ` [ptxdist] [PATCH] host-libcap: BUILD_GPERF is reserved Christian Melki
2022-01-05 12:18   ` Michael Olbrich
2021-12-22 13:02 ` [ptxdist] [PATCH] iptables: Version bump 1.8.3 -> 1.8.7 Christian Melki
2022-01-06  7:10   ` Michael Olbrich
2022-01-21  7:19   ` [ptxdist] [APPLIED] " Michael Olbrich
2021-12-22 13:02 ` [ptxdist] [PATCH] jimtcl: Verison bump 0.80 -> 0.81 Christian Melki
2022-01-21  7:19   ` [ptxdist] [APPLIED] " Michael Olbrich
2021-12-22 13:02 ` [ptxdist] [PATCH] libcap-ng: Version bump 0.7.10 -> 0.8.2 Christian Melki
2022-01-21  7:19   ` [ptxdist] [APPLIED] " Michael Olbrich
2021-12-22 13:02 ` [ptxdist] [PATCH] libcap: Version bump 2.51 -> 2.62 Christian Melki
2022-01-05 12:21   ` Michael Olbrich
2022-01-05 12:32     ` Christian Melki
2022-01-05 12:46       ` Michael Olbrich
2021-12-22 13:02 ` [ptxdist] [PATCH] libffi: Version bump 3.3 -> 3.4.2 Christian Melki
2022-01-21  7:19   ` [ptxdist] [APPLIED] " Michael Olbrich
2021-12-22 13:02 ` [ptxdist] [PATCH] libjpeg: Version bump 2.1.0 -> 2.1.2 Christian Melki
2022-01-21  7:19   ` [ptxdist] [APPLIED] " Michael Olbrich
2021-12-22 13:02 ` [ptxdist] [PATCH] libmbim: Version bump 1.24.2 -> 1.26.2 Christian Melki
2022-01-21  7:19   ` [ptxdist] [APPLIED] " Michael Olbrich
2021-12-22 13:02 ` [ptxdist] [PATCH] libseccomp: Version bump 2.5.1 -> 2.5.3 Christian Melki
2022-01-06 10:56   ` Michael Olbrich
2021-12-22 13:02 ` [ptxdist] [PATCH] libunwind: Version bump 1.5.0 -> 1.6.2 Christian Melki
2022-01-21  7:19   ` [ptxdist] [APPLIED] " Michael Olbrich
2021-12-22 13:02 ` [ptxdist] [PATCH] openssh: Version bump 8.6p1 -> 8.8p1 Christian Melki
2022-01-21  7:19   ` [ptxdist] [APPLIED] " Michael Olbrich
2021-12-22 13:02 ` [ptxdist] [PATCH] screen: Version bump 4.5.0 -> 4.8.0 Christian Melki
2022-01-06 10:55   ` Michael Olbrich
2022-01-07  9:58     ` Christian Melki
2022-01-07 11:05       ` Michael Olbrich
2022-01-21  7:19   ` [ptxdist] [APPLIED] " Michael Olbrich
2021-12-22 13:03 ` [ptxdist] [PATCH] strace: Version bump 5.9 -> 5.15 Christian Melki
2022-01-05 12:53   ` Michael Olbrich
2021-12-22 13:03 ` [ptxdist] [PATCH] tcpdump: Version bump 4.93 -> 4.99.1 Christian Melki
2022-01-06  7:22   ` Michael Olbrich
2021-12-22 13:03 ` [ptxdist] [WIP: PATCH] usbutils: Version bump 007 -> 014 Christian Melki
2022-01-05 12:38   ` Michael Olbrich
2022-01-06 21:52     ` Christian Melki
2022-01-07  8:09       ` Michael Olbrich
2021-12-22 13:03 ` [ptxdist] [PATCH] util-linux-ng: Version bump 2.37 -> 2.37.2 Christian Melki
2022-01-21  7:19   ` [ptxdist] [APPLIED] " Michael Olbrich
2021-12-22 13:03 ` [ptxdist] [PATCH] zstd: Version bump 1.5.0 -> 1.5.1 Christian Melki
2022-01-21  7:19   ` [ptxdist] [APPLIED] " Michael Olbrich
2022-01-05 13:00 ` Michael Olbrich [this message]

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=YdWWaWu6hllAgDnA@pengutronix.de \
    --to=m.olbrich@pengutronix.de \
    --cc=christian.melki@t2data.com \
    --cc=ptxdist@pengutronix.de \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox