]> git.ipfire.org Git - thirdparty/bash.git/blobdiff - variables.c
Bash-5.2 patch 26: fix typo when specifying readline's custom color prefix
[thirdparty/bash.git] / variables.c
index af3fd04abfd473debabb1e309a7d1a0737c7a48e..1a0c2c45c053fcce715ba2fa9095c41488994e70 100644 (file)
@@ -1,6 +1,6 @@
 /* variables.c -- Functions for hacking shell variables. */
 
-/* Copyright (C) 1987-2018 Free Software Foundation, Inc.
+/* Copyright (C) 1987-2022 Free Software Foundation, Inc.
 
    This file is part of GNU Bash, the Bourne Again SHell.
 
@@ -43,6 +43,7 @@
 #endif
 #include "bashansi.h"
 #include "bashintl.h"
+#include "filecntl.h"
 
 #define NEED_XTRACE_SET_DECL
 
 #define BASHFUNC_SUFFIX                "%%"
 #define BASHFUNC_SUFFLEN       2       /* == strlen(BASHFUNC_SUFFIX) */
 
+#if ARRAY_EXPORT
+#define BASHARRAY_PREFIX       "BASH_ARRAY_"
+#define BASHARRAY_PREFLEN      11
+#define BASHARRAY_SUFFIX       "%%"
+#define BASHARRAY_SUFFLEN      2
+
+#define BASHASSOC_PREFIX       "BASH_ASSOC_"
+#define BASHASSOC_PREFLEN      11
+#define BASHASSOC_SUFFIX       "%%"    /* needs to be the same as BASHARRAY_SUFFIX */
+#define BASHASSOC_SUFFLEN      2
+#endif
+
 /* flags for find_variable_internal */
 
 #define FV_FORCETEMPENV                0x01
 #define FV_SKIPINVISIBLE       0x02
+#define FV_NODYNAMIC           0x04
 
 extern char **environ;
 
 /* Variables used here and defined in other files. */
 extern time_t shell_start_time;
+extern struct timeval shellstart;
 
 /* The list of shell variables that the user has created at the global
    scope, or that came from the environment. */
@@ -143,6 +158,7 @@ int tempenv_assign_error;
    "$*", "$1", and all the cruft is kept. */
 char *dollar_vars[10];
 WORD_LIST *rest_of_args = (WORD_LIST *)NULL;
+int posparam_count = 0;
 
 /* The value of $$. */
 pid_t dollar_dollar_pid;
@@ -172,144 +188,154 @@ static HASH_TABLE *last_table_searched; /* hash_lookup sets this */
 static VAR_CONTEXT *last_context_searched;
 
 /* Some forward declarations. */
-static void create_variable_tables __P((void));
-
-static void set_machine_vars __P((void));
-static void set_home_var __P((void));
-static void set_shell_var __P((void));
-static char *get_bash_name __P((void));
-static void initialize_shell_level __P((void));
-static void uidset __P((void));
+static void create_variable_tables PARAMS((void));
+
+static void set_machine_vars PARAMS((void));
+static void set_home_var PARAMS((void));
+static void set_shell_var PARAMS((void));
+static char *get_bash_name PARAMS((void));
+static void initialize_shell_level PARAMS((void));
+static void uidset PARAMS((void));
 #if defined (ARRAY_VARS)
-static void make_vers_array __P((void));
+static void make_vers_array PARAMS((void));
 #endif
 
-static SHELL_VAR *null_assign __P((SHELL_VAR *, char *, arrayind_t, char *));
+static SHELL_VAR *null_assign PARAMS((SHELL_VAR *, char *, arrayind_t, char *));
 #if defined (ARRAY_VARS)
-static SHELL_VAR *null_array_assign __P((SHELL_VAR *, char *, arrayind_t, char *));
+static SHELL_VAR *null_array_assign PARAMS((SHELL_VAR *, char *, arrayind_t, char *));
 #endif
-static SHELL_VAR *get_self __P((SHELL_VAR *));
+static SHELL_VAR *get_self PARAMS((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));
+static SHELL_VAR *init_dynamic_array_var PARAMS((char *, sh_var_value_func_t *, sh_var_assign_func_t *, int));
+static SHELL_VAR *init_dynamic_assoc_var PARAMS((char *, sh_var_value_func_t *, sh_var_assign_func_t *, int));
 #endif
 
-static SHELL_VAR *assign_seconds __P((SHELL_VAR *, char *, arrayind_t, char *));
-static SHELL_VAR *get_seconds __P((SHELL_VAR *));
-static SHELL_VAR *init_seconds_var __P((void));
+static inline SHELL_VAR *set_int_value (SHELL_VAR *, intmax_t, int);
+static inline SHELL_VAR *set_string_value (SHELL_VAR *, const char *, int);
+
+static SHELL_VAR *assign_seconds PARAMS((SHELL_VAR *, char *, arrayind_t, char *));
+static SHELL_VAR *get_seconds PARAMS((SHELL_VAR *));
+static SHELL_VAR *init_seconds_var PARAMS((void));
+
+static SHELL_VAR *assign_random PARAMS((SHELL_VAR *, char *, arrayind_t, char *));
+static SHELL_VAR *get_random PARAMS((SHELL_VAR *));
+
+static SHELL_VAR *get_urandom PARAMS((SHELL_VAR *));
 
-static int brand __P((void));
-static void sbrand __P((unsigned long));               /* set bash random number generator. */
-static void seedrand __P((void));                      /* seed generator randomly */
-static SHELL_VAR *assign_random __P((SHELL_VAR *, char *, arrayind_t, char *));
-static SHELL_VAR *get_random __P((SHELL_VAR *));
+static SHELL_VAR *assign_lineno PARAMS((SHELL_VAR *, char *, arrayind_t, char *));
+static SHELL_VAR *get_lineno PARAMS((SHELL_VAR *));
 
-static SHELL_VAR *assign_lineno __P((SHELL_VAR *, char *, arrayind_t, char *));
-static SHELL_VAR *get_lineno __P((SHELL_VAR *));
+static SHELL_VAR *assign_subshell PARAMS((SHELL_VAR *, char *, arrayind_t, char *));
+static SHELL_VAR *get_subshell PARAMS((SHELL_VAR *));
 
-static SHELL_VAR *assign_subshell __P((SHELL_VAR *, char *, arrayind_t, char *));
-static SHELL_VAR *get_subshell __P((SHELL_VAR *));
+static SHELL_VAR *get_epochseconds PARAMS((SHELL_VAR *));
+static SHELL_VAR *get_epochrealtime PARAMS((SHELL_VAR *));
 
-static SHELL_VAR *get_epochseconds __P((SHELL_VAR *));
-static SHELL_VAR *get_epochrealtime __P((SHELL_VAR *));
+static SHELL_VAR *get_bashpid PARAMS((SHELL_VAR *));
 
-static SHELL_VAR *get_bashpid __P((SHELL_VAR *));
+static SHELL_VAR *get_bash_argv0 PARAMS((SHELL_VAR *));
+static SHELL_VAR *assign_bash_argv0 PARAMS((SHELL_VAR *, char *, arrayind_t, char *));
+static void set_argv0 PARAMS((void));
 
 #if defined (HISTORY)
-static SHELL_VAR *get_histcmd __P((SHELL_VAR *));
+static SHELL_VAR *get_histcmd PARAMS((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 *));
+static SHELL_VAR *get_comp_wordbreaks PARAMS((SHELL_VAR *));
+static SHELL_VAR *assign_comp_wordbreaks PARAMS((SHELL_VAR *, char *, arrayind_t, char *));
 #endif
 
 #if defined (PUSHD_AND_POPD) && defined (ARRAY_VARS)
-static SHELL_VAR *assign_dirstack __P((SHELL_VAR *, char *, arrayind_t, char *));
-static SHELL_VAR *get_dirstack __P((SHELL_VAR *));
+static SHELL_VAR *assign_dirstack PARAMS((SHELL_VAR *, char *, arrayind_t, char *));
+static SHELL_VAR *get_dirstack PARAMS((SHELL_VAR *));
 #endif
 
 #if defined (ARRAY_VARS)
-static SHELL_VAR *get_groupset __P((SHELL_VAR *));
+static SHELL_VAR *get_groupset PARAMS((SHELL_VAR *));
 #  if defined (DEBUGGER)
-static SHELL_VAR *get_bashargcv __P((SHELL_VAR *));
+static SHELL_VAR *get_bashargcv PARAMS((SHELL_VAR *));
 #  endif
