BSD 4_3_Net_2 release
[unix-history] / usr / src / lib / libc / gen / fts.c
index bed99c8..e2958d3 100644 (file)
@@ -2,11 +2,37 @@
  * Copyright (c) 1990 The Regents of the University of California.
  * All rights reserved.
  *
  * Copyright (c) 1990 The Regents of the University of California.
  * All rights reserved.
  *
- * %sccs.include.redist.c%
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ *    must display the following acknowledgement:
+ *     This product includes software developed by the University of
+ *     California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ *    may be used to endorse or promote products derived from this software
+ *    without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
  */
 
 #if defined(LIBC_SCCS) && !defined(lint)
  */
 
 #if defined(LIBC_SCCS) && !defined(lint)
-static char sccsid[] = "@(#)fts.c      5.14 (Berkeley) %G%";
+static char sccsid[] = "@(#)fts.c      5.19 (Berkeley) 5/9/91";
 #endif /* LIBC_SCCS and not lint */
 
 #include <sys/cdefs.h>
 #endif /* LIBC_SCCS and not lint */
 
 #include <sys/cdefs.h>
@@ -21,8 +47,7 @@ static char sccsid[] = "@(#)fts.c     5.14 (Berkeley) %G%";
 #include <unistd.h>
 
 static FTSENT *fts_alloc(), *fts_build(), *fts_sort();
 #include <unistd.h>
 
 static FTSENT *fts_alloc(), *fts_build(), *fts_sort();
-static void fts_lfree();
-static int fts_load();
+static void fts_load(), fts_lfree();
 static u_short fts_stat();
 static char *fts_path();
 
 static u_short fts_stat();
 static char *fts_path();
 
@@ -36,10 +61,6 @@ static char *fts_path();
 #define        BCHILD          1               /* from fts_children */
 #define        BREAD           2               /* from fts_read */
 
 #define        BCHILD          1               /* from fts_children */
 #define        BREAD           2               /* from fts_read */
 
-/* fts_level values */
-#define        ROOTLEVEL        0
-#define        ROOTPARENTLEVEL -1
-
 FTS *
 fts_open(argv, options, compar)
        char * const *argv;
 FTS *
 fts_open(argv, options, compar)
        char * const *argv;
@@ -66,7 +87,7 @@ fts_open(argv, options, compar)
        /* Allocate/initialize root's parent. */
        if (!(parent = fts_alloc(sp, "", 0)))
                goto mem1;
        /* Allocate/initialize root's parent. */
        if (!(parent = fts_alloc(sp, "", 0)))
                goto mem1;
-       parent->fts_level = ROOTPARENTLEVEL;
+       parent->fts_level = FTS_ROOTPARENTLEVEL;
 
        /* Allocate/initialize root(s). */
        maxlen = -1;
 
        /* Allocate/initialize root(s). */
        maxlen = -1;
@@ -78,6 +99,8 @@ fts_open(argv, options, compar)
                if (maxlen < len)
                        maxlen = len;
                p = fts_alloc(sp, *argv, len);
                if (maxlen < len)
                        maxlen = len;
                p = fts_alloc(sp, *argv, len);
+               p->fts_level = FTS_ROOTLEVEL;
+               p->fts_parent = parent;
                /*
                 * If comparison routine supplied, traverse in sorted
                 * order; otherwise traverse in the order specified.
                /*
                 * If comparison routine supplied, traverse in sorted
                 * order; otherwise traverse in the order specified.
@@ -97,8 +120,6 @@ fts_open(argv, options, compar)
                                tmp = p;
                        }
                }
                                tmp = p;
                        }
                }
-               p->fts_level = ROOTLEVEL;
-               p->fts_parent = parent;
        }
        if (compar && nitems > 1)
                root = fts_sort(sp, root, nitems);
        }
        if (compar && nitems > 1)
                root = fts_sort(sp, root, nitems);
@@ -136,19 +157,19 @@ mem1:     free(sp);
        return(NULL);
 }
 
        return(NULL);
 }
 
-static
+static void
 fts_load(sp, p)
        FTS *sp;
        register FTSENT *p;
 {
 fts_load(sp, p)
        FTS *sp;
        register FTSENT *p;
 {
-       static int need_to_cd;
        register int len;
        register char *cp;
 
        /*
        register int len;
        register char *cp;
 
        /*
-        * If changing the root level node, load the stream structure for the
-        * next traversal; set the fts_accpath field specially so the chdir
-        * gets done to the right place and the user can access the first node.
+        * Load the stream structure for the next traversal.  Since we don't
+        * actually enter the directory until after the preorder visit, set
+        * the fts_accpath field specially so the chdir gets done to the right
+        * place and the user can access the first node.
         */
        len = p->fts_pathlen = p->fts_namelen;
        bcopy(p->fts_name, sp->fts_path, len + 1);
         */
        len = p->fts_pathlen = p->fts_namelen;
        bcopy(p->fts_name, sp->fts_path, len + 1);
