• Main Page
  • Related Pages
  • Modules
  • Data Structures
  • Files
  • File List
  • Globals

rpmio/fts.c

Go to the documentation of this file.
00001 /*@-sysunrecog -noeffectuncon -nullpass -sizeoftype -unrecog -usereleased @*/
00002 /*@-compdef -compmempass -dependenttrans -retalias @*/
00003 /*-
00004  * Copyright (c) 1990, 1993, 1994
00005  *      The Regents of the University of California.  All rights reserved.
00006  *
00007  * Redistribution and use in source and binary forms, with or without
00008  * modification, are permitted provided that the following conditions
00009  * are met:
00010  * 1. Redistributions of source code must retain the above copyright
00011  *    notice, this list of conditions and the following disclaimer.
00012  * 2. Redistributions in binary form must reproduce the above copyright
00013  *    notice, this list of conditions and the following disclaimer in the
00014  *    documentation and/or other materials provided with the distribution.
00015  * 4. Neither the name of the University nor the names of its contributors
00016  *    may be used to endorse or promote products derived from this software
00017  *    without specific prior written permission.
00018  *
00019  * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
00020  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
00021  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
00022  * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
00023  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
00024  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
00025  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
00026  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
00027  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
00028  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
00029  * SUCH DAMAGE.
00030  */
00031 
00032 #include "system.h"
00033 
00034 #if defined(LIBC_SCCS) && !defined(lint)
00035 static char sccsid[] = "@(#)fts.c       8.6 (Berkeley) 8/14/94";
00036 #endif /* LIBC_SCCS and not lint */
00037 
00038 #if defined(_LIBC)
00039 #include <sys/param.h>
00040 #include <include/sys/stat.h>
00041 #include <fcntl.h>
00042 #include <dirent.h>
00043 #include <errno.h>
00044 #include <fts.h>
00045 #include <stdlib.h>
00046 #include <string.h>
00047 #include <unistd.h>
00048 #else
00049 #if defined(hpux) || defined(__hpux)
00050 # define        _INCLUDE_POSIX_SOURCE
00051 #   define __errno_location()   (&errno)
00052 #   define dirfd(dirp)          -1
00053 #   define stat64               stat
00054 #   define _STAT_VER            0
00055 #   define __fxstat64(_stat_ver, _fd, _sbp)     fstat((_fd), (_sbp))
00056 #   define _D_EXACT_NAMLEN(d) ((d)->d_namlen)
00057 #endif
00058 #if defined(sun) || defined(RPM_OS_UNIXWARE)
00059 #   define __errno_location()   (&errno)
00060 #   define dirfd(dirp)          -1
00061 #   define _STAT_VER            0
00062 #   define __fxstat64(_stat_ver, _fd, _sbp)     fstat((_fd), (_sbp))
00063 #endif
00064 #if defined(__APPLE__)
00065 #   include <sys/stat.h>
00066 #   define __errno_location()   (__error())
00067 #ifndef __DARWIN_STRUCT_STAT64
00068 #   define stat64               stat
00069 #endif
00070 #   define _STAT_VER            0
00071 #ifndef __DARWIN_STRUCT_STAT64
00072 #   define __fxstat64(_stat_ver, _fd, _sbp)     fstat((_fd), (_sbp))
00073 #else
00074 #   define __fxstat64(_stat_ver, _fd, _sbp)     fstat64((_fd), (_sbp))
00075 #endif
00076 #endif
00077 #if defined(__CYGWIN__) || defined(__MINGW32__)
00078 #   include <sys/stat.h>
00079 #if defined(__CYGWIN__)
00080 #   define __errno_location()   (__errno())
00081 #elif !defined(_UWIN)
00082 #   define __errno_location()   (_errno())
00083 #else
00084 #   define __errno_location()   (&errno)
00085 #endif
00086 #   define stat64               stat
00087 #   define _STAT_VER            0
00088 #   define __fxstat64(_stat_ver, _fd, _sbp)     fstat((_fd), (_sbp))
00089 #endif
00090 #if defined(__FreeBSD__) || defined(__NetBSD__) || defined(__OpenBSD__) || defined(__DragonFly__)
00091 #   define __errno_location()  (&errno)
00092 #   define stat64              stat
00093 #   define __fxstat64(_stat_ver, _fd, _sbp)    fstat((_fd), (_sbp))
00094 #   define _D_EXACT_NAMLEN(d) ((d)->d_namlen)
00095 #endif
00096 #if defined(__osf__)
00097 #   define __errno_location()   (&errno)
00098 #   define dirfd(dirp)          -1
00099 #   define stat64               stat
00100 #   define _STAT_VER            0
00101 #   define __fxstat64(_stat_ver, _fd, _sbp)     fstat((_fd), (_sbp))
00102 #   define _D_EXACT_NAMLEN(d) ((d)->d_namlen)
00103 #endif
00104 #if defined(RPM_OS_IRIX)
00105 #   define __errno_location()   (&errno)
00106 #   define dirfd(dirp)          -1
00107 #   define __fxstat64(_stat_ver, _fd, _sbp)     fstat((_fd), (_sbp))
00108 #   define _D_EXACT_NAMLEN(d) ((d)->d_reclen)
00109 #endif
00110 #if defined(RPM_OS_AIX)
00111 #   define __errno_location()   (&errno)
00112 #   define dirfd(dirp)          ((dirp)->dd_fd)
00113 #   define _STAT_VER            0
00114 #   define __fxstat64(_stat_ver, _fd, _sbp)     fstat((_fd), (_sbp))
00115 #   define _D_EXACT_NAMLEN(d) ((d)->d_namlen)
00116 #endif
00117 #if defined(RPM_OS_NTOQNX)
00118 #   define __errno_location()  (&errno)
00119 #   define stat64              stat
00120 #   define _STAT_VER           0
00121 #   define dirfd(dirp)         -1
00122 #   define __fxstat64(_stat_ver, _fd, _sbp)    fstat((_fd), (_sbp))
00123 #endif
00124 
00125 #if !defined(_D_EXACT_NAMLEN)
00126 #   define _D_EXACT_NAMLEN(d) (strlen((d)->d_name))
00127 #endif
00128 #include "fts.h"
00129 #include "rpmio.h"
00130 #include "rpmurl.h"
00131 #include "debug.h"
00132 #   define __set_errno(val) (*__errno_location ()) = (val)
00133 #   define __open       open
00134 #   define __close      close
00135 #   define __fchdir     fchdir
00136 #endif
00137 
00138 #if !defined(USHRT_MAX)
00139 #define USHRT_MAX       65535
00140 #endif
00141 
00142 /* Largest alignment size needed, minus one.
00143    Usually long double is the worst case.  */
00144 #ifndef ALIGNBYTES
00145 #if defined __GNUC__ && __GNUC__ >= 2
00146 # define alignof(TYPE) __alignof__ (TYPE)
00147 #else
00148 # define alignof(TYPE) \
00149     ((int) &((struct { char dummy1; TYPE dummy2; } *) 0)->dummy2)
00150 #endif
00151 #define ALIGNBYTES      (alignof(long double) - 1)
00152 #endif
00153 /* Align P to that size.  */
00154 #ifndef ALIGN
00155 #define ALIGN(p)        (((unsigned long int) (p) + ALIGNBYTES) & ~ALIGNBYTES)
00156 #endif
00157 
00158 /*@only@*/ /*@null@*/
00159 static FTSENT * fts_alloc(FTS * sp, const char * name, int namelen)
00160         /*@*/;
00161 /*@null@*/
00162 static FTSENT * fts_build(FTS * sp, int type)
00163         /*@globals fileSystem, internalState @*/
00164         /*@modifies *sp, fileSystem, internalState @*/;
00165 static void     fts_lfree(/*@only@*/ FTSENT * head)
00166         /*@modifies head @*/;
00167 static void     fts_load(FTS * sp, FTSENT * p)
00168         /*@modifies *sp, *p @*/;
00169 static size_t   fts_maxarglen(char * const * argv)
00170         /*@*/;
00171 static void     fts_padjust(FTS * sp, FTSENT * head)
00172         /*@modifies *sp, *head @*/;
00173 static int      fts_palloc(FTS * sp, size_t more)
00174         /*@modifies *sp @*/;
00175 static FTSENT * fts_sort(FTS * sp, /*@returned@*/ FTSENT * head, int nitems)
00176         /*@modifies *sp @*/;
00177 static u_short  fts_stat(FTS * sp, FTSENT * p, int follow)
00178         /*@modifies *p @*/;
00179 static int      fts_safe_changedir(FTS * sp, FTSENT * p, int fd,
00180                         const char * path)
00181         /*@globals fileSystem, internalState @*/
00182         /*@modifies fileSystem, internalState @*/;
00183 
00184 #define ISDOT(a)        (a[0] == '.' && (!a[1] || (a[1] == '.' && !a[2])))
00185 
00186 #define CLR(opt)        (sp->fts_options &= ~(opt))
00187 #define ISSET(opt)      (sp->fts_options & (opt))
00188 #define SET(opt)        (sp->fts_options |= (opt))
00189 
00190 #define FCHDIR(sp, fd)  (!ISSET(FTS_NOCHDIR) && __fchdir(fd))
00191 
00192 /* fts_build flags */
00193 #define BCHILD          1               /* fts_children */
00194 #define BNAMES          2               /* fts_children, names only */
00195 #define BREAD           3               /* fts_read */
00196 
00197 FTS *
00198 Fts_open(char * const * argv, int options,
00199                 int (*compar) (const FTSENT **, const FTSENT **))
00200 {
00201         register FTS *sp;
00202         register FTSENT *p, *root;
00203         register int nitems;
00204         FTSENT *parent = NULL;
00205         FTSENT *tmp = NULL;
00206         size_t len;
00207 
00208         /* Options check. */
00209         if (options & ~FTS_OPTIONMASK) {
00210                 __set_errno (EINVAL);
00211                 return (NULL);
00212         }
00213 
00214         /* Allocate/initialize the stream */
00215         if ((sp = malloc((u_int)sizeof(*sp))) == NULL)
00216                 return (NULL);
00217         memset(sp, 0, sizeof(*sp));
00218         sp->fts_compar = (int (*) (const void *, const void *)) compar;
00219         sp->fts_opendir = Opendir;
00220         sp->fts_readdir = Readdir;
00221         sp->fts_closedir = Closedir;
00222         sp->fts_stat = Stat;
00223         sp->fts_lstat = Lstat;
00224         sp->fts_options = options;
00225 
00226         /* Logical walks turn on NOCHDIR; symbolic links are too hard. */
00227         if (ISSET(FTS_LOGICAL))
00228                 SET(FTS_NOCHDIR);
00229 
00230         /*
00231          * Start out with 1K of path space, and enough, in any case,
00232          * to hold the user's paths.
00233          */
00234 #ifndef MAXPATHLEN
00235 #define MAXPATHLEN 1024
00236 #endif
00237         len = fts_maxarglen(argv);
00238         if (len < MAXPATHLEN)
00239             len = MAXPATHLEN;
00240         if (fts_palloc(sp, len))
00241                 goto mem1;
00242 
00243         /* Allocate/initialize root's parent. */
00244         if (*argv != NULL) {
00245                 if ((parent = fts_alloc(sp, "", 0)) == NULL)
00246                         goto mem2;
00247                 parent->fts_level = FTS_ROOTPARENTLEVEL;
00248         }
00249 
00250         /* Allocate/initialize root(s). */
00251         for (root = NULL, nitems = 0; *argv != NULL; ++argv, ++nitems) {
00252                 /* Don't allow zero-length paths. */
00253                 if ((len = strlen(*argv)) == 0) {
00254                         __set_errno (ENOENT);
00255                         goto mem3;
00256                 }
00257 
00258                 /* Use fchdir(2) speedup only if local DASDI. */
00259                 switch (urlIsURL(*argv)) {
00260                 case URL_IS_DASH:
00261                 case URL_IS_HKP:
00262                         __set_errno (ENOENT);
00263                         goto mem3;
00264                         /*@notreached@*/ /*@switchbreak@*/ break;
00265                 case URL_IS_HTTPS:
00266                 case URL_IS_HTTP:
00267                 case URL_IS_FTP:
00268                         SET(FTS_NOCHDIR);
00269                         /*@switchbreak@*/ break;
00270                 case URL_IS_UNKNOWN:
00271                 case URL_IS_PATH:
00272                         /*@switchbreak@*/ break;
00273                 }
00274 
00275                 p = fts_alloc(sp, *argv, (int)len);
00276                 if (p == NULL)
00277                         goto mem3;
00278                 p->fts_level = FTS_ROOTLEVEL;
00279                 p->fts_parent = parent;
00280                 p->fts_accpath = p->fts_name;
00281                 p->fts_info = fts_stat(sp, p, ISSET(FTS_COMFOLLOW));
00282 
00283                 /* Command-line "." and ".." are real directories. */
00284                 if (p->fts_info == FTS_DOT)
00285                         p->fts_info = FTS_D;
00286 
00287                 /*
00288                  * If comparison routine supplied, traverse in sorted
00289                  * order; otherwise traverse in the order specified.
00290                  */
00291                 if (compar) {
00292                         p->fts_link = root;
00293                         root = p;
00294                 } else {
00295                         p->fts_link = NULL;
00296                         if (root == NULL)
00297                                 tmp = root = p;
00298                         else {
00299                                 if (tmp != NULL)        /* XXX can't happen */
00300                                         tmp->fts_link = p;
00301                                 tmp = p;
00302                         }
00303                 }
00304         }
00305         if (compar && nitems > 1)
00306                 root = fts_sort(sp, root, nitems);
00307 
00308         /*
00309          * Allocate a dummy pointer and make fts_read think that we've just
00310          * finished the node before the root(s); set p->fts_info to FTS_INIT
00311          * so that everything about the "current" node is ignored.
00312          */
00313         if ((sp->fts_cur = fts_alloc(sp, "", 0)) == NULL)
00314                 goto mem3;
00315         sp->fts_cur->fts_link = root;
00316         sp->fts_cur->fts_info = FTS_INIT;
00317 
00318         /*
00319          * If using chdir(2), grab a file descriptor pointing to dot to ensure
00320          * that we can get back here; this could be avoided for some paths,
00321          * but almost certainly not worth the effort.  Slashes, symbolic links,
00322          * and ".." are all fairly nasty problems.  Note, if we can't get the
00323          * descriptor we run anyway, just more slowly.
00324          */
00325         if (!ISSET(FTS_NOCHDIR)
00326             && (sp->fts_rfd = __open(".", O_RDONLY, 0)) < 0)
00327                 SET(FTS_NOCHDIR);
00328 
00329         return (sp);
00330 
00331 mem3:   fts_lfree(root);
00332         free(parent);
00333 mem2:   free(sp->fts_path);
00334 mem1:   free(sp);
00335         return (NULL);
00336 }
00337 
00338 static void
00339 fts_load(FTS * sp, FTSENT * p)
00340 {
00341         register size_t len;
00342         register char *cp;
00343 
00344         /*
00345          * Load the stream structure for the next traversal.  Since we don't
00346          * actually enter the directory until after the preorder visit, set
00347          * the fts_accpath field specially so the chdir gets done to the right
00348          * place and the user can access the first node.  From fts_open it's
00349          * known that the path will fit.
00350          */
00351         len = p->fts_pathlen = p->fts_namelen;
00352         memmove(sp->fts_path, p->fts_name, len + 1);
00353         if ((cp = strrchr(p->fts_name, '/')) && (cp != p->fts_name || cp[1])) {
00354                 len = strlen(++cp);
00355                 memmove(p->fts_name, cp, len + 1);
00356                 p->fts_namelen = (u_short)len;
00357         }
00358         p->fts_accpath = p->fts_path = sp->fts_path;
00359         sp->fts_dev = p->fts_dev;
00360 }
00361 
00362 int
00363 Fts_close(FTS * sp)
00364 {
00365         register FTSENT *freep, *p;
00366         int saved_errno;
00367 
00368         if (sp == NULL)
00369                 return 0;
00370 
00371         /*
00372          * This still works if we haven't read anything -- the dummy structure
00373          * points to the root list, so we step through to the end of the root
00374          * list which has a valid parent pointer.
00375          */
00376         if (sp->fts_cur) {
00377                 for (p = sp->fts_cur; p->fts_level >= FTS_ROOTLEVEL;) {
00378                         freep = p;
00379                         p = p->fts_link != NULL ? p->fts_link : p->fts_parent;
00380                         free(freep);
00381                 }
00382                 free(p);
00383         }
00384 
00385         /* Free up child linked list, sort array, path buffer. */
00386         if (sp->fts_child)
00387                 fts_lfree(sp->fts_child);
00388         if (sp->fts_array)
00389                 free(sp->fts_array);
00390         free(sp->fts_path);
00391 
00392         /* Return to original directory, save errno if necessary. */
00393         if (!ISSET(FTS_NOCHDIR)) {
00394                 saved_errno = __fchdir(sp->fts_rfd) ? errno : 0;
00395                 (void)__close(sp->fts_rfd);
00396 
00397                 /* Set errno and return. */
00398                 if (saved_errno != 0) {
00399                         /* Free up the stream pointer. */
00400                         free(sp);
00401                         __set_errno (saved_errno);
00402                         return (-1);
00403                 }
00404         }
00405 
00406         /* Free up the stream pointer. */
00407         free(sp);
00408         return (0);
00409 }
00410 
00411 /*
00412  * Special case of "/" at the end of the path so that slashes aren't
00413  * appended which would cause paths to be written as "....//foo".
00414  */
00415 #define NAPPEND(p)                                                      \
00416         (p->fts_path[p->fts_pathlen - 1] == '/'                         \
00417             ? p->fts_pathlen - 1 : p->fts_pathlen)
00418 
00419 FTSENT *
00420 Fts_read(FTS * sp)
00421 {
00422         register FTSENT *p;
00423         register FTSENT *tmp;
00424         register int instr;
00425         register char *t;
00426         int saved_errno;
00427 
00428         /* If finished or unrecoverable error, return NULL. */
00429         if (sp == NULL || sp->fts_cur == NULL || ISSET(FTS_STOP))
00430                 return (NULL);
00431 
00432         /* Set current node pointer. */
00433         p = sp->fts_cur;
00434 
00435         /* Save and zero out user instructions. */
00436         instr = p->fts_instr;
00437         p->fts_instr = FTS_NOINSTR;
00438 
00439         /* Any type of file may be re-visited; re-stat and re-turn. */
00440         if (instr == FTS_AGAIN) {
00441                 p->fts_info = fts_stat(sp, p, 0);
00442                 return (p);
00443         }
00444 
00445         /*
00446          * Following a symlink -- SLNONE test allows application to see
00447          * SLNONE and recover.  If indirecting through a symlink, have
00448          * keep a pointer to current location.  If unable to get that
00449          * pointer, follow fails.
00450          */
00451         if (instr == FTS_FOLLOW &&
00452             (p->fts_info == FTS_SL || p->fts_info == FTS_SLNONE)) {
00453                 p->fts_info = fts_stat(sp, p, 1);
00454                 if (p->fts_info == FTS_D && !ISSET(FTS_NOCHDIR)) {
00455                         if ((p->fts_symfd = __open(".", O_RDONLY, 0)) < 0) {
00456                                 p->fts_errno = errno;
00457                                 p->fts_info = FTS_ERR;
00458                         } else
00459                                 p->fts_flags |= FTS_SYMFOLLOW;
00460                 }
00461                 return (p);
00462         }
00463 
00464         /* Directory in pre-order. */
00465         if (p->fts_info == FTS_D) {
00466                 /* If skipped or crossed mount point, do post-order visit. */
00467                 if (instr == FTS_SKIP ||
00468                     (ISSET(FTS_XDEV) && p->fts_dev != sp->fts_dev)) {
00469                         if (p->fts_flags & FTS_SYMFOLLOW)
00470                                 (void)__close(p->fts_symfd);
00471                         if (sp->fts_child) {
00472                                 fts_lfree(sp->fts_child);
00473                                 sp->fts_child = NULL;
00474                         }
00475                         p->fts_info = FTS_DP;
00476                         return (p);
00477                 }
00478 
00479                 /* Rebuild if only read the names and now traversing. */
00480                 if (sp->fts_child != NULL && ISSET(FTS_NAMEONLY)) {
00481                         CLR(FTS_NAMEONLY);
00482                         fts_lfree(sp->fts_child);
00483                         sp->fts_child = NULL;
00484                 }
00485 
00486                 /*
00487                  * Cd to the subdirectory.
00488                  *
00489                  * If have already read and now fail to chdir, whack the list
00490                  * to make the names come out right, and set the parent errno
00491                  * so the application will eventually get an error condition.
00492                  * Set the FTS_DONTCHDIR flag so that when we logically change
00493                  * directories back to the parent we don't do a chdir.
00494                  *
00495                  * If haven't read do so.  If the read fails, fts_build sets
00496                  * FTS_STOP or the fts_info field of the node.
00497                  */
00498                 if (sp->fts_child != NULL) {
00499                         if (fts_safe_changedir(sp, p, -1, p->fts_accpath)) {
00500                                 p->fts_errno = errno;
00501                                 p->fts_flags |= FTS_DONTCHDIR;
00502                                 for (p = sp->fts_child; p != NULL;
00503                                      p = p->fts_link)
00504                                         p->fts_accpath =
00505                                             p->fts_parent->fts_accpath;
00506                         }
00507                 } else if ((sp->fts_child = fts_build(sp, BREAD)) == NULL) {
00508                         if (ISSET(FTS_STOP))
00509                                 return (NULL);
00510                         return (p);
00511                 }
00512                 p = sp->fts_child;
00513                 sp->fts_child = NULL;
00514                 sp->fts_cur = p;
00515                 goto name;
00516         }
00517 
00518         /* Move to the next node on this level. */
00519 next:   tmp = p;
00520         if ((p = p->fts_link) != NULL) {
00521                 sp->fts_cur = p;
00522                 free(tmp);
00523 
00524                 /*
00525                  * If reached the top, return to the original directory (or
00526                  * the root of the tree), and load the paths for the next root.
00527                  */
00528                 if (p->fts_level == FTS_ROOTLEVEL) {
00529                         if (FCHDIR(sp, sp->fts_rfd)) {
00530                                 SET(FTS_STOP);
00531                                 return (NULL);
00532                         }
00533                         fts_load(sp, p);
00534                         return (p);
00535                 }
00536 
00537                 /*
00538                  * User may have called fts_set on the node.  If skipped,
00539                  * ignore.  If followed, get a file descriptor so we can
00540                  * get back if necessary.
00541                  */
00542                 if (p->fts_instr == FTS_SKIP)
00543                         goto next;
00544                 if (p->fts_instr == FTS_FOLLOW) {
00545                         p->fts_info = fts_stat(sp, p, 1);
00546                         if (p->fts_info == FTS_D && !ISSET(FTS_NOCHDIR)) {
00547                                 if ((p->fts_symfd =
00548                                     __open(".", O_RDONLY, 0)) < 0) {
00549                                         p->fts_errno = errno;
00550                                         p->fts_info = FTS_ERR;
00551                                 } else
00552                                         p->fts_flags |= FTS_SYMFOLLOW;
00553                         }
00554                         p->fts_instr = FTS_NOINSTR;
00555                 }
00556 
00557 name:           t = sp->fts_path + NAPPEND(p->fts_parent);
00558                 *t++ = '/';
00559                 memmove(t, p->fts_name, p->fts_namelen + 1);
00560                 return (p);
00561         }
00562 
00563         /* Move up to the parent node. */
00564         p = tmp->fts_parent;
00565         sp->fts_cur = p;
00566         free(tmp);
00567 
00568         if (p->fts_level == FTS_ROOTPARENTLEVEL) {
00569                 /*
00570                  * Done; free everything up and set errno to 0 so the user
00571                  * can distinguish between error and EOF.
00572                  */
00573                 free(p);
00574                 __set_errno (0);
00575                 return (sp->fts_cur = NULL);
00576         }
00577 
00578         /* NUL terminate the pathname. */
00579         sp->fts_path[p->fts_pathlen] = '\0';
00580 
00581         /*
00582          * Return to the parent directory.  If at a root node or came through
00583          * a symlink, go back through the file descriptor.  Otherwise, cd up
00584          * one directory.
00585          */
00586         if (p->fts_level == FTS_ROOTLEVEL) {
00587                 if (FCHDIR(sp, sp->fts_rfd)) {
00588                         SET(FTS_STOP);
00589                         return (NULL);
00590                 }
00591         } else if (p->fts_flags & FTS_SYMFOLLOW) {
00592                 if (FCHDIR(sp, p->fts_symfd)) {
00593                         saved_errno = errno;
00594                         (void)__close(p->fts_symfd);
00595                         __set_errno (saved_errno);
00596                         SET(FTS_STOP);
00597                         return (NULL);
00598                 }
00599                 (void)__close(p->fts_symfd);
00600         } else if (!(p->fts_flags & FTS_DONTCHDIR) &&
00601                    fts_safe_changedir(sp, p->fts_parent, -1, "..")) {
00602                 SET(FTS_STOP);
00603                 return (NULL);
00604         }
00605         p->fts_info = p->fts_errno ? FTS_ERR : FTS_DP;
00606         return (p);
00607 }
00608 
00609 /*
00610  * Fts_set takes the stream as an argument although it's not used in this
00611  * implementation; it would be necessary if anyone wanted to add global
00612  * semantics to fts using fts_set.  An error return is allowed for similar
00613  * reasons.
00614  */
00615 int
00616 Fts_set(/*@unused@*/ FTS * sp, FTSENT * p, int instr)
00617 {
00618         if (instr != 0 && instr != FTS_AGAIN && instr != FTS_FOLLOW &&
00619             instr != FTS_NOINSTR && instr != FTS_SKIP) {
00620                 __set_errno (EINVAL);
00621                 return (1);
00622         }
00623         p->fts_instr = instr;
00624         return (0);
00625 }
00626 
00627 FTSENT *
00628 Fts_children(FTS * sp, int instr)
00629 {
00630         register FTSENT *p;
00631         int fd;
00632 
00633         if (instr != 0 && instr != FTS_NAMEONLY) {
00634                 __set_errno (EINVAL);
00635                 return (NULL);
00636         }
00637 
00638         /* Set current node pointer. */
00639         p = sp->fts_cur;
00640 
00641         /*
00642          * Errno set to 0 so user can distinguish empty directory from
00643          * an error.
00644          */
00645         __set_errno (0);
00646 
00647         /* Fatal errors stop here. */
00648         if (ISSET(FTS_STOP))
00649                 return (NULL);
00650 
00651         /* Return logical hierarchy of user's arguments. */
00652         if (p->fts_info == FTS_INIT)
00653                 return (p->fts_link);
00654 
00655         /*
00656          * If not a directory being visited in pre-order, stop here.  Could
00657          * allow FTS_DNR, assuming the user has fixed the problem, but the
00658          * same effect is available with FTS_AGAIN.
00659          */
00660         if (p->fts_info != FTS_D /* && p->fts_info != FTS_DNR */)
00661                 return (NULL);
00662 
00663         /* Free up any previous child list. */
00664         if (sp->fts_child != NULL)
00665                 fts_lfree(sp->fts_child);
00666 
00667         if (instr == FTS_NAMEONLY) {
00668                 SET(FTS_NAMEONLY);
00669                 instr = BNAMES;
00670         } else
00671                 instr = BCHILD;
00672 
00673         /*
00674          * If using chdir on a relative path and called BEFORE fts_read does
00675          * its chdir to the root of a traversal, we can lose -- we need to
00676          * chdir into the subdirectory, and we don't know where the current
00677          * directory is, so we can't get back so that the upcoming chdir by
00678          * fts_read will work.
00679          */
00680         if (p->fts_level != FTS_ROOTLEVEL || p->fts_accpath[0] == '/' ||
00681             ISSET(FTS_NOCHDIR))
00682                 return (sp->fts_child = fts_build(sp, instr));
00683 
00684         if ((fd = __open(".", O_RDONLY, 0)) < 0)
00685                 return (NULL);
00686         sp->fts_child = fts_build(sp, instr);
00687         if (__fchdir(fd))
00688                 return (NULL);
00689         (void)__close(fd);
00690         return (sp->fts_child);
00691 }
00692 
00693 /*
00694  * This is the tricky part -- do not casually change *anything* in here.  The
00695  * idea is to build the linked list of entries that are used by fts_children
00696  * and fts_read.  There are lots of special cases.
00697  *
00698  * The real slowdown in walking the tree is the stat calls.  If FTS_NOSTAT is
00699  * set and it's a physical walk (so that symbolic links can't be directories),
00700  * we can do things quickly.  First, if it's a 4.4BSD file system, the type
00701  * of the file is in the directory entry.  Otherwise, we assume that the number
00702  * of subdirectories in a node is equal to the number of links to the parent.
00703  * The former skips all stat calls.  The latter skips stat calls in any leaf
00704  * directories and for any files after the subdirectories in the directory have
00705  * been found, cutting the stat calls by about 2/3.
00706  */
00707 static FTSENT *
00708 fts_build(FTS * sp, int type)
00709 {
00710         register struct dirent *dp;
00711         register FTSENT *p, *head;
00712         register int nitems;
00713         FTSENT *cur, *tail;
00714         DIR *dirp;
00715         void *oldaddr;
00716         int cderrno, descend, len, level, nlinks, saved_errno,
00717             nostat, doadjust;
00718         size_t maxlen;
00719         char *cp;
00720 
00721         /* Set current node pointer. */
00722         cur = sp->fts_cur;
00723 
00724         /*
00725          * Open the directory for reading.  If this fails, we're done.
00726          * If being called from fts_read, set the fts_info field.
00727          */
00728 #if defined FTS_WHITEOUT && 0
00729         if (ISSET(FTS_WHITEOUT))
00730                 oflag = DTF_NODUP|DTF_REWIND;
00731         else
00732                 oflag = DTF_HIDEW|DTF_NODUP|DTF_REWIND;
00733 #else
00734 # define __opendir2(path, flag) (*sp->fts_opendir) (path)
00735 #endif
00736        if ((dirp = __opendir2(cur->fts_accpath, oflag)) == NULL) {
00737                 if (type == BREAD) {
00738                         cur->fts_info = FTS_DNR;
00739                         cur->fts_errno = errno;
00740                 }
00741                 return (NULL);
00742         }
00743 
00744         /*
00745          * Nlinks is the number of possible entries of type directory in the
00746          * directory if we're cheating on stat calls, 0 if we're not doing
00747          * any stat calls at all, -1 if we're doing stats on everything.
00748          */
00749         if (type == BNAMES) {
00750                 nlinks = 0;
00751                 /* Be quiet about nostat, GCC. */
00752                 nostat = 0;
00753         } else if (ISSET(FTS_NOSTAT) && ISSET(FTS_PHYSICAL)) {
00754                 nlinks = cur->fts_nlink - (ISSET(FTS_SEEDOT) ? 0 : 2);
00755                 nostat = 1;
00756         } else {
00757                 nlinks = -1;
00758                 nostat = 0;
00759         }
00760 
00761 #ifdef notdef
00762         (void)printf("nlinks == %d (cur: %d)\n", nlinks, cur->fts_nlink);
00763         (void)printf("NOSTAT %d PHYSICAL %d SEEDOT %d\n",
00764             ISSET(FTS_NOSTAT), ISSET(FTS_PHYSICAL), ISSET(FTS_SEEDOT));
00765 #endif
00766         /*
00767          * If we're going to need to stat anything or we want to descend
00768          * and stay in the directory, chdir.  If this fails we keep going,
00769          * but set a flag so we don't chdir after the post-order visit.
00770          * We won't be able to stat anything, but we can still return the
00771          * names themselves.  Note, that since fts_read won't be able to
00772          * chdir into the directory, it will have to return different path
00773          * names than before, i.e. "a/b" instead of "b".  Since the node
00774          * has already been visited in pre-order, have to wait until the
00775          * post-order visit to return the error.  There is a special case
00776          * here, if there was nothing to stat then it's not an error to
00777          * not be able to stat.  This is all fairly nasty.  If a program
00778          * needed sorted entries or stat information, they had better be
00779          * checking FTS_NS on the returned nodes.
00780          */
00781         cderrno = 0;
00782         if (nlinks || type == BREAD) {
00783                 if (fts_safe_changedir(sp, cur, dirfd(dirp), NULL)) {
00784                         if (nlinks && type == BREAD)
00785                                 cur->fts_errno = errno;
00786                         cur->fts_flags |= FTS_DONTCHDIR;
00787                         descend = 0;
00788                         cderrno = errno;
00789                         (void) (*sp->fts_closedir) (dirp);
00790                         dirp = NULL;
00791                 } else
00792                         descend = 1;
00793         } else
00794                 descend = 0;
00795 
00796         /*
00797          * Figure out the max file name length that can be stored in the
00798          * current path -- the inner loop allocates more path as necessary.
00799          * We really wouldn't have to do the maxlen calculations here, we
00800          * could do them in fts_read before returning the path, but it's a
00801          * lot easier here since the length is part of the dirent structure.
00802          *
00803          * If not changing directories set a pointer so that can just append
00804          * each new name into the path.
00805          */
00806         len = NAPPEND(cur);
00807         if (ISSET(FTS_NOCHDIR)) {
00808                 cp = sp->fts_path + len;
00809                 *cp++ = '/';
00810         } else {
00811                 /* GCC, you're too verbose. */
00812                 cp = NULL;
00813         }
00814         len++;
00815         maxlen = sp->fts_pathlen - len;
00816 
00817         level = cur->fts_level + 1;
00818 
00819         /* Read the directory, attaching each entry to the `link' pointer. */
00820         doadjust = 0;
00821         for (head = tail = NULL, nitems = 0;
00822              dirp && (dp = (*sp->fts_readdir) (dirp));)
00823         {
00824                 if (!ISSET(FTS_SEEDOT) && ISDOT(dp->d_name))
00825                         continue;
00826 
00827                 if ((p = fts_alloc(sp, dp->d_name, (int)_D_EXACT_NAMLEN (dp))) == NULL)
00828                         goto mem1;
00829                 if (_D_EXACT_NAMLEN (dp) >= maxlen) {/* include space for NUL */
00830                         oldaddr = sp->fts_path;
00831                         if (fts_palloc(sp, _D_EXACT_NAMLEN (dp) + len + 1)) {
00832                                 /*
00833                                  * No more memory for path or structures.  Save
00834                                  * errno, free up the current structure and the
00835                                  * structures already allocated.
00836                                  */
00837 mem1:                           saved_errno = errno;
00838                                 if (p)
00839                                         free(p);
00840                                 fts_lfree(head);
00841                                 (void) (*sp->fts_closedir) (dirp);
00842                                 cur->fts_info = FTS_ERR;
00843                                 SET(FTS_STOP);
00844                                 __set_errno (saved_errno);
00845                                 return (NULL);
00846                         }
00847                         /* Did realloc() change the pointer? */
00848                         if (oldaddr != sp->fts_path) {
00849                                 doadjust = 1;
00850                                 if (ISSET(FTS_NOCHDIR))
00851                                         cp = sp->fts_path + len;
00852                         }
00853                         maxlen = sp->fts_pathlen - len;
00854                 }
00855 
00856                 if (len + _D_EXACT_NAMLEN (dp) >= USHRT_MAX) {
00857                         /*
00858                          * In an FTSENT, fts_pathlen is a u_short so it is
00859                          * possible to wraparound here.  If we do, free up
00860                          * the current structure and the structures already
00861                          * allocated, then error out with ENAMETOOLONG.
00862                          */
00863                         free(p);
00864                         fts_lfree(head);
00865                         (void) (*sp->fts_closedir) (dirp);
00866                         cur->fts_info = FTS_ERR;
00867                         SET(FTS_STOP);
00868                         __set_errno (ENAMETOOLONG);
00869                         return (NULL);
00870                 }
00871                 p->fts_level = level;
00872                 p->fts_parent = sp->fts_cur;
00873                 p->fts_pathlen = (u_short)(len + _D_EXACT_NAMLEN (dp));
00874 
00875 #if defined FTS_WHITEOUT && 0
00876                 if (dp->d_type == DT_WHT)
00877                         p->fts_flags |= FTS_ISW;
00878 #endif
00879 
00880 #if 0
00881                 /*
00882                  * Unreachable code.  cderrno is only ever set to a nonnull
00883                  * value if dirp is closed at the same time.  But then we
00884                  * cannot enter this loop.
00885                  */
00886                 if (cderrno) {
00887                         if (nlinks) {
00888                                 p->fts_info = FTS_NS;
00889                                 p->fts_errno = cderrno;
00890                         } else
00891                                 p->fts_info = FTS_NSOK;
00892                         p->fts_accpath = cur->fts_accpath;
00893                 } else
00894 #endif
00895                 if (nlinks == 0
00896 #if defined DT_DIR && defined _DIRENT_HAVE_D_TYPE
00897                            || (nostat &&
00898                                dp->d_type != DT_DIR && dp->d_type != DT_UNKNOWN)
00899 #endif
00900                     ) {
00901                         p->fts_accpath =
00902                             ISSET(FTS_NOCHDIR) ? p->fts_path : p->fts_name;
00903                         p->fts_info = FTS_NSOK;
00904                 } else {
00905                         /* Build a file name for fts_stat to stat. */
00906                         if (ISSET(FTS_NOCHDIR)) {
00907                                 p->fts_accpath = p->fts_path;
00908                                 memmove(cp, p->fts_name, p->fts_namelen + 1);
00909                         } else
00910                                 p->fts_accpath = p->fts_name;
00911                         /* Stat it. */
00912                         p->fts_info = fts_stat(sp, p, 0);
00913 
00914                         /* Decrement link count if applicable. */
00915                         if (nlinks > 0 && (p->fts_info == FTS_D ||
00916                             p->fts_info == FTS_DC || p->fts_info == FTS_DOT))
00917                                 --nlinks;
00918                 }
00919 
00920                 /* We walk in directory order so "ls -f" doesn't get upset. */
00921                 p->fts_link = NULL;
00922                 if (head == NULL)
00923                         head = tail = p;
00924                 else {
00925                         tail->fts_link = p;
00926                         tail = p;
00927                 }
00928                 ++nitems;
00929         }
00930         if (dirp)
00931                 (void) (*sp->fts_closedir) (dirp);
00932 
00933         /*
00934          * If realloc() changed the address of the path, adjust the
00935          * addresses for the rest of the tree and the dir list.
00936          */
00937         if (doadjust)
00938                 fts_padjust(sp, head);
00939 
00940         /*
00941          * If not changing directories, reset the path back to original
00942          * state.
00943          */
00944         if (ISSET(FTS_NOCHDIR)) {
00945                 if (len == sp->fts_pathlen || nitems == 0)
00946                         --cp;
00947                 if (cp != NULL) /* XXX can't happen */
00948                         *cp = '\0';
00949         }
00950 
00951         /*
00952          * If descended after called from fts_children or after called from
00953          * fts_read and nothing found, get back.  At the root level we use
00954          * the saved fd; if one of fts_open()'s arguments is a relative path
00955          * to an empty directory, we wind up here with no other way back.  If
00956          * can't get back, we're done.
00957          */
00958         if (descend && (type == BCHILD || !nitems) &&
00959             (cur->fts_level == FTS_ROOTLEVEL ?
00960              FCHDIR(sp, sp->fts_rfd) :
00961              fts_safe_changedir(sp, cur->fts_parent, -1, ".."))) {
00962                 cur->fts_info = FTS_ERR;
00963                 SET(FTS_STOP);
00964                 fts_lfree(head);
00965                 return (NULL);
00966         }
00967 
00968         /* If didn't find anything, return NULL. */
00969         if (!nitems) {
00970                 if (type == BREAD)
00971                         cur->fts_info = FTS_DP;
00972                 fts_lfree(head);
00973                 return (NULL);
00974         }
00975 
00976         /* Sort the entries. */
00977         if (sp->fts_compar && nitems > 1)
00978                 head = fts_sort(sp, head, nitems);
00979         return (head);
00980 }
00981 
00982 static u_short
00983 fts_stat(FTS * sp, FTSENT * p, int follow)
00984 {
00985         register FTSENT *t;
00986         register dev_t dev;
00987         register ino_t ino;
00988         struct stat *sbp, sb;
00989         int saved_errno;
00990 
00991         /* If user needs stat info, stat buffer already allocated. */
00992         sbp = ISSET(FTS_NOSTAT) ? &sb : p->fts_statp;
00993 
00994 #if defined FTS_WHITEOUT && 0
00995         /* check for whiteout */
00996         if (p->fts_flags & FTS_ISW) {
00997                 if (sbp != &sb) {
00998                         memset(sbp, '\0', sizeof (*sbp));
00999                         sbp->st_mode = S_IFWHT;
01000                 }
01001                 return (FTS_W);
01002        }
01003 #endif
01004 
01005         /*
01006          * If doing a logical walk, or application requested FTS_FOLLOW, do
01007          * a stat(2).  If that fails, check for a non-existent symlink.  If
01008          * fail, set the errno from the stat call.
01009          */
01010         if (ISSET(FTS_LOGICAL) || follow) {
01011                 if ((*sp->fts_stat) (p->fts_accpath, sbp)) {
01012                         saved_errno = errno;
01013                         if (!(*sp->fts_lstat) (p->fts_accpath, sbp)) {
01014                                 __set_errno (0);
01015                                 return (FTS_SLNONE);
01016                         }
01017                         p->fts_errno = saved_errno;
01018                         goto err;
01019                 }
01020         } else if ((*sp->fts_lstat) (p->fts_accpath, sbp)) {
01021                 p->fts_errno = errno;
01022 err:            memset(sbp, 0, sizeof(*sbp));
01023                 return (FTS_NS);
01024         }
01025 
01026         if (S_ISDIR(sbp->st_mode)) {
01027                 /*
01028                  * Set the device/inode.  Used to find cycles and check for
01029                  * crossing mount points.  Also remember the link count, used
01030                  * in fts_build to limit the number of stat calls.  It is
01031                  * understood that these fields are only referenced if fts_info
01032                  * is set to FTS_D.
01033                  */
01034                 dev = p->fts_dev = sbp->st_dev;
01035                 ino = p->fts_ino = sbp->st_ino;
01036                 p->fts_nlink = sbp->st_nlink;
01037 
01038                 if (ISDOT(p->fts_name))
01039                         return (FTS_DOT);
01040 
01041                 /*
01042                  * Cycle detection is done by brute force when the directory
01043                  * is first encountered.  If the tree gets deep enough or the
01044                  * number of symbolic links to directories is high enough,
01045                  * something faster might be worthwhile.
01046                  */
01047                 for (t = p->fts_parent;
01048                     t->fts_level >= FTS_ROOTLEVEL; t = t->fts_parent)
01049                         if (ino == t->fts_ino && dev == t->fts_dev) {
01050                                 p->fts_cycle = t;
01051                                 return (FTS_DC);
01052                         }
01053                 return (FTS_D);
01054         }
01055         if (S_ISLNK(sbp->st_mode))
01056                 return (FTS_SL);
01057         if (S_ISREG(sbp->st_mode))
01058                 return (FTS_F);
01059         return (FTS_DEFAULT);
01060 }
01061 
01062 static FTSENT *
01063 fts_sort(FTS * sp, FTSENT * head, int nitems)
01064 {
01065         register FTSENT **ap, *p;
01066 
01067         /*
01068          * Construct an array of pointers to the structures and call qsort(3).
01069          * Reassemble the array in the order returned by qsort.  If unable to
01070          * sort for memory reasons, return the directory entries in their
01071          * current order.  Allocate enough space for the current needs plus
01072          * 40 so don't realloc one entry at a time.
01073          */
01074         if (nitems > sp->fts_nitems) {
01075                 struct _ftsent **a;
01076 
01077                 sp->fts_nitems = nitems + 40;
01078                 if ((a = realloc(sp->fts_array,
01079                     (size_t)(sp->fts_nitems * sizeof(*sp->fts_array)))) == NULL)
01080                 {
01081                         free(sp->fts_array);
01082                         sp->fts_array = NULL;
01083                         sp->fts_nitems = 0;
01084                         return (head);
01085                 }
01086                 sp->fts_array = a;
01087         }
01088         for (ap = sp->fts_array, p = head; p != NULL; p = p->fts_link)
01089                 *ap++ = p;
01090         qsort((void *)sp->fts_array, nitems, sizeof(*sp->fts_array),
01091                 sp->fts_compar);
01092         for (head = *(ap = sp->fts_array); --nitems; ++ap)
01093                 ap[0]->fts_link = ap[1];
01094         ap[0]->fts_link = NULL;
01095         return (head);
01096 }
01097 
01098 static FTSENT *
01099 fts_alloc(FTS * sp, const char * name, int namelen)
01100 {
01101         register FTSENT *p;
01102         size_t len;
01103 
01104         /*
01105          * The file name is a variable length array and no stat structure is
01106          * necessary if the user has set the nostat bit.  Allocate the FTSENT
01107          * structure, the file name and the stat structure in one chunk, but
01108          * be careful that the stat structure is reasonably aligned.  Since the
01109          * fts_name field is declared to be of size 1, the fts_name pointer is
01110          * namelen + 2 before the first possible address of the stat structure.
01111          */
01112         len = sizeof(*p) + namelen;
01113         if (!ISSET(FTS_NOSTAT))
01114                 len += sizeof(*p->fts_statp) + ALIGNBYTES;
01115         if ((p = malloc(len)) == NULL)
01116                 return (NULL);
01117 
01118         /* Copy the name and guarantee NUL termination. */
01119         memmove(p->fts_name, name, namelen);
01120         p->fts_name[namelen] = '\0';
01121 
01122         if (!ISSET(FTS_NOSTAT))
01123                 p->fts_statp = (struct stat *)ALIGN(p->fts_name + namelen + 2);
01124         p->fts_namelen = namelen;
01125         p->fts_path = sp->fts_path;
01126         p->fts_errno = 0;
01127         p->fts_flags = 0;
01128         p->fts_instr = FTS_NOINSTR;
01129         p->fts_number = 0;
01130         p->fts_pointer = NULL;
01131         return (p);
01132 }
01133 
01134 static void
01135 fts_lfree(FTSENT * head)
01136 {
01137         register FTSENT *p;
01138 
01139         /* Free a linked list of structures. */
01140         while ((p = head)) {
01141                 head = head->fts_link;
01142                 free(p);
01143         }
01144 }
01145 
01146 /*
01147  * Allow essentially unlimited paths; find, rm, ls should all work on any tree.
01148  * Most systems will allow creation of paths much longer than MAXPATHLEN, even
01149  * though the kernel won't resolve them.  Add the size (not just what's needed)
01150  * plus 256 bytes so don't realloc the path 2 bytes at a time.
01151  */
01152 static int
01153 fts_palloc(FTS * sp, size_t more)
01154 {
01155         char *p;
01156 
01157         sp->fts_pathlen += more + 256;
01158         /*
01159          * Check for possible wraparound.  In an FTS, fts_pathlen is
01160          * a signed int but in an FTSENT it is an unsigned short.
01161          * We limit fts_pathlen to USHRT_MAX to be safe in both cases.
01162          */
01163         if (sp->fts_pathlen < 0 || sp->fts_pathlen >= USHRT_MAX) {
01164                 if (sp->fts_path)
01165                         free(sp->fts_path);
01166                 sp->fts_path = NULL;
01167                 __set_errno (ENAMETOOLONG);
01168                 return (1);
01169         }
01170         p = realloc(sp->fts_path, sp->fts_pathlen);
01171         if (p == NULL) {
01172                 free(sp->fts_path);
01173                 sp->fts_path = NULL;
01174                 return 1;
01175         }
01176         sp->fts_path = p;
01177         return 0;
01178 }
01179 
01180 /*
01181  * When the path is realloc'd, have to fix all of the pointers in structures
01182  * already returned.
01183  */
01184 static void
01185 fts_padjust(FTS * sp, FTSENT * head)
01186 {
01187         FTSENT *p;
01188         char *addr = sp->fts_path;
01189 
01190 #define ADJUST(p) do {                                                  \
01191         if ((p)->fts_accpath != (p)->fts_name) {                        \
01192                 (p)->fts_accpath =                                      \
01193                     (char *)addr + ((p)->fts_accpath - (p)->fts_path);  \
01194         }                                                               \
01195         (p)->fts_path = addr;                                           \
01196 } while (0)
01197         /* Adjust the current set of children. */
01198         for (p = sp->fts_child; p != NULL; p = p->fts_link)
01199                 ADJUST(p);
01200 
01201         /* Adjust the rest of the tree, including the current level. */
01202         for (p = head; p->fts_level >= FTS_ROOTLEVEL;) {
01203                 ADJUST(p);
01204                 p = p->fts_link ? p->fts_link : p->fts_parent;
01205         }
01206 }
01207 
01208 static size_t
01209 fts_maxarglen(char * const * argv)
01210 {
01211         size_t len, max;
01212 
01213         for (max = 0; *argv; ++argv)
01214                 if ((len = strlen(*argv)) > max)
01215                         max = len;
01216         return (max + 1);
01217 }
01218 
01219 /*
01220  * Change to dir specified by fd or p->fts_accpath without getting
01221  * tricked by someone changing the world out from underneath us.
01222  * Assumes p->fts_dev and p->fts_ino are filled in.
01223  */
01224 static int
01225 fts_safe_changedir(FTS * sp, FTSENT * p, int fd, const char * path)
01226 {
01227         int ret, oerrno, newfd;
01228         struct stat64 sb;
01229 
01230         newfd = fd;
01231         if (ISSET(FTS_NOCHDIR))
01232                 return (0);
01233         if (fd < 0 && (newfd = __open(path, O_RDONLY, 0)) < 0)
01234                 return (-1);
01235         if (__fxstat64(_STAT_VER, newfd, &sb)) {
01236                 ret = -1;
01237                 goto bail;
01238         }
01239         if (p->fts_dev != sb.st_dev || p->fts_ino != sb.st_ino) {
01240                 __set_errno (ENOENT);           /* disinformation */
01241                 ret = -1;
01242                 goto bail;
01243         }
01244         ret = __fchdir(newfd);
01245 bail:
01246         oerrno = errno;
01247         if (fd < 0)
01248                 (void)__close(newfd);
01249         __set_errno (oerrno);
01250         return (ret);
01251 }
01252 /*@=compdef =compmempass =dependenttrans =retalias @*/
01253 /*@=sysunrecog =noeffectuncon =nullpass =sizeoftype =unrecog =usereleased @*/

Generated on Mon Nov 29 2010 05:18:47 for rpm by  doxygen 1.7.2