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   315689735 use strict;
  3218         38583  
  3218         90066  
4 3218     3218   64388 use 5.008_001;
  3218         9654  
5              
6             our $VERSION = '0.24';
7              
8 3218     3218   16096 use Cwd;
  3218         6431  
  3218         186658  
9 3218     3218   19306 use File::Basename;
  3218         6436  
  3218         280000  
10 3218     3218   1476559 use File::Spec::Functions;
  3218         2654428  
  3218         221998  
11 3218     3218   1412437 use File::pushd;
  3218         73615363  
  3218         176918  
12 3218     3218   22541 use File::Temp;
  3218         6432  
  3218         183343  
13 3218     3218   19308 use File::Spec;
  3218         6433  
  3218         51477  
14 3218     3218   16083 use File::Path;
  3218         3229  
  3218         183358  
15 3218     3218   1409039 use Sub::Name 'subname';
  3218         1605333  
  3218         553429  
16              
17             our $RETURN_EXIT_VAL = undef;
18              
19             sub new {
20 3299     3299 1 18157 my ($class, %opts) = @_;
21              
22 3299   50     125399 $opts{namespace_root} ||= 'CGI::Compile::ROOT';
23              
24 3299         51042 bless \%opts, $class;
25             }
26              
27             our $USE_REAL_EXIT;
28             BEGIN {
29 3218     3218   12864 $USE_REAL_EXIT = 1;
30              
31 3218         6448 my $orig = *CORE::GLOBAL::exit{CODE};
32              
33 3218 100       45051 my $proto = $orig ? prototype $orig : prototype 'CORE::exit';
34              
35 3218 50       12866 $proto = $proto ? "($proto)" : '';
36              
37             $orig ||= sub {
38 3201         9196 my $exit_code = shift;
39              
40 3201 100       218915 CORE::exit(defined $exit_code ? $exit_code : 0);
41 3218   100     29016 };
42              
43 3218     3218   25721 no warnings 'redefine';
  3218         6431  
  3218         260565  
44              
45 3218 100 100 6436   363478 *CORE::GLOBAL::exit = eval qq{
  6436         7710090  
  6436         45913  
  3235         176437  
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       1186663 die $@ if $@;
55             }
56              
57             my %anon;
58              
59             sub compile {
60 3299     3299 1 171346522126 my($class, $script, $package) = @_;
61              
62 3299 100       145316 my $self = ref $class ? $class : $class->new;
63              
64 3299         45320 my($code, $path, $dir, $subname);
65              
66 3299 100       32828 if (ref $script eq 'SCALAR') {
67 88         146 $code = $$script;
68              
69 88   33     421 $package ||= (caller)[0];
70              
71 88         263 $subname = '__CGI' . $anon{$package}++ . '__';
72             } else {
73 3211         78477 $code = $self->_read_source($script);
74              
75 3211         182464 $path = Cwd::abs_path($script);
76 3211         556017 $dir = File::Basename::dirname($path);
77              
78 3211         14111 my $genned_package;
79              
80 3211   33     49211 ($genned_package, $subname) = $self->_build_subname($path || $script);
81              
82 3211   33     115942 $package ||= $genned_package;
83             }
84              
85 3299 100       72817 my $warnings = $code =~ /^#!.*\s-w\b/ ? 1 : 0;
86 3299         13908 $code =~ s/^__END__\r?\n.*//ms;
87 3299         40280 $code =~ s/^__DATA__\r?\n(.*)//ms;
88 3299 100       25414 my $data = defined $1 ? $1 : '';
89              
90             # TODO handle nph and command line switches?
91 3299 100       66279 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         9046 my $sub = do {
136 3218     3218   28944 no warnings 'uninitialized'; # for 5.8
  3218         6433  
  3218         2519346  
137             # NOTE: this is a workaround to fix a problem in Perl 5.10
138 3299         96769 local @SIG{keys %SIG} = @{[]} = values %SIG;
  3299         2339648  
139 3299         58052 local $USE_REAL_EXIT = 0;
140              
141 3299         21422 my $code = $self->_eval($eval);
142 3299         7367 my $exception = $@;
143              
144 3299 100       12780 die "Could not compile $script: $exception" if $exception;
145              
146             subname "${package}::$subname", sub {
147 3292     3292   3846610 my @args = @_;
148             # this is necessary for MSWin32
149 3292   100     31529 my $orig_warn = $SIG{__WARN__} || sub { warn(@_) };
150 3292 0       43518 local $SIG{__WARN__} = sub { $orig_warn->(@_) unless $_[0] =~ /^No such signal/ };
  0         0  
151 3292         15852 $code->($self, $data, $path, $dir, \@args)
152 3293         1494954 };
153             };
154              
155 3293         18194 return $sub;
156             }
157              
158             sub _read_source {
159 3211     3211   10225 my($self, $file) = @_;
160              
161 3211 50       351832 open my $fh, "<", $file or die "$file: $!";
162 3211         48537 return do { local $/; <$fh> };
  3211         69361  
  3211         209231  
163             }
164              
165             sub _build_subname {
166 3211     3211   11092 my($self, $path) = @_;
167              
168 3211         58546 my ($volume, $dirs, $file) = File::Spec::Functions::splitpath($path);
169 3211         336382 my @dirs = File::Spec::Functions::splitdir($dirs);
170              
171 3211 50       42754 my $package = join '_', grep { defined && length } $volume, @dirs;
  25688         102454  
172 3211         17139 my $name = $file;
173              
174             # Escape everything into valid perl identifiers
175 3211         46769 s/([^A-Za-z0-9_])/sprintf("_%2x", unpack("C", $1))/eg for $package, $name;
  19266         155272  
176              
177             # make sure the identifiers don't start with a digit
178 3211         23390 s/^(\d)/_$1/ for $package, $name;
179              
180 3211 50       112952 $package = $self->{namespace_root} . ($package ? "::$package" : '');
181              
182 3211         40924 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   8321 my $code = \$_[1];
190              
191             # we use a tmpdir chmodded to 0700 so that the tempfiles are secure
192 3299   66     400133 $tmp_dir ||= File::Spec->catfile(File::Spec->tmpdir, "cgi_compile_$$");
193              
194 3299 100       186521 if (! -d $tmp_dir) {
195 3214 50       204970 mkdir $tmp_dir or die "Could not mkdir $tmp_dir: $!";
196 3214 50       63413 chmod 0700, $tmp_dir or die "Could not chmod 0700 $tmp_dir: $!";
197             }
198              
199 3299         120480 my ($fh, $fname) = File::Temp::tempfile('cgi_compile_XXXXX',
200             UNLINK => 1, SUFFIX => '.pm', DIR => $tmp_dir);
201              
202 3299         2258909 print $fh $$code;
203 3299         113802 close $fh;
204              
205 3299         3738496 my $sub = do $fname;
206              
207 3299 50       2073569 unlink $fname or die "Could not delete $fname: $!";
208              
209 3299         14574 return $sub;
210             }
211              
212             END {
213 3218 100 66 3218   107723017 if ($tmp_dir and -d $tmp_dir) {
214 3214         2624243 File::Path::remove_tree($tmp_dir);
215             }
216             }
217              
218             1;
219              
220             __END__