mirror of
git://git.code.sf.net/p/zsh/code
synced 2025-09-02 22:11:54 +02:00
25415: Make DEBUG_BEFORE_CMD the default.
Reuse ERR_EXIT in DEBUG traps. Clean up trapreturn code.
This commit is contained in:
parent
c4f33330f6
commit
2706eac454
13 changed files with 154 additions and 53 deletions
10
ChangeLog
10
ChangeLog
|
@ -1,3 +1,13 @@
|
|||
2008-08-07 Peter Stephenson <pws@csr.com>
|
||||
|
||||
* 25415: README, Doc/Zsh/builtins.yo, Doc/Zsh/func.yo,
|
||||
Doc/Zsh/options.yo, Src/builtin.c, Src/exec.c, Src/init.c,
|
||||
Src/options.c, Src/signals.c, Src/zsh.h, Test/A05execution.ztst,
|
||||
Test/C03traps.ztst: Make DEBUG_BEFORE_CMD the default;
|
||||
make ERR_EXIT ineffective in DEBUG traps but allow it to
|
||||
be set to skip the next command (actually sublist); tidy
|
||||
up code associated with trapreturn.
|
||||
|
||||
2008-08-06 Peter Stephenson <pws@csr.com>
|
||||
|
||||
* 25405: Src/exec.c: return value was not set from anonymous
|
||||
|
|
|
@ -1304,8 +1304,15 @@ If var(sig) is tt(ZERR) then var(arg) will be executed
|
|||
after each command with a nonzero exit status. tt(ERR) is an alias
|
||||
for tt(ZERR) on systems that have no tt(SIGERR) signal (this is the
|
||||
usual case).
|
||||
|
||||
If var(sig) is tt(DEBUG) then var(arg) will be executed
|
||||
after each command.
|
||||
before each command if the option tt(DEBUG_BEFORE_CMD) is set
|
||||
(as it is by default), else after each command. In the former
|
||||
case it is possible to skip the next command; see
|
||||
the description of the tt(ERR_EXIT) option in
|
||||
ifzman(zmanref(zshoptions))\
|
||||
ifnzman(noderef(Description of Options)).
|
||||
|
||||
If var(sig) is tt(0) or tt(EXIT)
|
||||
and the tt(trap) statement is executed inside the body of a function,
|
||||
then the command var(arg) is executed after the function completes.
|
||||
|
|
|
@ -308,8 +308,12 @@ executed inside other traps.
|
|||
)
|
||||
findex(TRAPDEBUG)
|
||||
item(tt(TRAPDEBUG))(
|
||||
Executed after each command. If the option tt(DEBUG_BEFORE_CMD)
|
||||
is set, executed before each command instead.
|
||||
If the option tt(DEBUG_BEFORE_CMD) is set (as it is by default), executed
|
||||
before each command; otherwise executed after each command. In the former
|
||||
case it is possible to skip the next command; see the description of the
|
||||
tt(ERR_EXIT) option in
|
||||
ifzman(zmanref(zshoptions))\
|
||||
ifnzman(noderef(Description of Options)).
|
||||
)
|
||||
findex(TRAPEXIT)
|
||||
item(tt(TRAPEXIT))(
|
||||
|
|
|
@ -1046,7 +1046,7 @@ ifnzman(Arithmetic Evaluation)\
|
|||
ifzman(the section ARITHMETIC EVALUATION in zmanref(zshmisc))
|
||||
has an explicit list.
|
||||
)
|
||||
pindex(DEBUG_BEFORE_CMD)
|
||||
pindex(DEBUG_BEFORE_CMD <D>)
|
||||
cindex(traps, DEBUG, before or after command)
|
||||
cindex(DEBUG trap, before or after command)
|
||||
item(tt(DEBUG_BEFORE_CMD))(
|
||||
|
@ -1060,6 +1060,13 @@ item(tt(ERR_EXIT) (tt(-e), ksh: tt(-e)))(
|
|||
If a command has a non-zero exit status, execute the tt(ZERR)
|
||||
trap, if set, and exit. This is disabled while running initialization
|
||||
scripts.
|
||||
|
||||
The behaviour is also disabled inside tt(DEBUG) traps. In this
|
||||
case the option is handled specially: it is unset on entry to
|
||||
the trap. If the option tt(DEBUG_BEFORE_CMD) is set,
|
||||
as it is by default, and the option tt(ERR_EXIT) is found to have been set
|
||||
on exit, then the command for which the tt(DEBUG) trap is being executed is
|
||||
skipped. The option is restored after the trap exits.
|
||||
)
|
||||
pindex(ERR_RETURN)
|
||||
cindex(function return, on error)
|
||||
|
|
6
README
6
README
|
@ -56,6 +56,12 @@ behaviour.) Now it is treated identically to "$@". The same change
|
|||
applies to expressions with forced splitting such as ${=1+"$@"}, but
|
||||
otherwise the case where SH_WORD_SPLIT is not set is unaffected.
|
||||
|
||||
Debug traps (`trap ... DEBUG' or the function TRAPDEBUG) now run by default
|
||||
before the command to which they refer instead of after. This is almost
|
||||
always the right behaviour for the intended purpose of debugging and is
|
||||
consistent with recent versions of other shells. The option
|
||||
DEBUG_BEFORE_CMD can be unset to revert to the previous behaviour.
|
||||
|
||||
In previous versions of the shell it was possible to use index 0 in an
|
||||
array or string subscript to refer to the same element as index 1 if the
|
||||
option KSH_ARRAYS was not in effect. This was a limited approximation to
|
||||
|
|
|
@ -4462,8 +4462,10 @@ bin_break(char *name, char **argv, UNUSED(Options ops), int func)
|
|||
retflag = 1;
|
||||
breaks = loops;
|
||||
lastval = num;
|
||||
if (trapreturn == -2)
|
||||
trapreturn = lastval;
|
||||
if (trap_state == TRAP_STATE_PRIMED && trap_return == -2) {
|
||||
trap_state = TRAP_STATE_FORCE_RETURN;
|
||||
trap_return = lastval;
|
||||
}
|
||||
return lastval;
|
||||
}
|
||||
zexit(num, 0); /* else treat return as logout/exit */
|
||||
|
|
58
Src/exec.c
58
Src/exec.c
|
@ -65,16 +65,23 @@ int nohistsave;
|
|||
mod_export int errflag;
|
||||
|
||||
/*
|
||||
* Status of return from a trap.
|
||||
* State of trap return value. Value is from enum trap_state.
|
||||
*/
|
||||
|
||||
/**/
|
||||
int trap_state;
|
||||
|
||||
/*
|
||||
* Value associated with return from a trap.
|
||||
* This is only active if we are inside a trap, else its value
|
||||
* is irrelevant. It is initialised to -1 for a function trap and
|
||||
* -2 for a non-function trap and if negative is decremented as
|
||||
* we go deeper into functions and incremented as we come back up.
|
||||
* The value is used to decide if an explicit "return" should cause
|
||||
* a return from the caller of the trap; it does this by setting
|
||||
* trapreturn to a status (i.e. a non-negative value).
|
||||
* trap_return to a status (i.e. a non-negative value).
|
||||
*
|
||||
* In summary, trapreturn is
|
||||
* In summary, trap_return is
|
||||
* - zero unless we are in a trap
|
||||
* - negative in a trap unless it has triggered. Code uses this
|
||||
* to detect an active trap.
|
||||
|
@ -83,7 +90,7 @@ mod_export int errflag;
|
|||
*/
|
||||
|
||||
/**/
|
||||
int trapreturn;
|
||||
int trap_return;
|
||||
|
||||
/* != 0 if this is a subshell */
|
||||
|
||||
|
@ -1061,6 +1068,10 @@ execlist(Estate state, int dont_change_job, int exiting)
|
|||
}
|
||||
|
||||
if (sigtrapped[SIGDEBUG] && isset(DEBUGBEFORECMD)) {
|
||||
int oerrexit_opt = opts[ERREXIT];
|
||||
opts[ERREXIT] = 0;
|
||||
noerrexit = 1;
|
||||
|
||||
exiting = donetrap;
|
||||
ret = lastval;
|
||||
dotrap(SIGDEBUG);
|
||||
|
@ -1071,7 +1082,8 @@ execlist(Estate state, int dont_change_job, int exiting)
|
|||
* Only execute the trap once per sublist, even
|
||||
* if the DEBUGBEFORECMD option changes.
|
||||
*/
|
||||
donedebug = 1;
|
||||
donedebug = isset(ERREXIT) ? 2 : 1;
|
||||
opts[ERREXIT] = oerrexit_opt;
|
||||
} else
|
||||
donedebug = 0;
|
||||
|
||||
|
@ -1087,6 +1099,18 @@ execlist(Estate state, int dont_change_job, int exiting)
|
|||
|
||||
/* Loop through code followed by &&, ||, or end of sublist. */
|
||||
code = *state->pc++;
|
||||
if (donedebug == 2) {
|
||||
/* Skip sublist. */
|
||||
while (wc_code(code) == WC_SUBLIST) {
|
||||
state->pc = state->pc + WC_SUBLIST_SKIP(code);
|
||||
if (WC_SUBLIST_TYPE(code) == WC_SUBLIST_END)
|
||||
break;
|
||||
code = *state->pc++;
|
||||
}
|
||||
donetrap = 1;
|
||||
/* yucky but consistent... */
|
||||
goto sublist_done;
|
||||
}
|
||||
while (wc_code(code) == WC_SUBLIST) {
|
||||
next = state->pc + WC_SUBLIST_SKIP(code);
|
||||
if (!oldnoerrexit)
|
||||
|
@ -1177,12 +1201,20 @@ sublist_done:
|
|||
noerrexit = oldnoerrexit;
|
||||
|
||||
if (sigtrapped[SIGDEBUG] && !isset(DEBUGBEFORECMD) && !donedebug) {
|
||||
/*
|
||||
* Save and restore ERREXIT for consistency with
|
||||
* DEBUGBEFORECMD, even though it's not used.
|
||||
*/
|
||||
int oerrexit_opt = opts[ERREXIT];
|
||||
opts[ERREXIT] = 0;
|
||||
noerrexit = 1;
|
||||
exiting = donetrap;
|
||||
ret = lastval;
|
||||
dotrap(SIGDEBUG);
|
||||
lastval = ret;
|
||||
donetrap = exiting;
|
||||
noerrexit = oldnoerrexit;
|
||||
opts[ERREXIT] = oerrexit_opt;
|
||||
}
|
||||
|
||||
cmdsp = csp;
|
||||
|
@ -4145,8 +4177,8 @@ doshfunc(char *name, Eprog prog, LinkList doshargs, int flags, int noreturnval)
|
|||
|
||||
oargv0 = NULL;
|
||||
obreaks = breaks;;
|
||||
if (trapreturn < 0)
|
||||
trapreturn--;
|
||||
if (trap_state == TRAP_STATE_PRIMED)
|
||||
trap_return--;
|
||||
oldlastval = lastval;
|
||||
oldnumpipestats = numpipestats;
|
||||
if (noreturnval) {
|
||||
|
@ -4264,8 +4296,8 @@ doshfunc(char *name, Eprog prog, LinkList doshargs, int flags, int noreturnval)
|
|||
|
||||
endtrapscope();
|
||||
|
||||
if (trapreturn < -1)
|
||||
trapreturn++;
|
||||
if (trap_state == TRAP_STATE_PRIMED)
|
||||
trap_return++;
|
||||
ret = lastval;
|
||||
if (noreturnval) {
|
||||
lastval = oldlastval;
|
||||
|
@ -4528,7 +4560,9 @@ execsave(void)
|
|||
es->badcshglob = badcshglob;
|
||||
es->cmdoutpid = cmdoutpid;
|
||||
es->cmdoutval = cmdoutval;
|
||||
es->trapreturn = trapreturn;
|
||||
es->trap_return = trap_return;
|
||||
es->trap_state = trap_state;
|
||||
es->trapisfunc = trapisfunc;
|
||||
es->noerrs = noerrs;
|
||||
es->subsh_close = subsh_close;
|
||||
es->underscore = ztrdup(underscore);
|
||||
|
@ -4555,7 +4589,9 @@ execrestore(void)
|
|||
badcshglob = exstack->badcshglob;
|
||||
cmdoutpid = exstack->cmdoutpid;
|
||||
cmdoutval = exstack->cmdoutval;
|
||||
trapreturn = exstack->trapreturn;
|
||||
trap_return = exstack->trap_return;
|
||||
trap_state = exstack->trap_state;
|
||||
trapisfunc = exstack->trapisfunc;
|
||||
noerrs = exstack->noerrs;
|
||||
subsh_close = exstack->subsh_close;
|
||||
setunderscore(exstack->underscore);
|
||||
|
|
18
Src/init.c
18
Src/init.c
|
@ -191,10 +191,6 @@ loop(int toplevel, int justonce)
|
|||
exit(lastval);
|
||||
if (((!interact || sourcelevel) && errflag) || retflag)
|
||||
break;
|
||||
if (intrap && trapreturn >= 0) {
|
||||
lastval = trapreturn;
|
||||
trapreturn = 0;
|
||||
}
|
||||
if (isset(SINGLECOMMAND) && toplevel) {
|
||||
if (sigtrapped[SIGEXIT])
|
||||
dotrap(SIGEXIT);
|
||||
|
@ -880,7 +876,8 @@ setupvals(void)
|
|||
lastmailcheck = time(NULL);
|
||||
locallevel = sourcelevel = 0;
|
||||
sfcontext = SFC_NONE;
|
||||
trapreturn = 0;
|
||||
trap_return = 0;
|
||||
trap_state = TRAP_STATE_INACTIVE;
|
||||
noerrexit = -1;
|
||||
nohistsave = 1;
|
||||
dirstack = znewlinklist();
|
||||
|
@ -1060,6 +1057,7 @@ source(char *s)
|
|||
char *old_scriptname = scriptname, *us;
|
||||
unsigned char *ocs;
|
||||
int ocsp;
|
||||
int otrap_return = trap_return, otrap_state = trap_state;
|
||||
|
||||
if (!s ||
|
||||
(!(prog = try_source_file((us = unmeta(s)))) &&
|
||||
|
@ -1090,6 +1088,13 @@ source(char *s)
|
|||
dosetopt(SHINSTDIN, 0, 1);
|
||||
scriptname = s;
|
||||
|
||||
/*
|
||||
* The special return behaviour of traps shouldn't
|
||||
* trigger in files sourced from traps; the return
|
||||
* is just a return from the file.
|
||||
*/
|
||||
trap_state = TRAP_STATE_INACTIVE;
|
||||
|
||||
sourcelevel++;
|
||||
if (prog) {
|
||||
pushheap();
|
||||
|
@ -1100,6 +1105,9 @@ source(char *s)
|
|||
loop(0, 0); /* loop through the file to be sourced */
|
||||
sourcelevel--;
|
||||
|
||||
trap_state = otrap_state;
|
||||
trap_return = otrap_return;
|
||||
|
||||
/* restore the current shell state */
|
||||
if (prog)
|
||||
freeeprog(prog);
|
||||
|
|
|
@ -112,7 +112,7 @@ static struct optname optns[] = {
|
|||
{{NULL, "cshjunkiequotes", OPT_EMULATE|OPT_CSH}, CSHJUNKIEQUOTES},
|
||||
{{NULL, "cshnullcmd", OPT_EMULATE|OPT_CSH}, CSHNULLCMD},
|
||||
{{NULL, "cshnullglob", OPT_EMULATE|OPT_CSH}, CSHNULLGLOB},
|
||||
{{NULL, "debugbeforecmd", OPT_EMULATE}, DEBUGBEFORECMD},
|
||||
{{NULL, "debugbeforecmd", OPT_ALL}, DEBUGBEFORECMD},
|
||||
{{NULL, "emacs", 0}, EMACSMODE},
|
||||
{{NULL, "equals", OPT_EMULATE|OPT_ZSH}, EQUALS},
|
||||
{{NULL, "errexit", OPT_EMULATE}, ERREXIT},
|
||||
|
|
|
@ -1082,12 +1082,10 @@ dotrapargs(int sig, int *sigtr, void *sigfn)
|
|||
{
|
||||
LinkList args;
|
||||
char *name, num[4];
|
||||
int trapret = 0;
|
||||
int obreaks = breaks;
|
||||
int oretflag = retflag;
|
||||
int otrapreturn = trapreturn;
|
||||
int isfunc;
|
||||
int traperr;
|
||||
int traperr, new_trap_state, new_trap_return;
|
||||
|
||||
/* if signal is being ignored or the trap function *
|
||||
* is NULL, then return *
|
||||
|
@ -1123,6 +1121,7 @@ dotrapargs(int sig, int *sigtr, void *sigfn)
|
|||
*sigtr |= ZSIG_IGNORED;
|
||||
|
||||
lexsave();
|
||||
/* execsave will save the old trap_return and trap_state */
|
||||
execsave();
|
||||
breaks = retflag = 0;
|
||||
runhookdef(BEFORETRAPHOOK, NULL);
|
||||
|
@ -1148,7 +1147,8 @@ dotrapargs(int sig, int *sigtr, void *sigfn)
|
|||
sprintf(num, "%d", sig);
|
||||
zaddlinknode(args, num);
|
||||
|
||||
trapreturn = -1; /* incremented by doshfunc */
|
||||
trap_return = -1; /* incremented by doshfunc */
|
||||
trap_state = TRAP_STATE_PRIMED;
|
||||
trapisfunc = isfunc = 1;
|
||||
|
||||
sfcontext = SFC_SIGNAL;
|
||||
|
@ -1156,46 +1156,32 @@ dotrapargs(int sig, int *sigtr, void *sigfn)
|
|||
sfcontext = osc;
|
||||
freelinklist(args, (FreeFunc) NULL);
|
||||
zsfree(name);
|
||||
|
||||
} else {
|
||||
trapreturn = -2; /* not incremented, used at current level */
|
||||
trap_return = -2; /* not incremented, used at current level */
|
||||
trap_state = TRAP_STATE_PRIMED;
|
||||
trapisfunc = isfunc = 0;
|
||||
|
||||
execode(sigfn, 1, 0);
|
||||
}
|
||||
runhookdef(AFTERTRAPHOOK, NULL);
|
||||
|
||||
if (trapreturn > 0 && isfunc) {
|
||||
/*
|
||||
* Context was its own function. We propagate the return
|
||||
* value specially. Return value zero means don't do
|
||||
* anything special, so don't handle it.
|
||||
*/
|
||||
trapret = trapreturn;
|
||||
} else if (trapreturn >= 0 && !isfunc) {
|
||||
/*
|
||||
* Context was an inline trap. If an explicit return value
|
||||
* was used, we need to set `lastval'. Otherwise we use the
|
||||
* value restored by execrestore. In this case, all return
|
||||
* values indicate an explicit return from the current function,
|
||||
* so always handle specially. trapreturn is always restored by
|
||||
* execrestore.
|
||||
*/
|
||||
trapret = trapreturn + 1;
|
||||
}
|
||||
traperr = errflag;
|
||||
trapreturn = otrapreturn;
|
||||
|
||||
/* Grab values before they are restored */
|
||||
new_trap_state = trap_state;
|
||||
new_trap_return = trap_return;
|
||||
|
||||
execrestore();
|
||||
lexrestore();
|
||||
|
||||
if (trapret > 0) {
|
||||
if (new_trap_state == TRAP_STATE_FORCE_RETURN &&
|
||||
/* zero return from function isn't special */
|
||||
!(isfunc && new_trap_return == 0)) {
|
||||
if (isfunc) {
|
||||
breaks = loops;
|
||||
errflag = 1;
|
||||
lastval = trapret;
|
||||
} else {
|
||||
lastval = trapret-1;
|
||||
}
|
||||
lastval = new_trap_return;
|
||||
/* return triggered */
|
||||
retflag = 1;
|
||||
} else {
|
||||
|
|
22
Src/zsh.h
22
Src/zsh.h
|
@ -921,7 +921,9 @@ struct execstack {
|
|||
int badcshglob;
|
||||
pid_t cmdoutpid;
|
||||
int cmdoutval;
|
||||
int trapreturn;
|
||||
int trap_return;
|
||||
int trap_state;
|
||||
int trapisfunc;
|
||||
int noerrs;
|
||||
int subsh_close;
|
||||
char *underscore;
|
||||
|
@ -2225,6 +2227,24 @@ struct heap {
|
|||
#define ZSIG_ALIAS (1<<3) /* Trap is stored under an alias */
|
||||
#define ZSIG_SHIFT 4
|
||||
|
||||
/*
|
||||
* State of traps, stored in trap_state.
|
||||
*/
|
||||
enum trap_state {
|
||||
/* Traps are not active; trap_return is not useful. */
|
||||
TRAP_STATE_INACTIVE,
|
||||
/*
|
||||
* Traps are set but haven't triggered; trap_return gives
|
||||
* minus function depth.
|
||||
*/
|
||||
TRAP_STATE_PRIMED,
|
||||
/*
|
||||
* Trap has triggered to force a return; trap_return givens
|
||||
* return value.
|
||||
*/
|
||||
TRAP_STATE_FORCE_RETURN
|
||||
};
|
||||
|
||||
/***********/
|
||||
/* Sorting */
|
||||
/***********/
|
||||
|
|
|
@ -124,6 +124,7 @@
|
|||
0:TRAPEXIT
|
||||
>Exit
|
||||
|
||||
unsetopt DEBUG_BEFORE_CMD
|
||||
unfunction fn
|
||||
print 'TRAPDEBUG() {
|
||||
print Line $LINENO
|
||||
|
@ -138,6 +139,7 @@
|
|||
>Line 1
|
||||
>Line 1
|
||||
|
||||
unsetopt DEBUG_BEFORE_CMD
|
||||
unfunction fn
|
||||
print 'trap '\''print Line $LINENO'\'' DEBUG
|
||||
:
|
||||
|
|
|
@ -402,6 +402,19 @@
|
|||
0: trapreturn handling bug is properly fixed
|
||||
?./zsh-trapreturn-bug2:5: no such file or directory: ./fdasfsdafd
|
||||
|
||||
fn() {
|
||||
setopt localtraps localoptions debugbeforecmd
|
||||
trap '(( LINENO == 4 )) && setopt errexit' DEBUG
|
||||
print $LINENO three
|
||||
print $LINENO four
|
||||
print $LINENO five
|
||||
[[ -o errexit ]] && print "Hey, ERREXIT is set!"
|
||||
}
|
||||
fn
|
||||
1:Skip line from DEBUG trap
|
||||
>3 three
|
||||
>5 five
|
||||
|
||||
%clean
|
||||
|
||||
rm -f TRAPEXIT
|
||||
|
|
Loading…
Reference in a new issue