/* variables.c -- Functions for hacking shell variables. */
-/* Copyright (C) 1987-2013 Free Software Foundation, Inc.
+/* Copyright (C) 1987-2022 Free Software Foundation, Inc.
This file is part of GNU Bash, the Bourne Again SHell.
#endif
#include "bashansi.h"
#include "bashintl.h"
+#include "filecntl.h"
#define NEED_XTRACE_SET_DECL
#include "shell.h"
+#include "parser.h"
#include "flags.h"
#include "execute_cmd.h"
#include "findcmd.h"
# include "pcomplete.h"
#endif
+#define VARIABLES_HASH_BUCKETS 1024 /* must be power of two */
+#define FUNCTIONS_HASH_BUCKETS 512
#define TEMPENV_HASH_BUCKETS 4 /* must be power of two */
-#define ifsname(s) ((s)[0] == 'I' && (s)[1] == 'F' && (s)[2] == 'S' && (s)[3] == '\0')
+#define BASHFUNC_PREFIX "BASH_FUNC_"
+#define BASHFUNC_PREFLEN 10 /* == strlen(BASHFUNC_PREFIX */
+#define BASHFUNC_SUFFIX "%%"
+#define BASHFUNC_SUFFLEN 2 /* == strlen(BASHFUNC_SUFFIX) */
+
+#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 int posixly_correct;
-extern int line_number, line_number_base;
-extern int subshell_environment, indirection_level, subshell_level;
-extern int build_version, patch_level;
-extern int expanding_redir;
-extern int last_command_exit_value;
-extern char *dist_version, *release_status;
-extern char *shell_name;
-extern char *primary_prompt, *secondary_prompt;
-extern char *current_host_name;
-extern sh_builtin_func_t *this_shell_builtin;
-extern SHELL_VAR *this_shell_function;
-extern char *the_printed_command_except_trap;
-extern char *this_command_name;
-extern char *command_execution_string;
extern time_t shell_start_time;
-extern int assigning_in_environment;
-extern int executing_builtin;
-extern int funcnest_max;
-
-#if defined (READLINE)
-extern int no_line_editing;
-extern int perform_hostname_completion;
-#endif
+extern struct timeval shellstart;
/* The list of shell variables that the user has created at the global
scope, or that came from the environment. */
the environment. */
HASH_TABLE *shell_functions = (HASH_TABLE *)NULL;
+HASH_TABLE *invalid_env = (HASH_TABLE *)NULL;
+
#if defined (DEBUGGER)
/* The table of shell function definitions that the user defined or that
came from the environment. */
executing functions we are. */
int variable_context = 0;
+/* If non-zero, local variables inherit values and attributes from a variable
+ with the same name at a previous scope. */
+int localvar_inherit = 0;
+
+/* If non-zero, calling `unset' on local variables in previous scopes marks
+ them as invisible so lookups find them unset. This is the same behavior
+ as local variables in the current local scope. */
+int localvar_unset = 0;
+
/* The set of shell assignments which are made only in the environment
for a single command. */
HASH_TABLE *temporary_env = (HASH_TABLE *)NULL;
"$*", "$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;
static int winsize_assignment; /* currently assigning to LINES or COLUMNS */
#endif
+SHELL_VAR nameref_invalid_value;
+static SHELL_VAR nameref_maxloop_value;
+
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 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 *get_urandom 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_lineno PARAMS((SHELL_VAR *, char *, arrayind_t, char *));
+static SHELL_VAR *get_lineno 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 *assign_subshell PARAMS((SHELL_VAR *, char *, arrayind_t, char *));
+static SHELL_VAR *get_subshell PARAMS((SHELL_VAR *));
-static SHELL_VAR *get_bashpid __P((SHELL_VAR *));
+static SHELL_VAR *get_epochseconds PARAMS((SHELL_VAR *));
+static SHELL_VAR *get_epochrealtime PARAMS((SHELL_VAR *));
+
+static SHELL_VAR *get_bashpid PARAMS((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 *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 *get_groupset PARAMS((SHELL_VAR *));
+# if defined (DEBUGGER)
+static SHELL_VAR *get_bashargcv PARAMS((SHELL_VAR *));
+# endif
+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 PARAMS((void));
-static void initialize_dynamic_variables __P((void));
+static SHELL_VAR *bind_invalid_envvar PARAMS((const char *, char *, int));
-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 int var_sametype PARAMS((SHELL_VAR *, SHELL_VAR *));
-static void dispose_variable_value __P((SHELL_VAR *));
-static void free_variable_hash_data __P((PTR_T));
+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 VARLIST *vlist_alloc __P((int));
-static VARLIST *vlist_realloc __P((VARLIST *, int));
-static void vlist_add __P((VARLIST *, SHELL_VAR *, int));
+static void dispose_variable_value PARAMS((SHELL_VAR *));
+static void free_variable_hash_data PARAMS((PTR_T));
-static void flatten __P((HASH_TABLE *, sh_var_map_func_t *, VARLIST *, int));
+static VARLIST *vlist_alloc PARAMS((int));
+static VARLIST *vlist_realloc PARAMS((VARLIST *, int));
+static void vlist_add PARAMS((VARLIST *, SHELL_VAR *, int));
-static int qsort_var_comp __P((SHELL_VAR **, SHELL_VAR **));
+static void flatten PARAMS((HASH_TABLE *, sh_var_map_func_t *, VARLIST *, int));
-static SHELL_VAR **vapply __P((sh_var_map_func_t *));
-static SHELL_VAR **fapply __P((sh_var_map_func_t *));
+static int qsort_var_comp PARAMS((SHELL_VAR **, SHELL_VAR **));
-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 SHELL_VAR **vapply PARAMS((sh_var_map_func_t *));
+static SHELL_VAR **fapply PARAMS((sh_var_map_func_t *));
+
+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_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_variable_internal PARAMS((const char *, int));
+
+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_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 *));
-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_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 int find_special_var __P((const char *));
+static void delete_local_contexts PARAMS((VAR_CONTEXT *));
+
+/* 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 ()
{
shell_variables = global_variables = new_var_context ((char *)NULL, 0);
shell_variables->scope = 0;
- shell_variables->table = hash_create (0);
+ shell_variables->table = hash_create (VARIABLES_HASH_BUCKETS);
}
if (shell_functions == 0)
- shell_functions = hash_create (0);
+ shell_functions = hash_create (FUNCTIONS_HASH_BUCKETS);
#if defined (DEBUGGER)
if (shell_function_defs == 0)
- shell_function_defs = hash_create (0);
+ shell_function_defs = hash_create (FUNCTIONS_HASH_BUCKETS);
#endif
}
create_variable_tables ();
- for (string_index = 0; string = env[string_index++]; )
+ for (string_index = 0; env && (string = env[string_index++]); )
{
char_index = 0;
name = string;
temp_var = (SHELL_VAR *)NULL;
+#if defined (FUNCTION_IMPORT)
/* 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;
- strcpy (temp_string, name);
- temp_string[char_index] = ' ';
- strcpy (temp_string + char_index + 1, string);
+ tname = name + BASHFUNC_PREFLEN; /* start of func name */
+ tname[namelen] = '\0'; /* now tname == func name */
- if (posixly_correct == 0 || legal_identifier (name))
- parse_and_execute (temp_string, name, SEVAL_NONINT|SEVAL_NOHIST);
+ string_length = strlen (string);
+ temp_string = (char *)xmalloc (namelen + string_length + 2);
+
+ memcpy (temp_string, tname, namelen);
+ temp_string[namelen] = ' ';
+ memcpy (temp_string + namelen + 1, string, string_length + 1);
- /* 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 in posix mode, 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);
+ else
+ free (temp_string); /* parse_and_execute does this */
- if (temp_var = find_function (name))
+ if (temp_var = find_function (tname))
{
VSETATTR (temp_var, (att_exported|att_imported));
array_needs_making = 1;
}
else
{
- if (temp_var = bind_variable (name, string, 0))
+ if (temp_var = bind_invalid_envvar (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'"), name);
+ last_command_exit_value = EXECUTION_FAILURE;
+ 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];
}
+ else
+#endif /* FUNCTION_IMPORT */
#if defined (ARRAY_VARS)
# if ARRAY_EXPORT
/* Array variables may not yet be exported. */
- else 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);
+ 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;
+ }
}
-# endif /* ARRAY_EXPORT */
-#endif
-#if 0
- else if (legal_identifier (name))
-#else
else
+# endif /* ARRAY_EXPORT */
#endif
{
ro = 0;
- if (posixly_correct && STREQ (name, "SHELLOPTS"))
+ /* If we processed a command-line option that caused SHELLOPTS to be
+ set, it may already be set (and read-only) by the time we process
+ the shell's environment. */
+ 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))
{
- if (legal_identifier (name))
- VSETATTR (temp_var, (att_exported | att_imported));
- else
+ temp_var = bind_variable (name, string, 0);
+ if (temp_var)
+ {
+ VSETATTR (temp_var, (att_exported | att_imported));
+ if (ro)
+ VSETATTR (temp_var, att_readonly);
+ }
+ }
+ else
+ {
+ temp_var = bind_invalid_envvar (name, string, 0);
+ if (temp_var)
VSETATTR (temp_var, (att_exported | att_imported | att_invisible));
- if (ro)
- VSETATTR (temp_var, att_readonly);
- array_needs_making = 1;
}
+ if (temp_var)
+ array_needs_making = 1;
}
name[char_index] = '=';
/* Now make our own defaults in case the vars that we think are
important are missing. */
temp_var = set_if_not ("PATH", DEFAULT_PATH_VALUE);
-#if 0
- set_auto_export (temp_var); /* XXX */
-#endif
-
temp_var = set_if_not ("TERM", "dumb");
-#if 0
- set_auto_export (temp_var); /* XXX */
-#endif
#if defined (__QNX__)
/* set node id -- don't import it from the environment */
qnx_nidtostr (getnid (), node_name, sizeof (node_name));
# endif
temp_var = bind_variable ("NODE", node_name, 0);
- set_auto_export (temp_var);
+ if (temp_var)
+ set_auto_export (temp_var);
}
#endif
#endif
set_if_not ("PS2", secondary_prompt);
}
- set_if_not ("PS4", "+ ");
+
+ if (current_user.euid == 0)
+ bind_variable ("PS4", "+ ", 0);
+ else
+ set_if_not ("PS4", "+ ");
/* Don't allow IFS to be imported from the environment. */
temp_var = bind_variable ("IFS", " \t\n", 0);
set_ppid ();
+ set_argv0 ();
+
/* Initialize the `getopts' stuff. */
temp_var = bind_variable ("OPTIND", "1", 0);
VSETATTR (temp_var, att_integer);
}
#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. */
#endif /* HISTORY */
#if defined (READLINE) && defined (STRICT_POSIX)
- /* POSIXLY_CORRECT will only be 1 here if the shell was compiled
- -DSTRICT_POSIX */
+ /* POSIXLY_CORRECT will be 1 here if the shell was compiled
+ -DSTRICT_POSIX or if POSIXLY_CORRECT was supplied in the shell's
+ environment */
if (interactive_shell && posixly_correct && no_line_editing == 0)
rl_prefer_env_winsize = 1;
#endif /* READLINE && STRICT_POSIX */
- /*
- * 24 October 2001
- *
- * I'm tired of the arguing and bug reports. Bash now leaves SSH_CLIENT
- * and SSH2_CLIENT alone. I'm going to rely on the shell_level check in
- * isnetconn() to avoid running the startup files more often than wanted.
- * That will, of course, only work if the user's login shell is bash, so
- * I've made that behavior conditional on SSH_SOURCE_BASHRC being defined
- * in config-top.h.
- */
-#if 0
- temp_var = find_variable ("SSH_CLIENT");
- if (temp_var && imported_p (temp_var))
- {
- VUNSETATTR (temp_var, att_exported);
- array_needs_making = 1;
- }
- temp_var = find_variable ("SSH2_CLIENT");
- if (temp_var && imported_p (temp_var))
- {
- VUNSETATTR (temp_var, att_exported);
- array_needs_making = 1;
- }
-#endif
-
/* Get the user's real and effective user ids. */
uidset ();
+ temp_var = 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);
+ sv_shcompat ("BASH_COMPAT");
+
+ /* Allow FUNCNEST to be inherited from the environment. */
+ sv_funcnest ("FUNCNEST");
+
/* Initialize the dynamic variables, and seed their values. */
initialize_dynamic_variables ();
}
shell_level = old_level + change;
if (shell_level < 0)
shell_level = 0;
- else if (shell_level > 1000)
+ else if (shell_level >= 1000)
{
internal_warning (_("shell level (%d) too high, resetting to 1"), shell_level);
shell_level = 1;
set_pwd ()
{
SHELL_VAR *temp_var, *home_var;
- char *temp_string, *home_string;
+ char *temp_string, *home_string, *current_dir;
home_var = find_variable ("HOME");
home_string = home_var ? value_cell (home_var) : (char *)NULL;
temp_var = find_variable ("PWD");
+ /* Follow posix rules for importing PWD */
if (temp_var && imported_p (temp_var) &&
(temp_string = value_cell (temp_var)) &&
+ temp_string[0] == '/' &&
same_file (temp_string, ".", (struct stat *)NULL, (struct stat *)NULL))
- set_working_directory (temp_string);
+ {
+ current_dir = sh_canonpath (temp_string, PATH_CHECKDOTDOT|PATH_CHECKEXISTS);
+ if (current_dir == 0)
+ current_dir = get_working_directory ("shell_init");
+ else
+ set_working_directory (current_dir);
+ if (posixly_correct && current_dir)
+ {
+ temp_var = bind_variable ("PWD", current_dir, 0);
+ set_auto_export (temp_var);
+ }
+ free (current_dir);
+ }
else if (home_string && interactive_shell && login_shell &&
same_file (home_string, ".", (struct stat *)NULL, (struct stat *)NULL))
{
}
/* According to the Single Unix Specification, v2, $OLDPWD is an
- `environment variable' and therefore should be auto-exported.
- Make a dummy invisible variable for OLDPWD, and mark it as exported. */
- temp_var = bind_variable ("OLDPWD", (char *)NULL, 0);
- VSETATTR (temp_var, (att_exported | att_invisible));
+ `environment variable' and therefore should be auto-exported. If we
+ don't find OLDPWD in the environment, or it doesn't name a directory,
+ make a dummy invisible variable for OLDPWD, and mark it as exported. */
+ temp_var = find_variable ("OLDPWD");
+#if defined (OLDPWD_CHECK_DIRECTORY)
+ if (temp_var == 0 || value_cell (temp_var) == 0 || file_isdir (value_cell (temp_var)) == 0)
+#else
+ if (temp_var == 0 || value_cell (temp_var) == 0)
+#endif
+ {
+ temp_var = bind_variable ("OLDPWD", (char *)NULL, 0);
+ VSETATTR (temp_var, (att_exported | att_invisible));
+ }
}
/* Make a variable $PPID, which holds the pid of the shell's parent. */
ARRAY *av;
char *s, d[32], b[INT_STRLEN_BOUND(int) + 1];
- unbind_variable ("BASH_VERSINFO");
+ unbind_variable_noref ("BASH_VERSINFO");
vv = make_new_array_variable ("BASH_VERSINFO");
av = array_cell (vv);
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);
}
#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. */
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 *
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 *
INIT_DYNAMIC_VAR ("SECONDS", (v ? value_cell (v) : (char *)NULL), get_seconds, assign_seconds);
return v;
}
-
-/* The random number seed. You can change this by setting RANDOM. */
-static unsigned long rseed = 1;
-static int last_random_value;
-static int seeded_subshell = 0;
-
-/* A linear congruential random number generator based on the example
- one in the ANSI C standard. This one isn't very good, but a more
- complicated one is overkill. */
-
-/* Returns a pseudo-random number between 0 and 32767. */
-static int
-brand ()
-{
- /* From "Random number generators: good ones are hard to find",
- Park and Miller, Communications of the ACM, vol. 31, no. 10,
- October 1988, p. 1195. filtered through FreeBSD */
- long h, l;
-
- /* Can't seed with 0. */
- if (rseed == 0)
- rseed = 123459876;
- h = rseed / 127773;
- l = rseed % 127773;
- rseed = 16807 * l - 2836 * h;
-#if 0
- if (rseed < 0)
- rseed += 0x7fffffff;
-#endif
- return ((unsigned int)(rseed & 32767)); /* was % 32768 */
-}
-
-/* Set the random number generator seed to SEED. */
-static void
-sbrand (seed)
- unsigned long seed;
-{
- rseed = seed;
- last_random_value = 0;
-}
-static void
-seedrand ()
-{
- struct timeval tv;
+/* Functions for $RANDOM and $SRANDOM */
- gettimeofday (&tv, NULL);
- sbrand (tv.tv_sec ^ tv.tv_usec ^ getpid ());
-}
+int last_random_value;
+static int seeded_subshell = 0;
static SHELL_VAR *
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
do
rv = brand ();
while (rv == last_random_value);
- return rv;
+
+ return (last_random_value = rv);
}
static SHELL_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 *
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. */
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 *
get_subshell (var)
SHELL_VAR *var;
{
- char *p;
+ return (set_int_value (var, subshell_level, 0));
+}
- p = itos (subshell_level);
- FREE (value_cell (var));
- var_setvalue (var, p);
- return (var);
+static SHELL_VAR *
+get_epochseconds (var)
+ SHELL_VAR *var;
+{
+ intmax_t now;
+
+ now = NOW;
+ return (set_int_value (var, now, 0));
+}
+
+static SHELL_VAR *
+get_epochrealtime (var)
+ SHELL_VAR *var;
+{
+ char buf[32];
+ struct timeval tv;
+
+ gettimeofday (&tv, NULL);
+ snprintf (buf, sizeof (buf), "%u%c%06u", (unsigned)tv.tv_sec,
+ locale_decpoint (),
+ (unsigned)tv.tv_usec);
+
+ return (set_string_value (var, buf, 0));
}
static SHELL_VAR *
SHELL_VAR *var;
{
int pid;
- char *p;
pid = getpid ();
- p = itos (pid);
+ return (set_int_value (var, pid, 1));
+}
- FREE (value_cell (var));
- VSETATTR (var, att_integer|att_readonly);
- var_setvalue (var, p);
- return (var);
+static SHELL_VAR *
+get_bash_argv0 (var)
+ SHELL_VAR *var;
+{
+ return (set_string_value (var, dollar_vars[0], 0));
+}
+
+static char *static_shell_name = 0;
+
+static SHELL_VAR *
+assign_bash_argv0 (var, value, unused, key)
+ SHELL_VAR *var;
+ char *value;
+ arrayind_t unused;
+ char *key;
+{
+ size_t vlen;
+
+ if (value == 0)
+ return var;
+
+ FREE (dollar_vars[0]);
+ dollar_vars[0] = savestring (value);
+
+ /* Need these gyrations because shell_name isn't dynamically allocated */
+ vlen = STRLEN (value);
+ static_shell_name = xrealloc (static_shell_name, vlen + 1);
+ strcpy (static_shell_name, value);
+
+ 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)
SHELL_VAR *var;
{
char *p;
- if (the_printed_command_except_trap)
- p = savestring (the_printed_command_except_trap);
- else
- {
- p = (char *)xmalloc (1);
- p[0] = '\0';
- }
- FREE (value_cell (var));
- var_setvalue (var, p);
- return (var);
+ p = the_printed_command_except_trap ? the_printed_command_except_trap : "";
+ return (set_string_value (var, p, 0));
}
#if defined (HISTORY)
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
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
{
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;
return (self);
}
+# if defined (DEBUGGER)
+static SHELL_VAR *
+get_bashargcv (self)
+ SHELL_VAR *self;
+{
+ static int self_semaphore = 0;
+
+ /* Backwards compatibility: if we refer to BASH_ARGV or BASH_ARGC at the
+ top level without enabling debug mode, and we don't have an instance
+ of the variable set, initialize the arg arrays.
+ This will already have been done if debugging_mode != 0. */
+ if (self_semaphore == 0 && variable_context == 0 && debugging_mode == 0) /* don't do it for shell functions */
+ {
+ self_semaphore = 1;
+ init_bash_argv ();
+ self_semaphore = 0;
+ }
+ return self;
+}
+# endif
+
static SHELL_VAR *
build_hashcmd (self)
SHELL_VAR *self;
arrayind_t ind;
char *key;
{
+#if defined (RESTRICTED_SHELL)
+ char *full_path;
+
+ if (restricted)
+ {
+ if (strchr (value, '/'))
+ {
+ sh_restricted (value);
+ return (SHELL_VAR *)NULL;
+ }
+ /* If we are changing the hash table in a restricted shell, make sure the
+ target pathname can be found using a $PATH search. */
+ full_path = find_user_command (value);
+ if (full_path == 0 || *full_path == 0 || executable_file (full_path) == 0)
+ {
+ sh_notfound (value);
+ free (full_path);
+ return ((SHELL_VAR *)NULL);
+ }
+ free (full_path);
+ }
+#endif
phash_insert (key, value, 0, 0);
return (build_hashcmd (self));
}
arrayind_t ind;
char *key;
{
+ if (legal_alias_name (key, 0) == 0)
+ {
+ report_error (_("`%s': invalid alias name"), key);
+ return (self);
+ }
add_alias (key, value);
return (build_aliasvar (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);
}
v = init_seconds_var ();
+ INIT_DYNAMIC_VAR ("BASH_ARGV0", (char *)NULL, get_bash_argv0, assign_bash_argv0);
+
INIT_DYNAMIC_VAR ("BASH_COMMAND", (char *)NULL, get_bash_command, (sh_var_assign_func_t *)NULL);
INIT_DYNAMIC_VAR ("BASH_SUBSHELL", (char *)NULL, get_subshell, assign_subshell);
INIT_DYNAMIC_VAR ("RANDOM", (char *)NULL, get_random, assign_random);
VSETATTR (v, att_integer);
+ INIT_DYNAMIC_VAR ("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);
+ VSETATTR (v, att_regenerate);
INIT_DYNAMIC_VAR ("BASHPID", (char *)NULL, get_bashpid, null_assign);
- VSETATTR (v, att_integer|att_readonly);
+ VSETATTR (v, att_integer);
+
+ INIT_DYNAMIC_VAR ("EPOCHSECONDS", (char *)NULL, get_epochseconds, null_assign);
+ VSETATTR (v, att_regenerate);
+ INIT_DYNAMIC_VAR ("EPOCHREALTIME", (char *)NULL, get_epochrealtime, null_assign);
+ VSETATTR (v, att_regenerate);
#if defined (HISTORY)
INIT_DYNAMIC_VAR ("HISTCMD", (char *)NULL, get_histcmd, (sh_var_assign_func_t *)NULL);
v = init_dynamic_array_var ("GROUPS", get_groupset, null_array_assign, att_noassign);
# if defined (DEBUGGER)
- v = init_dynamic_array_var ("BASH_ARGC", get_self, null_array_assign, att_noassign|att_nounset);
- v = init_dynamic_array_var ("BASH_ARGV", get_self, null_array_assign, att_noassign|att_nounset);
+ v = init_dynamic_array_var ("BASH_ARGC", get_bashargcv, null_array_assign, att_noassign|att_nounset);
+ v = init_dynamic_array_var ("BASH_ARGV", get_bashargcv, null_array_assign, att_noassign|att_nounset);
# endif /* DEBUGGER */
v = init_dynamic_array_var ("BASH_SOURCE", get_self, null_array_assign, att_noassign|att_nounset);
v = init_dynamic_array_var ("BASH_LINENO", get_self, null_array_assign, att_noassign|att_nounset);
/* */
/* **************************************************************** */
-/* 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
+#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). */
static SHELL_VAR *
*/
SHELL_VAR *
-find_variable_internal (name, force_tempenv)
+find_variable_internal (name, flags)
const char *name;
- int force_tempenv;
+ int flags;
{
SHELL_VAR *var;
- int search_tempenv;
+ int search_tempenv, force_tempenv;
VAR_CONTEXT *vc;
var = (SHELL_VAR *)NULL;
+ force_tempenv = (flags & FV_FORCETEMPENV);
+
/* If explicitly requested, first look in the temporary environment for
the variable. This allows constructs such as "foo=x eval 'echo $foo'"
to get the `exported' value of $foo. This happens if we are executing
if (search_tempenv && temporary_env)
var = hash_lookup (name, temporary_env);
- vc = shell_variables;
-#if 0
-if (search_tempenv == 0 && /* (subshell_environment & SUBSHELL_COMSUB) && */
- expanding_redir &&
- (this_shell_builtin == eval_builtin || this_shell_builtin == command_builtin))
- {
- itrace("find_variable_internal: search_tempenv == 0: skipping VC_BLTNENV");
- while (vc && (vc->flags & VC_BLTNENV))
- vc = vc->down;
- if (vc == 0)
- vc = shell_variables;
- }
-#endif
-
if (var == 0)
- var = var_lookup (name, vc);
+ {
+ if ((flags & FV_SKIPINVISIBLE) == 0)
+ var = var_lookup (name, shell_variables);
+ else
+ {
+ /* essentially var_lookup expanded inline so we can check for
+ att_invisible */
+ for (vc = shell_variables; vc; vc = vc->down)
+ {
+ var = hash_lookup (name, vc->table);
+ if (var && invisible_p (var))
+ var = 0;
+ if (var)
+ break;
+ }
+ }
+ }
if (var == 0)
return ((SHELL_VAR *)NULL);
find_variable_nameref (v)
SHELL_VAR *v;
{
- int level;
+ int level, flags;
char *newname;
SHELL_VAR *orig, *oldv;
if (newname == 0 || *newname == '\0')
return ((SHELL_VAR *)0);
oldv = v;
- v = find_variable_internal (newname, (expanding_redir == 0 && (assigning_in_environment || executing_builtin)));
+ flags = 0;
+ if (expanding_redir == 0 && (assigning_in_environment || executing_builtin))
+ flags |= FV_FORCETEMPENV;
+ /* We don't handle array subscripts here. */
+ v = find_variable_internal (newname, flags);
if (v == orig || v == oldv)
{
internal_warning (_("%s: circular name reference"), orig->name);
+#if 1
+ /* XXX - provisional change - circular refs go to
+ global scope for resolution, without namerefs. */
+ if (variable_context && v->context)
+ return (find_global_variable_noref (v->name));
+ else
+#endif
return ((SHELL_VAR *)0);
}
}
/* Resolve the chain of nameref variables for NAME. XXX - could change later */
SHELL_VAR *
-find_variable_last_nameref (name)
+find_variable_last_nameref (name, vflags)
const char *name;
+ int vflags;
{
SHELL_VAR *v, *nv;
char *newname;
- int level;
+ int level, flags;
nv = v = find_variable_noref (name);
level = 0;
return ((SHELL_VAR *)0); /* error message here? */
newname = nameref_cell (v);
if (newname == 0 || *newname == '\0')
- return ((SHELL_VAR *)0);
+ return ((vflags && invisible_p (v)) ? v : (SHELL_VAR *)0);
nv = v;
- v = find_variable_internal (newname, (expanding_redir == 0 && (assigning_in_environment || executing_builtin)));
+ flags = 0;
+ if (expanding_redir == 0 && (assigning_in_environment || executing_builtin))
+ flags |= FV_FORCETEMPENV;
+ /* We don't accommodate array subscripts here. */
+ v = find_variable_internal (newname, flags);
}
return nv;
}
/* Resolve the chain of nameref variables for NAME. XXX - could change later */
SHELL_VAR *
-find_global_variable_last_nameref (name)
+find_global_variable_last_nameref (name, vflags)
const char *name;
+ int vflags;
{
SHELL_VAR *v, *nv;
char *newname;
return ((SHELL_VAR *)0); /* error message here? */
newname = nameref_cell (v);
if (newname == 0 || *newname == '\0')
- return ((SHELL_VAR *)0);
+ return ((vflags && invisible_p (v)) ? v : (SHELL_VAR *)0);
nv = v;
+ /* We don't accommodate array subscripts here. */
v = find_global_variable_noref (newname);
}
return nv;
VAR_CONTEXT *vc;
{
SHELL_VAR *nv, *nv2;
- VAR_CONTEXT *nvc;
char *newname;
int level;
{
level++;
if (level > NAMEREF_MAX)
- return ((SHELL_VAR *)NULL);
+ return (&nameref_maxloop_value);
newname = nameref_cell (nv);
if (newname == 0 || *newname == '\0')
return ((SHELL_VAR *)NULL);
for (nv = v, nvc = vc; nvc; nvc = nvc->down)
{
nv2 = find_nameref_at_context (nv, nvc);
+ if (nv2 == &nameref_maxloop_value)
+ return (nv2); /* XXX */
if (nv2 == 0)
continue;
nv = nv2;
for (nv = v, nvc = vc; nvc; nvc = nvc->down)
{
nv2 = find_nameref_at_context (nv, nvc);
+ if (nv2 == &nameref_maxloop_value)
+ return (nv2); /* XXX */
if (nv2 == 0)
continue;
nv = nv2;
return (nameref_p (nv) ? nv : (SHELL_VAR *)NULL);
}
+SHELL_VAR *
+find_variable_nameref_for_create (name, flags)
+ const char *name;
+ int flags;
+{
+ SHELL_VAR *var;
+
+ /* See if we have a nameref pointing to a variable that hasn't been
+ created yet. */
+ var = find_variable_last_nameref (name, 1);
+ if ((flags&1) && var && nameref_p (var) && invisible_p (var))
+ {
+ internal_warning (_("%s: removing nameref attribute"), name);
+ VUNSETATTR (var, att_nameref);
+ }
+ if (var && nameref_p (var))
+ {
+ if (legal_identifier (nameref_cell (var)) == 0)
+ {
+ sh_invalidid (nameref_cell (var) ? nameref_cell (var) : "");
+ return ((SHELL_VAR *)INVALID_NAMEREF_VALUE);
+ }
+ }
+ return (var);
+}
+
+SHELL_VAR *
+find_variable_nameref_for_assignment (name, flags)
+ const char *name;
+ int flags;
+{
+ SHELL_VAR *var;
+
+ /* See if we have a nameref pointing to a variable that hasn't been
+ created yet. */
+ var = find_variable_last_nameref (name, 1);
+ if (var && nameref_p (var) && invisible_p (var)) /* XXX - flags */
+ {
+ internal_warning (_("%s: removing nameref attribute"), name);
+ VUNSETATTR (var, att_nameref);
+ }
+ if (var && nameref_p (var))
+ {
+ if (valid_nameref_value (nameref_cell (var), 1) == 0)
+ {
+ sh_invalidid (nameref_cell (var) ? nameref_cell (var) : "");
+ return ((SHELL_VAR *)INVALID_NAMEREF_VALUE);
+ }
+ }
+ return (var);
+}
+
+/* If find_variable (name) returns NULL, check that it's not a nameref
+ referencing a variable that doesn't exist. If it is, return the new
+ name. If not, return the original name. Kind of like the previous
+ function, but dealing strictly with names. This takes assignment flags
+ so it can deal with the various assignment modes used by `declare'. */
+char *
+nameref_transform_name (name, flags)
+ char *name;
+ int flags;
+{
+ SHELL_VAR *v;
+ char *newname;
+
+ v = 0;
+ if (flags & ASS_MKLOCAL)
+ {
+ v = find_variable_last_nameref (name, 1);
+ /* If we're making local variables, only follow namerefs that point to
+ non-existent variables at the same variable context. */
+ if (v && v->context != variable_context)
+ v = 0;
+ }
+ else if (flags & ASS_MKGLOBAL)
+ v = (flags & ASS_CHKLOCAL) ? find_variable_last_nameref (name, 1)
+ : find_global_variable_last_nameref (name, 1);
+ if (v && nameref_p (v) && valid_nameref_value (nameref_cell (v), 1))
+ return nameref_cell (v);
+ return name;
+}
+
/* Find a variable, forcing a search of the temporary environment first */
SHELL_VAR *
find_variable_tempenv (name)
{
SHELL_VAR *var;
- var = find_variable_internal (name, 1);
+ var = find_variable_internal (name, FV_FORCETEMPENV);
if (var && nameref_p (var))
var = find_variable_nameref (var);
return (var);
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);
const char *name;
{
SHELL_VAR *v;
+ int flags;
+
+ last_table_searched = 0;
+ flags = 0;
+ if (expanding_redir == 0 && (assigning_in_environment || executing_builtin))
+ flags |= FV_FORCETEMPENV;
+ v = find_variable_internal (name, flags);
+ if (v && nameref_p (v))
+ v = find_variable_nameref (v);
+ return v;
+}
+
+/* Find the first instance of NAME in the variable context chain; return first
+ one found without att_invisible set; return 0 if no non-invisible instances
+ found. */
+SHELL_VAR *
+find_variable_no_invisible (name)
+ const char *name;
+{
+ SHELL_VAR *v;
+ int flags;
+
+ last_table_searched = 0;
+ flags = FV_SKIPINVISIBLE;
+ if (expanding_redir == 0 && (assigning_in_environment || executing_builtin))
+ flags |= FV_FORCETEMPENV;
+ v = find_variable_internal (name, flags);
+ if (v && nameref_p (v))
+ v = find_variable_nameref (v);
+ return v;
+}
+
+/* Find the first instance of NAME in the variable context chain; return first
+ one found even if att_invisible set. */
+SHELL_VAR *
+find_variable_for_assignment (name)
+ const char *name;
+{
+ SHELL_VAR *v;
+ int flags;
last_table_searched = 0;
- v = find_variable_internal (name, (expanding_redir == 0 && (assigning_in_environment || executing_builtin)));
+ flags = 0;
+ if (expanding_redir == 0 && (assigning_in_environment || executing_builtin))
+ flags |= FV_FORCETEMPENV;
+ v = find_variable_internal (name, flags);
if (v && nameref_p (v))
v = find_variable_nameref (v);
return v;
const char *name;
{
SHELL_VAR *v;
+ int flags;
- v = find_variable_internal (name, (expanding_redir == 0 && (assigning_in_environment || executing_builtin)));
+ flags = 0;
+ if (expanding_redir == 0 && (assigning_in_environment || executing_builtin))
+ flags |= FV_FORCETEMPENV;
+ v = find_variable_internal (name, flags);
return v;
}
/* Return the string value of a variable. Return NULL if the variable
doesn't exist. Don't cons a new string. This is a potential memory
- leak if the variable is found in the temporary environment. Since
- functions and variables have separate name spaces, returns NULL if
- var_name is a shell function only. */
+ leak if the variable is found in the temporary environment, but doesn't
+ 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)
const char *var_name;
/* */
/* **************************************************************** */
+static int
+var_sametype (v1, v2)
+ SHELL_VAR *v1;
+ SHELL_VAR *v2;
+{
+ if (v1 == 0 || v2 == 0)
+ return 0;
+#if defined (ARRAY_VARS)
+ else if (assoc_p (v1) && assoc_p (v2))
+ return 1;
+ else if (array_p (v1) && array_p (v2))
+ return 1;
+ else if (array_p (v1) || array_p (v2))
+ return 0;
+ else if (assoc_p (v1) || assoc_p (v2))
+ return 0;
+#endif
+ else
+ return 1;
+}
+
+int
+validate_inherited_value (var, type)
+ SHELL_VAR *var;
+ int type;
+{
+#if defined (ARRAY_VARS)
+ if (type == att_array && assoc_p (var))
+ return 0;
+ else if (type == att_assoc && array_p (var))
+ return 0;
+ else
+#endif
+ return 1; /* should we run convert_var_to_array here or let the caller? */
+}
+
/* Set NAME to VALUE if NAME has no value. */
SHELL_VAR *
set_if_not (name, value)
/* Create a local variable referenced by NAME. */
SHELL_VAR *
-make_local_variable (name)
+make_local_variable (name, flags)
const char *name;
+ int flags;
{
- SHELL_VAR *new_var, *old_var;
+ SHELL_VAR *new_var, *old_var, *old_ref;
VAR_CONTEXT *vc;
int was_tmpvar;
- char *tmp_value;
+ char *old_value;
+ /* We don't want to follow the nameref chain when making local variables; we
+ just want to create them. */
+ old_ref = find_variable_noref (name);
+ if (old_ref && nameref_p (old_ref) == 0)
+ old_ref = 0;
/* 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)
+ if (old_ref == 0 && old_var && local_p (old_var) && old_var->context == variable_context)
return (old_var);
+ /* local -n foo; local -n foo; is a no-op. */
+ if (old_ref && local_p (old_ref) && old_ref->context == variable_context)
+ return (old_ref);
+
+ /* From here on, we want to use the refvar, not the variable it references */
+ if (old_ref)
+ old_var = old_ref;
+
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
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);
+ VUNSETATTR (old_var, att_invisible); /* XXX */
+ /* 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;
+ /* Since we found the variable in a temporary environment, this will
+ succeed. */
+ for (vc = shell_variables; vc; vc = vc->down)
+ if (vc_isfuncenv (vc) && vc->scope == variable_context)
+ break;
+ goto set_local_var_flags;
+
return (old_var);
}
- if (was_tmpvar)
- tmp_value = value_cell (old_var);
+
+ /* If we want to change to "inherit the old variable's value" semantics,
+ here is where to save the old value. */
+ old_value = was_tmpvar ? value_cell (old_var) : (char *)NULL;
for (vc = shell_variables; vc; vc = vc->down)
if (vc_isfuncenv (vc) && vc->scope == variable_context)
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 we want to change the local variable semantics to "inherit
+ the old variable's value" here is where to set it. And we would
+ need to use copy_variable (currently unused) to do it for all
+ possible variable values. */
if (was_tmpvar)
- var_setvalue (new_var, savestring (tmp_value));
+ var_setvalue (new_var, savestring (old_value));
+ else if (localvar_inherit || (flags & MKLOC_INHERIT))
+ {
+ /* This may not make sense for nameref variables that are shadowing
+ variables with the same name, but we don't know that yet. */
+#if defined (ARRAY_VARS)
+ if (assoc_p (old_var))
+ var_setassoc (new_var, assoc_copy (assoc_cell (old_var)));
+ else if (array_p (old_var))
+ var_setarray (new_var, array_copy (array_cell (old_var)));
+ else if (value_cell (old_var))
+#else
+ if (value_cell (old_var))
+#endif
+ var_setvalue (new_var, savestring (value_cell (old_var)));
+ else
+ var_setvalue (new_var, (char *)NULL);
+ }
- new_var->attributes = exported_p (old_var) ? att_exported : 0;
+ if (localvar_inherit || (flags & MKLOC_INHERIT))
+ {
+ /* It doesn't make sense to inherit the nameref attribute */
+ new_var->attributes = old_var->attributes & ~att_nameref;
+ new_var->dynamic_value = old_var->dynamic_value;
+ new_var->assign_func = old_var->assign_func;
+ }
+ else
+ /* We inherit the export attribute, but no others. */
+ new_var->attributes = exported_p (old_var) ? att_exported : 0;
}
+set_local_var_flags:
vc->flags |= VC_HASLOCAL;
new_var->context = variable_context;
if (ifsname (name))
setifs (new_var);
- if (was_tmpvar == 0)
+ /* 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 && value_cell (new_var) == 0)
VSETATTR (new_var, att_invisible); /* XXX */
return (new_var);
}
}
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;
+
+ assoc_ok = flags & MKLOC_ASSOCOK;
- var = make_local_variable (name);
+ 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. */
if (var == 0 || array_p (var) || (assoc_ok && assoc_p (var)))
return var;
- array = array_create ();
+ /* Validate any value we inherited from a variable instance at a previous
+ scope and discard anything that's invalid. */
+ if (localvar_inherit && assoc_p (var))
+ {
+ internal_warning (_("%s: cannot inherit value from incompatible type"), name);
+ VUNSETATTR (var, att_assoc);
+ dispose_variable_value (var);
+ array = array_create ();
+ var_setarray (var, array);
+ }
+ else if (localvar_inherit)
+ var = convert_var_to_array (var); /* XXX */
+ else
+ {
+ dispose_variable_value (var);
+ array = array_create ();
+ var_setarray (var, array);
+ }
- dispose_variable_value (var);
- var_setarray (var, array);
VSETATTR (var, att_array);
return var;
}
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);
}
SHELL_VAR *
-make_local_assoc_variable (name)
+make_local_assoc_variable (name, flags)
char *name;
+ int flags;
{
SHELL_VAR *var;
HASH_TABLE *hash;
+ int array_ok;
- var = make_local_variable (name);
- if (var == 0 || assoc_p (var))
+ array_ok = flags & MKLOC_ARRAYOK;
+
+ 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. */
+ if (var == 0 || assoc_p (var) || (array_ok && array_p (var)))
return var;
- dispose_variable_value (var);
- hash = assoc_create (0);
+ /* Validate any value we inherited from a variable instance at a previous
+ scope and discard anything that's invalid. */
+ if (localvar_inherit && array_p (var))
+ {
+ internal_warning (_("%s: cannot inherit value from incompatible type"), name);
+ VUNSETATTR (var, att_array);
+ dispose_variable_value (var);
+ hash = assoc_create (ASSOC_HASH_BUCKETS);
+ var_setassoc (var, hash);
+ }
+ else if (localvar_inherit)
+ var = convert_var_to_assoc (var); /* XXX */
+ else
+ {
+ dispose_variable_value (var);
+ hash = assoc_create (ASSOC_HASH_BUCKETS);
+ var_setassoc (var, hash);
+ }
- var_setassoc (var, hash);
VSETATTR (var, att_assoc);
return var;
}
then do expression evaluation on it and store the result. The
functions in expr.c (evalexp()) and bind_int_variable() are responsible
for turning off the integer flag if they don't want further
- evaluation done. */
- if (integer_p (var))
+ evaluation done. Callers that find it inconvenient to do this can set
+ the ASS_NOEVAL flag. For the special case of arithmetic expression
+ evaluation, the caller can set ASS_NOLONGJMP to avoid jumping out to
+ top_level. */
+ if ((flags & ASS_NOEVAL) == 0 && integer_p (var))
{
if (flags & ASS_APPEND)
{
oval = value_cell (var);
- lval = evalexp (oval, &expok); /* ksh93 seems to do this */
+ lval = evalexp (oval, 0, &expok); /* ksh93 seems to do this */
if (expok == 0)
{
- top_level_cleanup ();
- jump_to_top_level (DISCARD);
+ if (flags & ASS_NOLONGJMP)
+ goto make_value;
+ else
+ {
+ top_level_cleanup ();
+ jump_to_top_level (DISCARD);
+ }
}
}
- rval = evalexp (value, &expok);
+ rval = evalexp (value, 0, &expok);
if (expok == 0)
{
- top_level_cleanup ();
- jump_to_top_level (DISCARD);
+ if (flags & ASS_NOLONGJMP)
+ goto make_value;
+ else
+ {
+ 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. */
retval = itos (rval);
}
#if defined (CASEMOD_ATTRS)
- else if (capcase_p (var) || uppercase_p (var) || lowercase_p (var))
+ else if ((flags & ASS_NOEVAL) == 0 && (capcase_p (var) || uppercase_p (var) || lowercase_p (var)))
{
if (flags & ASS_APPEND)
{
#endif /* CASEMOD_ATTRS */
else if (value)
{
+make_value:
if (flags & ASS_APPEND)
{
oval = get_variable_value (var);
return retval;
}
+/* If we can optimize appending to string variables, say so */
+static int
+can_optimize_assignment (entry, value, aflags)
+ SHELL_VAR *entry;
+ char *value;
+ int aflags;
+{
+ if ((aflags & ASS_APPEND) == 0)
+ return 0;
+#if defined (ARRAY_VARS)
+ if (array_p (entry) || assoc_p (entry))
+ return 0;
+#endif
+ if (integer_p (entry) || uppercase_p (entry) || lowercase_p (entry) || capcase_p (entry))
+ return 0;
+ if (readonly_p (entry) || noassign_p (entry))
+ return 0;
+ return 1;
+}
+
+/* right now we optimize appends to string variables */
+static SHELL_VAR *
+optimized_assignment (entry, value, aflags)
+ SHELL_VAR *entry;
+ char *value;
+ int aflags;
+{
+ size_t len, vlen;
+ char *v, *new;
+
+ v = value_cell (entry);
+ len = STRLEN (v);
+ vlen = STRLEN (value);
+
+ new = (char *)xrealloc (v, len + vlen + 8); /* for now */
+ if (vlen == 1)
+ {
+ new[len] = *value;
+ new[len+1] = '\0';
+ }
+ else
+ strcpy (new + len, value);
+ var_setvalue (entry, new);
+ return entry;
+}
+
/* Bind a variable NAME to VALUE in the HASH_TABLE TABLE, which may be the
- temporary environment (but usually is not). */
+ temporary environment (but usually is not). HFLAGS controls how NAME
+ is looked up in TABLE; AFLAGS controls how VALUE is assigned */
static SHELL_VAR *
bind_variable_internal (name, value, table, hflags, aflags)
const char *name;
HASH_TABLE *table;
int hflags, aflags;
{
- char *newval;
- SHELL_VAR *entry;
+ char *newval, *tname;
+ SHELL_VAR *entry, *tentry;
entry = (hflags & HASH_NOSRCH) ? (SHELL_VAR *)NULL : hash_lookup (name, table);
/* Follow the nameref chain here if this is the global variables table */
/* 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 */
+ entry = find_variable_last_nameref (name, 0); /* XXX */
if (entry == 0) /* just in case */
return (entry);
}
- /* The first clause handles `declare -n ref; ref=x;' */
+ /* The first clause handles `declare -n ref; ref=x;' or `declare -n ref;
+ declare -n ref' */
if (entry && invisible_p (entry) && nameref_p (entry))
- goto assign_value;
+ {
+ if ((aflags & ASS_FORCE) == 0 && value && valid_nameref_value (value, 0) == 0)
+ {
+ sh_invalidid (value);
+ return ((SHELL_VAR *)NULL);
+ }
+ goto assign_value;
+ }
else if (entry && nameref_p (entry))
{
- newval = nameref_cell (entry);
+ newval = nameref_cell (entry); /* XXX - newval can't be NULL here */
+ if (valid_nameref_value (newval, 0) == 0)
+ {
+ sh_invalidid (newval);
+ return ((SHELL_VAR *)NULL);
+ }
#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);
+ /* declare -n foo=x[2] ; foo=bar */
+ if (valid_array_reference (newval, 0))
+ {
+ tname = array_variable_name (newval, 0, (char **)0, (int *)0);
+ if (tname && (tentry = find_variable_noref (tname)) && nameref_p (tentry))
+ {
+ /* nameref variables can't be arrays */
+ internal_warning (_("%s: removing nameref attribute"), name_cell (tentry));
+ FREE (value_cell (tentry)); /* XXX - bash-4.3 compat */
+ var_setvalue (tentry, (char *)NULL);
+ VUNSETATTR (tentry, att_nameref);
+ }
+ free (tname);
+
+ /* 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;
+ }
else
#endif
- {
- entry = make_new_variable (newval, table);
- var_setvalue (entry, make_variable_value (entry, value, 0));
- }
+ {
+ entry = make_new_variable (newval, table);
+ var_setvalue (entry, make_variable_value (entry, value, aflags));
+ }
}
else if (entry == 0)
{
entry = make_new_variable (name, table);
- var_setvalue (entry, make_variable_value (entry, value, 0)); /* XXX */
+ var_setvalue (entry, make_variable_value (entry, value, aflags)); /* XXX */
}
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))
else
{
assign_value:
- if (readonly_p (entry) || noassign_p (entry))
+ if ((readonly_p (entry) && (aflags & ASS_FORCE) == 0) || noassign_p (entry))
{
if (readonly_p (entry))
- err_readonly (name);
+ err_readonly (name_cell (entry));
return (entry);
}
/* Variables which are bound are visible. */
VUNSETATTR (entry, att_invisible);
+ /* If we can optimize the assignment, do so and return. Right now, we
+ optimize appends to string variables. */
+ if (can_optimize_assignment (entry, value, aflags))
+ {
+ INVALIDATE_EXPORTSTR (entry);
+ optimized_assignment (entry, value, aflags);
+
+ if (mark_modified_vars)
+ VSETATTR (entry, att_exported);
+
+ if (exported_p (entry))
+ array_needs_making = 1;
+
+ return (entry);
+ }
+
#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 */
{
SHELL_VAR *v, *nv;
VAR_CONTEXT *vc, *nvc;
- int level;
if (shell_variables == 0)
create_variable_tables ();
shell_variables table. This allows sourced scripts to modify values
given to them in a temporary environment while modifying the variable
value that the caller sees. */
- if (temporary_env)
+ if (temporary_env && value) /* XXX - can value be null here? */
bind_tempenv_variable (name, value);
/* XXX -- handle local variables here. */
nvc = vc;
if (v && nameref_p (v))
{
+ /* This starts at the context where we found the nameref. If we
+ want to start the name resolution over again at the original
+ context, this is where we need to change it */
nv = find_variable_nameref_context (v, vc, &nvc);
if (nv == 0)
{
normal. */
if (nameref_cell (nv) == 0)
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, (array_eltstate_t *)0));
+ else
+#endif
return (bind_variable_internal (nameref_cell (nv), value, nvc->table, 0, flags));
}
+ else if (nv == &nameref_maxloop_value)
+ {
+ internal_warning (_("%s: circular name reference"), v->name);
+ return (bind_global_variable (v->name, value, flags));
+ }
else
v = nv;
}
+ else if (nv == &nameref_maxloop_value)
+ {
+ internal_warning (_("%s: circular name reference"), v->name);
+ return (bind_global_variable (v->name, value, flags));
+ }
else
v = nv;
}
char *value;
int flags;
{
- SHELL_VAR *v, *nv;
- VAR_CONTEXT *vc, *nvc;
- int level;
-
if (shell_variables == 0)
create_variable_tables ();
return (bind_variable_internal (name, value, global_variables->table, 0, flags));
}
+static SHELL_VAR *
+bind_invalid_envvar (name, value, flags)
+ const char *name;
+ char *value;
+ int flags;
+{
+ if (invalid_env == 0)
+ invalid_env = hash_create (64); /* XXX */
+ return (bind_variable_internal (name, value, invalid_env, HASH_NOSRCH, flags));
+}
+
/* Make VAR, a simple shell variable, have value VALUE. Once assigned a
value, variables are no longer invisible. This is a duplicate of part
of the internals of bind_variable. If the variable is exported, or
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
+ if ((aflags & (ASS_NAMEREF|ASS_FORCE)) == ASS_NAMEREF && check_selfref (name_cell (var), t, 0))
+ {
+ if (variable_context)
+ internal_warning (_("%s: circular name reference"), name_cell (var));
+ else
+ {
+ internal_error (_("%s: nameref variable self references not allowed"), name_cell (var));
+ free (t);
+ if (invis)
+ VSETATTR (var, att_invisible); /* XXX */
+ return ((SHELL_VAR *)NULL);
+ }
+ }
+ if ((aflags & ASS_NAMEREF) && (valid_nameref_value (t, 0) == 0))
{
free (t);
if (invis)
variable we set here, then turn it back on after binding as necessary. */
SHELL_VAR *
-bind_int_variable (lhs, rhs)
+bind_int_variable (lhs, rhs, flags)
char *lhs, *rhs;
+ 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))
+ /* 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, (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)
+ {
+ sh_invalidid (lhs);
+ return ((SHELL_VAR *)NULL);
}
else
#endif
#if defined (ARRAY_VARS)
if (isarr)
- v = assign_array_element (lhs, rhs, 0);
+ v = assign_array_element (lhs, rhs, flags, (array_eltstate_t *)0);
else if (implicitarray)
- v = bind_array_variable (lhs, 0, rhs, 0);
+ v = bind_array_variable (lhs, 0, rhs, 0); /* XXX - check on flags */
else
#endif
- v = bind_variable (lhs, rhs, 0);
+ v = bind_variable (lhs, rhs, 0); /* why not use bind_variable_value? */
- if (v && isint)
- VSETATTR (v, att_integer);
-
- VUNSETATTR (v, att_invisible);
+ if (v)
+ {
+ if (isint)
+ VSETATTR (v, att_integer);
+ VUNSETATTR (v, att_invisible);
+ }
+ if (v && nameref_p (v))
+ internal_warning (_("%s: assigning integer to name reference"), lhs);
+
return (v);
}
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));
+ return (bind_int_variable (var, p, flags));
}
/* Do a function binding to a variable. You pass the name and
#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.*/
+ information in addition to the command, into the FUNCTION_DEF hash table.
+ If (FLAGS & 1), overwrite any existing definition. If FLAGS == 0, leave
+ any existing definition alone. */
void
-bind_function_def (name, value)
+bind_function_def (name, value, flags)
const char *name;
FUNCTION_DEF *value;
+ int flags;
{
FUNCTION_DEF *entry;
BUCKET_CONTENTS *elt;
COMMAND *cmd;
entry = find_function_def (name);
- if (entry)
+ if (entry && (flags & 1))
{
dispose_function_def_contents (entry);
entry = copy_function_def_contents (value, entry);
}
+ else if (entry)
+ return;
else
{
cmd = value->command;
int flags;
{
int offset, aflags;
- char *name, *temp, *value;
+ char *name, *temp, *value, *newname;
SHELL_VAR *var;
const char *string;
aflags = 0;
offset = assignment (string, 0);
- name = savestring (string);
+ newname = name = savestring (string);
value = (char *)NULL;
if (name[offset] == '=')
aflags |= ASS_APPEND;
}
+ if (legal_identifier (name) == 0)
+ {
+ sh_invalidid (name);
+ free (name);
+ return (0);
+ }
+
var = find_variable (name);
+ if (var == 0)
+ {
+ var = find_variable_last_nameref (name, 1);
+ /* If we're assigning a value to a nameref variable in the temp
+ environment, and the value of the nameref is valid for assignment,
+ but the variable does not already exist, assign to the nameref
+ target and add the target to the temporary environment. This is
+ what ksh93 does */
+ /* We use 2 in the call to valid_nameref_value because we don't want
+ to allow array references here at all (newname will be used to
+ create a variable directly below) */
+ if (var && nameref_p (var) && valid_nameref_value (nameref_cell (var), 2))
+ {
+ newname = nameref_cell (var);
+ var = 0; /* don't use it for append */
+ }
+ }
+ else
+ newname = name_cell (var); /* no-op if not nameref */
+
if (var && (readonly_p (var) || noassign_p (var)))
{
if (readonly_p (var))
free (name);
return (0);
}
-
temp = name + offset + 1;
+
value = expand_assignment_string_to_string (temp, 0);
if (var && (aflags & ASS_APPEND))
{
+ if (value == 0)
+ {
+ value = (char *)xmalloc (1); /* like do_assignment_internal */
+ value[0] = '\0';
+ }
temp = make_variable_value (var, value, aflags);
FREE (value);
value = temp;
if (temporary_env == 0)
temporary_env = hash_create (TEMPENV_HASH_BUCKETS);
- var = hash_lookup (name, temporary_env);
+ var = hash_lookup (newname, temporary_env);
if (var == 0)
- var = make_new_variable (name, temporary_env);
+ var = make_new_variable (newname, temporary_env);
else
FREE (value_cell (var));
if (value == 0)
{
- value = (char *)xmalloc (1); /* like do_assignment_internal */
+ value = (char *)xmalloc (1); /* see above */
value[0] = '\0';
}
var->context = variable_context; /* XXX */
INVALIDATE_EXPORTSTR (var);
- var->exportstr = mk_env_string (name, value);
+ var->exportstr = mk_env_string (newname, value, 0);
array_needs_making = 1;
if (flags)
- stupidly_hack_special_variables (name);
+ {
+ 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,
return 0;
}
+/* Unbind the first instance of NAME, whether it's a nameref or not */
+int
+unbind_variable_noref (name)
+ const char *name;
+{
+ SHELL_VAR *v;
+
+ v = var_lookup (name, shell_variables);
+ if (v)
+ return makunbound (name, shell_variables);
+ 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;
+{
+ SHELL_VAR *v;
+
+ v = find_variable (name);
+ if (v && readonly_p (v))
+ {
+ internal_error (_("%s: cannot unset: readonly %s"), name, "variable");
+ return -2;
+ }
+ else if (v && non_unsettable_p (v))
+ {
+ internal_error (_("%s: cannot unset"), name);
+ return -2;
+ }
+ return (unbind_variable (name));
+}
+
/* Unset the shell function named NAME. */
int
unbind_func (name)
must be done so that if the variable is subsequently assigned a new
value inside the function, the `local' attribute is still present.
We also need to add it back into the correct hash table. */
- if (old_var && local_p (old_var) && variable_context == old_var->context)
+ if (old_var && local_p (old_var) &&
+ (old_var->context == variable_context || (localvar_unset && old_var->context < variable_context)))
{
if (nofree_p (old_var))
var_setvalue (old_var, (char *)NULL);
if (!entry) \
{ \
entry = bind_variable (name, "", 0); \
- if (!no_invisible_vars && entry) entry->attributes |= att_invisible; \
+ if (entry) entry->attributes |= att_invisible; \
} \
} \
while (0)
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;
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);
visible_array_vars (var)
SHELL_VAR *var;
{
- return (invisible_p (var) == 0 && array_p (var));
+ return (invisible_p (var) == 0 && (array_p (var) || assoc_p (var)));
}
SHELL_VAR **
char **tempvar_list;
int tvlist_ind;
+/* Take a variable from an assignment statement preceding a posix special
+ 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;
+{
+ SHELL_VAR *var, *v;
+ HASH_TABLE *binding_table;
+
+ var = (SHELL_VAR *)data;
+
+ /* Just like do_assignment_internal(). This makes assignments preceding
+ special builtins act like standalone assignment statements when in
+ posix mode, satisfying the posix requirement that this affect the
+ "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
+ if the variable is local, only that it's not global_variables->table */
+ binding_table = v->context ? shell_variables->table : global_variables->table;
+
+ /* global variables are no longer temporary and don't need propagating. */
+ if (v->context == 0)
+ var->attributes &= ~(att_tempvar|att_propagate);
+
+ if (v)
+ {
+ 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)
+ tempvar_list[tvlist_ind++] = savestring (var->name);
+
+ dispose_variable (var);
+}
+
/* Push the variable described by (SHELL_VAR *)DATA down to the next
- variable context from the temporary environment. */
+ variable context from the temporary environment. This can be called
+ from one context:
+ 1. propagate_temp_var: which is called to propagate variables in
+ assignments like `var=value declare -x var' to the surrounding
+ scope.
+
+ In this case, the variable should have the att_propagate flag set and
+ we can create variables in the current scope.
+*/
static void
push_temp_var (data)
PTR_T data;
{
if (shell_variables == global_variables)
/* shouldn't happen */
- binding_table = shell_variables->table = global_variables->table = hash_create (0);
+ binding_table = shell_variables->table = global_variables->table = hash_create (VARIABLES_HASH_BUCKETS);
else
binding_table = shell_variables->table = hash_create (TEMPENV_HASH_BUCKETS);
}
- v = bind_variable_internal (var->name, value_cell (var), binding_table, 0, 0);
+ v = bind_variable_internal (var->name, value_cell (var), binding_table, 0, ASS_FORCE|ASS_NOLONGJMP);
/* XXX - should we set the context here? It shouldn't matter because of how
- assign_in_env works, but might want to check. */
+ assign_in_env works, but we do it anyway. */
+ if (v)
+ v->context = shell_variables->scope;
+
if (binding_table == global_variables->table) /* XXX */
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;
}
- v->attributes |= var->attributes;
+ if (v)
+ v->attributes |= var->attributes;
if (find_special_var (var->name) >= 0)
tempvar_list[tvlist_ind++] = savestring (var->name);
dispose_variable (var);
}
+/* Take a variable described by DATA and push it to the surrounding scope if
+ the PROPAGATE attribute is set. That gets set by push_temp_var if we are
+ taking a variable like `var=value declare -x var' and propagating it to
+ the enclosing scope. */
static void
propagate_temp_var (data)
PTR_T data;
sh_free_func_t *pushf;
{
int i;
+ HASH_TABLE *disposer;
tempvar_list = strvec_create (HASH_ENTRIES (temporary_env) + 1);
tempvar_list[tvlist_ind = 0] = 0;
-
- hash_flush (temporary_env, pushf);
- hash_dispose (temporary_env);
+
+ disposer = temporary_env;
temporary_env = (HASH_TABLE *)NULL;
+ hash_flush (disposer, pushf);
+ hash_dispose (disposer);
+
tempvar_list[tvlist_ind] = 0;
array_needs_making = 1;
-#if 0
- sv_ifs ("IFS"); /* XXX here for now -- check setifs in assign_in_env */
-#endif
for (i = 0; i < tvlist_ind; i++)
stupidly_hack_special_variables (tempvar_list[i]);
}
/* Take all of the shell variables in the temporary environment HASH_TABLE
- and make shell variables from them at the current variable context. */
+ and make shell variables from them at the current variable context.
+ Right now, this is only called in Posix mode to implement the historical
+ accident of creating global variables from assignment statements preceding
+ special builtins, but we check in case this acquires another caller later. */
void
merge_temporary_env ()
+{
+ if (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 ()
+{
+ if (temporary_env)
+ {
+ hash_flush (temporary_env, free_variable_hash_data);
+ hash_dispose (temporary_env);
+ temporary_env = (HASH_TABLE *)NULL;
+ }
+}
+
/* **************************************************************** */
/* */
/* Creating and manipulating the environment */
/* **************************************************************** */
static inline char *
-mk_env_string (name, value)
+mk_env_string (name, value, attributes)
const char *name, *value;
+ int attributes;
{
- int name_len, value_len;
- char *p;
+ size_t name_len, value_len;
+ char *p, *q, *t;
+ int isfunc, isarray;
name_len = strlen (name);
value_len = STRLEN (value);
- p = (char *)xmalloc (2 + name_len + value_len);
- strcpy (p, name);
- p[name_len] = '=';
+
+ 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)
+ {
+ 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;
+ }
+#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);
+ memcpy (p, name, name_len);
+ q = p + name_len;
+ }
+
+ q[0] = '=';
if (value && *value)
- strcpy (p + name_len + 1, value);
+ {
+ if (isfunc)
+ {
+ t = dequote_escapes (value);
+ value_len = STRLEN (t);
+ memcpy (q + 1, t, value_len + 1);
+ free (t);
+ }
+ else
+ memcpy (q + 1, value, value_len + 1);
+ }
else
- p[name_len + 1] = '\0';
+ q[1] = '\0';
+
return (p);
}
}
#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;
list = strvec_create ((1 + strvec_len ((char **)vars)));
-#define USE_EXPORTSTR (value == var->exportstr)
-
for (i = 0, list_index = 0; var = vars[i]; i++)
{
#if defined (__CYGWIN__)
/* We don't use the exportstr stuff on Cygwin at all. */
INVALIDATE_EXPORTSTR (var);
#endif
+
+ /* If the value is generated dynamically, generate it here. */
+ if (regen_p (var) && var->dynamic_value)
+ {
+ var = (*(var->dynamic_value)) (var);
+ INVALIDATE_EXPORTSTR (var);
+ }
+
if (var->exportstr)
value = var->exportstr;
else if (function_p (var))
#if defined (ARRAY_VARS)
else if (array_p (var))
# if ARRAY_EXPORT
- value = array_to_assignment_string (array_cell (var));
+ value = array_to_assign (array_cell (var), 0);
# 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));
+# 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);
/* 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, var->attributes);
if (USE_EXPORTSTR == 0)
SAVE_EXPORTSTR (var, list[list_index]);
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
}
}
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)
{
register char **temp_array;
int new_size;
- VAR_CONTEXT *tcxt;
+ VAR_CONTEXT *tcxt, *icxt;
if (array_needs_making)
{
variables are not (yet) exported, this will always be big enough
for the exported variables and functions. */
new_size = n_shell_variables () + HASH_ENTRIES (shell_functions) + 1 +
- HASH_ENTRIES (temporary_env);
+ HASH_ENTRIES (temporary_env) + HASH_ENTRIES (invalid_env);
if (new_size > export_env_size)
{
export_env_size = new_size;
}
else
tcxt = shell_variables;
+
+ if (invalid_env)
+ {
+ icxt = new_var_context ((char *)NULL, 0);
+ icxt->table = invalid_env;
+ icxt->down = tcxt;
+ }
+ else
+ icxt = tcxt;
- temp_array = make_var_export_array (tcxt);
+ temp_array = make_var_export_array (icxt);
if (temp_array)
add_temp_array_to_env (temp_array, 0, 0);
+ if (icxt != tcxt)
+ free (icxt);
+
if (tcxt != shell_variables)
free (tcxt);
HASH_TABLE *tempvars;
{
VAR_CONTEXT *vc;
+ int posix_func_behavior;
+
+ /* As of IEEE Std 1003.1-2017, assignment statements preceding shell
+ 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. */
+ posix_func_behavior = 0;
vc = new_var_context (name, flags);
- vc->table = tempvars;
- if (tempvars)
+ /* Posix interp 1009, temporary assignments preceding function calls modify
+ the current environment *before* the command is executed. */
+ if (posix_func_behavior && (flags & VC_FUNCENV) && tempvars == temporary_env)
+ merge_temporary_env ();
+ else if (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;
}
return (shell_variables = vc);
}
-static void
-push_func_var (data)
- PTR_T data;
+/* 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 -- 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 -- isbltin == 0)
+
+ It takes variables out of a temporary environment hash table. We take the
+ variable in data.
+*/
+
+static inline void
+push_posix_tempvar_internal (var, isbltin)
+ SHELL_VAR *var;
+ int isbltin;
{
- SHELL_VAR *var, *v;
+ SHELL_VAR *v;
+ int posix_var_behavior;
- var = (SHELL_VAR *)data;
+ /* As of IEEE Std 1003.1-2017, assignment statements preceding shell
+ 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. */
+ posix_var_behavior = posixly_correct && isbltin;
+ v = 0;
- if (tempvar_p (var) && (posixly_correct || (var->attributes & att_propagate)))
+ if (local_p (var) && STREQ (var->name, "-"))
+ {
+ 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
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? */
+ shell_variables->table = hash_create (VARIABLES_HASH_BUCKETS);
v = bind_variable_internal (var->name, value_cell (var), shell_variables->table, 0, 0);
+ /* XXX - should we set v->context here? */
+ if (v)
+ v->context = shell_variables->scope;
if (shell_variables == global_variables)
var->attributes &= ~(att_tempvar|att_propagate);
else
shell_variables->flags |= VC_HASTMPVAR;
- v->attributes |= var->attributes;
+ if (v)
+ v->attributes |= var->attributes;
}
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);
}
+static void
+push_func_var (data)
+ PTR_T data;
+{
+ SHELL_VAR *var;
+
+ var = (SHELL_VAR *)data;
+ push_posix_tempvar_internal (var, 0);
+}
+
+static void
+push_builtin_var (data)
+ PTR_T data;
+{
+ SHELL_VAR *var;
+
+ var = (SHELL_VAR *)data;
+ push_posix_tempvar_internal (var, 1);
+}
+
/* Pop the top context off of VCXT and dispose of it, returning the rest of
the stack. */
void
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;
{
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 */
propagated, bind it in the previous scope before disposing it. */
/* XXX - This isn't exactly right, because all tempenv variables have the
export attribute set. */
-#if 0
- if (exported_p (var) || (var->attributes & att_propagate))
-#else
if (tempvar_p (var) && exported_p (var) && (var->attributes & att_propagate))
-#endif
{
var->attributes &= ~att_tempvar; /* XXX */
v = bind_variable_internal (var->name, value_cell (var), shell_variables->table, 0, 0);
if (shell_variables == global_variables)
var->attributes &= ~att_propagate;
- v->attributes |= var->attributes;
+ if (v)
+ {
+ v->attributes |= var->attributes;
+ v->context = shell_variables->scope;
+ }
}
else
stupidly_hack_special_variables (var->name); /* XXX */
dispose_variable (var);
}
+/* This is called to propagate variables in the temporary environment of a
+ special builtin (if IS_SPECIAL != 0) or exported variables that are the
+ result of a builtin like `source' or `command' that can operate on the
+ variables in its temporary environment. In the first case, we call
+ push_builtin_var, which does the right thing. */
void
pop_scope (is_special)
int is_special;
{
VAR_CONTEXT *vcxt, *ret;
+ int is_bltinenv;
vcxt = shell_variables;
if (vc_istempscope (vcxt) == 0)
internal_error (_("pop_scope: head of shell_variables not a temporary environment scope"));
return;
}
+ is_bltinenv = vc_isbltnenv (vcxt); /* XXX - for later */
ret = vcxt->down;
if (ret)
if (vcxt->table)
{
if (is_special)
- hash_flush (vcxt->table, push_func_var);
+ hash_flush (vcxt->table, push_builtin_var);
else
hash_flush (vcxt->table, push_exported_var);
hash_dispose (vcxt->table);
/* */
/* **************************************************************** */
-static WORD_LIST **dollar_arg_stack = (WORD_LIST **)NULL;
+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;
static int dollar_arg_stack_slots;
static int dollar_arg_stack_index;
-/* XXX - we might want to consider pushing and popping the `getopts' state
- when we modify the positional parameters. */
+/* Functions to manipulate dollar_vars array. Need to keep these in sync with
+ whatever remember_args() does. */
+static char **
+save_dollar_vars ()
+{
+ char **ret;
+ int i;
+
+ ret = strvec_create (10);
+ for (i = 1; i < 10; i++)
+ {
+ ret[i] = dollar_vars[i];
+ dollar_vars[i] = (char *)NULL;
+ }
+ return ret;
+}
+
+static void
+restore_dollar_vars (args)
+ char **args;
+{
+ int i;
+
+ for (i = 1; i < 10; i++)
+ dollar_vars[i] = args[i];
+}
+
+static void
+free_dollar_vars ()
+{
+ int i;
+
+ for (i = 1; i < 10; i++)
+ {
+ FREE (dollar_vars[i]);
+ dollar_vars[i] = (char *)NULL;
+ }
+}
+
+static void
+free_saved_dollar_vars (args)
+ char **args;
+{
+ int i;
+
+ for (i = 1; i < 10; i++)
+ 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)
char *name; /* function name */
{
if (dollar_arg_stack_index + 2 > dollar_arg_stack_slots)
{
- dollar_arg_stack = (WORD_LIST **)
+ dollar_arg_stack = (struct saved_dollar_vars *)
xrealloc (dollar_arg_stack, (dollar_arg_stack_slots += 10)
- * sizeof (WORD_LIST *));
+ * sizeof (struct saved_dollar_vars));
}
- dollar_arg_stack[dollar_arg_stack_index++] = list_rest_of_args ();
- dollar_arg_stack[dollar_arg_stack_index] = (WORD_LIST *)NULL;
+
+ 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;
}
/* Restore the positional parameters from our stack. */
void
pop_dollar_vars ()
{
- if (!dollar_arg_stack || dollar_arg_stack_index == 0)
+ if (dollar_arg_stack == 0 || dollar_arg_stack_index == 0)
return;
- remember_args (dollar_arg_stack[--dollar_arg_stack_index], 1);
- dispose_words (dollar_arg_stack[dollar_arg_stack_index]);
- dollar_arg_stack[dollar_arg_stack_index] = (WORD_LIST *)NULL;
+ /* 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 ();
}
void
dispose_saved_dollar_vars ()
{
- if (!dollar_arg_stack || dollar_arg_stack_index == 0)
+ if (dollar_arg_stack == 0 || dollar_arg_stack_index == 0)
return;
- dispose_words (dollar_arg_stack[dollar_arg_stack_index]);
- dollar_arg_stack[dollar_arg_stack_index] = (WORD_LIST *)NULL;
+ dispose_words (dollar_arg_stack[--dollar_arg_stack_index].rest);
+ free_saved_dollar_vars (dollar_arg_stack[dollar_arg_stack_index].first_ten);
+ free (dollar_arg_stack[dollar_arg_stack_index].first_ten);
+
+ 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
+ shell is initialized */
+void
+init_bash_argv ()
+{
+ if (bash_argv_initialized == 0)
+ {
+ save_bash_argv ();
+ bash_argv_initialized = 1;
+ }
+}
+
+void
+save_bash_argv ()
+{
+ WORD_LIST *list;
+
+ list = list_rest_of_args ();
+ push_args (list);
+ dispose_words (list);
}
/* Manipulate the special BASH_ARGV and BASH_ARGC variables. */
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;
*************************************************/
/* Extern declarations for variables this code has to manage. */
-extern int eof_encountered, eof_encountered_limit, ignoreeof;
-
-#if defined (READLINE)
-extern int hostname_list_initialized;
-#endif
/* An alist of name.function for each special variable. Most of the
functions don't do much, and in fact, this would be faster with a
{ "COMP_WORDBREAKS", sv_comp_wordbreaks },
#endif
+ { "EXECIGNORE", sv_execignore },
+
{ "FUNCNEST", sv_funcnest },
{ "GLOBIGNORE", sv_globignore },
funcnest_max = num;
}
+/* What to do when EXECIGNORE changes. */
+void
+sv_execignore (name)
+ char *name;
+{
+ setup_exec_ignore (name);
+}
+
/* What to do when GLOBIGNORE changes. */
void
sv_globignore (name)
return;
v = find_variable (name);
- if (v == 0 || var_isnull (v))
+ if (v == 0 || var_isset (v) == 0)
rl_reset_screen_size ();
else
{
else if (hmax >= 0) /* truncate HISTFILE if HISTFILESIZE >= 0 */
{
history_truncate_file (get_string_value ("HISTFILE"), hmax);
- if (hmax <= history_lines_in_file)
+ /* If we just shrank the history file to fewer lines than we've
+ already read, make sure we adjust our idea of how many lines
+ we have read from the file. */
+ if (hmax < history_lines_in_file)
history_lines_in_file = hmax;
}
}
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
eof_encountered = 0;
tmp_var = find_variable (name);
- ignoreeof = tmp_var != 0;
+ ignoreeof = tmp_var && var_isset (tmp_var);
temp = tmp_var ? value_cell (tmp_var) : (char *)NULL;
if (temp)
eof_encountered_limit = (*temp && all_digits (temp)) ? atoi (temp) : 10;
sv_optind (name)
char *name;
{
+ SHELL_VAR *var;
char *tt;
int s;
- tt = get_string_value ("OPTIND");
+ var = find_variable ("OPTIND");
+ tt = var ? get_variable_value (var) : (char *)NULL;
+
+ /* Assume that if var->context < variable_context and variable_context > 0
+ then we are restoring the variables's previous state while returning
+ from a function. */
if (tt && *tt)
{
s = atoi (tt);
sv_strict_posix (name)
char *name;
{
- SET_INT_VAR (name, posixly_correct);
+ SHELL_VAR *var;
+
+ var = find_variable (name);
+ posixly_correct = var && var_isset (var);
posix_initialize (posixly_correct);
#if defined (READLINE)
if (interactive_shell)
#if 1
if (r == 0 && posixly_correct)
- last_command_exit_value = 1;
+ set_exit_status (EXECUTION_FAILURE);
#endif
}
/* Fast case */
if (array_num_elements (a) == nproc && nproc == 1)
{
+#ifndef ALT_ARRAY_IMPLEMENTATION
ae = element_forw (a->head);
- free (element_value (ae));
- ae->value = 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));
- ae->value = itos (ps[i]);
+#else
+ ae = a->elements[i];
+#endif
+ ARRAY_ELEMENT_REPLACE (ae, itos (ps[i]));
}
/* add any more */
for ( ; i < nproc; i++)
}
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 */
}
}
save_pipestatus_array ()
{
SHELL_VAR *v;
- ARRAY *a, *a2;
+ ARRAY *a;
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));
+ a = array_copy (array_cell (v));
- return a2;
+ return a;
}
void
return;
}
/* Handle decimal-like compatibility version specifications: 4.2 */
- if (isdigit (val[0]) && val[1] == '.' && isdigit (val[2]) && val[3] == 0)
+ 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)
+ else if (ISDIGIT (val[0]) && ISDIGIT (val[1]) && val[2] == 0)
{
tens = val[0] - '0';
ones = val[1] - '0';