This file is set.def, from which is created set.c. It implements the "set" and "unset" builtins in Bash. Copyright (C) 1987-2020 Free Software Foundation, Inc. This file is part of GNU Bash, the Bourne Again SHell. Bash is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. Bash is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with Bash. If not, see . $PRODUCES set.c #include #if defined (HAVE_UNISTD_H) # ifdef _MINIX # include # endif # include #endif #include #include "../bashansi.h" #include "../bashintl.h" #include "../shell.h" #include "../parser.h" #include "../flags.h" #include "common.h" #include "bashgetopt.h" #if defined (READLINE) # include "../input.h" # include "../bashline.h" # include #endif #if defined (HISTORY) # include "../bashhist.h" #endif $BUILTIN set $FUNCTION set_builtin $SHORT_DOC set [-abefhkmnptuvxBCHP] [-o option-name] [--] [arg ...] Set or unset values of shell options and positional parameters. Change the value of shell attributes and positional parameters, or display the names and values of shell variables. Options: -a Mark variables which are modified or created for export. -b Notify of job termination immediately. -e Exit immediately if a command exits with a non-zero status. -f Disable file name generation (globbing). -h Remember the location of commands as they are looked up. -k All assignment arguments are placed in the environment for a command, not just those that precede the command name. -m Job control is enabled. -n Read commands but do not execute them. -o option-name Set the variable corresponding to option-name: allexport same as -a braceexpand same as -B #if defined (READLINE) emacs use an emacs-style line editing interface #endif /* READLINE */ errexit same as -e errtrace same as -E functrace same as -T hashall same as -h #if defined (BANG_HISTORY) histexpand same as -H #endif /* BANG_HISTORY */ #if defined (HISTORY) history enable command history #endif ignoreeof the shell will not exit upon reading EOF interactive-comments allow comments to appear in interactive commands keyword same as -k #if defined (JOB_CONTROL) monitor same as -m #endif noclobber same as -C noexec same as -n noglob same as -f nolog currently accepted but ignored #if defined (JOB_CONTROL) notify same as -b #endif nounset same as -u onecmd same as -t physical same as -P pipefail the return value of a pipeline is the status of the last command to exit with a non-zero status, or zero if no command exited with a non-zero status posix change the behavior of bash where the default operation differs from the Posix standard to match the standard privileged same as -p verbose same as -v #if defined (READLINE) vi use a vi-style line editing interface #endif /* READLINE */ xtrace same as -x -p Turned on whenever the real and effective user ids do not match. Disables processing of the $ENV file and importing of shell functions. Turning this option off causes the effective uid and gid to be set to the real uid and gid. -t Exit after reading and executing one command. -u Treat unset variables as an error when substituting. -v Print shell input lines as they are read. -x Print commands and their arguments as they are executed. #if defined (BRACE_EXPANSION) -B the shell will perform brace expansion #endif /* BRACE_EXPANSION */ -C If set, disallow existing regular files to be overwritten by redirection of output. -E If set, the ERR trap is inherited by shell functions. #if defined (BANG_HISTORY) -H Enable ! style history substitution. This flag is on by default when the shell is interactive. #endif /* BANG_HISTORY */ -P If set, do not resolve symbolic links when executing commands such as cd which change the current directory. -T If set, the DEBUG and RETURN traps are inherited by shell functions. -- Assign any remaining arguments to the positional parameters. If there are no remaining arguments, the positional parameters are unset. - Assign any remaining arguments to the positional parameters. The -x and -v options are turned off. Using + rather than - causes these flags to be turned off. The flags can also be used upon invocation of the shell. The current set of flags may be found in $-. The remaining n ARGs are positional parameters and are assigned, in order, to $1, $2, .. $n. If no ARGs are given, all shell variables are printed. Exit Status: Returns success unless an invalid option is given. $END typedef int setopt_set_func_t PARAMS((int, char *)); typedef int setopt_get_func_t PARAMS((char *)); static int find_minus_o_option PARAMS((char *)); static void print_minus_o_option PARAMS((char *, int, int)); static void print_all_shell_variables PARAMS((void)); static int set_ignoreeof PARAMS((int, char *)); static int set_posix_mode PARAMS((int, char *)); #if defined (READLINE) static int set_edit_mode PARAMS((int, char *)); static int get_edit_mode PARAMS((char *)); #endif #if defined (HISTORY) static int bash_set_history PARAMS((int, char *)); #endif static const char * const on = "on"; static const char * const off = "off"; static int previous_option_value; /* A struct used to match long options for set -o to the corresponding option letter or internal variable. The functions can be called to dynamically generate values. If you add a new variable name here that doesn't have a corresponding single-character option letter, make sure to set the value appropriately in reset_shell_options. */ const struct { char *name; int letter; int *variable; setopt_set_func_t *set_func; setopt_get_func_t *get_func; } o_options[] = { { "allexport", 'a', (int *)NULL, (setopt_set_func_t *)NULL, (setopt_get_func_t *)NULL }, #if defined (BRACE_EXPANSION) { "braceexpand",'B', (int *)NULL, (setopt_set_func_t *)NULL, (setopt_get_func_t *)NULL }, #endif #if defined (READLINE) { "emacs", '\0', (int *)NULL, set_edit_mode, get_edit_mode }, #endif { "errexit", 'e', (int *)NULL, (setopt_set_func_t *)NULL, (setopt_get_func_t *)NULL }, { "errtrace", 'E', (int *)NULL, (setopt_set_func_t *)NULL, (setopt_get_func_t *)NULL }, { "functrace", 'T', (int *)NULL, (setopt_set_func_t *)NULL, (setopt_get_func_t *)NULL }, { "hashall", 'h', (int *)NULL, (setopt_set_func_t *)NULL, (setopt_get_func_t *)NULL }, #if defined (BANG_HISTORY) { "histexpand", 'H', (int *)NULL, (setopt_set_func_t *)NULL, (setopt_get_func_t *)NULL }, #endif /* BANG_HISTORY */ #if defined (HISTORY) { "history", '\0', &enable_history_list, bash_set_history, (setopt_get_func_t *)NULL }, #endif { "ignoreeof", '\0', &ignoreeof, set_ignoreeof, (setopt_get_func_t *)NULL }, { "interactive-comments", '\0', &interactive_comments, (setopt_set_func_t *)NULL, (setopt_get_func_t *)NULL }, { "keyword", 'k', (int *)NULL, (setopt_set_func_t *)NULL, (setopt_get_func_t *)NULL }, #if defined (JOB_CONTROL) { "monitor", 'm', (int *)NULL, (setopt_set_func_t *)NULL, (setopt_get_func_t *)NULL }, #endif { "noclobber", 'C', (int *)NULL, (setopt_set_func_t *)NULL, (setopt_get_func_t *)NULL }, { "noexec", 'n', (int *)NULL, (setopt_set_func_t *)NULL, (setopt_get_func_t *)NULL }, { "noglob", 'f', (int *)NULL, (setopt_set_func_t *)NULL, (setopt_get_func_t *)NULL }, #if defined (HISTORY) { "nolog", '\0', &dont_save_function_defs, (setopt_set_func_t *)NULL, (setopt_get_func_t *)NULL }, #endif #if defined (JOB_CONTROL) { "notify", 'b', (int *)NULL, (setopt_set_func_t *)NULL, (setopt_get_func_t *)NULL }, #endif /* JOB_CONTROL */ { "nounset", 'u', (int *)NULL, (setopt_set_func_t *)NULL, (setopt_get_func_t *)NULL }, { "onecmd", 't', (int *)NULL, (setopt_set_func_t *)NULL, (setopt_get_func_t *)NULL }, { "physical", 'P', (int *)NULL, (setopt_set_func_t *)NULL, (setopt_get_func_t *)NULL }, { "pipefail", '\0', &pipefail_opt, (setopt_set_func_t *)NULL, (setopt_get_func_t *)NULL }, { "posix", '\0', &posixly_correct, set_posix_mode, (setopt_get_func_t *)NULL }, { "privileged", 'p', (int *)NULL, (setopt_set_func_t *)NULL, (setopt_get_func_t *)NULL }, { "verbose", 'v', (int *)NULL, (setopt_set_func_t *)NULL, (setopt_get_func_t *)NULL }, #if defined (READLINE) { "vi", '\0', (int *)NULL, set_edit_mode, get_edit_mode }, #endif { "xtrace", 'x', (int *)NULL, (setopt_set_func_t *)NULL, (setopt_get_func_t *)NULL }, {(char *)NULL, 0 , (int *)NULL, (setopt_set_func_t *)NULL, (setopt_get_func_t *)NULL }, }; #define N_O_OPTIONS (sizeof (o_options) / sizeof (o_options[0])) #define GET_BINARY_O_OPTION_VALUE(i, name) \ ((o_options[i].get_func) ? (*o_options[i].get_func) (name) \ : (*o_options[i].variable)) #define SET_BINARY_O_OPTION_VALUE(i, onoff, name) \ ((o_options[i].set_func) ? (*o_options[i].set_func) (onoff, name) \ : (*o_options[i].variable = (onoff == FLAG_ON))) static int find_minus_o_option (name) char *name; { register int i; for (i = 0; o_options[i].name; i++) if (STREQ (name, o_options[i].name)) return i; return -1; } int minus_o_option_value (name) char *name; { register int i; int *on_or_off; i = find_minus_o_option (name); if (i < 0) return (-1); if (o_options[i].letter) { on_or_off = find_flag (o_options[i].letter); return ((on_or_off == FLAG_UNKNOWN) ? -1 : *on_or_off); } else return (GET_BINARY_O_OPTION_VALUE (i, name)); } #define MINUS_O_FORMAT "%-15s\t%s\n" static void print_minus_o_option (name, value, pflag) char *name; int value, pflag; { if (pflag == 0) printf (MINUS_O_FORMAT, name, value ? on : off); else printf ("set %co %s\n", value ? '-' : '+', name); } void list_minus_o_opts (mode, reusable) int mode, reusable; { register int i; int *on_or_off, value; for (i = 0; o_options[i].name; i++) { if (o_options[i].letter) { value = 0; on_or_off = find_flag (o_options[i].letter); if (on_or_off == FLAG_UNKNOWN) on_or_off = &value; if (mode == -1 || mode == *on_or_off) print_minus_o_option (o_options[i].name, *on_or_off, reusable); } else { value = GET_BINARY_O_OPTION_VALUE (i, o_options[i].name); if (mode == -1 || mode == value) print_minus_o_option (o_options[i].name, value, reusable); } } } char ** get_minus_o_opts () { char **ret; int i; ret = strvec_create (N_O_OPTIONS + 1); for (i = 0; o_options[i].name; i++) ret[i] = o_options[i].name; ret[i] = (char *)NULL; return ret; } char * get_current_options () { char *temp; int i, posixopts; posixopts = num_posix_options (); /* shopts modified by posix mode */ /* Make the buffer big enough to hold the set -o options and the shopt options modified by posix mode. */ temp = (char *)xmalloc (1 + N_O_OPTIONS + posixopts); for (i = 0; o_options[i].name; i++) { if (o_options[i].letter) temp[i] = *(find_flag (o_options[i].letter)); else temp[i] = GET_BINARY_O_OPTION_VALUE (i, o_options[i].name); } /* Add the shell options that are modified by posix mode to the end of the bitmap. They will be handled in set_current_options() */ get_posix_options (temp+i); temp[i+posixopts] = '\0'; return (temp); } void set_current_options (bitmap) const char *bitmap; { int i, v, cv, *on_or_off; if (bitmap == 0) return; for (i = 0; o_options[i].name; i++) { v = bitmap[i] ? FLAG_ON : FLAG_OFF; if (o_options[i].letter) { /* We should not get FLAG_UNKNOWN here */ on_or_off = find_flag (o_options[i].letter); cv = *on_or_off ? FLAG_ON : FLAG_OFF; if (v != cv) change_flag (o_options[i].letter, v); } else { cv = GET_BINARY_O_OPTION_VALUE (i, o_options[i].name); cv = cv ? FLAG_ON : FLAG_OFF; if (v != cv) SET_BINARY_O_OPTION_VALUE (i, v, o_options[i].name); } } /* Now reset the variables changed by posix mode */ set_posix_options (bitmap+i); } static int set_ignoreeof (on_or_off, option_name) int on_or_off; char *option_name; { ignoreeof = on_or_off == FLAG_ON; unbind_variable_noref ("ignoreeof"); if (ignoreeof) bind_variable ("IGNOREEOF", "10", 0); else unbind_variable_noref ("IGNOREEOF"); sv_ignoreeof ("IGNOREEOF"); return 0; } static int set_posix_mode (on_or_off, option_name) int on_or_off; char *option_name; { /* short-circuit on no-op */ if ((on_or_off == FLAG_ON && posixly_correct) || (on_or_off == FLAG_OFF && posixly_correct == 0)) return 0; posixly_correct = on_or_off == FLAG_ON; if (posixly_correct == 0) unbind_variable_noref ("POSIXLY_CORRECT"); else bind_variable ("POSIXLY_CORRECT", "y", 0); sv_strict_posix ("POSIXLY_CORRECT"); return (0); } #if defined (READLINE) /* Magic. This code `knows' how readline handles rl_editing_mode. */ static int set_edit_mode (on_or_off, option_name) int on_or_off; char *option_name; { int isemacs; if (on_or_off == FLAG_ON) { rl_variable_bind ("editing-mode", option_name); if (interactive) with_input_from_stdin (); no_line_editing = 0; } else { isemacs = rl_editing_mode == 1; if ((isemacs && *option_name == 'e') || (!isemacs && *option_name == 'v')) { if (interactive) with_input_from_stream (stdin, "stdin"); no_line_editing = 1; } } return 1-no_line_editing; } static int get_edit_mode (name) char *name; { return (*name == 'e' ? no_line_editing == 0 && rl_editing_mode == 1 : no_line_editing == 0 && rl_editing_mode == 0); } #endif /* READLINE */ #if defined (HISTORY) static int bash_set_history (on_or_off, option_name) int on_or_off; char *option_name; { if (on_or_off == FLAG_ON) { enable_history_list = 1; bash_history_enable (); if (history_lines_this_session == 0) load_history (); } else { enable_history_list = 0; bash_history_disable (); } return (1 - enable_history_list); } #endif int set_minus_o_option (on_or_off, option_name) int on_or_off; char *option_name; { register int i; i = find_minus_o_option (option_name); if (i < 0) { sh_invalidoptname (option_name); return (EX_USAGE); } if (o_options[i].letter == 0) { previous_option_value = GET_BINARY_O_OPTION_VALUE (i, o_options[i].name); SET_BINARY_O_OPTION_VALUE (i, on_or_off, option_name); return (EXECUTION_SUCCESS); } else { if ((previous_option_value = change_flag (o_options[i].letter, on_or_off)) == FLAG_ERROR) { sh_invalidoptname (option_name); return (EXECUTION_FAILURE); } else return (EXECUTION_SUCCESS); } } static void print_all_shell_variables () { SHELL_VAR **vars; vars = all_shell_variables (); if (vars) { print_var_list (vars); free (vars); } /* POSIX.2 does not allow function names and definitions to be output when `set' is invoked without options (PASC Interp #202). */ if (posixly_correct == 0) { vars = all_shell_functions (); if (vars) { print_func_list (vars); free (vars); } } } void set_shellopts () { char *value; char tflag[N_O_OPTIONS]; int vsize, i, vptr, *ip, exported; SHELL_VAR *v; for (vsize = i = 0; o_options[i].name; i++) { tflag[i] = 0; if (o_options[i].letter) { ip = find_flag (o_options[i].letter); if (ip && *ip) { vsize += strlen (o_options[i].name) + 1; tflag[i] = 1; } } else if (GET_BINARY_O_OPTION_VALUE (i, o_options[i].name)) { vsize += strlen (o_options[i].name) + 1; tflag[i] = 1; } } value = (char *)xmalloc (vsize + 1); for (i = vptr = 0; o_options[i].name; i++) { if (tflag[i]) { strcpy (value + vptr, o_options[i].name); vptr += strlen (o_options[i].name); value[vptr++] = ':'; } } if (vptr) vptr--; /* cut off trailing colon */ value[vptr] = '\0'; v = find_variable ("SHELLOPTS"); /* Turn off the read-only attribute so we can bind the new value, and note whether or not the variable was exported. */ if (v) { VUNSETATTR (v, att_readonly); exported = exported_p (v); } else exported = 0; v = bind_variable ("SHELLOPTS", value, 0); /* Turn the read-only attribute back on, and turn off the export attribute if it was set implicitly by mark_modified_vars and SHELLOPTS was not exported before we bound the new value. */ VSETATTR (v, att_readonly); if (mark_modified_vars && exported == 0 && exported_p (v)) VUNSETATTR (v, att_exported); free (value); } void parse_shellopts (value) char *value; { char *vname; int vptr; vptr = 0; while (vname = extract_colon_unit (value, &vptr)) { set_minus_o_option (FLAG_ON, vname); free (vname); } } void initialize_shell_options (no_shellopts) int no_shellopts; { char *temp; SHELL_VAR *var; if (no_shellopts == 0) { var = find_variable ("SHELLOPTS"); /* set up any shell options we may have inherited. */ if (var && imported_p (var)) { temp = (array_p (var) || assoc_p (var)) ? (char *)NULL : savestring (value_cell (var)); if (temp) { parse_shellopts (temp); free (temp); } } } /* Set up the $SHELLOPTS variable. */ set_shellopts (); } /* Reset the values of the -o options that are not also shell flags. This is called from execute_cmd.c:initialize_subshell() when setting up a subshell to run an executable shell script without a leading `#!'. */ void reset_shell_options () { pipefail_opt = 0; ignoreeof = 0; #if defined (STRICT_POSIX) posixly_correct = 1; #else posixly_correct = 0; #endif #if defined (HISTORY) dont_save_function_defs = 0; remember_on_history = enable_history_list = 1; /* XXX */ #endif } /* Set some flags from the word values in the input list. If LIST is empty, then print out the values of the variables instead. If LIST contains non-flags, then set $1 - $9 to the successive words of LIST. */ int set_builtin (list) WORD_LIST *list; { int on_or_off, flag_name, force_assignment, opts_changed, rv, r; register char *arg; char s[3]; if (list == 0) { print_all_shell_variables (); return (sh_chkwrite (EXECUTION_SUCCESS)); } /* Check validity of flag arguments. */ rv = EXECUTION_SUCCESS; reset_internal_getopt (); while ((flag_name = internal_getopt (list, optflags)) != -1) { switch (flag_name) { case 'i': /* don't allow set -i */ s[0] = list_opttype; s[1] = 'i'; s[2] = '\0'; sh_invalidopt (s); builtin_usage (); return (EX_USAGE); CASE_HELPOPT; case '?': builtin_usage (); return (list_optopt == '?' ? EXECUTION_SUCCESS : EX_USAGE); default: break; } } /* Do the set command. While the list consists of words starting with '-' or '+' treat them as flags, otherwise, start assigning them to $1 ... $n. */ for (force_assignment = opts_changed = 0; list; ) { arg = list->word->word; /* If the argument is `--' or `-' then signal the end of the list and remember the remaining arguments. */ if (arg[0] == '-' && (!arg[1] || (arg[1] == '-' && !arg[2]))) { list = list->next; /* `set --' unsets the positional parameters. */ if (arg[1] == '-') force_assignment = 1; /* Until told differently, the old shell behaviour of `set - [arg ...]' being equivalent to `set +xv [arg ...]' stands. Posix.2 says the behaviour is marked as obsolescent. */ else { change_flag ('x', '+'); change_flag ('v', '+'); opts_changed = 1; } break; } if ((on_or_off = *arg) && (on_or_off == '-' || on_or_off == '+')) { while (flag_name = *++arg) { if (flag_name == '?') { builtin_usage (); return (EXECUTION_SUCCESS); } else if (flag_name == 'o') /* -+o option-name */ { char *option_name; WORD_LIST *opt; opt = list->next; if (opt == 0) { list_minus_o_opts (-1, (on_or_off == '+')); rv = sh_chkwrite (rv); continue; } option_name = opt->word->word; if (option_name == 0 || *option_name == '\0' || *option_name == '-' || *option_name == '+') { list_minus_o_opts (-1, (on_or_off == '+')); continue; } list = list->next; /* Skip over option name. */ opts_changed = 1; if ((r = set_minus_o_option (on_or_off, option_name)) != EXECUTION_SUCCESS) { set_shellopts (); return (r); } } else if (change_flag (flag_name, on_or_off) == FLAG_ERROR) { s[0] = on_or_off; s[1] = flag_name; s[2] = '\0'; sh_invalidopt (s); builtin_usage (); set_shellopts (); return (EXECUTION_FAILURE); } opts_changed = 1; } } else { break; } list = list->next; } /* Assigning $1 ... $n */ if (list || force_assignment) remember_args (list, 1); /* Set up new value of $SHELLOPTS */ if (opts_changed) set_shellopts (); return (rv); } $BUILTIN unset $FUNCTION unset_builtin $SHORT_DOC unset [-f] [-v] [-n] [name ...] Unset values and attributes of shell variables and functions. For each NAME, remove the corresponding variable or function. Options: -f treat each NAME as a shell function -v treat each NAME as a shell variable -n treat each NAME as a name reference and unset the variable itself rather than the variable it references Without options, unset first tries to unset a variable, and if that fails, tries to unset a function. Some variables cannot be unset; also see `readonly'. Exit Status: Returns success unless an invalid option is given or a NAME is read-only. $END #define NEXT_VARIABLE() any_failed++; list = list->next; continue; int unset_builtin (list) WORD_LIST *list; { int unset_function, unset_variable, unset_array, opt, nameref, any_failed; int global_unset_func, global_unset_var, vflags, valid_id; char *name, *tname; unset_function = unset_variable = unset_array = nameref = any_failed = 0; global_unset_func = global_unset_var = 0; reset_internal_getopt (); while ((opt = internal_getopt (list, "fnv")) != -1) { switch (opt) { case 'f': global_unset_func = 1; break; case 'v': global_unset_var = 1; break; case 'n': nameref = 1; break; CASE_HELPOPT; default: builtin_usage (); return (EX_USAGE); } } list = loptend; if (global_unset_func && global_unset_var) { builtin_error (_("cannot simultaneously unset a function and a variable")); return (EXECUTION_FAILURE); } else if (unset_function && nameref) nameref = 0; #if defined (ARRAY_VARS) vflags = assoc_expand_once ? (VA_NOEXPAND|VA_ONEWORD) : 0; #endif while (list) { SHELL_VAR *var; int tem; #if defined (ARRAY_VARS) char *t; #endif name = list->word->word; unset_function = global_unset_func; unset_variable = global_unset_var; #if defined (ARRAY_VARS) unset_array = 0; /* XXX valid array reference second arg was 0 */ if (!unset_function && nameref == 0 && valid_array_reference (name, vflags)) { t = strchr (name, '['); *t++ = '\0'; unset_array++; } #endif /* Get error checking out of the way first. The low-level functions just perform the unset, relying on the caller to verify. */ valid_id = legal_identifier (name); /* Whether or not we are in posix mode, if neither -f nor -v appears, skip over trying to unset variables with invalid names and just treat them as potential shell function names. */ if (global_unset_func == 0 && global_unset_var == 0 && valid_id == 0) { unset_variable = unset_array = 0; unset_function = 1; } /* Bash allows functions with names which are not valid identifiers to be created when not in posix mode, so check only when in posix mode when unsetting a function. */ if (unset_function == 0 && valid_id == 0) { sh_invalidid (name); NEXT_VARIABLE (); } /* Search for functions here if -f supplied or if NAME cannot be a variable name. */ var = unset_function ? find_function (name) : (nameref ? find_variable_last_nameref (name, 0) : find_variable (name)); /* Some variables (but not functions yet) cannot be unset, period. */ if (var && unset_function == 0 && non_unsettable_p (var)) { builtin_error (_("%s: cannot unset"), name); NEXT_VARIABLE (); } /* if we have a nameref we want to use it */ if (var && unset_function == 0 && nameref == 0 && STREQ (name, name_cell(var)) == 0) name = name_cell (var); /* Posix.2 says try variables first, then functions. If we would find a function after unsuccessfully searching for a variable, note that we're acting on a function now as if -f were supplied. The readonly check below takes care of it. */ if (var == 0 && nameref == 0 && unset_variable == 0 && unset_function == 0) { if (var = find_function (name)) unset_function = 1; } /* Posix.2 says that unsetting readonly variables is an error. */ if (var && readonly_p (var)) { builtin_error (_("%s: cannot unset: readonly %s"), var->name, unset_function ? "function" : "variable"); NEXT_VARIABLE (); } /* Unless the -f option is supplied, the name refers to a variable. */ #if defined (ARRAY_VARS) if (var && unset_array) { /* Let unbind_array_element decide what to do with non-array vars */ tem = unbind_array_element (var, t, vflags); /* XXX new third arg */ if (tem == -2 && array_p (var) == 0 && assoc_p (var) == 0) { builtin_error (_("%s: not an array variable"), var->name); NEXT_VARIABLE (); } else if (tem < 0) any_failed++; } else #endif /* ARRAY_VARS */ /* If we're trying to unset a nameref variable whose value isn't a set variable, make sure we still try to unset the nameref's value */ if (var == 0 && nameref == 0 && unset_function == 0) { var = find_variable_last_nameref (name, 0); if (var && nameref_p (var)) { #if defined (ARRAY_VARS) if (valid_array_reference (nameref_cell (var), 0)) { tname = savestring (nameref_cell (var)); if (var = array_variable_part (tname, 0, &t, (int *)0)) tem = unbind_array_element (var, t, vflags); /* XXX new third arg */ free (tname); } else #endif tem = unbind_variable (nameref_cell (var)); } else tem = unbind_variable (name); } else tem = unset_function ? unbind_func (name) : (nameref ? unbind_nameref (name) : unbind_variable (name)); /* This is what Posix.2 says: ``If neither -f nor -v is specified, the name refers to a variable; if a variable by that name does not exist, a function by that name, if any, shall be unset.'' */ if (tem == -1 && nameref == 0 && unset_function == 0 && unset_variable == 0) tem = unbind_func (name); name = list->word->word; /* reset above for namerefs */ /* SUSv3, POSIX.1-2001 say: ``Unsetting a variable or function that was not previously set shall not be considered an error.'' */ if (unset_function == 0) stupidly_hack_special_variables (name); list = list->next; } return (any_failed ? EXECUTION_FAILURE : EXECUTION_SUCCESS); }