Update.
authorUlrich Drepper <drepper@redhat.com>
Fri, 7 Nov 2003 23:00:00 +0000 (23:00 +0000)
committerUlrich Drepper <drepper@redhat.com>
Fri, 7 Nov 2003 23:00:00 +0000 (23:00 +0000)
2003-11-07  Jakub Jelinek  <jakub@redhat.com>

* io/ftw.c (NFTW_OLD_NAME, NFTW_NEW_NAME): Define.
(ftw_dir, ftw_startup): Add __attribute ((noinline)).
(NFTW_OLD_NAME, NFTW_NEW_NAME): New functions.
(NFTW_NAME): Only define if !_LIBC, add versioned_symbol
and compat_symbol.
* io/ftw64.c (NFTW_OLD_NAME, NFTW_NEW_NAME): Define.
* io/Versions (libc): Export nftw@@GLIBC_2.3.3
and nftw64@@GLIBC_2.3.3.

* io/ftw.h (FTW_ACTIONRETVAL): New flag.
(FTW_CONTINUE, FTW_STOP, FTW_SKIP_SUBTREE, FTW_SKIP_SIBLINGS): New.
* io/ftw.c (ftw_dir): Add old_dir argument.
Clear result if it was FTW_SKIP_SIBLINGS after processing all
dir entries.  Change cwd back if old_dir != NULL.
(process_entry): Adjust caller.  Don't change cwd back here.
Change FTW_SKIP_SUBTREE result to 0.
(ftw_startup): Adjust ftw_dir caller.
Clear result if it was FTW_SKIP_SUBTREE or FTW_SKIP_SIBLINGS.
* io/ftwtest.c (skip_subtree, skip_siblings): New variables.
(options, main): Add --skip-subtree and --skip-siblings options.
(cb): Use return FTW_CONTINUE instead of return 0.
Handle --skip-subtree and --skip-siblings.
* io/ftwtest-sh: Add tests for FTW_ACTIONRETVAL.
* manual/filesys.texi: Document FTW_ACTIONRETVAL.

ChangeLog
io/Versions
io/ftw.c
io/ftw.h
io/ftw64.c
io/ftwtest-sh
io/ftwtest.c
manual/filesys.texi

index d9d6b55..ff63de9 100644 (file)
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,3 +1,30 @@
+2003-11-07  Jakub Jelinek  <jakub@redhat.com>
+
+       * io/ftw.c (NFTW_OLD_NAME, NFTW_NEW_NAME): Define.
+       (ftw_dir, ftw_startup): Add __attribute ((noinline)).
+       (NFTW_OLD_NAME, NFTW_NEW_NAME): New functions.
+       (NFTW_NAME): Only define if !_LIBC, add versioned_symbol
+       and compat_symbol.
+       * io/ftw64.c (NFTW_OLD_NAME, NFTW_NEW_NAME): Define.
+       * io/Versions (libc): Export nftw@@GLIBC_2.3.3
+       and nftw64@@GLIBC_2.3.3.
+
+       * io/ftw.h (FTW_ACTIONRETVAL): New flag.
+       (FTW_CONTINUE, FTW_STOP, FTW_SKIP_SUBTREE, FTW_SKIP_SIBLINGS): New.
+       * io/ftw.c (ftw_dir): Add old_dir argument.
+       Clear result if it was FTW_SKIP_SIBLINGS after processing all
+       dir entries.  Change cwd back if old_dir != NULL.
+       (process_entry): Adjust caller.  Don't change cwd back here.
+       Change FTW_SKIP_SUBTREE result to 0.
+       (ftw_startup): Adjust ftw_dir caller.
+       Clear result if it was FTW_SKIP_SUBTREE or FTW_SKIP_SIBLINGS.
+       * io/ftwtest.c (skip_subtree, skip_siblings): New variables.
+       (options, main): Add --skip-subtree and --skip-siblings options.
+       (cb): Use return FTW_CONTINUE instead of return 0.
+       Handle --skip-subtree and --skip-siblings.
+       * io/ftwtest-sh: Add tests for FTW_ACTIONRETVAL.
+       * manual/filesys.texi: Document FTW_ACTIONRETVAL.
+
 2003-11-04  Jakub Jelinek  <jakub@redhat.com>
 
        * io/ftw.c (ftw_dir): Close dir if callback with FTW_D type returns
index 88f3a9a..9e650d4 100644 (file)
@@ -93,6 +93,10 @@ libc {
     # l*
     lchmod;
   }
