/* variables.c -- Functions for hacking shell variables. */
-/* Copyright (C) 1987-2009 Free Software Foundation, Inc.
+/* Copyright (C) 1987-2016 Free Software Foundation, Inc.
This file is part of GNU Bash, the Bourne Again SHell.
#include "bashansi.h"
#include "bashintl.h"
+#define NEED_XTRACE_SET_DECL
+
#include "shell.h"
#include "flags.h"
#include "execute_cmd.h"
#include "hashcmd.h"
#include "pathexp.h"
#include "alias.h"
+#include "jobs.h"
+
+#include "version.h"
#include "builtins/getopt.h"
#include "builtins/common.h"
+#include "builtins/builtext.h"
#if defined (READLINE)
# include "bashline.h"
# include "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) */
+
+/* flags for find_variable_internal */
+
+#define FV_FORCETEMPENV 0x01
+#define FV_SKIPINVISIBLE 0x02
extern char **environ;
/* Variables used here and defined in other files. */
extern int posixly_correct;
-extern int line_number;
+extern int line_number, line_number_base;
extern int subshell_environment, indirection_level, subshell_level;
extern int build_version, patch_level;
extern int expanding_redir;
+extern int last_command_exit_value;
extern char *dist_version, *release_status;
extern char *shell_name;
extern char *primary_prompt, *secondary_prompt;
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;
#if defined (READLINE)
static int winsize_assignment; /* currently assigning to LINES or COLUMNS */
-static int winsize_assigned; /* assigned 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 */
+
/* Some forward declarations. */
static void create_variable_tables __P((void));
static SHELL_VAR *build_hashcmd __P((SHELL_VAR *));
static SHELL_VAR *get_hashcmd __P((SHELL_VAR *));
static SHELL_VAR *assign_hashcmd __P((SHELL_VAR *, char *, arrayind_t, char *));
+# if defined (ALIAS)
static SHELL_VAR *build_aliasvar __P((SHELL_VAR *));
static SHELL_VAR *get_aliasvar __P((SHELL_VAR *));
static SHELL_VAR *assign_aliasvar __P((SHELL_VAR *, char *, arrayind_t, char *));
+# endif
#endif
static SHELL_VAR *get_funcname __P((SHELL_VAR *));
static int visible_var __P((SHELL_VAR *));
static int visible_and_exported __P((SHELL_VAR *));
+static int export_environment_candidate __P((SHELL_VAR *));
static int local_and_exported __P((SHELL_VAR *));
static int variable_in_context __P((SHELL_VAR *));
#if defined (ARRAY_VARS)
static int visible_array_vars __P((SHELL_VAR *));
#endif
+static SHELL_VAR *find_variable_internal __P((const char *, int));
+
+static SHELL_VAR *find_nameref_at_context __P((SHELL_VAR *, VAR_CONTEXT *));
+static SHELL_VAR *find_variable_nameref_context __P((SHELL_VAR *, VAR_CONTEXT *, VAR_CONTEXT **));
+static SHELL_VAR *find_variable_last_nameref_context __P((SHELL_VAR *, VAR_CONTEXT *, VAR_CONTEXT **));
+
static SHELL_VAR *bind_tempenv_variable __P((const char *, char *));
static void push_temp_var __P((PTR_T));
static void propagate_temp_var __P((PTR_T));
static void dispose_temporary_env __P((sh_free_func_t *));
-static inline char *mk_env_string __P((const char *, const char *));
+static inline char *mk_env_string __P((const char *, const char *, int));
static char **make_env_array_from_var_list __P((SHELL_VAR **));
static char **make_var_export_array __P((VAR_CONTEXT *));
static char **make_func_export_array __P((void));
{
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
}
int privmode;
{
char *name, *string, *temp_string;
- int c, char_index, string_index, string_length;
+ int c, char_index, string_index, string_length, ro;
SHELL_VAR *temp_var;
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 */
- parse_and_execute (temp_string, name, SEVAL_NONINT|SEVAL_NOHIST);
+ string_length = strlen (string);
+ temp_string = (char *)xmalloc (namelen + string_length + 2);
- /* 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';
+ memcpy (temp_string, tname, namelen);
+ temp_string[namelen] = ' ';
+ memcpy (temp_string + namelen + 1, string, string_length + 1);
+
+ /* Don't import function names that are invalid identifiers from the
+ environment 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
- report_error (_("error importing function definition for `%s'"), name);
+ {
+ if (temp_var = bind_variable (name, string, 0))
+ {
+ VSETATTR (temp_var, (att_exported | att_imported | att_invisible));
+ array_needs_making = 1;
+ }
+ last_command_exit_value = 1;
+ report_error (_("error importing function definition for `%s'"), tname);
+ }
- /* ( */
- if (name[char_index - 1] == ')' && name[char_index - 2] == '\0')
- name[char_index - 2] = '('; /* ) */
+ /* Restore original suffix */
+ tname[namelen] = BASHFUNC_SUFFIX[0];
}
+ else
+#endif /* FUNCTION_IMPORT */
#if defined (ARRAY_VARS)
-# if 0
+# if ARRAY_EXPORT
/* Array variables may not yet be exported. */
- else if (*string == '(' && string[1] == '[' && string[strlen (string) - 1] == ')')
+ if (*string == '(' && string[1] == '[' && string[strlen (string) - 1] == ')')
{
string_length = 1;
temp_string = extract_array_assignment_list (string, &string_length);
- temp_var = assign_array_from_string (name, temp_string);
+ temp_var = assign_array_from_string (name, temp_string, 0);
FREE (temp_string);
VSETATTR (temp_var, (att_exported | att_imported));
array_needs_making = 1;
}
-# endif
+ else
+# endif /* ARRAY_EXPORT */
#endif
- else if (legal_identifier (name))
{
+ ro = 0;
+ if (posixly_correct && STREQ (name, "SHELLOPTS"))
+ {
+ temp_var = find_variable ("SHELLOPTS");
+ ro = temp_var && readonly_p (temp_var);
+ if (temp_var)
+ VUNSETATTR (temp_var, att_readonly);
+ }
temp_var = bind_variable (name, string, 0);
- VSETATTR (temp_var, (att_exported | att_imported));
- array_needs_making = 1;
+ if (temp_var)
+ {
+ if (legal_identifier (name))
+ VSETATTR (temp_var, (att_exported | att_imported));
+ else
+ VSETATTR (temp_var, (att_exported | att_imported | att_invisible));
+ if (ro)
+ VSETATTR (temp_var, att_readonly);
+ array_needs_making = 1;
+ }
}
name[char_index] = '=';
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_if_not ("HISTFILE", name);
free (name);
-
-#if 0
- set_if_not ("HISTSIZE", "500");
- sv_histsize ("HISTSIZE");
-#endif
}
#endif /* HISTORY */
/* Get the user's real and effective user ids. */
uidset ();
+ temp_var = find_variable ("BASH_XTRACEFD");
+ if (temp_var && imported_p (temp_var))
+ sv_xtracefd (temp_var->name);
+
+ 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);
+ 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 (temp_var == 0 || value_cell (temp_var) == 0 || file_isdir (value_cell (temp_var)) == 0)
+ {
+ 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);
strcpy (d, dist_version);
- s = xstrchr (d, '.');
+ s = strchr (d, '.');
if (s)
*s++ = '\0';
array_insert (av, 0, d);
}
#if defined (ARRAY_VARS)
-/* A generic dynamic array variable initializer. Intialize array variable
+/* A generic dynamic array variable initializer. Initialize array variable
NAME with dynamic value function GETFUNC and assignment function SETFUNC. */
static SHELL_VAR *
init_dynamic_array_var (name, getfunc, setfunc, attrs)
static int
brand ()
{
-#if 0
- rseed = rseed * 1103515245 + 12345;
- return ((unsigned int)((rseed >> 16) & 32767)); /* was % 32768 */
-#else
/* 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)
- seedrand ();
+ rseed = 123459876;
h = rseed / 127773;
l = rseed % 127773;
rseed = 16807 * l - 2836 * h;
rseed += 0x7fffffff;
#endif
return ((unsigned int)(rseed & 32767)); /* was % 32768 */
-#endif
}
/* Set the random number generator seed to SEED. */
if (value == 0 || *value == '\0' || legal_number (value, &new_value) == 0)
new_value = 0;
- line_number = new_value;
+ line_number = line_number_base = new_value;
return var;
}
arrayind_t ind;
char *key;
{
+#if defined (RESTRICTED_SHELL)
+ if (restricted && strchr (value, '/'))
+ {
+ sh_restricted (value);
+ return (SHELL_VAR *)NULL;
+ }
+#endif
phash_insert (key, value, 0, 0);
return (build_hashcmd (self));
}
+#if defined (ALIAS)
static SHELL_VAR *
build_aliasvar (self)
SHELL_VAR *self;
add_alias (key, value);
return (build_aliasvar (self));
}
+#endif /* ALIAS */
+
#endif /* ARRAY_VARS */
/* If ARRAY_VARS is not defined, this just returns the name of any
v = init_dynamic_array_var ("BASH_LINENO", get_self, null_array_assign, att_noassign|att_nounset);
v = init_dynamic_assoc_var ("BASH_CMDS", get_hashcmd, assign_hashcmd, att_nofree);
+# if defined (ALIAS)
v = init_dynamic_assoc_var ("BASH_ALIASES", get_aliasvar, assign_aliasvar, att_nofree);
+# endif
#endif
v = init_funcname_var ();
BUCKET_CONTENTS *bucket;
bucket = hash_search (name, hashed_vars, 0);
+ /* If we find the name in HASHED_VARS, set LAST_TABLE_SEARCHED to that
+ table. */
+ if (bucket)
+ last_table_searched = hashed_vars;
return (bucket ? (SHELL_VAR *)bucket->data : (SHELL_VAR *)NULL);
}
*/
SHELL_VAR *
-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
var = hash_lookup (name, temporary_env);
if (var == 0)
- var = var_lookup (name, shell_variables);
+ {
+ 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);
+
+ return (var->dynamic_value ? (*(var->dynamic_value)) (var) : var);
+}
+
+/* Look up and resolve the chain of nameref variables starting at V all the
+ way to NULL or non-nameref. */
+SHELL_VAR *
+find_variable_nameref (v)
+ SHELL_VAR *v;
+{
+ int level, flags;
+ char *newname, *t;
+ SHELL_VAR *orig, *oldv;
+
+ level = 0;
+ orig = v;
+ while (v && nameref_p (v))
+ {
+ level++;
+ if (level > NAMEREF_MAX)
+ return ((SHELL_VAR *)0); /* error message here? */
+ newname = nameref_cell (v);
+ if (newname == 0 || *newname == '\0')
+ return ((SHELL_VAR *)0);
+ oldv = v;
+ 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);
+ return ((SHELL_VAR *)0);
+ }
+ }
+ return v;
+}
+
+/* Resolve the chain of nameref variables for NAME. XXX - could change later */
+SHELL_VAR *
+find_variable_last_nameref (name, vflags)
+ const char *name;
+ int vflags;
+{
+ SHELL_VAR *v, *nv;
+ char *newname;
+ int level, flags;
+
+ nv = v = find_variable_noref (name);
+ level = 0;
+ while (v && nameref_p (v))
+ {
+ level++;
+ if (level > NAMEREF_MAX)
+ return ((SHELL_VAR *)0); /* error message here? */
+ newname = nameref_cell (v);
+ if (newname == 0 || *newname == '\0')
+ return ((vflags && invisible_p (v)) ? v : (SHELL_VAR *)0);
+ nv = v;
+ 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, vflags)
+ const char *name;
+ int vflags;
+{
+ SHELL_VAR *v, *nv;
+ char *newname;
+ int level;
+
+ nv = v = find_global_variable_noref (name);
+ level = 0;
+ while (v && nameref_p (v))
+ {
+ level++;
+ if (level > NAMEREF_MAX)
+ return ((SHELL_VAR *)0); /* error message here? */
+ newname = nameref_cell (v);
+ if (newname == 0 || *newname == '\0')
+ return ((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;
+}
+
+static SHELL_VAR *
+find_nameref_at_context (v, vc)
+ SHELL_VAR *v;
+ VAR_CONTEXT *vc;
+{
+ SHELL_VAR *nv, *nv2;
+ VAR_CONTEXT *nvc;
+ char *newname;
+ int level;
+
+ nv = v;
+ level = 1;
+ while (nv && nameref_p (nv))
+ {
+ level++;
+ if (level > NAMEREF_MAX)
+ return (&nameref_maxloop_value);
+ newname = nameref_cell (nv);
+ if (newname == 0 || *newname == '\0')
+ return ((SHELL_VAR *)NULL);
+ nv2 = hash_lookup (newname, vc->table);
+ if (nv2 == 0)
+ break;
+ nv = nv2;
+ }
+ return nv;
+}
+
+/* Do nameref resolution from the VC, which is the local context for some
+ function or builtin, `up' the chain to the global variables context. If
+ NVCP is not NULL, return the variable context where we finally ended the
+ nameref resolution (so the bind_variable_internal can use the correct
+ variable context and hash table). */
+static SHELL_VAR *
+find_variable_nameref_context (v, vc, nvcp)
+ SHELL_VAR *v;
+ VAR_CONTEXT *vc;
+ VAR_CONTEXT **nvcp;
+{
+ SHELL_VAR *nv, *nv2;
+ VAR_CONTEXT *nvc;
+
+ /* Look starting at the current context all the way `up' */
+ for (nv = v, nvc = vc; nvc; nvc = nvc->down)
+ {
+ nv2 = find_nameref_at_context (nv, nvc);
+ if (nv2 == &nameref_maxloop_value)
+ return (nv2); /* XXX */
+ if (nv2 == 0)
+ continue;
+ nv = nv2;
+ if (*nvcp)
+ *nvcp = nvc;
+ if (nameref_p (nv) == 0)
+ break;
+ }
+ return (nameref_p (nv) ? (SHELL_VAR *)NULL : nv);
+}
+
+/* Do nameref resolution from the VC, which is the local context for some
+ function or builtin, `up' the chain to the global variables context. If
+ NVCP is not NULL, return the variable context where we finally ended the
+ nameref resolution (so the bind_variable_internal can use the correct
+ variable context and hash table). */
+static SHELL_VAR *
+find_variable_last_nameref_context (v, vc, nvcp)
+ SHELL_VAR *v;
+ VAR_CONTEXT *vc;
+ VAR_CONTEXT **nvcp;
+{
+ SHELL_VAR *nv, *nv2;
+ VAR_CONTEXT *nvc;
+
+ /* Look starting at the current context all the way `up' */
+ for (nv = v, nvc = vc; nvc; nvc = nvc->down)
+ {
+ nv2 = find_nameref_at_context (nv, nvc);
+ if (nv2 == &nameref_maxloop_value)
+ return (nv2); /* XXX */
+ if (nv2 == 0)
+ continue;
+ nv = nv2;
+ if (*nvcp)
+ *nvcp = nvc;
+ }
+ 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);
+}
+
+/* Find a variable, forcing a search of the temporary environment first */
+SHELL_VAR *
+find_variable_tempenv (name)
+ const char *name;
+{
+ SHELL_VAR *var;
+
+ var = find_variable_internal (name, FV_FORCETEMPENV);
+ if (var && nameref_p (var))
+ var = find_variable_nameref (var);
+ return (var);
+}
+
+/* Find a variable, not forcing a search of the temporary environment first */
+SHELL_VAR *
+find_variable_notempenv (name)
+ const char *name;
+{
+ SHELL_VAR *var;
+
+ var = find_variable_internal (name, 0);
+ if (var && nameref_p (var))
+ var = find_variable_nameref (var);
+ return (var);
+}
+
+SHELL_VAR *
+find_global_variable (name)
+ const char *name;
+{
+ SHELL_VAR *var;
+
+ var = var_lookup (name, global_variables);
+ if (var && nameref_p (var))
+ var = find_variable_nameref (var);
+
+ if (var == 0)
+ return ((SHELL_VAR *)NULL);
+
+ return (var->dynamic_value ? (*(var->dynamic_value)) (var) : var);
+}
+
+SHELL_VAR *
+find_global_variable_noref (name)
+ const char *name;
+{
+ SHELL_VAR *var;
+
+ var = var_lookup (name, global_variables);
+
+ if (var == 0)
+ return ((SHELL_VAR *)NULL);
+
+ return (var->dynamic_value ? (*(var->dynamic_value)) (var) : var);
+}
+
+SHELL_VAR *
+find_shell_variable (name)
+ const char *name;
+{
+ SHELL_VAR *var;
+
+ var = var_lookup (name, shell_variables);
+ if (var && nameref_p (var))
+ var = find_variable_nameref (var);
if (var == 0)
return ((SHELL_VAR *)NULL);
find_variable (name)
const char *name;
{
- return (find_variable_internal (name, (expanding_redir == 0 && (assigning_in_environment || executing_builtin))));
+ 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;
+ 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;
+}
+
+SHELL_VAR *
+find_variable_noref (name)
+ const char *name;
+{
+ SHELL_VAR *v;
+ int flags;
+
+ flags = 0;
+ if (expanding_redir == 0 && (assigning_in_environment || executing_builtin))
+ flags |= FV_FORCETEMPENV;
+ v = find_variable_internal (name, flags);
+ return v;
}
/* Look up the function entry whose name matches STRING.
/* 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;
make_local_variable (name)
const char *name;
{
- SHELL_VAR *new_var, *old_var;
+ SHELL_VAR *new_var, *old_var, *old_ref;
VAR_CONTEXT *vc;
int was_tmpvar;
char *tmp_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
+ can assume that a temporary var in the same context appears in the same
+ VAR_CONTEXT and can safely be returned without creating a new variable
+ (which results in duplicate names in the same VAR_CONTEXT->table */
+ /* We can't just test tmpvar_p because variables in the temporary env given
+ to a shell function appear in the function's local variable VAR_CONTEXT
+ but retain their tempvar attribute. We want temporary variables that are
+ found in temporary_env, hence the test for last_table_searched, which is
+ set in hash_lookup and only (so far) checked here. */
+ if (was_tmpvar && old_var->context == variable_context && last_table_searched != temporary_env)
{
- VUNSETATTR (old_var, att_invisible);
+ VUNSETATTR (old_var, att_invisible); /* XXX */
return (old_var);
}
-
- was_tmpvar = old_var && tempvar_p (old_var);
if (was_tmpvar)
tmp_value = value_cell (old_var);
{
if (readonly_p (old_var))
sh_readonly (name);
- return ((SHELL_VAR *)NULL);
+ else if (noassign_p (old_var))
+ builtin_error (_("%s: variable may not be assigned value"), name);
+#if 0
+ /* Let noassign variables through with a warning */
+ if (readonly_p (old_var))
+#endif
+ return ((SHELL_VAR *)NULL);
}
if (old_var == 0)
/* If we found this variable in one of the temporary environments,
inherit its value. Watch to see if this causes problems with
- things like `x=4 local x'. */
+ things like `x=4 local x'. XXX - see above for temporary env
+ variables with the same context level as variable_context */
+ /* XXX - we should only do this if the variable is not an array. */
if (was_tmpvar)
var_setvalue (new_var, savestring (tmp_value));
if (ifsname (name))
setifs (new_var);
+ if (was_tmpvar == 0 && no_invisible_vars == 0)
+ VSETATTR (new_var, att_invisible); /* XXX */
return (new_var);
}
entry->attributes = 0;
/* Always assume variables are to be made at toplevel!
- make_local_variable has the responsibilty of changing the
+ make_local_variable has the responsibility of changing the
variable context. */
entry->context = 0;
}
SHELL_VAR *
-make_local_array_variable (name)
+make_local_array_variable (name, assoc_ok)
char *name;
+ int assoc_ok;
{
SHELL_VAR *var;
ARRAY *array;
var = make_local_variable (name);
- if (var == 0 || array_p (var))
+ if (var == 0 || array_p (var) || (assoc_ok && assoc_p (var)))
return var;
array = array_create ();
top_level_cleanup ();
jump_to_top_level (DISCARD);
}
+ /* This can be fooled if the variable's value changes while evaluating
+ `rval'. We can change it if we move the evaluation of lval to here. */
if (flags & ASS_APPEND)
rval += lval;
retval = itos (rval);
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 */
+ if (entry && nameref_p (entry) && (invisible_p (entry) == 0) && table == global_variables->table)
+ {
+ entry = find_global_variable (entry->name);
+ /* Let's see if we have a nameref referencing a variable that hasn't yet
+ been created. */
+ if (entry == 0)
+ entry = find_variable_last_nameref (name, 0); /* XXX */
+ if (entry == 0) /* just in case */
+ return (entry);
+ }
- if (entry == 0)
+ /* The first clause handles `declare -n ref; ref=x;' or `declare -n ref;
+ declare -n ref' */
+ if (entry && invisible_p (entry) && nameref_p (entry))
+ {
+ 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);
+#if defined (ARRAY_VARS)
+ /* declare -n foo=x[2] ; foo=bar */
+ if (valid_array_reference (newval, 0))
+ {
+ tname = array_variable_name (newval, (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);
+ /* XXX - should it be aflags? */
+ entry = assign_array_element (newval, make_variable_value (entry, value, 0), aflags|ASS_NAMEREF);
+ if (entry == 0)
+ return entry;
+ }
+ else
+#endif
+ {
+ entry = make_new_variable (newval, table);
+ var_setvalue (entry, make_variable_value (entry, value, 0));
+ }
+ }
+ else if (entry == 0)
{
entry = make_new_variable (name, table);
var_setvalue (entry, make_variable_value (entry, value, 0)); /* XXX */
{
INVALIDATE_EXPORTSTR (entry);
newval = (aflags & ASS_APPEND) ? make_variable_value (entry, value, aflags) : value;
- entry = (*(entry->assign_func)) (entry, newval, -1, 0);
+ if (assoc_p (entry))
+ entry = (*(entry->assign_func)) (entry, newval, -1, savestring ("0"));
+ else if (array_p (entry))
+ entry = (*(entry->assign_func)) (entry, newval, 0, 0);
+ else
+ entry = (*(entry->assign_func)) (entry, newval, -1, 0);
if (newval != value)
free (newval);
return (entry);
}
else
{
- if (readonly_p (entry) || noassign_p (entry))
+assign_value:
+ 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 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 */
/* If an existing array variable x is being assigned to with x=b or
`read x' or something of that nature, silently convert it to
x[0]=b or `read x[0]'. */
- if (array_p (entry))
+ if (assoc_p (entry))
{
- array_insert (array_cell (entry), 0, newval);
+ assoc_insert (assoc_cell (entry), savestring ("0"), newval);
free (newval);
}
- else if (assoc_p (entry))
+ else if (array_p (entry))
{
- assoc_insert (assoc_cell (entry), "0", newval);
+ array_insert (array_cell (entry), 0, newval);
free (newval);
}
else
char *value;
int flags;
{
- SHELL_VAR *v;
- VAR_CONTEXT *vc;
+ 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. */
if (vc_isfuncenv (vc) || vc_isbltnenv (vc))
{
v = hash_lookup (name, vc->table);
+ nvc = vc;
+ if (v && nameref_p (v))
+ {
+ nv = find_variable_nameref_context (v, vc, &nvc);
+ if (nv == 0)
+ {
+ nv = find_variable_last_nameref_context (v, vc, &nvc);
+ if (nv && nameref_p (nv))
+ {
+ /* If this nameref variable doesn't have a value yet,
+ set the value. Otherwise, assign using the value as
+ normal. */
+ if (nameref_cell (nv) == 0)
+ return (bind_variable_internal (nv->name, value, nvc->table, 0, flags));
+#if defined (ARRAY_VARS)
+ else if (valid_array_reference (nameref_cell (nv), 0))
+ return (assign_array_element (nameref_cell (nv), value, flags));
+ 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);
+#if 0
+ return (bind_variable_value (v, value, flags|ASS_NAMEREF));
+#else
+ v = 0; /* backwards compat */
+#endif
+ }
+ else
+ v = nv;
+ }
+ else if (nv == &nameref_maxloop_value)
+ {
+ internal_warning (_("%s: circular name reference"), v->name);
+#if 0
+ return (bind_variable_value (v, value, flags|ASS_NAMEREF));
+#else
+ v = 0; /* backwards compat */
+#endif
+ }
+ else
+ v = nv;
+ }
if (v)
- return (bind_variable_internal (name, value, vc->table, 0, flags));
+ return (bind_variable_internal (v->name, value, nvc->table, 0, flags));
}
}
+ /* bind_variable_internal will handle nameref resolution in this case */
+ return (bind_variable_internal (name, value, global_variables->table, 0, flags));
+}
+
+SHELL_VAR *
+bind_global_variable (name, value, flags)
+ const char *name;
+ char *value;
+ int flags;
+{
+ SHELL_VAR *v, *nv;
+ VAR_CONTEXT *vc, *nvc;
+ int level;
+
+ if (shell_variables == 0)
+ create_variable_tables ();
+
+ /* bind_variable_internal will handle nameref resolution in this case */
return (bind_variable_internal (name, value, global_variables->table, 0, flags));
}
int aflags;
{
char *t;
+ int invis;
+ invis = invisible_p (var);
VUNSETATTR (var, att_invisible);
if (var->assign_func)
else
{
t = make_variable_value (var, value, aflags);
+ 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)
+ VSETATTR (var, att_invisible); /* XXX */
+ return ((SHELL_VAR *)NULL);
+ }
FREE (value_cell (var));
var_setvalue (var, t);
}
char *lhs, *rhs;
{
register SHELL_VAR *v;
- int isint, isarr;
+ int isint, isarr, implicitarray;
- isint = isarr = 0;
+ isint = isarr = implicitarray = 0;
#if defined (ARRAY_VARS)
- if (valid_array_reference (lhs))
+ if (valid_array_reference (lhs, 0))
{
isarr = 1;
v = array_variable_part (lhs, (char **)0, (int *)0);
{
isint = integer_p (v);
VUNSETATTR (v, att_integer);
+#if defined (ARRAY_VARS)
+ if (array_p (v) && isarr == 0)
+ implicitarray = 1;
+#endif
}
#if defined (ARRAY_VARS)
if (isarr)
v = assign_array_element (lhs, rhs, 0);
+ else if (implicitarray)
+ v = bind_array_variable (lhs, 0, rhs, 0);
else
#endif
v = bind_variable (lhs, rhs, 0);
- if (isint)
- VSETATTR (v, att_integer);
+ 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);
}
responsible for moving the main temporary env to one of the other
temporary environments. The expansion code in subst.c calls this. */
int
-assign_in_env (word)
+assign_in_env (word, flags)
WORD_DESC *word;
+ int flags;
{
- int offset;
- char *name, *temp, *value;
+ int offset, aflags;
+ char *name, *temp, *value, *newname;
SHELL_VAR *var;
const char *string;
string = word->word;
+ aflags = 0;
offset = assignment (string, 0);
- name = savestring (string);
+ newname = name = savestring (string);
value = (char *)NULL;
if (name[offset] == '=')
{
name[offset] = 0;
- /* ignore the `+' when assigning temporary environment */
+ /* don't ignore the `+' when assigning temporary environment */
if (name[offset - 1] == '+')
- name[offset - 1] = '\0';
+ {
+ name[offset - 1] = '\0';
+ aflags |= ASS_APPEND;
+ }
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 */
+ if (var && nameref_p (var) && valid_nameref_value (nameref_cell (var), 1))
+ {
+ 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 (ifsname (name))
- setifs (var);
+ if (flags)
+ stupidly_hack_special_variables (newname);
if (echo_command_at_execute)
/* The Korn shell prints the `+ ' in front of assignment statements,
else if (assoc_p (var))
var_setassoc (copy, assoc_copy (assoc_cell (var)));
#endif
- else if (value_cell (var))
+ else if (nameref_cell (var)) /* XXX - nameref */
+ var_setref (copy, savestring (nameref_cell (var)));
+ else if (value_cell (var)) /* XXX - nameref */
var_setvalue (copy, savestring (value_cell (var)));
else
var_setvalue (copy, (char *)NULL);
else if (assoc_p (var))
assoc_dispose (assoc_cell (var));
#endif
+ else if (nameref_p (var))
+ FREE (nameref_cell (var));
else
FREE (value_cell (var));
}
free (var);
}
-/* Unset the shell variable referenced by NAME. */
+/* Unset the shell variable referenced by NAME. Unsetting a nameref variable
+ unsets the variable it resolves to but leaves the nameref alone. */
int
unbind_variable (name)
const char *name;
{
- return makunbound (name, shell_variables);
+ SHELL_VAR *v, *nv;
+ int r;
+
+ v = var_lookup (name, shell_variables);
+ nv = (v && nameref_p (v)) ? find_variable_nameref (v) : (SHELL_VAR *)NULL;
+
+ r = nv ? makunbound (nv->name, shell_variables) : makunbound (name, shell_variables);
+ return r;
+}
+
+/* Unbind NAME, where NAME is assumed to be a nameref variable */
+int
+unbind_nameref (name)
+ const char *name;
+{
+ SHELL_VAR *v;
+
+ v = var_lookup (name, shell_variables);
+ if (v && nameref_p (v))
+ return makunbound (name, shell_variables);
+ return 0;
+}
+
+/* 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
+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 -1;
+ }
+ return (unbind_variable (name));
}
/* Unset the shell function named NAME. */
}
#endif /* DEBUGGER */
+int
+delete_var (name, vc)
+ const char *name;
+ VAR_CONTEXT *vc;
+{
+ BUCKET_CONTENTS *elt;
+ SHELL_VAR *old_var;
+ VAR_CONTEXT *v;
+
+ for (elt = (BUCKET_CONTENTS *)NULL, v = vc; v; v = v->down)
+ if (elt = hash_remove (name, v->table, 0))
+ break;
+
+ if (elt == 0)
+ return (-1);
+
+ old_var = (SHELL_VAR *)elt->data;
+ free (elt->key);
+ free (elt);
+
+ dispose_variable (old_var);
+ return (0);
+}
+
/* Make the variable associated with NAME go away. HASH_LIST is the
hash table from which this variable should be deleted (either
shell_variables or shell_functions).
else if (assoc_p (old_var))
assoc_dispose (assoc_cell (old_var));
#endif
+ else if (nameref_p (old_var))
+ FREE (nameref_cell (old_var));
else
FREE (value_cell (old_var));
/* Reset the attributes. Preserve the export attribute if the variable
if (!entry) \
{ \
entry = bind_variable (name, "", 0); \
- if (!no_invisible_vars) entry->attributes |= att_invisible; \
+ if (!no_invisible_vars && entry) entry->attributes |= att_invisible; \
} \
} \
while (0)
return (invisible_p (var) == 0 && exported_p (var));
}
+/* Candidate variables for the export environment are either valid variables
+ with the export attribute or invalid variables inherited from the initial
+ environment and simply passed through. */
+static int
+export_environment_candidate (var)
+ SHELL_VAR *var;
+{
+ return (exported_p (var) && (invisible_p (var) == 0 || imported_p (var)));
+}
+
/* Return non-zero if VAR is a local variable in the current context and
is exported. */
static int
return (temporary_env ? hash_lookup (name, temporary_env) : (SHELL_VAR *)NULL);
}
+char **tempvar_list;
+int tvlist_ind;
+
/* Push the variable described by (SHELL_VAR *)DATA down to the next
variable context from the temporary environment. */
static void
{
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);
/* XXX - should we set the context here? It shouldn't matter because of how
assign_in_env works, but might want to check. */
if (binding_table == 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);
}
if (tempvar_p (var) && (var->attributes & att_propagate))
push_temp_var (data);
else
- dispose_variable (var);
+ {
+ if (find_special_var (var->name) >= 0)
+ tempvar_list[tvlist_ind++] = savestring (var->name);
+ dispose_variable (var);
+ }
}
/* Free the storage used in the hash table for temporary
environment variables. PUSHF is a function to be called
to free each hash table entry. It takes care of pushing variables
- to previous scopes if appropriate. */
+ to previous scopes if appropriate. PUSHF stores names of variables
+ that require special handling (e.g., IFS) on tempvar_list, so this
+ function can call stupidly_hack_special_variables on all the
+ variables in the list when the temporary hash table is destroyed. */
static void
dispose_temporary_env (pushf)
sh_free_func_t *pushf;
{
+ int i;
+
+ tempvar_list = strvec_create (HASH_ENTRIES (temporary_env) + 1);
+ tempvar_list[tvlist_ind = 0] = 0;
+
hash_flush (temporary_env, pushf);
hash_dispose (temporary_env);
temporary_env = (HASH_TABLE *)NULL;
+ tempvar_list[tvlist_ind] = 0;
+
array_needs_making = 1;
- sv_ifs ("IFS"); /* XXX here for now */
+#if 0
+ sv_ifs ("IFS"); /* XXX here for now -- check setifs in assign_in_env */
+#endif
+ for (i = 0; i < tvlist_ind; i++)
+ stupidly_hack_special_variables (tempvar_list[i]);
+
+ strvec_dispose (tempvar_list);
+ tempvar_list = 0;
+ tvlist_ind = 0;
}
void
dispose_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, isfunc)
const char *name, *value;
+ int isfunc;
{
- int name_len, value_len;
- char *p;
+ size_t name_len, value_len;
+ char *p, *q;
name_len = strlen (name);
value_len = STRLEN (value);
- p = (char *)xmalloc (2 + name_len + value_len);
- strcpy (p, name);
- p[name_len] = '=';
+
+ /* If we are exporting a shell function, construct the encoded function
+ name. */
+ if (isfunc && value)
+ {
+ p = (char *)xmalloc (BASHFUNC_PREFLEN + name_len + BASHFUNC_SUFFLEN + value_len + 2);
+ q = p;
+ memcpy (q, BASHFUNC_PREFIX, BASHFUNC_PREFLEN);
+ q += BASHFUNC_PREFLEN;
+ memcpy (q, name, name_len);
+ q += name_len;
+ memcpy (q, BASHFUNC_SUFFIX, BASHFUNC_SUFFLEN);
+ q += BASHFUNC_SUFFLEN;
+ }
+ else
+ {
+ p = (char *)xmalloc (2 + name_len + value_len);
+ memcpy (p, name, name_len);
+ q = p + name_len;
+ }
+
+ q[0] = '=';
if (value && *value)
- strcpy (p + name_len + 1, value);
+ memcpy (q + 1, value, value_len + 1);
else
- p[name_len + 1] = '\0';
+ q[1] = '\0';
+
return (p);
}
char *s;
s = v->exportstr;
+ if (s == 0)
+ {
+ internal_error (_("%s has null exportstr"), v->name);
+ return (0);
+ }
if (legal_variable_starter ((unsigned char)*s) == 0)
{
internal_error (_("invalid character %d in exportstr for %s"), *s, v->name);
value = named_function_string ((char *)NULL, function_cell (var), 0);
#if defined (ARRAY_VARS)
else if (array_p (var))
-# if 0
- value = array_to_assignment_string (array_cell (var));
+# if ARRAY_EXPORT
+ value = array_to_assign (array_cell (var), 0);
# else
continue; /* XXX array vars cannot yet be exported */
-# endif
+# endif /* ARRAY_EXPORT */
else if (assoc_p (var))
# if 0
- value = assoc_to_assignment_string (assoc_cell (var));
+ value = assoc_to_assign (assoc_cell (var), 0);
# else
continue; /* XXX associative array vars cannot yet be exported */
# endif
/* Gee, I'd like to get away with not using savestring() if we're
using the cached exportstr... */
list[list_index] = USE_EXPORTSTR ? savestring (value)
- : mk_env_string (var->name, value);
+ : mk_env_string (var->name, value, function_p (var));
if (USE_EXPORTSTR == 0)
SAVE_EXPORTSTR (var, list[list_index]);
char **list;
SHELL_VAR **vars;
+#if 0
vars = map_over (visible_and_exported, vcxt);
+#else
+ vars = map_over (export_environment_candidate, vcxt);
+#endif
if (vars == 0)
return (char **)NULL;
return n;
}
+int
+chkexport (name)
+ char *name;
+{
+ SHELL_VAR *v;
+
+ v = find_variable (name);
+ if (v && exported_p (v))
+ {
+ array_needs_making = 1;
+ maybe_make_export_env ();
+ return 1;
+ }
+ return 0;
+}
+
void
maybe_make_export_env ()
{
}
export_env[export_env_index = 0] = (char *)NULL;
- /* Make a dummy variable context from the temporary_env, stick it on
+ /* Make a dummy variable context from the temporary_env, stick it on
the front of shell_variables, call make_var_export_array on the
whole thing to flatten it, and convert the list of SHELL_VAR *s
to the form needed by the environment. */
update_export_env_inplace ("_=", 2, command_name);
}
-#if 0 /* UNUSED -- it caused too many problems */
-void
-put_gnu_argv_flags_into_env (pid, flags_string)
- intmax_t pid;
- char *flags_string;
-{
- char *dummy, *pbuf;
- int l, fl;
-
- pbuf = itos (pid);
- l = strlen (pbuf);
-
- fl = strlen (flags_string);
-
- dummy = (char *)xmalloc (l + fl + 30);
- dummy[0] = '_';
- strcpy (dummy + 1, pbuf);
- strcpy (dummy + 1 + l, "_GNU_nonoption_argv_flags_");
- dummy[l + 27] = '=';
- strcpy (dummy + l + 28, flags_string);
-
- free (pbuf);
-
- export_env = add_or_supercede_exported_var (dummy, 0);
-}
-#endif
-
/* **************************************************************** */
/* */
/* Managing variable contexts */
var = (SHELL_VAR *)data;
- if (tempvar_p (var) && (posixly_correct || (var->attributes & att_propagate)))
+ if (local_p (var) && STREQ (var->name, "-"))
+ set_current_options (value_cell (var));
+ else if (tempvar_p (var) && (posixly_correct || (var->attributes & att_propagate)))
{
+ /* Make sure we have a hash table to store the variable in while it is
+ being propagated down to the global variables table. Create one if
+ we have to */
+ if ((vc_isfuncenv (shell_variables) || vc_istempenv (shell_variables)) && shell_variables->table == 0)
+ shell_variables->table = hash_create (VARIABLES_HASH_BUCKETS);
/* XXX - should we set v->context here? */
v = bind_variable_internal (var->name, value_cell (var), shell_variables->table, 0, 0);
+#if defined (ARRAY_VARS)
+ if (array_p (var) || assoc_p (var))
+ {
+ FREE (value_cell (v));
+ if (array_p (var))
+ var_setarray (v, array_copy (array_cell (var)));
+ else
+ var_setassoc (v, assoc_copy (assoc_cell (var)));
+ }
+#endif
if (shell_variables == global_variables)
var->attributes &= ~(att_tempvar|att_propagate);
else
shell_variables->flags |= VC_HASTMPVAR;
- v->attributes |= var->attributes;
+ if (v)
+ v->attributes |= var->attributes;
}
else
stupidly_hack_special_variables (var->name); /* 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;
}
else
stupidly_hack_special_variables (var->name); /* XXX */
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. */
+/* XXX - should always be followed by remember_args () */
void
push_context (name, is_subshell, tempvars)
char *name; /* function name */
{
dollar_arg_stack = (WORD_LIST **)
xrealloc (dollar_arg_stack, (dollar_arg_stack_slots += 10)
- * sizeof (WORD_LIST **));
+ * sizeof (WORD_LIST *));
}
dollar_arg_stack[dollar_arg_stack_index++] = list_rest_of_args ();
dollar_arg_stack[dollar_arg_stack_index] = (WORD_LIST *)NULL;
};
static struct name_and_function special_vars[] = {
+ { "BASH_COMPAT", sv_shcompat },
+ { "BASH_XTRACEFD", sv_xtracefd },
+
+#if defined (JOB_CONTROL)
+ { "CHILD_MAX", sv_childmax },
+#endif
+
#if defined (READLINE)
# if defined (STRICT_POSIX)
{ "COLUMNS", sv_winsize },
{ "COMP_WORDBREAKS", sv_comp_wordbreaks },
#endif
+ { "EXECIGNORE", sv_execignore },
+
+ { "FUNCNEST", sv_funcnest },
+
{ "GLOBIGNORE", sv_globignore },
#if defined (HISTORY)
{ "TEXTDOMAIN", sv_locale },
{ "TEXTDOMAINDIR", sv_locale },
-#if defined (HAVE_TZSET) && defined (PROMPT_STRING_DECODE)
+#if defined (HAVE_TZSET)
{ "TZ", sv_tz },
#endif
}
}
+void
+sv_funcnest (name)
+ char *name;
+{
+ SHELL_VAR *v;
+ intmax_t num;
+
+ v = find_variable (name);
+ if (v == 0)
+ funcnest_max = 0;
+ else if (legal_number (value_cell (v), &num) == 0)
+ funcnest_max = 0;
+ else
+ funcnest_max = num;
+}
+
+/* What to do when 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
{
if (legal_number (value_cell (v), &xd) == 0)
return;
- winsize_assignment = winsize_assigned = 1;
+ winsize_assignment = 1;
d = xd; /* truncate */
if (name[0] == 'L') /* LINES */
rl_set_screen_size (d, -1);
if (legal_number (temp, &num))
{
hmax = num;
- if (name[4] == 'S')
+ if (hmax < 0 && name[4] == 'S')
+ unstifle_history (); /* unstifle history if HISTSIZE < 0 */
+ else if (name[4] == 'S')
{
stifle_history (hmax);
hmax = where_history ();
if (history_lines_this_session > hmax)
history_lines_this_session = hmax;
}
- else
+ 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;
}
}
{
SHELL_VAR *v;
- v = find_variable (name);
+ if (v = find_variable (name))
+ {
+ if (history_comment_char == 0)
+ history_comment_char = '#';
+ }
history_write_timestamps = (v != 0);
}
#endif /* HISTORY */
-#if defined (HAVE_TZSET) && defined (PROMPT_STRING_DECODE)
+#if defined (HAVE_TZSET)
void
sv_tz (name)
char *name;
{
- tzset ();
+ if (chkexport (name))
+ tzset ();
}
#endif
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)
char *name;
{
char *v;
+ int r;
v = get_string_value (name);
if (name[0] == 'L' && name[1] == 'A') /* LANG */
- set_lang (name, v);
+ r = set_lang (name, v);
else
- set_locale_var (name, v); /* LC_*, TEXTDOMAIN* */
+ r = set_locale_var (name, v); /* LC_*, TEXTDOMAIN* */
+
+#if 1
+ if (r == 0 && posixly_correct)
+ last_command_exit_value = 1;
+#endif
}
#if defined (ARRAY_VARS)
}
}
}
+
+ARRAY *
+save_pipestatus_array ()
+{
+ SHELL_VAR *v;
+ ARRAY *a, *a2;
+
+ v = find_variable ("PIPESTATUS");
+ if (v == 0 || array_p (v) == 0 || array_cell (v) == 0)
+ return ((ARRAY *)NULL);
+
+ a = array_cell (v);
+ a2 = array_copy (array_cell (v));
+
+ return a2;
+}
+
+void
+restore_pipestatus_array (a)
+ ARRAY *a;
+{
+ SHELL_VAR *v;
+ ARRAY *a2;
+
+ v = find_variable ("PIPESTATUS");
+ /* XXX - should we still assign even if existing value is NULL? */
+ if (v == 0 || array_p (v) == 0 || array_cell (v) == 0)
+ return;
+
+ a2 = array_cell (v);
+ var_setarray (v, a);
+
+ array_dispose (a2);
+}
#endif
void
set_pipestatus_array (v, 1);
#endif
}
+
+void
+sv_xtracefd (name)
+ char *name;
+{
+ SHELL_VAR *v;
+ char *t, *e;
+ int fd;
+ FILE *fp;
+
+ v = find_variable (name);
+ if (v == 0)
+ {
+ xtrace_reset ();
+ return;
+ }
+
+ t = value_cell (v);
+ if (t == 0 || *t == 0)
+ xtrace_reset ();
+ else
+ {
+ fd = (int)strtol (t, &e, 10);
+ if (e != t && *e == '\0' && sh_validfd (fd))
+ {
+ fp = fdopen (fd, "w");
+ if (fp == 0)
+ internal_error (_("%s: %s: cannot open as FILE"), name, value_cell (v));
+ else
+ xtrace_set (fd, fp);
+ }
+ else
+ internal_error (_("%s: %s: invalid value for trace file descriptor"), name, value_cell (v));
+ }
+}
+
+#define MIN_COMPAT_LEVEL 31
+
+void
+sv_shcompat (name)
+ char *name;
+{
+ SHELL_VAR *v;
+ char *val;
+ int tens, ones, compatval;
+
+ v = find_variable (name);
+ if (v == 0)
+ {
+ shell_compatibility_level = DEFAULT_COMPAT_LEVEL;
+ set_compatibility_opts ();
+ return;
+ }
+ val = value_cell (v);
+ if (val == 0 || *val == '\0')
+ {
+ shell_compatibility_level = DEFAULT_COMPAT_LEVEL;
+ set_compatibility_opts ();
+ return;
+ }
+ /* Handle decimal-like compatibility version specifications: 4.2 */
+ if (ISDIGIT (val[0]) && val[1] == '.' && ISDIGIT (val[2]) && val[3] == 0)
+ {
+ tens = val[0] - '0';
+ ones = val[2] - '0';
+ compatval = tens*10 + ones;
+ }
+ /* Handle integer-like compatibility version specifications: 42 */
+ else if (ISDIGIT (val[0]) && ISDIGIT (val[1]) && val[2] == 0)
+ {
+ tens = val[0] - '0';
+ ones = val[1] - '0';
+ compatval = tens*10 + ones;
+ }
+ else
+ {
+compat_error:
+ internal_error (_("%s: %s: compatibility value out of range"), name, val);
+ shell_compatibility_level = DEFAULT_COMPAT_LEVEL;
+ set_compatibility_opts ();
+ return;
+ }
+
+ if (compatval < MIN_COMPAT_LEVEL || compatval > DEFAULT_COMPAT_LEVEL)
+ goto compat_error;
+
+ shell_compatibility_level = compatval;
+ set_compatibility_opts ();
+}
+
+#if defined (JOB_CONTROL)
+void
+sv_childmax (name)
+ char *name;
+{
+ char *tt;
+ int s;
+
+ tt = get_string_value (name);
+ s = (tt && *tt) ? atoi (tt) : 0;
+ set_maxchild (s);
+}
+#endif