File Coverage

lib/Stable/Module.pm
Criterion Covered Total %
statement 229 334 68.5
branch 49 124 39.5
condition 7 30 23.3
subroutine 30 40 75.0
pod 0 1 0.0
total 315 529 59.5


line stmt bran cond sub pod time code
1             package Stable::Module;
2             ######################################################################
3             #
4             # Stable::Module - frequently used modules on Perl5 application
5             #
6             # http://search.cpan.org/dist/Stable-Module/
7             #
8             # Copyright (c) 2014, 2016, 2017, 2018, 2019 INABA Hitoshi in a CPAN
9             ######################################################################
10              
11             $VERSION = '0.09';
12             $VERSION = $VERSION;
13              
14 25     25   110161 use 5.00503;
  25         208  
15 25     25   114 use strict;
  25         38  
  25         828  
16 25 50   25   552 BEGIN { $INC{'warnings.pm'} = '' if $] < 5.006 }; use warnings; $^W=1;
  25     25   121  
  25         37  
  25         1361  
17 25     25   132 use Carp;
  25         38  
  25         1404  
18 25     25   131 use FindBin;
  25         46  
  25         744  
19 25     25   10440 use IO::File;
  25         197611  
  25         2779  
20              
21 25     25   192 use vars qw($re_char $hide_stderr);
  25         44  
  25         4041  