-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 *));
+static SHELL_VAR *build_hashcmd PARAMS((SHELL_VAR *));
+static SHELL_VAR *get_hashcmd PARAMS((SHELL_VAR *));
+static SHELL_VAR *assign_hashcmd PARAMS((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 *));
+static SHELL_VAR *build_aliasvar PARAMS((SHELL_VAR *));
+static SHELL_VAR *get_aliasvar PARAMS((SHELL_VAR *));
+static SHELL_VAR *assign_aliasvar PARAMS((SHELL_VAR *,  char *, arrayind_t, char *));
 #  endif
 #endif
 
-static SHELL_VAR *get_funcname __P((SHELL_VAR *));
-static SHELL_VAR *init_funcname_var __P((void));
+static SHELL_VAR *get_funcname PARAMS((SHELL_VAR *));
+static SHELL_VAR *init_funcname_var PARAMS((void));
 
-static void initialize_dynamic_variables __P((void));
+static void initialize_dynamic_variables PARAMS((void));
 
-static SHELL_VAR *bind_invalid_envvar __P((const char *, char *, int));
+static SHELL_VAR *bind_invalid_envvar PARAMS((const char *, char *, int));
 
-static int var_sametype __P((SHELL_VAR *, SHELL_VAR *));
+static int var_sametype PARAMS((SHELL_VAR *, SHELL_VAR *));
 
-static SHELL_VAR *hash_lookup __P((const char *, HASH_TABLE *));
-static SHELL_VAR *new_shell_variable __P((const char *));
-static SHELL_VAR *make_new_variable __P((const char *, HASH_TABLE *));
-static SHELL_VAR *bind_variable_internal __P((const char *, char *, HASH_TABLE *, int, int));
+static SHELL_VAR *hash_lookup PARAMS((const char *, HASH_TABLE *));
+static SHELL_VAR *new_shell_variable PARAMS((const char *));
+static SHELL_VAR *make_new_variable PARAMS((const char *, HASH_TABLE *));
+static SHELL_VAR *bind_variable_internal PARAMS((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 void dispose_variable_value PARAMS((SHELL_VAR *));
+static void free_variable_hash_data PARAMS((PTR_T));
 
-static VARLIST *vlist_alloc __P((int));
-static VARLIST *vlist_realloc __P((VARLIST *, int));
-static void vlist_add __P((VARLIST *, SHELL_VAR *, int));
+static VARLIST *vlist_alloc PARAMS((int));
+static VARLIST *vlist_realloc PARAMS((VARLIST *, int));
+static void vlist_add PARAMS((VARLIST *, SHELL_VAR *, int));
 
-static void flatten __P((HASH_TABLE *, sh_var_map_func_t *, VARLIST *, int));
+static void flatten PARAMS((HASH_TABLE *, sh_var_map_func_t *, VARLIST *, int));
 
-static int qsort_var_comp __P((SHELL_VAR **, SHELL_VAR **));
+static int qsort_var_comp PARAMS((SHELL_VAR **, SHELL_VAR **));
 
-static SHELL_VAR **vapply __P((sh_var_map_func_t *));
-static SHELL_VAR **fapply __P((sh_var_map_func_t *));
+static SHELL_VAR **vapply PARAMS((sh_var_map_func_t *));
+static SHELL_VAR **fapply PARAMS((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 *));
+static int visible_var PARAMS((SHELL_VAR *));
+static int visible_and_exported PARAMS((SHELL_VAR *));
+static int export_environment_candidate PARAMS((SHELL_VAR *));
+static int local_and_exported PARAMS((SHELL_VAR *));
+static int visible_variable_in_context PARAMS((SHELL_VAR *));
+static int variable_in_context PARAMS((SHELL_VAR *));
 #if defined (ARRAY_VARS)
-static int visible_array_vars __P((SHELL_VAR *));
+static int visible_array_vars PARAMS((SHELL_VAR *));
 #endif
 
-static SHELL_VAR *find_variable_internal __P((const char *, int));
+static SHELL_VAR *find_variable_internal PARAMS((const char *, int));
 
-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 *find_nameref_at_context PARAMS((SHELL_VAR *, VAR_CONTEXT *));
+static SHELL_VAR *find_variable_nameref_context PARAMS((SHELL_VAR *, VAR_CONTEXT *, VAR_CONTEXT **));
+static SHELL_VAR *find_variable_last_nameref_context PARAMS((SHELL_VAR *, VAR_CONTEXT *, VAR_CONTEXT **));
 
-static SHELL_VAR *bind_tempenv_variable __P((const char *, char *));
-static void push_posix_temp_var __P((PTR_T));
-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 SHELL_VAR *bind_tempenv_variable PARAMS((const char *, char *));
+static void push_posix_temp_var PARAMS((PTR_T));
+static void push_temp_var PARAMS((PTR_T));
+static void propagate_temp_var PARAMS((PTR_T));
+static void dispose_temporary_env PARAMS((sh_free_func_t *));     
 
-static inline char *mk_env_string __P((const char *, const char *, int));
-static char **make_env_array_from_var_list __P((SHELL_VAR **));
-static char **make_var_export_array __P((VAR_CONTEXT *));
-static char **make_func_export_array __P((void));
-static void add_temp_array_to_env __P((char **, int, int));
+static inline char *mk_env_string PARAMS((const char *, const char *, int));
+static char **make_env_array_from_var_list PARAMS((SHELL_VAR **));
+static char **make_var_export_array PARAMS((VAR_CONTEXT *));
+static char **make_func_export_array PARAMS((void));
+static void add_temp_array_to_env PARAMS((char **, int, int));
 
-static int n_shell_variables __P((void));
-static int set_context __P((SHELL_VAR *));
+static int n_shell_variables PARAMS((void));
+static int set_context PARAMS((SHELL_VAR *));
 
-static void push_func_var __P((PTR_T));
-static void push_builtin_var __P((PTR_T));
-static void push_exported_var __P((PTR_T));
+static void push_func_var PARAMS((PTR_T));
+static void push_builtin_var PARAMS((PTR_T));
+static void push_exported_var PARAMS((PTR_T));
 
-static inline void push_posix_tempvar_internal __P((SHELL_VAR *, int));
+static void delete_local_contexts PARAMS((VAR_CONTEXT *));
 
-static inline int find_special_var __P((const char *));
+/* This needs to be looked at again. */
+static inline void push_posix_tempvar_internal PARAMS((SHELL_VAR *, int));
+
+static inline int find_special_var PARAMS((const char *));
 
 static void
 create_variable_tables ()
@@ -408,7 +434,7 @@ initialize_shell_variables (env, privmode)
                  VSETATTR (temp_var, (att_exported | att_imported | att_invisible));
                  array_needs_making = 1;
                }
-             last_command_exit_value = 1;
+             last_command_exit_value = EXECUTION_FAILURE;
              report_error (_("error importing function definition for `%s'"), tname);
            }
 
@@ -420,14 +446,54 @@ initialize_shell_variables (env, privmode)
 #if defined (ARRAY_VARS)
 #  if ARRAY_EXPORT
       /* Array variables may not yet be exported. */
-      if (*string == '(' && string[1] == '[' && string[strlen (string) - 1] == ')')
+      if (STREQN (BASHARRAY_PREFIX, name, BASHARRAY_PREFLEN) &&
+         STREQN (BASHARRAY_SUFFIX, name + char_index - BASHARRAY_SUFFLEN, BASHARRAY_SUFFLEN) &&
+         *string == '(' && string[1] == '[' && string[strlen (string) - 1] == ')')
        {
+         size_t namelen;
+         char *tname;          /* desired imported array variable name */
+
+         namelen = char_index - BASHARRAY_PREFLEN - BASHARRAY_SUFFLEN;
+
+         tname = name + BASHARRAY_PREFLEN;     /* start of variable name */
+         tname[namelen] = '\0';                /* now tname == varname */
+         
          string_length = 1;
          temp_string = extract_array_assignment_list (string, &string_length);
-         temp_var = assign_array_from_string (name, temp_string, 0);
+         temp_var = assign_array_from_string (tname, temp_string, 0);
+         FREE (temp_string);
+         if (temp_var)
+           {
+             VSETATTR (temp_var, (att_exported | att_imported));
+             array_needs_making = 1;
+           }
+       }
+      else if (STREQN (BASHASSOC_PREFIX, name, BASHASSOC_PREFLEN) &&
+         STREQN (BASHASSOC_SUFFIX, name + char_index - BASHASSOC_SUFFLEN, BASHASSOC_SUFFLEN) &&
+         *string == '(' && string[1] == '[' && string[strlen (string) - 1] == ')')
+       {
+         size_t namelen;
+         char *tname;          /* desired imported assoc variable name */
+
+         namelen = char_index - BASHASSOC_PREFLEN - BASHASSOC_SUFFLEN;
+
+         tname = name + BASHASSOC_PREFLEN;     /* start of variable name */
+         tname[namelen] = '\0';                /* now tname == varname */
+
+         /* need to make sure it exists as an associative array first */
+         temp_var = find_or_make_array_variable (tname, 2);
+         if (temp_var)
+           {
+             string_length = 1;
+             temp_string = extract_array_assignment_list (string, &string_length);
+             temp_var = assign_array_var_from_string (temp_var, temp_string, 0);
+           }
          FREE (temp_string);
-         VSETATTR (temp_var, (att_exported | att_imported));
-         array_needs_making = 1;
+         if (temp_var)
+           {
+             VSETATTR (temp_var, (att_exported | att_imported));
+             array_needs_making = 1;
+           }
        }
       else
 #  endif /* ARRAY_EXPORT */
