The Perl Toolchain Summit needs more sponsors. If your company depends on Perl, please support this very important event.
package Enbugger::OnError;

# COPYRIGHT AND LICENCE
#
# Copyright (C) 2007,2008 WhitePages.com, Inc. with primary
# development by Joshua ben Jore.
#
# This program is distributed WITHOUT ANY WARRANTY, including but not
# limited to the implied warranties of merchantability or fitness for
# a particular purpose.
#
# The program is free software.  You may distribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation (either version 2 or any later version)
# and the Perl Artistic License as published by O’Reilly Media, Inc.
# Please open the files named gpl-2.0.txt and Artistic for a copy of
# these licenses.

use strict;
use warnings;
use Carp();

use constant {
    'DEFAULT_SIGNALS' => [qw[ __DIE__ USR1 ]],
    'DEFAULT_HOOK'    => \&ExceptionHandler,
};

sub import {
    my ( $class, @signals ) = @_;
    
    my $hook = $class->DEFAULT_HOOK;
    my $signals =
      @signals
	? \ @signals
	  : $class->DEFAULT_SIGNALS;

    $class->hook_signals( $signals, $hook );
    
    return;
}

sub hook_signals {
    my ( $self, $signals, $hook ) = @_;
    
    @SIG{@$signals} = ($hook) x @$signals;
    
    return;
}

sub ExceptionHandler {

    # Find the list of things in %SIG that are trapped by this function.
    my ( @self_hooked_sigs, %self_hooked_sigs_lu );
    keys %SIG;
    while ( my ( $name, $handler ) = each %SIG ) {
	if ( ref $handler
	     and $handler == \ &ExceptionHandler ) {
	    push @self_hooked_sigs, $name;
	    $self_hooked_sigs_lu{$name} = undef;
	}
    }

    # When we are in a __DIE__ handler, do not accept when there is an
    # outer eval scope. Perhaps this should be configurable policy.
    if ( ( $_[0] eq '__DIE__'
	   or ( not exists $self_hooked_sigs_lu{$_[0]} ) )
	 and exists $self_hooked_sigs_lu{__DIE__} ) {
	for (
	     my $cx = 1;
	     my ( undef, undef, undef, $function ) = caller $cx;
	     ++ $cx
	    ) {
	    
	    
	    if ($function =~ /^\(eval *\d*\)\z/ ) {
		return 1;
	    }
	}
    }
    
    
    # Do not re-enter this handler while in it. In theory I could work
    # on this to make it safe for being reentrant but that's just not
    # work I'm doing today. Feel free to do this and send patches.
    local @SIG{ @self_hooked_sigs } = ('IGNORE') x @self_hooked_sigs;
    
    

    # Enable the debugger even if it wasn't used at compilation
    # time. ->debugger points to whatever the locally preferred
    # debugger is.
    require Enbugger;
    Enbugger->load_debugger;
    
    # Log the current exception.
    Enbugger->write( Carp::longmess("Received signal $_[0]") );
    
    
    # Trigger the debugger. I did some trial and error to get
    # this. perl5db.pl pays attention to $DB::signal. $^P gets set (if
    # it wasn't already) to statement level debugging and then enter
    # the DB() function. I originally tried this as goto &DB::DB but
    # found that I'd get popped out of the debugger. Whoops.
    Enbugger->stop;

    $@ = $_[0];
    DB::DB();
}


=begin emacs

## Local Variables:
## mode: cperl
## mode: auto-fill
## cperl-indent-level: 4
## End:

=end emacs

=cut

() = -.0