1package Religion; 2 3#v3 4 5sub import {} #nothing to export 6 7sub TraceBack { 8 # Given a starting scope offset, returns (get ready): 9 # 10 # Bool/Int: Am I in an eval?/How many evals are around me? 11 # Integer: What is the line number of this scope? 12 # String: What is the filename or eval number of this scope? 13 # Integer: What is the line number of the nearest scope 14 # that is a file, not an eval? 15 # String: What is the filename of the nearest scope that 16 # that is a file, not an eval? 17 # String: If I were to print out a message to the user, what 18 # should I say to explain the relation of the nearest 19 # file scope to my current scope? 20 21 my($level) = @_; 22 my($iline) = (caller($level))[2]; 23 my($ifile) = (caller($level))[1]; 24 my($nil,$ofile,$oline,$sub); 25 my($oscope)=""; 26 my($eval)=0; 27 28 while (($nil,$file,$line,$sub) = caller($level++)) { 29 if( $file =~ /^\(eval/ ) { 30 $oline = (caller($level))[2]; 31 $oscope .= "the eval at line $oline of "; 32 $eval++; 33 } else { 34 return ($eval,$iline,$ifile,$line,$file,$oscope); 35 } 36 } 37 38 die "Unable to trace scope"; # This can't happen. 39 40} 41 42sub TraceBackHandler { 43 my($sub,$oldhandler,$startlevel) = @_; 44 45 return sub { 46 my($msg,$fmsg,@trace,$level,$eval); 47 48 # This section has been moved out to $SIG{__DIE__} and WARN. 49 50 #if(@_==1) { 51 # # Invoked by die(), warn(), etc.; 52 # $msg = $_[0]; 53 # $msg =~ s/ at (\S+|\(.*\)) line \d+\.\n$//; 54 # $level=$startlevel; 55 # @trace = Religion::TraceBack($level+1); 56 # 57 # $fmsg = $msg . ((substr($msg,-1,1) ne "\n") ? 58 # " at line $trace[1] of $trace[5]$trace[4].\n" 59 # #" at $trace[2] line $trace[1].\n" 60 # : ""); 61 #} else { 62 ($msg,$fmsg,$level,@trace) = @_; 63 #} 64 65 my(@result); 66 my($result)="last"; 67 #anonymous block: 68 { 69 70 @result=&$sub($msg,$fmsg,$level+1,@trace); 71 $result="return"; 72 73 $msg = $result[0] if @result>0; 74 $fmsg = $result[1] if @result>1; 75 $level = $result[2]-1 if @result>2; 76 @trace[0..$#result-3] = @result[3..$#result] if @result>3; 77 78 if(@result==1) { 79 $fmsg = $msg . ((substr($msg,-1,1) ne "\n") ? 80 " at line $trace[1] of $trace[5]$trace[4].\n" 81 #" at $trace[2] line $trace[1].\n" 82 : ""); 83 } 84 85 } continue { 86 $result="next" if $result ne "return"; 87 88 if($oldhandler) { 89 return &$oldhandler($msg,$fmsg,$level+1,@trace); 90 } 91 } 92 93 # Return parsed info, whether we got single or multiple args 94 if( $result eq "return") { 95 ($msg,$fmsg,$level,@trace); 96 } elsif( $result eq "next") { 97 next; 98 } else { 99 last; 100 } 101 } 102}; 103 104 105package Warn; 106 107$Handler = $PreHandler = ""; 108 109$SIG{__WARN__} = sub { 110 local($^W) = 0; 111 my($msg,$fmsg,@trace,$level,@trace); 112 113 $msg = $_[0]; 114 $msg =~ s/ at (\S+|\(.*\)) line \d+\.\n$//; 115 $level=0; 116 @trace = Religion::TraceBack($level+1); 117 118 $fmsg = $msg . ((substr($msg,-1,1) ne "\n") ? 119 " at line $trace[1] of $trace[5]$trace[4].\n" 120 #" at $trace[2] line $trace[1].\n" 121 : ""); 122 123 unshift(@trace,$msg,$fmsg,$level); 124 125 my($ok)=0; 126 { 127 my(@result); 128 @result=&$PreHandler(@trace) if $PreHandler; 129 130 @trace[0..$#result]=@result; 131 } continue { 132 $ok=1; 133 } 134 return if !$ok; 135 136 my($ok)=0; 137 { 138 my(@result); 139 @result=&$Handler(@trace) if $Handler; 140 141 #$result[2]++ if $#result>=2; 142 @trace[0..$#result]=@result; 143 } continue { 144 $ok=1; 145 } 146 return if !$ok; 147 148 warn($trace[1]); 149}; 150 151package WarnHandler; 152 153sub new { 154 my($pkg,$sub) = @_; 155 return Religion::TraceBackHandler ($sub,$Warn::Handler,0); 156}; 157 158package WarnPreHandler; 159 160sub new { 161 my($pkg,$sub) = @_; 162 return Religion::TraceBackHandler ($sub,$Warn::PreHandler,0); 163}; 164 165 166package Die; 167 168$Handler = $PreHandler = ""; 169 170$SIG{__DIE__} = sub { 171 local($^W) = 0; # This cuts out warnings about exiting subs via 172 # next or last. 173 my($msg,$fmsg,@trace,$level,@trace); 174 175 $msg = $_[0]; 176 $msg =~ s/ at (\S+|\(.*\)) line \d+\.\n$//; 177 $level=0; 178 @trace = Religion::TraceBack($level+1); 179 180 $fmsg = $msg . ((substr($msg,-1,1) ne "\n") ? 181 " at line $trace[1] of $trace[5]$trace[4].\n" 182 #" at $trace[2] line $trace[1].\n" 183 : ""); 184 185 unshift(@trace,$msg,$fmsg,$level); 186 187 my($ok)=0; 188 { 189 my(@result); 190 @result = &$PreHandler(@trace) if $PreHandler; 191 192 #$result[2]++ if $#result>=2; 193 @trace[0..$#result]=@result; 194 } continue { 195 $ok=1; 196 } 197 die($trace[1]) if !$ok; 198 199 my($ok)=0; 200 { 201 my(@result); 202 @result = &$Handler(@trace) if $Handler; 203 204 #$result[2]++ if $#result>=2; 205 @trace[0..$#result]=@result; 206 } continue { 207 $ok=1; 208 } 209 210 die($trace[1]); 211}; 212 213package DieHandler; 214 215sub new { 216 my($pkg,$sub) = @_; 217 return Religion::TraceBackHandler ($sub,$Die::Handler,0); 218}; 219 220package DiePreHandler; 221 222sub new { 223 my($pkg,$sub) = @_; 224 return Religion::TraceBackHandler ($sub,$Die::PreHandler,0); 225}; 226 227 228package Religion; 229 230 2311; 232 233__END__; 234 235=head1 NAME 236 237Religion - Generate tracebacks and create and install die() and 238 warn() handlers. 239 240=head1 DESCRIPTION 241 242This is a second go at a module to simplify installing die() and warn() 243handlers, and to make such handlers easier to write and control. 244 245For most people, this just means that if use C<use Religion;> then you'll 246get noticably better error reporting from warn() and die(). This is especially 247useful if you are using eval(). 248 249Religion provides four classes, WarnHandler, DieHandler, WarnPreHandler, and 250DiePreHandler, that when you construct them return closures that can be 251stored in variables that in turn get invoked by $SIG{__DIE__} and 252$SIG{__WARN__}. Note that if Religion is in use, you should not modify 253$SIG{__DIE__} or $SIG{__WARN__}, unless you are careful about invoking 254chaining to the old handler. 255 256Religion also provides a TraceBack function, which is used by a DieHandler 257after you C<die()> to give a better handle on the current scope of your 258situation, and provide information about where you were, which might 259influence where you want to go next, either returning back to where you 260were, or going on to the very last. [Sorry - Ed.] 261 262See below for usage and examples. 263 264=head1 USAGE 265 266=over 8 267 268=item DieHandler SUB 269 270Invoke like this: 271 272 $Die::Handler = new DieHandler sub { 273 #... 274 }; 275 276where C<#...> contains your handler code. Your handler will receive the 277following arguments: 278 279 $message, $full_message, $level, $eval, 280 $iline, $ifile, $oline, $ofile, $oscope 281 282C<$message> is the message provided to die(). Note that the default addition 283of " at FILE line LINE.\n" will have been stripped off if it was present. 284If you want to add such a message back on, feel free to do so with $iline 285and $ifile. 286 287C<$full_message) is the message with a scope message added on if there was 288no newline at the end of C<$message>. Currently, 289this is I<not> the original message that die() tacked on, but something 290along the lines of " at line 3 of the eval at line 4 of Foo.pl\n". 291 292C<$eval> is non-zero if the die() was invoked inside an eval. 293 294The rest of the arguments are explained in the source for 295Religion::TraceBack. Yes, I need to document these, but not just now, for 296they are a pain to explain. 297 298 299Whenever you install a DieHandler, it will automatically store the current 300value of $Die::Handler so it can chain to it. If you want to install a 301handler only temporarily, use local(). 302 303 304If your handler returns data using C<return> or by falling off the end, 305then the items returns will be used to fill back in the argument list, and 306the next handler in the chain, if any, will be invoked. B<Don't fall off the 307end if you don't want to change the error message.> 308 309If your handler exits using C<last>, then no further handlers will be 310invoked, and the program will die immediatly. 311 312If your handler exits using C<next>, then the next handler in the chain will 313be invoked directly, without giving you a chance to change its arguments as 314you could if you used C<return>. 315 316If your handler invokes die(), then die() will proceed as if no handlers 317were installed. If you are inside an eval, then it will exit to the scope 318enclosing the eval, otherwise it will exit the program. 319 320=item WarnHandler SUB 321 322Invoke like this: 323 324 $Warn::Handler = new WarnHandler sub { 325 #... 326 }; 327 328For the rest of its explanation, see DieHandler, and subsitute warn() for 329die(). Note that once the last DieHandler completes (or C<last> is invoked) 330then execution will return to the code that invoked warn(). 331 332=item DiePreHandler SUB 333 334Invoke like this: 335 336 $Die::PreHandler = new DiePreHandler sub { 337 #... 338 }; 339 340This works identically to $Die::Handler, except that it forms a separate chain 341that is invoked I<before> the DieHandler chain. Since you can use C<last> to 342abort all the handlers and die immediately, or change the messages or scope 343details, this can be useful for modifying data that all future handlers will 344see, or to dispose of some messages from further handling. 345 346This is even more useful in $Warn::PreHandler, since you can just throw 347away warnings that you I<know> aren't needed. 348 349=item WarnPreHandler SUB 350 351Invoke like this: 352 353 $Warn::PreHandler = new WarnPreHandler sub { 354 #... 355 }; 356 357This works identically to $Warn::Handler, except that it forms a separate 358chain that is invoked I<before> the WarnHandler chain. Since you can use 359C<last> to abort all the handlers and return to the program, or change 360the messages or scope details, this can be useful for modifying data that 361all future handlers will see, or to dispose of some messages. 362 363This is very useful, since you can just throw 364away warnings that you I<know> aren't needed. 365 366=back 367 368=head1 EXAMPLES 369 370=over 8 371 372=item A dialog error message: 373 374 $Die::Handler = new DieHandler sub { 375 my($msg,$fmsg,$level,$eval) = @_; 376 if($eval) { 377 # if we are in an eval, skip to the next handler 378 next; 379 } else { 380 # show a message box describing the error. 381 print "ShowMessageBox $fmsg"; 382 383 # force the program to exit 384 exit 0; 385 next; 386 } 387 }; 388 389=item A handler that changes die() messages back to the original format 390 391 local($Die::Handler) = new DieHandler sub { 392 my($msg,$fmsg,$level,@trace) = @_; 393 394 $fmsg = $msg . ((substr($msg,-1,1) ne "\n") ? 395 " at $trace[2] line $trace[1].\n" 396 : ""); 397 return ($msg,$fmsg); 398 }; 399 400=item A warn handler that does nothing. 401 402 $Warn::Handler = new WarnHandler sub {next;}; 403 404=item A warn prehandler that throws away a warning. 405 406 $Warn::PreHandler = new WarnPreHandler sub { 407 my($msg,$fmsg,$level,$eval) = @_; 408 if($msg =~ /Use of uninitialized/) { 409 last; 410 } 411 next; 412 }; 413 414=back 415 416=cut 417 418