# -------------------------------------------------------------------- # # KCatch.pm - Catch warn and die to avoid "Internal Server Error" # Copyright (C) 1999-2000 Kawasaki Yuusuke # -------------------------------------------------------------------- # =head1 NAME KCatch.pm - Catch warn and die to avoid "Internal Server Error" =head1 SYNOPSIS use KCatch; print "Content-Type: text/html\n\n"; warn; die; =head1 DESCRIPTION 1ST STEP) Upload KCatch.pm to your script's directory or to @INC. 2ND STEP) Insert just a line "use KCatch;" at top of your script. 3RD STEP) This module could not work without the bug you made! :-) =head1 OPTIONS use KCatch qw( [OPTIONS] ); KCatch.pm would automatically detect whether running under CGI or not, when no options given. Available options are following: use KCatch qw( plain ); Force to make output as plain text for command-line use. use KCatch qw( html ); Force to make output as HTML for CGI. use KCatch qw( source ); Display also warned or died Perl source code for debugging. (Notes: The options could make influence on some security problems to display your code for the users.) use KCatch qw( stderr ); Output additional information of CGI to STDERR. =head1 VERSIONS 1999/11/05 v1.02 First Release 1999/11/23 v1.03 Recognize between use and require 2000/04/25 v1.04 Bug fix: undefined $ENV{GATEWAY_INTERFACE} 2000/05/03 v1.05 Add options "use KCatch qw( source );", etc. 2000/05/05 v1.06 Bug fix: undefined $ENV{REQUEST_URI} 2000/08/04 v1.07 Output additional information to STDERR =head1 SITES http://www.kawa.net/works/perl/catch/ http://www.harukaze.net/~mishima/perl/cgi-debug-env/deb-tech.html =head1 AUTHOR Copyright 1999-2000 Kawasaki Yusuke =cut # -------------------------------------------------------------------- # package KCatch; use strict; # -------------------------------------------------------------------- # my $argv = {}; # Option arguments when "use" my $messbuf = []; # Error messages buffer (null) $main::SIG{__WARN__} = \&warn; # Catch the signal from warn() $main::SIG{__DIE__} = \¨ # Catch the signal from die() # -------------------------------------------------------------------- # sub import { my $package = shift; $argv = { map { $_ => ! undef } @_ }; } # -------------------------------------------------------------------- # sub warn { my $mess = shift; my $caller = &get_caller(); push( @$messbuf, [ "warn", $mess, @$caller ] ) if $caller; CORE::warn( $mess ); } # -------------------------------------------------------------------- # sub die { my $mess = shift; my $caller = &get_caller(); push( @$messbuf, [ "die", $mess, @$caller ] ) if $caller; CORE::die( $mess ); } # -------------------------------------------------------------------- # END { return if ( $#$messbuf < 0 ); # Exit if no errors if ( $argv->{stderr} ) { &disp_stderr(); # Additional information } my $routine = \&disp_plain; # Plain text as default if ( $argv->{html} ) { $routine = \&disp_html; # Force HTML } elsif ( $argv->{plain} ) { $routine = \&disp_plain; # Force plain text } elsif ( defined $ENV{GATEWAY_INTERFACE} and $ENV{GATEWAY_INTERFACE} =~ /cgi/i ){ $routine = \&disp_html; # HTML under CGI }else{ $routine = \&disp_plain; # Plain text under non-CGI } &{$routine}(); # Display buffer } # -------------------------------------------------------------------- # # Output additional information to STDERR # -------------------------------------------------------------------- # sub disp_stderr { my $info = &conv_disp(); my $name = &get_int_name(); my( $sec, $min, $hour, $day, $mon, $year ) = localtime(); my $qlength = ( $ENV{REQUEST_METHOD} eq "POST" ) ? $ENV{CONTENT_LENGTH} : length( $ENV{QUERY_STRING} ); my $url = $ENV{REQUEST_URI}; $url =~ s/\?.*$//; printf STDERR "(Catch) %04d/%02d/%02d %02d:%02d:%02d %s [%05d] %s %s (%s) \"%s\"\n", $year+1900, $mon+1, $day, $hour, $min, $sec, $name, $$, $ENV{REMOTE_ADDR}, $url, $qlength, $ENV{HTTP_USER_AGENT}; } # -------------------------------------------------------------------- # # Get the name of this script # -------------------------------------------------------------------- # sub get_int_name { my $name; if ( defined $ENV{SCRIPT_NAME} ){ $name = (split( /[\?\#]/, $ENV{SCRIPT_NAME} ))[0]; $name = ( $name =~ m#([^/]+)$# )[0]; } if ( $name =~ /^$/ ) { $name = ( $0 =~ m#([^/]+)$# )[0] } $name; } # -------------------------------------------------------------------- # # Display infomation as plain text # -------------------------------------------------------------------- # sub disp_plain { print "\n"; print "-" x 79, "\n"; #
my $info = &conv_disp(); my( $name ) = ( $0 =~ m#([^/]+)$# ); print join( "", @$info ); print "-" x 79, "\n"; #
print $name; # Script name print " (Version $main::VERSION)" if defined $main::VERSION; print " with Perl $] for $^O\n"; # Perl versoin } # -------------------------------------------------------------------- # # Display infomation as HTML # -------------------------------------------------------------------- # sub disp_html { my $time = scalar localtime; my $info = &conv_disp(); foreach ( @$info ) { s#^>#\255#g; s#&#&#g; s#<#<#g; s#>#>#g; s:^\255(.*)$:>\t$1:g; s#\n#
\n#g; }; my $name; if ( defined $ENV{REQUEST_URI} ){ ( $name ) = split( /[\?\#]/, $ENV{REQUEST_URI} ); ( $name ) = ( $name =~ m#([^/]+)$# ); } ( $name ) = ( $0 =~ m#([^/]+)$# ) if ( $name =~ /^$/ ); print "Catch: $time
\n"; # Title again print "\n"; print @$info; print "
\n"; print "$name"; # Script name print " (Version $main::VERSION)" if defined $main::VERSION; print " with Perl $] for $^O\n"; # Perl versoin print "\n\n"; # HTML footer } # -------------------------------------------------------------------- # # Format warn/die message into text for output # -------------------------------------------------------------------- # sub conv_disp { my $out = []; foreach my $array ( @$messbuf ){ my( $type, $mess, $pack, $file, $line, $sub ) = @$array; $file =~ s#^.*[/\\]##; # Enshort filename chomp $mess; my $temp = sprintf( "[%s:%d:%s] %s\n", $file, $line, $type, $mess ); push( @$out, $temp ); if ( $argv->{source} and $line > 0 ) { open ( TEMP, $file ) and do { my $c = 1; while ( ){ if ( $line == $c++ ) { chomp; push( @$out, "> $_\n" ); last; } } }; } } $out; } # -------------------------------------------------------------------- # # false when in eval, or return caller (except for Carp.pm) # -------------------------------------------------------------------- # sub get_caller { my $c = 1; my $result; while( 1 ){ my( $pack, $file, $line, $sub, $hasargs, $wantarray, $evaltext, $is_require ) = caller( $c ++ ) or last; last if $is_require; # between require or use return undef if ( $sub eq "(eval)" ); # cancel when in eval if( $pack eq "Carp" ){ $c ++; }else{ $result ||= [ $pack, $file, $line, $sub ]; } } $result; } # -------------------------------------------------------------------- # 1; # -------------------------------------------------------------------- #