File Coverage

blib/lib/CGI/Compile.pm
Criterion Covered Total %
statement 110 111 99.1
branch 33 44 75.0
condition 14 25 56.0
subroutine 21 21 100.0
pod 2 2 100.0
total 180 203 88.6


line stmt bran cond sub pod time code
1             package CGI::Compile;
2              
3 17     17   2243425 use strict;
  17         35  
  17         673  
4 17     17   380 use 5.008_001;
  17         73  
5              
6             our $VERSION = '0.27';
7              
8 17     17   170 use Cwd;
  17         33  
  17         1519  
9 17     17   119 use File::Basename;
  17         88  
  17         3965  
10 17     17   23565 use File::Spec::Functions;
  17         16248  
  17         1724  
11 17     17   9808 use File::pushd;
  17         374705  
  17         1330  
12 17     17   144 use File::Temp;
  17         31  
  17         1232  
13 17     17   131 use File::Spec;
  17         51  
  17         2576  
14 17     17   78 use File::Path;
  17         30  
  17         1012  
15 17     17   11277 use Sub::Name 'subname';
  17         12678  
  17         6477  
16              
17             our $RETURN_EXIT_VAL = undef;
18              
19             sub new {
20 99     99 1 220849 my ($class, %opts) = @_;
21              
22 99   50     688 $opts{namespace_root} ||= 'CGI::Compile::ROOT';
23              
24 99         303 bless \%opts, $class;
25             }
26              
27             our $USE_REAL_EXIT;
28             BEGIN {
29 17     17   59 $USE_REAL_EXIT = 1;
30              
31 17         42 my $orig = *CORE::GLOBAL::exit{CODE};
32              
33 17 100       191 my $proto = $orig ? prototype $orig : prototype 'CORE::exit';
34              
35 17 50       66 $proto = $proto ? "($proto)" : '';
36              
37             $orig ||= sub {
38 1         2 my $exit_code = shift;
39              
40 1 50       141 CORE::exit(defined $exit_code ? $exit_code : 0);
41 17   66     2446 };
42              
43 17     17   136 no warnings 'redefine';
  17         36  
  17         2053  
44              
45 17 100 100 36   2323 *CORE::GLOBAL::exit = eval qq{
  36         248260  
  36         118  
  35         401  
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 17 50       7882 die $@ if $@;
55             }
56              
57             my %anon;
58              
59             sub compile {
60 99     99 1 2850648 my($class, $script, $package) = @_;
61              
62 99 100       531 my $self = ref $class ? $class : $class->new;
63              
64 99         213 my($code, $path, $dir, $subname);
65              
66 99 100       308 if (ref $script eq 'SCALAR') {
67 88         144 $code = $$script;
68              
69 88   33     532 $package ||= (caller)[0];
70              
71 88         259 $subname = '__CGI' . $anon{$package}++ . '__';
72             } else {
73 11         43 $code = $self->_read_source($script);
74              
75 11         476 $path = Cwd::abs_path($script);
76 11         791 $dir = File::Basename::dirname($path);
77              
78 11         34 my $genned_package;
79              
80 11   33     78 ($genned_package, $subname) = $self->_build_subname($path || $script);
81              
82 11   33     68 $package ||= $genned_package;
83             }
84              
85 99 100       491 my $warnings = $code =~ /^#!.*\s-w\b/ ? 1 : 0;
86 99         285 $code =~ s/^__END__\r?\n.*//ms;
87 99         270 $code =~ s/^__DATA__\r?\n(.*)//ms;
88 99 100       317 my $data = defined $1 ? $1 : '';
89              
90             # TODO handle nph and command line switches?
91 99 100       982 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 99         161 my $sub = do {
136 17     17   172 no warnings 'uninitialized'; # for 5.8
  17         200  
  17         18954  
137             # NOTE: this is a workaround to fix a problem in Perl 5.10
138 99         621 local @SIG{keys %SIG} = @{[]} = values %SIG;
  99         31879  
139 99         988 local $USE_REAL_EXIT = 0;
140              
141 99         433 my $code = $self->_eval($eval);
142 99         260 my $exception = $@;
143              
144 99 100       1961 die "Could not compile $script: $exception" if $exception;
145              
146             subname "${package}::$subname", sub {
147 92     92   34955 my @args = @_;
148             # this is necessary for MSWin32
149 92   66     581 my $orig_warn = $SIG{__WARN__} || sub { warn(@_) };
150 92 0       550 local $SIG{__WARN__} = sub { $orig_warn->(@_) unless $_[0] =~ /^No such signal/ };
  0         0  
151 92         348 $code->($self, $data, $path, $dir, \@args)
152 93         22899 };
153             };
154              
155 93         548 return $sub;
156             }
157              
158             sub _read_source {
159 11     11   74 my($self, $file) = @_;
160              
161 11 50       844 open my $fh, "<", $file or die "$file: $!";
162 11         36 return do { local $/; <$fh> };
  11         81  
  11         873  
163             }
164              
165             sub _build_subname {
166 11     11   49 my($self, $path) = @_;
167              
168 11         61 my ($volume, $dirs, $file) = File::Spec::Functions::splitpath($path);
169 11         253 my @dirs = File::Spec::Functions::splitdir($dirs);
170              
171 11         96 my $name = $file;
172 11 50       28 my $package = join '_', grep { defined && length } $volume, @dirs, $name;
  99         300  
173              
174             # Escape everything into valid perl identifiers
175 11         85 s/([^A-Za-z0-9_])/sprintf("_%2x", unpack("C", $1))/eg for $package, $name;
  77         352  
176              
177             # make sure the identifiers don't start with a digit
178 11         51 s/^(\d)/_$1/ for $package, $name;
179              
180 11 50       120 $package = $self->{namespace_root} . ($package ? "::$package" : '');
181              
182 11         91 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 99     99   182 my $code = \$_[1];
190              
191             # we use a tmpdir chmodded to 0700 so that the tempfiles are secure
192 99   66     2265 $tmp_dir ||= File::Spec->catfile(File::Spec->tmpdir, "cgi_compile_$$");
193              
194 99 100       3246 if (! -d $tmp_dir) {
195 14 50       2606 mkdir $tmp_dir or die "Could not mkdir $tmp_dir: $!";
196 14 50       928 chmod 0700, $tmp_dir or die "Could not chmod 0700 $tmp_dir: $!";
197             }
198              
199 99         590 my ($fh, $fname) = File::Temp::tempfile('cgi_compile_XXXXX',
200             UNLINK => 1, SUFFIX => '.pm', DIR => $tmp_dir);
201              
202 99         50571 print $fh $$code;
203 99         4830 close $fh;
204              
205 99         56470 my $sub = do $fname;
206              
207 99 50       50779 unlink $fname or die "Could not delete $fname: $!";
208              
209 99         508 return $sub;
210             }
211              
212             END {
213 17 100 66 17   521812 if ($tmp_dir and -d $tmp_dir) {
214 14         6583 File::Path::remove_tree($tmp_dir);
215             }
216             }
217              
218             1;
219              
220             __END__