-#include "config.h"
-
-#if HAVE_FTS
-
-int dummy;
-
-#else
-
-/* $Id: compat_fts.c,v 1.9 2015/03/18 19:29:48 schwarze Exp $ */
-/* $OpenBSD: fts.c,v 1.50 2015/01/16 16:48:51 deraadt Exp $ */
+/* $Id: compat_fts.c,v 1.17 2020/06/15 01:37:14 schwarze Exp $ */
+/* $OpenBSD: fts.c,v 1.59 2019/06/28 13:32:41 deraadt Exp $ */
/*-
* Copyright (c) 1990, 1993, 1994
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*/
+#include "config.h"
#include <sys/stat.h>
#include <sys/types.h>
static size_t fts_maxarglen(char * const *);
static void fts_padjust(FTS *, FTSENT *);
static int fts_palloc(FTS *, size_t);
+static FTSENT *fts_sort(FTS *, FTSENT *, int);
static unsigned short fts_stat(FTS *, FTSENT *);
+typedef int (*qsort_compar_proto)(const void *, const void *);
+
#define ISDOT(a) (a[0] == '.' && (!a[1] || (a[1] == '.' && !a[2])))
-#ifndef O_DIRECTORY
-#define O_DIRECTORY 0
-#endif
#ifndef O_CLOEXEC
#define O_CLOEXEC 0
#endif
#define SET(opt) (sp->fts_options |= (opt))
FTS *
-fts_open(char * const *argv, int options, void *dummy)
+fts_open(char * const *argv, int options,
+ int (*compar)(const FTSENT **, const FTSENT **))
{
FTS *sp;
FTSENT *p, *root;
int nitems;
- FTSENT *parent, *tmp;
- size_t len;
+ FTSENT *parent, *prev;
/* Options check. */
if (options & ~FTS_OPTIONMASK) {
return (NULL);
}
+ /* At least one path must be specified. */
+ if (*argv == NULL) {
+ errno = EINVAL;
+ return (NULL);
+ }
+
/* Allocate/initialize the stream */
if ((sp = calloc(1, sizeof(FTS))) == NULL)
return (NULL);
+ sp->fts_compar = compar;
sp->fts_options = options;
/*
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 = ENOENT;
- goto mem3;
- }
-
- if ((p = fts_alloc(sp, *argv, len)) == NULL)
+ for (root = prev = NULL, nitems = 0; *argv; ++argv, ++nitems) {
+ if ((p = fts_alloc(sp, *argv, strlen(*argv))) == NULL)
goto mem3;
p->fts_level = FTS_ROOTLEVEL;
p->fts_parent = parent;
if (p->fts_info == FTS_DOT)
p->fts_info = FTS_D;
- p->fts_link = NULL;
- if (root == NULL)
- tmp = root = p;
- else {
- tmp->fts_link = p;
- tmp = p;
+ /*
+ * 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)
+ root = p;
+ else
+ prev->fts_link = p;
+ prev = p;
}
}
+ if (compar && nitems > 1)
+ root = fts_sort(sp, root, nitems);
/*
* Allocate a dummy pointer and make fts_read think that we've just
/* Free up child linked list, sort array, path buffer, stream ptr.*/
if (sp->fts_child)
fts_lfree(sp->fts_child);
+ free(sp->fts_array);
free(sp->fts_path);
free(sp);
* semantics to fts using fts_set. An error return is allowed for similar
* reasons.
*/
-/* ARGSUSED */
int
fts_set(FTS *sp, FTSENT *p, int instr)
{
* structures already allocated.
*/
mem1: saved_errno = errno;
- if (p)
- free(p);
+ free(p);
fts_lfree(head);
(void)closedir(dirp);
cur->fts_info = FTS_ERR;
cur->fts_info = FTS_DP;
return (NULL);
}
+
+ /* Sort the entries. */
+ if (sp->fts_compar && nitems > 1)
+ head = fts_sort(sp, head, nitems);
return (head);
}
return (FTS_DEFAULT);
}
+static FTSENT *
+fts_sort(FTS *sp, FTSENT *head, int nitems)
+{
+ FTSENT **ap, *p;
+
+ /*
+ * 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 (nitems > sp->fts_nitems) {
+ struct _ftsent **a;
+
+ if ((a = reallocarray(sp->fts_array,
+ nitems + 40, sizeof(FTSENT *))) == NULL) {
+ free(sp->fts_array);
+ sp->fts_array = NULL;
+ sp->fts_nitems = 0;
+ return (head);
+ }
+ sp->fts_nitems = nitems + 40;
+ sp->fts_array = a;
+ }
+ for (ap = sp->fts_array, p = head; p; p = p->fts_link)
+ *ap++ = p;
+ qsort(sp->fts_array, nitems, sizeof(FTSENT *),
+ (qsort_compar_proto)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 *
fts_alloc(FTS *sp, const char *name, size_t namelen)
{
*/
more += 256;
if (sp->fts_pathlen + more < sp->fts_pathlen) {
- if (sp->fts_path)
- free(sp->fts_path);
+ free(sp->fts_path);
sp->fts_path = NULL;
errno = ENAMETOOLONG;
return (1);
}
- sp->fts_pathlen += more;
- p = realloc(sp->fts_path, sp->fts_pathlen);
+ p = recallocarray(sp->fts_path, sp->fts_pathlen,
+ sp->fts_pathlen + more, 1);
if (p == NULL) {
- if (sp->fts_path)
- free(sp->fts_path);
+ free(sp->fts_path);
sp->fts_path = NULL;
return (1);
}
+ sp->fts_pathlen += more;
sp->fts_path = p;
return (0);
}
max = len;
return (max + 1);
}
-
-#endif