ViewVC Help
View File | Revision Log | Show Annotations | Download File
/cvs/File-INC/INC.pm
Revision: 1.3
Committed: Wed May 1 03:09:22 2013 UTC (13 years ago) by root
Branch: MAIN
Changes since 1.2: +3 -0 lines
Log Message:
*** empty log message ***

File Contents

# Content
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 #todo
193
194 =cut
195
196 sub force_path {
197 die;
198 }
199
200 =item $fh = $inc->fh
201
202 =cut
203
204 sub fh {
205 if (defined $_[0][0]) { # path?
206 open my $fh, "<", $_[0][0];
207 return $fh;
208 } elsif (defined $_[0][1] && !defined $_[0][2]) { # fh. and no filter?
209 return $_[0][1];
210 } else {
211 return undef;
212 }
213 }
214
215 =item $fh = $inc->force_fh
216
217 #todo
218
219 =cut
220
221 sub force_fh {
222 die;
223 }
224
225 =item $data = $inc->data
226
227 =cut
228
229 sub data {
230 if ($_[0][0]) { # path
231 open my $fh, "<", $_[0][0]
232 or die "$_[0][0]: $!";
233
234 local $/;
235 return scalar <$fh>;
236
237 } elsif (defined $_[0][1]) { # fh
238 if (defined $_[0][2]) { # filter sub
239 my @data;
240 my $status;
241
242 local $_;
243 while () {
244 $_ = <$_[0][1]>;
245 $status = $_[0][2]->($_[0][3]);
246 push @data, $_;
247 last if $status <= 0;
248 }
249
250 return join "", @data;
251 }
252
253 # plain file, slurp
254 local $/;
255 return scalar <$_[0][1]>;
256
257 } elsif (defined $_[0][2]) {
258 # "filter" sub
259
260 my @data;
261 my $status;
262
263 local $_;
264 while () {
265 $_ = "";
266 $status = $_[0][2]->($_[0][3]);
267 push @data, $_;
268 last if $status <= 0;
269 }
270
271 return join "", @data;
272
273 } else {
274 die "internal error";
275 }
276 }
277
278 =back
279
280 =head1 LIMITATIONS
281
282 As with many of the newer additions to perl, C<@INC> hooks have been
283 poorly designed (the interface is pretty chaotic and does not follow Perl
284 language rules, and the subroutine magically has to know whether the line
285 passed to it is the last line of the file) and documented (for example,
286 the filter cache is completely undocumented), and as a result, it's not
287 (to the best of my knowledge) possible implement this module in pure
288 Perl. I hope this implementation is good enough for real world C<@INC>
289 hooks.
290
291 Known deficiencies are that file handles are detected differently,
292 only a copy of the state argument is passed to the subroutine, and the
293 undocumented filter cache return value is ignored.
294
295 =head1 AUTHOR AND CONTACT INFORMATION
296
297 Marc Lehmann <schmorp@schmorp.de>
298 http://software.schmorp.de/pkg/File-INC
299
300 =cut
301
302 1
303