Initial bulk commit for "Git on MSys"
[msysgit/historical-msysgit.git] / lib / perl5 / 5.6.1 / File / Find.pm
blob9e0c299c4feb639992c5d43512daf4ba47552e79
1 package File::Find;
2 use strict;
3 use warnings;
4 use 5.6.0;
5 our $VERSION = '1.01';
6 require Exporter;
7 require Cwd;
9 =head1 NAME
11 find - traverse a file tree
13 finddepth - traverse a directory structure depth-first
15 =head1 SYNOPSIS
17 use File::Find;
18 find(\&wanted, '/foo', '/bar');
19 sub wanted { ... }
21 use File::Find;
22 finddepth(\&wanted, '/foo', '/bar');
23 sub wanted { ... }
25 use File::Find;
26 find({ wanted => \&process, follow => 1 }, '.');
28 =head1 DESCRIPTION
30 The first argument to find() is either a hash reference describing the
31 operations to be performed for each file, or a code reference.
33 Here are the possible keys for the hash:
35 =over 3
37 =item C<wanted>
39 The value should be a code reference. This code reference is called
40 I<the wanted() function> below.
42 =item C<bydepth>
44 Reports the name of a directory only AFTER all its entries
45 have been reported. Entry point finddepth() is a shortcut for
46 specifying C<{ bydepth => 1 }> in the first argument of find().
48 =item C<preprocess>
50 The value should be a code reference. This code reference is used to
51 preprocess the current directory. The name of currently processed
52 directory is in $File::Find::dir. Your preprocessing function is
53 called after readdir() but before the loop that calls the wanted()
54 function. It is called with a list of strings (actually file/directory
55 names) and is expected to return a list of strings. The code can be
56 used to sort the file/directory names alphabetically, numerically,
57 or to filter out directory entries based on their name alone. When
58 I<follow> or I<follow_fast> are in effect, C<preprocess> is a no-op.
60 =item C<postprocess>
62 The value should be a code reference. It is invoked just before leaving
63 the currently processed directory. It is called in void context with no
64 arguments. The name of the current directory is in $File::Find::dir. This
65 hook is handy for summarizing a directory, such as calculating its disk
66 usage. When I<follow> or I<follow_fast> are in effect, C<postprocess> is a
67 no-op.
69 =item C<follow>
71 Causes symbolic links to be followed. Since directory trees with symbolic
72 links (followed) may contain files more than once and may even have
73 cycles, a hash has to be built up with an entry for each file.
74 This might be expensive both in space and time for a large
75 directory tree. See I<follow_fast> and I<follow_skip> below.
76 If either I<follow> or I<follow_fast> is in effect:
78 =over 6
80 =item *
82 It is guaranteed that an I<lstat> has been called before the user's
83 I<wanted()> function is called. This enables fast file checks involving S< _>.
85 =item *
87 There is a variable C<$File::Find::fullname> which holds the absolute
88 pathname of the file with all symbolic links resolved
90 =back
92 =item C<follow_fast>
94 This is similar to I<follow> except that it may report some files more
95 than once. It does detect cycles, however. Since only symbolic links
96 have to be hashed, this is much cheaper both in space and time. If
97 processing a file more than once (by the user's I<wanted()> function)
98 is worse than just taking time, the option I<follow> should be used.
100 =item C<follow_skip>
102 C<follow_skip==1>, which is the default, causes all files which are
103 neither directories nor symbolic links to be ignored if they are about
104 to be processed a second time. If a directory or a symbolic link
105 are about to be processed a second time, File::Find dies.
106 C<follow_skip==0> causes File::Find to die if any file is about to be
107 processed a second time.
108 C<follow_skip==2> causes File::Find to ignore any duplicate files and
109 directories but to proceed normally otherwise.
111 =item C<dangling_symlinks>
113 If true and a code reference, will be called with the symbolic link
114 name and the directory it lives in as arguments. Otherwise, if true
115 and warnings are on, warning "symbolic_link_name is a dangling
116 symbolic link\n" will be issued. If false, the dangling symbolic link
117 will be silently ignored.
119 =item C<no_chdir>
121 Does not C<chdir()> to each directory as it recurses. The wanted()
122 function will need to be aware of this, of course. In this case,
123 C<$_> will be the same as C<$File::Find::name>.
125 =item C<untaint>
127 If find is used in taint-mode (-T command line switch or if EUID != UID
128 or if EGID != GID) then internally directory names have to be untainted
129 before they can be chdir'ed to. Therefore they are checked against a regular
130 expression I<untaint_pattern>. Note that all names passed to the user's
131 I<wanted()> function are still tainted. If this option is used while
132 not in taint-mode, C<untaint> is a no-op.
134 =item C<untaint_pattern>
136 See above. This should be set using the C<qr> quoting operator.
137 The default is set to C<qr|^([-+@\w./]+)$|>.
138 Note that the parantheses are vital.
140 =item C<untaint_skip>
142 If set, a directory which fails the I<untaint_pattern> is skipped,
143 including all its sub-directories. The default is to 'die' in such a case.
145 =back
147 The wanted() function does whatever verifications you want.
148 C<$File::Find::dir> contains the current directory name, and C<$_> the
149 current filename within that directory. C<$File::Find::name> contains
150 the complete pathname to the file. You are chdir()'d to
151 C<$File::Find::dir> when the function is called, unless C<no_chdir>
152 was specified. When C<follow> or C<follow_fast> are in effect, there is
153 also a C<$File::Find::fullname>. The function may set
154 C<$File::Find::prune> to prune the tree unless C<bydepth> was
155 specified. Unless C<follow> or C<follow_fast> is specified, for
156 compatibility reasons (find.pl, find2perl) there are in addition the
157 following globals available: C<$File::Find::topdir>,
158 C<$File::Find::topdev>, C<$File::Find::topino>,
159 C<$File::Find::topmode> and C<$File::Find::topnlink>.
161 This library is useful for the C<find2perl> tool, which when fed,
163 find2perl / -name .nfs\* -mtime +7 \
164 -exec rm -f {} \; -o -fstype nfs -prune
166 produces something like:
168 sub wanted {
169 /^\.nfs.*\z/s &&
170 (($dev, $ino, $mode, $nlink, $uid, $gid) = lstat($_)) &&
171 int(-M _) > 7 &&
172 unlink($_)
174 ($nlink || (($dev, $ino, $mode, $nlink, $uid, $gid) = lstat($_))) &&
175 $dev < 0 &&
176 ($File::Find::prune = 1);
179 Set the variable C<$File::Find::dont_use_nlink> if you're using AFS,
180 since AFS cheats.
183 Here's another interesting wanted function. It will find all symlinks
184 that don't resolve:
186 sub wanted {
187 -l && !-e && print "bogus link: $File::Find::name\n";
190 See also the script C<pfind> on CPAN for a nice application of this
191 module.
193 =head1 CAVEAT
195 Be aware that the option to follow symbolic links can be dangerous.
196 Depending on the structure of the directory tree (including symbolic
197 links to directories) you might traverse a given (physical) directory
198 more than once (only if C<follow_fast> is in effect).
199 Furthermore, deleting or changing files in a symbolically linked directory
200 might cause very unpleasant surprises, since you delete or change files
201 in an unknown directory.
203 =head1 NOTES
205 =over 4
207 =item *
209 Mac OS (Classic) users should note a few differences:
211 =over 4
213 =item *
215 The path separator is ':', not '/', and the current directory is denoted
216 as ':', not '.'. You should be careful about specifying relative pathnames.
217 While a full path always begins with a volume name, a relative pathname
218 should always begin with a ':'. If specifying a volume name only, a
219 trailing ':' is required.
221 =item *
223 C<$File::Find::dir> is guaranteed to end with a ':'. If C<$_>
224 contains the name of a directory, that name may or may not end with a
225 ':'. Likewise, C<$File::Find::name>, which contains the complete
226 pathname to that directory, and C<$File::Find::fullname>, which holds
227 the absolute pathname of that directory with all symbolic links resolved,
228 may or may not end with a ':'.
230 =item *
232 The default C<untaint_pattern> (see above) on Mac OS is set to
233 C<qr|^(.+)$|>. Note that the parentheses are vital.
235 =item *
237 The invisible system file "Icon\015" is ignored. While this file may
238 appear in every directory, there are some more invisible system files
239 on every volume, which are all located at the volume root level (i.e.
240 "MacintoshHD:"). These system files are B<not> excluded automatically.
241 Your filter may use the following code to recognize invisible files or
242 directories (requires Mac::Files):
244 use Mac::Files;
246 # invisible() -- returns 1 if file/directory is invisible,
247 # 0 if it's visible or undef if an error occured
249 sub invisible($) {
250 my $file = shift;
251 my ($fileCat, $fileInfo);
252 my $invisible_flag = 1 << 14;
254 if ( $fileCat = FSpGetCatInfo($file) ) {
255 if ($fileInfo = $fileCat->ioFlFndrInfo() ) {
256 return (($fileInfo->fdFlags & $invisible_flag) && 1);
259 return undef;
262 Generally, invisible files are system files, unless an odd application
263 decides to use invisible files for its own purposes. To distinguish
264 such files from system files, you have to look at the B<type> and B<creator>
265 file attributes. The MacPerl built-in functions C<GetFileInfo(FILE)> and
266 C<SetFileInfo(CREATOR, TYPE, FILES)> offer access to these attributes
267 (see MacPerl.pm for details).
269 Files that appear on the desktop actually reside in an (hidden) directory
270 named "Desktop Folder" on the particular disk volume. Note that, although
271 all desktop files appear to be on the same "virtual" desktop, each disk
272 volume actually maintains its own "Desktop Folder" directory.
274 =back
276 =back
278 =head1 HISTORY
280 File::Find used to produce incorrect results if called recursively.
281 During the development of perl 5.8 this bug was fixed.
282 The first fixed version of File::Find was 1.01.
284 =cut
286 our @ISA = qw(Exporter);
287 our @EXPORT = qw(find finddepth);
290 use strict;
291 my $Is_VMS;
292 my $Is_MacOS;
294 require File::Basename;
295 require File::Spec;
297 # Should ideally be my() not our() but local() currently
298 # refuses to operate on lexicals
300 our %SLnkSeen;
301 our ($wanted_callback, $avoid_nlink, $bydepth, $no_chdir, $follow,
302 $follow_skip, $full_check, $untaint, $untaint_skip, $untaint_pat,
303 $pre_process, $post_process, $dangling_symlinks);
305 sub contract_name {
306 my ($cdir,$fn) = @_;
308 return substr($cdir,0,rindex($cdir,'/')) if $fn eq $File::Find::current_dir;
310 $cdir = substr($cdir,0,rindex($cdir,'/')+1);
312 $fn =~ s|^\./||;
314 my $abs_name= $cdir . $fn;
316 if (substr($fn,0,3) eq '../') {
317 1 while $abs_name =~ s!/[^/]*/\.\./!/!;
320 return $abs_name;
323 # return the absolute name of a directory or file
324 sub contract_name_Mac {
325 my ($cdir,$fn) = @_;
326 my $abs_name;
328 if ($fn =~ /^(:+)(.*)$/) { # valid pathname starting with a ':'
330 my $colon_count = length ($1);
331 if ($colon_count == 1) {
332 $abs_name = $cdir . $2;
333 return $abs_name;
335 else {
336 # need to move up the tree, but
337 # only if it's not a volume name
338 for (my $i=1; $i<$colon_count; $i++) {
339 unless ($cdir =~ /^[^:]+:$/) { # volume name
340 $cdir =~ s/[^:]+:$//;
342 else {
343 return undef;
346 $abs_name = $cdir . $2;
347 return $abs_name;
351 else {
353 # $fn may be a valid path to a directory or file or (dangling)
354 # symlink, without a leading ':'
355 if ( (-e $fn) || (-l $fn) ) {
356 if ($fn =~ /^[^:]+:/) { # a volume name like DataHD:*
357 return $fn; # $fn is already an absolute path
359 else {
360 $abs_name = $cdir . $fn;
361 return $abs_name;
364 else { # argh!, $fn is not a valid directory/file
365 return undef;
370 sub PathCombine($$) {
371 my ($Base,$Name) = @_;
372 my $AbsName;
374 if ($Is_MacOS) {
375 # $Name is the resolved symlink (always a full path on MacOS),
376 # i.e. there's no need to call contract_name_Mac()
377 $AbsName = $Name;
379 # (simple) check for recursion
380 if ( ( $Base =~ /^$AbsName/) && (-d $AbsName) ) { # recursion
381 return undef;
384 else {
385 if (substr($Name,0,1) eq '/') {
386 $AbsName= $Name;
388 else {
389 $AbsName= contract_name($Base,$Name);
392 # (simple) check for recursion
393 my $newlen= length($AbsName);
394 if ($newlen <= length($Base)) {
395 if (($newlen == length($Base) || substr($Base,$newlen,1) eq '/')
396 && $AbsName eq substr($Base,0,$newlen))
398 return undef;
402 return $AbsName;
405 sub Follow_SymLink($) {
406 my ($AbsName) = @_;
408 my ($NewName,$DEV, $INO);
409 ($DEV, $INO)= lstat $AbsName;
411 while (-l _) {
412 if ($SLnkSeen{$DEV, $INO}++) {
413 if ($follow_skip < 2) {
414 die "$AbsName is encountered a second time";
416 else {
417 return undef;
420 $NewName= PathCombine($AbsName, readlink($AbsName));
421 unless(defined $NewName) {
422 if ($follow_skip < 2) {
423 die "$AbsName is a recursive symbolic link";
425 else {
426 return undef;
429 else {
430 $AbsName= $NewName;
432 ($DEV, $INO) = lstat($AbsName);
433 return undef unless defined $DEV; # dangling symbolic link
436 if ($full_check && $SLnkSeen{$DEV, $INO}++) {
437 if ( ($follow_skip < 1) || ((-d _) && ($follow_skip < 2)) ) {
438 die "$AbsName encountered a second time";
440 else {
441 return undef;
445 return $AbsName;
448 our($dir, $name, $fullname, $prune);
449 sub _find_dir_symlnk($$$);
450 sub _find_dir($$$);
452 # check whether or not a scalar variable is tainted
453 # (code straight from the Camel, 3rd ed., page 561)
454 sub is_tainted_pp {
455 my $arg = shift;
456 my $nada = substr($arg, 0, 0); # zero-length
457 local $@;
458 eval { eval "# $nada" };
459 return length($@) != 0;
462 sub _find_opt {
463 my $wanted = shift;
464 die "invalid top directory" unless defined $_[0];
466 # This function must local()ize everything because callbacks may
467 # call find() or finddepth()
469 local %SLnkSeen;
470 local ($wanted_callback, $avoid_nlink, $bydepth, $no_chdir, $follow,
471 $follow_skip, $full_check, $untaint, $untaint_skip, $untaint_pat,
472 $pre_process, $post_process, $dangling_symlinks);
473 local($dir, $name, $fullname, $prune);
475 my $cwd = $wanted->{bydepth} ? Cwd::fastcwd() : Cwd::cwd();
476 my $cwd_untainted = $cwd;
477 my $check_t_cwd = 1;
478 $wanted_callback = $wanted->{wanted};
479 $bydepth = $wanted->{bydepth};
480 $pre_process = $wanted->{preprocess};
481 $post_process = $wanted->{postprocess};
482 $no_chdir = $wanted->{no_chdir};
483 $full_check = $wanted->{follow};
484 $follow = $full_check || $wanted->{follow_fast};
485 $follow_skip = $wanted->{follow_skip};
486 $untaint = $wanted->{untaint};
487 $untaint_pat = $wanted->{untaint_pattern};
488 $untaint_skip = $wanted->{untaint_skip};
489 $dangling_symlinks = $wanted->{dangling_symlinks};
491 # for compatability reasons (find.pl, find2perl)
492 local our ($topdir, $topdev, $topino, $topmode, $topnlink);
494 # a symbolic link to a directory doesn't increase the link count
495 $avoid_nlink = $follow || $File::Find::dont_use_nlink;
497 my ($abs_dir, $Is_Dir);
499 Proc_Top_Item:
500 foreach my $TOP (@_) {
501 my $top_item = $TOP;
503 if ($Is_MacOS) {
504 ($topdev,$topino,$topmode,$topnlink) = $follow ? stat $top_item : lstat $top_item;
505 $top_item = ":$top_item"
506 if ( (-d _) && ( $top_item !~ /:/ ) );
508 else {
509 $top_item =~ s|/\z|| unless $top_item eq '/';
510 ($topdev,$topino,$topmode,$topnlink) = $follow ? stat $top_item : lstat $top_item;
513 $Is_Dir= 0;
515 if ($follow) {
517 if ($Is_MacOS) {
518 $cwd = "$cwd:" unless ($cwd =~ /:$/); # for safety
520 if ($top_item eq $File::Find::current_dir) {
521 $abs_dir = $cwd;
523 else {
524 $abs_dir = contract_name_Mac($cwd, $top_item);
525 unless (defined $abs_dir) {
526 warn "Can't determine absolute path for $top_item (No such file or directory)\n" if $^W;
527 next Proc_Top_Item;
532 else {
533 if (substr($top_item,0,1) eq '/') {
534 $abs_dir = $top_item;
536 elsif ($top_item eq $File::Find::current_dir) {
537 $abs_dir = $cwd;
539 else { # care about any ../
540 $abs_dir = contract_name("$cwd/",$top_item);
543 $abs_dir= Follow_SymLink($abs_dir);
544 unless (defined $abs_dir) {
545 if ($dangling_symlinks) {
546 if (ref $dangling_symlinks eq 'CODE') {
547 $dangling_symlinks->($top_item, $cwd);
548 } else {
549 warn "$top_item is a dangling symbolic link\n" if $^W;
552 next Proc_Top_Item;
555 if (-d _) {
556 _find_dir_symlnk($wanted, $abs_dir, $top_item);
557 $Is_Dir= 1;
560 else { # no follow
561 $topdir = $top_item;
562 unless (defined $topnlink) {
563 warn "Can't stat $top_item: $!\n" if $^W;
564 next Proc_Top_Item;
566 if (-d _) {
567 $top_item =~ s/\.dir\z// if $Is_VMS;
568 _find_dir($wanted, $top_item, $topnlink);
569 $Is_Dir= 1;
571 else {
572 $abs_dir= $top_item;
576 unless ($Is_Dir) {
577 unless (($_,$dir) = File::Basename::fileparse($abs_dir)) {
578 if ($Is_MacOS) {
579 ($dir,$_) = (':', $top_item); # $File::Find::dir, $_
581 else {
582 ($dir,$_) = ('./', $top_item);
586 $abs_dir = $dir;
587 if (( $untaint ) && (is_tainted($dir) )) {
588 ( $abs_dir ) = $dir =~ m|$untaint_pat|;
589 unless (defined $abs_dir) {
590 if ($untaint_skip == 0) {
591 die "directory $dir is still tainted";
593 else {
594 next Proc_Top_Item;
599 unless ($no_chdir || chdir $abs_dir) {
600 warn "Couldn't chdir $abs_dir: $!\n" if $^W;
601 next Proc_Top_Item;
604 $name = $abs_dir . $_; # $File::Find::name
606 { &$wanted_callback }; # protect against wild "next"
610 unless ( $no_chdir ) {
611 if ( ($check_t_cwd) && (($untaint) && (is_tainted($cwd) )) ) {
612 ( $cwd_untainted ) = $cwd =~ m|$untaint_pat|;
613 unless (defined $cwd_untainted) {
614 die "insecure cwd in find(depth)";
616 $check_t_cwd = 0;
618 unless (chdir $cwd_untainted) {
619 die "Can't cd to $cwd: $!\n";
625 # API:
626 # $wanted
627 # $p_dir : "parent directory"
628 # $nlink : what came back from the stat
629 # preconditions:
630 # chdir (if not no_chdir) to dir
632 sub _find_dir($$$) {
633 my ($wanted, $p_dir, $nlink) = @_;
634 my ($CdLvl,$Level) = (0,0);
635 my @Stack;
636 my @filenames;
637 my ($subcount,$sub_nlink);
638 my $SE= [];
639 my $dir_name= $p_dir;
640 my $dir_pref;
641 my $dir_rel;
642 my $tainted = 0;
644 if ($Is_MacOS) {
645 $dir_pref= ($p_dir =~ /:$/) ? $p_dir : "$p_dir:"; # preface
646 $dir_rel= ':'; # directory name relative to current directory
648 else {
649 $dir_pref= ( $p_dir eq '/' ? '/' : "$p_dir/" );
650 $dir_rel= '.'; # directory name relative to current directory
653 local ($dir, $name, $prune, *DIR);
655 unless ( $no_chdir || ($p_dir eq $File::Find::current_dir)) {
656 my $udir = $p_dir;
657 if (( $untaint ) && (is_tainted($p_dir) )) {
658 ( $udir ) = $p_dir =~ m|$untaint_pat|;
659 unless (defined $udir) {
660 if ($untaint_skip == 0) {
661 die "directory $p_dir is still tainted";
663 else {
664 return;
668 unless (chdir $udir) {
669 warn "Can't cd to $udir: $!\n" if $^W;
670 return;
674 # push the starting directory
675 push @Stack,[$CdLvl,$p_dir,$dir_rel,-1] if $bydepth;
677 if ($Is_MacOS) {
678 $p_dir = $dir_pref; # ensure trailing ':'
681 while (defined $SE) {
682 unless ($bydepth) {
683 $dir= $p_dir; # $File::Find::dir
684 $name= $dir_name; # $File::Find::name
685 $_= ($no_chdir ? $dir_name : $dir_rel ); # $_
686 # prune may happen here
687 $prune= 0;
688 { &$wanted_callback }; # protect against wild "next"
689 next if $prune;
692 # change to that directory
693 unless ($no_chdir || ($dir_rel eq $File::Find::current_dir)) {
694 my $udir= $dir_rel;
695 if ( ($untaint) && (($tainted) || ($tainted = is_tainted($dir_rel) )) ) {
696 ( $udir ) = $dir_rel =~ m|$untaint_pat|;
697 unless (defined $udir) {
698 if ($untaint_skip == 0) {
699 if ($Is_MacOS) {
700 die "directory ($p_dir) $dir_rel is still tainted";
702 else {
703 die "directory (" . ($p_dir ne '/' ? $p_dir : '') . "/) $dir_rel is still tainted";
705 } else { # $untaint_skip == 1
706 next;
710 unless (chdir $udir) {
711 if ($Is_MacOS) {
712 warn "Can't cd to ($p_dir) $udir: $!\n" if $^W;
714 else {
715 warn "Can't cd to (" . ($p_dir ne '/' ? $p_dir : '') . "/) $udir: $!\n" if $^W;
717 next;
719 $CdLvl++;
722 if ($Is_MacOS) {
723 $dir_name = "$dir_name:" unless ($dir_name =~ /:$/);
726 $dir= $dir_name; # $File::Find::dir
728 # Get the list of files in the current directory.
729 unless (opendir DIR, ($no_chdir ? $dir_name : $File::Find::current_dir)) {
730 warn "Can't opendir($dir_name): $!\n" if $^W;
731 next;
733 @filenames = readdir DIR;
734 closedir(DIR);
735 @filenames = &$pre_process(@filenames) if $pre_process;
736 push @Stack,[$CdLvl,$dir_name,"",-2] if $post_process;
738 if ($nlink == 2 && !$avoid_nlink) {
739 # This dir has no subdirectories.
740 for my $FN (@filenames) {
741 next if $FN =~ $File::Find::skip_pattern;
743 $name = $dir_pref . $FN; # $File::Find::name
744 $_ = ($no_chdir ? $name : $FN); # $_
745 { &$wanted_callback }; # protect against wild "next"
749 else {
750 # This dir has subdirectories.
751 $subcount = $nlink - 2;
753 for my $FN (@filenames) {
754 next if $FN =~ $File::Find::skip_pattern;
755 if ($subcount > 0 || $avoid_nlink) {
756 # Seen all the subdirs?
757 # check for directoriness.
758 # stat is faster for a file in the current directory
759 $sub_nlink = (lstat ($no_chdir ? $dir_pref . $FN : $FN))[3];
761 if (-d _) {
762 --$subcount;
763 $FN =~ s/\.dir\z// if $Is_VMS;
764 push @Stack,[$CdLvl,$dir_name,$FN,$sub_nlink];
766 else {
767 $name = $dir_pref . $FN; # $File::Find::name
768 $_= ($no_chdir ? $name : $FN); # $_
769 { &$wanted_callback }; # protect against wild "next"
772 else {
773 $name = $dir_pref . $FN; # $File::Find::name
774 $_= ($no_chdir ? $name : $FN); # $_
775 { &$wanted_callback }; # protect against wild "next"
780 continue {
781 while ( defined ($SE = pop @Stack) ) {
782 ($Level, $p_dir, $dir_rel, $nlink) = @$SE;
783 if ($CdLvl > $Level && !$no_chdir) {
784 my $tmp;
785 if ($Is_MacOS) {
786 $tmp = (':' x ($CdLvl-$Level)) . ':';
788 else {
789 $tmp = join('/',('..') x ($CdLvl-$Level));
791 die "Can't cd to $dir_name" . $tmp
792 unless chdir ($tmp);
793 $CdLvl = $Level;
796 if ($Is_MacOS) {
797 # $pdir always has a trailing ':', except for the starting dir,
798 # where $dir_rel eq ':'
799 $dir_name = "$p_dir$dir_rel";
800 $dir_pref = "$dir_name:";
802 else {
803 $dir_name = ($p_dir eq '/' ? "/$dir_rel" : "$p_dir/$dir_rel");
804 $dir_pref = "$dir_name/";
807 if ( $nlink == -2 ) {
808 $name = $dir = $p_dir; # $File::Find::name / dir
809 if ($Is_MacOS) {
810 $_ = ':'; # $_
812 else {
813 $_ = '.';
815 &$post_process; # End-of-directory processing
817 elsif ( $nlink < 0 ) { # must be finddepth, report dirname now
818 $name = $dir_name;
819 if ($Is_MacOS) {
820 if ($dir_rel eq ':') { # must be the top dir, where we started
821 $name =~ s|:$||; # $File::Find::name
822 $p_dir = "$p_dir:" unless ($p_dir =~ /:$/);
824 $dir = $p_dir; # $File::Find::dir
825 $_ = ($no_chdir ? $name : $dir_rel); # $_
827 else {
828 if ( substr($name,-2) eq '/.' ) {
829 $name =~ s|/\.$||;
831 $dir = $p_dir;
832 $_ = ($no_chdir ? $dir_name : $dir_rel );
833 if ( substr($_,-2) eq '/.' ) {
834 s|/\.$||;
837 { &$wanted_callback }; # protect against wild "next"
839 else {
840 push @Stack,[$CdLvl,$p_dir,$dir_rel,-1] if $bydepth;
841 last;
848 # API:
849 # $wanted
850 # $dir_loc : absolute location of a dir
851 # $p_dir : "parent directory"
852 # preconditions:
853 # chdir (if not no_chdir) to dir
855 sub _find_dir_symlnk($$$) {
856 my ($wanted, $dir_loc, $p_dir) = @_; # $dir_loc is the absolute directory
857 my @Stack;
858 my @filenames;
859 my $new_loc;
860 my $updir_loc = $dir_loc; # untainted parent directory
861 my $SE = [];
862 my $dir_name = $p_dir;
863 my $dir_pref;
864 my $loc_pref;
865 my $dir_rel;
866 my $byd_flag; # flag for pending stack entry if $bydepth
867 my $tainted = 0;
868 my $ok = 1;
870 if ($Is_MacOS) {
871 $dir_pref = ($p_dir =~ /:$/) ? "$p_dir" : "$p_dir:";
872 $loc_pref = ($dir_loc =~ /:$/) ? "$dir_loc" : "$dir_loc:";
873 $dir_rel = ':'; # directory name relative to current directory
874 } else {
875 $dir_pref = ( $p_dir eq '/' ? '/' : "$p_dir/" );
876 $loc_pref = ( $dir_loc eq '/' ? '/' : "$dir_loc/" );
877 $dir_rel = '.'; # directory name relative to current directory
880 local ($dir, $name, $fullname, $prune, *DIR);
882 unless ($no_chdir) {
883 # untaint the topdir
884 if (( $untaint ) && (is_tainted($dir_loc) )) {
885 ( $updir_loc ) = $dir_loc =~ m|$untaint_pat|; # parent dir, now untainted
886 # once untainted, $updir_loc is pushed on the stack (as parent directory);
887 # hence, we don't need to untaint the parent directory every time we chdir
888 # to it later
889 unless (defined $updir_loc) {
890 if ($untaint_skip == 0) {
891 die "directory $dir_loc is still tainted";
893 else {
894 return;
898 $ok = chdir($updir_loc) unless ($p_dir eq $File::Find::current_dir);
899 unless ($ok) {
900 warn "Can't cd to $updir_loc: $!\n" if $^W;
901 return;
905 push @Stack,[$dir_loc,$updir_loc,$p_dir,$dir_rel,-1] if $bydepth;
907 if ($Is_MacOS) {
908 $p_dir = $dir_pref; # ensure trailing ':'
911 while (defined $SE) {
913 unless ($bydepth) {
914 # change (back) to parent directory (always untainted)
915 unless ($no_chdir) {
916 unless (chdir $updir_loc) {
917 warn "Can't cd to $updir_loc: $!\n" if $^W;
918 next;
921 $dir= $p_dir; # $File::Find::dir
922 $name= $dir_name; # $File::Find::name
923 $_= ($no_chdir ? $dir_name : $dir_rel ); # $_
924 $fullname= $dir_loc; # $File::Find::fullname
925 # prune may happen here
926 $prune= 0;
927 lstat($_); # make sure file tests with '_' work
928 { &$wanted_callback }; # protect against wild "next"
929 next if $prune;
932 # change to that directory
933 unless ($no_chdir || ($dir_rel eq $File::Find::current_dir)) {
934 $updir_loc = $dir_loc;
935 if ( ($untaint) && (($tainted) || ($tainted = is_tainted($dir_loc) )) ) {
936 # untaint $dir_loc, what will be pushed on the stack as (untainted) parent dir
937 ( $updir_loc ) = $dir_loc =~ m|$untaint_pat|;
938 unless (defined $updir_loc) {
939 if ($untaint_skip == 0) {
940 die "directory $dir_loc is still tainted";
942 else {
943 next;
947 unless (chdir $updir_loc) {
948 warn "Can't cd to $updir_loc: $!\n" if $^W;
949 next;
953 if ($Is_MacOS) {
954 $dir_name = "$dir_name:" unless ($dir_name =~ /:$/);
957 $dir = $dir_name; # $File::Find::dir
959 # Get the list of files in the current directory.
960 unless (opendir DIR, ($no_chdir ? $dir_loc : $File::Find::current_dir)) {
961 warn "Can't opendir($dir_loc): $!\n" if $^W;
962 next;
964 @filenames = readdir DIR;
965 closedir(DIR);
967 for my $FN (@filenames) {
968 next if $FN =~ $File::Find::skip_pattern;
970 # follow symbolic links / do an lstat
971 $new_loc = Follow_SymLink($loc_pref.$FN);
973 # ignore if invalid symlink
974 next unless defined $new_loc;
976 if (-d _) {
977 push @Stack,[$new_loc,$updir_loc,$dir_name,$FN,1];
979 else {
980 $fullname = $new_loc; # $File::Find::fullname
981 $name = $dir_pref . $FN; # $File::Find::name
982 $_ = ($no_chdir ? $name : $FN); # $_
983 { &$wanted_callback }; # protect against wild "next"
988 continue {
989 while (defined($SE = pop @Stack)) {
990 ($dir_loc, $updir_loc, $p_dir, $dir_rel, $byd_flag) = @$SE;
991 if ($Is_MacOS) {
992 # $p_dir always has a trailing ':', except for the starting dir,
993 # where $dir_rel eq ':'
994 $dir_name = "$p_dir$dir_rel";
995 $dir_pref = "$dir_name:";
996 $loc_pref = ($dir_loc =~ /:$/) ? $dir_loc : "$dir_loc:";
998 else {
999 $dir_name = ($p_dir eq '/' ? "/$dir_rel" : "$p_dir/$dir_rel");
1000 $dir_pref = "$dir_name/";
1001 $loc_pref = "$dir_loc/";
1003 if ( $byd_flag < 0 ) { # must be finddepth, report dirname now
1004 unless ($no_chdir || ($dir_rel eq $File::Find::current_dir)) {
1005 unless (chdir $updir_loc) { # $updir_loc (parent dir) is always untainted
1006 warn "Can't cd to $updir_loc: $!\n" if $^W;
1007 next;
1010 $fullname = $dir_loc; # $File::Find::fullname
1011 $name = $dir_name; # $File::Find::name
1012 if ($Is_MacOS) {
1013 if ($dir_rel eq ':') { # must be the top dir, where we started
1014 $name =~ s|:$||; # $File::Find::name
1015 $p_dir = "$p_dir:" unless ($p_dir =~ /:$/);
1017 $dir = $p_dir; # $File::Find::dir
1018 $_ = ($no_chdir ? $name : $dir_rel); # $_
1020 else {
1021 if ( substr($name,-2) eq '/.' ) {
1022 $name =~ s|/\.$||; # $File::Find::name
1024 $dir = $p_dir; # $File::Find::dir
1025 $_ = ($no_chdir ? $dir_name : $dir_rel); # $_
1026 if ( substr($_,-2) eq '/.' ) {
1027 s|/\.$||;
1031 lstat($_); # make sure file tests with '_' work
1032 { &$wanted_callback }; # protect against wild "next"
1034 else {
1035 push @Stack,[$dir_loc, $updir_loc, $p_dir, $dir_rel,-1] if $bydepth;
1036 last;
1043 sub wrap_wanted {
1044 my $wanted = shift;
1045 if ( ref($wanted) eq 'HASH' ) {
1046 if ( $wanted->{follow} || $wanted->{follow_fast}) {
1047 $wanted->{follow_skip} = 1 unless defined $wanted->{follow_skip};
1049 if ( $wanted->{untaint} ) {
1050 $wanted->{untaint_pattern} = $File::Find::untaint_pattern
1051 unless defined $wanted->{untaint_pattern};
1052 $wanted->{untaint_skip} = 0 unless defined $wanted->{untaint_skip};
1054 return $wanted;
1056 else {
1057 return { wanted => $wanted };
1061 sub find {
1062 my $wanted = shift;
1063 _find_opt(wrap_wanted($wanted), @_);
1066 sub finddepth {
1067 my $wanted = wrap_wanted(shift);
1068 $wanted->{bydepth} = 1;
1069 _find_opt($wanted, @_);
1072 # default
1073 $File::Find::skip_pattern = qr/^\.{1,2}\z/;
1074 $File::Find::untaint_pattern = qr|^([-+@\w./]+)$|;
1076 # These are hard-coded for now, but may move to hint files.
1077 if ($^O eq 'VMS') {
1078 $Is_VMS = 1;
1079 $File::Find::dont_use_nlink = 1;
1081 elsif ($^O eq 'MacOS') {
1082 $Is_MacOS = 1;
1083 $File::Find::dont_use_nlink = 1;
1084 $File::Find::skip_pattern = qr/^Icon\015\z/;
1085 $File::Find::untaint_pattern = qr|^(.+)$|;
1088 # this _should_ work properly on all platforms
1089 # where File::Find can be expected to work
1090 $File::Find::current_dir = File::Spec->curdir || '.';
1092 $File::Find::dont_use_nlink = 1
1093 if $^O eq 'os2' || $^O eq 'dos' || $^O eq 'amigaos' || $^O eq 'MSWin32' ||
1094 $^O eq 'cygwin' || $^O eq 'epoc' || $^O eq 'NetWare' || $^O eq 'msys';
1096 # Set dont_use_nlink in your hint file if your system's stat doesn't
1097 # report the number of links in a directory as an indication
1098 # of the number of files.
1099 # See, e.g. hints/machten.sh for MachTen 2.2.
1100 unless ($File::Find::dont_use_nlink) {
1101 require Config;
1102 $File::Find::dont_use_nlink = 1 if ($Config::Config{'dont_use_nlink'});
1105 # We need a function that checks if a scalar is tainted. Either use the
1106 # Scalar::Util module's tainted() function or our (slower) pure Perl
1107 # fallback is_tainted_pp()
1109 local $@;
1110 eval { require Scalar::Util };
1111 *is_tainted = $@ ? \&is_tainted_pp : \&Scalar::Util::tainted;