File Coverage

blib/lib/Module/Reader.pm
Criterion Covered Total %
statement 171 212 80.6
branch 81 130 62.3
condition 30 62 48.3
subroutine 38 49 77.5
pod 10 11 90.9
total 330 464 71.1


line stmt bran cond sub pod time code
1             package Module::Reader;
2 3     3   67019 BEGIN { require 5.006 }
3 3     3   25 use strict;
  3         11  
  3         102  
4 3     3   24 use warnings;
  3         9  
  3         223  
5              
6             our $VERSION = '0.003003';
7             $VERSION = eval $VERSION;
8              
9 3     3   33 use Exporter (); BEGIN { *import = \&Exporter::import }
  3     3   10  
  3         106  
  3         222  
10             our @EXPORT_OK = qw(module_content module_handle);
11             our %EXPORT_TAGS = (all => [@EXPORT_OK]);
12              
13 3     3   30 use File::Spec ();
  3         9  
  3         87  
14 3     3   22 use Scalar::Util qw(reftype refaddr openhandle);
  3         8  
  3         437  
15 3     3   27 use Carp qw(croak);
  3         9  
  3         194  
16 3     3   26 use Config ();
  3         8  
  3         103  
17 3     3   1945 use Errno qw(EACCES);
  3         5241  
  3         456  
18 3 50   3   31 use constant _OPEN_LAYERS => "$]" >= 5.008_000 ? ':' : '';
  3         28  
  3         366  
19 3     3   29 use constant _ABORT_ON_EACCES => "$]" >= 5.017_001;
  3         8  
  3         282  
20 3     3   28 use constant _ALLOW_PREFIX => "$]" >= 5.008009;
  3         9  
  3         330  
21 3   33 3   28 use constant _VMS => $^O eq 'VMS' && !!require VMS::Filespec;
  3         8  
  3         240  
22 3     3   28 use constant _WIN32 => $^O eq 'MSWin32';
  3         16  
  3         453  
23             use constant _PMC_ENABLED => !(
24             exists &Config::non_bincompat_options
25 24         441 ? grep { $_ eq 'PERL_DISABLE_PMC' } Config::non_bincompat_options()
26 3 50       58 : $Config::Config{ccflags} =~ /(?:^|\s)-DPERL_DISABLE_PMC\b/
27 3     3   28 );
  3         11  
28 3         6 use constant _FAKE_FILE_FORMAT => do {
29 3   50     288 my $uvx = $Config::Config{uvxformat} || '';
30 3         19 $uvx =~ tr/"\0//d;
31 3   50     18 $uvx ||= 'lx';
32 3         4739 "/loader/0x%$uvx/%s"
33 3     3   27 };
  3         9  
