=head2 The C<diagnostics> Pragma
This module extends the terse diagnostics normally emitted by both the
-perl compiler and the perl interpeter, augmenting them with the more
+perl compiler and the perl interpreter, augmenting them with the more
explicative and endearing descriptions found in L<perldiag>. Like the
other pragmata, it affects the compilation phase of your program rather
than merely the execution phase.
Due to the interaction between runtime and compiletime issues,
and because it's probably not a very good idea anyway,
you may not use C<no diagnostics> to turn them off at compiletime.
-However, you may control there behaviour at runtime using the
+However, you may control their behaviour at runtime using the
disable() and enable() methods to turn them off and on respectively.
The B<-verbose> flag first prints out the L<perldiag> introduction before
any other diagnostics. The $diagnostics::PRETTY variable can generate nicer
escape sequences for pagers.
+Warnings dispatched from perl itself (or more accurately, those that match
+descriptions found in L<perldiag>) are only displayed once (no duplicate
+descriptions). User code generated warnings ala warn() are unaffected,
+allowing duplicate user messages to be displayed.
+
=head2 The I<splain> Program
While apparently a whole nuther program, I<splain> is actually nothing
=cut
-require 5.001;
+use strict;
+use 5.006;
use Carp;
+our $VERSION = 1.1;
+our $DEBUG;
+our $VERBOSE;
+our $PRETTY;
+
use Config;
-($privlib, $archlib) = @Config{qw(privlibexp archlibexp)};
+my($privlib, $archlib) = @Config{qw(privlibexp archlibexp)};
if ($^O eq 'VMS') {
require VMS::Filespec;
$privlib = VMS::Filespec::unixify($privlib);
$archlib = VMS::Filespec::unixify($archlib);
}
-@trypod = ("$archlib/pod/perldiag.pod",
- "$privlib/pod/perldiag-$].pod",
- "$privlib/pod/perldiag.pod");
+my @trypod = (
+ "$archlib/pod/perldiag.pod",
+ "$privlib/pod/perldiag-$Config{version}.pod",
+ "$privlib/pod/perldiag.pod",
+ "$archlib/pods/perldiag.pod",
+ "$privlib/pods/perldiag-$Config{version}.pod",
+ "$privlib/pods/perldiag.pod",
+ );
# handy for development testing of new warnings etc
unshift @trypod, "./pod/perldiag.pod" if -e "pod/perldiag.pod";
-($PODFILE) = ((grep { -e } @trypod), $trypod[$#trypod])[0];
+(my $PODFILE) = ((grep { -e } @trypod), $trypod[$#trypod])[0];
+
+if ($^O eq 'MacOS') {
+ # just updir one from each lib dir, we'll find it ...
+ ($PODFILE) = grep { -e } map { "$_:pod:perldiag.pod" } @INC;
+}
+
$DEBUG ||= 0;
my $WHOAMI = ref bless []; # nobody's business, prolly not even mine
-$| = 1;
-
+local $| = 1;
local $_;
+my $standalone;
+my(%HTML_2_Troff, %HTML_2_Latin_1, %HTML_2_ASCII_7);
+
CONFIG: {
- $opt_p = $opt_d = $opt_v = $opt_f = '';
- %HTML_2_Troff = %HTML_2_Latin_1 = %HTML_2_ASCII_7 = ();
- %exact_duplicate = ();
+ our $opt_p = our $opt_d = our $opt_v = our $opt_f = '';
- unless (caller) {
+ unless (caller) {
$standalone++;
require Getopt::Std;
Getopt::Std::getopts('pdvf:')
$DEBUG = 2 if $opt_d;
$VERBOSE = $opt_v;
$PRETTY = $opt_p;
- }
+ }
if (open(POD_DIAG, $PODFILE)) {
warn "Happy happy podfile from real $PODFILE\n" if $DEBUG;
if (caller) {
INCPATH: {
- for $file ( (map { "$_/$WHOAMI.pm" } @INC), $0) {
+ for my $file ( (map { "$_/$WHOAMI.pm" } @INC), $0) {
warn "Checking $file\n" if $DEBUG;
if (open(POD_DIAG, $file)) {
while (<POD_DIAG>) {
- next unless /^__END__\s*# wish diag dbase were more accessible/;
+ next unless
+ /^__END__\s*# wish diag dbase were more accessible/;
print STDERR "podfile is $file\n" if $DEBUG;
last INCPATH;
}
# etc
);
+our %HTML_Escapes;
*HTML_Escapes = do {
if ($standalone) {
$PRETTY ? \%HTML_2_Latin_1 : \%HTML_2_ASCII_7;
*THITHER = $standalone ? *STDOUT : *STDERR;
-$transmo = <<EOFUNC;
+my $transmo = <<EOFUNC;
sub transmo {
- local \$^W = 0; # recursive warnings we do NOT need!
+ #local \$^W = 0; # recursive warnings we do NOT need!
study;
EOFUNC
-### sub finish_compilation { # 5.001e panic: top_level for embedded version
+my %msg;
+{
print STDERR "FINISHING COMPILATION for $_\n" if $DEBUG;
- ### local
- $RS = '';
+ local $/ = '';
local $_;
+ my $header;
+ my $for_item;
while (<POD_DIAG>) {
- #s/(.*)\n//;
- #$header = $1;
unescape();
if ($PRETTY) {
}
s/^/ /gm;
$msg{$header} .= $_;
+ undef $for_item;
}
next;
}
- unless ( s/=item (.*)\s*\Z//) {
+ unless ( s/=item (.*?)\s*\z//) {
if ( s/=head1\sDESCRIPTION//) {
$msg{$header = 'DESCRIPTION'} = '';
+ undef $for_item;
}
+ elsif( s/^=for\s+diagnostics\s*\n(.*?)\s*\z// ) {
+ $for_item = $1;
+ }
next;
}
+ if( $for_item ) { $header = $for_item; undef $for_item }
+ else {
+ $header = $1;
+ while( $header =~ /[;,]\z/ ) {
+ <POD_DIAG> =~ /^\s*(.*?)\s*\z/;
+ $header .= ' '.$1;
+ }
+ }
+
# strip formatting directives in =item line
- ($header = $1) =~ s/[A-Z]<(.*?)>/$1/g;
+ $header =~ s/[A-Z]<(.*?)>/$1/g;
- if ($header =~ /%[sd]/) {
- $rhs = $lhs = $header;
- #if ($lhs =~ s/(.*?)%d(?!%d)(.*)/\Q$1\E\\d+\Q$2\E\$/g) {
- if ($lhs =~ s/(.*?)%d(?!%d)(.*)/\Q$1\E\\d+\Q$2\E/g) {
+ if ($header =~ /%[csd]/) {
+ my $rhs = my $lhs = $header;
+ if ($lhs =~ s/(.*?)%d(?!%d)(.*)/\Q$1\E-?\\d+\Q$2\E/g) {
$lhs =~ s/\\%s/.*?/g;
} else {
- # if i had lookbehind negations, i wouldn't have to do this \377 noise
+ # if i had lookbehind negations,
+ # i wouldn't have to do this \377 noise
$lhs =~ s/(.*?)%s/\Q$1\E.*?\377/g;
- #$lhs =~ s/\377([^\377]*)$/\Q$1\E\$/;
$lhs =~ s/\377([^\377]*)$/\Q$1\E/;
$lhs =~ s/\377//g;
$lhs =~ s/\.\*\?$/.*/; # Allow %s at the end to eat it all
}
+ $lhs =~ s/\\%c/./g;
$transmo .= " s{^$lhs}\n {\Q$rhs\E}s\n\t&& return 1;\n";
} else {
$transmo .= " m{^\Q$header\E} && return 1;\n";
print STDERR $transmo if $DEBUG;
eval $transmo;
die $@ if $@;
- $RS = "\n";
-### }
+}
if ($standalone) {
if (!@ARGV and -t STDIN) { print STDERR "$0: Reading from STDIN\n" }
- while (defined ($error = <>)) {
+ while (defined (my $error = <>)) {
splainthis($error) || print THITHER $error;
}
exit;
-} else {
- $old_w = 0; $oldwarn = ''; $olddie = '';
-}
+}
+
+my $olddie;
+my $oldwarn;
sub import {
shift;
- $old_w = $^W;
- $^W = 1; # yup, clobbered the global variable; tough, if you
- # want diags, you want diags.
- return if $SIG{__WARN__} eq \&warn_trap;
+ $^W = 1; # yup, clobbered the global variable;
+ # tough, if you want diags, you want diags.
+ return if defined $SIG{__WARN__} && ($SIG{__WARN__} eq \&warn_trap);
for (@_) {
sub disable {
shift;
- $^W = $old_w;
return unless $SIG{__WARN__} eq \&warn_trap;
- $SIG{__WARN__} = $oldwarn;
- $SIG{__DIE__} = $olddie;
+ $SIG{__WARN__} = $oldwarn || '';
+ $SIG{__DIE__} = $olddie || '';
}
sub warn_trap {
if (caller eq $WHOAMI) { print STDERR "INTERNAL EXCEPTION: $exception"; }
&$olddie if defined $olddie and $olddie and $olddie ne \&death_trap;
+ return if $in_eval;
+
# We don't want to unset these if we're coming from an eval because
- # then we've turned off diagnostics. (Actually what does this next
- # line do? -PSeibel)
- $SIG{__DIE__} = $SIG{__WARN__} = '' unless $in_eval;
+ # then we've turned off diagnostics.
+
+ # Switch off our die/warn handlers so we don't wind up in our own
+ # traps.
+ $SIG{__DIE__} = $SIG{__WARN__} = '';
+
+ # Have carp skip over death_trap() when showing the stack trace.
local($Carp::CarpLevel) = 1;
+
confess "Uncaught exception from user code:\n\t$exception";
# up we go; where we stop, nobody knows, but i think we die now
# but i'm deeply afraid of the &$olddie guy reraising and us getting
# into an indirect recursion loop
};
+my %exact_duplicate;
+my %old_diag;
+my $count;
+my $wantspace;
sub splainthis {
local $_ = shift;
local $\;
s/\.?\n+$//;
my $orig = $_;
# return unless defined;
- if ($exact_duplicate{$_}++) {
- return 1;
- }
s/, <.*?> (?:line|chunk).*$//;
- $real = s/(.*?) at .*? (?:line|chunk) \d+.*/$1/;
+ my $real = s/(.*?) at .*? (?:line|chunk) \d+.*/$1/;
s/^\((.*)\)$/$1/;
- return 0 unless &transmo;
+ if ($exact_duplicate{$orig}++) {
+ return &transmo;
+ }
+ else {
+ return 0 unless &transmo;
+ }
$orig = shorten($orig);
if ($old_diag{$_}) {
autodescribe();
}
-# have to do this: RS isn't set until run time, but we're executing at compile time
-$RS = "\n";
-
1 unless $standalone; # or it'll complain about itself
__END__ # wish diag dbase were more accessible