File Coverage

lib/ExtUtils/Command.pm
Criterion Covered Total %
statement 86 102 84.3
branch 25 40 62.5
condition 7 15 46.6
subroutine 18 18 100.0
pod 12 13 92.3
total 148 188 78.7


line stmt bran cond sub pod time code
1             package ExtUtils::Command;
2              
3 3     3   21358 use 5.00503;
  3         27  
4 3     3   16 use strict;
  3         5  
  3         56  
5 3     3   13 use warnings;
  3         5  
  3         6599  
6             require Exporter;
7             our @ISA = qw(Exporter);
8             our @EXPORT = qw(cp rm_f rm_rf mv cat eqtime mkpath touch test_f test_d chmod
9             dos2unix);
10             our $VERSION = '7.70';
11             $VERSION =~ tr/_//d;
12              
13             my $Is_VMS = $^O eq 'VMS';
14             my $Is_VMS_mode = $Is_VMS;
15             my $Is_VMS_noefs = $Is_VMS;
16             my $Is_Win32 = $^O eq 'MSWin32';
17              
18             if( $Is_VMS ) {
19             my $vms_unix_rpt;
20             my $vms_efs;
21             my $vms_case;
22              
23             if (eval { local $SIG{__DIE__};
24             local @INC = @INC;
25             pop @INC if $INC[-1] eq '.';
26             require VMS::Feature; }) {
27             $vms_unix_rpt = VMS::Feature::current("filename_unix_report");
28             $vms_efs = VMS::Feature::current("efs_charset");
29             $vms_case = VMS::Feature::current("efs_case_preserve");
30             } else {
31             my $unix_rpt = $ENV{'DECC$FILENAME_UNIX_REPORT'} || '';
32             my $efs_charset = $ENV{'DECC$EFS_CHARSET'} || '';
33             my $efs_case = $ENV{'DECC$EFS_CASE_PRESERVE'} || '';
34             $vms_unix_rpt = $unix_rpt =~ /^[ET1]/i;
35             $vms_efs = $efs_charset =~ /^[ET1]/i;
36             $vms_case = $efs_case =~ /^[ET1]/i;
37             }
38             $Is_VMS_mode = 0 if $vms_unix_rpt;
39             $Is_VMS_noefs = 0 if ($vms_efs);
40             }
41              
42              
43             =head1 NAME
44              
45             ExtUtils::Command - utilities to replace common UNIX commands in Makefiles etc.
46              
47             =head1 SYNOPSIS
48              
49             perl -MExtUtils::Command -e cat files... > destination
50             perl -MExtUtils::Command -e mv source... destination
51             perl -MExtUtils::Command -e cp source... destination
52             perl -MExtUtils::Command -e touch files...
53             perl -MExtUtils::Command -e rm_f files...
54             perl -MExtUtils::Command -e rm_rf directories...
55             perl -MExtUtils::Command -e mkpath directories...
56             perl -MExtUtils::Command -e eqtime source destination
57             perl -MExtUtils::Command -e test_f file
58             perl -MExtUtils::Command -e test_d directory
59             perl -MExtUtils::Command -e chmod mode files...
60             ...
61              
62             =head1 DESCRIPTION
63              
64             The module is used to replace common UNIX commands. In all cases the
65             functions work from @ARGV rather than taking arguments. This makes
66             them easier to deal with in Makefiles. Call them like this:
67              
68             perl -MExtUtils::Command -e some_command some files to work on
69              
70             and I<NOT> like this:
71              
72             perl -MExtUtils::Command -e 'some_command qw(some files to work on)'
73              
74             For that use L<Shell::Command>.
75              
76             Filenames with * and ? will be glob expanded.
77              
78              
79             =head2 FUNCTIONS
80              
81             =over 4
82              
83             =cut
84              
85             # VMS uses % instead of ? to mean "one character"
86             my $wild_regex = $Is_VMS ? '*%' : '*?';
87             sub expand_wildcards
88             {
89 25 100   25 0 1349 @ARGV = map(/[$wild_regex]/o ? glob($_) : $_,@ARGV);
90             }
91              
92              
93             =item cat
94              
95             cat file ...
96              
97             Concatenates all files mentioned on command line to STDOUT.
98              
99             =cut
100              
101             sub cat ()
102             {
103 1     1 1 113 expand_wildcards();
104 1         82 print while (<>);
105             }
106              
107             =item eqtime
108              
109             eqtime source destination
110              
111             Sets modified time of destination to that of source.
112              
113             =cut
114              
115             sub eqtime
116             {
117 2     2 1 1004 my ($src,$dst) = @ARGV;
118 2         8 local @ARGV = ($dst); touch(); # in case $dst doesn't exist
  2         6  
119 2         45 utime((stat($src))[8,9],$dst);
120             }
121              
122             =item rm_rf
123              
124             rm_rf files or directories ...
125              
126             Removes files and directories - recursively (even if readonly)
127              
128             =cut
129              
130             sub rm_rf
131             {
132 2     2 1 1106 expand_wildcards();
133 2         13 require File::Path;
134 2         868 File::Path::rmtree([grep -e $_,@ARGV],0,0);
135             }
136              
137             =item rm_f
138              
139             rm_f file ...
140              
141             Removes files (even if readonly)
142              
143             =cut
144              
145             sub rm_f {
146 1     1 1 585 expand_wildcards();
147              
148 1         4 foreach my $file (@ARGV) {
149 2 50       29 next unless -f $file;
150              
151 2 50       11 next if _unlink($file);
152              
153 0         0 chmod(0777, $file);
154              
155 0 0       0 next if _unlink($file);
156              
157 0         0 require Carp;
158 0         0 Carp::carp("Cannot delete $file: $!");
159             }
160             }
161              
162             sub _unlink {
163 2     2   4 my $files_unlinked = 0;
164 2         5 foreach my $file (@_) {
165 2         3 my $delete_count = 0;
166 2         141 $delete_count++ while unlink $file;
167 2 50       12 $files_unlinked++ if $delete_count;
168             }
169 2         8 return $files_unlinked;
170             }
171              
172              
173             =item touch
174              
175             touch file ...
176              
177             Makes files exist, with current timestamp
178              
179             =cut
180              
181             sub touch {
182 4     4 1 3003358 my $t = time;
183 4         20 expand_wildcards();
184 4         11 foreach my $file (@ARGV) {
185 4 50       370 open(FILE,">>$file") || die "Cannot write $file:$!";
186 4         53 close(FILE);
187 4         93 utime($t,$t,$file);
188             }
189             }
190              
191             =item mv
192              
193             mv source_file destination_file
194             mv source_file source_file destination_dir
195              
196             Moves source to destination. Multiple sources are allowed if
197             destination is an existing directory.
198              
199             Returns true if all moves succeeded, false otherwise.
200              
201             =cut
202              
203             sub mv {
204 2     2 1 482 expand_wildcards();
205 2         7 my @src = @ARGV;
206 2         5 my $dst = pop @src;
207              
208 2 100 66     20 if (@src > 1 && ! -d $dst) {
209 1         9 require Carp;
210 1         93 Carp::croak("Too many arguments");
211             }
212              
213 1         6 require File::Copy;
214 1         2 my $nok = 0;
215 1         4 foreach my $src (@src) {
216 1   33     6 $nok ||= !File::Copy::move($src,$dst);
217             }
218 1         170 return !$nok;
219             }
220              
221             =item cp
222              
223             cp source_file destination_file
224             cp source_file source_file destination_dir
225              
226             Copies sources to the destination. Multiple sources are allowed if
227             destination is an existing directory.
228              
229             Returns true if all copies succeeded, false otherwise.
230              
231             =cut
232              
233             sub cp {
234 3     3 1 1688 expand_wildcards();
235 3         12 my @src = @ARGV;
236 3         8 my $dst = pop @src;
237              
238 3 100 66     32 if (@src > 1 && ! -d $dst) {
239 1         8 require Carp;
240 1         199 Carp::croak("Too many arguments");
241             }
242              
243 2         1170 require File::Copy;
244 2         4939 my $nok = 0;
245 2         7 foreach my $src (@src) {
246 2   33     15 $nok ||= !File::Copy::copy($src,$dst);
247              
248             # Win32 does not update the mod time of a copied file, just the
249             # created time which make does not look at.
250 2 50       781 utime(time, time, $dst) if $Is_Win32;
251             }
252 2         8 return $nok;
253             }
254              
255             =item chmod
256              
257             chmod mode files ...
258              
259             Sets UNIX like permissions 'mode' on all the files. e.g. 0666
260              
261             =cut
262              
263             sub chmod {
264 7     7 1 2874 local @ARGV = @ARGV;
265 7         15 my $mode = shift(@ARGV);
266 7         17 expand_wildcards();
267              
268 7 50 33     43 if( $Is_VMS_mode && $Is_VMS_noefs) {
269 0         0 require File::Spec;
270 0         0 foreach my $idx (0..$#ARGV) {
271 0         0 my $path = $ARGV[$idx];
272 0 0       0 next unless -d $path;
273              
274             # chmod 0777, [.foo.bar] doesn't work on VMS, you have to do
275             # chmod 0777, [.foo]bar.dir
276 0         0 my @dirs = File::Spec->splitdir( $path );
277 0         0 $dirs[-1] .= '.dir';
278 0         0 $path = File::Spec->catfile(@dirs);
279              
280 0         0 $ARGV[$idx] = $path;
281             }
282             }
283              
284 7 50       167 chmod(oct $mode,@ARGV) || die "Cannot chmod ".join(' ',$mode,@ARGV).":$!";
285             }
286              
287             =item mkpath
288              
289             mkpath directory ...
290              
291             Creates directories, including any parent directories.
292              
293             =cut
294              
295             sub mkpath
296             {
297 3     3 1 2025 expand_wildcards();
298 3         21 require File::Path;
299 3         568 File::Path::mkpath([@ARGV],0,0777);
300             }
301              
302             =item test_f
303              
304             test_f file
305              
306             Tests if a file exists. I<Exits> with 0 if it does, 1 if it does not (ie.
307             shell's idea of true and false).
308              
309             =cut
310              
311             sub test_f
312             {
313 2 100   2 1 3820 exit(-f $ARGV[0] ? 0 : 1);
314             }
315              
316             =item test_d
317              
318             test_d directory
319              
320             Tests if a directory exists. I<Exits> with 0 if it does, 1 if it does
321             not (ie. shell's idea of true and false).
322              
323             =cut
324              
325             sub test_d
326             {
327 2 100   2 1 1128 exit(-d $ARGV[0] ? 0 : 1);
328             }
329              
330             =item dos2unix
331              
332             dos2unix files or dirs ...
333              
334             Converts DOS and OS/2 linefeeds to Unix style recursively.
335              
336             =cut
337              
338             sub dos2unix {
339 1     1 1 224 require File::Find;
340             File::Find::find(sub {
341 3 100   3   104 return if -d;
342 2 50       14 return unless -w _;
343 2 50       13 return unless -r _;
344 2 100       101 return if -B _;
345              
346 1         6 local $\;
347              
348 1         3 my $orig = $_;
349 1         2 my $temp = '.dos2unix_tmp';
350 1 50       31 open ORIG, $_ or do { warn "dos2unix can't open $_: $!"; return };
  0         0  
  0         0  
351             open TEMP, ">$temp" or
352 1 50       66 do { warn "dos2unix can't create .dos2unix_tmp: $!"; return };
  0         0  
  0         0  
353 1         5 binmode ORIG; binmode TEMP;
  1         3  
354 1         25 while (my $line = <ORIG>) {
355 2         11 $line =~ s/\015\012/\012/g;
356 2         28 print TEMP $line;
357             }
358 1         9 close ORIG;
359 1         33 close TEMP;
360 1         167 rename $temp, $orig;
361              
362 1         116 }, @ARGV);
363             }
364              
365             =back
366              
367             =head1 SEE ALSO
368              
369             Shell::Command which is these same functions but take arguments normally.
370              
371              
372             =head1 AUTHOR
373              
374             Nick Ing-Simmons C<ni-s@cpan.org>
375              
376             Maintained by Michael G Schwern C<schwern@pobox.com> within the
377             ExtUtils-MakeMaker package and, as a separate CPAN package, by
378             Randy Kobes C<r.kobes@uwinnipeg.ca>.
379              
380             =cut
381