+  GLIBC_2.3.3 {
+    # n*
+    nftw; nftw64;
+  }
   GLIBC_PRIVATE {
     # functions which have an additional interface since they are
     # cancelable.
index 6d5cedf..c4d2157 100644 (file)
--- a/io/ftw.c
+++ b/io/ftw.c
@@ -135,6 +135,8 @@ int rpl_lstat (const char *, struct stat *);
 #ifndef FTW_NAME
 # define FTW_NAME ftw
 # define NFTW_NAME nftw
+# define NFTW_OLD_NAME __old_nftw
+# define NFTW_NEW_NAME __new_nftw
 # define INO_T ino_t
 # define STAT stat
 # ifdef _LIBC
@@ -217,7 +219,8 @@ static const int ftw_arr[] =
 
 
 /* Forward declarations of local functions.  */
-static int ftw_dir (struct ftw_data *data, struct STAT *st) internal_function;
+static int ftw_dir (struct ftw_data *data, struct STAT *st,
+                   struct dir_data *old_dir) internal_function;
 
 
 static int
@@ -415,43 +418,24 @@ process_entry (struct ftw_data *data, struct dir_data *dir, const char *name,
              || (!find_object (data, &st)
                  /* Remember the object.  */
                  && (result = add_object (data, &st)) == 0))
-           {
-             result = ftw_dir (data, &st);
-
-             if (result == 0 && (data->flags & FTW_CHDIR))
-               {
-                 /* Change back to the parent directory.  */
-                 int done = 0;
-                 if (dir->stream != NULL)
-                   if (__fchdir (dirfd (dir->stream)) == 0)
-                     done = 1;
-
-                 if (!done)
-                   {
-                     if (data->ftw.base == 1)
-                       {
-                         if (__chdir ("/") < 0)
-                           result = -1;
-                       }
-                     else
-                       if (__chdir ("..") < 0)
-                         result = -1;
-                   }
-               }
-           }
+           result = ftw_dir (data, &st, dir);
        }
       else
        result = (*data->func) (data->dirbuf, &st, data->cvt_arr[flag],
                                &data->ftw);
     }
 
+  if ((data->flags & FTW_ACTIONRETVAL) && result == FTW_SKIP_SUBTREE)
+    result = 0;
+
   return result;
 }
 
 
 static int
+__attribute ((noinline))
 internal_function
-ftw_dir (struct ftw_data *data, struct STAT *st)
+ftw_dir (struct ftw_data *data, struct STAT *st, struct dir_data *old_dir)
 {
   struct dir_data dir;
   struct dirent64 *d;
@@ -550,6 +534,9 @@ fail:
       __set_errno (save_err);
     }
 
+  if ((data->flags & FTW_ACTIONRETVAL) && result == FTW_SKIP_SIBLINGS)
+    result = 0;
+
   /* Prepare the return, revert the `struct FTW' information.  */
   data->dirbuf[data->ftw.base - 1] = '\0';
   --data->ftw.level;
@@ -559,11 +546,37 @@ fail:
   if (result == 0 && (data->flags & FTW_DEPTH))
     result = (*data->func) (data->dirbuf, st, FTW_DP, &data->ftw);
 
+  if (old_dir
+      && (data->flags & FTW_CHDIR)
+      && (result == 0
+         || ((data->flags & FTW_ACTIONRETVAL)
+             && (result != -1 && result != FTW_STOP))))
+    {
+      /* Change back to the parent directory.  */
+      int done = 0;
+      if (old_dir->stream != NULL)
+       if (__fchdir (dirfd (old_dir->stream)) == 0)
+         done = 1;
+
+      if (!done)
+       {
+         if (data->ftw.base == 1)
+           {
+             if (__chdir ("/") < 0)
+               result = -1;
+           }
+         else
+           if (__chdir ("..") < 0)
+             result = -1;
+       }
+    }
+
   return result;
 }
 
 
 static int
+__attribute ((noinline))
 internal_function
 ftw_startup (const char *dir, int is_nftw, void *func, int descriptors,
             int flags)
@@ -683,7 +696,7 @@ ftw_startup (const char *dir, int is_nftw, void *func, int descriptors,
                result = add_object (&data, &st);
 
              if (result == 0)
-               result = ftw_dir (&data, &st);
+               result = ftw_dir (&data, &st, NULL);
            }
          else
            {
@@ -693,6 +706,10 @@ ftw_startup (const char *dir, int is_nftw, void *func, int descriptors,
                                     &data.ftw);
            }
        }
+
+      if ((flags & FTW_ACTIONRETVAL)
+         && (result == FTW_SKIP_SUBTREE || result == FTW_SKIP_SIBLINGS))
+       result = 0;
     }
 
   /* Return to the start directory (if necessary).  */
