zsh-users
 help / color / mirror / code / Atom feed
* Differences between "TRAPERR()" and "trap foo ERR"
@ 2021-07-28  3:47 Zach Riggle
  2021-07-28  5:00 ` Bart Schaefer
  0 siblings, 1 reply; 3+ messages in thread
From: Zach Riggle @ 2021-07-28  3:47 UTC (permalink / raw)
  To: zsh-users

--------------------------------- BACKGROUND ---------------------------------
I've discovered some undocumented behavior of TRAPNAL functions, and
some divergence of behavior between TRAPNAL and "trap foo NAL".

Specifically, the former receives a $1 with the value of the signal
which invoked the routine, but the latter does not -- and that value
is the actual value of the signal (e.g. $1 == SIGSEGV == 11 for
TRAPSEGV).

For example, TRAPERR receives a value of 32, which is not a valid
signal value on my system (NSIG==32).

When setting via "trap 'my_trapfunc $@' SEGV", no argument is provided
to 'my_trapfunc'.

All of this is on ZSH v5.8.

---------------------------------- EXAMPLES ----------------------------------
So I've got two scripts, traperr and trapfunc.

    $ cat traperr
    #!/usr/bin/env zsh
    TRAPERR() {
        echo "Got ${(q-)@}"
    }
    false

    $ cat trapfunc
    #!/usr/bin/env zsh
    trap_err() {
        echo "Got ${(q-)@}"
    }
    trap trap_err ERR
    false

Note that "traperr" declares an all-caps TRAPERR.  "trapfunc" instead
uses the "trap" command to set the ERR trap.

Both generally work as expected, but the former ("TRAPERR") receives
an argument in "$1" that is undocumented.

    $ ./traperr
    Got 32

    $ ./trapfunc
    Got

------------------------------- DOCUMENTATION --------------------------------
I looked at the manual page (section 9.3.1) for trap functions:
https://zsh.sourceforge.io/Doc/Release/Functions.html#Trap-Functions

However, there's nothing mentioned about any parameters being
specified to TRAPNAL functions, and it generally seems that "TRAPNAL"
and "trap foo NAL" behave the same.

------------------------------ EXPERIMENTATION -------------------------------
At first, I thought the provenance of the value 32 comes from the
value of NSIG on my system (which on macOS is 32).

    $ cat nsig.c
    #include <stdio.h>
    #include <signal.h>

    int main() {
        printf("NSIG = %d\n", NSIG);
    }

    $ gcc -o nsig nsig.c

    $ ./nsig
    NSIG = 32

Second, I thought that perhaps it might come from ZSH's own list of
signals.  There is a ZERR entry, in any case.

    $ echo $signals
    EXIT HUP ...  ZERR DEBUG

    $ for i in {0..${#signals}}; do echo "$i: ${signals[$i]}"; done
    0:
    1: EXIT
    2: HUP
    ...
    31: USR1
    32: USR2
    33: ZERR
    34: DEBUG

As we can see here, the value received by "33: ZERR", is not the 32'th
entry in the list of $signals.  Since ZSH array indices start at 1,
this makes more sense and getting "32" for ZERR, is correct.

This is confirmed by setting e.g. TRAPSEGV, where SIGSEGV==11.

    $ cat trapsegv
    #!/usr/bin/env zsh
    TRAPSEGV() {
        echo "Got ${(q-)@}"
    }
    kill -s SEGV $$

    $ ./trapsegv
    Got 11

So we are receiving the actual value of the signal, when we declare a
TRAPNAL function.  This corresponds to the NAL+1'th offset in the
$signals array.

--------------------- ATTEMPT TO CONVERGE FUNCTIONALITY ----------------------
Since arguments are being provided to TRAPNAL functions, I thought I
would try to modify the "trapfunc" script to include its arguments
when invoking trap_err (note the extra $@ inside the trap statement
versus before).

    $ cat trapfunc_args
    #!/usr/bin/env zsh
    trap_err() {
        echo "Got ${(q-)@}"
    }
    trap 'trap_err $@' ERR
    false

    $ ./trapfunc_args
    Got

It seems this does not work to specify the signal number by including
e.g. $@ in the trap command list.

------------------------ CONCLUSION AND OBSERVATIONS -------------------------
Overall, there are two observations.

1. The behavior that TRAPNAL will receive NAL as its first argument,
where NAL corresponds to a the system-defined signal values (e.g.
SIGSEGV == 11).  The name for this signal can be gotten from
${signals[x+1]}.  This should be documented.

2. This behavior does not expand to "trap func NAL" statements, even
if we try to pass "$@" to some function.  This seems like a bug.

Am I missing something in the docs (quite likely) -- and is the
behavior difference intended -- or are these bugs.

Thanks for reading!
Zach Riggle


^ permalink raw reply	[flat|nested] 3+ messages in thread

* Re: Differences between "TRAPERR()" and "trap foo ERR"
  2021-07-28  3:47 Differences between "TRAPERR()" and "trap foo ERR" Zach Riggle
@ 2021-07-28  5:00 ` Bart Schaefer
  2021-07-28  5:04   ` Zach Riggle
  0 siblings, 1 reply; 3+ messages in thread
From: Bart Schaefer @ 2021-07-28  5:00 UTC (permalink / raw)
  To: Zach Riggle; +Cc: Zsh Users

On Tue, Jul 27, 2021 at 8:47 PM Zach Riggle <zachriggle@gmail.com> wrote:
>
> Overall, there are two observations.
>
> 1. The behavior that TRAPNAL will receive NAL as its first argument,
> where NAL corresponds to a the system-defined signal values (e.g.
> SIGSEGV == 11).  The name for this signal can be gotten from
> ${signals[x+1]}.  This should be documented.

9.3.2 Trap Functions:

TRAPNAL
     If defined and non-null, this function will be executed whenever
     the shell catches a signal SIGNAL, where NAL is a signal name as
     specified for the kill builtin.  The signal number will be passed
     as the first parameter to the function.

> 2. This behavior does not expand to "trap func NAL" statements, even
> if we try to pass "$@" to some function.  This seems like a bug.

It's not a bug; it's the normal behavior of the "trap" in the shell
language.  The TRAPnal form and the argument it receives are a zsh
extension.

The "trap" builtin takes an arbitrary list of commands rather than a
function body.  There is therefore no "argument list" where the signal
number can be passed.  If you write

  trap 'echo in a trap' USR2
  functions

you will see that there is no TRAPUSR2 function created.  "unfunction
TRAPUSR2" removes the USR2 trap as a side-effect, but also complains
"no such hash table element".  That's a bit of an oddity, but is in
part why the doc for the "trap" builtin says:

     Defining a trap under either name causes any trap under an
     alternative name to be removed.  However, it is recommended that
     for consistency users stick exclusively to one name or another.

Other distinctions considered important are also mentioned there:

     Note that traps defined with the trap builtin are slightly
     different from those defined as 'TRAPNAL () { ...  }', as the
     latter have their own function environment (line numbers, local
     variables, etc.)  while the former use the environment of the
     command in which they were called.  For example,

          trap 'print $LINENO' DEBUG

     will print the line number of a command executed after it has run,
     while

          TRAPDEBUG() { print $LINENO; }

     will always print the number zero.

You might also read about the POSIX_TRAPS and LOCAL_TRAPS setopts.


^ permalink raw reply	[flat|nested] 3+ messages in thread

* Re: Differences between "TRAPERR()" and "trap foo ERR"
  2021-07-28  5:00 ` Bart Schaefer
