1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186
|
package Fatal;
use 5.006_001;
use Carp;
use strict;
our($AUTOLOAD, $Debug, $VERSION);
$VERSION = 1.03;
$Debug = 0 unless defined $Debug;
sub import {
my $self = shift(@_);
my($sym, $pkg);
my $void = 0;
$pkg = (caller)[0];
foreach $sym (@_) {
if ($sym eq ":void") {
$void = 1;
}
else {
&_make_fatal($sym, $pkg, $void);
}
}
};
sub AUTOLOAD {
my $cmd = $AUTOLOAD;
$cmd =~ s/.*:://;
&_make_fatal($cmd, (caller)[0]);
goto &$AUTOLOAD;
}
sub fill_protos {
my $proto = shift;
my ($n, $isref, @out, @out1, $seen_semi) = -1;
while ($proto =~ /\S/) {
$n++;
push(@out1,[$n,@out]) if $seen_semi;
push(@out, $1 . "{\$_[$n]}"), next if $proto =~ s/^\s*\\([\@%\$\&])//;
push(@out, "\$_[$n]"), next if $proto =~ s/^\s*([*\$&])//;
push(@out, "\@_[$n..\$#_]"), last if $proto =~ s/^\s*(;\s*)?\@//;
$seen_semi = 1, $n--, next if $proto =~ s/^\s*;//; # XXXX ????
die "Unknown prototype letters: \"$proto\"";
}
push(@out1,[$n+1,@out]);
@out1;
}
sub write_invocation {
my ($core, $call, $name, $void, @argvs) = @_;
if (@argvs == 1) { # No optional arguments
my @argv = @{$argvs[0]};
shift @argv;
return "\t" . one_invocation($core, $call, $name, $void, @argv) . ";\n";
} else {
my $else = "\t";
my (@out, @argv, $n);
while (@argvs) {
@argv = @{shift @argvs};
$n = shift @argv;
push @out, "$ {else}if (\@_ == $n) {\n";
$else = "\t} els";
push @out,
"\t\treturn " . one_invocation($core, $call, $name, $void, @argv) . ";\n";
}
push @out, <<EOC;
}
die "$name(\@_): Do not expect to get ", scalar \@_, " arguments";
EOC
return join '', @out;
}
}
sub one_invocation {
my ($core, $call, $name, $void, @argv) = @_;
local $" = ', ';
if ($void) {
return qq/(defined wantarray)?$call(@argv):
$call(@argv) || croak "Can't $name(\@_)/ .
($core ? ': $!' : ', \$! is \"$!\"') . '"'
} else {
return qq{$call(@argv) || croak "Can't $name(\@_)} .
($core ? ': $!' : ', \$! is \"$!\"') . '"';
}
}
sub _make_fatal {
my($sub, $pkg, $void) = @_;
my($name, $code, $sref, $real_proto, $proto, $core, $call);
my $ini = $sub;
$sub = "${pkg}::$sub" unless $sub =~ /::/;
$name = $sub;
$name =~ s/.*::// or $name =~ s/^&//;
print "# _make_fatal: sub=$sub pkg=$pkg name=$name void=$void\n" if $Debug;
croak "Bad subroutine name for Fatal: $name" unless $name =~ /^\w+$/;
if (defined(&$sub)) { # user subroutine
$sref = \&$sub;
$proto = prototype $sref;
$call = '&$sref';
} elsif ($sub eq $ini) { # Stray user subroutine
die "$sub is not a Perl subroutine"
} else { # CORE subroutine
$proto = eval { prototype "CORE::$name" };
die "$name is neither a builtin, nor a Perl subroutine"
if $@;
die "Cannot make a non-overridable builtin fatal"
if not defined $proto;
$core = 1;
$call = "CORE::$name";
}
if (defined $proto) {
$real_proto = " ($proto)";
} else {
$real_proto = '';
$proto = '@';
}
$code = <<EOS;
sub$real_proto {
local(\$", \$!) = (', ', 0);
EOS
my @protos = fill_protos($proto);
$code .= write_invocation($core, $call, $name, $void, @protos);
$code .= "}\n";
print $code if $Debug;
{
no strict 'refs'; # to avoid: Can't use string (...) as a symbol ref ...
$code = eval("package $pkg; use Carp; $code");
die if $@;
no warnings; # to avoid: Subroutine foo redefined ...
*{$sub} = $code;
}
}
1;
__END__
=head1 NAME
Fatal - replace functions with equivalents which succeed or die
=head1 SYNOPSIS
use Fatal qw(open close);
sub juggle { . . . }
import Fatal 'juggle';
=head1 DESCRIPTION
C<Fatal> provides a way to conveniently replace functions which normally
return a false value when they fail with equivalents which raise exceptions
if they are not successful. This lets you use these functions without
having to test their return values explicitly on each call. Exceptions
can be caught using C<eval{}>. See L<perlfunc> and L<perlvar> for details.
The do-or-die equivalents are set up simply by calling Fatal's
C<import> routine, passing it the names of the functions to be
replaced. You may wrap both user-defined functions and overridable
CORE operators (except C<exec>, C<system> which cannot be expressed
via prototypes) in this way.
If the symbol C<:void> appears in the import list, then functions
named later in that import list raise an exception only when
these are called in void context--that is, when their return
values are ignored. For example
use Fatal qw/:void open close/;
# properly checked, so no exception raised on error
if(open(FH, "< /bogotic") {
warn "bogo file, dude: $!";
}
# not checked, so error raises an exception
close FH;
=head1 AUTHOR
Lionel.Cons@cern.ch
prototype updates by Ilya Zakharevich ilya@math.ohio-state.edu
=cut
|