]> git.ipfire.org Git - thirdparty/bash.git/blobdiff - variables.c
Bash-4.3 patch 32
[thirdparty/bash.git] / variables.c
index 024c05fe85ac5c0e072d8ed16a99b2769dfb4453..81b7877e32e8001cc2b74c30463113d06ea1b647 100644 (file)
@@ -1,22 +1,22 @@
 /* variables.c -- Functions for hacking shell variables. */
 
-/* Copyright (C) 1987-2004 Free Software Foundation, Inc.
+/* 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 2, or (at your option)
-   any later version.
+   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.
+   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; see the file COPYING.  If not, write to the Free
-   Software Foundation, 59 Temple Place, Suite 330, Boston, MA 02111 USA. */
+   along with Bash.  If not, see <http://www.gnu.org/licenses/>.
+*/
 
 #include "config.h"
 
 #include "posixstat.h"
 #include "posixtime.h"
 
-#if defined (qnx)
-#  include <sys/vc.h>
-#endif
+#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>
 
 #include <stdio.h>
 #include "chartypes.h"
-#include <pwd.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 "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"
 
 #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;
+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;
@@ -86,8 +107,12 @@ 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
 
@@ -128,6 +153,13 @@ 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. */
@@ -135,14 +167,15 @@ char **export_env = (char **)NULL;
 static int export_env_index;
 static int export_env_size;
 
-/* Non-zero means that we have to remake EXPORT_ENV. */
-int array_needs_making = 1;
+#if defined (READLINE)
+static int winsize_assignment;         /* currently assigning to LINES or COLUMNS */
+#endif
 
-/* The number of times BASH has been executed.  This is set
-   by initialize_variables (). */
-int shell_level = 0;
+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));
@@ -153,42 +186,60 @@ static void uidset __P((void));
 static void make_vers_array __P((void));
 #endif
 
-static SHELL_VAR *null_assign __P((SHELL_VAR *, char *, arrayind_t));
+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));
+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));
+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 SHELL_VAR *assign_random __P((SHELL_VAR *, char *, arrayind_t));
+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));
+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));
+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));
+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 *));
@@ -199,8 +250,9 @@ 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));
+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));
@@ -216,18 +268,23 @@ 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 *));
+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));
@@ -240,19 +297,10 @@ 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 *));
-              
-/* 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;
-  SHELL_VAR *temp_var;
 
+static void
+create_variable_tables ()
+{
   if (shell_variables == 0)
     {
       shell_variables = global_variables = new_var_context ((char *)NULL, 0);
@@ -267,6 +315,21 @@ initialize_shell_variables (env, privmode)
   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++]; )
     {
@@ -287,40 +350,59 @@ initialize_shell_variables (env, privmode)
       /* 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 ("() {", string, 4))
+      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))
        {
-         string_length = strlen (string);
-         temp_string = (char *)xmalloc (3 + string_length + char_index);
+         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 */
 
-         strcpy (temp_string, name);
-         temp_string[char_index] = ' ';
-         strcpy (temp_string + char_index + 1, string);
+         string_length = strlen (string);
+         temp_string = (char *)xmalloc (namelen + string_length + 2);
 
-         parse_and_execute (temp_string, name, SEVAL_NONINT|SEVAL_NOHIST);
+         memcpy (temp_string, tname, namelen);
+         temp_string[namelen] = ' ';
+         memcpy (temp_string + namelen + 1, string, string_length + 1);
 
-         /* Ancient backwards compatibility.  Old versions of bash exported
-            functions like name()=() {...} */
-         if (name[char_index - 1] == ')' && name[char_index - 2] == '(')
-           name[char_index - 2] = '\0';
+         /* 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 (name))
+         if (temp_var = find_function (tname))
            {
              VSETATTR (temp_var, (att_exported|att_imported));
              array_needs_making = 1;
            }
          else
-           report_error (_("error importing function definition for `%s'"), name);
+           {
+             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);
+           }
 
-         /* ( */
-         if (name[char_index - 1] == ')' && name[char_index - 2] == '\0')
-           name[char_index - 2] = '(';         /* ) */
+         /* Restore original suffix */
+         tname[namelen] = BASHFUNC_SUFFIX[0];
        }
 #if defined (ARRAY_VARS)
-#  if 0
+#  if ARRAY_EXPORT
       /* Array variables may not yet be exported. */
-      else if (*string == '(' && string[1] == '[' && xstrchr (string, ')'))
+      else if (*string == '(' && string[1] == '[' && string[strlen (string) - 1] == ')')
        {
          string_length = 1;
          temp_string = extract_array_assignment_list (string, &string_length);
@@ -329,13 +411,33 @@ initialize_shell_variables (env, privmode)
          VSETATTR (temp_var, (att_exported | att_imported));
          array_needs_making = 1;
        }
-#  endif
+#  endif /* ARRAY_EXPORT */
 #endif
+#if 0
+      else if (legal_identifier (name))
+#else
       else