34              
35             sub _mod_to_file {
36 35     35   76 my $module = shift;
37 35         137 (my $file = "$module.pm") =~ s{::}{/}g;
38 35         117 $file;
39             }
40              
41             sub module_content {
42 18   100 18 1 12146 my $opts = ref $_[-1] eq 'HASH' && pop @_ || {};
43 18         49 my $module = shift;
44 18 50       91 $opts->{inc} = [@_]
45             if @_;
46 18         92 __PACKAGE__->new($opts)->module($module)->content;
47             }
48              
49             sub module_handle {
50 0   0 0 1 0 my $opts = ref $_[-1] eq 'HASH' && pop @_ || {};
51 0         0 my $module = shift;
52 0 0       0 $opts->{inc} = [@_]
53             if @_;
54 0         0 __PACKAGE__->new($opts)->module($module)->handle;
55             }
56              
57             sub new {
58 83     83 0 65757 my $class = shift;
59 83         189 my %options;
60 83 100 66     480 if (@_ == 1 && ref $_[-1]) {
    50          
61 18         41 %options = %{(pop)};
  18         89  
62             }
63             elsif (@_ % 2 == 0) {
64 65         272 %options = @_;
65             }
66             else {
67 0         0 croak "Expected hash ref, or key value pairs. Got ".@_." arguments.";
68             }
69              
70 83   100     374 $options{inc} ||= \@INC;
71             $options{found} = \%INC
72 83 50 66     349 if exists $options{found} && $options{found} eq 1;
73             $options{pmc} = _PMC_ENABLED
74 83 100       254 if !exists $options{pmc};
75             $options{open} = 1
76 83 50       286 if !exists $options{open};
77             $options{abort_on_eacces} = _ABORT_ON_EACCES
78 83 100       237 if !exists $options{abort_on_eacces};
79             $options{check_hooks_for_nonsearchable} = 1
80 83 50       246 if !exists $options{check_hooks_for_nonsearchable};
81 83         363 bless \%options, $class;
82             }
83              
84             sub module {
85 35     35 1 149 my ($self, $module) = @_;
86 35         101 $self->file(_mod_to_file($module));
87             }
88              
89             sub modules {
90 0     0 1 0 my ($self, $module) = @_;
91 0         0 $self->files(_mod_to_file($module));
92             }
93              
94             sub file {
95 83     83 1 410 my ($self, $file) = @_;
96 83         245 $self->_find($file);
97             }
98              
99             sub files {
100 0     0 1 0 my ($self, $file) = @_;
101 0         0 $self->_find($file, 1);
102             }
103              
104             sub _searchable {
105 70     70   151 my $file = shift;
106 70 100       778 File::Spec->file_name_is_absolute($file) ? 0
    50          
107             : _WIN32 && $file =~ m{^\.\.?[/\\]} ? 0
108             : $file =~ m{^\.\.?/} ? 0
109             : 1
110             }
111              
112             sub _find {
113 83     83   197 my ($self, $file, $all) = @_;
114              
115 83         169 my @found;
116 83         157 eval {
117 83 100       310 if (my $found = $self->{found}) {
118 17 50       74 if (defined( my $full = $found->{$file} )) {
119 17 100       91 my $open = length ref $full ? $self->_open_ref($full, $file)
120             : $self->_open_file($full, $file);
121 17 100       86 push @found, $open
122             if $open;
123             }
124             }
125             };
126 83 50       221 if (!$all) {
127 83 100       261 return $found[0]
128             if @found;
129 70 50       223 die $@
130             if $@;
131             }
132              
133 70         196 my $searchable = _searchable($file);
134 70 100       245 if (!$searchable) {
135 16         61 my $open = $self->_open_file($file);
136 16 50       143 if ($all) {
    100          
137 0         0 push @found, $open;
138             }
139             elsif ($open) {
140 12         56 return $open;
141             }
142             else {
143 4         820 croak "Can't locate $file";
144             }
145             }
146              
147 54         119 my $search = $self->{inc};
148 54         159 for my $inc (@$search) {
149 75         138 my $open;
150 75 50       207 if (!$searchable) {
151             last
152 0 0       0 if !$self->{check_hooks_for_nonsearchable};
153             next
154 0 0       0 if !length ref $inc;
155             }
156 75         151 eval {
157 75 100       217 if (!length ref $inc) {
158 41         81 my $full = _VMS ? VMS::Filespec::unixpath($inc) : $inc;
159 41         256 $full =~ s{/?$}{/};
160 41         120 $full .= $file;
161 41         126 $open = $self->_open_file($full, $file, $inc);
162             }
163             else {
164 34         119 $open = $self->_open_ref($inc, $file);
165             }
166 63 100       231 push @found, $open
167             if $open;
168             };
169 75 50       265 if (!$all) {
170 75 100       314 return $found[0]
171             if @found;
172 33 100       154 die $@
173             if $@;
174             }
175             }
176 0 0       0 croak "Can't locate $file"
177             if !$all;
178 0         0 return @found;
179             }
180              
181             sub _open_file {
182 73     73   188 my ($self, $full, $file, $inc) = @_;
183 73 100       196 $file = $full
184             if !defined $file;
185 73 100 66     562 for my $try (
186             ($self->{pmc} && $file =~ /\.pm\z/ ? $full.'c' : ()),
187             $full,
188             ) {
189 98         248 my $pmc = $full ne $try;
190 98 100       1706 if (-e $try) {
191             next
192 73 100 66     396 if -d _ || -b _;
193 49 50       1520 if (open my $fh, '<'._OPEN_LAYERS, $try) {
194             return Module::Reader::File->new(
195             filename => $file,
196 49 50       509 ($self->{open} ? (raw_filehandle => $fh) : ()),
    100          
197             found_file => $full,
198             disk_file => $try,
199             is_pmc => $pmc,
200             (defined $inc ? (inc_entry => $inc) : ()),
201             );
202             }
203             }
204              
205             croak "Can't locate $file: $full: $!"
206 25 50 33     327 if $self->{abort_on_eacces} && $! == EACCES && !$pmc;
      33        
207             }
208 24         159 return;
209             }
210              
211             sub _open_ref {
212 35     35   108 my ($self, $inc, $file) = @_;
213              
214 35         72 my @cb;
215             {
216             # strings in arrayrefs are taken as sub names relative to main
217 35         69 package
218             main;
219 3     3   35 no strict 'refs';
  3         10  
  3         161  
220 3     3   25 no warnings 'uninitialized';
  3         13  
  3         1571  
221 35 100       314 @cb = defined Scalar::Util::blessed $inc ? $inc->INC($file)
    100          
222             : ref $inc eq 'ARRAY' ? $inc->[0]->($inc, $file)
223             : $inc->($inc, $file);
224             }
225              
226             return
227 23 100       1287 unless length ref $cb[0];
228              
229 20         143 my $fake_file = sprintf _FAKE_FILE_FORMAT, refaddr($inc), $file;
230              
231 20         86 my $fh;
232             my $prefix;
233 20         0 my $cb;
234 20         0 my $cb_options;
235              
236 20 50       97 if (_ALLOW_PREFIX && reftype $cb[0] eq 'SCALAR') {
237 0         0 $prefix = shift @cb;
238             }
239              
240 20 100 50     135 if ((reftype $cb[0]||'') eq 'GLOB' && openhandle $cb[0]) {
      66        
241 6         18 $fh = shift @cb;
242             }
243              
244 20 100 100     146 if ((reftype $cb[0]||'') eq 'CODE') {
    100 66        
245 12         30 $cb = $cb[0];
246             # only one or zero callback options will be passed
247 12 50       42 $cb_options = @cb > 1 ? [ $cb[1] ] : undef;
248             }
249             elsif (!defined $fh && !defined $prefix) {
250 2         8 return;
251             }
252 18 50       207 return Module::Reader::File->new(
    100          
    100          
    50          
253             filename => $file,
254             found_file => $fake_file,
255             inc_entry => $inc,
256             (defined $prefix ? (prefix => $prefix) : ()),
257             (defined $fh ? (raw_filehandle => $fh) : ()),
258             (defined $cb ? (read_callback => $cb) : ()),
259             (defined $cb_options ? (read_callback_options => $cb_options) : ()),
260             );
261             }
262              
263 0     0 1 0 sub inc { $_[0]->{inc} }
264 0     0 1 0 sub found { $_[0]->{found} }
265 0     0 1 0 sub pmc { $_[0]->{pmc} }
266 0     0 1 0 sub open { $_[0]->{open} }
267              
268             {
269             package Module::Reader::File;
270 3   33 3   29 use constant _OPEN_STRING => "$]" >= 5.008 || !require IO::String;
  3         10  
  3         239  
271 3     3   27 use Carp 'croak';
  3         10  
  3         3640  
272              
273             sub new {
274 67     67   403 my ($class, %opts) = @_;
275 67         168 my $filename = $opts{filename};
276 67 100 33     759 if (!exists $opts{module} && $opts{filename}
      66        
277             && $opts{filename} =~ m{\A(\w+(?:/\w+)?)\.pm\z}) {
278 55         200 my $module = $1;
279 55         138 $module =~ s{/}{::}g;
280 55         159 $opts{module} = $module;
281             }
282 67         303 bless \%opts, $class;
283             }
284              
285 0     0   0 sub filename { $_[0]->{filename} }
286 0     0   0 sub module { $_[0]->{module} }
287 1     1   7 sub found_file { $_[0]->{found_file} }
288 12     12   61 sub disk_file { $_[0]->{disk_file} }
289 36     36   145 sub is_pmc { $_[0]->{is_pmc} }
290 60     60   710 sub inc_entry { $_[0]->{inc_entry} }
291 6     6   14 sub read_callback { $_[0]->{read_callback} }
292 0     0   0 sub read_callback_options { $_[0]->{read_callback_options} }
293             sub raw_filehandle {
294 6 0 33 6   24 $_[0]->{raw_filehandle} ||= !$_[0]->{disk_file} ? undef : do {
295             open my $fh, '<'.Module::Reader::_OPEN_LAYERS, $_[0]->{disk_file}
296 0 0       0 or croak "Can't locate $_[0]->{disk_file}";
297 0         0 $fh;
298             };
299             }
300              
301             sub content {
302 6     6   16 my $self = shift;
303             return $self->{content}
304 6 50       22 if exists $self->{content};
305 6         18 my $fh = $self->raw_filehandle;
306 6         20 my $cb = $self->read_callback;
307 6 50       26 my $content = defined $self->{prefix} ? ${$self->{prefix}} : '';
  0         0  
308 6 50 33     44 if ($fh && !$cb) {
309 6         29 local $/;
310 6         53 $content .= <$fh>;
311             }
312 6 50       21 if ($cb) {
313 0 0       0 my @params = @{$self->read_callback_options||[]};
  0         0  
314 0         0 while (1) {
315 0 0       0 local $_ = $fh ? <$fh> : '';
316 0 0       0 $_ = ''
317             if !defined;
318             # perlfunc/require says that the first parameter will be a reference the
319             # sub itself. this is wrong. 0 will be passed.
320 0 0       0 last if !$cb->(0, @params);
321 0         0 $content .= $_;
322             }
323             }
324 6         46 return $self->{content} = $content;
325             }
326              
327             sub handle {
328 0     0     my $self = shift;
329 0           my $fh = $self->raw_filehandle;
330 0 0 0       if ($fh && !$self->read_callback && -f $fh) {
      0        
331 0 0         open my $dup, '<&', $fh
332             or croak "can't dup file handle: $!";
333 0           return $dup;
334             }
335 0           my $content = $self->content;
336 0           if (_OPEN_STRING) {
337 0           open my $fh, '<', \$content;
338 0           return $fh;
339             }
340             else {
341             return IO::String->new($content);
342             }
343             }
344             }
345              
346             1;
347              
348             __END__