/* variables.c -- Functions for hacking shell variables. */
-/* Copyright (C) 1987-2009 Free Software Foundation, Inc.
+/* Copyright (C) 1987-2013 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"
/* 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
+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_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));
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 ();
temp_string[char_index] = ' ';
strcpy (temp_string + char_index + 1, string);
- parse_and_execute (temp_string, name, SEVAL_NONINT|SEVAL_NOHIST);
+ if (posixly_correct == 0 || legal_identifier (name))
+ parse_and_execute (temp_string, name, SEVAL_NONINT|SEVAL_NOHIST);
/* Ancient backwards compatibility. Old versions of bash exported
functions like name()=() {...} */
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'"), name);
+ }
/* ( */
if (name[char_index - 1] == ')' && name[char_index - 2] == '\0')
name[char_index - 2] = '('; /* ) */
}
#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] == ')')
{
VSETATTR (temp_var, (att_exported | att_imported));
array_needs_making = 1;
}
-# endif
+# endif /* ARRAY_EXPORT */
#endif
+#if 0
else if (legal_identifier (name))
+#else
+ else
+#endif
{
+ 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] = '=';
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);
+
/* Initialize the dynamic variables, and seed their values. */
initialize_dynamic_variables ();
}
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;
}
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 *var;
int search_tempenv;
+ VAR_CONTEXT *vc;
var = (SHELL_VAR *)NULL;
if (search_tempenv && temporary_env)
var = hash_lookup (name, temporary_env);
+ vc = shell_variables;
+#if 0
+if (search_tempenv == 0 && /* (subshell_environment & SUBSHELL_COMSUB) && */
+ expanding_redir &&
+ (this_shell_builtin == eval_builtin || this_shell_builtin == command_builtin))
+ {
+ itrace("find_variable_internal: search_tempenv == 0: skipping VC_BLTNENV");
+ while (vc && (vc->flags & VC_BLTNENV))
+ vc = vc->down;
+ if (vc == 0)
+ vc = shell_variables;
+ }
+#endif
+
if (var == 0)
- var = var_lookup (name, shell_variables);
+ var = var_lookup (name, vc);
+
+ if (var == 0)
+ return ((SHELL_VAR *)NULL);
+
+ return (var->dynamic_value ? (*(var->dynamic_value)) (var) : var);
+}
+
+/* Look up and resolve the chain of nameref variables starting at V all the
+ way to NULL or non-nameref. */
+SHELL_VAR *
+find_variable_nameref (v)
+ SHELL_VAR *v;
+{
+ int level;
+ char *newname;
+ SHELL_VAR *orig, *oldv;
+
+ level = 0;
+ orig = v;
+ while (v && nameref_p (v))
+ {
+ level++;
+ if (level > NAMEREF_MAX)
+ return ((SHELL_VAR *)0); /* error message here? */
+ newname = nameref_cell (v);
+ if (newname == 0 || *newname == '\0')
+ return ((SHELL_VAR *)0);
+ oldv = v;
+ v = find_variable_internal (newname, (expanding_redir == 0 && (assigning_in_environment || executing_builtin)));
+ if (v == orig || v == oldv)
+ {
+ internal_warning (_("%s: circular name reference"), orig->name);
+ return ((SHELL_VAR *)0);
+ }
+ }
+ return v;
+}
+
+/* Resolve the chain of nameref variables for NAME. XXX - could change later */
+SHELL_VAR *
+find_variable_last_nameref (name)
+ const char *name;
+{
+ SHELL_VAR *v, *nv;
+ char *newname;
+ int level;
+
+ nv = v = find_variable_noref (name);
+ level = 0;
+ while (v && nameref_p (v))
+ {
+ level++;
+ if (level > NAMEREF_MAX)
+ return ((SHELL_VAR *)0); /* error message here? */
+ newname = nameref_cell (v);
+ if (newname == 0 || *newname == '\0')
+ return ((SHELL_VAR *)0);
+ nv = v;
+ v = find_variable_internal (newname, (expanding_redir == 0 && (assigning_in_environment || executing_builtin)));
+ }
+ return nv;
+}
+
+/* Resolve the chain of nameref variables for NAME. XXX - could change later */
+SHELL_VAR *
+find_global_variable_last_nameref (name)
+ const char *name;
+{
+ SHELL_VAR *v, *nv;
+ char *newname;
+ int level;
+
+ nv = v = find_global_variable_noref (name);
+ level = 0;
+ while (v && nameref_p (v))
+ {
+ level++;
+ if (level > NAMEREF_MAX)
+ return ((SHELL_VAR *)0); /* error message here? */
+ newname = nameref_cell (v);
+ if (newname == 0 || *newname == '\0')
+ return ((SHELL_VAR *)0);
+ nv = v;
+ v = find_global_variable_noref (newname);
+ }
+ return nv;
+}
+
+static SHELL_VAR *
+find_nameref_at_context (v, vc)
+ SHELL_VAR *v;
+ VAR_CONTEXT *vc;
+{
+ SHELL_VAR *nv, *nv2;
+ VAR_CONTEXT *nvc;
+ char *newname;
+ int level;
+
+ nv = v;
+ level = 1;
+ while (nv && nameref_p (nv))
+ {
+ level++;
+ if (level > NAMEREF_MAX)
+ return ((SHELL_VAR *)NULL);
+ newname = nameref_cell (nv);
+ if (newname == 0 || *newname == '\0')
+ return ((SHELL_VAR *)NULL);
+ nv2 = hash_lookup (newname, vc->table);
+ if (nv2 == 0)
+ break;
+ nv = nv2;
+ }
+ return nv;
+}
+
+/* Do nameref resolution from the VC, which is the local context for some
+ function or builtin, `up' the chain to the global variables context. If
+ NVCP is not NULL, return the variable context where we finally ended the
+ nameref resolution (so the bind_variable_internal can use the correct
+ variable context and hash table). */
+static SHELL_VAR *
+find_variable_nameref_context (v, vc, nvcp)
+ SHELL_VAR *v;
+ VAR_CONTEXT *vc;
+ VAR_CONTEXT **nvcp;
+{
+ SHELL_VAR *nv, *nv2;
+ VAR_CONTEXT *nvc;
+
+ /* Look starting at the current context all the way `up' */
+ for (nv = v, nvc = vc; nvc; nvc = nvc->down)
+ {
+ nv2 = find_nameref_at_context (nv, nvc);
+ if (nv2 == 0)
+ continue;
+ nv = nv2;
+ if (*nvcp)
+ *nvcp = nvc;
+ if (nameref_p (nv) == 0)
+ break;
+ }
+ return (nameref_p (nv) ? (SHELL_VAR *)NULL : nv);
+}
+
+/* Do nameref resolution from the VC, which is the local context for some
+ function or builtin, `up' the chain to the global variables context. If
+ NVCP is not NULL, return the variable context where we finally ended the
+ nameref resolution (so the bind_variable_internal can use the correct
+ variable context and hash table). */
+static SHELL_VAR *
+find_variable_last_nameref_context (v, vc, nvcp)
+ SHELL_VAR *v;
+ VAR_CONTEXT *vc;
+ VAR_CONTEXT **nvcp;
+{
+ SHELL_VAR *nv, *nv2;
+ VAR_CONTEXT *nvc;
+
+ /* Look starting at the current context all the way `up' */
+ for (nv = v, nvc = vc; nvc; nvc = nvc->down)
+ {
+ nv2 = find_nameref_at_context (nv, nvc);
+ if (nv2 == 0)
+ continue;
+ nv = nv2;
+ if (*nvcp)
+ *nvcp = nvc;
+ }
+ return (nameref_p (nv) ? nv : (SHELL_VAR *)NULL);
+}
+
+/* Find a variable, forcing a search of the temporary environment first */
+SHELL_VAR *
+find_variable_tempenv (name)
+ const char *name;
+{
+ SHELL_VAR *var;
+
+ var = find_variable_internal (name, 1);
+ if (var && nameref_p (var))
+ var = find_variable_nameref (var);
+ return (var);
+}
+
+/* Find a variable, not forcing a search of the temporary environment first */
+SHELL_VAR *
+find_variable_notempenv (name)
+ const char *name;
+{
+ SHELL_VAR *var;
+
+ var = find_variable_internal (name, 0);
+ if (var && nameref_p (var))
+ var = find_variable_nameref (var);
+ return (var);
+}
+
+SHELL_VAR *
+find_global_variable (name)
+ const char *name;
+{
+ SHELL_VAR *var;
+
+ var = var_lookup (name, global_variables);
+ if (var && nameref_p (var))
+ var = find_variable_nameref (var);
+
+ if (var == 0)
+ return ((SHELL_VAR *)NULL);
+
+ return (var->dynamic_value ? (*(var->dynamic_value)) (var) : var);
+}
+
+SHELL_VAR *
+find_global_variable_noref (name)
+ const char *name;
+{
+ SHELL_VAR *var;
+
+ var = var_lookup (name, global_variables);
+
+ if (var == 0)
+ return ((SHELL_VAR *)NULL);
+
+ return (var->dynamic_value ? (*(var->dynamic_value)) (var) : var);
+}
+
+SHELL_VAR *
+find_shell_variable (name)
+ const char *name;
+{
+ SHELL_VAR *var;
+
+ var = var_lookup (name, shell_variables);
+ if (var && nameref_p (var))
+ var = find_variable_nameref (var);
if (var == 0)
return ((SHELL_VAR *)NULL);
find_variable (name)
const char *name;
{
- return (find_variable_internal (name, (expanding_redir == 0 && (assigning_in_environment || executing_builtin))));
+ SHELL_VAR *v;
+
+ last_table_searched = 0;
+ v = find_variable_internal (name, (expanding_redir == 0 && (assigning_in_environment || executing_builtin)));
+ if (v && nameref_p (v))
+ v = find_variable_nameref (v);
+ return v;
+}
+
+SHELL_VAR *
+find_variable_noref (name)
+ const char *name;
+{
+ SHELL_VAR *v;
+
+ v = find_variable_internal (name, (expanding_redir == 0 && (assigning_in_environment || executing_builtin)));
+ return v;
}
/* Look up the function entry whose name matches STRING.
/* local foo; local foo; is a no-op. */
old_var = find_variable (name);
if (old_var && local_p (old_var) && old_var->context == variable_context)
+ return (old_var);
+
+ was_tmpvar = old_var && tempvar_p (old_var);
+ /* If we're making a local variable in a shell function, the temporary env
+ has already been merged into the function's variable context stack. We
+ can assume that a temporary var in the same context appears in the same
+ VAR_CONTEXT and can safely be returned without creating a new variable
+ (which results in duplicate names in the same VAR_CONTEXT->table */
+ /* We can't just test tmpvar_p because variables in the temporary env given
+ to a shell function appear in the function's local variable VAR_CONTEXT
+ but retain their tempvar attribute. We want temporary variables that are
+ found in temporary_env, hence the test for last_table_searched, which is
+ set in hash_lookup and only (so far) checked here. */
+ if (was_tmpvar && old_var->context == variable_context && last_table_searched != temporary_env)
{
VUNSETATTR (old_var, att_invisible);
return (old_var);
}
-
- was_tmpvar = old_var && tempvar_p (old_var);
if (was_tmpvar)
tmp_value = value_cell (old_var);
{
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)
+ 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);
SHELL_VAR *entry;
entry = (hflags & HASH_NOSRCH) ? (SHELL_VAR *)NULL : hash_lookup (name, table);
+ /* Follow the nameref chain here if this is the global variables table */
+ if (entry && nameref_p (entry) && (invisible_p (entry) == 0) && table == global_variables->table)
+ {
+ entry = find_global_variable (entry->name);
+ /* Let's see if we have a nameref referencing a variable that hasn't yet
+ been created. */
+ if (entry == 0)
+ entry = find_variable_last_nameref (name); /* XXX */
+ if (entry == 0) /* just in case */
+ return (entry);
+ }
- if (entry == 0)
+ /* The first clause handles `declare -n ref; ref=x;' */
+ if (entry && invisible_p (entry) && nameref_p (entry))
+ goto assign_value;
+ else if (entry && nameref_p (entry))
+ {
+ newval = nameref_cell (entry);
+#if defined (ARRAY_VARS)
+ /* declare -n foo=x[2] */
+ if (valid_array_reference (newval))
+ /* XXX - should it be aflags? */
+ entry = assign_array_element (newval, make_variable_value (entry, value, 0), aflags);
+ else
+#endif
+ {
+ entry = make_new_variable (newval, table);
+ var_setvalue (entry, make_variable_value (entry, value, 0));
+ }
+ }
+ else if (entry == 0)
{
entry = make_new_variable (name, table);
var_setvalue (entry, make_variable_value (entry, value, 0)); /* XXX */
{
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
{
+assign_value:
if (readonly_p (entry) || noassign_p (entry))
{
if (readonly_p (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 ();
if (vc_isfuncenv (vc) || vc_isbltnenv (vc))
{
v = hash_lookup (name, vc->table);
+ nvc = vc;
+ if (v && nameref_p (v))
+ {
+ nv = find_variable_nameref_context (v, vc, &nvc);
+ if (nv == 0)
+ {
+ nv = find_variable_last_nameref_context (v, vc, &nvc);
+ if (nv && nameref_p (nv))
+ {
+ /* If this nameref variable doesn't have a value yet,
+ set the value. Otherwise, assign using the value as
+ normal. */
+ if (nameref_cell (nv) == 0)
+ return (bind_variable_internal (nv->name, value, nvc->table, 0, flags));
+ return (bind_variable_internal (nameref_cell (nv), value, nvc->table, 0, flags));
+ }
+ else
+ v = nv;
+ }
+ else
+ v = nv;
+ }
if (v)
- return (bind_variable_internal (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 defined (ARRAY_VARS)
+ if ((aflags & ASS_NAMEREF) && (t == 0 || *t == 0 || (legal_identifier (t) == 0 && valid_array_reference (t) == 0)))
+#else
+ if ((aflags & ASS_NAMEREF) && (t == 0 || *t == 0 || legal_identifier (t) == 0))
+#endif
+ {
+ free (t);
+ if (invis)
+ VSETATTR (var, att_invisible); /* XXX */
+ return ((SHELL_VAR *)NULL);
+ }
FREE (value_cell (var));
var_setvalue (var, t);
}
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))
{
{
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)
+ if (v && isint)
VSETATTR (v, att_integer);
+ VUNSETATTR (v, att_invisible);
+
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;
+ int offset, aflags;
char *name, *temp, *value;
SHELL_VAR *var;
const char *string;
string = word->word;
+ aflags = 0;
offset = assignment (string, 0);
name = savestring (string);
value = (char *)NULL;
{
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 && (readonly_p (var) || noassign_p (var)))
temp = name + offset + 1;
value = expand_assignment_string_to_string (temp, 0);
+
+ if (var && (aflags & ASS_APPEND))
+ {
+ temp = make_variable_value (var, value, aflags);
+ FREE (value);
+ value = temp;
+ }
}
if (temporary_env == 0)
array_needs_making = 1;
- if (ifsname (name))
- setifs (var);
+ if (flags)
+ stupidly_hack_special_variables (name);
if (echo_command_at_execute)
/* The Korn shell prints the `+ ' in front of assignment statements,
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;
}
/* 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
}
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
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
+# if ARRAY_EXPORT
value = array_to_assignment_string (array_cell (var));
# 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));
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 */
if (tempvar_p (var) && (posixly_correct || (var->attributes & att_propagate)))
{
+ /* Make sure we have a hash table to store the variable in while it is
+ being propagated down to the global variables table. Create one if
+ we have to */
+ if ((vc_isfuncenv (shell_variables) || vc_istempenv (shell_variables)) && shell_variables->table == 0)
+ shell_variables->table = hash_create (0);
/* XXX - should we set v->context here? */
v = bind_variable_internal (var->name, value_cell (var), shell_variables->table, 0, 0);
if (shell_variables == global_variables)
{
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
+ { "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 GLOBIGNORE changes. */
void
sv_globignore (name)
{
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)
{
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
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