File Coverage

blib/lib/CGI/Compile.pm
Criterion Covered Total %
statement 110 111 99.1
branch 34 44 77.2
condition 14 23 60.8
subroutine 21 21 100.0
pod 2 2 100.0
total 181 201 90.0


line stmt bran cond sub pod time code
1             package CGI::Compile;
2              
3 3218     3218   340523301 use strict;
  3218         38600  
  3218         102925  
4 3218     3218   70901 use 5.008_001;
  3218         12862  
5              
6             our $VERSION = '0.25';
7              
8 3218     3218   19314 use Cwd;
  3218         6438  
  3218         231620  
9 3218     3218   19331 use File::Basename;
  3218         6450  
  3218         353961  
10 3218     3218   1724091 use File::Spec::Functions;
  3218         2819347  
  3218         234903  
11 3218     3218   1518820 use File::pushd;
  3218         78277236  
  3218         225028  
12 3218     3218   25730 use File::Temp;
  3218         6438  
  3218         177075  
13 3218     3218   19306 use File::Spec;
  3218         3232  
  3218         54694  
14 3218     3218   12879 use File::Path;
  3218         9634  
  3218         378708  
15 3218     3218   1554043 use Sub::Name 'subname';
  3218         1705148  
  3218         636956  
16              
17             our $RETURN_EXIT_VAL = undef;
18              
19             sub new {
20 3299     3299 1 29717 my ($class, %opts) = @_;
21              
22 3299   50     161154 $opts{namespace_root} ||= 'CGI::Compile::ROOT';
23              
24 3299         62915 bless \%opts, $class;
25             }
26              
27             our $USE_REAL_EXIT;
28             BEGIN {
29 3218     3218   12874 $USE_REAL_EXIT = 1;
30              
31 3218         9661 my $orig = *CORE::GLOBAL::exit{CODE};
32              
33 3218 100       48304 my $proto = $orig ? prototype $orig : prototype 'CORE::exit';
34              
35 3218 50       12902 $proto = $proto ? "($proto)" : '';
36              
37             $orig ||= sub {
38 3201         6789 my $exit_code = shift;
39              
40 3201 100       271994 CORE::exit(defined $exit_code ? $exit_code : 0);
41 3218   100     32224 };
42              
43 3218     3218   22531 no warnings 'redefine';
  3218         6431  
  3218         302308  
44              
45 3218 100 100 6436   389336 *CORE::GLOBAL::exit = eval qq{
  6436         9675641  
  6436         50279  
  3235         212840  
46             sub $proto {
47             my \$exit_code = shift;
48              
49             \$orig->(\$exit_code) if \$USE_REAL_EXIT;
50              
51             die [ "EXIT\n", \$exit_code || 0 ]
52             };
53             };
54 3218 50       1177621 die $@ if $@;
55             }
56              
57             my %anon;
58              
59             sub compile {
60 3299     3299 1 182219782863 my($class, $script, $package) = @_;
61              
62 3299 100       171734 my $self = ref $class ? $class : $class->new;
63              
64 3299         15795 my($code, $path, $dir, $subname);
65              
66 3299 100       41361 if (ref $script eq 'SCALAR') {
67 88         160 $code = $$script;
68              
69 88   33     474 $package ||= (caller)[0];
70              
71 88         270 $subname = '__CGI' . $anon{$package}++ . '__';
72             } else {
73 3211         59656 $code = $self->_read_source($script);
74              
75 3211         192121 $path = Cwd::abs_path($script);
76 3211         627546 $dir = File::Basename::dirname($path);
77              
78 3211         33181 my $genned_package;
79              
80 3211   33     55286 ($genned_package, $subname) = $self->_build_subname($path || $script);
81              
82 3211   33     134889 $package ||= $genned_package;
83             }
84              
85 3299 100       52825 my $warnings = $code =~ /^#!.*\s-w\b/ ? 1 : 0;
86 3299         12055 $code =~ s/^__END__\r?\n.*//ms;
87 3299         45613 $code =~ s/^__DATA__\r?\n(.*)//ms;
88 3299 100       24566 my $data = defined $1 ? $1 : '';
89              
90             # TODO handle nph and command line switches?
91 3299 100       85131 my $eval = join '',
    100          
    100          
92             "package $package;",
93             'sub {',
94             'local $CGI::Compile::USE_REAL_EXIT = 0;',
95             "\nCGI::initialize_globals() if defined &CGI::initialize_globals;",
96             'local ($0, $CGI::Compile::_dir, *DATA);',
97             '{ my ($data, $path, $dir) = @_[1..3];',
98             ($path ? '$0 = $path;' : ''),
99             ($dir ? '$CGI::Compile::_dir = File::pushd::pushd $dir;' : ''),
100             q{open DATA, '<', \$data;},
101             '}',
102             # NOTE: this is a workaround to fix a problem in Perl 5.10
103             q(local @SIG{keys %SIG} = do { no warnings 'uninitialized'; @{[]} = values %SIG };),
104             "local \$^W = $warnings;",
105             'my $rv = eval {',
106             'local @ARGV = @{ $_[4] };', # args to @ARGV
107             'local @_ = @{ $_[4] };', # args to @_ as well
108             ($path ? "\n#line 1 $path\n" : ''),
109             $code,
110             "\n};",
111             q{
112             {
113             no warnings qw(uninitialized numeric pack);
114             my $self = shift;
115             my $exit_val = unpack('C', pack('C', sprintf('%.0f', $rv)));
116             if ($@) {
117             die $@ unless (
118             ref($@) eq 'ARRAY' and
119             $@->[0] eq "EXIT\n"
120             );
121             my $exit_param = unpack('C', pack('C', sprintf('%.0f', $@->[1])));
122              
123             if ($exit_param != 0 && !$CGI::Compile::RETURN_EXIT_VAL && !$self->{return_exit_val}) {
124             die "exited nonzero: $exit_param";
125             }
126              
127             $exit_val = $exit_param;
128             }
129              
130             return $exit_val;
131             }
132             },
133             '};';
134              
135 3299         8499 my $sub = do {
136 3218     3218   25742 no warnings 'uninitialized'; # for 5.8
  3218         3236  
  3218         2626554  
137             # NOTE: this is a workaround to fix a problem in Perl 5.10
138 3299         74140 local @SIG{keys %SIG} = @{[]} = values %SIG;
  3299         2659071  
139 3299         58869 local $USE_REAL_EXIT = 0;
140              
141 3299         22114 my $code = $self->_eval($eval);
142 3299         20002 my $exception = $@;
143              
144 3299 100       13624 die "Could not compile $script: $exception" if $exception;
145              
146             subname "${package}::$subname", sub {
147 3292     3292   4670013 my @args = @_;
148             # this is necessary for MSWin32
149 3292   100     65561 my $orig_warn = $SIG{__WARN__} || sub { warn(@_) };
150 3292 0       53143 local $SIG{__WARN__} = sub { $orig_warn->(@_) unless $_[0] =~ /^No such signal/ };
  0         0  
151 3292         18816 $code->($self, $data, $path, $dir, \@args)
152 3293         1771601 };
153             };
154              
155 3293         31787 return $sub;
156             }
157              
158             sub _read_source {
159 3211     3211   16542 my($self, $file) = @_;
160              
161 3211 50       521715 open my $fh, "<", $file or die "$file: $!";
162 3211         35195 return do { local $/; <$fh> };
  3211         75280  
  3211         217505  
163             }
164              
165             sub _build_subname {
166 3211     3211   13181 my($self, $path) = @_;
167              
168 3211         87121 my ($volume, $dirs, $file) = File::Spec::Functions::splitpath($path);
169 3211         429208 my @dirs = File::Spec::Functions::splitdir($dirs);
170              
171 3211         63640 my $name = $file;
172 3211 50       23124 my $package = join '_', grep { defined && length } $volume, @dirs, $name;
  28899         191843  
173              
174             # Escape everything into valid perl identifiers
175 3211         82953 s/([^A-Za-z0-9_])/sprintf("_%2x", unpack("C", $1))/eg for $package, $name;
  22477         145600  
176              
177             # make sure the identifiers don't start with a digit
178 3211         39002 s/^(\d)/_$1/ for $package, $name;
179              
180 3211 50       113264 $package = $self->{namespace_root} . ($package ? "::$package" : '');
181              
182 3211         58916 return ($package, $name);
183             }
184              
185             # define tmp_dir value later on first usage, otherwise all children
186             # share the same directory when forked
187             my $tmp_dir;
188             sub _eval {
189 3299     3299   9642 my $code = \$_[1];
190              
191             # we use a tmpdir chmodded to 0700 so that the tempfiles are secure
192 3299   66     484204 $tmp_dir ||= File::Spec->catfile(File::Spec->tmpdir, "cgi_compile_$$");
193              
194 3299 100       320187 if (! -d $tmp_dir) {
195 3214 50       231215 mkdir $tmp_dir or die "Could not mkdir $tmp_dir: $!";
196 3214 50       106472 chmod 0700, $tmp_dir or die "Could not chmod 0700 $tmp_dir: $!";
197             }
198              
199 3299         105037 my ($fh, $fname) = File::Temp::tempfile('cgi_compile_XXXXX',
200             UNLINK => 1, SUFFIX => '.pm', DIR => $tmp_dir);
201              
202 3299         2507272 print $fh $$code;
203 3299         113015 close $fh;
204              
205 3299         4320079 my $sub = do $fname;
206              
207 3299 50       2448377 unlink $fname or die "Could not delete $fname: $!";
208              
209 3299         17697 return $sub;
210             }
211              
212             END {
213 3218 100 66 3218   112685413 if ($tmp_dir and -d $tmp_dir) {
214 3214         2276717 File::Path::remove_tree($tmp_dir);
215             }
216             }
217              
218             1;
219              
220             __END__