package File::Find;
+use 5.006;
use strict;
use warnings;
-use 5.6.0;
-our $VERSION = '1.01';
+use warnings::register;
+our $VERSION = '1.04';
require Exporter;
require Cwd;
the currently processed directory. It is called in void context with no
arguments. The name of the current directory is in $File::Find::dir. This
hook is handy for summarizing a directory, such as calculating its disk
-usage. When I<follow> or I<follow_fast> are in effect, C<preprocess> is a
+usage. When I<follow> or I<follow_fast> are in effect, C<postprocess> is a
no-op.
=item C<follow>
C<follow_skip==2> causes File::Find to ignore any duplicate files and
directories but to proceed normally otherwise.
+=item C<dangling_symlinks>
+
+If true and a code reference, will be called with the symbolic link
+name and the directory it lives in as arguments. Otherwise, if true
+and warnings are on, warning "symbolic_link_name is a dangling
+symbolic link\n" will be issued. If false, the dangling symbolic link
+will be silently ignored.
=item C<no_chdir>
See above. This should be set using the C<qr> quoting operator.
The default is set to C<qr|^([-+@\w./]+)$|>.
-Note that the parantheses are vital.
+Note that the parentheses are vital.
=item C<untaint_skip>
($File::Find::prune = 1);
}
-Set the variable C<$File::Find::dont_use_nlink> if you're using AFS,
-since AFS cheats.
-
+Notice the C<_> in the above C<int(-M _)>: the C<_> is a magical
+filehandle that caches the information from the preceding
+stat(), lstat(), or filetest.
-Here's another interesting wanted function. It will find all symlinks
-that don't resolve:
+Here's another interesting wanted function. It will find all symbolic
+links that don't resolve:
sub wanted {
-l && !-e && print "bogus link: $File::Find::name\n";
See also the script C<pfind> on CPAN for a nice application of this
module.
+=head1 WARNINGS
+
+If you run your program with the C<-w> switch, or if you use the
+C<warnings> pragma, File::Find will report warnings for several weird
+situations. You can disable these warnings by putting the statement
+
+ no warnings 'File::Find';
+
+in the appropriate scope. See L<perllexwarn> for more info about lexical
+warnings.
+
=head1 CAVEAT
+=over 2
+
+=item $dont_use_nlink
+
+You can set the variable C<$File::Find::dont_use_nlink> to 1, if you want to
+force File::Find to always stat directories. This was used for systems
+that do not have the correct C<nlink> count for directories. Examples are
+ISO-9660 (CD-R), AFS, and operating systems like OS/2, DOS and a couple of
+others.
+
+Since now File::Find should now detect such things on-the-fly and switch it
+self to using stat, this will probably not a problem to you.
+
+If you do set $dont_use_nlink to 1, you will notice slow-downs.
+
+=item symlinks
+
Be aware that the option to follow symbolic links can be dangerous.
Depending on the structure of the directory tree (including symbolic
links to directories) you might traverse a given (physical) directory
might cause very unpleasant surprises, since you delete or change files
in an unknown directory.
+=back
+
=head1 NOTES
=over 4
use Mac::Files;
# invisible() -- returns 1 if file/directory is invisible,
- # 0 if it's visible or undef if an error occured
+ # 0 if it's visible or undef if an error occurred
sub invisible($) {
my $file = shift;
our %SLnkSeen;
our ($wanted_callback, $avoid_nlink, $bydepth, $no_chdir, $follow,
$follow_skip, $full_check, $untaint, $untaint_skip, $untaint_pat,
- $pre_process, $post_process);
+ $pre_process, $post_process, $dangling_symlinks);
sub contract_name {
my ($cdir,$fn) = @_;
return undef unless defined $DEV; # dangling symbolic link
}
- if ($full_check && $SLnkSeen{$DEV, $INO}++) {
+ if ($full_check && defined $DEV && $SLnkSeen{$DEV, $INO}++) {
if ( ($follow_skip < 1) || ((-d _) && ($follow_skip < 2)) ) {
die "$AbsName encountered a second time";
}
local %SLnkSeen;
local ($wanted_callback, $avoid_nlink, $bydepth, $no_chdir, $follow,
$follow_skip, $full_check, $untaint, $untaint_skip, $untaint_pat,
- $pre_process, $post_process);
+ $pre_process, $post_process, $dangling_symlinks);
local($dir, $name, $fullname, $prune);
- my $cwd = $wanted->{bydepth} ? Cwd::fastcwd() : Cwd::cwd();
- my $cwd_untainted = $cwd;
- my $check_t_cwd = 1;
- $wanted_callback = $wanted->{wanted};
- $bydepth = $wanted->{bydepth};
- $pre_process = $wanted->{preprocess};
- $post_process = $wanted->{postprocess};
- $no_chdir = $wanted->{no_chdir};
- $full_check = $wanted->{follow};
- $follow = $full_check || $wanted->{follow_fast};
- $follow_skip = $wanted->{follow_skip};
- $untaint = $wanted->{untaint};
- $untaint_pat = $wanted->{untaint_pattern};
- $untaint_skip = $wanted->{untaint_skip};
-
- # for compatability reasons (find.pl, find2perl)
+ my $cwd = $wanted->{bydepth} ? Cwd::fastcwd() : Cwd::getcwd();
+ my $cwd_untainted = $cwd;
+ my $check_t_cwd = 1;
+ $wanted_callback = $wanted->{wanted};
+ $bydepth = $wanted->{bydepth};
+ $pre_process = $wanted->{preprocess};
+ $post_process = $wanted->{postprocess};
+ $no_chdir = $wanted->{no_chdir};
+ $full_check = $wanted->{follow};
+ $follow = $full_check || $wanted->{follow_fast};
+ $follow_skip = $wanted->{follow_skip};
+ $untaint = $wanted->{untaint};
+ $untaint_pat = $wanted->{untaint_pattern};
+ $untaint_skip = $wanted->{untaint_skip};
+ $dangling_symlinks = $wanted->{dangling_symlinks};
+
+ # for compatibility reasons (find.pl, find2perl)
local our ($topdir, $topdev, $topino, $topmode, $topnlink);
# a symbolic link to a directory doesn't increase the link count
if ($Is_MacOS) {
($topdev,$topino,$topmode,$topnlink) = $follow ? stat $top_item : lstat $top_item;
$top_item = ":$top_item"
- if ( (-d _) && ($top_item =~ /^[^:]+\z/) );
+ if ( (-d _) && ( $top_item !~ /:/ ) );
}
else {
$top_item =~ s|/\z|| unless $top_item eq '/';
else {
$abs_dir = contract_name_Mac($cwd, $top_item);
unless (defined $abs_dir) {
- warn "Can't determine absolute path for $top_item (No such file or directory)\n";
+ warnings::warnif "Can't determine absolute path for $top_item (No such file or directory)\n";
next Proc_Top_Item;
}
}
}
$abs_dir= Follow_SymLink($abs_dir);
unless (defined $abs_dir) {
- warn "$top_item is a dangling symbolic link\n";
+ if ($dangling_symlinks) {
+ if (ref $dangling_symlinks eq 'CODE') {
+ $dangling_symlinks->($top_item, $cwd);
+ } else {
+ warnings::warnif "$top_item is a dangling symbolic link\n";
+ }
+ }
next Proc_Top_Item;
}
else { # no follow
$topdir = $top_item;
unless (defined $topnlink) {
- warn "Can't stat $top_item: $!\n";
+ warnings::warnif "Can't stat $top_item: $!\n";
next Proc_Top_Item;
}
if (-d _) {
}
unless ($no_chdir || chdir $abs_dir) {
- warn "Couldn't chdir $abs_dir: $!\n";
+ warnings::warnif "Couldn't chdir $abs_dir: $!\n";
next Proc_Top_Item;
}
my $SE= [];
my $dir_name= $p_dir;
my $dir_pref;
- my $dir_rel;
+ my $dir_rel = $File::Find::current_dir;
my $tainted = 0;
+ my $no_nlink;
if ($Is_MacOS) {
$dir_pref= ($p_dir =~ /:$/) ? $p_dir : "$p_dir:"; # preface
- $dir_rel= ':'; # directory name relative to current directory
}
else {
$dir_pref= ( $p_dir eq '/' ? '/' : "$p_dir/" );
- $dir_rel= '.'; # directory name relative to current directory
}
local ($dir, $name, $prune, *DIR);
}
}
unless (chdir $udir) {
- warn "Can't cd to $udir: $!\n";
+ warnings::warnif "Can't cd to $udir: $!\n";
return;
}
}
}
unless (chdir $udir) {
if ($Is_MacOS) {
- warn "Can't cd to ($p_dir) $udir: $!\n";
+ warnings::warnif "Can't cd to ($p_dir) $udir: $!\n";
}
else {
- warn "Can't cd to (" . ($p_dir ne '/' ? $p_dir : '') . "/) $udir: $!\n";
+ warnings::warnif "Can't cd to (" .
+ ($p_dir ne '/' ? $p_dir : '') . "/) $udir: $!\n";
}
next;
}
# Get the list of files in the current directory.
unless (opendir DIR, ($no_chdir ? $dir_name : $File::Find::current_dir)) {
- warn "Can't opendir($dir_name): $!\n";
+ warnings::warnif "Can't opendir($dir_name): $!\n";
next;
}
@filenames = readdir DIR;
@filenames = &$pre_process(@filenames) if $pre_process;
push @Stack,[$CdLvl,$dir_name,"",-2] if $post_process;
- if ($nlink == 2 && !$avoid_nlink) {
+ # default: use whatever was specifid
+ # (if $nlink >= 2, and $avoid_nlink == 0, this will switch back)
+ $no_nlink = $avoid_nlink;
+ # if dir has wrong nlink count, force switch to slower stat method
+ $no_nlink = 1 if ($nlink < 2);
+
+ if ($nlink == 2 && !$no_nlink) {
# This dir has no subdirectories.
for my $FN (@filenames) {
next if $FN =~ $File::Find::skip_pattern;
for my $FN (@filenames) {
next if $FN =~ $File::Find::skip_pattern;
- if ($subcount > 0 || $avoid_nlink) {
+ if ($subcount > 0 || $no_nlink) {
# Seen all the subdirs?
# check for directoriness.
# stat is faster for a file in the current directory
if ( $nlink == -2 ) {
$name = $dir = $p_dir; # $File::Find::name / dir
- if ($Is_MacOS) {
- $_ = ':'; # $_
- }
- else {
- $_ = '.';
- }
+ $_ = $File::Find::current_dir;
&$post_process; # End-of-directory processing
}
elsif ( $nlink < 0 ) { # must be finddepth, report dirname now
my $dir_name = $p_dir;
my $dir_pref;
my $loc_pref;
- my $dir_rel;
+ my $dir_rel = $File::Find::current_dir;
my $byd_flag; # flag for pending stack entry if $bydepth
my $tainted = 0;
my $ok = 1;
if ($Is_MacOS) {
$dir_pref = ($p_dir =~ /:$/) ? "$p_dir" : "$p_dir:";
$loc_pref = ($dir_loc =~ /:$/) ? "$dir_loc" : "$dir_loc:";
- $dir_rel = ':'; # directory name relative to current directory
} else {
$dir_pref = ( $p_dir eq '/' ? '/' : "$p_dir/" );
$loc_pref = ( $dir_loc eq '/' ? '/' : "$dir_loc/" );
- $dir_rel = '.'; # directory name relative to current directory
}
local ($dir, $name, $fullname, $prune, *DIR);
}
$ok = chdir($updir_loc) unless ($p_dir eq $File::Find::current_dir);
unless ($ok) {
- warn "Can't cd to $updir_loc: $!\n";
+ warnings::warnif "Can't cd to $updir_loc: $!\n";
return;
}
}
# change (back) to parent directory (always untainted)
unless ($no_chdir) {
unless (chdir $updir_loc) {
- warn "Can't cd to $updir_loc: $!\n";
+ warnings::warnif "Can't cd to $updir_loc: $!\n";
next;
}
}
}
}
unless (chdir $updir_loc) {
- warn "Can't cd to $updir_loc: $!\n";
+ warnings::warnif "Can't cd to $updir_loc: $!\n";
next;
}
}
# Get the list of files in the current directory.
unless (opendir DIR, ($no_chdir ? $dir_loc : $File::Find::current_dir)) {
- warn "Can't opendir($dir_loc): $!\n";
+ warnings::warnif "Can't opendir($dir_loc): $!\n";
next;
}
@filenames = readdir DIR;
if ( $byd_flag < 0 ) { # must be finddepth, report dirname now
unless ($no_chdir || ($dir_rel eq $File::Find::current_dir)) {
unless (chdir $updir_loc) { # $updir_loc (parent dir) is always untainted
- warn "Can't cd to $updir_loc: $!\n";
+ warnings::warnif "Can't cd to $updir_loc: $!\n";
next;
}
}
$File::Find::dont_use_nlink = 1
if $^O eq 'os2' || $^O eq 'dos' || $^O eq 'amigaos' || $^O eq 'MSWin32' ||
- $^O eq 'cygwin' || $^O eq 'epoc';
+ $^O eq 'cygwin' || $^O eq 'epoc' || $^O eq 'NetWare';
# Set dont_use_nlink in your hint file if your system's stat doesn't
# report the number of links in a directory as an indication