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