@@ -159,23 +180,8 @@ fts_load(sp, p)
        }
        p->fts_accpath = p->fts_path = sp->fts_path;
 
        }
        p->fts_accpath = p->fts_path = sp->fts_path;
 
+       p->fts_info = fts_stat(sp, p, 0);
        sp->rdev = p->fts_statb.st_dev;
        sp->rdev = p->fts_statb.st_dev;
-
-       /*
-        * If not the first time, and it's not an absolute pathname, get back
-        * to starting directory.  If that fails, we're dead.
-        */
-       if (need_to_cd && p->fts_path[0] != '/' && FCHDIR(sp, sp->fts_rfd))
-               return(0);
-       need_to_cd = 1;
-
-       /*
-        * Special case error condition -- if we can't find the root of the
-        * traversal, make sure the user notices the error.
-        */
-       if ((p->fts_info = fts_stat(sp, p, 0)) == FTS_NS)
-               p->fts_info = FTS_ERR;
-       return(1);
 }
 
 fts_close(sp)
 }
 
 fts_close(sp)
@@ -190,7 +196,7 @@ fts_close(sp)
                 * structure points to the root list, so we step through to
                 * the end of the root list which has a valid parent pointer.
                 */
                 * structure points to the root list, so we step through to
                 * the end of the root list which has a valid parent pointer.
                 */
-               for (p = sp->fts_cur; p->fts_level > ROOTPARENTLEVEL;) {
+               for (p = sp->fts_cur; p->fts_level > FTS_ROOTPARENTLEVEL;) {
                        freep = p;
                        p = p->fts_link ? p->fts_link : p->fts_parent;
                        free(freep);
                        freep = p;
                        p = p->fts_link ? p->fts_link : p->fts_parent;
                        free(freep);
@@ -227,7 +233,7 @@ fts_close(sp)
  * paths to be written as "//foo".
  */
 #define        NAPPEND(p) \
  * paths to be written as "//foo".
  */
 #define        NAPPEND(p) \
-       (p->fts_level == ROOTLEVEL && p->fts_pathlen == 1 && \
+       (p->fts_level == FTS_ROOTLEVEL && p->fts_pathlen == 1 && \
            p->fts_path[0] == '/' ? 0 : p->fts_pathlen)
 
 FTSENT *
            p->fts_path[0] == '/' ? 0 : p->fts_pathlen)
 
 FTSENT *
@@ -288,10 +294,12 @@ fts_read(sp)
                 * Cd to the subdirectory, reading it if haven't already.  If
                 * the read fails for any reason, or the directory is empty,
                 * the fts_info field of the current node is set by fts_build.
                 * Cd to the subdirectory, reading it if haven't already.  If
                 * the read fails for any reason, or the directory is empty,
                 * the fts_info field of the current node is set by fts_build.
-                * If have already read and fail to chdir, do some quick
-                * whacking to make the names come out right, and set the
-                * parent state so the application will eventually get an
-                * error condition.
+                * If have already read and now fail to chdir, whack the list
+                * to make the names come out right, and set the parent state
+                * so the application will eventually get an error condition.
+                * If haven't read and fail to chdir, check to see if we're
+                * at the root node -- if so, we have to get back or the root
+                * node may be inaccessible.
                 */
                if (sp->fts_child) {
                        if (CHDIR(sp, p->fts_accpath)) {
                 */
                if (sp->fts_child) {
                        if (CHDIR(sp, p->fts_accpath)) {
@@ -300,8 +308,16 @@ fts_read(sp)
                                        p->fts_accpath =
                                            p->fts_parent->fts_accpath;
                        }
                                        p->fts_accpath =
                                            p->fts_parent->fts_accpath;
                        }
-               } else if (!(sp->fts_child = fts_build(sp, BREAD)))
-                       return(ISSET(FTS_STOP) ? NULL : p);
+               } else if (!(sp->fts_child = fts_build(sp, BREAD))) {
+                       if ISSET(FTS_STOP)
+                               return(NULL);
+                       if (p->fts_level == FTS_ROOTLEVEL &&
+                           FCHDIR(sp, sp->fts_rfd)) {
+                               SET(FTS_STOP);
+                               return(NULL);
+                       }
+                       return(p);
+               }
                p = sp->fts_child;
                sp->fts_child = NULL;
                goto name;
                p = sp->fts_child;
                sp->fts_child = NULL;
                goto name;
@@ -313,11 +329,8 @@ next:      tmp = p;
                free(tmp);
 
                /* If reached the top, load the paths for the next root. */
                free(tmp);
 
                /* If reached the top, load the paths for the next root. */
-               if (p->fts_level == ROOTLEVEL) {
-                       if (!fts_load(sp, p)) {
-                               SET(FTS_STOP);
-                               return(NULL);
-                       }
+               if (p->fts_level == FTS_ROOTLEVEL) {
+                       fts_load(sp, p);
                        return(sp->fts_cur = p);
                }
 
                        return(sp->fts_cur = p);
                }
 
@@ -335,11 +348,11 @@ name:             t = sp->fts_path + NAPPEND(p->fts_parent);
                return(sp->fts_cur = p);
        }
 
                return(sp->fts_cur = p);
        }
 
