]> git.cameronkatri.com Git - mandoc.git/blobdiff - compat_fts.c
Create the link from ./man to ./mandoc in the "all" target rather than
[mandoc.git] / compat_fts.c
index 194c565524607ae343da048d1f8657fd0221f933..e44298df02b84359eb2c38e76144fad36e8d491d 100644 (file)
@@ -1,13 +1,5 @@
-#include "config.h"
-
-#if HAVE_FTS
-
-int dummy;
-
-#else
-
-/*     $Id: compat_fts.c,v 1.8 2015/02/07 07:53:01 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
 
 /*-
  * 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.
  */
  * 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>
 
 #include <sys/stat.h>
 #include <sys/types.h>
@@ -59,13 +52,12 @@ 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 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 *);
 static unsigned short   fts_stat(FTS *, FTSENT *);
-static int      fts_safe_changedir(FTS *, FTSENT *, int, const char *);
+
+typedef int (*qsort_compar_proto)(const void *, const void *);
 
 #define        ISDOT(a)        (a[0] == '.' && (!a[1] || (a[1] == '.' && !a[2])))
 
 #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        O_CLOEXEC
 #define        O_CLOEXEC       0
 #endif
@@ -74,16 +66,14 @@ static int   fts_safe_changedir(FTS *, FTSENT *, int, const char *);
 #define        ISSET(opt)      (sp->fts_options & (opt))
 #define        SET(opt)        (sp->fts_options |= (opt))
 
 #define        ISSET(opt)      (sp->fts_options & (opt))
 #define        SET(opt)        (sp->fts_options |= (opt))
 