+#endif
        {
-         temp_var = bind_variable (name, string);
-         VSETATTR (temp_var, (att_exported | att_imported));
-         array_needs_making = 1;
+         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] = '=';
@@ -350,7 +452,7 @@ initialize_shell_variables (env, privmode)
   set_pwd ();
 
   /* Set up initial value of $_ */
-  temp_var = bind_variable ("_", dollar_vars[0]);
+  temp_var = set_if_not ("_", dollar_vars[0]);
 
   /* Remember this pid. */
   dollar_dollar_pid = getpid ();
@@ -367,12 +469,16 @@ initialize_shell_variables (env, privmode)
   set_auto_export (temp_var);  /* XXX */
 #endif
 
-#if defined (qnx)
+#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));
-    temp_var = bind_variable ("NODE", node_name);
+#  endif
+    temp_var = bind_variable ("NODE", node_name, 0);
     set_auto_export (temp_var);
   }
 #endif
@@ -392,7 +498,7 @@ initialize_shell_variables (env, privmode)
   set_if_not ("PS4", "+ ");
 
   /* Don't allow IFS to be imported from the environment. */
-  temp_var = bind_variable ("IFS", " \t\n");
+  temp_var = bind_variable ("IFS", " \t\n", 0);
   setifs (temp_var);
 
   /* Magic machine types.  Pretty convenient. */
@@ -403,7 +509,10 @@ initialize_shell_variables (env, privmode)
      names a mail file if MAILPATH is not set, and we should provide a
      default only if neither is set. */
   if (interactive_shell)
-    set_if_not ("MAILCHECK", posixly_correct ? "600" : "60");
+    {
+      temp_var = set_if_not ("MAILCHECK", posixly_correct ? "600" : "60");
+      VSETATTR (temp_var, att_integer);
+    }
 
   /* Do some things with shell level. */
   initialize_shell_level ();
@@ -411,18 +520,19 @@ initialize_shell_variables (env, privmode)
   set_ppid ();
 
   /* Initialize the `getopts' stuff. */
-  bind_variable ("OPTIND", "1");
+  temp_var = bind_variable ("OPTIND", "1", 0);
+  VSETATTR (temp_var, att_integer);
   getopts_reset (0);
-  bind_variable ("OPTERR", "1");
+  bind_variable ("OPTERR", "1", 0);
   sh_opterr = 1;
 
-  if (login_shell == 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);
+  temp_var = bind_variable ("BASH", name, 0);
   free (name);
 
   /* Make the exported environment variable SHELL be the user's login
@@ -432,13 +542,13 @@ initialize_shell_variables (env, privmode)
   set_shell_var ();
 
   /* Make a variable called BASH_VERSION which contains the version info. */
-  bind_variable ("BASH_VERSION", shell_version_string ());
+  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);
+    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. */
@@ -458,14 +568,11 @@ initialize_shell_variables (env, privmode)
 
       set_if_not ("HISTFILE", name);
       free (name);
-
-      set_if_not ("HISTSIZE", "500");
-      sv_histsize ("HISTSIZE");
     }
 #endif /* HISTORY */
 
   /* Seed the random number generator. */
-  sbrand (dollar_dollar_pid + shell_start_time);
+  seedrand ();
 
   /* Handle some "special" variables that we may have inherited from a
      parent shell. */
