File Coverage

blib/lib/Code/TidyAll/Zglob.pm
Criterion Covered Total %
statement 85 140 60.7
branch 49 106 46.2
condition 34 75 45.3
subroutine 13 14 92.8
pod 0 7 0.0
total 181 342 52.9


line stmt bran cond sub pod time code
1             package Code::TidyAll::Zglob;
2 27     27   194 use strict;
  27         67  
  27         995  
3 27     27   165 use warnings 'all', FATAL => 'recursion';
  27         69  
  27         1228  
4 27     27   739 use 5.008008;
  27         102  
5             our $VERSION = '0.83';
6 27     27   176 use base qw(Exporter);
  27         66  
  27         4382  
7              
8             our @EXPORT = qw(zglob);
9              
10 27     27   224 use File::Basename;
  27         109  
  27         56476  
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 43     43 0 382 my ($pattern) = @_;
23             #dbg("FOLDING: $pattern");
24             # support ~tokuhirom/
25 43 50       160 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 43         289 $pattern =~ s!^(\~[^$SEPCHAR]*)![glob($1)]->[0]!e;
  0         0  
30             }
31 43         158 my ($node, $matcher) = glob_prepare_pattern($pattern);
32             # $node : \0 if absolute path, \1 if relative.
33              
34             #dbg("pattern: ", $node, $matcher);
35 43         159 return _rec($node, $matcher, []);
36             }
37              
38             sub dbg(@) {
39 392 50   392 0 795 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 .= '<<undef>>';
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 48     48   126 my ($node, $matcher) = @_;
65             #dbg("recstar: ", $node, $matcher, $seed);
66             return (
67             _rec( $node, $matcher ),
68             (
69 48         147 map { _recstar( $_, $matcher ) }
  15         74  
70             glob_fs_fold( $node, qr{^[^.].*$}, 1 )
71             )
72             );
73             }
74              
75             sub _rec {
76 181     181   412 my ($node, $matcher) = @_;
77             # $matcher: ArrayRef[Any]
78              
79 181         290 my ($current, @rest) = @{$matcher};
  181         398  
80 181 50 66     1233 if (!defined $current) {
    100 33        
    50          
    100          
81             #dbg("FINISHED");
82 0         0 return ();
83             } elsif (ref($current) eq 'SCALAR' && $current == $DEEPFLAG) {
84             #dbg("** mode");
85 33         119 return _recstar($node, \@rest);
86             } elsif (ref($current) eq 'SCALAR' && $current == $PARENTFLAG) {
87 0 0 0     0 if (ref($node) eq 'SCALAR' && $$node eq 1) { #t
    0 0        
88 0         0 die "You cannot get a parent directory of root dir.";
89             } elsif (ref($node) eq 'SCALAR' && $$node eq 0) { #f
90 0         0 return _rec("..", \@rest);
91             } else {
92 0         0 return _rec("$node$SEPCHAR..", \@rest);
93             }
94             } elsif (@rest == 0) {
95             #dbg("file name");
96             # (folder proc seed node (car matcher) #f)
97 52         179 return glob_fs_fold($node, $current, 0);
98             } else {
99 96         280 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 196     196 0 473 my ($node, $regexp, $non_leaf_p, $rest) = @_;
113              
114 196         304 my $prefix = do {
115 196 100       581 if (ref $node eq 'SCALAR') {
    50          
116 43 50       141 if ($$node eq 1) { #t
    0          
117 43         142 $SEPCHAR
118             } elsif ($$node eq '0') { #f
119 0         0 '';
120             } else {
121 0         0 die "FATAL";
122             }
123             } elsif ($node !~ m{/$}) {
124 153         337 $node . '/';
125             } else {
126 0         0 $node;
127             }
128             };
129 196         588 dbg("prefix: $prefix");
130 196         484 dbg("regxp: ", $regexp);
131 196 0 33     653 if ($^O eq 'MSWin32' && ref $regexp eq 'SCALAR' && $$regexp =~ /^[a-zA-Z]\:$/) {
      33        
132 0         0 return _rec($$regexp . '/', $rest);
133             }
134 196 50 33     509 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 196         290 my $dir = do {
151 196 100 66     641 if (ref($node) eq 'SCALAR' && $$node eq 1) {
    50 33        
152 43         98 $SEPCHAR
153             } elsif (ref($node) eq 'SCALAR' && $$node eq 0) {
154 0         0 '.';
155             } else {
156 153         291 $node;
157             }
158             };
159             #dbg("dir: $dir");
160 196 50       5574 opendir my $dirh, $dir or do {
161             #dbg("cannot open dir: $dir: $!");
162 0         0 return ();
163             };
164 196         560 my @ret;
165 196         9731 while (defined(my $child = readdir($dirh))) {
166 1817 100 100     6354 next if $child eq '.' or $child eq '..';
167 1425         1766 my $full;
168             #dbg("non-leaf: ", $non_leaf_p);
169 1425 100 66     10026 if (($child =~ $regexp) && ($full = $prefix . $child) && (!$non_leaf_p || -d $full)) {
      100        
      100        
170             #dbg("matched: ", $regexp, $child, $full);
171 180 100       479 if ($rest) {
172 90         338 push @ret, _rec($full, $rest);
173             } else {
174 90         413 push @ret, $full;
175             }
176             # } else {
177             #dbg("Don't match: $child");
178             }
179             }
180 196         3555 return @ret;
181             }
182             }
183              
184             sub glob_prepare_pattern {
185 43     43 0 99 my ($pattern) = @_;
186 43         654 my @path = split $SEPCHAR, $pattern;
187              
188 43 50       193 my $is_absolute = $path[0] eq '' ? 1 : 0;
189 43 50       121 if ($is_absolute) {
190 43         82 shift @path;
191             }
192 43 50 33     172 if ($^O eq 'MSWin32' && $path[0] =~ /^[a-zA-Z]\:$/) {
193 0         0 $is_absolute = 1;
194             }
195              
196             @path = map {
197 43 100 33     97 if ($_ eq '**') {
  172 50       793  
    50          
    50          
    50          
198 33         69 $DEEPFLAG
199             } elsif ($_ eq '') {
200 0         0 $DIRFLAG
201             } elsif ($_ eq '.') {
202             ()
203 0         0 } elsif ($_ eq '..') {
204 0         0 $PARENTFLAG
205             } elsif ($^O eq 'MSWin32' && $_ =~ '^[a-zA-Z]\:$') {
206 0         0 \$_
207             } else {
208 139         279 glob_to_regex($_) # TODO: replace with original implementation?
209             }
210             } @path;
211              
212 43         185 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 139     139 0 290 my $glob = shift;
218 139         267 my $regex = glob_to_regex_string($glob);
219 139 50       3245 return $NOCASE ? qr/^$regex$/i : qr/^$regex$/;
220             }
221              
222             sub glob_to_regex_string {
223 139     139 0 223 my $glob = shift;
224 139         243 my ($regex, $in_curlies, $escaping);
225 139         178 local $_;
226 139         197 my $first_byte = 1;
227 139         992 for ($glob =~ m/(.)/gs) {
228 1126 100       1874 if ($first_byte) {
229 139 50       270 if ($STRICT_LEADING_DOT) {
230 139 50       391 $regex .= '(?=[^\.])' unless $_ eq '.';
231             }
232 139         212 $first_byte = 0;
233             }
234 1126 50       1839 if ($_ eq '/') {
235 0         0 $first_byte = 1;
236             }
237 1126 100 66     12198 if ($_ eq '.' || $_ eq '(' || $_ eq ')' || $_ eq '|' ||
    100 66        
    50 33        
    50 33        
    50 33        
    50 33        
    50 33        
      33        
      33        
      33        
238             $_ eq '+' || $_ eq '^' || $_ eq '$' || $_ eq '@' || $_ eq '%' ) {
239 32         65 $regex .= "\\$_";
240             }
241             elsif ($_ eq '*') {
242 39 50       133 $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 0 0       0 $regex .= $escaping ? "\\{" : "(";
251 0 0       0 ++$in_curlies unless $escaping;
252             }
253             elsif ($_ eq '}' && $in_curlies) {
254 0 0       0 $regex .= $escaping ? "}" : ")";
255 0 0       0 --$in_curlies unless $escaping;
256             }
257             elsif ($_ eq ',' && $in_curlies) {
258 0 0       0 $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 1055         1424 $regex .= $_;
272 1055         1351 $escaping = 0;
273             }
274 1126         1560 $escaping = 0;
275             }
276              
277 139         471 return $regex;
278             }
279              
280             1;
281              
282             # ABSTRACT: A borged copy of File::Zglob
283              
284             __END__
285              
286             =pod
287              
288             =encoding UTF-8
289              
290             =head1 NAME
291              
292             Code::TidyAll::Zglob - A borged copy of File::Zglob
293              
294             =head1 VERSION
295              
296             version 0.83
297              
298             =head1 DESCRIPTION
299              
300             File::Zglob has not been installable under Perl 5.26 and this has been an open
301             issue for 8+ months
302             (https://rt.cpan.org/Public/Bug/Display.html?id=120445). This module will go
303             away once File::Zglob is installable.
304              
305             =head1 LICENSE
306              
307             Copyright (C) Tokuhiro Matsuno
308              
309             This library is free software; you can redistribute it and/or modify
310             it under the same terms as Perl itself.
311              
312             =head1 SUPPORT
313              
314             Bugs may be submitted at L<https://github.com/houseabsolute/perl-code-tidyall/issues>.
315              
316             =head1 SOURCE
317              
318             The source code repository for Code-TidyAll can be found at L<https://github.com/houseabsolute/perl-code-tidyall>.
319              
320             =head1 AUTHORS
321              
322             =over 4
323              
324             =item *
325              
326             Jonathan Swartz <swartz@pobox.com>
327              
328             =item *
329              
330             Dave Rolsky <autarch@urth.org>
331              
332             =back
333              
334             =head1 COPYRIGHT AND LICENSE
335              
336             This software is copyright (c) 2011 - 2022 by Jonathan Swartz.
337              
338             This is free software; you can redistribute it and/or modify it under
339             the same terms as the Perl 5 programming language system itself.
340              
341             The full text of the license can be found in the
342             F<LICENSE> file included with this distribution.
343              
344             =cut