As we're not passing over (or copying in) a NUL, don't need that extra
[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
e2ba98a1 36(defaults to 0777), to be modified by the current umask.
fed7345c 37
38=back
39
037c8c09 40It returns a list of all directories (including intermediates, determined
cc61921f 41using the Unix '/' separator) created. In scalar context it returns
42the number of directories created.
fed7345c 43
070ed461 44If a system error prevents a directory from being created, then the
99c4c5e8 45C<mkpath> function throws a fatal error with C<Carp::croak>. This error
46can be trapped with an C<eval> block:
070ed461 47
48 eval { mkpath($dir) };
49 if ($@) {
50 print "Couldn't create $dir: $@";
51 }
52
fed7345c 53Similarly, the C<rmtree> function provides a convenient way to delete a
54subtree from the directory structure, much like the Unix command C<rm -r>.
55C<rmtree> takes three arguments:
56
57=over 4
58
59=item *
60
61the root of the subtree to delete, or a reference to
62a list of roots. All of the files and directories
63below each root, as well as the roots themselves,
567d72c2 64will be deleted.
fed7345c 65
66=item *
67
68a boolean value, which if TRUE will cause C<rmtree> to
748a9306 69print a message each time it examines a file, giving the
70name of the file, and indicating whether it's using C<rmdir>
71or C<unlink> to remove it, or that it's skipping it.
fed7345c 72(defaults to FALSE)
73
74=item *
75
76a boolean value, which if TRUE will cause C<rmtree> to
748a9306 77skip any files to which you do not have delete access
78(if running under VMS) or write access (if running
79under another OS). This will change in the future when
80a criterion for 'delete permission' under OSs other
96e4d5b1 81than VMS is settled. (defaults to FALSE)
fed7345c 82
83=back
84
cc61921f 85It returns the number of files, directories and symlinks successfully
86deleted. Symlinks are simply deleted and not followed.
fed7345c 87
e2ba98a1 88B<NOTE:> There are race conditions internal to the implementation of
89C<rmtree> making it unsafe to use on directory trees which may be
90altered or moved while C<rmtree> is running, and in particular on any
91directory trees with any path components or subdirectories potentially
92writable by untrusted users.
93
94Additionally, if the third parameter is not TRUE and C<rmtree> is
95interrupted, it may leave files and directories with permissions altered
96to allow deletion (and older versions of this module would even set
97files and directories to world-read/writable!)
98
99Note also that the occurrence of errors in C<rmtree> can be determined I<only>
100by trapping diagnostic messages using C<$SIG{__WARN__}>; it is not apparent
101from the return value.
96e4d5b1 102
b8d5f521 103=head1 DIAGNOSTICS
104
105=over 4
106
107=item *
108
109On Windows, if C<mkpath> gives you the warning: B<No such file or
110directory>, this may mean that you've exceeded your filesystem's
111maximum path length.
112
113=back
114
fed7345c 115=head1 AUTHORS
116
96e4d5b1 117Tim Bunce <F<Tim.Bunce@ig.co.uk>> and
bd3fa61c 118Charles Bailey <F<bailey@newman.upenn.edu>>
fed7345c 119
fed7345c 120=cut
121
3b825e41 122use 5.006;
037c8c09 123use File::Basename ();
037c8c09 124use Exporter ();
125use strict;
b395063c 126use warnings;
68dc0745 127
36beb999 128our $VERSION = "1.08";
ff21075d 129our @ISA = qw( Exporter );
130our @EXPORT = qw( mkpath rmtree );
fed7345c 131
68dc0745 132my $Is_VMS = $^O eq 'VMS';
ffb9ee5f 133my $Is_MacOS = $^O eq 'MacOS';
037c8c09 134
135# These OSes complain if you want to remove a file that you have no
136# write permission to:
6d697788 137my $force_writeable = ($^O eq 'os2' || $^O eq 'dos' || $^O eq 'MSWin32' ||
fa6a1c44 138 $^O eq 'amigaos' || $^O eq 'MacOS' || $^O eq 'epoc');
748a9306 139
8878f897 140sub carp {
141 require Carp;
142 goto &Carp::carp;
143}
144
145sub croak {
146 require Carp;
147 goto &Carp::croak;
148}
149
a5f75d66 150sub mkpath {
fed7345c 151 my($paths, $verbose, $mode) = @_;
152 # $paths -- either a path string or ref to list of paths
153 # $verbose -- optional print "mkdir $path" for each directory created
154 # $mode -- optional permissions, defaults to 0777
ffb9ee5f 155 local($")=$Is_MacOS ? ":" : "/";
fed7345c 156 $mode = 0777 unless defined($mode);
157 $paths = [$paths] unless ref $paths;
037c8c09 158 my(@created,$path);
68dc0745 159 foreach $path (@$paths) {
1b1e14d3 160 $path .= '/' if $^O eq 'os2' and $path =~ /^\w:\z/s; # feature of CRT
037c8c09 161 # Logic wants Unix paths, so go with the flow.
e3830a4e 162 if ($Is_VMS) {
163 next if $path eq '/';
164 $path = VMS::Filespec::unixify($path);
491527d0 165 }
e3830a4e 166 next if -d $path;
167 my $parent = File::Basename::dirname($path);
168 unless (-d $parent or $path eq $parent) {
169 push(@created,mkpath($parent, $verbose, $mode));
170 }
037c8c09 171 print "mkdir $path\n" if $verbose;
67e4c828 172 unless (mkdir($path,$mode)) {
c3420933 173 my $e = $!;
174 # allow for another process to have created it meanwhile
36beb999 175 $! = $e, croak ("mkdir $path: $e") unless -d $path;
67e4c828 176 }
037c8c09 177 push(@created, $path);
fed7345c 178 }
179 @created;
180}
181
182sub rmtree {
183 my($roots, $verbose, $safe) = @_;
7301ec2d 184 my(@files);
185 my($count) = 0;
037c8c09 186 $verbose ||= 0;
187 $safe ||= 0;
fed7345c 188
ee79a11f 189 if ( defined($roots) && length($roots) ) {
190 $roots = [$roots] unless ref $roots;
191 }
192 else {
8878f897 193 carp ("No root path(s) specified\n");
ee79a11f 194 return 0;
195 }
196
037c8c09 197 my($root);
fed7345c 198 foreach $root (@{$roots}) {
ffb9ee5f 199 if ($Is_MacOS) {
200 $root = ":$root" if $root !~ /:/;
201 $root =~ s#([^:])\z#$1:#;
202 } else {
203 $root =~ s#/\z##;
204 }
7025f710 205 (undef, undef, my $rp) = lstat $root or next;
206 $rp &= 07777; # don't forget setuid, setgid, sticky bits
207 if ( -d _ ) {
e2ba98a1 208 # notabene: 0700 is for making readable in the first place,
037c8c09 209 # it's also intended to change it to writable in case we have
210 # to recurse in which case we are better than rm -rf for
211 # subtrees with strange permissions
e2ba98a1 212 chmod($rp | 0700, ($Is_VMS ? VMS::Filespec::fileify($root) : $root))
8878f897 213 or carp ("Can't make directory $root read+writeable: $!")
037c8c09 214 unless $safe;
215
ff21075d 216 if (opendir my $d, $root) {
7068481f 217 no strict 'refs';
218 if (!defined ${"\cTAINT"} or ${"\cTAINT"}) {
219 # Blindly untaint dir names
220 @files = map { /^(.*)$/s ; $1 } readdir $d;
221 } else {
222 @files = readdir $d;
223 }
ff21075d 224 closedir $d;
225 }
226 else {
8878f897 227 carp ("Can't read $root: $!");
ff21075d 228 @files = ();
229 }
037c8c09 230
231 # Deleting large numbers of files from VMS Files-11 filesystems
232 # is faster if done in reverse ASCIIbetical order
233 @files = reverse @files if $Is_VMS;
1b1e14d3 234 ($root = VMS::Filespec::unixify($root)) =~ s#\.dir\z## if $Is_VMS;
ffb9ee5f 235 if ($Is_MacOS) {
236 @files = map("$root$_", @files);
237 } else {
238 @files = map("$root/$_", grep $_!~/^\.{1,2}\z/s,@files);
239 }
037c8c09 240 $count += rmtree(\@files,$verbose,$safe);
241 if ($safe &&
242 ($Is_VMS ? !&VMS::Filespec::candelete($root) : !-w $root)) {
243 print "skipped $root\n" if $verbose;
244 next;
245 }
e2ba98a1 246 chmod $rp | 0700, $root
8878f897 247 or carp ("Can't make directory $root writeable: $!")
037c8c09 248 if $force_writeable;
249 print "rmdir $root\n" if $verbose;
96e4d5b1 250 if (rmdir $root) {
251 ++$count;
252 }
253 else {
8878f897 254 carp ("Can't remove directory $root: $!");
96e4d5b1 255 chmod($rp, ($Is_VMS ? VMS::Filespec::fileify($root) : $root))
256 or carp("and can't restore permissions to "
257 . sprintf("0%o",$rp) . "\n");
258 }
037c8c09 259 }
260 else {
261 if ($safe &&
64f6ddac 262 ($Is_VMS ? !&VMS::Filespec::candelete($root)
263 : !(-l $root || -w $root)))
264 {
037c8c09 265 print "skipped $root\n" if $verbose;
266 next;
267 }
e2ba98a1 268 chmod $rp | 0600, $root
8878f897 269 or carp ("Can't make file $root writeable: $!")
037c8c09 270 if $force_writeable;
271 print "unlink $root\n" if $verbose;
272 # delete all versions under VMS
94d4f21c 273 for (;;) {
274 unless (unlink $root) {
8878f897 275 carp ("Can't unlink file $root: $!");
96e4d5b1 276 if ($force_writeable) {
277 chmod $rp, $root
278 or carp("and can't restore permissions to "
279 . sprintf("0%o",$rp) . "\n");
280 }
94d4f21c 281 last;
96e4d5b1 282 }
94d4f21c 283 ++$count;
284 last unless $Is_VMS && lstat $root;
037c8c09 285 }
286 }
fed7345c 287 }
288
289 $count;
290}
291
2921;