diff --git a/ext/File-Find/lib/File/Find.pm b/ext/File-Find/lib/File/Find.pm index 5b507bd13dfa..70db69319291 100644 --- a/ext/File-Find/lib/File/Find.pm +++ b/ext/File-Find/lib/File/Find.pm @@ -123,6 +123,16 @@ sub is_tainted_pp { return length($@) != 0; } +sub _is_absolute { + return $_[0] =~ m|^(?:[A-Za-z]:)?/| if $Is_Win32; + return substr($_[0], 0, 1) eq '/'; +} + +sub _is_root { + return $_[0] =~ m|^(?:[A-Za-z]:)?/\z| if $Is_Win32; + return $_[0] eq '/'; +} + sub _find_opt { my $wanted = shift; return unless @_; @@ -183,19 +193,17 @@ sub _find_opt { ($topdev,$topino,$topmode,$topnlink) = $follow ? stat $top_item : lstat $top_item; - if ($Is_Win32) { - $top_item =~ s|[/\\]\z|| - unless $top_item =~ m{^(?:\w:)?[/\\]$}; - } - else { - $top_item =~ s|/\z|| unless $top_item eq '/'; - } + # canonicalize directory separators + $top_item =~ s|[/\\]|/|g if $Is_Win32; + + # no trailing / unless path is root + $top_item =~ s|/\z|| unless _is_root($top_item); $Is_Dir= 0; if ($follow) { - if (substr($top_item,0,1) eq '/') { + if (_is_absolute($top_item)) { $abs_dir = $top_item; } elsif ($top_item eq $File::Find::current_dir) { diff --git a/ext/File-Find/t/correct-absolute-path-with-follow.t b/ext/File-Find/t/correct-absolute-path-with-follow.t new file mode 100644 index 000000000000..929a33968359 --- /dev/null +++ b/ext/File-Find/t/correct-absolute-path-with-follow.t @@ -0,0 +1,63 @@ +#!./perl + +use strict; +use warnings; + +use File::Find qw( find finddepth ); +use File::Temp qw(); +use Test::More; + +my $warn_msg; + +BEGIN { + $SIG{'__WARN__'} = sub { + $warn_msg = $_[0]; + warn "# $_[0]"; + return; + } +} + +sub test_find_correct_paths_with_follow { + $warn_msg = ''; + my $dir = File::Temp->newdir('file-find-XXXXXX', TMPDIR => 1, CLEANUP => 1); + + find( + { + follow => 1, + wanted => sub { return }, + }, + $dir, + ); + + unlike( + $warn_msg, + qr/Couldn't chdir/, + 'find: Derive absolute path correctly with follow => 1', + ); +} + +sub test_finddepth_correct_paths_with_follow { + $warn_msg = ''; + my $dir = File::Temp->newdir('file-find-XXXXXX', TMPDIR => 1, CLEANUP => 1); + + finddepth( + { + follow => 1, + wanted => sub { return }, + }, + $dir, + ); + + unlike( + $warn_msg, + qr/Couldn't chdir/, + 'finddepth: Derive absolute path correctly with follow => 1', + ); +} +sub run { + test_find_correct_paths_with_follow; + test_finddepth_correct_paths_with_follow; + done_testing; +} + +run(); diff --git a/ext/File-Find/t/find.t b/ext/File-Find/t/find.t index add20c268394..6b78296a88ed 100644 --- a/ext/File-Find/t/find.t +++ b/ext/File-Find/t/find.t @@ -1060,7 +1060,7 @@ if ($^O eq 'MSWin32') { 'wanted' => sub { -f or return; # the first call is for $root_dir itself. my $got = $File::Find::name; - my $exp = "$root_dir$expected_first_file"; + (my $exp = "$root_dir$expected_first_file") =~ s|\\|/|g; print "# no_chdir=$no_chdir $root_dir '$got'\n"; is($got, $exp, "Win32: Run 'find' with 'no_chdir' set to $no_chdir" );