1 |
=head1 NAME |
2 |
|
3 |
File::INC - find files via @INC |
4 |
|
5 |
=head1 SYNOPSIS |
6 |
|
7 |
use File::INC; |
8 |
|
9 |
=head1 DESCRIPTION |
10 |
|
11 |
Perl has two almost identical facilities to load "library" |
12 |
files: C<require> and C<do> (C<use> is basically a compile-time |
13 |
C<require>). |
14 |
|
15 |
These search C<@INC> for the file and compile it. |
16 |
|
17 |
Unfortunately, sometimes you want to locate data (or even source) files |
18 |
using the same mechanism. The problem is that Perl I<laways> evaluates the |
19 |
files as Perl code, which is not something you want for data files. |
20 |
|
21 |
This module implements the same (hopefully :) C<@INC> walk code as perl, |
22 |
and gives you access to the resulting files, without evaluating them. |
23 |
|
24 |
=head2 ALTERNATIVES |
25 |
|
26 |
There are alternatives to this module, both of which I regularly employ, |
27 |
but they come with their own drawbacks: |
28 |
|
29 |
=over 4 |
30 |
|
31 |
=item Walking C<@INC> manually and treating it as an array of directories. |
32 |
|
33 |
Basically, this: |
34 |
|
35 |
sub find_rcfile($) { |
36 |
my $path; |
37 |
|
38 |
for (@RC_PATH, "") { |
39 |
$path = "$RC_BASE/$_/$_[0]"; |
40 |
return $path if -e $path; |
41 |
} |
42 |
|
43 |
die "FATAL: can't find required file \"$_[0]\" in \"$RC_BASE\"\n"; |
44 |
} |
45 |
|
46 |
my $path = find_rcfile "My/Module/datafile.dat"; |
47 |
|
48 |
This is simple, but fails for any entries in C<@INC> that are not |
49 |
directories. Not handling these makes it fail in many environments, such |
50 |
as in L<App::Staticperl> or L<PAR::Packer>. |
51 |
|
52 |
This module basically implements a full-featured version of this that |
53 |
hopefully gets everything right. |
54 |
|
55 |
=item Embed datafiles using C<do>. |
56 |
|
57 |
You can embed datafiles by, say, uuencoding them and putting them into |
58 |
a F<.pm> file (I think L<PAR::Packer>) does this. This is extremely |
59 |
wasteful, but at least works around the C<@INC> problematic. A slightly |
60 |
better way is to use C<do>. Take this file, put as F<My/Module/data.pl> |
61 |
into the perl library: |
62 |
|
63 |
<<EOF |
64 |
text data... |
65 |
EOF |
66 |
|
67 |
Then you can load the "text data..." part using a simple C<do> call: |
68 |
|
69 |
my $data = do "My/Module/data.pl"; |
70 |
|
71 |
You can handle arbirary binary data either by finding a suitable C<EOF> |
72 |
marker that isn't part of it, or you could use utf8 encoding to the |
73 |
rescue, by using a >255 character code as text delimiter: |
74 |
|
75 |
use utf8; |
76 |
# use U+2026 as delimiter |
77 |
q…arbitary binary data....… |
78 |
|
79 |
While this takes care of C<@INC>, perl still has to compile and execute |
80 |
it every time (which might not be a significant problem), and it requires |
81 |
the data to be formatted specifically for this occasion, so is hard for |
82 |
accessing the sources of third-party modules. |
83 |
|
84 |
=back |
85 |
|
86 |
=head1 FUNCTIONS |
87 |
|
88 |
This module only provides one function, not exported, that creates |
89 |
C<File::INC> objects. |
90 |
|
91 |
=over 4 |
92 |
|
93 |
=cut |
94 |
|
95 |
package File::INC; |
96 |
|
97 |
use common::sense; |
98 |
|
99 |
use Carp (); |
100 |
|
101 |
our $VERSION = 0.2; |
102 |
|
103 |
=item $inc = File::INC::find $name |
104 |
|
105 |
The name is a file name of the same syntax as expected by C<do EXPR> or |
106 |
C<require EXPR>, e.g. F<File/INC.pm> or F<My/Module/data.bin>. Unlike |
107 |
C<require> et al., absolute paths are not supported. |
108 |
|
109 |
If the file is found, it returns a C<File::INC> object. Otherwise, it |
110 |
returns C<undef>. |
111 |
|
112 |
To work around potential bugs in C<@INC> hooks, it is recommended to only |
113 |
keep one C<File::INC> around at any single time. |
114 |
|
115 |
=cut |
116 |
|
117 |
sub find($) { |
118 |
my $file = shift; |
119 |
|
120 |
for my $inc (@INC) { |
121 |
if (ref $inc) { |
122 |
my $func; |
123 |
|
124 |
if (CODE:: eq ref $inc) { |
125 |
$func = $inc; |
126 |
} elsif (ARRAY:: eq ref $inc) { |
127 |
$func = $inc->[0]; |
128 |
} else { |
129 |
$func = $inc->can ("INC"); |
130 |
} |
131 |
|
132 |
if ($func) { |
133 |
my @r = $func->($inc, $file);; |
134 |
my ($cache, $fh, $sub, $state); |
135 |
|
136 |
# undocumented and unsupported |
137 |
if (!eval { fileno $r[0] } and CODE:: ne ref $r[0]) { |
138 |
$cache = shift @r; |
139 |
} |
140 |
|
141 |
if (defined eval { fileno $r[0] }) { |
142 |
$fh = shift @r; |
143 |
} |
144 |
|
145 |
if (CODE:: eq ref $r[0]) { |
146 |
$sub = shift @r; |
147 |
} |
148 |
|
149 |
$state = shift @r; |
150 |
|
151 |
if (defined $fh or defined $cache or defined $sub) { |
152 |
defined $cache |
153 |
and Carp::croak "File::INC: undocumented filter caches are not supported\n"; |
154 |
|
155 |
return bless [undef, $fh, $sub, $state, $cache]; |
156 |
} |
157 |
} |
158 |
|
159 |
} elsif (-e "$inc/$file") { |
160 |
# what a relief, the simple case |
161 |
return bless ["$inc/$file"]; |
162 |
} |
163 |
} |
164 |
|
165 |
undef |
166 |
} |
167 |
|
168 |
=back |
169 |
|
170 |
=head1 THE C<File::INC> CLASS |
171 |
|
172 |
Library files are represented by C<File::INC> objects. You can query it to |
173 |
find more information about the file (such as whether it even is a file |
174 |
:). |
175 |
|
176 |
=over 4 |
177 |
|
178 |
=item $path = $inc->path |
179 |
|
180 |
If the file is actually a file on disk (the most common, but not |
181 |
guaranteed, case), then this method returns the path to it. In other |
182 |
cases, it returns C<undef>. |
183 |
|
184 |
=cut |
185 |
|
186 |
sub path { |
187 |
$_[0][0] |
188 |
} |
189 |
|
190 |
=item $path = $inc->force_path |
191 |
|
192 |
Like C<$path>, but always returns a path. Or tries to, by creating a |
193 |
temporary file and writing the data to it, if neccessary. |
194 |
|
195 |
#todo |
196 |
|
197 |
=cut |
198 |
|
199 |
sub force_path { |
200 |
die; |
201 |
} |
202 |
|
203 |
=item $fh = $inc->fh |
204 |
|
205 |
=cut |
206 |
|
207 |
sub fh { |
208 |
if (defined $_[0][0]) { # path? |
209 |
open my $fh, "<", $_[0][0]; |
210 |
return $fh; |
211 |
} elsif (defined $_[0][1] && !defined $_[0][2]) { # fh. and no filter? |
212 |
return $_[0][1]; |
213 |
} else { |
214 |
return undef; |
215 |
} |
216 |
} |
217 |
|
218 |
=item $fh = $inc->force_fh |
219 |
|
220 |
#todo |
221 |
|
222 |
=cut |
223 |
|
224 |
sub force_fh { |
225 |
die; |
226 |
} |
227 |
|
228 |
=item $data = $inc->data |
229 |
|
230 |
=cut |
231 |
|
232 |
sub data { |
233 |
if ($_[0][0]) { # path |
234 |
open my $fh, "<", $_[0][0] |
235 |
or die "$_[0][0]: $!"; |
236 |
|
237 |
local $/; |
238 |
return scalar <$fh>; |
239 |
|
240 |
} elsif (defined $_[0][1]) { # fh |
241 |
if (defined $_[0][2]) { # filter sub |
242 |
my @data; |
243 |
my $status; |
244 |
|
245 |
local $_; |
246 |
while () { |
247 |
$_ = <$_[0][1]>; |
248 |
$status = $_[0][2]->($_[0][3]); |
249 |
push @data, $_; |
250 |
last if $status <= 0; |
251 |
} |
252 |
|
253 |
return join "", @data; |
254 |
} |
255 |
|
256 |
# plain file, slurp |
257 |
local $/; |
258 |
return scalar <$_[0][1]>; |
259 |
|
260 |
} elsif (defined $_[0][2]) { |
261 |
# "filter" sub |
262 |
|
263 |
my @data; |
264 |
my $status; |
265 |
|
266 |
local $_; |
267 |
while () { |
268 |
$_ = ""; |
269 |
$status = $_[0][2]->($_[0][3]); |
270 |
push @data, $_; |
271 |
last if $status <= 0; |
272 |
} |
273 |
|
274 |
return join "", @data; |
275 |
|
276 |
} else { |
277 |
die "internal error"; |
278 |
} |
279 |
} |
280 |
|
281 |
=back |
282 |
|
283 |
=head1 LIMITATIONS |
284 |
|
285 |
As with many of the newer additions to perl, C<@INC> hooks have been |
286 |
poorly designed (the interface is pretty chaotic and does not follow Perl |
287 |
language rules, and the subroutine magically has to know whether the line |
288 |
passed to it is the last line of the file) and documented (for example, |
289 |
the filter cache is completely undocumented), and as a result, it's not |
290 |
(to the best of my knowledge) possible implement this module in pure |
291 |
Perl. I hope this implementation is good enough for real world C<@INC> |
292 |
hooks. |
293 |
|
294 |
Known deficiencies are that file handles are detected differently, |
295 |
only a copy of the state argument is passed to the subroutine, and the |
296 |
undocumented filter cache return value is ignored. |
297 |
|
298 |
=head1 AUTHOR AND CONTACT INFORMATION |
299 |
|
300 |
Marc Lehmann <schmorp@schmorp.de> |
301 |
http://software.schmorp.de/pkg/File-INC |
302 |
|
303 |
=cut |
304 |
|
305 |
1 |
306 |
|