xref: /onnv-gate/usr/src/cmd/perl/5.8.4/distrib/lib/File/DosGlob.pm (revision 0:68f95e015346)
1*0Sstevel@tonic-gate#!perl -w
2*0Sstevel@tonic-gate
3*0Sstevel@tonic-gate# use strict fails
4*0Sstevel@tonic-gate#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*0Sstevel@tonic-gate
6*0Sstevel@tonic-gate#
7*0Sstevel@tonic-gate# Documentation at the __END__
8*0Sstevel@tonic-gate#
9*0Sstevel@tonic-gate
10*0Sstevel@tonic-gatepackage File::DosGlob;
11*0Sstevel@tonic-gate
12*0Sstevel@tonic-gateour $VERSION = '1.00';
13*0Sstevel@tonic-gateuse strict;
14*0Sstevel@tonic-gateuse warnings;
15*0Sstevel@tonic-gate
16*0Sstevel@tonic-gatesub doglob {
17*0Sstevel@tonic-gate    my $cond = shift;
18*0Sstevel@tonic-gate    my @retval = ();
19*0Sstevel@tonic-gate    #print "doglob: ", join('|', @_), "\n";
20*0Sstevel@tonic-gate  OUTER:
21*0Sstevel@tonic-gate    for my $pat (@_) {
22*0Sstevel@tonic-gate	my @matched = ();
23*0Sstevel@tonic-gate	my @globdirs = ();
24*0Sstevel@tonic-gate	my $head = '.';
25*0Sstevel@tonic-gate	my $sepchr = '/';
26*0Sstevel@tonic-gate        my $tail;
27*0Sstevel@tonic-gate	next OUTER unless defined $pat and $pat ne '';
28*0Sstevel@tonic-gate	# if arg is within quotes strip em and do no globbing
29*0Sstevel@tonic-gate	if ($pat =~ /^"(.*)"\z/s) {
30*0Sstevel@tonic-gate	    $pat = $1;
31*0Sstevel@tonic-gate	    if ($cond eq 'd') { push(@retval, $pat) if -d $pat }
32*0Sstevel@tonic-gate	    else              { push(@retval, $pat) if -e $pat }
33*0Sstevel@tonic-gate	    next OUTER;
34*0Sstevel@tonic-gate	}
35*0Sstevel@tonic-gate	# wildcards with a drive prefix such as h:*.pm must be changed
36*0Sstevel@tonic-gate	# to h:./*.pm to expand correctly
37*0Sstevel@tonic-gate	if ($pat =~ m|^([A-Za-z]:)[^/\\]|s) {
38*0Sstevel@tonic-gate	    substr($_,0,2) = $1 . "./";
39*0Sstevel@tonic-gate	}
40*0Sstevel@tonic-gate	if ($pat =~ m|^(.*)([\\/])([^\\/]*)\z|s) {
41*0Sstevel@tonic-gate	    ($head, $sepchr, $tail) = ($1,$2,$3);
42*0Sstevel@tonic-gate	    #print "div: |$head|$sepchr|$tail|\n";
43*0Sstevel@tonic-gate	    push (@retval, $pat), next OUTER if $tail eq '';
44*0Sstevel@tonic-gate	    if ($head =~ /[*?]/) {
45*0Sstevel@tonic-gate		@globdirs = doglob('d', $head);
46*0Sstevel@tonic-gate		push(@retval, doglob($cond, map {"$_$sepchr$tail"} @globdirs)),
47*0Sstevel@tonic-gate		    next OUTER if @globdirs;
48*0Sstevel@tonic-gate	    }
49*0Sstevel@tonic-gate	    $head .= $sepchr if $head eq '' or $head =~ /^[A-Za-z]:\z/s;
50*0Sstevel@tonic-gate	    $pat = $tail;
51*0Sstevel@tonic-gate	}
52*0Sstevel@tonic-gate	#
53*0Sstevel@tonic-gate	# If file component has no wildcards, we can avoid opendir
54*0Sstevel@tonic-gate	unless ($pat =~ /[*?]/) {
55*0Sstevel@tonic-gate	    $head = '' if $head eq '.';
56*0Sstevel@tonic-gate	    $head .= $sepchr unless $head eq '' or substr($head,-1) eq $sepchr;
57*0Sstevel@tonic-gate	    $head .= $pat;
58*0Sstevel@tonic-gate	    if ($cond eq 'd') { push(@retval,$head) if -d $head }
59*0Sstevel@tonic-gate	    else              { push(@retval,$head) if -e $head }
60*0Sstevel@tonic-gate	    next OUTER;
61*0Sstevel@tonic-gate	}
62*0Sstevel@tonic-gate	opendir(D, $head) or next OUTER;
63*0Sstevel@tonic-gate	my @leaves = readdir D;
64*0Sstevel@tonic-gate	closedir D;
65*0Sstevel@tonic-gate	$head = '' if $head eq '.';
66*0Sstevel@tonic-gate	$head .= $sepchr unless $head eq '' or substr($head,-1) eq $sepchr;
67*0Sstevel@tonic-gate
68*0Sstevel@tonic-gate	# escape regex metachars but not glob chars
69*0Sstevel@tonic-gate        $pat =~ s:([].+^\-\${}[|]):\\$1:g;
70*0Sstevel@tonic-gate	# and convert DOS-style wildcards to regex
71*0Sstevel@tonic-gate	$pat =~ s/\*/.*/g;
72*0Sstevel@tonic-gate	$pat =~ s/\?/.?/g;
73*0Sstevel@tonic-gate
74*0Sstevel@tonic-gate	#print "regex: '$pat', head: '$head'\n";
75*0Sstevel@tonic-gate	my $matchsub = sub { $_[0] =~ m|^$pat\z|is };
76*0Sstevel@tonic-gate      INNER:
77*0Sstevel@tonic-gate	for my $e (@leaves) {
78*0Sstevel@tonic-gate	    next INNER if $e eq '.' or $e eq '..';
79*0Sstevel@tonic-gate	    next INNER if $cond eq 'd' and ! -d "$head$e";
80*0Sstevel@tonic-gate	    push(@matched, "$head$e"), next INNER if &$matchsub($e);
81*0Sstevel@tonic-gate	    #
82*0Sstevel@tonic-gate	    # [DOS compatibility special case]
83*0Sstevel@tonic-gate	    # Failed, add a trailing dot and try again, but only
84*0Sstevel@tonic-gate	    # if name does not have a dot in it *and* pattern
85*0Sstevel@tonic-gate	    # has a dot *and* name is shorter than 9 chars.
86*0Sstevel@tonic-gate	    #
87*0Sstevel@tonic-gate	    if (index($e,'.') == -1 and length($e) < 9
88*0Sstevel@tonic-gate	        and index($pat,'\\.') != -1) {
89*0Sstevel@tonic-gate		push(@matched, "$head$e"), next INNER if &$matchsub("$e.");
90*0Sstevel@tonic-gate	    }
91*0Sstevel@tonic-gate	}
92*0Sstevel@tonic-gate	push @retval, @matched if @matched;
93*0Sstevel@tonic-gate    }
94*0Sstevel@tonic-gate    return @retval;
95*0Sstevel@tonic-gate}
96*0Sstevel@tonic-gate
97*0Sstevel@tonic-gate
98*0Sstevel@tonic-gate#
99*0Sstevel@tonic-gate# Do DOS-like globbing on Mac OS
100*0Sstevel@tonic-gate#
101*0Sstevel@tonic-gatesub doglob_Mac {
102*0Sstevel@tonic-gate    my $cond = shift;
103*0Sstevel@tonic-gate    my @retval = ();
104*0Sstevel@tonic-gate
105*0Sstevel@tonic-gate	#print "doglob_Mac: ", join('|', @_), "\n";
106*0Sstevel@tonic-gate  OUTER:
107*0Sstevel@tonic-gate    for my $arg (@_) {
108*0Sstevel@tonic-gate        local $_ = $arg;
109*0Sstevel@tonic-gate	my @matched = ();
110*0Sstevel@tonic-gate	my @globdirs = ();
111*0Sstevel@tonic-gate	my $head = ':';
112*0Sstevel@tonic-gate	my $not_esc_head = $head;
113*0Sstevel@tonic-gate	my $sepchr = ':';
114*0Sstevel@tonic-gate	next OUTER unless defined $_ and $_ ne '';
115*0Sstevel@tonic-gate	# if arg is within quotes strip em and do no globbing
116*0Sstevel@tonic-gate	if (/^"(.*)"\z/s) {
117*0Sstevel@tonic-gate	    $_ = $1;
118*0Sstevel@tonic-gate		# $_ may contain escaped metachars '\*', '\?' and '\'
119*0Sstevel@tonic-gate	        my $not_esc_arg = $_;
120*0Sstevel@tonic-gate		$not_esc_arg =~ s/\\([*?\\])/$1/g;
121*0Sstevel@tonic-gate	    if ($cond eq 'd') { push(@retval, $not_esc_arg) if -d $not_esc_arg }
122*0Sstevel@tonic-gate	    else              { push(@retval, $not_esc_arg) if -e $not_esc_arg }
123*0Sstevel@tonic-gate	    next OUTER;
124*0Sstevel@tonic-gate	}
125*0Sstevel@tonic-gate
126*0Sstevel@tonic-gate	if (m|^(.*?)(:+)([^:]*)\z|s) { # note: $1 is not greedy
127*0Sstevel@tonic-gate	    my $tail;
128*0Sstevel@tonic-gate	    ($head, $sepchr, $tail) = ($1,$2,$3);
129*0Sstevel@tonic-gate	    #print "div: |$head|$sepchr|$tail|\n";
130*0Sstevel@tonic-gate	    push (@retval, $_), next OUTER if $tail eq '';
131*0Sstevel@tonic-gate		#
132*0Sstevel@tonic-gate		# $head may contain escaped metachars '\*' and '\?'
133*0Sstevel@tonic-gate
134*0Sstevel@tonic-gate		my $tmp_head = $head;
135*0Sstevel@tonic-gate		# if a '*' or '?' is preceded by an odd count of '\', temporary delete
136*0Sstevel@tonic-gate		# it (and its preceding backslashes), i.e. don't treat '\*' and '\?' as
137*0Sstevel@tonic-gate		# wildcards
138*0Sstevel@tonic-gate		$tmp_head =~ s/(\\*)([*?])/$2 x ((length($1) + 1) % 2)/eg;
139*0Sstevel@tonic-gate
140*0Sstevel@tonic-gate		if ($tmp_head =~ /[*?]/) { # if there are wildcards ...
141*0Sstevel@tonic-gate		@globdirs = doglob_Mac('d', $head);
142*0Sstevel@tonic-gate		push(@retval, doglob_Mac($cond, map {"$_$sepchr$tail"} @globdirs)),
143*0Sstevel@tonic-gate		    next OUTER if @globdirs;
144*0Sstevel@tonic-gate	    }
145*0Sstevel@tonic-gate
146*0Sstevel@tonic-gate		$head .= $sepchr;
147*0Sstevel@tonic-gate		$not_esc_head = $head;
148*0Sstevel@tonic-gate		# unescape $head for file operations
149*0Sstevel@tonic-gate		$not_esc_head =~ s/\\([*?\\])/$1/g;
150*0Sstevel@tonic-gate	    $_ = $tail;
151*0Sstevel@tonic-gate	}
152*0Sstevel@tonic-gate	#
153*0Sstevel@tonic-gate	# If file component has no wildcards, we can avoid opendir
154*0Sstevel@tonic-gate
155*0Sstevel@tonic-gate	my $tmp_tail = $_;
156*0Sstevel@tonic-gate	# if a '*' or '?' is preceded by an odd count of '\', temporary delete
157*0Sstevel@tonic-gate	# it (and its preceding backslashes), i.e. don't treat '\*' and '\?' as
158*0Sstevel@tonic-gate	# wildcards
159*0Sstevel@tonic-gate	$tmp_tail =~ s/(\\*)([*?])/$2 x ((length($1) + 1) % 2)/eg;
160*0Sstevel@tonic-gate
161*0Sstevel@tonic-gate	unless ($tmp_tail =~ /[*?]/) { # if there are wildcards ...
162*0Sstevel@tonic-gate	    $not_esc_head = $head = '' if $head eq ':';
163*0Sstevel@tonic-gate	    my $not_esc_tail = $_;
164*0Sstevel@tonic-gate	    # unescape $head and $tail for file operations
165*0Sstevel@tonic-gate	    $not_esc_tail =~ s/\\([*?\\])/$1/g;
166*0Sstevel@tonic-gate	    $head .= $_;
167*0Sstevel@tonic-gate		$not_esc_head .= $not_esc_tail;
168*0Sstevel@tonic-gate	    if ($cond eq 'd') { push(@retval,$head) if -d $not_esc_head }
169*0Sstevel@tonic-gate	    else              { push(@retval,$head) if -e $not_esc_head }
170*0Sstevel@tonic-gate	    next OUTER;
171*0Sstevel@tonic-gate	}
172*0Sstevel@tonic-gate	#print "opendir($not_esc_head)\n";
173*0Sstevel@tonic-gate	opendir(D, $not_esc_head) or next OUTER;
174*0Sstevel@tonic-gate	my @leaves = readdir D;
175*0Sstevel@tonic-gate	closedir D;
176*0Sstevel@tonic-gate
177*0Sstevel@tonic-gate	# escape regex metachars but not '\' and glob chars '*', '?'
178*0Sstevel@tonic-gate	$_ =~ s:([].+^\-\${}[|]):\\$1:g;
179*0Sstevel@tonic-gate	# and convert DOS-style wildcards to regex,
180*0Sstevel@tonic-gate	# but only if they are not escaped
181*0Sstevel@tonic-gate	$_ =~ s/(\\*)([*?])/$1 . ('.' x ((length($1) + 1) % 2)) . $2/eg;
182*0Sstevel@tonic-gate
183*0Sstevel@tonic-gate	#print "regex: '$_', head: '$head', unescaped head: '$not_esc_head'\n";
184*0Sstevel@tonic-gate	my $matchsub = eval 'sub { $_[0] =~ m|^' . $_ . '\\z|ios }';
185*0Sstevel@tonic-gate	warn($@), next OUTER if $@;
186*0Sstevel@tonic-gate      INNER:
187*0Sstevel@tonic-gate	for my $e (@leaves) {
188*0Sstevel@tonic-gate	    next INNER if $e eq '.' or $e eq '..';
189*0Sstevel@tonic-gate	    next INNER if $cond eq 'd' and ! -d "$not_esc_head$e";
190*0Sstevel@tonic-gate
191*0Sstevel@tonic-gate		if (&$matchsub($e)) {
192*0Sstevel@tonic-gate			my $leave = (($not_esc_head eq ':') && (-f "$not_esc_head$e")) ?
193*0Sstevel@tonic-gate		            	"$e" : "$not_esc_head$e";
194*0Sstevel@tonic-gate			#
195*0Sstevel@tonic-gate			# On Mac OS, the two glob metachars '*' and '?' and the escape
196*0Sstevel@tonic-gate			# char '\' are valid characters for file and directory names.
197*0Sstevel@tonic-gate			# We have to escape and treat them specially.
198*0Sstevel@tonic-gate			$leave =~ s|([*?\\])|\\$1|g;
199*0Sstevel@tonic-gate			push(@matched, $leave);
200*0Sstevel@tonic-gate			next INNER;
201*0Sstevel@tonic-gate		}
202*0Sstevel@tonic-gate	}
203*0Sstevel@tonic-gate	push @retval, @matched if @matched;
204*0Sstevel@tonic-gate    }
205*0Sstevel@tonic-gate    return @retval;
206*0Sstevel@tonic-gate}
207*0Sstevel@tonic-gate
208*0Sstevel@tonic-gate#
209*0Sstevel@tonic-gate# _expand_volume() will only be used on Mac OS (Classic):
210*0Sstevel@tonic-gate# Takes an array of original patterns as argument and returns an array of
211*0Sstevel@tonic-gate# possibly modified patterns. Each original pattern is processed like
212*0Sstevel@tonic-gate# that:
213*0Sstevel@tonic-gate# + If there's a volume name in the pattern, we push a separate pattern
214*0Sstevel@tonic-gate#   for each mounted volume that matches (with '*', '?' and '\' escaped).
215*0Sstevel@tonic-gate# + If there's no volume name in the original pattern, it is pushed
216*0Sstevel@tonic-gate#   unchanged.
217*0Sstevel@tonic-gate# Note that the returned array of patterns may be empty.
218*0Sstevel@tonic-gate#
219*0Sstevel@tonic-gatesub _expand_volume {
220*0Sstevel@tonic-gate
221*0Sstevel@tonic-gate	require MacPerl; # to be verbose
222*0Sstevel@tonic-gate
223*0Sstevel@tonic-gate	my @pat = @_;
224*0Sstevel@tonic-gate	my @new_pat = ();
225*0Sstevel@tonic-gate	my @FSSpec_Vols = MacPerl::Volumes();
226*0Sstevel@tonic-gate	my @mounted_volumes = ();
227*0Sstevel@tonic-gate
228*0Sstevel@tonic-gate	foreach my $spec_vol (@FSSpec_Vols) {
229*0Sstevel@tonic-gate		# push all mounted volumes into array
230*0Sstevel@tonic-gate     	push @mounted_volumes, MacPerl::MakePath($spec_vol);
231*0Sstevel@tonic-gate	}
232*0Sstevel@tonic-gate	#print "mounted volumes: |@mounted_volumes|\n";
233*0Sstevel@tonic-gate
234*0Sstevel@tonic-gate	while (@pat) {
235*0Sstevel@tonic-gate		my $pat = shift @pat;
236*0Sstevel@tonic-gate		if ($pat =~ /^([^:]+:)(.*)\z/) { # match a volume name?
237*0Sstevel@tonic-gate			my $vol_pat = $1;
238*0Sstevel@tonic-gate			my $tail = $2;
239*0Sstevel@tonic-gate			#
240*0Sstevel@tonic-gate			# escape regex metachars but not '\' and glob chars '*', '?'
241*0Sstevel@tonic-gate			$vol_pat =~ s:([].+^\-\${}[|]):\\$1:g;
242*0Sstevel@tonic-gate			# and convert DOS-style wildcards to regex,
243*0Sstevel@tonic-gate			# but only if they are not escaped
244*0Sstevel@tonic-gate			$vol_pat =~ s/(\\*)([*?])/$1 . ('.' x ((length($1) + 1) % 2)) . $2/eg;
245*0Sstevel@tonic-gate			#print "volume regex: '$vol_pat' \n";
246*0Sstevel@tonic-gate
247*0Sstevel@tonic-gate			foreach my $volume (@mounted_volumes) {
248*0Sstevel@tonic-gate				if ($volume =~ m|^$vol_pat\z|ios) {
249*0Sstevel@tonic-gate					#
250*0Sstevel@tonic-gate					# On Mac OS, the two glob metachars '*' and '?' and the
251*0Sstevel@tonic-gate					# escape char '\' are valid characters for volume names.
252*0Sstevel@tonic-gate					# We have to escape and treat them specially.
253*0Sstevel@tonic-gate					$volume =~ s|([*?\\])|\\$1|g;
254*0Sstevel@tonic-gate					push @new_pat, $volume . $tail;
255*0Sstevel@tonic-gate				}
256*0Sstevel@tonic-gate			}
257*0Sstevel@tonic-gate		} else { # no volume name in pattern, push original pattern
258*0Sstevel@tonic-gate			push @new_pat, $pat;
259*0Sstevel@tonic-gate		}
260*0Sstevel@tonic-gate	}
261*0Sstevel@tonic-gate	return @new_pat;
262*0Sstevel@tonic-gate}
263*0Sstevel@tonic-gate
264*0Sstevel@tonic-gate
265*0Sstevel@tonic-gate#
266*0Sstevel@tonic-gate# _preprocess_pattern() will only be used on Mac OS (Classic):
267*0Sstevel@tonic-gate# Resolves any updirs in the pattern. Removes a single trailing colon
268*0Sstevel@tonic-gate# from the pattern, unless it's a volume name pattern like "*HD:"
269*0Sstevel@tonic-gate#
270*0Sstevel@tonic-gatesub _preprocess_pattern {
271*0Sstevel@tonic-gate	my @pat = @_;
272*0Sstevel@tonic-gate
273*0Sstevel@tonic-gate	foreach my $p (@pat) {
274*0Sstevel@tonic-gate		my $proceed;
275*0Sstevel@tonic-gate		# resolve any updirs, e.g. "*HD:t?p::a*" -> "*HD:a*"
276*0Sstevel@tonic-gate		do {
277*0Sstevel@tonic-gate			$proceed = ($p =~ s/^(.*):[^:]+::(.*?)\z/$1:$2/);
278*0Sstevel@tonic-gate		} while ($proceed);
279*0Sstevel@tonic-gate		# remove a single trailing colon, e.g. ":*:" -> ":*"
280*0Sstevel@tonic-gate		$p =~ s/:([^:]+):\z/:$1/;
281*0Sstevel@tonic-gate	}
282*0Sstevel@tonic-gate	return @pat;
283*0Sstevel@tonic-gate}
284*0Sstevel@tonic-gate
285*0Sstevel@tonic-gate
286*0Sstevel@tonic-gate#
287*0Sstevel@tonic-gate# _un_escape() will only be used on Mac OS (Classic):
288*0Sstevel@tonic-gate# Unescapes a list of arguments which may contain escaped
289*0Sstevel@tonic-gate# metachars '*', '?' and '\'.
290*0Sstevel@tonic-gate#
291*0Sstevel@tonic-gatesub _un_escape {
292*0Sstevel@tonic-gate	foreach (@_) {
293*0Sstevel@tonic-gate		s/\\([*?\\])/$1/g;
294*0Sstevel@tonic-gate	}
295*0Sstevel@tonic-gate	return @_;
296*0Sstevel@tonic-gate}
297*0Sstevel@tonic-gate
298*0Sstevel@tonic-gate#
299*0Sstevel@tonic-gate# this can be used to override CORE::glob in a specific
300*0Sstevel@tonic-gate# package by saying C<use File::DosGlob 'glob';> in that
301*0Sstevel@tonic-gate# namespace.
302*0Sstevel@tonic-gate#
303*0Sstevel@tonic-gate
304*0Sstevel@tonic-gate# context (keyed by second cxix arg provided by core)
305*0Sstevel@tonic-gatemy %iter;
306*0Sstevel@tonic-gatemy %entries;
307*0Sstevel@tonic-gate
308*0Sstevel@tonic-gatesub glob {
309*0Sstevel@tonic-gate    my($pat,$cxix) = @_;
310*0Sstevel@tonic-gate    my @pat;
311*0Sstevel@tonic-gate
312*0Sstevel@tonic-gate    # glob without args defaults to $_
313*0Sstevel@tonic-gate    $pat = $_ unless defined $pat;
314*0Sstevel@tonic-gate
315*0Sstevel@tonic-gate    # extract patterns
316*0Sstevel@tonic-gate    if ($pat =~ /\s/) {
317*0Sstevel@tonic-gate	require Text::ParseWords;
318*0Sstevel@tonic-gate	@pat = Text::ParseWords::parse_line('\s+',0,$pat);
319*0Sstevel@tonic-gate    }
320*0Sstevel@tonic-gate    else {
321*0Sstevel@tonic-gate	push @pat, $pat;
322*0Sstevel@tonic-gate    }
323*0Sstevel@tonic-gate
324*0Sstevel@tonic-gate    # Mike Mestnik: made to do abc{1,2,3} == abc1 abc2 abc3.
325*0Sstevel@tonic-gate    #   abc3 will be the original {3} (and drop the {}).
326*0Sstevel@tonic-gate    #   abc1 abc2 will be put in @appendpat.
327*0Sstevel@tonic-gate    # This was just the esiest way, not nearly the best.
328*0Sstevel@tonic-gate    REHASH: {
329*0Sstevel@tonic-gate	my @appendpat = ();
330*0Sstevel@tonic-gate	for (@pat) {
331*0Sstevel@tonic-gate	    # There must be a "," I.E. abc{efg} is not what we want.
332*0Sstevel@tonic-gate	    while ( /^(.*)(?<!\\)\{(.*?)(?<!\\)\,.*?(?<!\\)\}(.*)$/ ) {
333*0Sstevel@tonic-gate		my ($start, $match, $end) = ($1, $2, $3);
334*0Sstevel@tonic-gate		#print "Got: \n\t$start\n\t$match\n\t$end\n";
335*0Sstevel@tonic-gate		my $tmp = "$start$match$end";
336*0Sstevel@tonic-gate		while ( $tmp =~ s/^(.*?)(?<!\\)\{(?:.*(?<!\\)\,)?(.*\Q$match\E.*?)(?:(?<!\\)\,.*)?(?<!\\)\}(.*)$/$1$2$3/ ) {
337*0Sstevel@tonic-gate		    #print "Striped: $tmp\n";
338*0Sstevel@tonic-gate		    #  these expanshions will be preformed by the original,
339*0Sstevel@tonic-gate		    #  when we call REHASH.
340*0Sstevel@tonic-gate		}
341*0Sstevel@tonic-gate		push @appendpat, ("$tmp");
342*0Sstevel@tonic-gate		s/^\Q$start\E(?<!\\)\{\Q$match\E(?<!\\)\,/$start\{/;
343*0Sstevel@tonic-gate		if ( /^\Q$start\E(?<!\\)\{(?!.*?(?<!\\)\,.*?\Q$end\E$)(.*)(?<!\\)\}\Q$end\E$/ ) {
344*0Sstevel@tonic-gate		    $match = $1;
345*0Sstevel@tonic-gate		    #print "GOT: \n\t$start\n\t$match\n\t$end\n\n";
346*0Sstevel@tonic-gate		    $_ = "$start$match$end";
347*0Sstevel@tonic-gate		}
348*0Sstevel@tonic-gate	    }
349*0Sstevel@tonic-gate	    #print "Sould have "GOT" vs "Got"!\n";
350*0Sstevel@tonic-gate		#FIXME: There should be checking for this.
351*0Sstevel@tonic-gate		#  How or what should be done about failure is beond me.
352*0Sstevel@tonic-gate	}
353*0Sstevel@tonic-gate	if ( $#appendpat != -1
354*0Sstevel@tonic-gate		) {
355*0Sstevel@tonic-gate	    #print "LOOP\n";
356*0Sstevel@tonic-gate	    #FIXME: Max loop, no way! :")
357*0Sstevel@tonic-gate	    for ( @appendpat ) {
358*0Sstevel@tonic-gate	        push @pat, $_;
359*0Sstevel@tonic-gate	    }
360*0Sstevel@tonic-gate	    goto REHASH;
361*0Sstevel@tonic-gate	}
362*0Sstevel@tonic-gate    }
363*0Sstevel@tonic-gate    for ( @pat ) {
364*0Sstevel@tonic-gate	s/\\{/{/g;
365*0Sstevel@tonic-gate	s/\\}/}/g;
366*0Sstevel@tonic-gate	s/\\,/,/g;
367*0Sstevel@tonic-gate    }
368*0Sstevel@tonic-gate    #print join ("\n", @pat). "\n";
369*0Sstevel@tonic-gate
370*0Sstevel@tonic-gate    # assume global context if not provided one
371*0Sstevel@tonic-gate    $cxix = '_G_' unless defined $cxix;
372*0Sstevel@tonic-gate    $iter{$cxix} = 0 unless exists $iter{$cxix};
373*0Sstevel@tonic-gate
374*0Sstevel@tonic-gate    # if we're just beginning, do it all first
375*0Sstevel@tonic-gate    if ($iter{$cxix} == 0) {
376*0Sstevel@tonic-gate	if ($^O eq 'MacOS') {
377*0Sstevel@tonic-gate		# first, take care of updirs and trailing colons
378*0Sstevel@tonic-gate		@pat = _preprocess_pattern(@pat);
379*0Sstevel@tonic-gate		# expand volume names
380*0Sstevel@tonic-gate		@pat = _expand_volume(@pat);
381*0Sstevel@tonic-gate		$entries{$cxix} = (@pat) ? [_un_escape( doglob_Mac(1,@pat) )] : [()];
382*0Sstevel@tonic-gate	} else {
383*0Sstevel@tonic-gate		$entries{$cxix} = [doglob(1,@pat)];
384*0Sstevel@tonic-gate    }
385*0Sstevel@tonic-gate	}
386*0Sstevel@tonic-gate
387*0Sstevel@tonic-gate    # chuck it all out, quick or slow
388*0Sstevel@tonic-gate    if (wantarray) {
389*0Sstevel@tonic-gate	delete $iter{$cxix};
390*0Sstevel@tonic-gate	return @{delete $entries{$cxix}};
391*0Sstevel@tonic-gate    }
392*0Sstevel@tonic-gate    else {
393*0Sstevel@tonic-gate	if ($iter{$cxix} = scalar @{$entries{$cxix}}) {
394*0Sstevel@tonic-gate	    return shift @{$entries{$cxix}};
395*0Sstevel@tonic-gate	}
396*0Sstevel@tonic-gate	else {
397*0Sstevel@tonic-gate	    # return undef for EOL
398*0Sstevel@tonic-gate	    delete $iter{$cxix};
399*0Sstevel@tonic-gate	    delete $entries{$cxix};
400*0Sstevel@tonic-gate	    return undef;
401*0Sstevel@tonic-gate	}
402*0Sstevel@tonic-gate    }
403*0Sstevel@tonic-gate}
404*0Sstevel@tonic-gate
405*0Sstevel@tonic-gate{
406*0Sstevel@tonic-gate    no strict 'refs';
407*0Sstevel@tonic-gate
408*0Sstevel@tonic-gate    sub import {
409*0Sstevel@tonic-gate    my $pkg = shift;
410*0Sstevel@tonic-gate    return unless @_;
411*0Sstevel@tonic-gate    my $sym = shift;
412*0Sstevel@tonic-gate    my $callpkg = ($sym =~ s/^GLOBAL_//s ? 'CORE::GLOBAL' : caller(0));
413*0Sstevel@tonic-gate    *{$callpkg.'::'.$sym} = \&{$pkg.'::'.$sym} if $sym eq 'glob';
414*0Sstevel@tonic-gate    }
415*0Sstevel@tonic-gate}
416*0Sstevel@tonic-gate1;
417*0Sstevel@tonic-gate
418*0Sstevel@tonic-gate__END__
419*0Sstevel@tonic-gate
420*0Sstevel@tonic-gate=head1 NAME
421*0Sstevel@tonic-gate
422*0Sstevel@tonic-gateFile::DosGlob - DOS like globbing and then some
423*0Sstevel@tonic-gate
424*0Sstevel@tonic-gate=head1 SYNOPSIS
425*0Sstevel@tonic-gate
426*0Sstevel@tonic-gate    require 5.004;
427*0Sstevel@tonic-gate
428*0Sstevel@tonic-gate    # override CORE::glob in current package
429*0Sstevel@tonic-gate    use File::DosGlob 'glob';
430*0Sstevel@tonic-gate
431*0Sstevel@tonic-gate    # override CORE::glob in ALL packages (use with extreme caution!)
432*0Sstevel@tonic-gate    use File::DosGlob 'GLOBAL_glob';
433*0Sstevel@tonic-gate
434*0Sstevel@tonic-gate    @perlfiles = glob  "..\\pe?l/*.p?";
435*0Sstevel@tonic-gate    print <..\\pe?l/*.p?>;
436*0Sstevel@tonic-gate
437*0Sstevel@tonic-gate    # from the command line (overrides only in main::)
438*0Sstevel@tonic-gate    > perl -MFile::DosGlob=glob -e "print <../pe*/*p?>"
439*0Sstevel@tonic-gate
440*0Sstevel@tonic-gate=head1 DESCRIPTION
441*0Sstevel@tonic-gate
442*0Sstevel@tonic-gateA module that implements DOS-like globbing with a few enhancements.
443*0Sstevel@tonic-gateIt is largely compatible with perlglob.exe (the M$ setargv.obj
444*0Sstevel@tonic-gateversion) in all but one respect--it understands wildcards in
445*0Sstevel@tonic-gatedirectory components.
446*0Sstevel@tonic-gate
447*0Sstevel@tonic-gateFor example, C<<..\\l*b\\file/*glob.p?>> will work as expected (in
448*0Sstevel@tonic-gatethat it will find something like '..\lib\File/DosGlob.pm' alright).
449*0Sstevel@tonic-gateNote that all path components are case-insensitive, and that
450*0Sstevel@tonic-gatebackslashes and forward slashes are both accepted, and preserved.
451*0Sstevel@tonic-gateYou may have to double the backslashes if you are putting them in
452*0Sstevel@tonic-gateliterally, due to double-quotish parsing of the pattern by perl.
453*0Sstevel@tonic-gate
454*0Sstevel@tonic-gateSpaces in the argument delimit distinct patterns, so
455*0Sstevel@tonic-gateC<glob('*.exe *.dll')> globs all filenames that end in C<.exe>
456*0Sstevel@tonic-gateor C<.dll>.  If you want to put in literal spaces in the glob
457*0Sstevel@tonic-gatepattern, you can escape them with either double quotes, or backslashes.
458*0Sstevel@tonic-gatee.g. C<glob('c:/"Program Files"/*/*.dll')>, or
459*0Sstevel@tonic-gateC<glob('c:/Program\ Files/*/*.dll')>.  The argument is tokenized using
460*0Sstevel@tonic-gateC<Text::ParseWords::parse_line()>, so see L<Text::ParseWords> for details
461*0Sstevel@tonic-gateof the quoting rules used.
462*0Sstevel@tonic-gate
463*0Sstevel@tonic-gateExtending it to csh patterns is left as an exercise to the reader.
464*0Sstevel@tonic-gate
465*0Sstevel@tonic-gate=head1 NOTES
466*0Sstevel@tonic-gate
467*0Sstevel@tonic-gate=over 4
468*0Sstevel@tonic-gate
469*0Sstevel@tonic-gate=item *
470*0Sstevel@tonic-gate
471*0Sstevel@tonic-gateMac OS (Classic) users should note a few differences. The specification
472*0Sstevel@tonic-gateof pathnames in glob patterns adheres to the usual Mac OS conventions:
473*0Sstevel@tonic-gateThe path separator is a colon ':', not a slash '/' or backslash '\'. A
474*0Sstevel@tonic-gatefull path always begins with a volume name. A relative pathname on Mac
475*0Sstevel@tonic-gateOS must always begin with a ':', except when specifying a file or
476*0Sstevel@tonic-gatedirectory name in the current working directory, where the leading colon
477*0Sstevel@tonic-gateis optional. If specifying a volume name only, a trailing ':' is
478*0Sstevel@tonic-gaterequired. Due to these rules, a glob like E<lt>*:E<gt> will find all
479*0Sstevel@tonic-gatemounted volumes, while a glob like E<lt>*E<gt> or E<lt>:*E<gt> will find
480*0Sstevel@tonic-gateall files and directories in the current directory.
481*0Sstevel@tonic-gate
482*0Sstevel@tonic-gateNote that updirs in the glob pattern are resolved before the matching begins,
483*0Sstevel@tonic-gatei.e. a pattern like "*HD:t?p::a*" will be matched as "*HD:a*". Note also,
484*0Sstevel@tonic-gatethat a single trailing ':' in the pattern is ignored (unless it's a volume
485*0Sstevel@tonic-gatename pattern like "*HD:"), i.e. a glob like <:*:> will find both directories
486*0Sstevel@tonic-gateI<and> files (and not, as one might expect, only directories).
487*0Sstevel@tonic-gate
488*0Sstevel@tonic-gateThe metachars '*', '?' and the escape char '\' are valid characters in
489*0Sstevel@tonic-gatevolume, directory and file names on Mac OS. Hence, if you want to match
490*0Sstevel@tonic-gatea '*', '?' or '\' literally, you have to escape these characters. Due to
491*0Sstevel@tonic-gateperl's quoting rules, things may get a bit complicated, when you want to
492*0Sstevel@tonic-gatematch a string like '\*' literally, or when you want to match '\' literally,
493*0Sstevel@tonic-gatebut treat the immediately following character '*' as metachar. So, here's a
494*0Sstevel@tonic-gaterule of thumb (applies to both single- and double-quoted strings): escape
495*0Sstevel@tonic-gateeach '*' or '?' or '\' with a backslash, if you want to treat them literally,
496*0Sstevel@tonic-gateand then double each backslash and your are done. E.g.
497*0Sstevel@tonic-gate
498*0Sstevel@tonic-gate- Match '\*' literally
499*0Sstevel@tonic-gate
500*0Sstevel@tonic-gate   escape both '\' and '*'  : '\\\*'
501*0Sstevel@tonic-gate   double the backslashes   : '\\\\\\*'
502*0Sstevel@tonic-gate
503*0Sstevel@tonic-gate(Internally, the glob routine sees a '\\\*', which means that both '\' and
504*0Sstevel@tonic-gate'*' are escaped.)
505*0Sstevel@tonic-gate
506*0Sstevel@tonic-gate
507*0Sstevel@tonic-gate- Match '\' literally, treat '*' as metachar
508*0Sstevel@tonic-gate
509*0Sstevel@tonic-gate   escape '\' but not '*'   : '\\*'
510*0Sstevel@tonic-gate   double the backslashes   : '\\\\*'
511*0Sstevel@tonic-gate
512*0Sstevel@tonic-gate(Internally, the glob routine sees a '\\*', which means that '\' is escaped and
513*0Sstevel@tonic-gate'*' is not.)
514*0Sstevel@tonic-gate
515*0Sstevel@tonic-gateNote that you also have to quote literal spaces in the glob pattern, as described
516*0Sstevel@tonic-gateabove.
517*0Sstevel@tonic-gate
518*0Sstevel@tonic-gate=back
519*0Sstevel@tonic-gate
520*0Sstevel@tonic-gate=head1 EXPORTS (by request only)
521*0Sstevel@tonic-gate
522*0Sstevel@tonic-gateglob()
523*0Sstevel@tonic-gate
524*0Sstevel@tonic-gate=head1 BUGS
525*0Sstevel@tonic-gate
526*0Sstevel@tonic-gateShould probably be built into the core, and needs to stop
527*0Sstevel@tonic-gatepandering to DOS habits.  Needs a dose of optimizium too.
528*0Sstevel@tonic-gate
529*0Sstevel@tonic-gate=head1 AUTHOR
530*0Sstevel@tonic-gate
531*0Sstevel@tonic-gateGurusamy Sarathy <gsar@activestate.com>
532*0Sstevel@tonic-gate
533*0Sstevel@tonic-gate=head1 HISTORY
534*0Sstevel@tonic-gate
535*0Sstevel@tonic-gate=over 4
536*0Sstevel@tonic-gate
537*0Sstevel@tonic-gate=item *
538*0Sstevel@tonic-gate
539*0Sstevel@tonic-gateSupport for globally overriding glob() (GSAR 3-JUN-98)
540*0Sstevel@tonic-gate
541*0Sstevel@tonic-gate=item *
542*0Sstevel@tonic-gate
543*0Sstevel@tonic-gateScalar context, independent iterator context fixes (GSAR 15-SEP-97)
544*0Sstevel@tonic-gate
545*0Sstevel@tonic-gate=item *
546*0Sstevel@tonic-gate
547*0Sstevel@tonic-gateA few dir-vs-file optimizations result in glob importation being
548*0Sstevel@tonic-gate10 times faster than using perlglob.exe, and using perlglob.bat is
549*0Sstevel@tonic-gateonly twice as slow as perlglob.exe (GSAR 28-MAY-97)
550*0Sstevel@tonic-gate
551*0Sstevel@tonic-gate=item *
552*0Sstevel@tonic-gate
553*0Sstevel@tonic-gateSeveral cleanups prompted by lack of compatible perlglob.exe
554*0Sstevel@tonic-gateunder Borland (GSAR 27-MAY-97)
555*0Sstevel@tonic-gate
556*0Sstevel@tonic-gate=item *
557*0Sstevel@tonic-gate
558*0Sstevel@tonic-gateInitial version (GSAR 20-FEB-97)
559*0Sstevel@tonic-gate
560*0Sstevel@tonic-gate=back
561*0Sstevel@tonic-gate
562*0Sstevel@tonic-gate=head1 SEE ALSO
563*0Sstevel@tonic-gate
564*0Sstevel@tonic-gateperl
565*0Sstevel@tonic-gate
566*0Sstevel@tonic-gateperlglob.bat
567*0Sstevel@tonic-gate
568*0Sstevel@tonic-gateText::ParseWords
569*0Sstevel@tonic-gate
570*0Sstevel@tonic-gate=cut
571*0Sstevel@tonic-gate
572