add TODO tests for slow our() declaration
[p5sagit/p5-mst-13.2.git] / lib / File / Path.pm
CommitLineData
1fc4cb55 1package File::Path;
fed7345c 2
3=head1 NAME
4
8b87c192 5File::Path - create or remove directory trees
fed7345c 6
7=head1 SYNOPSIS
8
8b87c192 9 use File::Path;
fed7345c 10
8b87c192 11 mkpath(['/foo/bar/baz', 'blurfl/quux'], 1, 0711);
12 rmtree(['foo/bar/baz', 'blurfl/quux'], 1, 1);
fed7345c 13
14=head1 DESCRIPTION
15
037c8c09 16The C<mkpath> function provides a convenient way to create directories, even
17if your C<mkdir> kernel call won't create more than one level of directory at
18a time. C<mkpath> takes three arguments:
fed7345c 19
20=over 4
21
22=item *
23
24the name of the path to create, or a reference
25to a list of paths to create,
26
27=item *
28
29a boolean value, which if TRUE will cause C<mkpath>
30to print the name of each directory as it is created
31(defaults to FALSE), and
32
33=item *
34
35the numeric mode to use when creating the directories
36(defaults to 0777)
37
38=back
39
037c8c09 40It returns a list of all directories (including intermediates, determined
41using the Unix '/' separator) created.
fed7345c 42
070ed461 43If a system error prevents a directory from being created, then the
99c4c5e8 44C<mkpath> function throws a fatal error with C<Carp::croak>. This error
45can be trapped with an C<eval> block:
070ed461 46
47 eval { mkpath($dir) };
48 if ($@) {
49 print "Couldn't create $dir: $@";
50 }
51
fed7345c 52Similarly, the C<rmtree> function provides a convenient way to delete a
53subtree from the directory structure, much like the Unix command C<rm -r>.
54C<rmtree> takes three arguments:
55
56=over 4
57
58=item *
59
60the root of the subtree to delete, or a reference to
61a list of roots. All of the files and directories
62below each root, as well as the roots themselves,
567d72c2 63will be deleted.
fed7345c 64
65=item *
66
67a boolean value, which if TRUE will cause C<rmtree> to
748a9306 68print a message each time it examines a file, giving the
69name of the file, and indicating whether it's using C<rmdir>
70or C<unlink> to remove it, or that it's skipping it.
fed7345c 71(defaults to FALSE)
72
73=item *
74
75a boolean value, which if TRUE will cause C<rmtree> to
748a9306 76skip any files to which you do not have delete access
77(if running under VMS) or write access (if running
78under another OS). This will change in the future when
79a criterion for 'delete permission' under OSs other
96e4d5b1 80than VMS is settled. (defaults to FALSE)
fed7345c 81
82=back
83
96e4d5b1 84It returns the number of files successfully deleted. Symlinks are
341bd822 85simply deleted and not followed.
fed7345c 86
96e4d5b1 87B<NOTE:> If the third parameter is not TRUE, C<rmtree> is B<unsecure>
88in the face of failure or interruption. Files and directories which
89were not deleted may be left with permissions reset to allow world
90read and write access. Note also that the occurrence of errors in
91rmtree can be determined I<only> by trapping diagnostic messages
92using C<$SIG{__WARN__}>; it is not apparent from the return value.
93Therefore, you must be extremely careful about using C<rmtree($foo,$bar,0>
94in situations where security is an issue.
95
fed7345c 96=head1 AUTHORS
97
96e4d5b1 98Tim Bunce <F<Tim.Bunce@ig.co.uk>> and
bd3fa61c 99Charles Bailey <F<bailey@newman.upenn.edu>>
fed7345c 100
fed7345c 101=cut
102
3b825e41 103use 5.006;
fed7345c 104use Carp;
037c8c09 105use File::Basename ();
037c8c09 106use Exporter ();
107use strict;
b395063c 108use warnings;
68dc0745 109
7068481f 110our $VERSION = "1.05";
ff21075d 111our @ISA = qw( Exporter );
112our @EXPORT = qw( mkpath rmtree );
fed7345c 113
68dc0745 114my $Is_VMS = $^O eq 'VMS';
ffb9ee5f 115my $Is_MacOS = $^O eq 'MacOS';
037c8c09 116
117# These OSes complain if you want to remove a file that you have no
118# write permission to:
6d697788 119my $force_writeable = ($^O eq 'os2' || $^O eq 'dos' || $^O eq 'MSWin32' ||
fa6a1c44 120 $^O eq 'amigaos' || $^O eq 'MacOS' || $^O eq 'epoc');
748a9306 121
a5f75d66 122sub mkpath {
fed7345c 123 my($paths, $verbose, $mode) = @_;
124 # $paths -- either a path string or ref to list of paths
125 # $verbose -- optional print "mkdir $path" for each directory created
126 # $mode -- optional permissions, defaults to 0777
ffb9ee5f 127 local($")=$Is_MacOS ? ":" : "/";
fed7345c 128 $mode = 0777 unless defined($mode);
129 $paths = [$paths] unless ref $paths;
037c8c09 130 my(@created,$path);
68dc0745 131 foreach $path (@$paths) {
1b1e14d3 132 $path .= '/' if $^O eq 'os2' and $path =~ /^\w:\z/s; # feature of CRT
037c8c09 133 # Logic wants Unix paths, so go with the flow.
e3830a4e 134 if ($Is_VMS) {
135 next if $path eq '/';
136 $path = VMS::Filespec::unixify($path);
137 if ($path =~ m:^(/[^/]+)/?\z:) {
138 $path = $1.'/000000';
c3420933 139 }
491527d0 140 }
e3830a4e 141 next if -d $path;
142 my $parent = File::Basename::dirname($path);
143 unless (-d $parent or $path eq $parent) {
144 push(@created,mkpath($parent, $verbose, $mode));
145 }
037c8c09 146 print "mkdir $path\n" if $verbose;
67e4c828 147 unless (mkdir($path,$mode)) {
c3420933 148 my $e = $!;
149 # allow for another process to have created it meanwhile
150 croak "mkdir $path: $e" unless -d $path;
67e4c828 151 }
037c8c09 152 push(@created, $path);
fed7345c 153 }
154 @created;
155}
156
157sub rmtree {
158 my($roots, $verbose, $safe) = @_;
7301ec2d 159 my(@files);
160 my($count) = 0;
037c8c09 161 $verbose ||= 0;
162 $safe ||= 0;
fed7345c 163
ee79a11f 164 if ( defined($roots) && length($roots) ) {
165 $roots = [$roots] unless ref $roots;
166 }
167 else {
168 carp "No root path(s) specified\n";
169 return 0;
170 }
171
037c8c09 172 my($root);
fed7345c 173 foreach $root (@{$roots}) {
ffb9ee5f 174 if ($Is_MacOS) {
175 $root = ":$root" if $root !~ /:/;
176 $root =~ s#([^:])\z#$1:#;
177 } else {
178 $root =~ s#/\z##;
179 }
7025f710 180 (undef, undef, my $rp) = lstat $root or next;
181 $rp &= 07777; # don't forget setuid, setgid, sticky bits
182 if ( -d _ ) {
037c8c09 183 # notabene: 0777 is for making readable in the first place,
184 # it's also intended to change it to writable in case we have
185 # to recurse in which case we are better than rm -rf for
186 # subtrees with strange permissions
96e4d5b1 187 chmod(0777, ($Is_VMS ? VMS::Filespec::fileify($root) : $root))
037c8c09 188 or carp "Can't make directory $root read+writeable: $!"
189 unless $safe;
190
ff21075d 191 if (opendir my $d, $root) {
7068481f 192 no strict 'refs';
193 if (!defined ${"\cTAINT"} or ${"\cTAINT"}) {
194 # Blindly untaint dir names
195 @files = map { /^(.*)$/s ; $1 } readdir $d;
196 } else {
197 @files = readdir $d;
198 }
ff21075d 199 closedir $d;
200 }
201 else {
202 carp "Can't read $root: $!";
203 @files = ();
204 }
037c8c09 205
206 # Deleting large numbers of files from VMS Files-11 filesystems
207 # is faster if done in reverse ASCIIbetical order
208 @files = reverse @files if $Is_VMS;
1b1e14d3 209 ($root = VMS::Filespec::unixify($root)) =~ s#\.dir\z## if $Is_VMS;
ffb9ee5f 210 if ($Is_MacOS) {
211 @files = map("$root$_", @files);
212 } else {
213 @files = map("$root/$_", grep $_!~/^\.{1,2}\z/s,@files);
214 }
037c8c09 215 $count += rmtree(\@files,$verbose,$safe);
216 if ($safe &&
217 ($Is_VMS ? !&VMS::Filespec::candelete($root) : !-w $root)) {
218 print "skipped $root\n" if $verbose;
219 next;
220 }
221 chmod 0777, $root
222 or carp "Can't make directory $root writeable: $!"
223 if $force_writeable;
224 print "rmdir $root\n" if $verbose;
96e4d5b1 225 if (rmdir $root) {
226 ++$count;
227 }
228 else {
229 carp "Can't remove directory $root: $!";
230 chmod($rp, ($Is_VMS ? VMS::Filespec::fileify($root) : $root))
231 or carp("and can't restore permissions to "
232 . sprintf("0%o",$rp) . "\n");
233 }
037c8c09 234 }
235 else {
236 if ($safe &&
64f6ddac 237 ($Is_VMS ? !&VMS::Filespec::candelete($root)
238 : !(-l $root || -w $root)))
239 {
037c8c09 240 print "skipped $root\n" if $verbose;
241 next;
242 }
243 chmod 0666, $root
244 or carp "Can't make file $root writeable: $!"
245 if $force_writeable;
246 print "unlink $root\n" if $verbose;
247 # delete all versions under VMS
94d4f21c 248 for (;;) {
249 unless (unlink $root) {
96e4d5b1 250 carp "Can't unlink file $root: $!";
251 if ($force_writeable) {
252 chmod $rp, $root
253 or carp("and can't restore permissions to "
254 . sprintf("0%o",$rp) . "\n");
255 }
94d4f21c 256 last;
96e4d5b1 257 }
94d4f21c 258 ++$count;
259 last unless $Is_VMS && lstat $root;
037c8c09 260 }
261 }
fed7345c 262 }
263
264 $count;
265}
266
2671;