-#define        FCHDIR(sp, fd)  (!ISSET(FTS_NOCHDIR) && fchdir(fd))
-
 FTS *
 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;
 {
        FTS *sp;
        FTSENT *p, *root;
        int nitems;
-       FTSENT *parent, *tmp;
-       size_t len;
+       FTSENT *parent, *prev;
 
        /* Options check. */
        if (options & ~FTS_OPTIONMASK) {
 
        /* Options check. */
        if (options & ~FTS_OPTIONMASK) {
@@ -91,9 +81,16 @@ fts_open(char * const *argv, int options, void *dummy)
                return (NULL);
        }
 
                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);
        /* Allocate/initialize the stream */
        if ((sp = calloc(1, sizeof(FTS))) == NULL)
                return (NULL);
+       sp->fts_compar = compar;
        sp->fts_options = options;
 
        /*
        sp->fts_options = options;
 
        /*
@@ -109,14 +106,8 @@ fts_open(char * const *argv, int options, void *dummy)
        parent->fts_level = FTS_ROOTPARENTLEVEL;
 
        /* Allocate/initialize root(s). */
        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;
                        goto mem3;
                p->fts_level = FTS_ROOTLEVEL;
                p->fts_parent = parent;
@@ -127,14 +118,24 @@ fts_open(char * const *argv, int options, void *dummy)
                if (p->fts_info == FTS_DOT)
                        p->fts_info = FTS_D;
 
                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
 
        /*
         * Allocate a dummy pointer and make fts_read think that we've just
@@ -146,17 +147,6 @@ fts_open(char * const *argv, int options, void *dummy)
        sp->fts_cur->fts_link = root;
        sp->fts_cur->fts_info = FTS_INIT;
 
        sp->fts_cur->fts_link = root;
        sp->fts_cur->fts_info = FTS_INIT;
 
-       /*
-        * If using chdir(2), grab a file descriptor pointing to dot to ensure
-        * that we can get back here; this could be avoided for some paths,
-        * but almost certainly not worth the effort.  Slashes, symbolic links,
-        * and ".." are all fairly nasty problems.  Note, if we can't get the
-        * descriptor we run anyway, just more slowly.
-        */
-       if (!ISSET(FTS_NOCHDIR) &&
-           (sp->fts_rfd = open(".", O_RDONLY | O_CLOEXEC)) < 0)
-               SET(FTS_NOCHDIR);
-
        if (nitems == 0)
                free(parent);
 
        if (nitems == 0)
                free(parent);
 
@@ -197,7 +187,6 @@ int
 fts_close(FTS *sp)
 {
        FTSENT *freep, *p;
 fts_close(FTS *sp)
 {
        FTSENT *freep, *p;
-       int rfd, error = 0;
 
        /*
         * This still works if we haven't read anything -- the dummy structure
 
        /*
         * This still works if we haven't read anything -- the dummy structure
@@ -213,25 +202,14 @@ fts_close(FTS *sp)
                free(p);
        }
 
                free(p);
        }
 
-       /* Stash the original directory fd if needed. */
-       rfd = ISSET(FTS_NOCHDIR) ? -1 : sp->fts_rfd;
-
        /* Free up child linked list, sort array, path buffer, stream ptr.*/
        if (sp->fts_child)
                fts_lfree(sp->fts_child);
        /* 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);
 
        free(sp->fts_path);
        free(sp);
 
-       /* Return to original directory, checking for error. */
-       if (rfd != -1) {
-               int saved_errno;
-               error = fchdir(rfd);
-               saved_errno = errno;
-               (void)close(rfd);
-               errno = saved_errno;
-       }
-
-       return (error);
+       return (0);
 }
 
 /*
 }
 
 /*
@@ -274,25 +252,11 @@ fts_read(FTS *sp)
                }
 
                /*
                }
 
                /*
-                * Cd to the subdirectory.
-                *
-                * If have already read and now fail to chdir, whack the list
-                * to make the names come out right, and set the parent errno
-                * so the application will eventually get an error condition.
-                * Set the FTS_DONTCHDIR flag so that when we logically change
-                * directories back to the parent we don't do a chdir.
-                *
                 * If haven't read do so.  If the read fails, fts_build sets
                 * FTS_STOP or the fts_info field of the node.
                 */
                if (sp->fts_child) {
                 * If haven't read do so.  If the read fails, fts_build sets
                 * FTS_STOP or the fts_info field of the node.
                 */
                if (sp->fts_child) {
-                       if (fts_safe_changedir(sp, p, -1, p->fts_accpath)) {
-                               p->fts_errno = errno;
-                               p->fts_flags |= FTS_DONTCHDIR;
-                               for (p = sp->fts_child; p; p = p->fts_link)
-                                       p->fts_accpath =
-                                           p->fts_parent->fts_accpath;
-                       }
+                       /* nothing */
                } else if ((sp->fts_child = fts_build(sp)) == NULL) {
                        if (ISSET(FTS_STOP))
                                return (NULL);
                } else if ((sp->fts_child = fts_build(sp)) == NULL) {
                        if (ISSET(FTS_STOP))
                                return (NULL);
@@ -313,10 +277,6 @@ next:      tmp = p;
                 * the root of the tree), and load the paths for the next root.
                 */
                if (p->fts_level == FTS_ROOTLEVEL) {
                 * the root of the tree), and load the paths for the next root.
                 */
                if (p->fts_level == FTS_ROOTLEVEL) {
-                       if (FCHDIR(sp, sp->fts_rfd)) {
-                               SET(FTS_STOP);
-                               return (NULL);
-                       }
                        fts_load(sp, p);
                        return (sp->fts_cur = p);
                }
                        fts_load(sp, p);
                        return (sp->fts_cur = p);
                }
@@ -352,23 +312,6 @@ name:              t = sp->fts_path + NAPPEND(p->fts_parent);
        /* NUL terminate the pathname. */
        sp->fts_path[p->fts_pathlen] = '\0';
 
        /* NUL terminate the pathname. */
        sp->fts_path[p->fts_pathlen] = '\0';
 
-       /*
-        * Return to the parent directory.  If at a root node or came through
-        * a symlink, go back through the file descriptor.  Otherwise, cd up
-        * one directory.
-        */
-       if (p->fts_level == FTS_ROOTLEVEL) {
-               if (FCHDIR(sp, sp->fts_rfd)) {
-                       SET(FTS_STOP);
-                       sp->fts_cur = p;
-                       return (NULL);
-               }
-       } else if (!(p->fts_flags & FTS_DONTCHDIR) &&
-           fts_safe_changedir(sp, p->fts_parent, -1, "..")) {
-               SET(FTS_STOP);
-               sp->fts_cur = p;
-               return (NULL);
-       }
        p->fts_info = p->fts_errno ? FTS_ERR : FTS_DP;
        return (sp->fts_cur = p);
 }
        p->fts_info = p->fts_errno ? FTS_ERR : FTS_DP;
        return (sp->fts_cur = p);
 }
@@ -379,7 +322,6 @@ name:               t = sp->fts_path + NAPPEND(p->fts_parent);
  * semantics to fts using fts_set.  An error return is allowed for similar
  * reasons.
  */
  * semantics to fts using fts_set.  An error return is allowed for similar
  * reasons.
  */
