was Re: [PATCH: 6640] VMS Makefile.SH update (fwd)
[p5sagit/p5-mst-13.2.git] / lib / File / Find.pm
CommitLineData
a0d0e21e 1package File::Find;
17f410f9 2use 5.005_64;
a0d0e21e 3require Exporter;
6280b799 4require Cwd;
a0d0e21e 5
f06db76b 6=head1 NAME
7
8find - traverse a file tree
9
10finddepth - traverse a directory structure depth-first
11
12=head1 SYNOPSIS
13
14 use File::Find;
81793b90 15 find(\&wanted, '/foo', '/bar');
f06db76b 16 sub wanted { ... }
237437d0 17
f06db76b 18 use File::Find;
81793b90 19 finddepth(\&wanted, '/foo', '/bar');
f06db76b 20 sub wanted { ... }
3cb6de81 21
81793b90 22 use File::Find;
23 find({ wanted => \&process, follow => 1 }, '.');
f06db76b 24
25=head1 DESCRIPTION
26
20408e3c 27The first argument to find() is either a hash reference describing the
81793b90 28operations to be performed for each file, or a code reference.
20408e3c 29
81793b90 30Here are the possible keys for the hash:
31
32=over 3
33
34=item C<wanted>
35
36The value should be a code reference. This code reference is called
37I<the wanted() function> below.
38
39=item C<bydepth>
40
41Reports the name of a directory only AFTER all its entries
42have been reported. Entry point finddepth() is a shortcut for
43specifying C<{ bydepth => 1 }> in the first argument of find().
44
719c805e 45=item C<preprocess>
46
47The value should be a code reference. This code reference is used to
48preprocess a directory; it is called after readdir() but before the loop that
49calls the wanted() function. It is called with a list of strings and is
50expected to return a list of strings. The code can be used to sort the
51strings alphabetically, numerically, or to filter out directory entries based
52on their name alone.
53
54=item C<postprocess>
55
56The value should be a code reference. It is invoked just before leaving the
57current directory. It is called in void context with no arguments. The name
58of the current directory is in $File::Find::dir. This hook is handy for
59summarizing a directory, such as calculating its disk usage.
60
81793b90 61=item C<follow>
62
63Causes symbolic links to be followed. Since directory trees with symbolic
64links (followed) may contain files more than once and may even have
65cycles, a hash has to be built up with an entry for each file.
66This might be expensive both in space and time for a large
67directory tree. See I<follow_fast> and I<follow_skip> below.
68If either I<follow> or I<follow_fast> is in effect:
69
70=over 6
71
a45bd81d 72=item *
81793b90 73
74It is guarantueed that an I<lstat> has been called before the user's
75I<wanted()> function is called. This enables fast file checks involving S< _>.
76
a45bd81d 77=item *
81793b90 78
79There is a variable C<$File::Find::fullname> which holds the absolute
80pathname of the file with all symbolic links resolved
81
82=back
83
84=item C<follow_fast>
85
86This is similar to I<follow> except that it may report some files
87more than once. It does detect cycles however.
88Since only symbolic links have to be hashed, this is
89much cheaper both in space and time.
90If processing a file more than once (by the user's I<wanted()> function)
91is worse than just taking time, the option I<follow> should be used.
92
93=item C<follow_skip>
94
95C<follow_skip==1>, which is the default, causes all files which are
96neither directories nor symbolic links to be ignored if they are about
97to be processed a second time. If a directory or a symbolic link
98are about to be processed a second time, File::Find dies.
99C<follow_skip==0> causes File::Find to die if any file is about to be
100processed a second time.
101C<follow_skip==2> causes File::Find to ignore any duplicate files and
102dirctories but to proceed normally otherwise.
20408e3c 103
f06db76b 104
81793b90 105=item C<no_chdir>
106
107Does not C<chdir()> to each directory as it recurses. The wanted()
108function will need to be aware of this, of course. In this case,
109C<$_> will be the same as C<$File::Find::name>.
110
111=item C<untaint>
112
113If find is used in taint-mode (-T command line switch or if EUID != UID
114or if EGID != GID) then internally directory names have to be untainted
115before they can be cd'ed to. Therefore they are checked against a regular
116expression I<untaint_pattern>. Note, that all names passed to the
117user's I<wanted()> function are still tainted.
118
119=item C<untaint_pattern>
120
121See above. This should be set using the C<qr> quoting operator.
122The default is set to C<qr|^([-+@\w./]+)$|>.
123Note that the paranthesis which are vital.
124
125=item C<untaint_skip>
126
127If set, directories (subtrees) which fail the I<untaint_pattern>
128are skipped. The default is to 'die' in such a case.
129
130=back
131
132The wanted() function does whatever verifications you want.
133C<$File::Find::dir> contains the current directory name, and C<$_> the
134current filename within that directory. C<$File::Find::name> contains
135the complete pathname to the file. You are chdir()'d to C<$File::Find::dir> when
136the function is called, unless C<no_chdir> was specified.
137When <follow> or <follow_fast> are in effect there is also a
138C<$File::Find::fullname>.
139The function may set C<$File::Find::prune> to prune the tree
140unless C<bydepth> was specified.
e7b91b67 141Unless C<follow> or C<follow_fast> is specified, for compatibility
142reasons (find.pl, find2perl) there are in addition the following globals
143available: C<$File::Find::topdir>, C<$File::Find::topdev>, C<$File::Find::topino>,
144C<$File::Find::topmode> and C<$File::Find::topnlink>.
47a735e8 145
20408e3c 146This library is useful for the C<find2perl> tool, which when fed,
f06db76b 147
148 find2perl / -name .nfs\* -mtime +7 \
81793b90 149 -exec rm -f {} \; -o -fstype nfs -prune
f06db76b 150
151produces something like:
152
153 sub wanted {
c7b9dd21 154 /^\.nfs.*\z/s &&
81793b90 155 (($dev, $ino, $mode, $nlink, $uid, $gid) = lstat($_)) &&
f06db76b 156 int(-M _) > 7 &&
157 unlink($_)
158 ||
81793b90 159 ($nlink || (($dev, $ino, $mode, $nlink, $uid, $gid) = lstat($_))) &&
f06db76b 160 $dev < 0 &&
6280b799 161 ($File::Find::prune = 1);
f06db76b 162 }
163
81793b90 164Set the variable C<$File::Find::dont_use_nlink> if you're using AFS,
6280b799 165since AFS cheats.
f06db76b 166
f06db76b 167
168Here's another interesting wanted function. It will find all symlinks
169that don't resolve:
170
171 sub wanted {
81793b90 172 -l && !-e && print "bogus link: $File::Find::name\n";
237437d0 173 }
f06db76b 174
81793b90 175See also the script C<pfind> on CPAN for a nice application of this
176module.
177
178=head1 CAVEAT
179
180Be aware that the option to follow symblic links can be dangerous.
181Depending on the structure of the directory tree (including symbolic
182links to directories) you might traverse a given (physical) directory
183more than once (only if C<follow_fast> is in effect).
184Furthermore, deleting or changing files in a symbolically linked directory
185might cause very unpleasant surprises, since you delete or change files
186in an unknown directory.
0530a6c4 187
0530a6c4 188
f06db76b 189=cut
190
a0d0e21e 191@ISA = qw(Exporter);
6280b799 192@EXPORT = qw(find finddepth);
193
a0d0e21e 194
81793b90 195use strict;
196my $Is_VMS;
197
198require File::Basename;
199
200my %SLnkSeen;
201my ($wanted_callback, $avoid_nlink, $bydepth, $no_chdir, $follow,
719c805e 202 $follow_skip, $full_check, $untaint, $untaint_skip, $untaint_pat,
203 $pre_process, $post_process);
81793b90 204
205sub contract_name {
206 my ($cdir,$fn) = @_;
207
208 return substr($cdir,0,rindex($cdir,'/')) if $fn eq '.';
209
210 $cdir = substr($cdir,0,rindex($cdir,'/')+1);
211
212 $fn =~ s|^\./||;
213
214 my $abs_name= $cdir . $fn;
215
216 if (substr($fn,0,3) eq '../') {
217 do 1 while ($abs_name=~ s|/(?>[^/]+)/\.\./|/|);
218 }
219
220 return $abs_name;
221}
222
223
224sub PathCombine($$) {
225 my ($Base,$Name) = @_;
226 my $AbsName;
227
228 if (substr($Name,0,1) eq '/') {
229 $AbsName= $Name;
230 }
231 else {
232 $AbsName= contract_name($Base,$Name);
233 }
234
235 # (simple) check for recursion
236 my $newlen= length($AbsName);
237 if ($newlen <= length($Base)) {
238 if (($newlen == length($Base) || substr($Base,$newlen,1) eq '/')
239 && $AbsName eq substr($Base,0,$newlen))
240 {
241 return undef;
242 }
243 }
244 return $AbsName;
245}
246
247sub Follow_SymLink($) {
248 my ($AbsName) = @_;
249
250 my ($NewName,$DEV, $INO);
251 ($DEV, $INO)= lstat $AbsName;
252
253 while (-l _) {
254 if ($SLnkSeen{$DEV, $INO}++) {
255 if ($follow_skip < 2) {
256 die "$AbsName is encountered a second time";
a0d0e21e 257 }
258 else {
81793b90 259 return undef;
a0d0e21e 260 }
261 }
81793b90 262 $NewName= PathCombine($AbsName, readlink($AbsName));
263 unless(defined $NewName) {
264 if ($follow_skip < 2) {
265 die "$AbsName is a recursive symbolic link";
266 }
267 else {
268 return undef;
a0d0e21e 269 }
81793b90 270 }
271 else {
272 $AbsName= $NewName;
273 }
274 ($DEV, $INO) = lstat($AbsName);
275 return undef unless defined $DEV; # dangling symbolic link
276 }
277
278 if ($full_check && $SLnkSeen{$DEV, $INO}++) {
279 if ($follow_skip < 1) {
280 die "$AbsName encountered a second time";
281 }
282 else {
283 return undef;
284 }
285 }
286
287 return $AbsName;
288}
289
17f410f9 290our($dir, $name, $fullname, $prune);
81793b90 291sub _find_dir_symlnk($$$);
292sub _find_dir($$$);
293
294sub _find_opt {
295 my $wanted = shift;
296 die "invalid top directory" unless defined $_[0];
297
298 my $cwd = $wanted->{bydepth} ? Cwd::fastcwd() : Cwd::cwd();
299 my $cwd_untainted = $cwd;
300 $wanted_callback = $wanted->{wanted};
301 $bydepth = $wanted->{bydepth};
719c805e 302 $pre_process = $wanted->{preprocess};
303 $post_process = $wanted->{postprocess};
81793b90 304 $no_chdir = $wanted->{no_chdir};
305 $full_check = $wanted->{follow};
306 $follow = $full_check || $wanted->{follow_fast};
307 $follow_skip = $wanted->{follow_skip};
308 $untaint = $wanted->{untaint};
309 $untaint_pat = $wanted->{untaint_pattern};
310 $untaint_skip = $wanted->{untaint_skip};
311
e7b91b67 312 # for compatability reasons (find.pl, find2perl)
313 our ($topdir, $topdev, $topino, $topmode, $topnlink);
81793b90 314
315 # a symbolic link to a directory doesn't increase the link count
316 $avoid_nlink = $follow || $File::Find::dont_use_nlink;
317
318 if ( $untaint ) {
319 $cwd_untainted= $1 if $cwd_untainted =~ m|$untaint_pat|;
320 die "insecure cwd in find(depth)" unless defined($cwd_untainted);
321 }
322
e7b91b67 323 my ($abs_dir, $Is_Dir);
81793b90 324
325 Proc_Top_Item:
326 foreach my $TOP (@_) {
327 my $top_item = $TOP;
c7b9dd21 328 $top_item =~ s|/\z|| unless $top_item eq '/';
81793b90 329 $Is_Dir= 0;
330
3d1e7443 331 ($topdev,$topino,$topmode,$topnlink) = stat $top_item;
332
81793b90 333 if ($follow) {
334 if (substr($top_item,0,1) eq '/') {
335 $abs_dir = $top_item;
336 }
337 elsif ($top_item eq '.') {
338 $abs_dir = $cwd;
237437d0 339 }
81793b90 340 else { # care about any ../
341 $abs_dir = contract_name("$cwd/",$top_item);
342 }
343 $abs_dir= Follow_SymLink($abs_dir);
344 unless (defined $abs_dir) {
345 warn "$top_item is a dangling symbolic link\n";
346 next Proc_Top_Item;
347 }
348 if (-d _) {
349 _find_dir_symlnk($wanted, $abs_dir, $top_item);
350 $Is_Dir= 1;
351 }
352 }
353 else { # no follow
e7b91b67 354 $topdir = $top_item;
e7b91b67 355 unless (defined $topnlink) {
81793b90 356 warn "Can't stat $top_item: $!\n";
357 next Proc_Top_Item;
358 }
359 if (-d _) {
c7b9dd21 360 $top_item =~ s/\.dir\z// if $Is_VMS;
e7b91b67 361 _find_dir($wanted, $top_item, $topnlink);
81793b90 362 $Is_Dir= 1;
363 }
237437d0 364 else {
81793b90 365 $abs_dir= $top_item;
366 }
367 }
368
369 unless ($Is_Dir) {
370 unless (($_,$dir) = File::Basename::fileparse($abs_dir)) {
719911cc 371 ($dir,$_) = ('./', $top_item);
81793b90 372 }
373
374 $abs_dir = $dir;
375 if ($untaint) {
376 my $abs_dir_save = $abs_dir;
377 $abs_dir = $1 if $abs_dir =~ m|$untaint_pat|;
378 unless (defined $abs_dir) {
379 if ($untaint_skip == 0) {
380 die "directory $abs_dir_save is still tainted";
381 }
382 else {
383 next Proc_Top_Item;
384 }
385 }
386 }
387
388 unless ($no_chdir or chdir $abs_dir) {
389 warn "Couldn't chdir $abs_dir: $!\n";
390 next Proc_Top_Item;
391 }
719911cc 392
393 $name = $abs_dir . $_;
394
73396e07 395 { &$wanted_callback }; # protect against wild "next"
81793b90 396
397 }
398
399 $no_chdir or chdir $cwd_untainted;
400 }
401}
402
403# API:
404# $wanted
405# $p_dir : "parent directory"
406# $nlink : what came back from the stat
407# preconditions:
408# chdir (if not no_chdir) to dir
409
410sub _find_dir($$$) {
411 my ($wanted, $p_dir, $nlink) = @_;
412 my ($CdLvl,$Level) = (0,0);
413 my @Stack;
414 my @filenames;
415 my ($subcount,$sub_nlink);
416 my $SE= [];
417 my $dir_name= $p_dir;
07867069 418 my $dir_pref= ( $p_dir eq '/' ? '/' : "$p_dir/" );
81793b90 419 my $dir_rel= '.'; # directory name relative to current directory
420
421 local ($dir, $name, $prune, *DIR);
422
423 unless ($no_chdir or $p_dir eq '.') {
424 my $udir = $p_dir;
425 if ($untaint) {
426 $udir = $1 if $p_dir =~ m|$untaint_pat|;
427 unless (defined $udir) {
428 if ($untaint_skip == 0) {
429 die "directory $p_dir is still tainted";
430 }
431 else {
432 return;
433 }
237437d0 434 }
a0d0e21e 435 }
81793b90 436 unless (chdir $udir) {
437 warn "Can't cd to $udir: $!\n";
438 return;
439 }
440 }
57e73c4b 441
442 push @Stack,[$CdLvl,$p_dir,$dir_rel,-1] if $bydepth;
81793b90 443
444 while (defined $SE) {
445 unless ($bydepth) {
446 $dir= $p_dir;
447 $name= $dir_name;
448 $_= ($no_chdir ? $dir_name : $dir_rel );
449 # prune may happen here
450 $prune= 0;
73396e07 451 { &$wanted_callback }; # protect against wild "next"
81793b90 452 next if $prune;
453 }
454
455 # change to that directory
456 unless ($no_chdir or $dir_rel eq '.') {
457 my $udir= $dir_rel;
458 if ($untaint) {
459 $udir = $1 if $dir_rel =~ m|$untaint_pat|;
460 unless (defined $udir) {
461 if ($untaint_skip == 0) {
07867069 462 die "directory ("
463 . ($p_dir ne '/' ? $p_dir : '')
464 . "/) $dir_rel is still tainted";
81793b90 465 }
466 }
467 }
468 unless (chdir $udir) {
07867069 469 warn "Can't cd to ("
470 . ($p_dir ne '/' ? $p_dir : '')
471 . "/) $udir : $!\n";
81793b90 472 next;
473 }
474 $CdLvl++;
475 }
476
477 $dir= $dir_name;
478
479 # Get the list of files in the current directory.
480 unless (opendir DIR, ($no_chdir ? $dir_name : '.')) {
481 warn "Can't opendir($dir_name): $!\n";
482 next;
483 }
484 @filenames = readdir DIR;
485 closedir(DIR);
719c805e 486 @filenames = &$pre_process(@filenames) if $pre_process;
487 push @Stack,[$CdLvl,$dir_name,"",-2] if $post_process;
81793b90 488
489 if ($nlink == 2 && !$avoid_nlink) {
490 # This dir has no subdirectories.
491 for my $FN (@filenames) {
c7b9dd21 492 next if $FN =~ /^\.{1,2}\z/;
81793b90 493
07867069 494 $name = $dir_pref . $FN;
81793b90 495 $_ = ($no_chdir ? $name : $FN);
73396e07 496 { &$wanted_callback }; # protect against wild "next"
81793b90 497 }
498
499 }
500 else {
501 # This dir has subdirectories.
502 $subcount = $nlink - 2;
503
504 for my $FN (@filenames) {
c7b9dd21 505 next if $FN =~ /^\.{1,2}\z/;
81793b90 506 if ($subcount > 0 || $avoid_nlink) {
507 # Seen all the subdirs?
508 # check for directoriness.
509 # stat is faster for a file in the current directory
07867069 510 $sub_nlink = (lstat ($no_chdir ? $dir_pref . $FN : $FN))[3];
81793b90 511
512 if (-d _) {
513 --$subcount;
c7b9dd21 514 $FN =~ s/\.dir\z// if $Is_VMS;
81793b90 515 push @Stack,[$CdLvl,$dir_name,$FN,$sub_nlink];
516 }
517 else {
07867069 518 $name = $dir_pref . $FN;
81793b90 519 $_= ($no_chdir ? $name : $FN);
73396e07 520 { &$wanted_callback }; # protect against wild "next"
81793b90 521 }
522 }
07867069 523 else {
524 $name = $dir_pref . $FN;
81793b90 525 $_= ($no_chdir ? $name : $FN);
73396e07 526 { &$wanted_callback }; # protect against wild "next"
81793b90 527 }
528 }
529 }
17b275ff 530 }
531 continue {
57e73c4b 532 while ( defined ($SE = pop @Stack) ) {
81793b90 533 ($Level, $p_dir, $dir_rel, $nlink) = @$SE;
534 if ($CdLvl > $Level && !$no_chdir) {
f0963acb 535 my $tmp = join('/',('..') x ($CdLvl-$Level));
536 die "Can't cd to $dir_name" . $tmp
537 unless chdir ($tmp);
81793b90 538 $CdLvl = $Level;
539 }
07867069 540 $dir_name = ($p_dir eq '/' ? "/$dir_rel" : "$p_dir/$dir_rel");
541 $dir_pref = "$dir_name/";
719c805e 542 if ( $nlink == -2 ) {
543 $name = $dir = $p_dir;
544 $_ = ".";
545 &$post_process; # End-of-directory processing
546 } elsif ( $nlink < 0 ) { # must be finddepth, report dirname now
57e73c4b 547 $name = $dir_name;
57907763 548 if ( substr($name,-2) eq '/.' ) {
549 $name =~ s|/\.$||;
550 }
57e73c4b 551 $dir = $p_dir;
552 $_ = ($no_chdir ? $dir_name : $dir_rel );
57907763 553 if ( substr($_,-2) eq '/.' ) {
554 s|/\.$||;
555 }
73396e07 556 { &$wanted_callback }; # protect against wild "next"
57e73c4b 557 } else {
558 push @Stack,[$CdLvl,$p_dir,$dir_rel,-1] if $bydepth;
559 last;
560 }
81793b90 561 }
a0d0e21e 562 }
563}
564
81793b90 565
566# API:
567# $wanted
568# $dir_loc : absolute location of a dir
569# $p_dir : "parent directory"
570# preconditions:
571# chdir (if not no_chdir) to dir
572
573sub _find_dir_symlnk($$$) {
574 my ($wanted, $dir_loc, $p_dir) = @_;
575 my @Stack;
576 my @filenames;
577 my $new_loc;
57e73c4b 578 my $pdir_loc = $dir_loc;
81793b90 579 my $SE = [];
580 my $dir_name = $p_dir;
07867069 581 my $dir_pref = ( $p_dir eq '/' ? '/' : "$p_dir/" );
582 my $loc_pref = ( $dir_loc eq '/' ? '/' : "$dir_loc/" );
81793b90 583 my $dir_rel = '.'; # directory name relative to current directory
57e73c4b 584 my $byd_flag; # flag for pending stack entry if $bydepth
81793b90 585
586 local ($dir, $name, $fullname, $prune, *DIR);
587
588 unless ($no_chdir or $p_dir eq '.') {
589 my $udir = $dir_loc;
590 if ($untaint) {
591 $udir = $1 if $dir_loc =~ m|$untaint_pat|;
592 unless (defined $udir) {
593 if ($untaint_skip == 0) {
594 die "directory $dir_loc is still tainted";
595 }
596 else {
597 return;
598 }
599 }
600 }
601 unless (chdir $udir) {
602 warn "Can't cd to $udir: $!\n";
603 return;
604 }
605 }
606
57e73c4b 607 push @Stack,[$dir_loc,$pdir_loc,$p_dir,$dir_rel,-1] if $bydepth;
608
81793b90 609 while (defined $SE) {
610
611 unless ($bydepth) {
704ea872 612 # change to parent directory
613 unless ($no_chdir) {
614 my $udir = $pdir_loc;
615 if ($untaint) {
616 $udir = $1 if $pdir_loc =~ m|$untaint_pat|;
617 }
618 unless (chdir $udir) {
619 warn "Can't cd to $udir: $!\n";
620 next;
621 }
622 }
81793b90 623 $dir= $p_dir;
624 $name= $dir_name;
625 $_= ($no_chdir ? $dir_name : $dir_rel );
626 $fullname= $dir_loc;
627 # prune may happen here
628 $prune= 0;
704ea872 629 lstat($_); # make sure file tests with '_' work
73396e07 630 { &$wanted_callback }; # protect against wild "next"
81793b90 631 next if $prune;
632 }
633
634 # change to that directory
635 unless ($no_chdir or $dir_rel eq '.') {
636 my $udir = $dir_loc;
637 if ($untaint) {
638 $udir = $1 if $dir_loc =~ m|$untaint_pat|;
639 unless (defined $udir ) {
640 if ($untaint_skip == 0) {
641 die "directory $dir_loc is still tainted";
a0d0e21e 642 }
237437d0 643 else {
81793b90 644 next;
237437d0 645 }
a0d0e21e 646 }
647 }
81793b90 648 unless (chdir $udir) {
649 warn "Can't cd to $udir: $!\n";
650 next;
651 }
652 }
653
654 $dir = $dir_name;
655
656 # Get the list of files in the current directory.
657 unless (opendir DIR, ($no_chdir ? $dir_loc : '.')) {
658 warn "Can't opendir($dir_loc): $!\n";
659 next;
660 }
661 @filenames = readdir DIR;
662 closedir(DIR);
663
664 for my $FN (@filenames) {
c7b9dd21 665 next if $FN =~ /^\.{1,2}\z/;
81793b90 666
667 # follow symbolic links / do an lstat
07867069 668 $new_loc = Follow_SymLink($loc_pref.$FN);
81793b90 669
670 # ignore if invalid symlink
671 next unless defined $new_loc;
672
673 if (-d _) {
57e73c4b 674 push @Stack,[$new_loc,$dir_loc,$dir_name,$FN,1];
81793b90 675 }
676 else {
677 $fullname = $new_loc;
07867069 678 $name = $dir_pref . $FN;
81793b90 679 $_ = ($no_chdir ? $name : $FN);
73396e07 680 { &$wanted_callback }; # protect against wild "next"
81793b90 681 }
682 }
683
81793b90 684 }
685 continue {
57e73c4b 686 while (defined($SE = pop @Stack)) {
687 ($dir_loc, $pdir_loc, $p_dir, $dir_rel, $byd_flag) = @$SE;
07867069 688 $dir_name = ($p_dir eq '/' ? "/$dir_rel" : "$p_dir/$dir_rel");
689 $dir_pref = "$dir_name/";
690 $loc_pref = "$dir_loc/";
57e73c4b 691 if ( $byd_flag < 0 ) { # must be finddepth, report dirname now
692 unless ($no_chdir or $dir_rel eq '.') {
693 my $udir = $pdir_loc;
694 if ($untaint) {
695 $udir = $1 if $dir_loc =~ m|$untaint_pat|;
696 }
697 unless (chdir $udir) {
698 warn "Can't cd to $udir: $!\n";
699 next;
700 }
701 }
702 $fullname = $dir_loc;
703 $name = $dir_name;
57907763 704 if ( substr($name,-2) eq '/.' ) {
705 $name =~ s|/\.$||;
706 }
57e73c4b 707 $dir = $p_dir;
708 $_ = ($no_chdir ? $dir_name : $dir_rel);
57907763 709 if ( substr($_,-2) eq '/.' ) {
710 s|/\.$||;
711 }
712
704ea872 713 lstat($_); # make sure file tests with '_' work
73396e07 714 { &$wanted_callback }; # protect against wild "next"
57e73c4b 715 } else {
716 push @Stack,[$dir_loc, $pdir_loc, $p_dir, $dir_rel,-1] if $bydepth;
717 last;
718 }
a0d0e21e 719 }
720 }
721}
722
81793b90 723
20408e3c 724sub wrap_wanted {
81793b90 725 my $wanted = shift;
726 if ( ref($wanted) eq 'HASH' ) {
727 if ( $wanted->{follow} || $wanted->{follow_fast}) {
728 $wanted->{follow_skip} = 1 unless defined $wanted->{follow_skip};
729 }
730 if ( $wanted->{untaint} ) {
731 $wanted->{untaint_pattern} = qr|^([-+@\w./]+)$|
732 unless defined $wanted->{untaint_pattern};
733 $wanted->{untaint_skip} = 0 unless defined $wanted->{untaint_skip};
734 }
735 return $wanted;
736 }
737 else {
738 return { wanted => $wanted };
739 }
a0d0e21e 740}
741
20408e3c 742sub find {
81793b90 743 my $wanted = shift;
744 _find_opt(wrap_wanted($wanted), @_);
745 %SLnkSeen= (); # free memory
a0d0e21e 746}
747
55d729e4 748sub finddepth {
81793b90 749 my $wanted = wrap_wanted(shift);
750 $wanted->{bydepth} = 1;
751 _find_opt($wanted, @_);
752 %SLnkSeen= (); # free memory
20408e3c 753}
6280b799 754
755# These are hard-coded for now, but may move to hint files.
10eba763 756if ($^O eq 'VMS') {
81793b90 757 $Is_VMS = 1;
758 $File::Find::dont_use_nlink = 1;
748a9306 759}
760
81793b90 761$File::Find::dont_use_nlink = 1
497711e7 762 if $^O eq 'os2' || $^O eq 'dos' || $^O eq 'amigaos' || $^O eq 'MSWin32' ||
763 $^O eq 'cygwin';
6280b799 764
20408e3c 765# Set dont_use_nlink in your hint file if your system's stat doesn't
766# report the number of links in a directory as an indication
767# of the number of files.
768# See, e.g. hints/machten.sh for MachTen 2.2.
81793b90 769unless ($File::Find::dont_use_nlink) {
770 require Config;
771 $File::Find::dont_use_nlink = 1 if ($Config::Config{'dont_use_nlink'});
20408e3c 772}
773
a0d0e21e 7741;