summaryrefslogtreecommitdiffstats
path: root/compat_fts.c
diff options
context:
space:
mode:
authorIngo Schwarze <schwarze@openbsd.org>2016-10-18 23:58:12 +0000
committerIngo Schwarze <schwarze@openbsd.org>2016-10-18 23:58:12 +0000
commit1e34c928971dc21ec5ea4c387bcbb6da371f83e0 (patch)
treedb90bc963b4cafd1268e91c395a7068c6c6f2d1f /compat_fts.c
parentf5c419e2b475369801c425f525b0c45261c0000b (diff)
downloadmandoc-1e34c928971dc21ec5ea4c387bcbb6da371f83e0.tar.gz
bring back support for sorting
Diffstat (limited to 'compat_fts.c')
-rw-r--r--compat_fts.c67
1 files changed, 60 insertions, 7 deletions
diff --git a/compat_fts.c b/compat_fts.c
index f384626c..b830b184 100644
--- a/compat_fts.c
+++ b/compat_fts.c
@@ -59,6 +59,7 @@ static void fts_load(FTS *, FTSENT *);
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 *);
#define ISDOT(a) (a[0] == '.' && (!a[1] || (a[1] == '.' && !a[2])))
@@ -77,7 +78,8 @@ static unsigned short fts_stat(FTS *, FTSENT *);
#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;
@@ -99,6 +101,7 @@ fts_open(char * const *argv, int options, void *dummy)
/* Allocate/initialize the stream */
if ((sp = calloc(1, sizeof(FTS))) == NULL)
return (NULL);
+ sp->fts_compar = compar;
sp->fts_options = options;
/*
@@ -126,14 +129,25 @@ fts_open(char * const *argv, int options, void *dummy)
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)
+ tmp = root = p;
+ else {
+ tmp->fts_link = p;
+ tmp = p;
+ }
}
}
+ if (compar && nitems > 1)
+ root = fts_sort(sp, root, nitems);
/*
* Allocate a dummy pointer and make fts_read think that we've just
@@ -203,6 +217,7 @@ fts_close(FTS *sp)
/* 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);
@@ -490,6 +505,10 @@ mem1: saved_errno = errno;
cur->fts_info = FTS_DP;
return (NULL);
}
+
+ /* Sort the entries. */
+ if (sp->fts_compar && nitems > 1)
+ head = fts_sort(sp, head, nitems);
return (head);
}
@@ -547,6 +566,40 @@ fts_stat(FTS *sp, FTSENT *p)
}
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;
+
+ sp->fts_nitems = nitems + 40;
+ if ((a = reallocarray(sp->fts_array,
+ sp->fts_nitems, sizeof(FTSENT *))) == NULL) {
+ free(sp->fts_array);
+ sp->fts_array = NULL;
+ sp->fts_nitems = 0;
+ return (head);
+ }
+ 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 *), 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)
{
FTSENT *p;