-/* ARGSUSED */
 int
 fts_set(FTS *sp, FTSENT *p, int instr)
 {
 int
 fts_set(FTS *sp, FTSENT *p, int instr)
 {
@@ -414,7 +356,7 @@ fts_build(FTS *sp)
        DIR *dirp;
        void *oldaddr;
        size_t dlen, len, maxlen;
        DIR *dirp;
        void *oldaddr;
        size_t dlen, len, maxlen;
-       int nitems, cderrno, descend, level, doadjust;
+       int nitems, level, doadjust;
        int saved_errno;
        char *cp;
 
        int saved_errno;
        char *cp;
 
@@ -431,32 +373,6 @@ fts_build(FTS *sp)
                return (NULL);
        }
 
                return (NULL);
        }
 
-       /*
-        * If we're going to need to stat anything or we want to descend
-        * and stay in the directory, chdir.  If this fails we keep going,
-        * but set a flag so we don't chdir after the post-order visit.
-        * We won't be able to stat anything, but we can still return the
-        * names themselves.  Note, that since fts_read won't be able to
-        * chdir into the directory, it will have to return different path
-        * names than before, i.e. "a/b" instead of "b".  Since the node
-        * has already been visited in pre-order, have to wait until the
-        * post-order visit to return the error.  There is a special case
-        * here, if there was nothing to stat then it's not an error to
-        * not be able to stat.  This is all fairly nasty.  If a program
-        * needed sorted entries or stat information, they had better be
-        * checking FTS_NS on the returned nodes.
-        */
-       cderrno = 0;
-       if (fts_safe_changedir(sp, cur, dirfd(dirp), NULL)) {
-               cur->fts_errno = errno;
-               cur->fts_flags |= FTS_DONTCHDIR;
-               descend = 0;
-               cderrno = errno;
-               (void)closedir(dirp);
-               dirp = NULL;
-       } else
-               descend = 1;
-
        /*
         * Figure out the max file name length that can be stored in the
         * current path -- the inner loop allocates more path as necessary.
        /*
         * Figure out the max file name length that can be stored in the
         * current path -- the inner loop allocates more path as necessary.
@@ -468,10 +384,8 @@ fts_build(FTS *sp)
         * each new name into the path.
         */
        len = NAPPEND(cur);
         * each new name into the path.
         */
        len = NAPPEND(cur);
-       if (ISSET(FTS_NOCHDIR)) {
-               cp = sp->fts_path + len;
-               *cp++ = '/';
-       }
+       cp = sp->fts_path + len;
+       *cp++ = '/';
        len++;
        maxlen = sp->fts_pathlen - len;
 
        len++;
        maxlen = sp->fts_pathlen - len;
 
@@ -506,8 +420,7 @@ fts_build(FTS *sp)
                                 * structures already allocated.
                                 */
 mem1:                          saved_errno = errno;
                                 * structures already allocated.
                                 */
 mem1:                          saved_errno = errno;
-                               if (p)
-                                       free(p);
+                               free(p);
                                fts_lfree(head);
                                (void)closedir(dirp);
                                cur->fts_info = FTS_ERR;
                                fts_lfree(head);
                                (void)closedir(dirp);
                                cur->fts_info = FTS_ERR;
@@ -518,8 +431,7 @@ mem1:                               saved_errno = errno;
                        /* Did realloc() change the pointer? */
                        if (oldaddr != sp->fts_path) {
                                doadjust = 1;
                        /* Did realloc() change the pointer? */
                        if (oldaddr != sp->fts_path) {
                                doadjust = 1;
-                               if (ISSET(FTS_NOCHDIR))
-                                       cp = sp->fts_path + len;
+                               cp = sp->fts_path + len;
                        }
                        maxlen = sp->fts_pathlen - len;
                }
                        }
                        maxlen = sp->fts_pathlen - len;
                }
@@ -542,20 +454,11 @@ mem1:                             saved_errno = errno;
                        return (NULL);
                }
 
                        return (NULL);
                }
 
