This is a live mirror of the Perl 5 development currently hosted at https://github.com/perl/perl5
Retract #11712 for now. The real fix would probably
[perl5.git] / lib / diagnostics.pm
old mode 100644 (file)
new mode 100755 (executable)
index 89d7467..4ef9a2f
@@ -27,7 +27,7 @@ Aa a program:
 =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.
@@ -44,13 +44,18 @@ These still go out B<STDERR>.
 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
@@ -145,7 +150,7 @@ Not being able to say "no diagnostics" is annoying, but may not be
 insurmountable.
 
 The C<-pretty> directive is called too late to affect matters.
-You have to to this instead, and I<before> you load the module.
+You have to do this instead, and I<before> you load the module.
 
     BEGIN { $diagnostics::PRETTY = 1 } 
 
@@ -158,43 +163,66 @@ a program named I<splain>, you should expect a bit of whimsy.
 
 =head1 AUTHOR
 
-Tom Christiansen F<E<lt>tchrist@mox.perl.comE<gt>>, 25 June 1995.
+Tom Christiansen <F<tchrist@mox.perl.com>>, 25 June 1995.
 
 =cut
 
-require 5.001;
-use English;
+use strict;
+use 5.6.0;
 use Carp;
 
+our $VERSION = 1.0;
+our $DEBUG;
+our $VERBOSE;
+our $PRETTY;
+
 use Config;
+my($privlib, $archlib) = @Config{qw(privlibexp archlibexp)};
 if ($^O eq 'VMS') {
-    $PODFILE = VMS::Filespec::unixify($Config{privlibexp}).'/pod/perldiag.pod';
+    require VMS::Filespec;
+    $privlib = VMS::Filespec::unixify($privlib);
+    $archlib = VMS::Filespec::unixify($archlib);
 }
-else {
-    $PODFILE = $Config{privlibexp} . "/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";
+(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
 
-$OUTPUT_AUTOFLUSH = 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:') || die "Usage: $0 [-v] [-p] [-f splainpod]";
+       Getopt::Std::getopts('pdvf:')
+           or die "Usage: $0 [-v] [-p] [-f splainpod]";
        $PODFILE = $opt_f if $opt_f;
        $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;
@@ -203,11 +231,12 @@ CONFIG: {
 
     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;
                    }
@@ -256,6 +285,7 @@ if (eof(POD_DIAG)) {
     # etc
 );
 
+our %HTML_Escapes;
 *HTML_Escapes = do {
     if ($standalone) {
        $PRETTY ? \%HTML_2_Latin_1 : \%HTML_2_ASCII_7; 
@@ -266,20 +296,20 @@ if (eof(POD_DIAG)) {
 
 *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) {
@@ -303,31 +333,47 @@ EOFUNC
                } 
                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;
        }
-       $header = $1;
 
-       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( $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 =~ s/[A-Z]<(.*?)>/$1/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";
@@ -348,24 +394,23 @@ EOFUNC
     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.
+    $^W = 1; # yup, clobbered the global variable; 
+            # tough, if you want diags, you want diags.
     return if $SIG{__WARN__} eq \&warn_trap;
 
     for (@_) {
@@ -399,10 +444,9 @@ sub enable { &import }
 
 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 {
@@ -443,19 +487,26 @@ sub death_trap {
        # into an indirect recursion loop
 };
 
+my %exact_duplicate;
+my %old_diag;
+my $count;
+my $wantspace;
 sub splainthis {
     local $_ = shift;
+    local $\;
     ### &finish_compilation unless %msg;
     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();
@@ -498,7 +549,7 @@ sub unescape {
              exists $HTML_Escapes{$1}
                 ? do { $HTML_Escapes{$1} }
                 : do {
-                    warn "Unknown escape: $& in $_";
+                    warn "Unknown escape: E<$1> in $_";
                     "E<$1>";
                 } 
          } 
@@ -517,8 +568,5 @@ sub shorten {
 } 
 
 
-# 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