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