File Coverage

blib/lib/WebDyne/Base.pm
Criterion Covered Total %
statement 35 131 26.7
branch 9 70 12.8
condition 2 47 4.2
subroutine 10 17 58.8
pod 0 7 0.0
total 56 272 20.5


line stmt bran cond sub pod time code
1             #
2             # This file is part of WebDyne.
3             #
4             # This software is Copyright (c) 2017 by Andrew Speer .
5             #
6             # This is free software, licensed under:
7             #
8             # The GNU General Public License, Version 2, June 1991
9             #
10             # Full license text is available at:
11             #
12             #
13             #
14             package WebDyne::Base;
15              
16              
17             # Compiler Pragma
18             #
19 2     2   53 sub BEGIN {$^W=0}
20 2     2   9 use strict qw(vars);
  2         3  
  2         46  
21 2     2   7 use vars qw($VERSION @EXPORT);
  2         13  
  2         82  
22 2     2   11 use warnings;
  2         3  
  2         49  
23 2     2   7 no warnings qw(uninitialized redefine once);
  2         4  
  2         51  
24              
25              
26             # External modules
27             #
28 2     2   340 use Data::Dumper;
  2         4895  
  2         115  
29 2     2   271 use IO::File;
  2         6036  
  2         2859  
30              
31              
32             # Use Exporter
33             #
34             require Exporter;
35              
36              
37             # Exports
38             #
39             @EXPORT=qw(err errstr errclr errdump errsubst errstack errnofatal);
40              
41              
42             # Version information
43             #
44             $VERSION='1.250';
45              
46              
47             # Var to hold package wide hash, for data shared across package, and error stack
48             #
49             my (%Package, @Err);
50              
51              
52             # All done. Positive return
53             #
54             1;
55              
56              
57             #==================================================================================================
58              
59              
60             sub import {
61              
62              
63             # Get message
64             #
65 8     8   23 my ($message, @param)=@_;
66              
67              
68             # Get who is calling us
69             #
70 8   50     51 my $caller=(caller(0))[0] || return undef;
71              
72              
73             # fh we will write to
74             #
75 8         21 my $debug_fh;
76              
77              
78             # Environment var overrides all
79             #
80 8 50 33     38 if ($ENV{'WEBDYNE_DEBUG_FILE'}) {
    50          
    50          
    50          
81              
82             # fn is whatever spec'd
83             #
84 0         0 my $fn=$ENV{'WEBDYNE_DEBUG_FILE'};
85 0   0     0 $debug_fh=IO::File->new($fn, O_CREAT | O_APPEND | O_WRONLY) || do {
86             warn("unable to open file '$fn', $!");
87             undef;
88             }
89              
90             }
91             elsif ($ENV{'WEBDYNE_DEBUG'}) {
92              
93              
94             # fh is stderr
95             #
96 0         0 $debug_fh=\*STDERR;
97              
98              
99             }
100 8         53 elsif (ref(my $debug_hr=${"${caller}::DEBUG"}) eq 'HASH') {
101              
102              
103             # Debug is hash ref, extract filename etc and open
104             #
105 0         0 my ($fn, $mode, $package)=@{$debug_hr}{qw(filename mode package)};
  0         0  
106 0   0     0 $fn ||= $debug_hr->{'file'}; #Alias
107 0 0 0     0 if ($fn && ($package ? ($package eq $caller) : 1)) {
    0          
    0          
108 0   0     0 $mode ||= O_CREAT | O_APPEND | O_WRONLY;
109             $debug_fh=(
110             $Package{'debug_fh'}{$fn} ||= (
111 0   0     0 IO::File->new($fn, $mode) || do {
      0        
112             warn("unable to open file '$fn', $!");
113             undef;
114             }
115             ));
116             }
117             elsif (!$fn) {
118 0         0 warn(sprintf('no file name specified in DEBUG hash %s', Dumper($debug_hr)));
119             }
120              
121             }
122 8         38 elsif (!ref(my $fn=${"${caller}::DEBUG"}) && ${"${caller}::DEBUG"}) {
  8         38  
123              
124             # Just file name spec'd. Open
125             #
126             $debug_fh=(
127             $Package{'debug_fh'}{$fn} ||= (
128 0   0     0 IO::File->new($fn, O_CREAT | O_APPEND | O_WRONLY) || do {
      0        
129             warn("unable to open file '$fn', $!");
130             undef;
131             }
132             ));
133             }
134              
135              
136             # After all that did we get a file handle ? If so, import the debug handler
137             #
138 8 50       15 if ($debug_fh) {
139              
140             # Yes, setup debug routine
141             #
142 0         0 $debug_fh->autoflush(1);
143 0         0 *{"${caller}::debug"}=sub {
144 0     0   0 local $|=1;
145 0   0     0 my $method=(caller(1))[3] || 'main';
146 0         0 (my $subroutine=$method)=~s/^.*:://;
147 0 0 0     0 if ($ENV{'WEBDYNE_DEBUG'} && ($ENV{'WEBDYNE_DEBUG'} ne '1')) {
148 0         0 my @debug_target=split(/[,;:]/, $ENV{'WEBDYNE_DEBUG'});
149 0         0 foreach my $debug_target (@debug_target) {
150 0 0 0     0 if (($caller eq $debug_target) || ($method=~/\Q$debug_target\E$/)) {
151 0         0 CORE::print $debug_fh "[$subroutine] ", sprintf(shift(), @_), $/;
152             }
153             }
154             }
155             else {
156 0 0       0 CORE::print $debug_fh "[$subroutine] ", $_[1] ? sprintf(shift(), @_ ) : $_[0], $/;
157             }
158             }
159 0 0       0 unless UNIVERSAL::can($caller, 'debug');
160 0 0       0 *{"${caller}::Dumper"}=\&Data::Dumper::Dumper unless UNIVERSAL::can($caller, 'Dumper');
  0         0  
161              
162             }
163             else {
164              
165             # No, null our debug and Dumper routine
166             #
167 8     0   31 *{"${caller}::debug"}=sub { }
168 8 50       88 unless UNIVERSAL::can($caller, 'debug');
169              
170             #*{"${caller}::Dumper"}= sub {} unless UNIVERSAL::can($caller, 'Dumper');
171              
172             }
173              
174              
175             # Setup file handle for error backtrace
176             #
177 8 50       13 if (my $fn=${"${caller}::ERROR"}) {
  8         40  
178              
179             # Just file name spec'd. Log
180             #
181 0         0 $Package{'error_fn'}{$fn}++
182              
183             }
184              
185              
186             # Done
187             #
188 8         3666 goto &Exporter::import;
189              
190             }
191              
192              
193             sub errnofatal {
194              
195              
196             #
197             #
198 2 50   2 0 16 @_ ? $Package{'nofatal'}=@_ : $Package{'nofatal'};
199              
200              
201             }
202              
203              
204             sub err {
205              
206              
207             # Get the message and any sprintf params
208             #
209 0     0 0 0 my ($message, @param)=@_;
210              
211              
212             # If no message supplied return last one seen
213             #
214 0 0       0 unless ($message) {
215 0 0 0     0 $message=@Err ? $Err[$#Err]->[0] && return undef : 'undefined error';
216             }
217             else {
218 0 0       0 $message=sprintf($message, @param) if @param;
219             }
220              
221              
222             # Init the caller var and array
223             #
224 0         0 my @caller;
225 0         0 my $caller=(caller(0))[0];
226              
227              
228             # Populate the caller array
229             #
230 0         0 for (my $i=0; my @info=(caller($i))[0..3]; $i++) {
231              
232              
233             # Push onto the caller array
234             #
235 0         0 push @caller, \@info;
236              
237              
238             }
239              
240              
241             # If this message is *not* the same as the last one we saw,
242             # we will log it
243             #
244 0 0 0     0 unless ($message eq (@Err && $Err[0]->[0])) {
245              
246              
247             # Add to stack
248             #
249 0         0 unshift @Err, [$message, @caller];
250              
251              
252             # If caller has a debug function enabled, call this with the warning
253             #
254 0 0       0 if (UNIVERSAL::can($caller, 'debug')) {
255              
256              
257             # Yes, they are using the debug module, so can we call it
258             #
259 0         0 &{"${caller}::debug"}($message);
  0         0  
260              
261              
262             }
263              
264              
265             # Dump to backtrace file if enabled
266             #
267 0         0 foreach my $fn (keys %{$Package{'error_fn'}}) {
  0         0  
268              
269 0 0       0 unless (my $fh=IO::File->new($fn, O_CREAT | O_APPEND | O_WRONLY)) {
270 0         0 warn("unable to open file '$fn', $!");
271             }
272             else {
273 0         0 seek($fh, 0, 2); # Seek to EOF
274 0         0 my $errdump=&errdump();
275 0         0 CORE::print $fh $errdump, $/, $/;
276 0         0 $fh->close();
277             }
278              
279             }
280              
281              
282             }
283              
284              
285             # Return undef
286             #
287 0 0       0 return $Package{'nofatal'} ? undef : die(&errdump);
288              
289             }
290              
291              
292             sub errstr {
293              
294              
295             # Check that there are messages in the stack before trying to get
296             # the last one
297             #
298 27 50   27 0 89 if (my $count=@Err) {
299              
300              
301             # There are objects in the array, so it is safe to do a fetch
302             # on the last (-1) array slot
303             #
304 0         0 my $errstr=$Err[--$count]->[0];
305              
306              
307             # And return the errstr
308             #
309 0         0 return $errstr;
310              
311             }
312             else {
313              
314              
315             # Nothing in the array stack, return undef
316             #
317 27         231 return undef;
318              
319              
320             }
321              
322             }
323              
324              
325             sub errclr {
326              
327              
328             # Clear the warning stack
329             #
330 0     0 0   undef @Err;
331              
332              
333             # Replace errors if args
334             #
335 0 0         @_ && (return &err(@_));
336              
337              
338             # Return OK always
339             #
340 0           return 1;
341              
342             }
343              
344              
345             sub errsubst {
346              
347              
348             # Replace the current error message with a new one, keeping callback
349             # stack
350             #
351 0     0 0   my ($message, @param)=@_;
352              
353             # If no message supplied return last one seen
354             #
355 0 0         unless ($message) {
356 0 0 0       $message=@Err ? $Err[$#Err]->[0] && return undef : 'undefined error';
357             }
358             else {
359 0           $message=sprintf($message, @param);
360             }
361              
362             # Chomp the message
363             #
364 0           chomp($message);
365              
366              
367             # Replace if present, define if not
368             #
369 0 0         @Err ? ($Err[$#Err]->[0]=$message) : goto &err;
370              
371              
372             # Return
373             #
374 0           return undef;
375              
376              
377             }
378              
379              
380             sub errdump {
381              
382              
383             # Use can send additional info to dump as key/value pairs in hash ref
384             # supplied as arg
385             #
386 0     0 0   my $info_hr=shift();
387              
388              
389             # Return a dump of error in a nice format, no params. Do this with
390             # format strings, so define the ones we will use
391             #
392 0           my @format=(
393              
394             '+' . ('-' x 78) . "+\n",
395             "| @<<<<< | ^<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<< |\n",
396             "| | ^<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<~~ |\n"
397              
398             );
399              
400              
401             # Go through the message stack on error at a time in reverse order
402             #
403 0           foreach my $err_ar (reverse @Err) {
404              
405              
406             # Get message, clean up
407             #
408 0           my $message=ucfirst($err_ar->[0]);
409 0           $message=~s/\s+$//;
410 0 0         $message.='.' unless $message=~/[\.\!\?]$/;
411 0           my @message=split("\n", $message);
412 0 0         $message=shift @message if @message;
413              
414              
415             # Print out date, time, error message
416             #
417 0           formline $format[0];
418 0           formline $format[1], 'Date', scalar(localtime());
419 0           formline $format[0];
420 0           formline $format[1], 'Error', $message;
421 0 0         (formline $format[2], $message) if $message;
422 0 0         map {formline $format[2], $_} @message if @message;
  0            
423 0           formline $format[0];
424              
425              
426             # Flag so we know we have printed the caller field
427             #
428 0           my $caller_fg;
429              
430              
431             # Go through callback stack
432             #
433 0           for (my $i=1; defined($err_ar->[$i]); $i++) {
434              
435              
436             # Get method, line no and file
437             #
438 0   0       my $method=$err_ar->[$i+1][3] || $err_ar->[$i][0] || last;
439 0   0       my $lineno=$err_ar->[$i][2] || next;
440 0           my $filenm=$err_ar->[$i][1];
441              
442              
443             # Print them out, print out caller label unless we
444             # have already done so
445             #
446 0 0         formline $format[1],
447             $caller_fg++ ? '' : 'Caller', "$method, line $lineno";
448              
449             }
450              
451              
452             # Include any user supplied info
453             #
454 0           while (my ($key, $value)=each %{$info_hr}) {
  0            
455              
456              
457             # Print separator, info
458             #
459 0           formline $format[0];
460 0           formline $format[1], $key, $value;
461 0 0         (formline $format[2], $value) if $value;
462              
463             }
464              
465              
466             # Finish off formatting, print PID. Dont ask me why $$ has to be "$$",
467             # it does not show up any other way
468             #
469 0           formline $format[0];
470 0           formline $format[1], 'PID', "$$";
471 0           formline $format[0];
472 0           formline "\n";
473              
474              
475             }
476              
477              
478             # Empty the format accumulator and return it
479             #
480 0           my $return=$^A; undef $^A;
  0            
481 0           return $return;
482              
483             }
484              
485              
486             sub errstack {
487              
488             # Return or push the raw error stack
489             #
490 0 0   0 0   return @_ ? \(@Err=@{$_[1]}) : \@Err;
  0            
491              
492             }
493