File Coverage

blib/lib/File/Next.pm
Criterion Covered Total %
statement 139 148 93.9
branch 63 74 85.1
condition 22 36 61.1
subroutine 18 18 100.0
pod 7 7 100.0
total 249 283 87.9


line stmt bran cond sub pod time code
1             package File::Next;
2              
3 13     13   183163 use strict;
  13         21  
  13         295  
4 13     13   40 use warnings;
  13         16  
  13         774  
5              
6             =head1 NAME
7              
8             File::Next - File-finding iterator
9              
10             =head1 VERSION
11              
12             Version 1.16
13              
14             =cut
15              
16             our $VERSION = '1.16';
17              
18             =head1 SYNOPSIS
19              
20             File::Next is a lightweight, taint-safe file-finding module.
21             It's lightweight and has no non-core prerequisites.
22              
23             use File::Next;
24              
25             my $files = File::Next::files( '/tmp' );
26              
27             while ( defined ( my $file = $files->() ) ) {
28             # do something...
29             }
30              
31             =head1 OPERATIONAL THEORY
32              
33             The two major functions, I and I, return an iterator
34             that will walk through a directory tree. The simplest use case is:
35              
36             use File::Next;
37              
38             my $iter = File::Next::files( '/tmp' );
39              
40             while ( defined ( my $file = $iter->() ) ) {
41             print $file, "\n";
42             }
43              
44             # Prints...
45             /tmp/foo.txt
46             /tmp/bar.pl
47             /tmp/baz/1
48             /tmp/baz/2.txt
49             /tmp/baz/wango/tango/purple.txt
50              
51             Note that only files are returned by C's iterator.
52             Directories are ignored.
53              
54             In list context, the iterator returns a list containing I<$dir>,
55             I<$file> and I<$fullpath>, where I<$fullpath> is what would get
56             returned in scalar context.
57              
58             The first parameter to any of the iterator factory functions may
59             be a hashref of options.
60              
61             =head1 ITERATORS
62              
63             For the three iterators, the \%options are optional.
64              
65             =head2 files( [ \%options, ] @starting_points )
66              
67             Returns an iterator that walks directories starting with the items
68             in I<@starting_points>. Each call to the iterator returns another
69             regular file.
70              
71             =head2 dirs( [ \%options, ] @starting_points )
72              
73             Returns an iterator that walks directories starting with the items
74             in I<@starting_points>. Each call to the iterator returns another
75             directory.
76              
77             =head2 everything( [ \%options, ] @starting_points )
78              
79             Returns an iterator that walks directories starting with the items
80             in I<@starting_points>. Each call to the iterator returns another
81             file, whether it's a regular file, directory, symlink, socket, or
82             whatever.
83              
84             =head2 from_file( [ \%options, ] $filename )
85              
86             Returns an iterator that iterates over each of the files specified
87             in I<$filename>. If I<$filename> is C<->, then the files are read
88             from STDIN.
89              
90             The files are assumed to be in the file one filename per line. If
91             I<$nul_separated> is passed, then the files are assumed to be
92             NUL-separated, as by C.
93              
94             If there are blank lines or empty filenames in the input stream,
95             they are ignored.
96              
97             Each filename is checked to see that it is a regular file or a named
98             pipe. If the file does not exists or is a directory, then a warning
99             is thrown to I, and the file is skipped.
100              
101             The following options have no effect in C: I,
102             I, I.
103              
104             =head1 SUPPORT FUNCTIONS
105              
106             =head2 sort_standard( $a, $b )
107              
108             A sort function for passing as a C option:
109              
110             my $iter = File::Next::files( {
111             sort_files => \&File::Next::sort_standard,
112             }, 't/swamp' );
113              
114             This function is the default, so the code above is identical to:
115              
116             my $iter = File::Next::files( {
117             sort_files => 1,
118             }, 't/swamp' );
119              
120             =head2 sort_reverse( $a, $b )
121              
122             Same as C, but in reverse.
123              
124             =head2 reslash( $path )
125              
126             Takes a path with all forward slashes and rebuilds it with whatever
127             is appropriate for the platform. For example 'foo/bar/bat' will
128             become 'foo\bar\bat' on Windows.
129              
130             This is really just a convenience function. I'd make it private,
131             but F wants it, too.
132              
133             =cut
134              
135             =head1 CONSTRUCTOR PARAMETERS
136              
137             =head2 file_filter -> \&file_filter
138              
139             The file_filter lets you check to see if it's really a file you
140             want to get back. If the file_filter returns a true value, the
141             file will be returned; if false, it will be skipped.
142              
143             The file_filter function takes no arguments but rather does its work through
144             a collection of variables.
145              
146             =over 4
147              
148             =item * C<$_> is the current filename within that directory
149              
150             =item * C<$File::Next::dir> is the current directory name
151              
152             =item * C<$File::Next::name> is the complete pathname to the file
153              
154             =back
155              
156             These are analogous to the same variables in L.
157              
158             my $iter = File::Next::files( { file_filter => sub { /\.txt$/ } }, '/tmp' );
159              
160             By default, the I is C, or "all files".
161              
162             This filter has no effect if your iterator is only returning directories.
163              
164             =head2 descend_filter => \&descend_filter
165              
166             The descend_filter lets you check to see if the iterator should
167             descend into a given directory. Maybe you want to skip F and
168             F<.svn> directories.
169              
170             my $descend_filter = sub { $_ ne "CVS" && $_ ne ".svn" }
171              
172             The descend_filter function takes no arguments but rather does its work through
173             a collection of variables.
174              
175             =over 4
176              
177             =item * C<$_> is the current filename of the directory
178              
179             =item * C<$File::Next::dir> is the complete directory name
180              
181             =back
182              
183             The descend filter is NOT applied to any directory names specified
184             as I<@starting_points> in the constructor. For example,
185              
186             my $iter = File::Next::files( { descend_filter => sub{0} }, '/tmp' );
187              
188             always descends into I, as you would expect.
189              
190             By default, the I is C, or "always descend".
191              
192             =head2 error_handler => \&error_handler
193              
194             If I is set, then any errors will be sent through
195             it. If the error is OS-related (ex. file not found, not permissions), the
196             native error code is passed as a second argument. By default, this value is
197             C. This function must NOT return.
198              
199             =head2 warning_handler => \&warning_handler
200              
201             If I is set, then any errors will be sent through
202             it. By default, this value is C. Unlike the
203             I, this function must return.
204              
205             =head2 sort_files => [ 0 | 1 | \&sort_sub]
206              
207             If you want files sorted, pass in some true value, as in
208             C<< sort_files => 1 >>.
209              
210             If you want a special sort order, pass in a sort function like
211             C<< sort_files => sub { $a->[1] cmp $b->[1] } >>.
212             Note that the parms passed in to the sub are arrayrefs, where $a->[0]
213             is the directory name, $a->[1] is the file name and $a->[2] is the
214             full path. Typically you're going to be sorting on $a->[2].
215              
216             =head2 follow_symlinks => [ 0 | 1 ]
217              
218             If set to false, the iterator will ignore any files and directories
219             that are actually symlinks. This has no effect on non-Unixy systems
220             such as Windows. By default, this is true.
221              
222             Note that this filter does not apply to any of the I<@starting_points>
223             passed in to the constructor.
224              
225             You should not set C<< follow_symlinks => 0 >> unless you specifically
226             need that behavior. Setting C<< follow_symlinks => 0 >> can be a
227             speed hit, because File::Next must check to see if the file or
228             directory you're about to follow is actually a symlink.
229              
230             =head2 nul_separated => [ 0 | 1 ]
231              
232             Used by the C iterator. Specifies that the files
233             listed in the input file are separated by NUL characters, as from
234             the C command with the C<-print0> argument.
235              
236             =cut
237              
238 13     13   44 use File::Spec ();
  13         336  
  13         1350  
