X-Git-Url: https://git.cameronkatri.com/mandoc.git/blobdiff_plain/6f918b70d4fbe4fe01d50aaaf631876109db472d..101604fb661df3209a20d07b6b9070d8d3fbffac:/compat_fts.c

diff --git a/compat_fts.c b/compat_fts.c
index 65caa851..e44298df 100644
--- a/compat_fts.c
+++ b/compat_fts.c
@@ -1,13 +1,5 @@
-#include "config.h"
-
-#if HAVE_FTS
-
-int dummy;
-
-#else
-
-/*	$Id: compat_fts.c,v 1.11 2016/10/18 23:13:25 schwarze Exp $	*/
-/*	$OpenBSD: fts.c,v 1.56 2016/09/21 04:38:56 guenther 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
@@ -37,6 +29,7 @@ int dummy;
  * 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>
@@ -59,30 +52,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 +90,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 +106,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 +118,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 +205,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 +493,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);
 }
 
@@ -546,6 +553,41 @@ fts_stat(FTS *sp, FTSENT *p)
 	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)
 {
@@ -602,13 +644,14 @@ fts_palloc(FTS *sp, size_t more)
 		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) {
 		free(sp->fts_path);
 		sp->fts_path = NULL;
 		return (1);
 	}
+	sp->fts_pathlen += more;
 	sp->fts_path = p;
 	return (0);
 }
@@ -651,5 +694,3 @@ fts_maxarglen(char * const *argv)
 			max = len;
 	return (max + 1);
 }
-
-#endif