summaryrefslogtreecommitdiffhomepage
diff options
context:
space:
mode:
authorRoberto A. Foglietta <roberto.foglietta@gmail.com>2021-09-07 01:19:31 +0200
committerDenys Vlasenko <vda.linux@googlemail.com>2021-09-07 01:28:50 +0200
commite0bf3df0205d5ccef52df67b1760b8b54f15ec6e (patch)
tree3195ba74f0b9c3a93e36743a8693c8cd880f78af
parent4b032a4d6c4a9e24e66c262d6350e6499d0facb3 (diff)
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>
-rw-r--r--shell/ash.c86
1 files changed, 69 insertions, 17 deletions
diff --git a/shell/ash.c b/shell/ash.c
index 53c140930..cfe0433a8 100644
--- a/shell/ash.c
+++ b/shell/ash.c
@@ -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 */
}