[{"id":3678847,"web_url":"http://patchwork.ozlabs.org/comment/3678847/","msgid":"<a4d6a525-ed41-48c1-a08d-c7ee981e9472@linaro.org>","list_archive_url":null,"date":"2026-04-17T17:31:13","subject":"Re: [PATCH 1/3] io: Consolidate fts implementation","submitter":{"id":66065,"url":"http://patchwork.ozlabs.org/api/people/66065/","name":"Adhemerval Zanella Netto","email":"adhemerval.zanella@linaro.org"},"content":"This is a mechanical patch and I install if no one opposes it.\n\nOn 17/04/26 10:24, Adhemerval Zanella wrote:\n> Remove wordsize-64 and arch-specific implementations, for ABIs when\n> off_t is the same as off64_t (__OFF_T_MATCHES_OFF64_T) the fts64.c\n> will create the requires aliases.\n> \n> The fts.c implementation is moved to fts-common.c to simplify\n> the __OFF_T_MATCHES_OFF64_T usage.\n> ---\n>  SHARED-FILES                                  |    2 +-\n>  io/fts-common.c                               | 2208 ++++++++++++++++\n>  io/fts.c                                      | 2217 +----------------\n>  io/fts64-time64.c                             |    2 +-\n>  io/fts64.c                                    |   38 +-\n>  sysdeps/unix/sysv/linux/mips/mips64/n64/fts.c |    1 -\n>  .../unix/sysv/linux/mips/mips64/n64/fts64.c   |    1 -\n>  sysdeps/unix/sysv/linux/x86_64/x32/fts.c      |    1 -\n>  sysdeps/unix/sysv/linux/x86_64/x32/fts64.c    |    1 -\n>  sysdeps/wordsize-64/fts.c                     |   19 -\n>  sysdeps/wordsize-64/fts64.c                   |    1 -\n>  11 files changed, 2258 insertions(+), 2233 deletions(-)\n>  create mode 100644 io/fts-common.c\n>  delete mode 100644 sysdeps/unix/sysv/linux/mips/mips64/n64/fts.c\n>  delete mode 100644 sysdeps/unix/sysv/linux/mips/mips64/n64/fts64.c\n>  delete mode 100644 sysdeps/unix/sysv/linux/x86_64/x32/fts.c\n>  delete mode 100644 sysdeps/unix/sysv/linux/x86_64/x32/fts64.c\n>  delete mode 100644 sysdeps/wordsize-64/fts.c\n>  delete mode 100644 sysdeps/wordsize-64/fts64.c\n> \n> diff --git a/SHARED-FILES b/SHARED-FILES\n> index 126aaf096d..645040d7e1 100644\n> --- a/SHARED-FILES\n> +++ b/SHARED-FILES\n> @@ -49,7 +49,7 @@ gnulib:\n>    io/cycle-check.h\n>    io/dev-ino.h\n>    io/fts-cycle.c\n> -  io/fts.c\n> +  io/fts-common.c\n>    io/i-ring.c\n>    io/same-inode.h\n>    locale/programs/3level.h\n> diff --git a/io/fts-common.c b/io/fts-common.c\n> new file mode 100644\n> index 0000000000..3825f0aeb4\n> --- /dev/null\n> +++ b/io/fts-common.c\n> @@ -0,0 +1,2208 @@\n> +/* Traverse a file hierarchy.\n> +\n> +   Copyright (C) 2004-2026 Free Software Foundation, Inc.\n> +\n> +   This file is free software: you can redistribute it and/or modify\n> +   it under the terms of the GNU Lesser General Public License as\n> +   published by the Free Software Foundation; either version 2.1 of the\n> +   License, or (at your option) any later version.\n> +\n> +   This file is distributed in the hope that it will be useful,\n> +   but WITHOUT ANY WARRANTY; without even the implied warranty of\n> +   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n> +   GNU Lesser General Public License for more details.\n> +\n> +   You should have received a copy of the GNU Lesser General Public License\n> +   along with this program.  If not, see <https://www.gnu.org/licenses/>.  */\n> +\n> +/*-\n> + * Copyright (c) 1990, 1993, 1994\n> + *      The Regents of the University of California.  All rights reserved.\n> + *\n> + * Redistribution and use in source and binary forms, with or without\n> + * modification, are permitted provided that the following conditions\n> + * are met:\n> + * 1. Redistributions of source code must retain the above copyright\n> + *    notice, this list of conditions and the following disclaimer.\n> + * 2. Redistributions in binary form must reproduce the above copyright\n> + *    notice, this list of conditions and the following disclaimer in the\n> + *    documentation and/or other materials provided with the distribution.\n> + * 4. Neither the name of the University nor the names of its contributors\n> + *    may be used to endorse or promote products derived from this software\n> + *    without specific prior written permission.\n> + *\n> + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS \"AS IS\" AND\n> + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE\n> + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE\n> + * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE\n> + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL\n> + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS\n> + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)\n> + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT\n> + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY\n> + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF\n> + * SUCH DAMAGE.\n> + */\n> +\n> +#include <config.h>\n> +\n> +#if defined LIBC_SCCS && !defined GCC_LINT && !defined lint\n> +static char sccsid[] = \"@(#)fts.c       8.6 (Berkeley) 8/14/94\";\n> +#endif\n> +\n> +#if _LIBC\n> +# include <fts.h>\n> +#else\n> +# include \"fts_.h\"\n> +#endif\n> +#if _LIBC || HAVE_SYS_PARAM_H\n> +# include <sys/param.h>\n> +#endif\n> +#include <sys/stat.h>\n> +#include <fcntl.h>\n> +#include <errno.h>\n> +#include <stddef.h>\n> +#include <stdint.h>\n> +#include <stdlib.h>\n> +#include <string.h>\n> +#include <unistd.h>\n> +\n> +/* Support for the LFS API version.  */\n> +#ifndef FTS_OPEN\n> +# define FTS_OPEN fts_open\n> +# define FTS_CLOSE fts_close\n> +# define FTS_READ fts_read\n> +# define FTS_SET fts_set\n> +# define FTS_CHILDREN fts_children\n> +# define FTSOBJ FTS\n> +# define FTSENTRY FTSENT\n> +# define INO_T ino_t\n> +# define STRUCT_STAT stat\n> +# define FSTAT __fstat\n> +# define FSTATAT __fstatat\n> +# define STRUCT_STATFS statfs\n> +# define FSTATFS __fstatfs\n> +#endif\n> +\n> +#if ! _LIBC\n> +# include \"attribute.h\"\n> +# include \"fcntl--.h\"\n> +# include \"openat.h\"\n> +# include \"opendirat.h\"\n> +# include \"same-inode.h\"\n> +# define OPENDIRAT opendirat\n> +# define FTSENT_WRAPPER(__p) __p\n> +# define FTS_COMPAR_CAST(__fn) __fn\n> +#else\n> +# include <stdbool.h>\n> +\n> +# define internal_function\n> +# define FALLTHROUGH               ; [[fallthrough]]\n> +# define HAVE_STRUCT_DIRENT_D_TYPE 1\n> +# define GNULIB_FTS_DEBUG          0\n> +# ifdef O_PATH\n> +#  define O_SEARCH                 O_PATH\n> +# else\n> +#  define O_SEARCH                 O_RDONLY\n> +# endif\n> +# define HAVE_SYS_VFS_H            1\n> +# define HAVE_FSTATFS              1\n> +# define HAVE_STRUCT_STATFS_F_TYPE 1\n> +# define HAVE_OPENAT               1\n> +# define HAVE_WORKING_O_NOFOLLOW   1\n> +# define _GL_CMP(a, b)             ((a) < (b) ? -1 : (a) > (b))\n> +# define OPENDIRAT                 __opendirat\n> +\n> +static inline bool openat_needs_fchdir (void)\n> +{\n> +  return false;\n> +}\n> +\n> +static inline bool streq (const char *s1, const char *s2)\n> +{\n> +  return strcmp (s1, s2) == 0;\n> +}\n> +# define reallocarray              __libc_reallocarray\n> +# define fchdir                    __fchdir\n> +# define close                     __close\n> +# define closedir                  __closedir\n> +# define fcntl                     __fcntl\n> +# define readdir                   __readdir\n> +# ifndef dirfd\n> +#  define dirfd                    __dirfd\n> +# endif\n> +# define open                      __open\n> +# define openat                    __openat\n> +\n> +# include \"cycle-check.c\"\n> +# include \"i-ring.c\"\n> +\n> +struct FTSENT_wrapper\n> +{\n> +  FTSOBJ *fts_fts;                /* the file hierarchy itself */\n> +  DIR *fts_dirp;                  /* Dir pointer for any directory containing\n> +\t\t\t\t     more entries than we read at one time.  */\n> +  struct STRUCT_STAT fts_stat;\n> +\n> +  FTSENTRY ent;\n> +};\n> +\n> +/* glibc historicaly defines the FTS::fts_compar as having 'void *', while the\n> +   fts_open has a function point using 'FTSENT **' as argument.  */\n> +# define FTS_COMPAR_CAST(__fn) ((int (*) (void const *, void const *))__fn)\n> +\n> +# define FTSENT_WRAPPER(p) \\\n> +    ((struct FTSENT_wrapper *) ((char *)(p) - offsetof(struct FTSENT_wrapper, ent)))\n> +#endif\n> +#define FTSENT_FTS(p)   (FTSENT_WRAPPER(p)->fts_fts)\n> +#define FTSENT_DIRP(p)  (FTSENT_WRAPPER(p)->fts_dirp)\n> +\n> +#include \"flexmember.h\"\n> +\n> +#include <dirent.h>\n> +#ifndef _D_EXACT_NAMLEN\n> +# define _D_EXACT_NAMLEN(dirent) strlen ((dirent)->d_name)\n> +#endif\n> +\n> +#if HAVE_STRUCT_DIRENT_D_TYPE\n> +/* True if the type of the directory entry D is known.  */\n> +# define DT_IS_KNOWN(d) ((d)->d_type != DT_UNKNOWN)\n> +/* True if the type of the directory entry D must be T.  */\n> +# define DT_MUST_BE(d, t) ((d)->d_type == (t))\n> +# define D_TYPE(d) ((d)->d_type)\n> +#else\n> +# define DT_IS_KNOWN(d) false\n> +# define DT_MUST_BE(d, t) false\n> +# define D_TYPE(d) DT_UNKNOWN\n> +\n> +# undef DT_UNKNOWN\n> +# define DT_UNKNOWN 0\n> +\n> +/* Any nonzero values will do here, so long as they're distinct.\n> +   Undef any existing macros out of the way.  */\n> +# undef DT_BLK\n> +# undef DT_CHR\n> +# undef DT_DIR\n> +# undef DT_FIFO\n> +# undef DT_LNK\n> +# undef DT_REG\n> +# undef DT_SOCK\n> +# define DT_BLK 1\n> +# define DT_CHR 2\n> +# define DT_DIR 3\n> +# define DT_FIFO 4\n> +# define DT_LNK 5\n> +# define DT_REG 6\n> +# define DT_SOCK 7\n> +#endif\n> +\n> +#ifndef S_IFBLK\n> +# define S_IFBLK 0\n> +#endif\n> +#ifndef S_IFLNK\n> +# define S_IFLNK 0\n> +#endif\n> +#ifndef S_IFSOCK\n> +# define S_IFSOCK 0\n> +#endif\n> +\n> +enum\n> +{\n> +  NOT_AN_INODE_NUMBER = 0\n> +};\n> +\n> +#ifdef D_INO_IN_DIRENT\n> +# define D_INO(dp) (dp)->d_ino\n> +#else\n> +/* Some systems don't have inodes, so fake them to avoid lots of ifdefs.  */\n> +# define D_INO(dp) NOT_AN_INODE_NUMBER\n> +#endif\n> +\n> +/* If possible (see max_entries, below), read no more than this many directory\n> +   entries at a time.  Without this limit (i.e., when using non-NULL\n> +   fts_compar), processing a directory with 4,000,000 entries requires ~1GiB\n> +   of memory, and handling 64M entries would require 16GiB of memory.  */\n> +#ifndef FTS_MAX_READDIR_ENTRIES\n> +# define FTS_MAX_READDIR_ENTRIES 100000\n> +#endif\n> +\n> +/* If there are more than this many entries in a directory,\n> +   and the conditions mentioned below are satisfied, then sort\n> +   the entries on inode number before any further processing.  */\n> +#ifndef FTS_INODE_SORT_DIR_ENTRIES_THRESHOLD\n> +# define FTS_INODE_SORT_DIR_ENTRIES_THRESHOLD 10000\n> +#endif\n> +\n> +enum\n> +{\n> +  _FTS_INODE_SORT_DIR_ENTRIES_THRESHOLD = FTS_INODE_SORT_DIR_ENTRIES_THRESHOLD\n> +};\n> +\n> +enum Fts_stat\n> +{\n> +  FTS_NO_STAT_REQUIRED = 1,\n> +  FTS_STAT_REQUIRED = 2\n> +};\n> +\n> +#ifndef __set_errno\n> +# define __set_errno(Val) errno = (Val)\n> +#endif\n> +\n> +/* If this host provides the openat function, then we can avoid\n> +   attempting to open \".\" in some initialization code below.  */\n> +#ifdef HAVE_OPENAT\n> +# define HAVE_OPENAT_SUPPORT 1\n> +#else\n> +# define HAVE_OPENAT_SUPPORT 0\n> +#endif\n> +\n> +#ifdef NDEBUG\n> +# define fts_assert(expr) ((void) (0 && (expr)))\n> +#else\n> +# define fts_assert(expr)       \\\n> +    do                          \\\n> +      {                         \\\n> +        if (!(expr))            \\\n> +          abort ();             \\\n> +      }                         \\\n> +    while (false)\n> +#endif\n> +\n> +static FTSENTRY  *fts_alloc (FTSOBJ *, const char *, size_t);\n> +static FTSENTRY  *fts_build (FTSOBJ *, int);\n> +static void      fts_lfree (FTSENTRY *);\n> +static void      fts_load (FTSOBJ *, FTSENTRY *);\n> +static size_t    fts_maxarglen (char * const *);\n> +static void      fts_padjust (FTSOBJ *, FTSENTRY *);\n> +static bool      fts_palloc (FTSOBJ *, size_t);\n> +static FTSENTRY  *fts_sort (FTSOBJ *, FTSENTRY *, size_t);\n> +static unsigned short int fts_stat (FTSOBJ *, FTSENTRY *, bool);\n> +static int      fts_safe_changedir (FTSOBJ *, FTSENTRY *, int, const char *);\n> +\n> +#include \"fts-cycle.c\"\n> +\n> +#ifndef MAX\n> +# define MAX(a,b) ((a) > (b) ? (a) : (b))\n> +#endif\n> +\n> +#ifndef SIZE_MAX\n> +# define SIZE_MAX ((size_t) -1)\n> +#endif\n> +\n> +#define ISDOT(a)        (a[0] == '.' && (!a[1] || (a[1] == '.' && !a[2])))\n> +\n> +#define CLR(opt)        (sp->fts_options &= ~(opt))\n> +#define ISSET(opt)      ((sp->fts_options & (opt)) != 0)\n> +#define SET(opt)        (sp->fts_options |= (opt))\n> +\n> +/* FIXME: FTS_NOCHDIR is now misnamed.\n> +   Call it FTS_USE_FULL_RELATIVE_FILE_NAMES instead. */\n> +#define FCHDIR(sp, fd)                                  \\\n> +  (!ISSET(FTS_NOCHDIR) && (ISSET(FTS_CWDFD)             \\\n> +                           ? (cwd_advance_fd ((sp), (fd), true), 0) \\\n> +                           : fchdir (fd)))\n> +\n> +\n> +/* fts_build flags */\n> +/* FIXME: make this an enum */\n> +#define BCHILD          1               /* fts_children */\n> +#define BNAMES          2               /* fts_children, names only */\n> +#define BREAD           3               /* fts_read */\n> +\n> +#if GNULIB_FTS_DEBUG\n> +# include <inttypes.h>\n> +# include <stdio.h>\n> +bool fts_debug = false;\n> +# define Dprintf(x) do { if (fts_debug) printf x; } while (false)\n> +static void fd_ring_check (FTSOBJ const *);\n> +static void fd_ring_print (FTSOBJ const *, FILE *, char const *);\n> +#else\n> +# define Dprintf(x)\n> +# define fd_ring_check(x)\n> +# define fd_ring_print(a, b, c)\n> +#endif\n> +\n> +#define LEAVE_DIR(Fts, Ent, Tag)                                \\\n> +  do                                                            \\\n> +    {                                                           \\\n> +      Dprintf ((\"  %s-leaving: %s\\n\", Tag, (Ent)->fts_path));   \\\n> +      leave_dir (Fts, Ent);                                     \\\n> +      fd_ring_check (Fts);                                      \\\n> +    }                                                           \\\n> +  while (false)\n> +\n> +static void\n> +fd_ring_clear (I_ring *fd_ring)\n> +{\n> +  while ( ! i_ring_empty (fd_ring))\n> +    {\n> +      int fd = i_ring_pop (fd_ring);\n> +      if (0 <= fd)\n> +        close (fd);\n> +    }\n> +}\n> +\n> +/* Overload the fts_statp->st_size member (otherwise unused, when\n> +   fts_info is FTS_NSOK) to indicate whether fts_read should stat\n> +   this entry or not.  */\n> +static void\n> +fts_set_stat_required (FTSENTRY *p, bool required)\n> +{\n> +  fts_assert (p->fts_info == FTS_NSOK);\n> +  p->fts_statp->st_size = (required\n> +                           ? FTS_STAT_REQUIRED\n> +                           : FTS_NO_STAT_REQUIRED);\n> +}\n> +\n> +/* Virtual fchdir.  Advance SP's working directory file descriptor,\n> +   SP->fts_cwd_fd, to FD, and push the previous value onto the fd_ring.\n> +   CHDIR_DOWN_ONE is true if FD corresponds to an entry in the directory\n> +   open on sp->fts_cwd_fd; i.e., to move the working directory one level\n> +   down.  */\n> +static void\n> +internal_function\n> +cwd_advance_fd (FTSOBJ *sp, int fd, bool chdir_down_one)\n> +{\n> +  int old = sp->fts_cwd_fd;\n> +  fts_assert (old != fd || old == AT_FDCWD);\n> +\n> +  if (chdir_down_one)\n> +    {\n> +      /* Push \"old\" onto the ring.\n> +         If the displaced file descriptor is non-negative, close it.  */\n> +      int prev_fd_in_slot = i_ring_push (&sp->fts_fd_ring, old);\n> +      fd_ring_print (sp, stderr, \"post-push\");\n> +      if (0 <= prev_fd_in_slot)\n> +        close (prev_fd_in_slot); /* ignore any close failure */\n> +    }\n> +  else if ( ! ISSET (FTS_NOCHDIR))\n> +    {\n> +      if (0 <= old)\n> +        close (old); /* ignore any close failure */\n> +    }\n> +\n> +  sp->fts_cwd_fd = fd;\n> +}\n> +\n> +/* Restore the initial, pre-traversal, \"working directory\".\n> +   In FTS_CWDFD mode, we merely call cwd_advance_fd, otherwise,\n> +   we may actually change the working directory.\n> +   Return 0 upon success. Upon failure, set errno and return nonzero.  */\n> +static int\n> +restore_initial_cwd (FTSOBJ *sp)\n> +{\n> +  int fail = FCHDIR (sp, ISSET (FTS_CWDFD) ? AT_FDCWD : sp->fts_rfd);\n> +  fd_ring_clear (&(sp->fts_fd_ring));\n> +  return fail;\n> +}\n> +\n> +/* Open the directory DIR if possible, and return a file\n> +   descriptor.  Return -1 and set errno on failure.  It doesn't matter\n> +   whether the file descriptor has read or write access.  */\n> +\n> +static int\n> +internal_function\n> +diropen (FTSOBJ const *sp, char const *dir)\n> +{\n> +  int open_flags = (O_SEARCH | O_CLOEXEC | O_DIRECTORY | O_NOCTTY | O_NONBLOCK\n> +                    | (ISSET (FTS_PHYSICAL) ? O_NOFOLLOW : 0));\n> +\n> +  int fd = (ISSET (FTS_CWDFD)\n> +            ? openat (sp->fts_cwd_fd, dir, open_flags)\n> +            : open (dir, open_flags));\n> +  return fd;\n> +}\n> +\n> +FTSOBJ *\n> +FTS_OPEN (char * const *argv,\n> +          register int options,\n> +          int (*compar) (const FTSENTRY **, const FTSENTRY **))\n> +{\n> +        /* Options check: glibc added other flags after FTS_NAMEONLY and\n> +\t   FTS_STOP, and they are assumed to be private.  */\n> +        if (options & ~FTS_OPTIONMASK\n> +#if _LIBC\n> +\t    || options & (FTS_NAMEONLY | FTS_STOP)\n> +#endif\n> +\t    ) {\n> +                __set_errno (EINVAL);\n> +                return (NULL);\n> +        }\n> +        if ((options & FTS_NOCHDIR) && (options & FTS_CWDFD)) {\n> +                __set_errno (EINVAL);\n> +                return (NULL);\n> +        }\n> +#if !_LIBC\n> +        if ( ! (options & (FTS_LOGICAL | FTS_PHYSICAL))) {\n> +                __set_errno (EINVAL);\n> +                return (NULL);\n> +        }\n> +#else\n> +\t/* glibc historically falls to FTS_PHYSICAL if no FTS_PHYSICAL or\n> +\t   FTS_LOGICAL is specified.  */\n> +        if (! (options & (FTS_PHYSICAL | FTS_LOGICAL)))\n> +\t  options |= FTS_PHYSICAL;\n> +#endif\n> +\n> +        /* Allocate/initialize the stream */\n> +        register FTSOBJ *sp = calloc (1, sizeof *sp);\n> +        if (sp == NULL)\n> +                return (NULL);\n> +        sp->fts_compar = FTS_COMPAR_CAST(compar);\n> +        sp->fts_options = options;\n> +\n> +        /* Logical walks turn on NOCHDIR; symbolic links are too hard. */\n> +        if (ISSET(FTS_LOGICAL)) {\n> +                SET(FTS_NOCHDIR);\n> +                CLR(FTS_CWDFD);\n> +        }\n> +\n> +        /* Initialize fts_cwd_fd.  */\n> +        sp->fts_cwd_fd = AT_FDCWD;\n> +        if ( ISSET(FTS_CWDFD) && ! HAVE_OPENAT_SUPPORT)\n> +          {\n> +            /* While it isn't technically necessary to open \".\" this\n> +               early, doing it here saves us the trouble of ensuring\n> +               later (where it'd be messier) that \".\" can in fact\n> +               be opened.  If not, revert to FTS_NOCHDIR mode.  */\n> +            int fd = open (\".\", O_SEARCH | O_CLOEXEC);\n> +            if (fd < 0)\n> +              {\n> +                /* Even if \".\" is unreadable, don't revert to FTS_NOCHDIR mode\n> +                   on systems like Linux+PROC_FS, where our openat emulation\n> +                   is good enough.  Note: on a system that emulates\n> +                   openat via /proc, this technique can still fail, but\n> +                   only in extreme conditions, e.g., when the working\n> +                   directory cannot be saved (i.e. save_cwd fails) --\n> +                   and that happens on Linux only when \".\" is unreadable\n> +                   and the CWD would be longer than PATH_MAX.\n> +                   FIXME: once Linux kernel openat support is well established,\n> +                   replace the above open call and this entire if/else block\n> +                   with the body of the if-block below.  */\n> +                if ( openat_needs_fchdir ())\n> +                  {\n> +                    SET(FTS_NOCHDIR);\n> +                    CLR(FTS_CWDFD);\n> +                  }\n> +              }\n> +            else\n> +              {\n> +                close (fd);\n> +              }\n> +          }\n> +\n> +        /*\n> +         * Start out with 1K of file name space, and enough, in any case,\n> +         * to hold the user's file names.\n> +         */\n> +#ifndef MAXPATHLEN\n> +# define MAXPATHLEN 1024\n> +#endif\n> +        {\n> +          size_t maxarglen = fts_maxarglen(argv);\n> +          if (! fts_palloc(sp, MAX(maxarglen, MAXPATHLEN)))\n> +                  goto mem1;\n> +        }\n> +\n> +        /* Allocate/initialize root's parent. */\n> +        FTSENTRY *parent = NULL;\n> +        if (*argv != NULL) {\n> +                if ((parent = fts_alloc(sp, \"\", 0)) == NULL)\n> +                        goto mem2;\n> +                parent->fts_level = FTS_ROOTPARENTLEVEL;\n> +          }\n> +\n> +        /* The classic fts implementation would call fts_stat with\n> +           a new entry for each iteration of the loop below.\n> +           If the comparison function is not specified or if the\n> +           FTS_DEFER_STAT option is in effect, don't stat any entry\n> +           in this loop.  This is an attempt to minimize the interval\n> +           between the initial stat/lstat/fstatat and the point at which\n> +           a directory argument is first opened.  This matters for any\n> +           directory command line argument that resides on a file system\n> +           without genuine i-nodes.  If you specify FTS_DEFER_STAT along\n> +           with a comparison function, that function must not access any\n> +           data via the fts_statp pointer.  */\n> +        bool defer_stat = (compar == NULL || ISSET(FTS_DEFER_STAT));\n> +\n> +        /* Allocate/initialize root(s). */\n> +        register FTSENTRY *root;\n> +        register size_t nitems;\n> +        FTSENTRY *tmp = NULL;     /* pacify gcc */\n> +        for (root = NULL, nitems = 0; *argv != NULL; ++argv, ++nitems) {\n> +                /* *Do* allow zero-length file names. */\n> +                size_t len = strlen(*argv);\n> +\n> +                if ( ! (options & FTS_VERBATIM))\n> +                  {\n> +                    /* If there are two or more trailing slashes, trim all but one,\n> +                       but don't change \"//\" to \"/\", and do map \"///\" to \"/\".  */\n> +                    char const *v = *argv;\n> +                    if (2 < len && v[len - 1] == '/')\n> +                      while (1 < len && v[len - 2] == '/')\n> +                        --len;\n> +                  }\n> +\n> +                register FTSENTRY *p = fts_alloc(sp, *argv, len);\n> +                if (p == NULL)\n> +                        goto mem3;\n> +                p->fts_level = FTS_ROOTLEVEL;\n> +                p->fts_parent = parent;\n> +                p->fts_accpath = p->fts_name;\n> +                /* Even when defer_stat is true, be sure to stat the first\n> +                   command line argument, since fts_read (at least with\n> +                   FTS_XDEV) requires that.  */\n> +                if (defer_stat && root != NULL) {\n> +                        p->fts_info = FTS_NSOK;\n> +                        fts_set_stat_required(p, true);\n> +                } else {\n> +                        p->fts_info = fts_stat(sp, p, false);\n> +                }\n> +\n> +                /*\n> +                 * If comparison routine supplied, traverse in sorted\n> +                 * order; otherwise traverse in the order specified.\n> +                 */\n> +                if (compar) {\n> +                        p->fts_link = root;\n> +                        root = p;\n> +                } else {\n> +                        p->fts_link = NULL;\n> +                        if (root == NULL)\n> +                                tmp = root = p;\n> +                        else {\n> +                                tmp->fts_link = p;\n> +                                tmp = p;\n> +                        }\n> +                }\n> +        }\n> +        if (compar && nitems > 1)\n> +                root = fts_sort(sp, root, nitems);\n> +\n> +        /*\n> +         * Allocate a dummy pointer and make fts_read think that we've just\n> +         * finished the node before the root(s); set p->fts_info to FTS_INIT\n> +         * so that everything about the \"current\" node is ignored.\n> +         */\n> +        if ((sp->fts_cur = fts_alloc(sp, \"\", 0)) == NULL)\n> +                goto mem3;\n> +        sp->fts_cur->fts_link = root;\n> +        sp->fts_cur->fts_info = FTS_INIT;\n> +        sp->fts_cur->fts_level = 1;\n> +        if (! setup_dir (sp))\n> +                goto mem3;\n> +\n> +        /*\n> +         * If using chdir(2), grab a file descriptor pointing to dot to ensure\n> +         * that we can get back here; this could be avoided for some file names,\n> +         * but almost certainly not worth the effort.  Slashes, symbolic links,\n> +         * and \"..\" are all fairly nasty problems.  Note, if we can't get the\n> +         * descriptor we run anyway, just more slowly.\n> +         */\n> +        if (!ISSET(FTS_NOCHDIR) && !ISSET(FTS_CWDFD)\n> +            && (sp->fts_rfd = diropen (sp, \".\")) < 0)\n> +                SET(FTS_NOCHDIR);\n> +\n> +        i_ring_init (&sp->fts_fd_ring, -1);\n> +        return (sp);\n> +\n> +mem3:   fts_lfree(root);\n> +        free(FTSENT_WRAPPER(parent));\n> +mem2:   free(sp->fts_path);\n> +mem1:   free(sp);\n> +        return (NULL);\n> +}\n> +\n> +static void\n> +internal_function\n> +fts_load (FTSOBJ *sp, register FTSENTRY *p)\n> +{\n> +        /*\n> +         * Load the stream structure for the next traversal.  Since we don't\n> +         * actually enter the directory until after the preorder visit, set\n> +         * the fts_accpath field specially so the chdir gets done to the right\n> +         * place and the user can access the first node.  From fts_open it's\n> +         * known that the file name will fit.\n> +         */\n> +        register size_t len = p->fts_pathlen = p->fts_namelen;\n> +        memmove(sp->fts_path, p->fts_name, len + 1);\n> +        register char *cp = strrchr(p->fts_name, '/');\n> +        if (cp && (cp != p->fts_name || cp[1])) {\n> +                len = strlen(++cp);\n> +                memmove(p->fts_name, cp, len + 1);\n> +                p->fts_namelen = len;\n> +        }\n> +        p->fts_accpath = p->fts_path = sp->fts_path;\n> +}\n> +\n> +int\n> +FTS_CLOSE (FTSOBJ *sp)\n> +{\n> +        /*\n> +         * This still works if we haven't read anything -- the dummy structure\n> +         * points to the root list, so we step through to the end of the root\n> +         * list which has a valid parent pointer.\n> +         */\n> +        if (sp->fts_cur) {\n> +                register FTSENTRY *p;\n> +                for (p = sp->fts_cur; p->fts_level >= FTS_ROOTLEVEL;) {\n> +                        register FTSENTRY *freep = p;\n> +                        p = p->fts_link != NULL ? p->fts_link : p->fts_parent;\n> +                        free(FTSENT_WRAPPER(freep));\n> +                }\n> +                free(FTSENT_WRAPPER(p));\n> +        }\n> +\n> +        /* Free up child linked list, sort array, file name buffer. */\n> +        if (sp->fts_child)\n> +                fts_lfree(sp->fts_child);\n> +        free(sp->fts_array);\n> +        free(sp->fts_path);\n> +\n> +        int saved_errno = 0;\n> +        if (ISSET(FTS_CWDFD))\n> +          {\n> +            if (0 <= sp->fts_cwd_fd)\n> +              if (close (sp->fts_cwd_fd))\n> +                saved_errno = errno;\n> +          }\n> +        else if (!ISSET(FTS_NOCHDIR))\n> +          {\n> +            /* Return to original directory, save errno if necessary. */\n> +            if (fchdir(sp->fts_rfd))\n> +              saved_errno = errno;\n> +\n> +            /* If close fails, record errno only if saved_errno is zero,\n> +               so that we report the probably-more-meaningful fchdir errno.  */\n> +            if (close (sp->fts_rfd))\n> +              if (saved_errno == 0)\n> +                saved_errno = errno;\n> +          }\n> +\n> +        fd_ring_clear (&sp->fts_fd_ring);\n> +\n> +        if (sp->fts_leaf_optimization_works_ht)\n> +          hash_free (sp->fts_leaf_optimization_works_ht);\n> +\n> +        free_dir (sp);\n> +\n> +        /* Free up the stream pointer. */\n> +        free(sp);\n> +\n> +        /* Set errno and return. */\n> +        if (saved_errno) {\n> +                __set_errno (saved_errno);\n> +                return (-1);\n> +        }\n> +\n> +        return (0);\n> +}\n> +\n> +/* Minimum link count of a traditional Unix directory.  When leaf\n> +   optimization is OK and a directory's st_nlink == MIN_DIR_NLINK,\n> +   then the directory has no subdirectories.  */\n> +enum { MIN_DIR_NLINK = 2 };\n> +\n> +/* Whether leaf optimization is OK for a directory.  */\n> +enum leaf_optimization\n> +  {\n> +    /* st_nlink is not reliable for this directory's subdirectories.  */\n> +    NO_LEAF_OPTIMIZATION,\n> +\n> +    /* st_nlink == 2 means the directory lacks subdirectories.  */\n> +    OK_LEAF_OPTIMIZATION\n> +  };\n> +\n> +#if (defined __linux__ || defined __ANDROID__) \\\n> +  && HAVE_SYS_VFS_H && HAVE_FSTATFS && HAVE_STRUCT_STATFS_F_TYPE\n> +\n> +# include <sys/vfs.h>\n> +\n> +/* Linux-specific constants from coreutils' src/fs.h */\n> +# define S_MAGIC_AFS 0x5346414F\n> +# define S_MAGIC_CIFS 0xFF534D42\n> +# define S_MAGIC_LUSTRE 0x0BD00BD0\n> +# define S_MAGIC_NFS 0x6969\n> +# define S_MAGIC_PROC 0x9FA0\n> +# define S_MAGIC_TMPFS 0x1021994\n> +\n> +# ifdef HAVE___FSWORD_T\n> +typedef __fsword_t fsword;\n> +# else\n> +typedef long int fsword;\n> +# endif\n> +\n> +/* Map a stat.st_dev number to a file system type number f_ftype.  */\n> +struct dev_type\n> +{\n> +  dev_t st_dev;\n> +  fsword f_type;\n> +};\n> +\n> +/* Use a tiny initial size.  If a traversal encounters more than\n> +   a few devices, the cost of growing/rehashing this table will be\n> +   rendered negligible by the number of inodes processed.  */\n> +enum { DEV_TYPE_HT_INITIAL_SIZE = 13 };\n> +\n> +static size_t\n> +dev_type_hash (void const *x, size_t table_size)\n> +{\n> +  struct dev_type const *ax = x;\n> +  uintmax_t dev = ax->st_dev;\n> +  return dev % table_size;\n> +}\n> +\n> +static bool\n> +dev_type_compare (void const *x, void const *y)\n> +{\n> +  struct dev_type const *ax = x;\n> +  struct dev_type const *ay = y;\n> +  return ax->st_dev == ay->st_dev;\n> +}\n> +\n> +/* Return the file system type of P with file descriptor FD, or 0 if not known.\n> +   If FD is negative, P's file descriptor is unavailable.\n> +   Try to cache known values.  */\n> +\n> +static fsword\n> +filesystem_type (FTSENTRY const *p, int fd)\n> +{\n> +  FTSOBJ *sp = FTSENT_FTS(p);\n> +\n> +  /* If we're not in CWDFD mode, don't bother with this optimization,\n> +     since the caller is not serious about performance.  */\n> +  if (!ISSET (FTS_CWDFD))\n> +    return 0;\n> +\n> +  Hash_table *h = sp->fts_leaf_optimization_works_ht;\n> +  if (! h)\n> +    h = sp->fts_leaf_optimization_works_ht\n> +      = hash_initialize (DEV_TYPE_HT_INITIAL_SIZE, NULL, dev_type_hash,\n> +                         dev_type_compare, free);\n> +\n> +  if (h)\n> +    {\n> +      struct dev_type tmp;\n> +      tmp.st_dev = p->fts_statp->st_dev;\n> +      struct dev_type *ent = hash_lookup (h, &tmp);\n> +      if (ent)\n> +        return ent->f_type;\n> +    }\n> +\n> +  /* Look-up failed.  Query directly and cache the result.  */\n> +  struct STRUCT_STATFS fs_buf;\n> +  if (fd < 0 || FSTATFS (fd, &fs_buf) != 0)\n> +    return 0;\n> +\n> +  if (h)\n> +    {\n> +      struct dev_type *t2 = malloc (sizeof *t2);\n> +      if (t2)\n> +        {\n> +          t2->st_dev = p->fts_statp->st_dev;\n> +          t2->f_type = fs_buf.f_type;\n> +\n> +          struct dev_type *ent = hash_insert (h, t2);\n> +          if (ent)\n> +            fts_assert (ent == t2);\n> +          else\n> +            free (t2);\n> +        }\n> +    }\n> +\n> +  return fs_buf.f_type;\n> +}\n> +\n> +/* Return true if sorting dirents on inode numbers is known to improve\n> +   traversal performance for the directory P with descriptor DIR_FD.\n> +   Return false otherwise.  When in doubt, return true.\n> +   DIR_FD is negative if unavailable.  */\n> +static bool\n> +dirent_inode_sort_may_be_useful (FTSENTRY const *p, int dir_fd)\n> +{\n> +  /* Skip the sort only if we can determine efficiently\n> +     that skipping it is the right thing to do.\n> +     The cost of performing an unnecessary sort is negligible,\n> +     while the cost of *not* performing it can be O(N^2) with\n> +     a very large constant.  */\n> +\n> +  switch (filesystem_type (p, dir_fd))\n> +    {\n> +    case S_MAGIC_LUSTRE:\n> +      /* On Lustre, sorting directory entries interferes with its ability to\n> +         prefetch file metadata (via statahead).  This would make a command\n> +         like 'du' around 9 times slower.  See\n> +         <https://bugs.gnu.org/80106>.  */\n> +    case S_MAGIC_CIFS:\n> +    case S_MAGIC_NFS:\n> +    case S_MAGIC_TMPFS:\n> +      /* On a file system of any of these types, sorting\n> +         is unnecessary, and hence wasteful.  */\n> +      return false;\n> +\n> +    default:\n> +      return true;\n> +    }\n> +}\n> +\n> +/* Given an FTS entry P for a directory with descriptor DIR_FD,\n> +   return whether it is valid to apply leaf optimization.\n> +   The optimization is valid if a directory's st_nlink value equal\n> +   to MIN_DIR_NLINK means the directory has no subdirectories.\n> +   DIR_FD is negative if unavailable.  */\n> +static enum leaf_optimization\n> +leaf_optimization (FTSENTRY const *p, int dir_fd)\n> +{\n> +  switch (filesystem_type (p, dir_fd))\n> +    {\n> +    case 0:\n> +      /* Leaf optimization is unsafe if the file system type is unknown.  */\n> +      FALLTHROUGH;\n> +    case S_MAGIC_AFS:\n> +      /* Although AFS mount points are not counted in st_nlink, they\n> +         act like directories.  See <https://bugs.debian.org/143111>.  */\n> +      FALLTHROUGH;\n> +    case S_MAGIC_CIFS:\n> +      /* Leaf optimization causes 'find' to abort.  See\n> +         <https://lists.gnu.org/r/bug-gnulib/2018-04/msg00015.html>.  */\n> +      FALLTHROUGH;\n> +    case S_MAGIC_NFS:\n> +      /* NFS provides usable dirent.d_type but not necessarily for all entries\n> +         of large directories, so as per <https://bugzilla.redhat.com/1252549>\n> +         NFS should return true.  However st_nlink values are not accurate on\n> +         all implementations as per <https://bugzilla.redhat.com/1299169>.  */\n> +      FALLTHROUGH;\n> +    case S_MAGIC_PROC:\n> +      /* Per <https://bugs.debian.org/cgi-bin/bugreport.cgi?bug=143111> /proc\n> +         may have bogus stat.st_nlink values.  */\n> +      return NO_LEAF_OPTIMIZATION;\n> +\n> +    default:\n> +      return OK_LEAF_OPTIMIZATION;\n> +    }\n> +}\n> +\n> +#else\n> +static bool\n> +dirent_inode_sort_may_be_useful (_GL_UNUSED FTSENTRY const *p,\n> +                                 _GL_UNUSED int dir_fd)\n> +{\n> +  return true;\n> +}\n> +static enum leaf_optimization\n> +leaf_optimization (_GL_UNUSED FTSENTRY const *p, _GL_UNUSED int dir_fd)\n> +{\n> +  return NO_LEAF_OPTIMIZATION;\n> +}\n> +#endif\n> +\n> +/*\n> + * Special case of \"/\" at the end of the file name so that slashes aren't\n> + * appended which would cause file names to be written as \"....//foo\".\n> + */\n> +#define NAPPEND(p)                                                      \\\n> +        (p->fts_path[p->fts_pathlen - 1] == '/'                         \\\n> +            ? p->fts_pathlen - 1 : p->fts_pathlen)\n> +\n> +FTSENTRY *\n> +FTS_READ (FTSOBJ *sp)\n> +{\n> +        /* If finished or unrecoverable error, return NULL. */\n> +        if (sp->fts_cur == NULL || ISSET(FTS_STOP))\n> +                return (NULL);\n> +\n> +        /* Set current node pointer. */\n> +        register FTSENTRY *p = sp->fts_cur;\n> +\n> +        /* Save and zero out user instructions. */\n> +        register unsigned short int instr = p->fts_instr;\n> +        p->fts_instr = FTS_NOINSTR;\n> +\n> +        /* Any type of file may be re-visited; re-stat and re-turn. */\n> +        if (instr == FTS_AGAIN) {\n> +                p->fts_info = fts_stat(sp, p, false);\n> +                return (p);\n> +        }\n> +        Dprintf ((\"fts_read: p=%s\\n\",\n> +                  p->fts_info == FTS_INIT ? \"\" : p->fts_path));\n> +\n> +        /*\n> +         * Following a symlink -- SLNONE test allows application to see\n> +         * SLNONE and recover.  If indirecting through a symlink, have\n> +         * keep a pointer to current location.  If unable to get that\n> +         * pointer, follow fails.\n> +         */\n> +        if (instr == FTS_FOLLOW &&\n> +            (p->fts_info == FTS_SL || p->fts_info == FTS_SLNONE)) {\n> +                p->fts_info = fts_stat(sp, p, true);\n> +                if (p->fts_info == FTS_D && !ISSET(FTS_NOCHDIR)) {\n> +                        if ((p->fts_symfd = diropen (sp, \".\")) < 0) {\n> +                                p->fts_errno = errno;\n> +                                p->fts_info = FTS_ERR;\n> +                        } else\n> +                                p->fts_flags |= FTS_SYMFOLLOW;\n> +                }\n> +                goto check_for_dir;\n> +        }\n> +\n> +        /* Directory in pre-order. */\n> +        if (p->fts_info == FTS_D) {\n> +                /* If skipped or crossed mount point, do post-order visit. */\n> +                if (instr == FTS_SKIP ||\n> +                    (ISSET(FTS_XDEV) && p->fts_statp->st_dev != sp->fts_dev)) {\n> +                        if (p->fts_flags & FTS_SYMFOLLOW)\n> +                                (void)close(p->fts_symfd);\n> +                        if (sp->fts_child) {\n> +                                fts_lfree(sp->fts_child);\n> +                                sp->fts_child = NULL;\n> +                        }\n> +                        p->fts_info = FTS_DP;\n> +                        LEAVE_DIR (sp, p, \"1\");\n> +                        return (p);\n> +                }\n> +\n> +                /* Rebuild if only read the names and now traversing. */\n> +                if (sp->fts_child != NULL && ISSET(FTS_NAMEONLY)) {\n> +                        CLR(FTS_NAMEONLY);\n> +                        fts_lfree(sp->fts_child);\n> +                        sp->fts_child = NULL;\n> +                }\n> +\n> +                /*\n> +                 * Cd to the subdirectory.\n> +                 *\n> +                 * If have already read and now fail to chdir, whack the list\n> +                 * to make the names come out right, and set the parent errno\n> +                 * so the application will eventually get an error condition.\n> +                 * Set the FTS_DONTCHDIR flag so that when we logically change\n> +                 * directories back to the parent we don't do a chdir.\n> +                 *\n> +                 * If haven't read do so.  If the read fails, fts_build sets\n> +                 * FTS_STOP or the fts_info field of the node.\n> +                 */\n> +                if (sp->fts_child != NULL) {\n> +                        if (fts_safe_changedir(sp, p, -1, p->fts_accpath)) {\n> +                                p->fts_errno = errno;\n> +                                p->fts_flags |= FTS_DONTCHDIR;\n> +                                for (p = sp->fts_child; p != NULL;\n> +                                     p = p->fts_link)\n> +                                        p->fts_accpath =\n> +                                            p->fts_parent->fts_accpath;\n> +                        }\n> +                } else if ((sp->fts_child = fts_build(sp, BREAD)) == NULL) {\n> +                        if (ISSET(FTS_STOP))\n> +                                return (NULL);\n> +                        /* If fts_build's call to fts_safe_changedir failed\n> +                           because it was not able to fchdir into a\n> +                           subdirectory, tell the caller.  */\n> +                        if (p->fts_errno && p->fts_info != FTS_DNR)\n> +                                p->fts_info = FTS_ERR;\n> +                        LEAVE_DIR (sp, p, \"2\");\n> +                        return (p);\n> +                }\n> +                p = sp->fts_child;\n> +                sp->fts_child = NULL;\n> +                goto name;\n> +        }\n> +\n> +        /* Move to the next node on this level. */\n> +next: ;\n> +        register FTSENTRY *tmp = p;\n> +\n> +        /* If we have so many directory entries that we're reading them\n> +           in batches, and we've reached the end of the current batch,\n> +           read in a new batch.  */\n> +        if (p->fts_link == NULL && FTSENT_DIRP(p->fts_parent))\n> +          {\n> +            p = tmp->fts_parent;\n> +            sp->fts_cur = p;\n> +            sp->fts_path[p->fts_pathlen] = '\\0';\n> +\n> +            if ((p = fts_build (sp, BREAD)) == NULL)\n> +              {\n> +                if (ISSET(FTS_STOP))\n> +                  return NULL;\n> +                goto cd_dot_dot;\n> +              }\n> +\n> +            free(FTSENT_WRAPPER(tmp));\n> +            goto name;\n> +          }\n> +\n> +        if ((p = p->fts_link) != NULL) {\n> +                sp->fts_cur = p;\n> +                free(FTSENT_WRAPPER(tmp));\n> +\n> +                /*\n> +                 * If reached the top, return to the original directory (or\n> +                 * the root of the tree), and load the file names for the next\n> +                 * root.\n> +                 */\n> +                if (p->fts_level == FTS_ROOTLEVEL) {\n> +                        if (restore_initial_cwd(sp)) {\n> +                                SET(FTS_STOP);\n> +                                return (NULL);\n> +                        }\n> +                        free_dir(sp);\n> +                        fts_load(sp, p);\n> +                        if (! setup_dir(sp)) {\n> +                                free_dir(sp);\n> +                                return (NULL);\n> +                        }\n> +                        goto check_for_dir;\n> +                }\n> +\n> +                /*\n> +                 * User may have called fts_set on the node.  If skipped,\n> +                 * ignore.  If followed, get a file descriptor so we can\n> +                 * get back if necessary.\n> +                 */\n> +                if (p->fts_instr == FTS_SKIP)\n> +                        goto next;\n> +                if (p->fts_instr == FTS_FOLLOW) {\n> +                        p->fts_info = fts_stat(sp, p, true);\n> +                        if (p->fts_info == FTS_D && !ISSET(FTS_NOCHDIR)) {\n> +                                if ((p->fts_symfd = diropen (sp, \".\")) < 0) {\n> +                                        p->fts_errno = errno;\n> +                                        p->fts_info = FTS_ERR;\n> +                                } else\n> +                                        p->fts_flags |= FTS_SYMFOLLOW;\n> +                        }\n> +                        p->fts_instr = FTS_NOINSTR;\n> +                }\n> +\n> +name:           {\n> +                  register char *t = sp->fts_path + NAPPEND(p->fts_parent);\n> +                  *t++ = '/';\n> +                  memmove(t, p->fts_name, p->fts_namelen + 1);\n> +                }\n> +check_for_dir:\n> +                sp->fts_cur = p;\n> +                if (p->fts_info == FTS_NSOK)\n> +                  {\n> +                    if (p->fts_statp->st_size == FTS_STAT_REQUIRED)\n> +                      p->fts_info = fts_stat(sp, p, false);\n> +                    else\n> +                      fts_assert (p->fts_statp->st_size == FTS_NO_STAT_REQUIRED);\n> +                  }\n> +\n> +                /* Skip files with different device numbers when FTS_MOUNT\n> +                   is set.  */\n> +                if (ISSET (FTS_MOUNT) && p->fts_info != FTS_NS &&\n> +                    p->fts_level != FTS_ROOTLEVEL &&\n> +                    p->fts_statp->st_dev != sp->fts_dev)\n> +                      goto next;\n> +\n> +                if (p->fts_info == FTS_D)\n> +                  {\n> +                    /* Now that P->fts_statp is guaranteed to be valid, if\n> +                       this is a command-line directory, record its device\n> +                       number, to be used for FTS_MOUNT and FTS_XDEV.  */\n> +                    if (p->fts_level == FTS_ROOTLEVEL)\n> +                      sp->fts_dev = p->fts_statp->st_dev;\n> +                    Dprintf ((\"  entering: %s\\n\", p->fts_path));\n> +                    if (! enter_dir (sp, p))\n> +                      return NULL;\n> +                  }\n> +                return p;\n> +        }\n> +cd_dot_dot:\n> +\n> +        /* Move up to the parent node. */\n> +        p = tmp->fts_parent;\n> +        sp->fts_cur = p;\n> +        free(FTSENT_WRAPPER(tmp));\n> +\n> +        if (p->fts_level == FTS_ROOTPARENTLEVEL) {\n> +                /*\n> +                 * Done; free everything up and set errno to 0 so the user\n> +                 * can distinguish between error and EOF.\n> +                 */\n> +                free(FTSENT_WRAPPER(p));\n> +                __set_errno (0);\n> +                return (sp->fts_cur = NULL);\n> +        }\n> +\n> +        fts_assert (p->fts_info != FTS_NSOK);\n> +\n> +        /* NUL terminate the file name.  */\n> +        sp->fts_path[p->fts_pathlen] = '\\0';\n> +\n> +        /*\n> +         * Return to the parent directory.  If at a root node, restore\n> +         * the initial working directory.  If we came through a symlink,\n> +         * go back through the file descriptor.  Otherwise, move up\n> +         * one level, via \"..\".\n> +         */\n> +        if (p->fts_level == FTS_ROOTLEVEL) {\n> +                if (restore_initial_cwd(sp)) {\n> +                        p->fts_errno = errno;\n> +                        SET(FTS_STOP);\n> +                }\n> +        } else if (p->fts_flags & FTS_SYMFOLLOW) {\n> +                if (FCHDIR(sp, p->fts_symfd)) {\n> +                        p->fts_errno = errno;\n> +                        SET(FTS_STOP);\n> +                }\n> +                (void)close(p->fts_symfd);\n> +        } else if (!(p->fts_flags & FTS_DONTCHDIR) &&\n> +                   fts_safe_changedir(sp, p->fts_parent, -1, \"..\")) {\n> +                p->fts_errno = errno;\n> +                SET(FTS_STOP);\n> +        }\n> +\n> +        /* If the directory causes a cycle, preserve the FTS_DC flag and keep\n> +           the corresponding dev/ino pair in the hash table.  It is going to be\n> +           removed when leaving the original directory.  */\n> +        if (p->fts_info != FTS_DC) {\n> +                p->fts_info = p->fts_errno ? FTS_ERR : FTS_DP;\n> +                if (p->fts_errno == 0)\n> +                        LEAVE_DIR (sp, p, \"3\");\n> +        }\n> +        return ISSET(FTS_STOP) ? NULL : p;\n> +}\n> +\n> +/*\n> + * Fts_set takes the stream as an argument although it's not used in this\n> + * implementation; it would be necessary if anyone wanted to add global\n> + * semantics to fts using fts_set.  An error return is allowed for similar\n> + * reasons.\n> + */\n> +/* ARGSUSED */\n> +int\n> +FTS_SET (_GL_UNUSED FTSOBJ *sp, FTSENTRY *p, int instr)\n> +{\n> +        if (instr != 0 && instr != FTS_AGAIN && instr != FTS_FOLLOW &&\n> +            instr != FTS_NOINSTR && instr != FTS_SKIP) {\n> +                __set_errno (EINVAL);\n> +                return (1);\n> +        }\n> +        p->fts_instr = instr;\n> +        return (0);\n> +}\n> +\n> +FTSENTRY *\n> +FTS_CHILDREN (FTSOBJ *sp, int instr)\n> +{\n> +        if (instr != 0 && instr != FTS_NAMEONLY) {\n> +                __set_errno (EINVAL);\n> +                return (NULL);\n> +        }\n> +\n> +        /* Set current node pointer. */\n> +        register FTSENTRY *p = sp->fts_cur;\n> +\n> +        /*\n> +         * Errno set to 0 so user can distinguish empty directory from\n> +         * an error.\n> +         */\n> +        __set_errno (0);\n> +\n> +        /* Fatal errors stop here. */\n> +        if (ISSET(FTS_STOP))\n> +                return (NULL);\n> +\n> +        /* Return logical hierarchy of user's arguments. */\n> +        if (p->fts_info == FTS_INIT)\n> +                return (p->fts_link);\n> +\n> +        /*\n> +         * If not a directory being visited in pre-order, stop here.  Could\n> +         * allow FTS_DNR, assuming the user has fixed the problem, but the\n> +         * same effect is available with FTS_AGAIN.\n> +         */\n> +        if (p->fts_info != FTS_D /* && p->fts_info != FTS_DNR */)\n> +                return (NULL);\n> +\n> +        /* Free up any previous child list. */\n> +        if (sp->fts_child != NULL)\n> +                fts_lfree(sp->fts_child);\n> +\n> +        if (instr == FTS_NAMEONLY) {\n> +                SET(FTS_NAMEONLY);\n> +                instr = BNAMES;\n> +        } else\n> +                instr = BCHILD;\n> +\n> +        /*\n> +         * If using chdir on a relative file name and called BEFORE fts_read\n> +         * does its chdir to the root of a traversal, we can lose -- we need to\n> +         * chdir into the subdirectory, and we don't know where the current\n> +         * directory is, so we can't get back so that the upcoming chdir by\n> +         * fts_read will work.\n> +         */\n> +        if (p->fts_level != FTS_ROOTLEVEL || p->fts_accpath[0] == '/' ||\n> +            ISSET(FTS_NOCHDIR))\n> +                return (sp->fts_child = fts_build(sp, instr));\n> +\n> +        int fd = diropen (sp, \".\");\n> +        if (fd < 0)\n> +                return (sp->fts_child = NULL);\n> +        sp->fts_child = fts_build(sp, instr);\n> +        if (ISSET(FTS_CWDFD))\n> +          {\n> +            cwd_advance_fd (sp, fd, true);\n> +          }\n> +        else\n> +          {\n> +            if (fchdir(fd))\n> +              {\n> +                int saved_errno = errno;\n> +                close (fd);\n> +                __set_errno (saved_errno);\n> +                return NULL;\n> +              }\n> +            close (fd);\n> +          }\n> +        return (sp->fts_child);\n> +}\n> +\n> +/* A comparison function to sort on increasing inode number.\n> +   For some file system types, sorting either way makes a huge\n> +   performance difference for a directory with very many entries,\n> +   but sorting on increasing values is slightly better than sorting\n> +   on decreasing values.  The difference is in the 5% range.  */\n> +static int\n> +fts_compare_ino (FTSENTRY const **a, FTSENTRY const **b)\n> +{\n> +  return _GL_CMP (a[0]->fts_statp->st_ino, b[0]->fts_statp->st_ino);\n> +}\n> +\n> +/* Map the dirent.d_type value, DTYPE, to the corresponding stat.st_mode\n> +   S_IF* bit and set ST.st_mode, thus clearing all other bits in that field.  */\n> +static void\n> +set_stat_type (struct STRUCT_STAT *st, unsigned int dtype)\n> +{\n> +  mode_t type;\n> +  switch (dtype)\n> +    {\n> +    case DT_BLK:\n> +      type = S_IFBLK;\n> +      break;\n> +    case DT_CHR:\n> +      type = S_IFCHR;\n> +      break;\n> +    case DT_DIR:\n> +      type = S_IFDIR;\n> +      break;\n> +    case DT_FIFO:\n> +      type = S_IFIFO;\n> +      break;\n> +    case DT_LNK:\n> +      type = S_IFLNK;\n> +      break;\n> +    case DT_REG:\n> +      type = S_IFREG;\n> +      break;\n> +    case DT_SOCK:\n> +      type = S_IFSOCK;\n> +      break;\n> +    default:\n> +      type = 0;\n> +    }\n> +  st->st_mode = type;\n> +}\n> +\n> +#define closedir_and_clear(dirp)                \\\n> +  do                                            \\\n> +    {                                           \\\n> +      closedir (dirp);                          \\\n> +      dirp = NULL;                              \\\n> +    }                                           \\\n> +  while (0)\n> +\n> +#define fts_opendir(file, Pdir_fd)                              \\\n> +        OPENDIRAT((! ISSET(FTS_NOCHDIR) && ISSET(FTS_CWDFD)     \\\n> +                   ? sp->fts_cwd_fd : AT_FDCWD),                \\\n> +                  file,                                         \\\n> +                  (((ISSET(FTS_PHYSICAL)                        \\\n> +                     && ! (ISSET(FTS_COMFOLLOW)                 \\\n> +                           && cur->fts_level == FTS_ROOTLEVEL)) \\\n> +                    ? O_NOFOLLOW : 0)),                         \\\n> +                  Pdir_fd)\n> +\n> +/*\n> + * This is the tricky part -- do not casually change *anything* in here.  The\n> + * idea is to build the linked list of entries that are used by fts_children\n> + * and fts_read.  There are lots of special cases.\n> + *\n> + * The real slowdown in walking the tree is the stat calls.  If FTS_NOSTAT is\n> + * set and it's a physical walk (so that symbolic links can't be directories),\n> + * we can do things quickly.  First, if it's a 4.4BSD file system, the type\n> + * of the file is in the directory entry.  Otherwise, we assume that the number\n> + * of subdirectories in a node is equal to the number of links to the parent.\n> + * The former skips all stat calls.  The latter skips stat calls in any leaf\n> + * directories and for any files after the subdirectories in the directory have\n> + * been found, cutting the stat calls by about 2/3.\n> + */\n> +static FTSENTRY *\n> +internal_function\n> +fts_build (register FTSOBJ *sp, int type)\n> +{\n> +        FTSENTRY *cur = sp->fts_cur;\n> +        bool continue_readdir = !!FTSENT_DIRP(cur);\n> +\n> +        /* When cur->fts_dirp is non-NULL, that means we should\n> +           continue calling readdir on that existing DIR* pointer\n> +           rather than opening a new one.  */\n> +        int dir_fd;\n> +        if (continue_readdir)\n> +          {\n> +            DIR *dp = FTSENT_DIRP(cur);\n> +            dir_fd = dirfd (dp);\n> +            if (dir_fd < 0)\n> +              {\n> +                int dirfd_errno = errno;\n> +                closedir_and_clear (FTSENT_DIRP(cur));\n> +                if (type == BREAD)\n> +                  {\n> +                    cur->fts_info = FTS_DNR;\n> +                    cur->fts_errno = dirfd_errno;\n> +                  }\n> +                return NULL;\n> +              }\n> +          }\n> +        else\n> +          {\n> +            /* Open the directory for reading.  If this fails, we're done.\n> +               If being called from fts_read, set the fts_info field. */\n> +            if ((FTSENT_DIRP (cur) = fts_opendir(cur->fts_accpath, &dir_fd)) == NULL)\n> +              {\n> +                if (type == BREAD)\n> +                  {\n> +                    cur->fts_info = FTS_DNR;\n> +                    cur->fts_errno = errno;\n> +                  }\n> +                return NULL;\n> +              }\n> +            /* Rather than calling fts_stat for each and every entry encountered\n> +               in the readdir loop (below), stat each directory only right after\n> +               opening it.  */\n> +            bool stat_optimization = cur->fts_info == FTS_NSOK;\n> +\n> +            if (stat_optimization\n> +                /* Also read the stat info again after opening a directory to\n> +                   reveal eventual changes caused by a submount triggered by\n> +                   the traversal.  But do it only for utilities which use\n> +                   FTS_TIGHT_CYCLE_CHECK.  Therefore, only find and du\n> +                   benefit/suffer from this feature for now.  */\n> +                || ISSET (FTS_TIGHT_CYCLE_CHECK))\n> +              {\n> +                if (!stat_optimization)\n> +                  LEAVE_DIR (sp, cur, \"4\");\n> +                if (FSTAT (dir_fd, cur->fts_statp) != 0)\n> +                  {\n> +                    int fstat_errno = errno;\n> +                    closedir_and_clear (FTSENT_DIRP(cur));\n> +                    if (type == BREAD)\n> +                      {\n> +                        cur->fts_errno = fstat_errno;\n> +                        cur->fts_info = FTS_NS;\n> +                      }\n> +                    __set_errno (fstat_errno);\n> +                    return NULL;\n> +                  }\n> +                if (stat_optimization)\n> +                  cur->fts_info = FTS_D;\n> +                else if (! enter_dir (sp, cur))\n> +                  {\n> +                    int saved_errno = errno;\n> +                    closedir_and_clear (FTSENT_DIRP(cur));\n> +                    __set_errno (saved_errno);\n> +                    return NULL;\n> +                  }\n> +              }\n> +          }\n> +\n> +        /* Maximum number of readdir entries to read at one time.  This\n> +           limitation is to avoid reading millions of entries into memory\n> +           at once.  When an fts_compar function is specified, we have no\n> +           choice: we must read all entries into memory before calling that\n> +           function.  But when no such function is specified, we can read\n> +           entries in batches that are large enough to help us with inode-\n> +           sorting, yet not so large that we risk exhausting memory.  */\n> +        size_t max_entries = sp->fts_compar ? SIZE_MAX : FTS_MAX_READDIR_ENTRIES;\n> +\n> +        /*\n> +         * If we're going to need to stat anything or we want to descend\n> +         * and stay in the directory, chdir.  If this fails we keep going,\n> +         * but set a flag so we don't chdir after the post-order visit.\n> +         * We won't be able to stat anything, but we can still return the\n> +         * names themselves.  Note, that since fts_read won't be able to\n> +         * chdir into the directory, it will have to return different file\n> +         * names than before, i.e. \"a/b\" instead of \"b\".  Since the node\n> +         * has already been visited in pre-order, have to wait until the\n> +         * post-order visit to return the error.  There is a special case\n> +         * here, if there was nothing to stat then it's not an error to\n> +         * not be able to stat.  This is all fairly nasty.  If a program\n> +         * needed sorted entries or stat information, they had better be\n> +         * checking FTS_NS on the returned nodes.\n> +         */\n> +        bool descend;\n> +        if (continue_readdir)\n> +          {\n> +            /* When resuming a short readdir run, we already have\n> +               the required dirp and dir_fd.  */\n> +            descend = true;\n> +          }\n> +        else\n> +          {\n> +            /* Try to descend unless it is a names-only fts_children,\n> +               or the directory is known to lack subdirectories.  */\n> +            descend = (type != BNAMES\n> +                       && ! (ISSET (FTS_NOSTAT) && ISSET (FTS_PHYSICAL)\n> +                             && ! ISSET (FTS_SEEDOT)\n> +                             && cur->fts_statp->st_nlink == MIN_DIR_NLINK\n> +                             && (leaf_optimization (cur, dir_fd)\n> +                                 != NO_LEAF_OPTIMIZATION)));\n> +            if (descend || type == BREAD)\n> +              {\n> +                if (ISSET(FTS_CWDFD))\n> +                  dir_fd = fcntl (dir_fd, F_DUPFD_CLOEXEC, STDERR_FILENO + 1);\n> +                if (dir_fd < 0 || fts_safe_changedir(sp, cur, dir_fd, NULL)) {\n> +                        if (descend && type == BREAD)\n> +                                cur->fts_errno = errno;\n> +                        cur->fts_flags |= FTS_DONTCHDIR;\n> +                        descend = false;\n> +                        closedir_and_clear(FTSENT_DIRP(cur));\n> +                        if (ISSET(FTS_CWDFD) && 0 <= dir_fd)\n> +                                close (dir_fd);\n> +                        FTSENT_DIRP(cur) = NULL;\n> +                } else\n> +                        descend = true;\n> +              }\n> +          }\n> +\n> +        /*\n> +         * Figure out the max file name length that can be stored in the\n> +         * current buffer -- the inner loop allocates more space as necessary.\n> +         * We really wouldn't have to do the maxlen calculations here, we\n> +         * could do them in fts_read before returning the name, but it's a\n> +         * lot easier here since the length is part of the dirent structure.\n> +         *\n> +         * If not changing directories set a pointer so that can just append\n> +         * each new component into the file name.\n> +         */\n> +        size_t len = NAPPEND(cur);\n> +        char *cp;\n> +        if (ISSET(FTS_NOCHDIR)) {\n> +                cp = sp->fts_path + len;\n> +                *cp++ = '/';\n> +        } else {\n> +                /* GCC, you're too verbose. */\n> +                cp = NULL;\n> +        }\n> +        len++;\n> +        size_t maxlen = sp->fts_pathlen - len;\n> +\n> +        ptrdiff_t level = cur->fts_level + 1;\n> +\n> +        /* Read the directory, attaching each entry to the \"link\" pointer. */\n> +        bool doadjust = false;\n> +        register FTSENTRY *head = NULL;\n> +        FTSENTRY *tail = NULL;\n> +        register size_t nitems = 0;\n> +        bool sort_by_inode = false;\n> +        while (FTSENT_DIRP(cur)) {\n> +                __set_errno (0);\n> +                struct dirent *dp = readdir(FTSENT_DIRP(cur));\n> +                if (dp == NULL) {\n> +                        /* Some readdir()s do not absorb ENOENT (dir\n> +                           deleted but open).  This bug was fixed in\n> +                           glibc 2.3 (2002).  */\n> +#if ! (2 < __GLIBC__ + (3 <= __GLIBC_MINOR__))\n> +                        if (errno == ENOENT)\n> +                          errno = 0;\n> +#endif\n> +                        if (errno) {\n> +                                cur->fts_errno = errno;\n> +                                /* If we've not read any items yet, treat\n> +                                   the error as if we can't access the dir.  */\n> +                                cur->fts_info = (continue_readdir || nitems)\n> +                                                ? FTS_ERR : FTS_DNR;\n> +                        }\n> +                        closedir_and_clear(FTSENT_DIRP(cur));\n> +                        break;\n> +                }\n> +                if (!ISSET(FTS_SEEDOT) && ISDOT(dp->d_name))\n> +                        continue;\n> +\n> +                size_t d_namelen = _D_EXACT_NAMLEN (dp);\n> +                register FTSENTRY *p = fts_alloc (sp, dp->d_name, d_namelen);\n> +                if (!p)\n> +                        goto mem1;\n> +                if (d_namelen >= maxlen) {\n> +                        /* include space for NUL */\n> +                        uintptr_t oldaddr = (uintptr_t) sp->fts_path;\n> +                        if (! fts_palloc(sp, d_namelen + len + 1)) {\n> +                                /*\n> +                                 * No more memory.  Save\n> +                                 * errno, free up the current structure and the\n> +                                 * structures already allocated.\n> +                                 */\n> +mem1: ;\n> +                                int saved_errno = errno;\n> +                                free(FTSENT_WRAPPER(p));\n> +                                fts_lfree(head);\n> +                                closedir_and_clear(FTSENT_DIRP(cur));\n> +                                cur->fts_info = FTS_ERR;\n> +                                SET(FTS_STOP);\n> +                                __set_errno (saved_errno);\n> +                                return (NULL);\n> +                        }\n> +                        /* Did realloc() change the pointer? */\n> +                        if (oldaddr != (uintptr_t) sp->fts_path) {\n> +                                doadjust = true;\n> +                                if (ISSET(FTS_NOCHDIR))\n> +                                        cp = sp->fts_path + len;\n> +                        }\n> +                        maxlen = sp->fts_pathlen - len;\n> +                }\n> +\n> +                size_t new_len = len + d_namelen;\n> +                if (new_len < len) {\n> +                        /*\n> +                         * In the unlikely event that we would end up\n> +                         * with a file name longer than SIZE_MAX, free up\n> +                         * the current structure and the structures already\n> +                         * allocated, then error out with ENAMETOOLONG.\n> +                         */\n> +                        free(FTSENT_WRAPPER(p));\n> +                        fts_lfree(head);\n> +                        closedir_and_clear(FTSENT_DIRP(cur));\n> +                        cur->fts_info = FTS_ERR;\n> +                        SET(FTS_STOP);\n> +                        __set_errno (ENAMETOOLONG);\n> +                        return (NULL);\n> +                }\n> +                p->fts_level = level;\n> +                p->fts_parent = sp->fts_cur;\n> +                p->fts_pathlen = new_len;\n> +\n> +                /* Store dirent.d_ino, in case we need to sort\n> +                   entries before processing them.  */\n> +                p->fts_statp->st_ino = D_INO (dp);\n> +\n> +                /* Build a file name for fts_stat to stat. */\n> +                if (ISSET(FTS_NOCHDIR)) {\n> +                        p->fts_accpath = p->fts_path;\n> +                        memmove(cp, p->fts_name, p->fts_namelen + 1);\n> +                } else\n> +                        p->fts_accpath = p->fts_name;\n> +\n> +                if (sp->fts_compar == NULL || ISSET(FTS_DEFER_STAT)) {\n> +                        /* Record what fts_read will have to do with this\n> +                           entry. In many cases, it will simply fts_stat it,\n> +                           but we can take advantage of any d_type information\n> +                           to optimize away the unnecessary stat calls.  I.e.,\n> +                           if FTS_NOSTAT is in effect, we don't need device\n> +                           numbers unconditionally (FTS_MOUNT) and we're not\n> +                           following symlinks (FTS_PHYSICAL) and d_type\n> +                           indicates this is *not* a directory, then we won't\n> +                           have to stat it  at all.  If it *is* a directory,\n> +                           then (currently) we stat it regardless, in order to\n> +                           get device and inode numbers.  Some day we might\n> +                           optimize that away, too, for directories where\n> +                           d_ino is known to be valid.  */\n> +                        bool skip_stat = (ISSET(FTS_NOSTAT)\n> +                                          && DT_IS_KNOWN(dp)\n> +                                          && ! DT_MUST_BE(dp, DT_DIR)\n> +                                          && (ISSET(FTS_PHYSICAL)\n> +                                              || ! DT_MUST_BE(dp, DT_LNK))\n> +                                          && ! ISSET(FTS_MOUNT));\n> +                        p->fts_info = FTS_NSOK;\n> +                        /* Propagate dirent.d_type information back\n> +                           to caller, when possible.  */\n> +                        set_stat_type (p->fts_statp, D_TYPE (dp));\n> +                        fts_set_stat_required(p, !skip_stat);\n> +                } else {\n> +                        p->fts_info = fts_stat(sp, p, false);\n> +                }\n> +\n> +                /* We walk in directory order so \"ls -f\" doesn't get upset. */\n> +                p->fts_link = NULL;\n> +                if (head == NULL)\n> +                        head = tail = p;\n> +                else {\n> +                        tail->fts_link = p;\n> +                        tail = p;\n> +                }\n> +\n> +                /* If there are many entries, no sorting function has been\n> +                   specified, and this file system is of a type that may be\n> +                   slow with a large number of entries, arrange to sort the\n> +                   directory entries on increasing inode numbers.\n> +\n> +                   The NITEMS comparison uses ==, not >, because the test\n> +                   needs to be tried at most once once, and NITEMS will exceed\n> +                   the threshold after it is incremented below.  */\n> +                if (nitems == _FTS_INODE_SORT_DIR_ENTRIES_THRESHOLD\n> +                    && !sp->fts_compar)\n> +                  sort_by_inode = dirent_inode_sort_may_be_useful (cur, dir_fd);\n> +\n> +                ++nitems;\n> +                if (max_entries <= nitems) {\n> +                        /* When there are too many dir entries, leave\n> +                           fts_dirp open, so that a subsequent fts_read\n> +                           can take up where we leave off.  */\n> +                        break;\n> +                }\n> +        }\n> +\n> +        /*\n> +         * If realloc() changed the address of the file name, adjust the\n> +         * addresses for the rest of the tree and the dir list.\n> +         */\n> +        if (doadjust)\n> +                fts_padjust(sp, head);\n> +\n> +        /*\n> +         * If not changing directories, reset the file name back to original\n> +         * state.\n> +         */\n> +        if (ISSET(FTS_NOCHDIR)) {\n> +                if (len == sp->fts_pathlen || nitems == 0)\n> +                        --cp;\n> +                *cp = '\\0';\n> +        }\n> +\n> +        /*\n> +         * If descended after called from fts_children or after called from\n> +         * fts_read and nothing found, get back.  At the root level we use\n> +         * the saved fd; if one of fts_open()'s arguments is a relative name\n> +         * to an empty directory, we wind up here with no other way back.  If\n> +         * can't get back, we're done.\n> +         */\n> +        if (!continue_readdir && descend && (type == BCHILD || !nitems) &&\n> +            (cur->fts_level == FTS_ROOTLEVEL\n> +             ? restore_initial_cwd(sp)\n> +             : fts_safe_changedir(sp, cur->fts_parent, -1, \"..\"))) {\n> +                cur->fts_info = FTS_ERR;\n> +                SET(FTS_STOP);\n> +                fts_lfree(head);\n> +                return (NULL);\n> +        }\n> +\n> +        /* If didn't find anything, return NULL. */\n> +        if (!nitems) {\n> +                if (type == BREAD\n> +                    && cur->fts_info != FTS_DNR && cur->fts_info != FTS_ERR)\n> +                        cur->fts_info = FTS_DP;\n> +                fts_lfree(head);\n> +                return (NULL);\n> +        }\n> +\n> +        if (sort_by_inode) {\n> +                sp->fts_compar = FTS_COMPAR_CAST (fts_compare_ino);\n> +                head = fts_sort (sp, head, nitems);\n> +                sp->fts_compar = NULL;\n> +        }\n> +\n> +        /* Sort the entries. */\n> +        if (sp->fts_compar && nitems > 1)\n> +                head = fts_sort(sp, head, nitems);\n> +        return (head);\n> +}\n> +\n> +#if GNULIB_FTS_DEBUG\n> +\n> +struct devino {\n> +  intmax_t dev, ino;\n> +};\n> +#define PRINT_DEVINO \"(%jd,%jd)\"\n> +\n> +static struct devino\n> +getdevino (int fd)\n> +{\n> +  struct STRUCT_STAT st;\n> +  return (fd == AT_FDCWD\n> +          ? (struct devino) { -1, 0 }\n> +          : FSTAT (fd, &st) == 0\n> +          ? (struct devino) { st.st_dev, st.st_ino }\n> +          : (struct devino) { -1, errno });\n> +}\n> +\n> +/* Walk ->fts_parent links starting at E_CURR, until the root of the\n> +   current hierarchy.  There should be a directory with dev/inode\n> +   matching those of AD.  If not, print a lot of diagnostics.  */\n> +static void\n> +find_matching_ancestor (FTSENTRY const *e_curr, struct Active_dir const *ad)\n> +{\n> +  for (FTSENTRY const *ent = e_curr;\n> +       ent->fts_level >= FTS_ROOTLEVEL;\n> +       ent = ent->fts_parent)\n> +    {\n> +      if (ad->ino == ent->fts_statp->st_ino\n> +          && ad->dev == ent->fts_statp->st_dev)\n> +        return;\n> +    }\n> +  printf (\"ERROR: tree dir, %s, not active\\n\", ad->fts_ent->fts_accpath);\n> +  printf (\"active dirs:\\n\");\n> +  for (FTSENTRY const *ent = e_curr;\n> +       ent->fts_level >= FTS_ROOTLEVEL;\n> +       ent = ent->fts_parent)\n> +    printf (\"  %s(%\"PRIuMAX\"/%\"PRIuMAX\") to %s(%\"PRIuMAX\"/%\"PRIuMAX\")...\\n\",\n> +            ad->fts_ent->fts_accpath,\n> +            (uintmax_t) ad->dev,\n> +            (uintmax_t) ad->ino,\n> +            ent->fts_accpath,\n> +            (uintmax_t) ent->fts_statp->st_dev,\n> +            (uintmax_t) ent->fts_statp->st_ino);\n> +}\n> +\n> +void\n> +fts_cross_check (FTSOBJ const *sp)\n> +{\n> +  if ( ! ISSET (FTS_TIGHT_CYCLE_CHECK))\n> +    return;\n> +\n> +  FTSENTRY const *ent = sp->fts_cur;\n> +\n> +  Dprintf ((\"fts-cross-check cur=%s\\n\", ent->fts_path));\n> +  /* Make sure every parent dir is in the tree.  */\n> +  for (FTSENTRY const *t = ent->fts_parent;\n> +       t->fts_level >= FTS_ROOTLEVEL;\n> +       t = t->fts_parent)\n> +    {\n> +      struct Active_dir ad;\n> +      ad.ino = t->fts_statp->st_ino;\n> +      ad.dev = t->fts_statp->st_dev;\n> +      if ( ! hash_lookup (sp->fts_cycle.ht, &ad))\n> +        printf (\"ERROR: active dir, %s, not in tree\\n\", t->fts_path);\n> +    }\n> +\n> +  /* Make sure every dir in the tree is an active dir.\n> +     But ENT is not necessarily a directory.  If so, just skip this part. */\n> +  if (ent->fts_parent->fts_level >= FTS_ROOTLEVEL\n> +      && (ent->fts_info == FTS_DP\n> +          || ent->fts_info == FTS_D))\n> +    for (struct Active_dir *ad = hash_get_first (sp->fts_cycle.ht);\n> +         ad != NULL;\n> +         ad = hash_get_next (sp->fts_cycle.ht, ad))\n> +      {\n> +        find_matching_ancestor (ent, ad);\n> +      }\n> +}\n> +\n> +static bool\n> +same_fd (int fd1, int fd2)\n> +{\n> +  struct STRUCT_STAT sb1, sb2;\n> +  return (FSTAT (fd1, &sb1) == 0\n> +          && FSTAT (fd2, &sb2) == 0\n> +          && psame_inode (&sb1, &sb2));\n> +}\n> +\n> +static void\n> +fd_ring_print (FTSOBJ const *sp, FILE *stream, char const *msg)\n> +{\n> +  if (!fts_debug)\n> +    return;\n> +  I_ring const *fd_ring = &sp->fts_fd_ring;\n> +  struct devino cwd = getdevino (sp->fts_cwd_fd);\n> +  fprintf (stream, \"=== %s ========== \"PRINT_DEVINO\"\\n\", msg, cwd.dev, cwd.ino);\n> +  if (i_ring_empty (fd_ring))\n> +    return;\n> +\n> +  unsigned int i = fd_ring->ir_front;\n> +  while (true)\n> +    {\n> +      int fd = fd_ring->ir_data[i];\n> +      if (fd < 0)\n> +        fprintf (stream, \"%u: %d:\\n\", i, fd);\n> +      else\n> +        {\n> +          struct devino wd = getdevino (fd);\n> +          fprintf (stream, \"%u: %d: \"PRINT_DEVINO\"\\n\", i, fd, wd.dev, wd.ino);\n> +        }\n> +      if (i == fd_ring->ir_back)\n> +        break;\n> +      i = (i + I_RING_SIZE - 1) % I_RING_SIZE;\n> +    }\n> +}\n> +\n> +/* Ensure that each file descriptor on the fd_ring matches a\n> +   parent, grandparent, etc. of the current working directory.  */\n> +static void\n> +fd_ring_check (FTSOBJ const *sp)\n> +{\n> +  if (!fts_debug)\n> +    return;\n> +\n> +  /* Make a writable copy.  */\n> +  I_ring fd_w = sp->fts_fd_ring;\n> +\n> +  int cwd_fd = sp->fts_cwd_fd;\n> +  cwd_fd = fcntl (cwd_fd, F_DUPFD_CLOEXEC, STDERR_FILENO + 1);\n> +  struct devino dot = getdevino (cwd_fd);\n> +  fprintf (stderr, \"===== check ===== cwd: \"PRINT_DEVINO\"\\n\",\n> +           dot.dev, dot.ino);\n> +  while ( ! i_ring_empty (&fd_w))\n> +    {\n> +      int fd = i_ring_pop (&fd_w);\n> +      if (0 <= fd)\n> +        {\n> +          int open_flags = O_SEARCH | O_CLOEXEC;\n> +          int parent_fd = openat (cwd_fd, \"..\", open_flags);\n> +          if (parent_fd < 0)\n> +            {\n> +              // Warn?\n> +              break;\n> +            }\n> +          if (!same_fd (fd, parent_fd))\n> +            {\n> +              struct devino cwd = getdevino (fd);\n> +              fprintf (stderr, \"ring  : \"PRINT_DEVINO\"\\n\", cwd.dev, cwd.ino);\n> +              struct devino c2 = getdevino (parent_fd);\n> +              fprintf (stderr, \"parent: \"PRINT_DEVINO\"\\n\", c2.dev, c2.ino);\n> +              fts_assert (0);\n> +            }\n> +          close (cwd_fd);\n> +          cwd_fd = parent_fd;\n> +        }\n> +    }\n> +  close (cwd_fd);\n> +}\n> +#endif\n> +\n> +static unsigned short int\n> +internal_function\n> +fts_stat(FTSOBJ *sp, register FTSENTRY *p, bool follow)\n> +{\n> +        if (ISSET (FTS_LOGICAL)\n> +            || (ISSET (FTS_COMFOLLOW) && p->fts_level == FTS_ROOTLEVEL))\n> +                follow = true;\n> +\n> +        struct STRUCT_STAT *sbp = p->fts_statp;\n> +\n> +        /*\n> +         * If doing a logical walk, or application requested FTS_FOLLOW, do\n> +         * a stat(2).  If that fails, check for a nonexistent symlink.  If\n> +         * fail, set the errno from the stat call.\n> +         */\n> +        int flags = follow ? 0 : AT_SYMLINK_NOFOLLOW;\n> +        if (FSTATAT (sp->fts_cwd_fd, p->fts_accpath, sbp, flags) < 0)\n> +          {\n> +            if (follow && errno == ENOENT\n> +                && 0 <= FSTATAT (sp->fts_cwd_fd, p->fts_accpath, sbp,\n> +                                 AT_SYMLINK_NOFOLLOW))\n> +              {\n> +                __set_errno (0);\n> +                return FTS_SLNONE;\n> +              }\n> +\n> +            p->fts_errno = errno;\n> +           memset (sbp, 0, sizeof *sbp);\n> +            return FTS_NS;\n> +          }\n> +\n> +        if (S_ISDIR(sbp->st_mode)) {\n> +                if (ISDOT(p->fts_name)) {\n> +                        /* Command-line \".\" and \"..\" are real directories. */\n> +                        return (p->fts_level == FTS_ROOTLEVEL ? FTS_D : FTS_DOT);\n> +                }\n> +\n> +                return (FTS_D);\n> +        }\n> +        if (S_ISLNK(sbp->st_mode))\n> +                return (FTS_SL);\n> +        if (S_ISREG(sbp->st_mode))\n> +                return (FTS_F);\n> +        return (FTS_DEFAULT);\n> +}\n> +\n> +static int\n> +fts_compar (void const *a, void const *b)\n> +{\n> +  /* Convert A and B to the correct types, to pacify the compiler, and\n> +     for portability to bizarre hosts where \"void const *\" and \"FTSENT\n> +     const **\" differ in runtime representation.  The comparison\n> +     function cannot modify *a and *b, but there is no compile-time\n> +     check for this.  */\n> +  FTSENTRY const **pa = (FTSENTRY const **) a;\n> +  FTSENTRY const **pb = (FTSENTRY const **) b;\n> +  return FTSENT_FTS(pa[0])->fts_compar (pa, pb);\n> +}\n> +\n> +static FTSENTRY *\n> +internal_function\n> +fts_sort (FTSOBJ *sp, FTSENTRY *head, register size_t nitems)\n> +{\n> +        register FTSENTRY **ap, *p;\n> +\n> +        /* On most modern hosts, void * and FTSENT ** have the same\n> +           run-time representation, and one can convert sp->fts_compar to\n> +           the type qsort expects without problem.  Use the heuristic that\n> +           this is OK if the two pointer types are the same size, and if\n> +           converting FTSENT ** to uintptr_t is the same as converting\n> +           FTSENT ** to void * and then to uintptr_t.  This heuristic isn't\n> +           valid in general but we don't know of any counterexamples.  */\n> +        FTSENTRY *dummy;\n> +        int (*compare) (void const *, void const *) =\n> +          ((sizeof &dummy == sizeof (void *)\n> +            && (uintptr_t) &dummy == (uintptr_t) (void *) &dummy)\n> +           ? (int (*) (void const *, void const *)) sp->fts_compar\n> +           : fts_compar);\n> +\n> +        /*\n> +         * Construct an array of pointers to the structures and call qsort(3).\n> +         * Reassemble the array in the order returned by qsort.  If unable to\n> +         * sort for memory reasons, return the directory entries in their\n> +         * current order.  Allocate enough space for the current needs plus\n> +         * 40 so don't realloc one entry at a time.\n> +         */\n> +        if (nitems > sp->fts_nitems) {\n> +                sp->fts_nitems = nitems + 40;\n> +                FTSENTRY **a;\n> +                if (! (a = reallocarray (sp->fts_array,\n> +                                         sp->fts_nitems, sizeof *a))) {\n> +                        free(sp->fts_array);\n> +                        sp->fts_array = NULL;\n> +                        sp->fts_nitems = 0;\n> +                        return (head);\n> +                }\n> +                sp->fts_array = a;\n> +        }\n> +        for (ap = sp->fts_array, p = head; p; p = p->fts_link)\n> +                *ap++ = p;\n> +        qsort((void *)sp->fts_array, nitems, sizeof(FTSENTRY *), compare);\n> +        for (head = *(ap = sp->fts_array); --nitems; ++ap)\n> +                ap[0]->fts_link = ap[1];\n> +        ap[0]->fts_link = NULL;\n> +        return (head);\n> +}\n> +\n> +static FTSENTRY *\n> +internal_function\n> +fts_alloc (FTSOBJ *sp, const char *name, register size_t namelen)\n> +{\n> +        /*\n> +         * The file name is a variable length array.  Allocate the FTSENT\n> +         * structure and the file name in one chunk.\n> +         */\n> +        size_t len = FLEXSIZEOF(FTSENT, fts_name, namelen + 1);\n> +\tregister FTSENTRY *p;\n> +#if !_LIBC\n> +        p = malloc(len);\n> +        if (p == NULL)\n> +                return (NULL);\n> +#else\n> +\t/*\n> +\t * For glibc, we use a wrapper struct to provide the extra required\n> +\t * fields without changing the FSENT layout.\n> +\t */\n> +\tlen += sizeof (struct FTSENT_wrapper);\n> +\tstruct FTSENT_wrapper *wrapper = malloc(len);\n> +\tif (wrapper == NULL)\n> +\t\treturn (NULL);\n> +\tp = &wrapper->ent;\n> +        p->fts_statp = &wrapper->fts_stat;\n> +#endif\n> +\n> +        /* Copy the name and guarantee NUL termination. */\n> +        memcpy(p->fts_name, name, namelen);\n> +        p->fts_name[namelen] = '\\0';\n> +\n> +        p->fts_namelen = namelen;\n> +        FTSENT_FTS(p)= sp;\n> +        p->fts_path = sp->fts_path;\n> +        p->fts_errno = 0;\n> +        FTSENT_DIRP(p) = NULL;\n> +        p->fts_flags = 0;\n> +        p->fts_instr = FTS_NOINSTR;\n> +        p->fts_number = 0;\n> +        p->fts_pointer = NULL;\n> +        return (p);\n> +}\n> +\n> +static void\n> +internal_function\n> +fts_lfree (register FTSENTRY *head)\n> +{\n> +        int saved_errno = errno;\n> +\n> +        /* Free a linked list of structures. */\n> +        register FTSENTRY *p;\n> +        while ((p = head)) {\n> +                head = head->fts_link;\n> +                if (FTSENT_DIRP(p))\n> +                        closedir (FTSENT_DIRP(p));\n> +                free(FTSENT_WRAPPER(p));\n> +        }\n> +\n> +        __set_errno (saved_errno);\n> +}\n> +\n> +/*\n> + * Allow essentially unlimited file name lengths; find, rm, ls should\n> + * all work on any tree.  Most systems will allow creation of file\n> + * names much longer than MAXPATHLEN, even though the kernel won't\n> + * resolve them.  Add the size (not just what's needed) plus 256 bytes\n> + * so don't realloc the file name 2 bytes at a time.\n> + */\n> +static bool\n> +internal_function\n> +fts_palloc (FTSOBJ *sp, size_t more)\n> +{\n> +        size_t new_len = sp->fts_pathlen + more + 256;\n> +\n> +        /*\n> +         * See if fts_pathlen would overflow.\n> +         */\n> +        if (new_len < sp->fts_pathlen) {\n> +                free(sp->fts_path);\n> +                sp->fts_path = NULL;\n> +                __set_errno (ENAMETOOLONG);\n> +                return false;\n> +        }\n> +        sp->fts_pathlen = new_len;\n> +        char *p = realloc(sp->fts_path, sp->fts_pathlen);\n> +        if (p == NULL) {\n> +                free(sp->fts_path);\n> +                sp->fts_path = NULL;\n> +                return false;\n> +        }\n> +        sp->fts_path = p;\n> +        return true;\n> +}\n> +\n> +/*\n> + * When the file name is realloc'd, have to fix all of the pointers in\n> + *  structures already returned.\n> + */\n> +static void\n> +internal_function\n> +fts_padjust (FTSOBJ *sp, FTSENTRY *head)\n> +{\n> +        char *addr = sp->fts_path;\n> +\n> +        /* This code looks at bit-patterns of freed pointers to\n> +           relocate them, so it relies on undefined behavior.  If this\n> +           trick does not work on your platform, please report a bug.  */\n> +\n> +#define ADJUST(p) do {                                                  \\\n> +        uintptr_t old_accpath = (uintptr_t) (p)->fts_accpath;           \\\n> +        if (old_accpath != (uintptr_t) (p)->fts_name) {                 \\\n> +                (p)->fts_accpath =                                      \\\n> +                  addr + (old_accpath - (uintptr_t) (p)->fts_path);     \\\n> +        }                                                               \\\n> +        (p)->fts_path = addr;                                           \\\n> +} while (0)\n> +        /* Adjust the current set of children. */\n> +        for (FTSENTRY *p = sp->fts_child; p; p = p->fts_link)\n> +                ADJUST(p);\n> +\n> +        /* Adjust the rest of the tree, including the current level. */\n> +        for (FTSENTRY *p = head; p->fts_level >= FTS_ROOTLEVEL;) {\n> +                ADJUST(p);\n> +                p = p->fts_link ? p->fts_link : p->fts_parent;\n> +        }\n> +}\n> +\n> +static size_t\n> +internal_function _GL_ATTRIBUTE_PURE\n> +fts_maxarglen (char * const *argv)\n> +{\n> +        size_t max;\n> +\n> +        for (max = 0; *argv; ++argv) {\n> +                size_t len = strlen(*argv);\n> +                if (len > max)\n> +                        max = len;\n> +        }\n> +        return (max + 1);\n> +}\n> +\n> +/*\n> + * Change to dir specified by fd or file name without getting\n> + * tricked by someone changing the world out from underneath us.\n> + * Assumes p->fts_statp->st_dev and p->fts_statp->st_ino are filled in.\n> + * If FD is non-negative, expect it to be used after this function returns,\n> + * and to be closed eventually.  So don't pass e.g., 'dirfd(dirp)' and then\n> + * do closedir(dirp), because that would invalidate the saved FD.\n> + * Upon failure, close FD immediately and return nonzero.\n> + */\n> +static int\n> +internal_function\n> +fts_safe_changedir (FTSOBJ *sp, FTSENTRY *p, int fd, char const *dir)\n> +{\n> +        fts_assert (0 <= fd || dir != NULL);\n> +        bool is_dotdot = dir && streq (dir, \"..\");\n> +\n> +        /* This clause handles the unusual case in which FTS_NOCHDIR\n> +           is specified, along with FTS_CWDFD.  In that case, there is\n> +           no need to change even the virtual cwd file descriptor.\n> +           However, if FD is non-negative, we do close it here.  */\n> +        if (ISSET (FTS_NOCHDIR))\n> +          {\n> +            if (ISSET (FTS_CWDFD) && 0 <= fd)\n> +              close (fd);\n> +            return 0;\n> +          }\n> +\n> +        if (fd < 0 && is_dotdot && ISSET (FTS_CWDFD))\n> +          {\n> +            /* When possible, skip the diropen and subsequent fstat+dev/ino\n> +               comparison.  I.e., when changing to parent directory\n> +               (chdir (\"..\")), use a file descriptor from the ring and\n> +               save the overhead of diropen+fstat, as well as avoiding\n> +               failure when we lack \"x\" access to the virtual cwd.  */\n> +            if ( ! i_ring_empty (&sp->fts_fd_ring))\n> +              {\n> +                int parent_fd;\n> +                fd_ring_print (sp, stderr, \"pre-pop\");\n> +                parent_fd = i_ring_pop (&sp->fts_fd_ring);\n> +                if (0 <= parent_fd)\n> +                  {\n> +                    fd = parent_fd;\n> +                    dir = NULL;\n> +                  }\n> +              }\n> +          }\n> +\n> +        int newfd = fd;\n> +        if (fd < 0 && (newfd = diropen (sp, dir)) < 0)\n> +          return -1;\n> +\n> +        /* The following dev/inode check is necessary if we're doing a\n> +           \"logical\" traversal (through symlinks, a la chown -L), if the\n> +           system lacks O_NOFOLLOW support, or if we're changing to \"..\"\n> +           (but not via a popped file descriptor).  When changing to the\n> +           name \"..\", O_NOFOLLOW can't help.  In general, when the target is\n> +           not \"..\", diropen's use of O_NOFOLLOW ensures we don't mistakenly\n> +           follow a symlink, so we can avoid the expense of this fstat.  */\n> +        int ret;\n> +        if (ISSET(FTS_LOGICAL) || ! HAVE_WORKING_O_NOFOLLOW\n> +            || (dir && streq (dir, \"..\")))\n> +          {\n> +            struct STRUCT_STAT sb;\n> +            if (FSTAT (newfd, &sb))\n> +              {\n> +                ret = -1;\n> +                goto bail;\n> +              }\n> +            if (p->fts_statp->st_dev != sb.st_dev\n> +                || p->fts_statp->st_ino != sb.st_ino)\n> +              {\n> +                __set_errno (ENOENT);           /* disinformation */\n> +                ret = -1;\n> +                goto bail;\n> +              }\n> +          }\n> +\n> +        if (ISSET(FTS_CWDFD))\n> +          {\n> +            cwd_advance_fd (sp, newfd, ! is_dotdot);\n> +            return 0;\n> +          }\n> +\n> +        ret = fchdir(newfd);\n> +bail:\n> +        if (fd < 0)\n> +          {\n> +            int oerrno = errno;\n> +            (void)close(newfd);\n> +            __set_errno (oerrno);\n> +          }\n> +        return ret;\n> +}\n> diff --git a/io/fts.c b/io/fts.c\n> index 3825f0aeb4..d3b8e5a100 100644\n> --- a/io/fts.c\n> +++ b/io/fts.c\n> @@ -1,2208 +1,23 @@\n> -/* Traverse a file hierarchy.\n> +/* File tree traversal functions LFS version.\n> +   Copyright (C) 2026 Free Software Foundation, Inc.\n> +   This file is part of the GNU C Library.\n>  \n> -   Copyright (C) 2004-2026 Free Software Foundation, Inc.\n> +   The GNU C Library is free software; you can redistribute it and/or\n> +   modify it under the terms of the GNU Lesser General Public\n> +   License as published by the Free Software Foundation; either\n> +   version 2.1 of the License, or (at your option) any later version.\n>  \n> -   This file is free software: you can redistribute it and/or modify\n> -   it under the terms of the GNU Lesser General Public License as\n> -   published by the Free Software Foundation; either version 2.1 of the\n> -   License, or (at your option) any later version.\n> -\n> -   This file is distributed in the hope that it will be useful,\n> +   The GNU C Library is distributed in the hope that it will be useful,\n>     but WITHOUT ANY WARRANTY; without even the implied warranty of\n> -   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n> -   GNU Lesser General Public License for more details.\n> +   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU\n> +   Lesser General Public License for more details.\n>  \n> -   You should have received a copy of the GNU Lesser General Public License\n> -   along with this program.  If not, see <https://www.gnu.org/licenses/>.  */\n> +   You should have received a copy of the GNU Lesser General Public\n> +   License along with the GNU C Library; if not, see\n> +   <https://www.gnu.org/licenses/>.  */\n>  \n> -/*-\n> - * Copyright (c) 1990, 1993, 1994\n> - *      The Regents of the University of California.  All rights reserved.\n> - *\n> - * Redistribution and use in source and binary forms, with or without\n> - * modification, are permitted provided that the following conditions\n> - * are met:\n> - * 1. Redistributions of source code must retain the above copyright\n> - *    notice, this list of conditions and the following disclaimer.\n> - * 2. Redistributions in binary form must reproduce the above copyright\n> - *    notice, this list of conditions and the following disclaimer in the\n> - *    documentation and/or other materials provided with the distribution.\n> - * 4. Neither the name of the University nor the names of its contributors\n> - *    may be used to endorse or promote products derived from this software\n> - *    without specific prior written permission.\n> - *\n> - * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS \"AS IS\" AND\n> - * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE\n> - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE\n> - * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE\n> - * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL\n> - * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS\n> - * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)\n> - * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT\n> - * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY\n> - * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF\n> - * SUCH DAMAGE.\n> - */\n> +#include <sys/types.h>\n>  \n> -#include <config.h>\n> -\n> -#if defined LIBC_SCCS && !defined GCC_LINT && !defined lint\n> -static char sccsid[] = \"@(#)fts.c       8.6 (Berkeley) 8/14/94\";\n> +#ifndef __OFF_T_MATCHES_OFF64_T\n> +# include \"io/fts-common.c\"\n>  #endif\n> -\n> -#if _LIBC\n> -# include <fts.h>\n> -#else\n> -# include \"fts_.h\"\n> -#endif\n> -#if _LIBC || HAVE_SYS_PARAM_H\n> -# include <sys/param.h>\n> -#endif\n> -#include <sys/stat.h>\n> -#include <fcntl.h>\n> -#include <errno.h>\n> -#include <stddef.h>\n> -#include <stdint.h>\n> -#include <stdlib.h>\n> -#include <string.h>\n> -#include <unistd.h>\n> -\n> -/* Support for the LFS API version.  */\n> -#ifndef FTS_OPEN\n> -# define FTS_OPEN fts_open\n> -# define FTS_CLOSE fts_close\n> -# define FTS_READ fts_read\n> -# define FTS_SET fts_set\n> -# define FTS_CHILDREN fts_children\n> -# define FTSOBJ FTS\n> -# define FTSENTRY FTSENT\n> -# define INO_T ino_t\n> -# define STRUCT_STAT stat\n> -# define FSTAT __fstat\n> -# define FSTATAT __fstatat\n> -# define STRUCT_STATFS statfs\n> -# define FSTATFS __fstatfs\n> -#endif\n> -\n> -#if ! _LIBC\n> -# include \"attribute.h\"\n> -# include \"fcntl--.h\"\n> -# include \"openat.h\"\n> -# include \"opendirat.h\"\n> -# include \"same-inode.h\"\n> -# define OPENDIRAT opendirat\n> -# define FTSENT_WRAPPER(__p) __p\n> -# define FTS_COMPAR_CAST(__fn) __fn\n> -#else\n> -# include <stdbool.h>\n> -\n> -# define internal_function\n> -# define FALLTHROUGH               ; [[fallthrough]]\n> -# define HAVE_STRUCT_DIRENT_D_TYPE 1\n> -# define GNULIB_FTS_DEBUG          0\n> -# ifdef O_PATH\n> -#  define O_SEARCH                 O_PATH\n> -# else\n> -#  define O_SEARCH                 O_RDONLY\n> -# endif\n> -# define HAVE_SYS_VFS_H            1\n> -# define HAVE_FSTATFS              1\n> -# define HAVE_STRUCT_STATFS_F_TYPE 1\n> -# define HAVE_OPENAT               1\n> -# define HAVE_WORKING_O_NOFOLLOW   1\n> -# define _GL_CMP(a, b)             ((a) < (b) ? -1 : (a) > (b))\n> -# define OPENDIRAT                 __opendirat\n> -\n> -static inline bool openat_needs_fchdir (void)\n> -{\n> -  return false;\n> -}\n> -\n> -static inline bool streq (const char *s1, const char *s2)\n> -{\n> -  return strcmp (s1, s2) == 0;\n> -}\n> -# define reallocarray              __libc_reallocarray\n> -# define fchdir                    __fchdir\n> -# define close                     __close\n> -# define closedir                  __closedir\n> -# define fcntl                     __fcntl\n> -# define readdir                   __readdir\n> -# ifndef dirfd\n> -#  define dirfd                    __dirfd\n> -# endif\n> -# define open                      __open\n> -# define openat                    __openat\n> -\n> -# include \"cycle-check.c\"\n> -# include \"i-ring.c\"\n> -\n> -struct FTSENT_wrapper\n> -{\n> -  FTSOBJ *fts_fts;                /* the file hierarchy itself */\n> -  DIR *fts_dirp;                  /* Dir pointer for any directory containing\n> -\t\t\t\t     more entries than we read at one time.  */\n> -  struct STRUCT_STAT fts_stat;\n> -\n> -  FTSENTRY ent;\n> -};\n> -\n> -/* glibc historicaly defines the FTS::fts_compar as having 'void *', while the\n> -   fts_open has a function point using 'FTSENT **' as argument.  */\n> -# define FTS_COMPAR_CAST(__fn) ((int (*) (void const *, void const *))__fn)\n> -\n> -# define FTSENT_WRAPPER(p) \\\n> -    ((struct FTSENT_wrapper *) ((char *)(p) - offsetof(struct FTSENT_wrapper, ent)))\n> -#endif\n> -#define FTSENT_FTS(p)   (FTSENT_WRAPPER(p)->fts_fts)\n> -#define FTSENT_DIRP(p)  (FTSENT_WRAPPER(p)->fts_dirp)\n> -\n> -#include \"flexmember.h\"\n> -\n> -#include <dirent.h>\n> -#ifndef _D_EXACT_NAMLEN\n> -# define _D_EXACT_NAMLEN(dirent) strlen ((dirent)->d_name)\n> -#endif\n> -\n> -#if HAVE_STRUCT_DIRENT_D_TYPE\n> -/* True if the type of the directory entry D is known.  */\n> -# define DT_IS_KNOWN(d) ((d)->d_type != DT_UNKNOWN)\n> -/* True if the type of the directory entry D must be T.  */\n> -# define DT_MUST_BE(d, t) ((d)->d_type == (t))\n> -# define D_TYPE(d) ((d)->d_type)\n> -#else\n> -# define DT_IS_KNOWN(d) false\n> -# define DT_MUST_BE(d, t) false\n> -# define D_TYPE(d) DT_UNKNOWN\n> -\n> -# undef DT_UNKNOWN\n> -# define DT_UNKNOWN 0\n> -\n> -/* Any nonzero values will do here, so long as they're distinct.\n> -   Undef any existing macros out of the way.  */\n> -# undef DT_BLK\n> -# undef DT_CHR\n> -# undef DT_DIR\n> -# undef DT_FIFO\n> -# undef DT_LNK\n> -# undef DT_REG\n> -# undef DT_SOCK\n> -# define DT_BLK 1\n> -# define DT_CHR 2\n> -# define DT_DIR 3\n> -# define DT_FIFO 4\n> -# define DT_LNK 5\n> -# define DT_REG 6\n> -# define DT_SOCK 7\n> -#endif\n> -\n> -#ifndef S_IFBLK\n> -# define S_IFBLK 0\n> -#endif\n> -#ifndef S_IFLNK\n> -# define S_IFLNK 0\n> -#endif\n> -#ifndef S_IFSOCK\n> -# define S_IFSOCK 0\n> -#endif\n> -\n> -enum\n> -{\n> -  NOT_AN_INODE_NUMBER = 0\n> -};\n> -\n> -#ifdef D_INO_IN_DIRENT\n> -# define D_INO(dp) (dp)->d_ino\n> -#else\n> -/* Some systems don't have inodes, so fake them to avoid lots of ifdefs.  */\n> -# define D_INO(dp) NOT_AN_INODE_NUMBER\n> -#endif\n> -\n> -/* If possible (see max_entries, below), read no more than this many directory\n> -   entries at a time.  Without this limit (i.e., when using non-NULL\n> -   fts_compar), processing a directory with 4,000,000 entries requires ~1GiB\n> -   of memory, and handling 64M entries would require 16GiB of memory.  */\n> -#ifndef FTS_MAX_READDIR_ENTRIES\n> -# define FTS_MAX_READDIR_ENTRIES 100000\n> -#endif\n> -\n> -/* If there are more than this many entries in a directory,\n> -   and the conditions mentioned below are satisfied, then sort\n> -   the entries on inode number before any further processing.  */\n> -#ifndef FTS_INODE_SORT_DIR_ENTRIES_THRESHOLD\n> -# define FTS_INODE_SORT_DIR_ENTRIES_THRESHOLD 10000\n> -#endif\n> -\n> -enum\n> -{\n> -  _FTS_INODE_SORT_DIR_ENTRIES_THRESHOLD = FTS_INODE_SORT_DIR_ENTRIES_THRESHOLD\n> -};\n> -\n> -enum Fts_stat\n> -{\n> -  FTS_NO_STAT_REQUIRED = 1,\n> -  FTS_STAT_REQUIRED = 2\n> -};\n> -\n> -#ifndef __set_errno\n> -# define __set_errno(Val) errno = (Val)\n> -#endif\n> -\n> -/* If this host provides the openat function, then we can avoid\n> -   attempting to open \".\" in some initialization code below.  */\n> -#ifdef HAVE_OPENAT\n> -# define HAVE_OPENAT_SUPPORT 1\n> -#else\n> -# define HAVE_OPENAT_SUPPORT 0\n> -#endif\n> -\n> -#ifdef NDEBUG\n> -# define fts_assert(expr) ((void) (0 && (expr)))\n> -#else\n> -# define fts_assert(expr)       \\\n> -    do                          \\\n> -      {                         \\\n> -        if (!(expr))            \\\n> -          abort ();             \\\n> -      }                         \\\n> -    while (false)\n> -#endif\n> -\n> -static FTSENTRY  *fts_alloc (FTSOBJ *, const char *, size_t);\n> -static FTSENTRY  *fts_build (FTSOBJ *, int);\n> -static void      fts_lfree (FTSENTRY *);\n> -static void      fts_load (FTSOBJ *, FTSENTRY *);\n> -static size_t    fts_maxarglen (char * const *);\n> -static void      fts_padjust (FTSOBJ *, FTSENTRY *);\n> -static bool      fts_palloc (FTSOBJ *, size_t);\n> -static FTSENTRY  *fts_sort (FTSOBJ *, FTSENTRY *, size_t);\n> -static unsigned short int fts_stat (FTSOBJ *, FTSENTRY *, bool);\n> -static int      fts_safe_changedir (FTSOBJ *, FTSENTRY *, int, const char *);\n> -\n> -#include \"fts-cycle.c\"\n> -\n> -#ifndef MAX\n> -# define MAX(a,b) ((a) > (b) ? (a) : (b))\n> -#endif\n> -\n> -#ifndef SIZE_MAX\n> -# define SIZE_MAX ((size_t) -1)\n> -#endif\n> -\n> -#define ISDOT(a)        (a[0] == '.' && (!a[1] || (a[1] == '.' && !a[2])))\n> -\n> -#define CLR(opt)        (sp->fts_options &= ~(opt))\n> -#define ISSET(opt)      ((sp->fts_options & (opt)) != 0)\n> -#define SET(opt)        (sp->fts_options |= (opt))\n> -\n> -/* FIXME: FTS_NOCHDIR is now misnamed.\n> -   Call it FTS_USE_FULL_RELATIVE_FILE_NAMES instead. */\n> -#define FCHDIR(sp, fd)                                  \\\n> -  (!ISSET(FTS_NOCHDIR) && (ISSET(FTS_CWDFD)             \\\n> -                           ? (cwd_advance_fd ((sp), (fd), true), 0) \\\n> -                           : fchdir (fd)))\n> -\n> -\n> -/* fts_build flags */\n> -/* FIXME: make this an enum */\n> -#define BCHILD          1               /* fts_children */\n> -#define BNAMES          2               /* fts_children, names only */\n> -#define BREAD           3               /* fts_read */\n> -\n> -#if GNULIB_FTS_DEBUG\n> -# include <inttypes.h>\n> -# include <stdio.h>\n> -bool fts_debug = false;\n> -# define Dprintf(x) do { if (fts_debug) printf x; } while (false)\n> -static void fd_ring_check (FTSOBJ const *);\n> -static void fd_ring_print (FTSOBJ const *, FILE *, char const *);\n> -#else\n> -# define Dprintf(x)\n> -# define fd_ring_check(x)\n> -# define fd_ring_print(a, b, c)\n> -#endif\n> -\n> -#define LEAVE_DIR(Fts, Ent, Tag)                                \\\n> -  do                                                            \\\n> -    {                                                           \\\n> -      Dprintf ((\"  %s-leaving: %s\\n\", Tag, (Ent)->fts_path));   \\\n> -      leave_dir (Fts, Ent);                                     \\\n> -      fd_ring_check (Fts);                                      \\\n> -    }                                                           \\\n> -  while (false)\n> -\n> -static void\n> -fd_ring_clear (I_ring *fd_ring)\n> -{\n> -  while ( ! i_ring_empty (fd_ring))\n> -    {\n> -      int fd = i_ring_pop (fd_ring);\n> -      if (0 <= fd)\n> -        close (fd);\n> -    }\n> -}\n> -\n> -/* Overload the fts_statp->st_size member (otherwise unused, when\n> -   fts_info is FTS_NSOK) to indicate whether fts_read should stat\n> -   this entry or not.  */\n> -static void\n> -fts_set_stat_required (FTSENTRY *p, bool required)\n> -{\n> -  fts_assert (p->fts_info == FTS_NSOK);\n> -  p->fts_statp->st_size = (required\n> -                           ? FTS_STAT_REQUIRED\n> -                           : FTS_NO_STAT_REQUIRED);\n> -}\n> -\n> -/* Virtual fchdir.  Advance SP's working directory file descriptor,\n> -   SP->fts_cwd_fd, to FD, and push the previous value onto the fd_ring.\n> -   CHDIR_DOWN_ONE is true if FD corresponds to an entry in the directory\n> -   open on sp->fts_cwd_fd; i.e., to move the working directory one level\n> -   down.  */\n> -static void\n> -internal_function\n> -cwd_advance_fd (FTSOBJ *sp, int fd, bool chdir_down_one)\n> -{\n> -  int old = sp->fts_cwd_fd;\n> -  fts_assert (old != fd || old == AT_FDCWD);\n> -\n> -  if (chdir_down_one)\n> -    {\n> -      /* Push \"old\" onto the ring.\n> -         If the displaced file descriptor is non-negative, close it.  */\n> -      int prev_fd_in_slot = i_ring_push (&sp->fts_fd_ring, old);\n> -      fd_ring_print (sp, stderr, \"post-push\");\n> -      if (0 <= prev_fd_in_slot)\n> -        close (prev_fd_in_slot); /* ignore any close failure */\n> -    }\n> -  else if ( ! ISSET (FTS_NOCHDIR))\n> -    {\n> -      if (0 <= old)\n> -        close (old); /* ignore any close failure */\n> -    }\n> -\n> -  sp->fts_cwd_fd = fd;\n> -}\n> -\n> -/* Restore the initial, pre-traversal, \"working directory\".\n> -   In FTS_CWDFD mode, we merely call cwd_advance_fd, otherwise,\n> -   we may actually change the working directory.\n> -   Return 0 upon success. Upon failure, set errno and return nonzero.  */\n> -static int\n> -restore_initial_cwd (FTSOBJ *sp)\n> -{\n> -  int fail = FCHDIR (sp, ISSET (FTS_CWDFD) ? AT_FDCWD : sp->fts_rfd);\n> -  fd_ring_clear (&(sp->fts_fd_ring));\n> -  return fail;\n> -}\n> -\n> -/* Open the directory DIR if possible, and return a file\n> -   descriptor.  Return -1 and set errno on failure.  It doesn't matter\n> -   whether the file descriptor has read or write access.  */\n> -\n> -static int\n> -internal_function\n> -diropen (FTSOBJ const *sp, char const *dir)\n> -{\n> -  int open_flags = (O_SEARCH | O_CLOEXEC | O_DIRECTORY | O_NOCTTY | O_NONBLOCK\n> -                    | (ISSET (FTS_PHYSICAL) ? O_NOFOLLOW : 0));\n> -\n> -  int fd = (ISSET (FTS_CWDFD)\n> -            ? openat (sp->fts_cwd_fd, dir, open_flags)\n> -            : open (dir, open_flags));\n> -  return fd;\n> -}\n> -\n> -FTSOBJ *\n> -FTS_OPEN (char * const *argv,\n> -          register int options,\n> -          int (*compar) (const FTSENTRY **, const FTSENTRY **))\n> -{\n> -        /* Options check: glibc added other flags after FTS_NAMEONLY and\n> -\t   FTS_STOP, and they are assumed to be private.  */\n> -        if (options & ~FTS_OPTIONMASK\n> -#if _LIBC\n> -\t    || options & (FTS_NAMEONLY | FTS_STOP)\n> -#endif\n> -\t    ) {\n> -                __set_errno (EINVAL);\n> -                return (NULL);\n> -        }\n> -        if ((options & FTS_NOCHDIR) && (options & FTS_CWDFD)) {\n> -                __set_errno (EINVAL);\n> -                return (NULL);\n> -        }\n> -#if !_LIBC\n> -        if ( ! (options & (FTS_LOGICAL | FTS_PHYSICAL))) {\n> -                __set_errno (EINVAL);\n> -                return (NULL);\n> -        }\n> -#else\n> -\t/* glibc historically falls to FTS_PHYSICAL if no FTS_PHYSICAL or\n> -\t   FTS_LOGICAL is specified.  */\n> -        if (! (options & (FTS_PHYSICAL | FTS_LOGICAL)))\n> -\t  options |= FTS_PHYSICAL;\n> -#endif\n> -\n> -        /* Allocate/initialize the stream */\n> -        register FTSOBJ *sp = calloc (1, sizeof *sp);\n> -        if (sp == NULL)\n> -                return (NULL);\n> -        sp->fts_compar = FTS_COMPAR_CAST(compar);\n> -        sp->fts_options = options;\n> -\n> -        /* Logical walks turn on NOCHDIR; symbolic links are too hard. */\n> -        if (ISSET(FTS_LOGICAL)) {\n> -                SET(FTS_NOCHDIR);\n> -                CLR(FTS_CWDFD);\n> -        }\n> -\n> -        /* Initialize fts_cwd_fd.  */\n> -        sp->fts_cwd_fd = AT_FDCWD;\n> -        if ( ISSET(FTS_CWDFD) && ! HAVE_OPENAT_SUPPORT)\n> -          {\n> -            /* While it isn't technically necessary to open \".\" this\n> -               early, doing it here saves us the trouble of ensuring\n> -               later (where it'd be messier) that \".\" can in fact\n> -               be opened.  If not, revert to FTS_NOCHDIR mode.  */\n> -            int fd = open (\".\", O_SEARCH | O_CLOEXEC);\n> -            if (fd < 0)\n> -              {\n> -                /* Even if \".\" is unreadable, don't revert to FTS_NOCHDIR mode\n> -                   on systems like Linux+PROC_FS, where our openat emulation\n> -                   is good enough.  Note: on a system that emulates\n> -                   openat via /proc, this technique can still fail, but\n> -                   only in extreme conditions, e.g., when the working\n> -                   directory cannot be saved (i.e. save_cwd fails) --\n> -                   and that happens on Linux only when \".\" is unreadable\n> -                   and the CWD would be longer than PATH_MAX.\n> -                   FIXME: once Linux kernel openat support is well established,\n> -                   replace the above open call and this entire if/else block\n> -                   with the body of the if-block below.  */\n> -                if ( openat_needs_fchdir ())\n> -                  {\n> -                    SET(FTS_NOCHDIR);\n> -                    CLR(FTS_CWDFD);\n> -                  }\n> -              }\n> -            else\n> -              {\n> -                close (fd);\n> -              }\n> -          }\n> -\n> -        /*\n> -         * Start out with 1K of file name space, and enough, in any case,\n> -         * to hold the user's file names.\n> -         */\n> -#ifndef MAXPATHLEN\n> -# define MAXPATHLEN 1024\n> -#endif\n> -        {\n> -          size_t maxarglen = fts_maxarglen(argv);\n> -          if (! fts_palloc(sp, MAX(maxarglen, MAXPATHLEN)))\n> -                  goto mem1;\n> -        }\n> -\n> -        /* Allocate/initialize root's parent. */\n> -        FTSENTRY *parent = NULL;\n> -        if (*argv != NULL) {\n> -                if ((parent = fts_alloc(sp, \"\", 0)) == NULL)\n> -                        goto mem2;\n> -                parent->fts_level = FTS_ROOTPARENTLEVEL;\n> -          }\n> -\n> -        /* The classic fts implementation would call fts_stat with\n> -           a new entry for each iteration of the loop below.\n> -           If the comparison function is not specified or if the\n> -           FTS_DEFER_STAT option is in effect, don't stat any entry\n> -           in this loop.  This is an attempt to minimize the interval\n> -           between the initial stat/lstat/fstatat and the point at which\n> -           a directory argument is first opened.  This matters for any\n> -           directory command line argument that resides on a file system\n> -           without genuine i-nodes.  If you specify FTS_DEFER_STAT along\n> -           with a comparison function, that function must not access any\n> -           data via the fts_statp pointer.  */\n> -        bool defer_stat = (compar == NULL || ISSET(FTS_DEFER_STAT));\n> -\n> -        /* Allocate/initialize root(s). */\n> -        register FTSENTRY *root;\n> -        register size_t nitems;\n> -        FTSENTRY *tmp = NULL;     /* pacify gcc */\n> -        for (root = NULL, nitems = 0; *argv != NULL; ++argv, ++nitems) {\n> -                /* *Do* allow zero-length file names. */\n> -                size_t len = strlen(*argv);\n> -\n> -                if ( ! (options & FTS_VERBATIM))\n> -                  {\n> -                    /* If there are two or more trailing slashes, trim all but one,\n> -                       but don't change \"//\" to \"/\", and do map \"///\" to \"/\".  */\n> -                    char const *v = *argv;\n> -                    if (2 < len && v[len - 1] == '/')\n> -                      while (1 < len && v[len - 2] == '/')\n> -                        --len;\n> -                  }\n> -\n> -                register FTSENTRY *p = fts_alloc(sp, *argv, len);\n> -                if (p == NULL)\n> -                        goto mem3;\n> -                p->fts_level = FTS_ROOTLEVEL;\n> -                p->fts_parent = parent;\n> -                p->fts_accpath = p->fts_name;\n> -                /* Even when defer_stat is true, be sure to stat the first\n> -                   command line argument, since fts_read (at least with\n> -                   FTS_XDEV) requires that.  */\n> -                if (defer_stat && root != NULL) {\n> -                        p->fts_info = FTS_NSOK;\n> -                        fts_set_stat_required(p, true);\n> -                } else {\n> -                        p->fts_info = fts_stat(sp, p, false);\n> -                }\n> -\n> -                /*\n> -                 * If comparison routine supplied, traverse in sorted\n> -                 * order; otherwise traverse in the order specified.\n> -                 */\n> -                if (compar) {\n> -                        p->fts_link = root;\n> -                        root = p;\n> -                } else {\n> -                        p->fts_link = NULL;\n> -                        if (root == NULL)\n> -                                tmp = root = p;\n> -                        else {\n> -                                tmp->fts_link = p;\n> -                                tmp = p;\n> -                        }\n> -                }\n> -        }\n> -        if (compar && nitems > 1)\n> -                root = fts_sort(sp, root, nitems);\n> -\n> -        /*\n> -         * Allocate a dummy pointer and make fts_read think that we've just\n> -         * finished the node before the root(s); set p->fts_info to FTS_INIT\n> -         * so that everything about the \"current\" node is ignored.\n> -         */\n> -        if ((sp->fts_cur = fts_alloc(sp, \"\", 0)) == NULL)\n> -                goto mem3;\n> -        sp->fts_cur->fts_link = root;\n> -        sp->fts_cur->fts_info = FTS_INIT;\n> -        sp->fts_cur->fts_level = 1;\n> -        if (! setup_dir (sp))\n> -                goto mem3;\n> -\n> -        /*\n> -         * If using chdir(2), grab a file descriptor pointing to dot to ensure\n> -         * that we can get back here; this could be avoided for some file names,\n> -         * but almost certainly not worth the effort.  Slashes, symbolic links,\n> -         * and \"..\" are all fairly nasty problems.  Note, if we can't get the\n> -         * descriptor we run anyway, just more slowly.\n> -         */\n> -        if (!ISSET(FTS_NOCHDIR) && !ISSET(FTS_CWDFD)\n> -            && (sp->fts_rfd = diropen (sp, \".\")) < 0)\n> -                SET(FTS_NOCHDIR);\n> -\n> -        i_ring_init (&sp->fts_fd_ring, -1);\n> -        return (sp);\n> -\n> -mem3:   fts_lfree(root);\n> -        free(FTSENT_WRAPPER(parent));\n> -mem2:   free(sp->fts_path);\n> -mem1:   free(sp);\n> -        return (NULL);\n> -}\n> -\n> -static void\n> -internal_function\n> -fts_load (FTSOBJ *sp, register FTSENTRY *p)\n> -{\n> -        /*\n> -         * Load the stream structure for the next traversal.  Since we don't\n> -         * actually enter the directory until after the preorder visit, set\n> -         * the fts_accpath field specially so the chdir gets done to the right\n> -         * place and the user can access the first node.  From fts_open it's\n> -         * known that the file name will fit.\n> -         */\n> -        register size_t len = p->fts_pathlen = p->fts_namelen;\n> -        memmove(sp->fts_path, p->fts_name, len + 1);\n> -        register char *cp = strrchr(p->fts_name, '/');\n> -        if (cp && (cp != p->fts_name || cp[1])) {\n> -                len = strlen(++cp);\n> -                memmove(p->fts_name, cp, len + 1);\n> -                p->fts_namelen = len;\n> -        }\n> -        p->fts_accpath = p->fts_path = sp->fts_path;\n> -}\n> -\n> -int\n> -FTS_CLOSE (FTSOBJ *sp)\n> -{\n> -        /*\n> -         * This still works if we haven't read anything -- the dummy structure\n> -         * points to the root list, so we step through to the end of the root\n> -         * list which has a valid parent pointer.\n> -         */\n> -        if (sp->fts_cur) {\n> -                register FTSENTRY *p;\n> -                for (p = sp->fts_cur; p->fts_level >= FTS_ROOTLEVEL;) {\n> -                        register FTSENTRY *freep = p;\n> -                        p = p->fts_link != NULL ? p->fts_link : p->fts_parent;\n> -                        free(FTSENT_WRAPPER(freep));\n> -                }\n> -                free(FTSENT_WRAPPER(p));\n> -        }\n> -\n> -        /* Free up child linked list, sort array, file name buffer. */\n> -        if (sp->fts_child)\n> -                fts_lfree(sp->fts_child);\n> -        free(sp->fts_array);\n> -        free(sp->fts_path);\n> -\n> -        int saved_errno = 0;\n> -        if (ISSET(FTS_CWDFD))\n> -          {\n> -            if (0 <= sp->fts_cwd_fd)\n> -              if (close (sp->fts_cwd_fd))\n> -                saved_errno = errno;\n> -          }\n> -        else if (!ISSET(FTS_NOCHDIR))\n> -          {\n> -            /* Return to original directory, save errno if necessary. */\n> -            if (fchdir(sp->fts_rfd))\n> -              saved_errno = errno;\n> -\n> -            /* If close fails, record errno only if saved_errno is zero,\n> -               so that we report the probably-more-meaningful fchdir errno.  */\n> -            if (close (sp->fts_rfd))\n> -              if (saved_errno == 0)\n> -                saved_errno = errno;\n> -          }\n> -\n> -        fd_ring_clear (&sp->fts_fd_ring);\n> -\n> -        if (sp->fts_leaf_optimization_works_ht)\n> -          hash_free (sp->fts_leaf_optimization_works_ht);\n> -\n> -        free_dir (sp);\n> -\n> -        /* Free up the stream pointer. */\n> -        free(sp);\n> -\n> -        /* Set errno and return. */\n> -        if (saved_errno) {\n> -                __set_errno (saved_errno);\n> -                return (-1);\n> -        }\n> -\n> -        return (0);\n> -}\n> -\n> -/* Minimum link count of a traditional Unix directory.  When leaf\n> -   optimization is OK and a directory's st_nlink == MIN_DIR_NLINK,\n> -   then the directory has no subdirectories.  */\n> -enum { MIN_DIR_NLINK = 2 };\n> -\n> -/* Whether leaf optimization is OK for a directory.  */\n> -enum leaf_optimization\n> -  {\n> -    /* st_nlink is not reliable for this directory's subdirectories.  */\n> -    NO_LEAF_OPTIMIZATION,\n> -\n> -    /* st_nlink == 2 means the directory lacks subdirectories.  */\n> -    OK_LEAF_OPTIMIZATION\n> -  };\n> -\n> -#if (defined __linux__ || defined __ANDROID__) \\\n> -  && HAVE_SYS_VFS_H && HAVE_FSTATFS && HAVE_STRUCT_STATFS_F_TYPE\n> -\n> -# include <sys/vfs.h>\n> -\n> -/* Linux-specific constants from coreutils' src/fs.h */\n> -# define S_MAGIC_AFS 0x5346414F\n> -# define S_MAGIC_CIFS 0xFF534D42\n> -# define S_MAGIC_LUSTRE 0x0BD00BD0\n> -# define S_MAGIC_NFS 0x6969\n> -# define S_MAGIC_PROC 0x9FA0\n> -# define S_MAGIC_TMPFS 0x1021994\n> -\n> -# ifdef HAVE___FSWORD_T\n> -typedef __fsword_t fsword;\n> -# else\n> -typedef long int fsword;\n> -# endif\n> -\n> -/* Map a stat.st_dev number to a file system type number f_ftype.  */\n> -struct dev_type\n> -{\n> -  dev_t st_dev;\n> -  fsword f_type;\n> -};\n> -\n> -/* Use a tiny initial size.  If a traversal encounters more than\n> -   a few devices, the cost of growing/rehashing this table will be\n> -   rendered negligible by the number of inodes processed.  */\n> -enum { DEV_TYPE_HT_INITIAL_SIZE = 13 };\n> -\n> -static size_t\n> -dev_type_hash (void const *x, size_t table_size)\n> -{\n> -  struct dev_type const *ax = x;\n> -  uintmax_t dev = ax->st_dev;\n> -  return dev % table_size;\n> -}\n> -\n> -static bool\n> -dev_type_compare (void const *x, void const *y)\n> -{\n> -  struct dev_type const *ax = x;\n> -  struct dev_type const *ay = y;\n> -  return ax->st_dev == ay->st_dev;\n> -}\n> -\n> -/* Return the file system type of P with file descriptor FD, or 0 if not known.\n> -   If FD is negative, P's file descriptor is unavailable.\n> -   Try to cache known values.  */\n> -\n> -static fsword\n> -filesystem_type (FTSENTRY const *p, int fd)\n> -{\n> -  FTSOBJ *sp = FTSENT_FTS(p);\n> -\n> -  /* If we're not in CWDFD mode, don't bother with this optimization,\n> -     since the caller is not serious about performance.  */\n> -  if (!ISSET (FTS_CWDFD))\n> -    return 0;\n> -\n> -  Hash_table *h = sp->fts_leaf_optimization_works_ht;\n> -  if (! h)\n> -    h = sp->fts_leaf_optimization_works_ht\n> -      = hash_initialize (DEV_TYPE_HT_INITIAL_SIZE, NULL, dev_type_hash,\n> -                         dev_type_compare, free);\n> -\n> -  if (h)\n> -    {\n> -      struct dev_type tmp;\n> -      tmp.st_dev = p->fts_statp->st_dev;\n> -      struct dev_type *ent = hash_lookup (h, &tmp);\n> -      if (ent)\n> -        return ent->f_type;\n> -    }\n> -\n> -  /* Look-up failed.  Query directly and cache the result.  */\n> -  struct STRUCT_STATFS fs_buf;\n> -  if (fd < 0 || FSTATFS (fd, &fs_buf) != 0)\n> -    return 0;\n> -\n> -  if (h)\n> -    {\n> -      struct dev_type *t2 = malloc (sizeof *t2);\n> -      if (t2)\n> -        {\n> -          t2->st_dev = p->fts_statp->st_dev;\n> -          t2->f_type = fs_buf.f_type;\n> -\n> -          struct dev_type *ent = hash_insert (h, t2);\n> -          if (ent)\n> -            fts_assert (ent == t2);\n> -          else\n> -            free (t2);\n> -        }\n> -    }\n> -\n> -  return fs_buf.f_type;\n> -}\n> -\n> -/* Return true if sorting dirents on inode numbers is known to improve\n> -   traversal performance for the directory P with descriptor DIR_FD.\n> -   Return false otherwise.  When in doubt, return true.\n> -   DIR_FD is negative if unavailable.  */\n> -static bool\n> -dirent_inode_sort_may_be_useful (FTSENTRY const *p, int dir_fd)\n> -{\n> -  /* Skip the sort only if we can determine efficiently\n> -     that skipping it is the right thing to do.\n> -     The cost of performing an unnecessary sort is negligible,\n> -     while the cost of *not* performing it can be O(N^2) with\n> -     a very large constant.  */\n> -\n> -  switch (filesystem_type (p, dir_fd))\n> -    {\n> -    case S_MAGIC_LUSTRE:\n> -      /* On Lustre, sorting directory entries interferes with its ability to\n> -         prefetch file metadata (via statahead).  This would make a command\n> -         like 'du' around 9 times slower.  See\n> -         <https://bugs.gnu.org/80106>.  */\n> -    case S_MAGIC_CIFS:\n> -    case S_MAGIC_NFS:\n> -    case S_MAGIC_TMPFS:\n> -      /* On a file system of any of these types, sorting\n> -         is unnecessary, and hence wasteful.  */\n> -      return false;\n> -\n> -    default:\n> -      return true;\n> -    }\n> -}\n> -\n> -/* Given an FTS entry P for a directory with descriptor DIR_FD,\n> -   return whether it is valid to apply leaf optimization.\n> -   The optimization is valid if a directory's st_nlink value equal\n> -   to MIN_DIR_NLINK means the directory has no subdirectories.\n> -   DIR_FD is negative if unavailable.  */\n> -static enum leaf_optimization\n> -leaf_optimization (FTSENTRY const *p, int dir_fd)\n> -{\n> -  switch (filesystem_type (p, dir_fd))\n> -    {\n> -    case 0:\n> -      /* Leaf optimization is unsafe if the file system type is unknown.  */\n> -      FALLTHROUGH;\n> -    case S_MAGIC_AFS:\n> -      /* Although AFS mount points are not counted in st_nlink, they\n> -         act like directories.  See <https://bugs.debian.org/143111>.  */\n> -      FALLTHROUGH;\n> -    case S_MAGIC_CIFS:\n> -      /* Leaf optimization causes 'find' to abort.  See\n> -         <https://lists.gnu.org/r/bug-gnulib/2018-04/msg00015.html>.  */\n> -      FALLTHROUGH;\n> -    case S_MAGIC_NFS:\n> -      /* NFS provides usable dirent.d_type but not necessarily for all entries\n> -         of large directories, so as per <https://bugzilla.redhat.com/1252549>\n> -         NFS should return true.  However st_nlink values are not accurate on\n> -         all implementations as per <https://bugzilla.redhat.com/1299169>.  */\n> -      FALLTHROUGH;\n> -    case S_MAGIC_PROC:\n> -      /* Per <https://bugs.debian.org/cgi-bin/bugreport.cgi?bug=143111> /proc\n> -         may have bogus stat.st_nlink values.  */\n> -      return NO_LEAF_OPTIMIZATION;\n> -\n> -    default:\n> -      return OK_LEAF_OPTIMIZATION;\n> -    }\n> -}\n> -\n> -#else\n> -static bool\n> -dirent_inode_sort_may_be_useful (_GL_UNUSED FTSENTRY const *p,\n> -                                 _GL_UNUSED int dir_fd)\n> -{\n> -  return true;\n> -}\n> -static enum leaf_optimization\n> -leaf_optimization (_GL_UNUSED FTSENTRY const *p, _GL_UNUSED int dir_fd)\n> -{\n> -  return NO_LEAF_OPTIMIZATION;\n> -}\n> -#endif\n> -\n> -/*\n> - * Special case of \"/\" at the end of the file name so that slashes aren't\n> - * appended which would cause file names to be written as \"....//foo\".\n> - */\n> -#define NAPPEND(p)                                                      \\\n> -        (p->fts_path[p->fts_pathlen - 1] == '/'                         \\\n> -            ? p->fts_pathlen - 1 : p->fts_pathlen)\n> -\n> -FTSENTRY *\n> -FTS_READ (FTSOBJ *sp)\n> -{\n> -        /* If finished or unrecoverable error, return NULL. */\n> -        if (sp->fts_cur == NULL || ISSET(FTS_STOP))\n> -                return (NULL);\n> -\n> -        /* Set current node pointer. */\n> -        register FTSENTRY *p = sp->fts_cur;\n> -\n> -        /* Save and zero out user instructions. */\n> -        register unsigned short int instr = p->fts_instr;\n> -        p->fts_instr = FTS_NOINSTR;\n> -\n> -        /* Any type of file may be re-visited; re-stat and re-turn. */\n> -        if (instr == FTS_AGAIN) {\n> -                p->fts_info = fts_stat(sp, p, false);\n> -                return (p);\n> -        }\n> -        Dprintf ((\"fts_read: p=%s\\n\",\n> -                  p->fts_info == FTS_INIT ? \"\" : p->fts_path));\n> -\n> -        /*\n> -         * Following a symlink -- SLNONE test allows application to see\n> -         * SLNONE and recover.  If indirecting through a symlink, have\n> -         * keep a pointer to current location.  If unable to get that\n> -         * pointer, follow fails.\n> -         */\n> -        if (instr == FTS_FOLLOW &&\n> -            (p->fts_info == FTS_SL || p->fts_info == FTS_SLNONE)) {\n> -                p->fts_info = fts_stat(sp, p, true);\n> -                if (p->fts_info == FTS_D && !ISSET(FTS_NOCHDIR)) {\n> -                        if ((p->fts_symfd = diropen (sp, \".\")) < 0) {\n> -                                p->fts_errno = errno;\n> -                                p->fts_info = FTS_ERR;\n> -                        } else\n> -                                p->fts_flags |= FTS_SYMFOLLOW;\n> -                }\n> -                goto check_for_dir;\n> -        }\n> -\n> -        /* Directory in pre-order. */\n> -        if (p->fts_info == FTS_D) {\n> -                /* If skipped or crossed mount point, do post-order visit. */\n> -                if (instr == FTS_SKIP ||\n> -                    (ISSET(FTS_XDEV) && p->fts_statp->st_dev != sp->fts_dev)) {\n> -                        if (p->fts_flags & FTS_SYMFOLLOW)\n> -                                (void)close(p->fts_symfd);\n> -                        if (sp->fts_child) {\n> -                                fts_lfree(sp->fts_child);\n> -                                sp->fts_child = NULL;\n> -                        }\n> -                        p->fts_info = FTS_DP;\n> -                        LEAVE_DIR (sp, p, \"1\");\n> -                        return (p);\n> -                }\n> -\n> -                /* Rebuild if only read the names and now traversing. */\n> -                if (sp->fts_child != NULL && ISSET(FTS_NAMEONLY)) {\n> -                        CLR(FTS_NAMEONLY);\n> -                        fts_lfree(sp->fts_child);\n> -                        sp->fts_child = NULL;\n> -                }\n> -\n> -                /*\n> -                 * Cd to the subdirectory.\n> -                 *\n> -                 * If have already read and now fail to chdir, whack the list\n> -                 * to make the names come out right, and set the parent errno\n> -                 * so the application will eventually get an error condition.\n> -                 * Set the FTS_DONTCHDIR flag so that when we logically change\n> -                 * directories back to the parent we don't do a chdir.\n> -                 *\n> -                 * If haven't read do so.  If the read fails, fts_build sets\n> -                 * FTS_STOP or the fts_info field of the node.\n> -                 */\n> -                if (sp->fts_child != NULL) {\n> -                        if (fts_safe_changedir(sp, p, -1, p->fts_accpath)) {\n> -                                p->fts_errno = errno;\n> -                                p->fts_flags |= FTS_DONTCHDIR;\n> -                                for (p = sp->fts_child; p != NULL;\n> -                                     p = p->fts_link)\n> -                                        p->fts_accpath =\n> -                                            p->fts_parent->fts_accpath;\n> -                        }\n> -                } else if ((sp->fts_child = fts_build(sp, BREAD)) == NULL) {\n> -                        if (ISSET(FTS_STOP))\n> -                                return (NULL);\n> -                        /* If fts_build's call to fts_safe_changedir failed\n> -                           because it was not able to fchdir into a\n> -                           subdirectory, tell the caller.  */\n> -                        if (p->fts_errno && p->fts_info != FTS_DNR)\n> -                                p->fts_info = FTS_ERR;\n> -                        LEAVE_DIR (sp, p, \"2\");\n> -                        return (p);\n> -                }\n> -                p = sp->fts_child;\n> -                sp->fts_child = NULL;\n> -                goto name;\n> -        }\n> -\n> -        /* Move to the next node on this level. */\n> -next: ;\n> -        register FTSENTRY *tmp = p;\n> -\n> -        /* If we have so many directory entries that we're reading them\n> -           in batches, and we've reached the end of the current batch,\n> -           read in a new batch.  */\n> -        if (p->fts_link == NULL && FTSENT_DIRP(p->fts_parent))\n> -          {\n> -            p = tmp->fts_parent;\n> -            sp->fts_cur = p;\n> -            sp->fts_path[p->fts_pathlen] = '\\0';\n> -\n> -            if ((p = fts_build (sp, BREAD)) == NULL)\n> -              {\n> -                if (ISSET(FTS_STOP))\n> -                  return NULL;\n> -                goto cd_dot_dot;\n> -              }\n> -\n> -            free(FTSENT_WRAPPER(tmp));\n> -            goto name;\n> -          }\n> -\n> -        if ((p = p->fts_link) != NULL) {\n> -                sp->fts_cur = p;\n> -                free(FTSENT_WRAPPER(tmp));\n> -\n> -                /*\n> -                 * If reached the top, return to the original directory (or\n> -                 * the root of the tree), and load the file names for the next\n> -                 * root.\n> -                 */\n> -                if (p->fts_level == FTS_ROOTLEVEL) {\n> -                        if (restore_initial_cwd(sp)) {\n> -                                SET(FTS_STOP);\n> -                                return (NULL);\n> -                        }\n> -                        free_dir(sp);\n> -                        fts_load(sp, p);\n> -                        if (! setup_dir(sp)) {\n> -                                free_dir(sp);\n> -                                return (NULL);\n> -                        }\n> -                        goto check_for_dir;\n> -                }\n> -\n> -                /*\n> -                 * User may have called fts_set on the node.  If skipped,\n> -                 * ignore.  If followed, get a file descriptor so we can\n> -                 * get back if necessary.\n> -                 */\n> -                if (p->fts_instr == FTS_SKIP)\n> -                        goto next;\n> -                if (p->fts_instr == FTS_FOLLOW) {\n> -                        p->fts_info = fts_stat(sp, p, true);\n> -                        if (p->fts_info == FTS_D && !ISSET(FTS_NOCHDIR)) {\n> -                                if ((p->fts_symfd = diropen (sp, \".\")) < 0) {\n> -                                        p->fts_errno = errno;\n> -                                        p->fts_info = FTS_ERR;\n> -                                } else\n> -                                        p->fts_flags |= FTS_SYMFOLLOW;\n> -                        }\n> -                        p->fts_instr = FTS_NOINSTR;\n> -                }\n> -\n> -name:           {\n> -                  register char *t = sp->fts_path + NAPPEND(p->fts_parent);\n> -                  *t++ = '/';\n> -                  memmove(t, p->fts_name, p->fts_namelen + 1);\n> -                }\n> -check_for_dir:\n> -                sp->fts_cur = p;\n> -                if (p->fts_info == FTS_NSOK)\n> -                  {\n> -                    if (p->fts_statp->st_size == FTS_STAT_REQUIRED)\n> -                      p->fts_info = fts_stat(sp, p, false);\n> -                    else\n> -                      fts_assert (p->fts_statp->st_size == FTS_NO_STAT_REQUIRED);\n> -                  }\n> -\n> -                /* Skip files with different device numbers when FTS_MOUNT\n> -                   is set.  */\n> -                if (ISSET (FTS_MOUNT) && p->fts_info != FTS_NS &&\n> -                    p->fts_level != FTS_ROOTLEVEL &&\n> -                    p->fts_statp->st_dev != sp->fts_dev)\n> -                      goto next;\n> -\n> -                if (p->fts_info == FTS_D)\n> -                  {\n> -                    /* Now that P->fts_statp is guaranteed to be valid, if\n> -                       this is a command-line directory, record its device\n> -                       number, to be used for FTS_MOUNT and FTS_XDEV.  */\n> -                    if (p->fts_level == FTS_ROOTLEVEL)\n> -                      sp->fts_dev = p->fts_statp->st_dev;\n> -                    Dprintf ((\"  entering: %s\\n\", p->fts_path));\n> -                    if (! enter_dir (sp, p))\n> -                      return NULL;\n> -                  }\n> -                return p;\n> -        }\n> -cd_dot_dot:\n> -\n> -        /* Move up to the parent node. */\n> -        p = tmp->fts_parent;\n> -        sp->fts_cur = p;\n> -        free(FTSENT_WRAPPER(tmp));\n> -\n> -        if (p->fts_level == FTS_ROOTPARENTLEVEL) {\n> -                /*\n> -                 * Done; free everything up and set errno to 0 so the user\n> -                 * can distinguish between error and EOF.\n> -                 */\n> -                free(FTSENT_WRAPPER(p));\n> -                __set_errno (0);\n> -                return (sp->fts_cur = NULL);\n> -        }\n> -\n> -        fts_assert (p->fts_info != FTS_NSOK);\n> -\n> -        /* NUL terminate the file name.  */\n> -        sp->fts_path[p->fts_pathlen] = '\\0';\n> -\n> -        /*\n> -         * Return to the parent directory.  If at a root node, restore\n> -         * the initial working directory.  If we came through a symlink,\n> -         * go back through the file descriptor.  Otherwise, move up\n> -         * one level, via \"..\".\n> -         */\n> -        if (p->fts_level == FTS_ROOTLEVEL) {\n> -                if (restore_initial_cwd(sp)) {\n> -                        p->fts_errno = errno;\n> -                        SET(FTS_STOP);\n> -                }\n> -        } else if (p->fts_flags & FTS_SYMFOLLOW) {\n> -                if (FCHDIR(sp, p->fts_symfd)) {\n> -                        p->fts_errno = errno;\n> -                        SET(FTS_STOP);\n> -                }\n> -                (void)close(p->fts_symfd);\n> -        } else if (!(p->fts_flags & FTS_DONTCHDIR) &&\n> -                   fts_safe_changedir(sp, p->fts_parent, -1, \"..\")) {\n> -                p->fts_errno = errno;\n> -                SET(FTS_STOP);\n> -        }\n> -\n> -        /* If the directory causes a cycle, preserve the FTS_DC flag and keep\n> -           the corresponding dev/ino pair in the hash table.  It is going to be\n> -           removed when leaving the original directory.  */\n> -        if (p->fts_info != FTS_DC) {\n> -                p->fts_info = p->fts_errno ? FTS_ERR : FTS_DP;\n> -                if (p->fts_errno == 0)\n> -                        LEAVE_DIR (sp, p, \"3\");\n> -        }\n> -        return ISSET(FTS_STOP) ? NULL : p;\n> -}\n> -\n> -/*\n> - * Fts_set takes the stream as an argument although it's not used in this\n> - * implementation; it would be necessary if anyone wanted to add global\n> - * semantics to fts using fts_set.  An error return is allowed for similar\n> - * reasons.\n> - */\n> -/* ARGSUSED */\n> -int\n> -FTS_SET (_GL_UNUSED FTSOBJ *sp, FTSENTRY *p, int instr)\n> -{\n> -        if (instr != 0 && instr != FTS_AGAIN && instr != FTS_FOLLOW &&\n> -            instr != FTS_NOINSTR && instr != FTS_SKIP) {\n> -                __set_errno (EINVAL);\n> -                return (1);\n> -        }\n> -        p->fts_instr = instr;\n> -        return (0);\n> -}\n> -\n> -FTSENTRY *\n> -FTS_CHILDREN (FTSOBJ *sp, int instr)\n> -{\n> -        if (instr != 0 && instr != FTS_NAMEONLY) {\n> -                __set_errno (EINVAL);\n> -                return (NULL);\n> -        }\n> -\n> -        /* Set current node pointer. */\n> -        register FTSENTRY *p = sp->fts_cur;\n> -\n> -        /*\n> -         * Errno set to 0 so user can distinguish empty directory from\n> -         * an error.\n> -         */\n> -        __set_errno (0);\n> -\n> -        /* Fatal errors stop here. */\n> -        if (ISSET(FTS_STOP))\n> -                return (NULL);\n> -\n> -        /* Return logical hierarchy of user's arguments. */\n> -        if (p->fts_info == FTS_INIT)\n> -                return (p->fts_link);\n> -\n> -        /*\n> -         * If not a directory being visited in pre-order, stop here.  Could\n> -         * allow FTS_DNR, assuming the user has fixed the problem, but the\n> -         * same effect is available with FTS_AGAIN.\n> -         */\n> -        if (p->fts_info != FTS_D /* && p->fts_info != FTS_DNR */)\n> -                return (NULL);\n> -\n> -        /* Free up any previous child list. */\n> -        if (sp->fts_child != NULL)\n> -                fts_lfree(sp->fts_child);\n> -\n> -        if (instr == FTS_NAMEONLY) {\n> -                SET(FTS_NAMEONLY);\n> -                instr = BNAMES;\n> -        } else\n> -                instr = BCHILD;\n> -\n> -        /*\n> -         * If using chdir on a relative file name and called BEFORE fts_read\n> -         * does its chdir to the root of a traversal, we can lose -- we need to\n> -         * chdir into the subdirectory, and we don't know where the current\n> -         * directory is, so we can't get back so that the upcoming chdir by\n> -         * fts_read will work.\n> -         */\n> -        if (p->fts_level != FTS_ROOTLEVEL || p->fts_accpath[0] == '/' ||\n> -            ISSET(FTS_NOCHDIR))\n> -                return (sp->fts_child = fts_build(sp, instr));\n> -\n> -        int fd = diropen (sp, \".\");\n> -        if (fd < 0)\n> -                return (sp->fts_child = NULL);\n> -        sp->fts_child = fts_build(sp, instr);\n> -        if (ISSET(FTS_CWDFD))\n> -          {\n> -            cwd_advance_fd (sp, fd, true);\n> -          }\n> -        else\n> -          {\n> -            if (fchdir(fd))\n> -              {\n> -                int saved_errno = errno;\n> -                close (fd);\n> -                __set_errno (saved_errno);\n> -                return NULL;\n> -              }\n> -            close (fd);\n> -          }\n> -        return (sp->fts_child);\n> -}\n> -\n> -/* A comparison function to sort on increasing inode number.\n> -   For some file system types, sorting either way makes a huge\n> -   performance difference for a directory with very many entries,\n> -   but sorting on increasing values is slightly better than sorting\n> -   on decreasing values.  The difference is in the 5% range.  */\n> -static int\n> -fts_compare_ino (FTSENTRY const **a, FTSENTRY const **b)\n> -{\n> -  return _GL_CMP (a[0]->fts_statp->st_ino, b[0]->fts_statp->st_ino);\n> -}\n> -\n> -/* Map the dirent.d_type value, DTYPE, to the corresponding stat.st_mode\n> -   S_IF* bit and set ST.st_mode, thus clearing all other bits in that field.  */\n> -static void\n> -set_stat_type (struct STRUCT_STAT *st, unsigned int dtype)\n> -{\n> -  mode_t type;\n> -  switch (dtype)\n> -    {\n> -    case DT_BLK:\n> -      type = S_IFBLK;\n> -      break;\n> -    case DT_CHR:\n> -      type = S_IFCHR;\n> -      break;\n> -    case DT_DIR:\n> -      type = S_IFDIR;\n> -      break;\n> -    case DT_FIFO:\n> -      type = S_IFIFO;\n> -      break;\n> -    case DT_LNK:\n> -      type = S_IFLNK;\n> -      break;\n> -    case DT_REG:\n> -      type = S_IFREG;\n> -      break;\n> -    case DT_SOCK:\n> -      type = S_IFSOCK;\n> -      break;\n> -    default:\n> -      type = 0;\n> -    }\n> -  st->st_mode = type;\n> -}\n> -\n> -#define closedir_and_clear(dirp)                \\\n> -  do                                            \\\n> -    {                                           \\\n> -      closedir (dirp);                          \\\n> -      dirp = NULL;                              \\\n> -    }                                           \\\n> -  while (0)\n> -\n> -#define fts_opendir(file, Pdir_fd)                              \\\n> -        OPENDIRAT((! ISSET(FTS_NOCHDIR) && ISSET(FTS_CWDFD)     \\\n> -                   ? sp->fts_cwd_fd : AT_FDCWD),                \\\n> -                  file,                                         \\\n> -                  (((ISSET(FTS_PHYSICAL)                        \\\n> -                     && ! (ISSET(FTS_COMFOLLOW)                 \\\n> -                           && cur->fts_level == FTS_ROOTLEVEL)) \\\n> -                    ? O_NOFOLLOW : 0)),                         \\\n> -                  Pdir_fd)\n> -\n> -/*\n> - * This is the tricky part -- do not casually change *anything* in here.  The\n> - * idea is to build the linked list of entries that are used by fts_children\n> - * and fts_read.  There are lots of special cases.\n> - *\n> - * The real slowdown in walking the tree is the stat calls.  If FTS_NOSTAT is\n> - * set and it's a physical walk (so that symbolic links can't be directories),\n> - * we can do things quickly.  First, if it's a 4.4BSD file system, the type\n> - * of the file is in the directory entry.  Otherwise, we assume that the number\n> - * of subdirectories in a node is equal to the number of links to the parent.\n> - * The former skips all stat calls.  The latter skips stat calls in any leaf\n> - * directories and for any files after the subdirectories in the directory have\n> - * been found, cutting the stat calls by about 2/3.\n> - */\n> -static FTSENTRY *\n> -internal_function\n> -fts_build (register FTSOBJ *sp, int type)\n> -{\n> -        FTSENTRY *cur = sp->fts_cur;\n> -        bool continue_readdir = !!FTSENT_DIRP(cur);\n> -\n> -        /* When cur->fts_dirp is non-NULL, that means we should\n> -           continue calling readdir on that existing DIR* pointer\n> -           rather than opening a new one.  */\n> -        int dir_fd;\n> -        if (continue_readdir)\n> -          {\n> -            DIR *dp = FTSENT_DIRP(cur);\n> -            dir_fd = dirfd (dp);\n> -            if (dir_fd < 0)\n> -              {\n> -                int dirfd_errno = errno;\n> -                closedir_and_clear (FTSENT_DIRP(cur));\n> -                if (type == BREAD)\n> -                  {\n> -                    cur->fts_info = FTS_DNR;\n> -                    cur->fts_errno = dirfd_errno;\n> -                  }\n> -                return NULL;\n> -              }\n> -          }\n> -        else\n> -          {\n> -            /* Open the directory for reading.  If this fails, we're done.\n> -               If being called from fts_read, set the fts_info field. */\n> -            if ((FTSENT_DIRP (cur) = fts_opendir(cur->fts_accpath, &dir_fd)) == NULL)\n> -              {\n> -                if (type == BREAD)\n> -                  {\n> -                    cur->fts_info = FTS_DNR;\n> -                    cur->fts_errno = errno;\n> -                  }\n> -                return NULL;\n> -              }\n> -            /* Rather than calling fts_stat for each and every entry encountered\n> -               in the readdir loop (below), stat each directory only right after\n> -               opening it.  */\n> -            bool stat_optimization = cur->fts_info == FTS_NSOK;\n> -\n> -            if (stat_optimization\n> -                /* Also read the stat info again after opening a directory to\n> -                   reveal eventual changes caused by a submount triggered by\n> -                   the traversal.  But do it only for utilities which use\n> -                   FTS_TIGHT_CYCLE_CHECK.  Therefore, only find and du\n> -                   benefit/suffer from this feature for now.  */\n> -                || ISSET (FTS_TIGHT_CYCLE_CHECK))\n> -              {\n> -                if (!stat_optimization)\n> -                  LEAVE_DIR (sp, cur, \"4\");\n> -                if (FSTAT (dir_fd, cur->fts_statp) != 0)\n> -                  {\n> -                    int fstat_errno = errno;\n> -                    closedir_and_clear (FTSENT_DIRP(cur));\n> -                    if (type == BREAD)\n> -                      {\n> -                        cur->fts_errno = fstat_errno;\n> -                        cur->fts_info = FTS_NS;\n> -                      }\n> -                    __set_errno (fstat_errno);\n> -                    return NULL;\n> -                  }\n> -                if (stat_optimization)\n> -                  cur->fts_info = FTS_D;\n> -                else if (! enter_dir (sp, cur))\n> -                  {\n> -                    int saved_errno = errno;\n> -                    closedir_and_clear (FTSENT_DIRP(cur));\n> -                    __set_errno (saved_errno);\n> -                    return NULL;\n> -                  }\n> -              }\n> -          }\n> -\n> -        /* Maximum number of readdir entries to read at one time.  This\n> -           limitation is to avoid reading millions of entries into memory\n> -           at once.  When an fts_compar function is specified, we have no\n> -           choice: we must read all entries into memory before calling that\n> -           function.  But when no such function is specified, we can read\n> -           entries in batches that are large enough to help us with inode-\n> -           sorting, yet not so large that we risk exhausting memory.  */\n> -        size_t max_entries = sp->fts_compar ? SIZE_MAX : FTS_MAX_READDIR_ENTRIES;\n> -\n> -        /*\n> -         * If we're going to need to stat anything or we want to descend\n> -         * and stay in the directory, chdir.  If this fails we keep going,\n> -         * but set a flag so we don't chdir after the post-order visit.\n> -         * We won't be able to stat anything, but we can still return the\n> -         * names themselves.  Note, that since fts_read won't be able to\n> -         * chdir into the directory, it will have to return different file\n> -         * names than before, i.e. \"a/b\" instead of \"b\".  Since the node\n> -         * has already been visited in pre-order, have to wait until the\n> -         * post-order visit to return the error.  There is a special case\n> -         * here, if there was nothing to stat then it's not an error to\n> -         * not be able to stat.  This is all fairly nasty.  If a program\n> -         * needed sorted entries or stat information, they had better be\n> -         * checking FTS_NS on the returned nodes.\n> -         */\n> -        bool descend;\n> -        if (continue_readdir)\n> -          {\n> -            /* When resuming a short readdir run, we already have\n> -               the required dirp and dir_fd.  */\n> -            descend = true;\n> -          }\n> -        else\n> -          {\n> -            /* Try to descend unless it is a names-only fts_children,\n> -               or the directory is known to lack subdirectories.  */\n> -            descend = (type != BNAMES\n> -                       && ! (ISSET (FTS_NOSTAT) && ISSET (FTS_PHYSICAL)\n> -                             && ! ISSET (FTS_SEEDOT)\n> -                             && cur->fts_statp->st_nlink == MIN_DIR_NLINK\n> -                             && (leaf_optimization (cur, dir_fd)\n> -                                 != NO_LEAF_OPTIMIZATION)));\n> -            if (descend || type == BREAD)\n> -              {\n> -                if (ISSET(FTS_CWDFD))\n> -                  dir_fd = fcntl (dir_fd, F_DUPFD_CLOEXEC, STDERR_FILENO + 1);\n> -                if (dir_fd < 0 || fts_safe_changedir(sp, cur, dir_fd, NULL)) {\n> -                        if (descend && type == BREAD)\n> -                                cur->fts_errno = errno;\n> -                        cur->fts_flags |= FTS_DONTCHDIR;\n> -                        descend = false;\n> -                        closedir_and_clear(FTSENT_DIRP(cur));\n> -                        if (ISSET(FTS_CWDFD) && 0 <= dir_fd)\n> -                                close (dir_fd);\n> -                        FTSENT_DIRP(cur) = NULL;\n> -                } else\n> -                        descend = true;\n> -              }\n> -          }\n> -\n> -        /*\n> -         * Figure out the max file name length that can be stored in the\n> -         * current buffer -- the inner loop allocates more space as necessary.\n> -         * We really wouldn't have to do the maxlen calculations here, we\n> -         * could do them in fts_read before returning the name, but it's a\n> -         * lot easier here since the length is part of the dirent structure.\n> -         *\n> -         * If not changing directories set a pointer so that can just append\n> -         * each new component into the file name.\n> -         */\n> -        size_t len = NAPPEND(cur);\n> -        char *cp;\n> -        if (ISSET(FTS_NOCHDIR)) {\n> -                cp = sp->fts_path + len;\n> -                *cp++ = '/';\n> -        } else {\n> -                /* GCC, you're too verbose. */\n> -                cp = NULL;\n> -        }\n> -        len++;\n> -        size_t maxlen = sp->fts_pathlen - len;\n> -\n> -        ptrdiff_t level = cur->fts_level + 1;\n> -\n> -        /* Read the directory, attaching each entry to the \"link\" pointer. */\n> -        bool doadjust = false;\n> -        register FTSENTRY *head = NULL;\n> -        FTSENTRY *tail = NULL;\n> -        register size_t nitems = 0;\n> -        bool sort_by_inode = false;\n> -        while (FTSENT_DIRP(cur)) {\n> -                __set_errno (0);\n> -                struct dirent *dp = readdir(FTSENT_DIRP(cur));\n> -                if (dp == NULL) {\n> -                        /* Some readdir()s do not absorb ENOENT (dir\n> -                           deleted but open).  This bug was fixed in\n> -                           glibc 2.3 (2002).  */\n> -#if ! (2 < __GLIBC__ + (3 <= __GLIBC_MINOR__))\n> -                        if (errno == ENOENT)\n> -                          errno = 0;\n> -#endif\n> -                        if (errno) {\n> -                                cur->fts_errno = errno;\n> -                                /* If we've not read any items yet, treat\n> -                                   the error as if we can't access the dir.  */\n> -                                cur->fts_info = (continue_readdir || nitems)\n> -                                                ? FTS_ERR : FTS_DNR;\n> -                        }\n> -                        closedir_and_clear(FTSENT_DIRP(cur));\n> -                        break;\n> -                }\n> -                if (!ISSET(FTS_SEEDOT) && ISDOT(dp->d_name))\n> -                        continue;\n> -\n> -                size_t d_namelen = _D_EXACT_NAMLEN (dp);\n> -                register FTSENTRY *p = fts_alloc (sp, dp->d_name, d_namelen);\n> -                if (!p)\n> -                        goto mem1;\n> -                if (d_namelen >= maxlen) {\n> -                        /* include space for NUL */\n> -                        uintptr_t oldaddr = (uintptr_t) sp->fts_path;\n> -                        if (! fts_palloc(sp, d_namelen + len + 1)) {\n> -                                /*\n> -                                 * No more memory.  Save\n> -                                 * errno, free up the current structure and the\n> -                                 * structures already allocated.\n> -                                 */\n> -mem1: ;\n> -                                int saved_errno = errno;\n> -                                free(FTSENT_WRAPPER(p));\n> -                                fts_lfree(head);\n> -                                closedir_and_clear(FTSENT_DIRP(cur));\n> -                                cur->fts_info = FTS_ERR;\n> -                                SET(FTS_STOP);\n> -                                __set_errno (saved_errno);\n> -                                return (NULL);\n> -                        }\n> -                        /* Did realloc() change the pointer? */\n> -                        if (oldaddr != (uintptr_t) sp->fts_path) {\n> -                                doadjust = true;\n> -                                if (ISSET(FTS_NOCHDIR))\n> -                                        cp = sp->fts_path + len;\n> -                        }\n> -                        maxlen = sp->fts_pathlen - len;\n> -                }\n> -\n> -                size_t new_len = len + d_namelen;\n> -                if (new_len < len) {\n> -                        /*\n> -                         * In the unlikely event that we would end up\n> -                         * with a file name longer than SIZE_MAX, free up\n> -                         * the current structure and the structures already\n> -                         * allocated, then error out with ENAMETOOLONG.\n> -                         */\n> -                        free(FTSENT_WRAPPER(p));\n> -                        fts_lfree(head);\n> -                        closedir_and_clear(FTSENT_DIRP(cur));\n> -                        cur->fts_info = FTS_ERR;\n> -                        SET(FTS_STOP);\n> -                        __set_errno (ENAMETOOLONG);\n> -                        return (NULL);\n> -                }\n> -                p->fts_level = level;\n> -                p->fts_parent = sp->fts_cur;\n> -                p->fts_pathlen = new_len;\n> -\n> -                /* Store dirent.d_ino, in case we need to sort\n> -                   entries before processing them.  */\n> -                p->fts_statp->st_ino = D_INO (dp);\n> -\n> -                /* Build a file name for fts_stat to stat. */\n> -                if (ISSET(FTS_NOCHDIR)) {\n> -                        p->fts_accpath = p->fts_path;\n> -                        memmove(cp, p->fts_name, p->fts_namelen + 1);\n> -                } else\n> -                        p->fts_accpath = p->fts_name;\n> -\n> -                if (sp->fts_compar == NULL || ISSET(FTS_DEFER_STAT)) {\n> -                        /* Record what fts_read will have to do with this\n> -                           entry. In many cases, it will simply fts_stat it,\n> -                           but we can take advantage of any d_type information\n> -                           to optimize away the unnecessary stat calls.  I.e.,\n> -                           if FTS_NOSTAT is in effect, we don't need device\n> -                           numbers unconditionally (FTS_MOUNT) and we're not\n> -                           following symlinks (FTS_PHYSICAL) and d_type\n> -                           indicates this is *not* a directory, then we won't\n> -                           have to stat it  at all.  If it *is* a directory,\n> -                           then (currently) we stat it regardless, in order to\n> -                           get device and inode numbers.  Some day we might\n> -                           optimize that away, too, for directories where\n> -                           d_ino is known to be valid.  */\n> -                        bool skip_stat = (ISSET(FTS_NOSTAT)\n> -                                          && DT_IS_KNOWN(dp)\n> -                                          && ! DT_MUST_BE(dp, DT_DIR)\n> -                                          && (ISSET(FTS_PHYSICAL)\n> -                                              || ! DT_MUST_BE(dp, DT_LNK))\n> -                                          && ! ISSET(FTS_MOUNT));\n> -                        p->fts_info = FTS_NSOK;\n> -                        /* Propagate dirent.d_type information back\n> -                           to caller, when possible.  */\n> -                        set_stat_type (p->fts_statp, D_TYPE (dp));\n> -                        fts_set_stat_required(p, !skip_stat);\n> -                } else {\n> -                        p->fts_info = fts_stat(sp, p, false);\n> -                }\n> -\n> -                /* We walk in directory order so \"ls -f\" doesn't get upset. */\n> -                p->fts_link = NULL;\n> -                if (head == NULL)\n> -                        head = tail = p;\n> -                else {\n> -                        tail->fts_link = p;\n> -                        tail = p;\n> -                }\n> -\n> -                /* If there are many entries, no sorting function has been\n> -                   specified, and this file system is of a type that may be\n> -                   slow with a large number of entries, arrange to sort the\n> -                   directory entries on increasing inode numbers.\n> -\n> -                   The NITEMS comparison uses ==, not >, because the test\n> -                   needs to be tried at most once once, and NITEMS will exceed\n> -                   the threshold after it is incremented below.  */\n> -                if (nitems == _FTS_INODE_SORT_DIR_ENTRIES_THRESHOLD\n> -                    && !sp->fts_compar)\n> -                  sort_by_inode = dirent_inode_sort_may_be_useful (cur, dir_fd);\n> -\n> -                ++nitems;\n> -                if (max_entries <= nitems) {\n> -                        /* When there are too many dir entries, leave\n> -                           fts_dirp open, so that a subsequent fts_read\n> -                           can take up where we leave off.  */\n> -                        break;\n> -                }\n> -        }\n> -\n> -        /*\n> -         * If realloc() changed the address of the file name, adjust the\n> -         * addresses for the rest of the tree and the dir list.\n> -         */\n> -        if (doadjust)\n> -                fts_padjust(sp, head);\n> -\n> -        /*\n> -         * If not changing directories, reset the file name back to original\n> -         * state.\n> -         */\n> -        if (ISSET(FTS_NOCHDIR)) {\n> -                if (len == sp->fts_pathlen || nitems == 0)\n> -                        --cp;\n> -                *cp = '\\0';\n> -        }\n> -\n> -        /*\n> -         * If descended after called from fts_children or after called from\n> -         * fts_read and nothing found, get back.  At the root level we use\n> -         * the saved fd; if one of fts_open()'s arguments is a relative name\n> -         * to an empty directory, we wind up here with no other way back.  If\n> -         * can't get back, we're done.\n> -         */\n> -        if (!continue_readdir && descend && (type == BCHILD || !nitems) &&\n> -            (cur->fts_level == FTS_ROOTLEVEL\n> -             ? restore_initial_cwd(sp)\n> -             : fts_safe_changedir(sp, cur->fts_parent, -1, \"..\"))) {\n> -                cur->fts_info = FTS_ERR;\n> -                SET(FTS_STOP);\n> -                fts_lfree(head);\n> -                return (NULL);\n> -        }\n> -\n> -        /* If didn't find anything, return NULL. */\n> -        if (!nitems) {\n> -                if (type == BREAD\n> -                    && cur->fts_info != FTS_DNR && cur->fts_info != FTS_ERR)\n> -                        cur->fts_info = FTS_DP;\n> -                fts_lfree(head);\n> -                return (NULL);\n> -        }\n> -\n> -        if (sort_by_inode) {\n> -                sp->fts_compar = FTS_COMPAR_CAST (fts_compare_ino);\n> -                head = fts_sort (sp, head, nitems);\n> -                sp->fts_compar = NULL;\n> -        }\n> -\n> -        /* Sort the entries. */\n> -        if (sp->fts_compar && nitems > 1)\n> -                head = fts_sort(sp, head, nitems);\n> -        return (head);\n> -}\n> -\n> -#if GNULIB_FTS_DEBUG\n> -\n> -struct devino {\n> -  intmax_t dev, ino;\n> -};\n> -#define PRINT_DEVINO \"(%jd,%jd)\"\n> -\n> -static struct devino\n> -getdevino (int fd)\n> -{\n> -  struct STRUCT_STAT st;\n> -  return (fd == AT_FDCWD\n> -          ? (struct devino) { -1, 0 }\n> -          : FSTAT (fd, &st) == 0\n> -          ? (struct devino) { st.st_dev, st.st_ino }\n> -          : (struct devino) { -1, errno });\n> -}\n> -\n> -/* Walk ->fts_parent links starting at E_CURR, until the root of the\n> -   current hierarchy.  There should be a directory with dev/inode\n> -   matching those of AD.  If not, print a lot of diagnostics.  */\n> -static void\n> -find_matching_ancestor (FTSENTRY const *e_curr, struct Active_dir const *ad)\n> -{\n> -  for (FTSENTRY const *ent = e_curr;\n> -       ent->fts_level >= FTS_ROOTLEVEL;\n> -       ent = ent->fts_parent)\n> -    {\n> -      if (ad->ino == ent->fts_statp->st_ino\n> -          && ad->dev == ent->fts_statp->st_dev)\n> -        return;\n> -    }\n> -  printf (\"ERROR: tree dir, %s, not active\\n\", ad->fts_ent->fts_accpath);\n> -  printf (\"active dirs:\\n\");\n> -  for (FTSENTRY const *ent = e_curr;\n> -       ent->fts_level >= FTS_ROOTLEVEL;\n> -       ent = ent->fts_parent)\n> -    printf (\"  %s(%\"PRIuMAX\"/%\"PRIuMAX\") to %s(%\"PRIuMAX\"/%\"PRIuMAX\")...\\n\",\n> -            ad->fts_ent->fts_accpath,\n> -            (uintmax_t) ad->dev,\n> -            (uintmax_t) ad->ino,\n> -            ent->fts_accpath,\n> -            (uintmax_t) ent->fts_statp->st_dev,\n> -            (uintmax_t) ent->fts_statp->st_ino);\n> -}\n> -\n> -void\n> -fts_cross_check (FTSOBJ const *sp)\n> -{\n> -  if ( ! ISSET (FTS_TIGHT_CYCLE_CHECK))\n> -    return;\n> -\n> -  FTSENTRY const *ent = sp->fts_cur;\n> -\n> -  Dprintf ((\"fts-cross-check cur=%s\\n\", ent->fts_path));\n> -  /* Make sure every parent dir is in the tree.  */\n> -  for (FTSENTRY const *t = ent->fts_parent;\n> -       t->fts_level >= FTS_ROOTLEVEL;\n> -       t = t->fts_parent)\n> -    {\n> -      struct Active_dir ad;\n> -      ad.ino = t->fts_statp->st_ino;\n> -      ad.dev = t->fts_statp->st_dev;\n> -      if ( ! hash_lookup (sp->fts_cycle.ht, &ad))\n> -        printf (\"ERROR: active dir, %s, not in tree\\n\", t->fts_path);\n> -    }\n> -\n> -  /* Make sure every dir in the tree is an active dir.\n> -     But ENT is not necessarily a directory.  If so, just skip this part. */\n> -  if (ent->fts_parent->fts_level >= FTS_ROOTLEVEL\n> -      && (ent->fts_info == FTS_DP\n> -          || ent->fts_info == FTS_D))\n> -    for (struct Active_dir *ad = hash_get_first (sp->fts_cycle.ht);\n> -         ad != NULL;\n> -         ad = hash_get_next (sp->fts_cycle.ht, ad))\n> -      {\n> -        find_matching_ancestor (ent, ad);\n> -      }\n> -}\n> -\n> -static bool\n> -same_fd (int fd1, int fd2)\n> -{\n> -  struct STRUCT_STAT sb1, sb2;\n> -  return (FSTAT (fd1, &sb1) == 0\n> -          && FSTAT (fd2, &sb2) == 0\n> -          && psame_inode (&sb1, &sb2));\n> -}\n> -\n> -static void\n> -fd_ring_print (FTSOBJ const *sp, FILE *stream, char const *msg)\n> -{\n> -  if (!fts_debug)\n> -    return;\n> -  I_ring const *fd_ring = &sp->fts_fd_ring;\n> -  struct devino cwd = getdevino (sp->fts_cwd_fd);\n> -  fprintf (stream, \"=== %s ========== \"PRINT_DEVINO\"\\n\", msg, cwd.dev, cwd.ino);\n> -  if (i_ring_empty (fd_ring))\n> -    return;\n> -\n> -  unsigned int i = fd_ring->ir_front;\n> -  while (true)\n> -    {\n> -      int fd = fd_ring->ir_data[i];\n> -      if (fd < 0)\n> -        fprintf (stream, \"%u: %d:\\n\", i, fd);\n> -      else\n> -        {\n> -          struct devino wd = getdevino (fd);\n> -          fprintf (stream, \"%u: %d: \"PRINT_DEVINO\"\\n\", i, fd, wd.dev, wd.ino);\n> -        }\n> -      if (i == fd_ring->ir_back)\n> -        break;\n> -      i = (i + I_RING_SIZE - 1) % I_RING_SIZE;\n> -    }\n> -}\n> -\n> -/* Ensure that each file descriptor on the fd_ring matches a\n> -   parent, grandparent, etc. of the current working directory.  */\n> -static void\n> -fd_ring_check (FTSOBJ const *sp)\n> -{\n> -  if (!fts_debug)\n> -    return;\n> -\n> -  /* Make a writable copy.  */\n> -  I_ring fd_w = sp->fts_fd_ring;\n> -\n> -  int cwd_fd = sp->fts_cwd_fd;\n> -  cwd_fd = fcntl (cwd_fd, F_DUPFD_CLOEXEC, STDERR_FILENO + 1);\n> -  struct devino dot = getdevino (cwd_fd);\n> -  fprintf (stderr, \"===== check ===== cwd: \"PRINT_DEVINO\"\\n\",\n> -           dot.dev, dot.ino);\n> -  while ( ! i_ring_empty (&fd_w))\n> -    {\n> -      int fd = i_ring_pop (&fd_w);\n> -      if (0 <= fd)\n> -        {\n> -          int open_flags = O_SEARCH | O_CLOEXEC;\n> -          int parent_fd = openat (cwd_fd, \"..\", open_flags);\n> -          if (parent_fd < 0)\n> -            {\n> -              // Warn?\n> -              break;\n> -            }\n> -          if (!same_fd (fd, parent_fd))\n> -            {\n> -              struct devino cwd = getdevino (fd);\n> -              fprintf (stderr, \"ring  : \"PRINT_DEVINO\"\\n\", cwd.dev, cwd.ino);\n> -              struct devino c2 = getdevino (parent_fd);\n> -              fprintf (stderr, \"parent: \"PRINT_DEVINO\"\\n\", c2.dev, c2.ino);\n> -              fts_assert (0);\n> -            }\n> -          close (cwd_fd);\n> -          cwd_fd = parent_fd;\n> -        }\n> -    }\n> -  close (cwd_fd);\n> -}\n> -#endif\n> -\n> -static unsigned short int\n> -internal_function\n> -fts_stat(FTSOBJ *sp, register FTSENTRY *p, bool follow)\n> -{\n> -        if (ISSET (FTS_LOGICAL)\n> -            || (ISSET (FTS_COMFOLLOW) && p->fts_level == FTS_ROOTLEVEL))\n> -                follow = true;\n> -\n> -        struct STRUCT_STAT *sbp = p->fts_statp;\n> -\n> -        /*\n> -         * If doing a logical walk, or application requested FTS_FOLLOW, do\n> -         * a stat(2).  If that fails, check for a nonexistent symlink.  If\n> -         * fail, set the errno from the stat call.\n> -         */\n> -        int flags = follow ? 0 : AT_SYMLINK_NOFOLLOW;\n> -        if (FSTATAT (sp->fts_cwd_fd, p->fts_accpath, sbp, flags) < 0)\n> -          {\n> -            if (follow && errno == ENOENT\n> -                && 0 <= FSTATAT (sp->fts_cwd_fd, p->fts_accpath, sbp,\n> -                                 AT_SYMLINK_NOFOLLOW))\n> -              {\n> -                __set_errno (0);\n> -                return FTS_SLNONE;\n> -              }\n> -\n> -            p->fts_errno = errno;\n> -           memset (sbp, 0, sizeof *sbp);\n> -            return FTS_NS;\n> -          }\n> -\n> -        if (S_ISDIR(sbp->st_mode)) {\n> -                if (ISDOT(p->fts_name)) {\n> -                        /* Command-line \".\" and \"..\" are real directories. */\n> -                        return (p->fts_level == FTS_ROOTLEVEL ? FTS_D : FTS_DOT);\n> -                }\n> -\n> -                return (FTS_D);\n> -        }\n> -        if (S_ISLNK(sbp->st_mode))\n> -                return (FTS_SL);\n> -        if (S_ISREG(sbp->st_mode))\n> -                return (FTS_F);\n> -        return (FTS_DEFAULT);\n> -}\n> -\n> -static int\n> -fts_compar (void const *a, void const *b)\n> -{\n> -  /* Convert A and B to the correct types, to pacify the compiler, and\n> -     for portability to bizarre hosts where \"void const *\" and \"FTSENT\n> -     const **\" differ in runtime representation.  The comparison\n> -     function cannot modify *a and *b, but there is no compile-time\n> -     check for this.  */\n> -  FTSENTRY const **pa = (FTSENTRY const **) a;\n> -  FTSENTRY const **pb = (FTSENTRY const **) b;\n> -  return FTSENT_FTS(pa[0])->fts_compar (pa, pb);\n> -}\n> -\n> -static FTSENTRY *\n> -internal_function\n> -fts_sort (FTSOBJ *sp, FTSENTRY *head, register size_t nitems)\n> -{\n> -        register FTSENTRY **ap, *p;\n> -\n> -        /* On most modern hosts, void * and FTSENT ** have the same\n> -           run-time representation, and one can convert sp->fts_compar to\n> -           the type qsort expects without problem.  Use the heuristic that\n> -           this is OK if the two pointer types are the same size, and if\n> -           converting FTSENT ** to uintptr_t is the same as converting\n> -           FTSENT ** to void * and then to uintptr_t.  This heuristic isn't\n> -           valid in general but we don't know of any counterexamples.  */\n> -        FTSENTRY *dummy;\n> -        int (*compare) (void const *, void const *) =\n> -          ((sizeof &dummy == sizeof (void *)\n> -            && (uintptr_t) &dummy == (uintptr_t) (void *) &dummy)\n> -           ? (int (*) (void const *, void const *)) sp->fts_compar\n> -           : fts_compar);\n> -\n> -        /*\n> -         * Construct an array of pointers to the structures and call qsort(3).\n> -         * Reassemble the array in the order returned by qsort.  If unable to\n> -         * sort for memory reasons, return the directory entries in their\n> -         * current order.  Allocate enough space for the current needs plus\n> -         * 40 so don't realloc one entry at a time.\n> -         */\n> -        if (nitems > sp->fts_nitems) {\n> -                sp->fts_nitems = nitems + 40;\n> -                FTSENTRY **a;\n> -                if (! (a = reallocarray (sp->fts_array,\n> -                                         sp->fts_nitems, sizeof *a))) {\n> -                        free(sp->fts_array);\n> -                        sp->fts_array = NULL;\n> -                        sp->fts_nitems = 0;\n> -                        return (head);\n> -                }\n> -                sp->fts_array = a;\n> -        }\n> -        for (ap = sp->fts_array, p = head; p; p = p->fts_link)\n> -                *ap++ = p;\n> -        qsort((void *)sp->fts_array, nitems, sizeof(FTSENTRY *), compare);\n> -        for (head = *(ap = sp->fts_array); --nitems; ++ap)\n> -                ap[0]->fts_link = ap[1];\n> -        ap[0]->fts_link = NULL;\n> -        return (head);\n> -}\n> -\n> -static FTSENTRY *\n> -internal_function\n> -fts_alloc (FTSOBJ *sp, const char *name, register size_t namelen)\n> -{\n> -        /*\n> -         * The file name is a variable length array.  Allocate the FTSENT\n> -         * structure and the file name in one chunk.\n> -         */\n> -        size_t len = FLEXSIZEOF(FTSENT, fts_name, namelen + 1);\n> -\tregister FTSENTRY *p;\n> -#if !_LIBC\n> -        p = malloc(len);\n> -        if (p == NULL)\n> -                return (NULL);\n> -#else\n> -\t/*\n> -\t * For glibc, we use a wrapper struct to provide the extra required\n> -\t * fields without changing the FSENT layout.\n> -\t */\n> -\tlen += sizeof (struct FTSENT_wrapper);\n> -\tstruct FTSENT_wrapper *wrapper = malloc(len);\n> -\tif (wrapper == NULL)\n> -\t\treturn (NULL);\n> -\tp = &wrapper->ent;\n> -        p->fts_statp = &wrapper->fts_stat;\n> -#endif\n> -\n> -        /* Copy the name and guarantee NUL termination. */\n> -        memcpy(p->fts_name, name, namelen);\n> -        p->fts_name[namelen] = '\\0';\n> -\n> -        p->fts_namelen = namelen;\n> -        FTSENT_FTS(p)= sp;\n> -        p->fts_path = sp->fts_path;\n> -        p->fts_errno = 0;\n> -        FTSENT_DIRP(p) = NULL;\n> -        p->fts_flags = 0;\n> -        p->fts_instr = FTS_NOINSTR;\n> -        p->fts_number = 0;\n> -        p->fts_pointer = NULL;\n> -        return (p);\n> -}\n> -\n> -static void\n> -internal_function\n> -fts_lfree (register FTSENTRY *head)\n> -{\n> -        int saved_errno = errno;\n> -\n> -        /* Free a linked list of structures. */\n> -        register FTSENTRY *p;\n> -        while ((p = head)) {\n> -                head = head->fts_link;\n> -                if (FTSENT_DIRP(p))\n> -                        closedir (FTSENT_DIRP(p));\n> -                free(FTSENT_WRAPPER(p));\n> -        }\n> -\n> -        __set_errno (saved_errno);\n> -}\n> -\n> -/*\n> - * Allow essentially unlimited file name lengths; find, rm, ls should\n> - * all work on any tree.  Most systems will allow creation of file\n> - * names much longer than MAXPATHLEN, even though the kernel won't\n> - * resolve them.  Add the size (not just what's needed) plus 256 bytes\n> - * so don't realloc the file name 2 bytes at a time.\n> - */\n> -static bool\n> -internal_function\n> -fts_palloc (FTSOBJ *sp, size_t more)\n> -{\n> -        size_t new_len = sp->fts_pathlen + more + 256;\n> -\n> -        /*\n> -         * See if fts_pathlen would overflow.\n> -         */\n> -        if (new_len < sp->fts_pathlen) {\n> -                free(sp->fts_path);\n> -                sp->fts_path = NULL;\n> -                __set_errno (ENAMETOOLONG);\n> -                return false;\n> -        }\n> -        sp->fts_pathlen = new_len;\n> -        char *p = realloc(sp->fts_path, sp->fts_pathlen);\n> -        if (p == NULL) {\n> -                free(sp->fts_path);\n> -                sp->fts_path = NULL;\n> -                return false;\n> -        }\n> -        sp->fts_path = p;\n> -        return true;\n> -}\n> -\n> -/*\n> - * When the file name is realloc'd, have to fix all of the pointers in\n> - *  structures already returned.\n> - */\n> -static void\n> -internal_function\n> -fts_padjust (FTSOBJ *sp, FTSENTRY *head)\n> -{\n> -        char *addr = sp->fts_path;\n> -\n> -        /* This code looks at bit-patterns of freed pointers to\n> -           relocate them, so it relies on undefined behavior.  If this\n> -           trick does not work on your platform, please report a bug.  */\n> -\n> -#define ADJUST(p) do {                                                  \\\n> -        uintptr_t old_accpath = (uintptr_t) (p)->fts_accpath;           \\\n> -        if (old_accpath != (uintptr_t) (p)->fts_name) {                 \\\n> -                (p)->fts_accpath =                                      \\\n> -                  addr + (old_accpath - (uintptr_t) (p)->fts_path);     \\\n> -        }                                                               \\\n> -        (p)->fts_path = addr;                                           \\\n> -} while (0)\n> -        /* Adjust the current set of children. */\n> -        for (FTSENTRY *p = sp->fts_child; p; p = p->fts_link)\n> -                ADJUST(p);\n> -\n> -        /* Adjust the rest of the tree, including the current level. */\n> -        for (FTSENTRY *p = head; p->fts_level >= FTS_ROOTLEVEL;) {\n> -                ADJUST(p);\n> -                p = p->fts_link ? p->fts_link : p->fts_parent;\n> -        }\n> -}\n> -\n> -static size_t\n> -internal_function _GL_ATTRIBUTE_PURE\n> -fts_maxarglen (char * const *argv)\n> -{\n> -        size_t max;\n> -\n> -        for (max = 0; *argv; ++argv) {\n> -                size_t len = strlen(*argv);\n> -                if (len > max)\n> -                        max = len;\n> -        }\n> -        return (max + 1);\n> -}\n> -\n> -/*\n> - * Change to dir specified by fd or file name without getting\n> - * tricked by someone changing the world out from underneath us.\n> - * Assumes p->fts_statp->st_dev and p->fts_statp->st_ino are filled in.\n> - * If FD is non-negative, expect it to be used after this function returns,\n> - * and to be closed eventually.  So don't pass e.g., 'dirfd(dirp)' and then\n> - * do closedir(dirp), because that would invalidate the saved FD.\n> - * Upon failure, close FD immediately and return nonzero.\n> - */\n> -static int\n> -internal_function\n> -fts_safe_changedir (FTSOBJ *sp, FTSENTRY *p, int fd, char const *dir)\n> -{\n> -        fts_assert (0 <= fd || dir != NULL);\n> -        bool is_dotdot = dir && streq (dir, \"..\");\n> -\n> -        /* This clause handles the unusual case in which FTS_NOCHDIR\n> -           is specified, along with FTS_CWDFD.  In that case, there is\n> -           no need to change even the virtual cwd file descriptor.\n> -           However, if FD is non-negative, we do close it here.  */\n> -        if (ISSET (FTS_NOCHDIR))\n> -          {\n> -            if (ISSET (FTS_CWDFD) && 0 <= fd)\n> -              close (fd);\n> -            return 0;\n> -          }\n> -\n> -        if (fd < 0 && is_dotdot && ISSET (FTS_CWDFD))\n> -          {\n> -            /* When possible, skip the diropen and subsequent fstat+dev/ino\n> -               comparison.  I.e., when changing to parent directory\n> -               (chdir (\"..\")), use a file descriptor from the ring and\n> -               save the overhead of diropen+fstat, as well as avoiding\n> -               failure when we lack \"x\" access to the virtual cwd.  */\n> -            if ( ! i_ring_empty (&sp->fts_fd_ring))\n> -              {\n> -                int parent_fd;\n> -                fd_ring_print (sp, stderr, \"pre-pop\");\n> -                parent_fd = i_ring_pop (&sp->fts_fd_ring);\n> -                if (0 <= parent_fd)\n> -                  {\n> -                    fd = parent_fd;\n> -                    dir = NULL;\n> -                  }\n> -              }\n> -          }\n> -\n> -        int newfd = fd;\n> -        if (fd < 0 && (newfd = diropen (sp, dir)) < 0)\n> -          return -1;\n> -\n> -        /* The following dev/inode check is necessary if we're doing a\n> -           \"logical\" traversal (through symlinks, a la chown -L), if the\n> -           system lacks O_NOFOLLOW support, or if we're changing to \"..\"\n> -           (but not via a popped file descriptor).  When changing to the\n> -           name \"..\", O_NOFOLLOW can't help.  In general, when the target is\n> -           not \"..\", diropen's use of O_NOFOLLOW ensures we don't mistakenly\n> -           follow a symlink, so we can avoid the expense of this fstat.  */\n> -        int ret;\n> -        if (ISSET(FTS_LOGICAL) || ! HAVE_WORKING_O_NOFOLLOW\n> -            || (dir && streq (dir, \"..\")))\n> -          {\n> -            struct STRUCT_STAT sb;\n> -            if (FSTAT (newfd, &sb))\n> -              {\n> -                ret = -1;\n> -                goto bail;\n> -              }\n> -            if (p->fts_statp->st_dev != sb.st_dev\n> -                || p->fts_statp->st_ino != sb.st_ino)\n> -              {\n> -                __set_errno (ENOENT);           /* disinformation */\n> -                ret = -1;\n> -                goto bail;\n> -              }\n> -          }\n> -\n> -        if (ISSET(FTS_CWDFD))\n> -          {\n> -            cwd_advance_fd (sp, newfd, ! is_dotdot);\n> -            return 0;\n> -          }\n> -\n> -        ret = fchdir(newfd);\n> -bail:\n> -        if (fd < 0)\n> -          {\n> -            int oerrno = errno;\n> -            (void)close(newfd);\n> -            __set_errno (oerrno);\n> -          }\n> -        return ret;\n> -}\n> diff --git a/io/fts64-time64.c b/io/fts64-time64.c\n> index 6a1053194e..29f2dbc28c 100644\n> --- a/io/fts64-time64.c\n> +++ b/io/fts64-time64.c\n> @@ -33,5 +33,5 @@\n>  # define STRUCT_STATFS      statfs64\n>  # define FSTATFS            __fstatfs64\n>  \n> -# include \"fts.c\"\n> +# include \"fts-common.c\"\n>  #endif\n> diff --git a/io/fts64.c b/io/fts64.c\n> index a6f607a873..1efa06ab3b 100644\n> --- a/io/fts64.c\n> +++ b/io/fts64.c\n> @@ -16,11 +16,11 @@\n>     License along with the GNU C Library; if not, see\n>     <https://www.gnu.org/licenses/>.  */\n>  \n> -#define FTS_OPEN fts64_open\n> -#define FTS_CLOSE fts64_close\n> -#define FTS_READ fts64_read\n> -#define FTS_SET fts64_set\n> -#define FTS_CHILDREN fts64_children\n> +#define FTS_OPEN __fts64_open\n> +#define FTS_CLOSE __fts64_close\n> +#define FTS_READ __fts64_read\n> +#define FTS_SET __fts64_set\n> +#define FTS_CHILDREN __fts64_children\n>  #define FTSOBJ FTS64\n>  #define FTSENTRY FTSENT64\n>  #define INO_T ino64_t\n> @@ -30,4 +30,30 @@\n>  #define STRUCT_STATFS statfs64\n>  #define FSTATFS __fstatfs64\n>  \n> -#include \"fts.c\"\n> +#define fts_open __rename_fts_open\n> +#define fts_close __rename_fts_close\n> +#define fts_read __rename_fts_read\n> +#define fts_set __rename_fts_set\n> +#define fts_children __rename_fts_children\n> +\n> +#include \"fts-common.c\"\n> +\n> +#undef fts_open\n> +#undef fts_close\n> +#undef fts_read\n> +#undef fts_set\n> +#undef fts_children\n> +\n> +weak_alias (__fts64_open, fts64_open)\n> +weak_alias (__fts64_close, fts64_close)\n> +weak_alias (__fts64_read, fts64_read)\n> +weak_alias (__fts64_set, fts64_set)\n> +weak_alias (__fts64_children, fts64_children)\n> +\n> +#ifdef __OFF_T_MATCHES_OFF64_T\n> +weak_alias (__fts64_open, fts_open)\n> +weak_alias (__fts64_close, fts_close)\n> +weak_alias (__fts64_read, fts_read)\n> +weak_alias (__fts64_set, fts_set)\n> +weak_alias (__fts64_children, fts_children)\n> +#endif\n> diff --git a/sysdeps/unix/sysv/linux/mips/mips64/n64/fts.c b/sysdeps/unix/sysv/linux/mips/mips64/n64/fts.c\n> deleted file mode 100644\n> index d0c62e6195..0000000000\n> --- a/sysdeps/unix/sysv/linux/mips/mips64/n64/fts.c\n> +++ /dev/null\n> @@ -1 +0,0 @@\n> -#include <io/fts.c>\n> diff --git a/sysdeps/unix/sysv/linux/mips/mips64/n64/fts64.c b/sysdeps/unix/sysv/linux/mips/mips64/n64/fts64.c\n> deleted file mode 100644\n> index 2472f8bf75..0000000000\n> --- a/sysdeps/unix/sysv/linux/mips/mips64/n64/fts64.c\n> +++ /dev/null\n> @@ -1 +0,0 @@\n> -#include <io/fts64.c>\n> diff --git a/sysdeps/unix/sysv/linux/x86_64/x32/fts.c b/sysdeps/unix/sysv/linux/x86_64/x32/fts.c\n> deleted file mode 100644\n> index 980573ed68..0000000000\n> --- a/sysdeps/unix/sysv/linux/x86_64/x32/fts.c\n> +++ /dev/null\n> @@ -1 +0,0 @@\n> -#include <sysdeps/wordsize-64/fts.c>\n> diff --git a/sysdeps/unix/sysv/linux/x86_64/x32/fts64.c b/sysdeps/unix/sysv/linux/x86_64/x32/fts64.c\n> deleted file mode 100644\n> index 221d1b5608..0000000000\n> --- a/sysdeps/unix/sysv/linux/x86_64/x32/fts64.c\n> +++ /dev/null\n> @@ -1 +0,0 @@\n> -#include <sysdeps/wordsize-64/fts64.c>\n> diff --git a/sysdeps/wordsize-64/fts.c b/sysdeps/wordsize-64/fts.c\n> deleted file mode 100644\n> index 159dc1febe..0000000000\n> --- a/sysdeps/wordsize-64/fts.c\n> +++ /dev/null\n> @@ -1,19 +0,0 @@\n> -#define fts64_open __rename_fts64_open\n> -#define fts64_close __rename_fts64_close\n> -#define fts64_read __rename_fts64_read\n> -#define fts64_set __rename_fts64_set\n> -#define fts64_children __rename_fts64_children\n> -\n> -#include \"../../io/fts.c\"\n> -\n> -#undef fts64_open\n> -#undef fts64_close\n> -#undef fts64_read\n> -#undef fts64_set\n> -#undef fts64_children\n> -\n> -weak_alias (fts_open, fts64_open)\n> -weak_alias (fts_close, fts64_close)\n> -weak_alias (fts_read, fts64_read)\n> -weak_alias (fts_set, fts64_set)\n> -weak_alias (fts_children, fts64_children)\n> diff --git a/sysdeps/wordsize-64/fts64.c b/sysdeps/wordsize-64/fts64.c\n> deleted file mode 100644\n> index f2848fc3e4..0000000000\n> --- a/sysdeps/wordsize-64/fts64.c\n> +++ /dev/null\n> @@ -1 +0,0 @@\n> -/* Defined in fts.c.  */","headers":{"Return-Path":"<libc-alpha-bounces~incoming=patchwork.ozlabs.org@sourceware.org>","X-Original-To":["incoming@patchwork.ozlabs.org","libc-alpha@sourceware.org"],"Delivered-To":["patchwork-incoming@legolas.ozlabs.org","libc-alpha@sourceware.org"],"Authentication-Results":["legolas.ozlabs.org;\n\tdkim=pass (2048-bit key;\n unprotected) header.d=linaro.org header.i=@linaro.org header.a=rsa-sha256\n header.s=google header.b=QS2wroxX;\n\tdkim-atps=neutral","legolas.ozlabs.org;\n spf=pass (sender SPF authorized) smtp.mailfrom=sourceware.org\n (client-ip=2620:52:6:3111::32; helo=vm01.sourceware.org;\n envelope-from=libc-alpha-bounces~incoming=patchwork.ozlabs.org@sourceware.org;\n receiver=patchwork.ozlabs.org)","sourceware.org;\n\tdkim=pass (2048-bit key,\n unprotected) header.d=linaro.org header.i=@linaro.org header.a=rsa-sha256\n header.s=google header.b=QS2wroxX","sourceware.org;\n dmarc=pass (p=none dis=none) header.from=linaro.org","sourceware.org; spf=pass smtp.mailfrom=linaro.org","server2.sourceware.org;\n arc=none smtp.remote-ip=2607:f8b0:4864:20::1332"],"Received":["from vm01.sourceware.org (vm01.sourceware.org\n [IPv6:2620:52:6:3111::32])\n\t(using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits)\n\t key-exchange x25519 server-signature ECDSA (secp384r1) server-digest SHA384)\n\t(No client certificate requested)\n\tby legolas.ozlabs.org (Postfix) with ESMTPS id 4fy2LK448kz1yCv\n\tfor <incoming@patchwork.ozlabs.org>; Sat, 18 Apr 2026 03:43:53 +1000 (AEST)","from vm01.sourceware.org (localhost [127.0.0.1])\n\tby sourceware.org (Postfix) with ESMTP id 9BEB04BB3BA8\n\tfor <incoming@patchwork.ozlabs.org>; Fri, 17 Apr 2026 17:43:51 +0000 (GMT)","from mail-dy1-x1332.google.com (mail-dy1-x1332.google.com\n [IPv6:2607:f8b0:4864:20::1332])\n by sourceware.org (Postfix) with ESMTPS id CAB2C4CCCA31\n for <libc-alpha@sourceware.org>; Fri, 17 Apr 2026 17:31:20 +0000 (GMT)","by mail-dy1-x1332.google.com with SMTP id\n 5a478bee46e88-2dec803f9f0so1060360eec.0\n for <libc-alpha@sourceware.org>; Fri, 17 Apr 2026 10:31:20 -0700 (PDT)","from ?IPV6:2804:1b3:a7c3:d5d0:f8f7:3cdf:3cb1:d627?\n ([2804:1b3:a7c3:d5d0:f8f7:3cdf:3cb1:d627])\n by smtp.gmail.com with ESMTPSA id\n 5a478bee46e88-2e539fa5c38sm2985015eec.5.2026.04.17.10.31.14\n (version=TLS1_3 cipher=TLS_AES_128_GCM_SHA256 bits=128/128);\n Fri, 17 Apr 2026 10:31:16 -0700 (PDT)"],"DKIM-Filter":["OpenDKIM Filter v2.11.0 sourceware.org 9BEB04BB3BA8","OpenDKIM Filter v2.11.0 sourceware.org CAB2C4CCCA31"],"DMARC-Filter":"OpenDMARC Filter v1.4.2 sourceware.org CAB2C4CCCA31","ARC-Filter":"OpenARC Filter v1.0.0 sourceware.org CAB2C4CCCA31","ARC-Seal":"i=1; a=rsa-sha256; d=sourceware.org; s=key; t=1776447081; cv=none;\n b=qGJD0RGifjEqixoG4qNOwbHtewp4SK+p0M6sM+IOYGoYdYmXYvyzkwx6AtuWn7Sgk+XSgLk6cz9UJInH4u1K8yHAHJAddhgsSTmfrXXHxmsz0VQawJ9E1M2vyv/fkPmo/Mvi8a8yvExKcNkCsrU9WSLXTq000uB5fPwNHMjstks=","ARC-Message-Signature":"i=1; a=rsa-sha256; d=sourceware.org; s=key;\n t=1776447081; c=relaxed/simple;\n bh=fo+uqRq5w7lGIQQjPzBeTplSvQr9dfL9aBGB3b7pSaI=;\n h=DKIM-Signature:Message-ID:Date:MIME-Version:Subject:From:To;\n b=wJYJBFr61lPkJFm/DcAg/UP9pBTtN/R0p9Z8vLvBAPFqKcm6K0X8Na56Net+0D4gYO0aTaUjl+Z3VbEMRfnqsLeLLdBazRVitGNEmb9uaexNmO2uQW5o0DtaXnQSDvixx6m2dLWh2nrvNoOTiYXw62/YYBJT4pPwZGGRlj7ym04=","ARC-Authentication-Results":"i=1; server2.sourceware.org","DKIM-Signature":"v=1; a=rsa-sha256; c=relaxed/relaxed;\n d=linaro.org; s=google; t=1776447079; x=1777051879; darn=sourceware.org;\n h=content-transfer-encoding:in-reply-to:organization:content-language\n :references:cc:to:from:subject:user-agent:mime-version:date\n :message-id:from:to:cc:subject:date:message-id:reply-to;\n bh=cwkJ1NFR5FKHLsppXC/tUF4MPNtWRKrqo9ycZCCjL8I=;\n b=QS2wroxXJrDkdZMLMmlMuK4mpFwkHxoZ2EfRSKBXKYI8ACgS76DkJtLPLHLW1+UGHA\n mki5avxXTmk8Jgs5l9CtsjGCj17BdP81ClGtkdWoGQWaKXz4K3LK36dSE2FDpI5p4V/S\n fs4W0hzsTgz2oRRCNuD7JMSYZE3KOfclIkRLqL3C7HkoMYTtTyhD3tgP7xBKTNeQpIGu\n aDCP31h6WDTBhcaPoD8S2MxD/H1evUPOEnY/4dmMaqMYDGy8i0VEy2fRj7doK3/0kCpt\n jLywWQuR6f3LZbCFJ+pKhl32ThZ0XalMCVsVwR/TZC7BTcMZr00cf3pBcBrlaXLjPTQm\n +9XQ==","X-Google-DKIM-Signature":"v=1; a=rsa-sha256; c=relaxed/relaxed;\n d=1e100.net; s=20251104; t=1776447079; x=1777051879;\n h=content-transfer-encoding:in-reply-to:organization:content-language\n :references:cc:to:from:subject:user-agent:mime-version:date\n :message-id:x-gm-gg:x-gm-message-state:from:to:cc:subject:date\n :message-id:reply-to;\n bh=cwkJ1NFR5FKHLsppXC/tUF4MPNtWRKrqo9ycZCCjL8I=;\n b=LQPUjjM53nPqn8HmFSn4u62Rs6mRhFJTVwSEV49E/ZDJEdcUZj1KNgHfGMUbf0cYBM\n cU67VIBe/o8FWTey86mjYfp1OAscEUGRZtbRMoMd3x7H1cHSsQPLMv6/gYMGHiItVOl8\n oumizpKTNQLLjDNnhD7p0dxsEgNINJ0ca7//qfv5kBkjm8JZ8sfh7aUldDFQDLwiA6/n\n 6qvLXqp6YUhAMprGPlc5SMhLKm9pnaNCAexWIv/vwkLy3BwanFcLyQVyoHOqpOUjlVlo\n G2+IJBBK/LHAXvH/qecMnrDeCq3Ou0lYxnsCAFKpFl/mYCmu8cAPwL0Ob0wWOi2Uyj2J\n hC/A==","X-Gm-Message-State":"AOJu0YyNFyVX7LvUXenfvhhrjI9yAgyJuSwAOcH1S49a7dGXxzDxBggZ\n ocBdtGZb+1UqiNt9ES5rrHGH+jvz8otJCAFTbKpUTh7KYH9zsWzPuWCrNu30RDbUdsc96UgVU+i\n wSosS","X-Gm-Gg":"AeBDievzq31ZkQ2LP02mr6Ba0YUaC/7EiFULPO77lirQ/3ZYLrjxEnI7SeRdrLKgtzD\n 0iGstwRvdbQkZW6WdqEWITTIv4LTH/6DOsjDUtyCPk8nowR54k1itq+45RjxNyCiGeekEjXxk6p\n K5Pgwu2FK0GXVC/1M/lhunGoZFCp6r6shHBZ10Xi+Wmz0029F0BYV2vSiz8GYEoGwGRZNjLoed7\n 2m4QAUWUbpU21SRB4CT1NSnbEJMY/O+leFCJJPhf0Lnu4LFP3psQ3kMc4zqYto2nvCOqneEeKrD\n SM7uOoYUddQoKVst2pC3LXA/0RNXDa5Vb2gmTo0/2FZcsfoQWLNv4MOKFOvzcNBgqFfwjaVf7RJ\n sgXAVCYYZHjYLkzC5KcbfGa/R5/UxznpGnExyOnaOIJd86EWZxNIE+Dz4f70c1yD/2W7gm849vi\n wkdQVj3svFRjI+zz4pUuLowMxQRjn6IZN4sD7IX6fNoTOBVb9n79QQAQiTh5iBjV+SNiPPtuxSX\n YJ3sLoMcTJLnpRevQAbcMxU/BRenUAFcldwFnUR8Ssr","X-Received":"by 2002:a05:7300:72d5:b0:2a4:701a:b9ba with SMTP id\n 5a478bee46e88-2e42e10aeabmr1431898eec.14.1776447077287;\n Fri, 17 Apr 2026 10:31:17 -0700 (PDT)","Message-ID":"<a4d6a525-ed41-48c1-a08d-c7ee981e9472@linaro.org>","Date":"Fri, 17 Apr 2026 14:31:13 -0300","MIME-Version":"1.0","User-Agent":"Mozilla Thunderbird","Subject":"Re: [PATCH 1/3] io: Consolidate fts implementation","From":"Adhemerval Zanella Netto <adhemerval.zanella@linaro.org>","To":"libc-alpha@sourceware.org","Cc":"Collin Funk <collin.funk1@gmail.com>, Paul Eggert <eggert@cs.ucla.edu>","References":"<20260417132808.235562-1-adhemerval.zanella@linaro.org>\n <20260417132808.235562-2-adhemerval.zanella@linaro.org>","Content-Language":"en-US","Organization":"Linaro","In-Reply-To":"<20260417132808.235562-2-adhemerval.zanella@linaro.org>","Content-Type":"text/plain; charset=UTF-8","Content-Transfer-Encoding":"7bit","X-BeenThere":"libc-alpha@sourceware.org","X-Mailman-Version":"2.1.30","Precedence":"list","List-Id":"Libc-alpha mailing list <libc-alpha.sourceware.org>","List-Unsubscribe":"<https://sourceware.org/mailman/options/libc-alpha>,\n <mailto:libc-alpha-request@sourceware.org?subject=unsubscribe>","List-Archive":"<https://sourceware.org/pipermail/libc-alpha/>","List-Post":"<mailto:libc-alpha@sourceware.org>","List-Help":"<mailto:libc-alpha-request@sourceware.org?subject=help>","List-Subscribe":"<https://sourceware.org/mailman/listinfo/libc-alpha>,\n <mailto:libc-alpha-request@sourceware.org?subject=subscribe>","Errors-To":"libc-alpha-bounces~incoming=patchwork.ozlabs.org@sourceware.org"}},{"id":3678876,"web_url":"http://patchwork.ozlabs.org/comment/3678876/","msgid":"<7ce6d247-0538-4860-90be-5788e1b70782@redhat.com>","list_archive_url":null,"date":"2026-04-17T20:09:45","subject":"Re: [PATCH 1/3] io: Consolidate fts implementation","submitter":{"id":22438,"url":"http://patchwork.ozlabs.org/api/people/22438/","name":"Carlos O'Donell","email":"carlos@redhat.com"},"content":"On 4/17/26 1:31 PM, Adhemerval Zanella Netto wrote:\n> This is a mechanical patch and I install if no one opposes it.\n\nPlease feel free to push if it is just a mechanical sync from gnulib.\n\n> On 17/04/26 10:24, Adhemerval Zanella wrote:\n>> Remove wordsize-64 and arch-specific implementations, for ABIs when\n>> off_t is the same as off64_t (__OFF_T_MATCHES_OFF64_T) the fts64.c\n>> will create the requires aliases.\n>>\n>> The fts.c implementation is moved to fts-common.c to simplify\n>> the __OFF_T_MATCHES_OFF64_T usage.\n>> ---\n>>   SHARED-FILES                                  |    2 +-\n>>   io/fts-common.c                               | 2208 ++++++++++++++++\n>>   io/fts.c                                      | 2217 +----------------\n>>   io/fts64-time64.c                             |    2 +-\n>>   io/fts64.c                                    |   38 +-\n>>   sysdeps/unix/sysv/linux/mips/mips64/n64/fts.c |    1 -\n>>   .../unix/sysv/linux/mips/mips64/n64/fts64.c   |    1 -\n>>   sysdeps/unix/sysv/linux/x86_64/x32/fts.c      |    1 -\n>>   sysdeps/unix/sysv/linux/x86_64/x32/fts64.c    |    1 -\n>>   sysdeps/wordsize-64/fts.c                     |   19 -\n>>   sysdeps/wordsize-64/fts64.c                   |    1 -\n>>   11 files changed, 2258 insertions(+), 2233 deletions(-)\n>>   create mode 100644 io/fts-common.c\n>>   delete mode 100644 sysdeps/unix/sysv/linux/mips/mips64/n64/fts.c\n>>   delete mode 100644 sysdeps/unix/sysv/linux/mips/mips64/n64/fts64.c\n>>   delete mode 100644 sysdeps/unix/sysv/linux/x86_64/x32/fts.c\n>>   delete mode 100644 sysdeps/unix/sysv/linux/x86_64/x32/fts64.c\n>>   delete mode 100644 sysdeps/wordsize-64/fts.c\n>>   delete mode 100644 sysdeps/wordsize-64/fts64.c\n>>\n>> diff --git a/SHARED-FILES b/SHARED-FILES\n>> index 126aaf096d..645040d7e1 100644\n>> --- a/SHARED-FILES\n>> +++ b/SHARED-FILES\n>> @@ -49,7 +49,7 @@ gnulib:\n>>     io/cycle-check.h\n>>     io/dev-ino.h\n>>     io/fts-cycle.c\n>> -  io/fts.c\n>> +  io/fts-common.c\n>>     io/i-ring.c\n>>     io/same-inode.h\n>>     locale/programs/3level.h\n>> diff --git a/io/fts-common.c b/io/fts-common.c\n>> new file mode 100644\n>> index 0000000000..3825f0aeb4\n>> --- /dev/null\n>> +++ b/io/fts-common.c\n>> @@ -0,0 +1,2208 @@\n>> +/* Traverse a file hierarchy.\n>> +\n>> +   Copyright (C) 2004-2026 Free Software Foundation, Inc.\n>> +\n>> +   This file is free software: you can redistribute it and/or modify\n>> +   it under the terms of the GNU Lesser General Public License as\n>> +   published by the Free Software Foundation; either version 2.1 of the\n>> +   License, or (at your option) any later version.\n>> +\n>> +   This file is distributed in the hope that it will be useful,\n>> +   but WITHOUT ANY WARRANTY; without even the implied warranty of\n>> +   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n>> +   GNU Lesser General Public License for more details.\n>> +\n>> +   You should have received a copy of the GNU Lesser General Public License\n>> +   along with this program.  If not, see <https://www.gnu.org/licenses/>.  */\n>> +\n>> +/*-\n>> + * Copyright (c) 1990, 1993, 1994\n>> + *      The Regents of the University of California.  All rights reserved.\n>> + *\n>> + * Redistribution and use in source and binary forms, with or without\n>> + * modification, are permitted provided that the following conditions\n>> + * are met:\n>> + * 1. Redistributions of source code must retain the above copyright\n>> + *    notice, this list of conditions and the following disclaimer.\n>> + * 2. Redistributions in binary form must reproduce the above copyright\n>> + *    notice, this list of conditions and the following disclaimer in the\n>> + *    documentation and/or other materials provided with the distribution.\n>> + * 4. Neither the name of the University nor the names of its contributors\n>> + *    may be used to endorse or promote products derived from this software\n>> + *    without specific prior written permission.\n>> + *\n>> + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS \"AS IS\" AND\n>> + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE\n>> + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE\n>> + * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE\n>> + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL\n>> + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS\n>> + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)\n>> + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT\n>> + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY\n>> + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF\n>> + * SUCH DAMAGE.\n>> + */\n>> +\n>> +#include <config.h>\n>> +\n>> +#if defined LIBC_SCCS && !defined GCC_LINT && !defined lint\n>> +static char sccsid[] = \"@(#)fts.c       8.6 (Berkeley) 8/14/94\";\n>> +#endif\n>> +\n>> +#if _LIBC\n>> +# include <fts.h>\n>> +#else\n>> +# include \"fts_.h\"\n>> +#endif\n>> +#if _LIBC || HAVE_SYS_PARAM_H\n>> +# include <sys/param.h>\n>> +#endif\n>> +#include <sys/stat.h>\n>> +#include <fcntl.h>\n>> +#include <errno.h>\n>> +#include <stddef.h>\n>> +#include <stdint.h>\n>> +#include <stdlib.h>\n>> +#include <string.h>\n>> +#include <unistd.h>\n>> +\n>> +/* Support for the LFS API version.  */\n>> +#ifndef FTS_OPEN\n>> +# define FTS_OPEN fts_open\n>> +# define FTS_CLOSE fts_close\n>> +# define FTS_READ fts_read\n>> +# define FTS_SET fts_set\n>> +# define FTS_CHILDREN fts_children\n>> +# define FTSOBJ FTS\n>> +# define FTSENTRY FTSENT\n>> +# define INO_T ino_t\n>> +# define STRUCT_STAT stat\n>> +# define FSTAT __fstat\n>> +# define FSTATAT __fstatat\n>> +# define STRUCT_STATFS statfs\n>> +# define FSTATFS __fstatfs\n>> +#endif\n>> +\n>> +#if ! _LIBC\n>> +# include \"attribute.h\"\n>> +# include \"fcntl--.h\"\n>> +# include \"openat.h\"\n>> +# include \"opendirat.h\"\n>> +# include \"same-inode.h\"\n>> +# define OPENDIRAT opendirat\n>> +# define FTSENT_WRAPPER(__p) __p\n>> +# define FTS_COMPAR_CAST(__fn) __fn\n>> +#else\n>> +# include <stdbool.h>\n>> +\n>> +# define internal_function\n>> +# define FALLTHROUGH               ; [[fallthrough]]\n>> +# define HAVE_STRUCT_DIRENT_D_TYPE 1\n>> +# define GNULIB_FTS_DEBUG          0\n>> +# ifdef O_PATH\n>> +#  define O_SEARCH                 O_PATH\n>> +# else\n>> +#  define O_SEARCH                 O_RDONLY\n>> +# endif\n>> +# define HAVE_SYS_VFS_H            1\n>> +# define HAVE_FSTATFS              1\n>> +# define HAVE_STRUCT_STATFS_F_TYPE 1\n>> +# define HAVE_OPENAT               1\n>> +# define HAVE_WORKING_O_NOFOLLOW   1\n>> +# define _GL_CMP(a, b)             ((a) < (b) ? -1 : (a) > (b))\n>> +# define OPENDIRAT                 __opendirat\n>> +\n>> +static inline bool openat_needs_fchdir (void)\n>> +{\n>> +  return false;\n>> +}\n>> +\n>> +static inline bool streq (const char *s1, const char *s2)\n>> +{\n>> +  return strcmp (s1, s2) == 0;\n>> +}\n>> +# define reallocarray              __libc_reallocarray\n>> +# define fchdir                    __fchdir\n>> +# define close                     __close\n>> +# define closedir                  __closedir\n>> +# define fcntl                     __fcntl\n>> +# define readdir                   __readdir\n>> +# ifndef dirfd\n>> +#  define dirfd                    __dirfd\n>> +# endif\n>> +# define open                      __open\n>> +# define openat                    __openat\n>> +\n>> +# include \"cycle-check.c\"\n>> +# include \"i-ring.c\"\n>> +\n>> +struct FTSENT_wrapper\n>> +{\n>> +  FTSOBJ *fts_fts;                /* the file hierarchy itself */\n>> +  DIR *fts_dirp;                  /* Dir pointer for any directory containing\n>> +\t\t\t\t     more entries than we read at one time.  */\n>> +  struct STRUCT_STAT fts_stat;\n>> +\n>> +  FTSENTRY ent;\n>> +};\n>> +\n>> +/* glibc historicaly defines the FTS::fts_compar as having 'void *', while the\n>> +   fts_open has a function point using 'FTSENT **' as argument.  */\n>> +# define FTS_COMPAR_CAST(__fn) ((int (*) (void const *, void const *))__fn)\n>> +\n>> +# define FTSENT_WRAPPER(p) \\\n>> +    ((struct FTSENT_wrapper *) ((char *)(p) - offsetof(struct FTSENT_wrapper, ent)))\n>> +#endif\n>> +#define FTSENT_FTS(p)   (FTSENT_WRAPPER(p)->fts_fts)\n>> +#define FTSENT_DIRP(p)  (FTSENT_WRAPPER(p)->fts_dirp)\n>> +\n>> +#include \"flexmember.h\"\n>> +\n>> +#include <dirent.h>\n>> +#ifndef _D_EXACT_NAMLEN\n>> +# define _D_EXACT_NAMLEN(dirent) strlen ((dirent)->d_name)\n>> +#endif\n>> +\n>> +#if HAVE_STRUCT_DIRENT_D_TYPE\n>> +/* True if the type of the directory entry D is known.  */\n>> +# define DT_IS_KNOWN(d) ((d)->d_type != DT_UNKNOWN)\n>> +/* True if the type of the directory entry D must be T.  */\n>> +# define DT_MUST_BE(d, t) ((d)->d_type == (t))\n>> +# define D_TYPE(d) ((d)->d_type)\n>> +#else\n>> +# define DT_IS_KNOWN(d) false\n>> +# define DT_MUST_BE(d, t) false\n>> +# define D_TYPE(d) DT_UNKNOWN\n>> +\n>> +# undef DT_UNKNOWN\n>> +# define DT_UNKNOWN 0\n>> +\n>> +/* Any nonzero values will do here, so long as they're distinct.\n>> +   Undef any existing macros out of the way.  */\n>> +# undef DT_BLK\n>> +# undef DT_CHR\n>> +# undef DT_DIR\n>> +# undef DT_FIFO\n>> +# undef DT_LNK\n>> +# undef DT_REG\n>> +# undef DT_SOCK\n>> +# define DT_BLK 1\n>> +# define DT_CHR 2\n>> +# define DT_DIR 3\n>> +# define DT_FIFO 4\n>> +# define DT_LNK 5\n>> +# define DT_REG 6\n>> +# define DT_SOCK 7\n>> +#endif\n>> +\n>> +#ifndef S_IFBLK\n>> +# define S_IFBLK 0\n>> +#endif\n>> +#ifndef S_IFLNK\n>> +# define S_IFLNK 0\n>> +#endif\n>> +#ifndef S_IFSOCK\n>> +# define S_IFSOCK 0\n>> +#endif\n>> +\n>> +enum\n>> +{\n>> +  NOT_AN_INODE_NUMBER = 0\n>> +};\n>> +\n>> +#ifdef D_INO_IN_DIRENT\n>> +# define D_INO(dp) (dp)->d_ino\n>> +#else\n>> +/* Some systems don't have inodes, so fake them to avoid lots of ifdefs.  */\n>> +# define D_INO(dp) NOT_AN_INODE_NUMBER\n>> +#endif\n>> +\n>> +/* If possible (see max_entries, below), read no more than this many directory\n>> +   entries at a time.  Without this limit (i.e., when using non-NULL\n>> +   fts_compar), processing a directory with 4,000,000 entries requires ~1GiB\n>> +   of memory, and handling 64M entries would require 16GiB of memory.  */\n>> +#ifndef FTS_MAX_READDIR_ENTRIES\n>> +# define FTS_MAX_READDIR_ENTRIES 100000\n>> +#endif\n>> +\n>> +/* If there are more than this many entries in a directory,\n>> +   and the conditions mentioned below are satisfied, then sort\n>> +   the entries on inode number before any further processing.  */\n>> +#ifndef FTS_INODE_SORT_DIR_ENTRIES_THRESHOLD\n>> +# define FTS_INODE_SORT_DIR_ENTRIES_THRESHOLD 10000\n>> +#endif\n>> +\n>> +enum\n>> +{\n>> +  _FTS_INODE_SORT_DIR_ENTRIES_THRESHOLD = FTS_INODE_SORT_DIR_ENTRIES_THRESHOLD\n>> +};\n>> +\n>> +enum Fts_stat\n>> +{\n>> +  FTS_NO_STAT_REQUIRED = 1,\n>> +  FTS_STAT_REQUIRED = 2\n>> +};\n>> +\n>> +#ifndef __set_errno\n>> +# define __set_errno(Val) errno = (Val)\n>> +#endif\n>> +\n>> +/* If this host provides the openat function, then we can avoid\n>> +   attempting to open \".\" in some initialization code below.  */\n>> +#ifdef HAVE_OPENAT\n>> +# define HAVE_OPENAT_SUPPORT 1\n>> +#else\n>> +# define HAVE_OPENAT_SUPPORT 0\n>> +#endif\n>> +\n>> +#ifdef NDEBUG\n>> +# define fts_assert(expr) ((void) (0 && (expr)))\n>> +#else\n>> +# define fts_assert(expr)       \\\n>> +    do                          \\\n>> +      {                         \\\n>> +        if (!(expr))            \\\n>> +          abort ();             \\\n>> +      }                         \\\n>> +    while (false)\n>> +#endif\n>> +\n>> +static FTSENTRY  *fts_alloc (FTSOBJ *, const char *, size_t);\n>> +static FTSENTRY  *fts_build (FTSOBJ *, int);\n>> +static void      fts_lfree (FTSENTRY *);\n>> +static void      fts_load (FTSOBJ *, FTSENTRY *);\n>> +static size_t    fts_maxarglen (char * const *);\n>> +static void      fts_padjust (FTSOBJ *, FTSENTRY *);\n>> +static bool      fts_palloc (FTSOBJ *, size_t);\n>> +static FTSENTRY  *fts_sort (FTSOBJ *, FTSENTRY *, size_t);\n>> +static unsigned short int fts_stat (FTSOBJ *, FTSENTRY *, bool);\n>> +static int      fts_safe_changedir (FTSOBJ *, FTSENTRY *, int, const char *);\n>> +\n>> +#include \"fts-cycle.c\"\n>> +\n>> +#ifndef MAX\n>> +# define MAX(a,b) ((a) > (b) ? (a) : (b))\n>> +#endif\n>> +\n>> +#ifndef SIZE_MAX\n>> +# define SIZE_MAX ((size_t) -1)\n>> +#endif\n>> +\n>> +#define ISDOT(a)        (a[0] == '.' && (!a[1] || (a[1] == '.' && !a[2])))\n>> +\n>> +#define CLR(opt)        (sp->fts_options &= ~(opt))\n>> +#define ISSET(opt)      ((sp->fts_options & (opt)) != 0)\n>> +#define SET(opt)        (sp->fts_options |= (opt))\n>> +\n>> +/* FIXME: FTS_NOCHDIR is now misnamed.\n>> +   Call it FTS_USE_FULL_RELATIVE_FILE_NAMES instead. */\n>> +#define FCHDIR(sp, fd)                                  \\\n>> +  (!ISSET(FTS_NOCHDIR) && (ISSET(FTS_CWDFD)             \\\n>> +                           ? (cwd_advance_fd ((sp), (fd), true), 0) \\\n>> +                           : fchdir (fd)))\n>> +\n>> +\n>> +/* fts_build flags */\n>> +/* FIXME: make this an enum */\n>> +#define BCHILD          1               /* fts_children */\n>> +#define BNAMES          2               /* fts_children, names only */\n>> +#define BREAD           3               /* fts_read */\n>> +\n>> +#if GNULIB_FTS_DEBUG\n>> +# include <inttypes.h>\n>> +# include <stdio.h>\n>> +bool fts_debug = false;\n>> +# define Dprintf(x) do { if (fts_debug) printf x; } while (false)\n>> +static void fd_ring_check (FTSOBJ const *);\n>> +static void fd_ring_print (FTSOBJ const *, FILE *, char const *);\n>> +#else\n>> +# define Dprintf(x)\n>> +# define fd_ring_check(x)\n>> +# define fd_ring_print(a, b, c)\n>> +#endif\n>> +\n>> +#define LEAVE_DIR(Fts, Ent, Tag)                                \\\n>> +  do                                                            \\\n>> +    {                                                           \\\n>> +      Dprintf ((\"  %s-leaving: %s\\n\", Tag, (Ent)->fts_path));   \\\n>> +      leave_dir (Fts, Ent);                                     \\\n>> +      fd_ring_check (Fts);                                      \\\n>> +    }                                                           \\\n>> +  while (false)\n>> +\n>> +static void\n>> +fd_ring_clear (I_ring *fd_ring)\n>> +{\n>> +  while ( ! i_ring_empty (fd_ring))\n>> +    {\n>> +      int fd = i_ring_pop (fd_ring);\n>> +      if (0 <= fd)\n>> +        close (fd);\n>> +    }\n>> +}\n>> +\n>> +/* Overload the fts_statp->st_size member (otherwise unused, when\n>> +   fts_info is FTS_NSOK) to indicate whether fts_read should stat\n>> +   this entry or not.  */\n>> +static void\n>> +fts_set_stat_required (FTSENTRY *p, bool required)\n>> +{\n>> +  fts_assert (p->fts_info == FTS_NSOK);\n>> +  p->fts_statp->st_size = (required\n>> +                           ? FTS_STAT_REQUIRED\n>> +                           : FTS_NO_STAT_REQUIRED);\n>> +}\n>> +\n>> +/* Virtual fchdir.  Advance SP's working directory file descriptor,\n>> +   SP->fts_cwd_fd, to FD, and push the previous value onto the fd_ring.\n>> +   CHDIR_DOWN_ONE is true if FD corresponds to an entry in the directory\n>> +   open on sp->fts_cwd_fd; i.e., to move the working directory one level\n>> +   down.  */\n>> +static void\n>> +internal_function\n>> +cwd_advance_fd (FTSOBJ *sp, int fd, bool chdir_down_one)\n>> +{\n>> +  int old = sp->fts_cwd_fd;\n>> +  fts_assert (old != fd || old == AT_FDCWD);\n>> +\n>> +  if (chdir_down_one)\n>> +    {\n>> +      /* Push \"old\" onto the ring.\n>> +         If the displaced file descriptor is non-negative, close it.  */\n>> +      int prev_fd_in_slot = i_ring_push (&sp->fts_fd_ring, old);\n>> +      fd_ring_print (sp, stderr, \"post-push\");\n>> +      if (0 <= prev_fd_in_slot)\n>> +        close (prev_fd_in_slot); /* ignore any close failure */\n>> +    }\n>> +  else if ( ! ISSET (FTS_NOCHDIR))\n>> +    {\n>> +      if (0 <= old)\n>> +        close (old); /* ignore any close failure */\n>> +    }\n>> +\n>> +  sp->fts_cwd_fd = fd;\n>> +}\n>> +\n>> +/* Restore the initial, pre-traversal, \"working directory\".\n>> +   In FTS_CWDFD mode, we merely call cwd_advance_fd, otherwise,\n>> +   we may actually change the working directory.\n>> +   Return 0 upon success. Upon failure, set errno and return nonzero.  */\n>> +static int\n>> +restore_initial_cwd (FTSOBJ *sp)\n>> +{\n>> +  int fail = FCHDIR (sp, ISSET (FTS_CWDFD) ? AT_FDCWD : sp->fts_rfd);\n>> +  fd_ring_clear (&(sp->fts_fd_ring));\n>> +  return fail;\n>> +}\n>> +\n>> +/* Open the directory DIR if possible, and return a file\n>> +   descriptor.  Return -1 and set errno on failure.  It doesn't matter\n>> +   whether the file descriptor has read or write access.  */\n>> +\n>> +static int\n>> +internal_function\n>> +diropen (FTSOBJ const *sp, char const *dir)\n>> +{\n>> +  int open_flags = (O_SEARCH | O_CLOEXEC | O_DIRECTORY | O_NOCTTY | O_NONBLOCK\n>> +                    | (ISSET (FTS_PHYSICAL) ? O_NOFOLLOW : 0));\n>> +\n>> +  int fd = (ISSET (FTS_CWDFD)\n>> +            ? openat (sp->fts_cwd_fd, dir, open_flags)\n>> +            : open (dir, open_flags));\n>> +  return fd;\n>> +}\n>> +\n>> +FTSOBJ *\n>> +FTS_OPEN (char * const *argv,\n>> +          register int options,\n>> +          int (*compar) (const FTSENTRY **, const FTSENTRY **))\n>> +{\n>> +        /* Options check: glibc added other flags after FTS_NAMEONLY and\n>> +\t   FTS_STOP, and they are assumed to be private.  */\n>> +        if (options & ~FTS_OPTIONMASK\n>> +#if _LIBC\n>> +\t    || options & (FTS_NAMEONLY | FTS_STOP)\n>> +#endif\n>> +\t    ) {\n>> +                __set_errno (EINVAL);\n>> +                return (NULL);\n>> +        }\n>> +        if ((options & FTS_NOCHDIR) && (options & FTS_CWDFD)) {\n>> +                __set_errno (EINVAL);\n>> +                return (NULL);\n>> +        }\n>> +#if !_LIBC\n>> +        if ( ! (options & (FTS_LOGICAL | FTS_PHYSICAL))) {\n>> +                __set_errno (EINVAL);\n>> +                return (NULL);\n>> +        }\n>> +#else\n>> +\t/* glibc historically falls to FTS_PHYSICAL if no FTS_PHYSICAL or\n>> +\t   FTS_LOGICAL is specified.  */\n>> +        if (! (options & (FTS_PHYSICAL | FTS_LOGICAL)))\n>> +\t  options |= FTS_PHYSICAL;\n>> +#endif\n>> +\n>> +        /* Allocate/initialize the stream */\n>> +        register FTSOBJ *sp = calloc (1, sizeof *sp);\n>> +        if (sp == NULL)\n>> +                return (NULL);\n>> +        sp->fts_compar = FTS_COMPAR_CAST(compar);\n>> +        sp->fts_options = options;\n>> +\n>> +        /* Logical walks turn on NOCHDIR; symbolic links are too hard. */\n>> +        if (ISSET(FTS_LOGICAL)) {\n>> +                SET(FTS_NOCHDIR);\n>> +                CLR(FTS_CWDFD);\n>> +        }\n>> +\n>> +        /* Initialize fts_cwd_fd.  */\n>> +        sp->fts_cwd_fd = AT_FDCWD;\n>> +        if ( ISSET(FTS_CWDFD) && ! HAVE_OPENAT_SUPPORT)\n>> +          {\n>> +            /* While it isn't technically necessary to open \".\" this\n>> +               early, doing it here saves us the trouble of ensuring\n>> +               later (where it'd be messier) that \".\" can in fact\n>> +               be opened.  If not, revert to FTS_NOCHDIR mode.  */\n>> +            int fd = open (\".\", O_SEARCH | O_CLOEXEC);\n>> +            if (fd < 0)\n>> +              {\n>> +                /* Even if \".\" is unreadable, don't revert to FTS_NOCHDIR mode\n>> +                   on systems like Linux+PROC_FS, where our openat emulation\n>> +                   is good enough.  Note: on a system that emulates\n>> +                   openat via /proc, this technique can still fail, but\n>> +                   only in extreme conditions, e.g., when the working\n>> +                   directory cannot be saved (i.e. save_cwd fails) --\n>> +                   and that happens on Linux only when \".\" is unreadable\n>> +                   and the CWD would be longer than PATH_MAX.\n>> +                   FIXME: once Linux kernel openat support is well established,\n>> +                   replace the above open call and this entire if/else block\n>> +                   with the body of the if-block below.  */\n>> +                if ( openat_needs_fchdir ())\n>> +                  {\n>> +                    SET(FTS_NOCHDIR);\n>> +                    CLR(FTS_CWDFD);\n>> +                  }\n>> +              }\n>> +            else\n>> +              {\n>> +                close (fd);\n>> +              }\n>> +          }\n>> +\n>> +        /*\n>> +         * Start out with 1K of file name space, and enough, in any case,\n>> +         * to hold the user's file names.\n>> +         */\n>> +#ifndef MAXPATHLEN\n>> +# define MAXPATHLEN 1024\n>> +#endif\n>> +        {\n>> +          size_t maxarglen = fts_maxarglen(argv);\n>> +          if (! fts_palloc(sp, MAX(maxarglen, MAXPATHLEN)))\n>> +                  goto mem1;\n>> +        }\n>> +\n>> +        /* Allocate/initialize root's parent. */\n>> +        FTSENTRY *parent = NULL;\n>> +        if (*argv != NULL) {\n>> +                if ((parent = fts_alloc(sp, \"\", 0)) == NULL)\n>> +                        goto mem2;\n>> +                parent->fts_level = FTS_ROOTPARENTLEVEL;\n>> +          }\n>> +\n>> +        /* The classic fts implementation would call fts_stat with\n>> +           a new entry for each iteration of the loop below.\n>> +           If the comparison function is not specified or if the\n>> +           FTS_DEFER_STAT option is in effect, don't stat any entry\n>> +           in this loop.  This is an attempt to minimize the interval\n>> +           between the initial stat/lstat/fstatat and the point at which\n>> +           a directory argument is first opened.  This matters for any\n>> +           directory command line argument that resides on a file system\n>> +           without genuine i-nodes.  If you specify FTS_DEFER_STAT along\n>> +           with a comparison function, that function must not access any\n>> +           data via the fts_statp pointer.  */\n>> +        bool defer_stat = (compar == NULL || ISSET(FTS_DEFER_STAT));\n>> +\n>> +        /* Allocate/initialize root(s). */\n>> +        register FTSENTRY *root;\n>> +        register size_t nitems;\n>> +        FTSENTRY *tmp = NULL;     /* pacify gcc */\n>> +        for (root = NULL, nitems = 0; *argv != NULL; ++argv, ++nitems) {\n>> +                /* *Do* allow zero-length file names. */\n>> +                size_t len = strlen(*argv);\n>> +\n>> +                if ( ! (options & FTS_VERBATIM))\n>> +                  {\n>> +                    /* If there are two or more trailing slashes, trim all but one,\n>> +                       but don't change \"//\" to \"/\", and do map \"///\" to \"/\".  */\n>> +                    char const *v = *argv;\n>> +                    if (2 < len && v[len - 1] == '/')\n>> +                      while (1 < len && v[len - 2] == '/')\n>> +                        --len;\n>> +                  }\n>> +\n>> +                register FTSENTRY *p = fts_alloc(sp, *argv, len);\n>> +                if (p == NULL)\n>> +                        goto mem3;\n>> +                p->fts_level = FTS_ROOTLEVEL;\n>> +                p->fts_parent = parent;\n>> +                p->fts_accpath = p->fts_name;\n>> +                /* Even when defer_stat is true, be sure to stat the first\n>> +                   command line argument, since fts_read (at least with\n>> +                   FTS_XDEV) requires that.  */\n>> +                if (defer_stat && root != NULL) {\n>> +                        p->fts_info = FTS_NSOK;\n>> +                        fts_set_stat_required(p, true);\n>> +                } else {\n>> +                        p->fts_info = fts_stat(sp, p, false);\n>> +                }\n>> +\n>> +                /*\n>> +                 * If comparison routine supplied, traverse in sorted\n>> +                 * order; otherwise traverse in the order specified.\n>> +                 */\n>> +                if (compar) {\n>> +                        p->fts_link = root;\n>> +                        root = p;\n>> +                } else {\n>> +                        p->fts_link = NULL;\n>> +                        if (root == NULL)\n>> +                                tmp = root = p;\n>> +                        else {\n>> +                                tmp->fts_link = p;\n>> +                                tmp = p;\n>> +                        }\n>> +                }\n>> +        }\n>> +        if (compar && nitems > 1)\n>> +                root = fts_sort(sp, root, nitems);\n>> +\n>> +        /*\n>> +         * Allocate a dummy pointer and make fts_read think that we've just\n>> +         * finished the node before the root(s); set p->fts_info to FTS_INIT\n>> +         * so that everything about the \"current\" node is ignored.\n>> +         */\n>> +        if ((sp->fts_cur = fts_alloc(sp, \"\", 0)) == NULL)\n>> +                goto mem3;\n>> +        sp->fts_cur->fts_link = root;\n>> +        sp->fts_cur->fts_info = FTS_INIT;\n>> +        sp->fts_cur->fts_level = 1;\n>> +        if (! setup_dir (sp))\n>> +                goto mem3;\n>> +\n>> +        /*\n>> +         * If using chdir(2), grab a file descriptor pointing to dot to ensure\n>> +         * that we can get back here; this could be avoided for some file names,\n>> +         * but almost certainly not worth the effort.  Slashes, symbolic links,\n>> +         * and \"..\" are all fairly nasty problems.  Note, if we can't get the\n>> +         * descriptor we run anyway, just more slowly.\n>> +         */\n>> +        if (!ISSET(FTS_NOCHDIR) && !ISSET(FTS_CWDFD)\n>> +            && (sp->fts_rfd = diropen (sp, \".\")) < 0)\n>> +                SET(FTS_NOCHDIR);\n>> +\n>> +        i_ring_init (&sp->fts_fd_ring, -1);\n>> +        return (sp);\n>> +\n>> +mem3:   fts_lfree(root);\n>> +        free(FTSENT_WRAPPER(parent));\n>> +mem2:   free(sp->fts_path);\n>> +mem1:   free(sp);\n>> +        return (NULL);\n>> +}\n>> +\n>> +static void\n>> +internal_function\n>> +fts_load (FTSOBJ *sp, register FTSENTRY *p)\n>> +{\n>> +        /*\n>> +         * Load the stream structure for the next traversal.  Since we don't\n>> +         * actually enter the directory until after the preorder visit, set\n>> +         * the fts_accpath field specially so the chdir gets done to the right\n>> +         * place and the user can access the first node.  From fts_open it's\n>> +         * known that the file name will fit.\n>> +         */\n>> +        register size_t len = p->fts_pathlen = p->fts_namelen;\n>> +        memmove(sp->fts_path, p->fts_name, len + 1);\n>> +        register char *cp = strrchr(p->fts_name, '/');\n>> +        if (cp && (cp != p->fts_name || cp[1])) {\n>> +                len = strlen(++cp);\n>> +                memmove(p->fts_name, cp, len + 1);\n>> +                p->fts_namelen = len;\n>> +        }\n>> +        p->fts_accpath = p->fts_path = sp->fts_path;\n>> +}\n>> +\n>> +int\n>> +FTS_CLOSE (FTSOBJ *sp)\n>> +{\n>> +        /*\n>> +         * This still works if we haven't read anything -- the dummy structure\n>> +         * points to the root list, so we step through to the end of the root\n>> +         * list which has a valid parent pointer.\n>> +         */\n>> +        if (sp->fts_cur) {\n>> +                register FTSENTRY *p;\n>> +                for (p = sp->fts_cur; p->fts_level >= FTS_ROOTLEVEL;) {\n>> +                        register FTSENTRY *freep = p;\n>> +                        p = p->fts_link != NULL ? p->fts_link : p->fts_parent;\n>> +                        free(FTSENT_WRAPPER(freep));\n>> +                }\n>> +                free(FTSENT_WRAPPER(p));\n>> +        }\n>> +\n>> +        /* Free up child linked list, sort array, file name buffer. */\n>> +        if (sp->fts_child)\n>> +                fts_lfree(sp->fts_child);\n>> +        free(sp->fts_array);\n>> +        free(sp->fts_path);\n>> +\n>> +        int saved_errno = 0;\n>> +        if (ISSET(FTS_CWDFD))\n>> +          {\n>> +            if (0 <= sp->fts_cwd_fd)\n>> +              if (close (sp->fts_cwd_fd))\n>> +                saved_errno = errno;\n>> +          }\n>> +        else if (!ISSET(FTS_NOCHDIR))\n>> +          {\n>> +            /* Return to original directory, save errno if necessary. */\n>> +            if (fchdir(sp->fts_rfd))\n>> +              saved_errno = errno;\n>> +\n>> +            /* If close fails, record errno only if saved_errno is zero,\n>> +               so that we report the probably-more-meaningful fchdir errno.  */\n>> +            if (close (sp->fts_rfd))\n>> +              if (saved_errno == 0)\n>> +                saved_errno = errno;\n>> +          }\n>> +\n>> +        fd_ring_clear (&sp->fts_fd_ring);\n>> +\n>> +        if (sp->fts_leaf_optimization_works_ht)\n>> +          hash_free (sp->fts_leaf_optimization_works_ht);\n>> +\n>> +        free_dir (sp);\n>> +\n>> +        /* Free up the stream pointer. */\n>> +        free(sp);\n>> +\n>> +        /* Set errno and return. */\n>> +        if (saved_errno) {\n>> +                __set_errno (saved_errno);\n>> +                return (-1);\n>> +        }\n>> +\n>> +        return (0);\n>> +}\n>> +\n>> +/* Minimum link count of a traditional Unix directory.  When leaf\n>> +   optimization is OK and a directory's st_nlink == MIN_DIR_NLINK,\n>> +   then the directory has no subdirectories.  */\n>> +enum { MIN_DIR_NLINK = 2 };\n>> +\n>> +/* Whether leaf optimization is OK for a directory.  */\n>> +enum leaf_optimization\n>> +  {\n>> +    /* st_nlink is not reliable for this directory's subdirectories.  */\n>> +    NO_LEAF_OPTIMIZATION,\n>> +\n>> +    /* st_nlink == 2 means the directory lacks subdirectories.  */\n>> +    OK_LEAF_OPTIMIZATION\n>> +  };\n>> +\n>> +#if (defined __linux__ || defined __ANDROID__) \\\n>> +  && HAVE_SYS_VFS_H && HAVE_FSTATFS && HAVE_STRUCT_STATFS_F_TYPE\n>> +\n>> +# include <sys/vfs.h>\n>> +\n>> +/* Linux-specific constants from coreutils' src/fs.h */\n>> +# define S_MAGIC_AFS 0x5346414F\n>> +# define S_MAGIC_CIFS 0xFF534D42\n>> +# define S_MAGIC_LUSTRE 0x0BD00BD0\n>> +# define S_MAGIC_NFS 0x6969\n>> +# define S_MAGIC_PROC 0x9FA0\n>> +# define S_MAGIC_TMPFS 0x1021994\n>> +\n>> +# ifdef HAVE___FSWORD_T\n>> +typedef __fsword_t fsword;\n>> +# else\n>> +typedef long int fsword;\n>> +# endif\n>> +\n>> +/* Map a stat.st_dev number to a file system type number f_ftype.  */\n>> +struct dev_type\n>> +{\n>> +  dev_t st_dev;\n>> +  fsword f_type;\n>> +};\n>> +\n>> +/* Use a tiny initial size.  If a traversal encounters more than\n>> +   a few devices, the cost of growing/rehashing this table will be\n>> +   rendered negligible by the number of inodes processed.  */\n>> +enum { DEV_TYPE_HT_INITIAL_SIZE = 13 };\n>> +\n>> +static size_t\n>> +dev_type_hash (void const *x, size_t table_size)\n>> +{\n>> +  struct dev_type const *ax = x;\n>> +  uintmax_t dev = ax->st_dev;\n>> +  return dev % table_size;\n>> +}\n>> +\n>> +static bool\n>> +dev_type_compare (void const *x, void const *y)\n>> +{\n>> +  struct dev_type const *ax = x;\n>> +  struct dev_type const *ay = y;\n>> +  return ax->st_dev == ay->st_dev;\n>> +}\n>> +\n>> +/* Return the file system type of P with file descriptor FD, or 0 if not known.\n>> +   If FD is negative, P's file descriptor is unavailable.\n>> +   Try to cache known values.  */\n>> +\n>> +static fsword\n>> +filesystem_type (FTSENTRY const *p, int fd)\n>> +{\n>> +  FTSOBJ *sp = FTSENT_FTS(p);\n>> +\n>> +  /* If we're not in CWDFD mode, don't bother with this optimization,\n>> +     since the caller is not serious about performance.  */\n>> +  if (!ISSET (FTS_CWDFD))\n>> +    return 0;\n>> +\n>> +  Hash_table *h = sp->fts_leaf_optimization_works_ht;\n>> +  if (! h)\n>> +    h = sp->fts_leaf_optimization_works_ht\n>> +      = hash_initialize (DEV_TYPE_HT_INITIAL_SIZE, NULL, dev_type_hash,\n>> +                         dev_type_compare, free);\n>> +\n>> +  if (h)\n>> +    {\n>> +      struct dev_type tmp;\n>> +      tmp.st_dev = p->fts_statp->st_dev;\n>> +      struct dev_type *ent = hash_lookup (h, &tmp);\n>> +      if (ent)\n>> +        return ent->f_type;\n>> +    }\n>> +\n>> +  /* Look-up failed.  Query directly and cache the result.  */\n>> +  struct STRUCT_STATFS fs_buf;\n>> +  if (fd < 0 || FSTATFS (fd, &fs_buf) != 0)\n>> +    return 0;\n>> +\n>> +  if (h)\n>> +    {\n>> +      struct dev_type *t2 = malloc (sizeof *t2);\n>> +      if (t2)\n>> +        {\n>> +          t2->st_dev = p->fts_statp->st_dev;\n>> +          t2->f_type = fs_buf.f_type;\n>> +\n>> +          struct dev_type *ent = hash_insert (h, t2);\n>> +          if (ent)\n>> +            fts_assert (ent == t2);\n>> +          else\n>> +            free (t2);\n>> +        }\n>> +    }\n>> +\n>> +  return fs_buf.f_type;\n>> +}\n>> +\n>> +/* Return true if sorting dirents on inode numbers is known to improve\n>> +   traversal performance for the directory P with descriptor DIR_FD.\n>> +   Return false otherwise.  When in doubt, return true.\n>> +   DIR_FD is negative if unavailable.  */\n>> +static bool\n>> +dirent_inode_sort_may_be_useful (FTSENTRY const *p, int dir_fd)\n>> +{\n>> +  /* Skip the sort only if we can determine efficiently\n>> +     that skipping it is the right thing to do.\n>> +     The cost of performing an unnecessary sort is negligible,\n>> +     while the cost of *not* performing it can be O(N^2) with\n>> +     a very large constant.  */\n>> +\n>> +  switch (filesystem_type (p, dir_fd))\n>> +    {\n>> +    case S_MAGIC_LUSTRE:\n>> +      /* On Lustre, sorting directory entries interferes with its ability to\n>> +         prefetch file metadata (via statahead).  This would make a command\n>> +         like 'du' around 9 times slower.  See\n>> +         <https://bugs.gnu.org/80106>.  */\n>> +    case S_MAGIC_CIFS:\n>> +    case S_MAGIC_NFS:\n>> +    case S_MAGIC_TMPFS:\n>> +      /* On a file system of any of these types, sorting\n>> +         is unnecessary, and hence wasteful.  */\n>> +      return false;\n>> +\n>> +    default:\n>> +      return true;\n>> +    }\n>> +}\n>> +\n>> +/* Given an FTS entry P for a directory with descriptor DIR_FD,\n>> +   return whether it is valid to apply leaf optimization.\n>> +   The optimization is valid if a directory's st_nlink value equal\n>> +   to MIN_DIR_NLINK means the directory has no subdirectories.\n>> +   DIR_FD is negative if unavailable.  */\n>> +static enum leaf_optimization\n>> +leaf_optimization (FTSENTRY const *p, int dir_fd)\n>> +{\n>> +  switch (filesystem_type (p, dir_fd))\n>> +    {\n>> +    case 0:\n>> +      /* Leaf optimization is unsafe if the file system type is unknown.  */\n>> +      FALLTHROUGH;\n>> +    case S_MAGIC_AFS:\n>> +      /* Although AFS mount points are not counted in st_nlink, they\n>> +         act like directories.  See <https://bugs.debian.org/143111>.  */\n>> +      FALLTHROUGH;\n>> +    case S_MAGIC_CIFS:\n>> +      /* Leaf optimization causes 'find' to abort.  See\n>> +         <https://lists.gnu.org/r/bug-gnulib/2018-04/msg00015.html>.  */\n>> +      FALLTHROUGH;\n>> +    case S_MAGIC_NFS:\n>> +      /* NFS provides usable dirent.d_type but not necessarily for all entries\n>> +         of large directories, so as per <https://bugzilla.redhat.com/1252549>\n>> +         NFS should return true.  However st_nlink values are not accurate on\n>> +         all implementations as per <https://bugzilla.redhat.com/1299169>.  */\n>> +      FALLTHROUGH;\n>> +    case S_MAGIC_PROC:\n>> +      /* Per <https://bugs.debian.org/cgi-bin/bugreport.cgi?bug=143111> /proc\n>> +         may have bogus stat.st_nlink values.  */\n>> +      return NO_LEAF_OPTIMIZATION;\n>> +\n>> +    default:\n>> +      return OK_LEAF_OPTIMIZATION;\n>> +    }\n>> +}\n>> +\n>> +#else\n>> +static bool\n>> +dirent_inode_sort_may_be_useful (_GL_UNUSED FTSENTRY const *p,\n>> +                                 _GL_UNUSED int dir_fd)\n>> +{\n>> +  return true;\n>> +}\n>> +static enum leaf_optimization\n>> +leaf_optimization (_GL_UNUSED FTSENTRY const *p, _GL_UNUSED int dir_fd)\n>> +{\n>> +  return NO_LEAF_OPTIMIZATION;\n>> +}\n>> +#endif\n>> +\n>> +/*\n>> + * Special case of \"/\" at the end of the file name so that slashes aren't\n>> + * appended which would cause file names to be written as \"....//foo\".\n>> + */\n>> +#define NAPPEND(p)                                                      \\\n>> +        (p->fts_path[p->fts_pathlen - 1] == '/'                         \\\n>> +            ? p->fts_pathlen - 1 : p->fts_pathlen)\n>> +\n>> +FTSENTRY *\n>> +FTS_READ (FTSOBJ *sp)\n>> +{\n>> +        /* If finished or unrecoverable error, return NULL. */\n>> +        if (sp->fts_cur == NULL || ISSET(FTS_STOP))\n>> +                return (NULL);\n>> +\n>> +        /* Set current node pointer. */\n>> +        register FTSENTRY *p = sp->fts_cur;\n>> +\n>> +        /* Save and zero out user instructions. */\n>> +        register unsigned short int instr = p->fts_instr;\n>> +        p->fts_instr = FTS_NOINSTR;\n>> +\n>> +        /* Any type of file may be re-visited; re-stat and re-turn. */\n>> +        if (instr == FTS_AGAIN) {\n>> +                p->fts_info = fts_stat(sp, p, false);\n>> +                return (p);\n>> +        }\n>> +        Dprintf ((\"fts_read: p=%s\\n\",\n>> +                  p->fts_info == FTS_INIT ? \"\" : p->fts_path));\n>> +\n>> +        /*\n>> +         * Following a symlink -- SLNONE test allows application to see\n>> +         * SLNONE and recover.  If indirecting through a symlink, have\n>> +         * keep a pointer to current location.  If unable to get that\n>> +         * pointer, follow fails.\n>> +         */\n>> +        if (instr == FTS_FOLLOW &&\n>> +            (p->fts_info == FTS_SL || p->fts_info == FTS_SLNONE)) {\n>> +                p->fts_info = fts_stat(sp, p, true);\n>> +                if (p->fts_info == FTS_D && !ISSET(FTS_NOCHDIR)) {\n>> +                        if ((p->fts_symfd = diropen (sp, \".\")) < 0) {\n>> +                                p->fts_errno = errno;\n>> +                                p->fts_info = FTS_ERR;\n>> +                        } else\n>> +                                p->fts_flags |= FTS_SYMFOLLOW;\n>> +                }\n>> +                goto check_for_dir;\n>> +        }\n>> +\n>> +        /* Directory in pre-order. */\n>> +        if (p->fts_info == FTS_D) {\n>> +                /* If skipped or crossed mount point, do post-order visit. */\n>> +                if (instr == FTS_SKIP ||\n>> +                    (ISSET(FTS_XDEV) && p->fts_statp->st_dev != sp->fts_dev)) {\n>> +                        if (p->fts_flags & FTS_SYMFOLLOW)\n>> +                                (void)close(p->fts_symfd);\n>> +                        if (sp->fts_child) {\n>> +                                fts_lfree(sp->fts_child);\n>> +                                sp->fts_child = NULL;\n>> +                        }\n>> +                        p->fts_info = FTS_DP;\n>> +                        LEAVE_DIR (sp, p, \"1\");\n>> +                        return (p);\n>> +                }\n>> +\n>> +                /* Rebuild if only read the names and now traversing. */\n>> +                if (sp->fts_child != NULL && ISSET(FTS_NAMEONLY)) {\n>> +                        CLR(FTS_NAMEONLY);\n>> +                        fts_lfree(sp->fts_child);\n>> +                        sp->fts_child = NULL;\n>> +                }\n>> +\n>> +                /*\n>> +                 * Cd to the subdirectory.\n>> +                 *\n>> +                 * If have already read and now fail to chdir, whack the list\n>> +                 * to make the names come out right, and set the parent errno\n>> +                 * so the application will eventually get an error condition.\n>> +                 * Set the FTS_DONTCHDIR flag so that when we logically change\n>> +                 * directories back to the parent we don't do a chdir.\n>> +                 *\n>> +                 * If haven't read do so.  If the read fails, fts_build sets\n>> +                 * FTS_STOP or the fts_info field of the node.\n>> +                 */\n>> +                if (sp->fts_child != NULL) {\n>> +                        if (fts_safe_changedir(sp, p, -1, p->fts_accpath)) {\n>> +                                p->fts_errno = errno;\n>> +                                p->fts_flags |= FTS_DONTCHDIR;\n>> +                                for (p = sp->fts_child; p != NULL;\n>> +                                     p = p->fts_link)\n>> +                                        p->fts_accpath =\n>> +                                            p->fts_parent->fts_accpath;\n>> +                        }\n>> +                } else if ((sp->fts_child = fts_build(sp, BREAD)) == NULL) {\n>> +                        if (ISSET(FTS_STOP))\n>> +                                return (NULL);\n>> +                        /* If fts_build's call to fts_safe_changedir failed\n>> +                           because it was not able to fchdir into a\n>> +                           subdirectory, tell the caller.  */\n>> +                        if (p->fts_errno && p->fts_info != FTS_DNR)\n>> +                                p->fts_info = FTS_ERR;\n>> +                        LEAVE_DIR (sp, p, \"2\");\n>> +                        return (p);\n>> +                }\n>> +                p = sp->fts_child;\n>> +                sp->fts_child = NULL;\n>> +                goto name;\n>> +        }\n>> +\n>> +        /* Move to the next node on this level. */\n>> +next: ;\n>> +        register FTSENTRY *tmp = p;\n>> +\n>> +        /* If we have so many directory entries that we're reading them\n>> +           in batches, and we've reached the end of the current batch,\n>> +           read in a new batch.  */\n>> +        if (p->fts_link == NULL && FTSENT_DIRP(p->fts_parent))\n>> +          {\n>> +            p = tmp->fts_parent;\n>> +            sp->fts_cur = p;\n>> +            sp->fts_path[p->fts_pathlen] = '\\0';\n>> +\n>> +            if ((p = fts_build (sp, BREAD)) == NULL)\n>> +              {\n>> +                if (ISSET(FTS_STOP))\n>> +                  return NULL;\n>> +                goto cd_dot_dot;\n>> +              }\n>> +\n>> +            free(FTSENT_WRAPPER(tmp));\n>> +            goto name;\n>> +          }\n>> +\n>> +        if ((p = p->fts_link) != NULL) {\n>> +                sp->fts_cur = p;\n>> +                free(FTSENT_WRAPPER(tmp));\n>> +\n>> +                /*\n>> +                 * If reached the top, return to the original directory (or\n>> +                 * the root of the tree), and load the file names for the next\n>> +                 * root.\n>> +                 */\n>> +                if (p->fts_level == FTS_ROOTLEVEL) {\n>> +                        if (restore_initial_cwd(sp)) {\n>> +                                SET(FTS_STOP);\n>> +                                return (NULL);\n>> +                        }\n>> +                        free_dir(sp);\n>> +                        fts_load(sp, p);\n>> +                        if (! setup_dir(sp)) {\n>> +                                free_dir(sp);\n>> +                                return (NULL);\n>> +                        }\n>> +                        goto check_for_dir;\n>> +                }\n>> +\n>> +                /*\n>> +                 * User may have called fts_set on the node.  If skipped,\n>> +                 * ignore.  If followed, get a file descriptor so we can\n>> +                 * get back if necessary.\n>> +                 */\n>> +                if (p->fts_instr == FTS_SKIP)\n>> +                        goto next;\n>> +                if (p->fts_instr == FTS_FOLLOW) {\n>> +                        p->fts_info = fts_stat(sp, p, true);\n>> +                        if (p->fts_info == FTS_D && !ISSET(FTS_NOCHDIR)) {\n>> +                                if ((p->fts_symfd = diropen (sp, \".\")) < 0) {\n>> +                                        p->fts_errno = errno;\n>> +                                        p->fts_info = FTS_ERR;\n>> +                                } else\n>> +                                        p->fts_flags |= FTS_SYMFOLLOW;\n>> +                        }\n>> +                        p->fts_instr = FTS_NOINSTR;\n>> +                }\n>> +\n>> +name:           {\n>> +                  register char *t = sp->fts_path + NAPPEND(p->fts_parent);\n>> +                  *t++ = '/';\n>> +                  memmove(t, p->fts_name, p->fts_namelen + 1);\n>> +                }\n>> +check_for_dir:\n>> +                sp->fts_cur = p;\n>> +                if (p->fts_info == FTS_NSOK)\n>> +                  {\n>> +                    if (p->fts_statp->st_size == FTS_STAT_REQUIRED)\n>> +                      p->fts_info = fts_stat(sp, p, false);\n>> +                    else\n>> +                      fts_assert (p->fts_statp->st_size == FTS_NO_STAT_REQUIRED);\n>> +                  }\n>> +\n>> +                /* Skip files with different device numbers when FTS_MOUNT\n>> +                   is set.  */\n>> +                if (ISSET (FTS_MOUNT) && p->fts_info != FTS_NS &&\n>> +                    p->fts_level != FTS_ROOTLEVEL &&\n>> +                    p->fts_statp->st_dev != sp->fts_dev)\n>> +                      goto next;\n>> +\n>> +                if (p->fts_info == FTS_D)\n>> +                  {\n>> +                    /* Now that P->fts_statp is guaranteed to be valid, if\n>> +                       this is a command-line directory, record its device\n>> +                       number, to be used for FTS_MOUNT and FTS_XDEV.  */\n>> +                    if (p->fts_level == FTS_ROOTLEVEL)\n>> +                      sp->fts_dev = p->fts_statp->st_dev;\n>> +                    Dprintf ((\"  entering: %s\\n\", p->fts_path));\n>> +                    if (! enter_dir (sp, p))\n>> +                      return NULL;\n>> +                  }\n>> +                return p;\n>> +        }\n>> +cd_dot_dot:\n>> +\n>> +        /* Move up to the parent node. */\n>> +        p = tmp->fts_parent;\n>> +        sp->fts_cur = p;\n>> +        free(FTSENT_WRAPPER(tmp));\n>> +\n>> +        if (p->fts_level == FTS_ROOTPARENTLEVEL) {\n>> +                /*\n>> +                 * Done; free everything up and set errno to 0 so the user\n>> +                 * can distinguish between error and EOF.\n>> +                 */\n>> +                free(FTSENT_WRAPPER(p));\n>> +                __set_errno (0);\n>> +                return (sp->fts_cur = NULL);\n>> +        }\n>> +\n>> +        fts_assert (p->fts_info != FTS_NSOK);\n>> +\n>> +        /* NUL terminate the file name.  */\n>> +        sp->fts_path[p->fts_pathlen] = '\\0';\n>> +\n>> +        /*\n>> +         * Return to the parent directory.  If at a root node, restore\n>> +         * the initial working directory.  If we came through a symlink,\n>> +         * go back through the file descriptor.  Otherwise, move up\n>> +         * one level, via \"..\".\n>> +         */\n>> +        if (p->fts_level == FTS_ROOTLEVEL) {\n>> +                if (restore_initial_cwd(sp)) {\n>> +                        p->fts_errno = errno;\n>> +                        SET(FTS_STOP);\n>> +                }\n>> +        } else if (p->fts_flags & FTS_SYMFOLLOW) {\n>> +                if (FCHDIR(sp, p->fts_symfd)) {\n>> +                        p->fts_errno = errno;\n>> +                        SET(FTS_STOP);\n>> +                }\n>> +                (void)close(p->fts_symfd);\n>> +        } else if (!(p->fts_flags & FTS_DONTCHDIR) &&\n>> +                   fts_safe_changedir(sp, p->fts_parent, -1, \"..\")) {\n>> +                p->fts_errno = errno;\n>> +                SET(FTS_STOP);\n>> +        }\n>> +\n>> +        /* If the directory causes a cycle, preserve the FTS_DC flag and keep\n>> +           the corresponding dev/ino pair in the hash table.  It is going to be\n>> +           removed when leaving the original directory.  */\n>> +        if (p->fts_info != FTS_DC) {\n>> +                p->fts_info = p->fts_errno ? FTS_ERR : FTS_DP;\n>> +                if (p->fts_errno == 0)\n>> +                        LEAVE_DIR (sp, p, \"3\");\n>> +        }\n>> +        return ISSET(FTS_STOP) ? NULL : p;\n>> +}\n>> +\n>> +/*\n>> + * Fts_set takes the stream as an argument although it's not used in this\n>> + * implementation; it would be necessary if anyone wanted to add global\n>> + * semantics to fts using fts_set.  An error return is allowed for similar\n>> + * reasons.\n>> + */\n>> +/* ARGSUSED */\n>> +int\n>> +FTS_SET (_GL_UNUSED FTSOBJ *sp, FTSENTRY *p, int instr)\n>> +{\n>> +        if (instr != 0 && instr != FTS_AGAIN && instr != FTS_FOLLOW &&\n>> +            instr != FTS_NOINSTR && instr != FTS_SKIP) {\n>> +                __set_errno (EINVAL);\n>> +                return (1);\n>> +        }\n>> +        p->fts_instr = instr;\n>> +        return (0);\n>> +}\n>> +\n>> +FTSENTRY *\n>> +FTS_CHILDREN (FTSOBJ *sp, int instr)\n>> +{\n>> +        if (instr != 0 && instr != FTS_NAMEONLY) {\n>> +                __set_errno (EINVAL);\n>> +                return (NULL);\n>> +        }\n>> +\n>> +        /* Set current node pointer. */\n>> +        register FTSENTRY *p = sp->fts_cur;\n>> +\n>> +        /*\n>> +         * Errno set to 0 so user can distinguish empty directory from\n>> +         * an error.\n>> +         */\n>> +        __set_errno (0);\n>> +\n>> +        /* Fatal errors stop here. */\n>> +        if (ISSET(FTS_STOP))\n>> +                return (NULL);\n>> +\n>> +        /* Return logical hierarchy of user's arguments. */\n>> +        if (p->fts_info == FTS_INIT)\n>> +                return (p->fts_link);\n>> +\n>> +        /*\n>> +         * If not a directory being visited in pre-order, stop here.  Could\n>> +         * allow FTS_DNR, assuming the user has fixed the problem, but the\n>> +         * same effect is available with FTS_AGAIN.\n>> +         */\n>> +        if (p->fts_info != FTS_D /* && p->fts_info != FTS_DNR */)\n>> +                return (NULL);\n>> +\n>> +        /* Free up any previous child list. */\n>> +        if (sp->fts_child != NULL)\n>> +                fts_lfree(sp->fts_child);\n>> +\n>> +        if (instr == FTS_NAMEONLY) {\n>> +                SET(FTS_NAMEONLY);\n>> +                instr = BNAMES;\n>> +        } else\n>> +                instr = BCHILD;\n>> +\n>> +        /*\n>> +         * If using chdir on a relative file name and called BEFORE fts_read\n>> +         * does its chdir to the root of a traversal, we can lose -- we need to\n>> +         * chdir into the subdirectory, and we don't know where the current\n>> +         * directory is, so we can't get back so that the upcoming chdir by\n>> +         * fts_read will work.\n>> +         */\n>> +        if (p->fts_level != FTS_ROOTLEVEL || p->fts_accpath[0] == '/' ||\n>> +            ISSET(FTS_NOCHDIR))\n>> +                return (sp->fts_child = fts_build(sp, instr));\n>> +\n>> +        int fd = diropen (sp, \".\");\n>> +        if (fd < 0)\n>> +                return (sp->fts_child = NULL);\n>> +        sp->fts_child = fts_build(sp, instr);\n>> +        if (ISSET(FTS_CWDFD))\n>> +          {\n>> +            cwd_advance_fd (sp, fd, true);\n>> +          }\n>> +        else\n>> +          {\n>> +            if (fchdir(fd))\n>> +              {\n>> +                int saved_errno = errno;\n>> +                close (fd);\n>> +                __set_errno (saved_errno);\n>> +                return NULL;\n>> +              }\n>> +            close (fd);\n>> +          }\n>> +        return (sp->fts_child);\n>> +}\n>> +\n>> +/* A comparison function to sort on increasing inode number.\n>> +   For some file system types, sorting either way makes a huge\n>> +   performance difference for a directory with very many entries,\n>> +   but sorting on increasing values is slightly better than sorting\n>> +   on decreasing values.  The difference is in the 5% range.  */\n>> +static int\n>> +fts_compare_ino (FTSENTRY const **a, FTSENTRY const **b)\n>> +{\n>> +  return _GL_CMP (a[0]->fts_statp->st_ino, b[0]->fts_statp->st_ino);\n>> +}\n>> +\n>> +/* Map the dirent.d_type value, DTYPE, to the corresponding stat.st_mode\n>> +   S_IF* bit and set ST.st_mode, thus clearing all other bits in that field.  */\n>> +static void\n>> +set_stat_type (struct STRUCT_STAT *st, unsigned int dtype)\n>> +{\n>> +  mode_t type;\n>> +  switch (dtype)\n>> +    {\n>> +    case DT_BLK:\n>> +      type = S_IFBLK;\n>> +      break;\n>> +    case DT_CHR:\n>> +      type = S_IFCHR;\n>> +      break;\n>> +    case DT_DIR:\n>> +      type = S_IFDIR;\n>> +      break;\n>> +    case DT_FIFO:\n>> +      type = S_IFIFO;\n>> +      break;\n>> +    case DT_LNK:\n>> +      type = S_IFLNK;\n>> +      break;\n>> +    case DT_REG:\n>> +      type = S_IFREG;\n>> +      break;\n>> +    case DT_SOCK:\n>> +      type = S_IFSOCK;\n>> +      break;\n>> +    default:\n>> +      type = 0;\n>> +    }\n>> +  st->st_mode = type;\n>> +}\n>> +\n>> +#define closedir_and_clear(dirp)                \\\n>> +  do                                            \\\n>> +    {                                           \\\n>> +      closedir (dirp);                          \\\n>> +      dirp = NULL;                              \\\n>> +    }                                           \\\n>> +  while (0)\n>> +\n>> +#define fts_opendir(file, Pdir_fd)                              \\\n>> +        OPENDIRAT((! ISSET(FTS_NOCHDIR) && ISSET(FTS_CWDFD)     \\\n>> +                   ? sp->fts_cwd_fd : AT_FDCWD),                \\\n>> +                  file,                                         \\\n>> +                  (((ISSET(FTS_PHYSICAL)                        \\\n>> +                     && ! (ISSET(FTS_COMFOLLOW)                 \\\n>> +                           && cur->fts_level == FTS_ROOTLEVEL)) \\\n>> +                    ? O_NOFOLLOW : 0)),                         \\\n>> +                  Pdir_fd)\n>> +\n>> +/*\n>> + * This is the tricky part -- do not casually change *anything* in here.  The\n>> + * idea is to build the linked list of entries that are used by fts_children\n>> + * and fts_read.  There are lots of special cases.\n>> + *\n>> + * The real slowdown in walking the tree is the stat calls.  If FTS_NOSTAT is\n>> + * set and it's a physical walk (so that symbolic links can't be directories),\n>> + * we can do things quickly.  First, if it's a 4.4BSD file system, the type\n>> + * of the file is in the directory entry.  Otherwise, we assume that the number\n>> + * of subdirectories in a node is equal to the number of links to the parent.\n>> + * The former skips all stat calls.  The latter skips stat calls in any leaf\n>> + * directories and for any files after the subdirectories in the directory have\n>> + * been found, cutting the stat calls by about 2/3.\n>> + */\n>> +static FTSENTRY *\n>> +internal_function\n>> +fts_build (register FTSOBJ *sp, int type)\n>> +{\n>> +        FTSENTRY *cur = sp->fts_cur;\n>> +        bool continue_readdir = !!FTSENT_DIRP(cur);\n>> +\n>> +        /* When cur->fts_dirp is non-NULL, that means we should\n>> +           continue calling readdir on that existing DIR* pointer\n>> +           rather than opening a new one.  */\n>> +        int dir_fd;\n>> +        if (continue_readdir)\n>> +          {\n>> +            DIR *dp = FTSENT_DIRP(cur);\n>> +            dir_fd = dirfd (dp);\n>> +            if (dir_fd < 0)\n>> +              {\n>> +                int dirfd_errno = errno;\n>> +                closedir_and_clear (FTSENT_DIRP(cur));\n>> +                if (type == BREAD)\n>> +                  {\n>> +                    cur->fts_info = FTS_DNR;\n>> +                    cur->fts_errno = dirfd_errno;\n>> +                  }\n>> +                return NULL;\n>> +              }\n>> +          }\n>> +        else\n>> +          {\n>> +            /* Open the directory for reading.  If this fails, we're done.\n>> +               If being called from fts_read, set the fts_info field. */\n>> +            if ((FTSENT_DIRP (cur) = fts_opendir(cur->fts_accpath, &dir_fd)) == NULL)\n>> +              {\n>> +                if (type == BREAD)\n>> +                  {\n>> +                    cur->fts_info = FTS_DNR;\n>> +                    cur->fts_errno = errno;\n>> +                  }\n>> +                return NULL;\n>> +              }\n>> +            /* Rather than calling fts_stat for each and every entry encountered\n>> +               in the readdir loop (below), stat each directory only right after\n>> +               opening it.  */\n>> +            bool stat_optimization = cur->fts_info == FTS_NSOK;\n>> +\n>> +            if (stat_optimization\n>> +                /* Also read the stat info again after opening a directory to\n>> +                   reveal eventual changes caused by a submount triggered by\n>> +                   the traversal.  But do it only for utilities which use\n>> +                   FTS_TIGHT_CYCLE_CHECK.  Therefore, only find and du\n>> +                   benefit/suffer from this feature for now.  */\n>> +                || ISSET (FTS_TIGHT_CYCLE_CHECK))\n>> +              {\n>> +                if (!stat_optimization)\n>> +                  LEAVE_DIR (sp, cur, \"4\");\n>> +                if (FSTAT (dir_fd, cur->fts_statp) != 0)\n>> +                  {\n>> +                    int fstat_errno = errno;\n>> +                    closedir_and_clear (FTSENT_DIRP(cur));\n>> +                    if (type == BREAD)\n>> +                      {\n>> +                        cur->fts_errno = fstat_errno;\n>> +                        cur->fts_info = FTS_NS;\n>> +                      }\n>> +                    __set_errno (fstat_errno);\n>> +                    return NULL;\n>> +                  }\n>> +                if (stat_optimization)\n>> +                  cur->fts_info = FTS_D;\n>> +                else if (! enter_dir (sp, cur))\n>> +                  {\n>> +                    int saved_errno = errno;\n>> +                    closedir_and_clear (FTSENT_DIRP(cur));\n>> +                    __set_errno (saved_errno);\n>> +                    return NULL;\n>> +                  }\n>> +              }\n>> +          }\n>> +\n>> +        /* Maximum number of readdir entries to read at one time.  This\n>> +           limitation is to avoid reading millions of entries into memory\n>> +           at once.  When an fts_compar function is specified, we have no\n>> +           choice: we must read all entries into memory before calling that\n>> +           function.  But when no such function is specified, we can read\n>> +           entries in batches that are large enough to help us with inode-\n>> +           sorting, yet not so large that we risk exhausting memory.  */\n>> +        size_t max_entries = sp->fts_compar ? SIZE_MAX : FTS_MAX_READDIR_ENTRIES;\n>> +\n>> +        /*\n>> +         * If we're going to need to stat anything or we want to descend\n>> +         * and stay in the directory, chdir.  If this fails we keep going,\n>> +         * but set a flag so we don't chdir after the post-order visit.\n>> +         * We won't be able to stat anything, but we can still return the\n>> +         * names themselves.  Note, that since fts_read won't be able to\n>> +         * chdir into the directory, it will have to return different file\n>> +         * names than before, i.e. \"a/b\" instead of \"b\".  Since the node\n>> +         * has already been visited in pre-order, have to wait until the\n>> +         * post-order visit to return the error.  There is a special case\n>> +         * here, if there was nothing to stat then it's not an error to\n>> +         * not be able to stat.  This is all fairly nasty.  If a program\n>> +         * needed sorted entries or stat information, they had better be\n>> +         * checking FTS_NS on the returned nodes.\n>> +         */\n>> +        bool descend;\n>> +        if (continue_readdir)\n>> +          {\n>> +            /* When resuming a short readdir run, we already have\n>> +               the required dirp and dir_fd.  */\n>> +            descend = true;\n>> +          }\n>> +        else\n>> +          {\n>> +            /* Try to descend unless it is a names-only fts_children,\n>> +               or the directory is known to lack subdirectories.  */\n>> +            descend = (type != BNAMES\n>> +                       && ! (ISSET (FTS_NOSTAT) && ISSET (FTS_PHYSICAL)\n>> +                             && ! ISSET (FTS_SEEDOT)\n>> +                             && cur->fts_statp->st_nlink == MIN_DIR_NLINK\n>> +                             && (leaf_optimization (cur, dir_fd)\n>> +                                 != NO_LEAF_OPTIMIZATION)));\n>> +            if (descend || type == BREAD)\n>> +              {\n>> +                if (ISSET(FTS_CWDFD))\n>> +                  dir_fd = fcntl (dir_fd, F_DUPFD_CLOEXEC, STDERR_FILENO + 1);\n>> +                if (dir_fd < 0 || fts_safe_changedir(sp, cur, dir_fd, NULL)) {\n>> +                        if (descend && type == BREAD)\n>> +                                cur->fts_errno = errno;\n>> +                        cur->fts_flags |= FTS_DONTCHDIR;\n>> +                        descend = false;\n>> +                        closedir_and_clear(FTSENT_DIRP(cur));\n>> +                        if (ISSET(FTS_CWDFD) && 0 <= dir_fd)\n>> +                                close (dir_fd);\n>> +                        FTSENT_DIRP(cur) = NULL;\n>> +                } else\n>> +                        descend = true;\n>> +              }\n>> +          }\n>> +\n>> +        /*\n>> +         * Figure out the max file name length that can be stored in the\n>> +         * current buffer -- the inner loop allocates more space as necessary.\n>> +         * We really wouldn't have to do the maxlen calculations here, we\n>> +         * could do them in fts_read before returning the name, but it's a\n>> +         * lot easier here since the length is part of the dirent structure.\n>> +         *\n>> +         * If not changing directories set a pointer so that can just append\n>> +         * each new component into the file name.\n>> +         */\n>> +        size_t len = NAPPEND(cur);\n>> +        char *cp;\n>> +        if (ISSET(FTS_NOCHDIR)) {\n>> +                cp = sp->fts_path + len;\n>> +                *cp++ = '/';\n>> +        } else {\n>> +                /* GCC, you're too verbose. */\n>> +                cp = NULL;\n>> +        }\n>> +        len++;\n>> +        size_t maxlen = sp->fts_pathlen - len;\n>> +\n>> +        ptrdiff_t level = cur->fts_level + 1;\n>> +\n>> +        /* Read the directory, attaching each entry to the \"link\" pointer. */\n>> +        bool doadjust = false;\n>> +        register FTSENTRY *head = NULL;\n>> +        FTSENTRY *tail = NULL;\n>> +        register size_t nitems = 0;\n>> +        bool sort_by_inode = false;\n>> +        while (FTSENT_DIRP(cur)) {\n>> +                __set_errno (0);\n>> +                struct dirent *dp = readdir(FTSENT_DIRP(cur));\n>> +                if (dp == NULL) {\n>> +                        /* Some readdir()s do not absorb ENOENT (dir\n>> +                           deleted but open).  This bug was fixed in\n>> +                           glibc 2.3 (2002).  */\n>> +#if ! (2 < __GLIBC__ + (3 <= __GLIBC_MINOR__))\n>> +                        if (errno == ENOENT)\n>> +                          errno = 0;\n>> +#endif\n>> +                        if (errno) {\n>> +                                cur->fts_errno = errno;\n>> +                                /* If we've not read any items yet, treat\n>> +                                   the error as if we can't access the dir.  */\n>> +                                cur->fts_info = (continue_readdir || nitems)\n>> +                                                ? FTS_ERR : FTS_DNR;\n>> +                        }\n>> +                        closedir_and_clear(FTSENT_DIRP(cur));\n>> +                        break;\n>> +                }\n>> +                if (!ISSET(FTS_SEEDOT) && ISDOT(dp->d_name))\n>> +                        continue;\n>> +\n>> +                size_t d_namelen = _D_EXACT_NAMLEN (dp);\n>> +                register FTSENTRY *p = fts_alloc (sp, dp->d_name, d_namelen);\n>> +                if (!p)\n>> +                        goto mem1;\n>> +                if (d_namelen >= maxlen) {\n>> +                        /* include space for NUL */\n>> +                        uintptr_t oldaddr = (uintptr_t) sp->fts_path;\n>> +                        if (! fts_palloc(sp, d_namelen + len + 1)) {\n>> +                                /*\n>> +                                 * No more memory.  Save\n>> +                                 * errno, free up the current structure and the\n>> +                                 * structures already allocated.\n>> +                                 */\n>> +mem1: ;\n>> +                                int saved_errno = errno;\n>> +                                free(FTSENT_WRAPPER(p));\n>> +                                fts_lfree(head);\n>> +                                closedir_and_clear(FTSENT_DIRP(cur));\n>> +                                cur->fts_info = FTS_ERR;\n>> +                                SET(FTS_STOP);\n>> +                                __set_errno (saved_errno);\n>> +                                return (NULL);\n>> +                        }\n>> +                        /* Did realloc() change the pointer? */\n>> +                        if (oldaddr != (uintptr_t) sp->fts_path) {\n>> +                                doadjust = true;\n>> +                                if (ISSET(FTS_NOCHDIR))\n>> +                                        cp = sp->fts_path + len;\n>> +                        }\n>> +                        maxlen = sp->fts_pathlen - len;\n>> +                }\n>> +\n>> +                size_t new_len = len + d_namelen;\n>> +                if (new_len < len) {\n>> +                        /*\n>> +                         * In the unlikely event that we would end up\n>> +                         * with a file name longer than SIZE_MAX, free up\n>> +                         * the current structure and the structures already\n>> +                         * allocated, then error out with ENAMETOOLONG.\n>> +                         */\n>> +                        free(FTSENT_WRAPPER(p));\n>> +                        fts_lfree(head);\n>> +                        closedir_and_clear(FTSENT_DIRP(cur));\n>> +                        cur->fts_info = FTS_ERR;\n>> +                        SET(FTS_STOP);\n>> +                        __set_errno (ENAMETOOLONG);\n>> +                        return (NULL);\n>> +                }\n>> +                p->fts_level = level;\n>> +                p->fts_parent = sp->fts_cur;\n>> +                p->fts_pathlen = new_len;\n>> +\n>> +                /* Store dirent.d_ino, in case we need to sort\n>> +                   entries before processing them.  */\n>> +                p->fts_statp->st_ino = D_INO (dp);\n>> +\n>> +                /* Build a file name for fts_stat to stat. */\n>> +                if (ISSET(FTS_NOCHDIR)) {\n>> +                        p->fts_accpath = p->fts_path;\n>> +                        memmove(cp, p->fts_name, p->fts_namelen + 1);\n>> +                } else\n>> +                        p->fts_accpath = p->fts_name;\n>> +\n>> +                if (sp->fts_compar == NULL || ISSET(FTS_DEFER_STAT)) {\n>> +                        /* Record what fts_read will have to do with this\n>> +                           entry. In many cases, it will simply fts_stat it,\n>> +                           but we can take advantage of any d_type information\n>> +                           to optimize away the unnecessary stat calls.  I.e.,\n>> +                           if FTS_NOSTAT is in effect, we don't need device\n>> +                           numbers unconditionally (FTS_MOUNT) and we're not\n>> +                           following symlinks (FTS_PHYSICAL) and d_type\n>> +                           indicates this is *not* a directory, then we won't\n>> +                           have to stat it  at all.  If it *is* a directory,\n>> +                           then (currently) we stat it regardless, in order to\n>> +                           get device and inode numbers.  Some day we might\n>> +                           optimize that away, too, for directories where\n>> +                           d_ino is known to be valid.  */\n>> +                        bool skip_stat = (ISSET(FTS_NOSTAT)\n>> +                                          && DT_IS_KNOWN(dp)\n>> +                                          && ! DT_MUST_BE(dp, DT_DIR)\n>> +                                          && (ISSET(FTS_PHYSICAL)\n>> +                                              || ! DT_MUST_BE(dp, DT_LNK))\n>> +                                          && ! ISSET(FTS_MOUNT));\n>> +                        p->fts_info = FTS_NSOK;\n>> +                        /* Propagate dirent.d_type information back\n>> +                           to caller, when possible.  */\n>> +                        set_stat_type (p->fts_statp, D_TYPE (dp));\n>> +                        fts_set_stat_required(p, !skip_stat);\n>> +                } else {\n>> +                        p->fts_info = fts_stat(sp, p, false);\n>> +                }\n>> +\n>> +                /* We walk in directory order so \"ls -f\" doesn't get upset. */\n>> +                p->fts_link = NULL;\n>> +                if (head == NULL)\n>> +                        head = tail = p;\n>> +                else {\n>> +                        tail->fts_link = p;\n>> +                        tail = p;\n>> +                }\n>> +\n>> +                /* If there are many entries, no sorting function has been\n>> +                   specified, and this file system is of a type that may be\n>> +                   slow with a large number of entries, arrange to sort the\n>> +                   directory entries on increasing inode numbers.\n>> +\n>> +                   The NITEMS comparison uses ==, not >, because the test\n>> +                   needs to be tried at most once once, and NITEMS will exceed\n>> +                   the threshold after it is incremented below.  */\n>> +                if (nitems == _FTS_INODE_SORT_DIR_ENTRIES_THRESHOLD\n>> +                    && !sp->fts_compar)\n>> +                  sort_by_inode = dirent_inode_sort_may_be_useful (cur, dir_fd);\n>> +\n>> +                ++nitems;\n>> +                if (max_entries <= nitems) {\n>> +                        /* When there are too many dir entries, leave\n>> +                           fts_dirp open, so that a subsequent fts_read\n>> +                           can take up where we leave off.  */\n>> +                        break;\n>> +                }\n>> +        }\n>> +\n>> +        /*\n>> +         * If realloc() changed the address of the file name, adjust the\n>> +         * addresses for the rest of the tree and the dir list.\n>> +         */\n>> +        if (doadjust)\n>> +                fts_padjust(sp, head);\n>> +\n>> +        /*\n>> +         * If not changing directories, reset the file name back to original\n>> +         * state.\n>> +         */\n>> +        if (ISSET(FTS_NOCHDIR)) {\n>> +                if (len == sp->fts_pathlen || nitems == 0)\n>> +                        --cp;\n>> +                *cp = '\\0';\n>> +        }\n>> +\n>> +        /*\n>> +         * If descended after called from fts_children or after called from\n>> +         * fts_read and nothing found, get back.  At the root level we use\n>> +         * the saved fd; if one of fts_open()'s arguments is a relative name\n>> +         * to an empty directory, we wind up here with no other way back.  If\n>> +         * can't get back, we're done.\n>> +         */\n>> +        if (!continue_readdir && descend && (type == BCHILD || !nitems) &&\n>> +            (cur->fts_level == FTS_ROOTLEVEL\n>> +             ? restore_initial_cwd(sp)\n>> +             : fts_safe_changedir(sp, cur->fts_parent, -1, \"..\"))) {\n>> +                cur->fts_info = FTS_ERR;\n>> +                SET(FTS_STOP);\n>> +                fts_lfree(head);\n>> +                return (NULL);\n>> +        }\n>> +\n>> +        /* If didn't find anything, return NULL. */\n>> +        if (!nitems) {\n>> +                if (type == BREAD\n>> +                    && cur->fts_info != FTS_DNR && cur->fts_info != FTS_ERR)\n>> +                        cur->fts_info = FTS_DP;\n>> +                fts_lfree(head);\n>> +                return (NULL);\n>> +        }\n>> +\n>> +        if (sort_by_inode) {\n>> +                sp->fts_compar = FTS_COMPAR_CAST (fts_compare_ino);\n>> +                head = fts_sort (sp, head, nitems);\n>> +                sp->fts_compar = NULL;\n>> +        }\n>> +\n>> +        /* Sort the entries. */\n>> +        if (sp->fts_compar && nitems > 1)\n>> +                head = fts_sort(sp, head, nitems);\n>> +        return (head);\n>> +}\n>> +\n>> +#if GNULIB_FTS_DEBUG\n>> +\n>> +struct devino {\n>> +  intmax_t dev, ino;\n>> +};\n>> +#define PRINT_DEVINO \"(%jd,%jd)\"\n>> +\n>> +static struct devino\n>> +getdevino (int fd)\n>> +{\n>> +  struct STRUCT_STAT st;\n>> +  return (fd == AT_FDCWD\n>> +          ? (struct devino) { -1, 0 }\n>> +          : FSTAT (fd, &st) == 0\n>> +          ? (struct devino) { st.st_dev, st.st_ino }\n>> +          : (struct devino) { -1, errno });\n>> +}\n>> +\n>> +/* Walk ->fts_parent links starting at E_CURR, until the root of the\n>> +   current hierarchy.  There should be a directory with dev/inode\n>> +   matching those of AD.  If not, print a lot of diagnostics.  */\n>> +static void\n>> +find_matching_ancestor (FTSENTRY const *e_curr, struct Active_dir const *ad)\n>> +{\n>> +  for (FTSENTRY const *ent = e_curr;\n>> +       ent->fts_level >= FTS_ROOTLEVEL;\n>> +       ent = ent->fts_parent)\n>> +    {\n>> +      if (ad->ino == ent->fts_statp->st_ino\n>> +          && ad->dev == ent->fts_statp->st_dev)\n>> +        return;\n>> +    }\n>> +  printf (\"ERROR: tree dir, %s, not active\\n\", ad->fts_ent->fts_accpath);\n>> +  printf (\"active dirs:\\n\");\n>> +  for (FTSENTRY const *ent = e_curr;\n>> +       ent->fts_level >= FTS_ROOTLEVEL;\n>> +       ent = ent->fts_parent)\n>> +    printf (\"  %s(%\"PRIuMAX\"/%\"PRIuMAX\") to %s(%\"PRIuMAX\"/%\"PRIuMAX\")...\\n\",\n>> +            ad->fts_ent->fts_accpath,\n>> +            (uintmax_t) ad->dev,\n>> +            (uintmax_t) ad->ino,\n>> +            ent->fts_accpath,\n>> +            (uintmax_t) ent->fts_statp->st_dev,\n>> +            (uintmax_t) ent->fts_statp->st_ino);\n>> +}\n>> +\n>> +void\n>> +fts_cross_check (FTSOBJ const *sp)\n>> +{\n>> +  if ( ! ISSET (FTS_TIGHT_CYCLE_CHECK))\n>> +    return;\n>> +\n>> +  FTSENTRY const *ent = sp->fts_cur;\n>> +\n>> +  Dprintf ((\"fts-cross-check cur=%s\\n\", ent->fts_path));\n>> +  /* Make sure every parent dir is in the tree.  */\n>> +  for (FTSENTRY const *t = ent->fts_parent;\n>> +       t->fts_level >= FTS_ROOTLEVEL;\n>> +       t = t->fts_parent)\n>> +    {\n>> +      struct Active_dir ad;\n>> +      ad.ino = t->fts_statp->st_ino;\n>> +      ad.dev = t->fts_statp->st_dev;\n>> +      if ( ! hash_lookup (sp->fts_cycle.ht, &ad))\n>> +        printf (\"ERROR: active dir, %s, not in tree\\n\", t->fts_path);\n>> +    }\n>> +\n>> +  /* Make sure every dir in the tree is an active dir.\n>> +     But ENT is not necessarily a directory.  If so, just skip this part. */\n>> +  if (ent->fts_parent->fts_level >= FTS_ROOTLEVEL\n>> +      && (ent->fts_info == FTS_DP\n>> +          || ent->fts_info == FTS_D))\n>> +    for (struct Active_dir *ad = hash_get_first (sp->fts_cycle.ht);\n>> +         ad != NULL;\n>> +         ad = hash_get_next (sp->fts_cycle.ht, ad))\n>> +      {\n>> +        find_matching_ancestor (ent, ad);\n>> +      }\n>> +}\n>> +\n>> +static bool\n>> +same_fd (int fd1, int fd2)\n>> +{\n>> +  struct STRUCT_STAT sb1, sb2;\n>> +  return (FSTAT (fd1, &sb1) == 0\n>> +          && FSTAT (fd2, &sb2) == 0\n>> +          && psame_inode (&sb1, &sb2));\n>> +}\n>> +\n>> +static void\n>> +fd_ring_print (FTSOBJ const *sp, FILE *stream, char const *msg)\n>> +{\n>> +  if (!fts_debug)\n>> +    return;\n>> +  I_ring const *fd_ring = &sp->fts_fd_ring;\n>> +  struct devino cwd = getdevino (sp->fts_cwd_fd);\n>> +  fprintf (stream, \"=== %s ========== \"PRINT_DEVINO\"\\n\", msg, cwd.dev, cwd.ino);\n>> +  if (i_ring_empty (fd_ring))\n>> +    return;\n>> +\n>> +  unsigned int i = fd_ring->ir_front;\n>> +  while (true)\n>> +    {\n>> +      int fd = fd_ring->ir_data[i];\n>> +      if (fd < 0)\n>> +        fprintf (stream, \"%u: %d:\\n\", i, fd);\n>> +      else\n>> +        {\n>> +          struct devino wd = getdevino (fd);\n>> +          fprintf (stream, \"%u: %d: \"PRINT_DEVINO\"\\n\", i, fd, wd.dev, wd.ino);\n>> +        }\n>> +      if (i == fd_ring->ir_back)\n>> +        break;\n>> +      i = (i + I_RING_SIZE - 1) % I_RING_SIZE;\n>> +    }\n>> +}\n>> +\n>> +/* Ensure that each file descriptor on the fd_ring matches a\n>> +   parent, grandparent, etc. of the current working directory.  */\n>> +static void\n>> +fd_ring_check (FTSOBJ const *sp)\n>> +{\n>> +  if (!fts_debug)\n>> +    return;\n>> +\n>> +  /* Make a writable copy.  */\n>> +  I_ring fd_w = sp->fts_fd_ring;\n>> +\n>> +  int cwd_fd = sp->fts_cwd_fd;\n>> +  cwd_fd = fcntl (cwd_fd, F_DUPFD_CLOEXEC, STDERR_FILENO + 1);\n>> +  struct devino dot = getdevino (cwd_fd);\n>> +  fprintf (stderr, \"===== check ===== cwd: \"PRINT_DEVINO\"\\n\",\n>> +           dot.dev, dot.ino);\n>> +  while ( ! i_ring_empty (&fd_w))\n>> +    {\n>> +      int fd = i_ring_pop (&fd_w);\n>> +      if (0 <= fd)\n>> +        {\n>> +          int open_flags = O_SEARCH | O_CLOEXEC;\n>> +          int parent_fd = openat (cwd_fd, \"..\", open_flags);\n>> +          if (parent_fd < 0)\n>> +            {\n>> +              // Warn?\n>> +              break;\n>> +            }\n>> +          if (!same_fd (fd, parent_fd))\n>> +            {\n>> +              struct devino cwd = getdevino (fd);\n>> +              fprintf (stderr, \"ring  : \"PRINT_DEVINO\"\\n\", cwd.dev, cwd.ino);\n>> +              struct devino c2 = getdevino (parent_fd);\n>> +              fprintf (stderr, \"parent: \"PRINT_DEVINO\"\\n\", c2.dev, c2.ino);\n>> +              fts_assert (0);\n>> +            }\n>> +          close (cwd_fd);\n>> +          cwd_fd = parent_fd;\n>> +        }\n>> +    }\n>> +  close (cwd_fd);\n>> +}\n>> +#endif\n>> +\n>> +static unsigned short int\n>> +internal_function\n>> +fts_stat(FTSOBJ *sp, register FTSENTRY *p, bool follow)\n>> +{\n>> +        if (ISSET (FTS_LOGICAL)\n>> +            || (ISSET (FTS_COMFOLLOW) && p->fts_level == FTS_ROOTLEVEL))\n>> +                follow = true;\n>> +\n>> +        struct STRUCT_STAT *sbp = p->fts_statp;\n>> +\n>> +        /*\n>> +         * If doing a logical walk, or application requested FTS_FOLLOW, do\n>> +         * a stat(2).  If that fails, check for a nonexistent symlink.  If\n>> +         * fail, set the errno from the stat call.\n>> +         */\n>> +        int flags = follow ? 0 : AT_SYMLINK_NOFOLLOW;\n>> +        if (FSTATAT (sp->fts_cwd_fd, p->fts_accpath, sbp, flags) < 0)\n>> +          {\n>> +            if (follow && errno == ENOENT\n>> +                && 0 <= FSTATAT (sp->fts_cwd_fd, p->fts_accpath, sbp,\n>> +                                 AT_SYMLINK_NOFOLLOW))\n>> +              {\n>> +                __set_errno (0);\n>> +                return FTS_SLNONE;\n>> +              }\n>> +\n>> +            p->fts_errno = errno;\n>> +           memset (sbp, 0, sizeof *sbp);\n>> +            return FTS_NS;\n>> +          }\n>> +\n>> +        if (S_ISDIR(sbp->st_mode)) {\n>> +                if (ISDOT(p->fts_name)) {\n>> +                        /* Command-line \".\" and \"..\" are real directories. */\n>> +                        return (p->fts_level == FTS_ROOTLEVEL ? FTS_D : FTS_DOT);\n>> +                }\n>> +\n>> +                return (FTS_D);\n>> +        }\n>> +        if (S_ISLNK(sbp->st_mode))\n>> +                return (FTS_SL);\n>> +        if (S_ISREG(sbp->st_mode))\n>> +                return (FTS_F);\n>> +        return (FTS_DEFAULT);\n>> +}\n>> +\n>> +static int\n>> +fts_compar (void const *a, void const *b)\n>> +{\n>> +  /* Convert A and B to the correct types, to pacify the compiler, and\n>> +     for portability to bizarre hosts where \"void const *\" and \"FTSENT\n>> +     const **\" differ in runtime representation.  The comparison\n>> +     function cannot modify *a and *b, but there is no compile-time\n>> +     check for this.  */\n>> +  FTSENTRY const **pa = (FTSENTRY const **) a;\n>> +  FTSENTRY const **pb = (FTSENTRY const **) b;\n>> +  return FTSENT_FTS(pa[0])->fts_compar (pa, pb);\n>> +}\n>> +\n>> +static FTSENTRY *\n>> +internal_function\n>> +fts_sort (FTSOBJ *sp, FTSENTRY *head, register size_t nitems)\n>> +{\n>> +        register FTSENTRY **ap, *p;\n>> +\n>> +        /* On most modern hosts, void * and FTSENT ** have the same\n>> +           run-time representation, and one can convert sp->fts_compar to\n>> +           the type qsort expects without problem.  Use the heuristic that\n>> +           this is OK if the two pointer types are the same size, and if\n>> +           converting FTSENT ** to uintptr_t is the same as converting\n>> +           FTSENT ** to void * and then to uintptr_t.  This heuristic isn't\n>> +           valid in general but we don't know of any counterexamples.  */\n>> +        FTSENTRY *dummy;\n>> +        int (*compare) (void const *, void const *) =\n>> +          ((sizeof &dummy == sizeof (void *)\n>> +            && (uintptr_t) &dummy == (uintptr_t) (void *) &dummy)\n>> +           ? (int (*) (void const *, void const *)) sp->fts_compar\n>> +           : fts_compar);\n>> +\n>> +        /*\n>> +         * Construct an array of pointers to the structures and call qsort(3).\n>> +         * Reassemble the array in the order returned by qsort.  If unable to\n>> +         * sort for memory reasons, return the directory entries in their\n>> +         * current order.  Allocate enough space for the current needs plus\n>> +         * 40 so don't realloc one entry at a time.\n>> +         */\n>> +        if (nitems > sp->fts_nitems) {\n>> +                sp->fts_nitems = nitems + 40;\n>> +                FTSENTRY **a;\n>> +                if (! (a = reallocarray (sp->fts_array,\n>> +                                         sp->fts_nitems, sizeof *a))) {\n>> +                        free(sp->fts_array);\n>> +                        sp->fts_array = NULL;\n>> +                        sp->fts_nitems = 0;\n>> +                        return (head);\n>> +                }\n>> +                sp->fts_array = a;\n>> +        }\n>> +        for (ap = sp->fts_array, p = head; p; p = p->fts_link)\n>> +                *ap++ = p;\n>> +        qsort((void *)sp->fts_array, nitems, sizeof(FTSENTRY *), compare);\n>> +        for (head = *(ap = sp->fts_array); --nitems; ++ap)\n>> +                ap[0]->fts_link = ap[1];\n>> +        ap[0]->fts_link = NULL;\n>> +        return (head);\n>> +}\n>> +\n>> +static FTSENTRY *\n>> +internal_function\n>> +fts_alloc (FTSOBJ *sp, const char *name, register size_t namelen)\n>> +{\n>> +        /*\n>> +         * The file name is a variable length array.  Allocate the FTSENT\n>> +         * structure and the file name in one chunk.\n>> +         */\n>> +        size_t len = FLEXSIZEOF(FTSENT, fts_name, namelen + 1);\n>> +\tregister FTSENTRY *p;\n>> +#if !_LIBC\n>> +        p = malloc(len);\n>> +        if (p == NULL)\n>> +                return (NULL);\n>> +#else\n>> +\t/*\n>> +\t * For glibc, we use a wrapper struct to provide the extra required\n>> +\t * fields without changing the FSENT layout.\n>> +\t */\n>> +\tlen += sizeof (struct FTSENT_wrapper);\n>> +\tstruct FTSENT_wrapper *wrapper = malloc(len);\n>> +\tif (wrapper == NULL)\n>> +\t\treturn (NULL);\n>> +\tp = &wrapper->ent;\n>> +        p->fts_statp = &wrapper->fts_stat;\n>> +#endif\n>> +\n>> +        /* Copy the name and guarantee NUL termination. */\n>> +        memcpy(p->fts_name, name, namelen);\n>> +        p->fts_name[namelen] = '\\0';\n>> +\n>> +        p->fts_namelen = namelen;\n>> +        FTSENT_FTS(p)= sp;\n>> +        p->fts_path = sp->fts_path;\n>> +        p->fts_errno = 0;\n>> +        FTSENT_DIRP(p) = NULL;\n>> +        p->fts_flags = 0;\n>> +        p->fts_instr = FTS_NOINSTR;\n>> +        p->fts_number = 0;\n>> +        p->fts_pointer = NULL;\n>> +        return (p);\n>> +}\n>> +\n>> +static void\n>> +internal_function\n>> +fts_lfree (register FTSENTRY *head)\n>> +{\n>> +        int saved_errno = errno;\n>> +\n>> +        /* Free a linked list of structures. */\n>> +        register FTSENTRY *p;\n>> +        while ((p = head)) {\n>> +                head = head->fts_link;\n>> +                if (FTSENT_DIRP(p))\n>> +                        closedir (FTSENT_DIRP(p));\n>> +                free(FTSENT_WRAPPER(p));\n>> +        }\n>> +\n>> +        __set_errno (saved_errno);\n>> +}\n>> +\n>> +/*\n>> + * Allow essentially unlimited file name lengths; find, rm, ls should\n>> + * all work on any tree.  Most systems will allow creation of file\n>> + * names much longer than MAXPATHLEN, even though the kernel won't\n>> + * resolve them.  Add the size (not just what's needed) plus 256 bytes\n>> + * so don't realloc the file name 2 bytes at a time.\n>> + */\n>> +static bool\n>> +internal_function\n>> +fts_palloc (FTSOBJ *sp, size_t more)\n>> +{\n>> +        size_t new_len = sp->fts_pathlen + more + 256;\n>> +\n>> +        /*\n>> +         * See if fts_pathlen would overflow.\n>> +         */\n>> +        if (new_len < sp->fts_pathlen) {\n>> +                free(sp->fts_path);\n>> +                sp->fts_path = NULL;\n>> +                __set_errno (ENAMETOOLONG);\n>> +                return false;\n>> +        }\n>> +        sp->fts_pathlen = new_len;\n>> +        char *p = realloc(sp->fts_path, sp->fts_pathlen);\n>> +        if (p == NULL) {\n>> +                free(sp->fts_path);\n>> +                sp->fts_path = NULL;\n>> +                return false;\n>> +        }\n>> +        sp->fts_path = p;\n>> +        return true;\n>> +}\n>> +\n>> +/*\n>> + * When the file name is realloc'd, have to fix all of the pointers in\n>> + *  structures already returned.\n>> + */\n>> +static void\n>> +internal_function\n>> +fts_padjust (FTSOBJ *sp, FTSENTRY *head)\n>> +{\n>> +        char *addr = sp->fts_path;\n>> +\n>> +        /* This code looks at bit-patterns of freed pointers to\n>> +           relocate them, so it relies on undefined behavior.  If this\n>> +           trick does not work on your platform, please report a bug.  */\n>> +\n>> +#define ADJUST(p) do {                                                  \\\n>> +        uintptr_t old_accpath = (uintptr_t) (p)->fts_accpath;           \\\n>> +        if (old_accpath != (uintptr_t) (p)->fts_name) {                 \\\n>> +                (p)->fts_accpath =                                      \\\n>> +                  addr + (old_accpath - (uintptr_t) (p)->fts_path);     \\\n>> +        }                                                               \\\n>> +        (p)->fts_path = addr;                                           \\\n>> +} while (0)\n>> +        /* Adjust the current set of children. */\n>> +        for (FTSENTRY *p = sp->fts_child; p; p = p->fts_link)\n>> +                ADJUST(p);\n>> +\n>> +        /* Adjust the rest of the tree, including the current level. */\n>> +        for (FTSENTRY *p = head; p->fts_level >= FTS_ROOTLEVEL;) {\n>> +                ADJUST(p);\n>> +                p = p->fts_link ? p->fts_link : p->fts_parent;\n>> +        }\n>> +}\n>> +\n>> +static size_t\n>> +internal_function _GL_ATTRIBUTE_PURE\n>> +fts_maxarglen (char * const *argv)\n>> +{\n>> +        size_t max;\n>> +\n>> +        for (max = 0; *argv; ++argv) {\n>> +                size_t len = strlen(*argv);\n>> +                if (len > max)\n>> +                        max = len;\n>> +        }\n>> +        return (max + 1);\n>> +}\n>> +\n>> +/*\n>> + * Change to dir specified by fd or file name without getting\n>> + * tricked by someone changing the world out from underneath us.\n>> + * Assumes p->fts_statp->st_dev and p->fts_statp->st_ino are filled in.\n>> + * If FD is non-negative, expect it to be used after this function returns,\n>> + * and to be closed eventually.  So don't pass e.g., 'dirfd(dirp)' and then\n>> + * do closedir(dirp), because that would invalidate the saved FD.\n>> + * Upon failure, close FD immediately and return nonzero.\n>> + */\n>> +static int\n>> +internal_function\n>> +fts_safe_changedir (FTSOBJ *sp, FTSENTRY *p, int fd, char const *dir)\n>> +{\n>> +        fts_assert (0 <= fd || dir != NULL);\n>> +        bool is_dotdot = dir && streq (dir, \"..\");\n>> +\n>> +        /* This clause handles the unusual case in which FTS_NOCHDIR\n>> +           is specified, along with FTS_CWDFD.  In that case, there is\n>> +           no need to change even the virtual cwd file descriptor.\n>> +           However, if FD is non-negative, we do close it here.  */\n>> +        if (ISSET (FTS_NOCHDIR))\n>> +          {\n>> +            if (ISSET (FTS_CWDFD) && 0 <= fd)\n>> +              close (fd);\n>> +            return 0;\n>> +          }\n>> +\n>> +        if (fd < 0 && is_dotdot && ISSET (FTS_CWDFD))\n>> +          {\n>> +            /* When possible, skip the diropen and subsequent fstat+dev/ino\n>> +               comparison.  I.e., when changing to parent directory\n>> +               (chdir (\"..\")), use a file descriptor from the ring and\n>> +               save the overhead of diropen+fstat, as well as avoiding\n>> +               failure when we lack \"x\" access to the virtual cwd.  */\n>> +            if ( ! i_ring_empty (&sp->fts_fd_ring))\n>> +              {\n>> +                int parent_fd;\n>> +                fd_ring_print (sp, stderr, \"pre-pop\");\n>> +                parent_fd = i_ring_pop (&sp->fts_fd_ring);\n>> +                if (0 <= parent_fd)\n>> +                  {\n>> +                    fd = parent_fd;\n>> +                    dir = NULL;\n>> +                  }\n>> +              }\n>> +          }\n>> +\n>> +        int newfd = fd;\n>> +        if (fd < 0 && (newfd = diropen (sp, dir)) < 0)\n>> +          return -1;\n>> +\n>> +        /* The following dev/inode check is necessary if we're doing a\n>> +           \"logical\" traversal (through symlinks, a la chown -L), if the\n>> +           system lacks O_NOFOLLOW support, or if we're changing to \"..\"\n>> +           (but not via a popped file descriptor).  When changing to the\n>> +           name \"..\", O_NOFOLLOW can't help.  In general, when the target is\n>> +           not \"..\", diropen's use of O_NOFOLLOW ensures we don't mistakenly\n>> +           follow a symlink, so we can avoid the expense of this fstat.  */\n>> +        int ret;\n>> +        if (ISSET(FTS_LOGICAL) || ! HAVE_WORKING_O_NOFOLLOW\n>> +            || (dir && streq (dir, \"..\")))\n>> +          {\n>> +            struct STRUCT_STAT sb;\n>> +            if (FSTAT (newfd, &sb))\n>> +              {\n>> +                ret = -1;\n>> +                goto bail;\n>> +              }\n>> +            if (p->fts_statp->st_dev != sb.st_dev\n>> +                || p->fts_statp->st_ino != sb.st_ino)\n>> +              {\n>> +                __set_errno (ENOENT);           /* disinformation */\n>> +                ret = -1;\n>> +                goto bail;\n>> +              }\n>> +          }\n>> +\n>> +        if (ISSET(FTS_CWDFD))\n>> +          {\n>> +            cwd_advance_fd (sp, newfd, ! is_dotdot);\n>> +            return 0;\n>> +          }\n>> +\n>> +        ret = fchdir(newfd);\n>> +bail:\n>> +        if (fd < 0)\n>> +          {\n>> +            int oerrno = errno;\n>> +            (void)close(newfd);\n>> +            __set_errno (oerrno);\n>> +          }\n>> +        return ret;\n>> +}\n>> diff --git a/io/fts.c b/io/fts.c\n>> index 3825f0aeb4..d3b8e5a100 100644\n>> --- a/io/fts.c\n>> +++ b/io/fts.c\n>> @@ -1,2208 +1,23 @@\n>> -/* Traverse a file hierarchy.\n>> +/* File tree traversal functions LFS version.\n>> +   Copyright (C) 2026 Free Software Foundation, Inc.\n>> +   This file is part of the GNU C Library.\n>>   \n>> -   Copyright (C) 2004-2026 Free Software Foundation, Inc.\n>> +   The GNU C Library is free software; you can redistribute it and/or\n>> +   modify it under the terms of the GNU Lesser General Public\n>> +   License as published by the Free Software Foundation; either\n>> +   version 2.1 of the License, or (at your option) any later version.\n>>   \n>> -   This file is free software: you can redistribute it and/or modify\n>> -   it under the terms of the GNU Lesser General Public License as\n>> -   published by the Free Software Foundation; either version 2.1 of the\n>> -   License, or (at your option) any later version.\n>> -\n>> -   This file is distributed in the hope that it will be useful,\n>> +   The GNU C Library is distributed in the hope that it will be useful,\n>>      but WITHOUT ANY WARRANTY; without even the implied warranty of\n>> -   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n>> -   GNU Lesser General Public License for more details.\n>> +   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU\n>> +   Lesser General Public License for more details.\n>>   \n>> -   You should have received a copy of the GNU Lesser General Public License\n>> -   along with this program.  If not, see <https://www.gnu.org/licenses/>.  */\n>> +   You should have received a copy of the GNU Lesser General Public\n>> +   License along with the GNU C Library; if not, see\n>> +   <https://www.gnu.org/licenses/>.  */\n>>   \n>> -/*-\n>> - * Copyright (c) 1990, 1993, 1994\n>> - *      The Regents of the University of California.  All rights reserved.\n>> - *\n>> - * Redistribution and use in source and binary forms, with or without\n>> - * modification, are permitted provided that the following conditions\n>> - * are met:\n>> - * 1. Redistributions of source code must retain the above copyright\n>> - *    notice, this list of conditions and the following disclaimer.\n>> - * 2. Redistributions in binary form must reproduce the above copyright\n>> - *    notice, this list of conditions and the following disclaimer in the\n>> - *    documentation and/or other materials provided with the distribution.\n>> - * 4. Neither the name of the University nor the names of its contributors\n>> - *    may be used to endorse or promote products derived from this software\n>> - *    without specific prior written permission.\n>> - *\n>> - * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS \"AS IS\" AND\n>> - * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE\n>> - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE\n>> - * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE\n>> - * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL\n>> - * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS\n>> - * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)\n>> - * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT\n>> - * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY\n>> - * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF\n>> - * SUCH DAMAGE.\n>> - */\n>> +#include <sys/types.h>\n>>   \n>> -#include <config.h>\n>> -\n>> -#if defined LIBC_SCCS && !defined GCC_LINT && !defined lint\n>> -static char sccsid[] = \"@(#)fts.c       8.6 (Berkeley) 8/14/94\";\n>> +#ifndef __OFF_T_MATCHES_OFF64_T\n>> +# include \"io/fts-common.c\"\n>>   #endif\n>> -\n>> -#if _LIBC\n>> -# include <fts.h>\n>> -#else\n>> -# include \"fts_.h\"\n>> -#endif\n>> -#if _LIBC || HAVE_SYS_PARAM_H\n>> -# include <sys/param.h>\n>> -#endif\n>> -#include <sys/stat.h>\n>> -#include <fcntl.h>\n>> -#include <errno.h>\n>> -#include <stddef.h>\n>> -#include <stdint.h>\n>> -#include <stdlib.h>\n>> -#include <string.h>\n>> -#include <unistd.h>\n>> -\n>> -/* Support for the LFS API version.  */\n>> -#ifndef FTS_OPEN\n>> -# define FTS_OPEN fts_open\n>> -# define FTS_CLOSE fts_close\n>> -# define FTS_READ fts_read\n>> -# define FTS_SET fts_set\n>> -# define FTS_CHILDREN fts_children\n>> -# define FTSOBJ FTS\n>> -# define FTSENTRY FTSENT\n>> -# define INO_T ino_t\n>> -# define STRUCT_STAT stat\n>> -# define FSTAT __fstat\n>> -# define FSTATAT __fstatat\n>> -# define STRUCT_STATFS statfs\n>> -# define FSTATFS __fstatfs\n>> -#endif\n>> -\n>> -#if ! _LIBC\n>> -# include \"attribute.h\"\n>> -# include \"fcntl--.h\"\n>> -# include \"openat.h\"\n>> -# include \"opendirat.h\"\n>> -# include \"same-inode.h\"\n>> -# define OPENDIRAT opendirat\n>> -# define FTSENT_WRAPPER(__p) __p\n>> -# define FTS_COMPAR_CAST(__fn) __fn\n>> -#else\n>> -# include <stdbool.h>\n>> -\n>> -# define internal_function\n>> -# define FALLTHROUGH               ; [[fallthrough]]\n>> -# define HAVE_STRUCT_DIRENT_D_TYPE 1\n>> -# define GNULIB_FTS_DEBUG          0\n>> -# ifdef O_PATH\n>> -#  define O_SEARCH                 O_PATH\n>> -# else\n>> -#  define O_SEARCH                 O_RDONLY\n>> -# endif\n>> -# define HAVE_SYS_VFS_H            1\n>> -# define HAVE_FSTATFS              1\n>> -# define HAVE_STRUCT_STATFS_F_TYPE 1\n>> -# define HAVE_OPENAT               1\n>> -# define HAVE_WORKING_O_NOFOLLOW   1\n>> -# define _GL_CMP(a, b)             ((a) < (b) ? -1 : (a) > (b))\n>> -# define OPENDIRAT                 __opendirat\n>> -\n>> -static inline bool openat_needs_fchdir (void)\n>> -{\n>> -  return false;\n>> -}\n>> -\n>> -static inline bool streq (const char *s1, const char *s2)\n>> -{\n>> -  return strcmp (s1, s2) == 0;\n>> -}\n>> -# define reallocarray              __libc_reallocarray\n>> -# define fchdir                    __fchdir\n>> -# define close                     __close\n>> -# define closedir                  __closedir\n>> -# define fcntl                     __fcntl\n>> -# define readdir                   __readdir\n>> -# ifndef dirfd\n>> -#  define dirfd                    __dirfd\n>> -# endif\n>> -# define open                      __open\n>> -# define openat                    __openat\n>> -\n>> -# include \"cycle-check.c\"\n>> -# include \"i-ring.c\"\n>> -\n>> -struct FTSENT_wrapper\n>> -{\n>> -  FTSOBJ *fts_fts;                /* the file hierarchy itself */\n>> -  DIR *fts_dirp;                  /* Dir pointer for any directory containing\n>> -\t\t\t\t     more entries than we read at one time.  */\n>> -  struct STRUCT_STAT fts_stat;\n>> -\n>> -  FTSENTRY ent;\n>> -};\n>> -\n>> -/* glibc historicaly defines the FTS::fts_compar as having 'void *', while the\n>> -   fts_open has a function point using 'FTSENT **' as argument.  */\n>> -# define FTS_COMPAR_CAST(__fn) ((int (*) (void const *, void const *))__fn)\n>> -\n>> -# define FTSENT_WRAPPER(p) \\\n>> -    ((struct FTSENT_wrapper *) ((char *)(p) - offsetof(struct FTSENT_wrapper, ent)))\n>> -#endif\n>> -#define FTSENT_FTS(p)   (FTSENT_WRAPPER(p)->fts_fts)\n>> -#define FTSENT_DIRP(p)  (FTSENT_WRAPPER(p)->fts_dirp)\n>> -\n>> -#include \"flexmember.h\"\n>> -\n>> -#include <dirent.h>\n>> -#ifndef _D_EXACT_NAMLEN\n>> -# define _D_EXACT_NAMLEN(dirent) strlen ((dirent)->d_name)\n>> -#endif\n>> -\n>> -#if HAVE_STRUCT_DIRENT_D_TYPE\n>> -/* True if the type of the directory entry D is known.  */\n>> -# define DT_IS_KNOWN(d) ((d)->d_type != DT_UNKNOWN)\n>> -/* True if the type of the directory entry D must be T.  */\n>> -# define DT_MUST_BE(d, t) ((d)->d_type == (t))\n>> -# define D_TYPE(d) ((d)->d_type)\n>> -#else\n>> -# define DT_IS_KNOWN(d) false\n>> -# define DT_MUST_BE(d, t) false\n>> -# define D_TYPE(d) DT_UNKNOWN\n>> -\n>> -# undef DT_UNKNOWN\n>> -# define DT_UNKNOWN 0\n>> -\n>> -/* Any nonzero values will do here, so long as they're distinct.\n>> -   Undef any existing macros out of the way.  */\n>> -# undef DT_BLK\n>> -# undef DT_CHR\n>> -# undef DT_DIR\n>> -# undef DT_FIFO\n>> -# undef DT_LNK\n>> -# undef DT_REG\n>> -# undef DT_SOCK\n>> -# define DT_BLK 1\n>> -# define DT_CHR 2\n>> -# define DT_DIR 3\n>> -# define DT_FIFO 4\n>> -# define DT_LNK 5\n>> -# define DT_REG 6\n>> -# define DT_SOCK 7\n>> -#endif\n>> -\n>> -#ifndef S_IFBLK\n>> -# define S_IFBLK 0\n>> -#endif\n>> -#ifndef S_IFLNK\n>> -# define S_IFLNK 0\n>> -#endif\n>> -#ifndef S_IFSOCK\n>> -# define S_IFSOCK 0\n>> -#endif\n>> -\n>> -enum\n>> -{\n>> -  NOT_AN_INODE_NUMBER = 0\n>> -};\n>> -\n>> -#ifdef D_INO_IN_DIRENT\n>> -# define D_INO(dp) (dp)->d_ino\n>> -#else\n>> -/* Some systems don't have inodes, so fake them to avoid lots of ifdefs.  */\n>> -# define D_INO(dp) NOT_AN_INODE_NUMBER\n>> -#endif\n>> -\n>> -/* If possible (see max_entries, below), read no more than this many directory\n>> -   entries at a time.  Without this limit (i.e., when using non-NULL\n>> -   fts_compar), processing a directory with 4,000,000 entries requires ~1GiB\n>> -   of memory, and handling 64M entries would require 16GiB of memory.  */\n>> -#ifndef FTS_MAX_READDIR_ENTRIES\n>> -# define FTS_MAX_READDIR_ENTRIES 100000\n>> -#endif\n>> -\n>> -/* If there are more than this many entries in a directory,\n>> -   and the conditions mentioned below are satisfied, then sort\n>> -   the entries on inode number before any further processing.  */\n>> -#ifndef FTS_INODE_SORT_DIR_ENTRIES_THRESHOLD\n>> -# define FTS_INODE_SORT_DIR_ENTRIES_THRESHOLD 10000\n>> -#endif\n>> -\n>> -enum\n>> -{\n>> -  _FTS_INODE_SORT_DIR_ENTRIES_THRESHOLD = FTS_INODE_SORT_DIR_ENTRIES_THRESHOLD\n>> -};\n>> -\n>> -enum Fts_stat\n>> -{\n>> -  FTS_NO_STAT_REQUIRED = 1,\n>> -  FTS_STAT_REQUIRED = 2\n>> -};\n>> -\n>> -#ifndef __set_errno\n>> -# define __set_errno(Val) errno = (Val)\n>> -#endif\n>> -\n>> -/* If this host provides the openat function, then we can avoid\n>> -   attempting to open \".\" in some initialization code below.  */\n>> -#ifdef HAVE_OPENAT\n>> -# define HAVE_OPENAT_SUPPORT 1\n>> -#else\n>> -# define HAVE_OPENAT_SUPPORT 0\n>> -#endif\n>> -\n>> -#ifdef NDEBUG\n>> -# define fts_assert(expr) ((void) (0 && (expr)))\n>> -#else\n>> -# define fts_assert(expr)       \\\n>> -    do                          \\\n>> -      {                         \\\n>> -        if (!(expr))            \\\n>> -          abort ();             \\\n>> -      }                         \\\n>> -    while (false)\n>> -#endif\n>> -\n>> -static FTSENTRY  *fts_alloc (FTSOBJ *, const char *, size_t);\n>> -static FTSENTRY  *fts_build (FTSOBJ *, int);\n>> -static void      fts_lfree (FTSENTRY *);\n>> -static void      fts_load (FTSOBJ *, FTSENTRY *);\n>> -static size_t    fts_maxarglen (char * const *);\n>> -static void      fts_padjust (FTSOBJ *, FTSENTRY *);\n>> -static bool      fts_palloc (FTSOBJ *, size_t);\n>> -static FTSENTRY  *fts_sort (FTSOBJ *, FTSENTRY *, size_t);\n>> -static unsigned short int fts_stat (FTSOBJ *, FTSENTRY *, bool);\n>> -static int      fts_safe_changedir (FTSOBJ *, FTSENTRY *, int, const char *);\n>> -\n>> -#include \"fts-cycle.c\"\n>> -\n>> -#ifndef MAX\n>> -# define MAX(a,b) ((a) > (b) ? (a) : (b))\n>> -#endif\n>> -\n>> -#ifndef SIZE_MAX\n>> -# define SIZE_MAX ((size_t) -1)\n>> -#endif\n>> -\n>> -#define ISDOT(a)        (a[0] == '.' && (!a[1] || (a[1] == '.' && !a[2])))\n>> -\n>> -#define CLR(opt)        (sp->fts_options &= ~(opt))\n>> -#define ISSET(opt)      ((sp->fts_options & (opt)) != 0)\n>> -#define SET(opt)        (sp->fts_options |= (opt))\n>> -\n>> -/* FIXME: FTS_NOCHDIR is now misnamed.\n>> -   Call it FTS_USE_FULL_RELATIVE_FILE_NAMES instead. */\n>> -#define FCHDIR(sp, fd)                                  \\\n>> -  (!ISSET(FTS_NOCHDIR) && (ISSET(FTS_CWDFD)             \\\n>> -                           ? (cwd_advance_fd ((sp), (fd), true), 0) \\\n>> -                           : fchdir (fd)))\n>> -\n>> -\n>> -/* fts_build flags */\n>> -/* FIXME: make this an enum */\n>> -#define BCHILD          1               /* fts_children */\n>> -#define BNAMES          2               /* fts_children, names only */\n>> -#define BREAD           3               /* fts_read */\n>> -\n>> -#if GNULIB_FTS_DEBUG\n>> -# include <inttypes.h>\n>> -# include <stdio.h>\n>> -bool fts_debug = false;\n>> -# define Dprintf(x) do { if (fts_debug) printf x; } while (false)\n>> -static void fd_ring_check (FTSOBJ const *);\n>> -static void fd_ring_print (FTSOBJ const *, FILE *, char const *);\n>> -#else\n>> -# define Dprintf(x)\n>> -# define fd_ring_check(x)\n>> -# define fd_ring_print(a, b, c)\n>> -#endif\n>> -\n>> -#define LEAVE_DIR(Fts, Ent, Tag)                                \\\n>> -  do                                                            \\\n>> -    {                                                           \\\n>> -      Dprintf ((\"  %s-leaving: %s\\n\", Tag, (Ent)->fts_path));   \\\n>> -      leave_dir (Fts, Ent);                                     \\\n>> -      fd_ring_check (Fts);                                      \\\n>> -    }                                                           \\\n>> -  while (false)\n>> -\n>> -static void\n>> -fd_ring_clear (I_ring *fd_ring)\n>> -{\n>> -  while ( ! i_ring_empty (fd_ring))\n>> -    {\n>> -      int fd = i_ring_pop (fd_ring);\n>> -      if (0 <= fd)\n>> -        close (fd);\n>> -    }\n>> -}\n>> -\n>> -/* Overload the fts_statp->st_size member (otherwise unused, when\n>> -   fts_info is FTS_NSOK) to indicate whether fts_read should stat\n>> -   this entry or not.  */\n>> -static void\n>> -fts_set_stat_required (FTSENTRY *p, bool required)\n>> -{\n>> -  fts_assert (p->fts_info == FTS_NSOK);\n>> -  p->fts_statp->st_size = (required\n>> -                           ? FTS_STAT_REQUIRED\n>> -                           : FTS_NO_STAT_REQUIRED);\n>> -}\n>> -\n>> -/* Virtual fchdir.  Advance SP's working directory file descriptor,\n>> -   SP->fts_cwd_fd, to FD, and push the previous value onto the fd_ring.\n>> -   CHDIR_DOWN_ONE is true if FD corresponds to an entry in the directory\n>> -   open on sp->fts_cwd_fd; i.e., to move the working directory one level\n>> -   down.  */\n>> -static void\n>> -internal_function\n>> -cwd_advance_fd (FTSOBJ *sp, int fd, bool chdir_down_one)\n>> -{\n>> -  int old = sp->fts_cwd_fd;\n>> -  fts_assert (old != fd || old == AT_FDCWD);\n>> -\n>> -  if (chdir_down_one)\n>> -    {\n>> -      /* Push \"old\" onto the ring.\n>> -         If the displaced file descriptor is non-negative, close it.  */\n>> -      int prev_fd_in_slot = i_ring_push (&sp->fts_fd_ring, old);\n>> -      fd_ring_print (sp, stderr, \"post-push\");\n>> -      if (0 <= prev_fd_in_slot)\n>> -        close (prev_fd_in_slot); /* ignore any close failure */\n>> -    }\n>> -  else if ( ! ISSET (FTS_NOCHDIR))\n>> -    {\n>> -      if (0 <= old)\n>> -        close (old); /* ignore any close failure */\n>> -    }\n>> -\n>> -  sp->fts_cwd_fd = fd;\n>> -}\n>> -\n>> -/* Restore the initial, pre-traversal, \"working directory\".\n>> -   In FTS_CWDFD mode, we merely call cwd_advance_fd, otherwise,\n>> -   we may actually change the working directory.\n>> -   Return 0 upon success. Upon failure, set errno and return nonzero.  */\n>> -static int\n>> -restore_initial_cwd (FTSOBJ *sp)\n>> -{\n>> -  int fail = FCHDIR (sp, ISSET (FTS_CWDFD) ? AT_FDCWD : sp->fts_rfd);\n>> -  fd_ring_clear (&(sp->fts_fd_ring));\n>> -  return fail;\n>> -}\n>> -\n>> -/* Open the directory DIR if possible, and return a file\n>> -   descriptor.  Return -1 and set errno on failure.  It doesn't matter\n>> -   whether the file descriptor has read or write access.  */\n>> -\n>> -static int\n>> -internal_function\n>> -diropen (FTSOBJ const *sp, char const *dir)\n>> -{\n>> -  int open_flags = (O_SEARCH | O_CLOEXEC | O_DIRECTORY | O_NOCTTY | O_NONBLOCK\n>> -                    | (ISSET (FTS_PHYSICAL) ? O_NOFOLLOW : 0));\n>> -\n>> -  int fd = (ISSET (FTS_CWDFD)\n>> -            ? openat (sp->fts_cwd_fd, dir, open_flags)\n>> -            : open (dir, open_flags));\n>> -  return fd;\n>> -}\n>> -\n>> -FTSOBJ *\n>> -FTS_OPEN (char * const *argv,\n>> -          register int options,\n>> -          int (*compar) (const FTSENTRY **, const FTSENTRY **))\n>> -{\n>> -        /* Options check: glibc added other flags after FTS_NAMEONLY and\n>> -\t   FTS_STOP, and they are assumed to be private.  */\n>> -        if (options & ~FTS_OPTIONMASK\n>> -#if _LIBC\n>> -\t    || options & (FTS_NAMEONLY | FTS_STOP)\n>> -#endif\n>> -\t    ) {\n>> -                __set_errno (EINVAL);\n>> -                return (NULL);\n>> -        }\n>> -        if ((options & FTS_NOCHDIR) && (options & FTS_CWDFD)) {\n>> -                __set_errno (EINVAL);\n>> -                return (NULL);\n>> -        }\n>> -#if !_LIBC\n>> -        if ( ! (options & (FTS_LOGICAL | FTS_PHYSICAL))) {\n>> -                __set_errno (EINVAL);\n>> -                return (NULL);\n>> -        }\n>> -#else\n>> -\t/* glibc historically falls to FTS_PHYSICAL if no FTS_PHYSICAL or\n>> -\t   FTS_LOGICAL is specified.  */\n>> -        if (! (options & (FTS_PHYSICAL | FTS_LOGICAL)))\n>> -\t  options |= FTS_PHYSICAL;\n>> -#endif\n>> -\n>> -        /* Allocate/initialize the stream */\n>> -        register FTSOBJ *sp = calloc (1, sizeof *sp);\n>> -        if (sp == NULL)\n>> -                return (NULL);\n>> -        sp->fts_compar = FTS_COMPAR_CAST(compar);\n>> -        sp->fts_options = options;\n>> -\n>> -        /* Logical walks turn on NOCHDIR; symbolic links are too hard. */\n>> -        if (ISSET(FTS_LOGICAL)) {\n>> -                SET(FTS_NOCHDIR);\n>> -                CLR(FTS_CWDFD);\n>> -        }\n>> -\n>> -        /* Initialize fts_cwd_fd.  */\n>> -        sp->fts_cwd_fd = AT_FDCWD;\n>> -        if ( ISSET(FTS_CWDFD) && ! HAVE_OPENAT_SUPPORT)\n>> -          {\n>> -            /* While it isn't technically necessary to open \".\" this\n>> -               early, doing it here saves us the trouble of ensuring\n>> -               later (where it'd be messier) that \".\" can in fact\n>> -               be opened.  If not, revert to FTS_NOCHDIR mode.  */\n>> -            int fd = open (\".\", O_SEARCH | O_CLOEXEC);\n>> -            if (fd < 0)\n>> -              {\n>> -                /* Even if \".\" is unreadable, don't revert to FTS_NOCHDIR mode\n>> -                   on systems like Linux+PROC_FS, where our openat emulation\n>> -                   is good enough.  Note: on a system that emulates\n>> -                   openat via /proc, this technique can still fail, but\n>> -                   only in extreme conditions, e.g., when the working\n>> -                   directory cannot be saved (i.e. save_cwd fails) --\n>> -                   and that happens on Linux only when \".\" is unreadable\n>> -                   and the CWD would be longer than PATH_MAX.\n>> -                   FIXME: once Linux kernel openat support is well established,\n>> -                   replace the above open call and this entire if/else block\n>> -                   with the body of the if-block below.  */\n>> -                if ( openat_needs_fchdir ())\n>> -                  {\n>> -                    SET(FTS_NOCHDIR);\n>> -                    CLR(FTS_CWDFD);\n>> -                  }\n>> -              }\n>> -            else\n>> -              {\n>> -                close (fd);\n>> -              }\n>> -          }\n>> -\n>> -        /*\n>> -         * Start out with 1K of file name space, and enough, in any case,\n>> -         * to hold the user's file names.\n>> -         */\n>> -#ifndef MAXPATHLEN\n>> -# define MAXPATHLEN 1024\n>> -#endif\n>> -        {\n>> -          size_t maxarglen = fts_maxarglen(argv);\n>> -          if (! fts_palloc(sp, MAX(maxarglen, MAXPATHLEN)))\n>> -                  goto mem1;\n>> -        }\n>> -\n>> -        /* Allocate/initialize root's parent. */\n>> -        FTSENTRY *parent = NULL;\n>> -        if (*argv != NULL) {\n>> -                if ((parent = fts_alloc(sp, \"\", 0)) == NULL)\n>> -                        goto mem2;\n>> -                parent->fts_level = FTS_ROOTPARENTLEVEL;\n>> -          }\n>> -\n>> -        /* The classic fts implementation would call fts_stat with\n>> -           a new entry for each iteration of the loop below.\n>> -           If the comparison function is not specified or if the\n>> -           FTS_DEFER_STAT option is in effect, don't stat any entry\n>> -           in this loop.  This is an attempt to minimize the interval\n>> -           between the initial stat/lstat/fstatat and the point at which\n>> -           a directory argument is first opened.  This matters for any\n>> -           directory command line argument that resides on a file system\n>> -           without genuine i-nodes.  If you specify FTS_DEFER_STAT along\n>> -           with a comparison function, that function must not access any\n>> -           data via the fts_statp pointer.  */\n>> -        bool defer_stat = (compar == NULL || ISSET(FTS_DEFER_STAT));\n>> -\n>> -        /* Allocate/initialize root(s). */\n>> -        register FTSENTRY *root;\n>> -        register size_t nitems;\n>> -        FTSENTRY *tmp = NULL;     /* pacify gcc */\n>> -        for (root = NULL, nitems = 0; *argv != NULL; ++argv, ++nitems) {\n>> -                /* *Do* allow zero-length file names. */\n>> -                size_t len = strlen(*argv);\n>> -\n>> -                if ( ! (options & FTS_VERBATIM))\n>> -                  {\n>> -                    /* If there are two or more trailing slashes, trim all but one,\n>> -                       but don't change \"//\" to \"/\", and do map \"///\" to \"/\".  */\n>> -                    char const *v = *argv;\n>> -                    if (2 < len && v[len - 1] == '/')\n>> -                      while (1 < len && v[len - 2] == '/')\n>> -                        --len;\n>> -                  }\n>> -\n>> -                register FTSENTRY *p = fts_alloc(sp, *argv, len);\n>> -                if (p == NULL)\n>> -                        goto mem3;\n>> -                p->fts_level = FTS_ROOTLEVEL;\n>> -                p->fts_parent = parent;\n>> -                p->fts_accpath = p->fts_name;\n>> -                /* Even when defer_stat is true, be sure to stat the first\n>> -                   command line argument, since fts_read (at least with\n>> -                   FTS_XDEV) requires that.  */\n>> -                if (defer_stat && root != NULL) {\n>> -                        p->fts_info = FTS_NSOK;\n>> -                        fts_set_stat_required(p, true);\n>> -                } else {\n>> -                        p->fts_info = fts_stat(sp, p, false);\n>> -                }\n>> -\n>> -                /*\n>> -                 * If comparison routine supplied, traverse in sorted\n>> -                 * order; otherwise traverse in the order specified.\n>> -                 */\n>> -                if (compar) {\n>> -                        p->fts_link = root;\n>> -                        root = p;\n>> -                } else {\n>> -                        p->fts_link = NULL;\n>> -                        if (root == NULL)\n>> -                                tmp = root = p;\n>> -                        else {\n>> -                                tmp->fts_link = p;\n>> -                                tmp = p;\n>> -                        }\n>> -                }\n>> -        }\n>> -        if (compar && nitems > 1)\n>> -                root = fts_sort(sp, root, nitems);\n>> -\n>> -        /*\n>> -         * Allocate a dummy pointer and make fts_read think that we've just\n>> -         * finished the node before the root(s); set p->fts_info to FTS_INIT\n>> -         * so that everything about the \"current\" node is ignored.\n>> -         */\n>> -        if ((sp->fts_cur = fts_alloc(sp, \"\", 0)) == NULL)\n>> -                goto mem3;\n>> -        sp->fts_cur->fts_link = root;\n>> -        sp->fts_cur->fts_info = FTS_INIT;\n>> -        sp->fts_cur->fts_level = 1;\n>> -        if (! setup_dir (sp))\n>> -                goto mem3;\n>> -\n>> -        /*\n>> -         * If using chdir(2), grab a file descriptor pointing to dot to ensure\n>> -         * that we can get back here; this could be avoided for some file names,\n>> -         * but almost certainly not worth the effort.  Slashes, symbolic links,\n>> -         * and \"..\" are all fairly nasty problems.  Note, if we can't get the\n>> -         * descriptor we run anyway, just more slowly.\n>> -         */\n>> -        if (!ISSET(FTS_NOCHDIR) && !ISSET(FTS_CWDFD)\n>> -            && (sp->fts_rfd = diropen (sp, \".\")) < 0)\n>> -                SET(FTS_NOCHDIR);\n>> -\n>> -        i_ring_init (&sp->fts_fd_ring, -1);\n>> -        return (sp);\n>> -\n>> -mem3:   fts_lfree(root);\n>> -        free(FTSENT_WRAPPER(parent));\n>> -mem2:   free(sp->fts_path);\n>> -mem1:   free(sp);\n>> -        return (NULL);\n>> -}\n>> -\n>> -static void\n>> -internal_function\n>> -fts_load (FTSOBJ *sp, register FTSENTRY *p)\n>> -{\n>> -        /*\n>> -         * Load the stream structure for the next traversal.  Since we don't\n>> -         * actually enter the directory until after the preorder visit, set\n>> -         * the fts_accpath field specially so the chdir gets done to the right\n>> -         * place and the user can access the first node.  From fts_open it's\n>> -         * known that the file name will fit.\n>> -         */\n>> -        register size_t len = p->fts_pathlen = p->fts_namelen;\n>> -        memmove(sp->fts_path, p->fts_name, len + 1);\n>> -        register char *cp = strrchr(p->fts_name, '/');\n>> -        if (cp && (cp != p->fts_name || cp[1])) {\n>> -                len = strlen(++cp);\n>> -                memmove(p->fts_name, cp, len + 1);\n>> -                p->fts_namelen = len;\n>> -        }\n>> -        p->fts_accpath = p->fts_path = sp->fts_path;\n>> -}\n>> -\n>> -int\n>> -FTS_CLOSE (FTSOBJ *sp)\n>> -{\n>> -        /*\n>> -         * This still works if we haven't read anything -- the dummy structure\n>> -         * points to the root list, so we step through to the end of the root\n>> -         * list which has a valid parent pointer.\n>> -         */\n>> -        if (sp->fts_cur) {\n>> -                register FTSENTRY *p;\n>> -                for (p = sp->fts_cur; p->fts_level >= FTS_ROOTLEVEL;) {\n>> -                        register FTSENTRY *freep = p;\n>> -                        p = p->fts_link != NULL ? p->fts_link : p->fts_parent;\n>> -                        free(FTSENT_WRAPPER(freep));\n>> -                }\n>> -                free(FTSENT_WRAPPER(p));\n>> -        }\n>> -\n>> -        /* Free up child linked list, sort array, file name buffer. */\n>> -        if (sp->fts_child)\n>> -                fts_lfree(sp->fts_child);\n>> -        free(sp->fts_array);\n>> -        free(sp->fts_path);\n>> -\n>> -        int saved_errno = 0;\n>> -        if (ISSET(FTS_CWDFD))\n>> -          {\n>> -            if (0 <= sp->fts_cwd_fd)\n>> -              if (close (sp->fts_cwd_fd))\n>> -                saved_errno = errno;\n>> -          }\n>> -        else if (!ISSET(FTS_NOCHDIR))\n>> -          {\n>> -            /* Return to original directory, save errno if necessary. */\n>> -            if (fchdir(sp->fts_rfd))\n>> -              saved_errno = errno;\n>> -\n>> -            /* If close fails, record errno only if saved_errno is zero,\n>> -               so that we report the probably-more-meaningful fchdir errno.  */\n>> -            if (close (sp->fts_rfd))\n>> -              if (saved_errno == 0)\n>> -                saved_errno = errno;\n>> -          }\n>> -\n>> -        fd_ring_clear (&sp->fts_fd_ring);\n>> -\n>> -        if (sp->fts_leaf_optimization_works_ht)\n>> -          hash_free (sp->fts_leaf_optimization_works_ht);\n>> -\n>> -        free_dir (sp);\n>> -\n>> -        /* Free up the stream pointer. */\n>> -        free(sp);\n>> -\n>> -        /* Set errno and return. */\n>> -        if (saved_errno) {\n>> -                __set_errno (saved_errno);\n>> -                return (-1);\n>> -        }\n>> -\n>> -        return (0);\n>> -}\n>> -\n>> -/* Minimum link count of a traditional Unix directory.  When leaf\n>> -   optimization is OK and a directory's st_nlink == MIN_DIR_NLINK,\n>> -   then the directory has no subdirectories.  */\n>> -enum { MIN_DIR_NLINK = 2 };\n>> -\n>> -/* Whether leaf optimization is OK for a directory.  */\n>> -enum leaf_optimization\n>> -  {\n>> -    /* st_nlink is not reliable for this directory's subdirectories.  */\n>> -    NO_LEAF_OPTIMIZATION,\n>> -\n>> -    /* st_nlink == 2 means the directory lacks subdirectories.  */\n>> -    OK_LEAF_OPTIMIZATION\n>> -  };\n>> -\n>> -#if (defined __linux__ || defined __ANDROID__) \\\n>> -  && HAVE_SYS_VFS_H && HAVE_FSTATFS && HAVE_STRUCT_STATFS_F_TYPE\n>> -\n>> -# include <sys/vfs.h>\n>> -\n>> -/* Linux-specific constants from coreutils' src/fs.h */\n>> -# define S_MAGIC_AFS 0x5346414F\n>> -# define S_MAGIC_CIFS 0xFF534D42\n>> -# define S_MAGIC_LUSTRE 0x0BD00BD0\n>> -# define S_MAGIC_NFS 0x6969\n>> -# define S_MAGIC_PROC 0x9FA0\n>> -# define S_MAGIC_TMPFS 0x1021994\n>> -\n>> -# ifdef HAVE___FSWORD_T\n>> -typedef __fsword_t fsword;\n>> -# else\n>> -typedef long int fsword;\n>> -# endif\n>> -\n>> -/* Map a stat.st_dev number to a file system type number f_ftype.  */\n>> -struct dev_type\n>> -{\n>> -  dev_t st_dev;\n>> -  fsword f_type;\n>> -};\n>> -\n>> -/* Use a tiny initial size.  If a traversal encounters more than\n>> -   a few devices, the cost of growing/rehashing this table will be\n>> -   rendered negligible by the number of inodes processed.  */\n>> -enum { DEV_TYPE_HT_INITIAL_SIZE = 13 };\n>> -\n>> -static size_t\n>> -dev_type_hash (void const *x, size_t table_size)\n>> -{\n>> -  struct dev_type const *ax = x;\n>> -  uintmax_t dev = ax->st_dev;\n>> -  return dev % table_size;\n>> -}\n>> -\n>> -static bool\n>> -dev_type_compare (void const *x, void const *y)\n>> -{\n>> -  struct dev_type const *ax = x;\n>> -  struct dev_type const *ay = y;\n>> -  return ax->st_dev == ay->st_dev;\n>> -}\n>> -\n>> -/* Return the file system type of P with file descriptor FD, or 0 if not known.\n>> -   If FD is negative, P's file descriptor is unavailable.\n>> -   Try to cache known values.  */\n>> -\n>> -static fsword\n>> -filesystem_type (FTSENTRY const *p, int fd)\n>> -{\n>> -  FTSOBJ *sp = FTSENT_FTS(p);\n>> -\n>> -  /* If we're not in CWDFD mode, don't bother with this optimization,\n>> -     since the caller is not serious about performance.  */\n>> -  if (!ISSET (FTS_CWDFD))\n>> -    return 0;\n>> -\n>> -  Hash_table *h = sp->fts_leaf_optimization_works_ht;\n>> -  if (! h)\n>> -    h = sp->fts_leaf_optimization_works_ht\n>> -      = hash_initialize (DEV_TYPE_HT_INITIAL_SIZE, NULL, dev_type_hash,\n>> -                         dev_type_compare, free);\n>> -\n>> -  if (h)\n>> -    {\n>> -      struct dev_type tmp;\n>> -      tmp.st_dev = p->fts_statp->st_dev;\n>> -      struct dev_type *ent = hash_lookup (h, &tmp);\n>> -      if (ent)\n>> -        return ent->f_type;\n>> -    }\n>> -\n>> -  /* Look-up failed.  Query directly and cache the result.  */\n>> -  struct STRUCT_STATFS fs_buf;\n>> -  if (fd < 0 || FSTATFS (fd, &fs_buf) != 0)\n>> -    return 0;\n>> -\n>> -  if (h)\n>> -    {\n>> -      struct dev_type *t2 = malloc (sizeof *t2);\n>> -      if (t2)\n>> -        {\n>> -          t2->st_dev = p->fts_statp->st_dev;\n>> -          t2->f_type = fs_buf.f_type;\n>> -\n>> -          struct dev_type *ent = hash_insert (h, t2);\n>> -          if (ent)\n>> -            fts_assert (ent == t2);\n>> -          else\n>> -            free (t2);\n>> -        }\n>> -    }\n>> -\n>> -  return fs_buf.f_type;\n>> -}\n>> -\n>> -/* Return true if sorting dirents on inode numbers is known to improve\n>> -   traversal performance for the directory P with descriptor DIR_FD.\n>> -   Return false otherwise.  When in doubt, return true.\n>> -   DIR_FD is negative if unavailable.  */\n>> -static bool\n>> -dirent_inode_sort_may_be_useful (FTSENTRY const *p, int dir_fd)\n>> -{\n>> -  /* Skip the sort only if we can determine efficiently\n>> -     that skipping it is the right thing to do.\n>> -     The cost of performing an unnecessary sort is negligible,\n>> -     while the cost of *not* performing it can be O(N^2) with\n>> -     a very large constant.  */\n>> -\n>> -  switch (filesystem_type (p, dir_fd))\n>> -    {\n>> -    case S_MAGIC_LUSTRE:\n>> -      /* On Lustre, sorting directory entries interferes with its ability to\n>> -         prefetch file metadata (via statahead).  This would make a command\n>> -         like 'du' around 9 times slower.  See\n>> -         <https://bugs.gnu.org/80106>.  */\n>> -    case S_MAGIC_CIFS:\n>> -    case S_MAGIC_NFS:\n>> -    case S_MAGIC_TMPFS:\n>> -      /* On a file system of any of these types, sorting\n>> -         is unnecessary, and hence wasteful.  */\n>> -      return false;\n>> -\n>> -    default:\n>> -      return true;\n>> -    }\n>> -}\n>> -\n>> -/* Given an FTS entry P for a directory with descriptor DIR_FD,\n>> -   return whether it is valid to apply leaf optimization.\n>> -   The optimization is valid if a directory's st_nlink value equal\n>> -   to MIN_DIR_NLINK means the directory has no subdirectories.\n>> -   DIR_FD is negative if unavailable.  */\n>> -static enum leaf_optimization\n>> -leaf_optimization (FTSENTRY const *p, int dir_fd)\n>> -{\n>> -  switch (filesystem_type (p, dir_fd))\n>> -    {\n>> -    case 0:\n>> -      /* Leaf optimization is unsafe if the file system type is unknown.  */\n>> -      FALLTHROUGH;\n>> -    case S_MAGIC_AFS:\n>> -      /* Although AFS mount points are not counted in st_nlink, they\n>> -         act like directories.  See <https://bugs.debian.org/143111>.  */\n>> -      FALLTHROUGH;\n>> -    case S_MAGIC_CIFS:\n>> -      /* Leaf optimization causes 'find' to abort.  See\n>> -         <https://lists.gnu.org/r/bug-gnulib/2018-04/msg00015.html>.  */\n>> -      FALLTHROUGH;\n>> -    case S_MAGIC_NFS:\n>> -      /* NFS provides usable dirent.d_type but not necessarily for all entries\n>> -         of large directories, so as per <https://bugzilla.redhat.com/1252549>\n>> -         NFS should return true.  However st_nlink values are not accurate on\n>> -         all implementations as per <https://bugzilla.redhat.com/1299169>.  */\n>> -      FALLTHROUGH;\n>> -    case S_MAGIC_PROC:\n>> -      /* Per <https://bugs.debian.org/cgi-bin/bugreport.cgi?bug=143111> /proc\n>> -         may have bogus stat.st_nlink values.  */\n>> -      return NO_LEAF_OPTIMIZATION;\n>> -\n>> -    default:\n>> -      return OK_LEAF_OPTIMIZATION;\n>> -    }\n>> -}\n>> -\n>> -#else\n>> -static bool\n>> -dirent_inode_sort_may_be_useful (_GL_UNUSED FTSENTRY const *p,\n>> -                                 _GL_UNUSED int dir_fd)\n>> -{\n>> -  return true;\n>> -}\n>> -static enum leaf_optimization\n>> -leaf_optimization (_GL_UNUSED FTSENTRY const *p, _GL_UNUSED int dir_fd)\n>> -{\n>> -  return NO_LEAF_OPTIMIZATION;\n>> -}\n>> -#endif\n>> -\n>> -/*\n>> - * Special case of \"/\" at the end of the file name so that slashes aren't\n>> - * appended which would cause file names to be written as \"....//foo\".\n>> - */\n>> -#define NAPPEND(p)                                                      \\\n>> -        (p->fts_path[p->fts_pathlen - 1] == '/'                         \\\n>> -            ? p->fts_pathlen - 1 : p->fts_pathlen)\n>> -\n>> -FTSENTRY *\n>> -FTS_READ (FTSOBJ *sp)\n>> -{\n>> -        /* If finished or unrecoverable error, return NULL. */\n>> -        if (sp->fts_cur == NULL || ISSET(FTS_STOP))\n>> -                return (NULL);\n>> -\n>> -        /* Set current node pointer. */\n>> -        register FTSENTRY *p = sp->fts_cur;\n>> -\n>> -        /* Save and zero out user instructions. */\n>> -        register unsigned short int instr = p->fts_instr;\n>> -        p->fts_instr = FTS_NOINSTR;\n>> -\n>> -        /* Any type of file may be re-visited; re-stat and re-turn. */\n>> -        if (instr == FTS_AGAIN) {\n>> -                p->fts_info = fts_stat(sp, p, false);\n>> -                return (p);\n>> -        }\n>> -        Dprintf ((\"fts_read: p=%s\\n\",\n>> -                  p->fts_info == FTS_INIT ? \"\" : p->fts_path));\n>> -\n>> -        /*\n>> -         * Following a symlink -- SLNONE test allows application to see\n>> -         * SLNONE and recover.  If indirecting through a symlink, have\n>> -         * keep a pointer to current location.  If unable to get that\n>> -         * pointer, follow fails.\n>> -         */\n>> -        if (instr == FTS_FOLLOW &&\n>> -            (p->fts_info == FTS_SL || p->fts_info == FTS_SLNONE)) {\n>> -                p->fts_info = fts_stat(sp, p, true);\n>> -                if (p->fts_info == FTS_D && !ISSET(FTS_NOCHDIR)) {\n>> -                        if ((p->fts_symfd = diropen (sp, \".\")) < 0) {\n>> -                                p->fts_errno = errno;\n>> -                                p->fts_info = FTS_ERR;\n>> -                        } else\n>> -                                p->fts_flags |= FTS_SYMFOLLOW;\n>> -                }\n>> -                goto check_for_dir;\n>> -        }\n>> -\n>> -        /* Directory in pre-order. */\n>> -        if (p->fts_info == FTS_D) {\n>> -                /* If skipped or crossed mount point, do post-order visit. */\n>> -                if (instr == FTS_SKIP ||\n>> -                    (ISSET(FTS_XDEV) && p->fts_statp->st_dev != sp->fts_dev)) {\n>> -                        if (p->fts_flags & FTS_SYMFOLLOW)\n>> -                                (void)close(p->fts_symfd);\n>> -                        if (sp->fts_child) {\n>> -                                fts_lfree(sp->fts_child);\n>> -                                sp->fts_child = NULL;\n>> -                        }\n>> -                        p->fts_info = FTS_DP;\n>> -                        LEAVE_DIR (sp, p, \"1\");\n>> -                        return (p);\n>> -                }\n>> -\n>> -                /* Rebuild if only read the names and now traversing. */\n>> -                if (sp->fts_child != NULL && ISSET(FTS_NAMEONLY)) {\n>> -                        CLR(FTS_NAMEONLY);\n>> -                        fts_lfree(sp->fts_child);\n>> -                        sp->fts_child = NULL;\n>> -                }\n>> -\n>> -                /*\n>> -                 * Cd to the subdirectory.\n>> -                 *\n>> -                 * If have already read and now fail to chdir, whack the list\n>> -                 * to make the names come out right, and set the parent errno\n>> -                 * so the application will eventually get an error condition.\n>> -                 * Set the FTS_DONTCHDIR flag so that when we logically change\n>> -                 * directories back to the parent we don't do a chdir.\n>> -                 *\n>> -                 * If haven't read do so.  If the read fails, fts_build sets\n>> -                 * FTS_STOP or the fts_info field of the node.\n>> -                 */\n>> -                if (sp->fts_child != NULL) {\n>> -                        if (fts_safe_changedir(sp, p, -1, p->fts_accpath)) {\n>> -                                p->fts_errno = errno;\n>> -                                p->fts_flags |= FTS_DONTCHDIR;\n>> -                                for (p = sp->fts_child; p != NULL;\n>> -                                     p = p->fts_link)\n>> -                                        p->fts_accpath =\n>> -                                            p->fts_parent->fts_accpath;\n>> -                        }\n>> -                } else if ((sp->fts_child = fts_build(sp, BREAD)) == NULL) {\n>> -                        if (ISSET(FTS_STOP))\n>> -                                return (NULL);\n>> -                        /* If fts_build's call to fts_safe_changedir failed\n>> -                           because it was not able to fchdir into a\n>> -                           subdirectory, tell the caller.  */\n>> -                        if (p->fts_errno && p->fts_info != FTS_DNR)\n>> -                                p->fts_info = FTS_ERR;\n>> -                        LEAVE_DIR (sp, p, \"2\");\n>> -                        return (p);\n>> -                }\n>> -                p = sp->fts_child;\n>> -                sp->fts_child = NULL;\n>> -                goto name;\n>> -        }\n>> -\n>> -        /* Move to the next node on this level. */\n>> -next: ;\n>> -        register FTSENTRY *tmp = p;\n>> -\n>> -        /* If we have so many directory entries that we're reading them\n>> -           in batches, and we've reached the end of the current batch,\n>> -           read in a new batch.  */\n>> -        if (p->fts_link == NULL && FTSENT_DIRP(p->fts_parent))\n>> -          {\n>> -            p = tmp->fts_parent;\n>> -            sp->fts_cur = p;\n>> -            sp->fts_path[p->fts_pathlen] = '\\0';\n>> -\n>> -            if ((p = fts_build (sp, BREAD)) == NULL)\n>> -              {\n>> -                if (ISSET(FTS_STOP))\n>> -                  return NULL;\n>> -                goto cd_dot_dot;\n>> -              }\n>> -\n>> -            free(FTSENT_WRAPPER(tmp));\n>> -            goto name;\n>> -          }\n>> -\n>> -        if ((p = p->fts_link) != NULL) {\n>> -                sp->fts_cur = p;\n>> -                free(FTSENT_WRAPPER(tmp));\n>> -\n>> -                /*\n>> -                 * If reached the top, return to the original directory (or\n>> -                 * the root of the tree), and load the file names for the next\n>> -                 * root.\n>> -                 */\n>> -                if (p->fts_level == FTS_ROOTLEVEL) {\n>> -                        if (restore_initial_cwd(sp)) {\n>> -                                SET(FTS_STOP);\n>> -                                return (NULL);\n>> -                        }\n>> -                        free_dir(sp);\n>> -                        fts_load(sp, p);\n>> -                        if (! setup_dir(sp)) {\n>> -                                free_dir(sp);\n>> -                                return (NULL);\n>> -                        }\n>> -                        goto check_for_dir;\n>> -                }\n>> -\n>> -                /*\n>> -                 * User may have called fts_set on the node.  If skipped,\n>> -                 * ignore.  If followed, get a file descriptor so we can\n>> -                 * get back if necessary.\n>> -                 */\n>> -                if (p->fts_instr == FTS_SKIP)\n>> -                        goto next;\n>> -                if (p->fts_instr == FTS_FOLLOW) {\n>> -                        p->fts_info = fts_stat(sp, p, true);\n>> -                        if (p->fts_info == FTS_D && !ISSET(FTS_NOCHDIR)) {\n>> -                                if ((p->fts_symfd = diropen (sp, \".\")) < 0) {\n>> -                                        p->fts_errno = errno;\n>> -                                        p->fts_info = FTS_ERR;\n>> -                                } else\n>> -                                        p->fts_flags |= FTS_SYMFOLLOW;\n>> -                        }\n>> -                        p->fts_instr = FTS_NOINSTR;\n>> -                }\n>> -\n>> -name:           {\n>> -                  register char *t = sp->fts_path + NAPPEND(p->fts_parent);\n>> -                  *t++ = '/';\n>> -                  memmove(t, p->fts_name, p->fts_namelen + 1);\n>> -                }\n>> -check_for_dir:\n>> -                sp->fts_cur = p;\n>> -                if (p->fts_info == FTS_NSOK)\n>> -                  {\n>> -                    if (p->fts_statp->st_size == FTS_STAT_REQUIRED)\n>> -                      p->fts_info = fts_stat(sp, p, false);\n>> -                    else\n>> -                      fts_assert (p->fts_statp->st_size == FTS_NO_STAT_REQUIRED);\n>> -                  }\n>> -\n>> -                /* Skip files with different device numbers when FTS_MOUNT\n>> -                   is set.  */\n>> -                if (ISSET (FTS_MOUNT) && p->fts_info != FTS_NS &&\n>> -                    p->fts_level != FTS_ROOTLEVEL &&\n>> -                    p->fts_statp->st_dev != sp->fts_dev)\n>> -                      goto next;\n>> -\n>> -                if (p->fts_info == FTS_D)\n>> -                  {\n>> -                    /* Now that P->fts_statp is guaranteed to be valid, if\n>> -                       this is a command-line directory, record its device\n>> -                       number, to be used for FTS_MOUNT and FTS_XDEV.  */\n>> -                    if (p->fts_level == FTS_ROOTLEVEL)\n>> -                      sp->fts_dev = p->fts_statp->st_dev;\n>> -                    Dprintf ((\"  entering: %s\\n\", p->fts_path));\n>> -                    if (! enter_dir (sp, p))\n>> -                      return NULL;\n>> -                  }\n>> -                return p;\n>> -        }\n>> -cd_dot_dot:\n>> -\n>> -        /* Move up to the parent node. */\n>> -        p = tmp->fts_parent;\n>> -        sp->fts_cur = p;\n>> -        free(FTSENT_WRAPPER(tmp));\n>> -\n>> -        if (p->fts_level == FTS_ROOTPARENTLEVEL) {\n>> -                /*\n>> -                 * Done; free everything up and set errno to 0 so the user\n>> -                 * can distinguish between error and EOF.\n>> -                 */\n>> -                free(FTSENT_WRAPPER(p));\n>> -                __set_errno (0);\n>> -                return (sp->fts_cur = NULL);\n>> -        }\n>> -\n>> -        fts_assert (p->fts_info != FTS_NSOK);\n>> -\n>> -        /* NUL terminate the file name.  */\n>> -        sp->fts_path[p->fts_pathlen] = '\\0';\n>> -\n>> -        /*\n>> -         * Return to the parent directory.  If at a root node, restore\n>> -         * the initial working directory.  If we came through a symlink,\n>> -         * go back through the file descriptor.  Otherwise, move up\n>> -         * one level, via \"..\".\n>> -         */\n>> -        if (p->fts_level == FTS_ROOTLEVEL) {\n>> -                if (restore_initial_cwd(sp)) {\n>> -                        p->fts_errno = errno;\n>> -                        SET(FTS_STOP);\n>> -                }\n>> -        } else if (p->fts_flags & FTS_SYMFOLLOW) {\n>> -                if (FCHDIR(sp, p->fts_symfd)) {\n>> -                        p->fts_errno = errno;\n>> -                        SET(FTS_STOP);\n>> -                }\n>> -                (void)close(p->fts_symfd);\n>> -        } else if (!(p->fts_flags & FTS_DONTCHDIR) &&\n>> -                   fts_safe_changedir(sp, p->fts_parent, -1, \"..\")) {\n>> -                p->fts_errno = errno;\n>> -                SET(FTS_STOP);\n>> -        }\n>> -\n>> -        /* If the directory causes a cycle, preserve the FTS_DC flag and keep\n>> -           the corresponding dev/ino pair in the hash table.  It is going to be\n>> -           removed when leaving the original directory.  */\n>> -        if (p->fts_info != FTS_DC) {\n>> -                p->fts_info = p->fts_errno ? FTS_ERR : FTS_DP;\n>> -                if (p->fts_errno == 0)\n>> -                        LEAVE_DIR (sp, p, \"3\");\n>> -        }\n>> -        return ISSET(FTS_STOP) ? NULL : p;\n>> -}\n>> -\n>> -/*\n>> - * Fts_set takes the stream as an argument although it's not used in this\n>> - * implementation; it would be necessary if anyone wanted to add global\n>> - * semantics to fts using fts_set.  An error return is allowed for similar\n>> - * reasons.\n>> - */\n>> -/* ARGSUSED */\n>> -int\n>> -FTS_SET (_GL_UNUSED FTSOBJ *sp, FTSENTRY *p, int instr)\n>> -{\n>> -        if (instr != 0 && instr != FTS_AGAIN && instr != FTS_FOLLOW &&\n>> -            instr != FTS_NOINSTR && instr != FTS_SKIP) {\n>> -                __set_errno (EINVAL);\n>> -                return (1);\n>> -        }\n>> -        p->fts_instr = instr;\n>> -        return (0);\n>> -}\n>> -\n>> -FTSENTRY *\n>> -FTS_CHILDREN (FTSOBJ *sp, int instr)\n>> -{\n>> -        if (instr != 0 && instr != FTS_NAMEONLY) {\n>> -                __set_errno (EINVAL);\n>> -                return (NULL);\n>> -        }\n>> -\n>> -        /* Set current node pointer. */\n>> -        register FTSENTRY *p = sp->fts_cur;\n>> -\n>> -        /*\n>> -         * Errno set to 0 so user can distinguish empty directory from\n>> -         * an error.\n>> -         */\n>> -        __set_errno (0);\n>> -\n>> -        /* Fatal errors stop here. */\n>> -        if (ISSET(FTS_STOP))\n>> -                return (NULL);\n>> -\n>> -        /* Return logical hierarchy of user's arguments. */\n>> -        if (p->fts_info == FTS_INIT)\n>> -                return (p->fts_link);\n>> -\n>> -        /*\n>> -         * If not a directory being visited in pre-order, stop here.  Could\n>> -         * allow FTS_DNR, assuming the user has fixed the problem, but the\n>> -         * same effect is available with FTS_AGAIN.\n>> -         */\n>> -        if (p->fts_info != FTS_D /* && p->fts_info != FTS_DNR */)\n>> -                return (NULL);\n>> -\n>> -        /* Free up any previous child list. */\n>> -        if (sp->fts_child != NULL)\n>> -                fts_lfree(sp->fts_child);\n>> -\n>> -        if (instr == FTS_NAMEONLY) {\n>> -                SET(FTS_NAMEONLY);\n>> -                instr = BNAMES;\n>> -        } else\n>> -                instr = BCHILD;\n>> -\n>> -        /*\n>> -         * If using chdir on a relative file name and called BEFORE fts_read\n>> -         * does its chdir to the root of a traversal, we can lose -- we need to\n>> -         * chdir into the subdirectory, and we don't know where the current\n>> -         * directory is, so we can't get back so that the upcoming chdir by\n>> -         * fts_read will work.\n>> -         */\n>> -        if (p->fts_level != FTS_ROOTLEVEL || p->fts_accpath[0] == '/' ||\n>> -            ISSET(FTS_NOCHDIR))\n>> -                return (sp->fts_child = fts_build(sp, instr));\n>> -\n>> -        int fd = diropen (sp, \".\");\n>> -        if (fd < 0)\n>> -                return (sp->fts_child = NULL);\n>> -        sp->fts_child = fts_build(sp, instr);\n>> -        if (ISSET(FTS_CWDFD))\n>> -          {\n>> -            cwd_advance_fd (sp, fd, true);\n>> -          }\n>> -        else\n>> -          {\n>> -            if (fchdir(fd))\n>> -              {\n>> -                int saved_errno = errno;\n>> -                close (fd);\n>> -                __set_errno (saved_errno);\n>> -                return NULL;\n>> -              }\n>> -            close (fd);\n>> -          }\n>> -        return (sp->fts_child);\n>> -}\n>> -\n>> -/* A comparison function to sort on increasing inode number.\n>> -   For some file system types, sorting either way makes a huge\n>> -   performance difference for a directory with very many entries,\n>> -   but sorting on increasing values is slightly better than sorting\n>> -   on decreasing values.  The difference is in the 5% range.  */\n>> -static int\n>> -fts_compare_ino (FTSENTRY const **a, FTSENTRY const **b)\n>> -{\n>> -  return _GL_CMP (a[0]->fts_statp->st_ino, b[0]->fts_statp->st_ino);\n>> -}\n>> -\n>> -/* Map the dirent.d_type value, DTYPE, to the corresponding stat.st_mode\n>> -   S_IF* bit and set ST.st_mode, thus clearing all other bits in that field.  */\n>> -static void\n>> -set_stat_type (struct STRUCT_STAT *st, unsigned int dtype)\n>> -{\n>> -  mode_t type;\n>> -  switch (dtype)\n>> -    {\n>> -    case DT_BLK:\n>> -      type = S_IFBLK;\n>> -      break;\n>> -    case DT_CHR:\n>> -      type = S_IFCHR;\n>> -      break;\n>> -    case DT_DIR:\n>> -      type = S_IFDIR;\n>> -      break;\n>> -    case DT_FIFO:\n>> -      type = S_IFIFO;\n>> -      break;\n>> -    case DT_LNK:\n>> -      type = S_IFLNK;\n>> -      break;\n>> -    case DT_REG:\n>> -      type = S_IFREG;\n>> -      break;\n>> -    case DT_SOCK:\n>> -      type = S_IFSOCK;\n>> -      break;\n>> -    default:\n>> -      type = 0;\n>> -    }\n>> -  st->st_mode = type;\n>> -}\n>> -\n>> -#define closedir_and_clear(dirp)                \\\n>> -  do                                            \\\n>> -    {                                           \\\n>> -      closedir (dirp);                          \\\n>> -      dirp = NULL;                              \\\n>> -    }                                           \\\n>> -  while (0)\n>> -\n>> -#define fts_opendir(file, Pdir_fd)                              \\\n>> -        OPENDIRAT((! ISSET(FTS_NOCHDIR) && ISSET(FTS_CWDFD)     \\\n>> -                   ? sp->fts_cwd_fd : AT_FDCWD),                \\\n>> -                  file,                                         \\\n>> -                  (((ISSET(FTS_PHYSICAL)                        \\\n>> -                     && ! (ISSET(FTS_COMFOLLOW)                 \\\n>> -                           && cur->fts_level == FTS_ROOTLEVEL)) \\\n>> -                    ? O_NOFOLLOW : 0)),                         \\\n>> -                  Pdir_fd)\n>> -\n>> -/*\n>> - * This is the tricky part -- do not casually change *anything* in here.  The\n>> - * idea is to build the linked list of entries that are used by fts_children\n>> - * and fts_read.  There are lots of special cases.\n>> - *\n>> - * The real slowdown in walking the tree is the stat calls.  If FTS_NOSTAT is\n>> - * set and it's a physical walk (so that symbolic links can't be directories),\n>> - * we can do things quickly.  First, if it's a 4.4BSD file system, the type\n>> - * of the file is in the directory entry.  Otherwise, we assume that the number\n>> - * of subdirectories in a node is equal to the number of links to the parent.\n>> - * The former skips all stat calls.  The latter skips stat calls in any leaf\n>> - * directories and for any files after the subdirectories in the directory have\n>> - * been found, cutting the stat calls by about 2/3.\n>> - */\n>> -static FTSENTRY *\n>> -internal_function\n>> -fts_build (register FTSOBJ *sp, int type)\n>> -{\n>> -        FTSENTRY *cur = sp->fts_cur;\n>> -        bool continue_readdir = !!FTSENT_DIRP(cur);\n>> -\n>> -        /* When cur->fts_dirp is non-NULL, that means we should\n>> -           continue calling readdir on that existing DIR* pointer\n>> -           rather than opening a new one.  */\n>> -        int dir_fd;\n>> -        if (continue_readdir)\n>> -          {\n>> -            DIR *dp = FTSENT_DIRP(cur);\n>> -            dir_fd = dirfd (dp);\n>> -            if (dir_fd < 0)\n>> -              {\n>> -                int dirfd_errno = errno;\n>> -                closedir_and_clear (FTSENT_DIRP(cur));\n>> -                if (type == BREAD)\n>> -                  {\n>> -                    cur->fts_info = FTS_DNR;\n>> -                    cur->fts_errno = dirfd_errno;\n>> -                  }\n>> -                return NULL;\n>> -              }\n>> -          }\n>> -        else\n>> -          {\n>> -            /* Open the directory for reading.  If this fails, we're done.\n>> -               If being called from fts_read, set the fts_info field. */\n>> -            if ((FTSENT_DIRP (cur) = fts_opendir(cur->fts_accpath, &dir_fd)) == NULL)\n>> -              {\n>> -                if (type == BREAD)\n>> -                  {\n>> -                    cur->fts_info = FTS_DNR;\n>> -                    cur->fts_errno = errno;\n>> -                  }\n>> -                return NULL;\n>> -              }\n>> -            /* Rather than calling fts_stat for each and every entry encountered\n>> -               in the readdir loop (below), stat each directory only right after\n>> -               opening it.  */\n>> -            bool stat_optimization = cur->fts_info == FTS_NSOK;\n>> -\n>> -            if (stat_optimization\n>> -                /* Also read the stat info again after opening a directory to\n>> -                   reveal eventual changes caused by a submount triggered by\n>> -                   the traversal.  But do it only for utilities which use\n>> -                   FTS_TIGHT_CYCLE_CHECK.  Therefore, only find and du\n>> -                   benefit/suffer from this feature for now.  */\n>> -                || ISSET (FTS_TIGHT_CYCLE_CHECK))\n>> -              {\n>> -                if (!stat_optimization)\n>> -                  LEAVE_DIR (sp, cur, \"4\");\n>> -                if (FSTAT (dir_fd, cur->fts_statp) != 0)\n>> -                  {\n>> -                    int fstat_errno = errno;\n>> -                    closedir_and_clear (FTSENT_DIRP(cur));\n>> -                    if (type == BREAD)\n>> -                      {\n>> -                        cur->fts_errno = fstat_errno;\n>> -                        cur->fts_info = FTS_NS;\n>> -                      }\n>> -                    __set_errno (fstat_errno);\n>> -                    return NULL;\n>> -                  }\n>> -                if (stat_optimization)\n>> -                  cur->fts_info = FTS_D;\n>> -                else if (! enter_dir (sp, cur))\n>> -                  {\n>> -                    int saved_errno = errno;\n>> -                    closedir_and_clear (FTSENT_DIRP(cur));\n>> -                    __set_errno (saved_errno);\n>> -                    return NULL;\n>> -                  }\n>> -              }\n>> -          }\n>> -\n>> -        /* Maximum number of readdir entries to read at one time.  This\n>> -           limitation is to avoid reading millions of entries into memory\n>> -           at once.  When an fts_compar function is specified, we have no\n>> -           choice: we must read all entries into memory before calling that\n>> -           function.  But when no such function is specified, we can read\n>> -           entries in batches that are large enough to help us with inode-\n>> -           sorting, yet not so large that we risk exhausting memory.  */\n>> -        size_t max_entries = sp->fts_compar ? SIZE_MAX : FTS_MAX_READDIR_ENTRIES;\n>> -\n>> -        /*\n>> -         * If we're going to need to stat anything or we want to descend\n>> -         * and stay in the directory, chdir.  If this fails we keep going,\n>> -         * but set a flag so we don't chdir after the post-order visit.\n>> -         * We won't be able to stat anything, but we can still return the\n>> -         * names themselves.  Note, that since fts_read won't be able to\n>> -         * chdir into the directory, it will have to return different file\n>> -         * names than before, i.e. \"a/b\" instead of \"b\".  Since the node\n>> -         * has already been visited in pre-order, have to wait until the\n>> -         * post-order visit to return the error.  There is a special case\n>> -         * here, if there was nothing to stat then it's not an error to\n>> -         * not be able to stat.  This is all fairly nasty.  If a program\n>> -         * needed sorted entries or stat information, they had better be\n>> -         * checking FTS_NS on the returned nodes.\n>> -         */\n>> -        bool descend;\n>> -        if (continue_readdir)\n>> -          {\n>> -            /* When resuming a short readdir run, we already have\n>> -               the required dirp and dir_fd.  */\n>> -            descend = true;\n>> -          }\n>> -        else\n>> -          {\n>> -            /* Try to descend unless it is a names-only fts_children,\n>> -               or the directory is known to lack subdirectories.  */\n>> -            descend = (type != BNAMES\n>> -                       && ! (ISSET (FTS_NOSTAT) && ISSET (FTS_PHYSICAL)\n>> -                             && ! ISSET (FTS_SEEDOT)\n>> -                             && cur->fts_statp->st_nlink == MIN_DIR_NLINK\n>> -                             && (leaf_optimization (cur, dir_fd)\n>> -                                 != NO_LEAF_OPTIMIZATION)));\n>> -            if (descend || type == BREAD)\n>> -              {\n>> -                if (ISSET(FTS_CWDFD))\n>> -                  dir_fd = fcntl (dir_fd, F_DUPFD_CLOEXEC, STDERR_FILENO + 1);\n>> -                if (dir_fd < 0 || fts_safe_changedir(sp, cur, dir_fd, NULL)) {\n>> -                        if (descend && type == BREAD)\n>> -                                cur->fts_errno = errno;\n>> -                        cur->fts_flags |= FTS_DONTCHDIR;\n>> -                        descend = false;\n>> -                        closedir_and_clear(FTSENT_DIRP(cur));\n>> -                        if (ISSET(FTS_CWDFD) && 0 <= dir_fd)\n>> -                                close (dir_fd);\n>> -                        FTSENT_DIRP(cur) = NULL;\n>> -                } else\n>> -                        descend = true;\n>> -              }\n>> -          }\n>> -\n>> -        /*\n>> -         * Figure out the max file name length that can be stored in the\n>> -         * current buffer -- the inner loop allocates more space as necessary.\n>> -         * We really wouldn't have to do the maxlen calculations here, we\n>> -         * could do them in fts_read before returning the name, but it's a\n>> -         * lot easier here since the length is part of the dirent structure.\n>> -         *\n>> -         * If not changing directories set a pointer so that can just append\n>> -         * each new component into the file name.\n>> -         */\n>> -        size_t len = NAPPEND(cur);\n>> -        char *cp;\n>> -        if (ISSET(FTS_NOCHDIR)) {\n>> -                cp = sp->fts_path + len;\n>> -                *cp++ = '/';\n>> -        } else {\n>> -                /* GCC, you're too verbose. */\n>> -                cp = NULL;\n>> -        }\n>> -        len++;\n>> -        size_t maxlen = sp->fts_pathlen - len;\n>> -\n>> -        ptrdiff_t level = cur->fts_level + 1;\n>> -\n>> -        /* Read the directory, attaching each entry to the \"link\" pointer. */\n>> -        bool doadjust = false;\n>> -        register FTSENTRY *head = NULL;\n>> -        FTSENTRY *tail = NULL;\n>> -        register size_t nitems = 0;\n>> -        bool sort_by_inode = false;\n>> -        while (FTSENT_DIRP(cur)) {\n>> -                __set_errno (0);\n>> -                struct dirent *dp = readdir(FTSENT_DIRP(cur));\n>> -                if (dp == NULL) {\n>> -                        /* Some readdir()s do not absorb ENOENT (dir\n>> -                           deleted but open).  This bug was fixed in\n>> -                           glibc 2.3 (2002).  */\n>> -#if ! (2 < __GLIBC__ + (3 <= __GLIBC_MINOR__))\n>> -                        if (errno == ENOENT)\n>> -                          errno = 0;\n>> -#endif\n>> -                        if (errno) {\n>> -                                cur->fts_errno = errno;\n>> -                                /* If we've not read any items yet, treat\n>> -                                   the error as if we can't access the dir.  */\n>> -                                cur->fts_info = (continue_readdir || nitems)\n>> -                                                ? FTS_ERR : FTS_DNR;\n>> -                        }\n>> -                        closedir_and_clear(FTSENT_DIRP(cur));\n>> -                        break;\n>> -                }\n>> -                if (!ISSET(FTS_SEEDOT) && ISDOT(dp->d_name))\n>> -                        continue;\n>> -\n>> -                size_t d_namelen = _D_EXACT_NAMLEN (dp);\n>> -                register FTSENTRY *p = fts_alloc (sp, dp->d_name, d_namelen);\n>> -                if (!p)\n>> -                        goto mem1;\n>> -                if (d_namelen >= maxlen) {\n>> -                        /* include space for NUL */\n>> -                        uintptr_t oldaddr = (uintptr_t) sp->fts_path;\n>> -                        if (! fts_palloc(sp, d_namelen + len + 1)) {\n>> -                                /*\n>> -                                 * No more memory.  Save\n>> -                                 * errno, free up the current structure and the\n>> -                                 * structures already allocated.\n>> -                                 */\n>> -mem1: ;\n>> -                                int saved_errno = errno;\n>> -                                free(FTSENT_WRAPPER(p));\n>> -                                fts_lfree(head);\n>> -                                closedir_and_clear(FTSENT_DIRP(cur));\n>> -                                cur->fts_info = FTS_ERR;\n>> -                                SET(FTS_STOP);\n>> -                                __set_errno (saved_errno);\n>> -                                return (NULL);\n>> -                        }\n>> -                        /* Did realloc() change the pointer? */\n>> -                        if (oldaddr != (uintptr_t) sp->fts_path) {\n>> -                                doadjust = true;\n>> -                                if (ISSET(FTS_NOCHDIR))\n>> -                                        cp = sp->fts_path + len;\n>> -                        }\n>> -                        maxlen = sp->fts_pathlen - len;\n>> -                }\n>> -\n>> -                size_t new_len = len + d_namelen;\n>> -                if (new_len < len) {\n>> -                        /*\n>> -                         * In the unlikely event that we would end up\n>> -                         * with a file name longer than SIZE_MAX, free up\n>> -                         * the current structure and the structures already\n>> -                         * allocated, then error out with ENAMETOOLONG.\n>> -                         */\n>> -                        free(FTSENT_WRAPPER(p));\n>> -                        fts_lfree(head);\n>> -                        closedir_and_clear(FTSENT_DIRP(cur));\n>> -                        cur->fts_info = FTS_ERR;\n>> -                        SET(FTS_STOP);\n>> -                        __set_errno (ENAMETOOLONG);\n>> -                        return (NULL);\n>> -                }\n>> -                p->fts_level = level;\n>> -                p->fts_parent = sp->fts_cur;\n>> -                p->fts_pathlen = new_len;\n>> -\n>> -                /* Store dirent.d_ino, in case we need to sort\n>> -                   entries before processing them.  */\n>> -                p->fts_statp->st_ino = D_INO (dp);\n>> -\n>> -                /* Build a file name for fts_stat to stat. */\n>> -                if (ISSET(FTS_NOCHDIR)) {\n>> -                        p->fts_accpath = p->fts_path;\n>> -                        memmove(cp, p->fts_name, p->fts_namelen + 1);\n>> -                } else\n>> -                        p->fts_accpath = p->fts_name;\n>> -\n>> -                if (sp->fts_compar == NULL || ISSET(FTS_DEFER_STAT)) {\n>> -                        /* Record what fts_read will have to do with this\n>> -                           entry. In many cases, it will simply fts_stat it,\n>> -                           but we can take advantage of any d_type information\n>> -                           to optimize away the unnecessary stat calls.  I.e.,\n>> -                           if FTS_NOSTAT is in effect, we don't need device\n>> -                           numbers unconditionally (FTS_MOUNT) and we're not\n>> -                           following symlinks (FTS_PHYSICAL) and d_type\n>> -                           indicates this is *not* a directory, then we won't\n>> -                           have to stat it  at all.  If it *is* a directory,\n>> -                           then (currently) we stat it regardless, in order to\n>> -                           get device and inode numbers.  Some day we might\n>> -                           optimize that away, too, for directories where\n>> -                           d_ino is known to be valid.  */\n>> -                        bool skip_stat = (ISSET(FTS_NOSTAT)\n>> -                                          && DT_IS_KNOWN(dp)\n>> -                                          && ! DT_MUST_BE(dp, DT_DIR)\n>> -                                          && (ISSET(FTS_PHYSICAL)\n>> -                                              || ! DT_MUST_BE(dp, DT_LNK))\n>> -                                          && ! ISSET(FTS_MOUNT));\n>> -                        p->fts_info = FTS_NSOK;\n>> -                        /* Propagate dirent.d_type information back\n>> -                           to caller, when possible.  */\n>> -                        set_stat_type (p->fts_statp, D_TYPE (dp));\n>> -                        fts_set_stat_required(p, !skip_stat);\n>> -                } else {\n>> -                        p->fts_info = fts_stat(sp, p, false);\n>> -                }\n>> -\n>> -                /* We walk in directory order so \"ls -f\" doesn't get upset. */\n>> -                p->fts_link = NULL;\n>> -                if (head == NULL)\n>> -                        head = tail = p;\n>> -                else {\n>> -                        tail->fts_link = p;\n>> -                        tail = p;\n>> -                }\n>> -\n>> -                /* If there are many entries, no sorting function has been\n>> -                   specified, and this file system is of a type that may be\n>> -                   slow with a large number of entries, arrange to sort the\n>> -                   directory entries on increasing inode numbers.\n>> -\n>> -                   The NITEMS comparison uses ==, not >, because the test\n>> -                   needs to be tried at most once once, and NITEMS will exceed\n>> -                   the threshold after it is incremented below.  */\n>> -                if (nitems == _FTS_INODE_SORT_DIR_ENTRIES_THRESHOLD\n>> -                    && !sp->fts_compar)\n>> -                  sort_by_inode = dirent_inode_sort_may_be_useful (cur, dir_fd);\n>> -\n>> -                ++nitems;\n>> -                if (max_entries <= nitems) {\n>> -                        /* When there are too many dir entries, leave\n>> -                           fts_dirp open, so that a subsequent fts_read\n>> -                           can take up where we leave off.  */\n>> -                        break;\n>> -                }\n>> -        }\n>> -\n>> -        /*\n>> -         * If realloc() changed the address of the file name, adjust the\n>> -         * addresses for the rest of the tree and the dir list.\n>> -         */\n>> -        if (doadjust)\n>> -                fts_padjust(sp, head);\n>> -\n>> -        /*\n>> -         * If not changing directories, reset the file name back to original\n>> -         * state.\n>> -         */\n>> -        if (ISSET(FTS_NOCHDIR)) {\n>> -                if (len == sp->fts_pathlen || nitems == 0)\n>> -                        --cp;\n>> -                *cp = '\\0';\n>> -        }\n>> -\n>> -        /*\n>> -         * If descended after called from fts_children or after called from\n>> -         * fts_read and nothing found, get back.  At the root level we use\n>> -         * the saved fd; if one of fts_open()'s arguments is a relative name\n>> -         * to an empty directory, we wind up here with no other way back.  If\n>> -         * can't get back, we're done.\n>> -         */\n>> -        if (!continue_readdir && descend && (type == BCHILD || !nitems) &&\n>> -            (cur->fts_level == FTS_ROOTLEVEL\n>> -             ? restore_initial_cwd(sp)\n>> -             : fts_safe_changedir(sp, cur->fts_parent, -1, \"..\"))) {\n>> -                cur->fts_info = FTS_ERR;\n>> -                SET(FTS_STOP);\n>> -                fts_lfree(head);\n>> -                return (NULL);\n>> -        }\n>> -\n>> -        /* If didn't find anything, return NULL. */\n>> -        if (!nitems) {\n>> -                if (type == BREAD\n>> -                    && cur->fts_info != FTS_DNR && cur->fts_info != FTS_ERR)\n>> -                        cur->fts_info = FTS_DP;\n>> -                fts_lfree(head);\n>> -                return (NULL);\n>> -        }\n>> -\n>> -        if (sort_by_inode) {\n>> -                sp->fts_compar = FTS_COMPAR_CAST (fts_compare_ino);\n>> -                head = fts_sort (sp, head, nitems);\n>> -                sp->fts_compar = NULL;\n>> -        }\n>> -\n>> -        /* Sort the entries. */\n>> -        if (sp->fts_compar && nitems > 1)\n>> -                head = fts_sort(sp, head, nitems);\n>> -        return (head);\n>> -}\n>> -\n>> -#if GNULIB_FTS_DEBUG\n>> -\n>> -struct devino {\n>> -  intmax_t dev, ino;\n>> -};\n>> -#define PRINT_DEVINO \"(%jd,%jd)\"\n>> -\n>> -static struct devino\n>> -getdevino (int fd)\n>> -{\n>> -  struct STRUCT_STAT st;\n>> -  return (fd == AT_FDCWD\n>> -          ? (struct devino) { -1, 0 }\n>> -          : FSTAT (fd, &st) == 0\n>> -          ? (struct devino) { st.st_dev, st.st_ino }\n>> -          : (struct devino) { -1, errno });\n>> -}\n>> -\n>> -/* Walk ->fts_parent links starting at E_CURR, until the root of the\n>> -   current hierarchy.  There should be a directory with dev/inode\n>> -   matching those of AD.  If not, print a lot of diagnostics.  */\n>> -static void\n>> -find_matching_ancestor (FTSENTRY const *e_curr, struct Active_dir const *ad)\n>> -{\n>> -  for (FTSENTRY const *ent = e_curr;\n>> -       ent->fts_level >= FTS_ROOTLEVEL;\n>> -       ent = ent->fts_parent)\n>> -    {\n>> -      if (ad->ino == ent->fts_statp->st_ino\n>> -          && ad->dev == ent->fts_statp->st_dev)\n>> -        return;\n>> -    }\n>> -  printf (\"ERROR: tree dir, %s, not active\\n\", ad->fts_ent->fts_accpath);\n>> -  printf (\"active dirs:\\n\");\n>> -  for (FTSENTRY const *ent = e_curr;\n>> -       ent->fts_level >= FTS_ROOTLEVEL;\n>> -       ent = ent->fts_parent)\n>> -    printf (\"  %s(%\"PRIuMAX\"/%\"PRIuMAX\") to %s(%\"PRIuMAX\"/%\"PRIuMAX\")...\\n\",\n>> -            ad->fts_ent->fts_accpath,\n>> -            (uintmax_t) ad->dev,\n>> -            (uintmax_t) ad->ino,\n>> -            ent->fts_accpath,\n>> -            (uintmax_t) ent->fts_statp->st_dev,\n>> -            (uintmax_t) ent->fts_statp->st_ino);\n>> -}\n>> -\n>> -void\n>> -fts_cross_check (FTSOBJ const *sp)\n>> -{\n>> -  if ( ! ISSET (FTS_TIGHT_CYCLE_CHECK))\n>> -    return;\n>> -\n>> -  FTSENTRY const *ent = sp->fts_cur;\n>> -\n>> -  Dprintf ((\"fts-cross-check cur=%s\\n\", ent->fts_path));\n>> -  /* Make sure every parent dir is in the tree.  */\n>> -  for (FTSENTRY const *t = ent->fts_parent;\n>> -       t->fts_level >= FTS_ROOTLEVEL;\n>> -       t = t->fts_parent)\n>> -    {\n>> -      struct Active_dir ad;\n>> -      ad.ino = t->fts_statp->st_ino;\n>> -      ad.dev = t->fts_statp->st_dev;\n>> -      if ( ! hash_lookup (sp->fts_cycle.ht, &ad))\n>> -        printf (\"ERROR: active dir, %s, not in tree\\n\", t->fts_path);\n>> -    }\n>> -\n>> -  /* Make sure every dir in the tree is an active dir.\n>> -     But ENT is not necessarily a directory.  If so, just skip this part. */\n>> -  if (ent->fts_parent->fts_level >= FTS_ROOTLEVEL\n>> -      && (ent->fts_info == FTS_DP\n>> -          || ent->fts_info == FTS_D))\n>> -    for (struct Active_dir *ad = hash_get_first (sp->fts_cycle.ht);\n>> -         ad != NULL;\n>> -         ad = hash_get_next (sp->fts_cycle.ht, ad))\n>> -      {\n>> -        find_matching_ancestor (ent, ad);\n>> -      }\n>> -}\n>> -\n>> -static bool\n>> -same_fd (int fd1, int fd2)\n>> -{\n>> -  struct STRUCT_STAT sb1, sb2;\n>> -  return (FSTAT (fd1, &sb1) == 0\n>> -          && FSTAT (fd2, &sb2) == 0\n>> -          && psame_inode (&sb1, &sb2));\n>> -}\n>> -\n>> -static void\n>> -fd_ring_print (FTSOBJ const *sp, FILE *stream, char const *msg)\n>> -{\n>> -  if (!fts_debug)\n>> -    return;\n>> -  I_ring const *fd_ring = &sp->fts_fd_ring;\n>> -  struct devino cwd = getdevino (sp->fts_cwd_fd);\n>> -  fprintf (stream, \"=== %s ========== \"PRINT_DEVINO\"\\n\", msg, cwd.dev, cwd.ino);\n>> -  if (i_ring_empty (fd_ring))\n>> -    return;\n>> -\n>> -  unsigned int i = fd_ring->ir_front;\n>> -  while (true)\n>> -    {\n>> -      int fd = fd_ring->ir_data[i];\n>> -      if (fd < 0)\n>> -        fprintf (stream, \"%u: %d:\\n\", i, fd);\n>> -      else\n>> -        {\n>> -          struct devino wd = getdevino (fd);\n>> -          fprintf (stream, \"%u: %d: \"PRINT_DEVINO\"\\n\", i, fd, wd.dev, wd.ino);\n>> -        }\n>> -      if (i == fd_ring->ir_back)\n>> -        break;\n>> -      i = (i + I_RING_SIZE - 1) % I_RING_SIZE;\n>> -    }\n>> -}\n>> -\n>> -/* Ensure that each file descriptor on the fd_ring matches a\n>> -   parent, grandparent, etc. of the current working directory.  */\n>> -static void\n>> -fd_ring_check (FTSOBJ const *sp)\n>> -{\n>> -  if (!fts_debug)\n>> -    return;\n>> -\n>> -  /* Make a writable copy.  */\n>> -  I_ring fd_w = sp->fts_fd_ring;\n>> -\n>> -  int cwd_fd = sp->fts_cwd_fd;\n>> -  cwd_fd = fcntl (cwd_fd, F_DUPFD_CLOEXEC, STDERR_FILENO + 1);\n>> -  struct devino dot = getdevino (cwd_fd);\n>> -  fprintf (stderr, \"===== check ===== cwd: \"PRINT_DEVINO\"\\n\",\n>> -           dot.dev, dot.ino);\n>> -  while ( ! i_ring_empty (&fd_w))\n>> -    {\n>> -      int fd = i_ring_pop (&fd_w);\n>> -      if (0 <= fd)\n>> -        {\n>> -          int open_flags = O_SEARCH | O_CLOEXEC;\n>> -          int parent_fd = openat (cwd_fd, \"..\", open_flags);\n>> -          if (parent_fd < 0)\n>> -            {\n>> -              // Warn?\n>> -              break;\n>> -            }\n>> -          if (!same_fd (fd, parent_fd))\n>> -            {\n>> -              struct devino cwd = getdevino (fd);\n>> -              fprintf (stderr, \"ring  : \"PRINT_DEVINO\"\\n\", cwd.dev, cwd.ino);\n>> -              struct devino c2 = getdevino (parent_fd);\n>> -              fprintf (stderr, \"parent: \"PRINT_DEVINO\"\\n\", c2.dev, c2.ino);\n>> -              fts_assert (0);\n>> -            }\n>> -          close (cwd_fd);\n>> -          cwd_fd = parent_fd;\n>> -        }\n>> -    }\n>> -  close (cwd_fd);\n>> -}\n>> -#endif\n>> -\n>> -static unsigned short int\n>> -internal_function\n>> -fts_stat(FTSOBJ *sp, register FTSENTRY *p, bool follow)\n>> -{\n>> -        if (ISSET (FTS_LOGICAL)\n>> -            || (ISSET (FTS_COMFOLLOW) && p->fts_level == FTS_ROOTLEVEL))\n>> -                follow = true;\n>> -\n>> -        struct STRUCT_STAT *sbp = p->fts_statp;\n>> -\n>> -        /*\n>> -         * If doing a logical walk, or application requested FTS_FOLLOW, do\n>> -         * a stat(2).  If that fails, check for a nonexistent symlink.  If\n>> -         * fail, set the errno from the stat call.\n>> -         */\n>> -        int flags = follow ? 0 : AT_SYMLINK_NOFOLLOW;\n>> -        if (FSTATAT (sp->fts_cwd_fd, p->fts_accpath, sbp, flags) < 0)\n>> -          {\n>> -            if (follow && errno == ENOENT\n>> -                && 0 <= FSTATAT (sp->fts_cwd_fd, p->fts_accpath, sbp,\n>> -                                 AT_SYMLINK_NOFOLLOW))\n>> -              {\n>> -                __set_errno (0);\n>> -                return FTS_SLNONE;\n>> -              }\n>> -\n>> -            p->fts_errno = errno;\n>> -           memset (sbp, 0, sizeof *sbp);\n>> -            return FTS_NS;\n>> -          }\n>> -\n>> -        if (S_ISDIR(sbp->st_mode)) {\n>> -                if (ISDOT(p->fts_name)) {\n>> -                        /* Command-line \".\" and \"..\" are real directories. */\n>> -                        return (p->fts_level == FTS_ROOTLEVEL ? FTS_D : FTS_DOT);\n>> -                }\n>> -\n>> -                return (FTS_D);\n>> -        }\n>> -        if (S_ISLNK(sbp->st_mode))\n>> -                return (FTS_SL);\n>> -        if (S_ISREG(sbp->st_mode))\n>> -                return (FTS_F);\n>> -        return (FTS_DEFAULT);\n>> -}\n>> -\n>> -static int\n>> -fts_compar (void const *a, void const *b)\n>> -{\n>> -  /* Convert A and B to the correct types, to pacify the compiler, and\n>> -     for portability to bizarre hosts where \"void const *\" and \"FTSENT\n>> -     const **\" differ in runtime representation.  The comparison\n>> -     function cannot modify *a and *b, but there is no compile-time\n>> -     check for this.  */\n>> -  FTSENTRY const **pa = (FTSENTRY const **) a;\n>> -  FTSENTRY const **pb = (FTSENTRY const **) b;\n>> -  return FTSENT_FTS(pa[0])->fts_compar (pa, pb);\n>> -}\n>> -\n>> -static FTSENTRY *\n>> -internal_function\n>> -fts_sort (FTSOBJ *sp, FTSENTRY *head, register size_t nitems)\n>> -{\n>> -        register FTSENTRY **ap, *p;\n>> -\n>> -        /* On most modern hosts, void * and FTSENT ** have the same\n>> -           run-time representation, and one can convert sp->fts_compar to\n>> -           the type qsort expects without problem.  Use the heuristic that\n>> -           this is OK if the two pointer types are the same size, and if\n>> -           converting FTSENT ** to uintptr_t is the same as converting\n>> -           FTSENT ** to void * and then to uintptr_t.  This heuristic isn't\n>> -           valid in general but we don't know of any counterexamples.  */\n>> -        FTSENTRY *dummy;\n>> -        int (*compare) (void const *, void const *) =\n>> -          ((sizeof &dummy == sizeof (void *)\n>> -            && (uintptr_t) &dummy == (uintptr_t) (void *) &dummy)\n>> -           ? (int (*) (void const *, void const *)) sp->fts_compar\n>> -           : fts_compar);\n>> -\n>> -        /*\n>> -         * Construct an array of pointers to the structures and call qsort(3).\n>> -         * Reassemble the array in the order returned by qsort.  If unable to\n>> -         * sort for memory reasons, return the directory entries in their\n>> -         * current order.  Allocate enough space for the current needs plus\n>> -         * 40 so don't realloc one entry at a time.\n>> -         */\n>> -        if (nitems > sp->fts_nitems) {\n>> -                sp->fts_nitems = nitems + 40;\n>> -                FTSENTRY **a;\n>> -                if (! (a = reallocarray (sp->fts_array,\n>> -                                         sp->fts_nitems, sizeof *a))) {\n>> -                        free(sp->fts_array);\n>> -                        sp->fts_array = NULL;\n>> -                        sp->fts_nitems = 0;\n>> -                        return (head);\n>> -                }\n>> -                sp->fts_array = a;\n>> -        }\n>> -        for (ap = sp->fts_array, p = head; p; p = p->fts_link)\n>> -                *ap++ = p;\n>> -        qsort((void *)sp->fts_array, nitems, sizeof(FTSENTRY *), compare);\n>> -        for (head = *(ap = sp->fts_array); --nitems; ++ap)\n>> -                ap[0]->fts_link = ap[1];\n>> -        ap[0]->fts_link = NULL;\n>> -        return (head);\n>> -}\n>> -\n>> -static FTSENTRY *\n>> -internal_function\n>> -fts_alloc (FTSOBJ *sp, const char *name, register size_t namelen)\n>> -{\n>> -        /*\n>> -         * The file name is a variable length array.  Allocate the FTSENT\n>> -         * structure and the file name in one chunk.\n>> -         */\n>> -        size_t len = FLEXSIZEOF(FTSENT, fts_name, namelen + 1);\n>> -\tregister FTSENTRY *p;\n>> -#if !_LIBC\n>> -        p = malloc(len);\n>> -        if (p == NULL)\n>> -                return (NULL);\n>> -#else\n>> -\t/*\n>> -\t * For glibc, we use a wrapper struct to provide the extra required\n>> -\t * fields without changing the FSENT layout.\n>> -\t */\n>> -\tlen += sizeof (struct FTSENT_wrapper);\n>> -\tstruct FTSENT_wrapper *wrapper = malloc(len);\n>> -\tif (wrapper == NULL)\n>> -\t\treturn (NULL);\n>> -\tp = &wrapper->ent;\n>> -        p->fts_statp = &wrapper->fts_stat;\n>> -#endif\n>> -\n>> -        /* Copy the name and guarantee NUL termination. */\n>> -        memcpy(p->fts_name, name, namelen);\n>> -        p->fts_name[namelen] = '\\0';\n>> -\n>> -        p->fts_namelen = namelen;\n>> -        FTSENT_FTS(p)= sp;\n>> -        p->fts_path = sp->fts_path;\n>> -        p->fts_errno = 0;\n>> -        FTSENT_DIRP(p) = NULL;\n>> -        p->fts_flags = 0;\n>> -        p->fts_instr = FTS_NOINSTR;\n>> -        p->fts_number = 0;\n>> -        p->fts_pointer = NULL;\n>> -        return (p);\n>> -}\n>> -\n>> -static void\n>> -internal_function\n>> -fts_lfree (register FTSENTRY *head)\n>> -{\n>> -        int saved_errno = errno;\n>> -\n>> -        /* Free a linked list of structures. */\n>> -        register FTSENTRY *p;\n>> -        while ((p = head)) {\n>> -                head = head->fts_link;\n>> -                if (FTSENT_DIRP(p))\n>> -                        closedir (FTSENT_DIRP(p));\n>> -                free(FTSENT_WRAPPER(p));\n>> -        }\n>> -\n>> -        __set_errno (saved_errno);\n>> -}\n>> -\n>> -/*\n>> - * Allow essentially unlimited file name lengths; find, rm, ls should\n>> - * all work on any tree.  Most systems will allow creation of file\n>> - * names much longer than MAXPATHLEN, even though the kernel won't\n>> - * resolve them.  Add the size (not just what's needed) plus 256 bytes\n>> - * so don't realloc the file name 2 bytes at a time.\n>> - */\n>> -static bool\n>> -internal_function\n>> -fts_palloc (FTSOBJ *sp, size_t more)\n>> -{\n>> -        size_t new_len = sp->fts_pathlen + more + 256;\n>> -\n>> -        /*\n>> -         * See if fts_pathlen would overflow.\n>> -         */\n>> -        if (new_len < sp->fts_pathlen) {\n>> -                free(sp->fts_path);\n>> -                sp->fts_path = NULL;\n>> -                __set_errno (ENAMETOOLONG);\n>> -                return false;\n>> -        }\n>> -        sp->fts_pathlen = new_len;\n>> -        char *p = realloc(sp->fts_path, sp->fts_pathlen);\n>> -        if (p == NULL) {\n>> -                free(sp->fts_path);\n>> -                sp->fts_path = NULL;\n>> -                return false;\n>> -        }\n>> -        sp->fts_path = p;\n>> -        return true;\n>> -}\n>> -\n>> -/*\n>> - * When the file name is realloc'd, have to fix all of the pointers in\n>> - *  structures already returned.\n>> - */\n>> -static void\n>> -internal_function\n>> -fts_padjust (FTSOBJ *sp, FTSENTRY *head)\n>> -{\n>> -        char *addr = sp->fts_path;\n>> -\n>> -        /* This code looks at bit-patterns of freed pointers to\n>> -           relocate them, so it relies on undefined behavior.  If this\n>> -           trick does not work on your platform, please report a bug.  */\n>> -\n>> -#define ADJUST(p) do {                                                  \\\n>> -        uintptr_t old_accpath = (uintptr_t) (p)->fts_accpath;           \\\n>> -        if (old_accpath != (uintptr_t) (p)->fts_name) {                 \\\n>> -                (p)->fts_accpath =                                      \\\n>> -                  addr + (old_accpath - (uintptr_t) (p)->fts_path);     \\\n>> -        }                                                               \\\n>> -        (p)->fts_path = addr;                                           \\\n>> -} while (0)\n>> -        /* Adjust the current set of children. */\n>> -        for (FTSENTRY *p = sp->fts_child; p; p = p->fts_link)\n>> -                ADJUST(p);\n>> -\n>> -        /* Adjust the rest of the tree, including the current level. */\n>> -        for (FTSENTRY *p = head; p->fts_level >= FTS_ROOTLEVEL;) {\n>> -                ADJUST(p);\n>> -                p = p->fts_link ? p->fts_link : p->fts_parent;\n>> -        }\n>> -}\n>> -\n>> -static size_t\n>> -internal_function _GL_ATTRIBUTE_PURE\n>> -fts_maxarglen (char * const *argv)\n>> -{\n>> -        size_t max;\n>> -\n>> -        for (max = 0; *argv; ++argv) {\n>> -                size_t len = strlen(*argv);\n>> -                if (len > max)\n>> -                        max = len;\n>> -        }\n>> -        return (max + 1);\n>> -}\n>> -\n>> -/*\n>> - * Change to dir specified by fd or file name without getting\n>> - * tricked by someone changing the world out from underneath us.\n>> - * Assumes p->fts_statp->st_dev and p->fts_statp->st_ino are filled in.\n>> - * If FD is non-negative, expect it to be used after this function returns,\n>> - * and to be closed eventually.  So don't pass e.g., 'dirfd(dirp)' and then\n>> - * do closedir(dirp), because that would invalidate the saved FD.\n>> - * Upon failure, close FD immediately and return nonzero.\n>> - */\n>> -static int\n>> -internal_function\n>> -fts_safe_changedir (FTSOBJ *sp, FTSENTRY *p, int fd, char const *dir)\n>> -{\n>> -        fts_assert (0 <= fd || dir != NULL);\n>> -        bool is_dotdot = dir && streq (dir, \"..\");\n>> -\n>> -        /* This clause handles the unusual case in which FTS_NOCHDIR\n>> -           is specified, along with FTS_CWDFD.  In that case, there is\n>> -           no need to change even the virtual cwd file descriptor.\n>> -           However, if FD is non-negative, we do close it here.  */\n>> -        if (ISSET (FTS_NOCHDIR))\n>> -          {\n>> -            if (ISSET (FTS_CWDFD) && 0 <= fd)\n>> -              close (fd);\n>> -            return 0;\n>> -          }\n>> -\n>> -        if (fd < 0 && is_dotdot && ISSET (FTS_CWDFD))\n>> -          {\n>> -            /* When possible, skip the diropen and subsequent fstat+dev/ino\n>> -               comparison.  I.e., when changing to parent directory\n>> -               (chdir (\"..\")), use a file descriptor from the ring and\n>> -               save the overhead of diropen+fstat, as well as avoiding\n>> -               failure when we lack \"x\" access to the virtual cwd.  */\n>> -            if ( ! i_ring_empty (&sp->fts_fd_ring))\n>> -              {\n>> -                int parent_fd;\n>> -                fd_ring_print (sp, stderr, \"pre-pop\");\n>> -                parent_fd = i_ring_pop (&sp->fts_fd_ring);\n>> -                if (0 <= parent_fd)\n>> -                  {\n>> -                    fd = parent_fd;\n>> -                    dir = NULL;\n>> -                  }\n>> -              }\n>> -          }\n>> -\n>> -        int newfd = fd;\n>> -        if (fd < 0 && (newfd = diropen (sp, dir)) < 0)\n>> -          return -1;\n>> -\n>> -        /* The following dev/inode check is necessary if we're doing a\n>> -           \"logical\" traversal (through symlinks, a la chown -L), if the\n>> -           system lacks O_NOFOLLOW support, or if we're changing to \"..\"\n>> -           (but not via a popped file descriptor).  When changing to the\n>> -           name \"..\", O_NOFOLLOW can't help.  In general, when the target is\n>> -           not \"..\", diropen's use of O_NOFOLLOW ensures we don't mistakenly\n>> -           follow a symlink, so we can avoid the expense of this fstat.  */\n>> -        int ret;\n>> -        if (ISSET(FTS_LOGICAL) || ! HAVE_WORKING_O_NOFOLLOW\n>> -            || (dir && streq (dir, \"..\")))\n>> -          {\n>> -            struct STRUCT_STAT sb;\n>> -            if (FSTAT (newfd, &sb))\n>> -              {\n>> -                ret = -1;\n>> -                goto bail;\n>> -              }\n>> -            if (p->fts_statp->st_dev != sb.st_dev\n>> -                || p->fts_statp->st_ino != sb.st_ino)\n>> -              {\n>> -                __set_errno (ENOENT);           /* disinformation */\n>> -                ret = -1;\n>> -                goto bail;\n>> -              }\n>> -          }\n>> -\n>> -        if (ISSET(FTS_CWDFD))\n>> -          {\n>> -            cwd_advance_fd (sp, newfd, ! is_dotdot);\n>> -            return 0;\n>> -          }\n>> -\n>> -        ret = fchdir(newfd);\n>> -bail:\n>> -        if (fd < 0)\n>> -          {\n>> -            int oerrno = errno;\n>> -            (void)close(newfd);\n>> -            __set_errno (oerrno);\n>> -          }\n>> -        return ret;\n>> -}\n>> diff --git a/io/fts64-time64.c b/io/fts64-time64.c\n>> index 6a1053194e..29f2dbc28c 100644\n>> --- a/io/fts64-time64.c\n>> +++ b/io/fts64-time64.c\n>> @@ -33,5 +33,5 @@\n>>   # define STRUCT_STATFS      statfs64\n>>   # define FSTATFS            __fstatfs64\n>>   \n>> -# include \"fts.c\"\n>> +# include \"fts-common.c\"\n>>   #endif\n>> diff --git a/io/fts64.c b/io/fts64.c\n>> index a6f607a873..1efa06ab3b 100644\n>> --- a/io/fts64.c\n>> +++ b/io/fts64.c\n>> @@ -16,11 +16,11 @@\n>>      License along with the GNU C Library; if not, see\n>>      <https://www.gnu.org/licenses/>.  */\n>>   \n>> -#define FTS_OPEN fts64_open\n>> -#define FTS_CLOSE fts64_close\n>> -#define FTS_READ fts64_read\n>> -#define FTS_SET fts64_set\n>> -#define FTS_CHILDREN fts64_children\n>> +#define FTS_OPEN __fts64_open\n>> +#define FTS_CLOSE __fts64_close\n>> +#define FTS_READ __fts64_read\n>> +#define FTS_SET __fts64_set\n>> +#define FTS_CHILDREN __fts64_children\n>>   #define FTSOBJ FTS64\n>>   #define FTSENTRY FTSENT64\n>>   #define INO_T ino64_t\n>> @@ -30,4 +30,30 @@\n>>   #define STRUCT_STATFS statfs64\n>>   #define FSTATFS __fstatfs64\n>>   \n>> -#include \"fts.c\"\n>> +#define fts_open __rename_fts_open\n>> +#define fts_close __rename_fts_close\n>> +#define fts_read __rename_fts_read\n>> +#define fts_set __rename_fts_set\n>> +#define fts_children __rename_fts_children\n>> +\n>> +#include \"fts-common.c\"\n>> +\n>> +#undef fts_open\n>> +#undef fts_close\n>> +#undef fts_read\n>> +#undef fts_set\n>> +#undef fts_children\n>> +\n>> +weak_alias (__fts64_open, fts64_open)\n>> +weak_alias (__fts64_close, fts64_close)\n>> +weak_alias (__fts64_read, fts64_read)\n>> +weak_alias (__fts64_set, fts64_set)\n>> +weak_alias (__fts64_children, fts64_children)\n>> +\n>> +#ifdef __OFF_T_MATCHES_OFF64_T\n>> +weak_alias (__fts64_open, fts_open)\n>> +weak_alias (__fts64_close, fts_close)\n>> +weak_alias (__fts64_read, fts_read)\n>> +weak_alias (__fts64_set, fts_set)\n>> +weak_alias (__fts64_children, fts_children)\n>> +#endif\n>> diff --git a/sysdeps/unix/sysv/linux/mips/mips64/n64/fts.c b/sysdeps/unix/sysv/linux/mips/mips64/n64/fts.c\n>> deleted file mode 100644\n>> index d0c62e6195..0000000000\n>> --- a/sysdeps/unix/sysv/linux/mips/mips64/n64/fts.c\n>> +++ /dev/null\n>> @@ -1 +0,0 @@\n>> -#include <io/fts.c>\n>> diff --git a/sysdeps/unix/sysv/linux/mips/mips64/n64/fts64.c b/sysdeps/unix/sysv/linux/mips/mips64/n64/fts64.c\n>> deleted file mode 100644\n>> index 2472f8bf75..0000000000\n>> --- a/sysdeps/unix/sysv/linux/mips/mips64/n64/fts64.c\n>> +++ /dev/null\n>> @@ -1 +0,0 @@\n>> -#include <io/fts64.c>\n>> diff --git a/sysdeps/unix/sysv/linux/x86_64/x32/fts.c b/sysdeps/unix/sysv/linux/x86_64/x32/fts.c\n>> deleted file mode 100644\n>> index 980573ed68..0000000000\n>> --- a/sysdeps/unix/sysv/linux/x86_64/x32/fts.c\n>> +++ /dev/null\n>> @@ -1 +0,0 @@\n>> -#include <sysdeps/wordsize-64/fts.c>\n>> diff --git a/sysdeps/unix/sysv/linux/x86_64/x32/fts64.c b/sysdeps/unix/sysv/linux/x86_64/x32/fts64.c\n>> deleted file mode 100644\n>> index 221d1b5608..0000000000\n>> --- a/sysdeps/unix/sysv/linux/x86_64/x32/fts64.c\n>> +++ /dev/null\n>> @@ -1 +0,0 @@\n>> -#include <sysdeps/wordsize-64/fts64.c>\n>> diff --git a/sysdeps/wordsize-64/fts.c b/sysdeps/wordsize-64/fts.c\n>> deleted file mode 100644\n>> index 159dc1febe..0000000000\n>> --- a/sysdeps/wordsize-64/fts.c\n>> +++ /dev/null\n>> @@ -1,19 +0,0 @@\n>> -#define fts64_open __rename_fts64_open\n>> -#define fts64_close __rename_fts64_close\n>> -#define fts64_read __rename_fts64_read\n>> -#define fts64_set __rename_fts64_set\n>> -#define fts64_children __rename_fts64_children\n>> -\n>> -#include \"../../io/fts.c\"\n>> -\n>> -#undef fts64_open\n>> -#undef fts64_close\n>> -#undef fts64_read\n>> -#undef fts64_set\n>> -#undef fts64_children\n>> -\n>> -weak_alias (fts_open, fts64_open)\n>> -weak_alias (fts_close, fts64_close)\n>> -weak_alias (fts_read, fts64_read)\n>> -weak_alias (fts_set, fts64_set)\n>> -weak_alias (fts_children, fts64_children)\n>> diff --git a/sysdeps/wordsize-64/fts64.c b/sysdeps/wordsize-64/fts64.c\n>> deleted file mode 100644\n>> index f2848fc3e4..0000000000\n>> --- a/sysdeps/wordsize-64/fts64.c\n>> +++ /dev/null\n>> @@ -1 +0,0 @@\n>> -/* Defined in fts.c.  */\n>","headers":{"Return-Path":"<libc-alpha-bounces~incoming=patchwork.ozlabs.org@sourceware.org>","X-Original-To":["incoming@patchwork.ozlabs.org","libc-alpha@sourceware.org"],"Delivered-To":["patchwork-incoming@legolas.ozlabs.org","libc-alpha@sourceware.org"],"Authentication-Results":["legolas.ozlabs.org;\n\tdkim=pass (1024-bit key;\n unprotected) header.d=redhat.com header.i=@redhat.com header.a=rsa-sha256\n header.s=mimecast20190719 header.b=ZdMtNZKB;\n\tdkim-atps=neutral","legolas.ozlabs.org;\n spf=pass (sender SPF authorized) smtp.mailfrom=sourceware.org\n (client-ip=38.145.34.32; helo=vm01.sourceware.org;\n envelope-from=libc-alpha-bounces~incoming=patchwork.ozlabs.org@sourceware.org;\n receiver=patchwork.ozlabs.org)","sourceware.org;\n\tdkim=pass (1024-bit key,\n unprotected) header.d=redhat.com header.i=@redhat.com header.a=rsa-sha256\n header.s=mimecast20190719 header.b=ZdMtNZKB","sourceware.org; dmarc=pass (p=quarantine dis=none)\n header.from=redhat.com","sourceware.org; spf=pass smtp.mailfrom=redhat.com","server2.sourceware.org;\n arc=none smtp.remote-ip=170.10.129.124"],"Received":["from vm01.sourceware.org (vm01.sourceware.org [38.145.34.32])\n\t(using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits)\n\t key-exchange x25519 server-signature ECDSA (secp384r1) server-digest SHA384)\n\t(No client certificate requested)\n\tby legolas.ozlabs.org (Postfix) with ESMTPS id 4fy5bb3Yg5z1yD3\n\tfor <incoming@patchwork.ozlabs.org>; Sat, 18 Apr 2026 06:10:35 +1000 (AEST)","from vm01.sourceware.org (localhost [127.0.0.1])\n\tby sourceware.org (Postfix) with ESMTP id 6B6144D108F0\n\tfor <incoming@patchwork.ozlabs.org>; Fri, 17 Apr 2026 20:10:33 +0000 (GMT)","from us-smtp-delivery-124.mimecast.com\n (us-smtp-delivery-124.mimecast.com [170.10.129.124])\n by sourceware.org (Postfix) with ESMTP id 5B2704CD2002\n for <libc-alpha@sourceware.org>; Fri, 17 Apr 2026 20:09:54 +0000 (GMT)","from mail-qt1-f198.google.com (mail-qt1-f198.google.com\n [209.85.160.198]) by relay.mimecast.com with ESMTP with STARTTLS\n (version=TLSv1.3, cipher=TLS_AES_256_GCM_SHA384) id\n us-mta-147-6Ug26ok0PWqSuihHKeiqOw-1; Fri, 17 Apr 2026 16:09:52 -0400","by mail-qt1-f198.google.com with SMTP id\n d75a77b69052e-50b44f7b7bbso26431941cf.3\n for <libc-alpha@sourceware.org>; Fri, 17 Apr 2026 13:09:52 -0700 (PDT)","from [192.168.0.116] ([198.48.244.52])\n by smtp.gmail.com with ESMTPSA id\n d75a77b69052e-50e3944a7a3sm19462151cf.22.2026.04.17.13.09.45\n (version=TLS1_3 cipher=TLS_AES_128_GCM_SHA256 bits=128/128);\n Fri, 17 Apr 2026 13:09:46 -0700 (PDT)"],"DKIM-Filter":["OpenDKIM Filter v2.11.0 sourceware.org 6B6144D108F0","OpenDKIM Filter v2.11.0 sourceware.org 5B2704CD2002"],"DMARC-Filter":"OpenDMARC Filter v1.4.2 sourceware.org 5B2704CD2002","ARC-Filter":"OpenARC Filter v1.0.0 sourceware.org 5B2704CD2002","ARC-Seal":"i=1; a=rsa-sha256; d=sourceware.org; s=key; t=1776456594; cv=none;\n b=Iqp8fyDmFyeb36A8pHNfjREXcKGv+ZYJtlNHNhdokgqA1fjZ5gzY+2Is13+DqPMTIF5IXfykk5M7dyqIjqkNUOK4KYyLxm5rUrVJMZhRFu9GdJ1vFpqADBRLOtO4Uk1sg/rsYA025OztBxgYCfH6NfVYE84pzaJNjZiLf3326Ug=","ARC-Message-Signature":"i=1; a=rsa-sha256; d=sourceware.org; s=key;\n t=1776456594; c=relaxed/simple;\n bh=qVobl9QguNaDnRPTr49pneu7VSpluBa9F5LeT5ubiuU=;\n h=DKIM-Signature:Message-ID:Date:MIME-Version:Subject:To:From;\n b=gjMMPkkcwW+jmtLye4TvfCXX7ZCWhYt2PueQ0fbLXDpS3Oyd9mGWetJH5Fub3euGsRbQOiZi93aXhvhuuve7fhjSG4sp6NQ3woeyM27SdpvpubQhfE5eng2SUqU3MEVohQkD18tNTha+S/Semza1WIsyiBPHaPsxpNIKlk+/Yx0=","ARC-Authentication-Results":"i=1; server2.sourceware.org","DKIM-Signature":"v=1; a=rsa-sha256; c=relaxed/relaxed; d=redhat.com;\n s=mimecast20190719; t=1776456594;\n h=from:from:reply-to:subject:subject:date:date:message-id:message-id:\n to:to:cc:cc:mime-version:mime-version:content-type:content-type:\n content-transfer-encoding:content-transfer-encoding:\n in-reply-to:in-reply-to:references:references;\n bh=EYJePVZdV7+tmu4sL9LOtX2OZo1M1NIHG2UvtyzVIdE=;\n b=ZdMtNZKBOT9ZKbNtl+TxdvPgULm7JHvynwKhcvhaOESqkDhXj8T5wmlSVlyu6kDaXMJGgo\n nZThSDpiEYK5XT5lGZNk2w2igNhVgkuFb2TGMpl3Tm+n3l4cwR5i18zGaxN+q+mkfuDmdq\n kXs+J3EisW2wYvhiRhg4u6wJDB4iRgY=","X-MC-Unique":"6Ug26ok0PWqSuihHKeiqOw-1","X-Mimecast-MFC-AGG-ID":"6Ug26ok0PWqSuihHKeiqOw_1776456591","X-Google-DKIM-Signature":"v=1; a=rsa-sha256; c=relaxed/relaxed;\n d=1e100.net; s=20251104; t=1776456591; x=1777061391;\n h=content-transfer-encoding:in-reply-to:organization:from\n :content-language:references:cc:to:subject:user-agent:mime-version\n :date:message-id:x-gm-gg:x-gm-message-state:from:to:cc:subject:date\n :message-id:reply-to;\n bh=EYJePVZdV7+tmu4sL9LOtX2OZo1M1NIHG2UvtyzVIdE=;\n b=L+zwoYhB7bHla9iXu48jp3sc0cQGsHb32vgYRxCHUXM0Way8JjaRAZi7guDASU2lrD\n WH3DudhrELBHVWbRI72ZR6FbGEOtY5UYMBKxOidatyUnYohd3mOdhb8uyXh2IfsPf/4C\n hMbSjQf3CMWLt2uSmTBT4ouTkbOTsyWQs3cU4tGpQ0WnWP+9DzP1FezaUwBqa+axzuc9\n RScRn2JNeaHqMOub7xqK5Y/0Pi+hrlr70rfuV2QxgOcTzogU77bpWKp9mBKRWN3xfmLs\n wACoEcHuBXoOGuD16fiGIGgML6TRgw6VOPZhZXWeh9pYoAMxekxV0fyJkCTZSs13kEDb\n TbQQ==","X-Forwarded-Encrypted":"i=1;\n AFNElJ/vBwh93ddYORnLZQxt85S6QzcCammdK73qiKuXmq5qLqPL0Wk3iMKb5r4GHjuKsDpCfNkPGxy0cl+h@sourceware.org","X-Gm-Message-State":"AOJu0YxrMDrVDtOb+S+Zb0RJYqNMZ+3JPHEwVnmyqmeSAPckoYp+cZyB\n cvxaZjyEWhJTmIV+nYpb7SArZ7iJ+/LNVNNPqPFcuMBHRwnz2a0galMDg9VSyJ2Pkk/s7K5HGf+\n RLV14te5HpO/i2loxuab21w4Kmkso6m9npBleK0qd26kMaUyXZPfEmIsKlQIGWg==","X-Gm-Gg":"AeBDiesDDEcG9xLfgx6FHC9GBTI4PUaVbq5uiMkEHeYK3YiahiUduqxslwoEU97oDSg\n UfD+QRL6pRSZDTv2wIrYDlmUWx5czjGE+7jcAufrr3bGX4LNpxjdeBS3ddQ34qgvebrX/S49wSX\n x+ykWZEmbio2YIrQObITUnYNSFVo4qahlUhiX4fgPK5WkOZuZxZPC6LZZlC1QAl2LxD7rTXtXFg\n xdxBvV4cf0E/9Wiq8p3w7VGQvyjVt/XW53jFyO3Q3vb2SFOTZh9gdTpn8UwWq7qSyElOO0pDaBt\n Shr/Zv9B0aadXOrjLo6jKf/pZqp2MR0sipNdtxLlRuMkEnhOZnlLEcfK1eCqHusiuiZ9eLgRScF\n Y6MPaoPM795cnY51azSdf+sn77Qz9z1QBlY010sWMRpZxubb1m7sgEJoRyYojBsuWOoCNUfqVHV\n F9+/41d8DAj44cmsg929zTvvJemGFAHLtg","X-Received":["by 2002:ac8:6907:0:b0:50d:7384:a660 with SMTP id\n d75a77b69052e-50e36b3d6a6mr57753251cf.6.1776456589726;\n Fri, 17 Apr 2026 13:09:49 -0700 (PDT)","by 2002:ac8:6907:0:b0:50d:7384:a660 with SMTP id\n d75a77b69052e-50e36b3d6a6mr57751341cf.6.1776456587687;\n Fri, 17 Apr 2026 13:09:47 -0700 (PDT)"],"Message-ID":"<7ce6d247-0538-4860-90be-5788e1b70782@redhat.com>","Date":"Fri, 17 Apr 2026 16:09:45 -0400","MIME-Version":"1.0","User-Agent":"Mozilla Thunderbird","Subject":"Re: [PATCH 1/3] io: Consolidate fts implementation","To":"Adhemerval Zanella Netto <adhemerval.zanella@linaro.org>,\n libc-alpha@sourceware.org","Cc":"Collin Funk <collin.funk1@gmail.com>, Paul Eggert <eggert@cs.ucla.edu>","References":"<20260417132808.235562-1-adhemerval.zanella@linaro.org>\n <20260417132808.235562-2-adhemerval.zanella@linaro.org>\n <a4d6a525-ed41-48c1-a08d-c7ee981e9472@linaro.org>","From":"Carlos O'Donell <carlos@redhat.com>","Organization":"Red Hat, LLC.","In-Reply-To":"<a4d6a525-ed41-48c1-a08d-c7ee981e9472@linaro.org>","X-Mimecast-Spam-Score":"0","X-Mimecast-MFC-PROC-ID":"W3mNT6v_7OH8RwpM5O1f3lvJgYt7qR8gCYWRmTXAvCo_1776456591","X-Mimecast-Originator":"redhat.com","Content-Language":"en-US","Content-Type":"text/plain; charset=UTF-8; format=flowed","Content-Transfer-Encoding":"7bit","X-BeenThere":"libc-alpha@sourceware.org","X-Mailman-Version":"2.1.30","Precedence":"list","List-Id":"Libc-alpha mailing list <libc-alpha.sourceware.org>","List-Unsubscribe":"<https://sourceware.org/mailman/options/libc-alpha>,\n <mailto:libc-alpha-request@sourceware.org?subject=unsubscribe>","List-Archive":"<https://sourceware.org/pipermail/libc-alpha/>","List-Post":"<mailto:libc-alpha@sourceware.org>","List-Help":"<mailto:libc-alpha-request@sourceware.org?subject=help>","List-Subscribe":"<https://sourceware.org/mailman/listinfo/libc-alpha>,\n <mailto:libc-alpha-request@sourceware.org?subject=subscribe>","Errors-To":"libc-alpha-bounces~incoming=patchwork.ozlabs.org@sourceware.org"}},{"id":3679967,"web_url":"http://patchwork.ozlabs.org/comment/3679967/","msgid":"<21167075-d79a-8d99-9d5a-1a7ee189d6c9@redhat.com>","list_archive_url":null,"date":"2026-04-21T16:02:26","subject":"Re: [PATCH 1/3] io: Consolidate fts implementation","submitter":{"id":87810,"url":"http://patchwork.ozlabs.org/api/people/87810/","name":"Joseph Myers","email":"josmyers@redhat.com"},"content":"On Fri, 17 Apr 2026, Adhemerval Zanella wrote:\n\n> Remove wordsize-64 and arch-specific implementations, for ABIs when\n> off_t is the same as off64_t (__OFF_T_MATCHES_OFF64_T) the fts64.c\n> will create the requires aliases.\n\nDoesn't the fts ABI involve struct stat as well as off_t?  That is, is \nthis safe for MIPS n64 where struct stat and struct stat64 are different?  \n(Likewise for ftw.)","headers":{"Return-Path":"<libc-alpha-bounces~incoming=patchwork.ozlabs.org@sourceware.org>","X-Original-To":["incoming@patchwork.ozlabs.org","libc-alpha@sourceware.org"],"Delivered-To":["patchwork-incoming@legolas.ozlabs.org","libc-alpha@sourceware.org"],"Authentication-Results":["legolas.ozlabs.org;\n\tdkim=pass (1024-bit key;\n unprotected) header.d=redhat.com header.i=@redhat.com header.a=rsa-sha256\n header.s=mimecast20190719 header.b=LjSKNNxg;\n\tdkim-atps=neutral","legolas.ozlabs.org;\n spf=pass (sender SPF authorized) smtp.mailfrom=sourceware.org\n (client-ip=2620:52:6:3111::32; helo=vm01.sourceware.org;\n envelope-from=libc-alpha-bounces~incoming=patchwork.ozlabs.org@sourceware.org;\n receiver=patchwork.ozlabs.org)","sourceware.org;\n\tdkim=pass (1024-bit key,\n unprotected) header.d=redhat.com header.i=@redhat.com header.a=rsa-sha256\n header.s=mimecast20190719 header.b=LjSKNNxg","sourceware.org; dmarc=pass (p=quarantine dis=none)\n header.from=redhat.com","sourceware.org; spf=pass smtp.mailfrom=redhat.com","server2.sourceware.org;\n arc=none smtp.remote-ip=170.10.129.124"],"Received":["from vm01.sourceware.org (vm01.sourceware.org\n [IPv6:2620:52:6:3111::32])\n\t(using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits)\n\t key-exchange x25519 server-signature ECDSA (secp384r1) server-digest SHA384)\n\t(No client certificate requested)\n\tby legolas.ozlabs.org (Postfix) with ESMTPS id 4g0Rvy5mGRz1yGt\n\tfor <incoming@patchwork.ozlabs.org>; Wed, 22 Apr 2026 02:02:54 +1000 (AEST)","from vm01.sourceware.org (localhost [127.0.0.1])\n\tby sourceware.org (Postfix) with ESMTP id C6EB94BA2E3C\n\tfor <incoming@patchwork.ozlabs.org>; Tue, 21 Apr 2026 16:02:52 +0000 (GMT)","from us-smtp-delivery-124.mimecast.com\n (us-smtp-delivery-124.mimecast.com [170.10.129.124])\n by sourceware.org (Postfix) with ESMTP id 34D9C4B9DB76\n for <libc-alpha@sourceware.org>; Tue, 21 Apr 2026 16:02:33 +0000 (GMT)","from mail-wr1-f70.google.com (mail-wr1-f70.google.com\n [209.85.221.70]) by relay.mimecast.com with ESMTP with STARTTLS\n (version=TLSv1.3, cipher=TLS_AES_256_GCM_SHA384) id\n us-mta-189-Tvv5bavDPs6srJZOczQAig-1; Tue, 21 Apr 2026 12:02:31 -0400","by mail-wr1-f70.google.com with SMTP id\n ffacd0b85a97d-43d789cebcfso3067656f8f.1\n for <libc-alpha@sourceware.org>; Tue, 21 Apr 2026 09:02:31 -0700 (PDT)","from digraph.polyomino.org.uk (digraph.polyomino.org.uk.\n [2001:8b0:bf73:93f7::51bb:e332]) by smtp.gmail.com with ESMTPSA id\n 5b1f17b1804b1-48a55b8baaesm90405305e9.10.2026.04.21.09.02.27\n (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256);\n Tue, 21 Apr 2026 09:02:28 -0700 (PDT)","from jsm28 (helo=localhost)\n by digraph.polyomino.org.uk with local-esmtp (Exim 4.98.2)\n (envelope-from <josmyers@redhat.com>) id 1wFDYQ-00000004O1o-23wC;\n Tue, 21 Apr 2026 16:02:26 +0000"],"DKIM-Filter":["OpenDKIM Filter v2.11.0 sourceware.org C6EB94BA2E3C","OpenDKIM Filter v2.11.0 sourceware.org 34D9C4B9DB76"],"DMARC-Filter":"OpenDMARC Filter v1.4.2 sourceware.org 34D9C4B9DB76","ARC-Filter":"OpenARC Filter v1.0.0 sourceware.org 34D9C4B9DB76","ARC-Seal":"i=1; a=rsa-sha256; d=sourceware.org; s=key; t=1776787353; cv=none;\n b=fSK1RzreH6lx8N/2/i3eraMMa5T6Rkc05ugcYkQRr+BhtRAH1rQFowsOWnEyj640uUXekT+doW2oJ4aC8KF3xIXThVUYFY9x3/2spstz9wtRWlWB5SWlYmhepaOuS0af+eO62cKTze6eTu2GuWXLlKPKCNCfHlgW6H8ZRck8DCk=","ARC-Message-Signature":"i=1; a=rsa-sha256; d=sourceware.org; s=key;\n t=1776787353; c=relaxed/simple;\n bh=ocJUAVfzLs/Jb12noX/kaEhPQJ3G3CdxAqF3Tdpnt0E=;\n h=DKIM-Signature:Date:From:To:Subject:Message-ID:MIME-Version;\n b=Kn535V66uVvRzHkuoFS3U2vEsHBYfg6q/aibr622mmH9VR/h239Se9iWVI8BMIp99kF244PFf9S2fbYD/rBL7Xa3bNafbI4e1645aHiLbjyyXo2xdJtyWFBb9NJT/VLpaodNvHJY9PZZhmD1IHm+tqgBMo/18fyFyuXgYyNHUDA=","ARC-Authentication-Results":"i=1; server2.sourceware.org","DKIM-Signature":"v=1; a=rsa-sha256; c=relaxed/relaxed; d=redhat.com;\n s=mimecast20190719; t=1776787352;\n h=from:from:reply-to:subject:subject:date:date:message-id:message-id:\n to:to:cc:cc:mime-version:mime-version:content-type:content-type:\n in-reply-to:in-reply-to:references:references;\n bh=LIsVRH/Wgjcwver1bWlENlkaFzBxE5ybAhFDa1OtbhE=;\n b=LjSKNNxgYPEayZqD9w8okD/YoisSD74/kbWICTyzc3GmEQDnkv0keeItP6AfOmgl67AeP1\n 1F5DAc2M/BmLTWjzkgU4+HIr2RRJciuX71dwMTBzGPJshcPxu7kINwVAZWGWtoh3cLH87N\n 5ITJHWr6Xke55mpyApTeCJFqVh2R7ns=","X-MC-Unique":"Tvv5bavDPs6srJZOczQAig-1","X-Mimecast-MFC-AGG-ID":"Tvv5bavDPs6srJZOczQAig_1776787350","X-Google-DKIM-Signature":"v=1; a=rsa-sha256; c=relaxed/relaxed;\n d=1e100.net; s=20251104; t=1776787350; x=1777392150;\n h=mime-version:references:message-id:in-reply-to:subject:cc:to:from\n :date:x-gm-gg:x-gm-message-state:from:to:cc:subject:date:message-id\n :reply-to;\n bh=LIsVRH/Wgjcwver1bWlENlkaFzBxE5ybAhFDa1OtbhE=;\n b=XeEiJisY3rwUNAu0PkPfCTN6HNgUGva+mO+YcKwRHGTDZYZx+ylBbguavfRaNHqawk\n M+5gw1wT/YvOcvS7m3hKz5heaOa/dpDnsqOlpAc04dS86KpF4GRLgC8t1B/Tqtseo7C8\n y4hzXUEjMYgP8YvMRe/LqD7U7qZykLal7+yYFAptjh+Ygozo6RQvVsSZzfTuJSj5uzy8\n juncXVdzzsjgkWazmDZJp6fewAHvt7x/54kbpChZO0xbUTXt8L/48ZbgC77s+VbGqB+V\n w4RRuITDivdK8J31ow0+iVVRHDuTWp6XJpxByX9zeGimGF0HuF+VCnqzpbtgoFmfk8jk\n DWWw==","X-Gm-Message-State":"AOJu0YwtArF1XFxvuKSWNvvJaVMsVbaNw6TmT5efxY0CHQFs/HFKEpDf\n 8HlHy2D7sZyZvJHsekzl9TYoUM13+gbF51Bkq9gJaAhJbxXtB0Jv89SmLfh1S/izKa8o6CxMJCE\n ud5HSFBMMjKuv/oOHa9c//XjWs5HKNQS4NciIyFxditBzIvBKy5R3IR1EfHA8NQ==","X-Gm-Gg":"AeBDiesPwRJsMyUTEe3dWZ+NQdLCqWZrmI4+inGrebYq/N0sSWe7wQwqvwurYE1DF5G\n UFqOIPm3vbTTCfff53FjYzWrq1GV5iI3MZjzwvtPBsev4gcGsrOPyAvg5+KakLTu5DlmG1leUYs\n F+1wRMRvzel92oNfK4EX3Fo6QKVQFue0L4KluOmJc54H+JZ/sSU0t/eiy0N97jHvWSoKbeFK217\n l3R+N8BZnvJE61XAoUIU3oSj+pMrFVaozntOzaZsyOZC8uPVXPZKEiXd2DZEIrAuvWkWjLR6IsU\n vnEUIgNtrJqi+CfOrpa2SktFAQBf1HA9f8agS1Rdyi5NtEJOv2pnWGAzkn9WPnCydSqSMAr20t0\n wej35dM+EcBelRmmnHB+isrW1aV3cMIQQtWCT+EgsHFi0p/UJXJiI6mbJrzbI1QA=","X-Received":["by 2002:a05:600c:3b2a:b0:48a:534a:eed8 with SMTP id\n 5b1f17b1804b1-48a534af0ffmr73460095e9.1.1776787349940;\n Tue, 21 Apr 2026 09:02:29 -0700 (PDT)","by 2002:a05:600c:3b2a:b0:48a:534a:eed8 with SMTP id\n 5b1f17b1804b1-48a534af0ffmr73459385e9.1.1776787349366;\n Tue, 21 Apr 2026 09:02:29 -0700 (PDT)"],"Date":"Tue, 21 Apr 2026 16:02:26 +0000 (UTC)","From":"Joseph Myers <josmyers@redhat.com>","To":"Adhemerval Zanella <adhemerval.zanella@linaro.org>","cc":"libc-alpha@sourceware.org, Collin Funk <collin.funk1@gmail.com>,\n Paul Eggert <eggert@cs.ucla.edu>","Subject":"Re: [PATCH 1/3] io: Consolidate fts implementation","In-Reply-To":"<20260417132808.235562-2-adhemerval.zanella@linaro.org>","Message-ID":"<21167075-d79a-8d99-9d5a-1a7ee189d6c9@redhat.com>","References":"<20260417132808.235562-1-adhemerval.zanella@linaro.org>\n <20260417132808.235562-2-adhemerval.zanella@linaro.org>","MIME-Version":"1.0","X-Mimecast-Spam-Score":"0","X-Mimecast-MFC-PROC-ID":"g8rt6N4kaXPjmlV5mer5v22v6tFwsJy4zbSjMVsog70_1776787350","X-Mimecast-Originator":"redhat.com","Content-Type":"text/plain; charset=US-ASCII","X-BeenThere":"libc-alpha@sourceware.org","X-Mailman-Version":"2.1.30","Precedence":"list","List-Id":"Libc-alpha mailing list <libc-alpha.sourceware.org>","List-Unsubscribe":"<https://sourceware.org/mailman/options/libc-alpha>,\n <mailto:libc-alpha-request@sourceware.org?subject=unsubscribe>","List-Archive":"<https://sourceware.org/pipermail/libc-alpha/>","List-Post":"<mailto:libc-alpha@sourceware.org>","List-Help":"<mailto:libc-alpha-request@sourceware.org?subject=help>","List-Subscribe":"<https://sourceware.org/mailman/listinfo/libc-alpha>,\n <mailto:libc-alpha-request@sourceware.org?subject=subscribe>","Errors-To":"libc-alpha-bounces~incoming=patchwork.ozlabs.org@sourceware.org"}},{"id":3679983,"web_url":"http://patchwork.ozlabs.org/comment/3679983/","msgid":"<5410EB13-EECF-4EB6-B1EB-E452949C3D33@linaro.org>","list_archive_url":null,"date":"2026-04-21T16:57:00","subject":"Re: [PATCH 1/3] io: Consolidate fts implementation","submitter":{"id":66065,"url":"http://patchwork.ozlabs.org/api/people/66065/","name":"Adhemerval Zanella Netto","email":"adhemerval.zanella@linaro.org"},"content":"> Em 21 de abr. de 2026, à(s) 13:02, Joseph Myers <josmyers@redhat.com> escreveu:\n> \n> ﻿On Fri, 17 Apr 2026, Adhemerval Zanella wrote:\n> \n>> Remove wordsize-64 and arch-specific implementations, for ABIs when\n>> off_t is the same as off64_t (__OFF_T_MATCHES_OFF64_T) the fts64.c\n>> will create the requires aliases.\n> \n> Doesn't the fts ABI involve struct stat as well as off_t?  That is, is\n> this safe for MIPS n64 where struct stat and struct stat64 are different?  \n> (Likewise for ftw.)\n\nRight, I will check out if there is any changes for such ABIs. I did run io tests on mips n32, but I did not check for n64.","headers":{"Return-Path":"<libc-alpha-bounces~incoming=patchwork.ozlabs.org@sourceware.org>","X-Original-To":["incoming@patchwork.ozlabs.org","libc-alpha@sourceware.org"],"Delivered-To":["patchwork-incoming@legolas.ozlabs.org","libc-alpha@sourceware.org"],"Authentication-Results":["legolas.ozlabs.org;\n\tdkim=pass (2048-bit key;\n unprotected) header.d=linaro.org header.i=@linaro.org header.a=rsa-sha256\n header.s=google header.b=hkO1mTjm;\n\tdkim-atps=neutral","legolas.ozlabs.org;\n spf=pass (sender SPF authorized) smtp.mailfrom=sourceware.org\n (client-ip=2620:52:6:3111::32; helo=vm01.sourceware.org;\n envelope-from=libc-alpha-bounces~incoming=patchwork.ozlabs.org@sourceware.org;\n receiver=patchwork.ozlabs.org)","sourceware.org;\n\tdkim=pass (2048-bit key,\n unprotected) header.d=linaro.org header.i=@linaro.org header.a=rsa-sha256\n header.s=google header.b=hkO1mTjm","sourceware.org;\n dmarc=pass (p=none dis=none) header.from=linaro.org","sourceware.org; spf=pass smtp.mailfrom=linaro.org","server2.sourceware.org;\n arc=none smtp.remote-ip=2607:f8b0:4864:20::1331"],"Received":["from vm01.sourceware.org (vm01.sourceware.org\n [IPv6:2620:52:6:3111::32])\n\t(using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits)\n\t key-exchange x25519 server-signature ECDSA (secp384r1) server-digest SHA384)\n\t(No client certificate requested)\n\tby legolas.ozlabs.org (Postfix) with ESMTPS id 4g0T7540K1z1yCv\n\tfor <incoming@patchwork.ozlabs.org>; Wed, 22 Apr 2026 02:57:37 +1000 (AEST)","from vm01.sourceware.org (localhost [127.0.0.1])\n\tby sourceware.org (Postfix) with ESMTP id BA20F4BA900B\n\tfor <incoming@patchwork.ozlabs.org>; Tue, 21 Apr 2026 16:57:35 +0000 (GMT)","from mail-dy1-x1331.google.com (mail-dy1-x1331.google.com\n [IPv6:2607:f8b0:4864:20::1331])\n by sourceware.org (Postfix) with ESMTPS id 58EAE4BA23D9\n for <libc-alpha@sourceware.org>; Tue, 21 Apr 2026 16:57:15 +0000 (GMT)","by mail-dy1-x1331.google.com with SMTP id\n 5a478bee46e88-2b6b0500e06so7351320eec.1\n for <libc-alpha@sourceware.org>; Tue, 21 Apr 2026 09:57:15 -0700 (PDT)","from smtpclient.apple ([2804:18:1139:6a71:ac96:27ee:5f2b:181])\n by smtp.gmail.com with ESMTPSA id\n 5a478bee46e88-2e53d8b944bsm19946312eec.28.2026.04.21.09.57.13\n (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256);\n Tue, 21 Apr 2026 09:57:13 -0700 (PDT)"],"DKIM-Filter":["OpenDKIM Filter v2.11.0 sourceware.org BA20F4BA900B","OpenDKIM Filter v2.11.0 sourceware.org 58EAE4BA23D9"],"DMARC-Filter":"OpenDMARC Filter v1.4.2 sourceware.org 58EAE4BA23D9","ARC-Filter":"OpenARC Filter v1.0.0 sourceware.org 58EAE4BA23D9","ARC-Seal":"i=1; a=rsa-sha256; d=sourceware.org; s=key; t=1776790635; cv=none;\n b=PPJcXubA78PiMjjc9AmIIvTvOfjj3a7IroefcGs31enYFQnLtXZeiwGyb295iqvoiUSii8HJydyK9TDq10bkH3GlEMtMZPXQyleXahOEf46h7VGgn1jbI6qTASKvxXzeaa8HF5iwwsT0I6nWd0PBkGP+k1ztOY+Vy5uNgis7xPc=","ARC-Message-Signature":"i=1; a=rsa-sha256; d=sourceware.org; s=key;\n t=1776790635; c=relaxed/simple;\n bh=5chDM4VwWqAwof637IBwsCX6eP4mkNQ30hsQDH9qiP8=;\n h=DKIM-Signature:From:Mime-Version:Subject:Date:Message-Id:To;\n b=H4WYlWGnJ1N26LRScSWAUnRuyyFzkW5hg9Fs+yjwwfciUjuCkmybDQHfmBAq4O+OTd7e4jMphuY3uHbPwoo33ZXxcU2SoJW3Sxnr06wuA66zsTykMlrkH0azvVusa/ck94H3Ba/WoPmEOvVkro0hvij7jSkCc7/jhDrFQjXoYPc=","ARC-Authentication-Results":"i=1; server2.sourceware.org","DKIM-Signature":"v=1; a=rsa-sha256; c=relaxed/relaxed;\n d=linaro.org; s=google; t=1776790634; x=1777395434; darn=sourceware.org;\n h=to:in-reply-to:cc:references:message-id:date:subject:mime-version\n :from:content-transfer-encoding:from:to:cc:subject:date:message-id\n :reply-to; bh=lPwTGIw8GlvILd3D3u5/eXoYSDQXvtr6y0LwU6bavaY=;\n b=hkO1mTjmHTbkaLdVggPORqZCPRH9Z0vho/Bzzy2mxyZtKyFpgRxfojCNkA7CV2K64B\n 5v7I0P7DrTmaiK3aXZtP5rGvVueqod4hx9MvJPS8RHIqCh3r3Q6cKqyO78hscUc2C0iu\n koYLNZ1Ii9n1Lofz9V2vGGf/I/Ky4TjA8W6eW04pywIvvh122YxWaP2OO/TvwcTmVX2Q\n 1+GJxve6twgduRZlQ1jIh9ymjhxlP4yxqamWY/t4ejGrRhGvsawaQZ9VZteMkI8jIRo2\n gAhO8kyybZEYB+D7SHtA7C4Oss38flmu7FWuL2NhJ0IiobX+Q3I4S3c2MOrwpxSD6I5y\n 8P2Q==","X-Google-DKIM-Signature":"v=1; a=rsa-sha256; c=relaxed/relaxed;\n d=1e100.net; s=20251104; t=1776790634; x=1777395434;\n h=to:in-reply-to:cc:references:message-id:date:subject:mime-version\n :from:content-transfer-encoding:x-gm-gg:x-gm-message-state:from:to\n :cc:subject:date:message-id:reply-to;\n bh=lPwTGIw8GlvILd3D3u5/eXoYSDQXvtr6y0LwU6bavaY=;\n b=Kd4JS+j6rEk1fCwcwqlBEt9vbdhu6I6nb8oMQe6BGjZ15V8RP6NqtzMhLJHkFQSzgm\n 0HATb5beyChcgRVUe4luiE2XclHKblHG8iJXkKIiYSUkqhO6xuGLSZlyGYSpPVU35Oxw\n I8cJJqrS149XVfVL1P0o8v7x0CIWH7NPpGPsuu73VZ45m6KF9B0XtAjQ2OSVv9vRPOgW\n 8TVJgM/PhUHE3fKwq3q58SvERbSEi37/oFypIKhMM3f4VMhNjbiZSTT6AIOmxf2gaqQg\n pn0cDeaucDh996sDBB1fExlRMIU3bCxs48kgsacqkiBOgyjAQoSKVVaeR2wmOdWRLMos\n R9QA==","X-Gm-Message-State":"AOJu0YyqHIfPEZwfjJ1tIPlYUeLhlFBgdSDCjNdPHBXgQH4+z+ZnVv3V\n Q9S0m4msT8ZB9pTCQk4XaAP1aVcgQmaahYwAQws5XoFzl2Bc0CA0JRem4bovo+ZsC8k=","X-Gm-Gg":"AeBDievgD0Qdz6CrakTdwQAHGCV9LsWy9f+au3XIrx+88gaxq/W6I73X5HlQEY+GA6k\n dpOAaCzP3NMn98iJiwXtDIZh0d5Hyuo3HSHcrabnQP7QKv+OetoNuaXAHkDx51RwnkShQenbKc7\n 6mjW3P7dIXcWc2vlsNBu73y1ZPtP5wz88oQ0g8vj9wwVgmcfugQ5GKOGkt/LFN033jQpuK1gbqh\n 3HC1IK/fk7iOsQpLVK8CJzISz32MnCUEyO7w2Ub+3hqV3CtVMgvzTXgawD2Omo8MqtxgO1uJhV3\n fG0qmQOoZFEH13R1AY/74IO45/Ok0/qHEBsWh9UhIqQlDb2DoZkrT44tPVb3RbBI966sCERiOZg\n DR3E2tsV49nff2UGMt2ZNm0sddgHlmLYJLxIVcPm103tdsCOtgqpV2yOGr5A/fa3kRfyP57PRFl\n aJ1wk0ypHxVJ+9OE9JIkfi2PGPx9OdmP7vpoELgVEohSskoItGtq05B8sKHrdGgNB04gjm6rV8o\n 34=","X-Received":"by 2002:a05:693c:2c0f:b0:2c1:6cfd:73ee with SMTP id\n 5a478bee46e88-2e47901911amr10239637eec.24.1776790633931;\n Tue, 21 Apr 2026 09:57:13 -0700 (PDT)","Content-Type":"text/plain; charset=utf-8","Content-Transfer-Encoding":"quoted-printable","From":"Adhemerval Zanella <adhemerval.zanella@linaro.org>","Mime-Version":"1.0 (1.0)","Subject":"Re: [PATCH 1/3] io: Consolidate fts implementation","Date":"Tue, 21 Apr 2026 13:57:00 -0300","Message-Id":"<5410EB13-EECF-4EB6-B1EB-E452949C3D33@linaro.org>","References":"<21167075-d79a-8d99-9d5a-1a7ee189d6c9@redhat.com>","Cc":"libc-alpha@sourceware.org, Collin Funk <collin.funk1@gmail.com>,\n Paul Eggert <eggert@cs.ucla.edu>","In-Reply-To":"<21167075-d79a-8d99-9d5a-1a7ee189d6c9@redhat.com>","To":"Joseph Myers <josmyers@redhat.com>","X-Mailer":"iPhone Mail (23D8133)","X-BeenThere":"libc-alpha@sourceware.org","X-Mailman-Version":"2.1.30","Precedence":"list","List-Id":"Libc-alpha mailing list <libc-alpha.sourceware.org>","List-Unsubscribe":"<https://sourceware.org/mailman/options/libc-alpha>,\n <mailto:libc-alpha-request@sourceware.org?subject=unsubscribe>","List-Archive":"<https://sourceware.org/pipermail/libc-alpha/>","List-Post":"<mailto:libc-alpha@sourceware.org>","List-Help":"<mailto:libc-alpha-request@sourceware.org?subject=help>","List-Subscribe":"<https://sourceware.org/mailman/listinfo/libc-alpha>,\n <mailto:libc-alpha-request@sourceware.org?subject=subscribe>","Errors-To":"libc-alpha-bounces~incoming=patchwork.ozlabs.org@sourceware.org"}}]