File Coverage

blib/lib/File/SearchPath.pm
Criterion Covered Total %
statement 77 78 98.7
branch 35 46 76.0
condition 12 18 66.6
subroutine 11 11 100.0
pod 1 1 100.0
total 136 154 88.3


line stmt bran cond sub pod time code
1             package File::SearchPath;
2              
3             =head1 NAME
4              
5             File::SearchPath - Search for a file in an environment variable path
6              
7             =head1 SYNOPSIS
8              
9             use File::SearchPath qw/ searchpath /;
10              
11             $file = searchpath( 'libperl.a', env => 'LD_LIBRARY_PATH' );
12             $file = searchpath( 'my.cfg', env => 'CFG_DIR', subdir => 'ME' );
13              
14             $path = searchpath( $file, env => 'PATH', exe => 1 );
15             $path = searchpath( $file, env => 'PATH', dir => 1 );
16              
17             $file = searchpath( 'ls', $ENV{PATH} );
18              
19             $exe = searchpath( 'ls' );
20              
21             =head1 DESCRIPTION
22              
23             This module provides the ability to search a path-like environment
24             variable for a file (that does not necessarily have to be an
25             executable).
26              
27             =cut
28              
29 1     1   646 use 5.006;
  1         3  
  1         33  
30 1     1   4 use Carp;
  1         2  
  1         66  
31 1     1   12 use warnings;
  1         2  
  1         35  
32 1     1   4 use strict;
  1         1  
  1         36  
33              
34 1     1   3 use base qw/ Exporter /;
  1         1  
  1         82  
35 1     1   5 use vars qw/ $VERSION @EXPORT_OK /;
  1         1  
  1         51  
36              
37 1     1   4 use File::Spec;
  1         1  
  1         19  
38 1     1   4 use Config;
  1         1  
  1         600  
