X-Git-Url: http://review.tizen.org/git/?a=blobdiff_plain;f=variables.c;h=70fac3bd0eb9b1fbc5972299f289e710d5354242;hb=ac50fbac377e32b98d2de396f016ea81e8ee9961;hp=7bb850fabdf2a5bb553256cbc6760078f9fc80fb;hpb=4539d736f1aff232857a854fd2a68df0c98d9f34;p=platform%2Fupstream%2Fbash.git diff --git a/variables.c b/variables.c index 7bb850f..70fac3b 100644 --- a/variables.c +++ b/variables.c @@ -1,6 +1,6 @@ /* variables.c -- Functions for hacking shell variables. */ -/* Copyright (C) 1987-2010 Free Software Foundation, Inc. +/* Copyright (C) 1987-2013 Free Software Foundation, Inc. This file is part of GNU Bash, the Bourne Again SHell. @@ -55,9 +55,13 @@ #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" @@ -87,6 +91,7 @@ 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; @@ -161,6 +166,8 @@ static int export_env_size; static int winsize_assignment; /* currently assigning 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)); @@ -263,6 +270,10 @@ static int variable_in_context __P((SHELL_VAR *)); 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)); @@ -310,7 +321,7 @@ initialize_shell_variables (env, privmode) 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 (); @@ -347,7 +358,8 @@ initialize_shell_variables (env, privmode) 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()=() {...} */ @@ -360,14 +372,22 @@ initialize_shell_variables (env, privmode) 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] == ')') { @@ -378,7 +398,7 @@ initialize_shell_variables (env, privmode) VSETATTR (temp_var, (att_exported | att_imported)); array_needs_making = 1; } -# endif +# endif /* ARRAY_EXPORT */ #endif #if 0 else if (legal_identifier (name)) @@ -386,6 +406,14 @@ initialize_shell_variables (env, privmode) 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); if (temp_var) { @@ -393,6 +421,8 @@ initialize_shell_variables (env, privmode) 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; } } @@ -525,11 +555,6 @@ initialize_shell_variables (env, privmode) set_if_not ("HISTFILE", name); free (name); - -#if 0 - set_if_not ("HISTSIZE", "500"); - sv_histsize ("HISTSIZE"); -#endif } #endif /* HISTORY */ @@ -1133,7 +1158,7 @@ get_self (self) } #if defined (ARRAY_VARS) -/* A generic dynamic array variable initializer. Intialize array variable +/* A generic dynamic array variable initializer. Initialize array variable NAME with dynamic value function GETFUNC and assignment function SETFUNC. */ static SHELL_VAR * init_dynamic_array_var (name, getfunc, setfunc, attrs) @@ -1739,6 +1764,10 @@ hash_lookup (name, hashed_vars) BUCKET_CONTENTS *bucket; bucket = hash_search (name, hashed_vars, 0); + /* If we find the name in HASHED_VARS, set LAST_TABLE_SEARCHED to that + table. */ + if (bucket) + last_table_searched = hashed_vars; return (bucket ? (SHELL_VAR *)bucket->data : (SHELL_VAR *)NULL); } @@ -1772,6 +1801,7 @@ find_variable_internal (name, force_tempenv) { SHELL_VAR *var; int search_tempenv; + VAR_CONTEXT *vc; var = (SHELL_VAR *)NULL; @@ -1785,8 +1815,22 @@ find_variable_internal (name, force_tempenv) if (search_tempenv && temporary_env) var = hash_lookup (name, temporary_env); + vc = shell_variables; +#if 0 +if (search_tempenv == 0 && /* (subshell_environment & SUBSHELL_COMSUB) && */ + expanding_redir && + (this_shell_builtin == eval_builtin || this_shell_builtin == command_builtin)) + { + itrace("find_variable_internal: search_tempenv == 0: skipping VC_BLTNENV"); + while (vc && (vc->flags & VC_BLTNENV)) + vc = vc->down; + if (vc == 0) + vc = shell_variables; + } +#endif + if (var == 0) - var = var_lookup (name, shell_variables); + var = var_lookup (name, vc); if (var == 0) return ((SHELL_VAR *)NULL); @@ -1794,6 +1838,197 @@ find_variable_internal (name, force_tempenv) 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; @@ -1801,6 +2036,38 @@ find_global_variable (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); @@ -1813,7 +2080,23 @@ SHELL_VAR * 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. @@ -1915,11 +2198,26 @@ make_local_variable (name) old_var = find_variable (name); if (old_var && local_p (old_var) && old_var->context == variable_context) { - VUNSETATTR (old_var, att_invisible); + VUNSETATTR (old_var, att_invisible); /* XXX */ 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); + } if (was_tmpvar) tmp_value = value_cell (old_var); @@ -1947,7 +2245,13 @@ make_local_variable (name) { if (readonly_p (old_var)) sh_readonly (name); - return ((SHELL_VAR *)NULL); + else if (noassign_p (old_var)) + builtin_error (_("%s: variable may not be assigned value"), name); +#if 0 + /* Let noassign variables through with a warning */ + if (readonly_p (old_var)) +#endif + return ((SHELL_VAR *)NULL); } if (old_var == 0) @@ -1958,7 +2262,9 @@ make_local_variable (name) /* 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)); @@ -1973,6 +2279,8 @@ make_local_variable (name) if (ifsname (name)) setifs (new_var); + if (was_tmpvar == 0) + VSETATTR (new_var, att_invisible); /* XXX */ return (new_var); } @@ -1995,7 +2303,7 @@ new_shell_variable (name) entry->attributes = 0; /* Always assume variables are to be made at toplevel! - make_local_variable has the responsibilty of changing the + make_local_variable has the responsibility of changing the variable context. */ entry->context = 0; @@ -2041,14 +2349,15 @@ make_new_array_variable (name) } 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 (); @@ -2127,6 +2436,8 @@ make_variable_value (var, value, flags) 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); @@ -2199,8 +2510,37 @@ bind_variable_internal (name, value, table, hflags, aflags) 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 */ @@ -2221,6 +2561,7 @@ bind_variable_internal (name, value, table, hflags, aflags) } else { +assign_value: if (readonly_p (entry) || noassign_p (entry)) { if (readonly_p (entry)) @@ -2231,6 +2572,12 @@ bind_variable_internal (name, value, table, hflags, aflags) /* 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 */ @@ -2241,14 +2588,14 @@ bind_variable_internal (name, value, table, hflags, aflags) /* 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), savestring ("0"), newval); + array_insert (array_cell (entry), 0, newval); free (newval); } else @@ -2278,8 +2625,9 @@ bind_variable (name, value, flags) 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 (); @@ -2298,10 +2646,50 @@ bind_variable (name, value, flags) 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)); } @@ -2317,7 +2705,9 @@ bind_variable_value (var, value, aflags) int aflags; { char *t; + int invis; + invis = invisible_p (var); VUNSETATTR (var, att_invisible); if (var->assign_func) @@ -2332,6 +2722,17 @@ bind_variable_value (var, value, aflags) 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); } @@ -2362,9 +2763,9 @@ bind_int_variable (lhs, rhs) 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)) { @@ -2379,11 +2780,17 @@ bind_int_variable (lhs, rhs) { isint = integer_p (v); VUNSETATTR (v, att_integer); +#if defined (ARRAY_VARS) + if (array_p (v) && isarr == 0) + implicitarray = 1; +#endif } #if defined (ARRAY_VARS) if (isarr) v = assign_array_element (lhs, rhs, 0); + else if (implicitarray) + v = bind_array_variable (lhs, 0, rhs, 0); else #endif v = bind_variable (lhs, rhs, 0); @@ -2391,6 +2798,8 @@ bind_int_variable (lhs, rhs) if (v && isint) VSETATTR (v, att_integer); + VUNSETATTR (v, att_invisible); + return (v); } @@ -2491,13 +2900,14 @@ 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; @@ -2506,9 +2916,12 @@ assign_in_env (word, flags) { 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))) @@ -2521,6 +2934,13 @@ assign_in_env (word, flags) 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) @@ -2547,11 +2967,6 @@ assign_in_env (word, flags) array_needs_making = 1; -#if 0 - if (ifsname (name)) - setifs (var); -else -#endif if (flags) stupidly_hack_special_variables (name); @@ -2593,7 +3008,9 @@ copy_variable (var) else if (assoc_p (var)) var_setassoc (copy, assoc_copy (assoc_cell (var))); #endif - else if (value_cell (var)) + else if (nameref_cell (var)) /* XXX - nameref */ + var_setref (copy, savestring (nameref_cell (var))); + else if (value_cell (var)) /* XXX - nameref */ var_setvalue (copy, savestring (value_cell (var))); else var_setvalue (copy, (char *)NULL); @@ -2628,6 +3045,8 @@ dispose_variable_value (var) else if (assoc_p (var)) assoc_dispose (assoc_cell (var)); #endif + else if (nameref_p (var)) + FREE (nameref_cell (var)); else FREE (value_cell (var)); } @@ -2652,12 +3071,33 @@ dispose_variable (var) free (var); } -/* Unset the shell variable referenced by NAME. */ +/* Unset the shell variable referenced by NAME. Unsetting a nameref variable + unsets the variable it resolves to but leaves the nameref alone. */ int unbind_variable (name) const char *name; { - return makunbound (name, shell_variables); + SHELL_VAR *v, *nv; + int r; + + v = var_lookup (name, shell_variables); + nv = (v && nameref_p (v)) ? find_variable_nameref (v) : (SHELL_VAR *)NULL; + + r = nv ? makunbound (nv->name, shell_variables) : makunbound (name, shell_variables); + return r; +} + +/* Unbind NAME, where NAME is assumed to be a nameref variable */ +int +unbind_nameref (name) + const char *name; +{ + SHELL_VAR *v; + + v = var_lookup (name, shell_variables); + if (v && nameref_p (v)) + return makunbound (name, shell_variables); + return 0; } /* Unset the shell function named NAME. */ @@ -2715,6 +3155,30 @@ unbind_function_def (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). @@ -2757,6 +3221,8 @@ makunbound (name, vc) 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 @@ -3475,11 +3941,11 @@ make_env_array_from_var_list (vars) value = named_function_string ((char *)NULL, function_cell (var), 0); #if defined (ARRAY_VARS) else if (array_p (var)) -# if 0 +# if ARRAY_EXPORT value = array_to_assignment_string (array_cell (var)); # else continue; /* XXX array vars cannot yet be exported */ -# endif +# endif /* ARRAY_EXPORT */ else if (assoc_p (var)) # if 0 value = assoc_to_assignment_string (assoc_cell (var)); @@ -3760,33 +4226,6 @@ put_command_name_into_env (command_name) update_export_env_inplace ("_=", 2, command_name); } -#if 0 /* UNUSED -- it caused too many problems */ -void -put_gnu_argv_flags_into_env (pid, flags_string) - intmax_t pid; - char *flags_string; -{ - char *dummy, *pbuf; - int l, fl; - - pbuf = itos (pid); - l = strlen (pbuf); - - fl = strlen (flags_string); - - dummy = (char *)xmalloc (l + fl + 30); - dummy[0] = '_'; - strcpy (dummy + 1, pbuf); - strcpy (dummy + 1 + l, "_GNU_nonoption_argv_flags_"); - dummy[l + 27] = '='; - strcpy (dummy + l + 28, flags_string); - - free (pbuf); - - export_env = add_or_supercede_exported_var (dummy, 0); -} -#endif - /* **************************************************************** */ /* */ /* Managing variable contexts */ @@ -4061,7 +4500,7 @@ push_dollar_vars () { dollar_arg_stack = (WORD_LIST **) xrealloc (dollar_arg_stack, (dollar_arg_stack_slots += 10) - * sizeof (WORD_LIST **)); + * sizeof (WORD_LIST *)); } dollar_arg_stack[dollar_arg_stack_index++] = list_rest_of_args (); dollar_arg_stack[dollar_arg_stack_index] = (WORD_LIST *)NULL; @@ -4167,8 +4606,13 @@ struct name_and_function { }; static struct name_and_function special_vars[] = { + { "BASH_COMPAT", sv_shcompat }, { "BASH_XTRACEFD", sv_xtracefd }, +#if defined (JOB_CONTROL) + { "CHILD_MAX", sv_childmax }, +#endif + #if defined (READLINE) # if defined (STRICT_POSIX) { "COLUMNS", sv_winsize }, @@ -4477,14 +4921,16 @@ sv_histsize (name) 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) @@ -4569,7 +5015,11 @@ sv_histtimefmt (name) { SHELL_VAR *v; - v = find_variable (name); + if (v = find_variable (name)) + { + if (history_comment_char == 0) + history_comment_char = '#'; + } history_write_timestamps = (v != 0); } #endif /* HISTORY */ @@ -4654,12 +5104,18 @@ sv_locale (name) char *name; { char *v; + int r; v = get_string_value (name); if (name[0] == 'L' && name[1] == 'A') /* LANG */ - set_lang (name, v); + r = set_lang (name, v); else - set_locale_var (name, v); /* LC_*, TEXTDOMAIN* */ + r = set_locale_var (name, v); /* LC_*, TEXTDOMAIN* */ + +#if 1 + if (r == 0 && posixly_correct) + last_command_exit_value = 1; +#endif } #if defined (ARRAY_VARS) @@ -4808,3 +5264,71 @@ sv_xtracefd (name) 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