Once again syncing after too long an absence
[p5sagit/p5-mst-13.2.git] / lib / File / DosGlob.pm
CommitLineData
08aa1457 1#!perl -w
2
0e06870b 3# use strict fails
4#Can't use string ("main::glob") as a symbol ref while "strict refs" in use at /usr/lib/perl5/5.005/File/DosGlob.pm line 191.
5
08aa1457 6#
7# Documentation at the __END__
8#
9
10package File::DosGlob;
11
0e06870b 12our $VERSION = '1.00';
13use strict;
14
08aa1457 15sub doglob {
16 my $cond = shift;
17 my @retval = ();
18 #print "doglob: ", join('|', @_), "\n";
19 OUTER:
0e06870b 20 for my $pat (@_) {
08aa1457 21 my @matched = ();
22 my @globdirs = ();
23 my $head = '.';
24 my $sepchr = '/';
0e06870b 25 my $tail;
26 next OUTER unless defined $pat and $pat ne '';
08aa1457 27 # if arg is within quotes strip em and do no globbing
0e06870b 28 if ($pat =~ /^"(.*)"\z/s) {
29 $pat = $1;
30 if ($cond eq 'd') { push(@retval, $pat) if -d $pat }
31 else { push(@retval, $pat) if -e $pat }
08aa1457 32 next OUTER;
33 }
4dd406c2 34 # wildcards with a drive prefix such as h:*.pm must be changed
35 # to h:./*.pm to expand correctly
0e06870b 36 if ($pat =~ m|^([A-Za-z]:)[^/\\]|s) {
4dd406c2 37 substr($_,0,2) = $1 . "./";
38 }
0e06870b 39 if ($pat =~ m|^(.*)([\\/])([^\\/]*)\z|s) {
08aa1457 40 ($head, $sepchr, $tail) = ($1,$2,$3);
41 #print "div: |$head|$sepchr|$tail|\n";
0e06870b 42 push (@retval, $pat), next OUTER if $tail eq '';
08aa1457 43 if ($head =~ /[*?]/) {
44 @globdirs = doglob('d', $head);
45 push(@retval, doglob($cond, map {"$_$sepchr$tail"} @globdirs)),
46 next OUTER if @globdirs;
47 }
4dd406c2 48 $head .= $sepchr if $head eq '' or $head =~ /^[A-Za-z]:\z/s;
0e06870b 49 $pat = $tail;
08aa1457 50 }
51 #
52 # If file component has no wildcards, we can avoid opendir
0e06870b 53 unless ($pat =~ /[*?]/) {
08aa1457 54 $head = '' if $head eq '.';
55 $head .= $sepchr unless $head eq '' or substr($head,-1) eq $sepchr;
0e06870b 56 $head .= $pat;
08aa1457 57 if ($cond eq 'd') { push(@retval,$head) if -d $head }
58 else { push(@retval,$head) if -e $head }
59 next OUTER;
60 }
61 opendir(D, $head) or next OUTER;
62 my @leaves = readdir D;
63 closedir D;
64 $head = '' if $head eq '.';
65 $head .= $sepchr unless $head eq '' or substr($head,-1) eq $sepchr;
66
67 # escape regex metachars but not glob chars
0e06870b 68 $pat =~ s:([].+^\-\${}[|]):\\$1:g;
08aa1457 69 # and convert DOS-style wildcards to regex
0e06870b 70 $pat =~ s/\*/.*/g;
71 $pat =~ s/\?/.?/g;
08aa1457 72
0e06870b 73 #print "regex: '$pat', head: '$head'\n";
74 my $matchsub = sub { $_[0] =~ m|^$pat\z|is };
08aa1457 75 INNER:
76 for my $e (@leaves) {
77 next INNER if $e eq '.' or $e eq '..';
78 next INNER if $cond eq 'd' and ! -d "$head$e";
79 push(@matched, "$head$e"), next INNER if &$matchsub($e);
80 #
81 # [DOS compatibility special case]
82 # Failed, add a trailing dot and try again, but only
83 # if name does not have a dot in it *and* pattern
84 # has a dot *and* name is shorter than 9 chars.
85 #
86 if (index($e,'.') == -1 and length($e) < 9
0e06870b 87 and index($pat,'\\.') != -1) {
08aa1457 88 push(@matched, "$head$e"), next INNER if &$matchsub("$e.");
89 }
90 }
91 push @retval, @matched if @matched;
92 }
93 return @retval;
94}
95
96#
fb73857a 97# this can be used to override CORE::glob in a specific
98# package by saying C<use File::DosGlob 'glob';> in that
99# namespace.
08aa1457 100#
fb73857a 101
102# context (keyed by second cxix arg provided by core)
103my %iter;
104my %entries;
105
106sub glob {
0e06870b 107 my($pat,$cxix) = @_;
163d180b 108 my @pat;
fb73857a 109
110 # glob without args defaults to $_
111 $pat = $_ unless defined $pat;
112
163d180b 113 # extract patterns
114 if ($pat =~ /\s/) {
115 require Text::ParseWords;
116 @pat = Text::ParseWords::parse_line('\s+',0,$pat);
117 }
118 else {
119 push @pat, $pat;
120 }
121
0e06870b 122 # Mike Mestnik: made to do abc{1,2,3} == abc1 abc2 abc3.
123 # abc3 will be the original {3} (and drop the {}).
124 # abc1 abc2 will be put in @appendpat.
125 # This was just the esiest way, not nearly the best.
126 REHASH: {
127 my @appendpat = ();
128 for (@pat) {
129 # There must be a "," I.E. abc{efg} is not what we want.
130 while ( /^(.*)(?<!\\)\{(.*?)(?<!\\)\,.*?(?<!\\)\}(.*)$/ ) {
131 my ($start, $match, $end) = ($1, $2, $3);
132 #print "Got: \n\t$start\n\t$match\n\t$end\n";
133 my $tmp = "$start$match$end";
134 while ( $tmp =~ s/^(.*?)(?<!\\)\{(?:.*(?<!\\)\,)?(.*\Q$match\E.*?)(?:(?<!\\)\,.*)?(?<!\\)\}(.*)$/$1$2$3/ ) {
135 #print "Striped: $tmp\n";
136 # these expanshions will be preformed by the original,
137 # when we call REHASH.
138 }
139 push @appendpat, ("$tmp");
140 s/^\Q$start\E(?<!\\)\{\Q$match\E(?<!\\)\,/$start\{/;
141 if ( /^\Q$start\E(?<!\\)\{(?!.*?(?<!\\)\,.*?\Q$end\E$)(.*)(?<!\\)\}\Q$end\E$/ ) {
142 $match = $1;
143 #print "GOT: \n\t$start\n\t$match\n\t$end\n\n";
144 $_ = "$start$match$end";
145 }
146 }
147 #print "Sould have "GOT" vs "Got"!\n";
148 #FIXME: There should be checking for this.
149 # How or what should be done about failure is beond me.
150 }
151 if ( $#appendpat != -1
152 ) {
153 #print "LOOP\n";
154 #FIXME: Max loop, no way! :")
155 for ( @appendpat ) {
156 push @pat, $_;
157 }
158 goto REHASH;
159 }
160 }
161 for ( @pat ) {
162 s/\\{/{/g;
163 s/\\}/}/g;
164 s/\\,/,/g;
165 }
166 #print join ("\n", @pat). "\n";
167
fb73857a 168 # assume global context if not provided one
169 $cxix = '_G_' unless defined $cxix;
170 $iter{$cxix} = 0 unless exists $iter{$cxix};
171
172 # if we're just beginning, do it all first
173 if ($iter{$cxix} == 0) {
163d180b 174 $entries{$cxix} = [doglob(1,@pat)];
fb73857a 175 }
176
177 # chuck it all out, quick or slow
178 if (wantarray) {
179 delete $iter{$cxix};
180 return @{delete $entries{$cxix}};
181 }
182 else {
183 if ($iter{$cxix} = scalar @{$entries{$cxix}}) {
184 return shift @{$entries{$cxix}};
185 }
186 else {
187 # return undef for EOL
188 delete $iter{$cxix};
189 delete $entries{$cxix};
190 return undef;
191 }
192 }
193}
08aa1457 194
0e06870b 195{
196 no strict 'refs';
197
198 sub import {
08aa1457 199 my $pkg = shift;
95d94a4f 200 return unless @_;
08aa1457 201 my $sym = shift;
4dd406c2 202 my $callpkg = ($sym =~ s/^GLOBAL_//s ? 'CORE::GLOBAL' : caller(0));
95d94a4f 203 *{$callpkg.'::'.$sym} = \&{$pkg.'::'.$sym} if $sym eq 'glob';
0e06870b 204 }
08aa1457 205}
08aa1457 2061;
207
208__END__
209
210=head1 NAME
211
212File::DosGlob - DOS like globbing and then some
213
08aa1457 214=head1 SYNOPSIS
215
216 require 5.004;
3cb6de81 217
fb73857a 218 # override CORE::glob in current package
219 use File::DosGlob 'glob';
3cb6de81 220
95d94a4f 221 # override CORE::glob in ALL packages (use with extreme caution!)
222 use File::DosGlob 'GLOBAL_glob';
223
08aa1457 224 @perlfiles = glob "..\\pe?l/*.p?";
225 print <..\\pe?l/*.p?>;
3cb6de81 226
fb73857a 227 # from the command line (overrides only in main::)
08aa1457 228 > perl -MFile::DosGlob=glob -e "print <../pe*/*p?>"
08aa1457 229
230=head1 DESCRIPTION
231
232A module that implements DOS-like globbing with a few enhancements.
dfb634a9 233It is largely compatible with perlglob.exe (the M$ setargv.obj
08aa1457 234version) in all but one respect--it understands wildcards in
235directory components.
236
237For example, C<<..\\l*b\\file/*glob.p?>> will work as expected (in
238that it will find something like '..\lib\File/DosGlob.pm' alright).
239Note that all path components are case-insensitive, and that
240backslashes and forward slashes are both accepted, and preserved.
241You may have to double the backslashes if you are putting them in
242literally, due to double-quotish parsing of the pattern by perl.
243
163d180b 244Spaces in the argument delimit distinct patterns, so
245C<glob('*.exe *.dll')> globs all filenames that end in C<.exe>
246or C<.dll>. If you want to put in literal spaces in the glob
247pattern, you can escape them with either double quotes, or backslashes.
248e.g. C<glob('c:/"Program Files"/*/*.dll')>, or
249C<glob('c:/Program\ Files/*/*.dll')>. The argument is tokenized using
250C<Text::ParseWords::parse_line()>, so see L<Text::ParseWords> for details
251of the quoting rules used.
252
08aa1457 253Extending it to csh patterns is left as an exercise to the reader.
254
255=head1 EXPORTS (by request only)
256
257glob()
258
259=head1 BUGS
260
261Should probably be built into the core, and needs to stop
262pandering to DOS habits. Needs a dose of optimizium too.
263
264=head1 AUTHOR
265
6e238990 266Gurusamy Sarathy <gsar@activestate.com>
08aa1457 267
268=head1 HISTORY
269
270=over 4
271
272=item *
273
95d94a4f 274Support for globally overriding glob() (GSAR 3-JUN-98)
275
276=item *
277
fb73857a 278Scalar context, independent iterator context fixes (GSAR 15-SEP-97)
279
280=item *
281
08aa1457 282A few dir-vs-file optimizations result in glob importation being
28310 times faster than using perlglob.exe, and using perlglob.bat is
284only twice as slow as perlglob.exe (GSAR 28-MAY-97)
285
286=item *
287
288Several cleanups prompted by lack of compatible perlglob.exe
289under Borland (GSAR 27-MAY-97)
290
291=item *
292
293Initial version (GSAR 20-FEB-97)
294
295=back
296
297=head1 SEE ALSO
298
299perl
300
dfb634a9 301perlglob.bat
302
163d180b 303Text::ParseWords
304
08aa1457 305=cut
306