Skip suid File::Copy tests on a nosuid partition
[perl.git] / warnings.pl
1 #!/usr/bin/perl
2
3 # Regenerate (overwriting only if changed):
4 #
5 #    lib/warnings.pm
6 #    warnings.h
7 #
8 # from information hardcoded into this script (the $tree hash), plus the
9 # template for warnings.pm in the DATA section.
10 #
11 # With an argument of 'tree', just dump the contents of $tree and exits.
12 # Also accepts the standard regen_lib -q and -v args.
13 #
14 # This script is normally invoked from regen.pl.
15
16 $VERSION = '1.02_03';
17
18 BEGIN {
19     require 'regen_lib.pl';
20     push @INC, './lib';
21 }
22 use strict ;
23
24 sub DEFAULT_ON  () { 1 }
25 sub DEFAULT_OFF () { 2 }
26
27 my $tree = {
28
29 'all' => [ 5.008, {
30         'io'            => [ 5.008, {   
31                                 'pipe'          => [ 5.008, DEFAULT_OFF],
32                                 'unopened'      => [ 5.008, DEFAULT_OFF],
33                                 'closed'        => [ 5.008, DEFAULT_OFF],
34                                 'newline'       => [ 5.008, DEFAULT_OFF],
35                                 'exec'          => [ 5.008, DEFAULT_OFF],
36                                 'layer'         => [ 5.008, DEFAULT_OFF],
37                            }],
38         'syntax'        => [ 5.008, {   
39                                 'ambiguous'     => [ 5.008, DEFAULT_OFF],
40                                 'semicolon'     => [ 5.008, DEFAULT_OFF],
41                                 'precedence'    => [ 5.008, DEFAULT_OFF],
42                                 'bareword'      => [ 5.008, DEFAULT_OFF],
43                                 'reserved'      => [ 5.008, DEFAULT_OFF],
44                                 'digit'         => [ 5.008, DEFAULT_OFF],
45                                 'parenthesis'   => [ 5.008, DEFAULT_OFF],
46                                 'printf'        => [ 5.008, DEFAULT_OFF],
47                                 'prototype'     => [ 5.008, DEFAULT_OFF],
48                                 'qw'            => [ 5.008, DEFAULT_OFF],
49                                 'illegalproto'  => [ 5.011, DEFAULT_OFF],
50                            }],
51         'severe'        => [ 5.008, {   
52                                 'inplace'       => [ 5.008, DEFAULT_ON],
53                                 'internal'      => [ 5.008, DEFAULT_ON],
54                                 'debugging'     => [ 5.008, DEFAULT_ON],
55                                 'malloc'        => [ 5.008, DEFAULT_ON],
56                            }],
57         'deprecated'    => [ 5.008, DEFAULT_OFF],
58         'void'          => [ 5.008, DEFAULT_OFF],
59         'recursion'     => [ 5.008, DEFAULT_OFF],
60         'redefine'      => [ 5.008, DEFAULT_OFF],
61         'numeric'       => [ 5.008, DEFAULT_OFF],
62         'uninitialized' => [ 5.008, DEFAULT_OFF],
63         'once'          => [ 5.008, DEFAULT_OFF],
64         'misc'          => [ 5.008, DEFAULT_OFF],
65         'regexp'        => [ 5.008, DEFAULT_OFF],
66         'glob'          => [ 5.008, DEFAULT_OFF],
67         'untie'         => [ 5.008, DEFAULT_OFF],
68         'substr'        => [ 5.008, DEFAULT_OFF],
69         'taint'         => [ 5.008, DEFAULT_OFF],
70         'signal'        => [ 5.008, DEFAULT_OFF],
71         'closure'       => [ 5.008, DEFAULT_OFF],
72         'overflow'      => [ 5.008, DEFAULT_OFF],
73         'portable'      => [ 5.008, DEFAULT_OFF],
74         'utf8'          => [ 5.008, DEFAULT_OFF],
75         'exiting'       => [ 5.008, DEFAULT_OFF],
76         'pack'          => [ 5.008, DEFAULT_OFF],
77         'unpack'        => [ 5.008, DEFAULT_OFF],
78         'threads'       => [ 5.008, DEFAULT_OFF],
79         'imprecision'   => [ 5.011, DEFAULT_OFF],
80
81          #'default'     => [ 5.008, DEFAULT_ON ],
82         }],
83 } ;
84
85 ###########################################################################
86 sub tab {
87     my($l, $t) = @_;
88     $t .= "\t" x ($l - (length($t) + 1) / 8);
89     $t;
90 }
91
92 ###########################################################################
93
94 my %list ;
95 my %Value ;
96 my %ValueToName ;
97 my %NameToValue ;
98 my $index ;
99
100 my %v_list = () ;
101
102 sub valueWalk
103 {
104     my $tre = shift ;
105     my @list = () ;
106     my ($k, $v) ;
107
108     foreach $k (sort keys %$tre) {
109         $v = $tre->{$k};
110         die "duplicate key $k\n" if defined $list{$k} ;
111         die "Value associated with key '$k' is not an ARRAY reference"
112             if !ref $v || ref $v ne 'ARRAY' ;
113
114         my ($ver, $rest) = @{ $v } ;
115         push @{ $v_list{$ver} }, $k;
116         
117         if (ref $rest)
118           { valueWalk ($rest) }
119
120     }
121
122 }
123
124 sub orderValues
125 {
126     my $index = 0;
127     foreach my $ver ( sort { $a <=> $b } keys %v_list ) {
128         foreach my $name (@{ $v_list{$ver} } ) {
129             $ValueToName{ $index } = [ uc $name, $ver ] ;
130             $NameToValue{ uc $name } = $index ++ ;
131         }
132     }
133
134     return $index ;
135 }
136
137 ###########################################################################
138
139 sub walk
140 {
141     my $tre = shift ;
142     my @list = () ;
143     my ($k, $v) ;
144
145     foreach $k (sort keys %$tre) {
146         $v = $tre->{$k};
147         die "duplicate key $k\n" if defined $list{$k} ;
148         #$Value{$index} = uc $k ;
149         die "Can't find key '$k'"
150             if ! defined $NameToValue{uc $k} ;
151         push @{ $list{$k} }, $NameToValue{uc $k} ;
152         die "Value associated with key '$k' is not an ARRAY reference"
153             if !ref $v || ref $v ne 'ARRAY' ;
154         
155         my ($ver, $rest) = @{ $v } ;
156         if (ref $rest)
157           { push (@{ $list{$k} }, walk ($rest)) }
158
159         push @list, @{ $list{$k} } ;
160     }
161
162    return @list ;
163 }
164
165 ###########################################################################
166
167 sub mkRange
168 {
169     my @a = @_ ;
170     my @out = @a ;
171     my $i ;
172
173
174     for ($i = 1 ; $i < @a; ++ $i) {
175         $out[$i] = ".."
176           if $a[$i] == $a[$i - 1] + 1 && $a[$i] + 1 == $a[$i + 1] ;
177     }
178
179     my $out = join(",",@out);
180
181     $out =~ s/,(\.\.,)+/../g ;
182     return $out;
183 }
184
185 ###########################################################################
186 sub printTree
187 {
188     my $tre = shift ;
189     my $prefix = shift ;
190     my ($k, $v) ;
191
192     my $max = (sort {$a <=> $b} map { length $_ } keys %$tre)[-1] ;
193     my @keys = sort keys %$tre ;
194
195     while ($k = shift @keys) {
196         $v = $tre->{$k};
197         die "Value associated with key '$k' is not an ARRAY reference"
198             if !ref $v || ref $v ne 'ARRAY' ;
199         
200         my $offset ;
201         if ($tre ne $tree) {
202             print $prefix . "|\n" ;
203             print $prefix . "+- $k" ;
204             $offset = ' ' x ($max + 4) ;
205         }
206         else {
207             print $prefix . "$k" ;
208             $offset = ' ' x ($max + 1) ;
209         }
210
211         my ($ver, $rest) = @{ $v } ;
212         if (ref $rest)
213         {
214             my $bar = @keys ? "|" : " ";
215             print " -" . "-" x ($max - length $k ) . "+\n" ;
216             printTree ($rest, $prefix . $bar . $offset )
217         }
218         else
219           { print "\n" }
220     }
221
222 }
223
224 ###########################################################################
225
226 sub mkHexOct
227 {
228     my ($f, $max, @a) = @_ ;
229     my $mask = "\x00" x $max ;
230     my $string = "" ;
231
232     foreach (@a) {
233         vec($mask, $_, 1) = 1 ;
234     }
235
236     foreach (unpack("C*", $mask)) {
237         if ($f eq 'x') {
238             $string .= '\x' . sprintf("%2.2x", $_)
239         }
240         else {
241             $string .= '\\' . sprintf("%o", $_)
242         }
243     }
244     return $string ;
245 }
246
247 sub mkHex
248 {
249     my($max, @a) = @_;
250     return mkHexOct("x", $max, @a);
251 }
252
253 sub mkOct
254 {
255     my($max, @a) = @_;
256     return mkHexOct("o", $max, @a);
257 }
258
259 ###########################################################################
260
261 if (@ARGV && $ARGV[0] eq "tree")
262 {
263     printTree($tree, "    ") ;
264     exit ;
265 }
266
267 my $warn = safer_open("warnings.h-new");
268 my $pm = safer_open("lib/warnings.pm-new");
269
270 print $warn <<'EOM' ;
271 /* -*- buffer-read-only: t -*-
272    !!!!!!!   DO NOT EDIT THIS FILE   !!!!!!!
273    This file is built by warnings.pl
274    Any changes made here will be lost!
275 */
276
277
278 #define Off(x)                  ((x) / 8)
279 #define Bit(x)                  (1 << ((x) % 8))
280 #define IsSet(a, x)             ((a)[Off(x)] & Bit(x))
281
282
283 #define G_WARN_OFF              0       /* $^W == 0 */
284 #define G_WARN_ON               1       /* -w flag and $^W != 0 */
285 #define G_WARN_ALL_ON           2       /* -W flag */
286 #define G_WARN_ALL_OFF          4       /* -X flag */
287 #define G_WARN_ONCE             8       /* set if 'once' ever enabled */
288 #define G_WARN_ALL_MASK         (G_WARN_ALL_ON|G_WARN_ALL_OFF)
289
290 #define pWARN_STD               NULL
291 #define pWARN_ALL               (((STRLEN*)0)+1)    /* use warnings 'all' */
292 #define pWARN_NONE              (((STRLEN*)0)+2)    /* no  warnings 'all' */
293
294 #define specialWARN(x)          ((x) == pWARN_STD || (x) == pWARN_ALL ||        \
295                                  (x) == pWARN_NONE)
296
297 /* if PL_warnhook is set to this value, then warnings die */
298 #define PERL_WARNHOOK_FATAL     (&PL_sv_placeholder)
299 EOM
300
301 my $offset = 0 ;
302
303 $index = $offset ;
304 #@{ $list{"all"} } = walk ($tree) ;
305 valueWalk ($tree) ;
306 my $index = orderValues();
307
308 die <<EOM if $index > 255 ;
309 Too many warnings categories -- max is 255
310     rewrite packWARN* & unpackWARN* macros 
311 EOM
312
313 walk ($tree) ;
314
315 $index *= 2 ;
316 my $warn_size = int($index / 8) + ($index % 8 != 0) ;
317
318 my $k ;
319 my $last_ver = 0;
320 foreach $k (sort { $a <=> $b } keys %ValueToName) {
321     my ($name, $version) = @{ $ValueToName{$k} };
322     print $warn "\n/* Warnings Categories added in Perl $version */\n\n"
323         if $last_ver != $version ;
324     print $warn tab(5, "#define WARN_$name"), "$k\n" ;
325     $last_ver = $version ;
326 }
327 print $warn "\n" ;
328
329 print $warn tab(5, '#define WARNsize'), "$warn_size\n" ;
330 #print WARN tab(5, '#define WARN_ALLstring'), '"', ('\377' x $warn_size) , "\"\n" ;
331 print $warn tab(5, '#define WARN_ALLstring'), '"', ('\125' x $warn_size) , "\"\n" ;
332 print $warn tab(5, '#define WARN_NONEstring'), '"', ('\0' x $warn_size) , "\"\n" ;
333
334 print $warn <<'EOM';
335
336 #define isLEXWARN_on    (PL_curcop->cop_warnings != pWARN_STD)
337 #define isLEXWARN_off   (PL_curcop->cop_warnings == pWARN_STD)
338 #define isWARN_ONCE     (PL_dowarn & (G_WARN_ON|G_WARN_ONCE))
339 #define isWARN_on(c,x)  (IsSet((U8 *)(c + 1), 2*(x)))
340 #define isWARNf_on(c,x) (IsSet((U8 *)(c + 1), 2*(x)+1))
341
342 #define DUP_WARNINGS(p)         \
343     (specialWARN(p) ? (STRLEN*)(p)      \
344     : (STRLEN*)CopyD(p, PerlMemShared_malloc(sizeof(*p)+*p), sizeof(*p)+*p, \
345                                              char))
346
347 #define ckWARN(w)               Perl_ckwarn(aTHX_ packWARN(w))
348 #define ckWARN2(w1,w2)          Perl_ckwarn(aTHX_ packWARN2(w1,w2))
349 #define ckWARN3(w1,w2,w3)       Perl_ckwarn(aTHX_ packWARN3(w1,w2,w3))
350 #define ckWARN4(w1,w2,w3,w4)    Perl_ckwarn(aTHX_ packWARN4(w1,w2,w3,w4))
351
352 #define ckWARN_d(w)             Perl_ckwarn_d(aTHX_ packWARN(w))
353 #define ckWARN2_d(w1,w2)        Perl_ckwarn_d(aTHX_ packWARN2(w1,w2))
354 #define ckWARN3_d(w1,w2,w3)     Perl_ckwarn_d(aTHX_ packWARN3(w1,w2,w3))
355 #define ckWARN4_d(w1,w2,w3,w4)  Perl_ckwarn_d(aTHX_ packWARN4(w1,w2,w3,w4))
356
357 #define WARNshift               8
358
359 #define packWARN(a)             (a                                      )
360 #define packWARN2(a,b)          ((a) | ((b)<<8)                         )
361 #define packWARN3(a,b,c)        ((a) | ((b)<<8) | ((c)<<16)             )
362 #define packWARN4(a,b,c,d)      ((a) | ((b)<<8) | ((c)<<16) | ((d) <<24))
363
364 #define unpackWARN1(x)          ((x)        & 0xFF)
365 #define unpackWARN2(x)          (((x) >>8)  & 0xFF)
366 #define unpackWARN3(x)          (((x) >>16) & 0xFF)
367 #define unpackWARN4(x)          (((x) >>24) & 0xFF)
368
369 #define ckDEAD(x)                                                       \
370            ( ! specialWARN(PL_curcop->cop_warnings) &&                  \
371             ( isWARNf_on(PL_curcop->cop_warnings, WARN_ALL) ||          \
372               isWARNf_on(PL_curcop->cop_warnings, unpackWARN1(x)) ||    \
373               isWARNf_on(PL_curcop->cop_warnings, unpackWARN2(x)) ||    \
374               isWARNf_on(PL_curcop->cop_warnings, unpackWARN3(x)) ||    \
375               isWARNf_on(PL_curcop->cop_warnings, unpackWARN4(x))))
376
377 /* end of file warnings.h */
378 /* ex: set ro: */
379 EOM
380
381 safer_close $warn;
382 rename_if_different("warnings.h-new", "warnings.h");
383
384 while (<DATA>) {
385     last if /^KEYWORDS$/ ;
386     print $pm $_ ;
387 }
388
389 #$list{'all'} = [ $offset .. 8 * ($warn_size/2) - 1 ] ;
390
391 $last_ver = 0;
392 print $pm "our %Offsets = (\n" ;
393 foreach my $k (sort { $a <=> $b } keys %ValueToName) {
394     my ($name, $version) = @{ $ValueToName{$k} };
395     $name = lc $name;
396     $k *= 2 ;
397     if ( $last_ver != $version ) {
398         print $pm "\n";
399         print $pm tab(4, "    # Warnings Categories added in Perl $version");
400         print $pm "\n\n";
401     }
402     print $pm tab(4, "    '$name'"), "=> $k,\n" ;
403     $last_ver = $version;
404 }
405
406 print $pm "  );\n\n" ;
407
408 print $pm "our %Bits = (\n" ;
409 foreach $k (sort keys  %list) {
410
411     my $v = $list{$k} ;
412     my @list = sort { $a <=> $b } @$v ;
413
414     print $pm tab(4, "    '$k'"), '=> "',
415                 # mkHex($warn_size, @list),
416                 mkHex($warn_size, map $_ * 2 , @list),
417                 '", # [', mkRange(@list), "]\n" ;
418 }
419
420 print $pm "  );\n\n" ;
421
422 print $pm "our %DeadBits = (\n" ;
423 foreach $k (sort keys  %list) {
424
425     my $v = $list{$k} ;
426     my @list = sort { $a <=> $b } @$v ;
427
428     print $pm tab(4, "    '$k'"), '=> "',
429                 # mkHex($warn_size, @list),
430                 mkHex($warn_size, map $_ * 2 + 1 , @list),
431                 '", # [', mkRange(@list), "]\n" ;
432 }
433
434 print $pm "  );\n\n" ;
435 print $pm '$NONE     = "', ('\0' x $warn_size) , "\";\n" ;
436 print $pm '$LAST_BIT = ' . "$index ;\n" ;
437 print $pm '$BYTES    = ' . "$warn_size ;\n" ;
438 while (<DATA>) {
439     print $pm $_ ;
440 }
441
442 print $pm "# ex: set ro:\n";
443 safer_close $pm;
444 rename_if_different("lib/warnings.pm-new", "lib/warnings.pm");
445
446 __END__
447 # -*- buffer-read-only: t -*-
448 # !!!!!!!   DO NOT EDIT THIS FILE   !!!!!!!
449 # This file was created by warnings.pl
450 # Any changes made here will be lost.
451 #
452
453 package warnings;
454
455 our $VERSION = '1.10';
456
457 # Verify that we're called correctly so that warnings will work.
458 # see also strict.pm.
459 unless ( __FILE__ =~ /(^|[\/\\])\Q${\__PACKAGE__}\E\.pmc?$/ ) {
460     my (undef, $f, $l) = caller;
461     die("Incorrect use of pragma '${\__PACKAGE__}' at $f line $l.\n");
462 }
463
464 =head1 NAME
465
466 warnings - Perl pragma to control optional warnings
467
468 =head1 SYNOPSIS
469
470     use warnings;
471     no warnings;
472
473     use warnings "all";
474     no warnings "all";
475
476     use warnings::register;
477     if (warnings::enabled()) {
478         warnings::warn("some warning");
479     }
480
481     if (warnings::enabled("void")) {
482         warnings::warn("void", "some warning");
483     }
484
485     if (warnings::enabled($object)) {
486         warnings::warn($object, "some warning");
487     }
488
489     warnings::warnif("some warning");
490     warnings::warnif("void", "some warning");
491     warnings::warnif($object, "some warning");
492
493 =head1 DESCRIPTION
494
495 The C<warnings> pragma is a replacement for the command line flag C<-w>,
496 but the pragma is limited to the enclosing block, while the flag is global.
497 See L<perllexwarn> for more information.
498
499 If no import list is supplied, all possible warnings are either enabled
500 or disabled.
501
502 A number of functions are provided to assist module authors.
503
504 =over 4
505
506 =item use warnings::register
507
508 Creates a new warnings category with the same name as the package where
509 the call to the pragma is used.
510
511 =item warnings::enabled()
512
513 Use the warnings category with the same name as the current package.
514
515 Return TRUE if that warnings category is enabled in the calling module.
516 Otherwise returns FALSE.
517
518 =item warnings::enabled($category)
519
520 Return TRUE if the warnings category, C<$category>, is enabled in the
521 calling module.
522 Otherwise returns FALSE.
523
524 =item warnings::enabled($object)
525
526 Use the name of the class for the object reference, C<$object>, as the
527 warnings category.
528
529 Return TRUE if that warnings category is enabled in the first scope
530 where the object is used.
531 Otherwise returns FALSE.
532
533 =item warnings::fatal_enabled()
534
535 Return TRUE if the warnings category with the same name as the current
536 package has been set to FATAL in the calling module.
537 Otherwise returns FALSE.
538
539 =item warnings::fatal_enabled($category)
540
541 Return TRUE if the warnings category C<$category> has been set to FATAL in
542 the calling module.
543 Otherwise returns FALSE.
544
545 =item warnings::fatal_enabled($object)
546
547 Use the name of the class for the object reference, C<$object>, as the
548 warnings category.
549
550 Return TRUE if that warnings category has been set to FATAL in the first
551 scope where the object is used.
552 Otherwise returns FALSE.
553
554 =item warnings::warn($message)
555
556 Print C<$message> to STDERR.
557
558 Use the warnings category with the same name as the current package.
559
560 If that warnings category has been set to "FATAL" in the calling module
561 then die. Otherwise return.
562
563 =item warnings::warn($category, $message)
564
565 Print C<$message> to STDERR.
566
567 If the warnings category, C<$category>, has been set to "FATAL" in the
568 calling module then die. Otherwise return.
569
570 =item warnings::warn($object, $message)
571
572 Print C<$message> to STDERR.
573
574 Use the name of the class for the object reference, C<$object>, as the
575 warnings category.
576
577 If that warnings category has been set to "FATAL" in the scope where C<$object>
578 is first used then die. Otherwise return.
579
580
581 =item warnings::warnif($message)
582
583 Equivalent to:
584
585     if (warnings::enabled())
586       { warnings::warn($message) }
587
588 =item warnings::warnif($category, $message)
589
590 Equivalent to:
591
592     if (warnings::enabled($category))
593       { warnings::warn($category, $message) }
594
595 =item warnings::warnif($object, $message)
596
597 Equivalent to:
598
599     if (warnings::enabled($object))
600       { warnings::warn($object, $message) }
601
602 =back
603
604 See L<perlmodlib/Pragmatic Modules> and L<perllexwarn>.
605
606 =cut
607
608 KEYWORDS
609
610 $All = "" ; vec($All, $Offsets{'all'}, 2) = 3 ;
611
612 sub Croaker
613 {
614     require Carp; # this initializes %CarpInternal
615     local $Carp::CarpInternal{'warnings'};
616     delete $Carp::CarpInternal{'warnings'};
617     Carp::croak(@_);
618 }
619
620 sub _bits {
621     my $mask = shift ;
622     my $catmask ;
623     my $fatal = 0 ;
624     my $no_fatal = 0 ;
625
626     foreach my $word ( @_ ) {
627         if ($word eq 'FATAL') {
628             $fatal = 1;
629             $no_fatal = 0;
630         }
631         elsif ($word eq 'NONFATAL') {
632             $fatal = 0;
633             $no_fatal = 1;
634         }
635         elsif ($catmask = $Bits{$word}) {
636             $mask |= $catmask ;
637             $mask |= $DeadBits{$word} if $fatal ;
638             $mask &= ~($DeadBits{$word}|$All) if $no_fatal ;
639         }
640         else
641           { Croaker("Unknown warnings category '$word'")}
642     }
643
644     return $mask ;
645 }
646
647 sub bits
648 {
649     # called from B::Deparse.pm
650     push @_, 'all' unless @_ ;
651     return _bits(undef, @_) ;
652 }
653
654 sub import 
655 {
656     shift;
657
658     my $mask = ${^WARNING_BITS} ;
659
660     if (vec($mask, $Offsets{'all'}, 1)) {
661         $mask |= $Bits{'all'} ;
662         $mask |= $DeadBits{'all'} if vec($mask, $Offsets{'all'}+1, 1);
663     }
664     
665     # Empty @_ is equivalent to @_ = 'all' ;
666     ${^WARNING_BITS} = @_ ? _bits($mask, @_) : $mask | $Bits{all} ;
667 }
668
669 sub unimport 
670 {
671     shift;
672
673     my $catmask ;
674     my $mask = ${^WARNING_BITS} ;
675
676     if (vec($mask, $Offsets{'all'}, 1)) {
677         $mask |= $Bits{'all'} ;
678         $mask |= $DeadBits{'all'} if vec($mask, $Offsets{'all'}+1, 1);
679     }
680
681     push @_, 'all' unless @_;
682
683     foreach my $word ( @_ ) {
684         if ($word eq 'FATAL') {
685             next; 
686         }
687         elsif ($catmask = $Bits{$word}) {
688             $mask &= ~($catmask | $DeadBits{$word} | $All);
689         }
690         else
691           { Croaker("Unknown warnings category '$word'")}
692     }
693
694     ${^WARNING_BITS} = $mask ;
695 }
696
697 my %builtin_type; @builtin_type{qw(SCALAR ARRAY HASH CODE REF GLOB LVALUE Regexp)} = ();
698
699 sub MESSAGE () { 4 };
700 sub FATAL () { 2 };
701 sub NORMAL () { 1 };
702
703 sub __chk
704 {
705     my $category ;
706     my $offset ;
707     my $isobj = 0 ;
708     my $wanted = shift;
709     my $has_message = $wanted & MESSAGE;
710
711     unless (@_ == 1 || @_ == ($has_message ? 2 : 0)) {
712         my $sub = (caller 1)[3];
713         my $syntax = $has_message ? "[category,] 'message'" : '[category]';
714         Croaker("Usage: $sub($syntax)");
715     }
716
717     my $message = pop if $has_message;
718
719     if (@_) {
720         # check the category supplied.
721         $category = shift ;
722         if (my $type = ref $category) {
723             Croaker("not an object")
724                 if exists $builtin_type{$type};
725             $category = $type;
726             $isobj = 1 ;
727         }
728         $offset = $Offsets{$category};
729         Croaker("Unknown warnings category '$category'")
730             unless defined $offset;
731     }
732     else {
733         $category = (caller(1))[0] ;
734         $offset = $Offsets{$category};
735         Croaker("package '$category' not registered for warnings")
736             unless defined $offset ;
737     }
738
739     my $i;
740
741     if ($isobj) {
742         my $pkg;
743         $i = 2;
744         while (do { { package DB; $pkg = (caller($i++))[0] } } ) {
745             last unless @DB::args && $DB::args[0] =~ /^$category=/ ;
746         }
747         $i -= 2 ;
748     }
749     else {
750         $i = _error_loc(); # see where Carp will allocate the error
751     }
752
753     # Defaulting this to 0 reduces complexity in code paths below.
754     my $callers_bitmask = (caller($i))[9] || 0 ;
755
756     my @results;
757     foreach my $type (FATAL, NORMAL) {
758         next unless $wanted & $type;
759
760         push @results, (vec($callers_bitmask, $offset + $type - 1, 1) ||
761                         vec($callers_bitmask, $Offsets{'all'} + $type - 1, 1));
762     }
763
764     # &enabled and &fatal_enabled
765     return $results[0] unless $has_message;
766
767     # &warnif, and the category is neither enabled as warning nor as fatal
768     return if $wanted == (NORMAL | FATAL | MESSAGE)
769         && !($results[0] || $results[1]);
770
771     require Carp;
772     Carp::croak($message) if $results[0];
773     # will always get here for &warn. will only get here for &warnif if the
774     # category is enabled
775     Carp::carp($message);
776 }
777
778 sub _error_loc {
779     require Carp;
780     goto &Carp::short_error_loc; # don't introduce another stack frame
781 }                                                             
782
783 sub enabled
784 {
785     return __chk(NORMAL, @_);
786 }
787
788 sub fatal_enabled
789 {
790     return __chk(FATAL, @_);
791 }
792
793 sub warn
794 {
795     return __chk(FATAL | MESSAGE, @_);
796 }
797
798 sub warnif
799 {
800     return __chk(NORMAL | FATAL | MESSAGE, @_);
801 }
802
803 # These are not part of any public interface, so we can delete them to save
804 # space.
805 delete $warnings::{$_} foreach qw(NORMAL FATAL MESSAGE);
806
807 1;