diff options
Diffstat (limited to 'io')
-rw-r--r-- | io/Makefile | 5 | ||||
-rw-r--r-- | io/Versions | 3 | ||||
-rw-r--r-- | io/fts.c | 148 | ||||
-rw-r--r-- | io/fts.h | 96 | ||||
-rw-r--r-- | io/fts64.c | 30 | ||||
-rw-r--r-- | io/tst-fts-lfs.c | 2 | ||||
-rw-r--r-- | io/tst-fts.c | 231 |
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; + } } @@ -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; @@ -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; +} |