@@ -541,6 +607,8 @@ initialize_shell_variables (env, privmode)
 
   set_ppid ();
 
+  set_argv0 ();
+
   /* Initialize the `getopts' stuff. */
   temp_var = bind_variable ("OPTIND", "1", 0);
   VSETATTR (temp_var, att_integer);
@@ -593,8 +661,9 @@ initialize_shell_variables (env, privmode)
     }
 #endif /* HISTORY */
 
-  /* Seed the random number generator. */
+  /* Seed the random number generators. */
   seedrand ();
+  seedrand32 ();
 
   /* Handle some "special" variables that we may have inherited from a
      parent shell. */
@@ -624,34 +693,11 @@ initialize_shell_variables (env, privmode)
     rl_prefer_env_winsize = 1;
 #endif /* READLINE && STRICT_POSIX */
 
-     /*
-      * 24 October 2001
-      *
-      * I'm tired of the arguing and bug reports.  Bash now leaves SSH_CLIENT
-      * and SSH2_CLIENT alone.  I'm going to rely on the shell_level check in
-      * isnetconn() to avoid running the startup files more often than wanted.
-      * That will, of course, only work if the user's login shell is bash, so
-      * I've made that behavior conditional on SSH_SOURCE_BASHRC being defined
-      * in config-top.h.
-      */
-#if 0
-  temp_var = find_variable ("SSH_CLIENT");
-  if (temp_var && imported_p (temp_var))
-    {
-      VUNSETATTR (temp_var, att_exported);
-      array_needs_making = 1;
-    }
-  temp_var = find_variable ("SSH2_CLIENT");
-  if (temp_var && imported_p (temp_var))
-    {
-      VUNSETATTR (temp_var, att_exported);
-      array_needs_making = 1;
-    }
-#endif
-
   /* Get the user's real and effective user ids. */
   uidset ();
 
+  temp_var = set_if_not ("BASH_LOADABLES_PATH", DEFAULT_LOADABLE_BUILTINS_PATH);
+
   temp_var = find_variable ("BASH_XTRACEFD");
   if (temp_var && imported_p (temp_var))
     sv_xtracefd (temp_var->name);
@@ -1147,7 +1193,7 @@ print_var_function (var)
    unused otherwise.
 
    dynamic_value is called from find_variable_internal to return a `new'
-   value for the specified dynamic varible.  If this function is NULL,
+   value for the specified dynamic variable.  If this function is NULL,
    the variable is treated as a `normal' shell variable.  If it is not,
    however, then this function is called like this:
        tempvar = (*(var->dynamic_value)) (var);
@@ -1261,6 +1307,39 @@ init_dynamic_assoc_var (name, getfunc, setfunc, attrs)
 }
 #endif
 
+/* Set the string value of VAR to the string representation of VALUE.
+   Right now this takes an INTMAX_T because that's what itos needs. If
+   FLAGS&1, we force the integer attribute on. */
+static inline SHELL_VAR *
+set_int_value (SHELL_VAR *var, intmax_t value, int flags)
+{
+  char *p;
+
+  p = itos (value);
+  FREE (value_cell (var));
+  var_setvalue (var, p);
+  if (flags & 1)
+    VSETATTR (var, att_integer);
+  return (var);
+}
+
+static inline SHELL_VAR *
+set_string_value (SHELL_VAR *var, const char *value, int flags)
+{
+  char *p;
+
+  if (value && *value)
+    p = savestring (value);
+  else
+    {
+      p = (char *)xmalloc (1);
+      p[0] = '\0';
+    }
+  FREE (value_cell (var));
+  var_setvalue (var, p);
+  return (var);
+}
+
 /* The value of $SECONDS.  This is the number of seconds since shell
    invocation, or, the number of seconds since the last assignment + the
    value of the last assignment. */
@@ -1273,10 +1352,17 @@ assign_seconds (self, value, unused, key)
      arrayind_t unused;
      char *key;
 {
-  if (legal_number (value, &seconds_value_assigned) == 0)
-    seconds_value_assigned = 0;
-  shell_start_time = NOW;
-  return (self);
+  intmax_t nval;
+  int expok;
+
+  if (integer_p (self))
+    nval = evalexp (value, 0, &expok);
+  else
+    expok = legal_number (value, &nval);
+  seconds_value_assigned = expok ? nval : 0;
+  gettimeofday (&shellstart, NULL);
+  shell_start_time = shellstart.tv_sec;
+  return (set_int_value (self, nval, integer_p (self) != 0));
 }
 
 static SHELL_VAR *
@@ -1284,16 +1370,11 @@ get_seconds (var)
      SHELL_VAR *var;
 {
   time_t time_since_start;
-  char *p;
-
-  time_since_start = NOW - shell_start_time;
-  p = itos(seconds_value_assigned + time_since_start);
-
-  FREE (value_cell (var));
+  struct timeval tv;
 
-  VSETATTR (var, att_integer);
-  var_setvalue (var, p);
-  return (var);
+  gettimeofday(&tv, NULL);
+  time_since_start = tv.tv_sec - shell_start_time;
+  return (set_int_value (var, seconds_value_assigned + time_since_start, 1));
 }
 
 static SHELL_VAR *
@@ -1310,75 +1391,11 @@ init_seconds_var ()
   INIT_DYNAMIC_VAR ("SECONDS", (v ? value_cell (v) : (char *)NULL), get_seconds, assign_seconds);
   return v;      
 }
-     
-/* The random number seed.  You can change this by setting RANDOM. */
-static u_bits32_t rseed = 1;
-static int last_random_value;
-static int seeded_subshell = 0;
-
-#define BASH_RANDOM_16 1
-
-#if BASH_RANDOM_16
-#  define BASH_RAND_MAX        32767           /* 0x7fff - 16 bits */
-#else
-#  define BASH_RAND_MAX        0x7fffffff      /* 32 bits */
-#endif
-
-/* Returns a pseudo-random number between 0 and 32767. */
-static int
-brand ()
-{
-  /* Minimal Standard generator 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.
-
-     x(n+1) = 16807 * x(n) mod (2**31 - 1).
-
-     We split up the calculations to avoid overflow.
-
-     h = rseed / q; l = x - h * q; t = a * l - h * r
-     m = 2147483647, a = 16807, q = 127773, r = 2836
-
-     There are lots of other combinations of constants to use; look at
-     https://www.gnu.org/software/gsl/manual/html_node/Other-random-number-generators.html#Other-random-number-generators */
-
-  bits32_t h, l, t;
-
-  /* Can't seed with 0. */
-  if (rseed == 0)
-    rseed = 123459876;
-  h = rseed / 127773;
-  l = rseed - (127773 * h);
-  t = 16807 * l - 2836 * h;
-  rseed = (t < 0) ? t + 0x7fffffff : t;
-
-  return ((unsigned int)(rseed & BASH_RAND_MAX));      /* was % BASH_RAND_MAX+1 */
-}
 
-/* Set the random number generator seed to SEED. */
-static void
-sbrand (seed)
-     unsigned long seed;
-{
-  rseed = seed;
-  last_random_value = 0;
-}
-
-static void
-seedrand ()
-{
-  struct timeval tv;
-  SHELL_VAR *v;
+/* Functions for $RANDOM and $SRANDOM */
 
-  gettimeofday (&tv, NULL);
-#if 0
-  v = find_variable ("BASH_VERSION");
-  sbrand (tv.tv_sec ^ tv.tv_usec ^ getpid () ^ ((u_bits32_t)&v & 0x7fffffff));
-#else
-  sbrand (tv.tv_sec ^ tv.tv_usec ^ getpid ());
-#endif
-}
+int last_random_value;
+static int seeded_subshell = 0;
 
 static SHELL_VAR *
 assign_random (self, value, unused, key)