-               if (cderrno) {
-                       p->fts_info = FTS_NS;
-                       p->fts_errno = cderrno;
-                       p->fts_accpath = cur->fts_accpath;
-               } else {
-                       /* Build a file name for fts_stat to stat. */
-                       if (ISSET(FTS_NOCHDIR)) {
-                               p->fts_accpath = p->fts_path;
-                               memmove(cp, p->fts_name, p->fts_namelen + 1);
-                       } else
-                               p->fts_accpath = p->fts_name;
-                       /* Stat it. */
-                       p->fts_info = fts_stat(sp, p);
-               }
+               /* Build a file name for fts_stat to stat. */
+               p->fts_accpath = p->fts_path;
+               memmove(cp, p->fts_name, p->fts_namelen + 1);
+               /* Stat it. */
+               p->fts_info = fts_stat(sp, p);
 
                /* We walk in directory order so "ls -f" doesn't get upset. */
                p->fts_link = NULL;
 
                /* We walk in directory order so "ls -f" doesn't get upset. */
                p->fts_link = NULL;
@@ -581,32 +484,19 @@ mem1:                             saved_errno = errno;
         * If not changing directories, reset the path back to original
         * state.
         */
         * If not changing directories, reset the path back to original
         * state.
         */
-       if (ISSET(FTS_NOCHDIR)) {
-               if (len == sp->fts_pathlen || nitems == 0)
-                       --cp;
-               *cp = '\0';
-       }
-
-       /*
-        * If descended after called from fts_children or after called from
-        * fts_read and nothing found, get back.  At the root level we use
-        * the saved fd; if one of fts_open()'s arguments is a relative path
-        * to an empty directory, we wind up here with no other way back.  If
-        * can't get back, we're done.
-        */
-       if (descend && !nitems &&
-           (cur->fts_level == FTS_ROOTLEVEL ? FCHDIR(sp, sp->fts_rfd) :
-           fts_safe_changedir(sp, cur->fts_parent, -1, ".."))) {
-               cur->fts_info = FTS_ERR;
-               SET(FTS_STOP);
-               return (NULL);
-       }
+       if (len == sp->fts_pathlen || nitems == 0)
+               --cp;
+       *cp = '\0';
 
        /* If didn't find anything, return NULL. */
        if (!nitems) {
                cur->fts_info = FTS_DP;
                return (NULL);
        }
 
        /* If didn't find anything, return NULL. */
        if (!nitems) {
                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 (head);
 }
 
@@ -663,6 +553,41 @@ fts_stat(FTS *sp, FTSENT *p)
        return (FTS_DEFAULT);
 }
 
        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)
 {
 static FTSENT *
 fts_alloc(FTS *sp, const char *name, size_t namelen)
 {
@@ -714,20 +639,19 @@ fts_palloc(FTS *sp, size_t more)
         */
        more += 256;
        if (sp->fts_pathlen + more < sp->fts_pathlen) {
         */
        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_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 (p == NULL) {
-               if (sp->fts_path)
-                       free(sp->fts_path);
+               free(sp->fts_path);
                sp->fts_path = NULL;
                return (1);
        }
                sp->fts_path = NULL;
                return (1);
        }
+       sp->fts_pathlen += more;
        sp->fts_path = p;
        return (0);
 }
        sp->fts_path = p;
        return (0);
 }
@@ -770,39 +694,3 @@ fts_maxarglen(char * const *argv)
                        max = len;
        return (max + 1);
 }
                        max = len;
        return (max + 1);
 }
-
-/*
- * Change to dir specified by fd or p->fts_accpath without getting
- * tricked by someone changing the world out from underneath us.
- * Assumes p->fts_dev and p->fts_ino are filled in.
- */
-static int
-fts_safe_changedir(FTS *sp, FTSENT *p, int fd, const char *path)
-{
-       int ret, oerrno, newfd;
-       struct stat sb;
-
-       newfd = fd;
-       if (ISSET(FTS_NOCHDIR))
-               return (0);
-       if (fd < 0 && (newfd = open(path, O_RDONLY|O_DIRECTORY|O_CLOEXEC)) < 0)
-               return (-1);
-       if (fstat(newfd, &sb)) {
-               ret = -1;
-               goto bail;
-       }
-       if (p->fts_dev != sb.st_dev || p->fts_ino != sb.st_ino) {
-               errno = ENOENT;         /* disinformation */
-               ret = -1;
-               goto bail;
-       }
-       ret = fchdir(newfd);
-bail:
-       oerrno = errno;
-       if (fd < 0)
-               (void)close(newfd);
-       errno = oerrno;
-       return (ret);
-}
-
-#endif