# emulate default behaviors for the various signals.

package Signals::XSIG::Default;
use strict;
use warnings;
use Config;
use Carp;
use POSIX ();

our %DEFAULT_BEHAVIOR;
our $VERSION = '0.05';

my @snam = split ' ', $Config{sig_name};
my @snum = split ' ', $Config{sig_num};

sub import {
  my $ignore = 1;
  while (<DATA>) {
    next if /^#/;
    next unless /\S/;
    if (/^\[(.+)\]/) {
      if ($1 eq 'default' || $1 eq $^O) {
	$ignore = 0;
      } else {
	$ignore = 1;
      }
    } elsif (/\{(.+)\/(\d+)\}/) {
      if ($1 eq $^O && $2 <= int($] * 1000)) {
	$ignore = 0;
      } else {
	$ignore = 1;
      }
    } elsif (!$ignore) {
      s/^\d+\. //;
      s/^SIG//;
      my ($sig, $num, $behavior) = /^(\w+)\s+\[(\d*)\]\s+=>\s+(.+)/;
      $DEFAULT_BEHAVIOR{$sig} = $behavior;
    }
  }
}

sub perform_default_behavior {
  my $signal = shift;
  my $funcname = 'default_SIG' . $signal;
  if (defined &$funcname) {
    no strict 'refs';
    return if $funcname->($signal, @_);
  }

  my $behavior = $DEFAULT_BEHAVIOR{$signal};
  if (!defined $behavior) {
    if ($signal =~ /^NUM(\d+)/) {
      my $signum = 0 + $1;
      $behavior = $DEFAULT_BEHAVIOR{"NUMxx"};
      $behavior =~ s/xx/$signum/;
    }
    if (!defined $behavior) {
      croak "Signals::XSIG: no default behavior is specified ",
	"for SIG$signal. Terminating this program.\n";
    }
  }

  if ($behavior eq 'IGNORE') {
    return;
  }

  if ($behavior eq 'SUSPEND') {
    suspend($signal);
    # ... then wait for the SIGCONT ... 
    return;
  }

  if ($behavior =~ /^ABORT/) {
    untie %SIG;
    %SIG = ();
    $SIG{$signal} = $SIG{"ABRT"} = "DEFAULT";
    killprog_with_signal("ABRT");
    POSIX::abort();
    croak "Abort\n";
  }
  if ($behavior =~ /^SIGSEGV/) {
    killprog_with_signal('SEGV');
    croak "Abort\n";
  }

  if ($behavior =~ /^EXIT (\d+)/) {
    my $exit_code = $1;
    exit($exit_code);
  }

  if ($behavior =~ /^TERMINATE/) {
    my $no;
    for (my $i=0; $i<@snum; $i++) {
      $no = $snum[$i] if $signal eq $snam[$i];
    }

    killprog_with_signal($signal, $no);
    croak "default behavior for SIG$signal should have killed script ",
        "but for some reason it didn't  :-(\n";
  }

  croak "Signals::XSIG: unknown behavior \"$behavior\" ",
    "for SIG$signal. Terminating this program.\n";
}

sub killprog_with_signal {
  my ($sig,$sig_no) = @_;
  untie %SIG;
  %SIG = ();
  $SIG{$sig} = 'DEFAULT';

  unless ($sig_no) {
    my @sig_name = split ' ', $Config{sig_name};
    my ($sig_no) = grep { $sig eq $sig_name[$_] } split ' ',$Config{sig_num};
  }

  kill $sig, $$;
  eval {
    use POSIX ();
    if ($sig_no) {
      # this is needed for Linux
      POSIX::sigaction($sig_no, &POSIX::SIG_DFL);
      POSIX::sigprocmask(&POSIX::SIG_UNBLOCK, POSIX::SigSet->new($sig_no));
    }
  };
  kill $sig, $$;

  my $miniprog = q[$SIG{'__SIGNAL__'}='DEFAULT';
                   kill '__SIGNAL__',$$;sleep 1;die];
  $miniprog =~ s/__SIGNAL__/$sig/g;
  exec($^X, "-e", $miniprog);
}

# in principle, SIGSTOP cannot be trapped.
sub suspend {
  if ($^O eq 'MSWin32') {
    # MSWin32 doesn't have signals as such.
    # Win32::API->SuspendProcess / SuspendThread ?
    # Win32::Process->suspend ?
    # Win32::Thread->suspend ?
  }
  kill 'STOP', $$;
}

##################################################################

# system specific and other special behaviors.
# Signals that don't fall into the terminate/suspend/ignore
# paradigm or that have other special needs can be implemented
# below.
# Return true if the signal is "handled" and no further 
# processing is necessary.

sub default_SIG__WARN__ {
  CORE::warn @_;
  return 1;
}

sub default_SIG__DIE__ {
  CORE::die @_;
  return 1;
}

1;

=head1 NAME

Signals::XSIG::Default

=head1 DESCRIPTION

