From: Michael Olbrich <m.olbrich@pengutronix.de>
To: Christian Melki <christian.melki@t2data.com>
Cc: ptxdist@pengutronix.de
Subject: Re: [ptxdist] [PATCH] bash: Version bump. 4.3.30 (+patches-33) -> 5.1.8 (patches-12).
Date: Wed, 5 Jan 2022 14:00:25 +0100 [thread overview]
Message-ID: <YdWWaWu6hllAgDnA@pengutronix.de> (raw)
In-Reply-To: <20211222130304.2549154-1-christian.melki@t2data.com>
On Wed, Dec 22, 2021 at 02:02:42PM +0100, Christian Melki wrote:
> Upgrade bash to 5.1.8 plus patches to patchlevel 12.
> Roll up patches as in previous series.
>
> Fixes CVE-2019-18276 and CVE-2019-9924 with a new baseline, without patches.
> Fixes loads of bugs in bash.
License file changed. Please check & update.
"ptxdist licensecheck <package>" is your friend :-).
Michael
> Signed-off-by: Christian Melki <christian.melki@t2data.com>
> ---
> .../bash-4.3.30/0001-Bash-4.3-patch-31.patch | 5467 -----------------
> .../bash-4.3.30/0002-Bash-4.3-patch-32.patch | 5409 ----------------
> .../bash-4.3.30/0003-Bash-4.3-patch-33.patch | 204 -
> patches/bash-4.3.30/series | 6 -
> .../bash-5.1.8/0001-Bash-5.1-patch-12.patch | 262 +
> patches/bash-5.1.8/series | 1 +
> rules/bash.make | 4 +-
> 7 files changed, 265 insertions(+), 11088 deletions(-)
> delete mode 100644 patches/bash-4.3.30/0001-Bash-4.3-patch-31.patch
> delete mode 100644 patches/bash-4.3.30/0002-Bash-4.3-patch-32.patch
> delete mode 100644 patches/bash-4.3.30/0003-Bash-4.3-patch-33.patch
> delete mode 100644 patches/bash-4.3.30/series
> create mode 100644 patches/bash-5.1.8/0001-Bash-5.1-patch-12.patch
> create mode 100644 patches/bash-5.1.8/series
>
> diff --git a/patches/bash-4.3.30/0001-Bash-4.3-patch-31.patch b/patches/bash-4.3.30/0001-Bash-4.3-patch-31.patch
> deleted file mode 100644
> index d9a187dcb..000000000
> --- a/patches/bash-4.3.30/0001-Bash-4.3-patch-31.patch
> +++ /dev/null
> @@ -1,5467 +0,0 @@
> -From: Chet Ramey <chet.ramey@case.edu>
> -Date: Thu, 15 Jan 2015 10:20:04 -0500
> -Subject: [PATCH] Bash-4.3 patch 31
> -
> ----
> - patchlevel.h | 2 +-
> - subst.h | 1 +
> - variables.c | 32 +-
> - variables.c.orig | 5365 ++++++++++++++++++++++++++++++++++++++++++++++++++++++
> - 4 files changed, 5397 insertions(+), 3 deletions(-)
> - create mode 100644 variables.c.orig
> -
> -diff --git a/patchlevel.h b/patchlevel.h
> -index e5dde5245275..0ad46aafbdd9 100644
> ---- a/patchlevel.h
> -+++ b/patchlevel.h
> -@@ -25,6 +25,6 @@
> - regexp `^#define[ ]*PATCHLEVEL', since that's what support/mkversion.sh
> - looks for to find the patch level (for the sccs version string). */
> -
> --#define PATCHLEVEL 30
> -+#define PATCHLEVEL 31
> -
> - #endif /* _PATCHLEVEL_H_ */
> -diff --git a/subst.h b/subst.h
> -index cedaf8b6b444..1c300ab96b04 100644
> ---- a/subst.h
> -+++ b/subst.h
> -@@ -47,6 +47,7 @@
> - #define ASS_MKASSOC 0x0004
> - #define ASS_MKGLOBAL 0x0008 /* force global assignment */
> - #define ASS_NAMEREF 0x0010 /* assigning to nameref variable */
> -+#define ASS_FROMREF 0x0020 /* assigning from value of nameref variable */
> -
> - /* Flags for the string extraction functions. */
> - #define SX_NOALLOC 0x0001 /* just skip; don't return substring */
> -diff --git a/variables.c b/variables.c
> -index 7c82710e0f0b..81b7877e32e8 100644
> ---- a/variables.c
> -+++ b/variables.c
> -@@ -2516,10 +2516,27 @@ bind_variable_internal (name, value, table, hflags, aflags)
> - HASH_TABLE *table;
> - int hflags, aflags;
> - {
> -- char *newval;
> -+ char *newname, *newval;
> - SHELL_VAR *entry;
> -+#if defined (ARRAY_VARS)
> -+ arrayind_t ind;
> -+ char *subp;
> -+ int sublen;
> -+#endif
> -
> -+ newname = 0;
> -+#if defined (ARRAY_VARS)
> -+ if ((aflags & ASS_FROMREF) && (hflags & HASH_NOSRCH) == 0 && valid_array_reference (name))
> -+ {
> -+ newname = array_variable_name (name, &subp, &sublen);
> -+ if (newname == 0)
> -+ return (SHELL_VAR *)NULL; /* XXX */
> -+ entry = hash_lookup (newname, table);
> -+ }
> -+ else
> -+#endif
> - entry = (hflags & HASH_NOSRCH) ? (SHELL_VAR *)NULL : hash_lookup (name, table);
> -+
> - /* Follow the nameref chain here if this is the global variables table */
> - if (entry && nameref_p (entry) && (invisible_p (entry) == 0) && table == global_variables->table)
> - {
> -@@ -2550,6 +2567,16 @@ bind_variable_internal (name, value, table, hflags, aflags)
> - var_setvalue (entry, make_variable_value (entry, value, 0));
> - }
> - }
> -+#if defined (ARRAY_VARS)
> -+ else if (entry == 0 && newname)
> -+ {
> -+ entry = make_new_array_variable (newname); /* indexed array by default */
> -+ if (entry == 0)
> -+ return entry;
> -+ ind = array_expand_index (name, subp, sublen);
> -+ bind_array_element (entry, ind, value, aflags);
> -+ }
> -+#endif
> - else if (entry == 0)
> - {
> - entry = make_new_variable (name, table);
> -@@ -2670,7 +2697,8 @@ bind_variable (name, value, flags)
> - normal. */
> - if (nameref_cell (nv) == 0)
> - return (bind_variable_internal (nv->name, value, nvc->table, 0, flags));
> -- return (bind_variable_internal (nameref_cell (nv), value, nvc->table, 0, flags));
> -+ /* XXX - bug here with ref=array[index] */
> -+ return (bind_variable_internal (nameref_cell (nv), value, nvc->table, 0, flags|ASS_FROMREF));
> - }
> - else
> - v = nv;
> -diff --git a/variables.c.orig b/variables.c.orig
> -new file mode 100644
> -index 000000000000..7c82710e0f0b
> ---- /dev/null
> -+++ b/variables.c.orig
> -@@ -0,0 +1,5365 @@
> -+/* variables.c -- Functions for hacking shell variables. */
> -+
> -+/* Copyright (C) 1987-2013 Free Software Foundation, Inc.
> -+
> -+ This file is part of GNU Bash, the Bourne Again SHell.
> -+
> -+ Bash is free software: you can redistribute it and/or modify
> -+ it under the terms of the GNU General Public License as published by
> -+ the Free Software Foundation, either version 3 of the License, or
> -+ (at your option) any later version.
> -+
> -+ Bash is distributed in the hope that it will be useful,
> -+ but WITHOUT ANY WARRANTY; without even the implied warranty of
> -+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
> -+ GNU General Public License for more details.
> -+
> -+ You should have received a copy of the GNU General Public License
> -+ along with Bash. If not, see <http://www.gnu.org/licenses/>.
> -+*/
> -+
> -+#include "config.h"
> -+
> -+#include "bashtypes.h"
> -+#include "posixstat.h"
> -+#include "posixtime.h"
> -+
> -+#if defined (__QNX__)
> -+# if defined (__QNXNTO__)
> -+# include <sys/netmgr.h>
> -+# else
> -+# include <sys/vc.h>
> -+# endif /* !__QNXNTO__ */
> -+#endif /* __QNX__ */
> -+
> -+#if defined (HAVE_UNISTD_H)
> -+# include <unistd.h>
> -+#endif
> -+
> -+#include <stdio.h>
> -+#include "chartypes.h"
> -+#if defined (HAVE_PWD_H)
> -+# include <pwd.h>
> -+#endif
> -+#include "bashansi.h"
> -+#include "bashintl.h"
> -+
> -+#define NEED_XTRACE_SET_DECL
> -+
> -+#include "shell.h"
> -+#include "flags.h"
> -+#include "execute_cmd.h"
> -+#include "findcmd.h"
> -+#include "mailcheck.h"
> -+#include "input.h"
> -+#include "hashcmd.h"
> -+#include "pathexp.h"
> -+#include "alias.h"
> -+#include "jobs.h"
> -+
> -+#include "version.h"
> -+
> -+#include "builtins/getopt.h"
> -+#include "builtins/common.h"
> -+#include "builtins/builtext.h"
> -+
> -+#if defined (READLINE)
> -+# include "bashline.h"
> -+# include <readline/readline.h>
> -+#else
> -+# include <tilde/tilde.h>
> -+#endif
> -+
> -+#if defined (HISTORY)
> -+# include "bashhist.h"
> -+# include <readline/history.h>
> -+#endif /* HISTORY */
> -+
> -+#if defined (PROGRAMMABLE_COMPLETION)
> -+# include "pcomplete.h"
> -+#endif
> -+
> -+#define TEMPENV_HASH_BUCKETS 4 /* must be power of two */
> -+
> -+#define ifsname(s) ((s)[0] == 'I' && (s)[1] == 'F' && (s)[2] == 'S' && (s)[3] == '\0')
> -+
> -+#define BASHFUNC_PREFIX "BASH_FUNC_"
> -+#define BASHFUNC_PREFLEN 10 /* == strlen(BASHFUNC_PREFIX */
> -+#define BASHFUNC_SUFFIX "%%"
> -+#define BASHFUNC_SUFFLEN 2 /* == strlen(BASHFUNC_SUFFIX) */
> -+
> -+extern char **environ;
> -+
> -+/* Variables used here and defined in other files. */
> -+extern int posixly_correct;
> -+extern int line_number, line_number_base;
> -+extern int subshell_environment, indirection_level, subshell_level;
> -+extern int build_version, patch_level;
> -+extern int expanding_redir;
> -+extern int last_command_exit_value;
> -+extern char *dist_version, *release_status;
> -+extern char *shell_name;
> -+extern char *primary_prompt, *secondary_prompt;
> -+extern char *current_host_name;
> -+extern sh_builtin_func_t *this_shell_builtin;
> -+extern SHELL_VAR *this_shell_function;
> -+extern char *the_printed_command_except_trap;
> -+extern char *this_command_name;
> -+extern char *command_execution_string;
> -+extern time_t shell_start_time;
> -+extern int assigning_in_environment;
> -+extern int executing_builtin;
> -+extern int funcnest_max;
> -+
> -+#if defined (READLINE)
> -+extern int no_line_editing;
> -+extern int perform_hostname_completion;
> -+#endif
> -+
> -+/* The list of shell variables that the user has created at the global
> -+ scope, or that came from the environment. */
> -+VAR_CONTEXT *global_variables = (VAR_CONTEXT *)NULL;
> -+
> -+/* The current list of shell variables, including function scopes */
> -+VAR_CONTEXT *shell_variables = (VAR_CONTEXT *)NULL;
> -+
> -+/* The list of shell functions that the user has created, or that came from
> -+ the environment. */
> -+HASH_TABLE *shell_functions = (HASH_TABLE *)NULL;
> -+
> -+#if defined (DEBUGGER)
> -+/* The table of shell function definitions that the user defined or that
> -+ came from the environment. */
> -+HASH_TABLE *shell_function_defs = (HASH_TABLE *)NULL;
> -+#endif
> -+
> -+/* The current variable context. This is really a count of how deep into
> -+ executing functions we are. */
> -+int variable_context = 0;
> -+
> -+/* The set of shell assignments which are made only in the environment
> -+ for a single command. */
> -+HASH_TABLE *temporary_env = (HASH_TABLE *)NULL;
> -+
> -+/* Set to non-zero if an assignment error occurs while putting variables
> -+ into the temporary environment. */
> -+int tempenv_assign_error;
> -+
> -+/* Some funky variables which are known about specially. Here is where
> -+ "$*", "$1", and all the cruft is kept. */
> -+char *dollar_vars[10];
> -+WORD_LIST *rest_of_args = (WORD_LIST *)NULL;
> -+
> -+/* The value of $$. */
> -+pid_t dollar_dollar_pid;
> -+
> -+/* Non-zero means that we have to remake EXPORT_ENV. */
> -+int array_needs_making = 1;
> -+
> -+/* The number of times BASH has been executed. This is set
> -+ by initialize_variables (). */
> -+int shell_level = 0;
> -+
> -+/* An array which is passed to commands as their environment. It is
> -+ manufactured from the union of the initial environment and the
> -+ shell variables that are marked for export. */
> -+char **export_env = (char **)NULL;
> -+static int export_env_index;
> -+static int export_env_size;
> -+
> -+#if defined (READLINE)
> -+static int winsize_assignment; /* currently assigning to LINES or COLUMNS */
> -+#endif
> -+
> -+static HASH_TABLE *last_table_searched; /* hash_lookup sets this */
> -+
> -+/* Some forward declarations. */
> -+static void create_variable_tables __P((void));
> -+
> -+static void set_machine_vars __P((void));
> -+static void set_home_var __P((void));
> -+static void set_shell_var __P((void));
> -+static char *get_bash_name __P((void));
> -+static void initialize_shell_level __P((void));
> -+static void uidset __P((void));
> -+#if defined (ARRAY_VARS)
> -+static void make_vers_array __P((void));
> -+#endif
> -+
> -+static SHELL_VAR *null_assign __P((SHELL_VAR *, char *, arrayind_t, char *));
> -+#if defined (ARRAY_VARS)
> -+static SHELL_VAR *null_array_assign __P((SHELL_VAR *, char *, arrayind_t, char *));
> -+#endif
> -+static SHELL_VAR *get_self __P((SHELL_VAR *));
> -+
> -+#if defined (ARRAY_VARS)
> -+static SHELL_VAR *init_dynamic_array_var __P((char *, sh_var_value_func_t *, sh_var_assign_func_t *, int));
> -+static SHELL_VAR *init_dynamic_assoc_var __P((char *, sh_var_value_func_t *, sh_var_assign_func_t *, int));
> -+#endif
> -+
> -+static SHELL_VAR *assign_seconds __P((SHELL_VAR *, char *, arrayind_t, char *));
> -+static SHELL_VAR *get_seconds __P((SHELL_VAR *));
> -+static SHELL_VAR *init_seconds_var __P((void));
> -+
> -+static int brand __P((void));
> -+static void sbrand __P((unsigned long)); /* set bash random number generator. */
> -+static void seedrand __P((void)); /* seed generator randomly */
> -+static SHELL_VAR *assign_random __P((SHELL_VAR *, char *, arrayind_t, char *));
> -+static SHELL_VAR *get_random __P((SHELL_VAR *));
> -+
> -+static SHELL_VAR *assign_lineno __P((SHELL_VAR *, char *, arrayind_t, char *));
> -+static SHELL_VAR *get_lineno __P((SHELL_VAR *));
> -+
> -+static SHELL_VAR *assign_subshell __P((SHELL_VAR *, char *, arrayind_t, char *));
> -+static SHELL_VAR *get_subshell __P((SHELL_VAR *));
> -+
> -+static SHELL_VAR *get_bashpid __P((SHELL_VAR *));
> -+
> -+#if defined (HISTORY)
> -+static SHELL_VAR *get_histcmd __P((SHELL_VAR *));
> -+#endif
> -+
> -+#if defined (READLINE)
> -+static SHELL_VAR *get_comp_wordbreaks __P((SHELL_VAR *));
> -+static SHELL_VAR *assign_comp_wordbreaks __P((SHELL_VAR *, char *, arrayind_t, char *));
> -+#endif
> -+
> -+#if defined (PUSHD_AND_POPD) && defined (ARRAY_VARS)
> -+static SHELL_VAR *assign_dirstack __P((SHELL_VAR *, char *, arrayind_t, char *));
> -+static SHELL_VAR *get_dirstack __P((SHELL_VAR *));
> -+#endif
> -+
> -+#if defined (ARRAY_VARS)
> -+static SHELL_VAR *get_groupset __P((SHELL_VAR *));
> -+
> -+static SHELL_VAR *build_hashcmd __P((SHELL_VAR *));
> -+static SHELL_VAR *get_hashcmd __P((SHELL_VAR *));
> -+static SHELL_VAR *assign_hashcmd __P((SHELL_VAR *, char *, arrayind_t, char *));
> -+# if defined (ALIAS)
> -+static SHELL_VAR *build_aliasvar __P((SHELL_VAR *));
> -+static SHELL_VAR *get_aliasvar __P((SHELL_VAR *));
> -+static SHELL_VAR *assign_aliasvar __P((SHELL_VAR *, char *, arrayind_t, char *));
> -+# endif
> -+#endif
> -+
> -+static SHELL_VAR *get_funcname __P((SHELL_VAR *));
> -+static SHELL_VAR *init_funcname_var __P((void));
> -+
> -+static void initialize_dynamic_variables __P((void));
> -+
> -+static SHELL_VAR *hash_lookup __P((const char *, HASH_TABLE *));
> -+static SHELL_VAR *new_shell_variable __P((const char *));
> -+static SHELL_VAR *make_new_variable __P((const char *, HASH_TABLE *));
> -+static SHELL_VAR *bind_variable_internal __P((const char *, char *, HASH_TABLE *, int, int));
> -+
> -+static void dispose_variable_value __P((SHELL_VAR *));
> -+static void free_variable_hash_data __P((PTR_T));
> -+
> -+static VARLIST *vlist_alloc __P((int));
> -+static VARLIST *vlist_realloc __P((VARLIST *, int));
> -+static void vlist_add __P((VARLIST *, SHELL_VAR *, int));
> -+
> -+static void flatten __P((HASH_TABLE *, sh_var_map_func_t *, VARLIST *, int));
> -+
> -+static int qsort_var_comp __P((SHELL_VAR **, SHELL_VAR **));
> -+
> -+static SHELL_VAR **vapply __P((sh_var_map_func_t *));
> -+static SHELL_VAR **fapply __P((sh_var_map_func_t *));
> -+
> -+static int visible_var __P((SHELL_VAR *));
> -+static int visible_and_exported __P((SHELL_VAR *));
> -+static int export_environment_candidate __P((SHELL_VAR *));
> -+static int local_and_exported __P((SHELL_VAR *));
> -+static int variable_in_context __P((SHELL_VAR *));
> -+#if defined (ARRAY_VARS)
> -+static int visible_array_vars __P((SHELL_VAR *));
> -+#endif
> -+
> -+static SHELL_VAR *find_nameref_at_context __P((SHELL_VAR *, VAR_CONTEXT *));
> -+static SHELL_VAR *find_variable_nameref_context __P((SHELL_VAR *, VAR_CONTEXT *, VAR_CONTEXT **));
> -+static SHELL_VAR *find_variable_last_nameref_context __P((SHELL_VAR *, VAR_CONTEXT *, VAR_CONTEXT **));
> -+
> -+static SHELL_VAR *bind_tempenv_variable __P((const char *, char *));
> -+static void push_temp_var __P((PTR_T));
> -+static void propagate_temp_var __P((PTR_T));
> -+static void dispose_temporary_env __P((sh_free_func_t *));
> -+
> -+static inline char *mk_env_string __P((const char *, const char *, int));
> -+static char **make_env_array_from_var_list __P((SHELL_VAR **));
> -+static char **make_var_export_array __P((VAR_CONTEXT *));
> -+static char **make_func_export_array __P((void));
> -+static void add_temp_array_to_env __P((char **, int, int));
> -+
> -+static int n_shell_variables __P((void));
> -+static int set_context __P((SHELL_VAR *));
> -+
> -+static void push_func_var __P((PTR_T));
> -+static void push_exported_var __P((PTR_T));
> -+
> -+static inline int find_special_var __P((const char *));
> -+
> -+static void
> -+create_variable_tables ()
> -+{
> -+ if (shell_variables == 0)
> -+ {
> -+ shell_variables = global_variables = new_var_context ((char *)NULL, 0);
> -+ shell_variables->scope = 0;
> -+ shell_variables->table = hash_create (0);
> -+ }
> -+
> -+ if (shell_functions == 0)
> -+ shell_functions = hash_create (0);
> -+
> -+#if defined (DEBUGGER)
> -+ if (shell_function_defs == 0)
> -+ shell_function_defs = hash_create (0);
> -+#endif
> -+}
> -+
> -+/* Initialize the shell variables from the current environment.
> -+ If PRIVMODE is nonzero, don't import functions from ENV or
> -+ parse $SHELLOPTS. */
> -+void
> -+initialize_shell_variables (env, privmode)
> -+ char **env;
> -+ int privmode;
> -+{
> -+ char *name, *string, *temp_string;
> -+ int c, char_index, string_index, string_length, ro;
> -+ SHELL_VAR *temp_var;
> -+
> -+ create_variable_tables ();
> -+
> -+ for (string_index = 0; string = env[string_index++]; )
> -+ {
> -+ char_index = 0;
> -+ name = string;
> -+ while ((c = *string++) && c != '=')
> -+ ;
> -+ if (string[-1] == '=')
> -+ char_index = string - name - 1;
> -+
> -+ /* If there are weird things in the environment, like `=xxx' or a
> -+ string without an `=', just skip them. */
> -+ if (char_index == 0)
> -+ continue;
> -+
> -+ /* ASSERT(name[char_index] == '=') */
> -+ name[char_index] = '\0';
> -+ /* Now, name = env variable name, string = env variable value, and
> -+ char_index == strlen (name) */
> -+
> -+ temp_var = (SHELL_VAR *)NULL;
> -+
> -+ /* If exported function, define it now. Don't import functions from
> -+ the environment in privileged mode. */
> -+ if (privmode == 0 && read_but_dont_execute == 0 &&
> -+ STREQN (BASHFUNC_PREFIX, name, BASHFUNC_PREFLEN) &&
> -+ STREQ (BASHFUNC_SUFFIX, name + char_index - BASHFUNC_SUFFLEN) &&
> -+ STREQN ("() {", string, 4))
> -+ {
> -+ size_t namelen;
> -+ char *tname; /* desired imported function name */
> -+
> -+ namelen = char_index - BASHFUNC_PREFLEN - BASHFUNC_SUFFLEN;
> -+
> -+ tname = name + BASHFUNC_PREFLEN; /* start of func name */
> -+ tname[namelen] = '\0'; /* now tname == func name */
> -+
> -+ string_length = strlen (string);
> -+ temp_string = (char *)xmalloc (namelen + string_length + 2);
> -+
> -+ memcpy (temp_string, tname, namelen);
> -+ temp_string[namelen] = ' ';
> -+ memcpy (temp_string + namelen + 1, string, string_length + 1);
> -+
> -+ /* Don't import function names that are invalid identifiers from the
> -+ environment, though we still allow them to be defined as shell
> -+ variables. */
> -+ if (absolute_program (tname) == 0 && (posixly_correct == 0 || legal_identifier (tname)))
> -+ parse_and_execute (temp_string, tname, SEVAL_NONINT|SEVAL_NOHIST|SEVAL_FUNCDEF|SEVAL_ONECMD);
> -+
> -+ if (temp_var = find_function (tname))
> -+ {
> -+ VSETATTR (temp_var, (att_exported|att_imported));
> -+ array_needs_making = 1;
> -+ }
> -+ else
> -+ {
> -+ if (temp_var = bind_variable (name, string, 0))
> -+ {
> -+ VSETATTR (temp_var, (att_exported | att_imported | att_invisible));
> -+ array_needs_making = 1;
> -+ }
> -+ last_command_exit_value = 1;
> -+ report_error (_("error importing function definition for `%s'"), tname);
> -+ }
> -+
> -+ /* Restore original suffix */
> -+ tname[namelen] = BASHFUNC_SUFFIX[0];
> -+ }
> -+#if defined (ARRAY_VARS)
> -+# if ARRAY_EXPORT
> -+ /* Array variables may not yet be exported. */
> -+ else if (*string == '(' && string[1] == '[' && string[strlen (string) - 1] == ')')
> -+ {
> -+ string_length = 1;
> -+ temp_string = extract_array_assignment_list (string, &string_length);
> -+ temp_var = assign_array_from_string (name, temp_string);
> -+ FREE (temp_string);
> -+ VSETATTR (temp_var, (att_exported | att_imported));
> -+ array_needs_making = 1;
> -+ }
> -+# endif /* ARRAY_EXPORT */
> -+#endif
> -+#if 0
> -+ else if (legal_identifier (name))
> -+#else
> -+ else
> -+#endif
> -+ {
> -+ ro = 0;
> -+ if (posixly_correct && STREQ (name, "SHELLOPTS"))
> -+ {
> -+ temp_var = find_variable ("SHELLOPTS");
> -+ ro = temp_var && readonly_p (temp_var);
> -+ if (temp_var)
> -+ VUNSETATTR (temp_var, att_readonly);
> -+ }
> -+ temp_var = bind_variable (name, string, 0);
> -+ if (temp_var)
> -+ {
> -+ if (legal_identifier (name))
> -+ VSETATTR (temp_var, (att_exported | att_imported));
> -+ else
> -+ VSETATTR (temp_var, (att_exported | att_imported | att_invisible));
> -+ if (ro)
> -+ VSETATTR (temp_var, att_readonly);
> -+ array_needs_making = 1;
> -+ }
> -+ }
> -+
> -+ name[char_index] = '=';
> -+ /* temp_var can be NULL if it was an exported function with a syntax
> -+ error (a different bug, but it still shouldn't dump core). */
> -+ if (temp_var && function_p (temp_var) == 0) /* XXX not yet */
> -+ {
> -+ CACHE_IMPORTSTR (temp_var, name);
> -+ }
> -+ }
> -+
> -+ set_pwd ();
> -+
> -+ /* Set up initial value of $_ */
> -+ temp_var = set_if_not ("_", dollar_vars[0]);
> -+
> -+ /* Remember this pid. */
> -+ dollar_dollar_pid = getpid ();
> -+
> -+ /* Now make our own defaults in case the vars that we think are
> -+ important are missing. */
> -+ temp_var = set_if_not ("PATH", DEFAULT_PATH_VALUE);
> -+#if 0
> -+ set_auto_export (temp_var); /* XXX */
> -+#endif
> -+
> -+ temp_var = set_if_not ("TERM", "dumb");
> -+#if 0
> -+ set_auto_export (temp_var); /* XXX */
> -+#endif
> -+
> -+#if defined (__QNX__)
> -+ /* set node id -- don't import it from the environment */
> -+ {
> -+ char node_name[22];
> -+# if defined (__QNXNTO__)
> -+ netmgr_ndtostr(ND2S_LOCAL_STR, ND_LOCAL_NODE, node_name, sizeof(node_name));
> -+# else
> -+ qnx_nidtostr (getnid (), node_name, sizeof (node_name));
> -+# endif
> -+ temp_var = bind_variable ("NODE", node_name, 0);
> -+ set_auto_export (temp_var);
> -+ }
> -+#endif
> -+
> -+ /* set up the prompts. */
> -+ if (interactive_shell)
> -+ {
> -+#if defined (PROMPT_STRING_DECODE)
> -+ set_if_not ("PS1", primary_prompt);
> -+#else
> -+ if (current_user.uid == -1)
> -+ get_current_user_info ();
> -+ set_if_not ("PS1", current_user.euid == 0 ? "# " : primary_prompt);
> -+#endif
> -+ set_if_not ("PS2", secondary_prompt);
> -+ }
> -+ set_if_not ("PS4", "+ ");
> -+
> -+ /* Don't allow IFS to be imported from the environment. */
> -+ temp_var = bind_variable ("IFS", " \t\n", 0);
> -+ setifs (temp_var);
> -+
> -+ /* Magic machine types. Pretty convenient. */
> -+ set_machine_vars ();
> -+
> -+ /* Default MAILCHECK for interactive shells. Defer the creation of a
> -+ default MAILPATH until the startup files are read, because MAIL
> -+ names a mail file if MAILPATH is not set, and we should provide a
> -+ default only if neither is set. */
> -+ if (interactive_shell)
> -+ {
> -+ temp_var = set_if_not ("MAILCHECK", posixly_correct ? "600" : "60");
> -+ VSETATTR (temp_var, att_integer);
> -+ }
> -+
> -+ /* Do some things with shell level. */
> -+ initialize_shell_level ();
> -+
> -+ set_ppid ();
> -+
> -+ /* Initialize the `getopts' stuff. */
> -+ temp_var = bind_variable ("OPTIND", "1", 0);
> -+ VSETATTR (temp_var, att_integer);
> -+ getopts_reset (0);
> -+ bind_variable ("OPTERR", "1", 0);
> -+ sh_opterr = 1;
> -+
> -+ if (login_shell == 1 && posixly_correct == 0)
> -+ set_home_var ();
> -+
> -+ /* Get the full pathname to THIS shell, and set the BASH variable
> -+ to it. */
> -+ name = get_bash_name ();
> -+ temp_var = bind_variable ("BASH", name, 0);
> -+ free (name);
> -+
> -+ /* Make the exported environment variable SHELL be the user's login
> -+ shell. Note that the `tset' command looks at this variable
> -+ to determine what style of commands to output; if it ends in "csh",
> -+ then C-shell commands are output, else Bourne shell commands. */
> -+ set_shell_var ();
> -+
> -+ /* Make a variable called BASH_VERSION which contains the version info. */
> -+ bind_variable ("BASH_VERSION", shell_version_string (), 0);
> -+#if defined (ARRAY_VARS)
> -+ make_vers_array ();
> -+#endif
> -+
> -+ if (command_execution_string)
> -+ bind_variable ("BASH_EXECUTION_STRING", command_execution_string, 0);
> -+
> -+ /* Find out if we're supposed to be in Posix.2 mode via an
> -+ environment variable. */
> -+ temp_var = find_variable ("POSIXLY_CORRECT");
> -+ if (!temp_var)
> -+ temp_var = find_variable ("POSIX_PEDANTIC");
> -+ if (temp_var && imported_p (temp_var))
> -+ sv_strict_posix (temp_var->name);
> -+
> -+#if defined (HISTORY)
> -+ /* Set history variables to defaults, and then do whatever we would
> -+ do if the variable had just been set. Do this only in the case
> -+ that we are remembering commands on the history list. */
> -+ if (remember_on_history)
> -+ {
> -+ name = bash_tilde_expand (posixly_correct ? "~/.sh_history" : "~/.bash_history", 0);
> -+
> -+ set_if_not ("HISTFILE", name);
> -+ free (name);
> -+ }
> -+#endif /* HISTORY */
> -+
> -+ /* Seed the random number generator. */
> -+ seedrand ();
> -+
> -+ /* Handle some "special" variables that we may have inherited from a
> -+ parent shell. */
> -+ if (interactive_shell)
> -+ {
> -+ temp_var = find_variable ("IGNOREEOF");
> -+ if (!temp_var)
> -+ temp_var = find_variable ("ignoreeof");
> -+ if (temp_var && imported_p (temp_var))
> -+ sv_ignoreeof (temp_var->name);
> -+ }
> -+
> -+#if defined (HISTORY)
> -+ if (interactive_shell && remember_on_history)
> -+ {
> -+ sv_history_control ("HISTCONTROL");
> -+ sv_histignore ("HISTIGNORE");
> -+ sv_histtimefmt ("HISTTIMEFORMAT");
> -+ }
> -+#endif /* HISTORY */
> -+
> -+#if defined (READLINE) && defined (STRICT_POSIX)
> -+ /* POSIXLY_CORRECT will only be 1 here if the shell was compiled
> -+ -DSTRICT_POSIX */
> -+ if (interactive_shell && posixly_correct && no_line_editing == 0)
> -+ rl_prefer_env_winsize = 1;
> -+#endif /* READLINE && STRICT_POSIX */
> -+
> -+ /*
> -+ * 24 October 2001
> -+ *
> -+ * I'm tired of the arguing and bug reports. Bash now leaves SSH_CLIENT
> -+ * and SSH2_CLIENT alone. I'm going to rely on the shell_level check in
> -+ * isnetconn() to avoid running the startup files more often than wanted.
> -+ * That will, of course, only work if the user's login shell is bash, so
> -+ * I've made that behavior conditional on SSH_SOURCE_BASHRC being defined
> -+ * in config-top.h.
> -+ */
> -+#if 0
> -+ temp_var = find_variable ("SSH_CLIENT");
> -+ if (temp_var && imported_p (temp_var))
> -+ {
> -+ VUNSETATTR (temp_var, att_exported);
> -+ array_needs_making = 1;
> -+ }
> -+ temp_var = find_variable ("SSH2_CLIENT");
> -+ if (temp_var && imported_p (temp_var))
> -+ {
> -+ VUNSETATTR (temp_var, att_exported);
> -+ array_needs_making = 1;
> -+ }
> -+#endif
> -+
> -+ /* Get the user's real and effective user ids. */
> -+ uidset ();
> -+
> -+ temp_var = find_variable ("BASH_XTRACEFD");
> -+ if (temp_var && imported_p (temp_var))
> -+ sv_xtracefd (temp_var->name);
> -+
> -+ /* Initialize the dynamic variables, and seed their values. */
> -+ initialize_dynamic_variables ();
> -+}
> -+
> -+/* **************************************************************** */
> -+/* */
> -+/* Setting values for special shell variables */
> -+/* */
> -+/* **************************************************************** */
> -+
> -+static void
> -+set_machine_vars ()
> -+{
> -+ SHELL_VAR *temp_var;
> -+
> -+ temp_var = set_if_not ("HOSTTYPE", HOSTTYPE);
> -+ temp_var = set_if_not ("OSTYPE", OSTYPE);
> -+ temp_var = set_if_not ("MACHTYPE", MACHTYPE);
> -+
> -+ temp_var = set_if_not ("HOSTNAME", current_host_name);
> -+}
> -+
> -+/* Set $HOME to the information in the password file if we didn't get
> -+ it from the environment. */
> -+
> -+/* This function is not static so the tilde and readline libraries can
> -+ use it. */
> -+char *
> -+sh_get_home_dir ()
> -+{
> -+ if (current_user.home_dir == 0)
> -+ get_current_user_info ();
> -+ return current_user.home_dir;
> -+}
> -+
> -+static void
> -+set_home_var ()
> -+{
> -+ SHELL_VAR *temp_var;
> -+
> -+ temp_var = find_variable ("HOME");
> -+ if (temp_var == 0)
> -+ temp_var = bind_variable ("HOME", sh_get_home_dir (), 0);
> -+#if 0
> -+ VSETATTR (temp_var, att_exported);
> -+#endif
> -+}
> -+
> -+/* Set $SHELL to the user's login shell if it is not already set. Call
> -+ get_current_user_info if we haven't already fetched the shell. */
> -+static void
> -+set_shell_var ()
> -+{
> -+ SHELL_VAR *temp_var;
> -+
> -+ temp_var = find_variable ("SHELL");
> -+ if (temp_var == 0)
> -+ {
> -+ if (current_user.shell == 0)
> -+ get_current_user_info ();
> -+ temp_var = bind_variable ("SHELL", current_user.shell, 0);
> -+ }
> -+#if 0
> -+ VSETATTR (temp_var, att_exported);
> -+#endif
> -+}
> -+
> -+static char *
> -+get_bash_name ()
> -+{
> -+ char *name;
> -+
> -+ if ((login_shell == 1) && RELPATH(shell_name))
> -+ {
> -+ if (current_user.shell == 0)
> -+ get_current_user_info ();
> -+ name = savestring (current_user.shell);
> -+ }
> -+ else if (ABSPATH(shell_name))
> -+ name = savestring (shell_name);
> -+ else if (shell_name[0] == '.' && shell_name[1] == '/')
> -+ {
> -+ /* Fast path for common case. */
> -+ char *cdir;
> -+ int len;
> -+
> -+ cdir = get_string_value ("PWD");
> -+ if (cdir)
> -+ {
> -+ len = strlen (cdir);
> -+ name = (char *)xmalloc (len + strlen (shell_name) + 1);
> -+ strcpy (name, cdir);
> -+ strcpy (name + len, shell_name + 1);
> -+ }
> -+ else
> -+ name = savestring (shell_name);
> -+ }
> -+ else
> -+ {
> -+ char *tname;
> -+ int s;
> -+
> -+ tname = find_user_command (shell_name);
> -+
> -+ if (tname == 0)
> -+ {
> -+ /* Try the current directory. If there is not an executable
> -+ there, just punt and use the login shell. */
> -+ s = file_status (shell_name);
> -+ if (s & FS_EXECABLE)
> -+ {
> -+ tname = make_absolute (shell_name, get_string_value ("PWD"));
> -+ if (*shell_name == '.')
> -+ {
> -+ name = sh_canonpath (tname, PATH_CHECKDOTDOT|PATH_CHECKEXISTS);
> -+ if (name == 0)
> -+ name = tname;
> -+ else
> -+ free (tname);
> -+ }
> -+ else
> -+ name = tname;
> -+ }
> -+ else
> -+ {
> -+ if (current_user.shell == 0)
> -+ get_current_user_info ();
> -+ name = savestring (current_user.shell);
> -+ }
> -+ }
> -+ else
> -+ {
> -+ name = full_pathname (tname);
> -+ free (tname);
> -+ }
> -+ }
> -+
> -+ return (name);
> -+}
> -+
> -+void
> -+adjust_shell_level (change)
> -+ int change;
> -+{
> -+ char new_level[5], *old_SHLVL;
> -+ intmax_t old_level;
> -+ SHELL_VAR *temp_var;
> -+
> -+ old_SHLVL = get_string_value ("SHLVL");
> -+ if (old_SHLVL == 0 || *old_SHLVL == '\0' || legal_number (old_SHLVL, &old_level) == 0)
> -+ old_level = 0;
> -+
> -+ shell_level = old_level + change;
> -+ if (shell_level < 0)
> -+ shell_level = 0;
> -+ else if (shell_level > 1000)
> -+ {
> -+ internal_warning (_("shell level (%d) too high, resetting to 1"), shell_level);
> -+ shell_level = 1;
> -+ }
> -+
> -+ /* We don't need the full generality of itos here. */
> -+ if (shell_level < 10)
> -+ {
> -+ new_level[0] = shell_level + '0';
> -+ new_level[1] = '\0';
> -+ }
> -+ else if (shell_level < 100)
> -+ {
> -+ new_level[0] = (shell_level / 10) + '0';
> -+ new_level[1] = (shell_level % 10) + '0';
> -+ new_level[2] = '\0';
> -+ }
> -+ else if (shell_level < 1000)
> -+ {
> -+ new_level[0] = (shell_level / 100) + '0';
> -+ old_level = shell_level % 100;
> -+ new_level[1] = (old_level / 10) + '0';
> -+ new_level[2] = (old_level % 10) + '0';
> -+ new_level[3] = '\0';
> -+ }
> -+
> -+ temp_var = bind_variable ("SHLVL", new_level, 0);
> -+ set_auto_export (temp_var);
> -+}
> -+
> -+static void
> -+initialize_shell_level ()
> -+{
> -+ adjust_shell_level (1);
> -+}
> -+
> -+/* If we got PWD from the environment, update our idea of the current
> -+ working directory. In any case, make sure that PWD exists before
> -+ checking it. It is possible for getcwd () to fail on shell startup,
> -+ and in that case, PWD would be undefined. If this is an interactive
> -+ login shell, see if $HOME is the current working directory, and if
> -+ that's not the same string as $PWD, set PWD=$HOME. */
> -+
> -+void
> -+set_pwd ()
> -+{
> -+ SHELL_VAR *temp_var, *home_var;
> -+ char *temp_string, *home_string;
> -+
> -+ home_var = find_variable ("HOME");
> -+ home_string = home_var ? value_cell (home_var) : (char *)NULL;
> -+
> -+ temp_var = find_variable ("PWD");
> -+ if (temp_var && imported_p (temp_var) &&
> -+ (temp_string = value_cell (temp_var)) &&
> -+ same_file (temp_string, ".", (struct stat *)NULL, (struct stat *)NULL))
> -+ set_working_directory (temp_string);
> -+ else if (home_string && interactive_shell && login_shell &&
> -+ same_file (home_string, ".", (struct stat *)NULL, (struct stat *)NULL))
> -+ {
> -+ set_working_directory (home_string);
> -+ temp_var = bind_variable ("PWD", home_string, 0);
> -+ set_auto_export (temp_var);
> -+ }
> -+ else
> -+ {
> -+ temp_string = get_working_directory ("shell-init");
> -+ if (temp_string)
> -+ {
> -+ temp_var = bind_variable ("PWD", temp_string, 0);
> -+ set_auto_export (temp_var);
> -+ free (temp_string);
> -+ }
> -+ }
> -+
> -+ /* According to the Single Unix Specification, v2, $OLDPWD is an
> -+ `environment variable' and therefore should be auto-exported.
> -+ Make a dummy invisible variable for OLDPWD, and mark it as exported. */
> -+ temp_var = bind_variable ("OLDPWD", (char *)NULL, 0);
> -+ VSETATTR (temp_var, (att_exported | att_invisible));
> -+}
> -+
> -+/* Make a variable $PPID, which holds the pid of the shell's parent. */
> -+void
> -+set_ppid ()
> -+{
> -+ char namebuf[INT_STRLEN_BOUND(pid_t) + 1], *name;
> -+ SHELL_VAR *temp_var;
> -+
> -+ name = inttostr (getppid (), namebuf, sizeof(namebuf));
> -+ temp_var = find_variable ("PPID");
> -+ if (temp_var)
> -+ VUNSETATTR (temp_var, (att_readonly | att_exported));
> -+ temp_var = bind_variable ("PPID", name, 0);
> -+ VSETATTR (temp_var, (att_readonly | att_integer));
> -+}
> -+
> -+static void
> -+uidset ()
> -+{
> -+ char buff[INT_STRLEN_BOUND(uid_t) + 1], *b;
> -+ register SHELL_VAR *v;
> -+
> -+ b = inttostr (current_user.uid, buff, sizeof (buff));
> -+ v = find_variable ("UID");
> -+ if (v == 0)
> -+ {
> -+ v = bind_variable ("UID", b, 0);
> -+ VSETATTR (v, (att_readonly | att_integer));
> -+ }
> -+
> -+ if (current_user.euid != current_user.uid)
> -+ b = inttostr (current_user.euid, buff, sizeof (buff));
> -+
> -+ v = find_variable ("EUID");
> -+ if (v == 0)
> -+ {
> -+ v = bind_variable ("EUID", b, 0);
> -+ VSETATTR (v, (att_readonly | att_integer));
> -+ }
> -+}
> -+
> -+#if defined (ARRAY_VARS)
> -+static void
> -+make_vers_array ()
> -+{
> -+ SHELL_VAR *vv;
> -+ ARRAY *av;
> -+ char *s, d[32], b[INT_STRLEN_BOUND(int) + 1];
> -+
> -+ unbind_variable ("BASH_VERSINFO");
> -+
> -+ vv = make_new_array_variable ("BASH_VERSINFO");
> -+ av = array_cell (vv);
> -+ strcpy (d, dist_version);
> -+ s = strchr (d, '.');
> -+ if (s)
> -+ *s++ = '\0';
> -+ array_insert (av, 0, d);
> -+ array_insert (av, 1, s);
> -+ s = inttostr (patch_level, b, sizeof (b));
> -+ array_insert (av, 2, s);
> -+ s = inttostr (build_version, b, sizeof (b));
> -+ array_insert (av, 3, s);
> -+ array_insert (av, 4, release_status);
> -+ array_insert (av, 5, MACHTYPE);
> -+
> -+ VSETATTR (vv, att_readonly);
> -+}
> -+#endif /* ARRAY_VARS */
> -+
> -+/* Set the environment variables $LINES and $COLUMNS in response to
> -+ a window size change. */
> -+void
> -+sh_set_lines_and_columns (lines, cols)
> -+ int lines, cols;
> -+{
> -+ char val[INT_STRLEN_BOUND(int) + 1], *v;
> -+
> -+#if defined (READLINE)
> -+ /* If we are currently assigning to LINES or COLUMNS, don't do anything. */
> -+ if (winsize_assignment)
> -+ return;
> -+#endif
> -+
> -+ v = inttostr (lines, val, sizeof (val));
> -+ bind_variable ("LINES", v, 0);
> -+
> -+ v = inttostr (cols, val, sizeof (val));
> -+ bind_variable ("COLUMNS", v, 0);
> -+}
> -+
> -+/* **************************************************************** */
> -+/* */
> -+/* Printing variables and values */
> -+/* */
> -+/* **************************************************************** */
> -+
> -+/* Print LIST (a list of shell variables) to stdout in such a way that
> -+ they can be read back in. */
> -+void
> -+print_var_list (list)
> -+ register SHELL_VAR **list;
> -+{
> -+ register int i;
> -+ register SHELL_VAR *var;
> -+
> -+ for (i = 0; list && (var = list[i]); i++)
> -+ if (invisible_p (var) == 0)
> -+ print_assignment (var);
> -+}
> -+
> -+/* Print LIST (a list of shell functions) to stdout in such a way that
> -+ they can be read back in. */
> -+void
> -+print_func_list (list)
> -+ register SHELL_VAR **list;
> -+{
> -+ register int i;
> -+ register SHELL_VAR *var;
> -+
> -+ for (i = 0; list && (var = list[i]); i++)
> -+ {
> -+ printf ("%s ", var->name);
> -+ print_var_function (var);
> -+ printf ("\n");
> -+ }
> -+}
> -+
> -+/* Print the value of a single SHELL_VAR. No newline is
> -+ output, but the variable is printed in such a way that
> -+ it can be read back in. */
> -+void
> -+print_assignment (var)
> -+ SHELL_VAR *var;
> -+{
> -+ if (var_isset (var) == 0)
> -+ return;
> -+
> -+ if (function_p (var))
> -+ {
> -+ printf ("%s", var->name);
> -+ print_var_function (var);
> -+ printf ("\n");
> -+ }
> -+#if defined (ARRAY_VARS)
> -+ else if (array_p (var))
> -+ print_array_assignment (var, 0);
> -+ else if (assoc_p (var))
> -+ print_assoc_assignment (var, 0);
> -+#endif /* ARRAY_VARS */
> -+ else
> -+ {
> -+ printf ("%s=", var->name);
> -+ print_var_value (var, 1);
> -+ printf ("\n");
> -+ }
> -+}
> -+
> -+/* Print the value cell of VAR, a shell variable. Do not print
> -+ the name, nor leading/trailing newline. If QUOTE is non-zero,
> -+ and the value contains shell metacharacters, quote the value
> -+ in such a way that it can be read back in. */
> -+void
> -+print_var_value (var, quote)
> -+ SHELL_VAR *var;
> -+ int quote;
> -+{
> -+ char *t;
> -+
> -+ if (var_isset (var) == 0)
> -+ return;
> -+
> -+ if (quote && posixly_correct == 0 && ansic_shouldquote (value_cell (var)))
> -+ {
> -+ t = ansic_quote (value_cell (var), 0, (int *)0);
> -+ printf ("%s", t);
> -+ free (t);
> -+ }
> -+ else if (quote && sh_contains_shell_metas (value_cell (var)))
> -+ {
> -+ t = sh_single_quote (value_cell (var));
> -+ printf ("%s", t);
> -+ free (t);
> -+ }
> -+ else
> -+ printf ("%s", value_cell (var));
> -+}
> -+
> -+/* Print the function cell of VAR, a shell variable. Do not
> -+ print the name, nor leading/trailing newline. */
> -+void
> -+print_var_function (var)
> -+ SHELL_VAR *var;
> -+{
> -+ char *x;
> -+
> -+ if (function_p (var) && var_isset (var))
> -+ {
> -+ x = named_function_string ((char *)NULL, function_cell(var), FUNC_MULTILINE|FUNC_EXTERNAL);
> -+ printf ("%s", x);
> -+ }
> -+}
> -+
> -+/* **************************************************************** */
> -+/* */
> -+/* Dynamic Variables */
> -+/* */
> -+/* **************************************************************** */
> -+
> -+/* DYNAMIC VARIABLES
> -+
> -+ These are variables whose values are generated anew each time they are
> -+ referenced. These are implemented using a pair of function pointers
> -+ in the struct variable: assign_func, which is called from bind_variable
> -+ and, if arrays are compiled into the shell, some of the functions in
> -+ arrayfunc.c, and dynamic_value, which is called from find_variable.
> -+
> -+ assign_func is called from bind_variable_internal, if
> -+ bind_variable_internal discovers that the variable being assigned to
> -+ has such a function. The function is called as
> -+ SHELL_VAR *temp = (*(entry->assign_func)) (entry, value, ind)
> -+ and the (SHELL_VAR *)temp is returned as the value of bind_variable. It
> -+ is usually ENTRY (self). IND is an index for an array variable, and
> -+ unused otherwise.
> -+
> -+ dynamic_value is called from find_variable_internal to return a `new'
> -+ value for the specified dynamic varible. If this function is NULL,
> -+ the variable is treated as a `normal' shell variable. If it is not,
> -+ however, then this function is called like this:
> -+ tempvar = (*(var->dynamic_value)) (var);
> -+
> -+ Sometimes `tempvar' will replace the value of `var'. Other times, the
> -+ shell will simply use the string value. Pretty object-oriented, huh?
> -+
> -+ Be warned, though: if you `unset' a special variable, it loses its
> -+ special meaning, even if you subsequently set it.
> -+
> -+ The special assignment code would probably have been better put in
> -+ subst.c: do_assignment_internal, in the same style as
> -+ stupidly_hack_special_variables, but I wanted the changes as
> -+ localized as possible. */
> -+
> -+#define INIT_DYNAMIC_VAR(var, val, gfunc, afunc) \
> -+ do \
> -+ { \
> -+ v = bind_variable (var, (val), 0); \
> -+ v->dynamic_value = gfunc; \
> -+ v->assign_func = afunc; \
> -+ } \
> -+ while (0)
> -+
> -+#define INIT_DYNAMIC_ARRAY_VAR(var, gfunc, afunc) \
> -+ do \
> -+ { \
> -+ v = make_new_array_variable (var); \
> -+ v->dynamic_value = gfunc; \
> -+ v->assign_func = afunc; \
> -+ } \
> -+ while (0)
> -+
> -+#define INIT_DYNAMIC_ASSOC_VAR(var, gfunc, afunc) \
> -+ do \
> -+ { \
> -+ v = make_new_assoc_variable (var); \
> -+ v->dynamic_value = gfunc; \
> -+ v->assign_func = afunc; \
> -+ } \
> -+ while (0)
> -+
> -+static SHELL_VAR *
> -+null_assign (self, value, unused, key)
> -+ SHELL_VAR *self;
> -+ char *value;
> -+ arrayind_t unused;
> -+ char *key;
> -+{
> -+ return (self);
> -+}
> -+
> -+#if defined (ARRAY_VARS)
> -+static SHELL_VAR *
> -+null_array_assign (self, value, ind, key)
> -+ SHELL_VAR *self;
> -+ char *value;
> -+ arrayind_t ind;
> -+ char *key;
> -+{
> -+ return (self);
> -+}
> -+#endif
> -+
> -+/* Degenerate `dynamic_value' function; just returns what's passed without
> -+ manipulation. */
> -+static SHELL_VAR *
> -+get_self (self)
> -+ SHELL_VAR *self;
> -+{
> -+ return (self);
> -+}
> -+
> -+#if defined (ARRAY_VARS)
> -+/* A generic dynamic array variable initializer. Initialize array variable
> -+ NAME with dynamic value function GETFUNC and assignment function SETFUNC. */
> -+static SHELL_VAR *
> -+init_dynamic_array_var (name, getfunc, setfunc, attrs)
> -+ char *name;
> -+ sh_var_value_func_t *getfunc;
> -+ sh_var_assign_func_t *setfunc;
> -+ int attrs;
> -+{
> -+ SHELL_VAR *v;
> -+
> -+ v = find_variable (name);
> -+ if (v)
> -+ return (v);
> -+ INIT_DYNAMIC_ARRAY_VAR (name, getfunc, setfunc);
> -+ if (attrs)
> -+ VSETATTR (v, attrs);
> -+ return v;
> -+}
> -+
> -+static SHELL_VAR *
> -+init_dynamic_assoc_var (name, getfunc, setfunc, attrs)
> -+ char *name;
> -+ sh_var_value_func_t *getfunc;
> -+ sh_var_assign_func_t *setfunc;
> -+ int attrs;
> -+{
> -+ SHELL_VAR *v;
> -+
> -+ v = find_variable (name);
> -+ if (v)
> -+ return (v);
> -+ INIT_DYNAMIC_ASSOC_VAR (name, getfunc, setfunc);
> -+ if (attrs)
> -+ VSETATTR (v, attrs);
> -+ return v;
> -+}
> -+#endif
> -+
> -+/* The value of $SECONDS. This is the number of seconds since shell
> -+ invocation, or, the number of seconds since the last assignment + the
> -+ value of the last assignment. */
> -+static intmax_t seconds_value_assigned;
> -+
> -+static SHELL_VAR *
> -+assign_seconds (self, value, unused, key)
> -+ SHELL_VAR *self;
> -+ char *value;
> -+ arrayind_t unused;
> -+ char *key;
> -+{
> -+ if (legal_number (value, &seconds_value_assigned) == 0)
> -+ seconds_value_assigned = 0;
> -+ shell_start_time = NOW;
> -+ return (self);
> -+}
> -+
> -+static SHELL_VAR *
> -+get_seconds (var)
> -+ SHELL_VAR *var;
> -+{
> -+ time_t time_since_start;
> -+ char *p;
> -+
> -+ time_since_start = NOW - shell_start_time;
> -+ p = itos(seconds_value_assigned + time_since_start);
> -+
> -+ FREE (value_cell (var));
> -+
> -+ VSETATTR (var, att_integer);
> -+ var_setvalue (var, p);
> -+ return (var);
> -+}
> -+
> -+static SHELL_VAR *
> -+init_seconds_var ()
> -+{
> -+ SHELL_VAR *v;
> -+
> -+ v = find_variable ("SECONDS");
> -+ if (v)
> -+ {
> -+ if (legal_number (value_cell(v), &seconds_value_assigned) == 0)
> -+ seconds_value_assigned = 0;
> -+ }
> -+ INIT_DYNAMIC_VAR ("SECONDS", (v ? value_cell (v) : (char *)NULL), get_seconds, assign_seconds);
> -+ return v;
> -+}
> -+
> -+/* The random number seed. You can change this by setting RANDOM. */
> -+static unsigned long rseed = 1;
> -+static int last_random_value;
> -+static int seeded_subshell = 0;
> -+
> -+/* A linear congruential random number generator based on the example
> -+ one in the ANSI C standard. This one isn't very good, but a more
> -+ complicated one is overkill. */
> -+
> -+/* Returns a pseudo-random number between 0 and 32767. */
> -+static int
> -+brand ()
> -+{
> -+ /* From "Random number generators: good ones are hard to find",
> -+ Park and Miller, Communications of the ACM, vol. 31, no. 10,
> -+ October 1988, p. 1195. filtered through FreeBSD */
> -+ long h, l;
> -+
> -+ /* Can't seed with 0. */
> -+ if (rseed == 0)
> -+ rseed = 123459876;
> -+ h = rseed / 127773;
> -+ l = rseed % 127773;
> -+ rseed = 16807 * l - 2836 * h;
> -+#if 0
> -+ if (rseed < 0)
> -+ rseed += 0x7fffffff;
> -+#endif
> -+ return ((unsigned int)(rseed & 32767)); /* was % 32768 */
> -+}
> -+
> -+/* Set the random number generator seed to SEED. */
> -+static void
> -+sbrand (seed)
> -+ unsigned long seed;
> -+{
> -+ rseed = seed;
> -+ last_random_value = 0;
> -+}
> -+
> -+static void
> -+seedrand ()
> -+{
> -+ struct timeval tv;
> -+
> -+ gettimeofday (&tv, NULL);
> -+ sbrand (tv.tv_sec ^ tv.tv_usec ^ getpid ());
> -+}
> -+
> -+static SHELL_VAR *
> -+assign_random (self, value, unused, key)
> -+ SHELL_VAR *self;
> -+ char *value;
> -+ arrayind_t unused;
> -+ char *key;
> -+{
> -+ sbrand (strtoul (value, (char **)NULL, 10));
> -+ if (subshell_environment)
> -+ seeded_subshell = getpid ();
> -+ return (self);
> -+}
> -+
> -+int
> -+get_random_number ()
> -+{
> -+ int rv, pid;
> -+
> -+ /* Reset for command and process substitution. */
> -+ pid = getpid ();
> -+ if (subshell_environment && seeded_subshell != pid)
> -+ {
> -+ seedrand ();
> -+ seeded_subshell = pid;
> -+ }
> -+
> -+ do
> -+ rv = brand ();
> -+ while (rv == last_random_value);
> -+ return rv;
> -+}
> -+
> -+static SHELL_VAR *
> -+get_random (var)
> -+ SHELL_VAR *var;
> -+{
> -+ int rv;
> -+ char *p;
> -+
> -+ rv = get_random_number ();
> -+ last_random_value = rv;
> -+ p = itos (rv);
> -+
> -+ FREE (value_cell (var));
> -+
> -+ VSETATTR (var, att_integer);
> -+ var_setvalue (var, p);
> -+ return (var);
> -+}
> -+
> -+static SHELL_VAR *
> -+assign_lineno (var, value, unused, key)
> -+ SHELL_VAR *var;
> -+ char *value;
> -+ arrayind_t unused;
> -+ char *key;
> -+{
> -+ intmax_t new_value;
> -+
> -+ if (value == 0 || *value == '\0' || legal_number (value, &new_value) == 0)
> -+ new_value = 0;
> -+ line_number = line_number_base = new_value;
> -+ return var;
> -+}
> -+
> -+/* Function which returns the current line number. */
> -+static SHELL_VAR *
> -+get_lineno (var)
> -+ SHELL_VAR *var;
> -+{
> -+ char *p;
> -+ int ln;
> -+
> -+ ln = executing_line_number ();
> -+ p = itos (ln);
> -+ FREE (value_cell (var));
> -+ var_setvalue (var, p);
> -+ return (var);
> -+}
> -+
> -+static SHELL_VAR *
> -+assign_subshell (var, value, unused, key)
> -+ SHELL_VAR *var;
> -+ char *value;
> -+ arrayind_t unused;
> -+ char *key;
> -+{
> -+ intmax_t new_value;
> -+
> -+ if (value == 0 || *value == '\0' || legal_number (value, &new_value) == 0)
> -+ new_value = 0;
> -+ subshell_level = new_value;
> -+ return var;
> -+}
> -+
> -+static SHELL_VAR *
> -+get_subshell (var)
> -+ SHELL_VAR *var;
> -+{
> -+ char *p;
> -+
> -+ p = itos (subshell_level);
> -+ FREE (value_cell (var));
> -+ var_setvalue (var, p);
> -+ return (var);
> -+}
> -+
> -+static SHELL_VAR *
> -+get_bashpid (var)
> -+ SHELL_VAR *var;
> -+{
> -+ int pid;
> -+ char *p;
> -+
> -+ pid = getpid ();
> -+ p = itos (pid);
> -+
> -+ FREE (value_cell (var));
> -+ VSETATTR (var, att_integer|att_readonly);
> -+ var_setvalue (var, p);
> -+ return (var);
> -+}
> -+
> -+static SHELL_VAR *
> -+get_bash_command (var)
> -+ SHELL_VAR *var;
> -+{
> -+ char *p;
> -+
> -+ if (the_printed_command_except_trap)
> -+ p = savestring (the_printed_command_except_trap);
> -+ else
> -+ {
> -+ p = (char *)xmalloc (1);
> -+ p[0] = '\0';
> -+ }
> -+ FREE (value_cell (var));
> -+ var_setvalue (var, p);
> -+ return (var);
> -+}
> -+
> -+#if defined (HISTORY)
> -+static SHELL_VAR *
> -+get_histcmd (var)
> -+ SHELL_VAR *var;
> -+{
> -+ char *p;
> -+
> -+ p = itos (history_number ());
> -+ FREE (value_cell (var));
> -+ var_setvalue (var, p);
> -+ return (var);
> -+}
> -+#endif
> -+
> -+#if defined (READLINE)
> -+/* When this function returns, VAR->value points to malloced memory. */
> -+static SHELL_VAR *
> -+get_comp_wordbreaks (var)
> -+ SHELL_VAR *var;
> -+{
> -+ /* If we don't have anything yet, assign a default value. */
> -+ if (rl_completer_word_break_characters == 0 && bash_readline_initialized == 0)
> -+ enable_hostname_completion (perform_hostname_completion);
> -+
> -+ FREE (value_cell (var));
> -+ var_setvalue (var, savestring (rl_completer_word_break_characters));
> -+
> -+ return (var);
> -+}
> -+
> -+/* When this function returns, rl_completer_word_break_characters points to
> -+ malloced memory. */
> -+static SHELL_VAR *
> -+assign_comp_wordbreaks (self, value, unused, key)
> -+ SHELL_VAR *self;
> -+ char *value;
> -+ arrayind_t unused;
> -+ char *key;
> -+{
> -+ if (rl_completer_word_break_characters &&
> -+ rl_completer_word_break_characters != rl_basic_word_break_characters)
> -+ free (rl_completer_word_break_characters);
> -+
> -+ rl_completer_word_break_characters = savestring (value);
> -+ return self;
> -+}
> -+#endif /* READLINE */
> -+
> -+#if defined (PUSHD_AND_POPD) && defined (ARRAY_VARS)
> -+static SHELL_VAR *
> -+assign_dirstack (self, value, ind, key)
> -+ SHELL_VAR *self;
> -+ char *value;
> -+ arrayind_t ind;
> -+ char *key;
> -+{
> -+ set_dirstack_element (ind, 1, value);
> -+ return self;
> -+}
> -+
> -+static SHELL_VAR *
> -+get_dirstack (self)
> -+ SHELL_VAR *self;
> -+{
> -+ ARRAY *a;
> -+ WORD_LIST *l;
> -+
> -+ l = get_directory_stack (0);
> -+ a = array_from_word_list (l);
> -+ array_dispose (array_cell (self));
> -+ dispose_words (l);
> -+ var_setarray (self, a);
> -+ return self;
> -+}
> -+#endif /* PUSHD AND POPD && ARRAY_VARS */
> -+
> -+#if defined (ARRAY_VARS)
> -+/* We don't want to initialize the group set with a call to getgroups()
> -+ unless we're asked to, but we only want to do it once. */
> -+static SHELL_VAR *
> -+get_groupset (self)
> -+ SHELL_VAR *self;
> -+{
> -+ register int i;
> -+ int ng;
> -+ ARRAY *a;
> -+ static char **group_set = (char **)NULL;
> -+
> -+ if (group_set == 0)
> -+ {
> -+ group_set = get_group_list (&ng);
> -+ a = array_cell (self);
> -+ for (i = 0; i < ng; i++)
> -+ array_insert (a, i, group_set[i]);
> -+ }
> -+ return (self);
> -+}
> -+
> -+static SHELL_VAR *
> -+build_hashcmd (self)
> -+ SHELL_VAR *self;
> -+{
> -+ HASH_TABLE *h;
> -+ int i;
> -+ char *k, *v;
> -+ BUCKET_CONTENTS *item;
> -+
> -+ h = assoc_cell (self);
> -+ if (h)
> -+ assoc_dispose (h);
> -+
> -+ if (hashed_filenames == 0 || HASH_ENTRIES (hashed_filenames) == 0)
> -+ {
> -+ var_setvalue (self, (char *)NULL);
> -+ return self;
> -+ }
> -+
> -+ h = assoc_create (hashed_filenames->nbuckets);
> -+ for (i = 0; i < hashed_filenames->nbuckets; i++)
> -+ {
> -+ for (item = hash_items (i, hashed_filenames); item; item = item->next)
> -+ {
> -+ k = savestring (item->key);
> -+ v = pathdata(item)->path;
> -+ assoc_insert (h, k, v);
> -+ }
> -+ }
> -+
> -+ var_setvalue (self, (char *)h);
> -+ return self;
> -+}
> -+
> -+static SHELL_VAR *
> -+get_hashcmd (self)
> -+ SHELL_VAR *self;
> -+{
> -+ build_hashcmd (self);
> -+ return (self);
> -+}
> -+
> -+static SHELL_VAR *
> -+assign_hashcmd (self, value, ind, key)
> -+ SHELL_VAR *self;
> -+ char *value;
> -+ arrayind_t ind;
> -+ char *key;
> -+{
> -+ phash_insert (key, value, 0, 0);
> -+ return (build_hashcmd (self));
> -+}
> -+
> -+#if defined (ALIAS)
> -+static SHELL_VAR *
> -+build_aliasvar (self)
> -+ SHELL_VAR *self;
> -+{
> -+ HASH_TABLE *h;
> -+ int i;
> -+ char *k, *v;
> -+ BUCKET_CONTENTS *item;
> -+
> -+ h = assoc_cell (self);
> -+ if (h)
> -+ assoc_dispose (h);
> -+
> -+ if (aliases == 0 || HASH_ENTRIES (aliases) == 0)
> -+ {
> -+ var_setvalue (self, (char *)NULL);
> -+ return self;
> -+ }
> -+
> -+ h = assoc_create (aliases->nbuckets);
> -+ for (i = 0; i < aliases->nbuckets; i++)
> -+ {
> -+ for (item = hash_items (i, aliases); item; item = item->next)
> -+ {
> -+ k = savestring (item->key);
> -+ v = ((alias_t *)(item->data))->value;
> -+ assoc_insert (h, k, v);
> -+ }
> -+ }
> -+
> -+ var_setvalue (self, (char *)h);
> -+ return self;
> -+}
> -+
> -+static SHELL_VAR *
> -+get_aliasvar (self)
> -+ SHELL_VAR *self;
> -+{
> -+ build_aliasvar (self);
> -+ return (self);
> -+}
> -+
> -+static SHELL_VAR *
> -+assign_aliasvar (self, value, ind, key)
> -+ SHELL_VAR *self;
> -+ char *value;
> -+ arrayind_t ind;
> -+ char *key;
> -+{
> -+ add_alias (key, value);
> -+ return (build_aliasvar (self));
> -+}
> -+#endif /* ALIAS */
> -+
> -+#endif /* ARRAY_VARS */
> -+
> -+/* If ARRAY_VARS is not defined, this just returns the name of any
> -+ currently-executing function. If we have arrays, it's a call stack. */
> -+static SHELL_VAR *
> -+get_funcname (self)
> -+ SHELL_VAR *self;
> -+{
> -+#if ! defined (ARRAY_VARS)
> -+ char *t;
> -+ if (variable_context && this_shell_function)
> -+ {
> -+ FREE (value_cell (self));
> -+ t = savestring (this_shell_function->name);
> -+ var_setvalue (self, t);
> -+ }
> -+#endif
> -+ return (self);
> -+}
> -+
> -+void
> -+make_funcname_visible (on_or_off)
> -+ int on_or_off;
> -+{
> -+ SHELL_VAR *v;
> -+
> -+ v = find_variable ("FUNCNAME");
> -+ if (v == 0 || v->dynamic_value == 0)
> -+ return;
> -+
> -+ if (on_or_off)
> -+ VUNSETATTR (v, att_invisible);
> -+ else
> -+ VSETATTR (v, att_invisible);
> -+}
> -+
> -+static SHELL_VAR *
> -+init_funcname_var ()
> -+{
> -+ SHELL_VAR *v;
> -+
> -+ v = find_variable ("FUNCNAME");
> -+ if (v)
> -+ return v;
> -+#if defined (ARRAY_VARS)
> -+ INIT_DYNAMIC_ARRAY_VAR ("FUNCNAME", get_funcname, null_array_assign);
> -+#else
> -+ INIT_DYNAMIC_VAR ("FUNCNAME", (char *)NULL, get_funcname, null_assign);
> -+#endif
> -+ VSETATTR (v, att_invisible|att_noassign);
> -+ return v;
> -+}
> -+
> -+static void
> -+initialize_dynamic_variables ()
> -+{
> -+ SHELL_VAR *v;
> -+
> -+ v = init_seconds_var ();
> -+
> -+ INIT_DYNAMIC_VAR ("BASH_COMMAND", (char *)NULL, get_bash_command, (sh_var_assign_func_t *)NULL);
> -+ INIT_DYNAMIC_VAR ("BASH_SUBSHELL", (char *)NULL, get_subshell, assign_subshell);
> -+
> -+ INIT_DYNAMIC_VAR ("RANDOM", (char *)NULL, get_random, assign_random);
> -+ VSETATTR (v, att_integer);
> -+ INIT_DYNAMIC_VAR ("LINENO", (char *)NULL, get_lineno, assign_lineno);
> -+ VSETATTR (v, att_integer);
> -+
> -+ INIT_DYNAMIC_VAR ("BASHPID", (char *)NULL, get_bashpid, null_assign);
> -+ VSETATTR (v, att_integer|att_readonly);
> -+
> -+#if defined (HISTORY)
> -+ INIT_DYNAMIC_VAR ("HISTCMD", (char *)NULL, get_histcmd, (sh_var_assign_func_t *)NULL);
> -+ VSETATTR (v, att_integer);
> -+#endif
> -+
> -+#if defined (READLINE)
> -+ INIT_DYNAMIC_VAR ("COMP_WORDBREAKS", (char *)NULL, get_comp_wordbreaks, assign_comp_wordbreaks);
> -+#endif
> -+
> -+#if defined (PUSHD_AND_POPD) && defined (ARRAY_VARS)
> -+ v = init_dynamic_array_var ("DIRSTACK", get_dirstack, assign_dirstack, 0);
> -+#endif /* PUSHD_AND_POPD && ARRAY_VARS */
> -+
> -+#if defined (ARRAY_VARS)
> -+ v = init_dynamic_array_var ("GROUPS", get_groupset, null_array_assign, att_noassign);
> -+
> -+# if defined (DEBUGGER)
> -+ v = init_dynamic_array_var ("BASH_ARGC", get_self, null_array_assign, att_noassign|att_nounset);
> -+ v = init_dynamic_array_var ("BASH_ARGV", get_self, null_array_assign, att_noassign|att_nounset);
> -+# endif /* DEBUGGER */
> -+ v = init_dynamic_array_var ("BASH_SOURCE", get_self, null_array_assign, att_noassign|att_nounset);
> -+ v = init_dynamic_array_var ("BASH_LINENO", get_self, null_array_assign, att_noassign|att_nounset);
> -+
> -+ v = init_dynamic_assoc_var ("BASH_CMDS", get_hashcmd, assign_hashcmd, att_nofree);
> -+# if defined (ALIAS)
> -+ v = init_dynamic_assoc_var ("BASH_ALIASES", get_aliasvar, assign_aliasvar, att_nofree);
> -+# endif
> -+#endif
> -+
> -+ v = init_funcname_var ();
> -+}
> -+
> -+/* **************************************************************** */
> -+/* */
> -+/* Retrieving variables and values */
> -+/* */
> -+/* **************************************************************** */
> -+
> -+/* How to get a pointer to the shell variable or function named NAME.
> -+ HASHED_VARS is a pointer to the hash table containing the list
> -+ of interest (either variables or functions). */
> -+
> -+static SHELL_VAR *
> -+hash_lookup (name, hashed_vars)
> -+ const char *name;
> -+ HASH_TABLE *hashed_vars;
> -+{
> -+ BUCKET_CONTENTS *bucket;
> -+
> -+ bucket = hash_search (name, hashed_vars, 0);
> -+ /* If we find the name in HASHED_VARS, set LAST_TABLE_SEARCHED to that
> -+ table. */
> -+ if (bucket)
> -+ last_table_searched = hashed_vars;
> -+ return (bucket ? (SHELL_VAR *)bucket->data : (SHELL_VAR *)NULL);
> -+}
> -+
> -+SHELL_VAR *
> -+var_lookup (name, vcontext)
> -+ const char *name;
> -+ VAR_CONTEXT *vcontext;
> -+{
> -+ VAR_CONTEXT *vc;
> -+ SHELL_VAR *v;
> -+
> -+ v = (SHELL_VAR *)NULL;
> -+ for (vc = vcontext; vc; vc = vc->down)
> -+ if (v = hash_lookup (name, vc->table))
> -+ break;
> -+
> -+ return v;
> -+}
> -+
> -+/* Look up the variable entry named NAME. If SEARCH_TEMPENV is non-zero,
> -+ then also search the temporarily built list of exported variables.
> -+ The lookup order is:
> -+ temporary_env
> -+ shell_variables list
> -+*/
> -+
> -+SHELL_VAR *
> -+find_variable_internal (name, force_tempenv)
> -+ const char *name;
> -+ int force_tempenv;
> -+{
> -+ SHELL_VAR *var;
> -+ int search_tempenv;
> -+ VAR_CONTEXT *vc;
> -+
> -+ var = (SHELL_VAR *)NULL;
> -+
> -+ /* If explicitly requested, first look in the temporary environment for
> -+ the variable. This allows constructs such as "foo=x eval 'echo $foo'"
> -+ to get the `exported' value of $foo. This happens if we are executing
> -+ a function or builtin, or if we are looking up a variable in a
> -+ "subshell environment". */
> -+ search_tempenv = force_tempenv || (expanding_redir == 0 && subshell_environment);
> -+
> -+ if (search_tempenv && temporary_env)
> -+ var = hash_lookup (name, temporary_env);
> -+
> -+ vc = shell_variables;
> -+#if 0
> -+if (search_tempenv == 0 && /* (subshell_environment & SUBSHELL_COMSUB) && */
> -+ expanding_redir &&
> -+ (this_shell_builtin == eval_builtin || this_shell_builtin == command_builtin))
> -+ {
> -+ itrace("find_variable_internal: search_tempenv == 0: skipping VC_BLTNENV");
> -+ while (vc && (vc->flags & VC_BLTNENV))
> -+ vc = vc->down;
> -+ if (vc == 0)
> -+ vc = shell_variables;
> -+ }
> -+#endif
> -+
> -+ if (var == 0)
> -+ var = var_lookup (name, vc);
> -+
> -+ if (var == 0)
> -+ return ((SHELL_VAR *)NULL);
> -+
> -+ return (var->dynamic_value ? (*(var->dynamic_value)) (var) : var);
> -+}
> -+
> -+/* Look up and resolve the chain of nameref variables starting at V all the
> -+ way to NULL or non-nameref. */
> -+SHELL_VAR *
> -+find_variable_nameref (v)
> -+ SHELL_VAR *v;
> -+{
> -+ int level;
> -+ char *newname;
> -+ SHELL_VAR *orig, *oldv;
> -+
> -+ level = 0;
> -+ orig = v;
> -+ while (v && nameref_p (v))
> -+ {
> -+ level++;
> -+ if (level > NAMEREF_MAX)
> -+ return ((SHELL_VAR *)0); /* error message here? */
> -+ newname = nameref_cell (v);
> -+ if (newname == 0 || *newname == '\0')
> -+ return ((SHELL_VAR *)0);
> -+ oldv = v;
> -+ v = find_variable_internal (newname, (expanding_redir == 0 && (assigning_in_environment || executing_builtin)));
> -+ if (v == orig || v == oldv)
> -+ {
> -+ internal_warning (_("%s: circular name reference"), orig->name);
> -+ return ((SHELL_VAR *)0);
> -+ }
> -+ }
> -+ return v;
> -+}
> -+
> -+/* Resolve the chain of nameref variables for NAME. XXX - could change later */
> -+SHELL_VAR *
> -+find_variable_last_nameref (name)
> -+ const char *name;
> -+{
> -+ SHELL_VAR *v, *nv;
> -+ char *newname;
> -+ int level;
> -+
> -+ nv = v = find_variable_noref (name);
> -+ level = 0;
> -+ while (v && nameref_p (v))
> -+ {
> -+ level++;
> -+ if (level > NAMEREF_MAX)
> -+ return ((SHELL_VAR *)0); /* error message here? */
> -+ newname = nameref_cell (v);
> -+ if (newname == 0 || *newname == '\0')
> -+ return ((SHELL_VAR *)0);
> -+ nv = v;
> -+ v = find_variable_internal (newname, (expanding_redir == 0 && (assigning_in_environment || executing_builtin)));
> -+ }
> -+ return nv;
> -+}
> -+
> -+/* Resolve the chain of nameref variables for NAME. XXX - could change later */
> -+SHELL_VAR *
> -+find_global_variable_last_nameref (name)
> -+ const char *name;
> -+{
> -+ SHELL_VAR *v, *nv;
> -+ char *newname;
> -+ int level;
> -+
> -+ nv = v = find_global_variable_noref (name);
> -+ level = 0;
> -+ while (v && nameref_p (v))
> -+ {
> -+ level++;
> -+ if (level > NAMEREF_MAX)
> -+ return ((SHELL_VAR *)0); /* error message here? */
> -+ newname = nameref_cell (v);
> -+ if (newname == 0 || *newname == '\0')
> -+ return ((SHELL_VAR *)0);
> -+ nv = v;
> -+ v = find_global_variable_noref (newname);
> -+ }
> -+ return nv;
> -+}
> -+
> -+static SHELL_VAR *
> -+find_nameref_at_context (v, vc)
> -+ SHELL_VAR *v;
> -+ VAR_CONTEXT *vc;
> -+{
> -+ SHELL_VAR *nv, *nv2;
> -+ VAR_CONTEXT *nvc;
> -+ char *newname;
> -+ int level;
> -+
> -+ nv = v;
> -+ level = 1;
> -+ while (nv && nameref_p (nv))
> -+ {
> -+ level++;
> -+ if (level > NAMEREF_MAX)
> -+ return ((SHELL_VAR *)NULL);
> -+ newname = nameref_cell (nv);
> -+ if (newname == 0 || *newname == '\0')
> -+ return ((SHELL_VAR *)NULL);
> -+ nv2 = hash_lookup (newname, vc->table);
> -+ if (nv2 == 0)
> -+ break;
> -+ nv = nv2;
> -+ }
> -+ return nv;
> -+}
> -+
> -+/* Do nameref resolution from the VC, which is the local context for some
> -+ function or builtin, `up' the chain to the global variables context. If
> -+ NVCP is not NULL, return the variable context where we finally ended the
> -+ nameref resolution (so the bind_variable_internal can use the correct
> -+ variable context and hash table). */
> -+static SHELL_VAR *
> -+find_variable_nameref_context (v, vc, nvcp)
> -+ SHELL_VAR *v;
> -+ VAR_CONTEXT *vc;
> -+ VAR_CONTEXT **nvcp;
> -+{
> -+ SHELL_VAR *nv, *nv2;
> -+ VAR_CONTEXT *nvc;
> -+
> -+ /* Look starting at the current context all the way `up' */
> -+ for (nv = v, nvc = vc; nvc; nvc = nvc->down)
> -+ {
> -+ nv2 = find_nameref_at_context (nv, nvc);
> -+ if (nv2 == 0)
> -+ continue;
> -+ nv = nv2;
> -+ if (*nvcp)
> -+ *nvcp = nvc;
> -+ if (nameref_p (nv) == 0)
> -+ break;
> -+ }
> -+ return (nameref_p (nv) ? (SHELL_VAR *)NULL : nv);
> -+}
> -+
> -+/* Do nameref resolution from the VC, which is the local context for some
> -+ function or builtin, `up' the chain to the global variables context. If
> -+ NVCP is not NULL, return the variable context where we finally ended the
> -+ nameref resolution (so the bind_variable_internal can use the correct
> -+ variable context and hash table). */
> -+static SHELL_VAR *
> -+find_variable_last_nameref_context (v, vc, nvcp)
> -+ SHELL_VAR *v;
> -+ VAR_CONTEXT *vc;
> -+ VAR_CONTEXT **nvcp;
> -+{
> -+ SHELL_VAR *nv, *nv2;
> -+ VAR_CONTEXT *nvc;
> -+
> -+ /* Look starting at the current context all the way `up' */
> -+ for (nv = v, nvc = vc; nvc; nvc = nvc->down)
> -+ {
> -+ nv2 = find_nameref_at_context (nv, nvc);
> -+ if (nv2 == 0)
> -+ continue;
> -+ nv = nv2;
> -+ if (*nvcp)
> -+ *nvcp = nvc;
> -+ }
> -+ return (nameref_p (nv) ? nv : (SHELL_VAR *)NULL);
> -+}
> -+
> -+/* Find a variable, forcing a search of the temporary environment first */
> -+SHELL_VAR *
> -+find_variable_tempenv (name)
> -+ const char *name;
> -+{
> -+ SHELL_VAR *var;
> -+
> -+ var = find_variable_internal (name, 1);
> -+ if (var && nameref_p (var))
> -+ var = find_variable_nameref (var);
> -+ return (var);
> -+}
> -+
> -+/* Find a variable, not forcing a search of the temporary environment first */
> -+SHELL_VAR *
> -+find_variable_notempenv (name)
> -+ const char *name;
> -+{
> -+ SHELL_VAR *var;
> -+
> -+ var = find_variable_internal (name, 0);
> -+ if (var && nameref_p (var))
> -+ var = find_variable_nameref (var);
> -+ return (var);
> -+}
> -+
> -+SHELL_VAR *
> -+find_global_variable (name)
> -+ const char *name;
> -+{
> -+ SHELL_VAR *var;
> -+
> -+ var = var_lookup (name, global_variables);
> -+ if (var && nameref_p (var))
> -+ var = find_variable_nameref (var);
> -+
> -+ if (var == 0)
> -+ return ((SHELL_VAR *)NULL);
> -+
> -+ return (var->dynamic_value ? (*(var->dynamic_value)) (var) : var);
> -+}
> -+
> -+SHELL_VAR *
> -+find_global_variable_noref (name)
> -+ const char *name;
> -+{
> -+ SHELL_VAR *var;
> -+
> -+ var = var_lookup (name, global_variables);
> -+
> -+ if (var == 0)
> -+ return ((SHELL_VAR *)NULL);
> -+
> -+ return (var->dynamic_value ? (*(var->dynamic_value)) (var) : var);
> -+}
> -+
> -+SHELL_VAR *
> -+find_shell_variable (name)
> -+ const char *name;
> -+{
> -+ SHELL_VAR *var;
> -+
> -+ var = var_lookup (name, shell_variables);
> -+ if (var && nameref_p (var))
> -+ var = find_variable_nameref (var);
> -+
> -+ if (var == 0)
> -+ return ((SHELL_VAR *)NULL);
> -+
> -+ return (var->dynamic_value ? (*(var->dynamic_value)) (var) : var);
> -+}
> -+
> -+/* Look up the variable entry named NAME. Returns the entry or NULL. */
> -+SHELL_VAR *
> -+find_variable (name)
> -+ const char *name;
> -+{
> -+ SHELL_VAR *v;
> -+
> -+ last_table_searched = 0;
> -+ v = find_variable_internal (name, (expanding_redir == 0 && (assigning_in_environment || executing_builtin)));
> -+ if (v && nameref_p (v))
> -+ v = find_variable_nameref (v);
> -+ return v;
> -+}
> -+
> -+SHELL_VAR *
> -+find_variable_noref (name)
> -+ const char *name;
> -+{
> -+ SHELL_VAR *v;
> -+
> -+ v = find_variable_internal (name, (expanding_redir == 0 && (assigning_in_environment || executing_builtin)));
> -+ return v;
> -+}
> -+
> -+/* Look up the function entry whose name matches STRING.
> -+ Returns the entry or NULL. */
> -+SHELL_VAR *
> -+find_function (name)
> -+ const char *name;
> -+{
> -+ return (hash_lookup (name, shell_functions));
> -+}
> -+
> -+/* Find the function definition for the shell function named NAME. Returns
> -+ the entry or NULL. */
> -+FUNCTION_DEF *
> -+find_function_def (name)
> -+ const char *name;
> -+{
> -+#if defined (DEBUGGER)
> -+ return ((FUNCTION_DEF *)hash_lookup (name, shell_function_defs));
> -+#else
> -+ return ((FUNCTION_DEF *)0);
> -+#endif
> -+}
> -+
> -+/* Return the value of VAR. VAR is assumed to have been the result of a
> -+ lookup without any subscript, if arrays are compiled into the shell. */
> -+char *
> -+get_variable_value (var)
> -+ SHELL_VAR *var;
> -+{
> -+ if (var == 0)
> -+ return ((char *)NULL);
> -+#if defined (ARRAY_VARS)
> -+ else if (array_p (var))
> -+ return (array_reference (array_cell (var), 0));
> -+ else if (assoc_p (var))
> -+ return (assoc_reference (assoc_cell (var), "0"));
> -+#endif
> -+ else
> -+ return (value_cell (var));
> -+}
> -+
> -+/* Return the string value of a variable. Return NULL if the variable
> -+ doesn't exist. Don't cons a new string. This is a potential memory
> -+ leak if the variable is found in the temporary environment. Since
> -+ functions and variables have separate name spaces, returns NULL if
> -+ var_name is a shell function only. */
> -+char *
> -+get_string_value (var_name)
> -+ const char *var_name;
> -+{
> -+ SHELL_VAR *var;
> -+
> -+ var = find_variable (var_name);
> -+ return ((var) ? get_variable_value (var) : (char *)NULL);
> -+}
> -+
> -+/* This is present for use by the tilde and readline libraries. */
> -+char *
> -+sh_get_env_value (v)
> -+ const char *v;
> -+{
> -+ return get_string_value (v);
> -+}
> -+
> -+/* **************************************************************** */
> -+/* */
> -+/* Creating and setting variables */
> -+/* */
> -+/* **************************************************************** */
> -+
> -+/* Set NAME to VALUE if NAME has no value. */
> -+SHELL_VAR *
> -+set_if_not (name, value)
> -+ char *name, *value;
> -+{
> -+ SHELL_VAR *v;
> -+
> -+ if (shell_variables == 0)
> -+ create_variable_tables ();
> -+
> -+ v = find_variable (name);
> -+ if (v == 0)
> -+ v = bind_variable_internal (name, value, global_variables->table, HASH_NOSRCH, 0);
> -+ return (v);
> -+}
> -+
> -+/* Create a local variable referenced by NAME. */
> -+SHELL_VAR *
> -+make_local_variable (name)
> -+ const char *name;
> -+{
> -+ SHELL_VAR *new_var, *old_var;
> -+ VAR_CONTEXT *vc;
> -+ int was_tmpvar;
> -+ char *tmp_value;
> -+
> -+ /* local foo; local foo; is a no-op. */
> -+ old_var = find_variable (name);
> -+ if (old_var && local_p (old_var) && old_var->context == variable_context)
> -+ return (old_var);
> -+
> -+ was_tmpvar = old_var && tempvar_p (old_var);
> -+ /* If we're making a local variable in a shell function, the temporary env
> -+ has already been merged into the function's variable context stack. We
> -+ can assume that a temporary var in the same context appears in the same
> -+ VAR_CONTEXT and can safely be returned without creating a new variable
> -+ (which results in duplicate names in the same VAR_CONTEXT->table */
> -+ /* We can't just test tmpvar_p because variables in the temporary env given
> -+ to a shell function appear in the function's local variable VAR_CONTEXT
> -+ but retain their tempvar attribute. We want temporary variables that are
> -+ found in temporary_env, hence the test for last_table_searched, which is
> -+ set in hash_lookup and only (so far) checked here. */
> -+ if (was_tmpvar && old_var->context == variable_context && last_table_searched != temporary_env)
> -+ {
> -+ VUNSETATTR (old_var, att_invisible);
> -+ return (old_var);
> -+ }
> -+ if (was_tmpvar)
> -+ tmp_value = value_cell (old_var);
> -+
> -+ for (vc = shell_variables; vc; vc = vc->down)
> -+ if (vc_isfuncenv (vc) && vc->scope == variable_context)
> -+ break;
> -+
> -+ if (vc == 0)
> -+ {
> -+ internal_error (_("make_local_variable: no function context at current scope"));
> -+ return ((SHELL_VAR *)NULL);
> -+ }
> -+ else if (vc->table == 0)
> -+ vc->table = hash_create (TEMPENV_HASH_BUCKETS);
> -+
> -+ /* Since this is called only from the local/declare/typeset code, we can
> -+ call builtin_error here without worry (of course, it will also work
> -+ for anything that sets this_command_name). Variables with the `noassign'
> -+ attribute may not be made local. The test against old_var's context
> -+ level is to disallow local copies of readonly global variables (since I
> -+ believe that this could be a security hole). Readonly copies of calling
> -+ function local variables are OK. */
> -+ if (old_var && (noassign_p (old_var) ||
> -+ (readonly_p (old_var) && old_var->context == 0)))
> -+ {
> -+ if (readonly_p (old_var))
> -+ sh_readonly (name);
> -+ else if (noassign_p (old_var))
> -+ builtin_error (_("%s: variable may not be assigned value"), name);
> -+#if 0
> -+ /* Let noassign variables through with a warning */
> -+ if (readonly_p (old_var))
> -+#endif
> -+ return ((SHELL_VAR *)NULL);
> -+ }
> -+
> -+ if (old_var == 0)
> -+ new_var = make_new_variable (name, vc->table);
> -+ else
> -+ {
> -+ new_var = make_new_variable (name, vc->table);
> -+
> -+ /* If we found this variable in one of the temporary environments,
> -+ inherit its value. Watch to see if this causes problems with
> -+ things like `x=4 local x'. XXX - see above for temporary env
> -+ variables with the same context level as variable_context */
> -+ /* XXX - we should only do this if the variable is not an array. */
> -+ if (was_tmpvar)
> -+ var_setvalue (new_var, savestring (tmp_value));
> -+
> -+ new_var->attributes = exported_p (old_var) ? att_exported : 0;
> -+ }
> -+
> -+ vc->flags |= VC_HASLOCAL;
> -+
> -+ new_var->context = variable_context;
> -+ VSETATTR (new_var, att_local);
> -+
> -+ if (ifsname (name))
> -+ setifs (new_var);
> -+
> -+ if (was_tmpvar == 0)
> -+ VSETATTR (new_var, att_invisible); /* XXX */
> -+ return (new_var);
> -+}
> -+
> -+/* Create a new shell variable with name NAME. */
> -+static SHELL_VAR *
> -+new_shell_variable (name)
> -+ const char *name;
> -+{
> -+ SHELL_VAR *entry;
> -+
> -+ entry = (SHELL_VAR *)xmalloc (sizeof (SHELL_VAR));
> -+
> -+ entry->name = savestring (name);
> -+ var_setvalue (entry, (char *)NULL);
> -+ CLEAR_EXPORTSTR (entry);
> -+
> -+ entry->dynamic_value = (sh_var_value_func_t *)NULL;
> -+ entry->assign_func = (sh_var_assign_func_t *)NULL;
> -+
> -+ entry->attributes = 0;
> -+
> -+ /* Always assume variables are to be made at toplevel!
> -+ make_local_variable has the responsibility of changing the
> -+ variable context. */
> -+ entry->context = 0;
> -+
> -+ return (entry);
> -+}
> -+
> -+/* Create a new shell variable with name NAME and add it to the hash table
> -+ TABLE. */
> -+static SHELL_VAR *
> -+make_new_variable (name, table)
> -+ const char *name;
> -+ HASH_TABLE *table;
> -+{
> -+ SHELL_VAR *entry;
> -+ BUCKET_CONTENTS *elt;
> -+
> -+ entry = new_shell_variable (name);
> -+
> -+ /* Make sure we have a shell_variables hash table to add to. */
> -+ if (shell_variables == 0)
> -+ create_variable_tables ();
> -+
> -+ elt = hash_insert (savestring (name), table, HASH_NOSRCH);
> -+ elt->data = (PTR_T)entry;
> -+
> -+ return entry;
> -+}
> -+
> -+#if defined (ARRAY_VARS)
> -+SHELL_VAR *
> -+make_new_array_variable (name)
> -+ char *name;
> -+{
> -+ SHELL_VAR *entry;
> -+ ARRAY *array;
> -+
> -+ entry = make_new_variable (name, global_variables->table);
> -+ array = array_create ();
> -+
> -+ var_setarray (entry, array);
> -+ VSETATTR (entry, att_array);
> -+ return entry;
> -+}
> -+
> -+SHELL_VAR *
> -+make_local_array_variable (name, assoc_ok)
> -+ char *name;
> -+ int assoc_ok;
> -+{
> -+ SHELL_VAR *var;
> -+ ARRAY *array;
> -+
> -+ var = make_local_variable (name);
> -+ if (var == 0 || array_p (var) || (assoc_ok && assoc_p (var)))
> -+ return var;
> -+
> -+ array = array_create ();
> -+
> -+ dispose_variable_value (var);
> -+ var_setarray (var, array);
> -+ VSETATTR (var, att_array);
> -+ return var;
> -+}
> -+
> -+SHELL_VAR *
> -+make_new_assoc_variable (name)
> -+ char *name;
> -+{
> -+ SHELL_VAR *entry;
> -+ HASH_TABLE *hash;
> -+
> -+ entry = make_new_variable (name, global_variables->table);
> -+ hash = assoc_create (0);
> -+
> -+ var_setassoc (entry, hash);
> -+ VSETATTR (entry, att_assoc);
> -+ return entry;
> -+}
> -+
> -+SHELL_VAR *
> -+make_local_assoc_variable (name)
> -+ char *name;
> -+{
> -+ SHELL_VAR *var;
> -+ HASH_TABLE *hash;
> -+
> -+ var = make_local_variable (name);
> -+ if (var == 0 || assoc_p (var))
> -+ return var;
> -+
> -+ dispose_variable_value (var);
> -+ hash = assoc_create (0);
> -+
> -+ var_setassoc (var, hash);
> -+ VSETATTR (var, att_assoc);
> -+ return var;
> -+}
> -+#endif
> -+
> -+char *
> -+make_variable_value (var, value, flags)
> -+ SHELL_VAR *var;
> -+ char *value;
> -+ int flags;
> -+{
> -+ char *retval, *oval;
> -+ intmax_t lval, rval;
> -+ int expok, olen, op;
> -+
> -+ /* If this variable has had its type set to integer (via `declare -i'),
> -+ then do expression evaluation on it and store the result. The
> -+ functions in expr.c (evalexp()) and bind_int_variable() are responsible
> -+ for turning off the integer flag if they don't want further
> -+ evaluation done. */
> -+ if (integer_p (var))
> -+ {
> -+ if (flags & ASS_APPEND)
> -+ {
> -+ oval = value_cell (var);
> -+ lval = evalexp (oval, &expok); /* ksh93 seems to do this */
> -+ if (expok == 0)
> -+ {
> -+ top_level_cleanup ();
> -+ jump_to_top_level (DISCARD);
> -+ }
> -+ }
> -+ rval = evalexp (value, &expok);
> -+ if (expok == 0)
> -+ {
> -+ top_level_cleanup ();
> -+ jump_to_top_level (DISCARD);
> -+ }
> -+ /* This can be fooled if the variable's value changes while evaluating
> -+ `rval'. We can change it if we move the evaluation of lval to here. */
> -+ if (flags & ASS_APPEND)
> -+ rval += lval;
> -+ retval = itos (rval);
> -+ }
> -+#if defined (CASEMOD_ATTRS)
> -+ else if (capcase_p (var) || uppercase_p (var) || lowercase_p (var))
> -+ {
> -+ if (flags & ASS_APPEND)
> -+ {
> -+ oval = get_variable_value (var);
> -+ if (oval == 0) /* paranoia */
> -+ oval = "";
> -+ olen = STRLEN (oval);
> -+ retval = (char *)xmalloc (olen + (value ? STRLEN (value) : 0) + 1);
> -+ strcpy (retval, oval);
> -+ if (value)
> -+ strcpy (retval+olen, value);
> -+ }
> -+ else if (*value)
> -+ retval = savestring (value);
> -+ else
> -+ {
> -+ retval = (char *)xmalloc (1);
> -+ retval[0] = '\0';
> -+ }
> -+ op = capcase_p (var) ? CASE_CAPITALIZE
> -+ : (uppercase_p (var) ? CASE_UPPER : CASE_LOWER);
> -+ oval = sh_modcase (retval, (char *)0, op);
> -+ free (retval);
> -+ retval = oval;
> -+ }
> -+#endif /* CASEMOD_ATTRS */
> -+ else if (value)
> -+ {
> -+ if (flags & ASS_APPEND)
> -+ {
> -+ oval = get_variable_value (var);
> -+ if (oval == 0) /* paranoia */
> -+ oval = "";
> -+ olen = STRLEN (oval);
> -+ retval = (char *)xmalloc (olen + (value ? STRLEN (value) : 0) + 1);
> -+ strcpy (retval, oval);
> -+ if (value)
> -+ strcpy (retval+olen, value);
> -+ }
> -+ else if (*value)
> -+ retval = savestring (value);
> -+ else
> -+ {
> -+ retval = (char *)xmalloc (1);
> -+ retval[0] = '\0';
> -+ }
> -+ }
> -+ else
> -+ retval = (char *)NULL;
> -+
> -+ return retval;
> -+}
> -+
> -+/* Bind a variable NAME to VALUE in the HASH_TABLE TABLE, which may be the
> -+ temporary environment (but usually is not). */
> -+static SHELL_VAR *
> -+bind_variable_internal (name, value, table, hflags, aflags)
> -+ const char *name;
> -+ char *value;
> -+ HASH_TABLE *table;
> -+ int hflags, aflags;
> -+{
> -+ char *newval;
> -+ SHELL_VAR *entry;
> -+
> -+ entry = (hflags & HASH_NOSRCH) ? (SHELL_VAR *)NULL : hash_lookup (name, table);
> -+ /* Follow the nameref chain here if this is the global variables table */
> -+ if (entry && nameref_p (entry) && (invisible_p (entry) == 0) && table == global_variables->table)
> -+ {
> -+ entry = find_global_variable (entry->name);
> -+ /* Let's see if we have a nameref referencing a variable that hasn't yet
> -+ been created. */
> -+ if (entry == 0)
> -+ entry = find_variable_last_nameref (name); /* XXX */
> -+ if (entry == 0) /* just in case */
> -+ return (entry);
> -+ }
> -+
> -+ /* The first clause handles `declare -n ref; ref=x;' */
> -+ if (entry && invisible_p (entry) && nameref_p (entry))
> -+ goto assign_value;
> -+ else if (entry && nameref_p (entry))
> -+ {
> -+ newval = nameref_cell (entry);
> -+#if defined (ARRAY_VARS)
> -+ /* declare -n foo=x[2] */
> -+ if (valid_array_reference (newval))
> -+ /* XXX - should it be aflags? */
> -+ entry = assign_array_element (newval, make_variable_value (entry, value, 0), aflags);
> -+ else
> -+#endif
> -+ {
> -+ entry = make_new_variable (newval, table);
> -+ var_setvalue (entry, make_variable_value (entry, value, 0));
> -+ }
> -+ }
> -+ else if (entry == 0)
> -+ {
> -+ entry = make_new_variable (name, table);
> -+ var_setvalue (entry, make_variable_value (entry, value, 0)); /* XXX */
> -+ }
> -+ else if (entry->assign_func) /* array vars have assign functions now */
> -+ {
> -+ INVALIDATE_EXPORTSTR (entry);
> -+ newval = (aflags & ASS_APPEND) ? make_variable_value (entry, value, aflags) : value;
> -+ if (assoc_p (entry))
> -+ entry = (*(entry->assign_func)) (entry, newval, -1, savestring ("0"));
> -+ else if (array_p (entry))
> -+ entry = (*(entry->assign_func)) (entry, newval, 0, 0);
> -+ else
> -+ entry = (*(entry->assign_func)) (entry, newval, -1, 0);
> -+ if (newval != value)
> -+ free (newval);
> -+ return (entry);
> -+ }
> -+ else
> -+ {
> -+assign_value:
> -+ if (readonly_p (entry) || noassign_p (entry))
> -+ {
> -+ if (readonly_p (entry))
> -+ err_readonly (name);
> -+ return (entry);
> -+ }
> -+
> -+ /* Variables which are bound are visible. */
> -+ VUNSETATTR (entry, att_invisible);
> -+
> -+#if defined (ARRAY_VARS)
> -+ if (assoc_p (entry) || array_p (entry))
> -+ newval = make_array_variable_value (entry, 0, "0", value, aflags);
> -+ else
> -+#endif
> -+
> -+ newval = make_variable_value (entry, value, aflags); /* XXX */
> -+
> -+ /* Invalidate any cached export string */
> -+ INVALIDATE_EXPORTSTR (entry);
> -+
> -+#if defined (ARRAY_VARS)
> -+ /* XXX -- this bears looking at again -- XXX */
> -+ /* If an existing array variable x is being assigned to with x=b or
> -+ `read x' or something of that nature, silently convert it to
> -+ x[0]=b or `read x[0]'. */
> -+ if (assoc_p (entry))
> -+ {
> -+ assoc_insert (assoc_cell (entry), savestring ("0"), newval);
> -+ free (newval);
> -+ }
> -+ else if (array_p (entry))
> -+ {
> -+ array_insert (array_cell (entry), 0, newval);
> -+ free (newval);
> -+ }
> -+ else
> -+#endif
> -+ {
> -+ FREE (value_cell (entry));
> -+ var_setvalue (entry, newval);
> -+ }
> -+ }
> -+
> -+ if (mark_modified_vars)
> -+ VSETATTR (entry, att_exported);
> -+
> -+ if (exported_p (entry))
> -+ array_needs_making = 1;
> -+
> -+ return (entry);
> -+}
> -+
> -+/* Bind a variable NAME to VALUE. This conses up the name
> -+ and value strings. If we have a temporary environment, we bind there
> -+ first, then we bind into shell_variables. */
> -+
> -+SHELL_VAR *
> -+bind_variable (name, value, flags)
> -+ const char *name;
> -+ char *value;
> -+ int flags;
> -+{
> -+ SHELL_VAR *v, *nv;
> -+ VAR_CONTEXT *vc, *nvc;
> -+ int level;
> -+
> -+ if (shell_variables == 0)
> -+ create_variable_tables ();
> -+
> -+ /* If we have a temporary environment, look there first for the variable,
> -+ and, if found, modify the value there before modifying it in the
> -+ shell_variables table. This allows sourced scripts to modify values
> -+ given to them in a temporary environment while modifying the variable
> -+ value that the caller sees. */
> -+ if (temporary_env)
> -+ bind_tempenv_variable (name, value);
> -+
> -+ /* XXX -- handle local variables here. */
> -+ for (vc = shell_variables; vc; vc = vc->down)
> -+ {
> -+ if (vc_isfuncenv (vc) || vc_isbltnenv (vc))
> -+ {
> -+ v = hash_lookup (name, vc->table);
> -+ nvc = vc;
> -+ if (v && nameref_p (v))
> -+ {
> -+ nv = find_variable_nameref_context (v, vc, &nvc);
> -+ if (nv == 0)
> -+ {
> -+ nv = find_variable_last_nameref_context (v, vc, &nvc);
> -+ if (nv && nameref_p (nv))
> -+ {
> -+ /* If this nameref variable doesn't have a value yet,
> -+ set the value. Otherwise, assign using the value as
> -+ normal. */
> -+ if (nameref_cell (nv) == 0)
> -+ return (bind_variable_internal (nv->name, value, nvc->table, 0, flags));
> -+ return (bind_variable_internal (nameref_cell (nv), value, nvc->table, 0, flags));
> -+ }
> -+ else
> -+ v = nv;
> -+ }
> -+ else
> -+ v = nv;
> -+ }
> -+ if (v)
> -+ return (bind_variable_internal (v->name, value, nvc->table, 0, flags));
> -+ }
> -+ }
> -+ /* bind_variable_internal will handle nameref resolution in this case */
> -+ return (bind_variable_internal (name, value, global_variables->table, 0, flags));
> -+}
> -+
> -+SHELL_VAR *
> -+bind_global_variable (name, value, flags)
> -+ const char *name;
> -+ char *value;
> -+ int flags;
> -+{
> -+ SHELL_VAR *v, *nv;
> -+ VAR_CONTEXT *vc, *nvc;
> -+ int level;
> -+
> -+ if (shell_variables == 0)
> -+ create_variable_tables ();
> -+
> -+ /* bind_variable_internal will handle nameref resolution in this case */
> -+ return (bind_variable_internal (name, value, global_variables->table, 0, flags));
> -+}
> -+
> -+/* Make VAR, a simple shell variable, have value VALUE. Once assigned a
> -+ value, variables are no longer invisible. This is a duplicate of part
> -+ of the internals of bind_variable. If the variable is exported, or
> -+ all modified variables should be exported, mark the variable for export
> -+ and note that the export environment needs to be recreated. */
> -+SHELL_VAR *
> -+bind_variable_value (var, value, aflags)
> -+ SHELL_VAR *var;
> -+ char *value;
> -+ int aflags;
> -+{
> -+ char *t;
> -+ int invis;
> -+
> -+ invis = invisible_p (var);
> -+ VUNSETATTR (var, att_invisible);
> -+
> -+ if (var->assign_func)
> -+ {
> -+ /* If we're appending, we need the old value, so use
> -+ make_variable_value */
> -+ t = (aflags & ASS_APPEND) ? make_variable_value (var, value, aflags) : value;
> -+ (*(var->assign_func)) (var, t, -1, 0);
> -+ if (t != value && t)
> -+ free (t);
> -+ }
> -+ else
> -+ {
> -+ t = make_variable_value (var, value, aflags);
> -+#if defined (ARRAY_VARS)
> -+ if ((aflags & ASS_NAMEREF) && (t == 0 || *t == 0 || (legal_identifier (t) == 0 && valid_array_reference (t) == 0)))
> -+#else
> -+ if ((aflags & ASS_NAMEREF) && (t == 0 || *t == 0 || legal_identifier (t) == 0))
> -+#endif
> -+ {
> -+ free (t);
> -+ if (invis)
> -+ VSETATTR (var, att_invisible); /* XXX */
> -+ return ((SHELL_VAR *)NULL);
> -+ }
> -+ FREE (value_cell (var));
> -+ var_setvalue (var, t);
> -+ }
> -+
> -+ INVALIDATE_EXPORTSTR (var);
> -+
> -+ if (mark_modified_vars)
> -+ VSETATTR (var, att_exported);
> -+
> -+ if (exported_p (var))
> -+ array_needs_making = 1;
> -+
> -+ return (var);
> -+}
> -+
> -+/* Bind/create a shell variable with the name LHS to the RHS.
> -+ This creates or modifies a variable such that it is an integer.
> -+
> -+ This used to be in expr.c, but it is here so that all of the
> -+ variable binding stuff is localized. Since we don't want any
> -+ recursive evaluation from bind_variable() (possible without this code,
> -+ since bind_variable() calls the evaluator for variables with the integer
> -+ attribute set), we temporarily turn off the integer attribute for each
> -+ variable we set here, then turn it back on after binding as necessary. */
> -+
> -+SHELL_VAR *
> -+bind_int_variable (lhs, rhs)
> -+ char *lhs, *rhs;
> -+{
> -+ register SHELL_VAR *v;
> -+ int isint, isarr, implicitarray;
> -+
> -+ isint = isarr = implicitarray = 0;
> -+#if defined (ARRAY_VARS)
> -+ if (valid_array_reference (lhs))
> -+ {
> -+ isarr = 1;
> -+ v = array_variable_part (lhs, (char **)0, (int *)0);
> -+ }
> -+ else
> -+#endif
> -+ v = find_variable (lhs);
> -+
> -+ if (v)
> -+ {
> -+ isint = integer_p (v);
> -+ VUNSETATTR (v, att_integer);
> -+#if defined (ARRAY_VARS)
> -+ if (array_p (v) && isarr == 0)
> -+ implicitarray = 1;
> -+#endif
> -+ }
> -+
> -+#if defined (ARRAY_VARS)
> -+ if (isarr)
> -+ v = assign_array_element (lhs, rhs, 0);
> -+ else if (implicitarray)
> -+ v = bind_array_variable (lhs, 0, rhs, 0);
> -+ else
> -+#endif
> -+ v = bind_variable (lhs, rhs, 0);
> -+
> -+ if (v && isint)
> -+ VSETATTR (v, att_integer);
> -+
> -+ VUNSETATTR (v, att_invisible);
> -+
> -+ return (v);
> -+}
> -+
> -+SHELL_VAR *
> -+bind_var_to_int (var, val)
> -+ char *var;
> -+ intmax_t val;
> -+{
> -+ char ibuf[INT_STRLEN_BOUND (intmax_t) + 1], *p;
> -+
> -+ p = fmtulong (val, 10, ibuf, sizeof (ibuf), 0);
> -+ return (bind_int_variable (var, p));
> -+}
> -+
> -+/* Do a function binding to a variable. You pass the name and
> -+ the command to bind to. This conses the name and command. */
> -+SHELL_VAR *
> -+bind_function (name, value)
> -+ const char *name;
> -+ COMMAND *value;
> -+{
> -+ SHELL_VAR *entry;
> -+
> -+ entry = find_function (name);
> -+ if (entry == 0)
> -+ {
> -+ BUCKET_CONTENTS *elt;
> -+
> -+ elt = hash_insert (savestring (name), shell_functions, HASH_NOSRCH);
> -+ entry = new_shell_variable (name);
> -+ elt->data = (PTR_T)entry;
> -+ }
> -+ else
> -+ INVALIDATE_EXPORTSTR (entry);
> -+
> -+ if (var_isset (entry))
> -+ dispose_command (function_cell (entry));
> -+
> -+ if (value)
> -+ var_setfunc (entry, copy_command (value));
> -+ else
> -+ var_setfunc (entry, 0);
> -+
> -+ VSETATTR (entry, att_function);
> -+
> -+ if (mark_modified_vars)
> -+ VSETATTR (entry, att_exported);
> -+
> -+ VUNSETATTR (entry, att_invisible); /* Just to be sure */
> -+
> -+ if (exported_p (entry))
> -+ array_needs_making = 1;
> -+
> -+#if defined (PROGRAMMABLE_COMPLETION)
> -+ set_itemlist_dirty (&it_functions);
> -+#endif
> -+
> -+ return (entry);
> -+}
> -+
> -+#if defined (DEBUGGER)
> -+/* Bind a function definition, which includes source file and line number
> -+ information in addition to the command, into the FUNCTION_DEF hash table.*/
> -+void
> -+bind_function_def (name, value)
> -+ const char *name;
> -+ FUNCTION_DEF *value;
> -+{
> -+ FUNCTION_DEF *entry;
> -+ BUCKET_CONTENTS *elt;
> -+ COMMAND *cmd;
> -+
> -+ entry = find_function_def (name);
> -+ if (entry)
> -+ {
> -+ dispose_function_def_contents (entry);
> -+ entry = copy_function_def_contents (value, entry);
> -+ }
> -+ else
> -+ {
> -+ cmd = value->command;
> -+ value->command = 0;
> -+ entry = copy_function_def (value);
> -+ value->command = cmd;
> -+
> -+ elt = hash_insert (savestring (name), shell_function_defs, HASH_NOSRCH);
> -+ elt->data = (PTR_T *)entry;
> -+ }
> -+}
> -+#endif /* DEBUGGER */
> -+
> -+/* Add STRING, which is of the form foo=bar, to the temporary environment
> -+ HASH_TABLE (temporary_env). The functions in execute_cmd.c are
> -+ responsible for moving the main temporary env to one of the other
> -+ temporary environments. The expansion code in subst.c calls this. */
> -+int
> -+assign_in_env (word, flags)
> -+ WORD_DESC *word;
> -+ int flags;
> -+{
> -+ int offset, aflags;
> -+ char *name, *temp, *value;
> -+ SHELL_VAR *var;
> -+ const char *string;
> -+
> -+ string = word->word;
> -+
> -+ aflags = 0;
> -+ offset = assignment (string, 0);
> -+ name = savestring (string);
> -+ value = (char *)NULL;
> -+
> -+ if (name[offset] == '=')
> -+ {
> -+ name[offset] = 0;
> -+
> -+ /* don't ignore the `+' when assigning temporary environment */
> -+ if (name[offset - 1] == '+')
> -+ {
> -+ name[offset - 1] = '\0';
> -+ aflags |= ASS_APPEND;
> -+ }
> -+
> -+ var = find_variable (name);
> -+ if (var && (readonly_p (var) || noassign_p (var)))
> -+ {
> -+ if (readonly_p (var))
> -+ err_readonly (name);
> -+ free (name);
> -+ return (0);
> -+ }
> -+
> -+ temp = name + offset + 1;
> -+ value = expand_assignment_string_to_string (temp, 0);
> -+
> -+ if (var && (aflags & ASS_APPEND))
> -+ {
> -+ temp = make_variable_value (var, value, aflags);
> -+ FREE (value);
> -+ value = temp;
> -+ }
> -+ }
> -+
> -+ if (temporary_env == 0)
> -+ temporary_env = hash_create (TEMPENV_HASH_BUCKETS);
> -+
> -+ var = hash_lookup (name, temporary_env);
> -+ if (var == 0)
> -+ var = make_new_variable (name, temporary_env);
> -+ else
> -+ FREE (value_cell (var));
> -+
> -+ if (value == 0)
> -+ {
> -+ value = (char *)xmalloc (1); /* like do_assignment_internal */
> -+ value[0] = '\0';
> -+ }
> -+
> -+ var_setvalue (var, value);
> -+ var->attributes |= (att_exported|att_tempvar);
> -+ var->context = variable_context; /* XXX */
> -+
> -+ INVALIDATE_EXPORTSTR (var);
> -+ var->exportstr = mk_env_string (name, value, 0);
> -+
> -+ array_needs_making = 1;
> -+
> -+ if (flags)
> -+ stupidly_hack_special_variables (name);
> -+
> -+ if (echo_command_at_execute)
> -+ /* The Korn shell prints the `+ ' in front of assignment statements,
> -+ so we do too. */
> -+ xtrace_print_assignment (name, value, 0, 1);
> -+
> -+ free (name);
> -+ return 1;
> -+}
> -+
> -+/* **************************************************************** */
> -+/* */
> -+/* Copying variables */
> -+/* */
> -+/* **************************************************************** */
> -+
> -+#ifdef INCLUDE_UNUSED
> -+/* Copy VAR to a new data structure and return that structure. */
> -+SHELL_VAR *
> -+copy_variable (var)
> -+ SHELL_VAR *var;
> -+{
> -+ SHELL_VAR *copy = (SHELL_VAR *)NULL;
> -+
> -+ if (var)
> -+ {
> -+ copy = (SHELL_VAR *)xmalloc (sizeof (SHELL_VAR));
> -+
> -+ copy->attributes = var->attributes;
> -+ copy->name = savestring (var->name);
> -+
> -+ if (function_p (var))
> -+ var_setfunc (copy, copy_command (function_cell (var)));
> -+#if defined (ARRAY_VARS)
> -+ else if (array_p (var))
> -+ var_setarray (copy, array_copy (array_cell (var)));
> -+ else if (assoc_p (var))
> -+ var_setassoc (copy, assoc_copy (assoc_cell (var)));
> -+#endif
> -+ else if (nameref_cell (var)) /* XXX - nameref */
> -+ var_setref (copy, savestring (nameref_cell (var)));
> -+ else if (value_cell (var)) /* XXX - nameref */
> -+ var_setvalue (copy, savestring (value_cell (var)));
> -+ else
> -+ var_setvalue (copy, (char *)NULL);
> -+
> -+ copy->dynamic_value = var->dynamic_value;
> -+ copy->assign_func = var->assign_func;
> -+
> -+ copy->exportstr = COPY_EXPORTSTR (var);
> -+
> -+ copy->context = var->context;
> -+ }
> -+ return (copy);
> -+}
> -+#endif
> -+
> -+/* **************************************************************** */
> -+/* */
> -+/* Deleting and unsetting variables */
> -+/* */
> -+/* **************************************************************** */
> -+
> -+/* Dispose of the information attached to VAR. */
> -+static void
> -+dispose_variable_value (var)
> -+ SHELL_VAR *var;
> -+{
> -+ if (function_p (var))
> -+ dispose_command (function_cell (var));
> -+#if defined (ARRAY_VARS)
> -+ else if (array_p (var))
> -+ array_dispose (array_cell (var));
> -+ else if (assoc_p (var))
> -+ assoc_dispose (assoc_cell (var));
> -+#endif
> -+ else if (nameref_p (var))
> -+ FREE (nameref_cell (var));
> -+ else
> -+ FREE (value_cell (var));
> -+}
> -+
> -+void
> -+dispose_variable (var)
> -+ SHELL_VAR *var;
> -+{
> -+ if (var == 0)
> -+ return;
> -+
> -+ if (nofree_p (var) == 0)
> -+ dispose_variable_value (var);
> -+
> -+ FREE_EXPORTSTR (var);
> -+
> -+ free (var->name);
> -+
> -+ if (exported_p (var))
> -+ array_needs_making = 1;
> -+
> -+ free (var);
> -+}
> -+
> -+/* Unset the shell variable referenced by NAME. Unsetting a nameref variable
> -+ unsets the variable it resolves to but leaves the nameref alone. */
> -+int
> -+unbind_variable (name)
> -+ const char *name;
> -+{
> -+ SHELL_VAR *v, *nv;
> -+ int r;
> -+
> -+ v = var_lookup (name, shell_variables);
> -+ nv = (v && nameref_p (v)) ? find_variable_nameref (v) : (SHELL_VAR *)NULL;
> -+
> -+ r = nv ? makunbound (nv->name, shell_variables) : makunbound (name, shell_variables);
> -+ return r;
> -+}
> -+
> -+/* Unbind NAME, where NAME is assumed to be a nameref variable */
> -+int
> -+unbind_nameref (name)
> -+ const char *name;
> -+{
> -+ SHELL_VAR *v;
> -+
> -+ v = var_lookup (name, shell_variables);
> -+ if (v && nameref_p (v))
> -+ return makunbound (name, shell_variables);
> -+ return 0;
> -+}
> -+
> -+/* Unset the shell function named NAME. */
> -+int
> -+unbind_func (name)
> -+ const char *name;
> -+{
> -+ BUCKET_CONTENTS *elt;
> -+ SHELL_VAR *func;
> -+
> -+ elt = hash_remove (name, shell_functions, 0);
> -+
> -+ if (elt == 0)
> -+ return -1;
> -+
> -+#if defined (PROGRAMMABLE_COMPLETION)
> -+ set_itemlist_dirty (&it_functions);
> -+#endif
> -+
> -+ func = (SHELL_VAR *)elt->data;
> -+ if (func)
> -+ {
> -+ if (exported_p (func))
> -+ array_needs_making++;
> -+ dispose_variable (func);
> -+ }
> -+
> -+ free (elt->key);
> -+ free (elt);
> -+
> -+ return 0;
> -+}
> -+
> -+#if defined (DEBUGGER)
> -+int
> -+unbind_function_def (name)
> -+ const char *name;
> -+{
> -+ BUCKET_CONTENTS *elt;
> -+ FUNCTION_DEF *funcdef;
> -+
> -+ elt = hash_remove (name, shell_function_defs, 0);
> -+
> -+ if (elt == 0)
> -+ return -1;
> -+
> -+ funcdef = (FUNCTION_DEF *)elt->data;
> -+ if (funcdef)
> -+ dispose_function_def (funcdef);
> -+
> -+ free (elt->key);
> -+ free (elt);
> -+
> -+ return 0;
> -+}
> -+#endif /* DEBUGGER */
> -+
> -+int
> -+delete_var (name, vc)
> -+ const char *name;
> -+ VAR_CONTEXT *vc;
> -+{
> -+ BUCKET_CONTENTS *elt;
> -+ SHELL_VAR *old_var;
> -+ VAR_CONTEXT *v;
> -+
> -+ for (elt = (BUCKET_CONTENTS *)NULL, v = vc; v; v = v->down)
> -+ if (elt = hash_remove (name, v->table, 0))
> -+ break;
> -+
> -+ if (elt == 0)
> -+ return (-1);
> -+
> -+ old_var = (SHELL_VAR *)elt->data;
> -+ free (elt->key);
> -+ free (elt);
> -+
> -+ dispose_variable (old_var);
> -+ return (0);
> -+}
> -+
> -+/* Make the variable associated with NAME go away. HASH_LIST is the
> -+ hash table from which this variable should be deleted (either
> -+ shell_variables or shell_functions).
> -+ Returns non-zero if the variable couldn't be found. */
> -+int
> -+makunbound (name, vc)
> -+ const char *name;
> -+ VAR_CONTEXT *vc;
> -+{
> -+ BUCKET_CONTENTS *elt, *new_elt;
> -+ SHELL_VAR *old_var;
> -+ VAR_CONTEXT *v;
> -+ char *t;
> -+
> -+ for (elt = (BUCKET_CONTENTS *)NULL, v = vc; v; v = v->down)
> -+ if (elt = hash_remove (name, v->table, 0))
> -+ break;
> -+
> -+ if (elt == 0)
> -+ return (-1);
> -+
> -+ old_var = (SHELL_VAR *)elt->data;
> -+
> -+ if (old_var && exported_p (old_var))
> -+ array_needs_making++;
> -+
> -+ /* If we're unsetting a local variable and we're still executing inside
> -+ the function, just mark the variable as invisible. The function
> -+ eventually called by pop_var_context() will clean it up later. This
> -+ must be done so that if the variable is subsequently assigned a new
> -+ value inside the function, the `local' attribute is still present.
> -+ We also need to add it back into the correct hash table. */
> -+ if (old_var && local_p (old_var) && variable_context == old_var->context)
> -+ {
> -+ if (nofree_p (old_var))
> -+ var_setvalue (old_var, (char *)NULL);
> -+#if defined (ARRAY_VARS)
> -+ else if (array_p (old_var))
> -+ array_dispose (array_cell (old_var));
> -+ else if (assoc_p (old_var))
> -+ assoc_dispose (assoc_cell (old_var));
> -+#endif
> -+ else if (nameref_p (old_var))
> -+ FREE (nameref_cell (old_var));
> -+ else
> -+ FREE (value_cell (old_var));
> -+ /* Reset the attributes. Preserve the export attribute if the variable
> -+ came from a temporary environment. Make sure it stays local, and
> -+ make it invisible. */
> -+ old_var->attributes = (exported_p (old_var) && tempvar_p (old_var)) ? att_exported : 0;
> -+ VSETATTR (old_var, att_local);
> -+ VSETATTR (old_var, att_invisible);
> -+ var_setvalue (old_var, (char *)NULL);
> -+ INVALIDATE_EXPORTSTR (old_var);
> -+
> -+ new_elt = hash_insert (savestring (old_var->name), v->table, 0);
> -+ new_elt->data = (PTR_T)old_var;
> -+ stupidly_hack_special_variables (old_var->name);
> -+
> -+ free (elt->key);
> -+ free (elt);
> -+ return (0);
> -+ }
> -+
> -+ /* Have to save a copy of name here, because it might refer to
> -+ old_var->name. If so, stupidly_hack_special_variables will
> -+ reference freed memory. */
> -+ t = savestring (name);
> -+
> -+ free (elt->key);
> -+ free (elt);
> -+
> -+ dispose_variable (old_var);
> -+ stupidly_hack_special_variables (t);
> -+ free (t);
> -+
> -+ return (0);
> -+}
> -+
> -+/* Get rid of all of the variables in the current context. */
> -+void
> -+kill_all_local_variables ()
> -+{
> -+ VAR_CONTEXT *vc;
> -+
> -+ for (vc = shell_variables; vc; vc = vc->down)
> -+ if (vc_isfuncenv (vc) && vc->scope == variable_context)
> -+ break;
> -+ if (vc == 0)
> -+ return; /* XXX */
> -+
> -+ if (vc->table && vc_haslocals (vc))
> -+ {
> -+ delete_all_variables (vc->table);
> -+ hash_dispose (vc->table);
> -+ }
> -+ vc->table = (HASH_TABLE *)NULL;
> -+}
> -+
> -+static void
> -+free_variable_hash_data (data)
> -+ PTR_T data;
> -+{
> -+ SHELL_VAR *var;
> -+
> -+ var = (SHELL_VAR *)data;
> -+ dispose_variable (var);
> -+}
> -+
> -+/* Delete the entire contents of the hash table. */
> -+void
> -+delete_all_variables (hashed_vars)
> -+ HASH_TABLE *hashed_vars;
> -+{
> -+ hash_flush (hashed_vars, free_variable_hash_data);
> -+}
> -+
> -+/* **************************************************************** */
> -+/* */
> -+/* Setting variable attributes */
> -+/* */
> -+/* **************************************************************** */
> -+
> -+#define FIND_OR_MAKE_VARIABLE(name, entry) \
> -+ do \
> -+ { \
> -+ entry = find_variable (name); \
> -+ if (!entry) \
> -+ { \
> -+ entry = bind_variable (name, "", 0); \
> -+ if (!no_invisible_vars && entry) entry->attributes |= att_invisible; \
> -+ } \
> -+ } \
> -+ while (0)
> -+
> -+/* Make the variable associated with NAME be readonly.
> -+ If NAME does not exist yet, create it. */
> -+void
> -+set_var_read_only (name)
> -+ char *name;
> -+{
> -+ SHELL_VAR *entry;
> -+
> -+ FIND_OR_MAKE_VARIABLE (name, entry);
> -+ VSETATTR (entry, att_readonly);
> -+}
> -+
> -+#ifdef INCLUDE_UNUSED
> -+/* Make the function associated with NAME be readonly.
> -+ If NAME does not exist, we just punt, like auto_export code below. */
> -+void
> -+set_func_read_only (name)
> -+ const char *name;
> -+{
> -+ SHELL_VAR *entry;
> -+
> -+ entry = find_function (name);
> -+ if (entry)
> -+ VSETATTR (entry, att_readonly);
> -+}
> -+
> -+/* Make the variable associated with NAME be auto-exported.
> -+ If NAME does not exist yet, create it. */
> -+void
> -+set_var_auto_export (name)
> -+ char *name;
> -+{
> -+ SHELL_VAR *entry;
> -+
> -+ FIND_OR_MAKE_VARIABLE (name, entry);
> -+ set_auto_export (entry);
> -+}
> -+
> -+/* Make the function associated with NAME be auto-exported. */
> -+void
> -+set_func_auto_export (name)
> -+ const char *name;
> -+{
> -+ SHELL_VAR *entry;
> -+
> -+ entry = find_function (name);
> -+ if (entry)
> -+ set_auto_export (entry);
> -+}
> -+#endif
> -+
> -+/* **************************************************************** */
> -+/* */
> -+/* Creating lists of variables */
> -+/* */
> -+/* **************************************************************** */
> -+
> -+static VARLIST *
> -+vlist_alloc (nentries)
> -+ int nentries;
> -+{
> -+ VARLIST *vlist;
> -+
> -+ vlist = (VARLIST *)xmalloc (sizeof (VARLIST));
> -+ vlist->list = (SHELL_VAR **)xmalloc ((nentries + 1) * sizeof (SHELL_VAR *));
> -+ vlist->list_size = nentries;
> -+ vlist->list_len = 0;
> -+ vlist->list[0] = (SHELL_VAR *)NULL;
> -+
> -+ return vlist;
> -+}
> -+
> -+static VARLIST *
> -+vlist_realloc (vlist, n)
> -+ VARLIST *vlist;
> -+ int n;
> -+{
> -+ if (vlist == 0)
> -+ return (vlist = vlist_alloc (n));
> -+ if (n > vlist->list_size)
> -+ {
> -+ vlist->list_size = n;
> -+ vlist->list = (SHELL_VAR **)xrealloc (vlist->list, (vlist->list_size + 1) * sizeof (SHELL_VAR *));
> -+ }
> -+ return vlist;
> -+}
> -+
> -+static void
> -+vlist_add (vlist, var, flags)
> -+ VARLIST *vlist;
> -+ SHELL_VAR *var;
> -+ int flags;
> -+{
> -+ register int i;
> -+
> -+ for (i = 0; i < vlist->list_len; i++)
> -+ if (STREQ (var->name, vlist->list[i]->name))
> -+ break;
> -+ if (i < vlist->list_len)
> -+ return;
> -+
> -+ if (i >= vlist->list_size)
> -+ vlist = vlist_realloc (vlist, vlist->list_size + 16);
> -+
> -+ vlist->list[vlist->list_len++] = var;
> -+ vlist->list[vlist->list_len] = (SHELL_VAR *)NULL;
> -+}
> -+
> -+/* Map FUNCTION over the variables in VAR_HASH_TABLE. Return an array of the
> -+ variables for which FUNCTION returns a non-zero value. A NULL value
> -+ for FUNCTION means to use all variables. */
> -+SHELL_VAR **
> -+map_over (function, vc)
> -+ sh_var_map_func_t *function;
> -+ VAR_CONTEXT *vc;
> -+{
> -+ VAR_CONTEXT *v;
> -+ VARLIST *vlist;
> -+ SHELL_VAR **ret;
> -+ int nentries;
> -+
> -+ for (nentries = 0, v = vc; v; v = v->down)
> -+ nentries += HASH_ENTRIES (v->table);
> -+
> -+ if (nentries == 0)
> -+ return (SHELL_VAR **)NULL;
> -+
> -+ vlist = vlist_alloc (nentries);
> -+
> -+ for (v = vc; v; v = v->down)
> -+ flatten (v->table, function, vlist, 0);
> -+
> -+ ret = vlist->list;
> -+ free (vlist);
> -+ return ret;
> -+}
> -+
> -+SHELL_VAR **
> -+map_over_funcs (function)
> -+ sh_var_map_func_t *function;
> -+{
> -+ VARLIST *vlist;
> -+ SHELL_VAR **ret;
> -+
> -+ if (shell_functions == 0 || HASH_ENTRIES (shell_functions) == 0)
> -+ return ((SHELL_VAR **)NULL);
> -+
> -+ vlist = vlist_alloc (HASH_ENTRIES (shell_functions));
> -+
> -+ flatten (shell_functions, function, vlist, 0);
> -+
> -+ ret = vlist->list;
> -+ free (vlist);
> -+ return ret;
> -+}
> -+
> -+/* Flatten VAR_HASH_TABLE, applying FUNC to each member and adding those
> -+ elements for which FUNC succeeds to VLIST->list. FLAGS is reserved
> -+ for future use. Only unique names are added to VLIST. If FUNC is
> -+ NULL, each variable in VAR_HASH_TABLE is added to VLIST. If VLIST is
> -+ NULL, FUNC is applied to each SHELL_VAR in VAR_HASH_TABLE. If VLIST
> -+ and FUNC are both NULL, nothing happens. */
> -+static void
> -+flatten (var_hash_table, func, vlist, flags)
> -+ HASH_TABLE *var_hash_table;
> -+ sh_var_map_func_t *func;
> -+ VARLIST *vlist;
> -+ int flags;
> -+{
> -+ register int i;
> -+ register BUCKET_CONTENTS *tlist;
> -+ int r;
> -+ SHELL_VAR *var;
> -+
> -+ if (var_hash_table == 0 || (HASH_ENTRIES (var_hash_table) == 0) || (vlist == 0 && func == 0))
> -+ return;
> -+
> -+ for (i = 0; i < var_hash_table->nbuckets; i++)
> -+ {
> -+ for (tlist = hash_items (i, var_hash_table); tlist; tlist = tlist->next)
> -+ {
> -+ var = (SHELL_VAR *)tlist->data;
> -+
> -+ r = func ? (*func) (var) : 1;
> -+ if (r && vlist)
> -+ vlist_add (vlist, var, flags);
> -+ }
> -+ }
> -+}
> -+
> -+void
> -+sort_variables (array)
> -+ SHELL_VAR **array;
> -+{
> -+ qsort (array, strvec_len ((char **)array), sizeof (SHELL_VAR *), (QSFUNC *)qsort_var_comp);
> -+}
> -+
> -+static int
> -+qsort_var_comp (var1, var2)
> -+ SHELL_VAR **var1, **var2;
> -+{
> -+ int result;
> -+
> -+ if ((result = (*var1)->name[0] - (*var2)->name[0]) == 0)
> -+ result = strcmp ((*var1)->name, (*var2)->name);
> -+
> -+ return (result);
> -+}
> -+
> -+/* Apply FUNC to each variable in SHELL_VARIABLES, adding each one for
> -+ which FUNC succeeds to an array of SHELL_VAR *s. Returns the array. */
> -+static SHELL_VAR **
> -+vapply (func)
> -+ sh_var_map_func_t *func;
> -+{
> -+ SHELL_VAR **list;
> -+
> -+ list = map_over (func, shell_variables);
> -+ if (list /* && posixly_correct */)
> -+ sort_variables (list);
> -+ return (list);
> -+}
> -+
> -+/* Apply FUNC to each variable in SHELL_FUNCTIONS, adding each one for
> -+ which FUNC succeeds to an array of SHELL_VAR *s. Returns the array. */
> -+static SHELL_VAR **
> -+fapply (func)
> -+ sh_var_map_func_t *func;
> -+{
> -+ SHELL_VAR **list;
> -+
> -+ list = map_over_funcs (func);
> -+ if (list /* && posixly_correct */)
> -+ sort_variables (list);
> -+ return (list);
> -+}
> -+
> -+/* Create a NULL terminated array of all the shell variables. */
> -+SHELL_VAR **
> -+all_shell_variables ()
> -+{
> -+ return (vapply ((sh_var_map_func_t *)NULL));
> -+}
> -+
> -+/* Create a NULL terminated array of all the shell functions. */
> -+SHELL_VAR **
> -+all_shell_functions ()
> -+{
> -+ return (fapply ((sh_var_map_func_t *)NULL));
> -+}
> -+
> -+static int
> -+visible_var (var)
> -+ SHELL_VAR *var;
> -+{
> -+ return (invisible_p (var) == 0);
> -+}
> -+
> -+SHELL_VAR **
> -+all_visible_functions ()
> -+{
> -+ return (fapply (visible_var));
> -+}
> -+
> -+SHELL_VAR **
> -+all_visible_variables ()
> -+{
> -+ return (vapply (visible_var));
> -+}
> -+
> -+/* Return non-zero if the variable VAR is visible and exported. Array
> -+ variables cannot be exported. */
> -+static int
> -+visible_and_exported (var)
> -+ SHELL_VAR *var;
> -+{
> -+ return (invisible_p (var) == 0 && exported_p (var));
> -+}
> -+
> -+/* Candidate variables for the export environment are either valid variables
> -+ with the export attribute or invalid variables inherited from the initial
> -+ environment and simply passed through. */
> -+static int
> -+export_environment_candidate (var)
> -+ SHELL_VAR *var;
> -+{
> -+ return (exported_p (var) && (invisible_p (var) == 0 || imported_p (var)));
> -+}
> -+
> -+/* Return non-zero if VAR is a local variable in the current context and
> -+ is exported. */
> -+static int
> -+local_and_exported (var)
> -+ SHELL_VAR *var;
> -+{
> -+ return (invisible_p (var) == 0 && local_p (var) && var->context == variable_context && exported_p (var));
> -+}
> -+
> -+SHELL_VAR **
> -+all_exported_variables ()
> -+{
> -+ return (vapply (visible_and_exported));
> -+}
> -+
> -+SHELL_VAR **
> -+local_exported_variables ()
> -+{
> -+ return (vapply (local_and_exported));
> -+}
> -+
> -+static int
> -+variable_in_context (var)
> -+ SHELL_VAR *var;
> -+{
> -+ return (invisible_p (var) == 0 && local_p (var) && var->context == variable_context);
> -+}
> -+
> -+SHELL_VAR **
> -+all_local_variables ()
> -+{
> -+ VARLIST *vlist;
> -+ SHELL_VAR **ret;
> -+ VAR_CONTEXT *vc;
> -+
> -+ vc = shell_variables;
> -+ for (vc = shell_variables; vc; vc = vc->down)
> -+ if (vc_isfuncenv (vc) && vc->scope == variable_context)
> -+ break;
> -+
> -+ if (vc == 0)
> -+ {
> -+ internal_error (_("all_local_variables: no function context at current scope"));
> -+ return (SHELL_VAR **)NULL;
> -+ }
> -+ if (vc->table == 0 || HASH_ENTRIES (vc->table) == 0 || vc_haslocals (vc) == 0)
> -+ return (SHELL_VAR **)NULL;
> -+
> -+ vlist = vlist_alloc (HASH_ENTRIES (vc->table));
> -+
> -+ flatten (vc->table, variable_in_context, vlist, 0);
> -+
> -+ ret = vlist->list;
> -+ free (vlist);
> -+ if (ret)
> -+ sort_variables (ret);
> -+ return ret;
> -+}
> -+
> -+#if defined (ARRAY_VARS)
> -+/* Return non-zero if the variable VAR is visible and an array. */
> -+static int
> -+visible_array_vars (var)
> -+ SHELL_VAR *var;
> -+{
> -+ return (invisible_p (var) == 0 && array_p (var));
> -+}
> -+
> -+SHELL_VAR **
> -+all_array_variables ()
> -+{
> -+ return (vapply (visible_array_vars));
> -+}
> -+#endif /* ARRAY_VARS */
> -+
> -+char **
> -+all_variables_matching_prefix (prefix)
> -+ const char *prefix;
> -+{
> -+ SHELL_VAR **varlist;
> -+ char **rlist;
> -+ int vind, rind, plen;
> -+
> -+ plen = STRLEN (prefix);
> -+ varlist = all_visible_variables ();
> -+ for (vind = 0; varlist && varlist[vind]; vind++)
> -+ ;
> -+ if (varlist == 0 || vind == 0)
> -+ return ((char **)NULL);
> -+ rlist = strvec_create (vind + 1);
> -+ for (vind = rind = 0; varlist[vind]; vind++)
> -+ {
> -+ if (plen == 0 || STREQN (prefix, varlist[vind]->name, plen))
> -+ rlist[rind++] = savestring (varlist[vind]->name);
> -+ }
> -+ rlist[rind] = (char *)0;
> -+ free (varlist);
> -+
> -+ return rlist;
> -+}
> -+
> -+/* **************************************************************** */
> -+/* */
> -+/* Managing temporary variable scopes */
> -+/* */
> -+/* **************************************************************** */
> -+
> -+/* Make variable NAME have VALUE in the temporary environment. */
> -+static SHELL_VAR *
> -+bind_tempenv_variable (name, value)
> -+ const char *name;
> -+ char *value;
> -+{
> -+ SHELL_VAR *var;
> -+
> -+ var = temporary_env ? hash_lookup (name, temporary_env) : (SHELL_VAR *)NULL;
> -+
> -+ if (var)
> -+ {
> -+ FREE (value_cell (var));
> -+ var_setvalue (var, savestring (value));
> -+ INVALIDATE_EXPORTSTR (var);
> -+ }
> -+
> -+ return (var);
> -+}
> -+
> -+/* Find a variable in the temporary environment that is named NAME.
> -+ Return the SHELL_VAR *, or NULL if not found. */
> -+SHELL_VAR *
> -+find_tempenv_variable (name)
> -+ const char *name;
> -+{
> -+ return (temporary_env ? hash_lookup (name, temporary_env) : (SHELL_VAR *)NULL);
> -+}
> -+
> -+char **tempvar_list;
> -+int tvlist_ind;
> -+
> -+/* Push the variable described by (SHELL_VAR *)DATA down to the next
> -+ variable context from the temporary environment. */
> -+static void
> -+push_temp_var (data)
> -+ PTR_T data;
> -+{
> -+ SHELL_VAR *var, *v;
> -+ HASH_TABLE *binding_table;
> -+
> -+ var = (SHELL_VAR *)data;
> -+
> -+ binding_table = shell_variables->table;
> -+ if (binding_table == 0)
> -+ {
> -+ if (shell_variables == global_variables)
> -+ /* shouldn't happen */
> -+ binding_table = shell_variables->table = global_variables->table = hash_create (0);
> -+ else
> -+ binding_table = shell_variables->table = hash_create (TEMPENV_HASH_BUCKETS);
> -+ }
> -+
> -+ v = bind_variable_internal (var->name, value_cell (var), binding_table, 0, 0);
> -+
> -+ /* XXX - should we set the context here? It shouldn't matter because of how
> -+ assign_in_env works, but might want to check. */
> -+ if (binding_table == global_variables->table) /* XXX */
> -+ var->attributes &= ~(att_tempvar|att_propagate);
> -+ else
> -+ {
> -+ var->attributes |= att_propagate;
> -+ if (binding_table == shell_variables->table)
> -+ shell_variables->flags |= VC_HASTMPVAR;
> -+ }
> -+ v->attributes |= var->attributes;
> -+
> -+ if (find_special_var (var->name) >= 0)
> -+ tempvar_list[tvlist_ind++] = savestring (var->name);
> -+
> -+ dispose_variable (var);
> -+}
> -+
> -+static void
> -+propagate_temp_var (data)
> -+ PTR_T data;
> -+{
> -+ SHELL_VAR *var;
> -+
> -+ var = (SHELL_VAR *)data;
> -+ if (tempvar_p (var) && (var->attributes & att_propagate))
> -+ push_temp_var (data);
> -+ else
> -+ {
> -+ if (find_special_var (var->name) >= 0)
> -+ tempvar_list[tvlist_ind++] = savestring (var->name);
> -+ dispose_variable (var);
> -+ }
> -+}
> -+
> -+/* Free the storage used in the hash table for temporary
> -+ environment variables. PUSHF is a function to be called
> -+ to free each hash table entry. It takes care of pushing variables
> -+ to previous scopes if appropriate. PUSHF stores names of variables
> -+ that require special handling (e.g., IFS) on tempvar_list, so this
> -+ function can call stupidly_hack_special_variables on all the
> -+ variables in the list when the temporary hash table is destroyed. */
> -+static void
> -+dispose_temporary_env (pushf)
> -+ sh_free_func_t *pushf;
> -+{
> -+ int i;
> -+
> -+ tempvar_list = strvec_create (HASH_ENTRIES (temporary_env) + 1);
> -+ tempvar_list[tvlist_ind = 0] = 0;
> -+
> -+ hash_flush (temporary_env, pushf);
> -+ hash_dispose (temporary_env);
> -+ temporary_env = (HASH_TABLE *)NULL;
> -+
> -+ tempvar_list[tvlist_ind] = 0;
> -+
> -+ array_needs_making = 1;
> -+
> -+#if 0
> -+ sv_ifs ("IFS"); /* XXX here for now -- check setifs in assign_in_env */
> -+#endif
> -+ for (i = 0; i < tvlist_ind; i++)
> -+ stupidly_hack_special_variables (tempvar_list[i]);
> -+
> -+ strvec_dispose (tempvar_list);
> -+ tempvar_list = 0;
> -+ tvlist_ind = 0;
> -+}
> -+
> -+void
> -+dispose_used_env_vars ()
> -+{
> -+ if (temporary_env)
> -+ {
> -+ dispose_temporary_env (propagate_temp_var);
> -+ maybe_make_export_env ();
> -+ }
> -+}
> -+
> -+/* Take all of the shell variables in the temporary environment HASH_TABLE
> -+ and make shell variables from them at the current variable context. */
> -+void
> -+merge_temporary_env ()
> -+{
> -+ if (temporary_env)
> -+ dispose_temporary_env (push_temp_var);
> -+}
> -+
> -+/* **************************************************************** */
> -+/* */
> -+/* Creating and manipulating the environment */
> -+/* */
> -+/* **************************************************************** */
> -+
> -+static inline char *
> -+mk_env_string (name, value, isfunc)
> -+ const char *name, *value;
> -+ int isfunc;
> -+{
> -+ size_t name_len, value_len;
> -+ char *p, *q;
> -+
> -+ name_len = strlen (name);
> -+ value_len = STRLEN (value);
> -+
> -+ /* If we are exporting a shell function, construct the encoded function
> -+ name. */
> -+ if (isfunc && value)
> -+ {
> -+ p = (char *)xmalloc (BASHFUNC_PREFLEN + name_len + BASHFUNC_SUFFLEN + value_len + 2);
> -+ q = p;
> -+ memcpy (q, BASHFUNC_PREFIX, BASHFUNC_PREFLEN);
> -+ q += BASHFUNC_PREFLEN;
> -+ memcpy (q, name, name_len);
> -+ q += name_len;
> -+ memcpy (q, BASHFUNC_SUFFIX, BASHFUNC_SUFFLEN);
> -+ q += BASHFUNC_SUFFLEN;
> -+ }
> -+ else
> -+ {
> -+ p = (char *)xmalloc (2 + name_len + value_len);
> -+ memcpy (p, name, name_len);
> -+ q = p + name_len;
> -+ }
> -+
> -+ q[0] = '=';
> -+ if (value && *value)
> -+ memcpy (q + 1, value, value_len + 1);
> -+ else
> -+ q[1] = '\0';
> -+
> -+ return (p);
> -+}
> -+
> -+#ifdef DEBUG
> -+/* Debugging */
> -+static int
> -+valid_exportstr (v)
> -+ SHELL_VAR *v;
> -+{
> -+ char *s;
> -+
> -+ s = v->exportstr;
> -+ if (s == 0)
> -+ {
> -+ internal_error (_("%s has null exportstr"), v->name);
> -+ return (0);
> -+ }
> -+ if (legal_variable_starter ((unsigned char)*s) == 0)
> -+ {
> -+ internal_error (_("invalid character %d in exportstr for %s"), *s, v->name);
> -+ return (0);
> -+ }
> -+ for (s = v->exportstr + 1; s && *s; s++)
> -+ {
> -+ if (*s == '=')
> -+ break;
> -+ if (legal_variable_char ((unsigned char)*s) == 0)
> -+ {
> -+ internal_error (_("invalid character %d in exportstr for %s"), *s, v->name);
> -+ return (0);
> -+ }
> -+ }
> -+ if (*s != '=')
> -+ {
> -+ internal_error (_("no `=' in exportstr for %s"), v->name);
> -+ return (0);
> -+ }
> -+ return (1);
> -+}
> -+#endif
> -+
> -+static char **
> -+make_env_array_from_var_list (vars)
> -+ SHELL_VAR **vars;
> -+{
> -+ register int i, list_index;
> -+ register SHELL_VAR *var;
> -+ char **list, *value;
> -+
> -+ list = strvec_create ((1 + strvec_len ((char **)vars)));
> -+
> -+#define USE_EXPORTSTR (value == var->exportstr)
> -+
> -+ for (i = 0, list_index = 0; var = vars[i]; i++)
> -+ {
> -+#if defined (__CYGWIN__)
> -+ /* We don't use the exportstr stuff on Cygwin at all. */
> -+ INVALIDATE_EXPORTSTR (var);
> -+#endif
> -+ if (var->exportstr)
> -+ value = var->exportstr;
> -+ else if (function_p (var))
> -+ value = named_function_string ((char *)NULL, function_cell (var), 0);
> -+#if defined (ARRAY_VARS)
> -+ else if (array_p (var))
> -+# if ARRAY_EXPORT
> -+ value = array_to_assignment_string (array_cell (var));
> -+# else
> -+ continue; /* XXX array vars cannot yet be exported */
> -+# endif /* ARRAY_EXPORT */
> -+ else if (assoc_p (var))
> -+# if 0
> -+ value = assoc_to_assignment_string (assoc_cell (var));
> -+# else
> -+ continue; /* XXX associative array vars cannot yet be exported */
> -+# endif
> -+#endif
> -+ else
> -+ value = value_cell (var);
> -+
> -+ if (value)
> -+ {
> -+ /* Gee, I'd like to get away with not using savestring() if we're
> -+ using the cached exportstr... */
> -+ list[list_index] = USE_EXPORTSTR ? savestring (value)
> -+ : mk_env_string (var->name, value, function_p (var));
> -+
> -+ if (USE_EXPORTSTR == 0)
> -+ SAVE_EXPORTSTR (var, list[list_index]);
> -+
> -+ list_index++;
> -+#undef USE_EXPORTSTR
> -+
> -+#if 0 /* not yet */
> -+#if defined (ARRAY_VARS)
> -+ if (array_p (var) || assoc_p (var))
> -+ free (value);
> -+#endif
> -+#endif
> -+ }
> -+ }
> -+
> -+ list[list_index] = (char *)NULL;
> -+ return (list);
> -+}
> -+
> -+/* Make an array of assignment statements from the hash table
> -+ HASHED_VARS which contains SHELL_VARs. Only visible, exported
> -+ variables are eligible. */
> -+static char **
> -+make_var_export_array (vcxt)
> -+ VAR_CONTEXT *vcxt;
> -+{
> -+ char **list;
> -+ SHELL_VAR **vars;
> -+
> -+#if 0
> -+ vars = map_over (visible_and_exported, vcxt);
> -+#else
> -+ vars = map_over (export_environment_candidate, vcxt);
> -+#endif
> -+
> -+ if (vars == 0)
> -+ return (char **)NULL;
> -+
> -+ list = make_env_array_from_var_list (vars);
> -+
> -+ free (vars);
> -+ return (list);
> -+}
> -+
> -+static char **
> -+make_func_export_array ()
> -+{
> -+ char **list;
> -+ SHELL_VAR **vars;
> -+
> -+ vars = map_over_funcs (visible_and_exported);
> -+ if (vars == 0)
> -+ return (char **)NULL;
> -+
> -+ list = make_env_array_from_var_list (vars);
> -+
> -+ free (vars);
> -+ return (list);
> -+}
> -+
> -+/* Add ENVSTR to the end of the exported environment, EXPORT_ENV. */
> -+#define add_to_export_env(envstr,do_alloc) \
> -+do \
> -+ { \
> -+ if (export_env_index >= (export_env_size - 1)) \
> -+ { \
> -+ export_env_size += 16; \
> -+ export_env = strvec_resize (export_env, export_env_size); \
> -+ environ = export_env; \
> -+ } \
> -+ export_env[export_env_index++] = (do_alloc) ? savestring (envstr) : envstr; \
> -+ export_env[export_env_index] = (char *)NULL; \
> -+ } while (0)
> -+
> -+/* Add ASSIGN to EXPORT_ENV, or supercede a previous assignment in the
> -+ array with the same left-hand side. Return the new EXPORT_ENV. */
> -+char **
> -+add_or_supercede_exported_var (assign, do_alloc)
> -+ char *assign;
> -+ int do_alloc;
> -+{
> -+ register int i;
> -+ int equal_offset;
> -+
> -+ equal_offset = assignment (assign, 0);
> -+ if (equal_offset == 0)
> -+ return (export_env);
> -+
> -+ /* If this is a function, then only supersede the function definition.
> -+ We do this by including the `=() {' in the comparison, like
> -+ initialize_shell_variables does. */
> -+ if (assign[equal_offset + 1] == '(' &&
> -+ strncmp (assign + equal_offset + 2, ") {", 3) == 0) /* } */
> -+ equal_offset += 4;
> -+
> -+ for (i = 0; i < export_env_index; i++)
> -+ {
> -+ if (STREQN (assign, export_env[i], equal_offset + 1))
> -+ {
> -+ free (export_env[i]);
> -+ export_env[i] = do_alloc ? savestring (assign) : assign;
> -+ return (export_env);
> -+ }
> -+ }
> -+ add_to_export_env (assign, do_alloc);
> -+ return (export_env);
> -+}
> -+
> -+static void
> -+add_temp_array_to_env (temp_array, do_alloc, do_supercede)
> -+ char **temp_array;
> -+ int do_alloc, do_supercede;
> -+{
> -+ register int i;
> -+
> -+ if (temp_array == 0)
> -+ return;
> -+
> -+ for (i = 0; temp_array[i]; i++)
> -+ {
> -+ if (do_supercede)
> -+ export_env = add_or_supercede_exported_var (temp_array[i], do_alloc);
> -+ else
> -+ add_to_export_env (temp_array[i], do_alloc);
> -+ }
> -+
> -+ free (temp_array);
> -+}
> -+
> -+/* Make the environment array for the command about to be executed, if the
> -+ array needs making. Otherwise, do nothing. If a shell action could
> -+ change the array that commands receive for their environment, then the
> -+ code should `array_needs_making++'.
> -+
> -+ The order to add to the array is:
> -+ temporary_env
> -+ list of var contexts whose head is shell_variables
> -+ shell_functions
> -+
> -+ This is the shell variable lookup order. We add only new variable
> -+ names at each step, which allows local variables and variables in
> -+ the temporary environments to shadow variables in the global (or
> -+ any previous) scope.
> -+*/
> -+
> -+static int
> -+n_shell_variables ()
> -+{
> -+ VAR_CONTEXT *vc;
> -+ int n;
> -+
> -+ for (n = 0, vc = shell_variables; vc; vc = vc->down)
> -+ n += HASH_ENTRIES (vc->table);
> -+ return n;
> -+}
> -+
> -+int
> -+chkexport (name)
> -+ char *name;
> -+{
> -+ SHELL_VAR *v;
> -+
> -+ v = find_variable (name);
> -+ if (v && exported_p (v))
> -+ {
> -+ array_needs_making = 1;
> -+ maybe_make_export_env ();
> -+ return 1;
> -+ }
> -+ return 0;
> -+}
> -+
> -+void
> -+maybe_make_export_env ()
> -+{
> -+ register char **temp_array;
> -+ int new_size;
> -+ VAR_CONTEXT *tcxt;
> -+
> -+ if (array_needs_making)
> -+ {
> -+ if (export_env)
> -+ strvec_flush (export_env);
> -+
> -+ /* Make a guess based on how many shell variables and functions we
> -+ have. Since there will always be array variables, and array
> -+ variables are not (yet) exported, this will always be big enough
> -+ for the exported variables and functions. */
> -+ new_size = n_shell_variables () + HASH_ENTRIES (shell_functions) + 1 +
> -+ HASH_ENTRIES (temporary_env);
> -+ if (new_size > export_env_size)
> -+ {
> -+ export_env_size = new_size;
> -+ export_env = strvec_resize (export_env, export_env_size);
> -+ environ = export_env;
> -+ }
> -+ export_env[export_env_index = 0] = (char *)NULL;
> -+
> -+ /* Make a dummy variable context from the temporary_env, stick it on
> -+ the front of shell_variables, call make_var_export_array on the
> -+ whole thing to flatten it, and convert the list of SHELL_VAR *s
> -+ to the form needed by the environment. */
> -+ if (temporary_env)
> -+ {
> -+ tcxt = new_var_context ((char *)NULL, 0);
> -+ tcxt->table = temporary_env;
> -+ tcxt->down = shell_variables;
> -+ }
> -+ else
> -+ tcxt = shell_variables;
> -+
> -+ temp_array = make_var_export_array (tcxt);
> -+ if (temp_array)
> -+ add_temp_array_to_env (temp_array, 0, 0);
> -+
> -+ if (tcxt != shell_variables)
> -+ free (tcxt);
> -+
> -+#if defined (RESTRICTED_SHELL)
> -+ /* Restricted shells may not export shell functions. */
> -+ temp_array = restricted ? (char **)0 : make_func_export_array ();
> -+#else
> -+ temp_array = make_func_export_array ();
> -+#endif
> -+ if (temp_array)
> -+ add_temp_array_to_env (temp_array, 0, 0);
> -+
> -+ array_needs_making = 0;
> -+ }
> -+}
> -+
> -+/* This is an efficiency hack. PWD and OLDPWD are auto-exported, so
> -+ we will need to remake the exported environment every time we
> -+ change directories. `_' is always put into the environment for
> -+ every external command, so without special treatment it will always
> -+ cause the environment to be remade.
> -+
> -+ If there is no other reason to make the exported environment, we can
> -+ just update the variables in place and mark the exported environment
> -+ as no longer needing a remake. */
> -+void
> -+update_export_env_inplace (env_prefix, preflen, value)
> -+ char *env_prefix;
> -+ int preflen;
> -+ char *value;
> -+{
> -+ char *evar;
> -+
> -+ evar = (char *)xmalloc (STRLEN (value) + preflen + 1);
> -+ strcpy (evar, env_prefix);
> -+ if (value)
> -+ strcpy (evar + preflen, value);
> -+ export_env = add_or_supercede_exported_var (evar, 0);
> -+}
> -+
> -+/* We always put _ in the environment as the name of this command. */
> -+void
> -+put_command_name_into_env (command_name)
> -+ char *command_name;
> -+{
> -+ update_export_env_inplace ("_=", 2, command_name);
> -+}
> -+
> -+/* **************************************************************** */
> -+/* */
> -+/* Managing variable contexts */
> -+/* */
> -+/* **************************************************************** */
> -+
> -+/* Allocate and return a new variable context with NAME and FLAGS.
> -+ NAME can be NULL. */
> -+
> -+VAR_CONTEXT *
> -+new_var_context (name, flags)
> -+ char *name;
> -+ int flags;
> -+{
> -+ VAR_CONTEXT *vc;
> -+
> -+ vc = (VAR_CONTEXT *)xmalloc (sizeof (VAR_CONTEXT));
> -+ vc->name = name ? savestring (name) : (char *)NULL;
> -+ vc->scope = variable_context;
> -+ vc->flags = flags;
> -+
> -+ vc->up = vc->down = (VAR_CONTEXT *)NULL;
> -+ vc->table = (HASH_TABLE *)NULL;
> -+
> -+ return vc;
> -+}
> -+
> -+/* Free a variable context and its data, including the hash table. Dispose
> -+ all of the variables. */
> -+void
> -+dispose_var_context (vc)
> -+ VAR_CONTEXT *vc;
> -+{
> -+ FREE (vc->name);
> -+
> -+ if (vc->table)
> -+ {
> -+ delete_all_variables (vc->table);
> -+ hash_dispose (vc->table);
> -+ }
> -+
> -+ free (vc);
> -+}
> -+
> -+/* Set VAR's scope level to the current variable context. */
> -+static int
> -+set_context (var)
> -+ SHELL_VAR *var;
> -+{
> -+ return (var->context = variable_context);
> -+}
> -+
> -+/* Make a new variable context with NAME and FLAGS and a HASH_TABLE of
> -+ temporary variables, and push it onto shell_variables. This is
> -+ for shell functions. */
> -+VAR_CONTEXT *
> -+push_var_context (name, flags, tempvars)
> -+ char *name;
> -+ int flags;
> -+ HASH_TABLE *tempvars;
> -+{
> -+ VAR_CONTEXT *vc;
> -+
> -+ vc = new_var_context (name, flags);
> -+ vc->table = tempvars;
> -+ if (tempvars)
> -+ {
> -+ /* Have to do this because the temp environment was created before
> -+ variable_context was incremented. */
> -+ flatten (tempvars, set_context, (VARLIST *)NULL, 0);
> -+ vc->flags |= VC_HASTMPVAR;
> -+ }
> -+ vc->down = shell_variables;
> -+ shell_variables->up = vc;
> -+
> -+ return (shell_variables = vc);
> -+}
> -+
> -+static void
> -+push_func_var (data)
> -+ PTR_T data;
> -+{
> -+ SHELL_VAR *var, *v;
> -+
> -+ var = (SHELL_VAR *)data;
> -+
> -+ if (tempvar_p (var) && (posixly_correct || (var->attributes & att_propagate)))
> -+ {
> -+ /* Make sure we have a hash table to store the variable in while it is
> -+ being propagated down to the global variables table. Create one if
> -+ we have to */
> -+ if ((vc_isfuncenv (shell_variables) || vc_istempenv (shell_variables)) && shell_variables->table == 0)
> -+ shell_variables->table = hash_create (0);
> -+ /* XXX - should we set v->context here? */
> -+ v = bind_variable_internal (var->name, value_cell (var), shell_variables->table, 0, 0);
> -+ if (shell_variables == global_variables)
> -+ var->attributes &= ~(att_tempvar|att_propagate);
> -+ else
> -+ shell_variables->flags |= VC_HASTMPVAR;
> -+ v->attributes |= var->attributes;
> -+ }
> -+ else
> -+ stupidly_hack_special_variables (var->name); /* XXX */
> -+
> -+ dispose_variable (var);
> -+}
> -+
> -+/* Pop the top context off of VCXT and dispose of it, returning the rest of
> -+ the stack. */
> -+void
> -+pop_var_context ()
> -+{
> -+ VAR_CONTEXT *ret, *vcxt;
> -+
> -+ vcxt = shell_variables;
> -+ if (vc_isfuncenv (vcxt) == 0)
> -+ {
> -+ internal_error (_("pop_var_context: head of shell_variables not a function context"));
> -+ return;
> -+ }
> -+
> -+ if (ret = vcxt->down)
> -+ {
> -+ ret->up = (VAR_CONTEXT *)NULL;
> -+ shell_variables = ret;
> -+ if (vcxt->table)
> -+ hash_flush (vcxt->table, push_func_var);
> -+ dispose_var_context (vcxt);
> -+ }
> -+ else
> -+ internal_error (_("pop_var_context: no global_variables context"));
> -+}
> -+
> -+/* Delete the HASH_TABLEs for all variable contexts beginning at VCXT, and
> -+ all of the VAR_CONTEXTs except GLOBAL_VARIABLES. */
> -+void
> -+delete_all_contexts (vcxt)
> -+ VAR_CONTEXT *vcxt;
> -+{
> -+ VAR_CONTEXT *v, *t;
> -+
> -+ for (v = vcxt; v != global_variables; v = t)
> -+ {
> -+ t = v->down;
> -+ dispose_var_context (v);
> -+ }
> -+
> -+ delete_all_variables (global_variables->table);
> -+ shell_variables = global_variables;
> -+}
> -+
> -+/* **************************************************************** */
> -+/* */
> -+/* Pushing and Popping temporary variable scopes */
> -+/* */
> -+/* **************************************************************** */
> -+
> -+VAR_CONTEXT *
> -+push_scope (flags, tmpvars)
> -+ int flags;
> -+ HASH_TABLE *tmpvars;
> -+{
> -+ return (push_var_context ((char *)NULL, flags, tmpvars));
> -+}
> -+
> -+static void
> -+push_exported_var (data)
> -+ PTR_T data;
> -+{
> -+ SHELL_VAR *var, *v;
> -+
> -+ var = (SHELL_VAR *)data;
> -+
> -+ /* If a temp var had its export attribute set, or it's marked to be
> -+ propagated, bind it in the previous scope before disposing it. */
> -+ /* XXX - This isn't exactly right, because all tempenv variables have the
> -+ export attribute set. */
> -+#if 0
> -+ if (exported_p (var) || (var->attributes & att_propagate))
> -+#else
> -+ if (tempvar_p (var) && exported_p (var) && (var->attributes & att_propagate))
> -+#endif
> -+ {
> -+ var->attributes &= ~att_tempvar; /* XXX */
> -+ v = bind_variable_internal (var->name, value_cell (var), shell_variables->table, 0, 0);
> -+ if (shell_variables == global_variables)
> -+ var->attributes &= ~att_propagate;
> -+ v->attributes |= var->attributes;
> -+ }
> -+ else
> -+ stupidly_hack_special_variables (var->name); /* XXX */
> -+
> -+ dispose_variable (var);
> -+}
> -+
> -+void
> -+pop_scope (is_special)
> -+ int is_special;
> -+{
> -+ VAR_CONTEXT *vcxt, *ret;
> -+
> -+ vcxt = shell_variables;
> -+ if (vc_istempscope (vcxt) == 0)
> -+ {
> -+ internal_error (_("pop_scope: head of shell_variables not a temporary environment scope"));
> -+ return;
> -+ }
> -+
> -+ ret = vcxt->down;
> -+ if (ret)
> -+ ret->up = (VAR_CONTEXT *)NULL;
> -+
> -+ shell_variables = ret;
> -+
> -+ /* Now we can take care of merging variables in VCXT into set of scopes
> -+ whose head is RET (shell_variables). */
> -+ FREE (vcxt->name);
> -+ if (vcxt->table)
> -+ {
> -+ if (is_special)
> -+ hash_flush (vcxt->table, push_func_var);
> -+ else
> -+ hash_flush (vcxt->table, push_exported_var);
> -+ hash_dispose (vcxt->table);
> -+ }
> -+ free (vcxt);
> -+
> -+ sv_ifs ("IFS"); /* XXX here for now */
> -+}
> -+
> -+/* **************************************************************** */
> -+/* */
> -+/* Pushing and Popping function contexts */
> -+/* */
> -+/* **************************************************************** */
> -+
> -+static WORD_LIST **dollar_arg_stack = (WORD_LIST **)NULL;
> -+static int dollar_arg_stack_slots;
> -+static int dollar_arg_stack_index;
> -+
> -+/* XXX - we might want to consider pushing and popping the `getopts' state
> -+ when we modify the positional parameters. */
> -+void
> -+push_context (name, is_subshell, tempvars)
> -+ char *name; /* function name */
> -+ int is_subshell;
> -+ HASH_TABLE *tempvars;
> -+{
> -+ if (is_subshell == 0)
> -+ push_dollar_vars ();
> -+ variable_context++;
> -+ push_var_context (name, VC_FUNCENV, tempvars);
> -+}
> -+
> -+/* Only called when subshell == 0, so we don't need to check, and can
> -+ unconditionally pop the dollar vars off the stack. */
> -+void
> -+pop_context ()
> -+{
> -+ pop_dollar_vars ();
> -+ variable_context--;
> -+ pop_var_context ();
> -+
> -+ sv_ifs ("IFS"); /* XXX here for now */
> -+}
> -+
> -+/* Save the existing positional parameters on a stack. */
> -+void
> -+push_dollar_vars ()
> -+{
> -+ if (dollar_arg_stack_index + 2 > dollar_arg_stack_slots)
> -+ {
> -+ dollar_arg_stack = (WORD_LIST **)
> -+ xrealloc (dollar_arg_stack, (dollar_arg_stack_slots += 10)
> -+ * sizeof (WORD_LIST *));
> -+ }
> -+ dollar_arg_stack[dollar_arg_stack_index++] = list_rest_of_args ();
> -+ dollar_arg_stack[dollar_arg_stack_index] = (WORD_LIST *)NULL;
> -+}
> -+
> -+/* Restore the positional parameters from our stack. */
> -+void
> -+pop_dollar_vars ()
> -+{
> -+ if (!dollar_arg_stack || dollar_arg_stack_index == 0)
> -+ return;
> -+
> -+ remember_args (dollar_arg_stack[--dollar_arg_stack_index], 1);
> -+ dispose_words (dollar_arg_stack[dollar_arg_stack_index]);
> -+ dollar_arg_stack[dollar_arg_stack_index] = (WORD_LIST *)NULL;
> -+ set_dollar_vars_unchanged ();
> -+}
> -+
> -+void
> -+dispose_saved_dollar_vars ()
> -+{
> -+ if (!dollar_arg_stack || dollar_arg_stack_index == 0)
> -+ return;
> -+
> -+ dispose_words (dollar_arg_stack[dollar_arg_stack_index]);
> -+ dollar_arg_stack[dollar_arg_stack_index] = (WORD_LIST *)NULL;
> -+}
> -+
> -+/* Manipulate the special BASH_ARGV and BASH_ARGC variables. */
> -+
> -+void
> -+push_args (list)
> -+ WORD_LIST *list;
> -+{
> -+#if defined (ARRAY_VARS) && defined (DEBUGGER)
> -+ SHELL_VAR *bash_argv_v, *bash_argc_v;
> -+ ARRAY *bash_argv_a, *bash_argc_a;
> -+ WORD_LIST *l;
> -+ arrayind_t i;
> -+ char *t;
> -+
> -+ GET_ARRAY_FROM_VAR ("BASH_ARGV", bash_argv_v, bash_argv_a);
> -+ GET_ARRAY_FROM_VAR ("BASH_ARGC", bash_argc_v, bash_argc_a);
> -+
> -+ for (l = list, i = 0; l; l = l->next, i++)
> -+ array_push (bash_argv_a, l->word->word);
> -+
> -+ t = itos (i);
> -+ array_push (bash_argc_a, t);
> -+ free (t);
> -+#endif /* ARRAY_VARS && DEBUGGER */
> -+}
> -+
> -+/* Remove arguments from BASH_ARGV array. Pop top element off BASH_ARGC
> -+ array and use that value as the count of elements to remove from
> -+ BASH_ARGV. */
> -+void
> -+pop_args ()
> -+{
> -+#if defined (ARRAY_VARS) && defined (DEBUGGER)
> -+ SHELL_VAR *bash_argv_v, *bash_argc_v;
> -+ ARRAY *bash_argv_a, *bash_argc_a;
> -+ ARRAY_ELEMENT *ce;
> -+ intmax_t i;
> -+
> -+ GET_ARRAY_FROM_VAR ("BASH_ARGV", bash_argv_v, bash_argv_a);
> -+ GET_ARRAY_FROM_VAR ("BASH_ARGC", bash_argc_v, bash_argc_a);
> -+
> -+ ce = array_shift (bash_argc_a, 1, 0);
> -+ if (ce == 0 || legal_number (element_value (ce), &i) == 0)
> -+ i = 0;
> -+
> -+ for ( ; i > 0; i--)
> -+ array_pop (bash_argv_a);
> -+ array_dispose_element (ce);
> -+#endif /* ARRAY_VARS && DEBUGGER */
> -+}
> -+
> -+/*************************************************
> -+ * *
> -+ * Functions to manage special variables *
> -+ * *
> -+ *************************************************/
> -+
> -+/* Extern declarations for variables this code has to manage. */
> -+extern int eof_encountered, eof_encountered_limit, ignoreeof;
> -+
> -+#if defined (READLINE)
> -+extern int hostname_list_initialized;
> -+#endif
> -+
> -+/* An alist of name.function for each special variable. Most of the
> -+ functions don't do much, and in fact, this would be faster with a
> -+ switch statement, but by the end of this file, I am sick of switch
> -+ statements. */
> -+
> -+#define SET_INT_VAR(name, intvar) intvar = find_variable (name) != 0
> -+
> -+/* This table will be sorted with qsort() the first time it's accessed. */
> -+struct name_and_function {
> -+ char *name;
> -+ sh_sv_func_t *function;
> -+};
> -+
> -+static struct name_and_function special_vars[] = {
> -+ { "BASH_COMPAT", sv_shcompat },
> -+ { "BASH_XTRACEFD", sv_xtracefd },
> -+
> -+#if defined (JOB_CONTROL)
> -+ { "CHILD_MAX", sv_childmax },
> -+#endif
> -+
> -+#if defined (READLINE)
> -+# if defined (STRICT_POSIX)
> -+ { "COLUMNS", sv_winsize },
> -+# endif
> -+ { "COMP_WORDBREAKS", sv_comp_wordbreaks },
> -+#endif
> -+
> -+ { "FUNCNEST", sv_funcnest },
> -+
> -+ { "GLOBIGNORE", sv_globignore },
> -+
> -+#if defined (HISTORY)
> -+ { "HISTCONTROL", sv_history_control },
> -+ { "HISTFILESIZE", sv_histsize },
> -+ { "HISTIGNORE", sv_histignore },
> -+ { "HISTSIZE", sv_histsize },
> -+ { "HISTTIMEFORMAT", sv_histtimefmt },
> -+#endif
> -+
> -+#if defined (__CYGWIN__)
> -+ { "HOME", sv_home },
> -+#endif
> -+
> -+#if defined (READLINE)
> -+ { "HOSTFILE", sv_hostfile },
> -+#endif
> -+
> -+ { "IFS", sv_ifs },
> -+ { "IGNOREEOF", sv_ignoreeof },
> -+
> -+ { "LANG", sv_locale },
> -+ { "LC_ALL", sv_locale },
> -+ { "LC_COLLATE", sv_locale },
> -+ { "LC_CTYPE", sv_locale },
> -+ { "LC_MESSAGES", sv_locale },
> -+ { "LC_NUMERIC", sv_locale },
> -+ { "LC_TIME", sv_locale },
> -+
> -+#if defined (READLINE) && defined (STRICT_POSIX)
> -+ { "LINES", sv_winsize },
> -+#endif
> -+
> -+ { "MAIL", sv_mail },
> -+ { "MAILCHECK", sv_mail },
> -+ { "MAILPATH", sv_mail },
> -+
> -+ { "OPTERR", sv_opterr },
> -+ { "OPTIND", sv_optind },
> -+
> -+ { "PATH", sv_path },
> -+ { "POSIXLY_CORRECT", sv_strict_posix },
> -+
> -+#if defined (READLINE)
> -+ { "TERM", sv_terminal },
> -+ { "TERMCAP", sv_terminal },
> -+ { "TERMINFO", sv_terminal },
> -+#endif /* READLINE */
> -+
> -+ { "TEXTDOMAIN", sv_locale },
> -+ { "TEXTDOMAINDIR", sv_locale },
> -+
> -+#if defined (HAVE_TZSET)
> -+ { "TZ", sv_tz },
> -+#endif
> -+
> -+#if defined (HISTORY) && defined (BANG_HISTORY)
> -+ { "histchars", sv_histchars },
> -+#endif /* HISTORY && BANG_HISTORY */
> -+
> -+ { "ignoreeof", sv_ignoreeof },
> -+
> -+ { (char *)0, (sh_sv_func_t *)0 }
> -+};
> -+
> -+#define N_SPECIAL_VARS (sizeof (special_vars) / sizeof (special_vars[0]) - 1)
> -+
> -+static int
> -+sv_compare (sv1, sv2)
> -+ struct name_and_function *sv1, *sv2;
> -+{
> -+ int r;
> -+
> -+ if ((r = sv1->name[0] - sv2->name[0]) == 0)
> -+ r = strcmp (sv1->name, sv2->name);
> -+ return r;
> -+}
> -+
> -+static inline int
> -+find_special_var (name)
> -+ const char *name;
> -+{
> -+ register int i, r;
> -+
> -+ for (i = 0; special_vars[i].name; i++)
> -+ {
> -+ r = special_vars[i].name[0] - name[0];
> -+ if (r == 0)
> -+ r = strcmp (special_vars[i].name, name);
> -+ if (r == 0)
> -+ return i;
> -+ else if (r > 0)
> -+ /* Can't match any of rest of elements in sorted list. Take this out
> -+ if it causes problems in certain environments. */
> -+ break;
> -+ }
> -+ return -1;
> -+}
> -+
> -+/* The variable in NAME has just had its state changed. Check to see if it
> -+ is one of the special ones where something special happens. */
> -+void
> -+stupidly_hack_special_variables (name)
> -+ char *name;
> -+{
> -+ static int sv_sorted = 0;
> -+ int i;
> -+
> -+ if (sv_sorted == 0) /* shouldn't need, but it's fairly cheap. */
> -+ {
> -+ qsort (special_vars, N_SPECIAL_VARS, sizeof (special_vars[0]),
> -+ (QSFUNC *)sv_compare);
> -+ sv_sorted = 1;
> -+ }
> -+
> -+ i = find_special_var (name);
> -+ if (i != -1)
> -+ (*(special_vars[i].function)) (name);
> -+}
> -+
> -+/* Special variables that need hooks to be run when they are unset as part
> -+ of shell reinitialization should have their sv_ functions run here. */
> -+void
> -+reinit_special_variables ()
> -+{
> -+#if defined (READLINE)
> -+ sv_comp_wordbreaks ("COMP_WORDBREAKS");
> -+#endif
> -+ sv_globignore ("GLOBIGNORE");
> -+ sv_opterr ("OPTERR");
> -+}
> -+
> -+void
> -+sv_ifs (name)
> -+ char *name;
> -+{
> -+ SHELL_VAR *v;
> -+
> -+ v = find_variable ("IFS");
> -+ setifs (v);
> -+}
> -+
> -+/* What to do just after the PATH variable has changed. */
> -+void
> -+sv_path (name)
> -+ char *name;
> -+{
> -+ /* hash -r */
> -+ phash_flush ();
> -+}
> -+
> -+/* What to do just after one of the MAILxxxx variables has changed. NAME
> -+ is the name of the variable. This is called with NAME set to one of
> -+ MAIL, MAILCHECK, or MAILPATH. */
> -+void
> -+sv_mail (name)
> -+ char *name;
> -+{
> -+ /* If the time interval for checking the files has changed, then
> -+ reset the mail timer. Otherwise, one of the pathname vars
> -+ to the users mailbox has changed, so rebuild the array of
> -+ filenames. */
> -+ if (name[4] == 'C') /* if (strcmp (name, "MAILCHECK") == 0) */
> -+ reset_mail_timer ();
> -+ else
> -+ {
> -+ free_mail_files ();
> -+ remember_mail_dates ();
> -+ }
> -+}
> -+
> -+void
> -+sv_funcnest (name)
> -+ char *name;
> -+{
> -+ SHELL_VAR *v;
> -+ intmax_t num;
> -+
> -+ v = find_variable (name);
> -+ if (v == 0)
> -+ funcnest_max = 0;
> -+ else if (legal_number (value_cell (v), &num) == 0)
> -+ funcnest_max = 0;
> -+ else
> -+ funcnest_max = num;
> -+}
> -+
> -+/* What to do when GLOBIGNORE changes. */
> -+void
> -+sv_globignore (name)
> -+ char *name;
> -+{
> -+ if (privileged_mode == 0)
> -+ setup_glob_ignore (name);
> -+}
> -+
> -+#if defined (READLINE)
> -+void
> -+sv_comp_wordbreaks (name)
> -+ char *name;
> -+{
> -+ SHELL_VAR *sv;
> -+
> -+ sv = find_variable (name);
> -+ if (sv == 0)
> -+ reset_completer_word_break_chars ();
> -+}
> -+
> -+/* What to do just after one of the TERMxxx variables has changed.
> -+ If we are an interactive shell, then try to reset the terminal
> -+ information in readline. */
> -+void
> -+sv_terminal (name)
> -+ char *name;
> -+{
> -+ if (interactive_shell && no_line_editing == 0)
> -+ rl_reset_terminal (get_string_value ("TERM"));
> -+}
> -+
> -+void
> -+sv_hostfile (name)
> -+ char *name;
> -+{
> -+ SHELL_VAR *v;
> -+
> -+ v = find_variable (name);
> -+ if (v == 0)
> -+ clear_hostname_list ();
> -+ else
> -+ hostname_list_initialized = 0;
> -+}
> -+
> -+#if defined (STRICT_POSIX)
> -+/* In strict posix mode, we allow assignments to LINES and COLUMNS (and values
> -+ found in the initial environment) to override the terminal size reported by
> -+ the kernel. */
> -+void
> -+sv_winsize (name)
> -+ char *name;
> -+{
> -+ SHELL_VAR *v;
> -+ intmax_t xd;
> -+ int d;
> -+
> -+ if (posixly_correct == 0 || interactive_shell == 0 || no_line_editing)
> -+ return;
> -+
> -+ v = find_variable (name);
> -+ if (v == 0 || var_isnull (v))
> -+ rl_reset_screen_size ();
> -+ else
> -+ {
> -+ if (legal_number (value_cell (v), &xd) == 0)
> -+ return;
> -+ winsize_assignment = 1;
> -+ d = xd; /* truncate */
> -+ if (name[0] == 'L') /* LINES */
> -+ rl_set_screen_size (d, -1);
> -+ else /* COLUMNS */
> -+ rl_set_screen_size (-1, d);
> -+ winsize_assignment = 0;
> -+ }
> -+}
> -+#endif /* STRICT_POSIX */
> -+#endif /* READLINE */
> -+
> -+/* Update the value of HOME in the export environment so tilde expansion will
> -+ work on cygwin. */
> -+#if defined (__CYGWIN__)
> -+sv_home (name)
> -+ char *name;
> -+{
> -+ array_needs_making = 1;
> -+ maybe_make_export_env ();
> -+}
> -+#endif
> -+
> -+#if defined (HISTORY)
> -+/* What to do after the HISTSIZE or HISTFILESIZE variables change.
> -+ If there is a value for this HISTSIZE (and it is numeric), then stifle
> -+ the history. Otherwise, if there is NO value for this variable,
> -+ unstifle the history. If name is HISTFILESIZE, and its value is
> -+ numeric, truncate the history file to hold no more than that many
> -+ lines. */
> -+void
> -+sv_histsize (name)
> -+ char *name;
> -+{
> -+ char *temp;
> -+ intmax_t num;
> -+ int hmax;
> -+
> -+ temp = get_string_value (name);
> -+
> -+ if (temp && *temp)
> -+ {
> -+ if (legal_number (temp, &num))
> -+ {
> -+ hmax = num;
> -+ if (hmax < 0 && name[4] == 'S')
> -+ unstifle_history (); /* unstifle history if HISTSIZE < 0 */
> -+ else if (name[4] == 'S')
> -+ {
> -+ stifle_history (hmax);
> -+ hmax = where_history ();
> -+ if (history_lines_this_session > hmax)
> -+ history_lines_this_session = hmax;
> -+ }
> -+ else if (hmax >= 0) /* truncate HISTFILE if HISTFILESIZE >= 0 */
> -+ {
> -+ history_truncate_file (get_string_value ("HISTFILE"), hmax);
> -+ if (hmax <= history_lines_in_file)
> -+ history_lines_in_file = hmax;
> -+ }
> -+ }
> -+ }
> -+ else if (name[4] == 'S')
> -+ unstifle_history ();
> -+}
> -+
> -+/* What to do after the HISTIGNORE variable changes. */
> -+void
> -+sv_histignore (name)
> -+ char *name;
> -+{
> -+ setup_history_ignore (name);
> -+}
> -+
> -+/* What to do after the HISTCONTROL variable changes. */
> -+void
> -+sv_history_control (name)
> -+ char *name;
> -+{
> -+ char *temp;
> -+ char *val;
> -+ int tptr;
> -+
> -+ history_control = 0;
> -+ temp = get_string_value (name);
> -+
> -+ if (temp == 0 || *temp == 0)
> -+ return;
> -+
> -+ tptr = 0;
> -+ while (val = extract_colon_unit (temp, &tptr))
> -+ {
> -+ if (STREQ (val, "ignorespace"))
> -+ history_control |= HC_IGNSPACE;
> -+ else if (STREQ (val, "ignoredups"))
> -+ history_control |= HC_IGNDUPS;
> -+ else if (STREQ (val, "ignoreboth"))
> -+ history_control |= HC_IGNBOTH;
> -+ else if (STREQ (val, "erasedups"))
> -+ history_control |= HC_ERASEDUPS;
> -+
> -+ free (val);
> -+ }
> -+}
> -+
> -+#if defined (BANG_HISTORY)
> -+/* Setting/unsetting of the history expansion character. */
> -+void
> -+sv_histchars (name)
> -+ char *name;
> -+{
> -+ char *temp;
> -+
> -+ temp = get_string_value (name);
> -+ if (temp)
> -+ {
> -+ history_expansion_char = *temp;
> -+ if (temp[0] && temp[1])
> -+ {
> -+ history_subst_char = temp[1];
> -+ if (temp[2])
> -+ history_comment_char = temp[2];
> -+ }
> -+ }
> -+ else
> -+ {
> -+ history_expansion_char = '!';
> -+ history_subst_char = '^';
> -+ history_comment_char = '#';
> -+ }
> -+}
> -+#endif /* BANG_HISTORY */
> -+
> -+void
> -+sv_histtimefmt (name)
> -+ char *name;
> -+{
> -+ SHELL_VAR *v;
> -+
> -+ if (v = find_variable (name))
> -+ {
> -+ if (history_comment_char == 0)
> -+ history_comment_char = '#';
> -+ }
> -+ history_write_timestamps = (v != 0);
> -+}
> -+#endif /* HISTORY */
> -+
> -+#if defined (HAVE_TZSET)
> -+void
> -+sv_tz (name)
> -+ char *name;
> -+{
> -+ if (chkexport (name))
> -+ tzset ();
> -+}
> -+#endif
> -+
> -+/* If the variable exists, then the value of it can be the number
> -+ of times we actually ignore the EOF. The default is small,
> -+ (smaller than csh, anyway). */
> -+void
> -+sv_ignoreeof (name)
> -+ char *name;
> -+{
> -+ SHELL_VAR *tmp_var;
> -+ char *temp;
> -+
> -+ eof_encountered = 0;
> -+
> -+ tmp_var = find_variable (name);
> -+ ignoreeof = tmp_var != 0;
> -+ temp = tmp_var ? value_cell (tmp_var) : (char *)NULL;
> -+ if (temp)
> -+ eof_encountered_limit = (*temp && all_digits (temp)) ? atoi (temp) : 10;
> -+ set_shellopts (); /* make sure `ignoreeof' is/is not in $SHELLOPTS */
> -+}
> -+
> -+void
> -+sv_optind (name)
> -+ char *name;
> -+{
> -+ char *tt;
> -+ int s;
> -+
> -+ tt = get_string_value ("OPTIND");
> -+ if (tt && *tt)
> -+ {
> -+ s = atoi (tt);
> -+
> -+ /* According to POSIX, setting OPTIND=1 resets the internal state
> -+ of getopt (). */
> -+ if (s < 0 || s == 1)
> -+ s = 0;
> -+ }
> -+ else
> -+ s = 0;
> -+ getopts_reset (s);
> -+}
> -+
> -+void
> -+sv_opterr (name)
> -+ char *name;
> -+{
> -+ char *tt;
> -+
> -+ tt = get_string_value ("OPTERR");
> -+ sh_opterr = (tt && *tt) ? atoi (tt) : 1;
> -+}
> -+
> -+void
> -+sv_strict_posix (name)
> -+ char *name;
> -+{
> -+ SET_INT_VAR (name, posixly_correct);
> -+ posix_initialize (posixly_correct);
> -+#if defined (READLINE)
> -+ if (interactive_shell)
> -+ posix_readline_initialize (posixly_correct);
> -+#endif /* READLINE */
> -+ set_shellopts (); /* make sure `posix' is/is not in $SHELLOPTS */
> -+}
> -+
> -+void
> -+sv_locale (name)
> -+ char *name;
> -+{
> -+ char *v;
> -+ int r;
> -+
> -+ v = get_string_value (name);
> -+ if (name[0] == 'L' && name[1] == 'A') /* LANG */
> -+ r = set_lang (name, v);
> -+ else
> -+ r = set_locale_var (name, v); /* LC_*, TEXTDOMAIN* */
> -+
> -+#if 1
> -+ if (r == 0 && posixly_correct)
> -+ last_command_exit_value = 1;
> -+#endif
> -+}
> -+
> -+#if defined (ARRAY_VARS)
> -+void
> -+set_pipestatus_array (ps, nproc)
> -+ int *ps;
> -+ int nproc;
> -+{
> -+ SHELL_VAR *v;
> -+ ARRAY *a;
> -+ ARRAY_ELEMENT *ae;
> -+ register int i;
> -+ char *t, tbuf[INT_STRLEN_BOUND(int) + 1];
> -+
> -+ v = find_variable ("PIPESTATUS");
> -+ if (v == 0)
> -+ v = make_new_array_variable ("PIPESTATUS");
> -+ if (array_p (v) == 0)
> -+ return; /* Do nothing if not an array variable. */
> -+ a = array_cell (v);
> -+
> -+ if (a == 0 || array_num_elements (a) == 0)
> -+ {
> -+ for (i = 0; i < nproc; i++) /* was ps[i] != -1, not i < nproc */
> -+ {
> -+ t = inttostr (ps[i], tbuf, sizeof (tbuf));
> -+ array_insert (a, i, t);
> -+ }
> -+ return;
> -+ }
> -+
> -+ /* Fast case */
> -+ if (array_num_elements (a) == nproc && nproc == 1)
> -+ {
> -+ ae = element_forw (a->head);
> -+ free (element_value (ae));
> -+ ae->value = itos (ps[0]);
> -+ }
> -+ else if (array_num_elements (a) <= nproc)
> -+ {
> -+ /* modify in array_num_elements members in place, then add */
> -+ ae = a->head;
> -+ for (i = 0; i < array_num_elements (a); i++)
> -+ {
> -+ ae = element_forw (ae);
> -+ free (element_value (ae));
> -+ ae->value = itos (ps[i]);
> -+ }
> -+ /* add any more */
> -+ for ( ; i < nproc; i++)
> -+ {
> -+ t = inttostr (ps[i], tbuf, sizeof (tbuf));
> -+ array_insert (a, i, t);
> -+ }
> -+ }
> -+ else
> -+ {
> -+ /* deleting elements. it's faster to rebuild the array. */
> -+ array_flush (a);
> -+ for (i = 0; ps[i] != -1; i++)
> -+ {
> -+ t = inttostr (ps[i], tbuf, sizeof (tbuf));
> -+ array_insert (a, i, t);
> -+ }
> -+ }
> -+}
> -+
> -+ARRAY *
> -+save_pipestatus_array ()
> -+{
> -+ SHELL_VAR *v;
> -+ ARRAY *a, *a2;
> -+
> -+ v = find_variable ("PIPESTATUS");
> -+ if (v == 0 || array_p (v) == 0 || array_cell (v) == 0)
> -+ return ((ARRAY *)NULL);
> -+
> -+ a = array_cell (v);
> -+ a2 = array_copy (array_cell (v));
> -+
> -+ return a2;
> -+}
> -+
> -+void
> -+restore_pipestatus_array (a)
> -+ ARRAY *a;
> -+{
> -+ SHELL_VAR *v;
> -+ ARRAY *a2;
> -+
> -+ v = find_variable ("PIPESTATUS");
> -+ /* XXX - should we still assign even if existing value is NULL? */
> -+ if (v == 0 || array_p (v) == 0 || array_cell (v) == 0)
> -+ return;
> -+
> -+ a2 = array_cell (v);
> -+ var_setarray (v, a);
> -+
> -+ array_dispose (a2);
> -+}
> -+#endif
> -+
> -+void
> -+set_pipestatus_from_exit (s)
> -+ int s;
> -+{
> -+#if defined (ARRAY_VARS)
> -+ static int v[2] = { 0, -1 };
> -+
> -+ v[0] = s;
> -+ set_pipestatus_array (v, 1);
> -+#endif
> -+}
> -+
> -+void
> -+sv_xtracefd (name)
> -+ char *name;
> -+{
> -+ SHELL_VAR *v;
> -+ char *t, *e;
> -+ int fd;
> -+ FILE *fp;
> -+
> -+ v = find_variable (name);
> -+ if (v == 0)
> -+ {
> -+ xtrace_reset ();
> -+ return;
> -+ }
> -+
> -+ t = value_cell (v);
> -+ if (t == 0 || *t == 0)
> -+ xtrace_reset ();
> -+ else
> -+ {
> -+ fd = (int)strtol (t, &e, 10);
> -+ if (e != t && *e == '\0' && sh_validfd (fd))
> -+ {
> -+ fp = fdopen (fd, "w");
> -+ if (fp == 0)
> -+ internal_error (_("%s: %s: cannot open as FILE"), name, value_cell (v));
> -+ else
> -+ xtrace_set (fd, fp);
> -+ }
> -+ else
> -+ internal_error (_("%s: %s: invalid value for trace file descriptor"), name, value_cell (v));
> -+ }
> -+}
> -+
> -+#define MIN_COMPAT_LEVEL 31
> -+
> -+void
> -+sv_shcompat (name)
> -+ char *name;
> -+{
> -+ SHELL_VAR *v;
> -+ char *val;
> -+ int tens, ones, compatval;
> -+
> -+ v = find_variable (name);
> -+ if (v == 0)
> -+ {
> -+ shell_compatibility_level = DEFAULT_COMPAT_LEVEL;
> -+ set_compatibility_opts ();
> -+ return;
> -+ }
> -+ val = value_cell (v);
> -+ if (val == 0 || *val == '\0')
> -+ {
> -+ shell_compatibility_level = DEFAULT_COMPAT_LEVEL;
> -+ set_compatibility_opts ();
> -+ return;
> -+ }
> -+ /* Handle decimal-like compatibility version specifications: 4.2 */
> -+ if (isdigit (val[0]) && val[1] == '.' && isdigit (val[2]) && val[3] == 0)
> -+ {
> -+ tens = val[0] - '0';
> -+ ones = val[2] - '0';
> -+ compatval = tens*10 + ones;
> -+ }
> -+ /* Handle integer-like compatibility version specifications: 42 */
> -+ else if (isdigit (val[0]) && isdigit (val[1]) && val[2] == 0)
> -+ {
> -+ tens = val[0] - '0';
> -+ ones = val[1] - '0';
> -+ compatval = tens*10 + ones;
> -+ }
> -+ else
> -+ {
> -+compat_error:
> -+ internal_error (_("%s: %s: compatibility value out of range"), name, val);
> -+ shell_compatibility_level = DEFAULT_COMPAT_LEVEL;
> -+ set_compatibility_opts ();
> -+ return;
> -+ }
> -+
> -+ if (compatval < MIN_COMPAT_LEVEL || compatval > DEFAULT_COMPAT_LEVEL)
> -+ goto compat_error;
> -+
> -+ shell_compatibility_level = compatval;
> -+ set_compatibility_opts ();
> -+}
> -+
> -+#if defined (JOB_CONTROL)
> -+void
> -+sv_childmax (name)
> -+ char *name;
> -+{
> -+ char *tt;
> -+ int s;
> -+
> -+ tt = get_string_value (name);
> -+ s = (tt && *tt) ? atoi (tt) : 0;
> -+ set_maxchild (s);
> -+}
> -+#endif
> diff --git a/patches/bash-4.3.30/0002-Bash-4.3-patch-32.patch b/patches/bash-4.3.30/0002-Bash-4.3-patch-32.patch
> deleted file mode 100644
> index 801b4a609..000000000
> --- a/patches/bash-4.3.30/0002-Bash-4.3-patch-32.patch
> +++ /dev/null
> @@ -1,5409 +0,0 @@
> -From: Chet Ramey <chet.ramey@case.edu>
> -Date: Thu, 15 Jan 2015 10:20:45 -0500
> -Subject: [PATCH] Bash-4.3 patch 32
> -
> ----
> - jobs.c | 4 +-
> - patchlevel.h | 2 +-
> - variables.c.orig | 5365 ------------------------------------------------------
> - 3 files changed, 4 insertions(+), 5367 deletions(-)
> - delete mode 100644 variables.c.orig
> -
> -diff --git a/jobs.c b/jobs.c
> -index f38b0c3f4446..b6e59eba0de8 100644
> ---- a/jobs.c
> -+++ b/jobs.c
> -@@ -3339,7 +3339,9 @@ itrace("waitchld: waitpid returns %d block = %d", pid, block);
> - if (posixly_correct && this_shell_builtin && this_shell_builtin == wait_builtin)
> - {
> - interrupt_immediately = 0;
> -- trap_handler (SIGCHLD); /* set pending_traps[SIGCHLD] */
> -+ /* This was trap_handler (SIGCHLD) but that can lose traps if
> -+ children_exited > 1 */
> -+ queue_sigchld_trap (children_exited);
> - wait_signal_received = SIGCHLD;
> - /* If we're in a signal handler, let CHECK_WAIT_INTR pick it up;
> - run_pending_traps will call run_sigchld_trap later */
> -diff --git a/patchlevel.h b/patchlevel.h
> -index 0ad46aafbdd9..b8bf38704ed2 100644
> ---- a/patchlevel.h
> -+++ b/patchlevel.h
> -@@ -25,6 +25,6 @@
> - regexp `^#define[ ]*PATCHLEVEL', since that's what support/mkversion.sh
> - looks for to find the patch level (for the sccs version string). */
> -
> --#define PATCHLEVEL 31
> -+#define PATCHLEVEL 32
> -
> - #endif /* _PATCHLEVEL_H_ */
> -diff --git a/variables.c.orig b/variables.c.orig
> -deleted file mode 100644
> -index 7c82710e0f0b..000000000000
> ---- a/variables.c.orig
> -+++ /dev/null
> -@@ -1,5365 +0,0 @@
> --/* variables.c -- Functions for hacking shell variables. */
> --
> --/* Copyright (C) 1987-2013 Free Software Foundation, Inc.
> --
> -- This file is part of GNU Bash, the Bourne Again SHell.
> --
> -- Bash is free software: you can redistribute it and/or modify
> -- it under the terms of the GNU General Public License as published by
> -- the Free Software Foundation, either version 3 of the License, or
> -- (at your option) any later version.
> --
> -- Bash is distributed in the hope that it will be useful,
> -- but WITHOUT ANY WARRANTY; without even the implied warranty of
> -- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
> -- GNU General Public License for more details.
> --
> -- You should have received a copy of the GNU General Public License
> -- along with Bash. If not, see <http://www.gnu.org/licenses/>.
> --*/
> --
> --#include "config.h"
> --
> --#include "bashtypes.h"
> --#include "posixstat.h"
> --#include "posixtime.h"
> --
> --#if defined (__QNX__)
> --# if defined (__QNXNTO__)
> --# include <sys/netmgr.h>
> --# else
> --# include <sys/vc.h>
> --# endif /* !__QNXNTO__ */
> --#endif /* __QNX__ */
> --
> --#if defined (HAVE_UNISTD_H)
> --# include <unistd.h>
> --#endif
> --
> --#include <stdio.h>
> --#include "chartypes.h"
> --#if defined (HAVE_PWD_H)
> --# include <pwd.h>
> --#endif
> --#include "bashansi.h"
> --#include "bashintl.h"
> --
> --#define NEED_XTRACE_SET_DECL
> --
> --#include "shell.h"
> --#include "flags.h"
> --#include "execute_cmd.h"
> --#include "findcmd.h"
> --#include "mailcheck.h"
> --#include "input.h"
> --#include "hashcmd.h"
> --#include "pathexp.h"
> --#include "alias.h"
> --#include "jobs.h"
> --
> --#include "version.h"
> --
> --#include "builtins/getopt.h"
> --#include "builtins/common.h"
> --#include "builtins/builtext.h"
> --
> --#if defined (READLINE)
> --# include "bashline.h"
> --# include <readline/readline.h>
> --#else
> --# include <tilde/tilde.h>
> --#endif
> --
> --#if defined (HISTORY)
> --# include "bashhist.h"
> --# include <readline/history.h>
> --#endif /* HISTORY */
> --
> --#if defined (PROGRAMMABLE_COMPLETION)
> --# include "pcomplete.h"
> --#endif
> --
> --#define TEMPENV_HASH_BUCKETS 4 /* must be power of two */
> --
> --#define ifsname(s) ((s)[0] == 'I' && (s)[1] == 'F' && (s)[2] == 'S' && (s)[3] == '\0')
> --
> --#define BASHFUNC_PREFIX "BASH_FUNC_"
> --#define BASHFUNC_PREFLEN 10 /* == strlen(BASHFUNC_PREFIX */
> --#define BASHFUNC_SUFFIX "%%"
> --#define BASHFUNC_SUFFLEN 2 /* == strlen(BASHFUNC_SUFFIX) */
> --
> --extern char **environ;
> --
> --/* Variables used here and defined in other files. */
> --extern int posixly_correct;
> --extern int line_number, line_number_base;
> --extern int subshell_environment, indirection_level, subshell_level;
> --extern int build_version, patch_level;
> --extern int expanding_redir;
> --extern int last_command_exit_value;
> --extern char *dist_version, *release_status;
> --extern char *shell_name;
> --extern char *primary_prompt, *secondary_prompt;
> --extern char *current_host_name;
> --extern sh_builtin_func_t *this_shell_builtin;
> --extern SHELL_VAR *this_shell_function;
> --extern char *the_printed_command_except_trap;
> --extern char *this_command_name;
> --extern char *command_execution_string;
> --extern time_t shell_start_time;
> --extern int assigning_in_environment;
> --extern int executing_builtin;
> --extern int funcnest_max;
> --
> --#if defined (READLINE)
> --extern int no_line_editing;
> --extern int perform_hostname_completion;
> --#endif
> --
> --/* The list of shell variables that the user has created at the global
> -- scope, or that came from the environment. */
> --VAR_CONTEXT *global_variables = (VAR_CONTEXT *)NULL;
> --
> --/* The current list of shell variables, including function scopes */
> --VAR_CONTEXT *shell_variables = (VAR_CONTEXT *)NULL;
> --
> --/* The list of shell functions that the user has created, or that came from
> -- the environment. */
> --HASH_TABLE *shell_functions = (HASH_TABLE *)NULL;
> --
> --#if defined (DEBUGGER)
> --/* The table of shell function definitions that the user defined or that
> -- came from the environment. */
> --HASH_TABLE *shell_function_defs = (HASH_TABLE *)NULL;
> --#endif
> --
> --/* The current variable context. This is really a count of how deep into
> -- executing functions we are. */
> --int variable_context = 0;
> --
> --/* The set of shell assignments which are made only in the environment
> -- for a single command. */
> --HASH_TABLE *temporary_env = (HASH_TABLE *)NULL;
> --
> --/* Set to non-zero if an assignment error occurs while putting variables
> -- into the temporary environment. */
> --int tempenv_assign_error;
> --
> --/* Some funky variables which are known about specially. Here is where
> -- "$*", "$1", and all the cruft is kept. */
> --char *dollar_vars[10];
> --WORD_LIST *rest_of_args = (WORD_LIST *)NULL;
> --
> --/* The value of $$. */
> --pid_t dollar_dollar_pid;
> --
> --/* Non-zero means that we have to remake EXPORT_ENV. */
> --int array_needs_making = 1;
> --
> --/* The number of times BASH has been executed. This is set
> -- by initialize_variables (). */
> --int shell_level = 0;
> --
> --/* An array which is passed to commands as their environment. It is
> -- manufactured from the union of the initial environment and the
> -- shell variables that are marked for export. */
> --char **export_env = (char **)NULL;
> --static int export_env_index;
> --static int export_env_size;
> --
> --#if defined (READLINE)
> --static int winsize_assignment; /* currently assigning to LINES or COLUMNS */
> --#endif
> --
> --static HASH_TABLE *last_table_searched; /* hash_lookup sets this */
> --
> --/* Some forward declarations. */
> --static void create_variable_tables __P((void));
> --
> --static void set_machine_vars __P((void));
> --static void set_home_var __P((void));
> --static void set_shell_var __P((void));
> --static char *get_bash_name __P((void));
> --static void initialize_shell_level __P((void));
> --static void uidset __P((void));
> --#if defined (ARRAY_VARS)
> --static void make_vers_array __P((void));
> --#endif
> --
> --static SHELL_VAR *null_assign __P((SHELL_VAR *, char *, arrayind_t, char *));
> --#if defined (ARRAY_VARS)
> --static SHELL_VAR *null_array_assign __P((SHELL_VAR *, char *, arrayind_t, char *));
> --#endif
> --static SHELL_VAR *get_self __P((SHELL_VAR *));
> --
> --#if defined (ARRAY_VARS)
> --static SHELL_VAR *init_dynamic_array_var __P((char *, sh_var_value_func_t *, sh_var_assign_func_t *, int));
> --static SHELL_VAR *init_dynamic_assoc_var __P((char *, sh_var_value_func_t *, sh_var_assign_func_t *, int));
> --#endif
> --
> --static SHELL_VAR *assign_seconds __P((SHELL_VAR *, char *, arrayind_t, char *));
> --static SHELL_VAR *get_seconds __P((SHELL_VAR *));
> --static SHELL_VAR *init_seconds_var __P((void));
> --
> --static int brand __P((void));
> --static void sbrand __P((unsigned long)); /* set bash random number generator. */
> --static void seedrand __P((void)); /* seed generator randomly */
> --static SHELL_VAR *assign_random __P((SHELL_VAR *, char *, arrayind_t, char *));
> --static SHELL_VAR *get_random __P((SHELL_VAR *));
> --
> --static SHELL_VAR *assign_lineno __P((SHELL_VAR *, char *, arrayind_t, char *));
> --static SHELL_VAR *get_lineno __P((SHELL_VAR *));
> --
> --static SHELL_VAR *assign_subshell __P((SHELL_VAR *, char *, arrayind_t, char *));
> --static SHELL_VAR *get_subshell __P((SHELL_VAR *));
> --
> --static SHELL_VAR *get_bashpid __P((SHELL_VAR *));
> --
> --#if defined (HISTORY)
> --static SHELL_VAR *get_histcmd __P((SHELL_VAR *));
> --#endif
> --
> --#if defined (READLINE)
> --static SHELL_VAR *get_comp_wordbreaks __P((SHELL_VAR *));
> --static SHELL_VAR *assign_comp_wordbreaks __P((SHELL_VAR *, char *, arrayind_t, char *));
> --#endif
> --
> --#if defined (PUSHD_AND_POPD) && defined (ARRAY_VARS)
> --static SHELL_VAR *assign_dirstack __P((SHELL_VAR *, char *, arrayind_t, char *));
> --static SHELL_VAR *get_dirstack __P((SHELL_VAR *));
> --#endif
> --
> --#if defined (ARRAY_VARS)
> --static SHELL_VAR *get_groupset __P((SHELL_VAR *));
> --
> --static SHELL_VAR *build_hashcmd __P((SHELL_VAR *));
> --static SHELL_VAR *get_hashcmd __P((SHELL_VAR *));
> --static SHELL_VAR *assign_hashcmd __P((SHELL_VAR *, char *, arrayind_t, char *));
> --# if defined (ALIAS)
> --static SHELL_VAR *build_aliasvar __P((SHELL_VAR *));
> --static SHELL_VAR *get_aliasvar __P((SHELL_VAR *));
> --static SHELL_VAR *assign_aliasvar __P((SHELL_VAR *, char *, arrayind_t, char *));
> --# endif
> --#endif
> --
> --static SHELL_VAR *get_funcname __P((SHELL_VAR *));
> --static SHELL_VAR *init_funcname_var __P((void));
> --
> --static void initialize_dynamic_variables __P((void));
> --
> --static SHELL_VAR *hash_lookup __P((const char *, HASH_TABLE *));
> --static SHELL_VAR *new_shell_variable __P((const char *));
> --static SHELL_VAR *make_new_variable __P((const char *, HASH_TABLE *));
> --static SHELL_VAR *bind_variable_internal __P((const char *, char *, HASH_TABLE *, int, int));
> --
> --static void dispose_variable_value __P((SHELL_VAR *));
> --static void free_variable_hash_data __P((PTR_T));
> --
> --static VARLIST *vlist_alloc __P((int));
> --static VARLIST *vlist_realloc __P((VARLIST *, int));
> --static void vlist_add __P((VARLIST *, SHELL_VAR *, int));
> --
> --static void flatten __P((HASH_TABLE *, sh_var_map_func_t *, VARLIST *, int));
> --
> --static int qsort_var_comp __P((SHELL_VAR **, SHELL_VAR **));
> --
> --static SHELL_VAR **vapply __P((sh_var_map_func_t *));
> --static SHELL_VAR **fapply __P((sh_var_map_func_t *));
> --
> --static int visible_var __P((SHELL_VAR *));
> --static int visible_and_exported __P((SHELL_VAR *));
> --static int export_environment_candidate __P((SHELL_VAR *));
> --static int local_and_exported __P((SHELL_VAR *));
> --static int variable_in_context __P((SHELL_VAR *));
> --#if defined (ARRAY_VARS)
> --static int visible_array_vars __P((SHELL_VAR *));
> --#endif
> --
> --static SHELL_VAR *find_nameref_at_context __P((SHELL_VAR *, VAR_CONTEXT *));
> --static SHELL_VAR *find_variable_nameref_context __P((SHELL_VAR *, VAR_CONTEXT *, VAR_CONTEXT **));
> --static SHELL_VAR *find_variable_last_nameref_context __P((SHELL_VAR *, VAR_CONTEXT *, VAR_CONTEXT **));
> --
> --static SHELL_VAR *bind_tempenv_variable __P((const char *, char *));
> --static void push_temp_var __P((PTR_T));
> --static void propagate_temp_var __P((PTR_T));
> --static void dispose_temporary_env __P((sh_free_func_t *));
> --
> --static inline char *mk_env_string __P((const char *, const char *, int));
> --static char **make_env_array_from_var_list __P((SHELL_VAR **));
> --static char **make_var_export_array __P((VAR_CONTEXT *));
> --static char **make_func_export_array __P((void));
> --static void add_temp_array_to_env __P((char **, int, int));
> --
> --static int n_shell_variables __P((void));
> --static int set_context __P((SHELL_VAR *));
> --
> --static void push_func_var __P((PTR_T));
> --static void push_exported_var __P((PTR_T));
> --
> --static inline int find_special_var __P((const char *));
> --
> --static void
> --create_variable_tables ()
> --{
> -- if (shell_variables == 0)
> -- {
> -- shell_variables = global_variables = new_var_context ((char *)NULL, 0);
> -- shell_variables->scope = 0;
> -- shell_variables->table = hash_create (0);
> -- }
> --
> -- if (shell_functions == 0)
> -- shell_functions = hash_create (0);
> --
> --#if defined (DEBUGGER)
> -- if (shell_function_defs == 0)
> -- shell_function_defs = hash_create (0);
> --#endif
> --}
> --
> --/* Initialize the shell variables from the current environment.
> -- If PRIVMODE is nonzero, don't import functions from ENV or
> -- parse $SHELLOPTS. */
> --void
> --initialize_shell_variables (env, privmode)
> -- char **env;
> -- int privmode;
> --{
> -- char *name, *string, *temp_string;
> -- int c, char_index, string_index, string_length, ro;
> -- SHELL_VAR *temp_var;
> --
> -- create_variable_tables ();
> --
> -- for (string_index = 0; string = env[string_index++]; )
> -- {
> -- char_index = 0;
> -- name = string;
> -- while ((c = *string++) && c != '=')
> -- ;
> -- if (string[-1] == '=')
> -- char_index = string - name - 1;
> --
> -- /* If there are weird things in the environment, like `=xxx' or a
> -- string without an `=', just skip them. */
> -- if (char_index == 0)
> -- continue;
> --
> -- /* ASSERT(name[char_index] == '=') */
> -- name[char_index] = '\0';
> -- /* Now, name = env variable name, string = env variable value, and
> -- char_index == strlen (name) */
> --
> -- temp_var = (SHELL_VAR *)NULL;
> --
> -- /* If exported function, define it now. Don't import functions from
> -- the environment in privileged mode. */
> -- if (privmode == 0 && read_but_dont_execute == 0 &&
> -- STREQN (BASHFUNC_PREFIX, name, BASHFUNC_PREFLEN) &&
> -- STREQ (BASHFUNC_SUFFIX, name + char_index - BASHFUNC_SUFFLEN) &&
> -- STREQN ("() {", string, 4))
> -- {
> -- size_t namelen;
> -- char *tname; /* desired imported function name */
> --
> -- namelen = char_index - BASHFUNC_PREFLEN - BASHFUNC_SUFFLEN;
> --
> -- tname = name + BASHFUNC_PREFLEN; /* start of func name */
> -- tname[namelen] = '\0'; /* now tname == func name */
> --
> -- string_length = strlen (string);
> -- temp_string = (char *)xmalloc (namelen + string_length + 2);
> --
> -- memcpy (temp_string, tname, namelen);
> -- temp_string[namelen] = ' ';
> -- memcpy (temp_string + namelen + 1, string, string_length + 1);
> --
> -- /* Don't import function names that are invalid identifiers from the
> -- environment, though we still allow them to be defined as shell
> -- variables. */
> -- if (absolute_program (tname) == 0 && (posixly_correct == 0 || legal_identifier (tname)))
> -- parse_and_execute (temp_string, tname, SEVAL_NONINT|SEVAL_NOHIST|SEVAL_FUNCDEF|SEVAL_ONECMD);
> --
> -- if (temp_var = find_function (tname))
> -- {
> -- VSETATTR (temp_var, (att_exported|att_imported));
> -- array_needs_making = 1;
> -- }
> -- else
> -- {
> -- if (temp_var = bind_variable (name, string, 0))
> -- {
> -- VSETATTR (temp_var, (att_exported | att_imported | att_invisible));
> -- array_needs_making = 1;
> -- }
> -- last_command_exit_value = 1;
> -- report_error (_("error importing function definition for `%s'"), tname);
> -- }
> --
> -- /* Restore original suffix */
> -- tname[namelen] = BASHFUNC_SUFFIX[0];
> -- }
> --#if defined (ARRAY_VARS)
> --# if ARRAY_EXPORT
> -- /* Array variables may not yet be exported. */
> -- else if (*string == '(' && string[1] == '[' && string[strlen (string) - 1] == ')')
> -- {
> -- string_length = 1;
> -- temp_string = extract_array_assignment_list (string, &string_length);
> -- temp_var = assign_array_from_string (name, temp_string);
> -- FREE (temp_string);
> -- VSETATTR (temp_var, (att_exported | att_imported));
> -- array_needs_making = 1;
> -- }
> --# endif /* ARRAY_EXPORT */
> --#endif
> --#if 0
> -- else if (legal_identifier (name))
> --#else
> -- else
> --#endif
> -- {
> -- ro = 0;
> -- if (posixly_correct && STREQ (name, "SHELLOPTS"))
> -- {
> -- temp_var = find_variable ("SHELLOPTS");
> -- ro = temp_var && readonly_p (temp_var);
> -- if (temp_var)
> -- VUNSETATTR (temp_var, att_readonly);
> -- }
> -- temp_var = bind_variable (name, string, 0);
> -- if (temp_var)
> -- {
> -- if (legal_identifier (name))
> -- VSETATTR (temp_var, (att_exported | att_imported));
> -- else
> -- VSETATTR (temp_var, (att_exported | att_imported | att_invisible));
> -- if (ro)
> -- VSETATTR (temp_var, att_readonly);
> -- array_needs_making = 1;
> -- }
> -- }
> --
> -- name[char_index] = '=';
> -- /* temp_var can be NULL if it was an exported function with a syntax
> -- error (a different bug, but it still shouldn't dump core). */
> -- if (temp_var && function_p (temp_var) == 0) /* XXX not yet */
> -- {
> -- CACHE_IMPORTSTR (temp_var, name);
> -- }
> -- }
> --
> -- set_pwd ();
> --
> -- /* Set up initial value of $_ */
> -- temp_var = set_if_not ("_", dollar_vars[0]);
> --
> -- /* Remember this pid. */
> -- dollar_dollar_pid = getpid ();
> --
> -- /* Now make our own defaults in case the vars that we think are
> -- important are missing. */
> -- temp_var = set_if_not ("PATH", DEFAULT_PATH_VALUE);
> --#if 0
> -- set_auto_export (temp_var); /* XXX */
> --#endif
> --
> -- temp_var = set_if_not ("TERM", "dumb");
> --#if 0
> -- set_auto_export (temp_var); /* XXX */
> --#endif
> --
> --#if defined (__QNX__)
> -- /* set node id -- don't import it from the environment */
> -- {
> -- char node_name[22];
> --# if defined (__QNXNTO__)
> -- netmgr_ndtostr(ND2S_LOCAL_STR, ND_LOCAL_NODE, node_name, sizeof(node_name));
> --# else
> -- qnx_nidtostr (getnid (), node_name, sizeof (node_name));
> --# endif
> -- temp_var = bind_variable ("NODE", node_name, 0);
> -- set_auto_export (temp_var);
> -- }
> --#endif
> --
> -- /* set up the prompts. */
> -- if (interactive_shell)
> -- {
> --#if defined (PROMPT_STRING_DECODE)
> -- set_if_not ("PS1", primary_prompt);
> --#else
> -- if (current_user.uid == -1)
> -- get_current_user_info ();
> -- set_if_not ("PS1", current_user.euid == 0 ? "# " : primary_prompt);
> --#endif
> -- set_if_not ("PS2", secondary_prompt);
> -- }
> -- set_if_not ("PS4", "+ ");
> --
> -- /* Don't allow IFS to be imported from the environment. */
> -- temp_var = bind_variable ("IFS", " \t\n", 0);
> -- setifs (temp_var);
> --
> -- /* Magic machine types. Pretty convenient. */
> -- set_machine_vars ();
> --
> -- /* Default MAILCHECK for interactive shells. Defer the creation of a
> -- default MAILPATH until the startup files are read, because MAIL
> -- names a mail file if MAILPATH is not set, and we should provide a
> -- default only if neither is set. */
> -- if (interactive_shell)
> -- {
> -- temp_var = set_if_not ("MAILCHECK", posixly_correct ? "600" : "60");
> -- VSETATTR (temp_var, att_integer);
> -- }
> --
> -- /* Do some things with shell level. */
> -- initialize_shell_level ();
> --
> -- set_ppid ();
> --
> -- /* Initialize the `getopts' stuff. */
> -- temp_var = bind_variable ("OPTIND", "1", 0);
> -- VSETATTR (temp_var, att_integer);
> -- getopts_reset (0);
> -- bind_variable ("OPTERR", "1", 0);
> -- sh_opterr = 1;
> --
> -- if (login_shell == 1 && posixly_correct == 0)
> -- set_home_var ();
> --
> -- /* Get the full pathname to THIS shell, and set the BASH variable
> -- to it. */
> -- name = get_bash_name ();
> -- temp_var = bind_variable ("BASH", name, 0);
> -- free (name);
> --
> -- /* Make the exported environment variable SHELL be the user's login
> -- shell. Note that the `tset' command looks at this variable
> -- to determine what style of commands to output; if it ends in "csh",
> -- then C-shell commands are output, else Bourne shell commands. */
> -- set_shell_var ();
> --
> -- /* Make a variable called BASH_VERSION which contains the version info. */
> -- bind_variable ("BASH_VERSION", shell_version_string (), 0);
> --#if defined (ARRAY_VARS)
> -- make_vers_array ();
> --#endif
> --
> -- if (command_execution_string)
> -- bind_variable ("BASH_EXECUTION_STRING", command_execution_string, 0);
> --
> -- /* Find out if we're supposed to be in Posix.2 mode via an
> -- environment variable. */
> -- temp_var = find_variable ("POSIXLY_CORRECT");
> -- if (!temp_var)
> -- temp_var = find_variable ("POSIX_PEDANTIC");
> -- if (temp_var && imported_p (temp_var))
> -- sv_strict_posix (temp_var->name);
> --
> --#if defined (HISTORY)
> -- /* Set history variables to defaults, and then do whatever we would
> -- do if the variable had just been set. Do this only in the case
> -- that we are remembering commands on the history list. */
> -- if (remember_on_history)
> -- {
> -- name = bash_tilde_expand (posixly_correct ? "~/.sh_history" : "~/.bash_history", 0);
> --
> -- set_if_not ("HISTFILE", name);
> -- free (name);
> -- }
> --#endif /* HISTORY */
> --
> -- /* Seed the random number generator. */
> -- seedrand ();
> --
> -- /* Handle some "special" variables that we may have inherited from a
> -- parent shell. */
> -- if (interactive_shell)
> -- {
> -- temp_var = find_variable ("IGNOREEOF");
> -- if (!temp_var)
> -- temp_var = find_variable ("ignoreeof");
> -- if (temp_var && imported_p (temp_var))
> -- sv_ignoreeof (temp_var->name);
> -- }
> --
> --#if defined (HISTORY)
> -- if (interactive_shell && remember_on_history)
> -- {
> -- sv_history_control ("HISTCONTROL");
> -- sv_histignore ("HISTIGNORE");
> -- sv_histtimefmt ("HISTTIMEFORMAT");
> -- }
> --#endif /* HISTORY */
> --
> --#if defined (READLINE) && defined (STRICT_POSIX)
> -- /* POSIXLY_CORRECT will only be 1 here if the shell was compiled
> -- -DSTRICT_POSIX */
> -- if (interactive_shell && posixly_correct && no_line_editing == 0)
> -- rl_prefer_env_winsize = 1;
> --#endif /* READLINE && STRICT_POSIX */
> --
> -- /*
> -- * 24 October 2001
> -- *
> -- * I'm tired of the arguing and bug reports. Bash now leaves SSH_CLIENT
> -- * and SSH2_CLIENT alone. I'm going to rely on the shell_level check in
> -- * isnetconn() to avoid running the startup files more often than wanted.
> -- * That will, of course, only work if the user's login shell is bash, so
> -- * I've made that behavior conditional on SSH_SOURCE_BASHRC being defined
> -- * in config-top.h.
> -- */
> --#if 0
> -- temp_var = find_variable ("SSH_CLIENT");
> -- if (temp_var && imported_p (temp_var))
> -- {
> -- VUNSETATTR (temp_var, att_exported);
> -- array_needs_making = 1;
> -- }
> -- temp_var = find_variable ("SSH2_CLIENT");
> -- if (temp_var && imported_p (temp_var))
> -- {
> -- VUNSETATTR (temp_var, att_exported);
> -- array_needs_making = 1;
> -- }
> --#endif
> --
> -- /* Get the user's real and effective user ids. */
> -- uidset ();
> --
> -- temp_var = find_variable ("BASH_XTRACEFD");
> -- if (temp_var && imported_p (temp_var))
> -- sv_xtracefd (temp_var->name);
> --
> -- /* Initialize the dynamic variables, and seed their values. */
> -- initialize_dynamic_variables ();
> --}
> --
> --/* **************************************************************** */
> --/* */
> --/* Setting values for special shell variables */
> --/* */
> --/* **************************************************************** */
> --
> --static void
> --set_machine_vars ()
> --{
> -- SHELL_VAR *temp_var;
> --
> -- temp_var = set_if_not ("HOSTTYPE", HOSTTYPE);
> -- temp_var = set_if_not ("OSTYPE", OSTYPE);
> -- temp_var = set_if_not ("MACHTYPE", MACHTYPE);
> --
> -- temp_var = set_if_not ("HOSTNAME", current_host_name);
> --}
> --
> --/* Set $HOME to the information in the password file if we didn't get
> -- it from the environment. */
> --
> --/* This function is not static so the tilde and readline libraries can
> -- use it. */
> --char *
> --sh_get_home_dir ()
> --{
> -- if (current_user.home_dir == 0)
> -- get_current_user_info ();
> -- return current_user.home_dir;
> --}
> --
> --static void
> --set_home_var ()
> --{
> -- SHELL_VAR *temp_var;
> --
> -- temp_var = find_variable ("HOME");
> -- if (temp_var == 0)
> -- temp_var = bind_variable ("HOME", sh_get_home_dir (), 0);
> --#if 0
> -- VSETATTR (temp_var, att_exported);
> --#endif
> --}
> --
> --/* Set $SHELL to the user's login shell if it is not already set. Call
> -- get_current_user_info if we haven't already fetched the shell. */
> --static void
> --set_shell_var ()
> --{
> -- SHELL_VAR *temp_var;
> --
> -- temp_var = find_variable ("SHELL");
> -- if (temp_var == 0)
> -- {
> -- if (current_user.shell == 0)
> -- get_current_user_info ();
> -- temp_var = bind_variable ("SHELL", current_user.shell, 0);
> -- }
> --#if 0
> -- VSETATTR (temp_var, att_exported);
> --#endif
> --}
> --
> --static char *
> --get_bash_name ()
> --{
> -- char *name;
> --
> -- if ((login_shell == 1) && RELPATH(shell_name))
> -- {
> -- if (current_user.shell == 0)
> -- get_current_user_info ();
> -- name = savestring (current_user.shell);
> -- }
> -- else if (ABSPATH(shell_name))
> -- name = savestring (shell_name);
> -- else if (shell_name[0] == '.' && shell_name[1] == '/')
> -- {
> -- /* Fast path for common case. */
> -- char *cdir;
> -- int len;
> --
> -- cdir = get_string_value ("PWD");
> -- if (cdir)
> -- {
> -- len = strlen (cdir);
> -- name = (char *)xmalloc (len + strlen (shell_name) + 1);
> -- strcpy (name, cdir);
> -- strcpy (name + len, shell_name + 1);
> -- }
> -- else
> -- name = savestring (shell_name);
> -- }
> -- else
> -- {
> -- char *tname;
> -- int s;
> --
> -- tname = find_user_command (shell_name);
> --
> -- if (tname == 0)
> -- {
> -- /* Try the current directory. If there is not an executable
> -- there, just punt and use the login shell. */
> -- s = file_status (shell_name);
> -- if (s & FS_EXECABLE)
> -- {
> -- tname = make_absolute (shell_name, get_string_value ("PWD"));
> -- if (*shell_name == '.')
> -- {
> -- name = sh_canonpath (tname, PATH_CHECKDOTDOT|PATH_CHECKEXISTS);
> -- if (name == 0)
> -- name = tname;
> -- else
> -- free (tname);
> -- }
> -- else
> -- name = tname;
> -- }
> -- else
> -- {
> -- if (current_user.shell == 0)
> -- get_current_user_info ();
> -- name = savestring (current_user.shell);
> -- }
> -- }
> -- else
> -- {
> -- name = full_pathname (tname);
> -- free (tname);
> -- }
> -- }
> --
> -- return (name);
> --}
> --
> --void
> --adjust_shell_level (change)
> -- int change;
> --{
> -- char new_level[5], *old_SHLVL;
> -- intmax_t old_level;
> -- SHELL_VAR *temp_var;
> --
> -- old_SHLVL = get_string_value ("SHLVL");
> -- if (old_SHLVL == 0 || *old_SHLVL == '\0' || legal_number (old_SHLVL, &old_level) == 0)
> -- old_level = 0;
> --
> -- shell_level = old_level + change;
> -- if (shell_level < 0)
> -- shell_level = 0;
> -- else if (shell_level > 1000)
> -- {
> -- internal_warning (_("shell level (%d) too high, resetting to 1"), shell_level);
> -- shell_level = 1;
> -- }
> --
> -- /* We don't need the full generality of itos here. */
> -- if (shell_level < 10)
> -- {
> -- new_level[0] = shell_level + '0';
> -- new_level[1] = '\0';
> -- }
> -- else if (shell_level < 100)
> -- {
> -- new_level[0] = (shell_level / 10) + '0';
> -- new_level[1] = (shell_level % 10) + '0';
> -- new_level[2] = '\0';
> -- }
> -- else if (shell_level < 1000)
> -- {
> -- new_level[0] = (shell_level / 100) + '0';
> -- old_level = shell_level % 100;
> -- new_level[1] = (old_level / 10) + '0';
> -- new_level[2] = (old_level % 10) + '0';
> -- new_level[3] = '\0';
> -- }
> --
> -- temp_var = bind_variable ("SHLVL", new_level, 0);
> -- set_auto_export (temp_var);
> --}
> --
> --static void
> --initialize_shell_level ()
> --{
> -- adjust_shell_level (1);
> --}
> --
> --/* If we got PWD from the environment, update our idea of the current
> -- working directory. In any case, make sure that PWD exists before
> -- checking it. It is possible for getcwd () to fail on shell startup,
> -- and in that case, PWD would be undefined. If this is an interactive
> -- login shell, see if $HOME is the current working directory, and if
> -- that's not the same string as $PWD, set PWD=$HOME. */
> --
> --void
> --set_pwd ()
> --{
> -- SHELL_VAR *temp_var, *home_var;
> -- char *temp_string, *home_string;
> --
> -- home_var = find_variable ("HOME");
> -- home_string = home_var ? value_cell (home_var) : (char *)NULL;
> --
> -- temp_var = find_variable ("PWD");
> -- if (temp_var && imported_p (temp_var) &&
> -- (temp_string = value_cell (temp_var)) &&
> -- same_file (temp_string, ".", (struct stat *)NULL, (struct stat *)NULL))
> -- set_working_directory (temp_string);
> -- else if (home_string && interactive_shell && login_shell &&
> -- same_file (home_string, ".", (struct stat *)NULL, (struct stat *)NULL))
> -- {
> -- set_working_directory (home_string);
> -- temp_var = bind_variable ("PWD", home_string, 0);
> -- set_auto_export (temp_var);
> -- }
> -- else
> -- {
> -- temp_string = get_working_directory ("shell-init");
> -- if (temp_string)
> -- {
> -- temp_var = bind_variable ("PWD", temp_string, 0);
> -- set_auto_export (temp_var);
> -- free (temp_string);
> -- }
> -- }
> --
> -- /* According to the Single Unix Specification, v2, $OLDPWD is an
> -- `environment variable' and therefore should be auto-exported.
> -- Make a dummy invisible variable for OLDPWD, and mark it as exported. */
> -- temp_var = bind_variable ("OLDPWD", (char *)NULL, 0);
> -- VSETATTR (temp_var, (att_exported | att_invisible));
> --}
> --
> --/* Make a variable $PPID, which holds the pid of the shell's parent. */
> --void
> --set_ppid ()
> --{
> -- char namebuf[INT_STRLEN_BOUND(pid_t) + 1], *name;
> -- SHELL_VAR *temp_var;
> --
> -- name = inttostr (getppid (), namebuf, sizeof(namebuf));
> -- temp_var = find_variable ("PPID");
> -- if (temp_var)
> -- VUNSETATTR (temp_var, (att_readonly | att_exported));
> -- temp_var = bind_variable ("PPID", name, 0);
> -- VSETATTR (temp_var, (att_readonly | att_integer));
> --}
> --
> --static void
> --uidset ()
> --{
> -- char buff[INT_STRLEN_BOUND(uid_t) + 1], *b;
> -- register SHELL_VAR *v;
> --
> -- b = inttostr (current_user.uid, buff, sizeof (buff));
> -- v = find_variable ("UID");
> -- if (v == 0)
> -- {
> -- v = bind_variable ("UID", b, 0);
> -- VSETATTR (v, (att_readonly | att_integer));
> -- }
> --
> -- if (current_user.euid != current_user.uid)
> -- b = inttostr (current_user.euid, buff, sizeof (buff));
> --
> -- v = find_variable ("EUID");
> -- if (v == 0)
> -- {
> -- v = bind_variable ("EUID", b, 0);
> -- VSETATTR (v, (att_readonly | att_integer));
> -- }
> --}
> --
> --#if defined (ARRAY_VARS)
> --static void
> --make_vers_array ()
> --{
> -- SHELL_VAR *vv;
> -- ARRAY *av;
> -- char *s, d[32], b[INT_STRLEN_BOUND(int) + 1];
> --
> -- unbind_variable ("BASH_VERSINFO");
> --
> -- vv = make_new_array_variable ("BASH_VERSINFO");
> -- av = array_cell (vv);
> -- strcpy (d, dist_version);
> -- s = strchr (d, '.');
> -- if (s)
> -- *s++ = '\0';
> -- array_insert (av, 0, d);
> -- array_insert (av, 1, s);
> -- s = inttostr (patch_level, b, sizeof (b));
> -- array_insert (av, 2, s);
> -- s = inttostr (build_version, b, sizeof (b));
> -- array_insert (av, 3, s);
> -- array_insert (av, 4, release_status);
> -- array_insert (av, 5, MACHTYPE);
> --
> -- VSETATTR (vv, att_readonly);
> --}
> --#endif /* ARRAY_VARS */
> --
> --/* Set the environment variables $LINES and $COLUMNS in response to
> -- a window size change. */
> --void
> --sh_set_lines_and_columns (lines, cols)
> -- int lines, cols;
> --{
> -- char val[INT_STRLEN_BOUND(int) + 1], *v;
> --
> --#if defined (READLINE)
> -- /* If we are currently assigning to LINES or COLUMNS, don't do anything. */
> -- if (winsize_assignment)
> -- return;
> --#endif
> --
> -- v = inttostr (lines, val, sizeof (val));
> -- bind_variable ("LINES", v, 0);
> --
> -- v = inttostr (cols, val, sizeof (val));
> -- bind_variable ("COLUMNS", v, 0);
> --}
> --
> --/* **************************************************************** */
> --/* */
> --/* Printing variables and values */
> --/* */
> --/* **************************************************************** */
> --
> --/* Print LIST (a list of shell variables) to stdout in such a way that
> -- they can be read back in. */
> --void
> --print_var_list (list)
> -- register SHELL_VAR **list;
> --{
> -- register int i;
> -- register SHELL_VAR *var;
> --
> -- for (i = 0; list && (var = list[i]); i++)
> -- if (invisible_p (var) == 0)
> -- print_assignment (var);
> --}
> --
> --/* Print LIST (a list of shell functions) to stdout in such a way that
> -- they can be read back in. */
> --void
> --print_func_list (list)
> -- register SHELL_VAR **list;
> --{
> -- register int i;
> -- register SHELL_VAR *var;
> --
> -- for (i = 0; list && (var = list[i]); i++)
> -- {
> -- printf ("%s ", var->name);
> -- print_var_function (var);
> -- printf ("\n");
> -- }
> --}
> --
> --/* Print the value of a single SHELL_VAR. No newline is
> -- output, but the variable is printed in such a way that
> -- it can be read back in. */
> --void
> --print_assignment (var)
> -- SHELL_VAR *var;
> --{
> -- if (var_isset (var) == 0)
> -- return;
> --
> -- if (function_p (var))
> -- {
> -- printf ("%s", var->name);
> -- print_var_function (var);
> -- printf ("\n");
> -- }
> --#if defined (ARRAY_VARS)
> -- else if (array_p (var))
> -- print_array_assignment (var, 0);
> -- else if (assoc_p (var))
> -- print_assoc_assignment (var, 0);
> --#endif /* ARRAY_VARS */
> -- else
> -- {
> -- printf ("%s=", var->name);
> -- print_var_value (var, 1);
> -- printf ("\n");
> -- }
> --}
> --
> --/* Print the value cell of VAR, a shell variable. Do not print
> -- the name, nor leading/trailing newline. If QUOTE is non-zero,
> -- and the value contains shell metacharacters, quote the value
> -- in such a way that it can be read back in. */
> --void
> --print_var_value (var, quote)
> -- SHELL_VAR *var;
> -- int quote;
> --{
> -- char *t;
> --
> -- if (var_isset (var) == 0)
> -- return;
> --
> -- if (quote && posixly_correct == 0 && ansic_shouldquote (value_cell (var)))
> -- {
> -- t = ansic_quote (value_cell (var), 0, (int *)0);
> -- printf ("%s", t);
> -- free (t);
> -- }
> -- else if (quote && sh_contains_shell_metas (value_cell (var)))
> -- {
> -- t = sh_single_quote (value_cell (var));
> -- printf ("%s", t);
> -- free (t);
> -- }
> -- else
> -- printf ("%s", value_cell (var));
> --}
> --
> --/* Print the function cell of VAR, a shell variable. Do not
> -- print the name, nor leading/trailing newline. */
> --void
> --print_var_function (var)
> -- SHELL_VAR *var;
> --{
> -- char *x;
> --
> -- if (function_p (var) && var_isset (var))
> -- {
> -- x = named_function_string ((char *)NULL, function_cell(var), FUNC_MULTILINE|FUNC_EXTERNAL);
> -- printf ("%s", x);
> -- }
> --}
> --
> --/* **************************************************************** */
> --/* */
> --/* Dynamic Variables */
> --/* */
> --/* **************************************************************** */
> --
> --/* DYNAMIC VARIABLES
> --
> -- These are variables whose values are generated anew each time they are
> -- referenced. These are implemented using a pair of function pointers
> -- in the struct variable: assign_func, which is called from bind_variable
> -- and, if arrays are compiled into the shell, some of the functions in
> -- arrayfunc.c, and dynamic_value, which is called from find_variable.
> --
> -- assign_func is called from bind_variable_internal, if
> -- bind_variable_internal discovers that the variable being assigned to
> -- has such a function. The function is called as
> -- SHELL_VAR *temp = (*(entry->assign_func)) (entry, value, ind)
> -- and the (SHELL_VAR *)temp is returned as the value of bind_variable. It
> -- is usually ENTRY (self). IND is an index for an array variable, and
> -- unused otherwise.
> --
> -- dynamic_value is called from find_variable_internal to return a `new'
> -- value for the specified dynamic varible. If this function is NULL,
> -- the variable is treated as a `normal' shell variable. If it is not,
> -- however, then this function is called like this:
> -- tempvar = (*(var->dynamic_value)) (var);
> --
> -- Sometimes `tempvar' will replace the value of `var'. Other times, the
> -- shell will simply use the string value. Pretty object-oriented, huh?
> --
> -- Be warned, though: if you `unset' a special variable, it loses its
> -- special meaning, even if you subsequently set it.
> --
> -- The special assignment code would probably have been better put in
> -- subst.c: do_assignment_internal, in the same style as
> -- stupidly_hack_special_variables, but I wanted the changes as
> -- localized as possible. */
> --
> --#define INIT_DYNAMIC_VAR(var, val, gfunc, afunc) \
> -- do \
> -- { \
> -- v = bind_variable (var, (val), 0); \
> -- v->dynamic_value = gfunc; \
> -- v->assign_func = afunc; \
> -- } \
> -- while (0)
> --
> --#define INIT_DYNAMIC_ARRAY_VAR(var, gfunc, afunc) \
> -- do \
> -- { \
> -- v = make_new_array_variable (var); \
> -- v->dynamic_value = gfunc; \
> -- v->assign_func = afunc; \
> -- } \
> -- while (0)
> --
> --#define INIT_DYNAMIC_ASSOC_VAR(var, gfunc, afunc) \
> -- do \
> -- { \
> -- v = make_new_assoc_variable (var); \
> -- v->dynamic_value = gfunc; \
> -- v->assign_func = afunc; \
> -- } \
> -- while (0)
> --
> --static SHELL_VAR *
> --null_assign (self, value, unused, key)
> -- SHELL_VAR *self;
> -- char *value;
> -- arrayind_t unused;
> -- char *key;
> --{
> -- return (self);
> --}
> --
> --#if defined (ARRAY_VARS)
> --static SHELL_VAR *
> --null_array_assign (self, value, ind, key)
> -- SHELL_VAR *self;
> -- char *value;
> -- arrayind_t ind;
> -- char *key;
> --{
> -- return (self);
> --}
> --#endif
> --
> --/* Degenerate `dynamic_value' function; just returns what's passed without
> -- manipulation. */
> --static SHELL_VAR *
> --get_self (self)
> -- SHELL_VAR *self;
> --{
> -- return (self);
> --}
> --
> --#if defined (ARRAY_VARS)
> --/* A generic dynamic array variable initializer. Initialize array variable
> -- NAME with dynamic value function GETFUNC and assignment function SETFUNC. */
> --static SHELL_VAR *
> --init_dynamic_array_var (name, getfunc, setfunc, attrs)
> -- char *name;
> -- sh_var_value_func_t *getfunc;
> -- sh_var_assign_func_t *setfunc;
> -- int attrs;
> --{
> -- SHELL_VAR *v;
> --
> -- v = find_variable (name);
> -- if (v)
> -- return (v);
> -- INIT_DYNAMIC_ARRAY_VAR (name, getfunc, setfunc);
> -- if (attrs)
> -- VSETATTR (v, attrs);
> -- return v;
> --}
> --
> --static SHELL_VAR *
> --init_dynamic_assoc_var (name, getfunc, setfunc, attrs)
> -- char *name;
> -- sh_var_value_func_t *getfunc;
> -- sh_var_assign_func_t *setfunc;
> -- int attrs;
> --{
> -- SHELL_VAR *v;
> --
> -- v = find_variable (name);
> -- if (v)
> -- return (v);
> -- INIT_DYNAMIC_ASSOC_VAR (name, getfunc, setfunc);
> -- if (attrs)
> -- VSETATTR (v, attrs);
> -- return v;
> --}
> --#endif
> --
> --/* The value of $SECONDS. This is the number of seconds since shell
> -- invocation, or, the number of seconds since the last assignment + the
> -- value of the last assignment. */
> --static intmax_t seconds_value_assigned;
> --
> --static SHELL_VAR *
> --assign_seconds (self, value, unused, key)
> -- SHELL_VAR *self;
> -- char *value;
> -- arrayind_t unused;
> -- char *key;
> --{
> -- if (legal_number (value, &seconds_value_assigned) == 0)
> -- seconds_value_assigned = 0;
> -- shell_start_time = NOW;
> -- return (self);
> --}
> --
> --static SHELL_VAR *
> --get_seconds (var)
> -- SHELL_VAR *var;
> --{
> -- time_t time_since_start;
> -- char *p;
> --
> -- time_since_start = NOW - shell_start_time;
> -- p = itos(seconds_value_assigned + time_since_start);
> --
> -- FREE (value_cell (var));
> --
> -- VSETATTR (var, att_integer);
> -- var_setvalue (var, p);
> -- return (var);
> --}
> --
> --static SHELL_VAR *
> --init_seconds_var ()
> --{
> -- SHELL_VAR *v;
> --
> -- v = find_variable ("SECONDS");
> -- if (v)
> -- {
> -- if (legal_number (value_cell(v), &seconds_value_assigned) == 0)
> -- seconds_value_assigned = 0;
> -- }
> -- INIT_DYNAMIC_VAR ("SECONDS", (v ? value_cell (v) : (char *)NULL), get_seconds, assign_seconds);
> -- return v;
> --}
> --
> --/* The random number seed. You can change this by setting RANDOM. */
> --static unsigned long rseed = 1;
> --static int last_random_value;
> --static int seeded_subshell = 0;
> --
> --/* A linear congruential random number generator based on the example
> -- one in the ANSI C standard. This one isn't very good, but a more
> -- complicated one is overkill. */
> --
> --/* Returns a pseudo-random number between 0 and 32767. */
> --static int
> --brand ()
> --{
> -- /* From "Random number generators: good ones are hard to find",
> -- Park and Miller, Communications of the ACM, vol. 31, no. 10,
> -- October 1988, p. 1195. filtered through FreeBSD */
> -- long h, l;
> --
> -- /* Can't seed with 0. */
> -- if (rseed == 0)
> -- rseed = 123459876;
> -- h = rseed / 127773;
> -- l = rseed % 127773;
> -- rseed = 16807 * l - 2836 * h;
> --#if 0
> -- if (rseed < 0)
> -- rseed += 0x7fffffff;
> --#endif
> -- return ((unsigned int)(rseed & 32767)); /* was % 32768 */
> --}
> --
> --/* Set the random number generator seed to SEED. */
> --static void
> --sbrand (seed)
> -- unsigned long seed;
> --{
> -- rseed = seed;
> -- last_random_value = 0;
> --}
> --
> --static void
> --seedrand ()
> --{
> -- struct timeval tv;
> --
> -- gettimeofday (&tv, NULL);
> -- sbrand (tv.tv_sec ^ tv.tv_usec ^ getpid ());
> --}
> --
> --static SHELL_VAR *
> --assign_random (self, value, unused, key)
> -- SHELL_VAR *self;
> -- char *value;
> -- arrayind_t unused;
> -- char *key;
> --{
> -- sbrand (strtoul (value, (char **)NULL, 10));
> -- if (subshell_environment)
> -- seeded_subshell = getpid ();
> -- return (self);
> --}
> --
> --int
> --get_random_number ()
> --{
> -- int rv, pid;
> --
> -- /* Reset for command and process substitution. */
> -- pid = getpid ();
> -- if (subshell_environment && seeded_subshell != pid)
> -- {
> -- seedrand ();
> -- seeded_subshell = pid;
> -- }
> --
> -- do
> -- rv = brand ();
> -- while (rv == last_random_value);
> -- return rv;
> --}
> --
> --static SHELL_VAR *
> --get_random (var)
> -- SHELL_VAR *var;
> --{
> -- int rv;
> -- char *p;
> --
> -- rv = get_random_number ();
> -- last_random_value = rv;
> -- p = itos (rv);
> --
> -- FREE (value_cell (var));
> --
> -- VSETATTR (var, att_integer);
> -- var_setvalue (var, p);
> -- return (var);
> --}
> --
> --static SHELL_VAR *
> --assign_lineno (var, value, unused, key)
> -- SHELL_VAR *var;
> -- char *value;
> -- arrayind_t unused;
> -- char *key;
> --{
> -- intmax_t new_value;
> --
> -- if (value == 0 || *value == '\0' || legal_number (value, &new_value) == 0)
> -- new_value = 0;
> -- line_number = line_number_base = new_value;
> -- return var;
> --}
> --
> --/* Function which returns the current line number. */
> --static SHELL_VAR *
> --get_lineno (var)
> -- SHELL_VAR *var;
> --{
> -- char *p;
> -- int ln;
> --
> -- ln = executing_line_number ();
> -- p = itos (ln);
> -- FREE (value_cell (var));
> -- var_setvalue (var, p);
> -- return (var);
> --}
> --
> --static SHELL_VAR *
> --assign_subshell (var, value, unused, key)
> -- SHELL_VAR *var;
> -- char *value;
> -- arrayind_t unused;
> -- char *key;
> --{
> -- intmax_t new_value;
> --
> -- if (value == 0 || *value == '\0' || legal_number (value, &new_value) == 0)
> -- new_value = 0;
> -- subshell_level = new_value;
> -- return var;
> --}
> --
> --static SHELL_VAR *
> --get_subshell (var)
> -- SHELL_VAR *var;
> --{
> -- char *p;
> --
> -- p = itos (subshell_level);
> -- FREE (value_cell (var));
> -- var_setvalue (var, p);
> -- return (var);
> --}
> --
> --static SHELL_VAR *
> --get_bashpid (var)
> -- SHELL_VAR *var;
> --{
> -- int pid;
> -- char *p;
> --
> -- pid = getpid ();
> -- p = itos (pid);
> --
> -- FREE (value_cell (var));
> -- VSETATTR (var, att_integer|att_readonly);
> -- var_setvalue (var, p);
> -- return (var);
> --}
> --
> --static SHELL_VAR *
> --get_bash_command (var)
> -- SHELL_VAR *var;
> --{
> -- char *p;
> --
> -- if (the_printed_command_except_trap)
> -- p = savestring (the_printed_command_except_trap);
> -- else
> -- {
> -- p = (char *)xmalloc (1);
> -- p[0] = '\0';
> -- }
> -- FREE (value_cell (var));
> -- var_setvalue (var, p);
> -- return (var);
> --}
> --
> --#if defined (HISTORY)
> --static SHELL_VAR *
> --get_histcmd (var)
> -- SHELL_VAR *var;
> --{
> -- char *p;
> --
> -- p = itos (history_number ());
> -- FREE (value_cell (var));
> -- var_setvalue (var, p);
> -- return (var);
> --}
> --#endif
> --
> --#if defined (READLINE)
> --/* When this function returns, VAR->value points to malloced memory. */
> --static SHELL_VAR *
> --get_comp_wordbreaks (var)
> -- SHELL_VAR *var;
> --{
> -- /* If we don't have anything yet, assign a default value. */
> -- if (rl_completer_word_break_characters == 0 && bash_readline_initialized == 0)
> -- enable_hostname_completion (perform_hostname_completion);
> --
> -- FREE (value_cell (var));
> -- var_setvalue (var, savestring (rl_completer_word_break_characters));
> --
> -- return (var);
> --}
> --
> --/* When this function returns, rl_completer_word_break_characters points to
> -- malloced memory. */
> --static SHELL_VAR *
> --assign_comp_wordbreaks (self, value, unused, key)
> -- SHELL_VAR *self;
> -- char *value;
> -- arrayind_t unused;
> -- char *key;
> --{
> -- if (rl_completer_word_break_characters &&
> -- rl_completer_word_break_characters != rl_basic_word_break_characters)
> -- free (rl_completer_word_break_characters);
> --
> -- rl_completer_word_break_characters = savestring (value);
> -- return self;
> --}
> --#endif /* READLINE */
> --
> --#if defined (PUSHD_AND_POPD) && defined (ARRAY_VARS)
> --static SHELL_VAR *
> --assign_dirstack (self, value, ind, key)
> -- SHELL_VAR *self;
> -- char *value;
> -- arrayind_t ind;
> -- char *key;
> --{
> -- set_dirstack_element (ind, 1, value);
> -- return self;
> --}
> --
> --static SHELL_VAR *
> --get_dirstack (self)
> -- SHELL_VAR *self;
> --{
> -- ARRAY *a;
> -- WORD_LIST *l;
> --
> -- l = get_directory_stack (0);
> -- a = array_from_word_list (l);
> -- array_dispose (array_cell (self));
> -- dispose_words (l);
> -- var_setarray (self, a);
> -- return self;
> --}
> --#endif /* PUSHD AND POPD && ARRAY_VARS */
> --
> --#if defined (ARRAY_VARS)
> --/* We don't want to initialize the group set with a call to getgroups()
> -- unless we're asked to, but we only want to do it once. */
> --static SHELL_VAR *
> --get_groupset (self)
> -- SHELL_VAR *self;
> --{
> -- register int i;
> -- int ng;
> -- ARRAY *a;
> -- static char **group_set = (char **)NULL;
> --
> -- if (group_set == 0)
> -- {
> -- group_set = get_group_list (&ng);
> -- a = array_cell (self);
> -- for (i = 0; i < ng; i++)
> -- array_insert (a, i, group_set[i]);
> -- }
> -- return (self);
> --}
> --
> --static SHELL_VAR *
> --build_hashcmd (self)
> -- SHELL_VAR *self;
> --{
> -- HASH_TABLE *h;
> -- int i;
> -- char *k, *v;
> -- BUCKET_CONTENTS *item;
> --
> -- h = assoc_cell (self);
> -- if (h)
> -- assoc_dispose (h);
> --
> -- if (hashed_filenames == 0 || HASH_ENTRIES (hashed_filenames) == 0)
> -- {
> -- var_setvalue (self, (char *)NULL);
> -- return self;
> -- }
> --
> -- h = assoc_create (hashed_filenames->nbuckets);
> -- for (i = 0; i < hashed_filenames->nbuckets; i++)
> -- {
> -- for (item = hash_items (i, hashed_filenames); item; item = item->next)
> -- {
> -- k = savestring (item->key);
> -- v = pathdata(item)->path;
> -- assoc_insert (h, k, v);
> -- }
> -- }
> --
> -- var_setvalue (self, (char *)h);
> -- return self;
> --}
> --
> --static SHELL_VAR *
> --get_hashcmd (self)
> -- SHELL_VAR *self;
> --{
> -- build_hashcmd (self);
> -- return (self);
> --}
> --
> --static SHELL_VAR *
> --assign_hashcmd (self, value, ind, key)
> -- SHELL_VAR *self;
> -- char *value;
> -- arrayind_t ind;
> -- char *key;
> --{
> -- phash_insert (key, value, 0, 0);
> -- return (build_hashcmd (self));
> --}
> --
> --#if defined (ALIAS)
> --static SHELL_VAR *
> --build_aliasvar (self)
> -- SHELL_VAR *self;
> --{
> -- HASH_TABLE *h;
> -- int i;
> -- char *k, *v;
> -- BUCKET_CONTENTS *item;
> --
> -- h = assoc_cell (self);
> -- if (h)
> -- assoc_dispose (h);
> --
> -- if (aliases == 0 || HASH_ENTRIES (aliases) == 0)
> -- {
> -- var_setvalue (self, (char *)NULL);
> -- return self;
> -- }
> --
> -- h = assoc_create (aliases->nbuckets);
> -- for (i = 0; i < aliases->nbuckets; i++)
> -- {
> -- for (item = hash_items (i, aliases); item; item = item->next)
> -- {
> -- k = savestring (item->key);
> -- v = ((alias_t *)(item->data))->value;
> -- assoc_insert (h, k, v);
> -- }
> -- }
> --
> -- var_setvalue (self, (char *)h);
> -- return self;
> --}
> --
> --static SHELL_VAR *
> --get_aliasvar (self)
> -- SHELL_VAR *self;
> --{
> -- build_aliasvar (self);
> -- return (self);
> --}
> --
> --static SHELL_VAR *
> --assign_aliasvar (self, value, ind, key)
> -- SHELL_VAR *self;
> -- char *value;
> -- arrayind_t ind;
> -- char *key;
> --{
> -- add_alias (key, value);
> -- return (build_aliasvar (self));
> --}
> --#endif /* ALIAS */
> --
> --#endif /* ARRAY_VARS */
> --
> --/* If ARRAY_VARS is not defined, this just returns the name of any
> -- currently-executing function. If we have arrays, it's a call stack. */
> --static SHELL_VAR *
> --get_funcname (self)
> -- SHELL_VAR *self;
> --{
> --#if ! defined (ARRAY_VARS)
> -- char *t;
> -- if (variable_context && this_shell_function)
> -- {
> -- FREE (value_cell (self));
> -- t = savestring (this_shell_function->name);
> -- var_setvalue (self, t);
> -- }
> --#endif
> -- return (self);
> --}
> --
> --void
> --make_funcname_visible (on_or_off)
> -- int on_or_off;
> --{
> -- SHELL_VAR *v;
> --
> -- v = find_variable ("FUNCNAME");
> -- if (v == 0 || v->dynamic_value == 0)
> -- return;
> --
> -- if (on_or_off)
> -- VUNSETATTR (v, att_invisible);
> -- else
> -- VSETATTR (v, att_invisible);
> --}
> --
> --static SHELL_VAR *
> --init_funcname_var ()
> --{
> -- SHELL_VAR *v;
> --
> -- v = find_variable ("FUNCNAME");
> -- if (v)
> -- return v;
> --#if defined (ARRAY_VARS)
> -- INIT_DYNAMIC_ARRAY_VAR ("FUNCNAME", get_funcname, null_array_assign);
> --#else
> -- INIT_DYNAMIC_VAR ("FUNCNAME", (char *)NULL, get_funcname, null_assign);
> --#endif
> -- VSETATTR (v, att_invisible|att_noassign);
> -- return v;
> --}
> --
> --static void
> --initialize_dynamic_variables ()
> --{
> -- SHELL_VAR *v;
> --
> -- v = init_seconds_var ();
> --
> -- INIT_DYNAMIC_VAR ("BASH_COMMAND", (char *)NULL, get_bash_command, (sh_var_assign_func_t *)NULL);
> -- INIT_DYNAMIC_VAR ("BASH_SUBSHELL", (char *)NULL, get_subshell, assign_subshell);
> --
> -- INIT_DYNAMIC_VAR ("RANDOM", (char *)NULL, get_random, assign_random);
> -- VSETATTR (v, att_integer);
> -- INIT_DYNAMIC_VAR ("LINENO", (char *)NULL, get_lineno, assign_lineno);
> -- VSETATTR (v, att_integer);
> --
> -- INIT_DYNAMIC_VAR ("BASHPID", (char *)NULL, get_bashpid, null_assign);
> -- VSETATTR (v, att_integer|att_readonly);
> --
> --#if defined (HISTORY)
> -- INIT_DYNAMIC_VAR ("HISTCMD", (char *)NULL, get_histcmd, (sh_var_assign_func_t *)NULL);
> -- VSETATTR (v, att_integer);
> --#endif
> --
> --#if defined (READLINE)
> -- INIT_DYNAMIC_VAR ("COMP_WORDBREAKS", (char *)NULL, get_comp_wordbreaks, assign_comp_wordbreaks);
> --#endif
> --
> --#if defined (PUSHD_AND_POPD) && defined (ARRAY_VARS)
> -- v = init_dynamic_array_var ("DIRSTACK", get_dirstack, assign_dirstack, 0);
> --#endif /* PUSHD_AND_POPD && ARRAY_VARS */
> --
> --#if defined (ARRAY_VARS)
> -- v = init_dynamic_array_var ("GROUPS", get_groupset, null_array_assign, att_noassign);
> --
> --# if defined (DEBUGGER)
> -- v = init_dynamic_array_var ("BASH_ARGC", get_self, null_array_assign, att_noassign|att_nounset);
> -- v = init_dynamic_array_var ("BASH_ARGV", get_self, null_array_assign, att_noassign|att_nounset);
> --# endif /* DEBUGGER */
> -- v = init_dynamic_array_var ("BASH_SOURCE", get_self, null_array_assign, att_noassign|att_nounset);
> -- v = init_dynamic_array_var ("BASH_LINENO", get_self, null_array_assign, att_noassign|att_nounset);
> --
> -- v = init_dynamic_assoc_var ("BASH_CMDS", get_hashcmd, assign_hashcmd, att_nofree);
> --# if defined (ALIAS)
> -- v = init_dynamic_assoc_var ("BASH_ALIASES", get_aliasvar, assign_aliasvar, att_nofree);
> --# endif
> --#endif
> --
> -- v = init_funcname_var ();
> --}
> --
> --/* **************************************************************** */
> --/* */
> --/* Retrieving variables and values */
> --/* */
> --/* **************************************************************** */
> --
> --/* How to get a pointer to the shell variable or function named NAME.
> -- HASHED_VARS is a pointer to the hash table containing the list
> -- of interest (either variables or functions). */
> --
> --static SHELL_VAR *
> --hash_lookup (name, hashed_vars)
> -- const char *name;
> -- HASH_TABLE *hashed_vars;
> --{
> -- BUCKET_CONTENTS *bucket;
> --
> -- bucket = hash_search (name, hashed_vars, 0);
> -- /* If we find the name in HASHED_VARS, set LAST_TABLE_SEARCHED to that
> -- table. */
> -- if (bucket)
> -- last_table_searched = hashed_vars;
> -- return (bucket ? (SHELL_VAR *)bucket->data : (SHELL_VAR *)NULL);
> --}
> --
> --SHELL_VAR *
> --var_lookup (name, vcontext)
> -- const char *name;
> -- VAR_CONTEXT *vcontext;
> --{
> -- VAR_CONTEXT *vc;
> -- SHELL_VAR *v;
> --
> -- v = (SHELL_VAR *)NULL;
> -- for (vc = vcontext; vc; vc = vc->down)
> -- if (v = hash_lookup (name, vc->table))
> -- break;
> --
> -- return v;
> --}
> --
> --/* Look up the variable entry named NAME. If SEARCH_TEMPENV is non-zero,
> -- then also search the temporarily built list of exported variables.
> -- The lookup order is:
> -- temporary_env
> -- shell_variables list
> --*/
> --
> --SHELL_VAR *
> --find_variable_internal (name, force_tempenv)
> -- const char *name;
> -- int force_tempenv;
> --{
> -- SHELL_VAR *var;
> -- int search_tempenv;
> -- VAR_CONTEXT *vc;
> --
> -- var = (SHELL_VAR *)NULL;
> --
> -- /* If explicitly requested, first look in the temporary environment for
> -- the variable. This allows constructs such as "foo=x eval 'echo $foo'"
> -- to get the `exported' value of $foo. This happens if we are executing
> -- a function or builtin, or if we are looking up a variable in a
> -- "subshell environment". */
> -- search_tempenv = force_tempenv || (expanding_redir == 0 && subshell_environment);
> --
> -- if (search_tempenv && temporary_env)
> -- var = hash_lookup (name, temporary_env);
> --
> -- vc = shell_variables;
> --#if 0
> --if (search_tempenv == 0 && /* (subshell_environment & SUBSHELL_COMSUB) && */
> -- expanding_redir &&
> -- (this_shell_builtin == eval_builtin || this_shell_builtin == command_builtin))
> -- {
> -- itrace("find_variable_internal: search_tempenv == 0: skipping VC_BLTNENV");
> -- while (vc && (vc->flags & VC_BLTNENV))
> -- vc = vc->down;
> -- if (vc == 0)
> -- vc = shell_variables;
> -- }
> --#endif
> --
> -- if (var == 0)
> -- var = var_lookup (name, vc);
> --
> -- if (var == 0)
> -- return ((SHELL_VAR *)NULL);
> --
> -- return (var->dynamic_value ? (*(var->dynamic_value)) (var) : var);
> --}
> --
> --/* Look up and resolve the chain of nameref variables starting at V all the
> -- way to NULL or non-nameref. */
> --SHELL_VAR *
> --find_variable_nameref (v)
> -- SHELL_VAR *v;
> --{
> -- int level;
> -- char *newname;
> -- SHELL_VAR *orig, *oldv;
> --
> -- level = 0;
> -- orig = v;
> -- while (v && nameref_p (v))
> -- {
> -- level++;
> -- if (level > NAMEREF_MAX)
> -- return ((SHELL_VAR *)0); /* error message here? */
> -- newname = nameref_cell (v);
> -- if (newname == 0 || *newname == '\0')
> -- return ((SHELL_VAR *)0);
> -- oldv = v;
> -- v = find_variable_internal (newname, (expanding_redir == 0 && (assigning_in_environment || executing_builtin)));
> -- if (v == orig || v == oldv)
> -- {
> -- internal_warning (_("%s: circular name reference"), orig->name);
> -- return ((SHELL_VAR *)0);
> -- }
> -- }
> -- return v;
> --}
> --
> --/* Resolve the chain of nameref variables for NAME. XXX - could change later */
> --SHELL_VAR *
> --find_variable_last_nameref (name)
> -- const char *name;
> --{
> -- SHELL_VAR *v, *nv;
> -- char *newname;
> -- int level;
> --
> -- nv = v = find_variable_noref (name);
> -- level = 0;
> -- while (v && nameref_p (v))
> -- {
> -- level++;
> -- if (level > NAMEREF_MAX)
> -- return ((SHELL_VAR *)0); /* error message here? */
> -- newname = nameref_cell (v);
> -- if (newname == 0 || *newname == '\0')
> -- return ((SHELL_VAR *)0);
> -- nv = v;
> -- v = find_variable_internal (newname, (expanding_redir == 0 && (assigning_in_environment || executing_builtin)));
> -- }
> -- return nv;
> --}
> --
> --/* Resolve the chain of nameref variables for NAME. XXX - could change later */
> --SHELL_VAR *
> --find_global_variable_last_nameref (name)
> -- const char *name;
> --{
> -- SHELL_VAR *v, *nv;
> -- char *newname;
> -- int level;
> --
> -- nv = v = find_global_variable_noref (name);
> -- level = 0;
> -- while (v && nameref_p (v))
> -- {
> -- level++;
> -- if (level > NAMEREF_MAX)
> -- return ((SHELL_VAR *)0); /* error message here? */
> -- newname = nameref_cell (v);
> -- if (newname == 0 || *newname == '\0')
> -- return ((SHELL_VAR *)0);
> -- nv = v;
> -- v = find_global_variable_noref (newname);
> -- }
> -- return nv;
> --}
> --
> --static SHELL_VAR *
> --find_nameref_at_context (v, vc)
> -- SHELL_VAR *v;
> -- VAR_CONTEXT *vc;
> --{
> -- SHELL_VAR *nv, *nv2;
> -- VAR_CONTEXT *nvc;
> -- char *newname;
> -- int level;
> --
> -- nv = v;
> -- level = 1;
> -- while (nv && nameref_p (nv))
> -- {
> -- level++;
> -- if (level > NAMEREF_MAX)
> -- return ((SHELL_VAR *)NULL);
> -- newname = nameref_cell (nv);
> -- if (newname == 0 || *newname == '\0')
> -- return ((SHELL_VAR *)NULL);
> -- nv2 = hash_lookup (newname, vc->table);
> -- if (nv2 == 0)
> -- break;
> -- nv = nv2;
> -- }
> -- return nv;
> --}
> --
> --/* Do nameref resolution from the VC, which is the local context for some
> -- function or builtin, `up' the chain to the global variables context. If
> -- NVCP is not NULL, return the variable context where we finally ended the
> -- nameref resolution (so the bind_variable_internal can use the correct
> -- variable context and hash table). */
> --static SHELL_VAR *
> --find_variable_nameref_context (v, vc, nvcp)
> -- SHELL_VAR *v;
> -- VAR_CONTEXT *vc;
> -- VAR_CONTEXT **nvcp;
> --{
> -- SHELL_VAR *nv, *nv2;
> -- VAR_CONTEXT *nvc;
> --
> -- /* Look starting at the current context all the way `up' */
> -- for (nv = v, nvc = vc; nvc; nvc = nvc->down)
> -- {
> -- nv2 = find_nameref_at_context (nv, nvc);
> -- if (nv2 == 0)
> -- continue;
> -- nv = nv2;
> -- if (*nvcp)
> -- *nvcp = nvc;
> -- if (nameref_p (nv) == 0)
> -- break;
> -- }
> -- return (nameref_p (nv) ? (SHELL_VAR *)NULL : nv);
> --}
> --
> --/* Do nameref resolution from the VC, which is the local context for some
> -- function or builtin, `up' the chain to the global variables context. If
> -- NVCP is not NULL, return the variable context where we finally ended the
> -- nameref resolution (so the bind_variable_internal can use the correct
> -- variable context and hash table). */
> --static SHELL_VAR *
> --find_variable_last_nameref_context (v, vc, nvcp)
> -- SHELL_VAR *v;
> -- VAR_CONTEXT *vc;
> -- VAR_CONTEXT **nvcp;
> --{
> -- SHELL_VAR *nv, *nv2;
> -- VAR_CONTEXT *nvc;
> --
> -- /* Look starting at the current context all the way `up' */
> -- for (nv = v, nvc = vc; nvc; nvc = nvc->down)
> -- {
> -- nv2 = find_nameref_at_context (nv, nvc);
> -- if (nv2 == 0)
> -- continue;
> -- nv = nv2;
> -- if (*nvcp)
> -- *nvcp = nvc;
> -- }
> -- return (nameref_p (nv) ? nv : (SHELL_VAR *)NULL);
> --}
> --
> --/* Find a variable, forcing a search of the temporary environment first */
> --SHELL_VAR *
> --find_variable_tempenv (name)
> -- const char *name;
> --{
> -- SHELL_VAR *var;
> --
> -- var = find_variable_internal (name, 1);
> -- if (var && nameref_p (var))
> -- var = find_variable_nameref (var);
> -- return (var);
> --}
> --
> --/* Find a variable, not forcing a search of the temporary environment first */
> --SHELL_VAR *
> --find_variable_notempenv (name)
> -- const char *name;
> --{
> -- SHELL_VAR *var;
> --
> -- var = find_variable_internal (name, 0);
> -- if (var && nameref_p (var))
> -- var = find_variable_nameref (var);
> -- return (var);
> --}
> --
> --SHELL_VAR *
> --find_global_variable (name)
> -- const char *name;
> --{
> -- SHELL_VAR *var;
> --
> -- var = var_lookup (name, global_variables);
> -- if (var && nameref_p (var))
> -- var = find_variable_nameref (var);
> --
> -- if (var == 0)
> -- return ((SHELL_VAR *)NULL);
> --
> -- return (var->dynamic_value ? (*(var->dynamic_value)) (var) : var);
> --}
> --
> --SHELL_VAR *
> --find_global_variable_noref (name)
> -- const char *name;
> --{
> -- SHELL_VAR *var;
> --
> -- var = var_lookup (name, global_variables);
> --
> -- if (var == 0)
> -- return ((SHELL_VAR *)NULL);
> --
> -- return (var->dynamic_value ? (*(var->dynamic_value)) (var) : var);
> --}
> --
> --SHELL_VAR *
> --find_shell_variable (name)
> -- const char *name;
> --{
> -- SHELL_VAR *var;
> --
> -- var = var_lookup (name, shell_variables);
> -- if (var && nameref_p (var))
> -- var = find_variable_nameref (var);
> --
> -- if (var == 0)
> -- return ((SHELL_VAR *)NULL);
> --
> -- return (var->dynamic_value ? (*(var->dynamic_value)) (var) : var);
> --}
> --
> --/* Look up the variable entry named NAME. Returns the entry or NULL. */
> --SHELL_VAR *
> --find_variable (name)
> -- const char *name;
> --{
> -- SHELL_VAR *v;
> --
> -- last_table_searched = 0;
> -- v = find_variable_internal (name, (expanding_redir == 0 && (assigning_in_environment || executing_builtin)));
> -- if (v && nameref_p (v))
> -- v = find_variable_nameref (v);
> -- return v;
> --}
> --
> --SHELL_VAR *
> --find_variable_noref (name)
> -- const char *name;
> --{
> -- SHELL_VAR *v;
> --
> -- v = find_variable_internal (name, (expanding_redir == 0 && (assigning_in_environment || executing_builtin)));
> -- return v;
> --}
> --
> --/* Look up the function entry whose name matches STRING.
> -- Returns the entry or NULL. */
> --SHELL_VAR *
> --find_function (name)
> -- const char *name;
> --{
> -- return (hash_lookup (name, shell_functions));
> --}
> --
> --/* Find the function definition for the shell function named NAME. Returns
> -- the entry or NULL. */
> --FUNCTION_DEF *
> --find_function_def (name)
> -- const char *name;
> --{
> --#if defined (DEBUGGER)
> -- return ((FUNCTION_DEF *)hash_lookup (name, shell_function_defs));
> --#else
> -- return ((FUNCTION_DEF *)0);
> --#endif
> --}
> --
> --/* Return the value of VAR. VAR is assumed to have been the result of a
> -- lookup without any subscript, if arrays are compiled into the shell. */
> --char *
> --get_variable_value (var)
> -- SHELL_VAR *var;
> --{
> -- if (var == 0)
> -- return ((char *)NULL);
> --#if defined (ARRAY_VARS)
> -- else if (array_p (var))
> -- return (array_reference (array_cell (var), 0));
> -- else if (assoc_p (var))
> -- return (assoc_reference (assoc_cell (var), "0"));
> --#endif
> -- else
> -- return (value_cell (var));
> --}
> --
> --/* Return the string value of a variable. Return NULL if the variable
> -- doesn't exist. Don't cons a new string. This is a potential memory
> -- leak if the variable is found in the temporary environment. Since
> -- functions and variables have separate name spaces, returns NULL if
> -- var_name is a shell function only. */
> --char *
> --get_string_value (var_name)
> -- const char *var_name;
> --{
> -- SHELL_VAR *var;
> --
> -- var = find_variable (var_name);
> -- return ((var) ? get_variable_value (var) : (char *)NULL);
> --}
> --
> --/* This is present for use by the tilde and readline libraries. */
> --char *
> --sh_get_env_value (v)
> -- const char *v;
> --{
> -- return get_string_value (v);
> --}
> --
> --/* **************************************************************** */
> --/* */
> --/* Creating and setting variables */
> --/* */
> --/* **************************************************************** */
> --
> --/* Set NAME to VALUE if NAME has no value. */
> --SHELL_VAR *
> --set_if_not (name, value)
> -- char *name, *value;
> --{
> -- SHELL_VAR *v;
> --
> -- if (shell_variables == 0)
> -- create_variable_tables ();
> --
> -- v = find_variable (name);
> -- if (v == 0)
> -- v = bind_variable_internal (name, value, global_variables->table, HASH_NOSRCH, 0);
> -- return (v);
> --}
> --
> --/* Create a local variable referenced by NAME. */
> --SHELL_VAR *
> --make_local_variable (name)
> -- const char *name;
> --{
> -- SHELL_VAR *new_var, *old_var;
> -- VAR_CONTEXT *vc;
> -- int was_tmpvar;
> -- char *tmp_value;
> --
> -- /* local foo; local foo; is a no-op. */
> -- old_var = find_variable (name);
> -- if (old_var && local_p (old_var) && old_var->context == variable_context)
> -- return (old_var);
> --
> -- was_tmpvar = old_var && tempvar_p (old_var);
> -- /* If we're making a local variable in a shell function, the temporary env
> -- has already been merged into the function's variable context stack. We
> -- can assume that a temporary var in the same context appears in the same
> -- VAR_CONTEXT and can safely be returned without creating a new variable
> -- (which results in duplicate names in the same VAR_CONTEXT->table */
> -- /* We can't just test tmpvar_p because variables in the temporary env given
> -- to a shell function appear in the function's local variable VAR_CONTEXT
> -- but retain their tempvar attribute. We want temporary variables that are
> -- found in temporary_env, hence the test for last_table_searched, which is
> -- set in hash_lookup and only (so far) checked here. */
> -- if (was_tmpvar && old_var->context == variable_context && last_table_searched != temporary_env)
> -- {
> -- VUNSETATTR (old_var, att_invisible);
> -- return (old_var);
> -- }
> -- if (was_tmpvar)
> -- tmp_value = value_cell (old_var);
> --
> -- for (vc = shell_variables; vc; vc = vc->down)
> -- if (vc_isfuncenv (vc) && vc->scope == variable_context)
> -- break;
> --
> -- if (vc == 0)
> -- {
> -- internal_error (_("make_local_variable: no function context at current scope"));
> -- return ((SHELL_VAR *)NULL);
> -- }
> -- else if (vc->table == 0)
> -- vc->table = hash_create (TEMPENV_HASH_BUCKETS);
> --
> -- /* Since this is called only from the local/declare/typeset code, we can
> -- call builtin_error here without worry (of course, it will also work
> -- for anything that sets this_command_name). Variables with the `noassign'
> -- attribute may not be made local. The test against old_var's context
> -- level is to disallow local copies of readonly global variables (since I
> -- believe that this could be a security hole). Readonly copies of calling
> -- function local variables are OK. */
> -- if (old_var && (noassign_p (old_var) ||
> -- (readonly_p (old_var) && old_var->context == 0)))
> -- {
> -- if (readonly_p (old_var))
> -- sh_readonly (name);
> -- else if (noassign_p (old_var))
> -- builtin_error (_("%s: variable may not be assigned value"), name);
> --#if 0
> -- /* Let noassign variables through with a warning */
> -- if (readonly_p (old_var))
> --#endif
> -- return ((SHELL_VAR *)NULL);
> -- }
> --
> -- if (old_var == 0)
> -- new_var = make_new_variable (name, vc->table);
> -- else
> -- {
> -- new_var = make_new_variable (name, vc->table);
> --
> -- /* If we found this variable in one of the temporary environments,
> -- inherit its value. Watch to see if this causes problems with
> -- things like `x=4 local x'. XXX - see above for temporary env
> -- variables with the same context level as variable_context */
> -- /* XXX - we should only do this if the variable is not an array. */
> -- if (was_tmpvar)
> -- var_setvalue (new_var, savestring (tmp_value));
> --
> -- new_var->attributes = exported_p (old_var) ? att_exported : 0;
> -- }
> --
> -- vc->flags |= VC_HASLOCAL;
> --
> -- new_var->context = variable_context;
> -- VSETATTR (new_var, att_local);
> --
> -- if (ifsname (name))
> -- setifs (new_var);
> --
> -- if (was_tmpvar == 0)
> -- VSETATTR (new_var, att_invisible); /* XXX */
> -- return (new_var);
> --}
> --
> --/* Create a new shell variable with name NAME. */
> --static SHELL_VAR *
> --new_shell_variable (name)
> -- const char *name;
> --{
> -- SHELL_VAR *entry;
> --
> -- entry = (SHELL_VAR *)xmalloc (sizeof (SHELL_VAR));
> --
> -- entry->name = savestring (name);
> -- var_setvalue (entry, (char *)NULL);
> -- CLEAR_EXPORTSTR (entry);
> --
> -- entry->dynamic_value = (sh_var_value_func_t *)NULL;
> -- entry->assign_func = (sh_var_assign_func_t *)NULL;
> --
> -- entry->attributes = 0;
> --
> -- /* Always assume variables are to be made at toplevel!
> -- make_local_variable has the responsibility of changing the
> -- variable context. */
> -- entry->context = 0;
> --
> -- return (entry);
> --}
> --
> --/* Create a new shell variable with name NAME and add it to the hash table
> -- TABLE. */
> --static SHELL_VAR *
> --make_new_variable (name, table)
> -- const char *name;
> -- HASH_TABLE *table;
> --{
> -- SHELL_VAR *entry;
> -- BUCKET_CONTENTS *elt;
> --
> -- entry = new_shell_variable (name);
> --
> -- /* Make sure we have a shell_variables hash table to add to. */
> -- if (shell_variables == 0)
> -- create_variable_tables ();
> --
> -- elt = hash_insert (savestring (name), table, HASH_NOSRCH);
> -- elt->data = (PTR_T)entry;
> --
> -- return entry;
> --}
> --
> --#if defined (ARRAY_VARS)
> --SHELL_VAR *
> --make_new_array_variable (name)
> -- char *name;
> --{
> -- SHELL_VAR *entry;
> -- ARRAY *array;
> --
> -- entry = make_new_variable (name, global_variables->table);
> -- array = array_create ();
> --
> -- var_setarray (entry, array);
> -- VSETATTR (entry, att_array);
> -- return entry;
> --}
> --
> --SHELL_VAR *
> --make_local_array_variable (name, assoc_ok)
> -- char *name;
> -- int assoc_ok;
> --{
> -- SHELL_VAR *var;
> -- ARRAY *array;
> --
> -- var = make_local_variable (name);
> -- if (var == 0 || array_p (var) || (assoc_ok && assoc_p (var)))
> -- return var;
> --
> -- array = array_create ();
> --
> -- dispose_variable_value (var);
> -- var_setarray (var, array);
> -- VSETATTR (var, att_array);
> -- return var;
> --}
> --
> --SHELL_VAR *
> --make_new_assoc_variable (name)
> -- char *name;
> --{
> -- SHELL_VAR *entry;
> -- HASH_TABLE *hash;
> --
> -- entry = make_new_variable (name, global_variables->table);
> -- hash = assoc_create (0);
> --
> -- var_setassoc (entry, hash);
> -- VSETATTR (entry, att_assoc);
> -- return entry;
> --}
> --
> --SHELL_VAR *
> --make_local_assoc_variable (name)
> -- char *name;
> --{
> -- SHELL_VAR *var;
> -- HASH_TABLE *hash;
> --
> -- var = make_local_variable (name);
> -- if (var == 0 || assoc_p (var))
> -- return var;
> --
> -- dispose_variable_value (var);
> -- hash = assoc_create (0);
> --
> -- var_setassoc (var, hash);
> -- VSETATTR (var, att_assoc);
> -- return var;
> --}
> --#endif
> --
> --char *
> --make_variable_value (var, value, flags)
> -- SHELL_VAR *var;
> -- char *value;
> -- int flags;
> --{
> -- char *retval, *oval;
> -- intmax_t lval, rval;
> -- int expok, olen, op;
> --
> -- /* If this variable has had its type set to integer (via `declare -i'),
> -- then do expression evaluation on it and store the result. The
> -- functions in expr.c (evalexp()) and bind_int_variable() are responsible
> -- for turning off the integer flag if they don't want further
> -- evaluation done. */
> -- if (integer_p (var))
> -- {
> -- if (flags & ASS_APPEND)
> -- {
> -- oval = value_cell (var);
> -- lval = evalexp (oval, &expok); /* ksh93 seems to do this */
> -- if (expok == 0)
> -- {
> -- top_level_cleanup ();
> -- jump_to_top_level (DISCARD);
> -- }
> -- }
> -- rval = evalexp (value, &expok);
> -- if (expok == 0)
> -- {
> -- top_level_cleanup ();
> -- jump_to_top_level (DISCARD);
> -- }
> -- /* This can be fooled if the variable's value changes while evaluating
> -- `rval'. We can change it if we move the evaluation of lval to here. */
> -- if (flags & ASS_APPEND)
> -- rval += lval;
> -- retval = itos (rval);
> -- }
> --#if defined (CASEMOD_ATTRS)
> -- else if (capcase_p (var) || uppercase_p (var) || lowercase_p (var))
> -- {
> -- if (flags & ASS_APPEND)
> -- {
> -- oval = get_variable_value (var);
> -- if (oval == 0) /* paranoia */
> -- oval = "";
> -- olen = STRLEN (oval);
> -- retval = (char *)xmalloc (olen + (value ? STRLEN (value) : 0) + 1);
> -- strcpy (retval, oval);
> -- if (value)
> -- strcpy (retval+olen, value);
> -- }
> -- else if (*value)
> -- retval = savestring (value);
> -- else
> -- {
> -- retval = (char *)xmalloc (1);
> -- retval[0] = '\0';
> -- }
> -- op = capcase_p (var) ? CASE_CAPITALIZE
> -- : (uppercase_p (var) ? CASE_UPPER : CASE_LOWER);
> -- oval = sh_modcase (retval, (char *)0, op);
> -- free (retval);
> -- retval = oval;
> -- }
> --#endif /* CASEMOD_ATTRS */
> -- else if (value)
> -- {
> -- if (flags & ASS_APPEND)
> -- {
> -- oval = get_variable_value (var);
> -- if (oval == 0) /* paranoia */
> -- oval = "";
> -- olen = STRLEN (oval);
> -- retval = (char *)xmalloc (olen + (value ? STRLEN (value) : 0) + 1);
> -- strcpy (retval, oval);
> -- if (value)
> -- strcpy (retval+olen, value);
> -- }
> -- else if (*value)
> -- retval = savestring (value);
> -- else
> -- {
> -- retval = (char *)xmalloc (1);
> -- retval[0] = '\0';
> -- }
> -- }
> -- else
> -- retval = (char *)NULL;
> --
> -- return retval;
> --}
> --
> --/* Bind a variable NAME to VALUE in the HASH_TABLE TABLE, which may be the
> -- temporary environment (but usually is not). */
> --static SHELL_VAR *
> --bind_variable_internal (name, value, table, hflags, aflags)
> -- const char *name;
> -- char *value;
> -- HASH_TABLE *table;
> -- int hflags, aflags;
> --{
> -- char *newval;
> -- SHELL_VAR *entry;
> --
> -- entry = (hflags & HASH_NOSRCH) ? (SHELL_VAR *)NULL : hash_lookup (name, table);
> -- /* Follow the nameref chain here if this is the global variables table */
> -- if (entry && nameref_p (entry) && (invisible_p (entry) == 0) && table == global_variables->table)
> -- {
> -- entry = find_global_variable (entry->name);
> -- /* Let's see if we have a nameref referencing a variable that hasn't yet
> -- been created. */
> -- if (entry == 0)
> -- entry = find_variable_last_nameref (name); /* XXX */
> -- if (entry == 0) /* just in case */
> -- return (entry);
> -- }
> --
> -- /* The first clause handles `declare -n ref; ref=x;' */
> -- if (entry && invisible_p (entry) && nameref_p (entry))
> -- goto assign_value;
> -- else if (entry && nameref_p (entry))
> -- {
> -- newval = nameref_cell (entry);
> --#if defined (ARRAY_VARS)
> -- /* declare -n foo=x[2] */
> -- if (valid_array_reference (newval))
> -- /* XXX - should it be aflags? */
> -- entry = assign_array_element (newval, make_variable_value (entry, value, 0), aflags);
> -- else
> --#endif
> -- {
> -- entry = make_new_variable (newval, table);
> -- var_setvalue (entry, make_variable_value (entry, value, 0));
> -- }
> -- }
> -- else if (entry == 0)
> -- {
> -- entry = make_new_variable (name, table);
> -- var_setvalue (entry, make_variable_value (entry, value, 0)); /* XXX */
> -- }
> -- else if (entry->assign_func) /* array vars have assign functions now */
> -- {
> -- INVALIDATE_EXPORTSTR (entry);
> -- newval = (aflags & ASS_APPEND) ? make_variable_value (entry, value, aflags) : value;
> -- if (assoc_p (entry))
> -- entry = (*(entry->assign_func)) (entry, newval, -1, savestring ("0"));
> -- else if (array_p (entry))
> -- entry = (*(entry->assign_func)) (entry, newval, 0, 0);
> -- else
> -- entry = (*(entry->assign_func)) (entry, newval, -1, 0);
> -- if (newval != value)
> -- free (newval);
> -- return (entry);
> -- }
> -- else
> -- {
> --assign_value:
> -- if (readonly_p (entry) || noassign_p (entry))
> -- {
> -- if (readonly_p (entry))
> -- err_readonly (name);
> -- return (entry);
> -- }
> --
> -- /* Variables which are bound are visible. */
> -- VUNSETATTR (entry, att_invisible);
> --
> --#if defined (ARRAY_VARS)
> -- if (assoc_p (entry) || array_p (entry))
> -- newval = make_array_variable_value (entry, 0, "0", value, aflags);
> -- else
> --#endif
> --
> -- newval = make_variable_value (entry, value, aflags); /* XXX */
> --
> -- /* Invalidate any cached export string */
> -- INVALIDATE_EXPORTSTR (entry);
> --
> --#if defined (ARRAY_VARS)
> -- /* XXX -- this bears looking at again -- XXX */
> -- /* If an existing array variable x is being assigned to with x=b or
> -- `read x' or something of that nature, silently convert it to
> -- x[0]=b or `read x[0]'. */
> -- if (assoc_p (entry))
> -- {
> -- assoc_insert (assoc_cell (entry), savestring ("0"), newval);
> -- free (newval);
> -- }
> -- else if (array_p (entry))
> -- {
> -- array_insert (array_cell (entry), 0, newval);
> -- free (newval);
> -- }
> -- else
> --#endif
> -- {
> -- FREE (value_cell (entry));
> -- var_setvalue (entry, newval);
> -- }
> -- }
> --
> -- if (mark_modified_vars)
> -- VSETATTR (entry, att_exported);
> --
> -- if (exported_p (entry))
> -- array_needs_making = 1;
> --
> -- return (entry);
> --}
> --
> --/* Bind a variable NAME to VALUE. This conses up the name
> -- and value strings. If we have a temporary environment, we bind there
> -- first, then we bind into shell_variables. */
> --
> --SHELL_VAR *
> --bind_variable (name, value, flags)
> -- const char *name;
> -- char *value;
> -- int flags;
> --{
> -- SHELL_VAR *v, *nv;
> -- VAR_CONTEXT *vc, *nvc;
> -- int level;
> --
> -- if (shell_variables == 0)
> -- create_variable_tables ();
> --
> -- /* If we have a temporary environment, look there first for the variable,
> -- and, if found, modify the value there before modifying it in the
> -- shell_variables table. This allows sourced scripts to modify values
> -- given to them in a temporary environment while modifying the variable
> -- value that the caller sees. */
> -- if (temporary_env)
> -- bind_tempenv_variable (name, value);
> --
> -- /* XXX -- handle local variables here. */
> -- for (vc = shell_variables; vc; vc = vc->down)
> -- {
> -- if (vc_isfuncenv (vc) || vc_isbltnenv (vc))
> -- {
> -- v = hash_lookup (name, vc->table);
> -- nvc = vc;
> -- if (v && nameref_p (v))
> -- {
> -- nv = find_variable_nameref_context (v, vc, &nvc);
> -- if (nv == 0)
> -- {
> -- nv = find_variable_last_nameref_context (v, vc, &nvc);
> -- if (nv && nameref_p (nv))
> -- {
> -- /* If this nameref variable doesn't have a value yet,
> -- set the value. Otherwise, assign using the value as
> -- normal. */
> -- if (nameref_cell (nv) == 0)
> -- return (bind_variable_internal (nv->name, value, nvc->table, 0, flags));
> -- return (bind_variable_internal (nameref_cell (nv), value, nvc->table, 0, flags));
> -- }
> -- else
> -- v = nv;
> -- }
> -- else
> -- v = nv;
> -- }
> -- if (v)
> -- return (bind_variable_internal (v->name, value, nvc->table, 0, flags));
> -- }
> -- }
> -- /* bind_variable_internal will handle nameref resolution in this case */
> -- return (bind_variable_internal (name, value, global_variables->table, 0, flags));
> --}
> --
> --SHELL_VAR *
> --bind_global_variable (name, value, flags)
> -- const char *name;
> -- char *value;
> -- int flags;
> --{
> -- SHELL_VAR *v, *nv;
> -- VAR_CONTEXT *vc, *nvc;
> -- int level;
> --
> -- if (shell_variables == 0)
> -- create_variable_tables ();
> --
> -- /* bind_variable_internal will handle nameref resolution in this case */
> -- return (bind_variable_internal (name, value, global_variables->table, 0, flags));
> --}
> --
> --/* Make VAR, a simple shell variable, have value VALUE. Once assigned a
> -- value, variables are no longer invisible. This is a duplicate of part
> -- of the internals of bind_variable. If the variable is exported, or
> -- all modified variables should be exported, mark the variable for export
> -- and note that the export environment needs to be recreated. */
> --SHELL_VAR *
> --bind_variable_value (var, value, aflags)
> -- SHELL_VAR *var;
> -- char *value;
> -- int aflags;
> --{
> -- char *t;
> -- int invis;
> --
> -- invis = invisible_p (var);
> -- VUNSETATTR (var, att_invisible);
> --
> -- if (var->assign_func)
> -- {
> -- /* If we're appending, we need the old value, so use
> -- make_variable_value */
> -- t = (aflags & ASS_APPEND) ? make_variable_value (var, value, aflags) : value;
> -- (*(var->assign_func)) (var, t, -1, 0);
> -- if (t != value && t)
> -- free (t);
> -- }
> -- else
> -- {
> -- t = make_variable_value (var, value, aflags);
> --#if defined (ARRAY_VARS)
> -- if ((aflags & ASS_NAMEREF) && (t == 0 || *t == 0 || (legal_identifier (t) == 0 && valid_array_reference (t) == 0)))
> --#else
> -- if ((aflags & ASS_NAMEREF) && (t == 0 || *t == 0 || legal_identifier (t) == 0))
> --#endif
> -- {
> -- free (t);
> -- if (invis)
> -- VSETATTR (var, att_invisible); /* XXX */
> -- return ((SHELL_VAR *)NULL);
> -- }
> -- FREE (value_cell (var));
> -- var_setvalue (var, t);
> -- }
> --
> -- INVALIDATE_EXPORTSTR (var);
> --
> -- if (mark_modified_vars)
> -- VSETATTR (var, att_exported);
> --
> -- if (exported_p (var))
> -- array_needs_making = 1;
> --
> -- return (var);
> --}
> --
> --/* Bind/create a shell variable with the name LHS to the RHS.
> -- This creates or modifies a variable such that it is an integer.
> --
> -- This used to be in expr.c, but it is here so that all of the
> -- variable binding stuff is localized. Since we don't want any
> -- recursive evaluation from bind_variable() (possible without this code,
> -- since bind_variable() calls the evaluator for variables with the integer
> -- attribute set), we temporarily turn off the integer attribute for each
> -- variable we set here, then turn it back on after binding as necessary. */
> --
> --SHELL_VAR *
> --bind_int_variable (lhs, rhs)
> -- char *lhs, *rhs;
> --{
> -- register SHELL_VAR *v;
> -- int isint, isarr, implicitarray;
> --
> -- isint = isarr = implicitarray = 0;
> --#if defined (ARRAY_VARS)
> -- if (valid_array_reference (lhs))
> -- {
> -- isarr = 1;
> -- v = array_variable_part (lhs, (char **)0, (int *)0);
> -- }
> -- else
> --#endif
> -- v = find_variable (lhs);
> --
> -- if (v)
> -- {
> -- isint = integer_p (v);
> -- VUNSETATTR (v, att_integer);
> --#if defined (ARRAY_VARS)
> -- if (array_p (v) && isarr == 0)
> -- implicitarray = 1;
> --#endif
> -- }
> --
> --#if defined (ARRAY_VARS)
> -- if (isarr)
> -- v = assign_array_element (lhs, rhs, 0);
> -- else if (implicitarray)
> -- v = bind_array_variable (lhs, 0, rhs, 0);
> -- else
> --#endif
> -- v = bind_variable (lhs, rhs, 0);
> --
> -- if (v && isint)
> -- VSETATTR (v, att_integer);
> --
> -- VUNSETATTR (v, att_invisible);
> --
> -- return (v);
> --}
> --
> --SHELL_VAR *
> --bind_var_to_int (var, val)
> -- char *var;
> -- intmax_t val;
> --{
> -- char ibuf[INT_STRLEN_BOUND (intmax_t) + 1], *p;
> --
> -- p = fmtulong (val, 10, ibuf, sizeof (ibuf), 0);
> -- return (bind_int_variable (var, p));
> --}
> --
> --/* Do a function binding to a variable. You pass the name and
> -- the command to bind to. This conses the name and command. */
> --SHELL_VAR *
> --bind_function (name, value)
> -- const char *name;
> -- COMMAND *value;
> --{
> -- SHELL_VAR *entry;
> --
> -- entry = find_function (name);
> -- if (entry == 0)
> -- {
> -- BUCKET_CONTENTS *elt;
> --
> -- elt = hash_insert (savestring (name), shell_functions, HASH_NOSRCH);
> -- entry = new_shell_variable (name);
> -- elt->data = (PTR_T)entry;
> -- }
> -- else
> -- INVALIDATE_EXPORTSTR (entry);
> --
> -- if (var_isset (entry))
> -- dispose_command (function_cell (entry));
> --
> -- if (value)
> -- var_setfunc (entry, copy_command (value));
> -- else
> -- var_setfunc (entry, 0);
> --
> -- VSETATTR (entry, att_function);
> --
> -- if (mark_modified_vars)
> -- VSETATTR (entry, att_exported);
> --
> -- VUNSETATTR (entry, att_invisible); /* Just to be sure */
> --
> -- if (exported_p (entry))
> -- array_needs_making = 1;
> --
> --#if defined (PROGRAMMABLE_COMPLETION)
> -- set_itemlist_dirty (&it_functions);
> --#endif
> --
> -- return (entry);
> --}
> --
> --#if defined (DEBUGGER)
> --/* Bind a function definition, which includes source file and line number
> -- information in addition to the command, into the FUNCTION_DEF hash table.*/
> --void
> --bind_function_def (name, value)
> -- const char *name;
> -- FUNCTION_DEF *value;
> --{
> -- FUNCTION_DEF *entry;
> -- BUCKET_CONTENTS *elt;
> -- COMMAND *cmd;
> --
> -- entry = find_function_def (name);
> -- if (entry)
> -- {
> -- dispose_function_def_contents (entry);
> -- entry = copy_function_def_contents (value, entry);
> -- }
> -- else
> -- {
> -- cmd = value->command;
> -- value->command = 0;
> -- entry = copy_function_def (value);
> -- value->command = cmd;
> --
> -- elt = hash_insert (savestring (name), shell_function_defs, HASH_NOSRCH);
> -- elt->data = (PTR_T *)entry;
> -- }
> --}
> --#endif /* DEBUGGER */
> --
> --/* Add STRING, which is of the form foo=bar, to the temporary environment
> -- HASH_TABLE (temporary_env). The functions in execute_cmd.c are
> -- responsible for moving the main temporary env to one of the other
> -- temporary environments. The expansion code in subst.c calls this. */
> --int
> --assign_in_env (word, flags)
> -- WORD_DESC *word;
> -- int flags;
> --{
> -- int offset, aflags;
> -- char *name, *temp, *value;
> -- SHELL_VAR *var;
> -- const char *string;
> --
> -- string = word->word;
> --
> -- aflags = 0;
> -- offset = assignment (string, 0);
> -- name = savestring (string);
> -- value = (char *)NULL;
> --
> -- if (name[offset] == '=')
> -- {
> -- name[offset] = 0;
> --
> -- /* don't ignore the `+' when assigning temporary environment */
> -- if (name[offset - 1] == '+')
> -- {
> -- name[offset - 1] = '\0';
> -- aflags |= ASS_APPEND;
> -- }
> --
> -- var = find_variable (name);
> -- if (var && (readonly_p (var) || noassign_p (var)))
> -- {
> -- if (readonly_p (var))
> -- err_readonly (name);
> -- free (name);
> -- return (0);
> -- }
> --
> -- temp = name + offset + 1;
> -- value = expand_assignment_string_to_string (temp, 0);
> --
> -- if (var && (aflags & ASS_APPEND))
> -- {
> -- temp = make_variable_value (var, value, aflags);
> -- FREE (value);
> -- value = temp;
> -- }
> -- }
> --
> -- if (temporary_env == 0)
> -- temporary_env = hash_create (TEMPENV_HASH_BUCKETS);
> --
> -- var = hash_lookup (name, temporary_env);
> -- if (var == 0)
> -- var = make_new_variable (name, temporary_env);
> -- else
> -- FREE (value_cell (var));
> --
> -- if (value == 0)
> -- {
> -- value = (char *)xmalloc (1); /* like do_assignment_internal */
> -- value[0] = '\0';
> -- }
> --
> -- var_setvalue (var, value);
> -- var->attributes |= (att_exported|att_tempvar);
> -- var->context = variable_context; /* XXX */
> --
> -- INVALIDATE_EXPORTSTR (var);
> -- var->exportstr = mk_env_string (name, value, 0);
> --
> -- array_needs_making = 1;
> --
> -- if (flags)
> -- stupidly_hack_special_variables (name);
> --
> -- if (echo_command_at_execute)
> -- /* The Korn shell prints the `+ ' in front of assignment statements,
> -- so we do too. */
> -- xtrace_print_assignment (name, value, 0, 1);
> --
> -- free (name);
> -- return 1;
> --}
> --
> --/* **************************************************************** */
> --/* */
> --/* Copying variables */
> --/* */
> --/* **************************************************************** */
> --
> --#ifdef INCLUDE_UNUSED
> --/* Copy VAR to a new data structure and return that structure. */
> --SHELL_VAR *
> --copy_variable (var)
> -- SHELL_VAR *var;
> --{
> -- SHELL_VAR *copy = (SHELL_VAR *)NULL;
> --
> -- if (var)
> -- {
> -- copy = (SHELL_VAR *)xmalloc (sizeof (SHELL_VAR));
> --
> -- copy->attributes = var->attributes;
> -- copy->name = savestring (var->name);
> --
> -- if (function_p (var))
> -- var_setfunc (copy, copy_command (function_cell (var)));
> --#if defined (ARRAY_VARS)
> -- else if (array_p (var))
> -- var_setarray (copy, array_copy (array_cell (var)));
> -- else if (assoc_p (var))
> -- var_setassoc (copy, assoc_copy (assoc_cell (var)));
> --#endif
> -- else if (nameref_cell (var)) /* XXX - nameref */
> -- var_setref (copy, savestring (nameref_cell (var)));
> -- else if (value_cell (var)) /* XXX - nameref */
> -- var_setvalue (copy, savestring (value_cell (var)));
> -- else
> -- var_setvalue (copy, (char *)NULL);
> --
> -- copy->dynamic_value = var->dynamic_value;
> -- copy->assign_func = var->assign_func;
> --
> -- copy->exportstr = COPY_EXPORTSTR (var);
> --
> -- copy->context = var->context;
> -- }
> -- return (copy);
> --}
> --#endif
> --
> --/* **************************************************************** */
> --/* */
> --/* Deleting and unsetting variables */
> --/* */
> --/* **************************************************************** */
> --
> --/* Dispose of the information attached to VAR. */
> --static void
> --dispose_variable_value (var)
> -- SHELL_VAR *var;
> --{
> -- if (function_p (var))
> -- dispose_command (function_cell (var));
> --#if defined (ARRAY_VARS)
> -- else if (array_p (var))
> -- array_dispose (array_cell (var));
> -- else if (assoc_p (var))
> -- assoc_dispose (assoc_cell (var));
> --#endif
> -- else if (nameref_p (var))
> -- FREE (nameref_cell (var));
> -- else
> -- FREE (value_cell (var));
> --}
> --
> --void
> --dispose_variable (var)
> -- SHELL_VAR *var;
> --{
> -- if (var == 0)
> -- return;
> --
> -- if (nofree_p (var) == 0)
> -- dispose_variable_value (var);
> --
> -- FREE_EXPORTSTR (var);
> --
> -- free (var->name);
> --
> -- if (exported_p (var))
> -- array_needs_making = 1;
> --
> -- free (var);
> --}
> --
> --/* Unset the shell variable referenced by NAME. Unsetting a nameref variable
> -- unsets the variable it resolves to but leaves the nameref alone. */
> --int
> --unbind_variable (name)
> -- const char *name;
> --{
> -- SHELL_VAR *v, *nv;
> -- int r;
> --
> -- v = var_lookup (name, shell_variables);
> -- nv = (v && nameref_p (v)) ? find_variable_nameref (v) : (SHELL_VAR *)NULL;
> --
> -- r = nv ? makunbound (nv->name, shell_variables) : makunbound (name, shell_variables);
> -- return r;
> --}
> --
> --/* Unbind NAME, where NAME is assumed to be a nameref variable */
> --int
> --unbind_nameref (name)
> -- const char *name;
> --{
> -- SHELL_VAR *v;
> --
> -- v = var_lookup (name, shell_variables);
> -- if (v && nameref_p (v))
> -- return makunbound (name, shell_variables);
> -- return 0;
> --}
> --
> --/* Unset the shell function named NAME. */
> --int
> --unbind_func (name)
> -- const char *name;
> --{
> -- BUCKET_CONTENTS *elt;
> -- SHELL_VAR *func;
> --
> -- elt = hash_remove (name, shell_functions, 0);
> --
> -- if (elt == 0)
> -- return -1;
> --
> --#if defined (PROGRAMMABLE_COMPLETION)
> -- set_itemlist_dirty (&it_functions);
> --#endif
> --
> -- func = (SHELL_VAR *)elt->data;
> -- if (func)
> -- {
> -- if (exported_p (func))
> -- array_needs_making++;
> -- dispose_variable (func);
> -- }
> --
> -- free (elt->key);
> -- free (elt);
> --
> -- return 0;
> --}
> --
> --#if defined (DEBUGGER)
> --int
> --unbind_function_def (name)
> -- const char *name;
> --{
> -- BUCKET_CONTENTS *elt;
> -- FUNCTION_DEF *funcdef;
> --
> -- elt = hash_remove (name, shell_function_defs, 0);
> --
> -- if (elt == 0)
> -- return -1;
> --
> -- funcdef = (FUNCTION_DEF *)elt->data;
> -- if (funcdef)
> -- dispose_function_def (funcdef);
> --
> -- free (elt->key);
> -- free (elt);
> --
> -- return 0;
> --}
> --#endif /* DEBUGGER */
> --
> --int
> --delete_var (name, vc)
> -- const char *name;
> -- VAR_CONTEXT *vc;
> --{
> -- BUCKET_CONTENTS *elt;
> -- SHELL_VAR *old_var;
> -- VAR_CONTEXT *v;
> --
> -- for (elt = (BUCKET_CONTENTS *)NULL, v = vc; v; v = v->down)
> -- if (elt = hash_remove (name, v->table, 0))
> -- break;
> --
> -- if (elt == 0)
> -- return (-1);
> --
> -- old_var = (SHELL_VAR *)elt->data;
> -- free (elt->key);
> -- free (elt);
> --
> -- dispose_variable (old_var);
> -- return (0);
> --}
> --
> --/* Make the variable associated with NAME go away. HASH_LIST is the
> -- hash table from which this variable should be deleted (either
> -- shell_variables or shell_functions).
> -- Returns non-zero if the variable couldn't be found. */
> --int
> --makunbound (name, vc)
> -- const char *name;
> -- VAR_CONTEXT *vc;
> --{
> -- BUCKET_CONTENTS *elt, *new_elt;
> -- SHELL_VAR *old_var;
> -- VAR_CONTEXT *v;
> -- char *t;
> --
> -- for (elt = (BUCKET_CONTENTS *)NULL, v = vc; v; v = v->down)
> -- if (elt = hash_remove (name, v->table, 0))
> -- break;
> --
> -- if (elt == 0)
> -- return (-1);
> --
> -- old_var = (SHELL_VAR *)elt->data;
> --
> -- if (old_var && exported_p (old_var))
> -- array_needs_making++;
> --
> -- /* If we're unsetting a local variable and we're still executing inside
> -- the function, just mark the variable as invisible. The function
> -- eventually called by pop_var_context() will clean it up later. This
> -- must be done so that if the variable is subsequently assigned a new
> -- value inside the function, the `local' attribute is still present.
> -- We also need to add it back into the correct hash table. */
> -- if (old_var && local_p (old_var) && variable_context == old_var->context)
> -- {
> -- if (nofree_p (old_var))
> -- var_setvalue (old_var, (char *)NULL);
> --#if defined (ARRAY_VARS)
> -- else if (array_p (old_var))
> -- array_dispose (array_cell (old_var));
> -- else if (assoc_p (old_var))
> -- assoc_dispose (assoc_cell (old_var));
> --#endif
> -- else if (nameref_p (old_var))
> -- FREE (nameref_cell (old_var));
> -- else
> -- FREE (value_cell (old_var));
> -- /* Reset the attributes. Preserve the export attribute if the variable
> -- came from a temporary environment. Make sure it stays local, and
> -- make it invisible. */
> -- old_var->attributes = (exported_p (old_var) && tempvar_p (old_var)) ? att_exported : 0;
> -- VSETATTR (old_var, att_local);
> -- VSETATTR (old_var, att_invisible);
> -- var_setvalue (old_var, (char *)NULL);
> -- INVALIDATE_EXPORTSTR (old_var);
> --
> -- new_elt = hash_insert (savestring (old_var->name), v->table, 0);
> -- new_elt->data = (PTR_T)old_var;
> -- stupidly_hack_special_variables (old_var->name);
> --
> -- free (elt->key);
> -- free (elt);
> -- return (0);
> -- }
> --
> -- /* Have to save a copy of name here, because it might refer to
> -- old_var->name. If so, stupidly_hack_special_variables will
> -- reference freed memory. */
> -- t = savestring (name);
> --
> -- free (elt->key);
> -- free (elt);
> --
> -- dispose_variable (old_var);
> -- stupidly_hack_special_variables (t);
> -- free (t);
> --
> -- return (0);
> --}
> --
> --/* Get rid of all of the variables in the current context. */
> --void
> --kill_all_local_variables ()
> --{
> -- VAR_CONTEXT *vc;
> --
> -- for (vc = shell_variables; vc; vc = vc->down)
> -- if (vc_isfuncenv (vc) && vc->scope == variable_context)
> -- break;
> -- if (vc == 0)
> -- return; /* XXX */
> --
> -- if (vc->table && vc_haslocals (vc))
> -- {
> -- delete_all_variables (vc->table);
> -- hash_dispose (vc->table);
> -- }
> -- vc->table = (HASH_TABLE *)NULL;
> --}
> --
> --static void
> --free_variable_hash_data (data)
> -- PTR_T data;
> --{
> -- SHELL_VAR *var;
> --
> -- var = (SHELL_VAR *)data;
> -- dispose_variable (var);
> --}
> --
> --/* Delete the entire contents of the hash table. */
> --void
> --delete_all_variables (hashed_vars)
> -- HASH_TABLE *hashed_vars;
> --{
> -- hash_flush (hashed_vars, free_variable_hash_data);
> --}
> --
> --/* **************************************************************** */
> --/* */
> --/* Setting variable attributes */
> --/* */
> --/* **************************************************************** */
> --
> --#define FIND_OR_MAKE_VARIABLE(name, entry) \
> -- do \
> -- { \
> -- entry = find_variable (name); \
> -- if (!entry) \
> -- { \
> -- entry = bind_variable (name, "", 0); \
> -- if (!no_invisible_vars && entry) entry->attributes |= att_invisible; \
> -- } \
> -- } \
> -- while (0)
> --
> --/* Make the variable associated with NAME be readonly.
> -- If NAME does not exist yet, create it. */
> --void
> --set_var_read_only (name)
> -- char *name;
> --{
> -- SHELL_VAR *entry;
> --
> -- FIND_OR_MAKE_VARIABLE (name, entry);
> -- VSETATTR (entry, att_readonly);
> --}
> --
> --#ifdef INCLUDE_UNUSED
> --/* Make the function associated with NAME be readonly.
> -- If NAME does not exist, we just punt, like auto_export code below. */
> --void
> --set_func_read_only (name)
> -- const char *name;
> --{
> -- SHELL_VAR *entry;
> --
> -- entry = find_function (name);
> -- if (entry)
> -- VSETATTR (entry, att_readonly);
> --}
> --
> --/* Make the variable associated with NAME be auto-exported.
> -- If NAME does not exist yet, create it. */
> --void
> --set_var_auto_export (name)
> -- char *name;
> --{
> -- SHELL_VAR *entry;
> --
> -- FIND_OR_MAKE_VARIABLE (name, entry);
> -- set_auto_export (entry);
> --}
> --
> --/* Make the function associated with NAME be auto-exported. */
> --void
> --set_func_auto_export (name)
> -- const char *name;
> --{
> -- SHELL_VAR *entry;
> --
> -- entry = find_function (name);
> -- if (entry)
> -- set_auto_export (entry);
> --}
> --#endif
> --
> --/* **************************************************************** */
> --/* */
> --/* Creating lists of variables */
> --/* */
> --/* **************************************************************** */
> --
> --static VARLIST *
> --vlist_alloc (nentries)
> -- int nentries;
> --{
> -- VARLIST *vlist;
> --
> -- vlist = (VARLIST *)xmalloc (sizeof (VARLIST));
> -- vlist->list = (SHELL_VAR **)xmalloc ((nentries + 1) * sizeof (SHELL_VAR *));
> -- vlist->list_size = nentries;
> -- vlist->list_len = 0;
> -- vlist->list[0] = (SHELL_VAR *)NULL;
> --
> -- return vlist;
> --}
> --
> --static VARLIST *
> --vlist_realloc (vlist, n)
> -- VARLIST *vlist;
> -- int n;
> --{
> -- if (vlist == 0)
> -- return (vlist = vlist_alloc (n));
> -- if (n > vlist->list_size)
> -- {
> -- vlist->list_size = n;
> -- vlist->list = (SHELL_VAR **)xrealloc (vlist->list, (vlist->list_size + 1) * sizeof (SHELL_VAR *));
> -- }
> -- return vlist;
> --}
> --
> --static void
> --vlist_add (vlist, var, flags)
> -- VARLIST *vlist;
> -- SHELL_VAR *var;
> -- int flags;
> --{
> -- register int i;
> --
> -- for (i = 0; i < vlist->list_len; i++)
> -- if (STREQ (var->name, vlist->list[i]->name))
> -- break;
> -- if (i < vlist->list_len)
> -- return;
> --
> -- if (i >= vlist->list_size)
> -- vlist = vlist_realloc (vlist, vlist->list_size + 16);
> --
> -- vlist->list[vlist->list_len++] = var;
> -- vlist->list[vlist->list_len] = (SHELL_VAR *)NULL;
> --}
> --
> --/* Map FUNCTION over the variables in VAR_HASH_TABLE. Return an array of the
> -- variables for which FUNCTION returns a non-zero value. A NULL value
> -- for FUNCTION means to use all variables. */
> --SHELL_VAR **
> --map_over (function, vc)
> -- sh_var_map_func_t *function;
> -- VAR_CONTEXT *vc;
> --{
> -- VAR_CONTEXT *v;
> -- VARLIST *vlist;
> -- SHELL_VAR **ret;
> -- int nentries;
> --
> -- for (nentries = 0, v = vc; v; v = v->down)
> -- nentries += HASH_ENTRIES (v->table);
> --
> -- if (nentries == 0)
> -- return (SHELL_VAR **)NULL;
> --
> -- vlist = vlist_alloc (nentries);
> --
> -- for (v = vc; v; v = v->down)
> -- flatten (v->table, function, vlist, 0);
> --
> -- ret = vlist->list;
> -- free (vlist);
> -- return ret;
> --}
> --
> --SHELL_VAR **
> --map_over_funcs (function)
> -- sh_var_map_func_t *function;
> --{
> -- VARLIST *vlist;
> -- SHELL_VAR **ret;
> --
> -- if (shell_functions == 0 || HASH_ENTRIES (shell_functions) == 0)
> -- return ((SHELL_VAR **)NULL);
> --
> -- vlist = vlist_alloc (HASH_ENTRIES (shell_functions));
> --
> -- flatten (shell_functions, function, vlist, 0);
> --
> -- ret = vlist->list;
> -- free (vlist);
> -- return ret;
> --}
> --
> --/* Flatten VAR_HASH_TABLE, applying FUNC to each member and adding those
> -- elements for which FUNC succeeds to VLIST->list. FLAGS is reserved
> -- for future use. Only unique names are added to VLIST. If FUNC is
> -- NULL, each variable in VAR_HASH_TABLE is added to VLIST. If VLIST is
> -- NULL, FUNC is applied to each SHELL_VAR in VAR_HASH_TABLE. If VLIST
> -- and FUNC are both NULL, nothing happens. */
> --static void
> --flatten (var_hash_table, func, vlist, flags)
> -- HASH_TABLE *var_hash_table;
> -- sh_var_map_func_t *func;
> -- VARLIST *vlist;
> -- int flags;
> --{
> -- register int i;
> -- register BUCKET_CONTENTS *tlist;
> -- int r;
> -- SHELL_VAR *var;
> --
> -- if (var_hash_table == 0 || (HASH_ENTRIES (var_hash_table) == 0) || (vlist == 0 && func == 0))
> -- return;
> --
> -- for (i = 0; i < var_hash_table->nbuckets; i++)
> -- {
> -- for (tlist = hash_items (i, var_hash_table); tlist; tlist = tlist->next)
> -- {
> -- var = (SHELL_VAR *)tlist->data;
> --
> -- r = func ? (*func) (var) : 1;
> -- if (r && vlist)
> -- vlist_add (vlist, var, flags);
> -- }
> -- }
> --}
> --
> --void
> --sort_variables (array)
> -- SHELL_VAR **array;
> --{
> -- qsort (array, strvec_len ((char **)array), sizeof (SHELL_VAR *), (QSFUNC *)qsort_var_comp);
> --}
> --
> --static int
> --qsort_var_comp (var1, var2)
> -- SHELL_VAR **var1, **var2;
> --{
> -- int result;
> --
> -- if ((result = (*var1)->name[0] - (*var2)->name[0]) == 0)
> -- result = strcmp ((*var1)->name, (*var2)->name);
> --
> -- return (result);
> --}
> --
> --/* Apply FUNC to each variable in SHELL_VARIABLES, adding each one for
> -- which FUNC succeeds to an array of SHELL_VAR *s. Returns the array. */
> --static SHELL_VAR **
> --vapply (func)
> -- sh_var_map_func_t *func;
> --{
> -- SHELL_VAR **list;
> --
> -- list = map_over (func, shell_variables);
> -- if (list /* && posixly_correct */)
> -- sort_variables (list);
> -- return (list);
> --}
> --
> --/* Apply FUNC to each variable in SHELL_FUNCTIONS, adding each one for
> -- which FUNC succeeds to an array of SHELL_VAR *s. Returns the array. */
> --static SHELL_VAR **
> --fapply (func)
> -- sh_var_map_func_t *func;
> --{
> -- SHELL_VAR **list;
> --
> -- list = map_over_funcs (func);
> -- if (list /* && posixly_correct */)
> -- sort_variables (list);
> -- return (list);
> --}
> --
> --/* Create a NULL terminated array of all the shell variables. */
> --SHELL_VAR **
> --all_shell_variables ()
> --{
> -- return (vapply ((sh_var_map_func_t *)NULL));
> --}
> --
> --/* Create a NULL terminated array of all the shell functions. */
> --SHELL_VAR **
> --all_shell_functions ()
> --{
> -- return (fapply ((sh_var_map_func_t *)NULL));
> --}
> --
> --static int
> --visible_var (var)
> -- SHELL_VAR *var;
> --{
> -- return (invisible_p (var) == 0);
> --}
> --
> --SHELL_VAR **
> --all_visible_functions ()
> --{
> -- return (fapply (visible_var));
> --}
> --
> --SHELL_VAR **
> --all_visible_variables ()
> --{
> -- return (vapply (visible_var));
> --}
> --
> --/* Return non-zero if the variable VAR is visible and exported. Array
> -- variables cannot be exported. */
> --static int
> --visible_and_exported (var)
> -- SHELL_VAR *var;
> --{
> -- return (invisible_p (var) == 0 && exported_p (var));
> --}
> --
> --/* Candidate variables for the export environment are either valid variables
> -- with the export attribute or invalid variables inherited from the initial
> -- environment and simply passed through. */
> --static int
> --export_environment_candidate (var)
> -- SHELL_VAR *var;
> --{
> -- return (exported_p (var) && (invisible_p (var) == 0 || imported_p (var)));
> --}
> --
> --/* Return non-zero if VAR is a local variable in the current context and
> -- is exported. */
> --static int
> --local_and_exported (var)
> -- SHELL_VAR *var;
> --{
> -- return (invisible_p (var) == 0 && local_p (var) && var->context == variable_context && exported_p (var));
> --}
> --
> --SHELL_VAR **
> --all_exported_variables ()
> --{
> -- return (vapply (visible_and_exported));
> --}
> --
> --SHELL_VAR **
> --local_exported_variables ()
> --{
> -- return (vapply (local_and_exported));
> --}
> --
> --static int
> --variable_in_context (var)
> -- SHELL_VAR *var;
> --{
> -- return (invisible_p (var) == 0 && local_p (var) && var->context == variable_context);
> --}
> --
> --SHELL_VAR **
> --all_local_variables ()
> --{
> -- VARLIST *vlist;
> -- SHELL_VAR **ret;
> -- VAR_CONTEXT *vc;
> --
> -- vc = shell_variables;
> -- for (vc = shell_variables; vc; vc = vc->down)
> -- if (vc_isfuncenv (vc) && vc->scope == variable_context)
> -- break;
> --
> -- if (vc == 0)
> -- {
> -- internal_error (_("all_local_variables: no function context at current scope"));
> -- return (SHELL_VAR **)NULL;
> -- }
> -- if (vc->table == 0 || HASH_ENTRIES (vc->table) == 0 || vc_haslocals (vc) == 0)
> -- return (SHELL_VAR **)NULL;
> --
> -- vlist = vlist_alloc (HASH_ENTRIES (vc->table));
> --
> -- flatten (vc->table, variable_in_context, vlist, 0);
> --
> -- ret = vlist->list;
> -- free (vlist);
> -- if (ret)
> -- sort_variables (ret);
> -- return ret;
> --}
> --
> --#if defined (ARRAY_VARS)
> --/* Return non-zero if the variable VAR is visible and an array. */
> --static int
> --visible_array_vars (var)
> -- SHELL_VAR *var;
> --{
> -- return (invisible_p (var) == 0 && array_p (var));
> --}
> --
> --SHELL_VAR **
> --all_array_variables ()
> --{
> -- return (vapply (visible_array_vars));
> --}
> --#endif /* ARRAY_VARS */
> --
> --char **
> --all_variables_matching_prefix (prefix)
> -- const char *prefix;
> --{
> -- SHELL_VAR **varlist;
> -- char **rlist;
> -- int vind, rind, plen;
> --
> -- plen = STRLEN (prefix);
> -- varlist = all_visible_variables ();
> -- for (vind = 0; varlist && varlist[vind]; vind++)
> -- ;
> -- if (varlist == 0 || vind == 0)
> -- return ((char **)NULL);
> -- rlist = strvec_create (vind + 1);
> -- for (vind = rind = 0; varlist[vind]; vind++)
> -- {
> -- if (plen == 0 || STREQN (prefix, varlist[vind]->name, plen))
> -- rlist[rind++] = savestring (varlist[vind]->name);
> -- }
> -- rlist[rind] = (char *)0;
> -- free (varlist);
> --
> -- return rlist;
> --}
> --
> --/* **************************************************************** */
> --/* */
> --/* Managing temporary variable scopes */
> --/* */
> --/* **************************************************************** */
> --
> --/* Make variable NAME have VALUE in the temporary environment. */
> --static SHELL_VAR *
> --bind_tempenv_variable (name, value)
> -- const char *name;
> -- char *value;
> --{
> -- SHELL_VAR *var;
> --
> -- var = temporary_env ? hash_lookup (name, temporary_env) : (SHELL_VAR *)NULL;
> --
> -- if (var)
> -- {
> -- FREE (value_cell (var));
> -- var_setvalue (var, savestring (value));
> -- INVALIDATE_EXPORTSTR (var);
> -- }
> --
> -- return (var);
> --}
> --
> --/* Find a variable in the temporary environment that is named NAME.
> -- Return the SHELL_VAR *, or NULL if not found. */
> --SHELL_VAR *
> --find_tempenv_variable (name)
> -- const char *name;
> --{
> -- return (temporary_env ? hash_lookup (name, temporary_env) : (SHELL_VAR *)NULL);
> --}
> --
> --char **tempvar_list;
> --int tvlist_ind;
> --
> --/* Push the variable described by (SHELL_VAR *)DATA down to the next
> -- variable context from the temporary environment. */
> --static void
> --push_temp_var (data)
> -- PTR_T data;
> --{
> -- SHELL_VAR *var, *v;
> -- HASH_TABLE *binding_table;
> --
> -- var = (SHELL_VAR *)data;
> --
> -- binding_table = shell_variables->table;
> -- if (binding_table == 0)
> -- {
> -- if (shell_variables == global_variables)
> -- /* shouldn't happen */
> -- binding_table = shell_variables->table = global_variables->table = hash_create (0);
> -- else
> -- binding_table = shell_variables->table = hash_create (TEMPENV_HASH_BUCKETS);
> -- }
> --
> -- v = bind_variable_internal (var->name, value_cell (var), binding_table, 0, 0);
> --
> -- /* XXX - should we set the context here? It shouldn't matter because of how
> -- assign_in_env works, but might want to check. */
> -- if (binding_table == global_variables->table) /* XXX */
> -- var->attributes &= ~(att_tempvar|att_propagate);
> -- else
> -- {
> -- var->attributes |= att_propagate;
> -- if (binding_table == shell_variables->table)
> -- shell_variables->flags |= VC_HASTMPVAR;
> -- }
> -- v->attributes |= var->attributes;
> --
> -- if (find_special_var (var->name) >= 0)
> -- tempvar_list[tvlist_ind++] = savestring (var->name);
> --
> -- dispose_variable (var);
> --}
> --
> --static void
> --propagate_temp_var (data)
> -- PTR_T data;
> --{
> -- SHELL_VAR *var;
> --
> -- var = (SHELL_VAR *)data;
> -- if (tempvar_p (var) && (var->attributes & att_propagate))
> -- push_temp_var (data);
> -- else
> -- {
> -- if (find_special_var (var->name) >= 0)
> -- tempvar_list[tvlist_ind++] = savestring (var->name);
> -- dispose_variable (var);
> -- }
> --}
> --
> --/* Free the storage used in the hash table for temporary
> -- environment variables. PUSHF is a function to be called
> -- to free each hash table entry. It takes care of pushing variables
> -- to previous scopes if appropriate. PUSHF stores names of variables
> -- that require special handling (e.g., IFS) on tempvar_list, so this
> -- function can call stupidly_hack_special_variables on all the
> -- variables in the list when the temporary hash table is destroyed. */
> --static void
> --dispose_temporary_env (pushf)
> -- sh_free_func_t *pushf;
> --{
> -- int i;
> --
> -- tempvar_list = strvec_create (HASH_ENTRIES (temporary_env) + 1);
> -- tempvar_list[tvlist_ind = 0] = 0;
> --
> -- hash_flush (temporary_env, pushf);
> -- hash_dispose (temporary_env);
> -- temporary_env = (HASH_TABLE *)NULL;
> --
> -- tempvar_list[tvlist_ind] = 0;
> --
> -- array_needs_making = 1;
> --
> --#if 0
> -- sv_ifs ("IFS"); /* XXX here for now -- check setifs in assign_in_env */
> --#endif
> -- for (i = 0; i < tvlist_ind; i++)
> -- stupidly_hack_special_variables (tempvar_list[i]);
> --
> -- strvec_dispose (tempvar_list);
> -- tempvar_list = 0;
> -- tvlist_ind = 0;
> --}
> --
> --void
> --dispose_used_env_vars ()
> --{
> -- if (temporary_env)
> -- {
> -- dispose_temporary_env (propagate_temp_var);
> -- maybe_make_export_env ();
> -- }
> --}
> --
> --/* Take all of the shell variables in the temporary environment HASH_TABLE
> -- and make shell variables from them at the current variable context. */
> --void
> --merge_temporary_env ()
> --{
> -- if (temporary_env)
> -- dispose_temporary_env (push_temp_var);
> --}
> --
> --/* **************************************************************** */
> --/* */
> --/* Creating and manipulating the environment */
> --/* */
> --/* **************************************************************** */
> --
> --static inline char *
> --mk_env_string (name, value, isfunc)
> -- const char *name, *value;
> -- int isfunc;
> --{
> -- size_t name_len, value_len;
> -- char *p, *q;
> --
> -- name_len = strlen (name);
> -- value_len = STRLEN (value);
> --
> -- /* If we are exporting a shell function, construct the encoded function
> -- name. */
> -- if (isfunc && value)
> -- {
> -- p = (char *)xmalloc (BASHFUNC_PREFLEN + name_len + BASHFUNC_SUFFLEN + value_len + 2);
> -- q = p;
> -- memcpy (q, BASHFUNC_PREFIX, BASHFUNC_PREFLEN);
> -- q += BASHFUNC_PREFLEN;
> -- memcpy (q, name, name_len);
> -- q += name_len;
> -- memcpy (q, BASHFUNC_SUFFIX, BASHFUNC_SUFFLEN);
> -- q += BASHFUNC_SUFFLEN;
> -- }
> -- else
> -- {
> -- p = (char *)xmalloc (2 + name_len + value_len);
> -- memcpy (p, name, name_len);
> -- q = p + name_len;
> -- }
> --
> -- q[0] = '=';
> -- if (value && *value)
> -- memcpy (q + 1, value, value_len + 1);
> -- else
> -- q[1] = '\0';
> --
> -- return (p);
> --}
> --
> --#ifdef DEBUG
> --/* Debugging */
> --static int
> --valid_exportstr (v)
> -- SHELL_VAR *v;
> --{
> -- char *s;
> --
> -- s = v->exportstr;
> -- if (s == 0)
> -- {
> -- internal_error (_("%s has null exportstr"), v->name);
> -- return (0);
> -- }
> -- if (legal_variable_starter ((unsigned char)*s) == 0)
> -- {
> -- internal_error (_("invalid character %d in exportstr for %s"), *s, v->name);
> -- return (0);
> -- }
> -- for (s = v->exportstr + 1; s && *s; s++)
> -- {
> -- if (*s == '=')
> -- break;
> -- if (legal_variable_char ((unsigned char)*s) == 0)
> -- {
> -- internal_error (_("invalid character %d in exportstr for %s"), *s, v->name);
> -- return (0);
> -- }
> -- }
> -- if (*s != '=')
> -- {
> -- internal_error (_("no `=' in exportstr for %s"), v->name);
> -- return (0);
> -- }
> -- return (1);
> --}
> --#endif
> --
> --static char **
> --make_env_array_from_var_list (vars)
> -- SHELL_VAR **vars;
> --{
> -- register int i, list_index;
> -- register SHELL_VAR *var;
> -- char **list, *value;
> --
> -- list = strvec_create ((1 + strvec_len ((char **)vars)));
> --
> --#define USE_EXPORTSTR (value == var->exportstr)
> --
> -- for (i = 0, list_index = 0; var = vars[i]; i++)
> -- {
> --#if defined (__CYGWIN__)
> -- /* We don't use the exportstr stuff on Cygwin at all. */
> -- INVALIDATE_EXPORTSTR (var);
> --#endif
> -- if (var->exportstr)
> -- value = var->exportstr;
> -- else if (function_p (var))
> -- value = named_function_string ((char *)NULL, function_cell (var), 0);
> --#if defined (ARRAY_VARS)
> -- else if (array_p (var))
> --# if ARRAY_EXPORT
> -- value = array_to_assignment_string (array_cell (var));
> --# else
> -- continue; /* XXX array vars cannot yet be exported */
> --# endif /* ARRAY_EXPORT */
> -- else if (assoc_p (var))
> --# if 0
> -- value = assoc_to_assignment_string (assoc_cell (var));
> --# else
> -- continue; /* XXX associative array vars cannot yet be exported */
> --# endif
> --#endif
> -- else
> -- value = value_cell (var);
> --
> -- if (value)
> -- {
> -- /* Gee, I'd like to get away with not using savestring() if we're
> -- using the cached exportstr... */
> -- list[list_index] = USE_EXPORTSTR ? savestring (value)
> -- : mk_env_string (var->name, value, function_p (var));
> --
> -- if (USE_EXPORTSTR == 0)
> -- SAVE_EXPORTSTR (var, list[list_index]);
> --
> -- list_index++;
> --#undef USE_EXPORTSTR
> --
> --#if 0 /* not yet */
> --#if defined (ARRAY_VARS)
> -- if (array_p (var) || assoc_p (var))
> -- free (value);
> --#endif
> --#endif
> -- }
> -- }
> --
> -- list[list_index] = (char *)NULL;
> -- return (list);
> --}
> --
> --/* Make an array of assignment statements from the hash table
> -- HASHED_VARS which contains SHELL_VARs. Only visible, exported
> -- variables are eligible. */
> --static char **
> --make_var_export_array (vcxt)
> -- VAR_CONTEXT *vcxt;
> --{
> -- char **list;
> -- SHELL_VAR **vars;
> --
> --#if 0
> -- vars = map_over (visible_and_exported, vcxt);
> --#else
> -- vars = map_over (export_environment_candidate, vcxt);
> --#endif
> --
> -- if (vars == 0)
> -- return (char **)NULL;
> --
> -- list = make_env_array_from_var_list (vars);
> --
> -- free (vars);
> -- return (list);
> --}
> --
> --static char **
> --make_func_export_array ()
> --{
> -- char **list;
> -- SHELL_VAR **vars;
> --
> -- vars = map_over_funcs (visible_and_exported);
> -- if (vars == 0)
> -- return (char **)NULL;
> --
> -- list = make_env_array_from_var_list (vars);
> --
> -- free (vars);
> -- return (list);
> --}
> --
> --/* Add ENVSTR to the end of the exported environment, EXPORT_ENV. */
> --#define add_to_export_env(envstr,do_alloc) \
> --do \
> -- { \
> -- if (export_env_index >= (export_env_size - 1)) \
> -- { \
> -- export_env_size += 16; \
> -- export_env = strvec_resize (export_env, export_env_size); \
> -- environ = export_env; \
> -- } \
> -- export_env[export_env_index++] = (do_alloc) ? savestring (envstr) : envstr; \
> -- export_env[export_env_index] = (char *)NULL; \
> -- } while (0)
> --
> --/* Add ASSIGN to EXPORT_ENV, or supercede a previous assignment in the
> -- array with the same left-hand side. Return the new EXPORT_ENV. */
> --char **
> --add_or_supercede_exported_var (assign, do_alloc)
> -- char *assign;
> -- int do_alloc;
> --{
> -- register int i;
> -- int equal_offset;
> --
> -- equal_offset = assignment (assign, 0);
> -- if (equal_offset == 0)
> -- return (export_env);
> --
> -- /* If this is a function, then only supersede the function definition.
> -- We do this by including the `=() {' in the comparison, like
> -- initialize_shell_variables does. */
> -- if (assign[equal_offset + 1] == '(' &&
> -- strncmp (assign + equal_offset + 2, ") {", 3) == 0) /* } */
> -- equal_offset += 4;
> --
> -- for (i = 0; i < export_env_index; i++)
> -- {
> -- if (STREQN (assign, export_env[i], equal_offset + 1))
> -- {
> -- free (export_env[i]);
> -- export_env[i] = do_alloc ? savestring (assign) : assign;
> -- return (export_env);
> -- }
> -- }
> -- add_to_export_env (assign, do_alloc);
> -- return (export_env);
> --}
> --
> --static void
> --add_temp_array_to_env (temp_array, do_alloc, do_supercede)
> -- char **temp_array;
> -- int do_alloc, do_supercede;
> --{
> -- register int i;
> --
> -- if (temp_array == 0)
> -- return;
> --
> -- for (i = 0; temp_array[i]; i++)
> -- {
> -- if (do_supercede)
> -- export_env = add_or_supercede_exported_var (temp_array[i], do_alloc);
> -- else
> -- add_to_export_env (temp_array[i], do_alloc);
> -- }
> --
> -- free (temp_array);
> --}
> --
> --/* Make the environment array for the command about to be executed, if the
> -- array needs making. Otherwise, do nothing. If a shell action could
> -- change the array that commands receive for their environment, then the
> -- code should `array_needs_making++'.
> --
> -- The order to add to the array is:
> -- temporary_env
> -- list of var contexts whose head is shell_variables
> -- shell_functions
> --
> -- This is the shell variable lookup order. We add only new variable
> -- names at each step, which allows local variables and variables in
> -- the temporary environments to shadow variables in the global (or
> -- any previous) scope.
> --*/
> --
> --static int
> --n_shell_variables ()
> --{
> -- VAR_CONTEXT *vc;
> -- int n;
> --
> -- for (n = 0, vc = shell_variables; vc; vc = vc->down)
> -- n += HASH_ENTRIES (vc->table);
> -- return n;
> --}
> --
> --int
> --chkexport (name)
> -- char *name;
> --{
> -- SHELL_VAR *v;
> --
> -- v = find_variable (name);
> -- if (v && exported_p (v))
> -- {
> -- array_needs_making = 1;
> -- maybe_make_export_env ();
> -- return 1;
> -- }
> -- return 0;
> --}
> --
> --void
> --maybe_make_export_env ()
> --{
> -- register char **temp_array;
> -- int new_size;
> -- VAR_CONTEXT *tcxt;
> --
> -- if (array_needs_making)
> -- {
> -- if (export_env)
> -- strvec_flush (export_env);
> --
> -- /* Make a guess based on how many shell variables and functions we
> -- have. Since there will always be array variables, and array
> -- variables are not (yet) exported, this will always be big enough
> -- for the exported variables and functions. */
> -- new_size = n_shell_variables () + HASH_ENTRIES (shell_functions) + 1 +
> -- HASH_ENTRIES (temporary_env);
> -- if (new_size > export_env_size)
> -- {
> -- export_env_size = new_size;
> -- export_env = strvec_resize (export_env, export_env_size);
> -- environ = export_env;
> -- }
> -- export_env[export_env_index = 0] = (char *)NULL;
> --
> -- /* Make a dummy variable context from the temporary_env, stick it on
> -- the front of shell_variables, call make_var_export_array on the
> -- whole thing to flatten it, and convert the list of SHELL_VAR *s
> -- to the form needed by the environment. */
> -- if (temporary_env)
> -- {
> -- tcxt = new_var_context ((char *)NULL, 0);
> -- tcxt->table = temporary_env;
> -- tcxt->down = shell_variables;
> -- }
> -- else
> -- tcxt = shell_variables;
> --
> -- temp_array = make_var_export_array (tcxt);
> -- if (temp_array)
> -- add_temp_array_to_env (temp_array, 0, 0);
> --
> -- if (tcxt != shell_variables)
> -- free (tcxt);
> --
> --#if defined (RESTRICTED_SHELL)
> -- /* Restricted shells may not export shell functions. */
> -- temp_array = restricted ? (char **)0 : make_func_export_array ();
> --#else
> -- temp_array = make_func_export_array ();
> --#endif
> -- if (temp_array)
> -- add_temp_array_to_env (temp_array, 0, 0);
> --
> -- array_needs_making = 0;
> -- }
> --}
> --
> --/* This is an efficiency hack. PWD and OLDPWD are auto-exported, so
> -- we will need to remake the exported environment every time we
> -- change directories. `_' is always put into the environment for
> -- every external command, so without special treatment it will always
> -- cause the environment to be remade.
> --
> -- If there is no other reason to make the exported environment, we can
> -- just update the variables in place and mark the exported environment
> -- as no longer needing a remake. */
> --void
> --update_export_env_inplace (env_prefix, preflen, value)
> -- char *env_prefix;
> -- int preflen;
> -- char *value;
> --{
> -- char *evar;
> --
> -- evar = (char *)xmalloc (STRLEN (value) + preflen + 1);
> -- strcpy (evar, env_prefix);
> -- if (value)
> -- strcpy (evar + preflen, value);
> -- export_env = add_or_supercede_exported_var (evar, 0);
> --}
> --
> --/* We always put _ in the environment as the name of this command. */
> --void
> --put_command_name_into_env (command_name)
> -- char *command_name;
> --{
> -- update_export_env_inplace ("_=", 2, command_name);
> --}
> --
> --/* **************************************************************** */
> --/* */
> --/* Managing variable contexts */
> --/* */
> --/* **************************************************************** */
> --
> --/* Allocate and return a new variable context with NAME and FLAGS.
> -- NAME can be NULL. */
> --
> --VAR_CONTEXT *
> --new_var_context (name, flags)
> -- char *name;
> -- int flags;
> --{
> -- VAR_CONTEXT *vc;
> --
> -- vc = (VAR_CONTEXT *)xmalloc (sizeof (VAR_CONTEXT));
> -- vc->name = name ? savestring (name) : (char *)NULL;
> -- vc->scope = variable_context;
> -- vc->flags = flags;
> --
> -- vc->up = vc->down = (VAR_CONTEXT *)NULL;
> -- vc->table = (HASH_TABLE *)NULL;
> --
> -- return vc;
> --}
> --
> --/* Free a variable context and its data, including the hash table. Dispose
> -- all of the variables. */
> --void
> --dispose_var_context (vc)
> -- VAR_CONTEXT *vc;
> --{
> -- FREE (vc->name);
> --
> -- if (vc->table)
> -- {
> -- delete_all_variables (vc->table);
> -- hash_dispose (vc->table);
> -- }
> --
> -- free (vc);
> --}
> --
> --/* Set VAR's scope level to the current variable context. */
> --static int
> --set_context (var)
> -- SHELL_VAR *var;
> --{
> -- return (var->context = variable_context);
> --}
> --
> --/* Make a new variable context with NAME and FLAGS and a HASH_TABLE of
> -- temporary variables, and push it onto shell_variables. This is
> -- for shell functions. */
> --VAR_CONTEXT *
> --push_var_context (name, flags, tempvars)
> -- char *name;
> -- int flags;
> -- HASH_TABLE *tempvars;
> --{
> -- VAR_CONTEXT *vc;
> --
> -- vc = new_var_context (name, flags);
> -- vc->table = tempvars;
> -- if (tempvars)
> -- {
> -- /* Have to do this because the temp environment was created before
> -- variable_context was incremented. */
> -- flatten (tempvars, set_context, (VARLIST *)NULL, 0);
> -- vc->flags |= VC_HASTMPVAR;
> -- }
> -- vc->down = shell_variables;
> -- shell_variables->up = vc;
> --
> -- return (shell_variables = vc);
> --}
> --
> --static void
> --push_func_var (data)
> -- PTR_T data;
> --{
> -- SHELL_VAR *var, *v;
> --
> -- var = (SHELL_VAR *)data;
> --
> -- if (tempvar_p (var) && (posixly_correct || (var->attributes & att_propagate)))
> -- {
> -- /* Make sure we have a hash table to store the variable in while it is
> -- being propagated down to the global variables table. Create one if
> -- we have to */
> -- if ((vc_isfuncenv (shell_variables) || vc_istempenv (shell_variables)) && shell_variables->table == 0)
> -- shell_variables->table = hash_create (0);
> -- /* XXX - should we set v->context here? */
> -- v = bind_variable_internal (var->name, value_cell (var), shell_variables->table, 0, 0);
> -- if (shell_variables == global_variables)
> -- var->attributes &= ~(att_tempvar|att_propagate);
> -- else
> -- shell_variables->flags |= VC_HASTMPVAR;
> -- v->attributes |= var->attributes;
> -- }
> -- else
> -- stupidly_hack_special_variables (var->name); /* XXX */
> --
> -- dispose_variable (var);
> --}
> --
> --/* Pop the top context off of VCXT and dispose of it, returning the rest of
> -- the stack. */
> --void
> --pop_var_context ()
> --{
> -- VAR_CONTEXT *ret, *vcxt;
> --
> -- vcxt = shell_variables;
> -- if (vc_isfuncenv (vcxt) == 0)
> -- {
> -- internal_error (_("pop_var_context: head of shell_variables not a function context"));
> -- return;
> -- }
> --
> -- if (ret = vcxt->down)
> -- {
> -- ret->up = (VAR_CONTEXT *)NULL;
> -- shell_variables = ret;
> -- if (vcxt->table)
> -- hash_flush (vcxt->table, push_func_var);
> -- dispose_var_context (vcxt);
> -- }
> -- else
> -- internal_error (_("pop_var_context: no global_variables context"));
> --}
> --
> --/* Delete the HASH_TABLEs for all variable contexts beginning at VCXT, and
> -- all of the VAR_CONTEXTs except GLOBAL_VARIABLES. */
> --void
> --delete_all_contexts (vcxt)
> -- VAR_CONTEXT *vcxt;
> --{
> -- VAR_CONTEXT *v, *t;
> --
> -- for (v = vcxt; v != global_variables; v = t)
> -- {
> -- t = v->down;
> -- dispose_var_context (v);
> -- }
> --
> -- delete_all_variables (global_variables->table);
> -- shell_variables = global_variables;
> --}
> --
> --/* **************************************************************** */
> --/* */
> --/* Pushing and Popping temporary variable scopes */
> --/* */
> --/* **************************************************************** */
> --
> --VAR_CONTEXT *
> --push_scope (flags, tmpvars)
> -- int flags;
> -- HASH_TABLE *tmpvars;
> --{
> -- return (push_var_context ((char *)NULL, flags, tmpvars));
> --}
> --
> --static void
> --push_exported_var (data)
> -- PTR_T data;
> --{
> -- SHELL_VAR *var, *v;
> --
> -- var = (SHELL_VAR *)data;
> --
> -- /* If a temp var had its export attribute set, or it's marked to be
> -- propagated, bind it in the previous scope before disposing it. */
> -- /* XXX - This isn't exactly right, because all tempenv variables have the
> -- export attribute set. */
> --#if 0
> -- if (exported_p (var) || (var->attributes & att_propagate))
> --#else
> -- if (tempvar_p (var) && exported_p (var) && (var->attributes & att_propagate))
> --#endif
> -- {
> -- var->attributes &= ~att_tempvar; /* XXX */
> -- v = bind_variable_internal (var->name, value_cell (var), shell_variables->table, 0, 0);
> -- if (shell_variables == global_variables)
> -- var->attributes &= ~att_propagate;
> -- v->attributes |= var->attributes;
> -- }
> -- else
> -- stupidly_hack_special_variables (var->name); /* XXX */
> --
> -- dispose_variable (var);
> --}
> --
> --void
> --pop_scope (is_special)
> -- int is_special;
> --{
> -- VAR_CONTEXT *vcxt, *ret;
> --
> -- vcxt = shell_variables;
> -- if (vc_istempscope (vcxt) == 0)
> -- {
> -- internal_error (_("pop_scope: head of shell_variables not a temporary environment scope"));
> -- return;
> -- }
> --
> -- ret = vcxt->down;
> -- if (ret)
> -- ret->up = (VAR_CONTEXT *)NULL;
> --
> -- shell_variables = ret;
> --
> -- /* Now we can take care of merging variables in VCXT into set of scopes
> -- whose head is RET (shell_variables). */
> -- FREE (vcxt->name);
> -- if (vcxt->table)
> -- {
> -- if (is_special)
> -- hash_flush (vcxt->table, push_func_var);
> -- else
> -- hash_flush (vcxt->table, push_exported_var);
> -- hash_dispose (vcxt->table);
> -- }
> -- free (vcxt);
> --
> -- sv_ifs ("IFS"); /* XXX here for now */
> --}
> --
> --/* **************************************************************** */
> --/* */
> --/* Pushing and Popping function contexts */
> --/* */
> --/* **************************************************************** */
> --
> --static WORD_LIST **dollar_arg_stack = (WORD_LIST **)NULL;
> --static int dollar_arg_stack_slots;
> --static int dollar_arg_stack_index;
> --
> --/* XXX - we might want to consider pushing and popping the `getopts' state
> -- when we modify the positional parameters. */
> --void
> --push_context (name, is_subshell, tempvars)
> -- char *name; /* function name */
> -- int is_subshell;
> -- HASH_TABLE *tempvars;
> --{
> -- if (is_subshell == 0)
> -- push_dollar_vars ();
> -- variable_context++;
> -- push_var_context (name, VC_FUNCENV, tempvars);
> --}
> --
> --/* Only called when subshell == 0, so we don't need to check, and can
> -- unconditionally pop the dollar vars off the stack. */
> --void
> --pop_context ()
> --{
> -- pop_dollar_vars ();
> -- variable_context--;
> -- pop_var_context ();
> --
> -- sv_ifs ("IFS"); /* XXX here for now */
> --}
> --
> --/* Save the existing positional parameters on a stack. */
> --void
> --push_dollar_vars ()
> --{
> -- if (dollar_arg_stack_index + 2 > dollar_arg_stack_slots)
> -- {
> -- dollar_arg_stack = (WORD_LIST **)
> -- xrealloc (dollar_arg_stack, (dollar_arg_stack_slots += 10)
> -- * sizeof (WORD_LIST *));
> -- }
> -- dollar_arg_stack[dollar_arg_stack_index++] = list_rest_of_args ();
> -- dollar_arg_stack[dollar_arg_stack_index] = (WORD_LIST *)NULL;
> --}
> --
> --/* Restore the positional parameters from our stack. */
> --void
> --pop_dollar_vars ()
> --{
> -- if (!dollar_arg_stack || dollar_arg_stack_index == 0)
> -- return;
> --
> -- remember_args (dollar_arg_stack[--dollar_arg_stack_index], 1);
> -- dispose_words (dollar_arg_stack[dollar_arg_stack_index]);
> -- dollar_arg_stack[dollar_arg_stack_index] = (WORD_LIST *)NULL;
> -- set_dollar_vars_unchanged ();
> --}
> --
> --void
> --dispose_saved_dollar_vars ()
> --{
> -- if (!dollar_arg_stack || dollar_arg_stack_index == 0)
> -- return;
> --
> -- dispose_words (dollar_arg_stack[dollar_arg_stack_index]);
> -- dollar_arg_stack[dollar_arg_stack_index] = (WORD_LIST *)NULL;
> --}
> --
> --/* Manipulate the special BASH_ARGV and BASH_ARGC variables. */
> --
> --void
> --push_args (list)
> -- WORD_LIST *list;
> --{
> --#if defined (ARRAY_VARS) && defined (DEBUGGER)
> -- SHELL_VAR *bash_argv_v, *bash_argc_v;
> -- ARRAY *bash_argv_a, *bash_argc_a;
> -- WORD_LIST *l;
> -- arrayind_t i;
> -- char *t;
> --
> -- GET_ARRAY_FROM_VAR ("BASH_ARGV", bash_argv_v, bash_argv_a);
> -- GET_ARRAY_FROM_VAR ("BASH_ARGC", bash_argc_v, bash_argc_a);
> --
> -- for (l = list, i = 0; l; l = l->next, i++)
> -- array_push (bash_argv_a, l->word->word);
> --
> -- t = itos (i);
> -- array_push (bash_argc_a, t);
> -- free (t);
> --#endif /* ARRAY_VARS && DEBUGGER */
> --}
> --
> --/* Remove arguments from BASH_ARGV array. Pop top element off BASH_ARGC
> -- array and use that value as the count of elements to remove from
> -- BASH_ARGV. */
> --void
> --pop_args ()
> --{
> --#if defined (ARRAY_VARS) && defined (DEBUGGER)
> -- SHELL_VAR *bash_argv_v, *bash_argc_v;
> -- ARRAY *bash_argv_a, *bash_argc_a;
> -- ARRAY_ELEMENT *ce;
> -- intmax_t i;
> --
> -- GET_ARRAY_FROM_VAR ("BASH_ARGV", bash_argv_v, bash_argv_a);
> -- GET_ARRAY_FROM_VAR ("BASH_ARGC", bash_argc_v, bash_argc_a);
> --
> -- ce = array_shift (bash_argc_a, 1, 0);
> -- if (ce == 0 || legal_number (element_value (ce), &i) == 0)
> -- i = 0;
> --
> -- for ( ; i > 0; i--)
> -- array_pop (bash_argv_a);
> -- array_dispose_element (ce);
> --#endif /* ARRAY_VARS && DEBUGGER */
> --}
> --
> --/*************************************************
> -- * *
> -- * Functions to manage special variables *
> -- * *
> -- *************************************************/
> --
> --/* Extern declarations for variables this code has to manage. */
> --extern int eof_encountered, eof_encountered_limit, ignoreeof;
> --
> --#if defined (READLINE)
> --extern int hostname_list_initialized;
> --#endif
> --
> --/* An alist of name.function for each special variable. Most of the
> -- functions don't do much, and in fact, this would be faster with a
> -- switch statement, but by the end of this file, I am sick of switch
> -- statements. */
> --
> --#define SET_INT_VAR(name, intvar) intvar = find_variable (name) != 0
> --
> --/* This table will be sorted with qsort() the first time it's accessed. */
> --struct name_and_function {
> -- char *name;
> -- sh_sv_func_t *function;
> --};
> --
> --static struct name_and_function special_vars[] = {
> -- { "BASH_COMPAT", sv_shcompat },
> -- { "BASH_XTRACEFD", sv_xtracefd },
> --
> --#if defined (JOB_CONTROL)
> -- { "CHILD_MAX", sv_childmax },
> --#endif
> --
> --#if defined (READLINE)
> --# if defined (STRICT_POSIX)
> -- { "COLUMNS", sv_winsize },
> --# endif
> -- { "COMP_WORDBREAKS", sv_comp_wordbreaks },
> --#endif
> --
> -- { "FUNCNEST", sv_funcnest },
> --
> -- { "GLOBIGNORE", sv_globignore },
> --
> --#if defined (HISTORY)
> -- { "HISTCONTROL", sv_history_control },
> -- { "HISTFILESIZE", sv_histsize },
> -- { "HISTIGNORE", sv_histignore },
> -- { "HISTSIZE", sv_histsize },
> -- { "HISTTIMEFORMAT", sv_histtimefmt },
> --#endif
> --
> --#if defined (__CYGWIN__)
> -- { "HOME", sv_home },
> --#endif
> --
> --#if defined (READLINE)
> -- { "HOSTFILE", sv_hostfile },
> --#endif
> --
> -- { "IFS", sv_ifs },
> -- { "IGNOREEOF", sv_ignoreeof },
> --
> -- { "LANG", sv_locale },
> -- { "LC_ALL", sv_locale },
> -- { "LC_COLLATE", sv_locale },
> -- { "LC_CTYPE", sv_locale },
> -- { "LC_MESSAGES", sv_locale },
> -- { "LC_NUMERIC", sv_locale },
> -- { "LC_TIME", sv_locale },
> --
> --#if defined (READLINE) && defined (STRICT_POSIX)
> -- { "LINES", sv_winsize },
> --#endif
> --
> -- { "MAIL", sv_mail },
> -- { "MAILCHECK", sv_mail },
> -- { "MAILPATH", sv_mail },
> --
> -- { "OPTERR", sv_opterr },
> -- { "OPTIND", sv_optind },
> --
> -- { "PATH", sv_path },
> -- { "POSIXLY_CORRECT", sv_strict_posix },
> --
> --#if defined (READLINE)
> -- { "TERM", sv_terminal },
> -- { "TERMCAP", sv_terminal },
> -- { "TERMINFO", sv_terminal },
> --#endif /* READLINE */
> --
> -- { "TEXTDOMAIN", sv_locale },
> -- { "TEXTDOMAINDIR", sv_locale },
> --
> --#if defined (HAVE_TZSET)
> -- { "TZ", sv_tz },
> --#endif
> --
> --#if defined (HISTORY) && defined (BANG_HISTORY)
> -- { "histchars", sv_histchars },
> --#endif /* HISTORY && BANG_HISTORY */
> --
> -- { "ignoreeof", sv_ignoreeof },
> --
> -- { (char *)0, (sh_sv_func_t *)0 }
> --};
> --
> --#define N_SPECIAL_VARS (sizeof (special_vars) / sizeof (special_vars[0]) - 1)
> --
> --static int
> --sv_compare (sv1, sv2)
> -- struct name_and_function *sv1, *sv2;
> --{
> -- int r;
> --
> -- if ((r = sv1->name[0] - sv2->name[0]) == 0)
> -- r = strcmp (sv1->name, sv2->name);
> -- return r;
> --}
> --
> --static inline int
> --find_special_var (name)
> -- const char *name;
> --{
> -- register int i, r;
> --
> -- for (i = 0; special_vars[i].name; i++)
> -- {
> -- r = special_vars[i].name[0] - name[0];
> -- if (r == 0)
> -- r = strcmp (special_vars[i].name, name);
> -- if (r == 0)
> -- return i;
> -- else if (r > 0)
> -- /* Can't match any of rest of elements in sorted list. Take this out
> -- if it causes problems in certain environments. */
> -- break;
> -- }
> -- return -1;
> --}
> --
> --/* The variable in NAME has just had its state changed. Check to see if it
> -- is one of the special ones where something special happens. */
> --void
> --stupidly_hack_special_variables (name)
> -- char *name;
> --{
> -- static int sv_sorted = 0;
> -- int i;
> --
> -- if (sv_sorted == 0) /* shouldn't need, but it's fairly cheap. */
> -- {
> -- qsort (special_vars, N_SPECIAL_VARS, sizeof (special_vars[0]),
> -- (QSFUNC *)sv_compare);
> -- sv_sorted = 1;
> -- }
> --
> -- i = find_special_var (name);
> -- if (i != -1)
> -- (*(special_vars[i].function)) (name);
> --}
> --
> --/* Special variables that need hooks to be run when they are unset as part
> -- of shell reinitialization should have their sv_ functions run here. */
> --void
> --reinit_special_variables ()
> --{
> --#if defined (READLINE)
> -- sv_comp_wordbreaks ("COMP_WORDBREAKS");
> --#endif
> -- sv_globignore ("GLOBIGNORE");
> -- sv_opterr ("OPTERR");
> --}
> --
> --void
> --sv_ifs (name)
> -- char *name;
> --{
> -- SHELL_VAR *v;
> --
> -- v = find_variable ("IFS");
> -- setifs (v);
> --}
> --
> --/* What to do just after the PATH variable has changed. */
> --void
> --sv_path (name)
> -- char *name;
> --{
> -- /* hash -r */
> -- phash_flush ();
> --}
> --
> --/* What to do just after one of the MAILxxxx variables has changed. NAME
> -- is the name of the variable. This is called with NAME set to one of
> -- MAIL, MAILCHECK, or MAILPATH. */
> --void
> --sv_mail (name)
> -- char *name;
> --{
> -- /* If the time interval for checking the files has changed, then
> -- reset the mail timer. Otherwise, one of the pathname vars
> -- to the users mailbox has changed, so rebuild the array of
> -- filenames. */
> -- if (name[4] == 'C') /* if (strcmp (name, "MAILCHECK") == 0) */
> -- reset_mail_timer ();
> -- else
> -- {
> -- free_mail_files ();
> -- remember_mail_dates ();
> -- }
> --}
> --
> --void
> --sv_funcnest (name)
> -- char *name;
> --{
> -- SHELL_VAR *v;
> -- intmax_t num;
> --
> -- v = find_variable (name);
> -- if (v == 0)
> -- funcnest_max = 0;
> -- else if (legal_number (value_cell (v), &num) == 0)
> -- funcnest_max = 0;
> -- else
> -- funcnest_max = num;
> --}
> --
> --/* What to do when GLOBIGNORE changes. */
> --void
> --sv_globignore (name)
> -- char *name;
> --{
> -- if (privileged_mode == 0)
> -- setup_glob_ignore (name);
> --}
> --
> --#if defined (READLINE)
> --void
> --sv_comp_wordbreaks (name)
> -- char *name;
> --{
> -- SHELL_VAR *sv;
> --
> -- sv = find_variable (name);
> -- if (sv == 0)
> -- reset_completer_word_break_chars ();
> --}
> --
> --/* What to do just after one of the TERMxxx variables has changed.
> -- If we are an interactive shell, then try to reset the terminal
> -- information in readline. */
> --void
> --sv_terminal (name)
> -- char *name;
> --{
> -- if (interactive_shell && no_line_editing == 0)
> -- rl_reset_terminal (get_string_value ("TERM"));
> --}
> --
> --void
> --sv_hostfile (name)
> -- char *name;
> --{
> -- SHELL_VAR *v;
> --
> -- v = find_variable (name);
> -- if (v == 0)
> -- clear_hostname_list ();
> -- else
> -- hostname_list_initialized = 0;
> --}
> --
> --#if defined (STRICT_POSIX)
> --/* In strict posix mode, we allow assignments to LINES and COLUMNS (and values
> -- found in the initial environment) to override the terminal size reported by
> -- the kernel. */
> --void
> --sv_winsize (name)
> -- char *name;
> --{
> -- SHELL_VAR *v;
> -- intmax_t xd;
> -- int d;
> --
> -- if (posixly_correct == 0 || interactive_shell == 0 || no_line_editing)
> -- return;
> --
> -- v = find_variable (name);
> -- if (v == 0 || var_isnull (v))
> -- rl_reset_screen_size ();
> -- else
> -- {
> -- if (legal_number (value_cell (v), &xd) == 0)
> -- return;
> -- winsize_assignment = 1;
> -- d = xd; /* truncate */
> -- if (name[0] == 'L') /* LINES */
> -- rl_set_screen_size (d, -1);
> -- else /* COLUMNS */
> -- rl_set_screen_size (-1, d);
> -- winsize_assignment = 0;
> -- }
> --}
> --#endif /* STRICT_POSIX */
> --#endif /* READLINE */
> --
> --/* Update the value of HOME in the export environment so tilde expansion will
> -- work on cygwin. */
> --#if defined (__CYGWIN__)
> --sv_home (name)
> -- char *name;
> --{
> -- array_needs_making = 1;
> -- maybe_make_export_env ();
> --}
> --#endif
> --
> --#if defined (HISTORY)
> --/* What to do after the HISTSIZE or HISTFILESIZE variables change.
> -- If there is a value for this HISTSIZE (and it is numeric), then stifle
> -- the history. Otherwise, if there is NO value for this variable,
> -- unstifle the history. If name is HISTFILESIZE, and its value is
> -- numeric, truncate the history file to hold no more than that many
> -- lines. */
> --void
> --sv_histsize (name)
> -- char *name;
> --{
> -- char *temp;
> -- intmax_t num;
> -- int hmax;
> --
> -- temp = get_string_value (name);
> --
> -- if (temp && *temp)
> -- {
> -- if (legal_number (temp, &num))
> -- {
> -- hmax = num;
> -- if (hmax < 0 && name[4] == 'S')
> -- unstifle_history (); /* unstifle history if HISTSIZE < 0 */
> -- else if (name[4] == 'S')
> -- {
> -- stifle_history (hmax);
> -- hmax = where_history ();
> -- if (history_lines_this_session > hmax)
> -- history_lines_this_session = hmax;
> -- }
> -- else if (hmax >= 0) /* truncate HISTFILE if HISTFILESIZE >= 0 */
> -- {
> -- history_truncate_file (get_string_value ("HISTFILE"), hmax);
> -- if (hmax <= history_lines_in_file)
> -- history_lines_in_file = hmax;
> -- }
> -- }
> -- }
> -- else if (name[4] == 'S')
> -- unstifle_history ();
> --}
> --
> --/* What to do after the HISTIGNORE variable changes. */
> --void
> --sv_histignore (name)
> -- char *name;
> --{
> -- setup_history_ignore (name);
> --}
> --
> --/* What to do after the HISTCONTROL variable changes. */
> --void
> --sv_history_control (name)
> -- char *name;
> --{
> -- char *temp;
> -- char *val;
> -- int tptr;
> --
> -- history_control = 0;
> -- temp = get_string_value (name);
> --
> -- if (temp == 0 || *temp == 0)
> -- return;
> --
> -- tptr = 0;
> -- while (val = extract_colon_unit (temp, &tptr))
> -- {
> -- if (STREQ (val, "ignorespace"))
> -- history_control |= HC_IGNSPACE;
> -- else if (STREQ (val, "ignoredups"))
> -- history_control |= HC_IGNDUPS;
> -- else if (STREQ (val, "ignoreboth"))
> -- history_control |= HC_IGNBOTH;
> -- else if (STREQ (val, "erasedups"))
> -- history_control |= HC_ERASEDUPS;
> --
> -- free (val);
> -- }
> --}
> --
> --#if defined (BANG_HISTORY)
> --/* Setting/unsetting of the history expansion character. */
> --void
> --sv_histchars (name)
> -- char *name;
> --{
> -- char *temp;
> --
> -- temp = get_string_value (name);
> -- if (temp)
> -- {
> -- history_expansion_char = *temp;
> -- if (temp[0] && temp[1])
> -- {
> -- history_subst_char = temp[1];
> -- if (temp[2])
> -- history_comment_char = temp[2];
> -- }
> -- }
> -- else
> -- {
> -- history_expansion_char = '!';
> -- history_subst_char = '^';
> -- history_comment_char = '#';
> -- }
> --}
> --#endif /* BANG_HISTORY */
> --
> --void
> --sv_histtimefmt (name)
> -- char *name;
> --{
> -- SHELL_VAR *v;
> --
> -- if (v = find_variable (name))
> -- {
> -- if (history_comment_char == 0)
> -- history_comment_char = '#';
> -- }
> -- history_write_timestamps = (v != 0);
> --}
> --#endif /* HISTORY */
> --
> --#if defined (HAVE_TZSET)
> --void
> --sv_tz (name)
> -- char *name;
> --{
> -- if (chkexport (name))
> -- tzset ();
> --}
> --#endif
> --
> --/* If the variable exists, then the value of it can be the number
> -- of times we actually ignore the EOF. The default is small,
> -- (smaller than csh, anyway). */
> --void
> --sv_ignoreeof (name)
> -- char *name;
> --{
> -- SHELL_VAR *tmp_var;
> -- char *temp;
> --
> -- eof_encountered = 0;
> --
> -- tmp_var = find_variable (name);
> -- ignoreeof = tmp_var != 0;
> -- temp = tmp_var ? value_cell (tmp_var) : (char *)NULL;
> -- if (temp)
> -- eof_encountered_limit = (*temp && all_digits (temp)) ? atoi (temp) : 10;
> -- set_shellopts (); /* make sure `ignoreeof' is/is not in $SHELLOPTS */
> --}
> --
> --void
> --sv_optind (name)
> -- char *name;
> --{
> -- char *tt;
> -- int s;
> --
> -- tt = get_string_value ("OPTIND");
> -- if (tt && *tt)
> -- {
> -- s = atoi (tt);
> --
> -- /* According to POSIX, setting OPTIND=1 resets the internal state
> -- of getopt (). */
> -- if (s < 0 || s == 1)
> -- s = 0;
> -- }
> -- else
> -- s = 0;
> -- getopts_reset (s);
> --}
> --
> --void
> --sv_opterr (name)
> -- char *name;
> --{
> -- char *tt;
> --
> -- tt = get_string_value ("OPTERR");
> -- sh_opterr = (tt && *tt) ? atoi (tt) : 1;
> --}
> --
> --void
> --sv_strict_posix (name)
> -- char *name;
> --{
> -- SET_INT_VAR (name, posixly_correct);
> -- posix_initialize (posixly_correct);
> --#if defined (READLINE)
> -- if (interactive_shell)
> -- posix_readline_initialize (posixly_correct);
> --#endif /* READLINE */
> -- set_shellopts (); /* make sure `posix' is/is not in $SHELLOPTS */
> --}
> --
> --void
> --sv_locale (name)
> -- char *name;
> --{
> -- char *v;
> -- int r;
> --
> -- v = get_string_value (name);
> -- if (name[0] == 'L' && name[1] == 'A') /* LANG */
> -- r = set_lang (name, v);
> -- else
> -- r = set_locale_var (name, v); /* LC_*, TEXTDOMAIN* */
> --
> --#if 1
> -- if (r == 0 && posixly_correct)
> -- last_command_exit_value = 1;
> --#endif
> --}
> --
> --#if defined (ARRAY_VARS)
> --void
> --set_pipestatus_array (ps, nproc)
> -- int *ps;
> -- int nproc;
> --{
> -- SHELL_VAR *v;
> -- ARRAY *a;
> -- ARRAY_ELEMENT *ae;
> -- register int i;
> -- char *t, tbuf[INT_STRLEN_BOUND(int) + 1];
> --
> -- v = find_variable ("PIPESTATUS");
> -- if (v == 0)
> -- v = make_new_array_variable ("PIPESTATUS");
> -- if (array_p (v) == 0)
> -- return; /* Do nothing if not an array variable. */
> -- a = array_cell (v);
> --
> -- if (a == 0 || array_num_elements (a) == 0)
> -- {
> -- for (i = 0; i < nproc; i++) /* was ps[i] != -1, not i < nproc */
> -- {
> -- t = inttostr (ps[i], tbuf, sizeof (tbuf));
> -- array_insert (a, i, t);
> -- }
> -- return;
> -- }
> --
> -- /* Fast case */
> -- if (array_num_elements (a) == nproc && nproc == 1)
> -- {
> -- ae = element_forw (a->head);
> -- free (element_value (ae));
> -- ae->value = itos (ps[0]);
> -- }
> -- else if (array_num_elements (a) <= nproc)
> -- {
> -- /* modify in array_num_elements members in place, then add */
> -- ae = a->head;
> -- for (i = 0; i < array_num_elements (a); i++)
> -- {
> -- ae = element_forw (ae);
> -- free (element_value (ae));
> -- ae->value = itos (ps[i]);
> -- }
> -- /* add any more */
> -- for ( ; i < nproc; i++)
> -- {
> -- t = inttostr (ps[i], tbuf, sizeof (tbuf));
> -- array_insert (a, i, t);
> -- }
> -- }
> -- else
> -- {
> -- /* deleting elements. it's faster to rebuild the array. */
> -- array_flush (a);
> -- for (i = 0; ps[i] != -1; i++)
> -- {
> -- t = inttostr (ps[i], tbuf, sizeof (tbuf));
> -- array_insert (a, i, t);
> -- }
> -- }
> --}
> --
> --ARRAY *
> --save_pipestatus_array ()
> --{
> -- SHELL_VAR *v;
> -- ARRAY *a, *a2;
> --
> -- v = find_variable ("PIPESTATUS");
> -- if (v == 0 || array_p (v) == 0 || array_cell (v) == 0)
> -- return ((ARRAY *)NULL);
> --
> -- a = array_cell (v);
> -- a2 = array_copy (array_cell (v));
> --
> -- return a2;
> --}
> --
> --void
> --restore_pipestatus_array (a)
> -- ARRAY *a;
> --{
> -- SHELL_VAR *v;
> -- ARRAY *a2;
> --
> -- v = find_variable ("PIPESTATUS");
> -- /* XXX - should we still assign even if existing value is NULL? */
> -- if (v == 0 || array_p (v) == 0 || array_cell (v) == 0)
> -- return;
> --
> -- a2 = array_cell (v);
> -- var_setarray (v, a);
> --
> -- array_dispose (a2);
> --}
> --#endif
> --
> --void
> --set_pipestatus_from_exit (s)
> -- int s;
> --{
> --#if defined (ARRAY_VARS)
> -- static int v[2] = { 0, -1 };
> --
> -- v[0] = s;
> -- set_pipestatus_array (v, 1);
> --#endif
> --}
> --
> --void
> --sv_xtracefd (name)
> -- char *name;
> --{
> -- SHELL_VAR *v;
> -- char *t, *e;
> -- int fd;
> -- FILE *fp;
> --
> -- v = find_variable (name);
> -- if (v == 0)
> -- {
> -- xtrace_reset ();
> -- return;
> -- }
> --
> -- t = value_cell (v);
> -- if (t == 0 || *t == 0)
> -- xtrace_reset ();
> -- else
> -- {
> -- fd = (int)strtol (t, &e, 10);
> -- if (e != t && *e == '\0' && sh_validfd (fd))
> -- {
> -- fp = fdopen (fd, "w");
> -- if (fp == 0)
> -- internal_error (_("%s: %s: cannot open as FILE"), name, value_cell (v));
> -- else
> -- xtrace_set (fd, fp);
> -- }
> -- else
> -- internal_error (_("%s: %s: invalid value for trace file descriptor"), name, value_cell (v));
> -- }
> --}
> --
> --#define MIN_COMPAT_LEVEL 31
> --
> --void
> --sv_shcompat (name)
> -- char *name;
> --{
> -- SHELL_VAR *v;
> -- char *val;
> -- int tens, ones, compatval;
> --
> -- v = find_variable (name);
> -- if (v == 0)
> -- {
> -- shell_compatibility_level = DEFAULT_COMPAT_LEVEL;
> -- set_compatibility_opts ();
> -- return;
> -- }
> -- val = value_cell (v);
> -- if (val == 0 || *val == '\0')
> -- {
> -- shell_compatibility_level = DEFAULT_COMPAT_LEVEL;
> -- set_compatibility_opts ();
> -- return;
> -- }
> -- /* Handle decimal-like compatibility version specifications: 4.2 */
> -- if (isdigit (val[0]) && val[1] == '.' && isdigit (val[2]) && val[3] == 0)
> -- {
> -- tens = val[0] - '0';
> -- ones = val[2] - '0';
> -- compatval = tens*10 + ones;
> -- }
> -- /* Handle integer-like compatibility version specifications: 42 */
> -- else if (isdigit (val[0]) && isdigit (val[1]) && val[2] == 0)
> -- {
> -- tens = val[0] - '0';
> -- ones = val[1] - '0';
> -- compatval = tens*10 + ones;
> -- }
> -- else
> -- {
> --compat_error:
> -- internal_error (_("%s: %s: compatibility value out of range"), name, val);
> -- shell_compatibility_level = DEFAULT_COMPAT_LEVEL;
> -- set_compatibility_opts ();
> -- return;
> -- }
> --
> -- if (compatval < MIN_COMPAT_LEVEL || compatval > DEFAULT_COMPAT_LEVEL)
> -- goto compat_error;
> --
> -- shell_compatibility_level = compatval;
> -- set_compatibility_opts ();
> --}
> --
> --#if defined (JOB_CONTROL)
> --void
> --sv_childmax (name)
> -- char *name;
> --{
> -- char *tt;
> -- int s;
> --
> -- tt = get_string_value (name);
> -- s = (tt && *tt) ? atoi (tt) : 0;
> -- set_maxchild (s);
> --}
> --#endif
> diff --git a/patches/bash-4.3.30/0003-Bash-4.3-patch-33.patch b/patches/bash-4.3.30/0003-Bash-4.3-patch-33.patch
> deleted file mode 100644
> index cda179735..000000000
> --- a/patches/bash-4.3.30/0003-Bash-4.3-patch-33.patch
> +++ /dev/null
> @@ -1,204 +0,0 @@
> -From: Chet Ramey <chet.ramey@case.edu>
> -Date: Thu, 15 Jan 2015 10:21:08 -0500
> -Subject: [PATCH] Bash-4.3 patch 33
> -
> ----
> - bashline.c | 6 ++++--
> - builtins/common.h | 4 ++++
> - builtins/read.def | 31 ++++++++++++++++++++++++++++---
> - patchlevel.h | 2 +-
> - shell.c | 9 +++++++++
> - sig.c | 6 ++++--
> - 6 files changed, 50 insertions(+), 8 deletions(-)
> -
> -diff --git a/bashline.c b/bashline.c
> -index 77ca033f2cc8..c87415171a4a 100644
> ---- a/bashline.c
> -+++ b/bashline.c
> -@@ -202,6 +202,7 @@ extern int current_command_line_count, saved_command_line_count;
> - extern int last_command_exit_value;
> - extern int array_needs_making;
> - extern int posixly_correct, no_symbolic_links;
> -+extern int sigalrm_seen;
> - extern char *current_prompt_string, *ps1_prompt;
> - extern STRING_INT_ALIST word_token_alist[];
> - extern sh_builtin_func_t *last_shell_builtin, *this_shell_builtin;
> -@@ -4208,8 +4209,9 @@ bash_event_hook ()
> - {
> - /* If we're going to longjmp to top_level, make sure we clean up readline.
> - check_signals will call QUIT, which will eventually longjmp to top_level,
> -- calling run_interrupt_trap along the way. */
> -- if (interrupt_state)
> -+ calling run_interrupt_trap along the way. The check for sigalrm_seen is
> -+ to clean up the read builtin's state. */
> -+ if (terminating_signal || interrupt_state || sigalrm_seen)
> - rl_cleanup_after_signal ();
> - bashline_reset_event_hook ();
> - check_signals_and_traps (); /* XXX */
> -diff --git a/builtins/common.h b/builtins/common.h
> -index cae16b10fb65..a1298cb9c84a 100644
> ---- a/builtins/common.h
> -+++ b/builtins/common.h
> -@@ -122,6 +122,10 @@ extern void bash_logout __P((void));
> - /* Functions from getopts.def */
> - extern void getopts_reset __P((int));
> -
> -+/* Functions from read.def */
> -+extern void read_tty_cleanup __P((void));
> -+extern int read_tty_modified __P((void));
> -+
> - /* Functions from set.def */
> - extern int minus_o_option_value __P((char *));
> - extern void list_minus_o_opts __P((int, int));
> -diff --git a/builtins/read.def b/builtins/read.def
> -index 43971544d081..56c23010bbe8 100644
> ---- a/builtins/read.def
> -+++ b/builtins/read.def
> -@@ -140,10 +140,12 @@ static void reset_alarm __P((void));
> - procenv_t alrmbuf;
> - int sigalrm_seen;
> -
> --static int reading;
> -+static int reading, tty_modified;
> - static SigHandler *old_alrm;
> - static unsigned char delim;
> -
> -+static struct ttsave termsave;
> -+
> - /* In all cases, SIGALRM just sets a flag that we check periodically. This
> - avoids problems with the semi-tricky stuff we do with the xfree of
> - input_string at the top of the unwind-protect list (see below). */
> -@@ -188,7 +190,6 @@ read_builtin (list)
> - struct stat tsb;
> - SHELL_VAR *var;
> - TTYSTRUCT ttattrs, ttset;
> -- struct ttsave termsave;
> - #if defined (ARRAY_VARS)
> - WORD_LIST *alist;
> - #endif
> -@@ -221,7 +222,7 @@ read_builtin (list)
> - USE_VAR(ps2);
> - USE_VAR(lastsig);
> -
> -- sigalrm_seen = reading = 0;
> -+ sigalrm_seen = reading = tty_modified = 0;
> -
> - i = 0; /* Index into the string that we are reading. */
> - raw = edit = 0; /* Not reading raw input by default. */
> -@@ -438,6 +439,8 @@ read_builtin (list)
> - retval = 128+SIGALRM;
> - goto assign_vars;
> - }
> -+ if (interactive_shell == 0)
> -+ initialize_terminating_signals ();
> - old_alrm = set_signal_handler (SIGALRM, sigalrm);
> - add_unwind_protect (reset_alarm, (char *)NULL);
> - #if defined (READLINE)
> -@@ -482,7 +485,10 @@ read_builtin (list)
> - i = silent ? ttfd_cbreak (fd, &ttset) : ttfd_onechar (fd, &ttset);
> - if (i < 0)
> - sh_ttyerror (1);
> -+ tty_modified = 1;
> - add_unwind_protect ((Function *)ttyrestore, (char *)&termsave);
> -+ if (interactive_shell == 0)
> -+ initialize_terminating_signals ();
> - }
> - }
> - else if (silent) /* turn off echo but leave term in canonical mode */
> -@@ -497,7 +503,10 @@ read_builtin (list)
> - if (i < 0)
> - sh_ttyerror (1);
> -
> -+ tty_modified = 1;
> - add_unwind_protect ((Function *)ttyrestore, (char *)&termsave);
> -+ if (interactive_shell == 0)
> -+ initialize_terminating_signals ();
> - }
> -
> - /* This *must* be the top unwind-protect on the stack, so the manipulation
> -@@ -588,6 +597,8 @@ read_builtin (list)
> - }
> - else
> - lastsig = 0;
> -+ if (terminating_signal && tty_modified)
> -+ ttyrestore (&termsave); /* fix terminal before exiting */
> - CHECK_TERMSIG;
> - eof = 1;
> - break;
> -@@ -978,6 +989,20 @@ ttyrestore (ttp)
> - struct ttsave *ttp;
> - {
> - ttsetattr (ttp->fd, ttp->attrs);
> -+ tty_modified = 0;
> -+}
> -+
> -+void
> -+read_tty_cleanup ()
> -+{
> -+ if (tty_modified)
> -+ ttyrestore (&termsave);
> -+}
> -+
> -+int
> -+read_tty_modified ()
> -+{
> -+ return (tty_modified);
> - }
> -
> - #if defined (READLINE)
> -diff --git a/patchlevel.h b/patchlevel.h
> -index b8bf38704ed2..cefe6bdd3a13 100644
> ---- a/patchlevel.h
> -+++ b/patchlevel.h
> -@@ -25,6 +25,6 @@
> - regexp `^#define[ ]*PATCHLEVEL', since that's what support/mkversion.sh
> - looks for to find the patch level (for the sccs version string). */
> -
> --#define PATCHLEVEL 32
> -+#define PATCHLEVEL 33
> -
> - #endif /* _PATCHLEVEL_H_ */
> -diff --git a/shell.c b/shell.c
> -index bbc8a66cc2eb..2fd8179ba10d 100644
> ---- a/shell.c
> -+++ b/shell.c
> -@@ -73,6 +73,7 @@
> - #endif
> -
> - #if defined (READLINE)
> -+# include <readline/readline.h>
> - # include "bashline.h"
> - #endif
> -
> -@@ -909,6 +910,14 @@ exit_shell (s)
> - fflush (stdout); /* XXX */
> - fflush (stderr);
> -
> -+ /* Clean up the terminal if we are in a state where it's been modified. */
> -+#if defined (READLINE)
> -+ if (RL_ISSTATE (RL_STATE_TERMPREPPED) && rl_deprep_term_function)
> -+ (*rl_deprep_term_function) ();
> -+#endif
> -+ if (read_tty_modified ())
> -+ read_tty_cleanup ();
> -+
> - /* Do trap[0] if defined. Allow it to override the exit status
> - passed to us. */
> - if (signal_is_trapped (0))
> -diff --git a/sig.c b/sig.c
> -index 3b62ea5d7c5d..8bc45c17f478 100644
> ---- a/sig.c
> -+++ b/sig.c
> -@@ -532,8 +532,10 @@ termsig_sighandler (sig)
> - #if defined (READLINE)
> - /* Set the event hook so readline will call it after the signal handlers
> - finish executing, so if this interrupted character input we can get
> -- quick response. */
> -- if (interactive_shell && interactive && no_line_editing == 0)
> -+ quick response. If readline is active or has modified the terminal we
> -+ need to set this no matter what the signal is, though the check for
> -+ RL_STATE_TERMPREPPED is possibly redundant. */
> -+ if (RL_ISSTATE (RL_STATE_SIGHANDLER) || RL_ISSTATE (RL_STATE_TERMPREPPED))
> - bashline_set_event_hook ();
> - #endif
> -
> diff --git a/patches/bash-4.3.30/series b/patches/bash-4.3.30/series
> deleted file mode 100644
> index 2e1fdf17f..000000000
> --- a/patches/bash-4.3.30/series
> +++ /dev/null
> @@ -1,6 +0,0 @@
> -# generated by git-ptx-patches
> -#tag:base --start-number 1
> -0001-Bash-4.3-patch-31.patch
> -0002-Bash-4.3-patch-32.patch
> -0003-Bash-4.3-patch-33.patch
> -# 602897f584d96d29536a2fa60f8d5e23 - git-ptx-patches magic
> diff --git a/patches/bash-5.1.8/0001-Bash-5.1-patch-12.patch b/patches/bash-5.1.8/0001-Bash-5.1-patch-12.patch
> new file mode 100644
> index 000000000..ef8b6ae7f
> --- /dev/null
> +++ b/patches/bash-5.1.8/0001-Bash-5.1-patch-12.patch
> @@ -0,0 +1,262 @@
> +diff -urN bash-5.1.8.orig/builtins/wait.def bash-5.1.8/builtins/wait.def
> +--- bash-5.1.8.orig/builtins/wait.def 2021-12-21 12:57:08.083139502 +0100
> ++++ bash-5.1.8/builtins/wait.def 2021-12-21 12:58:24.304849937 +0100
> +@@ -111,7 +111,8 @@
> + wait_builtin (list)
> + WORD_LIST *list;
> + {
> +- int status, code, opt, nflag, wflags;
> ++ int status, code, opt, nflag;
> ++ volatile int wflags;
> + char *vname;
> + SHELL_VAR *pidvar;
> + struct procstat pstat;
> +@@ -180,6 +181,8 @@
> + last_command_exit_signal = wait_signal_received;
> + status = 128 + wait_signal_received;
> + wait_sigint_cleanup ();
> ++ if (wflags & JWAIT_WAITING)
> ++ unset_waitlist ();
> + WAIT_RETURN (status);
> + }
> +
> +diff -urN bash-5.1.8.orig/command.h bash-5.1.8/command.h
> +--- bash-5.1.8.orig/command.h 2021-12-21 12:57:08.099139862 +0100
> ++++ bash-5.1.8/command.h 2021-12-21 12:58:27.760927380 +0100
> +@@ -124,6 +124,7 @@
> + #define SUBSHELL_PROCSUB 0x20 /* subshell caused by <(command) or >(command) */
> + #define SUBSHELL_COPROC 0x40 /* subshell from a coproc pipeline */
> + #define SUBSHELL_RESETTRAP 0x80 /* subshell needs to reset trap strings on first call to trap */
> ++#define SUBSHELL_IGNTRAP 0x100 /* subshell should reset trapped signals from trap_handler */
> +
> + /* A structure which represents a word. */
> + typedef struct word_desc {
> +diff -urN bash-5.1.8.orig/execute_cmd.c bash-5.1.8/execute_cmd.c
> +--- bash-5.1.8.orig/execute_cmd.c 2021-12-21 12:57:08.091139683 +0100
> ++++ bash-5.1.8/execute_cmd.c 2021-12-21 12:58:27.764927470 +0100
> +@@ -1547,6 +1547,9 @@
> + clear_pending_traps ();
> + reset_signal_handlers ();
> + subshell_environment |= SUBSHELL_RESETTRAP;
> ++ /* Note that signal handlers have been reset, so we should no longer
> ++ reset the handler and resend trapped signals to ourselves. */
> ++ subshell_environment &= ~SUBSHELL_IGNTRAP;
> +
> + /* We are in a subshell, so forget that we are running a trap handler or
> + that the signal handler has changed (we haven't changed it!) */
> +@@ -4320,7 +4323,8 @@
> + already_forked = 1;
> + cmdflags |= CMD_NO_FORK;
> +
> +- subshell_environment = SUBSHELL_FORK; /* XXX */
> ++ /* We redo some of what make_child() does with SUBSHELL_IGNTRAP */
> ++ subshell_environment = SUBSHELL_FORK|SUBSHELL_IGNTRAP; /* XXX */
> + if (pipe_in != NO_PIPE || pipe_out != NO_PIPE)
> + subshell_environment |= SUBSHELL_PIPE;
> + if (async)
> +@@ -4574,6 +4578,7 @@
> + trap strings if we run trap to change a signal disposition. */
> + reset_signal_handlers ();
> + subshell_environment |= SUBSHELL_RESETTRAP;
> ++ subshell_environment &= ~SUBSHELL_IGNTRAP;
> +
> + if (async)
> + {
> +@@ -5514,6 +5519,7 @@
> + reset_terminating_signals (); /* XXX */
> + /* Cancel traps, in trap.c. */
> + restore_original_signals ();
> ++ subshell_environment &= ~SUBSHELL_IGNTRAP;
> +
> + #if defined (JOB_CONTROL)
> + FREE (p);
> +diff -urN bash-5.1.8.orig/jobs.c bash-5.1.8/jobs.c
> +--- bash-5.1.8.orig/jobs.c 2021-12-21 12:57:08.091139683 +0100
> ++++ bash-5.1.8/jobs.c 2021-12-21 12:58:27.764927470 +0100
> +@@ -2217,6 +2217,8 @@
> + signals to the default state for a new process. */
> + pid_t mypid;
> +
> ++ subshell_environment |= SUBSHELL_IGNTRAP;
> ++
> + /* If this ends up being changed to modify or use `command' in the
> + child process, go back and change callers who free `command' in
> + the child process when this returns. */
> +diff -urN bash-5.1.8.orig/lib/malloc/malloc.c bash-5.1.8/lib/malloc/malloc.c
> +--- bash-5.1.8.orig/lib/malloc/malloc.c 2021-12-21 12:57:08.095139773 +0100
> ++++ bash-5.1.8/lib/malloc/malloc.c 2021-12-21 12:58:22.200802784 +0100
> +@@ -1286,13 +1286,12 @@
> + p = (union mhead *) ap - 1;
> + }
> +
> +- /* XXX - should we return 0 if ISFREE? */
> +- maxbytes = binsize(p->mh_index);
> +-
> +- /* So the usable size is the maximum number of bytes in the bin less the
> +- malloc overhead */
> +- maxbytes -= MOVERHEAD + MSLOP;
> +- return (maxbytes);
> ++ /* return 0 if ISFREE */
> ++ if (p->mh_alloc == ISFREE)
> ++ return 0;
> ++
> ++ /* Since we use bounds checking, the usable size is the last requested size. */
> ++ return (p->mh_nbytes);
> + }
> +
> + #if !defined (NO_VALLOC)
> +diff -urN bash-5.1.8.orig/nojobs.c bash-5.1.8/nojobs.c
> +--- bash-5.1.8.orig/nojobs.c 2021-12-21 12:57:08.091139683 +0100
> ++++ bash-5.1.8/nojobs.c 2021-12-21 12:58:27.764927470 +0100
> +@@ -575,6 +575,8 @@
> + last_asynchronous_pid = getpid ();
> + #endif
> +
> ++ subshell_environment |= SUBSHELL_IGNTRAP;
> ++
> + default_tty_job_signals ();
> + }
> + else
> +diff -urN bash-5.1.8.orig/parse.y bash-5.1.8/parse.y
> +--- bash-5.1.8.orig/parse.y 2021-12-21 12:57:08.099139862 +0100
> ++++ bash-5.1.8/parse.y 2021-12-21 12:58:26.112890455 +0100
> +@@ -6493,10 +6493,8 @@
> + old_expand_aliases = expand_aliases;
> +
> + push_stream (1);
> +-#if 0 /* TAG: bash-5.2 Alex fxmbsw7 Ratchev <fxmbsw7@gmail.com> 11/17/2020 */
> + if (ea = expanding_alias ())
> + parser_save_alias ();
> +-#endif
> + last_read_token = WORD; /* WORD to allow reserved words here */
> + current_command_line_count = 0;
> + echo_input_at_read = expand_aliases = 0;
> +@@ -6531,10 +6529,8 @@
> + last_read_token = '\n';
> + pop_stream ();
> +
> +-#if 0 /* TAG: bash-5.2 */
> + if (ea)
> + parser_restore_alias ();
> +-#endif
> +
> + #if defined (HISTORY)
> + remember_on_history = old_remember_on_history;
> +diff -urN bash-5.1.8.orig/patchlevel.h bash-5.1.8/patchlevel.h
> +--- bash-5.1.8.orig/patchlevel.h 2021-12-21 12:57:08.075139321 +0100
> ++++ bash-5.1.8/patchlevel.h 2021-12-21 12:58:27.764927470 +0100
> +@@ -25,6 +25,6 @@
> + regexp `^#define[ ]*PATCHLEVEL', since that's what support/mkversion.sh
> + looks for to find the patch level (for the sccs version string). */
> +
> +-#define PATCHLEVEL 8
> ++#define PATCHLEVEL 12
> +
> + #endif /* _PATCHLEVEL_H_ */
> +diff -urN bash-5.1.8.orig/sig.c bash-5.1.8/sig.c
> +--- bash-5.1.8.orig/sig.c 2021-12-21 12:57:08.071139231 +0100
> ++++ bash-5.1.8/sig.c 2021-12-21 12:58:27.764927470 +0100
> +@@ -55,7 +55,8 @@
> + # include "bashhist.h"
> + #endif
> +
> +-extern void initialize_siglist ();
> ++extern void initialize_siglist PARAMS((void));
> ++extern void set_original_signal PARAMS((int, SigHandler *));
> +
> + #if !defined (JOB_CONTROL)
> + extern void initialize_job_signals PARAMS((void));
> +@@ -255,6 +256,13 @@
> + sigaction (XSIG (i), &act, &oact);
> + XHANDLER(i) = oact.sa_handler;
> + XSAFLAGS(i) = oact.sa_flags;
> ++
> ++#if 0
> ++ set_original_signal (XSIG(i), XHANDLER(i)); /* optimization */
> ++#else
> ++ set_original_signal (XSIG(i), act.sa_handler); /* optimization */
> ++#endif
> ++
> + /* Don't do anything with signals that are ignored at shell entry
> + if the shell is not interactive. */
> + /* XXX - should we do this for interactive shells, too? */
> +diff -urN bash-5.1.8.orig/subst.c bash-5.1.8/subst.c
> +--- bash-5.1.8.orig/subst.c 2021-12-21 12:57:08.099139862 +0100
> ++++ bash-5.1.8/subst.c 2021-12-21 12:58:27.764927470 +0100
> +@@ -5951,6 +5951,7 @@
> + free_pushed_string_input ();
> + /* Cancel traps, in trap.c. */
> + restore_original_signals (); /* XXX - what about special builtins? bash-4.2 */
> ++ subshell_environment &= ~SUBSHELL_IGNTRAP;
> + QUIT; /* catch any interrupts we got post-fork */
> + setup_async_signals ();
> + #if 0
> +@@ -6382,6 +6383,7 @@
> + }
> + QUIT; /* catch any interrupts we got post-fork */
> + subshell_environment |= SUBSHELL_RESETTRAP;
> ++ subshell_environment &= ~SUBSHELL_IGNTRAP;
> + }
> +
> + #if defined (JOB_CONTROL)
> +diff -urN bash-5.1.8.orig/trap.c bash-5.1.8/trap.c
> +--- bash-5.1.8.orig/trap.c 2021-12-21 12:57:08.083139502 +0100
> ++++ bash-5.1.8/trap.c 2021-12-21 12:58:27.764927470 +0100
> +@@ -481,6 +481,32 @@
> + SIGRETURN (0);
> + }
> +
> ++ /* This means we're in a subshell, but have not yet reset the handler for
> ++ trapped signals. We're not supposed to execute the trap in this situation;
> ++ we should restore the original signal and resend the signal to ourselves
> ++ to preserve the Posix "signal traps that are not being ignored shall be
> ++ set to the default action" semantics. */
> ++ if ((subshell_environment & SUBSHELL_IGNTRAP) && trap_list[sig] != (char *)IGNORE_SIG)
> ++ {
> ++ sigset_t mask;
> ++
> ++ /* Paranoia */
> ++ if (original_signals[sig] == IMPOSSIBLE_TRAP_HANDLER)
> ++ original_signals[sig] = SIG_DFL;
> ++
> ++ restore_signal (sig);
> ++
> ++ /* Make sure we let the signal we just caught through */
> ++ sigemptyset (&mask);
> ++ sigprocmask (SIG_SETMASK, (sigset_t *)NULL, &mask);
> ++ sigdelset (&mask, sig);
> ++ sigprocmask (SIG_SETMASK, &mask, (sigset_t *)NULL);
> ++
> ++ kill (getpid (), sig);
> ++
> ++ SIGRETURN (0);
> ++ }
> ++
> + if ((sig >= NSIG) ||
> + (trap_list[sig] == (char *)DEFAULT_SIG) ||
> + (trap_list[sig] == (char *)IGNORE_SIG))
> +diff -urN bash-5.1.8.orig/y.tab.c bash-5.1.8/y.tab.c
> +--- bash-5.1.8.orig/y.tab.c 2021-12-21 12:57:08.075139321 +0100
> ++++ bash-5.1.8/y.tab.c 2021-12-21 12:58:26.116890545 +0100
> +@@ -8787,10 +8787,8 @@
> + old_expand_aliases = expand_aliases;
> +
> + push_stream (1);
> +-#if 0 /* TAG: bash-5.2 Alex fxmbsw7 Ratchev <fxmbsw7@gmail.com> 11/17/2020 */
> + if (ea = expanding_alias ())
> + parser_save_alias ();
> +-#endif
> + last_read_token = WORD; /* WORD to allow reserved words here */
> + current_command_line_count = 0;
> + echo_input_at_read = expand_aliases = 0;
> +@@ -8825,10 +8823,8 @@
> + last_read_token = '\n';
> + pop_stream ();
> +
> +-#if 0 /* TAG: bash-5.2 */
> + if (ea)
> + parser_restore_alias ();
> +-#endif
> +
> + #if defined (HISTORY)
> + remember_on_history = old_remember_on_history;
> diff --git a/patches/bash-5.1.8/series b/patches/bash-5.1.8/series
> new file mode 100644
> index 000000000..0e53e492a
> --- /dev/null
> +++ b/patches/bash-5.1.8/series
> @@ -0,0 +1 @@
> +0001-Bash-5.1-patch-12.patch
> diff --git a/rules/bash.make b/rules/bash.make
> index bed121586..c46196f7f 100644
> --- a/rules/bash.make
> +++ b/rules/bash.make
> @@ -13,8 +13,8 @@ PACKAGES-$(PTXCONF_BASH) += bash
> #
> # Paths and names
> #
> -BASH_VERSION := 4.3.30
> -BASH_MD5 := a27b3ee9be83bd3ba448c0ff52b28447
> +BASH_VERSION := 5.1.8
> +BASH_MD5 := 23eee6195b47318b9fd878e590ccb38c
> BASH := bash-$(BASH_VERSION)
> BASH_SUFFIX := tar.gz
> BASH_URL := $(call ptx/mirror, GNU, bash/$(BASH).$(BASH_SUFFIX))
> --
> 2.30.2
>
>
> _______________________________________________
> ptxdist mailing list
> ptxdist@pengutronix.de
> To unsubscribe, send a mail with subject "unsubscribe" to ptxdist-request@pengutronix.de
>
--
Pengutronix e.K. | |
Steuerwalder Str. 21 | http://www.pengutronix.de/ |
31137 Hildesheim, Germany | Phone: +49-5121-206917-0 |
Amtsgericht Hildesheim, HRA 2686 | Fax: +49-5121-206917-5555 |
_______________________________________________
ptxdist mailing list
ptxdist@pengutronix.de
To unsubscribe, send a mail with subject "unsubscribe" to ptxdist-request@pengutronix.de
prev parent reply other threads:[~2022-01-05 13:00 UTC|newest]
Thread overview: 54+ messages / expand[flat|nested] mbox.gz Atom feed top
2021-12-22 13:02 Christian Melki
2021-12-22 13:02 ` [ptxdist] [PATCH] bridge-utils: Version bump. 1.6 -> 1.7.1 Christian Melki
2022-01-21 7:18 ` [ptxdist] [APPLIED] " Michael Olbrich
2021-12-22 13:02 ` [ptxdist] [PATCH] curl: Version bump 7.77.0 -> 7.80.0 Christian Melki
2022-01-21 7:19 ` [ptxdist] [APPLIED] " Michael Olbrich
2021-12-22 13:02 ` [ptxdist] [PATCH] e2fsprogs: Version bump 1.46.2 -> 1.46.4 Christian Melki
2022-01-21 7:19 ` [ptxdist] [APPLIED] " Michael Olbrich
2021-12-22 13:02 ` [ptxdist] [PATCH] ethtool: Version bump. 5.13 -> 5.15 Christian Melki
2022-01-21 7:19 ` [ptxdist] [APPLIED] " Michael Olbrich
2021-12-22 13:02 ` [ptxdist] [PATCH] expat: Version bump 2.4.1 -> 2.4.2 Christian Melki
2022-01-21 7:19 ` [ptxdist] [APPLIED] " Michael Olbrich
2021-12-22 13:02 ` [ptxdist] [PATCH] host-libcap: BUILD_GPERF is reserved Christian Melki
2022-01-05 12:18 ` Michael Olbrich
2021-12-22 13:02 ` [ptxdist] [PATCH] iptables: Version bump 1.8.3 -> 1.8.7 Christian Melki
2022-01-06 7:10 ` Michael Olbrich
2022-01-21 7:19 ` [ptxdist] [APPLIED] " Michael Olbrich
2021-12-22 13:02 ` [ptxdist] [PATCH] jimtcl: Verison bump 0.80 -> 0.81 Christian Melki
2022-01-21 7:19 ` [ptxdist] [APPLIED] " Michael Olbrich
2021-12-22 13:02 ` [ptxdist] [PATCH] libcap-ng: Version bump 0.7.10 -> 0.8.2 Christian Melki
2022-01-21 7:19 ` [ptxdist] [APPLIED] " Michael Olbrich
2021-12-22 13:02 ` [ptxdist] [PATCH] libcap: Version bump 2.51 -> 2.62 Christian Melki
2022-01-05 12:21 ` Michael Olbrich
2022-01-05 12:32 ` Christian Melki
2022-01-05 12:46 ` Michael Olbrich
2021-12-22 13:02 ` [ptxdist] [PATCH] libffi: Version bump 3.3 -> 3.4.2 Christian Melki
2022-01-21 7:19 ` [ptxdist] [APPLIED] " Michael Olbrich
2021-12-22 13:02 ` [ptxdist] [PATCH] libjpeg: Version bump 2.1.0 -> 2.1.2 Christian Melki
2022-01-21 7:19 ` [ptxdist] [APPLIED] " Michael Olbrich
2021-12-22 13:02 ` [ptxdist] [PATCH] libmbim: Version bump 1.24.2 -> 1.26.2 Christian Melki
2022-01-21 7:19 ` [ptxdist] [APPLIED] " Michael Olbrich
2021-12-22 13:02 ` [ptxdist] [PATCH] libseccomp: Version bump 2.5.1 -> 2.5.3 Christian Melki
2022-01-06 10:56 ` Michael Olbrich
2021-12-22 13:02 ` [ptxdist] [PATCH] libunwind: Version bump 1.5.0 -> 1.6.2 Christian Melki
2022-01-21 7:19 ` [ptxdist] [APPLIED] " Michael Olbrich
2021-12-22 13:02 ` [ptxdist] [PATCH] openssh: Version bump 8.6p1 -> 8.8p1 Christian Melki
2022-01-21 7:19 ` [ptxdist] [APPLIED] " Michael Olbrich
2021-12-22 13:02 ` [ptxdist] [PATCH] screen: Version bump 4.5.0 -> 4.8.0 Christian Melki
2022-01-06 10:55 ` Michael Olbrich
2022-01-07 9:58 ` Christian Melki
2022-01-07 11:05 ` Michael Olbrich
2022-01-21 7:19 ` [ptxdist] [APPLIED] " Michael Olbrich
2021-12-22 13:03 ` [ptxdist] [PATCH] strace: Version bump 5.9 -> 5.15 Christian Melki
2022-01-05 12:53 ` Michael Olbrich
2021-12-22 13:03 ` [ptxdist] [PATCH] tcpdump: Version bump 4.93 -> 4.99.1 Christian Melki
2022-01-06 7:22 ` Michael Olbrich
2021-12-22 13:03 ` [ptxdist] [WIP: PATCH] usbutils: Version bump 007 -> 014 Christian Melki
2022-01-05 12:38 ` Michael Olbrich
2022-01-06 21:52 ` Christian Melki
2022-01-07 8:09 ` Michael Olbrich
2021-12-22 13:03 ` [ptxdist] [PATCH] util-linux-ng: Version bump 2.37 -> 2.37.2 Christian Melki
2022-01-21 7:19 ` [ptxdist] [APPLIED] " Michael Olbrich
2021-12-22 13:03 ` [ptxdist] [PATCH] zstd: Version bump 1.5.0 -> 1.5.1 Christian Melki
2022-01-21 7:19 ` [ptxdist] [APPLIED] " Michael Olbrich
2022-01-05 13:00 ` Michael Olbrich [this message]
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=YdWWaWu6hllAgDnA@pengutronix.de \
--to=m.olbrich@pengutronix.de \
--cc=christian.melki@t2data.com \
--cc=ptxdist@pengutronix.de \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox