/* Traverse a file hierarchy.
- Copyright (C) 2004-2009 Free Software Foundation, Inc.
+ Copyright (C) 2004-2010 Free Software Foundation, Inc.
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
/*-
* Copyright (c) 1990, 1993, 1994
- * The Regents of the University of California. All rights reserved.
+ * The Regents of the University of California. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
#include <config.h>
#if defined(LIBC_SCCS) && !defined(lint)
-static char sccsid[] = "@(#)fts.c 8.6 (Berkeley) 8/14/94";
+static char sccsid[] = "@(#)fts.c 8.6 (Berkeley) 8/14/94";
#endif /* LIBC_SCCS and not lint */
#include "fts_.h"
# define __set_errno(Val) errno = (Val)
#endif
-#ifndef __attribute__
-# if __GNUC__ < 2 || (__GNUC__ == 2 && __GNUC_MINOR__ < 8)
-# define __attribute__(x) /* empty */
-# endif
-#endif
-
-#ifndef ATTRIBUTE_UNUSED
-# define ATTRIBUTE_UNUSED __attribute__ ((__unused__))
-#endif
-
/* If this host provides the openat function, then we can avoid
attempting to open "." in some initialization code below. */
#ifdef HAVE_OPENAT
#ifdef NDEBUG
# define fts_assert(expr) ((void) 0)
#else
-# define fts_assert(expr) \
- do \
- { \
- if (!(expr)) \
- abort (); \
- } \
+# define fts_assert(expr) \
+ do \
+ { \
+ if (!(expr)) \
+ abort (); \
+ } \
while (false)
#endif
-static FTSENT *fts_alloc (FTS *, const char *, size_t) internal_function;
-static FTSENT *fts_build (FTS *, int) internal_function;
-static void fts_lfree (FTSENT *) internal_function;
-static void fts_load (FTS *, FTSENT *) internal_function;
-static size_t fts_maxarglen (char * const *) internal_function;
-static void fts_padjust (FTS *, FTSENT *) internal_function;
-static bool fts_palloc (FTS *, size_t) internal_function;
-static FTSENT *fts_sort (FTS *, FTSENT *, size_t) internal_function;
+static FTSENT *fts_alloc (FTS *, const char *, size_t) internal_function;
+static FTSENT *fts_build (FTS *, int) internal_function;
+static void fts_lfree (FTSENT *) internal_function;
+static void fts_load (FTS *, FTSENT *) internal_function;
+static size_t fts_maxarglen (char * const *) internal_function;
+static void fts_padjust (FTS *, FTSENT *) internal_function;
+static bool fts_palloc (FTS *, size_t) internal_function;
+static FTSENT *fts_sort (FTS *, FTSENT *, size_t) internal_function;
static unsigned short int fts_stat (FTS *, FTSENT *, bool) internal_function;
static int fts_safe_changedir (FTS *, FTSENT *, int, const char *)
internal_function;
# define SIZE_MAX ((size_t) -1)
#endif
-#define ISDOT(a) (a[0] == '.' && (!a[1] || (a[1] == '.' && !a[2])))
-#define STREQ(a, b) (strcmp ((a), (b)) == 0)
+#define ISDOT(a) (a[0] == '.' && (!a[1] || (a[1] == '.' && !a[2])))
+#define STREQ(a, b) (strcmp (a, b) == 0)
-#define CLR(opt) (sp->fts_options &= ~(opt))
-#define ISSET(opt) (sp->fts_options & (opt))
-#define SET(opt) (sp->fts_options |= (opt))
+#define CLR(opt) (sp->fts_options &= ~(opt))
+#define ISSET(opt) (sp->fts_options & (opt))
+#define SET(opt) (sp->fts_options |= (opt))
/* FIXME: make this a function */
-#define RESTORE_INITIAL_CWD(sp) \
- (fd_ring_clear (&((sp)->fts_fd_ring)), \
+#define RESTORE_INITIAL_CWD(sp) \
+ (fd_ring_clear (&((sp)->fts_fd_ring)), \
FCHDIR ((sp), (ISSET (FTS_CWDFD) ? AT_FDCWD : (sp)->fts_rfd)))
/* FIXME: FTS_NOCHDIR is now misnamed.
Call it FTS_USE_FULL_RELATIVE_FILE_NAMES instead. */
-#define FCHDIR(sp, fd) \
- (!ISSET(FTS_NOCHDIR) && (ISSET(FTS_CWDFD) \
- ? (cwd_advance_fd ((sp), (fd), true), 0) \
- : fchdir (fd)))
+#define FCHDIR(sp, fd) \
+ (!ISSET(FTS_NOCHDIR) && (ISSET(FTS_CWDFD) \
+ ? (cwd_advance_fd ((sp), (fd), true), 0) \
+ : fchdir (fd)))
/* fts_build flags */
/* FIXME: make this an enum */
-#define BCHILD 1 /* fts_children */
-#define BNAMES 2 /* fts_children, names only */
-#define BREAD 3 /* fts_read */
+#define BCHILD 1 /* fts_children */
+#define BNAMES 2 /* fts_children, names only */
+#define BREAD 3 /* fts_read */
#if FTS_DEBUG
# include <inttypes.h>
# define fd_ring_print(a, b, c)
#endif
-#define LEAVE_DIR(Fts, Ent, Tag) \
- do \
- { \
- Dprintf ((" %s-leaving: %s\n", Tag, (Ent)->fts_path)); \
- leave_dir (Fts, Ent); \
- fd_ring_check (Fts); \
- } \
+#define LEAVE_DIR(Fts, Ent, Tag) \
+ do \
+ { \
+ Dprintf ((" %s-leaving: %s\n", Tag, (Ent)->fts_path)); \
+ leave_dir (Fts, Ent); \
+ fd_ring_check (Fts); \
+ } \
while (false)
static void
{
int fd = i_ring_pop (fd_ring);
if (0 <= fd)
- close (fd);
+ close (fd);
}
}
{
fts_assert (p->fts_info == FTS_NSOK);
p->fts_statp->st_size = (required
- ? FTS_STAT_REQUIRED
- : FTS_NO_STAT_REQUIRED);
+ ? FTS_STAT_REQUIRED
+ : FTS_NO_STAT_REQUIRED);
}
/* file-descriptor-relative opendir. */
opendirat (int fd, char const *dir)
{
int new_fd = openat (fd, dir,
- O_RDONLY | O_DIRECTORY | O_NOCTTY | O_NONBLOCK);
+ O_RDONLY | O_DIRECTORY | O_NOCTTY | O_NONBLOCK);
DIR *dirp;
if (new_fd < 0)
if (chdir_down_one)
{
/* Push "old" onto the ring.
- If the displaced file descriptor is non-negative, close it. */
+ If the displaced file descriptor is non-negative, close it. */
int prev_fd_in_slot = i_ring_push (&sp->fts_fd_ring, old);
fd_ring_print (sp, stderr, "post-push");
if (0 <= prev_fd_in_slot)
- close (prev_fd_in_slot); /* ignore any close failure */
+ close (prev_fd_in_slot); /* ignore any close failure */
}
else if ( ! ISSET (FTS_NOCHDIR))
{
if (0 <= old)
- close (old); /* ignore any close failure */
+ close (old); /* ignore any close failure */
}
sp->fts_cwd_fd = fd;
diropen (FTS const *sp, char const *dir)
{
int open_flags = (O_RDONLY | O_DIRECTORY | O_NOCTTY | O_NONBLOCK
- | (ISSET (FTS_PHYSICAL) ? O_NOFOLLOW : 0));
+ | (ISSET (FTS_PHYSICAL) ? O_NOFOLLOW : 0));
int fd = (ISSET (FTS_CWDFD)
? openat (sp->fts_cwd_fd, dir, open_flags)
FTS *
fts_open (char * const *argv,
- register int options,
- int (*compar) (FTSENT const **, FTSENT const **))
+ register int options,
+ int (*compar) (FTSENT const **, FTSENT const **))
{
- register FTS *sp;
- register FTSENT *p, *root;
- register size_t nitems;
- FTSENT *parent = NULL;
- FTSENT *tmp = NULL; /* pacify gcc */
- size_t len;
- bool defer_stat;
-
- /* Options check. */
- if (options & ~FTS_OPTIONMASK) {
- __set_errno (EINVAL);
- return (NULL);
- }
- if ((options & FTS_NOCHDIR) && (options & FTS_CWDFD)) {
- __set_errno (EINVAL);
- return (NULL);
- }
- if ( ! (options & (FTS_LOGICAL | FTS_PHYSICAL))) {
- __set_errno (EINVAL);
- return (NULL);
- }
-
- /* Allocate/initialize the stream */
- if ((sp = malloc(sizeof(FTS))) == NULL)
- return (NULL);
- memset(sp, 0, sizeof(FTS));
- sp->fts_compar = compar;
- sp->fts_options = options;
-
- /* Logical walks turn on NOCHDIR; symbolic links are too hard. */
- if (ISSET(FTS_LOGICAL)) {
- SET(FTS_NOCHDIR);
- CLR(FTS_CWDFD);
- }
-
- /* Initialize fts_cwd_fd. */
- sp->fts_cwd_fd = AT_FDCWD;
- if ( ISSET(FTS_CWDFD) && ! HAVE_OPENAT_SUPPORT)
- {
- /* While it isn't technically necessary to open "." this
- early, doing it here saves us the trouble of ensuring
- later (where it'd be messier) that "." can in fact
- be opened. If not, revert to FTS_NOCHDIR mode. */
- int fd = open (".", O_RDONLY);
- if (fd < 0)
- {
- /* Even if `.' is unreadable, don't revert to FTS_NOCHDIR mode
- on systems like Linux+PROC_FS, where our openat emulation
- is good enough. Note: on a system that emulates
- openat via /proc, this technique can still fail, but
- only in extreme conditions, e.g., when the working
- directory cannot be saved (i.e. save_cwd fails) --
- and that happens on Linux only when "." is unreadable
- and the CWD would be longer than PATH_MAX.
- FIXME: once Linux kernel openat support is well established,
- replace the above open call and this entire if/else block
- with the body of the if-block below. */
- if ( openat_needs_fchdir ())
- {
- SET(FTS_NOCHDIR);
- CLR(FTS_CWDFD);
- }
- }
- else
- {
- close (fd);
- }
- }
-
- /*
- * Start out with 1K of file name space, and enough, in any case,
- * to hold the user's file names.
- */
+ register FTS *sp;
+ register FTSENT *p, *root;
+ register size_t nitems;
+ FTSENT *parent = NULL;
+ FTSENT *tmp = NULL; /* pacify gcc */
+ bool defer_stat;
+
+ /* Options check. */
+ if (options & ~FTS_OPTIONMASK) {
+ __set_errno (EINVAL);
+ return (NULL);
+ }
+ if ((options & FTS_NOCHDIR) && (options & FTS_CWDFD)) {
+ __set_errno (EINVAL);
+ return (NULL);
+ }
+ if ( ! (options & (FTS_LOGICAL | FTS_PHYSICAL))) {
+ __set_errno (EINVAL);
+ return (NULL);
+ }
+
+ /* Allocate/initialize the stream */
+ if ((sp = malloc(sizeof(FTS))) == NULL)
+ return (NULL);
+ memset(sp, 0, sizeof(FTS));
+ sp->fts_compar = compar;
+ sp->fts_options = options;
+
+ /* Logical walks turn on NOCHDIR; symbolic links are too hard. */
+ if (ISSET(FTS_LOGICAL)) {
+ SET(FTS_NOCHDIR);
+ CLR(FTS_CWDFD);
+ }
+
+ /* Initialize fts_cwd_fd. */
+ sp->fts_cwd_fd = AT_FDCWD;
+ if ( ISSET(FTS_CWDFD) && ! HAVE_OPENAT_SUPPORT)
+ {
+ /* While it isn't technically necessary to open "." this
+ early, doing it here saves us the trouble of ensuring
+ later (where it'd be messier) that "." can in fact
+ be opened. If not, revert to FTS_NOCHDIR mode. */
+ int fd = open (".", O_RDONLY);
+ if (fd < 0)
+ {
+ /* Even if `.' is unreadable, don't revert to FTS_NOCHDIR mode
+ on systems like Linux+PROC_FS, where our openat emulation
+ is good enough. Note: on a system that emulates
+ openat via /proc, this technique can still fail, but
+ only in extreme conditions, e.g., when the working
+ directory cannot be saved (i.e. save_cwd fails) --
+ and that happens on Linux only when "." is unreadable
+ and the CWD would be longer than PATH_MAX.
+ FIXME: once Linux kernel openat support is well established,
+ replace the above open call and this entire if/else block
+ with the body of the if-block below. */
+ if ( openat_needs_fchdir ())
+ {
+ SET(FTS_NOCHDIR);
+ CLR(FTS_CWDFD);
+ }
+ }
+ else
+ {
+ close (fd);
+ }
+ }
+
+ /*
+ * Start out with 1K of file name space, and enough, in any case,
+ * to hold the user's file names.
+ */
#ifndef MAXPATHLEN
# define MAXPATHLEN 1024
#endif
- {
- size_t maxarglen = fts_maxarglen(argv);
- if (! fts_palloc(sp, MAX(maxarglen, MAXPATHLEN)))
- goto mem1;
- }
-
- /* Allocate/initialize root's parent. */
- if (*argv != NULL) {
- if ((parent = fts_alloc(sp, "", 0)) == NULL)
- goto mem2;
- parent->fts_level = FTS_ROOTPARENTLEVEL;
- }
-
- /* The classic fts implementation would call fts_stat with
- a new entry for each iteration of the loop below.
- If the comparison function is not specified or if the
- FTS_DEFER_STAT option is in effect, don't stat any entry
- in this loop. This is an attempt to minimize the interval
- between the initial stat/lstat/fstatat and the point at which
- a directory argument is first opened. This matters for any
- directory command line argument that resides on a file system
- without genuine i-nodes. If you specify FTS_DEFER_STAT along
- with a comparison function, that function must not access any
- data via the fts_statp pointer. */
- defer_stat = (compar == NULL || ISSET(FTS_DEFER_STAT));
-
- /* Allocate/initialize root(s). */
- for (root = NULL, nitems = 0; *argv != NULL; ++argv, ++nitems) {
- /* Don't allow zero-length file names. */
- if ((len = strlen(*argv)) == 0) {
- __set_errno (ENOENT);
- goto mem3;
- }
-
- if ((p = fts_alloc(sp, *argv, len)) == NULL)
- goto mem3;
- p->fts_level = FTS_ROOTLEVEL;
- p->fts_parent = parent;
- p->fts_accpath = p->fts_name;
- /* Even when defer_stat is true, be sure to stat the first
- command line argument, since fts_read (at least with
- FTS_XDEV) requires that. */
- if (defer_stat && root != NULL) {
- p->fts_info = FTS_NSOK;
- fts_set_stat_required(p, true);
- } else {
- p->fts_info = fts_stat(sp, p, false);
- }
-
- /*
- * If comparison routine supplied, traverse in sorted
- * order; otherwise traverse in the order specified.
- */
- if (compar) {
- p->fts_link = root;
- root = p;
- } else {
- p->fts_link = NULL;
- if (root == NULL)
- tmp = root = p;
- else {
- tmp->fts_link = p;
- tmp = p;
- }
- }
- }
- if (compar && nitems > 1)
- root = fts_sort(sp, root, nitems);
-
- /*
- * Allocate a dummy pointer and make fts_read think that we've just
- * finished the node before the root(s); set p->fts_info to FTS_INIT
- * so that everything about the "current" node is ignored.
- */
- if ((sp->fts_cur = fts_alloc(sp, "", 0)) == NULL)
- goto mem3;
- sp->fts_cur->fts_link = root;
- sp->fts_cur->fts_info = FTS_INIT;
- if (! setup_dir (sp))
- goto mem3;
-
- /*
- * If using chdir(2), grab a file descriptor pointing to dot to ensure
- * that we can get back here; this could be avoided for some file names,
- * but almost certainly not worth the effort. Slashes, symbolic links,
- * and ".." are all fairly nasty problems. Note, if we can't get the
- * descriptor we run anyway, just more slowly.
- */
- if (!ISSET(FTS_NOCHDIR) && !ISSET(FTS_CWDFD)
- && (sp->fts_rfd = diropen (sp, ".")) < 0)
- SET(FTS_NOCHDIR);
-
- i_ring_init (&sp->fts_fd_ring, -1);
- return (sp);
-
-mem3: fts_lfree(root);
- free(parent);
-mem2: free(sp->fts_path);
-mem1: free(sp);
- return (NULL);
+ {
+ size_t maxarglen = fts_maxarglen(argv);
+ if (! fts_palloc(sp, MAX(maxarglen, MAXPATHLEN)))
+ goto mem1;
+ }
+
+ /* Allocate/initialize root's parent. */
+ if (*argv != NULL) {
+ if ((parent = fts_alloc(sp, "", 0)) == NULL)
+ goto mem2;
+ parent->fts_level = FTS_ROOTPARENTLEVEL;
+ }
+
+ /* The classic fts implementation would call fts_stat with
+ a new entry for each iteration of the loop below.
+ If the comparison function is not specified or if the
+ FTS_DEFER_STAT option is in effect, don't stat any entry
+ in this loop. This is an attempt to minimize the interval
+ between the initial stat/lstat/fstatat and the point at which
+ a directory argument is first opened. This matters for any
+ directory command line argument that resides on a file system
+ without genuine i-nodes. If you specify FTS_DEFER_STAT along
+ with a comparison function, that function must not access any
+ data via the fts_statp pointer. */
+ defer_stat = (compar == NULL || ISSET(FTS_DEFER_STAT));
+
+ /* Allocate/initialize root(s). */
+ for (root = NULL, nitems = 0; *argv != NULL; ++argv, ++nitems) {
+ /* *Do* allow zero-length file names. */
+ size_t len = strlen(*argv);
+ if ((p = fts_alloc(sp, *argv, len)) == NULL)
+ goto mem3;
+ p->fts_level = FTS_ROOTLEVEL;
+ p->fts_parent = parent;
+ p->fts_accpath = p->fts_name;
+ /* Even when defer_stat is true, be sure to stat the first
+ command line argument, since fts_read (at least with
+ FTS_XDEV) requires that. */
+ if (defer_stat && root != NULL) {
+ p->fts_info = FTS_NSOK;
+ fts_set_stat_required(p, true);
+ } else {
+ p->fts_info = fts_stat(sp, p, false);
+ }
+
+ /*
+ * If comparison routine supplied, traverse in sorted
+ * order; otherwise traverse in the order specified.
+ */
+ if (compar) {
+ p->fts_link = root;
+ root = p;
+ } else {
+ p->fts_link = NULL;
+ if (root == NULL)
+ tmp = root = p;
+ else {
+ tmp->fts_link = p;
+ tmp = p;
+ }
+ }
+ }
+ if (compar && nitems > 1)
+ root = fts_sort(sp, root, nitems);
+
+ /*
+ * Allocate a dummy pointer and make fts_read think that we've just
+ * finished the node before the root(s); set p->fts_info to FTS_INIT
+ * so that everything about the "current" node is ignored.
+ */
+ if ((sp->fts_cur = fts_alloc(sp, "", 0)) == NULL)
+ goto mem3;
+ sp->fts_cur->fts_link = root;
+ sp->fts_cur->fts_info = FTS_INIT;
+ if (! setup_dir (sp))
+ goto mem3;
+
+ /*
+ * If using chdir(2), grab a file descriptor pointing to dot to ensure
+ * that we can get back here; this could be avoided for some file names,
+ * but almost certainly not worth the effort. Slashes, symbolic links,
+ * and ".." are all fairly nasty problems. Note, if we can't get the
+ * descriptor we run anyway, just more slowly.
+ */
+ if (!ISSET(FTS_NOCHDIR) && !ISSET(FTS_CWDFD)
+ && (sp->fts_rfd = diropen (sp, ".")) < 0)
+ SET(FTS_NOCHDIR);
+
+ i_ring_init (&sp->fts_fd_ring, -1);
+ return (sp);
+
+mem3: fts_lfree(root);
+ free(parent);
+mem2: free(sp->fts_path);
+mem1: free(sp);
+ return (NULL);
}
static void
internal_function
fts_load (FTS *sp, register FTSENT *p)
{
- register size_t len;
- register char *cp;
-
- /*
- * Load the stream structure for the next traversal. Since we don't
- * actually enter the directory until after the preorder visit, set
- * the fts_accpath field specially so the chdir gets done to the right
- * place and the user can access the first node. From fts_open it's
- * known that the file name will fit.
- */
- len = p->fts_pathlen = p->fts_namelen;
- memmove(sp->fts_path, p->fts_name, len + 1);
- if ((cp = strrchr(p->fts_name, '/')) && (cp != p->fts_name || cp[1])) {
- len = strlen(++cp);
- memmove(p->fts_name, cp, len + 1);
- p->fts_namelen = len;
- }
- p->fts_accpath = p->fts_path = sp->fts_path;
+ register size_t len;
+ register char *cp;
+
+ /*
+ * Load the stream structure for the next traversal. Since we don't
+ * actually enter the directory until after the preorder visit, set
+ * the fts_accpath field specially so the chdir gets done to the right
+ * place and the user can access the first node. From fts_open it's
+ * known that the file name will fit.
+ */
+ len = p->fts_pathlen = p->fts_namelen;
+ memmove(sp->fts_path, p->fts_name, len + 1);
+ if ((cp = strrchr(p->fts_name, '/')) && (cp != p->fts_name || cp[1])) {
+ len = strlen(++cp);
+ memmove(p->fts_name, cp, len + 1);
+ p->fts_namelen = len;
+ }
+ p->fts_accpath = p->fts_path = sp->fts_path;
}
int
fts_close (FTS *sp)
{
- register FTSENT *freep, *p;
- int saved_errno = 0;
-
- /*
- * This still works if we haven't read anything -- the dummy structure
- * points to the root list, so we step through to the end of the root
- * list which has a valid parent pointer.
- */
- if (sp->fts_cur) {
- for (p = sp->fts_cur; p->fts_level >= FTS_ROOTLEVEL;) {
- freep = p;
- p = p->fts_link != NULL ? p->fts_link : p->fts_parent;
- free(freep);
- }
- free(p);
- }
-
- /* Free up child linked list, sort array, file name buffer. */
- if (sp->fts_child)
- fts_lfree(sp->fts_child);
- free(sp->fts_array);
- free(sp->fts_path);
-
- if (ISSET(FTS_CWDFD))
- {
- if (0 <= sp->fts_cwd_fd)
- if (close (sp->fts_cwd_fd))
- saved_errno = errno;
- }
- else if (!ISSET(FTS_NOCHDIR))
- {
- /* Return to original directory, save errno if necessary. */
- if (fchdir(sp->fts_rfd))
- saved_errno = errno;
-
- /* If close fails, record errno only if saved_errno is zero,
- so that we report the probably-more-meaningful fchdir errno. */
- if (close (sp->fts_rfd))
- if (saved_errno == 0)
- saved_errno = errno;
- }
-
- fd_ring_clear (&sp->fts_fd_ring);
+ register FTSENT *freep, *p;
+ int saved_errno = 0;
+
+ /*
+ * This still works if we haven't read anything -- the dummy structure
+ * points to the root list, so we step through to the end of the root
+ * list which has a valid parent pointer.
+ */
+ if (sp->fts_cur) {
+ for (p = sp->fts_cur; p->fts_level >= FTS_ROOTLEVEL;) {
+ freep = p;
+ p = p->fts_link != NULL ? p->fts_link : p->fts_parent;
+ free(freep);
+ }
+ free(p);
+ }
+
+ /* Free up child linked list, sort array, file name buffer. */
+ if (sp->fts_child)
+ fts_lfree(sp->fts_child);
+ free(sp->fts_array);
+ free(sp->fts_path);
+
+ if (ISSET(FTS_CWDFD))
+ {
+ if (0 <= sp->fts_cwd_fd)
+ if (close (sp->fts_cwd_fd))
+ saved_errno = errno;
+ }
+ else if (!ISSET(FTS_NOCHDIR))
+ {
+ /* Return to original directory, save errno if necessary. */
+ if (fchdir(sp->fts_rfd))
+ saved_errno = errno;
+
+ /* If close fails, record errno only if saved_errno is zero,
+ so that we report the probably-more-meaningful fchdir errno. */
+ if (close (sp->fts_rfd))
+ if (saved_errno == 0)
+ saved_errno = errno;
+ }
+
+ fd_ring_clear (&sp->fts_fd_ring);
#if GNULIB_FTS
- if (sp->fts_leaf_optimization_works_ht)
- hash_free (sp->fts_leaf_optimization_works_ht);
+ if (sp->fts_leaf_optimization_works_ht)
+ hash_free (sp->fts_leaf_optimization_works_ht);
#endif
- free_dir (sp);
+ free_dir (sp);
- /* Free up the stream pointer. */
- free(sp);
+ /* Free up the stream pointer. */
+ free(sp);
- /* Set errno and return. */
- if (saved_errno) {
- __set_errno (saved_errno);
- return (-1);
- }
+ /* Set errno and return. */
+ if (saved_errno) {
+ __set_errno (saved_errno);
+ return (-1);
+ }
- return (0);
+ return (0);
}
#if defined __linux__ \
case S_MAGIC_TMPFS:
case S_MAGIC_NFS:
/* On a file system of any of these types, sorting
- is unnecessary, and hence wasteful. */
+ is unnecessary, and hence wasteful. */
return false;
default:
switch (fs_buf.f_type)
{
/* List here the file system types that lack useable dirent.d_type
- info, yet for which the optimization does apply. */
+ info, yet for which the optimization does apply. */
case S_MAGIC_REISERFS:
return true;
case S_MAGIC_PROC:
/* Explicitly listing this or any other file system type for which
- the optimization is not applicable is not necessary, but we leave
- it here to document the risk. Per http://bugs.debian.org/143111,
- /proc may have bogus stat.st_nlink values. */
+ the optimization is not applicable is not necessary, but we leave
+ it here to document the risk. Per http://bugs.debian.org/143111,
+ /proc may have bogus stat.st_nlink values. */
/* fall through */
default:
return false;
}
#else
-static bool dirent_inode_sort_may_be_useful (int dir_fd) { return true; }
-static bool leaf_optimization_applies (int dir_fd) { return false; }
+static bool
+dirent_inode_sort_may_be_useful (int dir_fd _GL_UNUSED) { return true; }
+static bool
+leaf_optimization_applies (int dir_fd _GL_UNUSED) { return false; }
#endif
#if GNULIB_FTS
if (h == NULL)
{
h = sp->fts_leaf_optimization_works_ht
- = hash_initialize (LCO_HT_INITIAL_SIZE, NULL, LCO_hash,
- LCO_compare, free);
+ = hash_initialize (LCO_HT_INITIAL_SIZE, NULL, LCO_hash,
+ LCO_compare, free);
if (h == NULL)
- return false;
+ return false;
}
tmp.st_dev = p->fts_statp->st_dev;
ent = hash_lookup (h, &tmp);
* Special case of "/" at the end of the file name so that slashes aren't
* appended which would cause file names to be written as "....//foo".
*/
-#define NAPPEND(p) \
- (p->fts_path[p->fts_pathlen - 1] == '/' \
- ? p->fts_pathlen - 1 : p->fts_pathlen)
+#define NAPPEND(p) \
+ (p->fts_path[p->fts_pathlen - 1] == '/' \
+ ? p->fts_pathlen - 1 : p->fts_pathlen)
FTSENT *
fts_read (register FTS *sp)
{
- register FTSENT *p, *tmp;
- register unsigned short int instr;
- register char *t;
-
- /* If finished or unrecoverable error, return NULL. */
- if (sp->fts_cur == NULL || ISSET(FTS_STOP))
- return (NULL);
-
- /* Set current node pointer. */
- p = sp->fts_cur;
-
- /* Save and zero out user instructions. */
- instr = p->fts_instr;
- p->fts_instr = FTS_NOINSTR;
-
- /* Any type of file may be re-visited; re-stat and re-turn. */
- if (instr == FTS_AGAIN) {
- p->fts_info = fts_stat(sp, p, false);
- return (p);
- }
- Dprintf (("fts_read: p=%s\n",
- p->fts_info == FTS_INIT ? "" : p->fts_path));
-
- /*
- * Following a symlink -- SLNONE test allows application to see
- * SLNONE and recover. If indirecting through a symlink, have
- * keep a pointer to current location. If unable to get that
- * pointer, follow fails.
- */
- if (instr == FTS_FOLLOW &&
- (p->fts_info == FTS_SL || p->fts_info == FTS_SLNONE)) {
- p->fts_info = fts_stat(sp, p, true);
- if (p->fts_info == FTS_D && !ISSET(FTS_NOCHDIR)) {
- if ((p->fts_symfd = diropen (sp, ".")) < 0) {
- p->fts_errno = errno;
- p->fts_info = FTS_ERR;
- } else
- p->fts_flags |= FTS_SYMFOLLOW;
- }
- goto check_for_dir;
- }
-
- /* Directory in pre-order. */
- if (p->fts_info == FTS_D) {
- /* If skipped or crossed mount point, do post-order visit. */
- if (instr == FTS_SKIP ||
- (ISSET(FTS_XDEV) && p->fts_statp->st_dev != sp->fts_dev)) {
- if (p->fts_flags & FTS_SYMFOLLOW)
- (void)close(p->fts_symfd);
- if (sp->fts_child) {
- fts_lfree(sp->fts_child);
- sp->fts_child = NULL;
- }
- p->fts_info = FTS_DP;
- LEAVE_DIR (sp, p, "1");
- return (p);
- }
-
- /* Rebuild if only read the names and now traversing. */
- if (sp->fts_child != NULL && ISSET(FTS_NAMEONLY)) {
- CLR(FTS_NAMEONLY);
- fts_lfree(sp->fts_child);
- sp->fts_child = NULL;
- }
-
- /*
- * Cd to the subdirectory.
- *
- * If have already read and now fail to chdir, whack the list
- * to make the names come out right, and set the parent errno
- * so the application will eventually get an error condition.
- * Set the FTS_DONTCHDIR flag so that when we logically change
- * directories back to the parent we don't do a chdir.
- *
- * If haven't read do so. If the read fails, fts_build sets
- * FTS_STOP or the fts_info field of the node.
- */
- if (sp->fts_child != NULL) {
- if (fts_safe_changedir(sp, p, -1, p->fts_accpath)) {
- p->fts_errno = errno;
- p->fts_flags |= FTS_DONTCHDIR;
- for (p = sp->fts_child; p != NULL;
- p = p->fts_link)
- p->fts_accpath =
- p->fts_parent->fts_accpath;
- }
- } else if ((sp->fts_child = fts_build(sp, BREAD)) == NULL) {
- if (ISSET(FTS_STOP))
- return (NULL);
- /* If fts_build's call to fts_safe_changedir failed
- because it was not able to fchdir into a
- subdirectory, tell the caller. */
- if (p->fts_errno && p->fts_info != FTS_DNR)
- p->fts_info = FTS_ERR;
- LEAVE_DIR (sp, p, "2");
- return (p);
- }
- p = sp->fts_child;
- sp->fts_child = NULL;
- goto name;
- }
-
- /* Move to the next node on this level. */
-next: tmp = p;
- if ((p = p->fts_link) != NULL) {
- sp->fts_cur = p;
- free(tmp);
-
- /*
- * If reached the top, return to the original directory (or
- * the root of the tree), and load the file names for the next
- * root.
- */
- if (p->fts_level == FTS_ROOTLEVEL) {
- if (RESTORE_INITIAL_CWD(sp)) {
- SET(FTS_STOP);
- return (NULL);
- }
- free_dir(sp);
- fts_load(sp, p);
- setup_dir(sp);
- goto check_for_dir;
- }
-
- /*
- * User may have called fts_set on the node. If skipped,
- * ignore. If followed, get a file descriptor so we can
- * get back if necessary.
- */
- if (p->fts_instr == FTS_SKIP)
- goto next;
- if (p->fts_instr == FTS_FOLLOW) {
- p->fts_info = fts_stat(sp, p, true);
- if (p->fts_info == FTS_D && !ISSET(FTS_NOCHDIR)) {
- if ((p->fts_symfd = diropen (sp, ".")) < 0) {
- p->fts_errno = errno;
- p->fts_info = FTS_ERR;
- } else
- p->fts_flags |= FTS_SYMFOLLOW;
- }
- p->fts_instr = FTS_NOINSTR;
- }
-
-name: t = sp->fts_path + NAPPEND(p->fts_parent);
- *t++ = '/';
- memmove(t, p->fts_name, p->fts_namelen + 1);
+ register FTSENT *p, *tmp;
+ register unsigned short int instr;
+ register char *t;
+
+ /* If finished or unrecoverable error, return NULL. */
+ if (sp->fts_cur == NULL || ISSET(FTS_STOP))
+ return (NULL);
+
+ /* Set current node pointer. */
+ p = sp->fts_cur;
+
+ /* Save and zero out user instructions. */
+ instr = p->fts_instr;
+ p->fts_instr = FTS_NOINSTR;
+
+ /* Any type of file may be re-visited; re-stat and re-turn. */
+ if (instr == FTS_AGAIN) {
+ p->fts_info = fts_stat(sp, p, false);
+ return (p);
+ }
+ Dprintf (("fts_read: p=%s\n",
+ p->fts_info == FTS_INIT ? "" : p->fts_path));
+
+ /*
+ * Following a symlink -- SLNONE test allows application to see
+ * SLNONE and recover. If indirecting through a symlink, have
+ * keep a pointer to current location. If unable to get that
+ * pointer, follow fails.
+ */
+ if (instr == FTS_FOLLOW &&
+ (p->fts_info == FTS_SL || p->fts_info == FTS_SLNONE)) {
+ p->fts_info = fts_stat(sp, p, true);
+ if (p->fts_info == FTS_D && !ISSET(FTS_NOCHDIR)) {
+ if ((p->fts_symfd = diropen (sp, ".")) < 0) {
+ p->fts_errno = errno;
+ p->fts_info = FTS_ERR;
+ } else
+ p->fts_flags |= FTS_SYMFOLLOW;
+ }
+ goto check_for_dir;
+ }
+
+ /* Directory in pre-order. */
+ if (p->fts_info == FTS_D) {
+ /* If skipped or crossed mount point, do post-order visit. */
+ if (instr == FTS_SKIP ||
+ (ISSET(FTS_XDEV) && p->fts_statp->st_dev != sp->fts_dev)) {
+ if (p->fts_flags & FTS_SYMFOLLOW)
+ (void)close(p->fts_symfd);
+ if (sp->fts_child) {
+ fts_lfree(sp->fts_child);
+ sp->fts_child = NULL;
+ }
+ p->fts_info = FTS_DP;
+ LEAVE_DIR (sp, p, "1");
+ return (p);
+ }
+
+ /* Rebuild if only read the names and now traversing. */
+ if (sp->fts_child != NULL && ISSET(FTS_NAMEONLY)) {
+ CLR(FTS_NAMEONLY);
+ fts_lfree(sp->fts_child);
+ sp->fts_child = NULL;
+ }
+
+ /*
+ * Cd to the subdirectory.
+ *
+ * If have already read and now fail to chdir, whack the list
+ * to make the names come out right, and set the parent errno
+ * so the application will eventually get an error condition.
+ * Set the FTS_DONTCHDIR flag so that when we logically change
+ * directories back to the parent we don't do a chdir.
+ *
+ * If haven't read do so. If the read fails, fts_build sets
+ * FTS_STOP or the fts_info field of the node.
+ */
+ if (sp->fts_child != NULL) {
+ if (fts_safe_changedir(sp, p, -1, p->fts_accpath)) {
+ p->fts_errno = errno;
+ p->fts_flags |= FTS_DONTCHDIR;
+ for (p = sp->fts_child; p != NULL;
+ p = p->fts_link)
+ p->fts_accpath =
+ p->fts_parent->fts_accpath;
+ }
+ } else if ((sp->fts_child = fts_build(sp, BREAD)) == NULL) {
+ if (ISSET(FTS_STOP))
+ return (NULL);
+ /* If fts_build's call to fts_safe_changedir failed
+ because it was not able to fchdir into a
+ subdirectory, tell the caller. */
+ if (p->fts_errno && p->fts_info != FTS_DNR)
+ p->fts_info = FTS_ERR;
+ LEAVE_DIR (sp, p, "2");
+ return (p);
+ }
+ p = sp->fts_child;
+ sp->fts_child = NULL;
+ goto name;
+ }
+
+ /* Move to the next node on this level. */
+next: tmp = p;
+ if ((p = p->fts_link) != NULL) {
+ sp->fts_cur = p;
+ free(tmp);
+
+ /*
+ * If reached the top, return to the original directory (or
+ * the root of the tree), and load the file names for the next
+ * root.
+ */
+ if (p->fts_level == FTS_ROOTLEVEL) {
+ if (RESTORE_INITIAL_CWD(sp)) {
+ SET(FTS_STOP);
+ return (NULL);
+ }
+ free_dir(sp);
+ fts_load(sp, p);
+ setup_dir(sp);
+ goto check_for_dir;
+ }
+
+ /*
+ * User may have called fts_set on the node. If skipped,
+ * ignore. If followed, get a file descriptor so we can
+ * get back if necessary.
+ */
+ if (p->fts_instr == FTS_SKIP)
+ goto next;
+ if (p->fts_instr == FTS_FOLLOW) {
+ p->fts_info = fts_stat(sp, p, true);
+ if (p->fts_info == FTS_D && !ISSET(FTS_NOCHDIR)) {
+ if ((p->fts_symfd = diropen (sp, ".")) < 0) {
+ p->fts_errno = errno;
+ p->fts_info = FTS_ERR;
+ } else
+ p->fts_flags |= FTS_SYMFOLLOW;
+ }
+ p->fts_instr = FTS_NOINSTR;
+ }
+
+name: t = sp->fts_path + NAPPEND(p->fts_parent);
+ *t++ = '/';
+ memmove(t, p->fts_name, p->fts_namelen + 1);
check_for_dir:
- sp->fts_cur = p;
- if (p->fts_info == FTS_NSOK)
- {
- if (p->fts_statp->st_size == FTS_STAT_REQUIRED)
- {
- FTSENT *parent = p->fts_parent;
- if (FTS_ROOTLEVEL < p->fts_level
- /* ->fts_n_dirs_remaining is not valid
- for command-line-specified names. */
- && parent->fts_n_dirs_remaining == 0
- && ISSET(FTS_NOSTAT)
- && ISSET(FTS_PHYSICAL)
- && link_count_optimize_ok (parent))
- {
- /* nothing more needed */
- }
- else
- {
- p->fts_info = fts_stat(sp, p, false);
- if (S_ISDIR(p->fts_statp->st_mode)
- && p->fts_level != FTS_ROOTLEVEL
- && parent->fts_n_dirs_remaining)
- parent->fts_n_dirs_remaining--;
- }
- }
- else
- fts_assert (p->fts_statp->st_size == FTS_NO_STAT_REQUIRED);
- }
-
- if (p->fts_info == FTS_D)
- {
- /* Now that P->fts_statp is guaranteed to be valid,
- if this is a command-line directory, record its
- device number, to be used for FTS_XDEV. */
- if (p->fts_level == FTS_ROOTLEVEL)
- sp->fts_dev = p->fts_statp->st_dev;
- Dprintf ((" entering: %s\n", p->fts_path));
- if (! enter_dir (sp, p))
- {
- __set_errno (ENOMEM);
- return NULL;
- }
- }
- return p;
- }
-
- /* Move up to the parent node. */
- p = tmp->fts_parent;
- sp->fts_cur = p;
- free(tmp);
-
- if (p->fts_level == FTS_ROOTPARENTLEVEL) {
- /*
- * Done; free everything up and set errno to 0 so the user
- * can distinguish between error and EOF.
- */
- free(p);
- __set_errno (0);
- return (sp->fts_cur = NULL);
- }
-
- fts_assert (p->fts_info != FTS_NSOK);
-
- /* NUL terminate the file name. */
- sp->fts_path[p->fts_pathlen] = '\0';
-
- /*
- * Return to the parent directory. If at a root node, restore
- * the initial working directory. If we came through a symlink,
- * go back through the file descriptor. Otherwise, move up
- * one level, via "..".
- */
- if (p->fts_level == FTS_ROOTLEVEL) {
- if (RESTORE_INITIAL_CWD(sp)) {
- p->fts_errno = errno;
- SET(FTS_STOP);
- }
- } else if (p->fts_flags & FTS_SYMFOLLOW) {
- if (FCHDIR(sp, p->fts_symfd)) {
- int saved_errno = errno;
- (void)close(p->fts_symfd);
- __set_errno (saved_errno);
- p->fts_errno = errno;
- SET(FTS_STOP);
- }
- (void)close(p->fts_symfd);
- } else if (!(p->fts_flags & FTS_DONTCHDIR) &&
- fts_safe_changedir(sp, p->fts_parent, -1, "..")) {
- p->fts_errno = errno;
- SET(FTS_STOP);
- }
- p->fts_info = p->fts_errno ? FTS_ERR : FTS_DP;
- if (p->fts_errno == 0)
- LEAVE_DIR (sp, p, "3");
- return ISSET(FTS_STOP) ? NULL : p;
+ sp->fts_cur = p;
+ if (p->fts_info == FTS_NSOK)
+ {
+ if (p->fts_statp->st_size == FTS_STAT_REQUIRED)
+ {
+ FTSENT *parent = p->fts_parent;
+ if (FTS_ROOTLEVEL < p->fts_level
+ /* ->fts_n_dirs_remaining is not valid
+ for command-line-specified names. */
+ && parent->fts_n_dirs_remaining == 0
+ && ISSET(FTS_NOSTAT)
+ && ISSET(FTS_PHYSICAL)
+ && link_count_optimize_ok (parent))
+ {
+ /* nothing more needed */
+ }
+ else
+ {
+ p->fts_info = fts_stat(sp, p, false);
+ if (S_ISDIR(p->fts_statp->st_mode)
+ && p->fts_level != FTS_ROOTLEVEL
+ && parent->fts_n_dirs_remaining)
+ parent->fts_n_dirs_remaining--;
+ }
+ }
+ else
+ fts_assert (p->fts_statp->st_size == FTS_NO_STAT_REQUIRED);
+ }
+
+ if (p->fts_info == FTS_D)
+ {
+ /* Now that P->fts_statp is guaranteed to be valid,
+ if this is a command-line directory, record its
+ device number, to be used for FTS_XDEV. */
+ if (p->fts_level == FTS_ROOTLEVEL)
+ sp->fts_dev = p->fts_statp->st_dev;
+ Dprintf ((" entering: %s\n", p->fts_path));
+ if (! enter_dir (sp, p))
+ {
+ __set_errno (ENOMEM);
+ return NULL;
+ }
+ }
+ return p;
+ }
+
+ /* Move up to the parent node. */
+ p = tmp->fts_parent;
+ sp->fts_cur = p;
+ free(tmp);
+
+ if (p->fts_level == FTS_ROOTPARENTLEVEL) {
+ /*
+ * Done; free everything up and set errno to 0 so the user
+ * can distinguish between error and EOF.
+ */
+ free(p);
+ __set_errno (0);
+ return (sp->fts_cur = NULL);
+ }
+
+ fts_assert (p->fts_info != FTS_NSOK);
+
+ /* NUL terminate the file name. */
+ sp->fts_path[p->fts_pathlen] = '\0';
+
+ /*
+ * Return to the parent directory. If at a root node, restore
+ * the initial working directory. If we came through a symlink,
+ * go back through the file descriptor. Otherwise, move up
+ * one level, via "..".
+ */
+ if (p->fts_level == FTS_ROOTLEVEL) {
+ if (RESTORE_INITIAL_CWD(sp)) {
+ p->fts_errno = errno;
+ SET(FTS_STOP);
+ }
+ } else if (p->fts_flags & FTS_SYMFOLLOW) {
+ if (FCHDIR(sp, p->fts_symfd)) {
+ int saved_errno = errno;
+ (void)close(p->fts_symfd);
+ __set_errno (saved_errno);
+ p->fts_errno = errno;
+ SET(FTS_STOP);
+ }
+ (void)close(p->fts_symfd);
+ } else if (!(p->fts_flags & FTS_DONTCHDIR) &&
+ fts_safe_changedir(sp, p->fts_parent, -1, "..")) {
+ p->fts_errno = errno;
+ SET(FTS_STOP);
+ }
+ p->fts_info = p->fts_errno ? FTS_ERR : FTS_DP;
+ if (p->fts_errno == 0)
+ LEAVE_DIR (sp, p, "3");
+ return ISSET(FTS_STOP) ? NULL : p;
}
/*
*/
/* ARGSUSED */
int
-fts_set(FTS *sp ATTRIBUTE_UNUSED, FTSENT *p, int instr)
+fts_set(FTS *sp _GL_UNUSED, FTSENT *p, int instr)
{
- if (instr != 0 && instr != FTS_AGAIN && instr != FTS_FOLLOW &&
- instr != FTS_NOINSTR && instr != FTS_SKIP) {
- __set_errno (EINVAL);
- return (1);
- }
- p->fts_instr = instr;
- return (0);
+ if (instr != 0 && instr != FTS_AGAIN && instr != FTS_FOLLOW &&
+ instr != FTS_NOINSTR && instr != FTS_SKIP) {
+ __set_errno (EINVAL);
+ return (1);
+ }
+ p->fts_instr = instr;
+ return (0);
}
FTSENT *
fts_children (register FTS *sp, int instr)
{
- register FTSENT *p;
- int fd;
-
- if (instr != 0 && instr != FTS_NAMEONLY) {
- __set_errno (EINVAL);
- return (NULL);
- }
-
- /* Set current node pointer. */
- p = sp->fts_cur;
-
- /*
- * Errno set to 0 so user can distinguish empty directory from
- * an error.
- */
- __set_errno (0);
-
- /* Fatal errors stop here. */
- if (ISSET(FTS_STOP))
- return (NULL);
-
- /* Return logical hierarchy of user's arguments. */
- if (p->fts_info == FTS_INIT)
- return (p->fts_link);
-
- /*
- * If not a directory being visited in pre-order, stop here. Could
- * allow FTS_DNR, assuming the user has fixed the problem, but the
- * same effect is available with FTS_AGAIN.
- */
- if (p->fts_info != FTS_D /* && p->fts_info != FTS_DNR */)
- return (NULL);
-
- /* Free up any previous child list. */
- if (sp->fts_child != NULL)
- fts_lfree(sp->fts_child);
-
- if (instr == FTS_NAMEONLY) {
- SET(FTS_NAMEONLY);
- instr = BNAMES;
- } else
- instr = BCHILD;
-
- /*
- * If using chdir on a relative file name and called BEFORE fts_read
- * does its chdir to the root of a traversal, we can lose -- we need to
- * chdir into the subdirectory, and we don't know where the current
- * directory is, so we can't get back so that the upcoming chdir by
- * fts_read will work.
- */
- if (p->fts_level != FTS_ROOTLEVEL || p->fts_accpath[0] == '/' ||
- ISSET(FTS_NOCHDIR))
- return (sp->fts_child = fts_build(sp, instr));
-
- if ((fd = diropen (sp, ".")) < 0)
- return (sp->fts_child = NULL);
- sp->fts_child = fts_build(sp, instr);
- if (ISSET(FTS_CWDFD))
- {
- cwd_advance_fd (sp, fd, true);
- }
- else
- {
- if (fchdir(fd))
- {
- int saved_errno = errno;
- close (fd);
- __set_errno (saved_errno);
- return NULL;
- }
- close (fd);
- }
- return (sp->fts_child);
+ register FTSENT *p;
+ int fd;
+
+ if (instr != 0 && instr != FTS_NAMEONLY) {
+ __set_errno (EINVAL);
+ return (NULL);
+ }
+
+ /* Set current node pointer. */
+ p = sp->fts_cur;
+
+ /*
+ * Errno set to 0 so user can distinguish empty directory from
+ * an error.
+ */
+ __set_errno (0);
+
+ /* Fatal errors stop here. */
+ if (ISSET(FTS_STOP))
+ return (NULL);
+
+ /* Return logical hierarchy of user's arguments. */
+ if (p->fts_info == FTS_INIT)
+ return (p->fts_link);
+
+ /*
+ * If not a directory being visited in pre-order, stop here. Could
+ * allow FTS_DNR, assuming the user has fixed the problem, but the
+ * same effect is available with FTS_AGAIN.
+ */
+ if (p->fts_info != FTS_D /* && p->fts_info != FTS_DNR */)
+ return (NULL);
+
+ /* Free up any previous child list. */
+ if (sp->fts_child != NULL)
+ fts_lfree(sp->fts_child);
+
+ if (instr == FTS_NAMEONLY) {
+ SET(FTS_NAMEONLY);
+ instr = BNAMES;
+ } else
+ instr = BCHILD;
+
+ /*
+ * If using chdir on a relative file name and called BEFORE fts_read
+ * does its chdir to the root of a traversal, we can lose -- we need to
+ * chdir into the subdirectory, and we don't know where the current
+ * directory is, so we can't get back so that the upcoming chdir by
+ * fts_read will work.
+ */
+ if (p->fts_level != FTS_ROOTLEVEL || p->fts_accpath[0] == '/' ||
+ ISSET(FTS_NOCHDIR))
+ return (sp->fts_child = fts_build(sp, instr));
+
+ if ((fd = diropen (sp, ".")) < 0)
+ return (sp->fts_child = NULL);
+ sp->fts_child = fts_build(sp, instr);
+ if (ISSET(FTS_CWDFD))
+ {
+ cwd_advance_fd (sp, fd, true);
+ }
+ else
+ {
+ if (fchdir(fd))
+ {
+ int saved_errno = errno;
+ close (fd);
+ __set_errno (saved_errno);
+ return NULL;
+ }
+ close (fd);
+ }
+ return (sp->fts_child);
}
/* A comparison function to sort on increasing inode number.
fts_compare_ino (struct _ftsent const **a, struct _ftsent const **b)
{
return (a[0]->fts_statp->st_ino < b[0]->fts_statp->st_ino ? -1
- : b[0]->fts_statp->st_ino < a[0]->fts_statp->st_ino ? 1 : 0);
+ : b[0]->fts_statp->st_ino < a[0]->fts_statp->st_ino ? 1 : 0);
}
/* Map the dirent.d_type value, DTYPE, to the corresponding stat.st_mode
internal_function
fts_build (register FTS *sp, int type)
{
- register struct dirent *dp;
- register FTSENT *p, *head;
- register size_t nitems;
- FTSENT *cur, *tail;
- DIR *dirp;
- void *oldaddr;
- int saved_errno;
- bool descend;
- bool doadjust;
- ptrdiff_t level;
- nlink_t nlinks;
- bool nostat;
- size_t len, maxlen, new_len;
- char *cp;
-
- /* Set current node pointer. */
- cur = sp->fts_cur;
-
- /*
- * Open the directory for reading. If this fails, we're done.
- * If being called from fts_read, set the fts_info field.
- */
+ register struct dirent *dp;
+ register FTSENT *p, *head;
+ register size_t nitems;
+ FTSENT *cur, *tail;
+ DIR *dirp;
+ void *oldaddr;
+ int saved_errno;
+ bool descend;
+ bool doadjust;
+ ptrdiff_t level;
+ nlink_t nlinks;
+ bool nostat;
+ size_t len, maxlen, new_len;
+ char *cp;
+
+ /* Set current node pointer. */
+ cur = sp->fts_cur;
+
+ /*
+ * Open the directory for reading. If this fails, we're done.
+ * If being called from fts_read, set the fts_info field.
+ */
#if defined FTS_WHITEOUT && 0
- if (ISSET(FTS_WHITEOUT))
- oflag = DTF_NODUP|DTF_REWIND;
- else
- oflag = DTF_HIDEW|DTF_NODUP|DTF_REWIND;
+ if (ISSET(FTS_WHITEOUT))
+ oflag = DTF_NODUP|DTF_REWIND;
+ else
+ oflag = DTF_HIDEW|DTF_NODUP|DTF_REWIND;
#else
# define __opendir2(file, flag) \
- ( ! ISSET(FTS_NOCHDIR) && ISSET(FTS_CWDFD) \
- ? opendirat(sp->fts_cwd_fd, file) \
- : opendir(file))
+ ( ! ISSET(FTS_NOCHDIR) && ISSET(FTS_CWDFD) \
+ ? opendirat(sp->fts_cwd_fd, file) \
+ : opendir(file))
#endif
if ((dirp = __opendir2(cur->fts_accpath, oflag)) == NULL) {
- if (type == BREAD) {
- cur->fts_info = FTS_DNR;
- cur->fts_errno = errno;
- }
- return (NULL);
- }
+ if (type == BREAD) {
+ cur->fts_info = FTS_DNR;
+ cur->fts_errno = errno;
+ }
+ return (NULL);
+ }
/* Rather than calling fts_stat for each and every entry encountered
- in the readdir loop (below), stat each directory only right after
- opening it. */
+ in the readdir loop (below), stat each directory only right after
+ opening it. */
if (cur->fts_info == FTS_NSOK)
- cur->fts_info = fts_stat(sp, cur, false);
-
- /*
- * Nlinks is the number of possible entries of type directory in the
- * directory if we're cheating on stat calls, 0 if we're not doing
- * any stat calls at all, (nlink_t) -1 if we're statting everything.
- */
- if (type == BNAMES) {
- nlinks = 0;
- /* Be quiet about nostat, GCC. */
- nostat = false;
- } else if (ISSET(FTS_NOSTAT) && ISSET(FTS_PHYSICAL)) {
- nlinks = (cur->fts_statp->st_nlink
- - (ISSET(FTS_SEEDOT) ? 0 : 2));
- nostat = true;
- } else {
- nlinks = -1;
- nostat = false;
- }
-
- /*
- * If we're going to need to stat anything or we want to descend
- * and stay in the directory, chdir. If this fails we keep going,
- * but set a flag so we don't chdir after the post-order visit.
- * We won't be able to stat anything, but we can still return the
- * names themselves. Note, that since fts_read won't be able to
- * chdir into the directory, it will have to return different file
- * names than before, i.e. "a/b" instead of "b". Since the node
- * has already been visited in pre-order, have to wait until the
- * post-order visit to return the error. There is a special case
- * here, if there was nothing to stat then it's not an error to
- * not be able to stat. This is all fairly nasty. If a program
- * needed sorted entries or stat information, they had better be
- * checking FTS_NS on the returned nodes.
- */
- if (nlinks || type == BREAD) {
- int dir_fd = dirfd(dirp);
- if (ISSET(FTS_CWDFD) && 0 <= dir_fd)
- {
- dir_fd = dup (dir_fd);
- set_cloexec_flag (dir_fd, true);
- }
- if (dir_fd < 0 || fts_safe_changedir(sp, cur, dir_fd, NULL)) {
- if (nlinks && type == BREAD)
- cur->fts_errno = errno;
- cur->fts_flags |= FTS_DONTCHDIR;
- descend = false;
- closedir(dirp);
- if (ISSET(FTS_CWDFD) && 0 <= dir_fd)
- close (dir_fd);
- dirp = NULL;
- } else
- descend = true;
- } else
- descend = false;
-
- /*
- * Figure out the max file name length that can be stored in the
- * current buffer -- the inner loop allocates more space as necessary.
- * We really wouldn't have to do the maxlen calculations here, we
- * could do them in fts_read before returning the name, but it's a
- * lot easier here since the length is part of the dirent structure.
- *
- * If not changing directories set a pointer so that can just append
- * each new component into the file name.
- */
- len = NAPPEND(cur);
- if (ISSET(FTS_NOCHDIR)) {
- cp = sp->fts_path + len;
- *cp++ = '/';
- } else {
- /* GCC, you're too verbose. */
- cp = NULL;
- }
- len++;
- maxlen = sp->fts_pathlen - len;
-
- level = cur->fts_level + 1;
-
- /* Read the directory, attaching each entry to the `link' pointer. */
- doadjust = false;
- for (head = tail = NULL, nitems = 0; dirp && (dp = readdir(dirp));) {
- bool is_dir;
-
- if (!ISSET(FTS_SEEDOT) && ISDOT(dp->d_name))
- continue;
-
- if ((p = fts_alloc (sp, dp->d_name,
- _D_EXACT_NAMLEN (dp))) == NULL)
- goto mem1;
- if (_D_EXACT_NAMLEN (dp) >= maxlen) {
- /* include space for NUL */
- oldaddr = sp->fts_path;
- if (! fts_palloc(sp, _D_EXACT_NAMLEN (dp) + len + 1)) {
- /*
- * No more memory. Save
- * errno, free up the current structure and the
- * structures already allocated.
- */
-mem1: saved_errno = errno;
- free(p);
- fts_lfree(head);
- closedir(dirp);
- cur->fts_info = FTS_ERR;
- SET(FTS_STOP);
- __set_errno (saved_errno);
- return (NULL);
- }
- /* Did realloc() change the pointer? */
- if (oldaddr != sp->fts_path) {
- doadjust = true;
- if (ISSET(FTS_NOCHDIR))
- cp = sp->fts_path + len;
- }
- maxlen = sp->fts_pathlen - len;
- }
-
- new_len = len + _D_EXACT_NAMLEN (dp);
- if (new_len < len) {
- /*
- * In the unlikely event that we would end up
- * with a file name longer than SIZE_MAX, free up
- * the current structure and the structures already
- * allocated, then error out with ENAMETOOLONG.
- */
- free(p);
- fts_lfree(head);
- closedir(dirp);
- cur->fts_info = FTS_ERR;
- SET(FTS_STOP);
- __set_errno (ENAMETOOLONG);
- return (NULL);
- }
- p->fts_level = level;
- p->fts_parent = sp->fts_cur;
- p->fts_pathlen = new_len;
+ cur->fts_info = fts_stat(sp, cur, false);
+ else if (sp->fts_options & FTS_TIGHT_CYCLE_CHECK) {
+ /* Now read the stat info again after opening a directory to
+ * reveal eventual changes caused by a submount triggered by
+ * the traversal. But do it only for utilities which use
+ * FTS_TIGHT_CYCLE_CHECK. Therefore, only find and du
+ * benefit/suffer from this feature for now.
+ */
+ LEAVE_DIR (sp, cur, "4");
+ fts_stat (sp, cur, false);
+ if (! enter_dir (sp, cur)) {
+ __set_errno (ENOMEM);
+ return NULL;
+ }
+ }
+
+ /*
+ * Nlinks is the number of possible entries of type directory in the
+ * directory if we're cheating on stat calls, 0 if we're not doing
+ * any stat calls at all, (nlink_t) -1 if we're statting everything.
+ */
+ if (type == BNAMES) {
+ nlinks = 0;
+ /* Be quiet about nostat, GCC. */
+ nostat = false;
+ } else if (ISSET(FTS_NOSTAT) && ISSET(FTS_PHYSICAL)) {
+ nlinks = (cur->fts_statp->st_nlink
+ - (ISSET(FTS_SEEDOT) ? 0 : 2));
+ nostat = true;
+ } else {
+ nlinks = -1;
+ nostat = false;
+ }
+
+ /*
+ * If we're going to need to stat anything or we want to descend
+ * and stay in the directory, chdir. If this fails we keep going,
+ * but set a flag so we don't chdir after the post-order visit.
+ * We won't be able to stat anything, but we can still return the
+ * names themselves. Note, that since fts_read won't be able to
+ * chdir into the directory, it will have to return different file
+ * names than before, i.e. "a/b" instead of "b". Since the node
+ * has already been visited in pre-order, have to wait until the
+ * post-order visit to return the error. There is a special case
+ * here, if there was nothing to stat then it's not an error to
+ * not be able to stat. This is all fairly nasty. If a program
+ * needed sorted entries or stat information, they had better be
+ * checking FTS_NS on the returned nodes.
+ */
+ if (nlinks || type == BREAD) {
+ int dir_fd = dirfd(dirp);
+ if (ISSET(FTS_CWDFD) && 0 <= dir_fd)
+ {
+ dir_fd = dup (dir_fd);
+ set_cloexec_flag (dir_fd, true);
+ }
+ if (dir_fd < 0 || fts_safe_changedir(sp, cur, dir_fd, NULL)) {
+ if (nlinks && type == BREAD)
+ cur->fts_errno = errno;
+ cur->fts_flags |= FTS_DONTCHDIR;
+ descend = false;
+ closedir(dirp);
+ if (ISSET(FTS_CWDFD) && 0 <= dir_fd)
+ close (dir_fd);
+ dirp = NULL;
+ } else
+ descend = true;
+ } else
+ descend = false;
+
+ /*
+ * Figure out the max file name length that can be stored in the
+ * current buffer -- the inner loop allocates more space as necessary.
+ * We really wouldn't have to do the maxlen calculations here, we
+ * could do them in fts_read before returning the name, but it's a
+ * lot easier here since the length is part of the dirent structure.
+ *
+ * If not changing directories set a pointer so that can just append
+ * each new component into the file name.
+ */
+ len = NAPPEND(cur);
+ if (ISSET(FTS_NOCHDIR)) {
+ cp = sp->fts_path + len;
+ *cp++ = '/';
+ } else {
+ /* GCC, you're too verbose. */
+ cp = NULL;
+ }
+ len++;
+ maxlen = sp->fts_pathlen - len;
+
+ level = cur->fts_level + 1;
+
+ /* Read the directory, attaching each entry to the `link' pointer. */
+ doadjust = false;
+ for (head = tail = NULL, nitems = 0; dirp && (dp = readdir(dirp));) {
+ bool is_dir;
+
+ if (!ISSET(FTS_SEEDOT) && ISDOT(dp->d_name))
+ continue;
+
+ if ((p = fts_alloc (sp, dp->d_name,
+ _D_EXACT_NAMLEN (dp))) == NULL)
+ goto mem1;
+ if (_D_EXACT_NAMLEN (dp) >= maxlen) {
+ /* include space for NUL */
+ oldaddr = sp->fts_path;
+ if (! fts_palloc(sp, _D_EXACT_NAMLEN (dp) + len + 1)) {
+ /*
+ * No more memory. Save
+ * errno, free up the current structure and the
+ * structures already allocated.
+ */
+mem1: saved_errno = errno;
+ free(p);
+ fts_lfree(head);
+ closedir(dirp);
+ cur->fts_info = FTS_ERR;
+ SET(FTS_STOP);
+ __set_errno (saved_errno);
+ return (NULL);
+ }
+ /* Did realloc() change the pointer? */
+ if (oldaddr != sp->fts_path) {
+ doadjust = true;
+ if (ISSET(FTS_NOCHDIR))
+ cp = sp->fts_path + len;
+ }
+ maxlen = sp->fts_pathlen - len;
+ }
+
+ new_len = len + _D_EXACT_NAMLEN (dp);
+ if (new_len < len) {
+ /*
+ * In the unlikely event that we would end up
+ * with a file name longer than SIZE_MAX, free up
+ * the current structure and the structures already
+ * allocated, then error out with ENAMETOOLONG.
+ */
+ free(p);
+ fts_lfree(head);
+ closedir(dirp);
+ cur->fts_info = FTS_ERR;
+ SET(FTS_STOP);
+ __set_errno (ENAMETOOLONG);
+ return (NULL);
+ }
+ p->fts_level = level;
+ p->fts_parent = sp->fts_cur;
+ p->fts_pathlen = new_len;
#if defined FTS_WHITEOUT && 0
- if (dp->d_type == DT_WHT)
- p->fts_flags |= FTS_ISW;
+ if (dp->d_type == DT_WHT)
+ p->fts_flags |= FTS_ISW;
#endif
- /* Store dirent.d_ino, in case we need to sort
- entries before processing them. */
- p->fts_statp->st_ino = D_INO (dp);
-
- /* Build a file name for fts_stat to stat. */
- if (ISSET(FTS_NOCHDIR)) {
- p->fts_accpath = p->fts_path;
- memmove(cp, p->fts_name, p->fts_namelen + 1);
- } else
- p->fts_accpath = p->fts_name;
-
- if (sp->fts_compar == NULL || ISSET(FTS_DEFER_STAT)) {
- /* Record what fts_read will have to do with this
- entry. In many cases, it will simply fts_stat it,
- but we can take advantage of any d_type information
- to optimize away the unnecessary stat calls. I.e.,
- if FTS_NOSTAT is in effect and we're not following
- symlinks (FTS_PHYSICAL) and d_type indicates this
- is *not* a directory, then we won't have to stat it
- at all. If it *is* a directory, then (currently)
- we stat it regardless, in order to get device and
- inode numbers. Some day we might optimize that
- away, too, for directories where d_ino is known to
- be valid. */
- bool skip_stat = (ISSET(FTS_PHYSICAL)
- && ISSET(FTS_NOSTAT)
- && DT_IS_KNOWN(dp)
- && ! DT_MUST_BE(dp, DT_DIR));
- p->fts_info = FTS_NSOK;
- /* Propagate dirent.d_type information back
- to caller, when possible. */
- set_stat_type (p->fts_statp, D_TYPE (dp));
- fts_set_stat_required(p, !skip_stat);
- is_dir = (ISSET(FTS_PHYSICAL)
- && DT_MUST_BE(dp, DT_DIR));
- } else {
- p->fts_info = fts_stat(sp, p, false);
- is_dir = (p->fts_info == FTS_D
- || p->fts_info == FTS_DC
- || p->fts_info == FTS_DOT);
- }
-
- /* Decrement link count if applicable. */
- if (nlinks > 0 && is_dir)
- nlinks -= nostat;
-
- /* We walk in directory order so "ls -f" doesn't get upset. */
- p->fts_link = NULL;
- if (head == NULL)
- head = tail = p;
- else {
- tail->fts_link = p;
- tail = p;
- }
- ++nitems;
- }
- if (dirp)
- closedir(dirp);
-
- /*
- * If realloc() changed the address of the file name, adjust the
- * addresses for the rest of the tree and the dir list.
- */
- if (doadjust)
- fts_padjust(sp, head);
-
- /*
- * If not changing directories, reset the file name back to original
- * state.
- */
- if (ISSET(FTS_NOCHDIR)) {
- if (len == sp->fts_pathlen || nitems == 0)
- --cp;
- *cp = '\0';
- }
-
- /*
- * If descended after called from fts_children or after called from
- * fts_read and nothing found, get back. At the root level we use
- * the saved fd; if one of fts_open()'s arguments is a relative name
- * to an empty directory, we wind up here with no other way back. If
- * can't get back, we're done.
- */
- if (descend && (type == BCHILD || !nitems) &&
- (cur->fts_level == FTS_ROOTLEVEL
- ? RESTORE_INITIAL_CWD(sp)
- : fts_safe_changedir(sp, cur->fts_parent, -1, ".."))) {
- cur->fts_info = FTS_ERR;
- SET(FTS_STOP);
- fts_lfree(head);
- return (NULL);
- }
-
- /* If didn't find anything, return NULL. */
- if (!nitems) {
- if (type == BREAD)
- cur->fts_info = FTS_DP;
- fts_lfree(head);
- return (NULL);
- }
-
- /* If there are many entries, no sorting function has been specified,
- and this file system is of a type that may be slow with a large
- number of entries, then sort the directory entries on increasing
- inode numbers. */
- if (nitems > _FTS_INODE_SORT_DIR_ENTRIES_THRESHOLD
- && !sp->fts_compar
- && ISSET (FTS_CWDFD)
- && dirent_inode_sort_may_be_useful (sp->fts_cwd_fd)) {
- sp->fts_compar = fts_compare_ino;
- head = fts_sort (sp, head, nitems);
- sp->fts_compar = NULL;
- }
-
- /* Sort the entries. */
- if (sp->fts_compar && nitems > 1)
- head = fts_sort(sp, head, nitems);
- return (head);
+ /* Store dirent.d_ino, in case we need to sort
+ entries before processing them. */
+ p->fts_statp->st_ino = D_INO (dp);
+
+ /* Build a file name for fts_stat to stat. */
+ if (ISSET(FTS_NOCHDIR)) {
+ p->fts_accpath = p->fts_path;
+ memmove(cp, p->fts_name, p->fts_namelen + 1);
+ } else
+ p->fts_accpath = p->fts_name;
+
+ if (sp->fts_compar == NULL || ISSET(FTS_DEFER_STAT)) {
+ /* Record what fts_read will have to do with this
+ entry. In many cases, it will simply fts_stat it,
+ but we can take advantage of any d_type information
+ to optimize away the unnecessary stat calls. I.e.,
+ if FTS_NOSTAT is in effect and we're not following
+ symlinks (FTS_PHYSICAL) and d_type indicates this
+ is *not* a directory, then we won't have to stat it
+ at all. If it *is* a directory, then (currently)
+ we stat it regardless, in order to get device and
+ inode numbers. Some day we might optimize that
+ away, too, for directories where d_ino is known to
+ be valid. */
+ bool skip_stat = (ISSET(FTS_PHYSICAL)
+ && ISSET(FTS_NOSTAT)
+ && DT_IS_KNOWN(dp)
+ && ! DT_MUST_BE(dp, DT_DIR));
+ p->fts_info = FTS_NSOK;
+ /* Propagate dirent.d_type information back
+ to caller, when possible. */
+ set_stat_type (p->fts_statp, D_TYPE (dp));
+ fts_set_stat_required(p, !skip_stat);
+ is_dir = (ISSET(FTS_PHYSICAL)
+ && DT_MUST_BE(dp, DT_DIR));
+ } else {
+ p->fts_info = fts_stat(sp, p, false);
+ is_dir = (p->fts_info == FTS_D
+ || p->fts_info == FTS_DC
+ || p->fts_info == FTS_DOT);
+ }
+
+ /* Decrement link count if applicable. */
+ if (nlinks > 0 && is_dir)
+ nlinks -= nostat;
+
+ /* We walk in directory order so "ls -f" doesn't get upset. */
+ p->fts_link = NULL;
+ if (head == NULL)
+ head = tail = p;
+ else {
+ tail->fts_link = p;
+ tail = p;
+ }
+ ++nitems;
+ }
+ if (dirp)
+ closedir(dirp);
+
+ /*
+ * If realloc() changed the address of the file name, adjust the
+ * addresses for the rest of the tree and the dir list.
+ */
+ if (doadjust)
+ fts_padjust(sp, head);
+
+ /*
+ * If not changing directories, reset the file name back to original
+ * state.
+ */
+ if (ISSET(FTS_NOCHDIR)) {
+ if (len == sp->fts_pathlen || nitems == 0)
+ --cp;
+ *cp = '\0';
+ }
+
+ /*
+ * If descended after called from fts_children or after called from
+ * fts_read and nothing found, get back. At the root level we use
+ * the saved fd; if one of fts_open()'s arguments is a relative name
+ * to an empty directory, we wind up here with no other way back. If
+ * can't get back, we're done.
+ */
+ if (descend && (type == BCHILD || !nitems) &&
+ (cur->fts_level == FTS_ROOTLEVEL
+ ? RESTORE_INITIAL_CWD(sp)
+ : fts_safe_changedir(sp, cur->fts_parent, -1, ".."))) {
+ cur->fts_info = FTS_ERR;
+ SET(FTS_STOP);
+ fts_lfree(head);
+ return (NULL);
+ }
+
+ /* If didn't find anything, return NULL. */
+ if (!nitems) {
+ if (type == BREAD)
+ cur->fts_info = FTS_DP;
+ fts_lfree(head);
+ return (NULL);
+ }
+
+ /* If there are many entries, no sorting function has been specified,
+ and this file system is of a type that may be slow with a large
+ number of entries, then sort the directory entries on increasing
+ inode numbers. */
+ if (nitems > _FTS_INODE_SORT_DIR_ENTRIES_THRESHOLD
+ && !sp->fts_compar
+ && ISSET (FTS_CWDFD)
+ && dirent_inode_sort_may_be_useful (sp->fts_cwd_fd)) {
+ sp->fts_compar = fts_compare_ino;
+ head = fts_sort (sp, head, nitems);
+ sp->fts_compar = NULL;
+ }
+
+ /* Sort the entries. */
+ if (sp->fts_compar && nitems > 1)
+ head = fts_sort(sp, head, nitems);
+ return (head);
}
#if FTS_DEBUG
for (ent = e_curr; ent->fts_level >= FTS_ROOTLEVEL; ent = ent->fts_parent)
{
if (ad->ino == ent->fts_statp->st_ino
- && ad->dev == ent->fts_statp->st_dev)
- return;
+ && ad->dev == ent->fts_statp->st_dev)
+ return;
}
printf ("ERROR: tree dir, %s, not active\n", ad->fts_ent->fts_accpath);
printf ("active dirs:\n");
for (ent = e_curr;
ent->fts_level >= FTS_ROOTLEVEL; ent = ent->fts_parent)
printf (" %s(%"PRIuMAX"/%"PRIuMAX") to %s(%"PRIuMAX"/%"PRIuMAX")...\n",
- ad->fts_ent->fts_accpath,
- (uintmax_t) ad->dev,
- (uintmax_t) ad->ino,
- ent->fts_accpath,
- (uintmax_t) ent->fts_statp->st_dev,
- (uintmax_t) ent->fts_statp->st_ino);
+ ad->fts_ent->fts_accpath,
+ (uintmax_t) ad->dev,
+ (uintmax_t) ad->ino,
+ ent->fts_accpath,
+ (uintmax_t) ent->fts_statp->st_dev,
+ (uintmax_t) ent->fts_statp->st_ino);
}
void
ad.ino = t->fts_statp->st_ino;
ad.dev = t->fts_statp->st_dev;
if ( ! hash_lookup (sp->fts_cycle.ht, &ad))
- printf ("ERROR: active dir, %s, not in tree\n", t->fts_path);
+ printf ("ERROR: active dir, %s, not in tree\n", t->fts_path);
}
/* Make sure every dir in the tree is an active dir.
But ENT is not necessarily a directory. If so, just skip this part. */
if (ent->fts_parent->fts_level >= FTS_ROOTLEVEL
&& (ent->fts_info == FTS_DP
- || ent->fts_info == FTS_D))
+ || ent->fts_info == FTS_D))
{
struct Active_dir *ad;
for (ad = hash_get_first (sp->fts_cycle.ht); ad != NULL;
- ad = hash_get_next (sp->fts_cycle.ht, ad))
- {
- find_matching_ancestor (ent, ad);
- }
+ ad = hash_get_next (sp->fts_cycle.ht, ad))
+ {
+ find_matching_ancestor (ent, ad);
+ }
}
}
{
struct stat sb1, sb2;
return (fstat (fd1, &sb1) == 0
- && fstat (fd2, &sb2) == 0
- && SAME_INODE (sb1, sb2));
+ && fstat (fd2, &sb2) == 0
+ && SAME_INODE (sb1, sb2));
}
static void
{
int fd = fd_ring->fts_fd_ring[i];
if (fd < 0)
- fprintf (stream, "%d: %d:\n", i, fd);
+ fprintf (stream, "%d: %d:\n", i, fd);
else
- {
- char *wd = getcwdat (fd, NULL, 0);
- fprintf (stream, "%d: %d: %s\n", i, fd, wd);
- free (wd);
- }
+ {
+ char *wd = getcwdat (fd, NULL, 0);
+ fprintf (stream, "%d: %d: %s\n", i, fd, wd);
+ free (wd);
+ }
if (i == fd_ring->fts_back)
- break;
+ break;
i = (i + I_RING_SIZE - 1) % I_RING_SIZE;
}
}
{
int fd = i_ring_pop (&fd_w);
if (0 <= fd)
- {
- int parent_fd = openat (cwd_fd, "..", O_RDONLY);
- if (parent_fd < 0)
- {
- // Warn?
- break;
- }
- if (!same_fd (fd, parent_fd))
- {
- char *cwd = getcwdat (fd, NULL, 0);
- error (0, errno, "ring : %s", cwd);
- char *c2 = getcwdat (parent_fd, NULL, 0);
- error (0, errno, "parent: %s", c2);
- free (cwd);
- free (c2);
- fts_assert (0);
- }
- close (cwd_fd);
- cwd_fd = parent_fd;
- }
+ {
+ int parent_fd = openat (cwd_fd, "..", O_RDONLY);
+ if (parent_fd < 0)
+ {
+ // Warn?
+ break;
+ }
+ if (!same_fd (fd, parent_fd))
+ {
+ char *cwd = getcwdat (fd, NULL, 0);
+ error (0, errno, "ring : %s", cwd);
+ char *c2 = getcwdat (parent_fd, NULL, 0);
+ error (0, errno, "parent: %s", c2);
+ free (cwd);
+ free (c2);
+ fts_assert (0);
+ }
+ close (cwd_fd);
+ cwd_fd = parent_fd;
+ }
}
close (cwd_fd);
}
internal_function
fts_stat(FTS *sp, register FTSENT *p, bool follow)
{
- struct stat *sbp = p->fts_statp;
- int saved_errno;
+ struct stat *sbp = p->fts_statp;
+ int saved_errno;
- if (p->fts_level == FTS_ROOTLEVEL && ISSET(FTS_COMFOLLOW))
- follow = true;
+ if (p->fts_level == FTS_ROOTLEVEL && ISSET(FTS_COMFOLLOW))
+ follow = true;
#if defined FTS_WHITEOUT && 0
- /* check for whiteout */
- if (p->fts_flags & FTS_ISW) {
- memset(sbp, '\0', sizeof (*sbp));
- sbp->st_mode = S_IFWHT;
- return (FTS_W);
+ /* check for whiteout */
+ if (p->fts_flags & FTS_ISW) {
+ memset(sbp, '\0', sizeof (*sbp));
+ sbp->st_mode = S_IFWHT;
+ return (FTS_W);
}
#endif
- /*
- * If doing a logical walk, or application requested FTS_FOLLOW, do
- * a stat(2). If that fails, check for a non-existent symlink. If
- * fail, set the errno from the stat call.
- */
- if (ISSET(FTS_LOGICAL) || follow) {
- if (stat(p->fts_accpath, sbp)) {
- saved_errno = errno;
- if (errno == ENOENT
- && lstat(p->fts_accpath, sbp) == 0) {
- __set_errno (0);
- return (FTS_SLNONE);
- }
- p->fts_errno = saved_errno;
- goto err;
- }
- } else if (fstatat(sp->fts_cwd_fd, p->fts_accpath, sbp,
- AT_SYMLINK_NOFOLLOW)) {
- p->fts_errno = errno;
-err: memset(sbp, 0, sizeof(struct stat));
- return (FTS_NS);
- }
-
- if (S_ISDIR(sbp->st_mode)) {
- p->fts_n_dirs_remaining = (sbp->st_nlink
- - (ISSET(FTS_SEEDOT) ? 0 : 2));
- if (ISDOT(p->fts_name)) {
- /* Command-line "." and ".." are real directories. */
- return (p->fts_level == FTS_ROOTLEVEL ? FTS_D : FTS_DOT);
- }
+ /*
+ * If doing a logical walk, or application requested FTS_FOLLOW, do
+ * a stat(2). If that fails, check for a non-existent symlink. If
+ * fail, set the errno from the stat call.
+ */
+ if (ISSET(FTS_LOGICAL) || follow) {
+ if (stat(p->fts_accpath, sbp)) {
+ saved_errno = errno;
+ if (errno == ENOENT
+ && lstat(p->fts_accpath, sbp) == 0) {
+ __set_errno (0);
+ return (FTS_SLNONE);
+ }
+ p->fts_errno = saved_errno;
+ goto err;
+ }
+ } else if (fstatat(sp->fts_cwd_fd, p->fts_accpath, sbp,
+ AT_SYMLINK_NOFOLLOW)) {
+ p->fts_errno = errno;
+err: memset(sbp, 0, sizeof(struct stat));
+ return (FTS_NS);
+ }
+
+ if (S_ISDIR(sbp->st_mode)) {
+ p->fts_n_dirs_remaining = (sbp->st_nlink
+ - (ISSET(FTS_SEEDOT) ? 0 : 2));
+ if (ISDOT(p->fts_name)) {
+ /* Command-line "." and ".." are real directories. */
+ return (p->fts_level == FTS_ROOTLEVEL ? FTS_D : FTS_DOT);
+ }
#if !GNULIB_FTS
- {
- /*
- * Cycle detection is done by brute force when the directory
- * is first encountered. If the tree gets deep enough or the
- * number of symbolic links to directories is high enough,
- * something faster might be worthwhile.
- */
- FTSENT *t;
-
- for (t = p->fts_parent;
- t->fts_level >= FTS_ROOTLEVEL; t = t->fts_parent)
- if (sbp->st_ino == t->fts_statp->st_ino
- && sbp->st_dev == t->fts_statp->st_dev)
- {
- p->fts_cycle = t;
- return (FTS_DC);
- }
- }
+ {
+ /*
+ * Cycle detection is done by brute force when the directory
+ * is first encountered. If the tree gets deep enough or the
+ * number of symbolic links to directories is high enough,
+ * something faster might be worthwhile.
+ */
+ FTSENT *t;
+
+ for (t = p->fts_parent;
+ t->fts_level >= FTS_ROOTLEVEL; t = t->fts_parent)
+ if (sbp->st_ino == t->fts_statp->st_ino
+ && sbp->st_dev == t->fts_statp->st_dev)
+ {
+ p->fts_cycle = t;
+ return (FTS_DC);
+ }
+ }
#endif
- return (FTS_D);
- }
- if (S_ISLNK(sbp->st_mode))
- return (FTS_SL);
- if (S_ISREG(sbp->st_mode))
- return (FTS_F);
- return (FTS_DEFAULT);
+ return (FTS_D);
+ }
+ if (S_ISLNK(sbp->st_mode))
+ return (FTS_SL);
+ if (S_ISREG(sbp->st_mode))
+ return (FTS_F);
+ return (FTS_DEFAULT);
}
static int
internal_function
fts_sort (FTS *sp, FTSENT *head, register size_t nitems)
{
- register FTSENT **ap, *p;
-
- /* On most modern hosts, void * and FTSENT ** have the same
- run-time representation, and one can convert sp->fts_compar to
- the type qsort expects without problem. Use the heuristic that
- this is OK if the two pointer types are the same size, and if
- converting FTSENT ** to long int is the same as converting
- FTSENT ** to void * and then to long int. This heuristic isn't
- valid in general but we don't know of any counterexamples. */
- FTSENT *dummy;
- int (*compare) (void const *, void const *) =
- ((sizeof &dummy == sizeof (void *)
- && (long int) &dummy == (long int) (void *) &dummy)
- ? (int (*) (void const *, void const *)) sp->fts_compar
- : fts_compar);
-
- /*
- * Construct an array of pointers to the structures and call qsort(3).
- * Reassemble the array in the order returned by qsort. If unable to
- * sort for memory reasons, return the directory entries in their
- * current order. Allocate enough space for the current needs plus
- * 40 so don't realloc one entry at a time.
- */
- if (nitems > sp->fts_nitems) {
- FTSENT **a;
-
- sp->fts_nitems = nitems + 40;
- if (SIZE_MAX / sizeof *a < sp->fts_nitems
- || ! (a = realloc (sp->fts_array,
- sp->fts_nitems * sizeof *a))) {
- free(sp->fts_array);
- sp->fts_array = NULL;
- sp->fts_nitems = 0;
- return (head);
- }
- sp->fts_array = a;
- }
- for (ap = sp->fts_array, p = head; p; p = p->fts_link)
- *ap++ = p;
- qsort((void *)sp->fts_array, nitems, sizeof(FTSENT *), compare);
- for (head = *(ap = sp->fts_array); --nitems; ++ap)
- ap[0]->fts_link = ap[1];
- ap[0]->fts_link = NULL;
- return (head);
+ register FTSENT **ap, *p;
+
+ /* On most modern hosts, void * and FTSENT ** have the same
+ run-time representation, and one can convert sp->fts_compar to
+ the type qsort expects without problem. Use the heuristic that
+ this is OK if the two pointer types are the same size, and if
+ converting FTSENT ** to long int is the same as converting
+ FTSENT ** to void * and then to long int. This heuristic isn't
+ valid in general but we don't know of any counterexamples. */
+ FTSENT *dummy;
+ int (*compare) (void const *, void const *) =
+ ((sizeof &dummy == sizeof (void *)
+ && (long int) &dummy == (long int) (void *) &dummy)
+ ? (int (*) (void const *, void const *)) sp->fts_compar
+ : fts_compar);
+
+ /*
+ * Construct an array of pointers to the structures and call qsort(3).
+ * Reassemble the array in the order returned by qsort. If unable to
+ * sort for memory reasons, return the directory entries in their
+ * current order. Allocate enough space for the current needs plus
+ * 40 so don't realloc one entry at a time.
+ */
+ if (nitems > sp->fts_nitems) {
+ FTSENT **a;
+
+ sp->fts_nitems = nitems + 40;
+ if (SIZE_MAX / sizeof *a < sp->fts_nitems
+ || ! (a = realloc (sp->fts_array,
+ sp->fts_nitems * sizeof *a))) {
+ free(sp->fts_array);
+ sp->fts_array = NULL;
+ sp->fts_nitems = 0;
+ return (head);
+ }
+ sp->fts_array = a;
+ }
+ for (ap = sp->fts_array, p = head; p; p = p->fts_link)
+ *ap++ = p;
+ qsort((void *)sp->fts_array, nitems, sizeof(FTSENT *), compare);
+ for (head = *(ap = sp->fts_array); --nitems; ++ap)
+ ap[0]->fts_link = ap[1];
+ ap[0]->fts_link = NULL;
+ return (head);
}
static FTSENT *
internal_function
fts_alloc (FTS *sp, const char *name, register size_t namelen)
{
- register FTSENT *p;
- size_t len;
-
- /*
- * The file name is a variable length array. Allocate the FTSENT
- * structure and the file name in one chunk.
- */
- len = sizeof(FTSENT) + namelen;
- if ((p = malloc(len)) == NULL)
- return (NULL);
-
- /* Copy the name and guarantee NUL termination. */
- memmove(p->fts_name, name, namelen);
- p->fts_name[namelen] = '\0';
-
- p->fts_namelen = namelen;
- p->fts_fts = sp;
- p->fts_path = sp->fts_path;
- p->fts_errno = 0;
- p->fts_flags = 0;
- p->fts_instr = FTS_NOINSTR;
- p->fts_number = 0;
- p->fts_pointer = NULL;
- return (p);
+ register FTSENT *p;
+ size_t len;
+
+ /*
+ * The file name is a variable length array. Allocate the FTSENT
+ * structure and the file name in one chunk.
+ */
+ len = sizeof(FTSENT) + namelen;
+ if ((p = malloc(len)) == NULL)
+ return (NULL);
+
+ /* Copy the name and guarantee NUL termination. */
+ memmove(p->fts_name, name, namelen);
+ p->fts_name[namelen] = '\0';
+
+ p->fts_namelen = namelen;
+ p->fts_fts = sp;
+ p->fts_path = sp->fts_path;
+ p->fts_errno = 0;
+ p->fts_flags = 0;
+ p->fts_instr = FTS_NOINSTR;
+ p->fts_number = 0;
+ p->fts_pointer = NULL;
+ return (p);
}
static void
internal_function
fts_lfree (register FTSENT *head)
{
- register FTSENT *p;
+ register FTSENT *p;
- /* Free a linked list of structures. */
- while ((p = head)) {
- head = head->fts_link;
- free(p);
- }
+ /* Free a linked list of structures. */
+ while ((p = head)) {
+ head = head->fts_link;
+ free(p);
+ }
}
/*
internal_function
fts_palloc (FTS *sp, size_t more)
{
- char *p;
- size_t new_len = sp->fts_pathlen + more + 256;
-
- /*
- * See if fts_pathlen would overflow.
- */
- if (new_len < sp->fts_pathlen) {
- free(sp->fts_path);
- sp->fts_path = NULL;
- __set_errno (ENAMETOOLONG);
- return false;
- }
- sp->fts_pathlen = new_len;
- p = realloc(sp->fts_path, sp->fts_pathlen);
- if (p == NULL) {
- free(sp->fts_path);
- sp->fts_path = NULL;
- return false;
- }
- sp->fts_path = p;
- return true;
+ char *p;
+ size_t new_len = sp->fts_pathlen + more + 256;
+
+ /*
+ * See if fts_pathlen would overflow.
+ */
+ if (new_len < sp->fts_pathlen) {
+ free(sp->fts_path);
+ sp->fts_path = NULL;
+ __set_errno (ENAMETOOLONG);
+ return false;
+ }
+ sp->fts_pathlen = new_len;
+ p = realloc(sp->fts_path, sp->fts_pathlen);
+ if (p == NULL) {
+ free(sp->fts_path);
+ sp->fts_path = NULL;
+ return false;
+ }
+ sp->fts_path = p;
+ return true;
}
/*
internal_function
fts_padjust (FTS *sp, FTSENT *head)
{
- FTSENT *p;
- char *addr = sp->fts_path;
-
-#define ADJUST(p) do { \
- if ((p)->fts_accpath != (p)->fts_name) { \
- (p)->fts_accpath = \
- (char *)addr + ((p)->fts_accpath - (p)->fts_path); \
- } \
- (p)->fts_path = addr; \
+ FTSENT *p;
+ char *addr = sp->fts_path;
+
+#define ADJUST(p) do { \
+ if ((p)->fts_accpath != (p)->fts_name) { \
+ (p)->fts_accpath = \
+ (char *)addr + ((p)->fts_accpath - (p)->fts_path); \
+ } \
+ (p)->fts_path = addr; \
} while (0)
- /* Adjust the current set of children. */
- for (p = sp->fts_child; p; p = p->fts_link)
- ADJUST(p);
-
- /* Adjust the rest of the tree, including the current level. */
- for (p = head; p->fts_level >= FTS_ROOTLEVEL;) {
- ADJUST(p);
- p = p->fts_link ? p->fts_link : p->fts_parent;
- }
+ /* Adjust the current set of children. */
+ for (p = sp->fts_child; p; p = p->fts_link)
+ ADJUST(p);
+
+ /* Adjust the rest of the tree, including the current level. */
+ for (p = head; p->fts_level >= FTS_ROOTLEVEL;) {
+ ADJUST(p);
+ p = p->fts_link ? p->fts_link : p->fts_parent;
+ }
}
static size_t
internal_function
fts_maxarglen (char * const *argv)
{
- size_t len, max;
+ size_t len, max;
- for (max = 0; *argv; ++argv)
- if ((len = strlen(*argv)) > max)
- max = len;
- return (max + 1);
+ for (max = 0; *argv; ++argv)
+ if ((len = strlen(*argv)) > max)
+ max = len;
+ return (max + 1);
}
/*
internal_function
fts_safe_changedir (FTS *sp, FTSENT *p, int fd, char const *dir)
{
- int ret;
- bool is_dotdot = dir && STREQ (dir, "..");
- int newfd;
-
- /* This clause handles the unusual case in which FTS_NOCHDIR
- is specified, along with FTS_CWDFD. In that case, there is
- no need to change even the virtual cwd file descriptor.
- However, if FD is non-negative, we do close it here. */
- if (ISSET (FTS_NOCHDIR))
- {
- if (ISSET (FTS_CWDFD) && 0 <= fd)
- close (fd);
- return 0;
- }
-
- if (fd < 0 && is_dotdot && ISSET (FTS_CWDFD))
- {
- /* When possible, skip the diropen and subsequent fstat+dev/ino
- comparison. I.e., when changing to parent directory
- (chdir ("..")), use a file descriptor from the ring and
- save the overhead of diropen+fstat, as well as avoiding
- failure when we lack "x" access to the virtual cwd. */
- if ( ! i_ring_empty (&sp->fts_fd_ring))
- {
- int parent_fd;
- fd_ring_print (sp, stderr, "pre-pop");
- parent_fd = i_ring_pop (&sp->fts_fd_ring);
- is_dotdot = true;
- if (0 <= parent_fd)
- {
- fd = parent_fd;
- dir = NULL;
- }
- }
- }
-
- newfd = fd;
- if (fd < 0 && (newfd = diropen (sp, dir)) < 0)
- return -1;
-
- /* The following dev/inode check is necessary if we're doing a
- `logical' traversal (through symlinks, a la chown -L), if the
- system lacks O_NOFOLLOW support, or if we're changing to ".."
- (but not via a popped file descriptor). When changing to the
- name "..", O_NOFOLLOW can't help. In general, when the target is
- not "..", diropen's use of O_NOFOLLOW ensures we don't mistakenly
- follow a symlink, so we can avoid the expense of this fstat. */
- if (ISSET(FTS_LOGICAL) || ! HAVE_WORKING_O_NOFOLLOW
- || (dir && STREQ (dir, "..")))
- {
- struct stat sb;
- if (fstat(newfd, &sb))
- {
- ret = -1;
- goto bail;
- }
- if (p->fts_statp->st_dev != sb.st_dev
- || p->fts_statp->st_ino != sb.st_ino)
- {
- __set_errno (ENOENT); /* disinformation */
- ret = -1;
- goto bail;
- }
- }
-
- if (ISSET(FTS_CWDFD))
- {
- cwd_advance_fd (sp, newfd, ! is_dotdot);
- return 0;
- }
-
- ret = fchdir(newfd);
+ int ret;
+ bool is_dotdot = dir && STREQ (dir, "..");
+ int newfd;
+
+ /* This clause handles the unusual case in which FTS_NOCHDIR
+ is specified, along with FTS_CWDFD. In that case, there is
+ no need to change even the virtual cwd file descriptor.
+ However, if FD is non-negative, we do close it here. */
+ if (ISSET (FTS_NOCHDIR))
+ {
+ if (ISSET (FTS_CWDFD) && 0 <= fd)
+ close (fd);
+ return 0;
+ }
+
+ if (fd < 0 && is_dotdot && ISSET (FTS_CWDFD))
+ {
+ /* When possible, skip the diropen and subsequent fstat+dev/ino
+ comparison. I.e., when changing to parent directory
+ (chdir ("..")), use a file descriptor from the ring and
+ save the overhead of diropen+fstat, as well as avoiding
+ failure when we lack "x" access to the virtual cwd. */
+ if ( ! i_ring_empty (&sp->fts_fd_ring))
+ {
+ int parent_fd;
+ fd_ring_print (sp, stderr, "pre-pop");
+ parent_fd = i_ring_pop (&sp->fts_fd_ring);
+ is_dotdot = true;
+ if (0 <= parent_fd)
+ {
+ fd = parent_fd;
+ dir = NULL;
+ }
+ }
+ }
+
+ newfd = fd;
+ if (fd < 0 && (newfd = diropen (sp, dir)) < 0)
+ return -1;
+
+ /* The following dev/inode check is necessary if we're doing a
+ `logical' traversal (through symlinks, a la chown -L), if the
+ system lacks O_NOFOLLOW support, or if we're changing to ".."
+ (but not via a popped file descriptor). When changing to the
+ name "..", O_NOFOLLOW can't help. In general, when the target is
+ not "..", diropen's use of O_NOFOLLOW ensures we don't mistakenly
+ follow a symlink, so we can avoid the expense of this fstat. */
+ if (ISSET(FTS_LOGICAL) || ! HAVE_WORKING_O_NOFOLLOW
+ || (dir && STREQ (dir, "..")))
+ {
+ struct stat sb;
+ if (fstat(newfd, &sb))
+ {
+ ret = -1;
+ goto bail;
+ }
+ if (p->fts_statp->st_dev != sb.st_dev
+ || p->fts_statp->st_ino != sb.st_ino)
+ {
+ __set_errno (ENOENT); /* disinformation */
+ ret = -1;
+ goto bail;
+ }
+ }
+
+ if (ISSET(FTS_CWDFD))
+ {
+ cwd_advance_fd (sp, newfd, ! is_dotdot);
+ return 0;
+ }
+
+ ret = fchdir(newfd);
bail:
- if (fd < 0)
- {
- int oerrno = errno;
- (void)close(newfd);
- __set_errno (oerrno);
- }
- return ret;
+ if (fd < 0)
+ {
+ int oerrno = errno;
+ (void)close(newfd);
+ __set_errno (oerrno);
+ }
+ return ret;
}