@@ -483,9 +590,17 @@ initialize_shell_variables (env, privmode)
     {
       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
       *
@@ -514,6 +629,10 @@ initialize_shell_variables (env, privmode)
   /* 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 ();
 }
@@ -556,7 +675,7 @@ set_home_var ()
 
   temp_var = find_variable ("HOME");
   if (temp_var == 0)
-    temp_var = bind_variable ("HOME", sh_get_home_dir ());
+    temp_var = bind_variable ("HOME", sh_get_home_dir (), 0);
 #if 0
   VSETATTR (temp_var, att_exported);
 #endif
@@ -574,7 +693,7 @@ set_shell_var ()
     {
       if (current_user.shell == 0)
        get_current_user_info ();
-      temp_var = bind_variable ("SHELL", current_user.shell);
+      temp_var = bind_variable ("SHELL", current_user.shell, 0);
     }
 #if 0
   VSETATTR (temp_var, att_exported);
@@ -696,7 +815,7 @@ adjust_shell_level (change)
       new_level[3] = '\0';
     }
 
-  temp_var = bind_variable ("SHLVL", new_level);
+  temp_var = bind_variable ("SHLVL", new_level, 0);
   set_auto_export (temp_var);
 }
 
@@ -731,7 +850,7 @@ set_pwd ()
           same_file (home_string, ".", (struct stat *)NULL, (struct stat *)NULL))
     {
       set_working_directory (home_string);
-      temp_var = bind_variable ("PWD", home_string);
+      temp_var = bind_variable ("PWD", home_string, 0);
       set_auto_export (temp_var);
     }
   else
@@ -739,7 +858,7 @@ set_pwd ()
       temp_string = get_working_directory ("shell-init");
       if (temp_string)
        {
-         temp_var = bind_variable ("PWD", temp_string);
+         temp_var = bind_variable ("PWD", temp_string, 0);
          set_auto_export (temp_var);
          free (temp_string);
        }
@@ -748,7 +867,7 @@ set_pwd ()
   /* 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);
+  temp_var = bind_variable ("OLDPWD", (char *)NULL, 0);
   VSETATTR (temp_var, (att_exported | att_invisible));
 }
 
@@ -763,7 +882,7 @@ set_ppid ()
   temp_var = find_variable ("PPID");
   if (temp_var)
     VUNSETATTR (temp_var, (att_readonly | att_exported));
-  temp_var = bind_variable ("PPID", name);
+  temp_var = bind_variable ("PPID", name, 0);
   VSETATTR (temp_var, (att_readonly | att_integer));
 }
 
@@ -777,7 +896,7 @@ uidset ()
   v = find_variable ("UID");
   if (v == 0)
     {
-      v = bind_variable ("UID", b);
+      v = bind_variable ("UID", b, 0);
       VSETATTR (v, (att_readonly | att_integer));
     }
 
@@ -787,7 +906,7 @@ uidset ()
   v = find_variable ("EUID");
   if (v == 0)
     {
-      v = bind_variable ("EUID", b);
+      v = bind_variable ("EUID", b, 0);
       VSETATTR (v, (att_readonly | att_integer));
     }
 }
@@ -805,7 +924,7 @@ make_vers_array ()
   vv = make_new_array_variable ("BASH_VERSINFO");
   av = array_cell (vv);
   strcpy (d, dist_version);
-  s = xstrchr (d, '.');
+  s = strchr (d, '.');
   if (s)
     *s++ = '\0';
   array_insert (av, 0, d);
@@ -829,11 +948,17 @@ sh_set_lines_and_columns (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);
+  bind_variable ("LINES", v, 0);
 
   v = inttostr (cols, val, sizeof (val));
-  bind_variable ("COLUMNS", v);
+  bind_variable ("COLUMNS", v, 0);
 }
 
 /* **************************************************************** */
@@ -892,6 +1017,8 @@ print_assignment (var)
 #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
     {
@@ -937,8 +1064,13 @@ void
 print_var_function (var)
      SHELL_VAR *var;
 {
+  char *x;
+
   if (function_p (var) && var_isset (var))
-    printf ("%s", named_function_string ((char *)NULL, function_cell(var), 1));
+    {
+      x = named_function_string ((char *)NULL, function_cell(var), FUNC_MULTILINE|FUNC_EXTERNAL);
+      printf ("%s", x);
+    }
 }
 
 /* **************************************************************** */
@@ -983,7 +1115,7 @@ print_var_function (var)
 #define INIT_DYNAMIC_VAR(var, val, gfunc, afunc) \
   do \
     { \
-      v = bind_variable (var, (val)); \
+      v = bind_variable (var, (val), 0); \
       v->dynamic_value = gfunc; \
       v->assign_func = afunc; \
     } \
@@ -998,21 +1130,32 @@ print_var_function (var)
     } \
   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)
+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)
+null_array_assign (self, value, ind, key)
      SHELL_VAR *self;
      char *value;
      arrayind_t ind;
+     char *key;
 {
   return (self);
 }
@@ -1028,7 +1171,7 @@ get_self (self)
 }
 
 #if defined (ARRAY_VARS)
-/* A generic dynamic array variable initializer.  Intialize array variable
+/* 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)
@@ -1047,8 +1190,25 @@ init_dynamic_array_var (name, getfunc, setfunc, attrs)
     VSETATTR (v, attrs);
   return v;
 }
-#endif
 
+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
@@ -1056,10 +1216,11 @@ init_dynamic_array_var (name, getfunc, setfunc, attrs)
 static intmax_t seconds_value_assigned;
 
 static SHELL_VAR *
-assign_seconds (self, value, unused)
+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;
@@ -1102,6 +1263,7 @@ init_seconds_var ()
 /* 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
@@ -1111,8 +1273,22 @@ static int last_random_value;
 static int
 brand ()
 {
-  rseed = rseed * 1103515245 + 12345;
-  return ((unsigned int)((rseed >> 16) & 32767));      /* was % 32768 */
+  /* 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. */
@@ -1124,24 +1300,40 @@ sbrand (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)
+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;
+  int rv, pid;
 
   /* Reset for command and process substitution. */
-  if (subshell_environment)
-    sbrand (rseed + getpid() + NOW);
+  pid = getpid ();
+  if (subshell_environment && seeded_subshell != pid)
+    {
+      seedrand ();
+      seeded_subshell = pid;
+    }
 
   do
     rv = brand ();
@@ -1168,16 +1360,17 @@ get_random (var)
 }
 
 static SHELL_VAR *
-assign_lineno (var, value, unused)
+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 = new_value;
+  line_number = line_number_base = new_value;
   return var;
 }
 
@@ -1197,10 +1390,11 @@ get_lineno (var)
 }
 
 static SHELL_VAR *
-assign_subshell (var, value, unused)
+assign_subshell (var, value, unused, key)
      SHELL_VAR *var;
      char *value;
      arrayind_t unused;
