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