22              
23             sub VERSION {
24 0     0 0 0 my($self,$version) = @_;
25 0 0       0 if ($version != $Stable::Module::VERSION) {
26 0         0 my($package,$filename,$line) = caller;
27 0         0 die "$filename requires Stable::Module $version, this is version $Stable::Module::VERSION, stopped at $filename line $line.\n";
28             }
29             }
30              
31             sub BEGIN {
32 25 50 33 25   240 if (($^O eq 'MSWin32') and (defined $ENV{'OS'}) and ($ENV{'OS'} eq 'Windows_NT')) {
      33        
33 0         0 $hide_stderr = '2>NUL';
34             }
35             else {
36 25         8760 $hide_stderr = '';
37             }
38             }
39              
40             sub import {
41 25     25   259 my($caller,$filename,$line) = caller;
42              
43             # verify that we're called correctly so that strictures will work.
44 25 50       192 if (__FILE__ !~ m{ \b Stable[/\\]Module\.pm \z}x) {
45 0         0 die "Incorrect use of module '${\__PACKAGE__}' at $filename line $line.\n";
  0         0  
46             }
47              
48             # get /./ (dot: one character) regexp
49 25         126 $re_char = qr/[\x00-\xFF]/;
50              
51             # Code Page Identifiers (Microsoft Windows)
52             # Identifier .NET Name Additional information
53 25         306 my %re_char = (
54              
55             # Sjis shift_jis ANSI/OEM Japanese; Japanese (Shift-JIS)
56             '932' => qr{[\x81-\x9F\xE0-\xFC][\x00-\xFF]|[\x00-\xFF]},
57              
58             # GBK gb2312 ANSI/OEM Simplified Chinese (PRC, Singapore); Chinese Simplified (GB2312)
59             '936' => qr{[\x81-\xFE][\x00-\xFF]|[\x00-\xFF]},
60              
61             # UHC ks_c_5601-1987 ANSI/OEM Korean (Unified Hangul Code)
62             '949' => qr{[\x81-\xFE][\x00-\xFF]|[\x00-\xFF]},
63              
64             # Big5Plus big5 ANSI/OEM Traditional Chinese (Taiwan; Hong Kong SAR, PRC); Chinese Traditional (Big5)
65             '950' => qr{[\x81-\xFE][\x00-\xFF]|[\x00-\xFF]},
66              
67             # Big5HKSCS HKSCS support on top of traditional Chinese Windows
68             '951' => qr{[\x81-\xFE][\x00-\xFF]|[\x00-\xFF]},
69              
70             # GB18030 GB18030 Windows XP and later: GB18030 Simplified Chinese (4 byte); Chinese Simplified (GB18030)
71             '54936' => qr{[\x81-\xFE][\x30-\x39][\x81-\xFE][\x30-\x39]|[\x81-\xFE][\x00-\xFF]|[\x00-\xFF]},
72             );
73              
74 25         50 my $codepage = 'File System Safe';
75 25 50       119 if ($^O =~ m{\A (?:MSWin32|NetWare|symbian|dos) \z}oxms) {
76 0         0 $codepage = (qx{chcp} =~ m{ ([0-9]{3,5}) \Z}oxms)[0];
77 0   0     0 $re_char = $re_char{$codepage} || $re_char;
78             }
79              
80 25     25   170 no strict 'refs';
  25         49  
  25         73024  
81              
82             # use Cwd qw(cwd);
83 25         118 require Cwd;
84 25 50       99 if ($^O =~ m{\A (?:MSWin32|NetWare|symbian|dos) \z}oxms) {
85 0     0   0 *{$caller.'::cwd'} = sub { _dos_path(Cwd::cwd) };
  0         0  
  0         0  
86             }
87             else {
88 25         54 *{$caller.'::cwd'} = \&Cwd::cwd;
  25         140  
89             }
90              
91             # use FindBin qw($Bin);
92 25         90 require FindBin;
93 25 50       91 if ($^O =~ m{\A (?:MSWin32|NetWare|symbian|dos) \z}oxms) {
94 0         0 $FindBin::Bin = _dos_path($FindBin::Bin);
95             }
96 25         42 *{$caller.'::Bin'} = \$FindBin::Bin;
  25         95  
97              
98             # use File::Basename qw(fileparse basename dirname);
99             # use internal routines always for compatibility -- for example, perl 5.6 has incompatible File::Basename
100 25         45 if (1 or exists $re_char{$codepage}) {
101 25         248 $INC{'File/Basename.pm'} = join '/', $FindBin::Bin, __FILE__;
102 25         75 *{$caller.'::fileparse'} = \&_fileparse;
  25         125  
103 25         52 *{$caller.'::basename'} = \&_basename;
  25         86  
104 25         49 *{$caller.'::dirname'} = \&_dirname;
  25         75  
105             }
106             else {
107             require File::Basename;
108             *{$caller.'::fileparse'} = \&File::Basename::fileparse;
109             *{$caller.'::basename'} = \&File::Basename::basename;
110             *{$caller.'::dirname'} = \&File::Basename::dirname;
111             }
112              
113             # use File::Path qw(mkpath rmtree);
114 25 50       79 if (exists $re_char{$codepage}) {
115 0         0 $INC{'File/Path.pm'} = join '/', $FindBin::Bin, __FILE__;
116 0         0 *{$caller.'::mkpath'} = \&_mkpath;
  0         0  
117 0         0 *{$caller.'::rmtree'} = \&_rmtree;
  0         0  
118             }
119             else {
120 25         97 require File::Path;
121 25         48 *{$caller.'::mkpath'} = \&File::Path::mkpath;
  25         181  
122 25         73 *{$caller.'::rmtree'} = \&File::Path::rmtree;
  25         174  
123             }
124              
125             # use File::Copy qw(copy move);
126 25 50       91 if (exists $re_char{$codepage}) {
127 0         0 $INC{'File/Copy.pm'} = join '/', $FindBin::Bin, __FILE__;
128 0         0 *{$caller.'::copy'} = \&_copy;
  0         0  
129 0         0 *{$caller.'::move'} = \&_move;
  0         0  
130             }
131             else {
132 25         11215 require File::Copy;
133 25         95178 *{$caller.'::copy'} = \&File::Copy::copy;
  25         614  
134 25         78 *{$caller.'::move'} = \&File::Copy::move;
  25         97  
135             }
136              
137             # use File::Compare qw(compare);
138 25         14656 require File::Compare;
139 25         20201 *{$caller.'::compare'} = \&File::Compare::compare;
  25         128  
140              
141             # use Sys::Hostname qw(hostname);
142 25         8950 require Sys::Hostname;
143 25         22749 *{$caller.'::hostname'} = \&Sys::Hostname::hostname;
  25         141  
144              
145             # use Time::Local qw(timelocal);
146 25         9438 require Time::Local;
147 25         72993 *{$caller.'::timelocal'} = \&Time::Local::timelocal;
  25         163  
148              
149             # internal List::Util::reduce
150             sub _reduce (&@) {
151 5     5   12 my $coderef = shift @_;
152 5         11 local $a = shift @_;
153 5         13 for $b (@_) {
154 77         93 $a = $coderef->();
155             }
156 5         13 return $a;
157             }
158              
159             # use List::Util qw(reduce first shuffle max maxstr min minstr sum);
160 25         59 *{$caller.'::first'} = \&_first;
  25         2722  
161 25         1347 *{$caller.'::shuffle'} = \&_shuffle;
  25         1390  
162 25 50   1   1597 *{$caller.'::max'} = sub { _reduce { $a > $b ? $a : $b } @_ };
  25         1454  
  1         12  
  9         14  
163 25 50   1   83 *{$caller.'::maxstr'} = sub { _reduce { $a gt $b ? $a : $b } @_ };
  25         93  
  1         18  
  25         42  
164 25 50   1   137 *{$caller.'::min'} = sub { _reduce { $a < $b ? $a : $b } @_ };
  25         89  
  1         13  
  9         14  
165 25 50   25   152 *{$caller.'::minstr'} = sub { _reduce { $a lt $b ? $a : $b } @_ };
  25         79  
  1         19  
  25         41  
166 25     9   68 *{$caller.'::sum'} = sub { _reduce { $a + $b } @_ };
  25         87  
  1         15  
  9         12  
167              
168             # use List::MoreUtils qw(all any none notall uniq);
169 25 100   4   85 *{$caller.'::all'} = sub (&@) { my $coderef = shift @_; for (@_) { return 0 if not $coderef->($_) } return 1; }; # All arguments are true
  25         76  
  4         673  
  4         8  
  10         26  
  1         5  
170 25 100   4   80 *{$caller.'::any'} = sub (&@) { my $coderef = shift @_; for (@_) { return 1 if $coderef->($_) } return 0; }; # One argument is true
  25         90  
  4         112  
  4         9  
  10         21  
  1         4  
171 25 100   4   76 *{$caller.'::none'} = sub (&@) { my $coderef = shift @_; for (@_) { return 0 if $coderef->($_) } return 1; }; # All arguments are false
  25         176  
  4         112  
  4         8  
  10         23  
  1         4  
172 25 100   4   158 *{$caller.'::notall'} = sub (&@) { my $coderef = shift @_; for (@_) { return 1 if not $coderef->($_) } return 0; }; # One argument is false
  25         107  
  4         112  
  4         8  
  10         23  
  1         4  
173 25         72 *{$caller.'::uniq'} = \&_uniq;
  25         80  
174              
175             # use feature qw(say);
176 25         51 *{$caller.'::say'} = \&_say;
  25         79  
177 25 50       31128 *IO::Handle::say = \&_say if not defined(*IO::Handle::say);
178             }
179              
180       0     sub unimport {
181             # nothing
182             }
183              
184             sub _fileparse {
185 8     8   322 my($fullname,@suffixlist) = @_;
186              
187 8 50       19 if (not defined $fullname) {
188 0         0 croak "fileparse(): need a valid pathname";
189             }
190 8         14 my $taint = substr($fullname,0,0); # Is $fullname tainted?
191              
192 8         10 my $dirname = '';
193 8         10 my $subdir = '';
194 8         64 while ($fullname =~ m{\G ($re_char) }oxmsgc) {
195 163         204 my $char = $1;
196 163 100       237 if ($char =~ m{\A [:\\/] \z}oxms) {
197 31         42 $dirname .= $subdir;
198 31         32 $dirname .= $char;
199 31         61 $subdir = '';
200             }
201             else {
202 132         262 $subdir .= $char;
203             }
204             }
205 8         10 my $name = $subdir;
206              
207 8 50 33     36 if (($dirname eq '') or ($dirname =~ m{ : \z}oxms)) {
208 0         0 $dirname .= '.\\';
209             }
210              
211             # ignore case of name
212 8         58 my @char = $name =~ m{\G ($re_char) }oxmsgc;
213 8         16 my $name_lc = join '', map { lcfirst } @char;
  47         75  
214 8         14 my $suffix = '';
215 8 100       15 if (@suffixlist) {
216 3         6 for my $s (@suffixlist) {
217              
218             # ignore case of suffix
219 3         23 my @char = $s =~ m{\G ($re_char) }oxmsgc;
220 3         5 my $s_lc = join '', map { lcfirst } @char;
  11         16  
221 3 100       11 if (substr($name_lc,-length($s_lc),length($s_lc)) eq $s_lc) {
222 2         3 $taint .= substr($s,0,0);
223 2         5 $suffix = substr($name,-length($s_lc),length($s_lc)) . $suffix;
224 2         7 $name = substr($name,0,length($name)-length($s_lc));
225             }
226             }
227             }
228              
229             # Ensure taint is propagated from the path to its pieces.
230 8         15 $name .= $taint;
231 8         10 $dirname .= $taint;
232 8         9 $suffix .= $taint;
233 8 50       14 if (wantarray) {
234 8         63 return ($name,$dirname,$suffix);
235             }
236             else {
237 0         0 return $name;
238             }
239             }
240              
241             sub _basename {
242 5     5   913 my($fullname,@suffixlist) = @_;
243              
244 5 50       15 if (not defined $fullname) {
245 0         0 croak "basename(): need a valid pathname";
246             }
247 5         10 my $taint = substr($fullname,0,0); # Is $fullname tainted?
248              
249             # From BSD basename(1)
250             # The basename utility deletes any prefix ending with the last slash '/'
251             # character present in string (after first stripping trailing slashes)
252 5         9 $fullname = _strip_trailing_sep($fullname);
253              
254 5         6 my $dirname = '';
255 5         7 my $subdir = '';
256 5         25 while ($fullname =~ m{\G ($re_char) }oxmsgc) {
257 76         103 my $char = $1;
258 76 100       113 if ($char =~ m{\A [:\\/] \z}oxms) {
259 15         17 $dirname .= $subdir;
260 15         15 $dirname .= $char;
261 15         59 $subdir = '';
262             }
263             else {
264 61         125 $subdir .= $char;
265             }
266             }
267 5         8 my $name = $subdir;
268              
269 5 50 33     21 if (($dirname eq '') or ($dirname =~ m{ : \z}oxms)) {
270 0         0 $dirname .= '.\\';
271             }
272              
273             # ignore case of name
274 5         33 my @char = $name =~ m{\G ($re_char) }oxmsgc;
275 5         9 my $name_lc = join '', map { lcfirst } @char;
  27         44  
276 5         8 my $suffix = '';
277 5 100       12 if (@suffixlist) {
278 2         4 for my $s (@suffixlist) {
279              
280             # ignore case of suffix
281 2         21 my @char = $s =~ m{\G ($re_char) }oxmsgc;
282 2         4 my $s_lc = join '', map { lcfirst } @char;
  8         13  
283 2 100       7 if (substr($name_lc,-length($s_lc),length($s_lc)) eq $s_lc) {
284 1         3 $taint .= substr($s,0,0);
285 1         3 $suffix = substr($name,-length($s_lc),length($s_lc)) . $suffix;
286 1         4 $name = substr($name,0,length($name)-length($s_lc));
287             }
288             }
289             }
290              
291             # From BSD basename(1)
292             # The suffix is not stripped if it is identical to the remaining
293             # characters in string.
294 5 50 33     11 if (($name eq '') and ($suffix ne '')) {
295 0         0 $name = $suffix;
296             }
297              
298             # Ensure that basename '/' == '/'
299 5 50       7 if ($name eq '') {
300 0         0 $name = $dirname;
301             }
302              
303             # Ensure taint is propagated from the path to its pieces.
304 5         9 $name .= $taint;
305 5         18 return $name;
306             }
307              
308             sub _dirname {
309 2     2   73 my($fullname) = @_;
310              
311 2 50       6 if (not defined $fullname) {
312 0         0 croak "dirname(): need a valid pathname";
313             }
314 2         4 my $taint = substr($fullname,0,0); # Is $fullname tainted?
315              
316 2         4 my($basename,$dirname) = _fileparse($fullname);
317 2         6 $dirname = _strip_trailing_sep($dirname);
318              
319 2 50       5 if ($basename eq '') {
320 0         0 ($basename,$dirname) = _fileparse($dirname);
321 0         0 $dirname = _strip_trailing_sep($dirname);
322             }
323              
324             # Ensure taint is propagated from the path to its pieces.
325 2         3 $dirname .= $taint;
326 2         5 return $dirname;
327             }
328              
329             sub _strip_trailing_sep {
330 7     7   12 my($dirname) = @_;
331              
332 7         121 my @char = $dirname =~ m{\G ([\\\/]+|$re_char) }oxmsgc;
333 7 50       121 if (scalar(@char) >= 2) {
334 7 100 66     33 if (($char[-1] =~ m{\A [\\\/]+ \z}oxms) and ($char[-2] ne ':')) {
335 3         5 pop @char;
336             }
337             }
338              
339 7         33 return join '', @char;
340             }
341              
342             sub _mkpath {
343 0     0   0 my $path = _dos_path($_[0]);
344              
345 0 0 0     0 if (_is_directory($_[0])) { # *NOT* _is_directory($path)
    0          
346 0         0 return 1;
347             }
348              
349             # cmd.exe on Windows NT, Windows 2000, Windows XP, Windows 2003 or later
350             elsif ((defined $ENV{'OS'}) and ($ENV{'OS'} eq 'Windows_NT')) {
351 0 0       0 if (CORE::system(qq{cmd.exe /E:ON /C mkdir $path >NUL $hide_stderr}) == 0) {
352 0         0 return 1;
353             }
354             }
355              
356             # COMMAND.COM on Windows 95, Windows 98, Windows 98 Second Edition, Windows Millennium Edition
357             else {
358 0         0 my @subdir = ();
359 0         0 my $i = 0;
360 0         0 while ($_[0] =~ m{\G ($re_char) }oxmsgc) {
361 0         0 my $char = $1;
362 0 0       0 if ($char =~ m{\A [\\/] \z}oxms) {
363 0         0 $i++;
364             }
365             else {
366 0         0 $subdir[$i] .= $char;
367             }
368             }
369 0 0       0 if (@subdir >= 2) {
370 0         0 for my $i (0 .. $#subdir-1) {
371 0         0 my $path = _dos_path(join '\\',@subdir[0..$i]);
372 0 0       0 if (not _is_directory($path)) {
373 0         0 CORE::system(qq{mkdir $path >NUL});
374             }
375             }
376             }
377 0         0 my $path = _dos_path(join '\\',@subdir);
378 0 0       0 if (CORE::system(qq{mkdir $path >NUL}) == 0) {
379 0         0 return 1;
380             }
381             }
382              
383 0 0       0 if (exists $INC{'Strict/Perl.pm'}) {
384 0         0 croak "mkpath: $path";
385             }
386             else {
387 0         0 return undef;
388             }
389             }
390              
391             sub _rmtree {
392 0     0   0 my $root = _dos_path($_[0]);
393              
394 0 0 0     0 if (not _is_directory($_[0])) { # *NOT* not _is_directory($root)
    0          
395 0         0 return 1;
396             }
397              
398             # cmd.exe on Windows NT, Windows 2000, Windows XP, Windows 2003 or later
399             elsif ((defined $ENV{'OS'}) and ($ENV{'OS'} eq 'Windows_NT')) {
400 0 0       0 if (CORE::system(qq{rmdir /S /Q $root >NUL $hide_stderr}) == 0) {
401 0         0 return 1;
402             }
403             }
404              
405             # COMMAND.COM on Windows 95, Windows 98, Windows 98 Second Edition, Windows Millennium Edition
406             else {
407 0         0 my @file = split /\n/, qx{dir /s /b /a-d $root};
408 0         0 for my $file (@file) {
409 0         0 $file = _dos_path($file);
410 0         0 CORE::system(qq{del $file >NUL});
411             }
412 0         0 my @dir = split /\n/, qx{dir /s /b /ad $root};
413 0         0 for my $dir (sort { length($b) <=> length($a) } @dir) {
  0         0  
414 0         0 $dir = _dos_path($dir);
415 0         0 CORE::system(qq{rmdir $dir >NUL});
416             }
417 0 0       0 if (CORE::system(qq{rmdir $root >NUL}) == 0) {
418 0         0 return 1;
419             }
420             }
421              
422 0 0       0 if (exists $INC{'Strict/Perl.pm'}) {
423 0         0 croak "rmdir: $root";
424             }
425             else {
426 0         0 return undef;
427             }
428             }
429              
430             sub _copy {
431 0     0   0 my $source = _dos_path($_[0]);
432 0         0 my $dest = _dos_path($_[1]);
433              
434 0 0       0 if (CORE::system(qq{copy /Y $source $dest >NUL $hide_stderr}) == 0) {
    0          
435 0         0 return 1;
436             }
437             elsif (exists $INC{'Strict/Perl.pm'}) {
438 0         0 croak "copy: $source $dest";
439             }
440             else {
441 0         0 return undef;
442             }
443             }
444              
445             sub _move {
446 0     0   0 my $source = _dos_path($_[0]);
447 0         0 my $dest = _dos_path($_[1]);
448              
449 0 0       0 if (CORE::system(qq{move /Y $source $dest >NUL $hide_stderr}) == 0) {
    0          
450 0         0 return 1;
451             }
452             elsif (exists $INC{'Strict/Perl.pm'}) {
453 0         0 croak "move: $source $dest";
454             }
455             else {
456 0         0 return undef;
457             }
458             }
459              
460             sub _dos_path {
461 0     0   0 my($path) = @_;
462              
463 0         0 my @char = $path =~ m{\G ($re_char) }oxmsg;
464 0 0       0 $path = join '', map {{'/' => '\\'}->{$_} || $_} @char;
  0         0  
465 0 0       0 $path = qq{"$path"} if $path =~ m{ };
466 0         0 return $path;
467             }
468              
469             sub _is_directory {
470 0     0   0 my($unknown) = @_;
471              
472 0 0       0 if (-e $unknown) {
    0          
473 0         0 return -d _;
474             }
475             elsif (_MSWin32_5Cended_path($unknown)) {
476 0         0 return -d "$unknown/.";
477             }
478 0         0 return undef;
479             }
480              
481             sub _MSWin32_5Cended_path {
482 0 0 0 0   0 if ((@_ >= 1) and ($_[0] ne '')) {
483 0 0       0 if ($^O =~ /\A (?: MSWin32 | NetWare | symbian | dos ) \z/oxms) {
484 0         0 my @char = $_[0] =~ m{\G ($re_char) }oxmsg;
485 0 0       0 if ($char[-1] =~ m{ \x5C \z}oxms) {
486 0         0 return 1;
487             }
488             }
489             }
490 0         0 return undef;
491             }
492              
493             sub _first(&@) {
494 1     1   13 my $coderef = shift @_;
495 1         3 for (@_) {
496 4 100       13 if ($coderef->()) {
497 1         5 return $_;
498             }
499             }
500 0         0 return undef;
501             }
502              
503             sub _shuffle(@) {
504 1     1   16 my @a = \(@_);
505 1         2 my $n;
506 1         2 my $i=@_;
507 1         3 return map { $n = rand($i--); (${$a[$n]}, $a[$n] = $a[$i])[0]; } @_;
  26         52  
  26         28  
  26         44  
508             }
509              
510             sub _uniq {
511 1     1   10 my %seen = ();
512 1         3 return grep { not $seen{$_}++ } @_;
  10         22  
513             }
514              
515             sub _say {
516 1     1   51 my $currfh = select();
517 1         2 my $handle;
518             {
519 25     25   213 no strict 'refs';
  25         50  
  25         2350  
  1         1  
520 1 50       8 $handle = defined(fileno($_[0])) ? shift : \*$currfh;
521             }
522 1 50       4 @_ = ($_) unless @_;
523 1         1 return print {$handle} @_, "\n";
  1         52  
524             }
525              
526             1;
527              
528             __END__