diff options
Diffstat (limited to 'variables.c')
| -rw-r--r-- | variables.c | 340 |
1 files changed, 296 insertions, 44 deletions
diff --git a/variables.c b/variables.c index 79a90071..00872d64 100644 --- a/variables.c +++ b/variables.c @@ -110,6 +110,8 @@ VAR_CONTEXT *shell_variables = (VAR_CONTEXT *)NULL; the environment. */ HASH_TABLE *shell_functions = (HASH_TABLE *)NULL; +HASH_TABLE *invalid_env = (HASH_TABLE *)NULL; + #if defined (DEBUGGER) /* The table of shell function definitions that the user defined or that came from the environment. */ @@ -229,7 +231,9 @@ static SHELL_VAR *get_dirstack __P((SHELL_VAR *)); #if defined (ARRAY_VARS) static SHELL_VAR *get_groupset __P((SHELL_VAR *)); - +# if defined (DEBUGGER) +static SHELL_VAR *get_bashargcv __P((SHELL_VAR *)); +# endif static SHELL_VAR *build_hashcmd __P((SHELL_VAR *)); static SHELL_VAR *get_hashcmd __P((SHELL_VAR *)); static SHELL_VAR *assign_hashcmd __P((SHELL_VAR *, char *, arrayind_t, char *)); @@ -245,6 +249,10 @@ static SHELL_VAR *init_funcname_var __P((void)); static void initialize_dynamic_variables __P((void)); +static SHELL_VAR *bind_invalid_envvar __P((const char *, char *, int)); + +static int var_sametype __P((SHELL_VAR *, SHELL_VAR *)); + static SHELL_VAR *hash_lookup __P((const char *, HASH_TABLE *)); static SHELL_VAR *new_shell_variable __P((const char *)); static SHELL_VAR *make_new_variable __P((const char *, HASH_TABLE *)); @@ -391,7 +399,7 @@ initialize_shell_variables (env, privmode) } else { - if (temp_var = bind_variable (name, string, 0)) + if (temp_var = bind_invalid_envvar (name, string, 0)) { VSETATTR (temp_var, (att_exported | att_imported | att_invisible)); array_needs_making = 1; @@ -432,17 +440,24 @@ initialize_shell_variables (env, privmode) if (temp_var) VUNSETATTR (temp_var, att_readonly); } - temp_var = bind_variable (name, string, 0); - if (temp_var) + if (legal_identifier (name)) { - if (legal_identifier (name)) - VSETATTR (temp_var, (att_exported | att_imported)); - else + temp_var = bind_variable (name, string, 0); + if (temp_var) + { + VSETATTR (temp_var, (att_exported | att_imported)); + if (ro) + VSETATTR (temp_var, att_readonly); + } + } + else + { + temp_var = bind_invalid_envvar (name, string, 0); + if (temp_var) VSETATTR (temp_var, (att_exported | att_imported | att_invisible)); - if (ro) - VSETATTR (temp_var, att_readonly); - array_needs_making = 1; } + if (temp_var) + array_needs_making = 1; } name[char_index] = '='; @@ -1652,6 +1667,27 @@ get_groupset (self) return (self); } +# if defined (DEBUGGER) +static SHELL_VAR * +get_bashargcv (self) + SHELL_VAR *self; +{ + static int self_semaphore = 0; + + /* Backwards compatibility: if we refer to BASH_ARGV or BASH_ARGC at the + top level without enabling debug mode, and we don't have an instance + of the variable set, initialize the arg arrays. + This will already have been done if debugging_mode != 0. */ + if (self_semaphore == 0 && variable_context == 0 && debugging_mode == 0) /* don't do it for shell functions */ + { + self_semaphore = 1; + init_bash_argv (); + self_semaphore = 0; + } + return self; +} +# endif + static SHELL_VAR * build_hashcmd (self) SHELL_VAR *self; @@ -1777,6 +1813,11 @@ assign_aliasvar (self, value, ind, key) arrayind_t ind; char *key; { + if (legal_alias_name (key, 0) == 0) + { + report_error (_("`%s': invalid alias name"), key); + return (self); + } add_alias (key, value); return (build_aliasvar (self)); } @@ -1877,8 +1918,8 @@ initialize_dynamic_variables () v = init_dynamic_array_var ("GROUPS", get_groupset, null_array_assign, att_noassign); # if defined (DEBUGGER) - v = init_dynamic_array_var ("BASH_ARGC", get_self, null_array_assign, att_noassign|att_nounset); - v = init_dynamic_array_var ("BASH_ARGV", get_self, null_array_assign, att_noassign|att_nounset); + v = init_dynamic_array_var ("BASH_ARGC", get_bashargcv, null_array_assign, att_noassign|att_nounset); + v = init_dynamic_array_var ("BASH_ARGV", get_bashargcv, null_array_assign, att_noassign|att_nounset); # endif /* DEBUGGER */ v = init_dynamic_array_var ("BASH_SOURCE", get_self, null_array_assign, att_noassign|att_nounset); v = init_dynamic_array_var ("BASH_LINENO", get_self, null_array_assign, att_noassign|att_nounset); @@ -2226,6 +2267,36 @@ find_variable_nameref_for_assignment (name, flags) return (var); } +/* If find_variable (name) returns NULL, check that it's not a nameref + referencing a variable that doesn't exist. If it is, return the new + name. If not, return the original name. Kind of like the previous + function, but dealing strictly with names. This takes assignment flags + so it can deal with the various assignment modes used by `declare'. */ +char * +nameref_transform_name (name, flags) + char *name; + int flags; +{ + SHELL_VAR *v; + char *newname; + + v = 0; + if (flags & ASS_MKLOCAL) + { + v = find_variable_last_nameref (name, 1); + /* If we're making local variables, only follow namerefs that point to + non-existant variables at the same variable context. */ + if (v && v->context != variable_context) + v = 0; + } + else if (flags & ASS_MKGLOBAL) + v = (flags & ASS_CHKLOCAL) ? find_variable_last_nameref (name, 1) + : find_global_variable_last_nameref (name, 1); + if (v && nameref_p (v) && valid_nameref_value (nameref_cell (v), 1)) + return nameref_cell (v); + return name; +} + /* Find a variable, forcing a search of the temporary environment first */ SHELL_VAR * find_variable_tempenv (name) @@ -2438,6 +2509,42 @@ sh_get_env_value (v) /* */ /* **************************************************************** */ +static int +var_sametype (v1, v2) + SHELL_VAR *v1; + SHELL_VAR *v2; +{ + if (v1 == 0 || v2 == 0) + return 0; +#if defined (ARRAY_VARS) + else if (assoc_p (v1) && assoc_p (v2)) + return 1; + else if (array_p (v1) && array_p (v2)) + return 1; + else if (array_p (v1) || array_p (v2)) + return 0; + else if (assoc_p (v1) || assoc_p (v2)) + return 0; +#endif + else + return 1; +} + +int +validate_inherited_value (var, type) + SHELL_VAR *var; + int type; +{ +#if defined (ARRAY_VARS) + if (type == att_array && assoc_p (var)) + return 0; + else if (type == att_assoc && array_p (var)) + return 0; + else +#endif + return 1; /* should we run convert_var_to_array here or let the caller? */ +} + /* Set NAME to VALUE if NAME has no value. */ SHELL_VAR * set_if_not (name, value) @@ -2672,13 +2779,31 @@ make_local_array_variable (name, assoc_ok) ARRAY *array; var = make_local_variable (name, 0); /* XXX for now */ + /* If ASSOC_OK is non-zero, assume that we are ok with letting an assoc + variable return to the caller without converting it. The caller will + either flag an error or do the conversion itself. */ if (var == 0 || array_p (var) || (assoc_ok && assoc_p (var))) return var; - array = array_create (); + /* Validate any value we inherited from a variable instance at a previous + scope and disard anything that's invalid. */ + if (localvar_inherit && assoc_p (var)) + { + internal_warning ("%s: cannot inherit value from incompatible type", name); + VUNSETATTR (var, att_assoc); + dispose_variable_value (var); + array = array_create (); + var_setarray (var, array); + } + else if (localvar_inherit) + var = convert_var_to_array (var); /* XXX */ + else + { + dispose_variable_value (var); + array = array_create (); + var_setarray (var, array); + } - dispose_variable_value (var); - var_setarray (var, array); VSETATTR (var, att_array); return var; } @@ -2699,20 +2824,39 @@ make_new_assoc_variable (name) } SHELL_VAR * -make_local_assoc_variable (name) +make_local_assoc_variable (name, array_ok) char *name; + int array_ok; { SHELL_VAR *var; HASH_TABLE *hash; var = make_local_variable (name, 0); /* XXX for now */ - if (var == 0 || assoc_p (var)) + /* If ARRAY_OK is non-zero, assume that we are ok with letting an array + variable return to the caller without converting it. The caller will + either flag an error or do the conversion itself. */ + if (var == 0 || assoc_p (var) || (array_ok && array_p (var))) return var; - dispose_variable_value (var); - hash = assoc_create (0); + /* Validate any value we inherited from a variable instance at a previous + scope and disard anything that's invalid. */ + if (localvar_inherit && array_p (var)) + { + internal_warning ("%s: cannot inherit value from incompatible type", name); + VUNSETATTR (var, att_array); + dispose_variable_value (var); + hash = assoc_create (0); + var_setassoc (var, hash); + } + else if (localvar_inherit) + var = convert_var_to_assoc (var); /* XXX */ + else + { + dispose_variable_value (var); + hash = assoc_create (0); + var_setassoc (var, hash); + } - var_setassoc (var, hash); VSETATTR (var, att_assoc); return var; } @@ -2911,7 +3055,12 @@ bind_variable_internal (name, value, table, hflags, aflags) } else if (entry && nameref_p (entry)) { - newval = nameref_cell (entry); + newval = nameref_cell (entry); /* XXX - newval can't be NULL here */ + if (valid_nameref_value (newval, 0) == 0) + { + sh_invalidid (newval); + return ((SHELL_VAR *)NULL); + } #if defined (ARRAY_VARS) /* declare -n foo=x[2] ; foo=bar */ if (valid_array_reference (newval, 0)) @@ -3124,6 +3273,17 @@ bind_global_variable (name, value, flags) return (bind_variable_internal (name, value, global_variables->table, 0, flags)); } +static SHELL_VAR * +bind_invalid_envvar (name, value, flags) + const char *name; + char *value; + int flags; +{ + if (invalid_env == 0) + invalid_env = hash_create (64); /* XXX */ + return (bind_variable_internal (name, value, invalid_env, HASH_NOSRCH, flags)); +} + /* Make VAR, a simple shell variable, have value VALUE. Once assigned a value, variables are no longer invisible. This is a duplicate of part of the internals of bind_variable. If the variable is exported, or @@ -3374,6 +3534,12 @@ assign_in_env (word, flags) aflags |= ASS_APPEND; } + if (legal_identifier (name) == 0) + { + sh_invalidid (name); + return (0); + } + var = find_variable (name); if (var == 0) { @@ -4741,7 +4907,7 @@ maybe_make_export_env () { register char **temp_array; int new_size; - VAR_CONTEXT *tcxt; + VAR_CONTEXT *tcxt, *icxt; if (array_needs_making) { @@ -4753,7 +4919,7 @@ maybe_make_export_env () variables are not (yet) exported, this will always be big enough for the exported variables and functions. */ new_size = n_shell_variables () + HASH_ENTRIES (shell_functions) + 1 + - HASH_ENTRIES (temporary_env); + HASH_ENTRIES (temporary_env) + HASH_ENTRIES (invalid_env); if (new_size > export_env_size) { export_env_size = new_size; @@ -4774,11 +4940,23 @@ maybe_make_export_env () } else tcxt = shell_variables; + + if (invalid_env) + { + icxt = new_var_context ((char *)NULL, 0); + icxt->table = invalid_env; + icxt->down = tcxt; + } + else + icxt = tcxt; - temp_array = make_var_export_array (tcxt); + temp_array = make_var_export_array (icxt); if (temp_array) add_temp_array_to_env (temp_array, 0, 0); + if (icxt != tcxt) + free (icxt); + if (tcxt != shell_variables) free (tcxt); @@ -4893,13 +5071,14 @@ push_var_context (name, flags, tempvars) /* As of IEEE Std 1003.1-2017, assignment statements preceding shell functions no longer behave like assignment statements preceding - special builtins, and do not persist in the current shell environment. */ - posix_func_behavior = posixly_correct; /* placeholder for later */ + special builtins, and do not persist in the current shell environment. + This is austin group interp #654, though nobody implements it yet. */ + posix_func_behavior = posixly_correct; vc = new_var_context (name, flags); /* Posix interp 1009, temporary assignments preceding function calls modify the current environment *before* the command is executed. */ - if (posix_func_behavior && tempvars == temporary_env) + if (posix_func_behavior && (flags & VC_FUNCENV) && tempvars == temporary_env) merge_temporary_env (); else if (tempvars) { @@ -4923,24 +5102,25 @@ push_var_context (name, flags, tempvars) calls to the surrounding scope. It takes variables out of a temporary environment hash table. We take the - variable in data + variable in data. */ static void push_func_var (data) PTR_T data; { SHELL_VAR *var, *v; - int posix_func_behavior; + int posix_var_behavior; var = (SHELL_VAR *)data; /* As of IEEE Std 1003.1-2017, assignment statements preceding shell functions no longer behave like assignment statements preceding - special builtins, and do not persist in the current shell environment. */ - posix_func_behavior = posixly_correct; /* placeholder for later */ + special builtins, and do not persist in the current shell environment. + This is austin group interp #654, though nobody implements it yet. */ + posix_var_behavior = posixly_correct; if (local_p (var) && STREQ (var->name, "-")) set_current_options (value_cell (var)); - else if (tempvar_p (var) && (posix_func_behavior || (var->attributes & att_propagate))) + else if (tempvar_p (var) && (posix_var_behavior || (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 @@ -5110,10 +5290,64 @@ pop_scope (is_special) /* */ /* **************************************************************** */ -static WORD_LIST **dollar_arg_stack = (WORD_LIST **)NULL; +struct saved_dollar_vars { + char **first_ten; + WORD_LIST *rest; +}; + +static struct saved_dollar_vars *dollar_arg_stack = (struct saved_dollar_vars *)NULL; static int dollar_arg_stack_slots; static int dollar_arg_stack_index; +/* Functions to manipulate dollar_vars array. Need to keep these in sync with + whatever remember_args() does. */ +static char ** +save_dollar_vars () +{ + char **ret; + int i; + + ret = strvec_create (10); + for (i = 1; i < 10; i++) + { + ret[i] = dollar_vars[i]; + dollar_vars[i] = (char *)NULL; + } + return ret; +} + +static void +restore_dollar_vars (args) + char **args; +{ + int i; + + for (i = 1; i < 10; i++) + dollar_vars[i] = args[i]; +} + +static void +free_dollar_vars () +{ + int i; + + for (i = 1; i < 10; i++) + { + FREE (dollar_vars[i]); + dollar_vars[i] = (char *)NULL; + } +} + +static void +free_saved_dollar_vars (args) + char **args; +{ + int i; + + for (i = 1; i < 10; i++) + FREE (args[i]); +} + /* XXX - should always be followed by remember_args () */ void push_context (name, is_subshell, tempvars) @@ -5145,35 +5379,53 @@ push_dollar_vars () { if (dollar_arg_stack_index + 2 > dollar_arg_stack_slots) { - dollar_arg_stack = (WORD_LIST **) + dollar_arg_stack = (struct saved_dollar_vars *) xrealloc (dollar_arg_stack, (dollar_arg_stack_slots += 10) - * sizeof (WORD_LIST *)); + * sizeof (struct saved_dollar_vars)); } - dollar_arg_stack[dollar_arg_stack_index++] = list_rest_of_args (); - dollar_arg_stack[dollar_arg_stack_index] = (WORD_LIST *)NULL; + + dollar_arg_stack[dollar_arg_stack_index].first_ten = save_dollar_vars (); + dollar_arg_stack[dollar_arg_stack_index++].rest = rest_of_args; + rest_of_args = (WORD_LIST *)NULL; + + dollar_arg_stack[dollar_arg_stack_index].first_ten = (char **)NULL; + dollar_arg_stack[dollar_arg_stack_index].rest = (WORD_LIST *)NULL; } /* Restore the positional parameters from our stack. */ void pop_dollar_vars () { - if (!dollar_arg_stack || dollar_arg_stack_index == 0) + if (dollar_arg_stack == 0 || dollar_arg_stack_index == 0) return; - remember_args (dollar_arg_stack[--dollar_arg_stack_index], 1); - dispose_words (dollar_arg_stack[dollar_arg_stack_index]); - dollar_arg_stack[dollar_arg_stack_index] = (WORD_LIST *)NULL; + /* Do what remember_args (xxx, 1) would have done. */ + free_dollar_vars (); + dispose_words (rest_of_args); + + rest_of_args = dollar_arg_stack[--dollar_arg_stack_index].rest; + restore_dollar_vars (dollar_arg_stack[dollar_arg_stack_index].first_ten); + free (dollar_arg_stack[dollar_arg_stack_index].first_ten); + + dollar_arg_stack[dollar_arg_stack_index].first_ten = (char **)NULL; + dollar_arg_stack[dollar_arg_stack_index].rest = (WORD_LIST *)NULL; + set_dollar_vars_unchanged (); + invalidate_cached_quoted_dollar_at (); } void dispose_saved_dollar_vars () { - if (!dollar_arg_stack || dollar_arg_stack_index == 0) + if (dollar_arg_stack == 0 || dollar_arg_stack_index == 0) return; - dispose_words (dollar_arg_stack[dollar_arg_stack_index]); - dollar_arg_stack[dollar_arg_stack_index] = (WORD_LIST *)NULL; + dispose_words (dollar_arg_stack[--dollar_arg_stack_index].rest); + free_saved_dollar_vars (dollar_arg_stack[dollar_arg_stack_index].first_ten); + free (dollar_arg_stack[dollar_arg_stack_index].first_ten); + + dollar_arg_stack[dollar_arg_stack_index].first_ten = (char **)NULL; + dollar_arg_stack[dollar_arg_stack_index].rest = (WORD_LIST *)NULL; } /* Initialize BASH_ARGV and BASH_ARGC after turning on extdebug after the |
