aboutsummaryrefslogtreecommitdiff
path: root/io
diff options
context:
space:
mode:
Diffstat (limited to 'io')
-rw-r--r--io/Makefile5
-rw-r--r--io/Versions3
-rw-r--r--io/fts.c148
-rw-r--r--io/fts.h96
-rw-r--r--io/fts64.c30
-rw-r--r--io/tst-fts-lfs.c2
-rw-r--r--io/tst-fts.c231
7 files changed, 449 insertions, 66 deletions
diff --git a/io/Makefile b/io/Makefile
index 613dce0..a9ad919 100644
--- a/io/Makefile
+++ b/io/Makefile
@@ -49,7 +49,7 @@ routines := \
ttyname ttyname_r isatty \
link linkat symlink symlinkat readlink readlinkat \
unlink unlinkat rmdir \
- ftw ftw64 fts poll ppoll \
+ ftw ftw64 fts fts64 poll ppoll \
posix_fadvise posix_fadvise64 \
posix_fallocate posix_fallocate64 \
sendfile sendfile64 \
@@ -71,7 +71,7 @@ tests := test-utime test-stat test-stat2 test-lfs tst-getcwd \
tst-renameat tst-fchownat tst-fchmodat tst-faccessat \
tst-symlinkat tst-linkat tst-readlinkat tst-mkdirat \
tst-mknodat tst-mkfifoat tst-ttyname_r bug-ftw5 \
- tst-posix_fallocate
+ tst-posix_fallocate tst-fts tst-fts-lfs
ifeq ($(run-built-tests),yes)
tests-special += $(objpfx)ftwtest.out
@@ -90,6 +90,7 @@ CFLAGS-fstatfs.c = -fexceptions
CFLAGS-statvfs.c = -fexceptions
CFLAGS-fstatvfs.c = -fexceptions
CFLAGS-fts.c = -Wno-uninitialized $(uses-callbacks) -fexceptions
+CFLAGS-fts64.c = -Wno-uninitialized $(uses-callbacks) -fexceptions
CFLAGS-ftw.c = $(uses-callbacks) -fexceptions
CFLAGS-ftw64.c = $(uses-callbacks) -fexceptions
CFLAGS-lockf.c = -fexceptions
diff --git a/io/Versions b/io/Versions
index 6c0a23b..64316cd 100644
--- a/io/Versions
+++ b/io/Versions
@@ -122,4 +122,7 @@ libc {
GLIBC_2.9 {
dup3; pipe2;
}
+ GLIBC_2.23 {
+ fts64_children; fts64_close; fts64_open; fts64_read; fts64_set;
+ }
}
diff --git a/io/fts.c b/io/fts.c
index f832676..e8d32a2 100644
--- a/io/fts.c
+++ b/io/fts.c
@@ -1,3 +1,21 @@
+/* File tree traversal functions.
+ Copyright (C) 1994-2015 Free Software Foundation, Inc.
+ This file is part of the GNU C Library.
+
+ The GNU C Library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Lesser General Public
+ License as published by the Free Software Foundation; either
+ version 2.1 of the License, or (at your option) any later version.
+
+ The GNU C Library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public
+ License along with the GNU C Library; if not, see
+ <http://www.gnu.org/licenses/>. */
+
/*-
* Copyright (c) 1990, 1993, 1994
* The Regents of the University of California. All rights reserved.
@@ -53,16 +71,30 @@ static char sccsid[] = "@(#)fts.c 8.6 (Berkeley) 8/14/94";
#endif
-static FTSENT *fts_alloc (FTS *, const char *, size_t) internal_function;
-static FTSENT *fts_build (FTS *, int) internal_function;
-static void fts_lfree (FTSENT *) internal_function;
-static void fts_load (FTS *, FTSENT *) internal_function;
+/* Support for the LFS API version. */
+#ifndef FTS_OPEN
+#define FTS_OPEN fts_open
+#define FTS_CLOSE fts_close
+#define FTS_READ fts_read
+#define FTS_SET fts_set
+#define FTS_CHILDREN fts_children
+# define FTSOBJ FTS
+# define FTSENTRY FTSENT
+# define INO_T ino_t
+# define STAT stat
+# define LSTAT lstat
+#endif
+
+static FTSENTRY *fts_alloc (FTSOBJ *, const char *, size_t) internal_function;
+static FTSENTRY *fts_build (FTSOBJ *, int) internal_function;
+static void fts_lfree (FTSENTRY *) internal_function;
+static void fts_load (FTSOBJ *, FTSENTRY *) internal_function;
static size_t fts_maxarglen (char * const *) internal_function;
-static void fts_padjust (FTS *, FTSENT *) internal_function;
-static int fts_palloc (FTS *, size_t) internal_function;
-static FTSENT *fts_sort (FTS *, FTSENT *, int) internal_function;
-static u_short fts_stat (FTS *, FTSENT *, int) internal_function;
-static int fts_safe_changedir (FTS *, FTSENT *, int, const char *)
+static void fts_padjust (FTSOBJ *, FTSENTRY *) internal_function;
+static int fts_palloc (FTSOBJ *, size_t) internal_function;
+static FTSENTRY *fts_sort (FTSOBJ *, FTSENTRY *, int) internal_function;
+static u_short fts_stat (FTSOBJ *, FTSENTRY *, int) internal_function;
+static int fts_safe_changedir (FTSOBJ *, FTSENTRY *, int, const char *)
internal_function;
#ifndef MAX
@@ -84,15 +116,15 @@ static int fts_safe_changedir (FTS *, FTSENT *, int, const char *)
#define BNAMES 2 /* fts_children, names only */
#define BREAD 3 /* fts_read */
-FTS *
-fts_open (char * const *argv, int options,
- int (*compar) (const FTSENT **, const FTSENT **))
+FTSOBJ *
+FTS_OPEN (char * const *argv, int options,
+ int (*compar) (const FTSENTRY **, const FTSENTRY **))
{
- FTS *sp;
- FTSENT *p, *root;
+ FTSOBJ *sp;
+ FTSENTRY *p, *root;
int nitems;
- FTSENT *parent = NULL;
- FTSENT *tmp;
+ FTSENTRY *parent = NULL;
+ FTSENTRY *tmp;
/* Options check. */
if (options & ~FTS_OPTIONMASK) {
@@ -101,9 +133,9 @@ fts_open (char * const *argv, int options,
}
/* Allocate/initialize the stream */
- if ((sp = malloc((u_int)sizeof(FTS))) == NULL)
+ if ((sp = malloc((u_int)sizeof(FTSOBJ))) == NULL)
return (NULL);
- memset(sp, 0, sizeof(FTS));
+ memset(sp, 0, sizeof(FTSOBJ));
sp->fts_compar = (int (*) (const void *, const void *)) compar;
sp->fts_options = options;
@@ -200,7 +232,7 @@ mem1: free(sp);
static void
internal_function
-fts_load (FTS *sp, FTSENT *p)
+fts_load (FTSOBJ *sp, FTSENTRY *p)
{
int len;
char *cp;
@@ -224,9 +256,9 @@ fts_load (FTS *sp, FTSENT *p)
}
int
-fts_close (FTS *sp)
+FTS_CLOSE (FTSOBJ *sp)
{
- FTSENT *freep, *p;
+ FTSENTRY *freep, *p;
int saved_errno;
/*
@@ -276,10 +308,10 @@ fts_close (FTS *sp)
(p->fts_path[p->fts_pathlen - 1] == '/' \
? p->fts_pathlen - 1 : p->fts_pathlen)
-FTSENT *
-fts_read (FTS *sp)
+FTSENTRY *
+FTS_READ (FTSOBJ *sp)
{
- FTSENT *p, *tmp;
+ FTSENTRY *p, *tmp;
int instr;
char *t;
int saved_errno;
@@ -473,7 +505,7 @@ name: t = sp->fts_path + NAPPEND(p->fts_parent);
*/
/* ARGSUSED */
int
-fts_set (FTS *sp, FTSENT *p, int instr)
+FTS_SET (FTSOBJ *sp, FTSENTRY *p, int instr)
{
if (instr != 0 && instr != FTS_AGAIN && instr != FTS_FOLLOW &&
instr != FTS_NOINSTR && instr != FTS_SKIP) {
@@ -484,10 +516,10 @@ fts_set (FTS *sp, FTSENT *p, int instr)
return (0);
}
-FTSENT *
-fts_children (FTS *sp, int instr)
+FTSENTRY *
+FTS_CHILDREN(FTSOBJ *sp, int instr)
{
- FTSENT *p;
+ FTSENTRY *p;
int fd;
if (instr != 0 && instr != FTS_NAMEONLY) {
@@ -574,14 +606,14 @@ dirent_not_directory(const struct dirent *dp)
* directories and for any files after the subdirectories in the directory have
* been found, cutting the stat calls by about 2/3.
*/
-static FTSENT *
+static FTSENTRY *
internal_function
-fts_build (FTS *sp, int type)
+fts_build (FTSOBJ *sp, int type)
{
struct dirent *dp;
- FTSENT *p, *head;
+ FTSENTRY *p, *head;
int nitems;
- FTSENT *cur, *tail;
+ FTSENTRY *cur, *tail;
DIR *dirp;
void *oldaddr;
int cderrno, descend, len, level, nlinks, saved_errno,
@@ -839,12 +871,12 @@ mem1: saved_errno = errno;
static u_short
internal_function
-fts_stat (FTS *sp, FTSENT *p, int follow)
+fts_stat (FTSOBJ *sp, FTSENTRY *p, int follow)
{
- FTSENT *t;
+ FTSENTRY *t;
dev_t dev;
- ino_t ino;
- struct stat *sbp, sb;
+ INO_T ino;
+ struct STAT *sbp, sb;
int saved_errno;
/* If user needs stat info, stat buffer already allocated. */
@@ -867,18 +899,18 @@ fts_stat (FTS *sp, FTSENT *p, int follow)
* fail, set the errno from the stat call.
*/
if (ISSET(FTS_LOGICAL) || follow) {
- if (stat(p->fts_accpath, sbp)) {
+ if (STAT(p->fts_accpath, sbp)) {
saved_errno = errno;
- if (!lstat(p->fts_accpath, sbp)) {
+ if (!LSTAT(p->fts_accpath, sbp)) {
__set_errno (0);
return (FTS_SLNONE);
}
p->fts_errno = saved_errno;
goto err;
}
- } else if (lstat(p->fts_accpath, sbp)) {
+ } else if (LSTAT(p->fts_accpath, sbp)) {
p->fts_errno = errno;
-err: memset(sbp, 0, sizeof(struct stat));
+err: memset(sbp, 0, sizeof(struct STAT));
return (FTS_NS);
}
@@ -918,11 +950,11 @@ err: memset(sbp, 0, sizeof(struct stat));
return (FTS_DEFAULT);
}
-static FTSENT *
+static FTSENTRY *
internal_function
-fts_sort (FTS *sp, FTSENT *head, int nitems)
+fts_sort (FTSOBJ *sp, FTSENTRY *head, int nitems)
{
- FTSENT **ap, *p;
+ FTSENTRY **ap, *p;
/*
* Construct an array of pointers to the structures and call qsort(3).
@@ -932,11 +964,11 @@ fts_sort (FTS *sp, FTSENT *head, int nitems)
* 40 so don't realloc one entry at a time.
*/
if (nitems > sp->fts_nitems) {
- struct _ftsent **a;
+ FTSENTRY **a;
sp->fts_nitems = nitems + 40;
if ((a = realloc(sp->fts_array,
- (size_t)(sp->fts_nitems * sizeof(FTSENT *)))) == NULL) {
+ (size_t)(sp->fts_nitems * sizeof(FTSENTRY *)))) == NULL) {
free(sp->fts_array);
sp->fts_array = NULL;
sp->fts_nitems = 0;
@@ -946,18 +978,18 @@ fts_sort (FTS *sp, FTSENT *head, int nitems)
}
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);
+ qsort((void *)sp->fts_array, nitems, sizeof(FTSENTRY *), sp->fts_compar);
for (head = *(ap = sp->fts_array); --nitems; ++ap)
ap[0]->fts_link = ap[1];
ap[0]->fts_link = NULL;
return (head);
}
-static FTSENT *
+static FTSENTRY *
internal_function
-fts_alloc (FTS *sp, const char *name, size_t namelen)
+fts_alloc (FTSOBJ *sp, const char *name, size_t namelen)
{
- FTSENT *p;
+ FTSENTRY *p;
size_t len;
/*
@@ -968,9 +1000,9 @@ fts_alloc (FTS *sp, const char *name, size_t namelen)
* 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;
+ len = sizeof(FTSENTRY) + namelen;
if (!ISSET(FTS_NOSTAT))
- len += sizeof(struct stat) + ALIGNBYTES;
+ len += sizeof(struct STAT) + ALIGNBYTES;
if ((p = malloc(len)) == NULL)
return (NULL);
@@ -979,7 +1011,7 @@ fts_alloc (FTS *sp, const char *name, size_t namelen)
p->fts_name[namelen] = '\0';
if (!ISSET(FTS_NOSTAT))
- p->fts_statp = (struct stat *)ALIGN(p->fts_name + namelen + 2);
+ 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;
@@ -992,9 +1024,9 @@ fts_alloc (FTS *sp, const char *name, size_t namelen)
static void
internal_function
-fts_lfree (FTSENT *head)
+fts_lfree (FTSENTRY *head)
{
- FTSENT *p;
+ FTSENTRY *p;
/* Free a linked list of structures. */
while ((p = head)) {
@@ -1011,7 +1043,7 @@ fts_lfree (FTSENT *head)
*/
static int
internal_function
-fts_palloc (FTS *sp, size_t more)
+fts_palloc (FTSOBJ *sp, size_t more)
{
char *p;
@@ -1043,9 +1075,9 @@ fts_palloc (FTS *sp, size_t more)
*/
static void
internal_function
-fts_padjust (FTS *sp, FTSENT *head)
+fts_padjust (FTSOBJ *sp, FTSENTRY *head)
{
- FTSENT *p;
+ FTSENTRY *p;
char *addr = sp->fts_path;
#define ADJUST(p) do { \
@@ -1085,7 +1117,7 @@ fts_maxarglen (char * const *argv)
*/
static int
internal_function
-fts_safe_changedir (FTS *sp, FTSENT *p, int fd, const char *path)
+fts_safe_changedir (FTSOBJ *sp, FTSENTRY *p, int fd, const char *path)
{
int ret, oerrno, newfd;
struct stat64 sb;
diff --git a/io/fts.h b/io/fts.h
index 0a070ba..2185e23 100644
--- a/io/fts.h
+++ b/io/fts.h
@@ -1,3 +1,21 @@
+/* File tree traversal functions declarations.
+ Copyright (C) 1994-2015 Free Software Foundation, Inc.
+ This file is part of the GNU C Library.
+
+ The GNU C Library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Lesser General Public
+ License as published by the Free Software Foundation; either
+ version 2.1 of the License, or (at your option) any later version.
+
+ The GNU C Library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public
+ License along with the GNU C Library; if not, see
+ <http://www.gnu.org/licenses/>. */
+
/*
* Copyright (c) 1989, 1993
* The Regents of the University of California. All rights reserved.
@@ -35,12 +53,6 @@
#include <features.h>
#include <sys/types.h>
-/* The fts interface is incompatible with the LFS interface which
- transparently uses the 64-bit file access functions. */
-#ifdef __USE_FILE_OFFSET64
-# error "<fts.h> cannot be used with -D_FILE_OFFSET_BITS==64"
-#endif
-
typedef struct {
struct _ftsent *fts_cur; /* current node */
@@ -68,6 +80,21 @@ typedef struct {
int fts_options; /* fts_open options, global flags */
} FTS;
+#ifdef __USE_LARGEFILE64
+typedef struct {
+ struct _ftsent64 *fts_cur; /* current node */
+ struct _ftsent64 *fts_child; /* linked list of children */
+ struct _ftsent64 **fts_array; /* sort array */
+ dev_t fts_dev; /* starting device # */
+ char *fts_path; /* path for this descent */
+ int fts_rfd; /* fd for root */
+ int fts_pathlen; /* sizeof(path) */
+ int fts_nitems; /* elements in the sort array */
+ int (*fts_compar) (const void *, const void *); /* compare fn */
+ int fts_options; /* fts_open options, global flags */
+} FTS64;
+#endif
+
typedef struct _ftsent {
struct _ftsent *fts_cycle; /* cycle node */
struct _ftsent *fts_parent; /* parent directory */
@@ -119,13 +146,70 @@ typedef struct _ftsent {
char fts_name[1]; /* file name */
} FTSENT;
+#ifdef __USE_LARGEFILE64
+typedef struct _ftsent64 {
+ struct _ftsent64 *fts_cycle; /* cycle node */
+ struct _ftsent64 *fts_parent; /* parent directory */
+ struct _ftsent64 *fts_link; /* next file in directory */
+ long fts_number; /* local numeric value */
+ void *fts_pointer; /* local address value */
+ char *fts_accpath; /* access path */
+ char *fts_path; /* root path */
+ int fts_errno; /* errno for this node */
+ int fts_symfd; /* fd for symlink */
+ u_short fts_pathlen; /* strlen(fts_path) */
+ u_short fts_namelen; /* strlen(fts_name) */
+
+ ino64_t fts_ino; /* inode */
+ dev_t fts_dev; /* device */
+ nlink_t fts_nlink; /* link count */
+
+ short fts_level; /* depth (-1 to N) */
+
+ u_short fts_info; /* user flags for FTSENT structure */
+
+ u_short fts_flags; /* private flags for FTSENT structure */
+
+ u_short fts_instr; /* fts_set() instructions */
+
+ struct stat64 *fts_statp; /* stat(2) information */
+ char fts_name[1]; /* file name */
+} FTSENT64;
+#endif
+
__BEGIN_DECLS
+#ifndef __USE_FILE_OFFSET64
FTSENT *fts_children (FTS *, int);
int fts_close (FTS *);
FTS *fts_open (char * const *, int,
int (*)(const FTSENT **, const FTSENT **));
FTSENT *fts_read (FTS *);
int fts_set (FTS *, FTSENT *, int) __THROW;
+#else
+# ifdef __REDIRECT
+FTSENT *__REDIRECT (fts_children, (FTS *, int), fts64_children);
+int __REDIRECT (fts_close, (FTS *), fts64_close);
+FTS *__REDIRECT (fts_open, (char * const *, int,
+ int (*)(const FTSENT **, const FTSENT **)),
+ fts64_open);
+FTSENT *__REDIRECT (fts_read, (FTS *), fts64_read);
+int __REDIRECT (fts_set, (FTS *, FTSENT *, int), fts64_set) __THROW;
+# else
+# define fts_children fts64_children
+# define fts_close fts64_close
+# define fts_open fts64_open
+# define fts_read fts64_read
+# define fts_set fts64_set
+# endif
+#endif
+#ifdef __USE_LARGEFILE64
+FTSENT64 *fts64_children (FTS64 *, int);
+int fts64_close (FTS64 *);
+FTS64 *fts64_open (char * const *, int,
+ int (*)(const FTSENT64 **, const FTSENT64 **));
+FTSENT64 *fts64_read (FTS64 *);
+int fts64_set (FTS64 *, FTSENT64 *, int) __THROW;
+#endif
__END_DECLS
#endif /* fts.h */
diff --git a/io/fts64.c b/io/fts64.c
new file mode 100644
index 0000000..bddf8c6
--- /dev/null
+++ b/io/fts64.c
@@ -0,0 +1,30 @@
+/* File tree traversal functions LFS version.
+ Copyright (C) 2015 Free Software Foundation, Inc.
+ This file is part of the GNU C Library.
+
+ The GNU C Library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Lesser General Public
+ License as published by the Free Software Foundation; either
+ version 2.1 of the License, or (at your option) any later version.
+
+ The GNU C Library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public
+ License along with the GNU C Library; if not, see
+ <http://www.gnu.org/licenses/>. */
+
+#define FTS_OPEN fts64_open
+#define FTS_CLOSE fts64_close
+#define FTS_READ fts64_read
+#define FTS_SET fts64_set
+#define FTS_CHILDREN fts64_children
+#define FTSOBJ FTS64
+#define FTSENTRY FTSENT64
+#define INO_T ino64_t
+#define STAT stat64
+#define LSTAT lstat64
+
+#include "fts.c"
diff --git a/io/tst-fts-lfs.c b/io/tst-fts-lfs.c
new file mode 100644
index 0000000..bc16934
--- /dev/null
+++ b/io/tst-fts-lfs.c
@@ -0,0 +1,2 @@
+#define _FILE_OFFSET_BITS 64
+#include "tst-fts.c"
diff --git a/io/tst-fts.c b/io/tst-fts.c
new file mode 100644
index 0000000..efe225d
--- /dev/null
+++ b/io/tst-fts.c
@@ -0,0 +1,231 @@
+/* Simple test for some fts functions.
+ Copyright (C) 2015 Free Software Foundation, Inc.
+ This file is part of the GNU C Library.
+
+ The GNU C Library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Lesser General Public
+ License as published by the Free Software Foundation; either
+ version 2.1 of the License, or (at your option) any later version.
+
+ The GNU C Library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public
+ License along with the GNU C Library; if not, see
+ <http://www.gnu.org/licenses/>. */
+
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fts.h>
+
+#include <errno.h>
+#include <error.h>
+#include <string.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+
+static void prepare (void);
+static int do_test (void);
+#define PREPARE(argc, argv) prepare ()
+#define TEST_FUNCTION do_test ()
+#include "../test-skeleton.c"
+
+static char *fts_test_dir;
+
+static void
+make_dir (const char *dirname)
+{
+ char *name;
+ if (asprintf (&name, "%s/%s", fts_test_dir, dirname) < 0)
+ {
+ puts ("out of memory");
+ exit (1);
+ }
+
+ if (mkdir (name, 0700) < 0)
+ {
+ printf ("cannot create dir \"%s\": %m\n", name);
+ exit (1);
+ }
+
+ add_temp_file (name);
+}
+
+static void
+make_file (const char *filename)
+{
+ char *name;
+ if (asprintf (&name, "%s/%s", fts_test_dir, filename) < 0)
+ {
+ puts ("out of memory");
+ exit (1);
+ }
+
+ int fd = open (name, O_WRONLY | O_CREAT | O_EXCL, 0600);
+ if (fd < 0)
+ {
+ printf ("cannot create file \"%s\": %m\n", name);
+ exit (1);
+ }
+ close (fd);
+
+ add_temp_file (name);
+}
+
+static void
+prepare (void)
+{
+ char *dirbuf;
+ char dir_name[] = "/tst-fts.XXXXXX";
+
+ if (asprintf (&dirbuf, "%s%s", test_dir, dir_name) < 0)
+ {
+ puts ("out of memory");
+ exit (1);
+ }
+
+ if (mkdtemp (dirbuf) == NULL)
+ {
+ puts ("cannot create temporary directory");
+ exit (1);
+ }
+
+ add_temp_file (dirbuf);
+ fts_test_dir = dirbuf;
+
+ make_file ("12");
+ make_file ("345");
+ make_file ("6789");
+
+ make_dir ("aaa");
+ make_file ("aaa/1234");
+ make_file ("aaa/5678");
+
+ make_dir ("bbb");
+ make_file ("bbb/1234");
+ make_file ("bbb/5678");
+ make_file ("bbb/90ab");
+}
+
+/* Largest name wins, otherwise strcmp. */
+static int
+compare_ents (const FTSENT **ent1, const FTSENT **ent2)
+{
+ short len1 = (*ent1)->fts_namelen;
+ short len2 = (*ent2)->fts_namelen;
+ if (len1 != len2)
+ return len1 - len2;
+ else
+ {
+ const char *name1 = (*ent1)->fts_name;
+ const char *name2 = (*ent2)->fts_name;
+ return strcmp (name1, name2);
+ }
+}
+
+/* Count the number of files seen as children. */
+static int files = 0;
+
+static void
+children (FTS *fts)
+{
+ FTSENT *child = fts_children (fts, 0);
+ if (child == NULL && errno != 0)
+ {
+ printf ("FAIL: fts_children: %m\n");
+ exit (1);
+ }
+
+ while (child != NULL)
+ {
+ short level = child->fts_level;
+ const char *name = child->fts_name;
+ if (child->fts_info == FTS_F || child->fts_info == FTS_NSOK)
+ {
+ files++;
+ printf ("%*s%s\n", 2 * level, "", name);
+ }
+ child = child->fts_link;
+ }
+}
+
+/* Count the number of dirs seen in the test. */
+static int dirs = 0;
+
+static int
+do_test (void)
+{
+ char *paths[2] = { fts_test_dir, NULL };
+ FTS *fts;
+ fts = fts_open (paths, FTS_LOGICAL, &compare_ents);
+ if (fts == NULL)
+ {
+ printf ("FAIL: fts_open: %m\n");
+ exit (1);
+ }
+
+ FTSENT *ent;
+ while ((ent = fts_read (fts)) != NULL)
+ {
+ const char *name = ent->fts_name;
+ short level = ent->fts_level;
+ switch (ent->fts_info)
+ {
+ case FTS_F:
+ /* Don't show anything, children will have on parent dir. */
+ break;
+
+ case FTS_D:
+ printf ("%*s%s =>\n", 2 * level, "", name);
+ children (fts);
+ break;
+
+ case FTS_DP:
+ dirs++;
+ printf ("%*s<= %s\n", 2 * level, "", name);
+ break;
+
+ case FTS_NS:
+ case FTS_ERR:
+ printf ("FAIL: fts_read ent: %s\n", strerror (ent->fts_errno));
+ exit (1);
+ break;
+
+ default:
+ printf ("FAIL: unexpected fts_read ent %s\n", name);
+ exit (1);
+ break;
+ }
+ }
+ /* fts_read returns NULL when done (and clears errno)
+ or when an error occured (with errno set). */
+ if (errno != 0)
+ {
+ printf ("FAIL: fts_read: %m\n");
+ exit (1);
+ }
+
+ if (fts_close (fts) != 0)
+ {
+ printf ("FAIL: fts_close: %m\n");
+ exit (1);
+ }
+
+ if (files != 8)
+ {
+ printf ("FAIL: Unexpected number of files: %d\n", files);
+ return 1;
+ }
+
+ if (dirs != 3)
+ {
+ printf ("FAIL: Unexpected number of dirs: %d\n", dirs);
+ return 1;
+ }
+
+ puts ("PASS");
+ return 0;
+}