+     char *key;
 {
   intmax_t new_value;
 
@@ -1222,13 +1416,35 @@ get_subshell (var)
   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;
 
-  p = savestring (the_printed_command_except_trap);
+  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);
@@ -1254,20 +1470,12 @@ static SHELL_VAR *
 get_comp_wordbreaks (var)
      SHELL_VAR *var;
 {
-  char *p;
-
   /* 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);
 
-#if 0
   FREE (value_cell (var));
-  p = savestring (rl_completer_word_break_characters);
-  
-  var_setvalue (var, p);
-#else
-  var_setvalue (var, rl_completer_word_break_characters);
-#endif
+  var_setvalue (var, savestring (rl_completer_word_break_characters));
 
   return (var);
 }
@@ -1275,10 +1483,11 @@ get_comp_wordbreaks (var)
 /* When this function returns, rl_completer_word_break_characters points to
    malloced memory. */
 static SHELL_VAR *
-assign_comp_wordbreaks (self, value, unused)
+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)
@@ -1291,10 +1500,11 @@ assign_comp_wordbreaks (self, value, unused)
 
 #if defined (PUSHD_AND_POPD) && defined (ARRAY_VARS)
 static SHELL_VAR *
-assign_dirstack (self, value, ind)
+assign_dirstack (self, value, ind, key)
      SHELL_VAR *self;
      char *value;
      arrayind_t ind;
+     char *key;
 {
   set_dirstack_element (ind, 1, value);
   return self;
@@ -1307,7 +1517,7 @@ get_dirstack (self)
   ARRAY *a;
   WORD_LIST *l;
 
-  l = get_directory_stack ();
+  l = get_directory_stack (0);
   a = array_from_word_list (l);
   array_dispose (array_cell (self));
   dispose_words (l);
@@ -1337,6 +1547,115 @@ get_groupset (self)
     }
   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
@@ -1401,10 +1720,16 @@ initialize_dynamic_variables ()
   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)
@@ -1419,11 +1744,16 @@ initialize_dynamic_variables ()
   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);
-  v = init_dynamic_array_var ("BASH_ARGV", get_self, null_array_assign, att_noassign);
+  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);
-  v = init_dynamic_array_var ("BASH_LINENO", get_self, null_array_assign, att_noassign);
+  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 ();
@@ -1447,6 +1777,10 @@ hash_lookup (name, 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);
 }
 
@@ -1470,7 +1804,7 @@ var_lookup (name, vcontext)
    then also search the temporarily built list of exported variables.
    The lookup order is:
        temporary_env
-        shell_variables list
+       shell_variables list
 */
 
 SHELL_VAR *
@@ -1480,6 +1814,7 @@ find_variable_internal (name, force_tempenv)
 {
   SHELL_VAR *var;
   int search_tempenv;
+  VAR_CONTEXT *vc;
 
   var = (SHELL_VAR *)NULL;
 
@@ -1493,8 +1828,22 @@ find_variable_internal (name, force_tempenv)
   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, shell_variables);
+    var = var_lookup (name, vc);
 
   if (var == 0)
     return ((SHELL_VAR *)NULL);
@@ -1502,43 +1851,302 @@ find_variable_internal (name, force_tempenv)
   return (var->dynamic_value ? (*(var->dynamic_value)) (var) : var);
 }
 
-/* Look up the variable entry named NAME.  Returns the entry or NULL. */
+/* Look up and resolve the chain of nameref variables starting at V all the
+   way to NULL or non-nameref. */
 SHELL_VAR *
-find_variable (name)
-     const char *name;
+find_variable_nameref (v)
+     SHELL_VAR *v;
 {
-  return (find_variable_internal (name, (expanding_redir == 0 && this_shell_builtin != 0)));
+  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;
 }
 
-/* Look up the function entry whose name matches STRING.
-   Returns the entry or NULL. */
+/* Resolve the chain of nameref variables for NAME.  XXX - could change later */
 SHELL_VAR *
-find_function (name)
+find_variable_last_nameref (name)
      const char *name;
 {
-  return (hash_lookup (name, shell_functions));
+  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;
 }
 
-/* Find the function definition for the shell function named NAME.  Returns
-   the entry or NULL. */
-FUNCTION_DEF *
-find_function_def (name)
+/* Resolve the chain of nameref variables for NAME.  XXX - could change later */
+SHELL_VAR *
+find_global_variable_last_nameref (name)
      const char *name;
 {
-  return ((FUNCTION_DEF *)hash_lookup (name, shell_function_defs));
+  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;
 }
 
-/* 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;
+static SHELL_VAR *
+find_nameref_at_context (v, vc)
+     SHELL_VAR *v;
+     VAR_CONTEXT *vc;
 {
-  if (var == 0)
-    return ((char *)NULL);
+  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));
@@ -1580,9 +2188,12 @@ set_if_not (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);
+    v = bind_variable_internal (name, value, global_variables->table, HASH_NOSRCH, 0);
   return (v);
 }
 
@@ -1599,12 +2210,24 @@ make_local_variable (name)
   /* 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);
     }
-
-  was_tmpvar = old_var && tempvar_p (old_var);
   if (was_tmpvar)
     tmp_value = value_cell (old_var);
 
@@ -1632,20 +2255,28 @@ make_local_variable (name)
     {
       if (readonly_p (old_var))
        sh_readonly (name);
-      return ((SHELL_VAR *)NULL);
+      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 = bind_variable_internal (name, "", vc->table, HASH_NOSRCH);
+    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'. */
+        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));
+       var_setvalue (new_var, savestring (tmp_value));
 
       new_var->attributes = exported_p (old_var) ? att_exported : 0;
     }
