summaryrefslogtreecommitdiffstats
path: root/patches/bash-4.3.30
diff options
context:
space:
mode:
authorCarsten Schlote <schlote@vahanus.net>2015-03-04 12:09:36 +0100
committerMichael Olbrich <m.olbrich@pengutronix.de>2015-03-26 09:36:26 +0100
commitca2eeabccfd464bd5b0cbe0432ba56029ab315b3 (patch)
tree80ec508bf42e1b6827768b9058b992ee9ba6e4ff /patches/bash-4.3.30
parent4737c3d2db9d783a2cb8ff814f86a68bbb5233d1 (diff)
downloadptxdist-ca2eeabccfd464bd5b0cbe0432ba56029ab315b3.tar.gz
ptxdist-ca2eeabccfd464bd5b0cbe0432ba56029ab315b3.tar.xz
bash: Updated the bash shell to release 4.3.30
systemd seems to use modern bash features like readarray, which is not provided by version 3.2.48 on current ptxdist. So I updated the bash package to a more recent bash version. The config fragment was updated with actual configure arguments, so that bash should be configurable similiar as for 3.2 version. Removed unneeded dependency statements and other tweaks like unconditionally selecting BASH_CONDITIONAL. The patches for version 3.2.48 are dropped as these patches are no longer needed for version 4.3. At least I got told so. Signed-off-by: Carsten Schlote <schlote@vahanus.net> [mol: add new and remove old patches, BASH_SEP_HELPFILES is broken] Signed-off-by: Michael Olbrich <m.olbrich@pengutronix.de>
Diffstat (limited to 'patches/bash-4.3.30')
-rw-r--r--patches/bash-4.3.30/0001-Bash-4.3-patch-31.patch5467
-rw-r--r--patches/bash-4.3.30/0002-Bash-4.3-patch-32.patch5409
-rw-r--r--patches/bash-4.3.30/0003-Bash-4.3-patch-33.patch204
-rw-r--r--patches/bash-4.3.30/series6
4 files changed, 11086 insertions, 0 deletions
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
new file mode 100644
index 000000000..d9a187dcb
--- /dev/null
+++ b/patches/bash-4.3.30/0001-Bash-4.3-patch-31.patch
@@ -0,0 +1,5467 @@
+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
new file mode 100644
index 000000000..801b4a609
--- /dev/null
+++ b/patches/bash-4.3.30/0002-Bash-4.3-patch-32.patch
@@ -0,0 +1,5409 @@
+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
new file mode 100644
index 000000000..cda179735
--- /dev/null
+++ b/patches/bash-4.3.30/0003-Bash-4.3-patch-33.patch
@@ -0,0 +1,204 @@
+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
new file mode 100644
index 000000000..2e1fdf17f
--- /dev/null
+++ b/patches/bash-4.3.30/series
@@ -0,0 +1,6 @@
+# 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