File Coverage

blib/lib/Module/Reader.pm
Criterion Covered Total %
statement 151 190 79.4
branch 72 112 64.2
condition 25 51 49.0
subroutine 36 47 76.6
pod 10 11 90.9
total 294 411 71.5


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