@@ -1387,10 +1404,19 @@ assign_random (self, value, unused, key)
      arrayind_t unused;
      char *key;
 {
-  sbrand (strtoul (value, (char **)NULL, 10));
+  intmax_t seedval;
+  int expok;
+
+  if (integer_p (self))
+    seedval = evalexp (value, 0, &expok);
+  else
+    expok = legal_number (value, &seedval);
+  if (expok == 0)
+    return (self);
+  sbrand (seedval);
   if (subshell_environment)
     seeded_subshell = getpid ();
-  return (self);
+  return (set_int_value (self, seedval, integer_p (self) != 0));
 }
 
 int
@@ -1409,7 +1435,8 @@ get_random_number ()
   do
     rv = brand ();
   while (rv == last_random_value);
-  return rv;
+
+  return (last_random_value = rv);
 }
 
 static SHELL_VAR *
@@ -1417,17 +1444,19 @@ get_random (var)
      SHELL_VAR *var;
 {
   int rv;
-  char *p;
 
   rv = get_random_number ();
-  last_random_value = rv;
-  p = itos (rv);
+  return (set_int_value (var, rv, 1));
+}
 
-  FREE (value_cell (var));
+static SHELL_VAR *
+get_urandom (var)
+     SHELL_VAR *var;
+{
+  u_bits32_t rv;
 
-  VSETATTR (var, att_integer);
-  var_setvalue (var, p);
-  return (var);
+  rv = get_urandom32 ();
+  return (set_int_value (var, rv, 1));
 }
 
 static SHELL_VAR *
@@ -1442,7 +1471,7 @@ assign_lineno (var, value, unused, key)
   if (value == 0 || *value == '\0' || legal_number (value, &new_value) == 0)
     new_value = 0;
   line_number = line_number_base = new_value;
-  return var;
+  return (set_int_value (var, line_number, integer_p (var) != 0));
 }
 
 /* Function which returns the current line number. */
@@ -1450,14 +1479,10 @@ static SHELL_VAR *
 get_lineno (var)
      SHELL_VAR *var;
 {
-  char *p;
   int ln;
 
   ln = executing_line_number ();
-  p = itos (ln);
-  FREE (value_cell (var));
-  var_setvalue (var, p);
-  return (var);
+  return (set_int_value (var, ln, 0));
 }
 
 static SHELL_VAR *
@@ -1479,12 +1504,7 @@ static SHELL_VAR *
 get_subshell (var)
      SHELL_VAR *var;
 {
-  char *p;
-
-  p = itos (subshell_level);
-  FREE (value_cell (var));
-  var_setvalue (var, p);
-  return (var);
+  return (set_int_value (var, subshell_level, 0));
 }
 
 static SHELL_VAR *
@@ -1492,14 +1512,9 @@ get_epochseconds (var)
      SHELL_VAR *var;
 {
   intmax_t now;
-  char *p;
 
   now = NOW;
-  p = itos (now);
-
-  FREE (value_cell (var));
-  var_setvalue (var, p);
-  return (var);
+  return (set_int_value (var, now, 0));
 }
 
 static SHELL_VAR *
@@ -1507,7 +1522,6 @@ get_epochrealtime (var)
      SHELL_VAR *var;
 {
   char buf[32];
-  char *p;
   struct timeval tv;
 
   gettimeofday (&tv, NULL);
@@ -1515,10 +1529,7 @@ get_epochrealtime (var)
                                           locale_decpoint (),
                                           (unsigned)tv.tv_usec);
 
-  p = savestring (buf);
-  FREE (value_cell (var));
-  var_setvalue (var, p);
-  return (var);
+  return (set_string_value (var, buf, 0));
 }
 
 static SHELL_VAR *
@@ -1526,27 +1537,16 @@ get_bashpid (var)
      SHELL_VAR *var;
 {
   int pid;
-  char *p;
 
   pid = getpid ();
-  p = itos (pid);
-
-  FREE (value_cell (var));
-  VSETATTR (var, att_integer); /* XXX - was also att_readonly */
-  var_setvalue (var, p);
-  return (var);
+  return (set_int_value (var, pid, 1));
 }
 
 static SHELL_VAR *
 get_bash_argv0 (var)
      SHELL_VAR *var;
 {
-  char *p;
-
-  p = savestring (dollar_vars[0]);
-  FREE (value_cell (var));
-  var_setvalue (var, p);
-  return var;
+  return (set_string_value (var, dollar_vars[0], 0));
 }
 
 static char *static_shell_name = 0;
@@ -1574,6 +1574,16 @@ assign_bash_argv0 (var, value, unused, key)
   shell_name = static_shell_name;
   return var;
 }
+
+static void
+set_argv0 ()
+{
+  SHELL_VAR *v;
+
+  v = find_variable ("BASH_ARGV0");
+  if (v && imported_p (v))
+    assign_bash_argv0 (v, value_cell (v), 0, 0);
+}
   
 static SHELL_VAR *
 get_bash_command (var)
@@ -1581,16 +1591,8 @@ get_bash_command (var)
 {
   char *p;
 
-  if (the_printed_command_except_trap)
-    p = savestring (the_printed_command_except_trap);
-  else
-    {
-      p = (char *)xmalloc (1);
-      p[0] = '\0';
-    }
-  FREE (value_cell (var));
-  var_setvalue (var, p);
-  return (var);
+  p = the_printed_command_except_trap ? the_printed_command_except_trap : "";
+  return (set_string_value (var, p, 0));
 }
 
 #if defined (HISTORY)
@@ -1598,12 +1600,17 @@ static SHELL_VAR *
 get_histcmd (var)
      SHELL_VAR *var;
 {
-  char *p;
+  int n;
 
-  p = itos (history_number ());
-  FREE (value_cell (var));
-  var_setvalue (var, p);
-  return (var);
+  /* Do the same adjustment here we do in parse.y:prompt_history_number,
+     assuming that we are in one of two states: decoding this as part of
+     the prompt string, in which case we do not want to assume that the
+     command has been saved to the history and the history number incremented,
+     or the expansion is part of the current command being executed and has
+     already been saved to history and the history number incremented.
+     Right now we use EXECUTING as the determinant. */
+  n = history_number () - executing;
+  return (set_int_value (var, n, 0));
 }
 #endif
 
@@ -1617,10 +1624,7 @@ get_comp_wordbreaks (var)
   if (rl_completer_word_break_characters == 0 && bash_readline_initialized == 0)
     enable_hostname_completion (perform_hostname_completion);
 
-  FREE (value_cell (var));
-  var_setvalue (var, savestring (rl_completer_word_break_characters));
-
-  return (var);
+  return (set_string_value (var, rl_completer_word_break_characters, 0));
 }
 
 /* When this function returns, rl_completer_word_break_characters points to
@@ -1634,7 +1638,7 @@ assign_comp_wordbreaks (self, value, unused, key)
 {
   if (rl_completer_word_break_characters &&
       rl_completer_word_break_characters != rl_basic_word_break_characters)
-    free (rl_completer_word_break_characters);
+    free ((void *)rl_completer_word_break_characters);
 
   rl_completer_word_break_characters = savestring (value);
   return self;
@@ -1856,13 +1860,8 @@ get_funcname (self)
      SHELL_VAR *self;
 {
 #if ! defined (ARRAY_VARS)
-  char *t;
   if (variable_context && this_shell_function)
-    {
-      FREE (value_cell (self));
-      t = savestring (this_shell_function->name);
-      var_setvalue (self, t);
-    }
+    return (set_string_value (self, this_shell_function->name, 0));
 #endif
   return (self);
 }
@@ -1914,8 +1913,10 @@ initialize_dynamic_variables ()
 
   INIT_DYNAMIC_VAR ("RANDOM", (char *)NULL, get_random, assign_random);
   VSETATTR (v, att_integer);
+  INIT_DYNAMIC_VAR ("SRANDOM", (char *)NULL, get_urandom, (sh_var_assign_func_t *)NULL);
+  VSETATTR (v, att_integer);  
   INIT_DYNAMIC_VAR ("LINENO", (char *)NULL, get_lineno, assign_lineno);
-  VSETATTR (v, att_integer|att_regenerate);
+  VSETATTR (v, att_regenerate);
 
   INIT_DYNAMIC_VAR ("BASHPID", (char *)NULL, get_bashpid, null_assign);
   VSETATTR (v, att_integer);
@@ -1963,6 +1964,22 @@ initialize_dynamic_variables ()
 /*                                                                 */
 /* **************************************************************** */
 
