From: Christian Melki <christian.melki@t2data.com>
To: ptxdist@pengutronix.de
Subject: [ptxdist] [PATCH] bash: Version bump. 4.3.30 (+patches-33) -> 5.1.8 (patches-12).
Date: Wed, 22 Dec 2021 14:02:42 +0100 [thread overview]
Message-ID: <20211222130304.2549154-1-christian.melki@t2data.com> (raw)
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.
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
next reply other threads:[~2021-12-22 13:07 UTC|newest]
Thread overview: 54+ messages / expand[flat|nested] mbox.gz Atom feed top
2021-12-22 13:02 Christian Melki [this message]
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 ` [ptxdist] [PATCH] bash: Version bump. 4.3.30 (+patches-33) -> 5.1.8 (patches-12) Michael Olbrich
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=20211222130304.2549154-1-christian.melki@t2data.com \
--to=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