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.
This commit is contained in:
Ulrich Drepper 2003-11-07 23:00:00 +00:00
parent c685b2b0b4
commit ca10f33858
8 changed files with 260 additions and 30 deletions

View File

@ -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> 2003-11-04 Jakub Jelinek <jakub@redhat.com>
* io/ftw.c (ftw_dir): Close dir if callback with FTW_D type returns * io/ftw.c (ftw_dir): Close dir if callback with FTW_D type returns

View File

@ -93,6 +93,10 @@ libc {
# l* # l*
lchmod; lchmod;
} }
GLIBC_2.3.3 {
# n*
nftw; nftw64;
}
GLIBC_PRIVATE { GLIBC_PRIVATE {
# functions which have an additional interface since they are # functions which have an additional interface since they are
# cancelable. # cancelable.

112
io/ftw.c
View File

@ -135,6 +135,8 @@ int rpl_lstat (const char *, struct stat *);
#ifndef FTW_NAME #ifndef FTW_NAME
# define FTW_NAME ftw # define FTW_NAME ftw
# define NFTW_NAME nftw # define NFTW_NAME nftw
# define NFTW_OLD_NAME __old_nftw
# define NFTW_NEW_NAME __new_nftw
# define INO_T ino_t # define INO_T ino_t
# define STAT stat # define STAT stat
# ifdef _LIBC # ifdef _LIBC
@ -217,7 +219,8 @@ static const int ftw_arr[] =
/* Forward declarations of local functions. */ /* 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 static int
@ -415,43 +418,24 @@ process_entry (struct ftw_data *data, struct dir_data *dir, const char *name,
|| (!find_object (data, &st) || (!find_object (data, &st)
/* Remember the object. */ /* Remember the object. */
&& (result = add_object (data, &st)) == 0)) && (result = add_object (data, &st)) == 0))
{ result = ftw_dir (data, &st, dir);
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;
}
}
}
} }
else else
result = (*data->func) (data->dirbuf, &st, data->cvt_arr[flag], result = (*data->func) (data->dirbuf, &st, data->cvt_arr[flag],
&data->ftw); &data->ftw);
} }
if ((data->flags & FTW_ACTIONRETVAL) && result == FTW_SKIP_SUBTREE)
result = 0;
return result; return result;
} }
static int static int
__attribute ((noinline))
internal_function 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 dir_data dir;
struct dirent64 *d; struct dirent64 *d;
@ -550,6 +534,9 @@ fail:
__set_errno (save_err); __set_errno (save_err);
} }
if ((data->flags & FTW_ACTIONRETVAL) && result == FTW_SKIP_SIBLINGS)
result = 0;
/* Prepare the return, revert the `struct FTW' information. */ /* Prepare the return, revert the `struct FTW' information. */
data->dirbuf[data->ftw.base - 1] = '\0'; data->dirbuf[data->ftw.base - 1] = '\0';
--data->ftw.level; --data->ftw.level;
@ -559,11 +546,37 @@ fail:
if (result == 0 && (data->flags & FTW_DEPTH)) if (result == 0 && (data->flags & FTW_DEPTH))
result = (*data->func) (data->dirbuf, st, FTW_DP, &data->ftw); 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; return result;
} }
static int static int
__attribute ((noinline))
internal_function internal_function
ftw_startup (const char *dir, int is_nftw, void *func, int descriptors, ftw_startup (const char *dir, int is_nftw, void *func, int descriptors,
int flags) int flags)
@ -683,7 +696,7 @@ ftw_startup (const char *dir, int is_nftw, void *func, int descriptors,
result = add_object (&data, &st); result = add_object (&data, &st);
if (result == 0) if (result == 0)
result = ftw_dir (&data, &st); result = ftw_dir (&data, &st, NULL);
} }
else else
{ {
@ -693,6 +706,10 @@ ftw_startup (const char *dir, int is_nftw, void *func, int descriptors,
&data.ftw); &data.ftw);
} }
} }
if ((flags & FTW_ACTIONRETVAL)
&& (result == FTW_SKIP_SUBTREE || result == FTW_SKIP_SIBLINGS))
result = 0;
} }
/* Return to the start directory (if necessary). */ /* Return to the start directory (if necessary). */
@ -726,6 +743,7 @@ FTW_NAME (path, func, descriptors)
return ftw_startup (path, 0, func, descriptors, 0); return ftw_startup (path, 0, func, descriptors, 0);
} }
#ifndef _LIBC
int int
NFTW_NAME (path, func, descriptors, flags) NFTW_NAME (path, func, descriptors, flags)
const char *path; const char *path;
@ -735,3 +753,43 @@ NFTW_NAME (path, func, descriptors, flags)
{ {
return ftw_startup (path, 1, 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

View File

@ -74,8 +74,34 @@ enum
# define FTW_CHDIR FTW_CHDIR # define FTW_CHDIR FTW_CHDIR
FTW_DEPTH = 8 /* Report files in directory before directory itself.*/ FTW_DEPTH = 8 /* Report files in directory before directory itself.*/
# define FTW_DEPTH FTW_DEPTH # 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'. */ /* Structure used for fourth argument to callback function for `nftw'. */
struct FTW struct FTW
{ {

View File

@ -20,6 +20,8 @@
#define FTW_NAME ftw64 #define FTW_NAME ftw64
#define NFTW_NAME nftw64 #define NFTW_NAME nftw64
#define NFTW_OLD_NAME __old_nftw64
#define NFTW_NEW_NAME __new_nftw64
#define INO_T ino64_t #define INO_T ino64_t
#define STAT stat64 #define STAT stat64
#define LXSTAT __lxstat64 #define LXSTAT __lxstat64

View File

@ -202,6 +202,83 @@ succeeded
EOF EOF
rm $testout 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 rm -fr $tmpdir
exit 0 exit 0

View File

@ -12,12 +12,16 @@ int do_depth;
int do_chdir; int do_chdir;
int do_phys; int do_phys;
int do_exit; int do_exit;
char *skip_subtree;
char *skip_siblings;
struct option options[] = struct option options[] =
{ {
{ "depth", no_argument, &do_depth, 1 }, { "depth", no_argument, &do_depth, 1 },
{ "chdir", no_argument, &do_chdir, 1 }, { "chdir", no_argument, &do_chdir, 1 },
{ "phys", no_argument, &do_phys, 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 }, { "early-exit", no_argument, &do_exit, 1 },
{ NULL, 0, NULL, 0 } { NULL, 0, NULL, 0 }
}; };
@ -38,7 +42,7 @@ static int
cb (const char *name, const struct stat *st, int flag, struct FTW *f) cb (const char *name, const struct stat *st, int flag, struct FTW *f)
{ {
if (do_exit && strcmp (name + f->base, "file@2")) if (do_exit && strcmp (name + f->base, "file@2"))
return 0; return FTW_CONTINUE;
printf ("base = \"%.*s\", file = \"%s\", flag = %s", printf ("base = \"%.*s\", file = \"%s\", flag = %s",
f->base, name, name + f->base, flag2name[flag]); 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); free (cwd);
} }
printf (", level = %d\n", f->level); 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 int
@ -61,7 +72,12 @@ main (int argc, char *argv[])
mtrace (); mtrace ();
while ((opt = getopt_long_only (argc, argv, "", options, NULL)) != -1) 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) if (do_chdir)
flag |= FTW_CHDIR; flag |= FTW_CHDIR;
@ -69,6 +85,15 @@ main (int argc, char *argv[])
flag |= FTW_DEPTH; flag |= FTW_DEPTH;
if (do_phys) if (do_phys)
flag |= FTW_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); char *cw1 = getcwd (NULL, 0);

View 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 them are processed before processing the top directory itself
(depth-first processing). This also means the type flag given to the (depth-first processing). This also means the type flag given to the
callback function is @code{FTW_DP} and not @code{FTW_D}. 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 @end vtable
The return value is computed in the same way as for @code{ftw}. The return value is computed in the same way as for @code{ftw}.