@@ -1658,30 +2289,11 @@ make_local_variable (name)
   if (ifsname (name))
     setifs (new_var);
 
+  if (was_tmpvar == 0)
+    VSETATTR (new_var, att_invisible); /* XXX */
   return (new_var);
 }
 
-#if defined (ARRAY_VARS)
-SHELL_VAR *
-make_local_array_variable (name)
-     char *name;
-{
-  SHELL_VAR *var;
-  ARRAY *array;
-
-  var = make_local_variable (name);
-  if (var == 0 || array_p (var))
-    return var;
-
-  array = array_create ();
-
-  FREE (value_cell(var));
-  var_setarray (var, array);
-  VSETATTR (var, att_array);
-  return var;
-}
-#endif /* ARRAY_VARS */
-
 /* Create a new shell variable with name NAME. */
 static SHELL_VAR *
 new_shell_variable (name)
@@ -1701,7 +2313,7 @@ new_shell_variable (name)
   entry->attributes = 0;
 
   /* Always assume variables are to be made at toplevel!
-     make_local_variable has the responsibilty of changing the
+     make_local_variable has the responsibility of changing the
      variable context. */
   entry->context = 0;
 
@@ -1722,11 +2334,7 @@ make_new_variable (name, table)
 
   /* Make sure we have a shell_variables hash table to add to. */
   if (shell_variables == 0)
-    {
-      shell_variables = global_variables = new_var_context ((char *)NULL, 0);
-      shell_variables->scope = 0;
-      shell_variables->table = hash_create (0);
-    }
+    create_variable_tables ();
 
   elt = hash_insert (savestring (name), table, HASH_NOSRCH);
   elt->data = (PTR_T)entry;
@@ -1744,20 +2352,76 @@ make_new_array_variable (name)
 
   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)
+make_variable_value (var, value, flags)
      SHELL_VAR *var;
      char *value;
+     int flags;
 {
-  char *retval;
-  intmax_t lval;
-  int expok;
+  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
@@ -1766,14 +2430,70 @@ make_variable_value (var, value)
      evaluation done. */
   if (integer_p (var))
     {
-      lval = evalexp (value, &expok);
+      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)
-       jump_to_top_level (DISCARD);
-      retval = itos (lval);
+       {
+         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 (*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
        {
@@ -1790,29 +2510,95 @@ make_variable_value (var, value)
 /* 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)
+bind_variable_internal (name, value, table, hflags, aflags)
      const char *name;
      char *value;
      HASH_TABLE *table;
-     int hflags;
+     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);
 
-  if (entry == 0)
+  /* 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));
+      }
+    }
+#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);
-      var_setvalue (entry, make_variable_value (entry, value));
+      var_setvalue (entry, make_variable_value (entry, value, 0)); /* XXX */
     }
   else if (entry->assign_func) /* array vars have assign functions now */
     {
       INVALIDATE_EXPORTSTR (entry);
-      return ((*(entry->assign_func)) (entry, value, -1));
+      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))
@@ -1823,7 +2609,13 @@ bind_variable_internal (name, value, table, hflags)
       /* Variables which are bound are visible. */
       VUNSETATTR (entry, att_invisible);
 
-      newval = make_variable_value (entry, value);
+#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);
@@ -1833,7 +2625,12 @@ bind_variable_internal (name, value, table, hflags)
       /* 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 (array_p (entry))
+      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);
@@ -1860,19 +2657,17 @@ bind_variable_internal (name, value, table, hflags)
    first, then we bind into shell_variables. */
 
 SHELL_VAR *
-bind_variable (name, value)
+bind_variable (name, value, flags)
      const char *name;
      char *value;
+     int flags;
 {
-  SHELL_VAR *v;
-  VAR_CONTEXT *vc;
+  SHELL_VAR *v, *nv;
+  VAR_CONTEXT *vc, *nvc;
+  int level;
 
   if (shell_variables == 0)
-    {
-      shell_variables = global_variables = new_var_context ((char *)NULL, 0);
-      shell_variables->scope = 0;
-      shell_variables->table = hash_create (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
@@ -1886,13 +2681,54 @@ bind_variable (name, value)
   for (vc = shell_variables; vc; vc = vc->down)
     {
       if (vc_isfuncenv (vc) || vc_isbltnenv (vc))
-        {
-          v = hash_lookup (name, vc->table);
-          if (v)
-           return (bind_variable_internal (name, value, vc->table, 0));
-        }
+       {
+         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));
+                     /* XXX - bug here with ref=array[index] */
+                     return (bind_variable_internal (nameref_cell (nv), value, nvc->table, 0, flags|ASS_FROMREF));
+                   }
+                 else
+                   v = nv;
+               }
+             else
+               v = nv;
+           }
+         if (v)
+           return (bind_variable_internal (v->name, value, nvc->table, 0, flags));
+       }
     }
