File Coverage

blib/lib/FastGlob.pm
Criterion Covered Total %
statement 71 78 91.0
branch 18 28 64.2
condition n/a
subroutine 6 6 100.0
pod 0 2 0.0
total 95 114 83.3


line stmt bran cond sub pod time code
1             #!/usr/local/bin/perl
2             package FastGlob;
3              
4             require 5.005;
5              
6             # ABSTRACT: A faster glob() implementation
7              
8             BEGIN {
9 1     1   67860 our $VERSION = '1.4'; # VERSION: generated by DZP::OurPkgVersion
10             }
11              
12              
13              
14 1     1   18 use Exporter ();
  1         3  
  1         56  
15              
16             @ISA = qw(Exporter);
17             @EXPORT = qw(&glob);
18             @EXPORT_OK = qw(dirsep rootpat curdir parentidr hidedotfiles);
19              
20 1     1   29 use 5.004;
  1         4  
21 1     1   7 use strict; # be good
  1         2  
  1         1011  
22              
23             # platform specifics
24              
25             our $dirsep = '/';
26             our $rootpat= '\A\Z';
27             our $curdir = '.';
28             our $parentdir = '..';
29             our $hidedotfiles = 1;
30             our $verbose = 0;
31              
32             #
33             # recursively wildcard expand a list of strings
34             #
35              
36             sub glob($) {
37              
38 18     18 0 28205 my @res;
39             my $part;
40 18         0 my $found1;
41 18         0 my $out;
42 18         68 my $bracepat = qr(\{([^\{\}]*)\});
43              
44             # deal with {xxx,yyy,zzz}
45 18         37 @res = ();
46 18         28 $found1 = 1;
47 18         42 while ($found1) {
48 22         30 $found1 = 0;
49 22         38 for (@_) {
50 29 100       116 if ( m{$bracepat} ) {
51 5         21 foreach $part (split(',',$1)) {
52 11         68 $out = $_;
53 11         61 $out =~ s/$bracepat/$part/;
54 11         27 push(@res, $out);
55             }
56 5         12 $found1 = 1;
57             } else {
58 24         54 push(@res, $_);
59             }
60             }
61 22         51 @_ = @res;
62 22         49 @res = ();
63             }
64              
65              
66 18         33 for (@_) {
67             # check for and do tilde expansion
68 24 50       94 if ( /^\~([^${dirsep}]*)/ ) {
69 0         0 my $usr = $1;
70 0 0       0 my $usrdir = ( ($1 eq "") ? getpwuid($<) : getpwnam($usr) )[7];
71 0 0       0 if ($usrdir ne "" ) {
72 0         0 s/^\~\Q$usr\E/$usrdir/;
73 0         0 push(@res, $_);
74             }
75             } else {
76 24         53 push(@res, $_);
77             }
78             }
79 18         44 @_ = @res;
80 18         25 @res = ();
81              
82 18         34 for (@_) {
83             # if there's no wildcards, just return it
84 24 100       120 unless (/(^|[^\\])[*?\[\]{}]/) {
85 5         10 push (@res, $_);
86 5         9 next;
87             }
88              
89             # Make the glob into a regexp
90             # escape + , and |
91 19         116 s/([+.|])/\\$1/go;
92              
93             # handle * and ?
94 19         86 s/(?
95 19         50 s/(?
96              
97             # deal with dot files
98 19 50       52 if ( $hidedotfiles ) {
99 19         96 s/(\A|$dirsep)\.\*/$1(?:[^.].*)?/go;
100 19         80 s/(\A|$dirsep)\./$1\[\^.\]/go;
101 19         61 s/(\A|$dirsep)\[\^([^].]*)\]/$1\[\^\\.$2\]/go;
102             }
103              
104             # debugging
105 19 50       38 print "regexp is $_\n" if ($verbose);
106              
107             # now split it into directory components
108 19         222 my @comps = split($dirsep);
109              
110 19 100       91 if ( $comps[0] =~ /($rootpat)/ ) {
111 3         6 shift(@comps);
112 3         14 push(@res, &recurseglob( "$1$dirsep", "$1$dirsep" , @comps ));
113             }
114             else {
115 16         45 push(@res, &recurseglob( $curdir, '' , @comps ));
116             }
117             }
118 18         104 return sort(@res);
119             }
120              
121             sub recurseglob($ $ @) {
122 29     29 0 86 my($dir, $dirname, @comps) = @_;
123 29         46 my(@res) = ();
124 29         41 my($re, $anymatches, @names);
125              
126              
127 29 50       75 if ( @comps == 0 ) {
    100          
128             # bottom of recursion, just return the path
129 0         0 chop($dirname); # always has gratiutous trailing slash
130 0         0 @res = ($dirname);
131             } elsif ($comps[0] eq '') {
132 1         2 shift(@comps);
133 1         6 unshift(@res, &recurseglob( "$dir$dirsep",
134             "$dirname$dirsep",
135             @comps ));
136             } else {
137 28         76 $re = '\A' . shift(@comps) . '\Z';
138              
139             # slurp in the directory
140 28         699 opendir(HANDLE, $dir);
141 28         1366 @names = readdir(HANDLE);
142 28         272 closedir(HANDLE);
143              
144             # look for matches, and if you find one, glob the rest of the
145             # components. We eval the loop so the regexp gets compiled in,
146             # making searches on large directories faster.
147 28         61 $anymatches = 0;
148 28 50       64 print "component re is qr($re)\n" if ($verbose);
149 28         475 my $regex = qr($re);
150 28         85 foreach (@names) {
151 526 50       766 print "considering |$_|\n" if ($verbose);
152 526 100       1281 if ( m{$regex} ) {
153 54 100       112 if ( $#comps > -1 ) {
154 9         47 unshift(@res, &recurseglob( "$dir$dirsep$_",
155             "$dirname$_$dirsep",
156             @comps ));
157             } else {
158 45         92 unshift(@res, "$dirname$_" );
159             }
160 54         88 $anymatches = 1;
161             }
162             }
163             }
164 29         131 return @res;
165             }
166              
167             1;
168              
169             __END__