Module Name: src Committed By: kre Date: Sun May 7 15:01:18 UTC 2017
Modified Files: src/bin/sh: sh.1 trap.c Log Message: Enhance the trap command to make it possible to do what POSIX wants (even if no shell in existence, that I am aware of, does that). That is, POSIX says ... [of the trap command with no args] The shell shall format the output, including the proper use of quoting, so that it is suitable for re-input to the shell as commands that achieve the same trapping results. For example: save_traps=$(trap) ... eval "$save_traps" It is obvious what the intent is there. But no shell makes it work. An example using bash (as the NetBSD shell, still does not do the save_traps= stuff correctly - but that is a problem for a different time and place...) Given this script printf 'At start: '; trap printf '\n' traps=$(trap) trap 'echo hello' INT printf 'inside : '; trap printf '\n' eval "${traps}" printf 'At end : '; trap printf '\n' One would expect that (assuming no traps are set at the start, and there aren't) that the first trap will print nothing, then the inside trap will show the trap that was set, and then when we get to the end everything will be back to nothing again. But: At start: inside : trap -- 'echo hello' SIGINT At end : trap -- 'echo hello' SIGINT And of course. when you think about it, it is obvious why this happens. The first "trap" command prints nothing ... nothing has changed when we get to the "traps=$(trap)" command ... that trap command also prints nothing. So this does traps=''. When we do eval "${traps}" we are doing eval "", and it is hardly surprising that this accomplishes nothing! Now we cannot rationally change the "trap" command without args to behave in a way that would make it useful for the posix purpose (and here, what they're aiming for is good, it should be possible to accomplish that objective) so is there some other way? I think I have seen some shell (but I do not remember which one) that actually has "trap -" that resets all traps to the default, so with that, if we changed the 'eval "${traps}"' line to 'trap -; eval "${traps}"' then things would actually work - kind of - that version has race conditions, so is not really safe to use (it will work, most of the time...) But, both ksh93 and bash have a -p arg to "trap" that allows information about the current trap status of named signals to be reported. Unfortunately they don't do quite the same thing, but that's not important right now, either would be usable, and they are, but it is a lot of effort, not nearly as simple as the posix example. First, while "trap -p" (with no signals specified) works, it works just the same (in both bash and ksh93, aside from output format) as "trap". That is, that is useless. But we can to trap_int=$(trap -p int) trap_hup=$(trap -p hup) ... and then reset them all, one by one, later... (bash syntax) test -n "${trap_int}" && eval "${trap_int}" || trap - int test -n "${trap_hup}" && eval "${trap_hup}" || trap - hup (ksh93 syntax) trap "${trap_int:-}" int trap "${trap_hup:-}" hup the test (for bash) and variable with default for ksh93, is needed because they both still print nothing if the signal action is the default. So, this modification attempts to fix all of that... 1) we add trap -p, but make it always output something for every signal listed (all of the signals if none are given) even if the signal action is the default. 2) choose the bash output format for trap -p, over the ksh93 format, even though the simpler usage just above makes the ksh93 form seem better. But it isn't. Consider: ksh93$ trap -p int hup echo hello One of the two traps has "echo hello" as its action, the other is still at the default, but which? From bash... bash$ trap -p int hup trap -- 'echo hello' SIGINT And now we know! Given the bash 'trap -p' format, the following function produces ksh93 format output (for use with named signals only) instead... ksh93_trap_p() { for _ARG_ do _TRAP_=$(trap -p "${_ARG_}") || return 1 eval set -- "${_TRAP_}" printf '%s' "$3${3:+ }" done return 0 } [ It needs to be entered without the indentation, that '}"' line has to be at the margin. If the shell running that has local vars (bash does) then _ARG_ and _TRAP_ should be made local. ] So the bash format was chosen (except we do not include the "SIG" on the signal names. That's irrelevant.) If no traps are set, "trap -p" will say (on NetBSD of course)... trap -- - EXIT HUP INT QUIT ILL TRAP ABRT EMT FPE KILL BUS SEGV SYS trap -- - PIPE ALRM TERM URG STOP TSTP CONT CHLD TTIN TTOU IO XCPU XFSZ trap -- - VTALRM PROF WINCH INFO USR1 USR2 PWR RT0 RT1 RT2 RT3 RT4 RT5 trap -- - RT6 RT7 RT8 RT9 RT10 RT11 RT12 RT13 RT14 RT15 RT16 RT17 RT18 trap -- - RT19 RT20 RT21 RT22 RT23 RT24 RT25 RT26 RT27 RT28 RT29 RT30 Obviously if traps are set, the relevant signal names will be removed from that list, and additional lines added for the trapped signals. With args, the signals names are listed, one line each, whatever the status of the trap for that signal is: $ trap -p HUP INT QUIT trap -- - HUP trap -- 'echo interrupted' INT trap -- - QUIT 3) we add "trap -" to reset all traps to default. (It is easy, and seems useful.) 4) While here, lots of generic cleanup. In particular, get rid of the NSIG+1 nonsense, and anything that ever believes a signo == NSIG is in any way rational. Before there was a bunch of confusion, as we need all the signals for traps, plus one more for the EXIT trap, which looks like we then need NSIG+1. But EXIT is 0, NSIG includes signals from 0..NSIG-1 but there is no signal 0, EXIT uses that slot, so we do not need to add and extra one, NSIG is enough. (To see the effect of this, use a /bin/sh from before this fix, and compare the output from trap '' 64 and trap '' 65 both invalid signal numbers. Then try just "trap" and watch your shell drop core...) Eventually NSIG needs to go away completely (from user apps), it is not POSIX, it isn't really useful (unless we make lots of assumptions about how signals are numbered, which are not guaranteed, so even if apps, like this sh, work on NetBSD, they're not portable,) and it isn't necessary (or will not be, soon.) But that is for another day... 5) As is kind of obvious above, when listing "all" traps, list all the ones still at their defaults, and all the ignored signals, on as few lines as possible (it could all be on one line - technically it would work as well, but it would have made this cvs log message really ugly...) Signals with a non-null action still get listed one to a line (even if several do have the exact same action.) 6) Man page updates as well. After this change, the following script: printf 'At start: '; trap printf '\n' trap -p >/tmp/out.$$ trap 'echo hello' INT printf 'inside : '; trap printf '\n' . /tmp/out.$$; rm /tmp/out.$$ printf 'At end : '; trap printf '\n' which is just the example from above, using "trap -p" instead of just "trap" to save the traps, and modified to a form that will work with the NetBSD shell today produces: At start: inside : trap -- 'echo hello' INT At end : [Do I get a prize for longest commit log message of the year?] To generate a diff of this commit: cvs rdiff -u -r1.135 -r1.136 src/bin/sh/sh.1 cvs rdiff -u -r1.39 -r1.40 src/bin/sh/trap.c Please note that diffs are not public domain; they are subject to the copyright notices on the relevant files.