aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--meta/recipes-core/fts/fts.bb39
-rw-r--r--meta/recipes-core/fts/fts/fts-header-correctness.patch25
-rw-r--r--meta/recipes-core/fts/fts/fts-uclibc.patch50
-rw-r--r--meta/recipes-core/fts/fts/gcc5.patch1368
-rw-r--r--meta/recipes-core/fts/fts/remove_cdefs.patch69
-rw-r--r--meta/recipes-core/fts/fts/stdint.patch15
6 files changed, 1566 insertions, 0 deletions
diff --git a/meta/recipes-core/fts/fts.bb b/meta/recipes-core/fts/fts.bb
new file mode 100644
index 0000000000..d8b4ed2a93
--- /dev/null
+++ b/meta/recipes-core/fts/fts.bb
@@ -0,0 +1,39 @@
+# Copyright (C) 2015 Khem Raj <raj.khem@gmail.com>
+# Released under the MIT license (see COPYING.MIT for the terms)
+
+DESCRIPTION = "keith bostic's POSIX file tree stream operations library"
+HOMEPAGE = "https://sites.google.com/a/bostic.com/keithbostic"
+LICENSE = "BSD-4-Clause"
+LIC_FILES_CHECKSUM = "file://${COREBASE}/meta/files/common-licenses/BSD-4-Clause;md5=624d9e67e8ac41a78f6b6c2c55a83a2b"
+SECTION = "libs"
+
+SRC_URI = "https://sites.google.com/a/bostic.com/keithbostic/files/fts.tar.gz \
+ file://fts-header-correctness.patch \
+ file://fts-uclibc.patch \
+ file://remove_cdefs.patch \
+ file://stdint.patch \
+ file://gcc5.patch \
+"
+
+SRC_URI[md5sum] = "120c14715485ec6ced14f494d059d20a"
+SRC_URI[sha256sum] = "3df9b9b5a45aeaf16f33bb84e692a10dc662e22ec8a51748f98767d67fb6f342"
+
+S = "${WORKDIR}/${BPN}"
+
+do_configure[noexec] = "1"
+
+VER = "0"
+do_compile () {
+ ${CC} -I${S} -fPIC -shared -o libfts.so.${VER} -Wl,-soname,libfts.so.${VER} ${S}/fts.c
+}
+
+do_install() {
+ install -Dm755 ${B}/libfts.so.${VER} ${D}${libdir}/libfts.so.${VER}
+ ln -sf libfts.so.${VER} ${D}${libdir}/libfts.so
+ install -Dm644 ${S}/fts.h ${D}${includedir}/fts.h
+}
+#
+# We will skip parsing for non-musl systems
+#
+COMPATIBLE_HOST = ".*-musl.*"
+
diff --git a/meta/recipes-core/fts/fts/fts-header-correctness.patch b/meta/recipes-core/fts/fts/fts-header-correctness.patch
new file mode 100644
index 0000000000..c73ddc95d8
--- /dev/null
+++ b/meta/recipes-core/fts/fts/fts-header-correctness.patch
@@ -0,0 +1,25 @@
+Included needed headers for compiling with musl
+
+Signed-off-by: Khem Raj <raj.khem@gmail.com>
+Upstream-Status: Inappropriate
+
+--- fts.orig/fts.h
++++ fts/fts.h
+@@ -38,6 +38,17 @@
+ #ifndef _FTS_H_
+ #define _FTS_H_
+
++#include <sys/types.h>
++#include <sys/param.h>
++#include <sys/stat.h>
++
++#include <dirent.h>
++#include <errno.h>
++#include <fcntl.h>
++#include <stdlib.h>
++#include <string.h>
++#include <unistd.h>
++
+ typedef struct {
+ struct _ftsent *fts_cur; /* current node */
+ struct _ftsent *fts_child; /* linked list of children */
diff --git a/meta/recipes-core/fts/fts/fts-uclibc.patch b/meta/recipes-core/fts/fts/fts-uclibc.patch
new file mode 100644
index 0000000000..397654bf51
--- /dev/null
+++ b/meta/recipes-core/fts/fts/fts-uclibc.patch
@@ -0,0 +1,50 @@
+Add missing defines for uclibc
+
+Signed-off-by: Khem Raj <raj.khem@gmail.com>
+Upstream-Status: Inappropriate
+
+--- fts.orig/fts.c
++++ fts/fts.c
+@@ -31,6 +31,10 @@
+ * SUCH DAMAGE.
+ */
+
++#define alignof(TYPE) ((int) &((struct { char dummy1; TYPE dummy2; } *) 0)->dummy2)
++#define ALIGNBYTES (alignof(long double) - 1)
++#define ALIGN(p) (((uintptr_t)(p) + ALIGNBYTES) & ~ALIGNBYTES)
++
+ #if defined(LIBC_SCCS) && !defined(lint)
+ static char sccsid[] = "@(#)fts.c 8.6 (Berkeley) 8/14/94";
+ #endif /* LIBC_SCCS and not lint */
+@@ -652,10 +656,10 @@
+ if (!ISSET(FTS_SEEDOT) && ISDOT(dp->d_name))
+ continue;
+
+- if ((p = fts_alloc(sp, dp->d_name, (int)dp->d_namlen)) == NULL)
++ if ((p = fts_alloc(sp, dp->d_name, (int)dp->d_reclen)) == NULL)
+ goto mem1;
+- if (dp->d_namlen > maxlen) {
+- if (fts_palloc(sp, (size_t)dp->d_namlen)) {
++ if (dp->d_reclen > maxlen) {
++ if (fts_palloc(sp, (size_t)dp->d_reclen)) {
+ /*
+ * No more memory for path or structures. Save
+ * errno, free up the current structure and the
+@@ -675,7 +679,7 @@
+ maxlen = sp->fts_pathlen - sp->fts_cur->fts_pathlen - 1;
+ }
+
+- p->fts_pathlen = len + dp->d_namlen + 1;
++ p->fts_pathlen = len + dp->d_reclen + 1;
+ p->fts_parent = sp->fts_cur;
+ p->fts_level = level;
+
+@@ -784,7 +788,7 @@
+ /* If user needs stat info, stat buffer already allocated. */
+ sbp = ISSET(FTS_NOSTAT) ? &sb : p->fts_statp;
+
+-#ifdef DT_WHT
++#ifdef S_IFWHT
+ /*
+ * Whited-out files don't really exist. However, there's stat(2) file
+ * mask for them, so we set it so that programs (i.e., find) don't have
diff --git a/meta/recipes-core/fts/fts/gcc5.patch b/meta/recipes-core/fts/fts/gcc5.patch
new file mode 100644
index 0000000000..f5b948edc0
--- /dev/null
+++ b/meta/recipes-core/fts/fts/gcc5.patch
@@ -0,0 +1,1368 @@
+Forward port the sources to be able to compile with c99/gcc5
+
+Signed-off-by: Khem Raj <raj.khem@gmail.com>
+Upstream-Status: Inappropriate
+
+Index: fts/fts.c
+===================================================================
+--- fts.orig/fts.c
++++ fts/fts.c
+@@ -51,16 +51,6 @@ static char sccsid[] = "@(#)fts.c 8.6 (B
+ #include <string.h>
+ #include <unistd.h>
+
+-static FTSENT *fts_alloc __P(FTS *, char *, int);
+-static FTSENT *fts_build __P(FTS *, int);
+-static void fts_lfree __P(FTSENT *);
+-static void fts_load __P(FTS *, FTSENT *);
+-static size_t fts_maxarglen __P(char * const *);
+-static void fts_padjust __P(FTS *, void *);
+-static int fts_palloc __P(FTS *, size_t);
+-static FTSENT *fts_sort __P(FTS *, FTSENT *, int);
+-static u_short fts_stat __P(FTS *, struct dirent *, FTSENT *, int);
+-
+ #define ISDOT(a) (a[0] == '.' && (!a[1] || a[1] == '.' && !a[2]))
+
+ #define ISSET(opt) (sp->fts_options & opt)
+@@ -73,119 +63,16 @@ static u_short fts_stat __P(FTS *, stru
+ #define BCHILD 1 /* fts_children */
+ #define BNAMES 2 /* fts_children, names only */
+ #define BREAD 3 /* fts_read */
+-
+-FTS *
+-fts_open(argv, options, compar)
+- char * const *argv;
+- register int options;
+- int (*compar)();
+-{
+- register FTS *sp;
+- register FTSENT *p, *root;
+- register int nitems;
+- FTSENT *parent, *tmp;
+- int len;
+-
+- /* Options check. */
+- if (options & ~FTS_OPTIONMASK) {
+- errno = EINVAL;
+- return (NULL);
+- }
+-
+- /* Allocate/initialize the stream */
+- if ((sp = malloc((u_int)sizeof(FTS))) == NULL)
+- return (NULL);
+- memset(sp, 0, sizeof(FTS));
+- sp->fts_compar = compar;
+- sp->fts_options = options;
+-
+- /* Logical walks turn on NOCHDIR; symbolic links are too hard. */
+- if (ISSET(FTS_LOGICAL))
+- SET(FTS_NOCHDIR);
+-
+- /*
+- * Start out with 1K of path space, and enough, in any case,
+- * to hold the user's paths.
+- */
+- if (fts_palloc(sp, MAX(fts_maxarglen(argv), MAXPATHLEN)))
+- goto mem1;
+-
+- /* Allocate/initialize root's parent. */
+- if ((parent = fts_alloc(sp, "", 0)) == NULL)
+- goto mem2;
+- parent->fts_level = FTS_ROOTPARENTLEVEL;
+-
+- /* Allocate/initialize root(s). */
+- for (root = NULL, nitems = 0; *argv; ++argv, ++nitems) {
+- /* Don't allow zero-length paths. */
+- if ((len = strlen(*argv)) == 0) {
+- errno = EINVAL;
+- goto mem3;
+- }
+-
+- p = fts_alloc(sp, *argv, len);
+- p->fts_level = FTS_ROOTLEVEL;
+- p->fts_parent = parent;
+- p->fts_accpath = p->fts_name;
+- p->fts_info = fts_stat(sp, NULL, p, ISSET(FTS_COMFOLLOW));
+-
+- /* Command-line "." and ".." are real directories. */
+- if (p->fts_info == FTS_DOT)
+- p->fts_info = FTS_D;
+-
+- /*
+- * If comparison routine supplied, traverse in sorted
+- * order; otherwise traverse in the order specified.
+- */
+- if (compar) {
+- p->fts_link = root;
+- root = p;
+- } else {
+- p->fts_link = NULL;
+- if (root == NULL)
+- tmp = root = p;
+- else {
+- tmp->fts_link = p;
+- tmp = p;
+- }
+- }
+- }
+- if (compar && nitems > 1)
+- root = fts_sort(sp, root, nitems);
+-
+- /*
+- * Allocate a dummy pointer and make fts_read think that we've just
+- * finished the node before the root(s); set p->fts_info to FTS_INIT
+- * so that everything about the "current" node is ignored.
+- */
+- if ((sp->fts_cur = fts_alloc(sp, "", 0)) == NULL)
+- goto mem3;
+- sp->fts_cur->fts_link = root;
+- sp->fts_cur->fts_info = FTS_INIT;
+-
+- /*
+- * If using chdir(2), grab a file descriptor pointing to dot to insure
+- * that we can get back here; this could be avoided for some paths,
+- * but almost certainly not worth the effort. Slashes, symbolic links,
+- * and ".." are all fairly nasty problems. Note, if we can't get the
+- * descriptor we run anyway, just more slowly.
+- */
+- if (!ISSET(FTS_NOCHDIR) && (sp->fts_rfd = open(".", O_RDONLY, 0)) < 0)
+- SET(FTS_NOCHDIR);
+-
+- return (sp);
+-
+-mem3: fts_lfree(root);
+- free(parent);
+-mem2: free(sp->fts_path);
+-mem1: free(sp);
+- return (NULL);
+-}
++/*
++ * Special case a root of "/" so that slashes aren't appended which would
++ * cause paths to be written as "//foo".
++ */
++#define NAPPEND(p) \
++ (p->fts_level == FTS_ROOTLEVEL && p->fts_pathlen == 1 && \
++ p->fts_path[0] == '/' ? 0 : p->fts_pathlen)
+
+ static void
+-fts_load(sp, p)
+- FTS *sp;
+- register FTSENT *p;
++fts_load(FTS *sp, register FTSENT *p)
+ {
+ register int len;
+ register char *cp;
+@@ -208,332 +95,214 @@ fts_load(sp, p)
+ sp->fts_dev = p->fts_dev;
+ }
+
+-int
+-fts_close(sp)
+- FTS *sp;
++static void
++fts_lfree(register FTSENT *head)
+ {
+- register FTSENT *freep, *p;
+- int saved_errno;
++ register FTSENT *p;
+
+- /*
+- * This still works if we haven't read anything -- the dummy structure
+- * points to the root list, so we step through to the end of the root
+- * list which has a valid parent pointer.
+- */
+- if (sp->fts_cur) {
+- for (p = sp->fts_cur; p->fts_level >= FTS_ROOTLEVEL;) {
+- freep = p;
+- p = p->fts_link ? p->fts_link : p->fts_parent;
+- free(freep);
+- }
++ /* Free a linked list of structures. */
++ while (p = head) {
++ head = head->fts_link;
+ free(p);
+ }
++}
+
+- /* Free up child linked list, sort array, path buffer. */
+- if (sp->fts_child)
+- fts_lfree(sp->fts_child);
+- if (sp->fts_array)
+- free(sp->fts_array);
+- free(sp->fts_path);
++static size_t
++fts_maxarglen(char * const *argv)
++{
++ size_t len, max;
+
+- /* Return to original directory, save errno if necessary. */
+- if (!ISSET(FTS_NOCHDIR)) {
+- saved_errno = fchdir(sp->fts_rfd) ? errno : 0;
+- (void)close(sp->fts_rfd);
+- }
++ for (max = 0; *argv; ++argv)
++ if ((len = strlen(*argv)) > max)
++ max = len;
++ return (max);
++}
+
+- /* Free up the stream pointer. */
+- free(sp);
+
+- /* Set errno and return. */
+- if (!ISSET(FTS_NOCHDIR) && saved_errno) {
+- errno = saved_errno;
+- return (-1);
++/*
++ * When the path is realloc'd, have to fix all of the pointers in structures
++ * already returned.
++ */
++static void
++fts_padjust(FTS *sp, void *addr)
++{
++ FTSENT *p;
++
++#define ADJUST(p) { \
++ (p)->fts_accpath = \
++ (char *)addr + ((p)->fts_accpath - (p)->fts_path); \
++ (p)->fts_path = addr; \
++}
++ /* Adjust the current set of children. */
++ for (p = sp->fts_child; p; p = p->fts_link)
++ ADJUST(p);
++
++ /* Adjust the rest of the tree. */
++ for (p = sp->fts_cur; p->fts_level >= FTS_ROOTLEVEL;) {
++ ADJUST(p);
++ p = p->fts_link ? p->fts_link : p->fts_parent;
+ }
+- return (0);
+ }
+
+ /*
+- * Special case a root of "/" so that slashes aren't appended which would
+- * cause paths to be written as "//foo".
++ * Allow essentially unlimited paths; find, rm, ls should all work on any tree.
++ * Most systems will allow creation of paths much longer than MAXPATHLEN, even
++ * though the kernel won't resolve them. Add the size (not just what's needed)
++ * plus 256 bytes so don't realloc the path 2 bytes at a time.
+ */
+-#define NAPPEND(p) \
+- (p->fts_level == FTS_ROOTLEVEL && p->fts_pathlen == 1 && \
+- p->fts_path[0] == '/' ? 0 : p->fts_pathlen)
++static int
++fts_palloc(FTS *sp, size_t more)
++{
++ sp->fts_pathlen += more + 256;
++ sp->fts_path = realloc(sp->fts_path, (size_t)sp->fts_pathlen);
++ return (sp->fts_path == NULL);
++}
+
+-FTSENT *
+-fts_read(sp)
+- register FTS *sp;
++static FTSENT *
++fts_alloc(FTS *sp, char *name, register int namelen)
+ {
+- register FTSENT *p, *tmp;
+- register int instr;
+- register char *t;
+- int saved_errno;
++ register FTSENT *p;
++ size_t len;
+
+- /* If finished or unrecoverable error, return NULL. */
+- if (sp->fts_cur == NULL || ISSET(FTS_STOP))
++ /*
++ * The file name is a variable length array and no stat structure is
++ * necessary if the user has set the nostat bit. Allocate the FTSENT
++ * structure, the file name and the stat structure in one chunk, but
++ * be careful that the stat structure is reasonably aligned. Since the
++ * fts_name field is declared to be of size 1, the fts_name pointer is
++ * namelen + 2 before the first possible address of the stat structure.
++ */
++ len = sizeof(FTSENT) + namelen;
++ if (!ISSET(FTS_NOSTAT))
++ len += sizeof(struct stat) + ALIGNBYTES;
++ if ((p = malloc(len)) == NULL)
+ return (NULL);
+
+- /* Set current node pointer. */
+- p = sp->fts_cur;
++ /* Copy the name plus the trailing NULL. */
++ memmove(p->fts_name, name, namelen + 1);
+
+- /* Save and zero out user instructions. */
+- instr = p->fts_instr;
++ if (!ISSET(FTS_NOSTAT))
++ p->fts_statp = (struct stat *)ALIGN(p->fts_name + namelen + 2);
++ p->fts_namelen = namelen;
++ p->fts_path = sp->fts_path;
++ p->fts_errno = 0;
++ p->fts_flags = 0;
+ p->fts_instr = FTS_NOINSTR;
++ p->fts_number = 0;
++ p->fts_pointer = NULL;
++ return (p);
++}
+
+- /* Any type of file may be re-visited; re-stat and re-turn. */
+- if (instr == FTS_AGAIN) {
+- p->fts_info = fts_stat(sp, NULL, p, 0);
+- return (p);
+- }
+
++static u_short
++fts_stat(FTS *sp, register FTSENT *p, struct dirent *dp, int follow)
++{
++ register FTSENT *t;
++ register dev_t dev;
++ register ino_t ino;
++ struct stat *sbp, sb;
++ int saved_errno;
++
++ /* If user needs stat info, stat buffer already allocated. */
++ sbp = ISSET(FTS_NOSTAT) ? &sb : p->fts_statp;
++
++#ifdef S_IFWHT
+ /*
+- * Following a symlink -- SLNONE test allows application to see
+- * SLNONE and recover. If indirecting through a symlink, have
+- * keep a pointer to current location. If unable to get that
+- * pointer, follow fails.
+- */
+- if (instr == FTS_FOLLOW &&
+- (p->fts_info == FTS_SL || p->fts_info == FTS_SLNONE)) {
+- p->fts_info = fts_stat(sp, NULL, p, 1);
+- if (p->fts_info == FTS_D && !ISSET(FTS_NOCHDIR))
+- if ((p->fts_symfd = open(".", O_RDONLY, 0)) < 0) {
+- p->fts_errno = errno;
+- p->fts_info = FTS_ERR;
+- } else
+- p->fts_flags |= FTS_SYMFOLLOW;
+- return (p);
++ * Whited-out files don't really exist. However, there's stat(2) file
++ * mask for them, so we set it so that programs (i.e., find) don't have
++ * to test FTS_W separately from other file types.
++ */
++ if (dp != NULL && dp->d_type == DT_WHT) {
++ memset(sbp, 0, sizeof(struct stat));
++ sbp->st_mode = S_IFWHT;
++ return (FTS_W);
+ }
+-
+- /* Directory in pre-order. */
+- if (p->fts_info == FTS_D) {
+- /* If skipped or crossed mount point, do post-order visit. */
+- if (instr == FTS_SKIP ||
+- ISSET(FTS_XDEV) && p->fts_dev != sp->fts_dev) {
+- if (p->fts_flags & FTS_SYMFOLLOW)
+- (void)close(p->fts_symfd);
+- if (sp->fts_child) {
+- fts_lfree(sp->fts_child);
+- sp->fts_child = NULL;
+- }
+- p->fts_info = FTS_DP;
+- return (p);
+- }
+-
+- /* Rebuild if only read the names and now traversing. */
+- if (sp->fts_child && sp->fts_options & FTS_NAMEONLY) {
+- sp->fts_options &= ~FTS_NAMEONLY;
+- fts_lfree(sp->fts_child);
+- sp->fts_child = NULL;
+- }
+-
+- /*
+- * Cd to the subdirectory.
+- *
+- * If have already read and now fail to chdir, whack the list
+- * to make the names come out right, and set the parent errno
+- * so the application will eventually get an error condition.
+- * Set the FTS_DONTCHDIR flag so that when we logically change
+- * directories back to the parent we don't do a chdir.
+- *
+- * If haven't read do so. If the read fails, fts_build sets
+- * FTS_STOP or the fts_info field of the node.
+- */
+- if (sp->fts_child) {
+- if (CHDIR(sp, p->fts_accpath)) {
+- p->fts_errno = errno;
+- p->fts_flags |= FTS_DONTCHDIR;
+- for (p = sp->fts_child; p; p = p->fts_link)
+- p->fts_accpath =
+- p->fts_parent->fts_accpath;
+- }
+- } else if ((sp->fts_child = fts_build(sp, BREAD)) == NULL) {
+- if (ISSET(FTS_STOP))
+- return (NULL);
+- return (p);
++#endif
++
++ /*
++ * If doing a logical walk, or application requested FTS_FOLLOW, do
++ * a stat(2). If that fails, check for a non-existent symlink. If
++ * fail, set the errno from the stat call.
++ */
++ if (ISSET(FTS_LOGICAL) || follow) {
++ if (stat(p->fts_accpath, sbp)) {
++ saved_errno = errno;
++ if (!lstat(p->fts_accpath, sbp)) {
++ errno = 0;
++ return (FTS_SLNONE);
++ }
++ p->fts_errno = saved_errno;
++ goto err;
+ }
+- p = sp->fts_child;
+- sp->fts_child = NULL;
+- goto name;
++ } else if (lstat(p->fts_accpath, sbp)) {
++ p->fts_errno = errno;
++err: memset(sbp, 0, sizeof(struct stat));
++ return (FTS_NS);
+ }
+
+- /* Move to the next node on this level. */
+-next: tmp = p;
+- if (p = p->fts_link) {
+- free(tmp);
+-
+- /*
+- * If reached the top, return to the original directory, and
+- * load the paths for the next root.
+- */
+- if (p->fts_level == FTS_ROOTLEVEL) {
+- if (!ISSET(FTS_NOCHDIR) && FCHDIR(sp, sp->fts_rfd)) {
+- SET(FTS_STOP);
+- return (NULL);
+- }
+- fts_load(sp, p);
+- return (sp->fts_cur = p);
+- }
+-
++ if (S_ISDIR(sbp->st_mode)) {
+ /*
+- * User may have called fts_set on the node. If skipped,
+- * ignore. If followed, get a file descriptor so we can
+- * get back if necessary.
++ * Set the device/inode. Used to find cycles and check for
++ * crossing mount points. Also remember the link count, used
++ * in fts_build to limit the number of stat calls. It is
++ * understood that these fields are only referenced if fts_info
++ * is set to FTS_D.
+ */
+- if (p->fts_instr == FTS_SKIP)
+- goto next;
+- if (p->fts_instr == FTS_FOLLOW) {
+- p->fts_info = fts_stat(sp, NULL, p, 1);
+- if (p->fts_info == FTS_D && !ISSET(FTS_NOCHDIR))
+- if ((p->fts_symfd =
+- open(".", O_RDONLY, 0)) < 0) {
+- p->fts_errno = errno;
+- p->fts_info = FTS_ERR;
+- } else
+- p->fts_flags |= FTS_SYMFOLLOW;
+- p->fts_instr = FTS_NOINSTR;
+- }
+-
+-name: t = sp->fts_path + NAPPEND(p->fts_parent);
+- *t++ = '/';
+- memmove(t, p->fts_name, p->fts_namelen + 1);
+- return (sp->fts_cur = p);
+- }
++ dev = p->fts_dev = sbp->st_dev;
++ ino = p->fts_ino = sbp->st_ino;
++ p->fts_nlink = sbp->st_nlink;
+
+- /* Move up to the parent node. */
+- p = tmp->fts_parent;
+- free(tmp);
++ if (ISDOT(p->fts_name))
++ return (FTS_DOT);
+
+- if (p->fts_level == FTS_ROOTPARENTLEVEL) {
+ /*
+- * Done; free everything up and set errno to 0 so the user
+- * can distinguish between error and EOF.
++ * Cycle detection is done by brute force when the directory
++ * is first encountered. If the tree gets deep enough or the
++ * number of symbolic links to directories is high enough,
++ * something faster might be worthwhile.
+ */
+- free(p);
+- errno = 0;
+- return (sp->fts_cur = NULL);
+- }
+-
+- /* Nul terminate the pathname. */
+- sp->fts_path[p->fts_pathlen] = '\0';
+-
+- /*
+- * Return to the parent directory. If at a root node or came through
+- * a symlink, go back through the file descriptor. Otherwise, cd up
+- * one directory.
+- */
+- if (p->fts_level == FTS_ROOTLEVEL) {
+- if (!ISSET(FTS_NOCHDIR) && FCHDIR(sp, sp->fts_rfd)) {
+- SET(FTS_STOP);
+- return (NULL);
+- }
+- } else if (p->fts_flags & FTS_SYMFOLLOW) {
+- if (FCHDIR(sp, p->fts_symfd)) {
+- saved_errno = errno;
+- (void)close(p->fts_symfd);
+- errno = saved_errno;
+- SET(FTS_STOP);
+- return (NULL);
+- }
+- (void)close(p->fts_symfd);
+- } else if (!(p->fts_flags & FTS_DONTCHDIR)) {
+- if (CHDIR(sp, "..")) {
+- SET(FTS_STOP);
+- return (NULL);
+- }
+- }
+- p->fts_info = p->fts_errno ? FTS_ERR : FTS_DP;
+- return (sp->fts_cur = p);
+-}
+-
+-/*
+- * Fts_set takes the stream as an argument although it's not used in this
+- * implementation; it would be necessary if anyone wanted to add global
+- * semantics to fts using fts_set. An error return is allowed for similar
+- * reasons.
+- */
+-/* ARGSUSED */
+-int
+-fts_set(sp, p, instr)
+- FTS *sp;
+- FTSENT *p;
+- int instr;
+-{
+- if (instr && instr != FTS_AGAIN && instr != FTS_FOLLOW &&
+- instr != FTS_NOINSTR && instr != FTS_SKIP) {
+- errno = EINVAL;
+- return (1);
++ for (t = p->fts_parent;
++ t->fts_level >= FTS_ROOTLEVEL; t = t->fts_parent)
++ if (ino == t->fts_ino && dev == t->fts_dev) {
++ p->fts_cycle = t;
++ return (FTS_DC);
++ }
++ return (FTS_D);
+ }
+- p->fts_instr = instr;
+- return (0);
++ if (S_ISLNK(sbp->st_mode))
++ return (FTS_SL);
++ if (S_ISREG(sbp->st_mode))
++ return (FTS_F);
++ return (FTS_DEFAULT);
+ }
+
+-FTSENT *
+-fts_children(sp, instr)
+- register FTS *sp;
+- int instr;
++static FTSENT *
++fts_sort(FTS *sp, FTSENT *head, register int nitems)
+ {
+- register FTSENT *p;
+- int fd;
+-
+- if (instr && instr != FTS_NAMEONLY) {
+- errno = EINVAL;
+- return (NULL);
+- }
+-
+- /* Set current node pointer. */
+- p = sp->fts_cur;
+-
+- /*
+- * Errno set to 0 so user can distinguish empty directory from
+- * an error.
+- */
+- errno = 0;
+-
+- /* Fatal errors stop here. */
+- if (ISSET(FTS_STOP))
+- return (NULL);
+-
+- /* Return logical hierarchy of user's arguments. */
+- if (p->fts_info == FTS_INIT)
+- return (p->fts_link);
+-
+- /*
+- * If not a directory being visited in pre-order, stop here. Could
+- * allow FTS_DNR, assuming the user has fixed the problem, but the
+- * same effect is available with FTS_AGAIN.
+- */
+- if (p->fts_info != FTS_D /* && p->fts_info != FTS_DNR */)
+- return (NULL);
+-
+- /* Free up any previous child list. */
+- if (sp->fts_child)
+- fts_lfree(sp->fts_child);
+-
+- if (instr == FTS_NAMEONLY) {
+- sp->fts_options |= FTS_NAMEONLY;
+- instr = BNAMES;
+- } else
+- instr = BCHILD;
++ register FTSENT **ap, *p;
+
+ /*
+- * If using chdir on a relative path and called BEFORE fts_read does
+- * its chdir to the root of a traversal, we can lose -- we need to
+- * chdir into the subdirectory, and we don't know where the current
+- * directory is, so we can't get back so that the upcoming chdir by
+- * fts_read will work.
++ * Construct an array of pointers to the structures and call qsort(3).
++ * Reassemble the array in the order returned by qsort. If unable to
++ * sort for memory reasons, return the directory entries in their
++ * current order. Allocate enough space for the current needs plus
++ * 40 so don't realloc one entry at a time.
+ */
+- if (p->fts_level != FTS_ROOTLEVEL || p->fts_accpath[0] == '/' ||
+- ISSET(FTS_NOCHDIR))
+- return (sp->fts_child = fts_build(sp, instr));
+-
+- if ((fd = open(".", O_RDONLY, 0)) < 0)
+- return (NULL);
+- sp->fts_child = fts_build(sp, instr);
+- if (fchdir(fd))
+- return (NULL);
+- (void)close(fd);
+- return (sp->fts_child);
++ if (nitems > sp->fts_nitems) {
++ sp->fts_nitems = nitems + 40;
++ if ((sp->fts_array = realloc(sp->fts_array,
++ (size_t)(sp->fts_nitems * sizeof(FTSENT *)))) == NULL) {
++ sp->fts_nitems = 0;
++ return (head);
++ }
++ }
++ for (ap = sp->fts_array, p = head; p; p = p->fts_link)
++ *ap++ = p;
++ qsort((void *)sp->fts_array, nitems, sizeof(FTSENT *), sp->fts_compar);
++ for (head = *(ap = sp->fts_array); --nitems; ++ap)
++ ap[0]->fts_link = ap[1];
++ ap[0]->fts_link = NULL;
++ return (head);
+ }
+
+ /*
+@@ -551,9 +320,7 @@ fts_children(sp, instr)
+ * been found, cutting the stat calls by about 2/3.
+ */
+ static FTSENT *
+-fts_build(sp, type)
+- register FTS *sp;
+- int type;
++fts_build(register FTS *sp, int type)
+ {
+ register struct dirent *dp;
+ register FTSENT *p, *head;
+@@ -716,283 +483,479 @@ mem1: saved_errno = errno;
+ --nlinks;
+ }
+
+- /* We walk in directory order so "ls -f" doesn't get upset. */
+- p->fts_link = NULL;
+- if (head == NULL)
+- head = tail = p;
+- else {
+- tail->fts_link = p;
+- tail = p;
++ /* We walk in directory order so "ls -f" doesn't get upset. */
++ p->fts_link = NULL;
++ if (head == NULL)
++ head = tail = p;
++ else {
++ tail->fts_link = p;
++ tail = p;
++ }
++ ++nitems;
++ }
++ (void)closedir(dirp);
++
++ /*
++ * If had to realloc the path, adjust the addresses for the rest
++ * of the tree.
++ */
++ if (adjaddr)
++ fts_padjust(sp, adjaddr);
++
++ /*
++ * If not changing directories, reset the path back to original
++ * state.
++ */
++ if (ISSET(FTS_NOCHDIR)) {
++ if (cp - 1 > sp->fts_path)
++ --cp;
++ *cp = '\0';
++ }
++
++ /*
++ * If descended after called from fts_children or after called from
++ * fts_read and nothing found, get back. At the root level we use
++ * the saved fd; if one of fts_open()'s arguments is a relative path
++ * to an empty directory, we wind up here with no other way back. If
++ * can't get back, we're done.
++ */
++ if (descend && (type == BCHILD || !nitems) &&
++ (cur->fts_level == FTS_ROOTLEVEL ?
++ FCHDIR(sp, sp->fts_rfd) : CHDIR(sp, ".."))) {
++ cur->fts_info = FTS_ERR;
++ SET(FTS_STOP);
++ return (NULL);
++ }
++
++ /* If didn't find anything, return NULL. */
++ if (!nitems) {
++ if (type == BREAD)
++ cur->fts_info = FTS_DP;
++ return (NULL);
++ }
++
++ /* Sort the entries. */
++ if (sp->fts_compar && nitems > 1)
++ head = fts_sort(sp, head, nitems);
++ return (head);
++}
++
++
++FTS *
++fts_open(char * const *argv, register int options, int (*compar)())
++{
++ register FTS *sp;
++ register FTSENT *p, *root;
++ register int nitems;
++ FTSENT *parent, *tmp;
++ int len;
++
++ /* Options check. */
++ if (options & ~FTS_OPTIONMASK) {
++ errno = EINVAL;
++ return (NULL);
++ }
++
++ /* Allocate/initialize the stream */
++ if ((sp = malloc((u_int)sizeof(FTS))) == NULL)
++ return (NULL);
++ memset(sp, 0, sizeof(FTS));
++ sp->fts_compar = compar;
++ sp->fts_options = options;
++
++ /* Logical walks turn on NOCHDIR; symbolic links are too hard. */
++ if (ISSET(FTS_LOGICAL))
++ SET(FTS_NOCHDIR);
++
++ /*
++ * Start out with 1K of path space, and enough, in any case,
++ * to hold the user's paths.
++ */
++ if (fts_palloc(sp, MAX(fts_maxarglen(argv), MAXPATHLEN)))
++ goto mem1;
++
++ /* Allocate/initialize root's parent. */
++ if ((parent = fts_alloc(sp, "", 0)) == NULL)
++ goto mem2;
++ parent->fts_level = FTS_ROOTPARENTLEVEL;
++
++ /* Allocate/initialize root(s). */
++ for (root = NULL, nitems = 0; *argv; ++argv, ++nitems) {
++ /* Don't allow zero-length paths. */
++ if ((len = strlen(*argv)) == 0) {
++ errno = EINVAL;
++ goto mem3;
++ }
++
++ p = fts_alloc(sp, *argv, len);
++ p->fts_level = FTS_ROOTLEVEL;
++ p->fts_parent = parent;
++ p->fts_accpath = p->fts_name;
++ p->fts_info = fts_stat(sp, NULL, p, ISSET(FTS_COMFOLLOW));
++
++ /* Command-line "." and ".." are real directories. */
++ if (p->fts_info == FTS_DOT)
++ p->fts_info = FTS_D;
++
++ /*
++ * If comparison routine supplied, traverse in sorted
++ * order; otherwise traverse in the order specified.
++ */
++ if (compar) {
++ p->fts_link = root;
++ root = p;
++ } else {
++ p->fts_link = NULL;
++ if (root == NULL)
++ tmp = root = p;
++ else {
++ tmp->fts_link = p;
++ tmp = p;
++ }
+ }
+- ++nitems;
+ }
+- (void)closedir(dirp);
+-
+- /*
+- * If had to realloc the path, adjust the addresses for the rest
+- * of the tree.
+- */
+- if (adjaddr)
+- fts_padjust(sp, adjaddr);
++ if (compar && nitems > 1)
++ root = fts_sort(sp, root, nitems);
+
+ /*
+- * If not changing directories, reset the path back to original
+- * state.
++ * Allocate a dummy pointer and make fts_read think that we've just
++ * finished the node before the root(s); set p->fts_info to FTS_INIT
++ * so that everything about the "current" node is ignored.
+ */
+- if (ISSET(FTS_NOCHDIR)) {
+- if (cp - 1 > sp->fts_path)
+- --cp;
+- *cp = '\0';
+- }
++ if ((sp->fts_cur = fts_alloc(sp, "", 0)) == NULL)
++ goto mem3;
++ sp->fts_cur->fts_link = root;
++ sp->fts_cur->fts_info = FTS_INIT;
+
+ /*
+- * If descended after called from fts_children or after called from
+- * fts_read and nothing found, get back. At the root level we use
+- * the saved fd; if one of fts_open()'s arguments is a relative path
+- * to an empty directory, we wind up here with no other way back. If
+- * can't get back, we're done.
++ * If using chdir(2), grab a file descriptor pointing to dot to insure
++ * that we can get back here; this could be avoided for some paths,
++ * but almost certainly not worth the effort. Slashes, symbolic links,
++ * and ".." are all fairly nasty problems. Note, if we can't get the
++ * descriptor we run anyway, just more slowly.
+ */
+- if (descend && (type == BCHILD || !nitems) &&
+- (cur->fts_level == FTS_ROOTLEVEL ?
+- FCHDIR(sp, sp->fts_rfd) : CHDIR(sp, ".."))) {
+- cur->fts_info = FTS_ERR;
+- SET(FTS_STOP);
+- return (NULL);
+- }
++ if (!ISSET(FTS_NOCHDIR) && (sp->fts_rfd = open(".", O_RDONLY, 0)) < 0)
++ SET(FTS_NOCHDIR);
+
+- /* If didn't find anything, return NULL. */
+- if (!nitems) {
+- if (type == BREAD)
+- cur->fts_info = FTS_DP;
+- return (NULL);
+- }
++ return (sp);
+
+- /* Sort the entries. */
+- if (sp->fts_compar && nitems > 1)
+- head = fts_sort(sp, head, nitems);
+- return (head);
++mem3: fts_lfree(root);
++ free(parent);
++mem2: free(sp->fts_path);
++mem1: free(sp);
++ return (NULL);
+ }
+
+-static u_short
+-fts_stat(sp, dp, p, follow)
+- FTS *sp;
+- register FTSENT *p;
+- struct dirent *dp;
+- int follow;
++FTSENT *
++fts_read(register FTS *sp)
+ {
+- register FTSENT *t;
+- register dev_t dev;
+- register ino_t ino;
+- struct stat *sbp, sb;
++ register FTSENT *p, *tmp;
++ register int instr;
++ register char *t;
+ int saved_errno;
+
+- /* If user needs stat info, stat buffer already allocated. */
+- sbp = ISSET(FTS_NOSTAT) ? &sb : p->fts_statp;
++ /* If finished or unrecoverable error, return NULL. */
++ if (sp->fts_cur == NULL || ISSET(FTS_STOP))
++ return (NULL);
+
+-#ifdef S_IFWHT
+- /*
+- * Whited-out files don't really exist. However, there's stat(2) file
+- * mask for them, so we set it so that programs (i.e., find) don't have
+- * to test FTS_W separately from other file types.
+- */
+- if (dp != NULL && dp->d_type == DT_WHT) {
+- memset(sbp, 0, sizeof(struct stat));
+- sbp->st_mode = S_IFWHT;
+- return (FTS_W);
++ /* Set current node pointer. */
++ p = sp->fts_cur;
++
++ /* Save and zero out user instructions. */
++ instr = p->fts_instr;
++ p->fts_instr = FTS_NOINSTR;
++
++ /* Any type of file may be re-visited; re-stat and re-turn. */
++ if (instr == FTS_AGAIN) {
++ p->fts_info = fts_stat(sp, NULL, p, 0);
++ return (p);
+ }
+-#endif
+-
++
+ /*
+- * If doing a logical walk, or application requested FTS_FOLLOW, do
+- * a stat(2). If that fails, check for a non-existent symlink. If
+- * fail, set the errno from the stat call.
++ * Following a symlink -- SLNONE test allows application to see
++ * SLNONE and recover. If indirecting through a symlink, have
++ * keep a pointer to current location. If unable to get that
++ * pointer, follow fails.
+ */
+- if (ISSET(FTS_LOGICAL) || follow) {
+- if (stat(p->fts_accpath, sbp)) {
+- saved_errno = errno;
+- if (!lstat(p->fts_accpath, sbp)) {
+- errno = 0;
+- return (FTS_SLNONE);
+- }
+- p->fts_errno = saved_errno;
+- goto err;
++ if (instr == FTS_FOLLOW &&
++ (p->fts_info == FTS_SL || p->fts_info == FTS_SLNONE)) {
++ p->fts_info = fts_stat(sp, NULL, p, 1);
++ if (p->fts_info == FTS_D && !ISSET(FTS_NOCHDIR))
++ if ((p->fts_symfd = open(".", O_RDONLY, 0)) < 0) {
++ p->fts_errno = errno;
++ p->fts_info = FTS_ERR;
++ } else
++ p->fts_flags |= FTS_SYMFOLLOW;
++ return (p);
++ }
++
++ /* Directory in pre-order. */
++ if (p->fts_info == FTS_D) {
++ /* If skipped or crossed mount point, do post-order visit. */
++ if (instr == FTS_SKIP ||
++ ISSET(FTS_XDEV) && p->fts_dev != sp->fts_dev) {
++ if (p->fts_flags & FTS_SYMFOLLOW)
++ (void)close(p->fts_symfd);
++ if (sp->fts_child) {
++ fts_lfree(sp->fts_child);
++ sp->fts_child = NULL;
++ }
++ p->fts_info = FTS_DP;
++ return (p);
+ }
+- } else if (lstat(p->fts_accpath, sbp)) {
+- p->fts_errno = errno;
+-err: memset(sbp, 0, sizeof(struct stat));
+- return (FTS_NS);
++
++ /* Rebuild if only read the names and now traversing. */
++ if (sp->fts_child && sp->fts_options & FTS_NAMEONLY) {
++ sp->fts_options &= ~FTS_NAMEONLY;
++ fts_lfree(sp->fts_child);
++ sp->fts_child = NULL;
++ }
++
++ /*
++ * Cd to the subdirectory.
++ *
++ * If have already read and now fail to chdir, whack the list
++ * to make the names come out right, and set the parent errno
++ * so the application will eventually get an error condition.
++ * Set the FTS_DONTCHDIR flag so that when we logically change
++ * directories back to the parent we don't do a chdir.
++ *
++ * If haven't read do so. If the read fails, fts_build sets
++ * FTS_STOP or the fts_info field of the node.
++ */
++ if (sp->fts_child) {
++ if (CHDIR(sp, p->fts_accpath)) {
++ p->fts_errno = errno;
++ p->fts_flags |= FTS_DONTCHDIR;
++ for (p = sp->fts_child; p; p = p->fts_link)
++ p->fts_accpath =
++ p->fts_parent->fts_accpath;
++ }
++ } else if ((sp->fts_child = fts_build(sp, BREAD)) == NULL) {
++ if (ISSET(FTS_STOP))
++ return (NULL);
++ return (p);
++ }
++ p = sp->fts_child;
++ sp->fts_child = NULL;
++ goto name;
+ }
+
+- if (S_ISDIR(sbp->st_mode)) {
++ /* Move to the next node on this level. */
++next: tmp = p;
++ if (p = p->fts_link) {
++ free(tmp);
++
+ /*
+- * Set the device/inode. Used to find cycles and check for
+- * crossing mount points. Also remember the link count, used
+- * in fts_build to limit the number of stat calls. It is
+- * understood that these fields are only referenced if fts_info
+- * is set to FTS_D.
++ * If reached the top, return to the original directory, and
++ * load the paths for the next root.
+ */
+- dev = p->fts_dev = sbp->st_dev;
+- ino = p->fts_ino = sbp->st_ino;
+- p->fts_nlink = sbp->st_nlink;
++ if (p->fts_level == FTS_ROOTLEVEL) {
++ if (!ISSET(FTS_NOCHDIR) && FCHDIR(sp, sp->fts_rfd)) {
++ SET(FTS_STOP);
++ return (NULL);
++ }
++ fts_load(sp, p);
++ return (sp->fts_cur = p);
++ }
++
++ /*
++ * User may have called fts_set on the node. If skipped,
++ * ignore. If followed, get a file descriptor so we can
++ * get back if necessary.
++ */
++ if (p->fts_instr == FTS_SKIP)
++ goto next;
++ if (p->fts_instr == FTS_FOLLOW) {
++ p->fts_info = fts_stat(sp, NULL, p, 1);
++ if (p->fts_info == FTS_D && !ISSET(FTS_NOCHDIR))
++ if ((p->fts_symfd =
++ open(".", O_RDONLY, 0)) < 0) {
++ p->fts_errno = errno;
++ p->fts_info = FTS_ERR;
++ } else
++ p->fts_flags |= FTS_SYMFOLLOW;
++ p->fts_instr = FTS_NOINSTR;
++ }
+
+- if (ISDOT(p->fts_name))
+- return (FTS_DOT);
++name: t = sp->fts_path + NAPPEND(p->fts_parent);
++ *t++ = '/';
++ memmove(t, p->fts_name, p->fts_namelen + 1);
++ return (sp->fts_cur = p);
++ }
+
++ /* Move up to the parent node. */
++ p = tmp->fts_parent;
++ free(tmp);
++
++ if (p->fts_level == FTS_ROOTPARENTLEVEL) {
+ /*
+- * Cycle detection is done by brute force when the directory
+- * is first encountered. If the tree gets deep enough or the
+- * number of symbolic links to directories is high enough,
+- * something faster might be worthwhile.
++ * Done; free everything up and set errno to 0 so the user
++ * can distinguish between error and EOF.
+ */
+- for (t = p->fts_parent;
+- t->fts_level >= FTS_ROOTLEVEL; t = t->fts_parent)
+- if (ino == t->fts_ino && dev == t->fts_dev) {
+- p->fts_cycle = t;
+- return (FTS_DC);
+- }
+- return (FTS_D);
++ free(p);
++ errno = 0;
++ return (sp->fts_cur = NULL);
+ }
+- if (S_ISLNK(sbp->st_mode))
+- return (FTS_SL);
+- if (S_ISREG(sbp->st_mode))
+- return (FTS_F);
+- return (FTS_DEFAULT);
+-}
+
+-static FTSENT *
+-fts_sort(sp, head, nitems)
+- FTS *sp;
+- FTSENT *head;
+- register int nitems;
+-{
+- register FTSENT **ap, *p;
++ /* Nul terminate the pathname. */
++ sp->fts_path[p->fts_pathlen] = '\0';
+
+ /*
+- * Construct an array of pointers to the structures and call qsort(3).
+- * Reassemble the array in the order returned by qsort. If unable to
+- * sort for memory reasons, return the directory entries in their
+- * current order. Allocate enough space for the current needs plus
+- * 40 so don't realloc one entry at a time.
++ * Return to the parent directory. If at a root node or came through
++ * a symlink, go back through the file descriptor. Otherwise, cd up
++ * one directory.
+ */
+- if (nitems > sp->fts_nitems) {
+- sp->fts_nitems = nitems + 40;
+- if ((sp->fts_array = realloc(sp->fts_array,
+- (size_t)(sp->fts_nitems * sizeof(FTSENT *)))) == NULL) {
+- sp->fts_nitems = 0;
+- return (head);
++ if (p->fts_level == FTS_ROOTLEVEL) {
++ if (!ISSET(FTS_NOCHDIR) && FCHDIR(sp, sp->fts_rfd)) {
++ SET(FTS_STOP);
++ return (NULL);
++ }
++ } else if (p->fts_flags & FTS_SYMFOLLOW) {
++ if (FCHDIR(sp, p->fts_symfd)) {
++ saved_errno = errno;
++ (void)close(p->fts_symfd);
++ errno = saved_errno;
++ SET(FTS_STOP);
++ return (NULL);
++ }
++ (void)close(p->fts_symfd);
++ } else if (!(p->fts_flags & FTS_DONTCHDIR)) {
++ if (CHDIR(sp, "..")) {
++ SET(FTS_STOP);
++ return (NULL);
+ }
+ }
+- for (ap = sp->fts_array, p = head; p; p = p->fts_link)
+- *ap++ = p;
+- qsort((void *)sp->fts_array, nitems, sizeof(FTSENT *), sp->fts_compar);
+- for (head = *(ap = sp->fts_array); --nitems; ++ap)
+- ap[0]->fts_link = ap[1];
+- ap[0]->fts_link = NULL;
+- return (head);
++ p->fts_info = p->fts_errno ? FTS_ERR : FTS_DP;
++ return (sp->fts_cur = p);
+ }
+
+-static FTSENT *
+-fts_alloc(sp, name, namelen)
+- FTS *sp;
+- char *name;
+- register int namelen;
++/*
++ * Fts_set takes the stream as an argument although it's not used in this
++ * implementation; it would be necessary if anyone wanted to add global
++ * semantics to fts using fts_set. An error return is allowed for similar
++ * reasons.
++ */
++/* ARGSUSED */
++int
++fts_set(FTS *sp, FTSENT *p, int instr)
++{
++ if (instr && instr != FTS_AGAIN && instr != FTS_FOLLOW &&
++ instr != FTS_NOINSTR && instr != FTS_SKIP) {
++ errno = EINVAL;
++ return (1);
++ }
++ p->fts_instr = instr;
++ return (0);
++}
++
++FTSENT *
++fts_children(register FTS *sp, int instr)
+ {
+ register FTSENT *p;
+- size_t len;
++ int fd;
++
++ if (instr && instr != FTS_NAMEONLY) {
++ errno = EINVAL;
++ return (NULL);
++ }
++
++ /* Set current node pointer. */
++ p = sp->fts_cur;
+
+ /*
+- * The file name is a variable length array and no stat structure is
+- * necessary if the user has set the nostat bit. Allocate the FTSENT
+- * structure, the file name and the stat structure in one chunk, but
+- * be careful that the stat structure is reasonably aligned. Since the
+- * fts_name field is declared to be of size 1, the fts_name pointer is
+- * namelen + 2 before the first possible address of the stat structure.
++ * Errno set to 0 so user can distinguish empty directory from
++ * an error.
+ */
+- len = sizeof(FTSENT) + namelen;
+- if (!ISSET(FTS_NOSTAT))
+- len += sizeof(struct stat) + ALIGNBYTES;
+- if ((p = malloc(len)) == NULL)
++ errno = 0;
++
++ /* Fatal errors stop here. */
++ if (ISSET(FTS_STOP))
+ return (NULL);
+
+- /* Copy the name plus the trailing NULL. */
+- memmove(p->fts_name, name, namelen + 1);
++ /* Return logical hierarchy of user's arguments. */
++ if (p->fts_info == FTS_INIT)
++ return (p->fts_link);
+
+- if (!ISSET(FTS_NOSTAT))
+- p->fts_statp = (struct stat *)ALIGN(p->fts_name + namelen + 2);
+- p->fts_namelen = namelen;
+- p->fts_path = sp->fts_path;
+- p->fts_errno = 0;
+- p->fts_flags = 0;
+- p->fts_instr = FTS_NOINSTR;
+- p->fts_number = 0;
+- p->fts_pointer = NULL;
+- return (p);
++ /*
++ * If not a directory being visited in pre-order, stop here. Could
++ * allow FTS_DNR, assuming the user has fixed the problem, but the
++ * same effect is available with FTS_AGAIN.
++ */
++ if (p->fts_info != FTS_D /* && p->fts_info != FTS_DNR */)
++ return (NULL);
++
++ /* Free up any previous child list. */
++ if (sp->fts_child)
++ fts_lfree(sp->fts_child);
++
++ if (instr == FTS_NAMEONLY) {
++ sp->fts_options |= FTS_NAMEONLY;
++ instr = BNAMES;
++ } else
++ instr = BCHILD;
++
++ /*
++ * If using chdir on a relative path and called BEFORE fts_read does
++ * its chdir to the root of a traversal, we can lose -- we need to
++ * chdir into the subdirectory, and we don't know where the current
++ * directory is, so we can't get back so that the upcoming chdir by
++ * fts_read will work.
++ */
++ if (p->fts_level != FTS_ROOTLEVEL || p->fts_accpath[0] == '/' ||
++ ISSET(FTS_NOCHDIR))
++ return (sp->fts_child = fts_build(sp, instr));
++
++ if ((fd = open(".", O_RDONLY, 0)) < 0)
++ return (NULL);
++ sp->fts_child = fts_build(sp, instr);
++ if (fchdir(fd))
++ return (NULL);
++ (void)close(fd);
++ return (sp->fts_child);
+ }
+
+-static void
+-fts_lfree(head)
+- register FTSENT *head;
++int
++fts_close(FTS *sp)
+ {
+- register FTSENT *p;
++ register FTSENT *freep, *p;
++ int saved_errno;
+
+- /* Free a linked list of structures. */
+- while (p = head) {
+- head = head->fts_link;
++ /*
++ * This still works if we haven't read anything -- the dummy structure
++ * points to the root list, so we step through to the end of the root
++ * list which has a valid parent pointer.
++ */
++ if (sp->fts_cur) {
++ for (p = sp->fts_cur; p->fts_level >= FTS_ROOTLEVEL;) {
++ freep = p;
++ p = p->fts_link ? p->fts_link : p->fts_parent;
++ free(freep);
++ }
+ free(p);
+ }
+-}
+
+-/*
+- * Allow essentially unlimited paths; find, rm, ls should all work on any tree.
+- * Most systems will allow creation of paths much longer than MAXPATHLEN, even
+- * though the kernel won't resolve them. Add the size (not just what's needed)
+- * plus 256 bytes so don't realloc the path 2 bytes at a time.
+- */
+-static int
+-fts_palloc(sp, more)
+- FTS *sp;
+- size_t more;
+-{
+- sp->fts_pathlen += more + 256;
+- sp->fts_path = realloc(sp->fts_path, (size_t)sp->fts_pathlen);
+- return (sp->fts_path == NULL);
+-}
++ /* Free up child linked list, sort array, path buffer. */
++ if (sp->fts_child)
++ fts_lfree(sp->fts_child);
++ if (sp->fts_array)
++ free(sp->fts_array);
++ free(sp->fts_path);
+
+-/*
+- * When the path is realloc'd, have to fix all of the pointers in structures
+- * already returned.
+- */
+-static void
+-fts_padjust(sp, addr)
+- FTS *sp;
+- void *addr;
+-{
+- FTSENT *p;
++ /* Return to original directory, save errno if necessary. */
++ if (!ISSET(FTS_NOCHDIR)) {
++ saved_errno = fchdir(sp->fts_rfd) ? errno : 0;
++ (void)close(sp->fts_rfd);
++ }
+
+-#define ADJUST(p) { \
+- (p)->fts_accpath = \
+- (char *)addr + ((p)->fts_accpath - (p)->fts_path); \
+- (p)->fts_path = addr; \
+-}
+- /* Adjust the current set of children. */
+- for (p = sp->fts_child; p; p = p->fts_link)
+- ADJUST(p);
++ /* Free up the stream pointer. */
++ free(sp);
+
+- /* Adjust the rest of the tree. */
+- for (p = sp->fts_cur; p->fts_level >= FTS_ROOTLEVEL;) {
+- ADJUST(p);
+- p = p->fts_link ? p->fts_link : p->fts_parent;
++ /* Set errno and return. */
++ if (!ISSET(FTS_NOCHDIR) && saved_errno) {
++ errno = saved_errno;
++ return (-1);
+ }
++ return (0);
+ }
+
+-static size_t
+-fts_maxarglen(argv)
+- char * const *argv;
+-{
+- size_t len, max;
+-
+- for (max = 0; *argv; ++argv)
+- if ((len = strlen(*argv)) > max)
+- max = len;
+- return (max);
+-}
diff --git a/meta/recipes-core/fts/fts/remove_cdefs.patch b/meta/recipes-core/fts/fts/remove_cdefs.patch
new file mode 100644
index 0000000000..c152704d44
--- /dev/null
+++ b/meta/recipes-core/fts/fts/remove_cdefs.patch
@@ -0,0 +1,69 @@
+Replace use of macros from sys/cdefs.h since cdefs.h is missing on musl
+
+Signed-off-by: Khem Raj <raj.khem@gmail.com>
+Upstream-Status: Inappropriate
+
+Index: fts/fts.h
+===================================================================
+--- fts.orig/fts.h
++++ fts/fts.h
+@@ -126,15 +126,21 @@ typedef struct _ftsent {
+ char fts_name[1]; /* file name */
+ } FTSENT;
+
+-#include <sys/cdefs.h>
++#ifdef __cplusplus
++extern "C" {
++#endif
+
+-__BEGIN_DECLS
+-FTSENT *fts_children __P((FTS *, int));
+-int fts_close __P((FTS *));
+-FTS *fts_open __P((char * const *, int,
+- int (*)(const FTSENT **, const FTSENT **)));
+-FTSENT *fts_read __P((FTS *));
+-int fts_set __P((FTS *, FTSENT *, int));
+-__END_DECLS
++#ifndef __P
++#define __P
++#endif
++FTSENT *fts_children (FTS *p, int opts);
++int fts_close (FTS *p);
++FTS *fts_open (char * const * path, int opts,
++ int (*compfn)(const FTSENT **, const FTSENT **));
++FTSENT *fts_read (FTS *p);
++int fts_set (FTS *p, FTSENT *f, int opts);
+
++#ifdef __cplusplus
++}
++#endif
+ #endif /* !_FTS_H_ */
+Index: fts/fts.c
+===================================================================
+--- fts.orig/fts.c
++++ fts/fts.c
+@@ -50,15 +50,15 @@ static char sccsid[] = "@(#)fts.c 8.6 (B
+ #include <string.h>
+ #include <unistd.h>
+
+-static FTSENT *fts_alloc __P((FTS *, char *, int));
+-static FTSENT *fts_build __P((FTS *, int));
+-static void fts_lfree __P((FTSENT *));
+-static void fts_load __P((FTS *, FTSENT *));
+-static size_t fts_maxarglen __P((char * const *));
+-static void fts_padjust __P((FTS *, void *));
+-static int fts_palloc __P((FTS *, size_t));
+-static FTSENT *fts_sort __P((FTS *, FTSENT *, int));
+-static u_short fts_stat __P((FTS *, struct dirent *, FTSENT *, int));
++static FTSENT *fts_alloc __P(FTS *, char *, int);
++static FTSENT *fts_build __P(FTS *, int);
++static void fts_lfree __P(FTSENT *);
++static void fts_load __P(FTS *, FTSENT *);
++static size_t fts_maxarglen __P(char * const *);
++static void fts_padjust __P(FTS *, void *);
++static int fts_palloc __P(FTS *, size_t);
++static FTSENT *fts_sort __P(FTS *, FTSENT *, int);
++static u_short fts_stat __P(FTS *, struct dirent *, FTSENT *, int);
+
+ #define ISDOT(a) (a[0] == '.' && (!a[1] || a[1] == '.' && !a[2]))
+
diff --git a/meta/recipes-core/fts/fts/stdint.patch b/meta/recipes-core/fts/fts/stdint.patch
new file mode 100644
index 0000000000..89e6097fcb
--- /dev/null
+++ b/meta/recipes-core/fts/fts/stdint.patch
@@ -0,0 +1,15 @@
+Include stdint.h for u_* typedefs
+
+Signed-off-by: Khem Raj <raj.khem@gmail.com>
+Upstream-Status: Inappropriate
+
+--- ./fts.c.orig
++++ ./fts.c
+@@ -46,6 +46,7 @@
+ #include <errno.h>
+ #include <fcntl.h>
+ #include <fts.h>
++#include <stdint.h>
+ #include <stdlib.h>
+ #include <string.h>
+ #include <unistd.h>