source: trunk/third/perl/lib/File/Path.pm @ 17035

Revision 17035, 6.8 KB checked in by zacheiss, 22 years ago (diff)
This commit was generated by cvs2svn to compensate for changes in r17034, which included commits to RCS files with non-trunk default branches.
Line 
1package File::Path;
2
3=head1 NAME
4
5File::Path - create or remove directory trees
6
7=head1 SYNOPSIS
8
9    use File::Path;
10
11    mkpath(['/foo/bar/baz', 'blurfl/quux'], 1, 0711);
12    rmtree(['foo/bar/baz', 'blurfl/quux'], 1, 1);
13
14=head1 DESCRIPTION
15
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:
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
40It returns a list of all directories (including intermediates, determined
41using the Unix '/' separator) created.
42
43Similarly, the C<rmtree> function provides a convenient way to delete a
44subtree from the directory structure, much like the Unix command C<rm -r>.
45C<rmtree> takes three arguments:
46
47=over 4
48
49=item *
50
51the root of the subtree to delete, or a reference to
52a list of roots.  All of the files and directories
53below each root, as well as the roots themselves,
54will be deleted.
55
56=item *
57
58a boolean value, which if TRUE will cause C<rmtree> to
59print a message each time it examines a file, giving the
60name of the file, and indicating whether it's using C<rmdir>
61or C<unlink> to remove it, or that it's skipping it.
62(defaults to FALSE)
63
64=item *
65
66a boolean value, which if TRUE will cause C<rmtree> to
67skip any files to which you do not have delete access
68(if running under VMS) or write access (if running
69under another OS).  This will change in the future when
70a criterion for 'delete permission' under OSs other
71than VMS is settled.  (defaults to FALSE)
72
73=back
74
75It returns the number of files successfully deleted.  Symlinks are
76simply deleted and not followed.
77
78B<NOTE:> If the third parameter is not TRUE, C<rmtree> is B<unsecure>
79in the face of failure or interruption.  Files and directories which
80were not deleted may be left with permissions reset to allow world
81read and write access.  Note also that the occurrence of errors in
82rmtree can be determined I<only> by trapping diagnostic messages
83using C<$SIG{__WARN__}>; it is not apparent from the return value.
84Therefore, you must be extremely careful about using C<rmtree($foo,$bar,0>
85in situations where security is an issue.
86
87=head1 AUTHORS
88
89Tim Bunce <F<Tim.Bunce@ig.co.uk>> and
90Charles Bailey <F<bailey@newman.upenn.edu>>
91
92=cut
93
94use 5.005_64;
95use Carp;
96use File::Basename ();
97use Exporter ();
98use strict;
99
100our $VERSION = "1.0404";
101our @ISA = qw( Exporter );
102our @EXPORT = qw( mkpath rmtree );
103
104my $Is_VMS = $^O eq 'VMS';
105my $Is_MacOS = $^O eq 'MacOS';
106
107# These OSes complain if you want to remove a file that you have no
108# write permission to:
109my $force_writeable = ($^O eq 'os2' || $^O eq 'dos' || $^O eq 'MSWin32' ||
110                       $^O eq 'amigaos' || $^O eq 'MacOS' || $^O eq 'epoc');
111
112sub mkpath {
113    my($paths, $verbose, $mode) = @_;
114    # $paths   -- either a path string or ref to list of paths
115    # $verbose -- optional print "mkdir $path" for each directory created
116    # $mode    -- optional permissions, defaults to 0777
117    local($")=$Is_MacOS ? ":" : "/";
118    $mode = 0777 unless defined($mode);
119    $paths = [$paths] unless ref $paths;
120    my(@created,$path);
121    foreach $path (@$paths) {
122        $path .= '/' if $^O eq 'os2' and $path =~ /^\w:\z/s; # feature of CRT
123        # Logic wants Unix paths, so go with the flow.
124        if ($Is_VMS) {
125            next if $path eq '/';
126            $path = VMS::Filespec::unixify($path);
127            if ($path =~ m:^(/[^/]+)/?\z:) {
128                $path = $1.'/000000';
129            }
130        }
131        next if -d $path;
132        my $parent = File::Basename::dirname($path);
133        unless (-d $parent or $path eq $parent) {
134            push(@created,mkpath($parent, $verbose, $mode));
135        }
136        print "mkdir $path\n" if $verbose;
137        unless (mkdir($path,$mode)) {
138            my $e = $!;
139            # allow for another process to have created it meanwhile
140            croak "mkdir $path: $e" unless -d $path;
141        }
142        push(@created, $path);
143    }
144    @created;
145}
146
147sub rmtree {
148    my($roots, $verbose, $safe) = @_;
149    my(@files);
150    my($count) = 0;
151    $verbose ||= 0;
152    $safe ||= 0;
153
154    if ( defined($roots) && length($roots) ) {
155      $roots = [$roots] unless ref $roots;
156    }
157    else {
158      carp "No root path(s) specified\n";
159      return 0;
160    }
161
162    my($root);
163    foreach $root (@{$roots}) {
164        if ($Is_MacOS) {
165            $root = ":$root" if $root !~ /:/;
166            $root =~ s#([^:])\z#$1:#;
167        } else {
168            $root =~ s#/\z##;
169        }
170        (undef, undef, my $rp) = lstat $root or next;
171        $rp &= 07777;   # don't forget setuid, setgid, sticky bits
172        if ( -d _ ) {
173            # notabene: 0777 is for making readable in the first place,
174            # it's also intended to change it to writable in case we have
175            # to recurse in which case we are better than rm -rf for
176            # subtrees with strange permissions
177            chmod(0777, ($Is_VMS ? VMS::Filespec::fileify($root) : $root))
178              or carp "Can't make directory $root read+writeable: $!"
179                unless $safe;
180
181            if (opendir my $d, $root) {
182                @files = readdir $d;
183                closedir $d;
184            }
185            else {
186                carp "Can't read $root: $!";
187                @files = ();
188            }
189
190            # Deleting large numbers of files from VMS Files-11 filesystems
191            # is faster if done in reverse ASCIIbetical order
192            @files = reverse @files if $Is_VMS;
193            ($root = VMS::Filespec::unixify($root)) =~ s#\.dir\z## if $Is_VMS;
194            if ($Is_MacOS) {
195                @files = map("$root$_", @files);
196            } else {
197                @files = map("$root/$_", grep $_!~/^\.{1,2}\z/s,@files);
198            }
199            $count += rmtree(\@files,$verbose,$safe);
200            if ($safe &&
201                ($Is_VMS ? !&VMS::Filespec::candelete($root) : !-w $root)) {
202                print "skipped $root\n" if $verbose;
203                next;
204            }
205            chmod 0777, $root
206              or carp "Can't make directory $root writeable: $!"
207                if $force_writeable;
208            print "rmdir $root\n" if $verbose;
209            if (rmdir $root) {
210                ++$count;
211            }
212            else {
213                carp "Can't remove directory $root: $!";
214                chmod($rp, ($Is_VMS ? VMS::Filespec::fileify($root) : $root))
215                    or carp("and can't restore permissions to "
216                            . sprintf("0%o",$rp) . "\n");
217            }
218        }
219        else {
220            if ($safe &&
221                ($Is_VMS ? !&VMS::Filespec::candelete($root)
222                         : !(-l $root || -w $root)))
223            {
224                print "skipped $root\n" if $verbose;
225                next;
226            }
227            chmod 0666, $root
228              or carp "Can't make file $root writeable: $!"
229                if $force_writeable;
230            print "unlink $root\n" if $verbose;
231            # delete all versions under VMS
232            for (;;) {
233                unless (unlink $root) {
234                    carp "Can't unlink file $root: $!";
235                    if ($force_writeable) {
236                        chmod $rp, $root
237                            or carp("and can't restore permissions to "
238                                    . sprintf("0%o",$rp) . "\n");
239                    }
240                    last;
241                }
242                ++$count;
243                last unless $Is_VMS && lstat $root;
244            }
245        }
246    }
247
248    $count;
249}
250
2511;
Note: See TracBrowser for help on using the repository browser.