239              
240             our $name; # name of the current file
241             our $dir; # dir of the current file
242              
243             our %files_defaults;
244             our %skip_dirs;
245              
246             BEGIN {
247             %files_defaults = (
248             file_filter => undef,
249             descend_filter => undef,
250 3         34 error_handler => sub { CORE::die $_[0] },
251 1         19 warning_handler => sub { CORE::warn @_ },
252 13     13   147 sort_files => undef,
253             follow_symlinks => 1,
254             nul_separated => 0,
255             );
256 13         125 %skip_dirs = map {($_,1)} (File::Spec->curdir, File::Spec->updir);
  26         14400  
257             }
258              
259              
260             sub files {
261 19 100 33 19 1 493225 die _bad_invocation() if @_ && defined($_[0]) && ($_[0] eq __PACKAGE__);
      66        
262              
263 17         53 my ($parms,@queue) = _setup( \%files_defaults, @_ );
264              
265             return sub {
266 210     210   5817 my $filter = $parms->{file_filter};
267 210         296 while (@queue) {
268 249         405 my ($dirname,$file,$fullpath) = splice( @queue, 0, 3 );
269 249 50 66     2036 if ( -f $fullpath || -p _ || $fullpath =~ m{^/dev/fd} ) {
      66        
270 205 100       242 if ( $filter ) {
271 20         28 local $_ = $file;
272 20         17 local $File::Next::dir = $dirname;
273 20         17 local $File::Next::name = $fullpath;
274 20 100       24 next if not $filter->();
275             }
276 196 100       982 return wantarray ? ($dirname,$file,$fullpath) : $fullpath;
277             }
278 44 50       86 if ( -d _ ) {
279 44         70 unshift( @queue, _candidate_files( $parms, $fullpath ) );
280             }
281             } # while
282              
283 14         22 return;
284 17         91 }; # iterator
285             }
286              
287              
288             sub dirs {
289 5 100 33 5 1 68977 die _bad_invocation() if @_ && defined($_[0]) && ($_[0] eq __PACKAGE__);
      66        
290              
291 3         9 my ($parms,@queue) = _setup( \%files_defaults, @_ );
292              
293             return sub {
294 6     6   392 while (@queue) {
295 44         43 my (undef,undef,$fullpath) = splice( @queue, 0, 3 );
296 44 100       643 if ( -d $fullpath ) {
297 5         8 unshift( @queue, _candidate_files( $parms, $fullpath ) );
298 5         18 return $fullpath;
299             }
300             } # while
301              
302 1         2 return;
303 2         18 }; # iterator
304             }
305              
306             sub everything {
307 5 100 33 5 1 132248 die _bad_invocation() if @_ && defined($_[0]) && ($_[0] eq __PACKAGE__);
      66        
308              
309 3         10 my ($parms,@queue) = _setup( \%files_defaults, @_ );
310              
311             return sub {
312 55     55   704 my $filter = $parms->{file_filter};
313 55         71 while (@queue) {
314 88         283 my ($dirname,$file,$fullpath) = splice( @queue, 0, 3 );
315 88 100       537 if ( -d $fullpath ) {
316 10         13 unshift( @queue, _candidate_files( $parms, $fullpath ) );
317             }
318 88 100       161 if ( $filter ) {
319 44         54 local $_ = $file;
320 44         39 local $File::Next::dir = $dirname;
321 44         28 local $File::Next::name = $fullpath;
322 44 100       51 next if not $filter->();
323             }
324 53 50       139 return wantarray ? ($dirname,$file,$fullpath) : $fullpath;
325             } # while
326              
327 2         7 return;
328 3         16 }; # iterator
329             }
330              
331             sub from_file {
332 6 50 66 6 1 94265 die _bad_invocation() if @_ && defined($_[0]) && ($_[0] eq __PACKAGE__);
      66        
333              
334 6         20 my ($parms,@queue) = _setup( \%files_defaults, @_ );
335 6         10 my $err = $parms->{error_handler};
336 6         7 my $warn = $parms->{warning_handler};
337              
338 6         7 my $filename = $queue[1];
339              
340 6 100       13 if ( !defined($filename) ) {
341 1         3 $err->( 'Must pass a filename to from_file()' );
342 0         0 return undef;
343             }
344              
345 5         5 my $fh;
346 5 50       9 if ( $filename eq '-' ) {
347 0         0 $fh = \*STDIN;
348             }
349             else {
350 5 100       132 if ( !open( $fh, '<', $filename ) ) {
351 1         14 $err->( "Unable to open $filename: $!", $! + 0 );
352 0         0 return undef;
353             }
354             }
355              
356             return sub {
357 68     68   1495 my $filter = $parms->{file_filter};
358 68 100       167 local $/ = $parms->{nul_separated} ? "\x00" : $/;
359 68         307 while ( my $fullpath = <$fh> ) {
360 67         93 chomp $fullpath;
361 67 100       165 next unless $fullpath =~ /./;
362 66 100 66     601 if ( not ( -f $fullpath || -p _ ) ) {
363 2         9 $warn->( "$fullpath: No such file" );
364 2         21 next;
365             }
366              
367 64         522 my ($volume,$dirname,$file) = File::Spec->splitpath( $fullpath );
368 64 50       105 if ( $filter ) {
369 0         0 local $_ = $file;
370 0         0 local $File::Next::dir = $dirname;
371 0         0 local $File::Next::name = $fullpath;
372 0 0       0 next if not $filter->();
373             }
374 64 50       244 return wantarray ? ($dirname,$file,$fullpath) : $fullpath;
375             } # while
376 4         26 close $fh;
377              
378 4         10 return;
379 4         27 }; # iterator
380             }
381              
382             sub _bad_invocation {
383 6     6   28 my $good = (caller(1))[3];
384 6         12 my $bad = $good;
385 6         44 $bad =~ s/(.+)::/$1->/;
386 6         48 return "$good must not be invoked as $bad";
387             }
388              
389 92     92 1 111 sub sort_standard($$) { return $_[0]->[1] cmp $_[1]->[1] } ## no critic (ProhibitSubroutinePrototypes)
390 91     91 1 106 sub sort_reverse($$) { return $_[1]->[1] cmp $_[0]->[1] } ## no critic (ProhibitSubroutinePrototypes)
391              
392             sub reslash {
393 695     695 1 42700 my $path = shift;
394              
395 695         917 my @parts = split( /\//, $path );
396              
397 695 100       971 return $path if @parts < 2;
398              
399 616         2886 return File::Spec->catfile( @parts );
400             }
401              
402              
403             =head1 PRIVATE FUNCTIONS
404              
405             =head2 _setup( $default_parms, @whatever_was_passed_to_files() )
406              
407             Handles all the scut-work for setting up the parms passed in.
408              
409             Returns a hashref of operational options, combined between
410             I<$passed_parms> and I<$defaults>, plus the queue.
411              
412             The queue prep stuff takes the strings in I<@starting_points> and
413             puts them in the format that queue needs.
414              
415             The C<@queue> that gets passed around is an array that has three
416             elements for each of the entries in the queue: $dir, $file and
417             $fullpath. Items must be pushed and popped off the queue three at
418             a time (spliced, really).
419              
420             =cut
421              
422             sub _setup {
423 29     29   32 my $defaults = shift;
424 29 100       87 my $passed_parms = ref $_[0] eq 'HASH' ? {%{+shift}} : {}; # copy parm hash
  14         46  
425              
426 29         37 my %passed_parms = %{$passed_parms};
  29         93  
427              
428 29         34 my $parms = {};
429 29         32 for my $key ( keys %{$defaults} ) {
  29         118  
430             $parms->{$key} =
431             exists $passed_parms{$key}
432             ? delete $passed_parms{$key}
433 203 100       402 : $defaults->{$key};
434             }
435              
436             # Any leftover keys are bogus
437 29         63 for my $badkey ( keys %passed_parms ) {
438 2         13 my $sub = (caller(1))[3];
439 2         7 $parms->{error_handler}->( "Invalid option passed to $sub(): $badkey" );
440             }
441              
442             # If it's not a code ref, assume standard sort
443 28 100 100     94 if ( $parms->{sort_files} && ( ref($parms->{sort_files}) ne 'CODE' ) ) {
444 1         2 $parms->{sort_files} = \&sort_standard;
445             }
446 28         29 my @queue;
447              
448 28         51 for ( @_ ) {
449 29         54 my $start = reslash( $_ );
450 29 100       428 if (-d $start) {
451 22         61 push @queue, ($start,undef,$start);
452             }
453             else {
454 7         19 push @queue, (undef,$start,$start);
455             }
456             }
457              
458 28         129 return ($parms,@queue);
459             }
460              
461             =head2 _candidate_files( $parms, $dir )
462              
463             Pulls out the files/dirs that might be worth looking into in I<$dir>.
464             If I<$dir> is the empty string, then search the current directory.
465              
466             I<$parms> is the hashref of parms passed into File::Next constructor.
467              
468             =cut
469              
470             sub _candidate_files {
471 59     59   65 my $parms = shift;
472 59         54 my $dirname = shift;
473              
474 59         46 my $dh;
475 59 50       1059 if ( !opendir $dh, $dirname ) {
476 0         0 $parms->{error_handler}->( "$dirname: $!", $! + 0 );
477 0         0 return;
478             }
479              
480 59         56 my @newfiles;
481 59         63 my $descend_filter = $parms->{descend_filter};
482 59         55 my $follow_symlinks = $parms->{follow_symlinks};
483 59         48 my $sort_sub = $parms->{sort_files};
484              
485 59         595 for my $file ( grep { !exists $skip_dirs{$_} } readdir $dh ) {
  497         602  
486 379         240 my $has_stat;
487              
488 379         1388 my $fullpath = File::Spec->catdir( $dirname, $file );
489 379 100       614 if ( !$follow_symlinks ) {
490 12 100       96 next if -l $fullpath;
491 10         7 $has_stat = 1;
492             }
493              
494             # Only do directory checking if we have a descend_filter
495 377 100       446 if ( $descend_filter ) {
496 36 50       264 if ( $has_stat ? (-d _) : (-d $fullpath) ) {
    100          
497 6         8 local $File::Next::dir = $fullpath;
498 6         9 local $_ = $file;
499 6 100       14 next if not $descend_filter->();
500             }
501             }
502 374 100       1785 if ( $sort_sub ) {
503 78         176 push( @newfiles, [ $dirname, $file, $fullpath ] );
504             }
505             else {
506 296         503 push( @newfiles, $dirname, $file, $fullpath );
507             }
508             }
509 59         425 closedir $dh;
510              
511 59 100       95 if ( $sort_sub ) {
512 13         25 return map { @{$_} } sort $sort_sub @newfiles;
  78         51  
  78         249  
513             }
514              
515 46         479 return @newfiles;
516             }
517              
518             =head1 DIAGNOSTICS
519              
520             =over
521              
522             =item C<< File::Next::files must not be invoked as File::Next->files >>
523              
524             =item C<< File::Next::dirs must not be invoked as File::Next->dirs >>
525              
526             =item C<< File::Next::everything must not be invoked as File::Next->everything >>
527              
528             =back
529              
530             The interface functions do not allow for the method invocation syntax and
531             throw errors with the messages above. You can work around this limitation
532             with L.
533              
534             for my $file_system_feature (qw(dirs files)) {
535             my $iterator = File::Next->can($file_system_feature)->($options, $target_directory);
536             while (defined(my $name = $iterator->())) {
537             # ...
538             }
539             }
540              
541             =head1 SPEED TWEAKS
542              
543             =over 4
544              
545             =item * Don't set C<< follow_symlinks => 0 >> unless you need it.
546              
547             =back
548              
549             =head1 AUTHOR
550              
551             Andy Lester, C<< >>
552              
553             =head1 BUGS
554              
555             Please report any bugs or feature requests to
556             L.
557              
558             Note that File::Next does NOT use L for bug tracking.
559              
560             =head1 SUPPORT
561              
562             You can find documentation for this module with the perldoc command.
563              
564             perldoc File::Next
565              
566             You can also look for information at:
567              
568             =over 4
569              
570             =item * File::Next's bug queue
571              
572             L
573              
574             =item * AnnoCPAN: Annotated CPAN documentation
575              
576             L
577              
578             =item * CPAN Ratings
579              
580             L
581              
582             =item * Search CPAN
583              
584             L
585              
586             =item * Source code repository
587              
588             L
589              
590             =back
591              
592             =head1 ACKNOWLEDGEMENTS
593              
594             All file-finding in this module is adapted from Mark Jason Dominus'
595             marvelous I, page 126.
596              
597             Thanks also for bug fixes and typo finding to
598             Gerhard Poul,
599             Brian Fraser,
600             Todd Rinaldo,
601             Bruce Woodward,
602             Christopher J. Madsen,
603             Bernhard Fisseni
604             and Rob Hoelz.
605              
606             =head1 COPYRIGHT & LICENSE
607              
608             Copyright 2005-2016 Andy Lester.
609              
610             This program is free software; you can redistribute it and/or modify
611             it under the terms of the Artistic License version 2.0.
612              
613             =cut
614              
615             1; # End of File::Next