File Coverage

blib/lib/FindBin/libs.pm
Criterion Covered Total %
statement 45 50 90.0
branch 17 28 60.7
condition 3 5 60.0
subroutine 9 9 100.0
pod n/a
total 74 92 80.4


line stmt bran cond sub pod time code
1             ########################################################################
2             # housekeeping
3             ########################################################################
4              
5             package FindBin::libs v4.0.4;
6 10     10   2426377 use v5.40;
  10         41  
7              
8 10     10   84 use File::Basename;
  10         21  
  10         1167  
9              
10 10     10   91 use Carp qw( croak );
  10         25  
  10         777  
11 10     10   121 use List::Util qw( uniq first );
  10         60  
  10         1102  
12 10     10   2748 use Symbol qw( qualify qualify_to_ref );
  10         8023  
  10         870  
13              
14             use File::Spec::Functions
15             qw
16 10         930 (
17             splitpath
18             splitdir
19             catpath
20             catdir
21             rel2abs
22 10     10   2379 );
  10         4339  
23              
24 10     10   3947 use FindBin::Bin qw( $Bin );
  10         31  
  10         1544  
25 10     10   4436 use FindBin::Parents qw( dir_paths );
  10         31  
  10         18174  
26              
27             ########################################################################
28             # package variables
29             ########################################################################
30              
31             my %defaultz =
32             (
33             base => 'lib'
34             , use => undef
35             , blib => undef # prefer ./blib at the first level
36              
37             , subdir => '' # add this subdir also if found.
38             , subonly => undef # leave out lib's, use only subdir.
39             , export => undef # push variable into caller's space.
40             , append => undef # push onto existing array (vs. overwrite)
41             , verbose => undef # boolean: print inputs, results.
42             , debug => undef # boolean: set internal breakpoints.
43              
44             , print => 1 # display the results
45              
46             , p5lib => undef # prefix PERL5LIB with the results
47              
48             , ignore => q{,usr} # dir's to skip looking for ./lib
49             );
50              
51             # saves passing this between import and $handle_args.
52              
53             my %argz = ();
54             my $verbose = '';
55             my $empty = q{};
56              
57             ########################################################################
58             # utility subs
59             ########################################################################
60              
61             # HAK ALERT: $Bin is an absolute path, there are cases
62             # where splitdir does not add the leading '' onto the
63             # directory path for it on VMS. Fix is to unshift a leading
64             # '' into @dirpath where the leading entry is true.
65              
66             my $append_subdir
67             = sub( $path, @subz )
68             {
69             my ( $vol, $dir ) = splitpath $path, 1;
70              
71             catpath
72             (
73             $vol
74             , catdir( $dir => @subz )
75             , ''
76             )
77             };
78              
79             my $find_libs
80             = sub
81             {
82             my $base = basename ( shift // $argz{ base } );
83             my $subdir = $argz{ subdir } // '';
84             my $subonly = defined $argz{ subonly } || defined $argz{ scalar };
85             my @found = ();
86             my $ignorz = $argz{ ignore };
87              
88             say STDERR "\nSearching $Bin for '$base' ($subdir)..."
89             if $verbose;
90              
91             my @search
92             = do
93             {
94             my @dirz = dir_paths $Bin, 1;
95              
96             say STDERR join "\n\t" => 'Parent Dirs:', @dirz
97             if $verbose;;
98              
99             my ( $vol ) = splitpath $dirz[0];
100             my %skipz
101             = map
102             {
103             (
104             catpath( $vol => $_, '' ) => 1
105             )
106             }
107             $ignorz->@*;
108              
109             my @subz
110             = grep
111             {
112             ! exists $skipz{ $_ }
113             }
114             map
115             {
116             $_->$append_subdir( $base )
117             }
118             @dirz;
119              
120             say STDERR join "\n\t" => 'Search Subs:', @subz
121             if $verbose;;
122              
123             if( $subdir )
124             {
125             map
126             {
127             my $sub = $_->$append_subdir( $subdir );
128              
129             $subonly
130             ? $sub
131             : [ $sub => $_ ]
132             }
133             @subz
134             }
135             else
136             {
137             $subonly = 1;
138             @subz
139             }
140             };
141              
142             if( $argz{ scalar } )
143             {
144             # return at most one item.
145              
146             first
147             {
148             -e
149             }
150             @search
151             }
152             elsif( $subonly )
153             {
154             # grab all the extant items on the list.
155              
156             grep
157             {
158             -e
159             }
160             @search
161             }
162             else
163             {
164             # if the subdir exists then return it and the subdir.
165              
166             map
167             {
168             -e $_->[0]
169             ? $_->@*
170             : ()
171             }
172             @search
173             }
174             };
175              
176             my $handle_args
177             = sub
178             {
179             # discard the module, rest are arguments.
180              
181             shift;
182              
183             # anything after the module are options with arguments
184             # assigned via '='.
185              
186             %argz
187             = map
188             {
189             my $use_undef
190             = do
191             {
192             my %a = ();
193             @a{ qw( export ignore ) } = ();
194             \%a
195             };
196              
197             my ( $k, $v ) = split '=', $_, 2;
198              
199             exists $use_undef->{ $k }
200             or $v //= 1;
201              
202             # "no" inverts the sense of the test.
203              
204             $k =~ s{^no}{}
205             and $v = ! $v;
206              
207             ( $k => $v )
208             }
209             @_;
210              
211             # exporting a subdir as a scalar requires stopping at
212             # the subdir, ignoring its parent.
213              
214             $argz{ subonly }
215             ||= $argz{ export } && $argz{ subdir } && $argz{ scalar };
216              
217             # stuff "debug=1" into your arguments and perl -d will stop here.
218              
219             $DB::single = 1 if defined $argz{ debug };
220              
221             # default if nothing is supplied is to use the result;
222             # otherwise, without use supplied either of export or
223             # p5lib will turn off use.
224              
225             if( exists $argz{ use } )
226             {
227             # nothing further to do
228             }
229             elsif( defined $argz{ export } || defined $argz{ p5lib } )
230             {
231             $argz{ use } = undef;
232             }
233             else
234             {
235             $argz{ use } = 1;
236             }
237              
238             # now apply the defaults, then sanity check the result.
239             # base is a special case since it always has to exist.
240             #
241             # if $argz{ export } is defined but false then it takes
242             # its default from $argz{ base }.
243              
244             while( my($k,$v) = each %defaultz )
245             {
246             # //= doesn't work here since undef may be a
247             # legit default.
248              
249             exists $argz{ $k }
250             or
251             $argz{ $k } = $v;
252             }
253              
254             exists $argz{ base } && $argz{ base }
255             or croak "Bogus FindBin::libs: missing/false base argument, should be 'base=NAME'";
256              
257             $argz{ export } //= $argz{ base }
258             if exists $argz{ export };
259              
260             $argz{ ignore } =
261             [
262             uniq split /\s*,\s*/, $argz{ ignore }
263             ];
264              
265             $verbose = defined $argz{ verbose };
266             };
267              
268             ########################################################################
269             # interface
270             ########################################################################
271              
272             sub import
273             {
274 14     14   21932 &$handle_args;
275              
276             my @libz
277             = map
278             {
279             # HAK ALERT: the regex does nothing for security,
280             # just dodges -T.
281             #
282             # undef happens when nothing matches and the list
283             # returned is empty. this just leaves @libz empty.
284              
285 14         40 defined
286 17 50       132 ? m{ (.+) }xs
287             : ()
288             }
289             $find_libs->();
290              
291 14 100       89 say join "\n\t" => 'Found Libs:', @libz
292             if $verbose;
293              
294 14         70 my $caller = caller;
295              
296 14 50 66     120 if( $verbose || defined $argz{ print } )
297             {
298 14         111 local $\ = "\n";
299 14         39 local $, = "\n\t";
300              
301 14 100       67 say STDERR "Found */$argz{ base }:", @libz
302             if $verbose;
303             }
304              
305 14 50       81 if( $argz{ export } )
306             {
307             # this has to run in order to install variables that
308             # the caller is expecting to exist at runtime -- even
309             # if they are empty/undef at the end of it.
310              
311 14         64 my $ref = qualify_to_ref $argz{ export }, $caller;
312              
313 14 100       542 if( $verbose )
314             {
315 1         2 my $dest = qualify $argz{ export }, $caller;
316              
317             $argz{ scalar }
318 1 50       12 ? say STDERR "\nExporting: \$$dest"
319             : say STDERR "\nExporting: \@$dest"
320             ;
321             }
322              
323 14 50 50     172 if( $argz{ scalar } )
    50          
324             {
325 0 0       0 *$ref
326             = @libz
327             ? \$libz[0]
328             : \( my $a = '' )
329             ;
330             }
331             elsif
332             (
333             $argz{ append }
334             and
335 0         0 my $ary = *{ $ref }{ ARRAY }
336             )
337             {
338 0         0 push @$ary, @libz;
339             }
340             else
341             {
342 14         62 *$ref = \@libz
343             }
344             }
345              
346             # no 'else', these are not exclusive
347              
348 14 100       45 if( @libz )
349             {
350 12 50       46 if( defined $argz{ p5lib } )
351             {
352             # stuff the lib's found at the front of $ENV{ PERL5LIB }
353             # yes, virginia, substr is an lvalue -- and saner than
354             # dealing with \Q and a regex on arbitrary paths.
355              
356 0         0 ( substr $ENV{ PERL5LIB }, 0, 0 ) = join ':', @libz, '';
357              
358 0 0       0 say STDERR "\nUpdated PERL5LIB:\t$ENV{ PERL5LIB }"
359             if $verbose;
360             }
361              
362 12 100       42 if( $argz{ use } )
363             {
364             # this obviously won't work if lib ever depends
365             # on the caller's package.
366             #
367             # it does avoids issues with -T blowing up on the
368             # old eval technique.
369              
370 7         3563 require lib;
371              
372 7         5902 lib->import( @libz );
373             }
374             }
375              
376             0
377 14         4160 }
378              
379             # keep require happy
380              
381             1
382              
383             __END__