+#if 0  /* not yet */
+int
+var_isset (var)
+     SHELL_VAR *var;
+{
+  return (var->value != 0);
+}
+
+int
+var_isunset (var)
+     SHELL_VAR *var;
+{
+  return (var->value == 0);
+}
+#endif
+
 /* How to get a pointer to the shell variable or function named NAME.
    HASHED_VARS is a pointer to the hash table containing the list
    of interest (either variables or functions). */
@@ -2309,7 +2326,7 @@ nameref_transform_name (name, flags)
     {
       v = find_variable_last_nameref (name, 1);
       /* If we're making local variables, only follow namerefs that point to
-        non-existant variables at the same variable context. */
+        non-existent variables at the same variable context. */
       if (v && v->context != variable_context)
        v = 0;
     }
@@ -2355,7 +2372,7 @@ find_global_variable (name)
 
   var = var_lookup (name, global_variables);
   if (var && nameref_p (var))
-    var = find_variable_nameref (var);
+    var = find_variable_nameref (var); /* XXX - find_global_variable_noref? */
 
   if (var == 0)
     return ((SHELL_VAR *)NULL);
@@ -2507,7 +2524,7 @@ get_variable_value (var)
 /* Return the string value of a variable.  Return NULL if the variable
    doesn't exist.  Don't cons a new string.  This is a potential memory
    leak if the variable is found in the temporary environment, but doesn't
-   leak in practice.  Since    functions and variables have separate name
+   leak in practice.  Since functions and variables have separate name
    spaces, returns NULL if var_name is a shell function only. */
 char *
 get_string_value (var_name)
@@ -2628,7 +2645,6 @@ make_local_variable (name, flags)
   if (was_tmpvar && old_var->context == variable_context && last_table_searched != temporary_env)
     {
       VUNSETATTR (old_var, att_invisible);     /* XXX */
-#if 0  /* TAG:bash-5.1 */
       /* We still want to flag this variable as local, though, and set things
          up so that it gets treated as a local variable. */
       new_var = old_var;
@@ -2638,7 +2654,7 @@ make_local_variable (name, flags)
        if (vc_isfuncenv (vc) && vc->scope == variable_context)
          break;
       goto set_local_var_flags;
-#endif
+
       return (old_var);
     }
 
@@ -2737,7 +2753,7 @@ set_local_var_flags:
 
   /* value_cell will be 0 if localvar_inherit == 0 or there was no old variable
      with the same name or the old variable was invisible */
-  if (was_tmpvar == 0 && no_invisible_vars == 0 && value_cell (new_var) == 0)
+  if (was_tmpvar == 0 && value_cell (new_var) == 0)
     VSETATTR (new_var, att_invisible); /* XXX */
   return (new_var);
 }
@@ -2807,14 +2823,17 @@ make_new_array_variable (name)
 }
 
 SHELL_VAR *
-make_local_array_variable (name, assoc_ok)
+make_local_array_variable (name, flags)
      char *name;
-     int assoc_ok;
+     int flags;
 {
   SHELL_VAR *var;
   ARRAY *array;
+  int assoc_ok;
 
-  var = make_local_variable (name, 0); /* XXX for now */
+  assoc_ok = flags & MKLOC_ASSOCOK;
+
+  var = make_local_variable (name, flags & MKLOC_INHERIT);     /* XXX for now */
   /* If ASSOC_OK is non-zero, assume that we are ok with letting an assoc
      variable return to the caller without converting it. The caller will
      either flag an error or do the conversion itself. */
@@ -2822,10 +2841,10 @@ make_local_array_variable (name, assoc_ok)
     return var;
 
   /* Validate any value we inherited from a variable instance at a previous
-     scope and disard anything that's invalid. */
+     scope and discard anything that's invalid. */
   if (localvar_inherit && assoc_p (var))
     {
-      internal_warning ("%s: cannot inherit value from incompatible type", name);
+      internal_warning (_("%s: cannot inherit value from incompatible type"), name);
       VUNSETATTR (var, att_assoc);
       dispose_variable_value (var);
       array = array_create ();
@@ -2852,7 +2871,7 @@ make_new_assoc_variable (name)
   HASH_TABLE *hash;
 
   entry = make_new_variable (name, global_variables->table);
-  hash = assoc_create (0);
+  hash = assoc_create (ASSOC_HASH_BUCKETS);
 
   var_setassoc (entry, hash);
   VSETATTR (entry, att_assoc);
@@ -2860,14 +2879,17 @@ make_new_assoc_variable (name)
 }
 
 SHELL_VAR *
-make_local_assoc_variable (name, array_ok)
+make_local_assoc_variable (name, flags)
      char *name;
-     int array_ok;
+     int flags;
 {
   SHELL_VAR *var;
   HASH_TABLE *hash;
+  int array_ok;
+
+  array_ok = flags & MKLOC_ARRAYOK;
 
-  var = make_local_variable (name, 0); /* XXX for now */
+  var = make_local_variable (name, flags & MKLOC_INHERIT);     /* XXX for now */
   /* If ARRAY_OK is non-zero, assume that we are ok with letting an array
      variable return to the caller without converting it. The caller will
      either flag an error or do the conversion itself. */
@@ -2875,13 +2897,13 @@ make_local_assoc_variable (name, array_ok)
     return var;
 
   /* Validate any value we inherited from a variable instance at a previous
-     scope and disard anything that's invalid. */
+     scope and discard anything that's invalid. */
   if (localvar_inherit && array_p (var))
     {
-      internal_warning ("%s: cannot inherit value from incompatible type", name);
+      internal_warning (_("%s: cannot inherit value from incompatible type"), name);
       VUNSETATTR (var, att_array);
       dispose_variable_value (var);
-      hash = assoc_create (0);
+      hash = assoc_create (ASSOC_HASH_BUCKETS);
       var_setassoc (var, hash);
     }
   else if (localvar_inherit)
@@ -2889,7 +2911,7 @@ make_local_assoc_variable (name, array_ok)
   else
     {
       dispose_variable_value (var);
-      hash = assoc_create (0);
+      hash = assoc_create (ASSOC_HASH_BUCKETS);
       var_setassoc (var, hash);
     }
 
@@ -3111,8 +3133,14 @@ bind_variable_internal (name, value, table, hflags, aflags)
              VUNSETATTR (tentry, att_nameref);
            }
          free (tname);
-          /* XXX - should it be aflags? */
-         entry = assign_array_element (newval, make_variable_value (entry, value, aflags), aflags|ASS_NAMEREF);
+
+         /* entry == nameref variable; tentry == array variable;
+            newval == x[2]; value = bar
+            We don't need to call make_variable_value here, since
+            assign_array_element will eventually do it itself based on
+            newval and aflags. */
+
+         entry = assign_array_element (newval, value, aflags|ASS_NAMEREF, (array_eltstate_t *)0);
          if (entry == 0)
            return entry;
        }