Module to emulate the default signal handler behavior for all
signals in your system. The emulator is used when you have
used L<Signals::XSIG> to register more than one 
handler for a signal, and one of those handlers is C<DEFAULT>.

See L<Signals::XSIG> for much more information.

=cut



# see  spike/analyze_default_signal_behavior.pl

# for each new system that is available to us, run
#    spike/analyze_default_signal_behavior.pl
# and include that data at the end of this file ...
#
# we can also infer behavior from CPAN tester results,
# see t/20-defaults.t
#
__DATA__
[default]
ABRT [] => ABORT
ALRM [] => TERMINATE
BREAK [] => TERMINATE
BUS [] => TERMINATE
CHLD [] => IGNORE
CLD [] => IGNORE
CONT [] => IGNORE
EMT [] => TERMINATE
FPE [] => TERMINATE
HUP [] => TERMINATE
ILL [] => TERMINATE
INT [] => TERMINATE
IO [] => TERMINATE
IOT [] => TERMINATE
KILL [] => TERMINATE
LOST [] => TERMINATE
NUMxx [] => TERMINATE xx
PIPE [] => TERMINATE
POLL [] => TERMINATE
PROF [] => TERMINATE
PWR [] => TERMINATE
QUIT [] => TERMINATE
SEGV [] => ABORT
STKFLT [] => TERMINATE
STOP [] => SUSPEND
SYS [] => TERMINATE
TERM [] => TERMINATE
TRAP [] => TERMINATE
TSTP [] => SUSPEND
TTIN [] => SUSPEND
TTOU [] => SUSPEND
URG [] => IGNORE
USR1 [] => TERMINATE
USR2 [] => TERMINATE
VTALRM [] => TERMINATE
WINCH [] => IGNORE
XCPU [] => TERMINATE
XFSZ [] => TERMINATE
ZERO [] => IGNORE
__DIE__ [] => IGNORE
__WARN__ [] => IGNORE

[MSWin32]
ABRT    [22] => EXIT 22
ALRM    [14] => EXIT 14
BREAK   [21] => TERMINATE 21
CHLD    [20] => EXIT 20
CLD     [20] => EXIT 20
CONT    [25] => EXIT 25
FPE     [8] => EXIT 8
HUP     [1] => EXIT 1
ILL     [4] => EXIT 4
INT     [2] => IGNORE
KILL    [9] => EXIT 9
NUMxx   [] => EXIT xx
PIPE    [13] => EXIT 13
QUIT    [21] => IGNORE
SEGV    [11] => EXIT 11
STOP    [23] => EXIT 23
TERM    [15] => IGNORE
ZERO    [] => IGNORE
__DIE__ [] => IGNORE
__WARN__ [] => IGNORE

[linux]
ABRT    [6] => TERMINATE 6
ALRM    [14] => TERMINATE 14
BUS     [7] => TERMINATE 7
CHLD    [17] => IGNORE
CLD     [17] => IGNORE
CONT    [18] => IGNORE
FPE     [8] => TERMINATE 8
HUP     [1] => TERMINATE 1
ILL     [4] => TERMINATE 4
INT     [2] => TERMINATE 2
IO      [29] => TERMINATE 29
IOT     [6] => TERMINATE 6
KILL    [9] => TERMINATE 9
NUMxx   [] => TERMINATE xx
PIPE    [13] => TERMINATE 13
POLL    [29] => TERMINATE 29
PROF    [27] => TERMINATE 27
PWR     [30] => TERMINATE 30
QUIT    [3] => TERMINATE 3
RTMAX   [64] => TERMINATE 64
RTMIN   [34] => TERMINATE 34
SEGV    [11] => TERMINATE 11
STKFLT  [16] => TERMINATE 16
STOP    [19] => SUSPEND
SYS     [31] => TERMINATE 31
TERM    [15] => TERMINATE 15
TRAP    [5] => TERMINATE 5
TSTP    [20] => SUSPEND
TTIN    [21] => SUSPEND
TTOU    [22] => SUSPEND
UNUSED  [31] => TERMINATE 31
URG     [23] => IGNORE
USR1    [10] => TERMINATE 10
USR2    [12] => TERMINATE 12
VTALRM  [26] => TERMINATE 26
WINCH   [28] => IGNORE
XCPU    [24] => TERMINATE 24
XFSZ    [25] => TERMINATE 25
ZERO    [0] => IGNORE
__DIE__ [] => IGNORE
__WARN__ [] => IGNORE