@ 2021-07-28  5:04   ` Zach Riggle
  0 siblings, 0 replies; 3+ messages in thread
From: Zach Riggle @ 2021-07-28  5:04 UTC (permalink / raw)
  To: Bart Schaefer; +Cc: Zsh Users

Thanks for the quick response, Bart!

> The signal number will be passed as the first parameter to the function.

Wow, I am dense and poor at reading documentation.  Thanks for
pointing out the relevant portion to me.

> The TRAPnal form and the argument it receives are a zsh extension.

Welp, that explains the rest of it!

> You might also read about the POSIX_TRAPS and LOCAL_TRAPS setopts.

Thanks, I will definitely look up the docs for this.

Humbled and appreciative,
Zach Riggle

On Wed, Jul 28, 2021 at 12:00 AM Bart Schaefer
<schaefer@brasslantern.com> wrote:
>
> On Tue, Jul 27, 2021 at 8:47 PM Zach Riggle <zachriggle@gmail.com> wrote:
> >
> > Overall, there are two observations.
> >
> > 1. The behavior that TRAPNAL will receive NAL as its first argument,
> > where NAL corresponds to a the system-defined signal values (e.g.
> > SIGSEGV == 11).  The name for this signal can be gotten from
> > ${signals[x+1]}.  This should be documented.
>
> 9.3.2 Trap Functions:
>
> TRAPNAL
>      If defined and non-null, this function will be executed whenever
>      the shell catches a signal SIGNAL, where NAL is a signal name as
>      specified for the kill builtin.  The signal number will be passed
>      as the first parameter to the function.
>
> > 2. This behavior does not expand to "trap func NAL" statements, even
> > if we try to pass "$@" to some function.  This seems like a bug.
>
> It's not a bug; it's the normal behavior of the "trap" in the shell
> language.  The TRAPnal form and the argument it receives are a zsh
> extension.
>
> The "trap" builtin takes an arbitrary list of commands rather than a
> function body.  There is therefore no "argument list" where the signal
> number can be passed.  If you write
>
>   trap 'echo in a trap' USR2
>   functions
>
> you will see that there is no TRAPUSR2 function created.  "unfunction
> TRAPUSR2" removes the USR2 trap as a side-effect, but also complains
> "no such hash table element".  That's a bit of an oddity, but is in
> part why the doc for the "trap" builtin says:
>
>      Defining a trap under either name causes any trap under an
>      alternative name to be removed.  However, it is recommended that
>      for consistency users stick exclusively to one name or another.
>
> Other distinctions considered important are also mentioned there:
>
>      Note that traps defined with the trap builtin are slightly
>      different from those defined as 'TRAPNAL () { ...  }', as the
>      latter have their own function environment (line numbers, local
>      variables, etc.)  while the former use the environment of the
>      command in which they were called.  For example,
>
>           trap 'print $LINENO' DEBUG
>
>      will print the line number of a command executed after it has run,
>      while
>
>           TRAPDEBUG() { print $LINENO; }
>
>      will always print the number zero.
>
> You might also read about the POSIX_TRAPS and LOCAL_TRAPS setopts.


^ permalink raw reply	[flat|nested] 3+ messages in thread

end of thread, other threads:[~2021-07-28  5:06 UTC | newest]

Thread overview: 3+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2021-07-28  3:47 Differences between "TRAPERR()" and "trap foo ERR" Zach Riggle
2021-07-28  5:00 ` Bart Schaefer
2021-07-28  5:04   ` Zach Riggle

Code repositories for project(s) associated with this public inbox

	https://git.vuxu.org/mirror/zsh/

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).