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   342418443 use strict;
  3218         48256  
  3218         144527  
4 3218     3218   134886 use 5.008_001;
  3218         32071  
5              
6             our $VERSION = '0.26';
7              
8 3218     3218   16104 use Cwd;
  3218         6432  
  3218         289122  
9 3218     3218   19332 use File::Basename;
  3218         6435  
  3218         417828  
10 3218     3218   1643821 use File::Spec::Functions;
  3218         3267445  
  3218         234870  
11 3218     3218   2776623 use File::pushd;
  3218         81198834  
  3218         196236  
12 3218     3218   22543 use File::Temp;
  3218         6439  
  3218         192990  
13 3218     3218   19303 use File::Spec;
  3218         3237  
  3218         51535  
14 3218     3218   16087 use File::Path;
  3218         6437  
  3218         215418  
15 3218     3218   1627566 use Sub::Name 'subname';
  3218         1926534  
  3218         640187  
16              
17             our $RETURN_EXIT_VAL = undef;
18              
19             sub new {
20 3299     3299 1 20985 my ($class, %opts) = @_;
21              
22 3299   50     190515 $opts{namespace_root} ||= 'CGI::Compile::ROOT';
23              
24 3299         99789 bless \%opts, $class;
25             }
26              
27             our $USE_REAL_EXIT;
28             BEGIN {
29 3218     3218   12881 $USE_REAL_EXIT = 1;
30              
31 3218         19268 my $orig = *CORE::GLOBAL::exit{CODE};
32              
33 3218 100       61092 my $proto = $orig ? prototype $orig : prototype 'CORE::exit';
34              
35 3218 50       9727 $proto = $proto ? "($proto)" : '';
36              
37             $orig ||= sub {
38 3201         11404 my $exit_code = shift;
39              
40 3201 100       206833 CORE::exit(defined $exit_code ? $exit_code : 0);
41 3218   100     38623 };
42              
43 3218     3218   22531 no warnings 'redefine';
  3218         6432  
  3218         328005  
44              
45 3218 100 100 6436   373454 *CORE::GLOBAL::exit = eval qq{
  6436         7232953  
  6436         63291  
  3235         176496  
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       1190704 die $@ if $@;
55             }
56              
57             my %anon;
58              
59             sub compile {
60 3299     3299 1 181444737695 my($class, $script, $package) = @_;
61              
62 3299 100       226596 my $self = ref $class ? $class : $class->new;
63              
64 3299         34510 my($code, $path, $dir, $subname);
65              
66 3299 100       80026 if (ref $script eq 'SCALAR') {
67 88         151 $code = $$script;
68              
69 88   33     463 $package ||= (caller)[0];
70              
71 88         272 $subname = '__CGI' . $anon{$package}++ . '__';
72             } else {
73 3211         53265 $code = $self->_read_source($script);
74              
75 3211         215180 $path = Cwd::abs_path($script);
76 3211         666133 $dir = File::Basename::dirname($path);
77              
78 3211         23493 my $genned_package;
79              
80 3211   33     62832 ($genned_package, $subname) = $self->_build_subname($path || $script);
81              
82 3211   33     151398 $package ||= $genned_package;
83             }
84              
85 3299 100       72654 my $warnings = $code =~ /^#!.*\s-w\b/ ? 1 : 0;
86 3299         15819 $code =~ s/^__END__\r?\n.*//ms;
87 3299         54374 $code =~ s/^__DATA__\r?\n(.*)//ms;
88 3299 100       23426 my $data = defined $1 ? $1 : '';
89              
90             # TODO handle nph and command line switches?
91 3299 100       107730 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         12751 my $sub = do {
136 3218     3218   32184 no warnings 'uninitialized'; # for 5.8
  3218         6445  
  3218         2722967  
137             # NOTE: this is a workaround to fix a problem in Perl 5.10
138 3299         102588 local @SIG{keys %SIG} = @{[]} = values %SIG;
  3299         2999070  
139 3299         64168 local $USE_REAL_EXIT = 0;
140              
141 3299         29854 my $code = $self->_eval($eval);
142 3299         15264 my $exception = $@;
143              
144 3299 100       15896 die "Could not compile $script: $exception" if $exception;
145              
146             subname "${package}::$subname", sub {
147 3292     3292   4731531 my @args = @_;
148             # this is necessary for MSWin32
149 3292   100     64289 my $orig_warn = $SIG{__WARN__} || sub { warn(@_) };
150 3292 0       51450 local $SIG{__WARN__} = sub { $orig_warn->(@_) unless $_[0] =~ /^No such signal/ };
  0         0  
151 3292         23263 $code->($self, $data, $path, $dir, \@args)
152 3293         1777931 };
153             };
154              
155 3293         31078 return $sub;
156             }
157              
158             sub _read_source {
159 3211     3211   15212 my($self, $file) = @_;
160              
161 3211 50       503160 open my $fh, "<", $file or die "$file: $!";
162 3211         45118 return do { local $/; <$fh> };
  3211         74238  
  3211         220777  
163             }
164              
165             sub _build_subname {
166 3211     3211   38839 my($self, $path) = @_;
167              
168 3211         94434 my ($volume, $dirs, $file) = File::Spec::Functions::splitpath($path);
169 3211         409949 my @dirs = File::Spec::Functions::splitdir($dirs);
170              
171 3211         36267 my $name = $file;
172 3211 50       9137 my $package = join '_', grep { defined && length } $volume, @dirs, $name;
  28899         134306  
173              
174             # Escape everything into valid perl identifiers
175 3211         58043 s/([^A-Za-z0-9_])/sprintf("_%2x", unpack("C", $1))/eg for $package, $name;
  22477         161760  
176              
177             # make sure the identifiers don't start with a digit
178 3211         67149 s/^(\d)/_$1/ for $package, $name;
179              
180 3211 50       124267 $package = $self->{namespace_root} . ($package ? "::$package" : '');
181              
182 3211         22532 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   10705 my $code = \$_[1];
190              
191             # we use a tmpdir chmodded to 0700 so that the tempfiles are secure
192 3299   66     488876 $tmp_dir ||= File::Spec->catfile(File::Spec->tmpdir, "cgi_compile_$$");
193              
194 3299 100       224700 if (! -d $tmp_dir) {
195 3214 50       301344 mkdir $tmp_dir or die "Could not mkdir $tmp_dir: $!";
196 3214 50       94590 chmod 0700, $tmp_dir or die "Could not chmod 0700 $tmp_dir: $!";
197             }
198              
199 3299         107941 my ($fh, $fname) = File::Temp::tempfile('cgi_compile_XXXXX',
200             UNLINK => 1, SUFFIX => '.pm', DIR => $tmp_dir);
201              
202 3299         2729579 print $fh $$code;
203 3299         173743 close $fh;
204              
205 3299         4549341 my $sub = do $fname;
206              
207 3299 50       2462347 unlink $fname or die "Could not delete $fname: $!";
208              
209 3299         34779 return $sub;
210             }
211              
212             END {
213 3218 100 66 3218   115362926 if ($tmp_dir and -d $tmp_dir) {
214 3214         1581635 File::Path::remove_tree($tmp_dir);
215             }
216             }
217              
218             1;
219              
220             __END__