diff options
Diffstat (limited to 'lib/libc/gen/fts.c')
-rw-r--r-- | lib/libc/gen/fts.c | 234 |
1 files changed, 171 insertions, 63 deletions
diff --git a/lib/libc/gen/fts.c b/lib/libc/gen/fts.c index 0c26c91feb84..cce959ba836a 100644 --- a/lib/libc/gen/fts.c +++ b/lib/libc/gen/fts.c @@ -48,6 +48,19 @@ #include "gen-private.h" +#ifdef __BLOCKS__ +#include <Block.h> +#else +#include "block_abi.h" +typedef DECLARE_BLOCK(int, fts_block, + const FTSENT * const *, const FTSENT * const *); +void qsort_b(void *, size_t, size_t, fts_block); +#endif /* __BLOCKS__ */ +/* only present if linked with blocks runtime */ +void *_Block_copy(const void *) __weak_symbol; +void _Block_release(const void *) __weak_symbol; +extern void *_NSConcreteGlobalBlock[] __weak_symbol; + static FTSENT *fts_alloc(FTS *, char *, size_t); static FTSENT *fts_build(FTS *, int); static void fts_lfree(FTSENT *); @@ -102,39 +115,21 @@ static const char *ufslike_filesystems[] = { 0 }; -FTS * -fts_open(char * const *argv, int options, - int (*compar)(const FTSENT * const *, const FTSENT * const *)) +static FTS * +__fts_open(FTS *sp, char * const *argv) { - struct _fts_private *priv; - FTS *sp; FTSENT *p, *root; FTSENT *parent, *tmp; size_t len, nitems; - /* Options check. */ - if (options & ~FTS_OPTIONMASK) { - errno = EINVAL; - return (NULL); - } - - /* fts_open() requires at least one path */ - if (*argv == NULL) { - errno = EINVAL; - return (NULL); - } - - /* Allocate/initialize the stream. */ - if ((priv = calloc(1, sizeof(*priv))) == NULL) - return (NULL); - sp = &priv->ftsp_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); + /* NOSTAT_TYPE implies NOSTAT */ + if (ISSET(FTS_NOSTAT_TYPE)) + SET(FTS_NOSTAT); + /* * Start out with 1K of path space, and enough, in any case, * to hold the user's paths. @@ -158,7 +153,9 @@ fts_open(char * const *argv, int options, p->fts_level = FTS_ROOTLEVEL; p->fts_parent = parent; p->fts_accpath = p->fts_name; - p->fts_info = fts_stat(sp, p, ISSET(FTS_COMFOLLOW), -1); + p->fts_info = fts_stat(sp, p, + ISSET(FTS_COMFOLLOWDIR) ? -1 : ISSET(FTS_COMFOLLOW), + -1); /* Command-line "." and ".." are real directories. */ if (p->fts_info == FTS_DOT) @@ -168,7 +165,7 @@ fts_open(char * const *argv, int options, * If comparison routine supplied, traverse in sorted * order; otherwise traverse in the order specified. */ - if (compar) { + if (sp->fts_compar) { p->fts_link = root; root = p; } else { @@ -181,7 +178,7 @@ fts_open(char * const *argv, int options, } } } - if (compar && nitems > 1) + if (sp->fts_compar && nitems > 1) root = fts_sort(sp, root, nitems); /* @@ -214,6 +211,97 @@ mem1: free(sp); return (NULL); } +FTS * +fts_open(char * const *argv, int options, + int (*compar)(const FTSENT * const *, const FTSENT * const *)) +{ + struct _fts_private *priv; + FTS *sp; + + /* Options check. */ + if (options & ~FTS_OPTIONMASK) { + errno = EINVAL; + return (NULL); + } + + /* fts_open() requires at least one path */ + if (*argv == NULL) { + errno = EINVAL; + return (NULL); + } + + /* Allocate/initialize the stream. */ + if ((priv = calloc(1, sizeof(*priv))) == NULL) + return (NULL); + sp = &priv->ftsp_fts; + sp->fts_compar = compar; + sp->fts_options = options; + + return (__fts_open(sp, argv)); +} + +#ifdef __BLOCKS__ +FTS * +fts_open_b(char * const *argv, int options, + int (^compar)(const FTSENT * const *, const FTSENT * const *)) +#else +FTS * +fts_open_b(char * const *argv, int options, fts_block compar) +#endif /* __BLOCKS__ */ +{ + struct _fts_private *priv; + FTS *sp; + + /* No blocks, no problems. */ + if (compar == NULL) + return (fts_open(argv, options, NULL)); + + /* Avoid segfault if blocks runtime is missing. */ + if (_Block_copy == NULL) { + errno = ENOSYS; + return (NULL); + } + + /* Options check. */ + if (options & ~FTS_OPTIONMASK) { + errno = EINVAL; + return (NULL); + } + + /* fts_open() requires at least one path */ + if (*argv == NULL) { + errno = EINVAL; + return (NULL); + } + + /* Allocate/initialize the stream. */ + if ((priv = calloc(1, sizeof(*priv))) == NULL) + return (NULL); + sp = &priv->ftsp_fts; +#ifdef __BLOCKS__ + compar = Block_copy(compar); +#else + if (compar->isa != &_NSConcreteGlobalBlock) + compar = _Block_copy(compar); +#endif /* __BLOCKS__ */ + if (compar == NULL) { + free(priv); + return (NULL); + } + sp->fts_compar_b = compar; + sp->fts_options = options | FTS_COMPAR_B; + + if ((sp = __fts_open(sp, argv)) == NULL) { +#ifdef __BLOCKS__ + Block_release(compar); +#else + if (compar->isa != &_NSConcreteGlobalBlock) + _Block_release(compar); +#endif /* __BLOCKS__ */ + } + return (sp); +} + static void fts_load(FTS *sp, FTSENT *p) { @@ -265,6 +353,17 @@ fts_close(FTS *sp) free(sp->fts_array); free(sp->fts_path); + /* Free up any block pointer. */ + if (ISSET(FTS_COMPAR_B) && sp->fts_compar_b != NULL) { +#ifdef __BLOCKS__ + Block_release(sp->fts_compar_b); +#else + if (((fts_block)(sp->fts_compar_b))->isa != + &_NSConcreteGlobalBlock) + _Block_release(sp->fts_compar_b); +#endif /* __BLOCKS__ */ + } + /* Return to original directory, save errno if necessary. */ if (!ISSET(FTS_NOCHDIR)) { saved_errno = fchdir(sp->fts_rfd) ? errno : 0; @@ -650,14 +749,10 @@ fts_build(FTS *sp, int type) * Open the directory for reading. If this fails, we're done. * If being called from fts_read, set the fts_info field. */ -#ifdef FTS_WHITEOUT if (ISSET(FTS_WHITEOUT)) oflag = DTF_NODUP; else oflag = DTF_HIDEW | DTF_NODUP; -#else -#define __opendir2(path, flag) opendir(path) -#endif if ((dirp = __opendir2(cur->fts_accpath, oflag)) == NULL) { if (type == BREAD) { cur->fts_info = FTS_DNR; @@ -784,10 +879,8 @@ mem1: saved_errno = errno; p->fts_parent = sp->fts_cur; p->fts_pathlen = len + dnamlen; -#ifdef FTS_WHITEOUT if (dp->d_type == DT_WHT) p->fts_flags |= FTS_ISW; -#endif if (cderrno) { if (nlinks) { @@ -796,12 +889,8 @@ mem1: saved_errno = errno; } else p->fts_info = FTS_NSOK; p->fts_accpath = cur->fts_accpath; - } else if (nlinks == 0 -#ifdef DT_DIR - || (nostat && - dp->d_type != DT_DIR && dp->d_type != DT_UNKNOWN) -#endif - ) { + } else if (nlinks == 0 || (nostat && + dp->d_type != DT_DIR && dp->d_type != DT_UNKNOWN)) { p->fts_accpath = ISSET(FTS_NOCHDIR) ? p->fts_path : p->fts_name; p->fts_info = FTS_NSOK; @@ -821,6 +910,25 @@ mem1: saved_errno = errno; p->fts_info == FTS_DC || p->fts_info == FTS_DOT)) --nlinks; } + if (p->fts_info == FTS_NSOK && ISSET(FTS_NOSTAT_TYPE)) { + switch (dp->d_type) { + case DT_FIFO: + case DT_CHR: + case DT_BLK: + case DT_SOCK: + p->fts_info = FTS_DEFAULT; + break; + case DT_REG: + p->fts_info = FTS_F; + break; + case DT_LNK: + p->fts_info = FTS_SL; + break; + case DT_WHT: + p->fts_info = FTS_W; + break; + } + } /* We walk in directory order so "ls -f" doesn't get upset. */ p->fts_link = NULL; @@ -897,7 +1005,7 @@ fts_stat(FTS *sp, FTSENT *p, int follow, int dfd) dev_t dev; ino_t ino; struct stat *sbp, sb; - int saved_errno; + int ret, saved_errno; const char *path; if (dfd == -1) { @@ -910,7 +1018,6 @@ fts_stat(FTS *sp, FTSENT *p, int follow, int dfd) /* If user needs stat info, stat buffer already allocated. */ sbp = ISSET(FTS_NOSTAT) ? &sb : p->fts_statp; -#ifdef FTS_WHITEOUT /* Check for whiteout. */ if (p->fts_flags & FTS_ISW) { if (sbp != &sb) { @@ -919,22 +1026,27 @@ fts_stat(FTS *sp, FTSENT *p, int follow, int dfd) } return (FTS_W); } -#endif /* - * If doing a logical walk, or application requested FTS_FOLLOW, do - * a stat(2). If that fails, check for a non-existent symlink. If - * fail, set the errno from the stat call. + * If doing a logical walk, or caller requested FTS_COMFOLLOW, do + * a full stat(2). If that fails, do an lstat(2) to check for a + * non-existent symlink. If that fails, set the errno from the + * stat(2) call. + * + * As a special case, if stat(2) succeeded but the target is not a + * directory and follow is negative (indicating FTS_COMFOLLOWDIR + * rather than FTS_COMFOLLOW), we also revert to lstat(2). */ if (ISSET(FTS_LOGICAL) || follow) { - if (fstatat(dfd, path, sbp, 0)) { + if ((ret = fstatat(dfd, path, sbp, 0)) != 0 || + (follow < 0 && !S_ISDIR(sbp->st_mode))) { saved_errno = errno; if (fstatat(dfd, path, sbp, AT_SYMLINK_NOFOLLOW)) { p->fts_errno = saved_errno; goto err; } errno = 0; - if (S_ISLNK(sbp->st_mode)) + if (ret != 0 && S_ISLNK(sbp->st_mode)) return (FTS_SLNONE); } } else if (fstatat(dfd, path, sbp, AT_SYMLINK_NOFOLLOW)) { @@ -979,21 +1091,6 @@ err: memset(sbp, 0, sizeof(struct stat)); return (FTS_DEFAULT); } -/* - * The comparison function takes pointers to pointers to FTSENT structures. - * Qsort wants a comparison function that takes pointers to void. - * (Both with appropriate levels of const-poisoning, of course!) - * Use a trampoline function to deal with the difference. - */ -static int -fts_compar(const void *a, const void *b) -{ - FTS *parent; - - parent = (*(const FTSENT * const *)a)->fts_fts; - return (*parent->fts_compar)(a, b); -} - static FTSENT * fts_sort(FTS *sp, FTSENT *head, size_t nitems) { @@ -1016,7 +1113,18 @@ fts_sort(FTS *sp, FTSENT *head, size_t nitems) } for (ap = sp->fts_array, p = head; p; p = p->fts_link) *ap++ = p; - qsort(sp->fts_array, nitems, sizeof(FTSENT *), fts_compar); + if (ISSET(FTS_COMPAR_B)) { +#ifdef __BLOCKS__ + qsort_b(sp->fts_array, nitems, sizeof(FTSENT *), + (int (^)(const void *, const void *))sp->fts_compar_b); +#else + qsort_b(sp->fts_array, nitems, sizeof(FTSENT *), + sp->fts_compar_b); +#endif /* __BLOCKS__ */ + } else { + qsort(sp->fts_array, nitems, sizeof(FTSENT *), + (int (*)(const void *, const void *))sp->fts_compar); + } for (head = *(ap = sp->fts_array); --nitems; ++ap) ap[0]->fts_link = ap[1]; ap[0]->fts_link = NULL; |