39              
40             $VERSION = '0.07';
41              
42             @EXPORT_OK = qw( searchpath );
43              
44             =head1 FUNCTIONS
45              
46             The following functions can be exported by this module.
47              
48             =over 4
49              
50             =item B
51              
52             This is the core function. The only mandatory argument is the name of
53             a file to be located. The filename should not be absolute although it
54             can include directory specifications.
55              
56             $path = searchpath( $file );
57             @matches = searchpath( $file );
58              
59             If only two arguments are provided, it is assumed that the second
60             argument is a path-like string. This interface is provided for
61             backwards compatibility with C version 0.01. It is not
62             as portable as specifying the name of the environment variable. Note also
63             that no specific attempt will be made to check whether the file is
64             executable when the subroutine is called in this way.
65              
66             $path = searchpath( $file, $ENV{PATH} );
67              
68             By default, this will search in $PATH for executable files and is
69             equivalent to:
70              
71             $path = searchpath( $file, env => 'PATH', exe => 0 );
72              
73             Hash-like options can be used to alter the behaviour of the
74             search:
75              
76             =over 8
77              
78             =item env
79              
80             Name of the environment variable to use as a starting point for the
81             search. Should be a path-like environment variable such as $PATH,
82             $LD_LIBRARY_PATH etc. Defaults to $PATH. An error occurs if the
83             environment variable is not set or not defined. If it is defined but
84             contains a blank string, the current directory will be assumed.
85              
86             =item exe
87              
88             If true, only executable files will be located in the search path.
89             If $PATH is being searched, the default is for this to be true. For all
90             other environment variables the default is false. If "dir" option
91             is specified "exe" will always default to false.
92              
93             =item dir
94              
95             If true, only directories will be located in the search path. Default
96             is false. "dir" and "exe" are not allowed to be true in the same
97             call. (triggering a croak() on error).
98              
99             =item subdir
100              
101             If you know that your file is in a subdirectory of the path described
102             by the environment variable, this direcotry can be specified here.
103             Alternatively, the path can be included in the file name itself.
104              
105             =back
106              
107             In scalar context the first match is returned. In list context all
108             matches are returned in the order corresponding to the directories
109             listed in the environment variable.
110              
111             Returns undef (or empty list) if no match could be found.
112              
113             If an absolute file name is provided, that filename is returned if it
114             exists and is readable, else undef is returned.
115              
116             =cut
117              
118             sub searchpath {
119 10     10 1 12987 my $file = shift;
120              
121             # read our arguments and assign defaults. Behaviour depends on whether
122             # we have a single argument remaining or not.
123 10         15 my %options;
124              
125             # If we only have one more argument then it must be the contents
126             # of a path variable
127             my $path_contents;
128 10 100       24 if ( scalar(@_) == 1) {
129             # Read the contents and store in options hash
130             # along with the backwards compatibility behaviour
131 1         9 %options = ( contents => shift,
132             exe => 0,
133             dir => 0,
134             subdir => File::Spec->curdir,
135             );
136              
137             } else {
138              
139             # options handling since we have zero or more than one argument.
140             # set up the default behaviour
141             # The exe() defaulting is env dependent
142 9         52 my %defaults = ( env => 'PATH', subdir => File::Spec->curdir );
143              
144 9         29 %options = ( %defaults, @_ );
145              
146             # if we specify a dir option then we default to no exe regardless
147             # of PATH
148 9 100 66     39 if (!exists $options{exe} && !exists $options{dir}) {
149             # exe was not specified
150 4 50       11 $options{exe} = ( $options{env} eq 'PATH' ? 1 : 0 );
151             }
152              
153 9 50 66     39 croak "Both exe and dir options were set in call to searchpath()"
154             if ($options{exe} && $options{dir});
155              
156             }
157              
158             # check for absolute file name and behave accordingly
159 10 100       60 if (File::Spec->file_name_is_absolute( $file )) {
160 1 50       5 return (_file_ok($file, $options{exe}, $options{dir}) ? $file : () );
161             }
162              
163             # if exe is true we can simply use Env::Path directly. It doesn't
164             # really simplify any code though since we still have to write
165             # the other search
166              
167             # first get the search directories from the path variable
168 9         31 my @searchdirs = _env_to_dirs( $options{env}, $options{contents} );
169              
170             # Now do the looping
171 9         91 my @matches;
172              
173 9         16 for my $d (@searchdirs) {
174             # blank means current directory
175 19 50       30 $d = File::Spec->curdir unless $d;
176              
177             # Create the filename
178 19         18 my $testfile;
179 19 100       29 if ($options{dir}) {
180 2         10 $testfile = File::Spec->catdir( $d, $options{subdir}, $file);
181             } else {
182 17         133 $testfile = File::Spec->catfile( $d, $options{subdir}, $file);
183             }
184              
185             # does the file exist?
186 19 100       49 next unless _file_ok( $testfile, $options{exe}, $options{dir} );
187              
188             # File looks to be found store it
189 10         21 push(@matches, $testfile);
190              
191             # if we are in a scalar context we do not need to keep on looking
192 10 100       24 last unless wantarray();
193              
194             }
195              
196             # return the result
197 9 100       16 if (wantarray) {
198 3         15 return @matches;
199             } else {
200 6         48 return $matches[0];
201             }
202             }
203              
204             =back
205              
206             =begin __PRIVATE__FUNCTIONS__
207              
208             =head2 Private Functions
209              
210             =over 4
211              
212             =item B<_env_to_dirs>
213              
214             Given an environment variable, splits it into chunks and returns
215             the list of directories to be searched.
216              
217             If Env::Path is installed, it is used since it understands a more
218             varied set of path delimiters, otherwise the variable is split on
219             the value of $Config{path_sep}.
220              
221             @dirs = _env_to_dirs( 'PATH' );
222              
223             Also, we can pass in the actual contents as a second argument. In this
224             case it is only read if the first is undef.
225              
226             @dirs = _env_to_dirs( undef, 'dir1:dir2' );
227              
228             =cut
229              
230             sub _env_to_dirs {
231 9     9   13 my $var = shift;
232 9         17 my $contents = shift;
233              
234 9 50 66     23 if (!defined $var && !defined $contents) {
235 0         0 croak "Error extracting directories from environment. No defined values supplied. Internal programming error";
236             }
237              
238             # behaviour now depends on whether we were given the actual
239             # contents or the name of the variable. Variable name trumps contents.
240              
241 9 100       17 if (defined $var) {
242              
243 8 50       21 croak "Environment variable $var is not defined. Unable to search it\n"
244             if !exists $ENV{$var};
245              
246 8 50       21 croak "Environment variable does exist but it is not defined. Unable to search it\n"
247             unless defined $ENV{$var};
248             }
249 9         10 my $use_env_path;
250             {
251             # Localise $@ so that we can use this command from perldl shell
252 9         6 local $@;
  9         9  
253 9         11 eval { require Env::Path };
  9         50  
254 9 50       20 $use_env_path = ( $@ ? 0 : 1 );
255             }
256 9 100 66     39 if (!$use_env_path || defined $contents) {
257             # no Env::Path so we just split on :
258 1 50       4 my $path = (defined $contents? $contents : $ENV{$var});
259 1         11 my $ps = $Config{path_sep};
260 1         23 return split(/\Q$ps\E/, $path);
261             } else {
262 8         35 my $path = Env::Path->$var;
263 8         136 return $path->List;
264             }
265             }
266              
267             =item B<_file_ok>
268              
269             Tests the file for existence, fileness and readability.
270              
271             $isthere = _file_ok( $file );
272              
273             Returns true if the file passes.
274              
275             An optional argument can be used to add a test for exectuableness.
276              
277             $isthere_and_exe = _file_ok( $file, 1 );
278              
279             An additional optional argument can be used to add a test for
280             directory as opposed to file existence.
281              
282             $isthere_and_dir = _file_ok( $dir, 0, 1 );
283              
284             =cut
285              
286             sub _file_ok {
287 20     20   23 my $testfile = shift;
288 20         19 my $testexe = shift;
289 20         21 my $testdir = shift;
290              
291             # do not allow both dir and exe flags
292 20 50 66     51 return 0 if ($testexe && $testdir);
293              
294 20 100       267 return unless -e $testfile;
295 13 50       96 return unless -r $testfile;
296              
297 13 100       31 if ($testdir) {
    100          
298 2         18 return (-d $testfile);
299             } elsif ($testexe) {
300 2   66     49 return (-f $testfile && -x $testfile);
301             } else {
302 9         75 return (-f $testfile);
303             }
304             }
305              
306              
307             =end __PRIVATE__FUNCTIONS__
308              
309             =head1 HISTORY
310              
311             C used to exist on CPAN (now on backpan) and was
312             written by Robert Spier. This version is completely new but retains
313             an interface that is compatible with Robert's version. Thanks to
314             Robert for allowing me to reuse this module name.
315              
316             =head1 NOTES
317              
318             If C module is installed it will be used. This allows for
319             more flexibility than simply assuming colon-separated paths.
320              
321             =head1 SEE ALSO
322              
323             L, L, L, L,
324             L.
325              
326             =head1 AUTHOR
327              
328             Tim Jenness Etjenness@cpan.orgE
329              
330             Copyright (C) 2005,2006, 2008 Particle Physics and Astronomy Research Council.
331             Copyright (C) 2009-2010 Science and Technology Facilities Council.
332             Copyright (C) 2015 Tim Jenness
333             All Rights Reserved.
334              
335             This program is free software; you can redistribute it and/or modify it under
336             the terms of the GNU General Public License as published by the Free Software
337             Foundation; either version 2 of the License, or (at your option) any later
338             version.
339              
340             This program is distributed in the hope that it will be useful,but WITHOUT ANY
341             WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
342             PARTICULAR PURPOSE. See the GNU General Public License for more details.
343              
344             You should have received a copy of the GNU General Public License along with
345             this program; if not, write to the Free Software Foundation, Inc., 59 Temple
346             Place,Suite 330, Boston, MA 02111-1307, USA
347              
348             =cut
349              
350             1;