[cygwin]
ABRT    [6] => TERMINATE 134
ALRM    [14] => TERMINATE 14
BUS     [10] => TERMINATE 10
CHLD    [20] => IGNORE
CLD     [20] => IGNORE
CONT    [19] => IGNORE
EMT     [7] => TERMINATE 7
FPE     [8] => TERMINATE 8
HUP     [1] => TERMINATE 1
ILL     [4] => TERMINATE 4
INT     [2] => TERMINATE 2
IO      [23] => IGNORE
KILL    [9] => TERMINATE 9
LOST    [29] => TERMINATE 29
PIPE    [13] => TERMINATE 13
POLL    [23] => IGNORE
PROF    [27] => TERMINATE 27
PWR     [29] => TERMINATE 29
QUIT    [3] => TERMINATE 131
RTMAX   [32] => TERMINATE 32
RTMIN   [32] => TERMINATE 32
SEGV    [11] => TERMINATE 11
STOP    [17] => SUSPEND
SYS     [12] => TERMINATE 12
TERM    [15] => TERMINATE 15
TRAP    [5] => TERMINATE 5
TSTP    [18] => SUSPEND
TTIN    [21] => SUSPEND
TTOU    [22] => SUSPEND
URG     [16] => IGNORE
USR1    [30] => TERMINATE 30
USR2    [31] => TERMINATE 31
VTALRM  [26] => TERMINATE 26
WINCH   [28] => IGNORE
XCPU    [24] => TERMINATE 24
XFSZ    [25] => TERMINATE 25
ZERO    [] => IGNORE
__DIE__ [] => IGNORE
__WARN__ [] => IGNORE

# www.cpantesters.org/cpan/report/4d60a820-e5c2-11df-bf62-5eb33ef6c52e
[darwin]
ABRT   [6] => TERMINATE 6
EMT    [7] => TERMINATE 7
SEGV   [11] => TERMINATE 11
IO     [] => IGNORE
IOT    [6] => TERMINATE 6

# www.cpantesters.org/cpan/report/4b99dd2a-e60f-11df-bb29-ad544afd17af
[dragonfly]
ABRT [6] => ABORT
EMT [7] => TERMINATE 135
SEGV [11] => TERMINATE 139
IO [] => IGNORE
IOT [] => ABORT

# www.cpantesters.org/cpan/report/62d0832e-e621-11df-858d-e879df34a846a
[freebsd]
ABRT [6] => ABORT
EMT [7] => TERMINATE 135
SEGV [11] => TERMINATE 139
IO [] => IGNORE
IOT [] => ABORT

# www.cpantesters.org/cpan/report/c8635a72-e5d6-11df-a833-d9c7245fd73a
[irix]
ABRT [6] => ABORT
EMT [7] => TERMINATE 135
SEGV [11] => TERMINATE 139
RTMIN [49] => TERMINATE 49
RTMAX [64] => TERMINATE 64
IOT [] => ABORT

# www.cpantesters.org/cpan/report/209fd0b6-e61e-11df-bb29-ad544afd17af
[netbsd]
ABRT [6] => ABORT
EMT [7] => TERMINATE 135
SEGV [11] => TERMINATE 139
IO [] => IGNORE
IOT [6] = ABORT

[openbsd]
ABRT [6] => ABORT
EMT [7] => TERMINATE 135
SEGV [11] => TERMINATE 139
IO [] => IGNORE

[solaris]
ABRT    [6] => TERMINATE 134
ALRM    [14] => TERMINATE 14
BUS     [10] => TERMINATE 138
CANCEL  [36] => IGNORE
CHLD    [18] => IGNORE
CLD     [18] => IGNORE
CONT    [25] => IGNORE
EMT     [7] => TERMINATE 135
FPE     [8] => TERMINATE 136
FREEZE  [34] => IGNORE
HUP     [1] => TERMINATE 1
ILL     [4] => TERMINATE 132
INT     [2] => TERMINATE 2
IO      [22] => TERMINATE 22
IOT     [6] => TERMINATE 134
JVM1    [39] => IGNORE
JVM2    [40] => IGNORE
KILL    [9] => TERMINATE 9
LOST    [37] => TERMINATE 37
LWP     [33] => IGNORE
NUMxx   [42] => TERMINATE xx
PIPE    [13] => TERMINATE 13
POLL    [22] => TERMINATE 22
PROF    [29] => TERMINATE 29
PWR     [19] => IGNORE
QUIT    [3] => TERMINATE 131
RTMAX   [48] => TERMINATE 48
RTMIN   [41] => TERMINATE 41
SEGV    [11] => TERMINATE 139
STOP    [23] => SUSPEND
SYS     [12] => TERMINATE 140
TERM    [15] => TERMINATE 15
THAW    [35] => IGNORE
TRAP    [5] => TERMINATE 5
TSTP    [24] => SUSPEND
TTIN    [26] => SUSPEND
TTOU    [27] => SUSPEND
URG     [21] => IGNORE
USR1    [16] => TERMINATE 16
USR2    [17] => TERMINATE 17
VTALRM  [28] => TERMINATE 28
WAITING [32] => IGNORE
WINCH   [20] => IGNORE
XCPU    [30] => TERMINATE 30
XFSZ    [31] => TERMINATE 159
XRES    [38] => IGNORE
ZERO    [] => IGNORE
__DIE__ [] => IGNORE
__WARN__ [] => IGNORE
