ash: add bash-like ERR trap and set -E

While at it, stop incrementing LINENO inside traps

function                                             old     new   delta
evaltree                                             567     762    +195
evalfun                                              268     348     +80
trapcmd                                              286     333     +47
dotrap                                               129     157     +28
exitshell                                            120     139     +19
readtoken1                                          3096    3110     +14
nlprompt                                              25      39     +14
nlnoprompt                                            19      33     +14
.rodata                                           104245  104255     +10
forkchild                                            610     617      +7
optletters_optnames                                   64      68      +4
------------------------------------------------------------------------------
(add/remove: 0/0 grow/shrink: 11/0 up/down: 432/0)            Total: 432 bytes

Signed-off-by: Roberto A. Foglietta <roberto.foglietta@gmail.com>
Signed-off-by: Denys Vlasenko <vda.linux@googlemail.com>
This commit is contained in:
Roberto A. Foglietta 2021-09-07 01:19:31 +02:00 committed by Denys Vlasenko
parent 4b032a4d6c
commit e0bf3df020

View File

@ -348,6 +348,7 @@ static const char *const optletters_optnames[] = {
"a" "allexport",
"b" "notify",
"u" "nounset",
"E" "errtrace",
"\0" "vi"
#if BASH_PIPEFAIL
,"\0" "pipefail"
@ -442,15 +443,16 @@ struct globals_misc {
#define aflag optlist[11]
#define bflag optlist[12]
#define uflag optlist[13]
#define viflag optlist[14]
#define Eflag optlist[14]
#define viflag optlist[15]
#if BASH_PIPEFAIL
# define pipefail optlist[15]
# define pipefail optlist[16]
#else
# define pipefail 0
#endif
#if DEBUG
# define nolog optlist[15 + BASH_PIPEFAIL]
# define debug optlist[16 + BASH_PIPEFAIL]
# define nolog optlist[16 + BASH_PIPEFAIL]
# define debug optlist[17 + BASH_PIPEFAIL]
#endif
/* trap handler commands */
@ -468,7 +470,11 @@ struct globals_misc {
/* indicates specified signal received */
uint8_t gotsig[NSIG - 1]; /* offset by 1: "signal" 0 is meaningless */
uint8_t may_have_traps; /* 0: definitely no traps are set, 1: some traps may be set */
char *trap[NSIG];
char *trap[NSIG + 1];
/* trap[0] is EXIT trap, trap[NTRAP_ERR] is ERR trap, other trap[i] are signal traps */
#define NTRAP_ERR NSIG
#define NTRAP_LAST NSIG
char **trap_ptr; /* used only by "trap hack" */
/* Rarely referenced stuff */
@ -2166,6 +2172,8 @@ struct globals_var {
struct var varinit[ARRAY_SIZE(varinit_data)];
int lineno;
char linenovar[sizeof("LINENO=") + sizeof(int)*3];
unsigned trap_depth;
bool in_trap_ERR; /* ERR cannot recurse, no need to be a counter */
};
extern struct globals_var *BB_GLOBAL_CONST ash_ptr_to_globals_var;
#define G_var (*ash_ptr_to_globals_var)
@ -2176,6 +2184,8 @@ extern struct globals_var *BB_GLOBAL_CONST ash_ptr_to_globals_var;
#define varinit (G_var.varinit )
#define lineno (G_var.lineno )
#define linenovar (G_var.linenovar )
#define trap_depth (G_var.trap_depth )
#define in_trap_ERR (G_var.in_trap_ERR )
#define vifs varinit[0]
#if ENABLE_ASH_MAIL
# define vmail varinit[1]
@ -5163,13 +5173,13 @@ clear_traps(void)
char **tp;
INT_OFF;
for (tp = trap; tp < &trap[NSIG]; tp++) {
for (tp = trap; tp <= &trap[NTRAP_LAST]; tp++) {
if (*tp && **tp) { /* trap not NULL or "" (SIG_IGN) */
if (trap_ptr == trap)
free(*tp);
/* else: it "belongs" to trap_ptr vector, don't free */
*tp = NULL;
if ((tp - trap) != 0)
if ((tp - trap) != 0 && (tp - trap) < NSIG)
setsignal(tp - trap);
}
}
@ -9253,7 +9263,9 @@ dotrap(void)
*g = 0;
if (!p)
continue;
trap_depth++;
evalstring(p, 0);
trap_depth--;
if (evalskip != SKIPFUNC)
exitstatus = status;
}
@ -9321,7 +9333,7 @@ evaltree(union node *n, int flags)
case NCMD:
evalfn = evalcommand;
checkexit:
if (eflag && !(flags & EV_TESTED))
if (!(flags & EV_TESTED))
checkexit = ~0;
goto calleval;
case NFOR:
@ -9395,8 +9407,32 @@ evaltree(union node *n, int flags)
*/
dotrap();
if (checkexit & status)
raise_exception(EXEND);
if (checkexit & status) {
if (trap[NTRAP_ERR] && !in_trap_ERR) {
int err;
struct jmploc *volatile savehandler = exception_handler;
struct jmploc jmploc;
in_trap_ERR = 1;
trap_depth++;
err = setjmp(jmploc.loc);
if (!err) {
exception_handler = &jmploc;
savestatus = exitstatus;
evalstring(trap[NTRAP_ERR], 0);
}
trap_depth--;
in_trap_ERR = 0;
exception_handler = savehandler;
if (err && exception_type != EXERROR)
longjmp(exception_handler->loc, 1);
exitstatus = savestatus;
}
if (eflag)
raise_exception(EXEND);
}
if (flags & EV_EXIT)
raise_exception(EXEND);
@ -9861,7 +9897,12 @@ evalfun(struct funcnode *func, int argc, char **argv, int flags)
struct jmploc jmploc;
int e;
int savefuncline;
char *savetrap = NULL;
if (!Eflag) {
savetrap = trap[NTRAP_ERR];
trap[NTRAP_ERR] = NULL;
}
saveparam = shellparam;
savefuncline = funcline;
savehandler = exception_handler;
@ -9884,6 +9925,12 @@ evalfun(struct funcnode *func, int argc, char **argv, int flags)
evaltree(func->n.ndefun.body, flags & EV_TESTED);
funcdone:
INT_OFF;
if (savetrap) {
if (!trap[NTRAP_ERR])
trap[NTRAP_ERR] = savetrap;
else
free(savetrap);
}
funcline = savefuncline;
freefunc(func);
freeparam(&shellparam);
@ -10912,13 +10959,15 @@ preadbuffer(void)
static void
nlprompt(void)
{
g_parsefile->linno++;
if (trap_depth == 0)
g_parsefile->linno++;
setprompt_if(doprompt, 2);
}
static void
nlnoprompt(void)
{
g_parsefile->linno++;
if (trap_depth == 0)
g_parsefile->linno++;
needprompt = doprompt;
}
@ -12620,7 +12669,8 @@ checkend: {
if (c == '\n' || c == PEOF) {
c = PEOF;
g_parsefile->linno++;
if (trap_depth == 0)
g_parsefile->linno++;
needprompt = doprompt;
} else {
int len_here;
@ -13869,7 +13919,7 @@ trapcmd(int argc UNUSED_PARAM, char **argv UNUSED_PARAM)
nextopt(nullstr);
ap = argptr;
if (!*ap) {
for (signo = 0; signo < NSIG; signo++) {
for (signo = 0; signo <= NTRAP_LAST; signo++) {
char *tr = trap_ptr[signo];
if (tr) {
/* note: bash adds "SIG", but only if invoked
@ -13878,7 +13928,7 @@ trapcmd(int argc UNUSED_PARAM, char **argv UNUSED_PARAM)
* We are printing short names: */
out1fmt("trap -- %s %s\n",
single_quote(tr),
get_signame(signo));
(signo == NTRAP_ERR) ? "ERR" : get_signame(signo));
/* trap_ptr != trap only if we are in special-cased `trap` code.
* In this case, we will exit very soon, no need to free(). */
/* if (trap_ptr != trap && tp[0]) */
@ -13904,7 +13954,7 @@ trapcmd(int argc UNUSED_PARAM, char **argv UNUSED_PARAM)
exitcode = 0;
while (*ap) {
signo = get_signum(*ap);
signo = strcmp(*ap, "ERR") == 0 ? NTRAP_ERR : get_signum(*ap);
if (signo < 0) {
/* Mimic bash message exactly */
ash_msg("%s: invalid signal specification", *ap);
@ -13923,7 +13973,7 @@ trapcmd(int argc UNUSED_PARAM, char **argv UNUSED_PARAM)
}
free(trap[signo]);
trap[signo] = action;
if (signo != 0)
if (signo != 0 && signo < NSIG)
setsignal(signo);
INT_ON;
next:
@ -14348,7 +14398,9 @@ exitshell(void)
if (p) {
trap[0] = NULL;
evalskip = 0;
trap_depth++;
evalstring(p, 0);
trap_depth--;
evalskip = SKIPFUNCDEF;
/*free(p); - we'll exit soon */
}