@@ -726,6 +743,7 @@ FTW_NAME (path, func, descriptors)
   return ftw_startup (path, 0, func, descriptors, 0);
 }
 
+#ifndef _LIBC
 int
 NFTW_NAME (path, func, descriptors, flags)
      const char *path;
@@ -735,3 +753,43 @@ NFTW_NAME (path, func, descriptors, flags)
 {
   return ftw_startup (path, 1, func, descriptors, flags);
 }
+#else
+
+#include <shlib-compat.h>
+
+int
+NFTW_NEW_NAME (path, func, descriptors, flags)
+     const char *path;
+     NFTW_FUNC_T func;
+     int descriptors;
+     int flags;
+{
+  if (flags
+      & ~(FTW_PHYS | FTW_MOUNT | FTW_CHDIR | FTW_DEPTH | FTW_ACTIONRETVAL))
+    {
+      __set_errno (EINVAL);
+      return -1;
+    }
+  return ftw_startup (path, 1, func, descriptors, flags);
+}
+
+versioned_symbol (libc, NFTW_NEW_NAME, NFTW_NAME, GLIBC_2_3_3);
+
+#if SHLIB_COMPAT(libc, GLIBC_2_1, GLIBC_2_3_3)
+
+/* Older nftw* version just ignored all unknown flags.  */
+
+int
+NFTW_OLD_NAME (path, func, descriptors, flags)
+     const char *path;
+     NFTW_FUNC_T func;
+     int descriptors;
+     int flags;
+{
+  flags &= (FTW_PHYS | FTW_MOUNT | FTW_CHDIR | FTW_DEPTH);
+  return ftw_startup (path, 1, func, descriptors, flags);
+}
+
+compat_symbol (libc, NFTW_OLD_NAME, NFTW_NAME, GLIBC_2_1);
+#endif
+#endif
index 4939297..03973cc 100644 (file)
--- a/io/ftw.h
+++ b/io/ftw.h
@@ -74,7 +74,33 @@ enum
 # define FTW_CHDIR     FTW_CHDIR
   FTW_DEPTH = 8                /* Report files in directory before directory itself.*/
 # define FTW_DEPTH     FTW_DEPTH
+# ifdef __USE_GNU
+  ,
+  FTW_ACTIONRETVAL = 16        /* Assume callback to return FTW_* values instead of
+                          zero to continue and non-zero to terminate.  */
+#  define FTW_ACTIONRETVAL FTW_ACTIONRETVAL
+# endif
+};
+
+#ifdef __USE_GNU
+/* Return values from callback functions.  */
+enum
+{
+  FTW_CONTINUE = 0,    /* Continue with next sibling or for FTW_D with the
+                          first child.  */
+# define FTW_CONTINUE  FTW_CONTINUE
+  FTW_STOP = 1,                /* Return from `ftw' or `nftw' with FTW_STOP as return
+                          value.  */
+# define FTW_STOP      FTW_STOP
+  FTW_SKIP_SUBTREE = 2,        /* Only meaningful for FTW_D: Don't walk through the
+                          subtree, instead just continue with its next
+                          sibling. */
+# define FTW_SKIP_SUBTREE FTW_SKIP_SUBTREE
+  FTW_SKIP_SIBLINGS = 3,/* Continue with FTW_DP callback for current directory
+                           (if FTW_DEPTH) and then its siblings.  */
+# define FTW_SKIP_SIBLINGS FTW_SKIP_SIBLINGS
 };
+#endif
 
 /* Structure used for fourth argument to callback function for `nftw'.  */
 struct FTW
index 430ee36..7913b7a 100644 (file)
@@ -20,6 +20,8 @@
 
 #define FTW_NAME ftw64
 #define NFTW_NAME nftw64
+#define NFTW_OLD_NAME __old_nftw64
+#define NFTW_NEW_NAME __new_nftw64
 #define INO_T ino64_t
 #define STAT stat64
 #define LXSTAT __lxstat64
index 76c1de7..2fed2ed 100644 (file)
@@ -202,6 +202,83 @@ succeeded
 EOF
 rm $testout
 
