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