]> git.cameronkatri.com Git - mandoc.git/blobdiff - compat_fts.c
Indentation must be measured in units of the surrounding text,
[mandoc.git] / compat_fts.c
index ed95854666a06cb8349755bfc3745c9726b7d4e9..c2cc9570c545b9e99774d320a1b38a25232f9a39 100644 (file)
@@ -6,8 +6,8 @@ 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.12 2016/10/18 23:58:12 schwarze Exp $     */
+/*     $OpenBSD: fts.c,v 1.56 2016/09/21 04:38:56 guenther Exp $       */
 
 /*-
  * Copyright (c) 1990, 1993, 1994
@@ -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])))
@@ -68,19 +69,22 @@ static unsigned short        fts_stat(FTS *, FTSENT *);
 #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;
-       size_t len;
 
        /* Options check. */
        if (options & ~FTS_OPTIONMASK) {
@@ -88,9 +92,16 @@ fts_open(char * const *argv, int options, void *dummy)
                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;
 
        /*
@@ -107,13 +118,7 @@ fts_open(char * const *argv, int options, void *dummy)
 
        /* 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)
+               if ((p = fts_alloc(sp, *argv, strlen(*argv))) == NULL)
                        goto mem3;
                p->fts_level = FTS_ROOTLEVEL;
                p->fts_parent = parent;
@@ -124,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
@@ -201,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);
 
@@ -317,7 +334,6 @@ name:               t = sp->fts_path + NAPPEND(p->fts_parent);
  * semantics to fts using fts_set.  An error return is allowed for similar
  * reasons.
  */
-/* ARGSUSED */
 int
 fts_set(FTS *sp, FTSENT *p, int instr)
 {
@@ -416,8 +432,7 @@ fts_build(FTS *sp)
                                 * structures already allocated.
                                 */
 mem1:                          saved_errno = errno;
-                               if (p)
-                                       free(p);
+                               free(p);
                                fts_lfree(head);
                                (void)closedir(dirp);
                                cur->fts_info = FTS_ERR;
@@ -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);
 }
 
@@ -546,6 +565,40 @@ 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;
+
+               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)
 {
@@ -597,8 +650,7 @@ fts_palloc(FTS *sp, size_t more)
         */
        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);
@@ -606,8 +658,7 @@ fts_palloc(FTS *sp, size_t more)
        sp->fts_pathlen += more;
        p = realloc(sp->fts_path, sp->fts_pathlen);
        if (p == NULL) {
-               if (sp->fts_path)
-                       free(sp->fts_path);
+               free(sp->fts_path);
                sp->fts_path = NULL;
                return (1);
        }