@@ -3130,6 +3158,13 @@ bind_variable_internal (name, value, table, hflags, aflags)
     }
   else if (entry->assign_func) /* array vars have assign functions now */
     {
+      if ((readonly_p (entry) && (aflags & ASS_FORCE) == 0) || noassign_p (entry))
+       {
+         if (readonly_p (entry))
+           err_readonly (name_cell (entry));
+         return (entry);
+       }
+
       INVALIDATE_EXPORTSTR (entry);
       newval = (aflags & ASS_APPEND) ? make_variable_value (entry, value, aflags) : value;
       if (assoc_p (entry))
@@ -3262,7 +3297,7 @@ bind_variable (name, value, flags)
                        return (bind_variable_internal (nv->name, value, nvc->table, 0, flags));
 #if defined (ARRAY_VARS)
                      else if (valid_array_reference (nameref_cell (nv), 0))
-                       return (assign_array_element (nameref_cell (nv), value, flags));
+                       return (assign_array_element (nameref_cell (nv), value, flags, (array_eltstate_t *)0));
                      else
 #endif
                      return (bind_variable_internal (nameref_cell (nv), value, nvc->table, 0, flags));
@@ -3270,12 +3305,7 @@ bind_variable (name, value, flags)
                  else if (nv == &nameref_maxloop_value)
                    {
                      internal_warning (_("%s: circular name reference"), v->name);
-#if 1
-                     /* TAG:bash-5.1 */
                      return (bind_global_variable (v->name, value, flags));
-#else
-                     v = 0;    /* backwards compat */
-#endif
                    }
                  else
                    v = nv;
@@ -3283,12 +3313,7 @@ bind_variable (name, value, flags)
              else if (nv == &nameref_maxloop_value)
                {
                  internal_warning (_("%s: circular name reference"), v->name);
-#if 1
-                 /* TAG:bash-5.1 */
                  return (bind_global_variable (v->name, value, flags));
-#else
-                 v = 0;        /* backwards compat */
-#endif
                }
              else
                v = nv;
@@ -3405,14 +3430,24 @@ bind_int_variable (lhs, rhs, flags)
      int flags;
 {
   register SHELL_VAR *v;
-  int isint, isarr, implicitarray;
+  int isint, isarr, implicitarray, vflags, avflags;
 
   isint = isarr = implicitarray = 0;
 #if defined (ARRAY_VARS)
-  if (valid_array_reference (lhs, (flags & ASS_NOEXPAND) != 0))
+  /* Don't rely on VA_NOEXPAND being 1, set it explicitly */
+  vflags = (flags & ASS_NOEXPAND) ? VA_NOEXPAND : 0;
+  if (flags & ASS_ONEWORD)
+    vflags |= VA_ONEWORD;
+  if (valid_array_reference (lhs, vflags))
     {
       isarr = 1;
-      v = array_variable_part (lhs, (flags & ASS_NOEXPAND) != 0, (char **)0, (int *)0);
+      avflags = 0;
+      /* Common code to translate between assignment and reference flags. */
+      if (flags & ASS_NOEXPAND)
+       avflags |= AV_NOEXPAND;
+      if (flags & ASS_ONEWORD)
+       avflags |= AV_ONEWORD;
+      v = array_variable_part (lhs, avflags, (char **)0, (int *)0);
     }
   else if (legal_identifier (lhs) == 0)
     {
@@ -3435,7 +3470,7 @@ bind_int_variable (lhs, rhs, flags)
 
 #if defined (ARRAY_VARS)
   if (isarr)
-    v = assign_array_element (lhs, rhs, flags);
+    v = assign_array_element (lhs, rhs, flags, (array_eltstate_t *)0);
   else if (implicitarray)
     v = bind_array_variable (lhs, 0, rhs, 0);  /* XXX - check on flags */
   else
@@ -3456,14 +3491,15 @@ bind_int_variable (lhs, rhs, flags)
 }
 
 SHELL_VAR *
-bind_var_to_int (var, val)
+bind_var_to_int (var, val, flags)
      char *var;
      intmax_t val;
+     int flags;
 {
   char ibuf[INT_STRLEN_BOUND (intmax_t) + 1], *p;
 
   p = fmtulong (val, 10, ibuf, sizeof (ibuf), 0);
-  return (bind_int_variable (var, p, 0));
+  return (bind_int_variable (var, p, flags));
 }
 
 /* Do a function binding to a variable.  You pass the name and
@@ -3583,6 +3619,7 @@ assign_in_env (word, flags)
       if (legal_identifier (name) == 0)
        {
          sh_invalidid (name);
+         free (name);
          return (0);
        }
   
@@ -3656,7 +3693,11 @@ assign_in_env (word, flags)
   array_needs_making = 1;
 
   if (flags)
-    stupidly_hack_special_variables (newname);
+    {
+      if (STREQ (newname, "POSIXLY_CORRECT") || STREQ (newname, "POSIX_PEDANDTIC"))
+       save_posix_options ();          /* XXX one level of saving right now */
+      stupidly_hack_special_variables (newname);
+    }
 
   if (echo_command_at_execute)
     /* The Korn shell prints the `+ ' in front of assignment statements,
@@ -3801,6 +3842,34 @@ unbind_variable_noref (name)
   return 0;
 }
 
+int
+unbind_global_variable (name)
+     const char *name;
+{
+  SHELL_VAR *v, *nv;
+  int r;
+
+  v = var_lookup (name, global_variables);
+  /* This starts at the current scope, just like find_global_variable; should we
+     use find_global_variable_nameref here? */
+  nv = (v && nameref_p (v)) ? find_variable_nameref (v) : (SHELL_VAR *)NULL;
+
+  r = nv ? makunbound (nv->name, shell_variables) : makunbound (name, global_variables);
+  return r;
+}
+
+int
+unbind_global_variable_noref (name)
+     const char *name;
+{
+  SHELL_VAR *v;
+
+  v = var_lookup (name, global_variables);
+  if (v)
+    return makunbound (name, global_variables);
+  return 0;
+}
 int
 check_unbind_variable (name)
      const char *name;
@@ -3811,7 +3880,12 @@ check_unbind_variable (name)
   if (v && readonly_p (v))
     {
       internal_error (_("%s: cannot unset: readonly %s"), name, "variable");
-      return -1;
+      return -2;
+    }
+  else if (v && non_unsettable_p (v))
+    {
+      internal_error (_("%s: cannot unset"), name);
+      return -2;
     }
   return (unbind_variable (name));
 }
@@ -4026,7 +4100,7 @@ delete_all_variables (hashed_vars)
       if (!entry) \
        { \
          entry = bind_variable (name, "", 0); \
-         if (!no_invisible_vars && entry) entry->attributes |= att_invisible; \
+         if (entry) entry->attributes |= att_invisible; \
        } \
     } \
   while (0)
@@ -4344,12 +4418,20 @@ local_exported_variables ()
 static int
 variable_in_context (var)
      SHELL_VAR *var;
+{
+  return (local_p (var) && var->context == variable_context);
+}
+
+static int
+visible_variable_in_context (var)
+     SHELL_VAR *var;
 {
   return (invisible_p (var) == 0 && local_p (var) && var->context == variable_context);
 }
 
 SHELL_VAR **