-  return (bind_variable_internal (name, value, global_variables->table, 0));
+  /* 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
@@ -1901,17 +2737,43 @@ bind_variable (name, value)
    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)
+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);
 
-  t = make_variable_value (var, value);
-  FREE (value_cell (var));
-  var_setvalue (var, t);
+  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);
 
@@ -1939,16 +2801,11 @@ bind_int_variable (lhs, rhs)
      char *lhs, *rhs;
 {
   register SHELL_VAR *v;
-  char *t;
-  int isint, isarr;
+  int isint, isarr, implicitarray;
 
-  isint = isarr = 0;
+  isint = isarr = implicitarray = 0;
 #if defined (ARRAY_VARS)
-#  if 0
-  if (t = xstrchr (lhs, '['))  /*]*/
-#  else
   if (valid_array_reference (lhs))
-#  endif
     {
       isarr = 1;
       v = array_variable_part (lhs, (char **)0, (int *)0);
@@ -1961,18 +2818,26 @@ bind_int_variable (lhs, rhs)
     {
       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);
+    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);
+    v = bind_variable (lhs, rhs, 0);
 
-  if (isint)
+  if (v && isint)
     VSETATTR (v, att_integer);
 
+  VUNSETATTR (v, att_invisible);
+
   return (v);
 }
 
@@ -2033,6 +2898,7 @@ bind_function (name, value)
   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
@@ -2061,19 +2927,25 @@ bind_function_def (name, value)
       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 (string)
-     const char *string;
+assign_in_env (word, flags)
+     WORD_DESC *word;
+     int flags;
 {
-  int offset;
+  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;
@@ -2082,6 +2954,13 @@ assign_in_env (string)
     {
       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)))
        {
@@ -2092,10 +2971,14 @@ assign_in_env (string)
        }
 
       temp = name + offset + 1;
-      temp = (xstrchr (temp, '~') != 0) ? bash_tilde_expand (temp, 1) : savestring (temp);
+      value = expand_assignment_string_to_string (temp, 0);
 
-      value = expand_string_unsplit_to_string (temp, 0);
-      free (temp);
+      if (var && (aflags & ASS_APPEND))
+       {
+         temp = make_variable_value (var, value, aflags);
+         FREE (value);
+         value = temp;
+       }
     }
 
   if (temporary_env == 0)
@@ -2118,12 +3001,12 @@ assign_in_env (string)
   var->context = variable_context;     /* XXX */
 
   INVALIDATE_EXPORTSTR (var);
-  var->exportstr = mk_env_string (name, value);
+  var->exportstr = mk_env_string (name, value, 0);
 
   array_needs_making = 1;
 
-  if (ifsname (name))
-    setifs (var);
+  if (flags)
+    stupidly_hack_special_variables (name);
 
   if (echo_command_at_execute)
     /* The Korn shell prints the `+ ' in front of assignment statements,
@@ -2159,9 +3042,13 @@ copy_variable (var)
        var_setfunc (copy, copy_command (function_cell (var)));
 #if defined (ARRAY_VARS)
       else if (array_p (var))
-       var_setarray (copy, dup_array (array_cell (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 (value_cell (var))
+      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);
@@ -2184,21 +3071,33 @@ copy_variable (var)
 /* **************************************************************** */
 
 /* Dispose of the information attached to VAR. */
-void
-dispose_variable (var)
+static void
+dispose_variable_value (var)
      SHELL_VAR *var;
 {
-  if (var == 0)
-    return;
-
   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);
 
@@ -2210,12 +3109,33 @@ dispose_variable (var)
   free (var);
 }
 
-/* Unset the shell variable referenced by NAME. */
+/* 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;
 {
-  return makunbound (name, shell_variables);
+  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. */
@@ -2249,6 +3169,7 @@ unbind_func (name)
   return 0;  
 }
 
+#if defined (DEBUGGER)
 int
 unbind_function_def (name)
      const char *name;
@@ -2270,6 +3191,31 @@ unbind_function_def (name)
 
   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
@@ -2305,13 +3251,24 @@ makunbound (name, vc)
      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. */ 
+        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);
-      FREE (value_cell (old_var));
       var_setvalue (old_var, (char *)NULL);
       INVALIDATE_EXPORTSTR (old_var);
 
@@ -2389,8 +3346,8 @@ delete_all_variables (hashed_vars)
       entry = find_variable (name); \
       if (!entry) \
        { \
-         entry = bind_variable (name, ""); \
-         if (!no_invisible_vars) entry->attributes |= att_invisible; \
+         entry = bind_variable (name, "", 0); \
+         if (!no_invisible_vars && entry) entry->attributes |= att_invisible; \
        } \
     } \
   while (0)
