This is a live mirror of the Perl 5 development currently hosted at https://github.com/perl/perl5
Follow up on elimination of $` $& $' in libraries
[perl5.git] / lib / diagnostics.pm
1 package diagnostics;
2
3 =head1 NAME
4
5 diagnostics - Perl compiler pragma to force verbose warning diagnostics
6
7 splain - standalone program to do the same thing
8
9 =head1 SYNOPSIS
10
11 As a pragma:
12
13     use diagnostics;
14     use diagnostics -verbose;
15
16     enable  diagnostics;
17     disable diagnostics;
18
19 Aa a program:
20
21     perl program 2>diag.out
22     splain [-v] [-p] diag.out
23
24
25 =head1 DESCRIPTION
26
27 =head2 The C<diagnostics> Pragma
28
29 This module extends the terse diagnostics normally emitted by both the
30 perl compiler and the perl interpeter, augmenting them with the more
31 explicative and endearing descriptions found in L<perldiag>.  Like the
32 other pragmata, it affects the compilation phase of your program rather
33 than merely the execution phase.
34
35 To use in your program as a pragma, merely invoke
36
37     use diagnostics;
38
39 at the start (or near the start) of your program.  (Note 
40 that this I<does> enable perl's B<-w> flag.)  Your whole
41 compilation will then be subject(ed :-) to the enhanced diagnostics.
42 These still go out B<STDERR>.
43
44 Due to the interaction between runtime and compiletime issues,
45 and because it's probably not a very good idea anyway,
46 you may not use C<no diagnostics> to turn them off at compiletime.
47 However, you may control there behaviour at runtime using the 
48 disable() and enable() methods to turn them off and on respectively.
49
50 The B<-verbose> flag first prints out the L<perldiag> introduction before
51 any other diagnostics.  The $diagnostics::PRETTY variable can generate nicer
52 escape sequences for pagers.
53
54 =head2 The I<splain> Program
55
56 While apparently a whole nuther program, I<splain> is actually nothing
57 more than a link to the (executable) F<diagnostics.pm> module, as well as
58 a link to the F<diagnostics.pod> documentation.  The B<-v> flag is like
59 the C<use diagnostics -verbose> directive.
60 The B<-p> flag is like the
61 $diagnostics::PRETTY variable.  Since you're post-processing with 
62 I<splain>, there's no sense in being able to enable() or disable() processing.
63
64 Output from I<splain> is directed to B<STDOUT>, unlike the pragma.
65
66 =head1 EXAMPLES
67
68 The following file is certain to trigger a few errors at both
69 runtime and compiletime:
70
71     use diagnostics;
72     print NOWHERE "nothing\n";
73     print STDERR "\n\tThis message should be unadorned.\n";
74     warn "\tThis is a user warning";
75     print "\nDIAGNOSTIC TESTER: Please enter a <CR> here: ";
76     my $a, $b = scalar <STDIN>;
77     print "\n";
78     print $x/$y;
79
80 If you prefer to run your program first and look at its problem
81 afterwards, do this:
82
83     perl -w test.pl 2>test.out
84     ./splain < test.out
85
86 Note that this is not in general possible in shells of more dubious heritage, 
87 as the theoretical 
88
89     (perl -w test.pl >/dev/tty) >& test.out
90     ./splain < test.out
91
92 Because you just moved the existing B<stdout> to somewhere else.
93
94 If you don't want to modify your source code, but still have on-the-fly
95 warnings, do this:
96
97     exec 3>&1; perl -w test.pl 2>&1 1>&3 3>&- | splain 1>&2 3>&- 
98
99 Nifty, eh?
100
101 If you want to control warnings on the fly, do something like this.
102 Make sure you do the C<use> first, or you won't be able to get
103 at the enable() or disable() methods.
104
105     use diagnostics; # checks entire compilation phase 
106         print "\ntime for 1st bogus diags: SQUAWKINGS\n";
107         print BOGUS1 'nada';
108         print "done with 1st bogus\n";
109
110     disable diagnostics; # only turns off runtime warnings
111         print "\ntime for 2nd bogus: (squelched)\n";
112         print BOGUS2 'nada';
113         print "done with 2nd bogus\n";
114
115     enable diagnostics; # turns back on runtime warnings
116         print "\ntime for 3rd bogus: SQUAWKINGS\n";
117         print BOGUS3 'nada';
118         print "done with 3rd bogus\n";
119
120     disable diagnostics;
121         print "\ntime for 4th bogus: (squelched)\n";
122         print BOGUS4 'nada';
123         print "done with 4th bogus\n";
124
125 =head1 INTERNALS
126
127 Diagnostic messages derive from the F<perldiag.pod> file when available at
128 runtime.  Otherwise, they may be embedded in the file itself when the
129 splain package is built.   See the F<Makefile> for details.
130
131 If an extant $SIG{__WARN__} handler is discovered, it will continue
132 to be honored, but only after the diagnostics::splainthis() function 
133 (the module's $SIG{__WARN__} interceptor) has had its way with your
134 warnings.
135
136 There is a $diagnostics::DEBUG variable you may set if you're desperately
137 curious what sorts of things are being intercepted.
138
139     BEGIN { $diagnostics::DEBUG = 1 } 
140
141
142 =head1 BUGS
143
144 Not being able to say "no diagnostics" is annoying, but may not be
145 insurmountable.
146
147 The C<-pretty> directive is called too late to affect matters.
148 You have to to this instead, and I<before> you load the module.
149
150     BEGIN { $diagnostics::PRETTY = 1 } 
151
152 I could start up faster by delaying compilation until it should be
153 needed, but this gets a "panic: top_level" when using the pragma form
154 in Perl 5.001e.
155
156 While it's true that this documentation is somewhat subserious, if you use
157 a program named I<splain>, you should expect a bit of whimsy.
158
159 =head1 AUTHOR
160
161 Tom Christiansen F<E<lt>tchrist@mox.perl.comE<gt>>, 25 June 1995.
162
163 =cut
164
165 require 5.001;
166 use Carp;
167
168 use Config;
169 if ($^O eq 'VMS') {
170     $PODFILE = VMS::Filespec::unixify($Config{privlibexp}).'/pod/perldiag.pod';
171 }
172 else {
173     $PODFILE = $Config{privlibexp} . "/pod/perldiag.pod";
174 }
175
176 $DEBUG ||= 0;
177 my $WHOAMI = ref bless [];  # nobody's business, prolly not even mine
178
179 $| = 1;
180
181 local $_;
182
183 CONFIG: {
184     $opt_p = $opt_d = $opt_v = $opt_f = '';
185     %HTML_2_Troff = %HTML_2_Latin_1 = %HTML_2_ASCII_7 = ();  
186     %exact_duplicate = ();
187
188     unless (caller) { 
189         $standalone++;
190         require Getopt::Std;
191         Getopt::Std::getopts('pdvf:') || die "Usage: $0 [-v] [-p] [-f splainpod]";
192         $PODFILE = $opt_f if $opt_f;
193         $DEBUG = 2 if $opt_d;
194         $VERBOSE = $opt_v;
195         $PRETTY = $opt_p;
196     } 
197
198     if (open(POD_DIAG, $PODFILE)) {
199         warn "Happy happy podfile from real $PODFILE\n" if $DEBUG;
200         last CONFIG;
201     } 
202
203     if (caller) {
204         INCPATH: {
205             for $file ( (map { "$_/$WHOAMI.pm" } @INC), $0) {
206                 warn "Checking $file\n" if $DEBUG;
207                 if (open(POD_DIAG, $file)) {
208                     while (<POD_DIAG>) {
209                         next unless /^__END__\s*# wish diag dbase were more accessible/;
210                         print STDERR "podfile is $file\n" if $DEBUG;
211                         last INCPATH;
212                     }
213                 }
214             } 
215         }
216     } else { 
217         print STDERR "podfile is <DATA>\n" if $DEBUG;
218         *POD_DIAG = *main::DATA;
219     }
220 }
221 if (eof(POD_DIAG)) { 
222     die "couldn't find diagnostic data in $PODFILE @INC $0";
223 }
224
225
226 %HTML_2_Troff = (
227     'amp'       =>      '&',    #   ampersand
228     'lt'        =>      '<',    #   left chevron, less-than
229     'gt'        =>      '>',    #   right chevron, greater-than
230     'quot'      =>      '"',    #   double quote
231
232     "Aacute"    =>      "A\\*'",        #   capital A, acute accent
233     # etc
234
235 );
236
237 %HTML_2_Latin_1 = (
238     'amp'       =>      '&',    #   ampersand
239     'lt'        =>      '<',    #   left chevron, less-than
240     'gt'        =>      '>',    #   right chevron, greater-than
241     'quot'      =>      '"',    #   double quote
242
243     "Aacute"    =>      "\xC1"  #   capital A, acute accent
244
245     # etc
246 );
247
248 %HTML_2_ASCII_7 = (
249     'amp'       =>      '&',    #   ampersand
250     'lt'        =>      '<',    #   left chevron, less-than
251     'gt'        =>      '>',    #   right chevron, greater-than
252     'quot'      =>      '"',    #   double quote
253
254     "Aacute"    =>      "A"     #   capital A, acute accent
255     # etc
256 );
257
258 *HTML_Escapes = do {
259     if ($standalone) {
260         $PRETTY ? \%HTML_2_Latin_1 : \%HTML_2_ASCII_7; 
261     } else {
262         \%HTML_2_Latin_1; 
263     }
264 }; 
265
266 *THITHER = $standalone ? *STDOUT : *STDERR;
267
268 $transmo = <<EOFUNC;
269 sub transmo {
270     local \$^W = 0;  # recursive warnings we do NOT need!
271     study;
272 EOFUNC
273
274 ### sub finish_compilation {  # 5.001e panic: top_level for embedded version
275     print STDERR "FINISHING COMPILATION for $_\n" if $DEBUG;
276     ### local 
277     $RS = '';
278     local $_;
279     while (<POD_DIAG>) {
280         #s/(.*)\n//;
281         #$header = $1;
282
283         unescape();
284         if ($PRETTY) {
285             sub noop   { return $_[0] }  # spensive for a noop
286             sub bold   { my $str =$_[0];  $str =~ s/(.)/$1\b$1/g; return $str; } 
287             sub italic { my $str = $_[0]; $str =~ s/(.)/_\b$1/g;  return $str; } 
288             s/[BC]<(.*?)>/bold($1)/ges;
289             s/[LIF]<(.*?)>/italic($1)/ges;
290         } else {
291             s/[BC]<(.*?)>/$1/gs;
292             s/[LIF]<(.*?)>/$1/gs;
293         } 
294         unless (/^=/) {
295             if (defined $header) { 
296                 if ( $header eq 'DESCRIPTION' && 
297                     (   /Optional warnings are enabled/ 
298                      || /Some of these messages are generic./
299                     ) )
300                 {
301                     next;
302                 } 
303                 s/^/    /gm;
304                 $msg{$header} .= $_;
305             }
306             next;
307         } 
308         unless ( s/=item (.*)\s*\Z//) {
309
310             if ( s/=head1\sDESCRIPTION//) {
311                 $msg{$header = 'DESCRIPTION'} = '';
312             }
313             next;
314         }
315
316         # strip formatting directives in =item line
317         ($header = $1) =~ s/[A-Z]<(.*?)>/$1/g;
318
319         if ($header =~ /%[sd]/) {
320             $rhs = $lhs = $header;
321             #if ($lhs =~ s/(.*?)%d(?!%d)(.*)/\Q$1\E\\d+\Q$2\E\$/g)  {
322             if ($lhs =~ s/(.*?)%d(?!%d)(.*)/\Q$1\E\\d+\Q$2\E/g)  {
323                 $lhs =~ s/\\%s/.*?/g;
324             } else {
325                 # if i had lookbehind negations, i wouldn't have to do this \377 noise
326                 $lhs =~ s/(.*?)%s/\Q$1\E.*?\377/g;
327                 #$lhs =~ s/\377([^\377]*)$/\Q$1\E\$/;
328                 $lhs =~ s/\377([^\377]*)$/\Q$1\E/;
329                 $lhs =~ s/\377//g;
330                 $lhs =~ s/\.\*\?$/.*/; # Allow %s at the end to eat it all
331             } 
332             $transmo .= "    s{^$lhs}\n     {\Q$rhs\E}s\n\t&& return 1;\n";
333         } else {
334             $transmo .= "    m{^\Q$header\E} && return 1;\n";
335         } 
336
337         print STDERR "$WHOAMI: Duplicate entry: \"$header\"\n"
338             if $msg{$header};
339
340         $msg{$header} = '';
341     } 
342
343
344     close POD_DIAG unless *main::DATA eq *POD_DIAG;
345
346     die "No diagnostics?" unless %msg;
347
348     $transmo .= "    return 0;\n}\n";
349     print STDERR $transmo if $DEBUG;
350     eval $transmo;
351     die $@ if $@;
352     $RS = "\n";
353 ### }
354
355 if ($standalone) {
356     if (!@ARGV and -t STDIN) { print STDERR "$0: Reading from STDIN\n" } 
357     while (defined ($error = <>)) {
358         splainthis($error) || print THITHER $error;
359     } 
360     exit;
361 } else { 
362     $old_w = 0; $oldwarn = ''; $olddie = '';
363 }
364
365 sub import {
366     shift;
367     $old_w = $^W;
368     $^W = 1; # yup, clobbered the global variable; tough, if you
369              # want diags, you want diags.
370     return if $SIG{__WARN__} eq \&warn_trap;
371
372     for (@_) {
373
374         /^-d(ebug)?$/           && do {
375                                     $DEBUG++;
376                                     next;
377                                    };
378
379         /^-v(erbose)?$/         && do {
380                                     $VERBOSE++;
381                                     next;
382                                    };
383
384         /^-p(retty)?$/          && do {
385                                     print STDERR "$0: I'm afraid it's too late for prettiness.\n";
386                                     $PRETTY++;
387                                     next;
388                                };
389
390         warn "Unknown flag: $_";
391     } 
392
393     $oldwarn = $SIG{__WARN__};
394     $olddie = $SIG{__DIE__};
395     $SIG{__WARN__} = \&warn_trap;
396     $SIG{__DIE__} = \&death_trap;
397
398
399 sub enable { &import }
400
401 sub disable {
402     shift;
403     $^W = $old_w;
404     return unless $SIG{__WARN__} eq \&warn_trap;
405     $SIG{__WARN__} = $oldwarn;
406     $SIG{__DIE__} = $olddie;
407
408
409 sub warn_trap {
410     my $warning = $_[0];
411     if (caller eq $WHOAMI or !splainthis($warning)) {
412         print STDERR $warning;
413     } 
414     &$oldwarn if defined $oldwarn and $oldwarn and $oldwarn ne \&warn_trap;
415 };
416
417 sub death_trap {
418     my $exception = $_[0];
419
420     # See if we are coming from anywhere within an eval. If so we don't
421     # want to explain the exception because it's going to get caught.
422     my $in_eval = 0;
423     my $i = 0;
424     while (1) {
425       my $caller = (caller($i++))[3] or last;
426       if ($caller eq '(eval)') {
427         $in_eval = 1;
428         last;
429       }
430     }
431
432     splainthis($exception) unless $in_eval;
433     if (caller eq $WHOAMI) { print STDERR "INTERNAL EXCEPTION: $exception"; } 
434     &$olddie if defined $olddie and $olddie and $olddie ne \&death_trap;
435
436     # We don't want to unset these if we're coming from an eval because
437     # then we've turned off diagnostics. (Actually what does this next
438     # line do?  -PSeibel)
439     $SIG{__DIE__} = $SIG{__WARN__} = '' unless $in_eval;
440     local($Carp::CarpLevel) = 1;
441     confess "Uncaught exception from user code:\n\t$exception";
442         # up we go; where we stop, nobody knows, but i think we die now
443         # but i'm deeply afraid of the &$olddie guy reraising and us getting
444         # into an indirect recursion loop
445 };
446
447 sub splainthis {
448     local $_ = shift;
449     ### &finish_compilation unless %msg;
450     s/\.?\n+$//;
451     my $orig = $_;
452     # return unless defined;
453     if ($exact_duplicate{$_}++) {
454         return 1;
455     } 
456     s/, <.*?> (?:line|chunk).*$//;
457     $real = s/(.*?) at .*? (?:line|chunk) \d+.*/$1/;
458     s/^\((.*)\)$/$1/;
459     return 0 unless &transmo;
460     $orig = shorten($orig);
461     if ($old_diag{$_}) {
462         autodescribe();
463         print THITHER "$orig (#$old_diag{$_})\n";
464         $wantspace = 1;
465     } else {
466         autodescribe();
467         $old_diag{$_} = ++$count;
468         print THITHER "\n" if $wantspace;
469         $wantspace = 0;
470         print THITHER "$orig (#$old_diag{$_})\n";
471         if ($msg{$_}) {
472             print THITHER $msg{$_};
473         } else {
474             if (0 and $standalone) { 
475                 print THITHER "    **** Error #$old_diag{$_} ",
476                         ($real ? "is" : "appears to be"),
477                         " an unknown diagnostic message.\n\n";
478             }
479             return 0;
480         } 
481     }
482     return 1;
483
484
485 sub autodescribe {
486     if ($VERBOSE and not $count) {
487         print THITHER &{$PRETTY ? \&bold : \&noop}("DESCRIPTION OF DIAGNOSTICS"),
488                 "\n$msg{DESCRIPTION}\n";
489     } 
490
491
492 sub unescape { 
493     s {
494             E<  
495             ( [A-Za-z]+ )       
496             >   
497     } { 
498          do {   
499              exists $HTML_Escapes{$1}
500                 ? do { $HTML_Escapes{$1} }
501                 : do {
502                     warn "Unknown escape: E<$1> in $_";
503                     "E<$1>";
504                 } 
505          } 
506     }egx;
507 }
508
509 sub shorten {
510     my $line = $_[0];
511     if (length($line) > 79 and index($line, "\n") == -1) {
512         my $space_place = rindex($line, ' ', 79);
513         if ($space_place != -1) {
514             substr($line, $space_place, 1) = "\n\t";
515         } 
516     } 
517     return $line;
518
519
520
521 # have to do this: RS isn't set until run time, but we're executing at compile time
522 $RS = "\n";
523
524 1 unless $standalone;  # or it'll complain about itself
525 __END__ # wish diag dbase were more accessible