-all_local_variables ()
+all_local_variables (visible_only)
+     int visible_only;
 {
   VARLIST *vlist;
   SHELL_VAR **ret;
@@ -4370,7 +4452,10 @@ all_local_variables ()
     
   vlist = vlist_alloc (HASH_ENTRIES (vc->table));
 
-  flatten (vc->table, variable_in_context, vlist, 0);
+  if (visible_only)
+    flatten (vc->table, visible_variable_in_context, vlist, 0);
+  else
+    flatten (vc->table, variable_in_context, vlist, 0);
 
   ret = vlist->list;
   free (vlist);
@@ -4460,9 +4545,9 @@ char **tempvar_list;
 int tvlist_ind;
 
 /* Take a variable from an assignment statement preceding a posix special
-   builtin (including `return') and create a variable from it as if a
-   standalone assignment statement had been performed. This is called from
-   merge_temporary_env, which is only called when in posix mode. */
+   builtin (including `return') and create a global variable from it. This
+   is called from merge_temporary_env, which is only called when in posix
+   mode. */
 static void
 push_posix_temp_var (data)
      PTR_T data;
@@ -4478,6 +4563,8 @@ push_posix_temp_var (data)
      "current execution environment." */
   v = bind_variable (var->name, value_cell (var), ASS_FORCE|ASS_NOLONGJMP);
 
+  /* XXX - do we need to worry about array variables here? */
+
   /* If this modifies an existing local variable, v->context will be non-zero.
      If it comes back with v->context == 0, we bound at the global context.
      Set binding_table appropriately. It doesn't matter whether it's correct
@@ -4485,13 +4572,19 @@ push_posix_temp_var (data)
   binding_table = v->context ? shell_variables->table : global_variables->table;
 
   /* global variables are no longer temporary and don't need propagating. */
-  if (binding_table == global_variables->table)
+  if (v->context == 0)
     var->attributes &= ~(att_tempvar|att_propagate);
 
   if (v)
     {
-      v->attributes |= var->attributes;
-      v->attributes &= ~att_tempvar;   /* not a temp var now */
+      v->attributes |= var->attributes;                /* preserve tempvar attribute if appropriate */
+      /* If we don't bind a local variable, propagate the value. If we bind a
+        local variable (the "current execution environment"), keep it as local
+        and don't propagate it to the calling environment. */
+      if (v->context > 0 && local_p (v) == 0)
+       v->attributes |= att_propagate;
+      else
+       v->attributes &= ~att_propagate;
     }
 
   if (find_special_var (var->name) >= 0)
@@ -4540,7 +4633,7 @@ push_temp_var (data)
     var->attributes &= ~(att_tempvar|att_propagate);
   else
     {
-      var->attributes |= att_propagate;
+      var->attributes |= att_propagate;                        /* XXX - propagate more than once? */
       if  (binding_table == shell_variables->table)
        shell_variables->flags |= VC_HASTMPVAR;
     }
@@ -4631,6 +4724,15 @@ merge_temporary_env ()
     dispose_temporary_env (posixly_correct ? push_posix_temp_var : push_temp_var);
 }
 
+/* Temporary function to use if we want to separate function and special
+   builtin behavior. */
+void
+merge_function_temporary_env ()
+{
+  if (temporary_env)
+    dispose_temporary_env (push_temp_var);
+}
+
 void
 flush_temporary_env ()
 {
@@ -4649,16 +4751,22 @@ flush_temporary_env ()
 /* **************************************************************** */
 
 static inline char *
-mk_env_string (name, value, isfunc)
+mk_env_string (name, value, attributes)
      const char *name, *value;
-     int isfunc;
+     int attributes;
 {
   size_t name_len, value_len;
   char *p, *q, *t;
+  int isfunc, isarray;
 
   name_len = strlen (name);
   value_len = STRLEN (value);
 
+  isfunc = attributes & att_function;
+#if defined (ARRAY_VARS) && defined (ARRAY_EXPORT)
+  isarray = attributes & (att_array|att_assoc);
+#endif
+
   /* If we are exporting a shell function, construct the encoded function
      name. */
   if (isfunc && value)
@@ -4672,6 +4780,39 @@ mk_env_string (name, value, isfunc)
       memcpy (q, BASHFUNC_SUFFIX, BASHFUNC_SUFFLEN);
       q += BASHFUNC_SUFFLEN;
     }
+#if defined (ARRAY_VARS) && defined (ARRAY_EXPORT)
+  else if (isarray && value)
+    {
+      if (attributes & att_assoc)
+       p = (char *)xmalloc (BASHASSOC_PREFLEN + name_len + BASHASSOC_SUFFLEN + value_len + 2);
+      else
+       p = (char *)xmalloc (BASHARRAY_PREFLEN + name_len + BASHARRAY_SUFFLEN + value_len + 2);
+      q = p;
+      if (attributes & att_assoc)
+       {
+         memcpy (q, BASHASSOC_PREFIX, BASHASSOC_PREFLEN);
+         q += BASHASSOC_PREFLEN;
+       }
+      else
+       {
+         memcpy (q, BASHARRAY_PREFIX, BASHARRAY_PREFLEN);
+         q += BASHARRAY_PREFLEN;
+       }
+      memcpy (q, name, name_len);
+      q += name_len;
+      /* These are actually the same currently */
+      if (attributes & att_assoc)
+        {
+         memcpy (q, BASHASSOC_SUFFIX, BASHASSOC_SUFFLEN);
+         q += BASHARRAY_SUFFLEN;
+        }
+      else
+        {
+         memcpy (q, BASHARRAY_SUFFIX, BASHARRAY_SUFFLEN);
+         q += BASHARRAY_SUFFLEN;
+        }
+    }
+#endif  
   else
     {
       p = (char *)xmalloc (2 + name_len + value_len);
@@ -4736,6 +4877,12 @@ valid_exportstr (v)
 }
 #endif
 
+#if defined (ARRAY_VARS)
+#  define USE_EXPORTSTR (value == var->exportstr && array_p (var) == 0 && assoc_p (var) == 0)
+#else
+#  define USE_EXPORTSTR (value == var->exportstr)
+#endif
+
 static char **
 make_env_array_from_var_list (vars)
      SHELL_VAR **vars;
@@ -4746,8 +4893,6 @@ make_env_array_from_var_list (vars)
 
   list = strvec_create ((1 + strvec_len ((char **)vars)));
 
-#define USE_EXPORTSTR (value == var->exportstr)
-
   for (i = 0, list_index = 0; var = vars[i]; i++)
     {
 #if defined (__CYGWIN__)
@@ -4774,11 +4919,11 @@ make_env_array_from_var_list (vars)
        continue;       /* XXX array vars cannot yet be exported */
 #  endif /* ARRAY_EXPORT */
       else if (assoc_p (var))
-#  if 0
+#  if ARRAY_EXPORT
        value = assoc_to_assign (assoc_cell (var), 0);
 #  else
        continue;       /* XXX associative array vars cannot yet be exported */
-#  endif
+#  endif /* ARRAY_EXPORT */
 #endif
       else
        value = value_cell (var);
@@ -4788,7 +4933,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, function_p (var));
+                                          : mk_env_string (var->name, value, var->attributes);
 
          if (USE_EXPORTSTR == 0)
            SAVE_EXPORTSTR (var, list[list_index]);
@@ -4796,11 +4941,9 @@ make_env_array_from_var_list (vars)
          list_index++;
 #undef USE_EXPORTSTR
 
-#if 0  /* not yet */
-#if defined (ARRAY_VARS)
+#if defined (ARRAY_VARS) && defined (ARRAY_EXPORT)
          if (array_p (var) || assoc_p (var))
            free (value);
-#endif
 #endif
        }
     }
@@ -4864,7 +5007,7 @@ do \
     export_env[export_env_index] = (char *)NULL; \
   } while (0)
 
-/* Add ASSIGN to EXPORT_ENV, or supercede a previous assignment in the
+/* Add ASSIGN to EXPORT_ENV, or supersede a previous assignment in the
    array with the same left-hand side.  Return the new EXPORT_ENV. */
 char **
 add_or_supercede_exported_var (assign, do_alloc)
@@ -5133,11 +5276,7 @@ push_var_context (name, flags, tempvars)
      functions no longer behave like assignment statements preceding
      special builtins, and do not persist in the current shell environment.
      This is austin group interp #654, though nobody implements it yet. */
-#if 0  /* XXX - TAG: bash-5.1 */
   posix_func_behavior = 0;
-#else
-  posix_func_behavior = posixly_correct;
-#endif
 
   vc = new_var_context (name, flags);
   /* Posix interp 1009, temporary assignments preceding function calls modify
@@ -5149,6 +5288,7 @@ push_var_context (name, flags, tempvars)
       vc->table = tempvars;
       /* Have to do this because the temp environment was created before
         variable_context was incremented. */
+      /* XXX - only need to do it if flags&VC_FUNCENV */
       flatten (tempvars, set_context, (VARLIST *)NULL, 0);
       vc->flags |= VC_HASTMPVAR;
     }
@@ -5161,10 +5301,10 @@ push_var_context (name, flags, tempvars)
 /* This can be called from one of two code paths:
        1. pop_scope, which implements the posix rules for propagating variable
           assignments preceding special builtins to the surrounding scope
-          (push_builtin_var);
+          (push_builtin_var -- isbltin == 1);
        2. pop_var_context, which is called from pop_context and implements the
           posix rules for propagating variable assignments preceding function
-          calls to the surrounding scope (push_func_var).
+          calls to the surrounding scope (push_func_var -- isbltin == 0)
 
   It takes variables out of a temporary environment hash table. We take the
   variable in data.
@@ -5182,15 +5322,33 @@ push_posix_tempvar_internal (var, isbltin)
      functions no longer behave like assignment statements preceding
      special builtins, and do not persist in the current shell environment.
      This is austin group interp #654, though nobody implements it yet. */
-#if 0  /* XXX - TAG: bash-5.1 */
   posix_var_behavior = posixly_correct && isbltin;
-#else
-  posix_var_behavior = posixly_correct;
-#endif
+  v = 0;
 
   if (local_p (var) && STREQ (var->name, "-"))
-    set_current_options (value_cell (var));
-  else if (tempvar_p (var) && (posix_var_behavior || (var->attributes & att_propagate)))
+    {
+      set_current_options (value_cell (var));
+      set_shellopts ();
+    }
+  /* This takes variable assignments preceding special builtins that can execute
+     multiple commands (source, eval, etc.) and performs the equivalent of
+     an assignment statement to modify the closest enclosing variable (the
+     posix "current execution environment"). This makes the behavior the same
+     as push_posix_temp_var; but the circumstances of calling are slightly
+     different. */
+  else if (tempvar_p (var) && posix_var_behavior)
+    {
+      /* similar to push_posix_temp_var */
+      v = bind_variable (var->name, value_cell (var), ASS_FORCE|ASS_NOLONGJMP);
+      if (v)
+       {
+         v->attributes |= var->attributes;
+         if (v->context == 0)
+           v->attributes &= ~(att_tempvar|att_propagate);
+         /* XXX - set att_propagate here if v->context > 0? */
+       }
+    }
+  else if (tempvar_p (var) && propagate_p (var))
     {
       /* 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
@@ -5201,16 +5359,6 @@ push_posix_tempvar_internal (var, isbltin)
       /* XXX - should we set v->context here? */
       if (v)
        v->context = shell_variables->scope;
-#if defined (ARRAY_VARS)
-      if (v && (array_p (var) || assoc_p (var)))
-       {
-         FREE (value_cell (v));
-         if (array_p (var))
-           var_setarray (v, array_copy (array_cell (var)));
-         else
-           var_setassoc (v, assoc_copy (assoc_cell (var)));
-       }
-#endif   
       if (shell_variables == global_variables)
        var->attributes &= ~(att_tempvar|att_propagate);
       else
@@ -5221,6 +5369,17 @@ push_posix_tempvar_internal (var, isbltin)
   else
     stupidly_hack_special_variables (var->name);       /* XXX */
 
+#if defined (ARRAY_VARS)
+  if (v && (array_p (var) || assoc_p (var)))
+    {
+      FREE (value_cell (v));
+      if (array_p (var))
+       var_setarray (v, array_copy (array_cell (var)));
+      else
+       var_setassoc (v, assoc_copy (assoc_cell (var)));
+    }
+#endif   
+
   dispose_variable (var);
 }
 
@@ -5270,10 +5429,8 @@ pop_var_context ()
     internal_error (_("pop_var_context: no global_variables context"));
 }
 
-/* Delete the HASH_TABLEs for all variable contexts beginning at VCXT, and
-   all of the VAR_CONTEXTs except GLOBAL_VARIABLES. */
-void
-delete_all_contexts (vcxt)
+static void
+delete_local_contexts (vcxt)
      VAR_CONTEXT *vcxt;
 {
   VAR_CONTEXT *v, *t;
@@ -5282,12 +5439,30 @@ delete_all_contexts (vcxt)
     {
       t = v->down;
       dispose_var_context (v);
-    }    
+    }
+}
 
+/* Delete the HASH_TABLEs for all variable contexts beginning at VCXT, and
+   all of the VAR_CONTEXTs except GLOBAL_VARIABLES. */
+void
+delete_all_contexts (vcxt)
+     VAR_CONTEXT *vcxt;
+{
+  delete_local_contexts (vcxt);
   delete_all_variables (global_variables->table);
   shell_variables = global_variables;
 }
 
+/* Reset the context so we are not executing in a shell function. Only call
+   this if you are getting ready to exit the shell. */
+void
+reset_local_contexts ()
+{
+  delete_local_contexts (shell_variables);
+  shell_variables = global_variables;
+  variable_context = 0;
+}
+
 /* **************************************************************** */
 /*                                                                 */
 /*        Pushing and Popping temporary variable scopes            */
@@ -5383,6 +5558,7 @@ pop_scope (is_special)
 struct saved_dollar_vars {
   char **first_ten;
   WORD_LIST *rest;
+  int count;
 };
 
 static struct saved_dollar_vars *dollar_arg_stack = (struct saved_dollar_vars *)NULL;
@@ -5438,6 +5614,17 @@ free_saved_dollar_vars (args)
     FREE (args[i]);
 }
 
+/* Do what remember_args (xxx, 1) would have done. */
+void
+clear_dollar_vars ()
+{
+  free_dollar_vars ();
+  dispose_words (rest_of_args);
+
+  rest_of_args = (WORD_LIST *)NULL;
+  posparam_count = 0;
+}
+
 /* XXX - should always be followed by remember_args () */
 void
 push_context (name, is_subshell, tempvars)
@@ -5473,10 +5660,12 @@ push_dollar_vars ()
        xrealloc (dollar_arg_stack, (dollar_arg_stack_slots += 10)
                  * sizeof (struct saved_dollar_vars));
     }
-  
+
+  dollar_arg_stack[dollar_arg_stack_index].count = posparam_count;
   dollar_arg_stack[dollar_arg_stack_index].first_ten = save_dollar_vars ();
   dollar_arg_stack[dollar_arg_stack_index++].rest = rest_of_args;
   rest_of_args = (WORD_LIST *)NULL;
+  posparam_count = 0;
   
   dollar_arg_stack[dollar_arg_stack_index].first_ten = (char **)NULL;
   dollar_arg_stack[dollar_arg_stack_index].rest = (WORD_LIST *)NULL;  
@@ -5489,17 +5678,18 @@ pop_dollar_vars ()
   if (dollar_arg_stack == 0 || dollar_arg_stack_index == 0)
     return;
 
-  /* Do what remember_args (xxx, 1) would have done. */
-  free_dollar_vars ();
-  dispose_words (rest_of_args);
-  
+  /* Wipe out current values */
+  clear_dollar_vars ();
+
   rest_of_args = dollar_arg_stack[--dollar_arg_stack_index].rest;
   restore_dollar_vars (dollar_arg_stack[dollar_arg_stack_index].first_ten);
   free (dollar_arg_stack[dollar_arg_stack_index].first_ten);
+  posparam_count = dollar_arg_stack[dollar_arg_stack_index].count;
 
   dollar_arg_stack[dollar_arg_stack_index].first_ten = (char **)NULL;
   dollar_arg_stack[dollar_arg_stack_index].rest = (WORD_LIST *)NULL;
-  
+  dollar_arg_stack[dollar_arg_stack_index].count = 0;
+
   set_dollar_vars_unchanged ();
   invalidate_cached_quoted_dollar_at ();
 }
@@ -5516,6 +5706,7 @@ dispose_saved_dollar_vars ()
 
   dollar_arg_stack[dollar_arg_stack_index].first_ten = (char **)NULL;  
   dollar_arg_stack[dollar_arg_stack_index].rest = (WORD_LIST *)NULL;
+  dollar_arg_stack[dollar_arg_stack_index].count = 0;
 }
 
 /* Initialize BASH_ARGV and BASH_ARGC after turning on extdebug after the
@@ -5580,7 +5771,7 @@ pop_args ()
   GET_ARRAY_FROM_VAR ("BASH_ARGV", bash_argv_v, bash_argv_a);
   GET_ARRAY_FROM_VAR ("BASH_ARGC", bash_argc_v, bash_argc_a);
 
-  ce = array_shift (bash_argc_a, 1, 0);
+  ce = array_unshift_element (bash_argc_a);
   if (ce == 0 || legal_number (element_value (ce), &i) == 0)
     i = 0;
 
@@ -6048,8 +6239,19 @@ void
 sv_tz (name)
      char *name;
 {
-  if (chkexport (name))
-    tzset ();
+  SHELL_VAR *v;
+
+  v = find_variable (name);
+  if (v && exported_p (v))
+    array_needs_making = 1;
+  else if (v == 0)
+    array_needs_making = 1;
+
+  if (array_needs_making)
+    {
+      maybe_make_export_env ();  
+      tzset ();
+    }
 }
 #endif
 
@@ -6142,7 +6344,7 @@ sv_locale (name)
 
 #if 1
   if (r == 0 && posixly_correct)
-    last_command_exit_value = 1;
+    set_exit_status (EXECUTION_FAILURE);
 #endif
 }
 
@@ -6178,19 +6380,27 @@ set_pipestatus_array (ps, nproc)
   /* Fast case */
   if (array_num_elements (a) == nproc && nproc == 1)
     {
+#ifndef ALT_ARRAY_IMPLEMENTATION
       ae = element_forw (a->head);
-      free (element_value (ae));
-      set_element_value (ae, itos (ps[0]));
+#else
+      ae = a->elements[0];
+#endif
+      ARRAY_ELEMENT_REPLACE (ae, itos (ps[0]));
     }
   else if (array_num_elements (a) <= nproc)
     {
       /* modify in array_num_elements members in place, then add */
+#ifndef ALT_ARRAY_IMPLEMENTATION
       ae = a->head;
+#endif
       for (i = 0; i < array_num_elements (a); i++)
        {
+#ifndef ALT_ARRAY_IMPLEMENTATION
          ae = element_forw (ae);
-         free (element_value (ae));
-         set_element_value (ae, itos (ps[i]));
+#else
+         ae = a->elements[i];
+#endif
+         ARRAY_ELEMENT_REPLACE (ae, itos (ps[i]));
        }
       /* add any more */
       for ( ; i < nproc; i++)
@@ -6201,13 +6411,32 @@ set_pipestatus_array (ps, nproc)
     }
   else
     {
+#ifndef ALT_ARRAY_IMPLEMENTATION
       /* deleting elements.  it's faster to rebuild the array. */        
       array_flush (a);
-      for (i = 0; ps[i] != -1; i++)
+      for (i = 0; i < nproc; i++)
        {
          t = inttostr (ps[i], tbuf, sizeof (tbuf));
          array_insert (a, i, t);
        }
+#else
+      /* deleting elements. replace the first NPROC, free the rest */
+      for (i = 0; i < nproc; i++)
+       {
+         ae = a->elements[i];
+         ARRAY_ELEMENT_REPLACE (ae, itos (ps[i]));
+       }
+      for ( ; i <= array_max_index (a); i++)
+       {
+         array_dispose_element (a->elements[i]);
+         a->elements[i] = (ARRAY_ELEMENT *)NULL;
+       }
+
+      /* bookkeeping usually taken care of by array_insert */
+      set_max_index (a, nproc - 1);
+      set_first_index (a, 0);
+      set_num_elements (a, nproc);
+#endif /* ALT_ARRAY_IMPLEMENTATION */
     }
 }