=================================================================== RCS file: /cvs/mandoc/compat_fts.c,v retrieving revision 1.11 retrieving revision 1.15 diff -u -p -r1.11 -r1.15 --- mandoc/compat_fts.c 2016/10/18 23:13:25 1.11 +++ mandoc/compat_fts.c 2020/06/14 22:49:36 1.15 @@ -6,8 +6,8 @@ int dummy; #else -/* $Id: compat_fts.c,v 1.11 2016/10/18 23:13:25 schwarze Exp $ */ -/* $OpenBSD: compat_fts.c,v 1.11 2016/10/18 23:13:25 schwarze Exp $ */ +/* $Id: compat_fts.c,v 1.15 2020/06/14 22:49:36 schwarze Exp $ */ +/* $OpenBSD: compat_fts.c,v 1.15 2020/06/14 22:49:36 schwarze Exp $ */ /*- * Copyright (c) 1990, 1993, 1994 @@ -59,30 +59,28 @@ 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 *); +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 -#ifndef PATH_MAX -#define PATH_MAX 4096 -#endif #define CLR(opt) (sp->fts_options &= ~(opt)) #define ISSET(opt) (sp->fts_options & (opt)) #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; + FTSENT *parent, *prev; /* Options check. */ if (options & ~FTS_OPTIONMASK) { @@ -99,6 +97,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; /* @@ -114,7 +113,7 @@ fts_open(char * const *argv, int options, void *dummy) parent->fts_level = FTS_ROOTPARENTLEVEL; /* Allocate/initialize root(s). */ - for (root = NULL, nitems = 0; *argv; ++argv, ++nitems) { + 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; @@ -126,14 +125,24 @@ 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) + 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 @@ -203,6 +212,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 +500,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); } @@ -544,6 +558,41 @@ fts_stat(FTS *sp, FTSENT *p) if (S_ISREG(sbp->st_mode)) return (FTS_F); 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; + + 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 *), + (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 *