line |
stmt |
bran |
cond |
sub |
pod |
time |
code |
1
|
|
|
|
|
|
|
package File::Zglob; |
2
|
4
|
|
|
4
|
|
111296
|
use strict; |
|
4
|
|
|
|
|
7
|
|
|
4
|
|
|
|
|
180
|
|
3
|
4
|
|
|
4
|
|
23
|
use warnings 'all', FATAL => 'recursion'; |
|
4
|
|
|
|
|
8
|
|
|
4
|
|
|
|
|
191
|
|
4
|
4
|
|
|
4
|
|
100
|
use 5.008008; |
|
4
|
|
|
|
|
18
|
|
|
4
|
|
|
|
|
274
|
|
5
|
|
|
|
|
|
|
our $VERSION = '0.11'; |
6
|
4
|
|
|
4
|
|
28
|
use base qw(Exporter); |
|
4
|
|
|
|
|
6
|
|
|
4
|
|
|
|
|
537
|
|
7
|
|
|
|
|
|
|
|
8
|
|
|
|
|
|
|
our @EXPORT = qw(zglob); |
9
|
|
|
|
|
|
|
|
10
|
4
|
|
|
4
|
|
22
|
use File::Basename; |
|
4
|
|
|
|
|
6
|
|
|
4
|
|
|
|
|
9002
|
|
11
|
|
|
|
|
|
|
|
12
|
|
|
|
|
|
|
our $SEPCHAR = '/'; |
13
|
|
|
|
|
|
|
our $NOCASE = $^O =~ /^(?:MSWin32|VMS|os2|dos|riscos|MacOS|darwin)$/ ? 1 : 0; |
14
|
|
|
|
|
|
|
our $DIRFLAG = \"DIR?"; |
15
|
|
|
|
|
|
|
our $DEEPFLAG = \"**"; |
16
|
|
|
|
|
|
|
our $PARENTFLAG = \".."; |
17
|
|
|
|
|
|
|
our $DEBUG = 0; |
18
|
|
|
|
|
|
|
our $STRICT_LEADING_DOT = 1; |
19
|
|
|
|
|
|
|
our $STRICT_WILDCARD_SLASH = 1; |
20
|
|
|
|
|
|
|
|
21
|
|
|
|
|
|
|
sub zglob { |
22
|
19
|
|
|
19
|
1
|
16634
|
my ($pattern) = @_; |
23
|
|
|
|
|
|
|
#dbg("FOLDING: $pattern"); |
24
|
|
|
|
|
|
|
# support ~tokuhirom/ |
25
|
19
|
50
|
|
|
|
59
|
if ($^O eq 'MSWin32') { |
26
|
0
|
|
|
|
|
0
|
require Win32; |
27
|
0
|
|
|
|
|
0
|
$pattern =~ s!^(\~[^$SEPCHAR]*)!Win32::GetLongPathName([glob($1)]->[0])!e; |
|
0
|
|
|
|
|
0
|
|
28
|
|
|
|
|
|
|
} else { |
29
|
19
|
|
|
|
|
121
|
$pattern =~ s!^(\~[^$SEPCHAR]*)![glob($1)]->[0]!e; |
|
1
|
|
|
|
|
21
|
|
30
|
|
|
|
|
|
|
} |
31
|
19
|
|
|
|
|
41
|
my ($node, $matcher) = glob_prepare_pattern($pattern); |
32
|
|
|
|
|
|
|
# $node : \0 if absolute path, \1 if relative. |
33
|
|
|
|
|
|
|
|
34
|
|
|
|
|
|
|
#dbg("pattern: ", $node, $matcher); |
35
|
19
|
|
|
|
|
67
|
return _rec($node, $matcher, []); |
36
|
|
|
|
|
|
|
} |
37
|
|
|
|
|
|
|
|
38
|
|
|
|
|
|
|
sub dbg(@) { |
39
|
164
|
50
|
|
164
|
0
|
296
|
return unless $DEBUG; |
40
|
0
|
|
|
|
|
0
|
my ($pkg, $filename, $line, $sub) = caller(1); |
41
|
0
|
|
|
|
|
0
|
my $i = 0; |
42
|
0
|
|
|
|
|
0
|
while (caller($i++)) { 1 } |
|
0
|
|
|
|
|
0
|
|
43
|
0
|
|
|
|
|
0
|
my $msg; |
44
|
0
|
|
|
|
|
0
|
$msg .= ('-' x ($i-5)); |
45
|
0
|
|
|
|
|
0
|
$msg .= " [$sub] "; |
46
|
0
|
|
|
|
|
0
|
for (@_) { |
47
|
0
|
|
|
|
|
0
|
$msg .= ' '; |
48
|
0
|
0
|
|
|
|
0
|
if (not defined $_) { |
|
|
0
|
|
|
|
|
|
49
|
0
|
|
|
|
|
0
|
$msg .= '<>'; |
50
|
|
|
|
|
|
|
} elsif (ref $_) { |
51
|
0
|
|
|
|
|
0
|
require Data::Dumper; |
52
|
0
|
|
|
|
|
0
|
local $Data::Dumper::Terse = 1; |
53
|
0
|
|
|
|
|
0
|
local $Data::Dumper::Indent = 0; |
54
|
0
|
|
|
|
|
0
|
$msg .= Data::Dumper::Dumper($_); |
55
|
|
|
|
|
|
|
} else { |
56
|
0
|
|
|
|
|
0
|
$msg .= $_; |
57
|
|
|
|
|
|
|
} |
58
|
|
|
|
|
|
|
} |
59
|
0
|
|
|
|
|
0
|
$msg .= " at $filename line $line\n"; |
60
|
0
|
|
|
|
|
0
|
print($msg); |
61
|
|
|
|
|
|
|
} |
62
|
|
|
|
|
|
|
|
63
|
|
|
|
|
|
|
sub _recstar { |
64
|
20
|
|
|
20
|
|
29
|
my ($node, $matcher) = @_; |
65
|
|
|
|
|
|
|
#dbg("recstar: ", $node, $matcher, $seed); |
66
|
|
|
|
|
|
|
return ( |
67
|
13
|
|
|
|
|
36
|
_rec( $node, $matcher ), |
68
|
|
|
|
|
|
|
( |
69
|
20
|
|
|
|
|
53
|
map { _recstar( $_, $matcher ) } |
70
|
|
|
|
|
|
|
glob_fs_fold( $node, qr{^[^.].*$}, 1 ) |
71
|
|
|
|
|
|
|
) |
72
|
|
|
|
|
|
|
); |
73
|
|
|
|
|
|
|
} |
74
|
|
|
|
|
|
|
|
75
|
|
|
|
|
|
|
sub _rec { |
76
|
72
|
|
|
72
|
|
98
|
my ($node, $matcher) = @_; |
77
|
|
|
|
|
|
|
# $matcher: ArrayRef[Any] |
78
|
|
|
|
|
|
|
|
79
|
72
|
|
|
|
|
68
|
my ($current, @rest) = @{$matcher}; |
|
72
|
|
|
|
|
133
|
|
80
|
72
|
100
|
100
|
|
|
412
|
if (!defined $current) { |
|
|
100
|
66
|
|
|
|
|
|
|
100
|
|
|
|
|
|
|
|
100
|
|
|
|
|
|
81
|
|
|
|
|
|
|
#dbg("FINISHED"); |
82
|
1
|
|
|
|
|
4
|
return (); |
83
|
|
|
|
|
|
|
} elsif (ref($current) eq 'SCALAR' && $current == $DEEPFLAG) { |
84
|
|
|
|
|
|
|
#dbg("** mode"); |
85
|
7
|
|
|
|
|
19
|
return _recstar($node, \@rest); |
86
|
|
|
|
|
|
|
} elsif (ref($current) eq 'SCALAR' && $current == $PARENTFLAG) { |
87
|
2
|
50
|
66
|
|
|
20
|
if (ref($node) eq 'SCALAR' && $$node eq 1) { #t |
|
|
100
|
66
|
|
|
|
|
88
|
0
|
|
|
|
|
0
|
die "You cannot get a parent directory of root dir."; |
89
|
|
|
|
|
|
|
} elsif (ref($node) eq 'SCALAR' && $$node eq 0) { #f |
90
|
1
|
|
|
|
|
6
|
return _rec("..", \@rest); |
91
|
|
|
|
|
|
|
} else { |
92
|
1
|
|
|
|
|
8
|
return _rec("$node$SEPCHAR..", \@rest); |
93
|
|
|
|
|
|
|
} |
94
|
|
|
|
|
|
|
} elsif (@rest == 0) { |
95
|
|
|
|
|
|
|
#dbg("file name"); |
96
|
|
|
|
|
|
|
# (folder proc seed node (car matcher) #f) |
97
|
37
|
|
|
|
|
76
|
return glob_fs_fold($node, $current, 0); |
98
|
|
|
|
|
|
|
} else { |
99
|
25
|
|
|
|
|
54
|
return glob_fs_fold($node, $current, 1, \@rest); |
100
|
|
|
|
|
|
|
} |
101
|
|
|
|
|
|
|
} |
102
|
|
|
|
|
|
|
|
103
|
|
|
|
|
|
|
|
104
|
|
|
|
|
|
|
# /^home$/ のような固定の文字列の場合に高速化をはかるための最適化予定地なので、とりあえず undef をかえしておいても問題がない |
105
|
|
|
|
|
|
|
sub fixed_regexp_p { |
106
|
0
|
|
|
0
|
0
|
0
|
return undef; |
107
|
0
|
|
|
|
|
0
|
die "TBI" |
108
|
|
|
|
|
|
|
} |
109
|
|
|
|
|
|
|
|
110
|
|
|
|
|
|
|
# returns arrayref of seeds. |
111
|
|
|
|
|
|
|
sub glob_fs_fold { |
112
|
82
|
|
|
82
|
0
|
126
|
my ($node, $regexp, $non_leaf_p, $rest) = @_; |
113
|
|
|
|
|
|
|
|
114
|
82
|
|
|
|
|
71
|
my $prefix = do { |
115
|
82
|
100
|
|
|
|
214
|
if (ref $node eq 'SCALAR') { |
|
|
50
|
|
|
|
|
|
116
|
19
|
100
|
|
|
|
53
|
if ($$node eq 1) { #t |
|
|
50
|
|
|
|
|
|
117
|
1
|
|
|
|
|
2
|
$SEPCHAR |
118
|
|
|
|
|
|
|
} elsif ($$node eq '0') { #f |
119
|
18
|
|
|
|
|
27
|
''; |
120
|
|
|
|
|
|
|
} else { |
121
|
0
|
|
|
|
|
0
|
die "FATAL"; |
122
|
|
|
|
|
|
|
} |
123
|
|
|
|
|
|
|
} elsif ($node !~ m{/$}) { |
124
|
63
|
|
|
|
|
201
|
$node . '/'; |
125
|
|
|
|
|
|
|
} else { |
126
|
0
|
|
|
|
|
0
|
$node; |
127
|
|
|
|
|
|
|
} |
128
|
|
|
|
|
|
|
}; |
129
|
82
|
|
|
|
|
177
|
dbg("prefix: $prefix"); |
130
|
82
|
|
|
|
|
135
|
dbg("regxp: ", $regexp); |
131
|
82
|
0
|
33
|
|
|
219
|
if ($^O eq 'MSWin32' && ref $regexp eq 'SCALAR' && $$regexp =~ /^[a-zA-Z]\:$/) { |
|
|
|
33
|
|
|
|
|
132
|
0
|
|
|
|
|
0
|
return _rec($$regexp . '/', $rest); |
133
|
|
|
|
|
|
|
} |
134
|
82
|
50
|
33
|
|
|
200
|
if (ref $regexp eq 'SCALAR' && $regexp == $DIRFLAG) { |
135
|
0
|
0
|
|
|
|
0
|
if ($rest) { |
136
|
0
|
|
|
|
|
0
|
return _rec($prefix, $rest); |
137
|
|
|
|
|
|
|
} else { |
138
|
0
|
|
|
|
|
0
|
return ($prefix); |
139
|
|
|
|
|
|
|
} |
140
|
|
|
|
|
|
|
# } elsif (my $string_portion = fixed_regexp_p($regexp)) { # /^path$/ |
141
|
|
|
|
|
|
|
# die "TBI"; |
142
|
|
|
|
|
|
|
# my $full = $prefix . $string_portion; |
143
|
|
|
|
|
|
|
# if (-e $full && (!$non_leaf_p || -d $full)) { |
144
|
|
|
|
|
|
|
# $proc->($full, $seed); |
145
|
|
|
|
|
|
|
# } else { |
146
|
|
|
|
|
|
|
# $proc; |
147
|
|
|
|
|
|
|
# } |
148
|
|
|
|
|
|
|
} else { # normal regexp |
149
|
|
|
|
|
|
|
#dbg("normal regexp"); |
150
|
82
|
|
|
|
|
83
|
my $dir = do { |
151
|
82
|
100
|
100
|
|
|
339
|
if (ref($node) eq 'SCALAR' && $$node eq 1) { |
|
|
100
|
66
|
|
|
|
|
152
|
1
|
|
|
|
|
3
|
$SEPCHAR |
153
|
|
|
|
|
|
|
} elsif (ref($node) eq 'SCALAR' && $$node eq 0) { |
154
|
18
|
|
|
|
|
27
|
'.'; |
155
|
|
|
|
|
|
|
} else { |
156
|
63
|
|
|
|
|
99
|
$node; |
157
|
|
|
|
|
|
|
} |
158
|
|
|
|
|
|
|
}; |
159
|
|
|
|
|
|
|
#dbg("dir: $dir"); |
160
|
82
|
50
|
|
|
|
1800
|
opendir my $dirh, $dir or do { |
161
|
|
|
|
|
|
|
#dbg("cannot open dir: $dir: $!"); |
162
|
0
|
|
|
|
|
0
|
return (); |
163
|
|
|
|
|
|
|
}; |
164
|
82
|
|
|
|
|
84
|
my @ret; |
165
|
82
|
|
|
|
|
478281
|
while (defined(my $child = readdir($dirh))) { |
166
|
595
|
100
|
100
|
|
|
2465
|
next if $child eq '.' or $child eq '..'; |
167
|
431
|
|
|
|
|
375
|
my $full; |
168
|
|
|
|
|
|
|
#dbg("non-leaf: ", $non_leaf_p); |
169
|
431
|
100
|
66
|
|
|
3387
|
if (($child =~ $regexp) && ($full = $prefix . $child) && (!$non_leaf_p || -d $full)) { |
|
|
|
100
|
|
|
|
|
|
|
|
66
|
|
|
|
|
170
|
|
|
|
|
|
|
#dbg("matched: ", $regexp, $child, $full); |
171
|
73
|
100
|
|
|
|
122
|
if ($rest) { |
172
|
31
|
|
|
|
|
60
|
push @ret, _rec($full, $rest); |
173
|
|
|
|
|
|
|
} else { |
174
|
42
|
|
|
|
|
162
|
push @ret, $full; |
175
|
|
|
|
|
|
|
} |
176
|
|
|
|
|
|
|
# } else { |
177
|
|
|
|
|
|
|
#dbg("Don't match: $child"); |
178
|
|
|
|
|
|
|
} |
179
|
|
|
|
|
|
|
} |
180
|
82
|
|
|
|
|
1296
|
return @ret; |
181
|
|
|
|
|
|
|
} |
182
|
|
|
|
|
|
|
} |
183
|
|
|
|
|
|
|
|
184
|
|
|
|
|
|
|
sub glob_prepare_pattern { |
185
|
22
|
|
|
22
|
0
|
4969
|
my ($pattern) = @_; |
186
|
22
|
|
|
|
|
117
|
my @path = split $SEPCHAR, $pattern; |
187
|
|
|
|
|
|
|
|
188
|
22
|
100
|
|
|
|
142
|
my $is_absolute = $path[0] eq '' ? 1 : 0; |
189
|
22
|
100
|
|
|
|
51
|
if ($is_absolute) { |
190
|
3
|
|
|
|
|
6
|
shift @path; |
191
|
|
|
|
|
|
|
} |
192
|
22
|
50
|
33
|
|
|
88
|
if ($^O eq 'MSWin32' && $path[0] =~ /^[a-zA-Z]\:$/) { |
193
|
0
|
|
|
|
|
0
|
$is_absolute = 1; |
194
|
|
|
|
|
|
|
} |
195
|
|
|
|
|
|
|
|
196
|
|
|
|
|
|
|
@path = map { |
197
|
22
|
100
|
33
|
|
|
38
|
if ($_ eq '**') { |
|
60
|
50
|
|
|
|
270
|
|
|
|
100
|
|
|
|
|
|
|
|
100
|
|
|
|
|
|
|
|
50
|
|
|
|
|
|
198
|
8
|
|
|
|
|
18
|
$DEEPFLAG |
199
|
|
|
|
|
|
|
} elsif ($_ eq '') { |
200
|
0
|
|
|
|
|
0
|
$DIRFLAG |
201
|
|
|
|
|
|
|
} elsif ($_ eq '.') { |
202
|
|
|
|
|
|
|
() |
203
|
4
|
|
|
|
|
4
|
} elsif ($_ eq '..') { |
204
|
2
|
|
|
|
|
4
|
$PARENTFLAG |
205
|
|
|
|
|
|
|
} elsif ($^O eq 'MSWin32' && $_ =~ '^[a-zA-Z]\:$') { |
206
|
0
|
|
|
|
|
0
|
\$_ |
207
|
|
|
|
|
|
|
} else { |
208
|
46
|
|
|
|
|
78
|
glob_to_regex($_) # TODO: replace with original implementation? |
209
|
|
|
|
|
|
|
} |
210
|
|
|
|
|
|
|
} @path; |
211
|
|
|
|
|
|
|
|
212
|
22
|
|
|
|
|
75
|
return ( \$is_absolute, \@path ); |
213
|
|
|
|
|
|
|
} |
214
|
|
|
|
|
|
|
|
215
|
|
|
|
|
|
|
# this is not a private function. '**' was handled at glob_prepare_pattern() function. |
216
|
|
|
|
|
|
|
sub glob_to_regex { |
217
|
46
|
|
|
46
|
0
|
59
|
my $glob = shift; |
218
|
46
|
|
|
|
|
65
|
my $regex = glob_to_regex_string($glob); |
219
|
46
|
50
|
|
|
|
1323
|
return $NOCASE ? qr/^$regex$/i : qr/^$regex$/; |
220
|
|
|
|
|
|
|
} |
221
|
|
|
|
|
|
|
|
222
|
|
|
|
|
|
|
sub glob_to_regex_string { |
223
|
46
|
|
|
46
|
0
|
53
|
my $glob = shift; |
224
|
46
|
|
|
|
|
42
|
my ($regex, $in_curlies, $escaping); |
225
|
46
|
|
|
|
|
40
|
local $_; |
226
|
46
|
|
|
|
|
51
|
my $first_byte = 1; |
227
|
46
|
|
|
|
|
260
|
for ($glob =~ m/(.)/gs) { |
228
|
158
|
100
|
|
|
|
269
|
if ($first_byte) { |
229
|
46
|
50
|
|
|
|
75
|
if ($STRICT_LEADING_DOT) { |
230
|
46
|
100
|
|
|
|
106
|
$regex .= '(?=[^\.])' unless $_ eq '.'; |
231
|
|
|
|
|
|
|
} |
232
|
46
|
|
|
|
|
47
|
$first_byte = 0; |
233
|
|
|
|
|
|
|
} |
234
|
158
|
50
|
|
|
|
248
|
if ($_ eq '/') { |
235
|
0
|
|
|
|
|
0
|
$first_byte = 1; |
236
|
|
|
|
|
|
|
} |
237
|
158
|
100
|
66
|
|
|
2886
|
if ($_ eq '.' || $_ eq '(' || $_ eq ')' || $_ eq '|' || |
|
|
100
|
66
|
|
|
|
|
|
|
50
|
33
|
|
|
|
|
|
|
100
|
33
|
|
|
|
|
|
|
100
|
33
|
|
|
|
|
|
|
100
|
33
|
|
|
|
|
|
|
50
|
33
|
|
|
|
|
|
|
|
33
|
|
|
|
|
|
|
|
66
|
|
|
|
|
|
|
|
66
|
|
|
|
|
238
|
|
|
|
|
|
|
$_ eq '+' || $_ eq '^' || $_ eq '$' || $_ eq '@' || $_ eq '%' ) { |
239
|
11
|
|
|
|
|
18
|
$regex .= "\\$_"; |
240
|
|
|
|
|
|
|
} |
241
|
|
|
|
|
|
|
elsif ($_ eq '*') { |
242
|
19
|
50
|
|
|
|
87
|
$regex .= $escaping ? "\\*" : |
|
|
50
|
|
|
|
|
|
243
|
|
|
|
|
|
|
$STRICT_WILDCARD_SLASH ? "[^/]*" : ".*"; |
244
|
|
|
|
|
|
|
} |
245
|
|
|
|
|
|
|
elsif ($_ eq '?') { |
246
|
0
|
0
|
|
|
|
0
|
$regex .= $escaping ? "\\?" : |
|
|
0
|
|
|
|
|
|
247
|
|
|
|
|
|
|
$STRICT_WILDCARD_SLASH ? "[^/]" : "."; |
248
|
|
|
|
|
|
|
} |
249
|
|
|
|
|
|
|
elsif ($_ eq '{') { |
250
|
1
|
50
|
|
|
|
5
|
$regex .= $escaping ? "\\{" : "("; |
251
|
1
|
50
|
|
|
|
4
|
++$in_curlies unless $escaping; |
252
|
|
|
|
|
|
|
} |
253
|
|
|
|
|
|
|
elsif ($_ eq '}' && $in_curlies) { |
254
|
1
|
50
|
|
|
|
4
|
$regex .= $escaping ? "}" : ")"; |
255
|
1
|
50
|
|
|
|
3
|
--$in_curlies unless $escaping; |
256
|
|
|
|
|
|
|
} |
257
|
|
|
|
|
|
|
elsif ($_ eq ',' && $in_curlies) { |
258
|
1
|
50
|
|
|
|
4
|
$regex .= $escaping ? "," : "|"; |
259
|
|
|
|
|
|
|
} |
260
|
|
|
|
|
|
|
elsif ($_ eq "\\") { |
261
|
0
|
0
|
|
|
|
0
|
if ($escaping) { |
262
|
0
|
|
|
|
|
0
|
$regex .= "\\\\"; |
263
|
0
|
|
|
|
|
0
|
$escaping = 0; |
264
|
|
|
|
|
|
|
} |
265
|
|
|
|
|
|
|
else { |
266
|
0
|
|
|
|
|
0
|
$escaping = 1; |
267
|
|
|
|
|
|
|
} |
268
|
0
|
|
|
|
|
0
|
next; |
269
|
|
|
|
|
|
|
} |
270
|
|
|
|
|
|
|
else { |
271
|
125
|
|
|
|
|
134
|
$regex .= $_; |
272
|
125
|
|
|
|
|
136
|
$escaping = 0; |
273
|
|
|
|
|
|
|
} |
274
|
158
|
|
|
|
|
218
|
$escaping = 0; |
275
|
|
|
|
|
|
|
} |
276
|
|
|
|
|
|
|
|
277
|
46
|
|
|
|
|
123
|
return $regex; |
278
|
|
|
|
|
|
|
} |
279
|
|
|
|
|
|
|
|
280
|
|
|
|
|
|
|
1; |
281
|
|
|
|
|
|
|
__END__ |