@@ -2674,6 +3631,16 @@ visible_and_exported (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
@@ -2810,6 +3777,9 @@ find_tempenv_variable (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
@@ -2831,7 +3801,7 @@ push_temp_var (data)
        binding_table = shell_variables->table = hash_create (TEMPENV_HASH_BUCKETS);
     }
 
-  v = bind_variable_internal (var->name, value_cell (var), binding_table, 0);
+  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. */
@@ -2845,6 +3815,9 @@ push_temp_var (data)
     }
   v->attributes |= var->attributes;
 
+  if (find_special_var (var->name) >= 0)
+    tempvar_list[tvlist_ind++] = savestring (var->name);
+
   dispose_variable (var);
 }
 
@@ -2858,31 +3831,56 @@ propagate_temp_var (data)
   if (tempvar_p (var) && (var->attributes & att_propagate))
     push_temp_var (data);
   else
-    dispose_variable (var);
+    {
+      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. */
+   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;
+  temporary_env = (HASH_TABLE *)NULL;
+
+  tempvar_list[tvlist_ind] = 0;
 
   array_needs_making = 1;
 
-  sv_ifs ("IFS");              /* XXX here for now */
+#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);
+    {
+      dispose_temporary_env (propagate_temp_var);
+      maybe_make_export_env ();
+    }
 }
 
 /* Take all of the shell variables in the temporary environment HASH_TABLE
@@ -2901,21 +3899,42 @@ merge_temporary_env ()
 /* **************************************************************** */
 
 static inline char *
-mk_env_string (name, value)
+mk_env_string (name, value, isfunc)
      const char *name, *value;
+     int isfunc;
 {
-  int name_len, value_len;
-  char *p;
+  size_t name_len, value_len;
+  char *p, *q;
 
   name_len = strlen (name);
   value_len = STRLEN (value);
-  p = (char *)xmalloc (2 + name_len + value_len);
-  strcpy (p, name);
-  p[name_len] = '=';
+
+  /* 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)
-    strcpy (p + name_len + 1, value);
+    memcpy (q + 1, value, value_len + 1);
   else
-    p[name_len + 1] = '\0';
+    q[1] = '\0';
+
   return (p);
 }
 
@@ -2928,6 +3947,11 @@ valid_exportstr (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);
@@ -2976,10 +4000,16 @@ make_env_array_from_var_list (vars)
        value = named_function_string ((char *)NULL, function_cell (var), 0);
 #if defined (ARRAY_VARS)
       else if (array_p (var))
-#  if 0
+#  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
@@ -2990,7 +4020,7 @@ make_env_array_from_var_list (vars)
          /* 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);
+                                          : mk_env_string (var->name, value, function_p (var));
 
          if (USE_EXPORTSTR == 0)
            SAVE_EXPORTSTR (var, list[list_index]);
@@ -3000,7 +4030,7 @@ make_env_array_from_var_list (vars)
 
 #if 0  /* not yet */
 #if defined (ARRAY_VARS)
-         if (array_p (var))
+         if (array_p (var) || assoc_p (var))
            free (value);
 #endif
 #endif
@@ -3021,7 +4051,11 @@ make_var_export_array (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;
@@ -3056,6 +4090,7 @@ do \
       { \
        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; \
@@ -3143,6 +4178,22 @@ n_shell_variables ()
   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 ()
 {
@@ -3153,7 +4204,7 @@ maybe_make_export_env ()
   if (array_needs_making)
     {
       if (export_env)
-        strvec_flush (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
@@ -3165,28 +4216,29 @@ maybe_make_export_env ()
        {
          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. */
+      /* 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;
-        }
+       {
+         tcxt = new_var_context ((char *)NULL, 0);
+         tcxt->table = temporary_env;
+         tcxt->down = shell_variables;
+       }
       else
-        tcxt = shell_variables;
+       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);
+       free (tcxt);
 
 #if defined (RESTRICTED_SHELL)
       /* Restricted shells may not export shell functions. */
@@ -3233,33 +4285,6 @@ put_command_name_into_env (command_name)
   update_export_env_inplace ("_=", 2, command_name);
 }
 
-#if 0  /* UNUSED -- it caused too many problems */
-void
-put_gnu_argv_flags_into_env (pid, flags_string)
-     intmax_t pid;
-     char *flags_string;
-{
-  char *dummy, *pbuf;
-  int l, fl;
-
-  pbuf = itos (pid);
-  l = strlen (pbuf);
-
-  fl = strlen (flags_string);
-
-  dummy = (char *)xmalloc (l + fl + 30);
-  dummy[0] = '_';
-  strcpy (dummy + 1, pbuf);
-  strcpy (dummy + 1 + l, "_GNU_nonoption_argv_flags_");
-  dummy[l + 27] = '=';
-  strcpy (dummy + l + 28, flags_string);
-
-  free (pbuf);
-
-  export_env = add_or_supercede_exported_var (dummy, 0);
-}
-#endif
-
 /* **************************************************************** */
 /*                                                                 */
 /*                   Managing variable contexts                    */
@@ -3348,14 +4373,21 @@ push_func_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);
+      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;
+       shell_variables->flags |= VC_HASTMPVAR;
       v->attributes |= var->attributes;
     }
+  else
+    stupidly_hack_special_variables (var->name);       /* XXX */
 
   dispose_variable (var);
 }
@@ -3398,7 +4430,7 @@ delete_all_contexts (vcxt)
     {
       t = v->down;
       dispose_var_context (v);
-    }            
+    }    
 
   delete_all_variables (global_variables->table);
   shell_variables = global_variables;
@@ -3428,14 +4460,22 @@ push_exported_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);
+      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);
 }