-       /* Move to parent. */
+       /* Move up to the parent node. */
        p = tmp->fts_parent;
        free(tmp);
 
        p = tmp->fts_parent;
        free(tmp);
 
-       if (p->fts_level == ROOTPARENTLEVEL) {
+       if (p->fts_level == FTS_ROOTPARENTLEVEL) {
                /*
                 * Done; free everything up and set errno to 0 so the user
                 * can distinguish between error and EOF.
                /*
                 * Done; free everything up and set errno to 0 so the user
                 * can distinguish between error and EOF.
@@ -352,28 +365,38 @@ name:             t = sp->fts_path + NAPPEND(p->fts_parent);
        sp->fts_path[p->fts_pathlen] = '\0';
 
        /*
        sp->fts_path[p->fts_pathlen] = '\0';
 
        /*
-        * If had a chdir error, set the info field to reflect this, and
-        * restore errno.  The error indicator has to be reset to 0 so that
-        * if the user does an FTS_AGAIN, it all works.  Can't cd up on
-        * the post-order visit to the root node, otherwise will be in the
-        * wrong directory.
+        * Cd back up to the parent directory.  If at a root node, have to cd
+        * back to the original place, otherwise may not be able to access the
+        * original node on post-order.
+        */
+       if (p->fts_level == FTS_ROOTLEVEL) {
+               if (FCHDIR(sp, sp->fts_rfd)) {
+                       SET(FTS_STOP);
+                       return(NULL);
+               }
+       }
+       else if (CHDIR(sp, "..")) {
+               SET(FTS_STOP);
+               return(NULL);
+       }
+
+       /* 
+        * If had a chdir error when trying to get into the directory, set the
+        * info field to reflect this, and restore errno.  The error indicator
+        * has to be reset to 0 so that if the user does an FTS_AGAIN, it all
+        * works.
         */
        if (p->fts_cderr) {
                errno = p->fts_cderr;
                p->fts_cderr = 0;
                p->fts_info = FTS_ERR;
         */
        if (p->fts_cderr) {
                errno = p->fts_cderr;
                p->fts_cderr = 0;
                p->fts_info = FTS_ERR;
-       } else {
-               if (p->fts_level != ROOTLEVEL && CHDIR(sp, "..")) {
-                       SET(FTS_STOP);
-                       return(NULL);
-               }
+       } else
                p->fts_info = FTS_DP;
                p->fts_info = FTS_DP;
-       }
        return(sp->fts_cur = p);
 }
 
 /*
        return(sp->fts_cur = p);
 }
 
 /*
- * fts_set takes the stream as an argument although it's not used in this
+ * Fts_set takes the stream as an argument although it's not used in this
  * implementation; it would be necessary if anyone wanted to add global
  * semantics to fts using fts_set.  An error return is allowed for similar
  * reasons.
  * implementation; it would be necessary if anyone wanted to add global
  * semantics to fts using fts_set.  An error return is allowed for similar
  * reasons.
@@ -419,7 +442,7 @@ fts_children(sp)
         * directory is, so we can't get back so that the upcoming chdir by
         * fts_read will work.
         */
         * directory is, so we can't get back so that the upcoming chdir by
         * fts_read will work.
         */
-       if (p->fts_level != ROOTLEVEL || p->fts_accpath[0] == '/' ||
+       if (p->fts_level != FTS_ROOTLEVEL || p->fts_accpath[0] == '/' ||
            ISSET(FTS_NOCHDIR))
                return(sp->fts_child = fts_build(sp, BCHILD));
 
            ISSET(FTS_NOCHDIR))
                return(sp->fts_child = fts_build(sp, BCHILD));
 
@@ -657,7 +680,7 @@ fts_stat(sp, p, follow)
 
                dev = p->fts_statb.st_dev;
                ino = p->fts_statb.st_ino;
 
                dev = p->fts_statb.st_dev;
                ino = p->fts_statb.st_ino;
-               for (t = p->fts_parent; t->fts_level > ROOTLEVEL;
+               for (t = p->fts_parent; t->fts_level > FTS_ROOTLEVEL;
                    t = t->fts_parent)
                        if (ino == t->fts_statb.st_ino &&
                            dev == t->fts_statb.st_dev) {
                    t = t->fts_parent)
                        if (ino == t->fts_statb.st_ino &&
                            dev == t->fts_statb.st_dev) {