(3) File::Find::find()/finddepth() bugs with toplevel paths
Conrad E. Kimball [Thu, 7 Aug 1997 00:00:00 +0000 (00:00 +0000)]
The File::Find module exhibits the following defects:

1) If the top-level directory is a symbolic link to another directory,
   the find() and finddepth() functions follow that symbolic link and
   traverse that directory.  This behavior is both contrary to the way
   the real find command works and contrary to the way find() and
   finddepth() treat symbolic links that occur lower down in the
   directory hierarchy (which aren't followed).

   Example:

   $ cd $HOME
   $ mkdir findbug; cd findbug
   $ ln -s /usr usr
   $ find usr -print
   usr
   $ find2perl usr -print | perl
   usr
   usr/lost+found
   usr/tmp
   usr/tmp/.zma25637cbbb
   ...

2) If the wanted() function sets $prune = 1 for a toplevel directory,
   the find() function ignores it.  It honors $prune for all lower level
   directories, but not the toplevel ones.  This, too, is contrary to
   the way the real find command works.

   Example:

   $ find /usr -print -prune
   /usr
   $ find2perl /usr -print -prune | perl
   /usr
   /usr/lost+found
   /usr/tmp
   /usr/bin
   /usr/man
   /usr/etc
   /usr/lib
   /usr/netdemo
   /usr/include
   /usr/adm
   ...

3) If finddepth() is passed a toplevel path that is not a directory, it
   fails to set $name before calling the wanted() function.  This, too,
   is contrary to the way the real find command works.

   Example:

   $ cd $HOME
   $ find /dev/null -depth -print
   /dev/null
   $ find2perl /dev/null -depth -print | perl

   $

The following patch corrects all three defects:

p5p-msgid: 199707040045.RAA24459@mailgate2.boeing.com

lib/File/Find.pm

index 1faea50..1d565f2 100644 (file)
@@ -78,18 +78,22 @@ sub find {
     # compatibility.
     local($topdir,$topdev,$topino,$topmode,$topnlink);
     foreach $topdir (@_) {
-       (($topdev,$topino,$topmode,$topnlink) = stat($topdir))
+       (($topdev,$topino,$topmode,$topnlink) =
+         ($Is_VMS ? stat($topdir) : lstat($topdir)))
          || (warn("Can't stat $topdir: $!\n"), next);
        if (-d _) {
            if (chdir($topdir)) {
                ($dir,$_) = ($topdir,'.');
                $name = $topdir;
+               $prune = 0;
                &$wanted;
-               my $fixtopdir = $topdir;
-               $fixtopdir =~ s,/$,, ;
-               $fixtopdir =~ s/\.dir$// if $Is_VMS;
-               $fixtopdir =~ s/\\dir$// if $Is_NT;
-               &finddir($wanted,$fixtopdir,$topnlink);
+               if (!$prune) {
+                   my $fixtopdir = $topdir;
+                   $fixtopdir =~ s,/$,, ;
+                   $fixtopdir =~ s/\.dir$// if $Is_VMS;
+                   $fixtopdir =~ s/\\dir$// if $Is_NT;
+                   &finddir($wanted,$fixtopdir,$topnlink);
+               }
            }
            else {
                warn "Can't cd to $topdir: $!\n";
@@ -169,7 +173,8 @@ sub finddepth {
     # compatibility.
     local($topdir, $topdev, $topino, $topmode, $topnlink);
     foreach $topdir (@_) {
-       (($topdev,$topino,$topmode,$topnlink) = stat($topdir))
+       (($topdev,$topino,$topmode,$topnlink) =
+         ($Is_VMS ? stat($topdir) : lstat($topdir)))
          || (warn("Can't stat $topdir: $!\n"), next);
        if (-d _) {
            if (chdir($topdir)) {
@@ -190,6 +195,7 @@ sub finddepth {
            unless (($_,$dir) = File::Basename::fileparse($topdir)) {
                ($dir,$_) = ('.', $topdir);
            }
+           $name = $topdir;
            chdir $dir && &$wanted;
        }
        chdir $cwd;