File Coverage

blib/lib/File/Glob/Windows.pm
Criterion Covered Total %
statement 38 115 33.0
branch 5 86 5.8
condition 1 15 6.6
subroutine 12 13 92.3
pod 5 5 100.0
total 61 234 26.0


line stmt bran cond sub pod time code
1             #!perl
2             package File::Glob::Windows;
3 3     3   26019 use strict;
  3         7  
  3         128  
4 3     3   15 use warnings;
  3         6  
  3         87  
5 3     3   9633 use utf8;
  3         39  
  3         13  
6 3     3   3143 use Encode;
  3         42495  
  3         351  
7 3     3   2727 use DirHandle;
  3         10158  
  3         119  
8 3     3   29 use Exporter;
  3         8  
  3         119  
9 3     3   19 use Carp;
  3         5  
  3         186  
10 3     3   47 use 5.005;
  3         10  
  3         13578  
11            
12             our $VERSION="0.1.5";
13            
14             our @ISA = qw(Exporter);
15             our @EXPORT = qw( glob );
16             our @EXPORT_OK = qw( glob getCodePage getCodePage_A getCodePage_B getCodePage_POSIX);
17            
18             ##############################################
19             # find native encoding
20            
21             sub getCodePage_A{
22 3 50   3 1 158 eval "require Win32::TieRegistry"; $@ and return;
  3         32  
23 0 0       0 my $key = Win32::TieRegistry->new('HKEY_LOCAL_MACHINE/SYSTEM/CurrentControlSet/Control/Nls/CodePage',{Delimiter=>"/"}) or return;
24 0         0 for('Default','ACP','OEMCP'){
25 0         0 my $v = $key->GetValue($_);
26 0 0 0     0 return "cp$v" if defined($v) and $v=~/(\d+)/;
27             }
28 0         0 return;
29             }
30             sub getCodePage_B{
31 3 50   3 1 247 eval "require Win32::API"; $@ and return;
  3         26  
32 0 0       0 my $f=Win32::API->new("Kernel32", "GetACP", '', 'N') or return;
33 0 0       0 my $v= $f->Call; $v and return "cp$v";
  0         0  
34 0         0 return;
35             }
36            
37             sub getCodePage_POSIX {
38 3     3 1 7220 require POSIX;
39 3         33380 my $v = POSIX::setlocale( &POSIX::LC_CTYPE );
40             #~ LC_TYPE returns
41             #~ English_United States.1252
42             #~ which matches ...Control/Nls/CodePage
43             #~ (default)=(value not set)
44             #~ ACP=1252
45             #~ OEMCP=437
46 3 50 33     42 return "cp$1" if defined($v) and $v=~/(\d+)$/;
47 3         16 return;
48             }
49            
50            
51             sub getCodePage{
52 3     3 1 19 for( \&getCodePage_B,\&getCodePage_A,\&getCodePage_POSIX ){
53 9         12 my $cp = eval{ &$_ };
  9         24  
54 9 50       31 next if $@;
55 9 50       37 $cp and return $cp;
56             }
57 3         9 return;
58             }
59            
60             ##############################################
61            
62             # public options
63             our $encoding = getCodePage();
64             our $sorttype = 0;
65             our $nocase = 1;
66            
67            
68             our %alpha;
69             our %glob_sortfunc=(
70             1=>sub{ $a->[2] cmp $b->[2] }, # name order
71             2=>sub{$b->[0] <=> $a->[0] or $a->[2] cmp $b->[2] }, # directory and name
72             3=>sub{$a->[0] <=> $b->[0] or $a->[2] cmp $b->[2] }, # fine and name
73             4=>sub{ $b->[2] cmp $a->[2] }, # name desc
74             );
75            
76             sub glob{
77 0     0 1   my($path)=@_;
78             # check input
79 0 0 0       (not defined $path or $path eq '') and croak "path is not specified";
80             # check encoding
81 0           my $enc = Encode::find_encoding($encoding);
82 0 0         ref($enc) or croak "encoding is not specified";
83            
84 0           my $sortfunc = $glob_sortfunc{$sorttype};
85            
86             # read volume and root
87 0 0         utf8::is_utf8($path) or $path = Encode::decode($enc,$path);
88 0           my $top='';
89 0 0         $path =~s!^([^:]+:|\\\\[^\\]+)!! and $top .=$1;
90 0 0         $path =~s!^([\\/]+)!! and $top .='\\';
91 0           $top= Encode::encode($enc,$top);
92 0 0         ($path eq '') and return ($top);
93            
94             # split path and convert wildcard to regex
95 0           my @node;
96 0           my $re1 = Encode::encode($enc,'.*?');
97 0           my $re2 = Encode::encode($enc,'.');
98 0 0 0       if($nocase and not %alpha){ $alpha{$_}=1 for 'A'..'Z','a'..'z';}
  0            
99 0           for my $t (split m![\\/]+!,$path){
100 0 0         next if $t eq '';
101 0 0         if( not $t =~ /[*?]/ ){ push @node,Encode::encode($enc,$t); next; }
  0            
  0            
102 0           my $r='';
103 0 0         if($nocase){
104 0           for(split /([*?A-Za-z])/,$t){
105 0 0         next if $_ eq '';
106 0 0         if($_ eq '*' ){ $r.=$re1 }
  0 0          
    0          
107 0           elsif($_ eq '?' ){ $r.=$re2 }
108 0           elsif($alpha{$_} ){ $r.=Encode::encode($enc,'['.uc($_).lc($_).']') }
109 0           else{ $r .= quotemeta(Encode::encode($enc,$_)) }
110             }
111             }else{
112 0           for(split /([*?])/,$t){
113 0 0         next if $_ eq '';
114 0 0         if($_ eq '*' ){ $r.=$re1 }
  0 0          
115 0           elsif($_ eq '?' ){ $r.=$re2 }
116 0           else{ $r .= quotemeta(Encode::encode($enc,$_)) }
117             }
118             }
119 0 0         utf8::is_utf8($r) and die "bad implement. pattern is_utf8 !!\n";
120 0           push @node,qr/^$r$/;
121             }
122            
123             # directory search
124 0           my @result;
125 0           my @stack=([0,'']);
126 0           while(@stack){
127 0           my($level,$prefix)=@{shift @stack};
  0            
128 0 0         if($level==-1){ push @result,$prefix; next; }
  0            
  0            
129 0           my($replace,$separator,$parent,$spec) = (0,'\\',$top.$prefix,$node[$level++]);
130 0 0 0       if($parent eq '' ){ ($parent,$replace)=('.',1); }
  0 0          
131 0           elsif(length($top) and not length($prefix) ){ $separator =''; }
132            
133 0           my @list;
134 0 0         if(ref $spec){
135 0 0         my $d = new DirHandle($parent) or next;
136 0           while(defined( $_=$d->read )){
137 0 0         next if not $_ =~ $spec;
138 0 0         my $path = ($replace?$_:"$parent$separator$_");
139 0 0         if($level==@node){ push @list,[-1,$path,$_]; }
  0 0          
140 0 0         elsif(-d $path){ push @list,[$level,($replace?$_:"$prefix$separator$_"),$_]; }
141             }
142 0 0         $sortfunc and @list = sort $sortfunc @list;
143 0           pop @$_ for @list;
144 0           splice @stack,0,0,@list;
145             }else{
146 0 0         my $path = ($replace?$spec:"$parent$separator$spec");
147 0 0         next if not -e $path;
148 0 0         if($level==@node){ push @result,$path; }
  0 0          
149 0 0         elsif(-d _){ unshift @stack,[$level,($replace?$spec:"$prefix$separator$spec")]; }
150             }
151             }
152 0           return @result;
153             }
154            
155             1;
156            
157             __END__