Changeset View
Changeset View
Standalone View
Standalone View
lib/libc/gen/fts.c
Show First 20 Lines • Show All 44 Lines • ▼ Show 20 Lines | |||||
#include <dirent.h> | #include <dirent.h> | ||||
#include <errno.h> | #include <errno.h> | ||||
#include <fcntl.h> | #include <fcntl.h> | ||||
#include <fts.h> | #include <fts.h> | ||||
#include <stdlib.h> | #include <stdlib.h> | ||||
#include <string.h> | #include <string.h> | ||||
#include <unistd.h> | #include <unistd.h> | ||||
#include "block_abi.h" | |||||
#include "un-namespace.h" | #include "un-namespace.h" | ||||
#include "gen-private.h" | #include "gen-private.h" | ||||
/* fts_block_t */ | |||||
typedef DECLARE_BLOCK(int, fts_block_t, const FTSENT * const *, | |||||
const FTSENT * const *); | |||||
static FTSENT *fts_alloc(FTS *, char *, size_t); | static FTSENT *fts_alloc(FTS *, char *, size_t); | ||||
theraven: We should move these declarations into block_abi.h and not have them copied into every file… | |||||
Done Inline ActionsI will move them. FWIW, I changed them to the expanded form as a reminder that we still don't have __weak_symbol. Currently __weak is still the only option in cdefs.h. pfg: I will move them. FWIW, I changed them to the expanded form as a reminder that we still don't… | |||||
static FTSENT *fts_build(FTS *, int); | static FTSENT *fts_build(FTS *, int); | ||||
static void fts_lfree(FTSENT *); | static void fts_lfree(FTSENT *); | ||||
static void fts_load(FTS *, FTSENT *); | static void fts_load(FTS *, FTSENT *); | ||||
static size_t fts_maxarglen(char * const *); | static size_t fts_maxarglen(char * const *); | ||||
static void fts_padjust(FTS *, FTSENT *); | static void fts_padjust(FTS *, FTSENT *); | ||||
static int fts_palloc(FTS *, size_t); | static int fts_palloc(FTS *, size_t); | ||||
static FTSENT *fts_sort(FTS *, FTSENT *, size_t); | static FTSENT *fts_sort(FTS *, FTSENT *, size_t); | ||||
static int fts_stat(FTS *, FTSENT *, int, int); | static int fts_stat(FTS *, FTSENT *, int, int); | ||||
Show All 38 Lines | static const char *ufslike_filesystems[] = { | ||||
"ufs", | "ufs", | ||||
"zfs", | "zfs", | ||||
"nfs", | "nfs", | ||||
"nfs4", | "nfs4", | ||||
"ext2fs", | "ext2fs", | ||||
0 | 0 | ||||
}; | }; | ||||
FTS * | static FTS * | ||||
fts_open(argv, options, compar) | _fts_open_common(argv, priv) | ||||
char * const *argv; | char * const *argv; | ||||
int options; | |||||
int (*compar)(const FTSENT * const *, const FTSENT * const *); | |||||
{ | |||||
struct _fts_private *priv; | struct _fts_private *priv; | ||||
FTS *sp; | { | ||||
FTS *sp = &priv->ftsp_fts; | |||||
FTSENT *p, *root; | FTSENT *p, *root; | ||||
FTSENT *parent, *tmp; | FTSENT *parent, *tmp; | ||||
size_t len, nitems; | 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; | |||||
/* Shush, GCC. */ | /* Shush, GCC. */ | ||||
tmp = NULL; | tmp = NULL; | ||||
/* Logical walks turn on NOCHDIR; symbolic links are too hard. */ | /* Logical walks turn on NOCHDIR; symbolic links are too hard. */ | ||||
if (ISSET(FTS_LOGICAL)) | if (ISSET(FTS_LOGICAL)) | ||||
SET(FTS_NOCHDIR); | SET(FTS_NOCHDIR); | ||||
/* | /* | ||||
Show All 21 Lines | for (root = NULL, nitems = 0; *argv != NULL; ++argv, ++nitems) { | ||||
/* Command-line "." and ".." are real directories. */ | /* Command-line "." and ".." are real directories. */ | ||||
if (p->fts_info == FTS_DOT) | if (p->fts_info == FTS_DOT) | ||||
p->fts_info = FTS_D; | p->fts_info = FTS_D; | ||||
/* | /* | ||||
* If comparison routine supplied, traverse in sorted | * If comparison routine supplied, traverse in sorted | ||||
* order; otherwise traverse in the order specified. | * order; otherwise traverse in the order specified. | ||||
*/ | */ | ||||
if (compar) { | if (sp->fts_compar) { | ||||
p->fts_link = root; | p->fts_link = root; | ||||
root = p; | root = p; | ||||
} else { | } else { | ||||
p->fts_link = NULL; | p->fts_link = NULL; | ||||
if (root == NULL) | if (root == NULL) | ||||
tmp = root = p; | tmp = root = p; | ||||
else { | else { | ||||
tmp->fts_link = p; | tmp->fts_link = p; | ||||
tmp = p; | tmp = p; | ||||
} | } | ||||
} | } | ||||
} | } | ||||
if (compar && nitems > 1) | if (sp->fts_compar && nitems > 1) | ||||
root = fts_sort(sp, root, nitems); | root = fts_sort(sp, root, nitems); | ||||
/* | /* | ||||
* Allocate a dummy pointer and make fts_read think that we've just | * 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 | * finished the node before the root(s); set p->fts_info to FTS_INIT | ||||
* so that everything about the "current" node is ignored. | * so that everything about the "current" node is ignored. | ||||
*/ | */ | ||||
if ((sp->fts_cur = fts_alloc(sp, "", 0)) == NULL) | if ((sp->fts_cur = fts_alloc(sp, "", 0)) == NULL) | ||||
Show All 16 Lines | |||||
mem3: fts_lfree(root); | mem3: fts_lfree(root); | ||||
free(parent); | free(parent); | ||||
mem2: free(sp->fts_path); | mem2: free(sp->fts_path); | ||||
mem1: free(sp); | mem1: free(sp); | ||||
return (NULL); | return (NULL); | ||||
} | } | ||||
static int | |||||
_fts_open_argcheck(char * const *argv, int options) | |||||
{ | |||||
/* | |||||
* Check for illegal options and for at least one path. | |||||
*/ | |||||
if (options & ~FTS_OPTIONMASK || *argv == NULL) | |||||
return (EINVAL); | |||||
return (0); | |||||
} | |||||
FTS * | |||||
fts_open(char * const *argv, int options, int (*compar)(const FTSENT * const *, | |||||
const FTSENT * const *)) | |||||
{ | |||||
FTS *sp; | |||||
struct _fts_private *priv; | |||||
/* Check arguments. */ | |||||
if ((errno = _fts_open_argcheck(argv, options)) != 0) | |||||
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_common(argv, priv)); | |||||
} | |||||
FTS * | |||||
fts_open_b(char * const *argv, int options, fts_block_t compar) | |||||
{ | |||||
FTS *sp; | |||||
struct _fts_private *priv; | |||||
/* Check arguments. */ | |||||
if ((errno = _fts_open_argcheck(argv, options)) != 0) | |||||
return (NULL); | |||||
/* Check to make sure we have the block runtime. */ | |||||
if (_Block_copy == 0) { | |||||
errno = ENOSYS; | |||||
return (NULL); | |||||
} | |||||
/* Allocate/initialize the stream. */ | |||||
if ((priv = calloc(1, sizeof(*priv))) == NULL) | |||||
return (NULL); | |||||
sp = &priv->ftsp_fts; | |||||
sp->fts_compar_b = _Block_copy(compar); | |||||
sp->fts_options = options | FTS_COMPAR_B; | |||||
return (_fts_open_common(argv, priv)); | |||||
} | |||||
static void | static void | ||||
fts_load(FTS *sp, FTSENT *p) | fts_load(FTS *sp, FTSENT *p) | ||||
{ | { | ||||
size_t len; | size_t len; | ||||
char *cp; | char *cp; | ||||
/* | /* | ||||
* Load the stream structure for the next traversal. Since we don't | * Load the stream structure for the next traversal. Since we don't | ||||
Show All 35 Lines | fts_close(FTS *sp) | ||||
/* Free up child linked list, sort array, path buffer. */ | /* Free up child linked list, sort array, path buffer. */ | ||||
if (sp->fts_child) | if (sp->fts_child) | ||||
fts_lfree(sp->fts_child); | fts_lfree(sp->fts_child); | ||||
if (sp->fts_array) | if (sp->fts_array) | ||||
free(sp->fts_array); | free(sp->fts_array); | ||||
free(sp->fts_path); | free(sp->fts_path); | ||||
/* Free block pointer, if any. */ | |||||
if (ISSET(FTS_COMPAR_B) && sp->fts_compar_b != NULL && | |||||
Not Done Inline ActionsPlease can we have an assert for _Block_release != 0 if sp->fts_compar_b != NULL? This will only happen if _Block_copy exists but _Block_release doesn't, which means that something's badly wrong. Ideally, we'd do the _Block_release != 0 check up on line 261, so we never get into this situation in the first place. theraven: Please can we have an assert for _Block_release != 0 if sp->fts_compar_b != NULL? This will… | |||||
_Block_release != 0) | |||||
_Block_release(sp->fts_compar_b); | |||||
/* Return to original directory, save errno if necessary. */ | /* Return to original directory, save errno if necessary. */ | ||||
if (!ISSET(FTS_NOCHDIR)) { | if (!ISSET(FTS_NOCHDIR)) { | ||||
saved_errno = fchdir(sp->fts_rfd) ? errno : 0; | saved_errno = fchdir(sp->fts_rfd) ? errno : 0; | ||||
(void)_close(sp->fts_rfd); | (void)_close(sp->fts_rfd); | ||||
/* Set errno and return. */ | /* Set errno and return. */ | ||||
if (saved_errno != 0) { | if (saved_errno != 0) { | ||||
/* Free up the stream pointer. */ | /* Free up the stream pointer. */ | ||||
▲ Show 20 Lines • Show All 680 Lines • ▼ Show 20 Lines | |||||
fts_compar(const void *a, const void *b) | fts_compar(const void *a, const void *b) | ||||
{ | { | ||||
FTS *parent; | FTS *parent; | ||||
parent = (*(const FTSENT * const *)a)->fts_fts; | parent = (*(const FTSENT * const *)a)->fts_fts; | ||||
return (*parent->fts_compar)(a, b); | return (*parent->fts_compar)(a, b); | ||||
} | } | ||||
static int | |||||
fts_compar_b(void *thunk, const void *a, const void *b) | |||||
{ | |||||
FTS *sp = (FTS *)thunk; | |||||
int (*funcp)(const void *, const void *); | |||||
funcp = (int (*)(const void *, const void *)) | |||||
GET_BLOCK_FUNCTION(sp->fts_compar_b); | |||||
return (*funcp)(a, b); | |||||
} | |||||
static FTSENT * | static FTSENT * | ||||
fts_sort(FTS *sp, FTSENT *head, size_t nitems) | fts_sort(FTS *sp, FTSENT *head, size_t nitems) | ||||
{ | { | ||||
FTSENT **ap, *p; | FTSENT **ap, *p; | ||||
/* | /* | ||||
* Construct an array of pointers to the structures and call qsort(3). | * Construct an array of pointers to the structures and call qsort(3). | ||||
* Reassemble the array in the order returned by qsort. If unable to | * Reassemble the array in the order returned by qsort. If unable to | ||||
* sort for memory reasons, return the directory entries in their | * sort for memory reasons, return the directory entries in their | ||||
* current order. Allocate enough space for the current needs plus | * current order. Allocate enough space for the current needs plus | ||||
* 40 so don't realloc one entry at a time. | * 40 so don't realloc one entry at a time. | ||||
*/ | */ | ||||
if (nitems > sp->fts_nitems) { | if (nitems > sp->fts_nitems) { | ||||
sp->fts_nitems = nitems + 40; | sp->fts_nitems = nitems + 40; | ||||
if ((sp->fts_array = reallocf(sp->fts_array, | if ((sp->fts_array = reallocf(sp->fts_array, | ||||
sp->fts_nitems * sizeof(FTSENT *))) == NULL) { | sp->fts_nitems * sizeof(FTSENT *))) == NULL) { | ||||
sp->fts_nitems = 0; | sp->fts_nitems = 0; | ||||
return (head); | return (head); | ||||
} | } | ||||
} | } | ||||
for (ap = sp->fts_array, p = head; p; p = p->fts_link) | for (ap = sp->fts_array, p = head; p; p = p->fts_link) | ||||
*ap++ = p; | *ap++ = p; | ||||
if (ISSET(FTS_COMPAR_B)) | |||||
qsort_r(sp->fts_array, nitems, sizeof(FTSENT *), | |||||
sp->fts_compar_b, fts_compar_b); | |||||
Not Done Inline ActionsIs there a reason not to use qsort_b here? theraven: Is there a reason not to use qsort_b here? | |||||
else | |||||
qsort(sp->fts_array, nitems, sizeof(FTSENT *), fts_compar); | qsort(sp->fts_array, nitems, sizeof(FTSENT *), fts_compar); | ||||
for (head = *(ap = sp->fts_array); --nitems; ++ap) | for (head = *(ap = sp->fts_array); --nitems; ++ap) | ||||
ap[0]->fts_link = ap[1]; | ap[0]->fts_link = ap[1]; | ||||
ap[0]->fts_link = NULL; | ap[0]->fts_link = NULL; | ||||
return (head); | return (head); | ||||
} | } | ||||
static FTSENT * | static FTSENT * | ||||
fts_alloc(FTS *sp, char *name, size_t namelen) | fts_alloc(FTS *sp, char *name, size_t namelen) | ||||
▲ Show 20 Lines • Show All 180 Lines • Show Last 20 Lines |
We should move these declarations into block_abi.h and not have them copied into every file that uses them. We should also use __weak_symbol, rather than the expanded form of the attribute.