+mkdir $tmpdir/foo/lvl1b
+echo > $tmpdir/foo/lvl1b/file@1b
+echo > $tmpdir/foo/lvl1b/file2@1b
+echo > $tmpdir/foo/lvl1b/file3@1b
+
+LD_LIBRARY_PATH=$objpfx $ldso $testprogram --skip-subtree=lvl1 $tmpdir |
+    sort > $testout
+
+cat <<EOF | diff -u $testout - || exit 1
+base = "/tmp/", file = "ftwtest.d", flag = FTW_D, level = 0
+base = "/tmp/ftwtest.d/", file = "bar", flag = FTW_D, level = 1
+base = "/tmp/ftwtest.d/", file = "baz", flag = FTW_F, level = 1
+base = "/tmp/ftwtest.d/", file = "foo", flag = FTW_D, level = 1
+base = "/tmp/ftwtest.d/bar/", file = "xo", flag = FTW_F, level = 2
+base = "/tmp/ftwtest.d/foo/", file = "lvl1", flag = FTW_D, level = 2
+base = "/tmp/ftwtest.d/foo/", file = "lvl1b", flag = FTW_D, level = 2
+base = "/tmp/ftwtest.d/foo/lvl1b/", file = "file2@1b", flag = FTW_F, level = 3
+base = "/tmp/ftwtest.d/foo/lvl1b/", file = "file3@1b", flag = FTW_F, level = 3
+base = "/tmp/ftwtest.d/foo/lvl1b/", file = "file@1b", flag = FTW_F, level = 3
+EOF
+rm $testout
+
+LD_LIBRARY_PATH=$objpfx $ldso $testprogram --skip-siblings=lvl1 $tmpdir |
+    sort > $testout
+
+# The filesystem is not required to put lvl1 before lvl1b.
+# If lvl1b comes after lvl1, it shouldn't be printed, while if it
+# comes before, it should.
+catcmd=cat
+[ -n "`ls -U $tmpdir/foo/ | sed -n '/lvl1$/,${/lvl1b$/p}'`" ] \
+  && catcmd="grep -v lvl1b"
+
+$catcmd <<EOF | diff -u $testout - || exit 1
+base = "/tmp/", file = "ftwtest.d", flag = FTW_D, level = 0
+base = "/tmp/ftwtest.d/", file = "bar", flag = FTW_D, level = 1
+base = "/tmp/ftwtest.d/", file = "baz", flag = FTW_F, level = 1
+base = "/tmp/ftwtest.d/", file = "foo", flag = FTW_D, level = 1
+base = "/tmp/ftwtest.d/bar/", file = "xo", flag = FTW_F, level = 2
+base = "/tmp/ftwtest.d/foo/", file = "lvl1", flag = FTW_D, level = 2
+base = "/tmp/ftwtest.d/foo/", file = "lvl1b", flag = FTW_D, level = 2
+base = "/tmp/ftwtest.d/foo/lvl1b/", file = "file2@1b", flag = FTW_F, level = 3
+base = "/tmp/ftwtest.d/foo/lvl1b/", file = "file3@1b", flag = FTW_F, level = 3
+base = "/tmp/ftwtest.d/foo/lvl1b/", file = "file@1b", flag = FTW_F, level = 3
+EOF
+rm $testout
+
+LD_LIBRARY_PATH=$objpfx $ldso $testprogram --skip-siblings=file@1b $tmpdir |
+    sort > $testout
+
+# The filesystem is not required to put file2@1b and file3@1b after file@1b.
+# If file[23]@1b come after file@1b, it shouldn't be printed, while if they
+# come before, they should.
+regexp=`echo $(ls -U /tmp/ftwtest.d/foo/lvl1b \
+              | sed -n '/file@1b$/,${/file[23]@1b$/p}') | sed 's, ,|,'`
+catcmd=cat
+[ -n "$regexp" ] && catcmd="egrep -v $regexp"
+
+$catcmd <<EOF | diff -u $testout - || exit 1
+base = "/tmp/", file = "ftwtest.d", flag = FTW_D, level = 0
+base = "/tmp/ftwtest.d/", file = "bar", flag = FTW_D, level = 1
+base = "/tmp/ftwtest.d/", file = "baz", flag = FTW_F, level = 1
+base = "/tmp/ftwtest.d/", file = "foo", flag = FTW_D, level = 1
+base = "/tmp/ftwtest.d/bar/", file = "xo", flag = FTW_F, level = 2
+base = "/tmp/ftwtest.d/foo/", file = "lvl1", flag = FTW_D, level = 2
+base = "/tmp/ftwtest.d/foo/", file = "lvl1b", flag = FTW_D, level = 2
+base = "/tmp/ftwtest.d/foo/lvl1/", file = "file@1", flag = FTW_F, level = 3
+base = "/tmp/ftwtest.d/foo/lvl1/", file = "link@1", flag = FTW_SLN, level = 3
+base = "/tmp/ftwtest.d/foo/lvl1/", file = "lvl2", flag = FTW_D, level = 3
+base = "/tmp/ftwtest.d/foo/lvl1/lvl2/", file = "file@2", flag = FTW_F, level = 4
+base = "/tmp/ftwtest.d/foo/lvl1/lvl2/", file = "lvl3", flag = FTW_D, level = 4
+base = "/tmp/ftwtest.d/foo/lvl1/lvl2/lvl3/", file = "file@3", flag = FTW_F, level = 5
+base = "/tmp/ftwtest.d/foo/lvl1b/", file = "file2@1b", flag = FTW_F, level = 3
+base = "/tmp/ftwtest.d/foo/lvl1b/", file = "file3@1b", flag = FTW_F, level = 3
+base = "/tmp/ftwtest.d/foo/lvl1b/", file = "file@1b", flag = FTW_F, level = 3
+EOF
+rm $testout
+
 rm -fr $tmpdir
 
 exit 0