@@ -3519,7 +4559,7 @@ push_dollar_vars ()
     {
       dollar_arg_stack = (WORD_LIST **)
        xrealloc (dollar_arg_stack, (dollar_arg_stack_slots += 10)
-                 * sizeof (WORD_LIST **));
+                 * sizeof (WORD_LIST *));
     }
   dollar_arg_stack[dollar_arg_stack_index++] = list_rest_of_args ();
   dollar_arg_stack[dollar_arg_stack_index] = (WORD_LIST *)NULL;
@@ -3608,7 +4648,6 @@ pop_args ()
 extern int eof_encountered, eof_encountered_limit, ignoreeof;
 
 #if defined (READLINE)
-extern int no_line_editing;
 extern int hostname_list_initialized;
 #endif
 
@@ -3626,6 +4665,22 @@ struct name_and_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)
@@ -3636,6 +4691,10 @@ static struct name_and_function special_vars[] = {
   { "HISTTIMEFORMAT", sv_histtimefmt },
 #endif
 
+#if defined (__CYGWIN__)
+  { "HOME", sv_home },
+#endif
+
 #if defined (READLINE)
   { "HOSTFILE", sv_hostfile },
 #endif
@@ -3649,6 +4708,11 @@ static struct name_and_function special_vars[] = {
   { "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 },
@@ -3669,7 +4733,7 @@ static struct name_and_function special_vars[] = {
   { "TEXTDOMAIN", sv_locale },
   { "TEXTDOMAINDIR", sv_locale },
 
-#if defined (HAVE_TZSET) && defined (PROMPT_STRING_DECODE)
+#if defined (HAVE_TZSET)
   { "TZ", sv_tz },
 #endif
 
@@ -3711,7 +4775,7 @@ find_special_var (name)
       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;
+       break;
     }
   return -1;
 }
@@ -3737,6 +4801,18 @@ stupidly_hack_special_variables (name)
     (*(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;
@@ -3776,15 +4852,43 @@ sv_mail (name)
     }
 }
 
+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;
 {
-  setup_glob_ignore (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. */
@@ -3808,8 +4912,52 @@ sv_hostfile (name)
   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
@@ -3823,6 +4971,7 @@ sv_histsize (name)
 {
   char *temp;
   intmax_t num;
+  int hmax;
 
   temp = get_string_value (name);
 
@@ -3830,18 +4979,21 @@ sv_histsize (name)
     {
       if (legal_number (temp, &num))
        {
-         if (name[4] == 'S')
+         hmax = num;
+         if (hmax < 0 && name[4] == 'S')
+           unstifle_history ();        /* unstifle history if HISTSIZE < 0 */
+         else if (name[4] == 'S')
            {
-             stifle_history (num);
-             num = where_history ();
-             if (history_lines_this_session > num)
-               history_lines_this_session = num;
+             stifle_history (hmax);
+             hmax = where_history ();
+             if (history_lines_this_session > hmax)
+               history_lines_this_session = hmax;
            }
-         else
+         else if (hmax >= 0)   /* truncate HISTFILE if HISTFILESIZE >= 0 */
            {
-             history_truncate_file (get_string_value ("HISTFILE"), (int)num);
-             if (num <= history_lines_in_file)
-               history_lines_in_file = num;
+             history_truncate_file (get_string_value ("HISTFILE"), hmax);
+             if (hmax <= history_lines_in_file)
+               history_lines_in_file = hmax;
            }
        }
     }
@@ -3922,17 +5074,22 @@ sv_histtimefmt (name)
 {
   SHELL_VAR *v;
 
-  v = find_variable (name);
+  if (v = find_variable (name))
+    {
+      if (history_comment_char == 0)
+       history_comment_char = '#';
+    }
   history_write_timestamps = (v != 0);
 }
 #endif /* HISTORY */
 
-#if defined (HAVE_TZSET) && defined (PROMPT_STRING_DECODE)
+#if defined (HAVE_TZSET)
 void
 sv_tz (name)
      char *name;
 {
-  tzset ();
+  if (chkexport (name))
+    tzset ();
 }
 #endif
 
@@ -4006,12 +5163,18 @@ sv_locale (name)
      char *name;
 {
   char *v;
+  int r;
 
   v = get_string_value (name);
   if (name[0] == 'L' && name[1] == 'A')        /* LANG */
-    set_lang (name, v);
+    r = set_lang (name, v);
   else
-    set_locale_var (name, v);          /* LC_*, TEXTDOMAIN* */
+    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)
@@ -4078,6 +5241,40 @@ set_pipestatus_array (ps, nproc)
        }
     }
 }
+
+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
@@ -4091,3 +5288,106 @@ set_pipestatus_from_exit (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