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