index 4f52799..77debd2 100644 (file)
@@ -12,12 +12,16 @@ int do_depth;
 int do_chdir;
 int do_phys;
 int do_exit;
+char *skip_subtree;
+char *skip_siblings;
 
 struct option options[] =
 {
   { "depth", no_argument, &do_depth, 1 },
   { "chdir", no_argument, &do_chdir, 1 },
   { "phys", no_argument, &do_phys, 1 },
+  { "skip-subtree", required_argument, NULL, 't' },
+  { "skip-siblings", required_argument, NULL, 's' },
   { "early-exit", no_argument, &do_exit, 1 },
   { NULL, 0, NULL, 0 }
 };
@@ -38,7 +42,7 @@ static int
 cb (const char *name, const struct stat *st, int flag, struct FTW *f)
 {
   if (do_exit && strcmp (name + f->base, "file@2"))
-    return 0;
+    return FTW_CONTINUE;
 
   printf ("base = \"%.*s\", file = \"%s\", flag = %s",
          f->base, name, name + f->base, flag2name[flag]);
@@ -49,7 +53,14 @@ cb (const char *name, const struct stat *st, int flag, struct FTW *f)
       free (cwd);
     }
   printf (", level = %d\n", f->level);
-  return do_exit ? 26 : 0;
+
+  if (skip_siblings && strcmp (name + f->base, skip_siblings) == 0)
+    return FTW_SKIP_SIBLINGS;
+
+  if (skip_subtree && strcmp (name + f->base, skip_subtree) == 0)
+    return FTW_SKIP_SUBTREE;
+
+  return do_exit ? 26 : FTW_CONTINUE;
 }
 
 int
@@ -61,7 +72,12 @@ main (int argc, char *argv[])
   mtrace ();
 
   while ((opt = getopt_long_only (argc, argv, "", options, NULL)) != -1)
-    ;
+    {
+      if (opt == 't')
+        skip_subtree = optarg;
+      else if (opt == 's')
+        skip_siblings = optarg;
+    }
 
   if (do_chdir)
     flag |= FTW_CHDIR;
@@ -69,6 +85,15 @@ main (int argc, char *argv[])
     flag |= FTW_DEPTH;
   if (do_phys)
     flag |= FTW_PHYS;
+  if (skip_subtree || skip_siblings)
+    {
+      flag |= FTW_ACTIONRETVAL;
+      if (do_exit)
+       {
+         printf ("--early-exit cannot be used together with --skip-{siblings,subtree}");
+         exit (1);
+       }
+    }
 
   char *cw1 = getcwd (NULL, 0);
 
index c858e2b..70889c2 100644 (file)
@@ -918,6 +918,17 @@ If this option is specified then all subdirectories and files within
 them are processed before processing the top directory itself
 (depth-first processing).  This also means the type flag given to the
 callback function is @code{FTW_DP} and not @code{FTW_D}.
+@item FTW_ACTIONRETVAL
+If this option is specified then return values from callbacks
+are handled differently.  If the callback returns @code{FTW_CONTINUE},
+walking continues normally.  @code{FTW_STOP} means walking stops
+and @code{FTW_STOP} is returned to the caller.  If @code{FTW_SKIP_SUBTREE}
+is returned by the callback with @code{FTW_D} argument, the subtree
+is skipped and walking continues with next sibling of the directory.
+If @code{FTW_SKIP_SIBLINGS} is returned by the callback, all siblings
+of the current entry are skipped and walking continues in its parent.
+No other return values should be returned from the callbacks if
+this option is set.  This option is a GNU extension.
 @end vtable
 
 The return value is computed in the same way as for @code{ftw}.