- parameter_brace_expand_length: handle namerefs with values that are
valid length expansion expressions but invalid identifiers. From
ed7-aspire4925@hotmail.com via https://savannah.gnu.org/support/?110799
+
+ 12/13
+ -----
+subst.c
+ - extract_heredoc_dolbrace_string: fix off-by-one error after calling
+ extract_command_subst and extract_process_subst that caused it to
+ copy one too many parsed characters. Fix for bug reported by
+ Norbert Lange <nolange79@gmail.com>
+
+ 12/14
+ -----
+execute_cmd.c
+ - execute_cond_node: if a regular expression fails to compile, print
+ an error message. Report from Emanuele Torre <torreemanuele6@gmail.com>
+ back on 6/15/2022
+
+trap.c
+ - trap_variable_context -> trap_return_context, initialize from
+ funcnest + sourcenest instead of variable_context so we handle
+ shell function execution and `./source', both of which can use
+ `return'. Idea from Koichi Murase <myoga.murase@gmail.com>
+
+builtins/common.c
+ - get_exitstat: compare trap_return_context against funcnest+sourcenest,
+ since that's how it's initialized now
+
+lib/readline/readline.c
+ - readline_internal_charloop: if we're not using the callback interface,
+ don't restore _rl_top_level from olevel, since we will just be going
+ around the loop again and will potentially need to use it multiple
+ times. Report from Emanuele Torre <torreemanuele6@gmail.com>
+
+ 12/15
+ -----
+shell.h
+ - EX_UTILERROR: new generic special builtin return status to indicate a
+ POSIX utility error that should cause a non-interactive shell to abort
+
+execute_cmd.c
+ - builtin_status: translate EX_UTILERROR to EXECUTION_FAILURE
+
+builtins/set.def
+ - unset_builtin: return EX_UTILERROR if posix_utility_error is set;
+ set it when trying to unset a non-identifier (variable) or a
+ non-unsettable or readonly variable
+
+ 12/16
+ -----
+subst.c
+ - de_backslash: now takes a second argument with the current quoting
+ flags
+ - de_backslash: if the quoting flags include Q_HERE_DOCUMENT and the
+ shell is in posix mode, remove backslashes quoting double quotes
+
+subst.h
+ - de_backslash: update extern declaration
+
+lib/readline/histexpand.c
+ - history_expand_internal,get_history_word_specifier,get_subst_pattern,
+ hist_error,history_find_word,hist_string_extract_single_quoted:
+ now take const char * string arguments
+
+lib/readline/mbutil.c
+ - _rl_get_char_len,_rl_adjust_point,_rl_find_next_mbchar_internal,
+ _rl_find_next_mbchar,_rl_find_prev_mbchar,_rl_find_prev_mbchar_internal,
+ _rl_test_nonzero,_rl_find_prev_utf8char,_rl_is_mbchar_matched,
+ _rl_compare_chars,_rl_char_value: take const char * string arguments
+
+ 12/17
+ -----
+builtins/umask.def
+ - parse_symbolic_umask: add missing POSIX pieces:
+ o `action' of ugo, meaning to copy portions of initial mask
+ o multiple `op' specs as part of the action string (`u=r-w')
+ (resets perm)
+ o missing perm characters Xst in action string
+ o default `who' equivalent to `a' instead of fixing up later
+
tests/builtins5.sub f
tests/builtins6.sub f
tests/builtins7.sub f
+tests/builtins8.sub f
tests/source1.sub f
tests/source2.sub f
tests/source3.sub f
trap gets to change $?, though, since that is part of its reason for
existing, and because the extended debug mode does things with the
return value. */
- if (this_shell_builtin == return_builtin && running_trap > 0 && running_trap != DEBUG_TRAP+1 && variable_context == trap_variable_context)
+ if (this_shell_builtin == return_builtin && running_trap > 0 && running_trap != DEBUG_TRAP+1 && trap_return_context == funcnest + sourcenest)
return (trap_saved_exit_value);
return (last_command_exit_value);
}
{
int unset_function, unset_variable, unset_array, opt, nameref, any_failed;
int global_unset_func, global_unset_var, vflags, base_vflags, valid_id;
+ int posix_utility_error;
char *name, *tname;
- unset_function = unset_variable = unset_array = nameref = any_failed = 0;
+ unset_function = unset_variable = unset_array = nameref = 0;
+ posix_utility_error = any_failed = 0;
global_unset_func = global_unset_var = 0;
reset_internal_getopt ();
if (unset_function == 0 && valid_id == 0)
{
sh_invalidid (name);
+ posix_utility_error++;
NEXT_VARIABLE ();
}
if (var && unset_function == 0 && non_unsettable_p (var))
{
builtin_error (_("%s: cannot unset"), name);
+ posix_utility_error++;
NEXT_VARIABLE ();
}
{
builtin_error (_("%s: cannot unset: readonly %s"),
var->name, unset_function ? "function" : "variable");
+ posix_utility_error++;
NEXT_VARIABLE ();
}
/* 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.'' */
+ that name does not exist, it is unspecified whether a function
+ by that name, if any, shall be unset.'' The unspecified part is a
+ recent addition, so we continue to try to unset a shell function if
+ we don't find a variable named NAME. */
if (tem == -1 && nameref == 0 && unset_function == 0 && unset_variable == 0)
tem = unbind_func (name);
list = list->next;
}
- return (any_failed ? EXECUTION_FAILURE : EXECUTION_SUCCESS);
+ return (any_failed ? (posix_utility_error ? EX_UTILERROR : EXECUTION_FAILURE) : EXECUTION_SUCCESS);
}
This file is umask.def, from which is created umask.c.
It implements the builtin "umask" in Bash.
-Copyright (C) 1987-2020 Free Software Foundation, Inc.
+Copyright (C) 1987-2022 Free Software Foundation, Inc.
This file is part of GNU Bash, the Bourne Again SHell.
printf ("u=%s,g=%s,o=%s\n", ubits, gbits, obits);
}
+static inline mode_t
+copyuser (mask)
+ mode_t mask;
+{
+ return ((mask & S_IRUSR) ? S_IRUGO : 0) |
+ ((mask & S_IWUSR) ? S_IWUGO : 0) |
+ ((mask & S_IXUSR) ? S_IXUGO : 0);
+}
+
+static inline mode_t
+copygroup (mask)
+ mode_t mask;
+{
+ return ((mask & S_IRGRP) ? S_IRUGO : 0) |
+ ((mask & S_IWGRP) ? S_IWUGO : 0) |
+ ((mask & S_IXGRP) ? S_IXUGO : 0);
+}
+
+static inline mode_t
+copyother (mask)
+ mode_t mask;
+{
+ return ((mask & S_IROTH) ? S_IRUGO : 0) |
+ ((mask & S_IWOTH) ? S_IWUGO : 0) |
+ ((mask & S_IXOTH) ? S_IXUGO : 0);
+}
+
int
parse_symbolic_mode (mode, initial_bits)
char *mode;
- int initial_bits;
+ mode_t initial_bits;
{
- int who, op, perm, bits, c;
+ char op, c;
+ mode_t who, perm, bits;
char *s;
for (s = mode, bits = initial_bits;;)
{
- who = op = perm = 0;
+ who = 0;
+ op = 0;
/* Parse the `who' portion of the symbolic mode clause. */
while (member (*s, "agou"))
}
}
+ /* default `who' is `a' */
+ if (who == 0)
+ who = S_IRWXU | S_IRWXG | S_IRWXO;
+
/* The operation is now sitting in *s. */
+start_op:
+ perm = 0;
op = *s++;
switch (op)
{
return (-1);
}
- /* Parse out the `perm' section of the symbolic mode clause. */
- while (member (*s, "rwx"))
+ /* Parse out the `action' section of the symbolic mode clause. An
+ action can be a set of permissions (rwxXst), a copy specification
+ (ugo), or another op (+-=). */
+ while (member (*s, "rwxXstugo"))
{
- c = *s++;
-
- switch (c)
+ switch (*s)
{
+ /* First the copy specification */
+ case 'u':
+ perm = copyuser (initial_bits);
+ break;
+ case 'g':
+ perm = copygroup (initial_bits);
+ break;
+ case 'o':
+ perm = copyother (initial_bits);
+ break;
+
+ /* Then the permissions. */
case 'r':
perm |= S_IRUGO;
break;
case 'w':
perm |= S_IWUGO;
break;
+ case 'X':
+ /* for chmod, this includes S_ISDIR but that doesn't make sense here */
+ if ((initial_bits & S_IXUGO) == 0)
+ break; /* no-op if original mask doesn't include execute bits */
+ /* FALLTHROUGH */
case 'x':
perm |= S_IXUGO;
break;
+ case 's':
+#ifdef S_ISUID
+ perm |= S_ISUID | S_ISGID;
+ break;
+#else
+ goto spec_error;
+#endif
+ case 't':
+#ifdef S_ISVTX
+ perm |= S_ISVTX;
+ break;
+#else
+ goto spec_error;
+#endif
}
+ s++;
}
/* Now perform the operation or return an error for a
bad permission string. */
- if (!*s || *s == ',')
- {
- if (who)
- perm &= who;
-
- switch (op)
- {
- case '+':
- bits |= perm;
- break;
- case '-':
- bits &= ~perm;
- break;
- case '=':
- if (who == 0)
- who = S_IRWXU | S_IRWXG | S_IRWXO;
- bits &= ~who;
- bits |= perm;
- break;
-
- /* No other values are possible. */
- }
+ perm &= who;
- if (*s == '\0')
- break;
- else
- s++; /* skip past ',' */
+ switch (op)
+ {
+ case '+':
+ bits |= perm;
+ break;
+ case '-':
+ bits &= ~perm;
+ break;
+ case '=':
+ bits &= ~who;
+ bits |= perm;
+ break;
}
+
+ /* Break if the end of the action string, loop if we're going to parse
+ another `who', go back to parsing another op if we have an op spec
+ (+-=). Return an invalid mode character error for everything else. */
+ if (*s == '\0')
+ break;
+ else if (*s == ',')
+ s++; /* skip past ',' */
+ else if (*s == '+' || *s == '-' || *s == '=')
+ goto start_op;
else
{
+spec_error:
builtin_error (_("`%c': invalid symbolic mode character"), *s);
return (-1);
}
symbolic_umask (list)
WORD_LIST *list;
{
- int um, bits;
+ mode_t um;
+ int bits;
/* Get the initial umask. Don't change it yet. */
um = umask (022);
redirection errors, variable assignment errors for assignments preceding
the command name, and so on.
+@item
+The @code{unset} builtin with the @option{-v} option specified returns a
+fatal error if it attempts to unset a @code{readonly} or @code{non-unsettable}
+variable, or encounters a variable name argument that is an invalid identifier,
+which causes a non-interactive shell to exit.
+
@item
A non-interactive shell exits with an error status if a variable
assignment error occurs when no command name follows the assignment
Bash removes an exited background process's status from the list of such
statuses after the @code{wait} builtin is used to obtain it.
+@item
+A double quote character (@samp{"}) is treated specially when it appears
+in a backquoted command substitution in the body of a here-document that
+undergoes expansion.
+That means, for example, that a backslash preceding a double quote
+character will escape it and the backslash will be removed.
+
@end enumerate
There is other @sc{posix} behavior that Bash does not implement by
#endif
result = sh_regmatch (arg1, arg2, mflags);
+ if (result == 2)
+ builtin_error (_("invalid regular expression `%s'"), arg2);
}
else
#endif /* COND_REGEXP */
case EX_REDIRFAIL:
case EX_BADASSIGN:
case EX_EXPFAIL:
+ case EX_UTILERROR:
r = EXECUTION_FAILURE;
break;
default:
rl_forced_update_display (void)
{
register char *temp;
- register int tlen;
if (visible_line)
memset (visible_line, 0, line_size);
specifications from word designators. Static for now */
static char *history_event_delimiter_chars = HISTORY_EVENT_DELIMITERS;
-static char *get_history_word_specifier (char *, char *, int *);
+static char *get_history_word_specifier (const char *, char *, int *);
static int history_tokenize_word (const char *, int);
static char **history_tokenize_internal (const char *, int, int *);
static char *history_substring (const char *, int, int);
static void freewords (char **, int);
-static char *history_find_word (char *, int);
+static char *history_find_word (const char *, int);
static char *quote_breaks (char *);
to the closing single quote. FLAGS currently used to allow backslash
to escape a single quote (e.g., for bash $'...'). */
static void
-hist_string_extract_single_quoted (char *string, int *sindex, int flags)
+hist_string_extract_single_quoted (const char *string, int *sindex, int flags)
{
register int i;
}
static char *
-hist_error(char *s, int start, int current, int errtype)
+hist_error(const char *s, int start, int current, int errtype)
{
char *temp;
const char *emsg;
subst_rhs is allowed to be set to the empty string. */
static char *
-get_subst_pattern (char *str, int *iptr, int delimiter, int is_rhs, int *lenptr)
+get_subst_pattern (const char *str, int *iptr, int delimiter, int is_rhs, int *lenptr)
{
register int si, i, j, k;
char *s;
*END_INDEX_PTR, and the expanded specifier in *RET_STRING. */
/* need current line for !# */
static int
-history_expand_internal (char *string, int start, int qc, int *end_index_ptr, char **ret_string, char *current_line)
+history_expand_internal (const char *string, int start, int qc, int *end_index_ptr, char **ret_string, char *current_line)
{
int i, n, starting_index;
int substitute_globally, subst_bywords, want_quotes, print_only;
CALLER_INDEX is the offset in SPEC to start looking; it is updated
to point to just after the last character parsed. */
static char *
-get_history_word_specifier (char *spec, char *from, int *caller_index)
+get_history_word_specifier (const char *spec, char *from, int *caller_index)
{
register int i = *caller_index;
int first, last;
in the history line LINE. Used to save the word matched by the
last history !?string? search. */
static char *
-history_find_word (char *line, int ind)
+history_find_word (const char *line, int ind)
{
char **words, *s;
int i, wind;
}
static int
-_rl_find_next_mbchar_internal (char *string, int seed, int count, int find_non_zero)
+_rl_find_next_mbchar_internal (const char *string, int seed, int count, int find_non_zero)
{
size_t tmp, len;
mbstate_t ps;
}
static inline int
-_rl_test_nonzero (char *string, int ind, int len)
+_rl_test_nonzero (const char *string, int ind, int len)
{
size_t tmp;
WCHAR_T wc;
/* experimental -- needs to handle zero-width characters better */
static int
-_rl_find_prev_utf8char (char *string, int seed, int find_non_zero)
+_rl_find_prev_utf8char (const char *string, int seed, int find_non_zero)
{
char *s;
unsigned char b;
}
/*static*/ int
-_rl_find_prev_mbchar_internal (char *string, int seed, int find_non_zero)
+_rl_find_prev_mbchar_internal (const char *string, int seed, int find_non_zero)
{
mbstate_t ps;
int prev, non_zero_prev, point, length;
if an invalid multibyte sequence was encountered. It returns (size_t)(-2)
if it couldn't parse a complete multibyte character. */
int
-_rl_get_char_len (char *src, mbstate_t *ps)
+_rl_get_char_len (const char *src, mbstate_t *ps)
{
size_t tmp, l;
int mb_cur_max;
else
{
mb_cur_max = MB_CUR_MAX;
- tmp = mbrlen((const char *)src, (l < mb_cur_max) ? l : mb_cur_max, ps);
+ tmp = mbrlen(src, (l < mb_cur_max) ? l : mb_cur_max, ps);
}
if (tmp == (size_t)(-2))
{
/* compare the specified two characters. If the characters matched,
return 1. Otherwise return 0. */
int
-_rl_compare_chars (char *buf1, int pos1, mbstate_t *ps1, char *buf2, int pos2, mbstate_t *ps2)
+_rl_compare_chars (const char *buf1, int pos1, mbstate_t *ps1, const char *buf2, int pos2, mbstate_t *ps2)
{
int i, w1, w2;
if point is invalid (point < 0 || more than string length),
it returns -1 */
int
-_rl_adjust_point (char *string, int point, mbstate_t *ps)
+_rl_adjust_point (const char *string, int point, mbstate_t *ps)
{
size_t tmp;
int length, pos;
}
int
-_rl_is_mbchar_matched (char *string, int seed, int end, char *mbchar, int length)
+_rl_is_mbchar_matched (const char *string, int seed, int end, char *mbchar, int length)
{
int i;
}
WCHAR_T
-_rl_char_value (char *buf, int ind)
+_rl_char_value (const char *buf, int ind)
{
size_t tmp;
WCHAR_T wc;
characters. */
#undef _rl_find_next_mbchar
int
-_rl_find_next_mbchar (char *string, int seed, int count, int flags)
+_rl_find_next_mbchar (const char *string, int seed, int count, int flags)
{
#if defined (HANDLE_MULTIBYTE)
return _rl_find_next_mbchar_internal (string, seed, count, flags);
we look for non-zero-width multibyte characters. */
#undef _rl_find_prev_mbchar
int
-_rl_find_prev_mbchar (char *string, int seed, int flags)
+_rl_find_prev_mbchar (const char *string, int seed, int flags)
{
#if defined (HANDLE_MULTIBYTE)
return _rl_find_prev_mbchar_internal (string, seed, flags);
{
(*rl_redisplay_function) ();
_rl_want_redisplay = 0;
- memcpy ((void *)_rl_top_level, (void *)olevel, sizeof (procenv_t));
+ if (RL_ISSTATE (RL_STATE_CALLBACK))
+ memcpy ((void *)_rl_top_level, (void *)olevel, sizeof (procenv_t));
/* If we longjmped because of a timeout, handle it here. */
if (RL_ISSTATE (RL_STATE_TIMEOUT))
#define MB_FIND_ANY 0x00
#define MB_FIND_NONZERO 0x01
-extern int _rl_find_prev_mbchar (char *, int, int);
-extern int _rl_find_next_mbchar (char *, int, int, int);
+extern int _rl_find_prev_mbchar (const char *, int, int);
+extern int _rl_find_next_mbchar (const char *, int, int, int);
#ifdef HANDLE_MULTIBYTE
-extern int _rl_compare_chars (char *, int, mbstate_t *, char *, int, mbstate_t *);
-extern int _rl_get_char_len (char *, mbstate_t *);
-extern int _rl_adjust_point (char *, int, mbstate_t *);
+extern int _rl_compare_chars (const char *, int, mbstate_t *, const char *, int, mbstate_t *);
+extern int _rl_get_char_len (const char *, mbstate_t *);
+extern int _rl_adjust_point (const char *, int, mbstate_t *);
extern int _rl_read_mbchar (char *, int);
extern int _rl_read_mbstring (int, char *, int);
-extern int _rl_is_mbchar_matched (char *, int, int, char *, int);
+extern int _rl_is_mbchar_matched (const char *, int, int, char *, int);
-extern WCHAR_T _rl_char_value (char *, int);
+extern WCHAR_T _rl_char_value (const char *, int);
extern int _rl_walphabetic (WCHAR_T);
#define _rl_to_wupper(wc) (iswlower (wc) ? towupper (wc) : (wc))
char *expanded;
alias_t *ap;
-#if 0
- if (((parser_state & PST_ALEXPNEXT) || command_token_position (last_read_token)) &&
- (parser_state & PST_CASEPAT) == 0)
-#else
if ((parser_state & PST_ALEXPNEXT) || assignment_acceptable (last_read_token))
-#endif
{
ap = find_alias (tokstr);
regexp `^#define[ ]*PATCHLEVEL', since that's what support/mkversion.sh
looks for to find the patch level (for the sccs version string). */
-#define PATCHLEVEL 12
+#define PATCHLEVEL 15
#endif /* _PATCHLEVEL_H_ */
#define EX_BADASSIGN 260 /* variable assignment error */
#define EX_EXPFAIL 261 /* word expansion failed */
#define EX_DISKFALLBACK 262 /* fall back to disk command from builtin */
+#define EX_UTILERROR 263 /* Posix special builtin utility error */
/* Flag values that control parameter pattern substitution. */
#define MATCH_ANY 0x000
#define VT_STARSUB 128 /* $* or ${array[*]} -- used to split */
-
/* Flags for quoted_strchr */
#define ST_BACKSL 0x01
#define ST_CTLESC 0x02
t = extract_command_subst (string, &si, flags);
CHECK_STRING_OVERRUN (i, si, slen, c);
- tlen = si - i - 1;
+ tlen = si - i - 2;
RESIZE_MALLOCED_BUFFER (result, result_index, tlen + 4, result_size, 64);
result[result_index++] = c;
result[result_index++] = LPAREN;
t = extract_process_subst (string, (string[i] == '<' ? "<(" : ">)"), &si, flags);
CHECK_STRING_OVERRUN (i, si, slen, c);
- tlen = si - i - 1;
+ tlen = si - i - 2;
RESIZE_MALLOCED_BUFFER (result, result_index, tlen + 4, result_size, 64);
result[result_index++] = c;
result[result_index++] = LPAREN;
/* Remove backslashes which are quoting backquotes from STRING. Modifies
STRING, and returns a pointer to it. */
char *
-de_backslash (string)
+de_backslash (string, qflags)
char *string;
+ int qflags;
{
register size_t slen;
register int i, j, prev_i;
if (string[i] == '\\' && (string[i + 1] == '`' || string[i + 1] == '\\' ||
string[i + 1] == '$'))
i++;
+ else if (posixly_correct && (qflags & Q_HERE_DOCUMENT) && string[i] == '\\' && string[i + 1] == '"')
+ i++;
prev_i = i;
ADVANCE_CHAR (string, slen, i);
if (j < prev_i)
temp1 = substring (string, t_index, sindex + 1);
else
{
- de_backslash (temp);
+ de_backslash (temp, quoted);
tword = command_substitute (temp, quoted, PF_BACKQUOTE);
temp1 = tword ? tword->word : (char *)NULL;
if (tword)
/* Remove backslashes which are quoting backquotes from STRING. Modifies
STRING, and returns a pointer to it. */
-extern char * de_backslash PARAMS((char *));
+extern char * de_backslash PARAMS((char *, int));
/* Replace instances of \! in a string with !. */
extern void unquote_bang PARAMS((char *));
+ command -p -- command -v type
type
+ set +x
-./builtins.tests: line 284: exit: status: numeric argument required
+u=rw,g=rx,o=rx
+u=r,g=rx,o=rx
+u=rwx,g=rwx,o=
+u=rw,g=wx,o=rx
+u=rx,g=rx,o=rx
+u=rwx,g=rx,o=rwx
+u=rwx,g=rwx,o=rx
+u=rx,g=rx,o=rx
+u=rwx,g=rx,o=rx
+u=rwx,g=rwx,o=rwx
+u=rwx,g=rwx,o=rwx
+u=rwx,g=rx,o=rx
+u=rwx,g=rx,o=rx
+./builtins.tests: line 287: exit: status: numeric argument required
unset -v AVAR BVAR
-# test umask
+# basic umask tests
mask=$(umask)
umask 022
umask
# test behavior of command builtin after changing it to a pseudo-keyword
${THIS_SH} ./builtins7.sub
+# POSIX complete symbolic umask tests
+${THIS_SH} ./builtins8.sub
+
# this must be last -- it is a fatal error
exit status
--- /dev/null
+umask 022
+umask u=r+w
+umask -S
+
+umask 022
+umask u=r-w
+umask -S
+
+umask 022
+umask g+u,o+rwx-u
+umask -S
+
+umask 022
+umask u=r+w,g=wx,o+xr
+umask -S
+
+umask 022
+umask u+w=r+x
+umask -S
+
+umask 022
+umask o=u
+umask -S
+
+umask 022
+umask g=u
+umask -S
+
+umask 022
+umask u=rwx,u-w
+umask -S
+
+umask 022
+umask u=xwr
+umask -S
+
+umask 022
+umask +xwr
+umask -S
+
+umask 022
+umask a+xwr
+umask -S
+
+umask 022
+umask +xr
+umask -S
+
+umask 022
+umask a+xr
+umask -S
+
+
+
)
)
+BUILDDIR=/builds/test
+read << EOC
+Dir: ${BUILDDIR#<(echo a)/}
+EOC
+
${THIS_SH} ./comsub1.sub
${THIS_SH} ./comsub2.sub
${THIS_SH} ./comsub3.sub
match control-a 3
match control-a 4
match control-a 5
+./cond-regexp2.sub: line 18: [[: invalid regular expression `[\.'
ok 1
ok 2
ok 3
./errors.tests: line 157: umask: `:': invalid symbolic mode operator
./errors.tests: line 160: umask: -i: invalid option
umask: usage: umask [-p] [-S] [mode]
-./errors.tests: line 164: umask: `u': invalid symbolic mode character
+./errors.tests: line 164: umask: `p': invalid symbolic mode character
./errors.tests: line 173: VAR: readonly variable
./errors.tests: line 176: declare: VAR: readonly variable
./errors.tests: line 177: declare: VAR: readonly variable
bash: line 1: return: can only `return' from a function or sourced script
after return
bash: line 1: return: can only `return' from a function or sourced script
-./errors.tests: line 305: `!!': not a valid identifier
+sh: line 1: unset: a: cannot unset: readonly variable
+sh: line 1: unset: `a-b': not a valid identifier
+sh: line 1: /nosuchfile: No such file or directory
+sh: line 1: trap: SIGNOSIG: invalid signal specification
+after trap
+./errors.tests: line 316: `!!': not a valid identifier
# bad assignments shouldn't change the umask
mask=$(umask)
-umask g=u
+umask g=p
mask2=$(umask)
if [ "$mask" != "$mask2" ]; then
echo "umask errors change process umask"
${THIS_SH} -c 'return ; echo after return' bash
${THIS_SH} -o posix -c 'return ; echo after return' bash
+# various posix-mode special builtin fatal (or not) errors
+
+# posix says unsetting readonly variables is a fatal error
+${THIS_SH} -o posix -c 'readonly a=a ; unset -v a; echo after unset 1' sh
+# the same with non-identifiers
+${THIS_SH} -o posix -c 'unset -v a-b; echo after unset 2' sh
+# and sourcing a non-existent file is fatal too
+${THIS_SH} -o posix -c '. /nosuchfile ; echo after source' sh
+# but trap specifying a bad signal nunber is non-fatal
+${THIS_SH} -o posix -c 'trap "echo bad" SIGNOSIG; echo after trap' sh
+
# this must be last!
# in posix mode, a function name must be a valid identifier
# this can't go in posix2.tests, since it causes the shell to exit
/* trap.c -- Not the trap command, but useful functions for manipulating
those objects. The trap command is in builtins/trap.def. */
-/* Copyright (C) 1987-2021 Free Software Foundation, Inc.
+/* Copyright (C) 1987-2022 Free Software Foundation, Inc.
This file is part of GNU Bash, the Bourne Again SHell.
trap command (e.g., when `return' is executed in the trap command). */
int running_trap;
-/* The variable context (function execution level) when we began running
- this trap command. */
-int trap_variable_context;
+/* The execution context (function/source execution level) when we began
+ running this trap command. This is used to determine whether we have
+ executed any shell functions or sourced files from the trap action, and
+ determines where `return' without arguments gets its return status. */
+int trap_return_context;
/* Set to last_command_exit_value before running a trap. */
int trap_saved_exit_value;
ps = save_pipestatus_array ();
#endif
old_running = running_trap;
- old_context = trap_variable_context;
+ old_context = trap_return_context;
for (sig = 1; sig < NSIG; sig++)
{
{
/* XXX - set last_command_exit_value = trap_saved_exit_value here? */
running_trap = sig + 1;
- trap_variable_context = variable_context;
+ trap_return_context = funcnest + sourcenest;
if (sig == SIGINT)
{
if (function_code)
{
running_trap = old_running; /* XXX */
- trap_variable_context = old_context;
+ trap_return_context = old_context;
/* caller will set last_command_exit_value */
sh_longjmp (return_catch, 1);
}
pending_traps[sig] = 0; /* XXX - move before evalstring? */
running_trap = old_running;
- trap_variable_context = old_context;
+ trap_return_context = old_context;
}
}
retval = trap_saved_exit_value;
running_trap = 1;
- trap_variable_context = variable_context;
+ trap_return_context = funcnest + sourcenest;
code = setjmp_nosigs (top_level);
old_trap = trap_list[sig];
old_modes = sigmodes[sig];
old_running = running_trap;
- old_context = trap_variable_context;
+ old_context = trap_return_context;
sigmodes[sig] |= SIG_INPROGRESS;
sigmodes[sig] &= ~SIG_CHANGED; /* just to be sure */
trap_command = savestring (old_trap);
running_trap = sig + 1;
- trap_variable_context = variable_context;
+ trap_return_context = funcnest + sourcenest;
old_int = interrupt_state; /* temporarily suppress pending interrupts */
CLRINTERRUPT;
running_trap = old_running;
interrupt_state = old_int;
- trap_variable_context = old_context;
+ trap_return_context = old_context;
if (sigmodes[sig] & SIG_CHANGED)
{
extern int trapped_signal_received;
extern int wait_signal_received;
extern int running_trap;
-extern int trap_variable_context;
+extern int trap_return_context;
extern int trap_saved_exit_value;
extern int suppress_debug_trap_verbose;