- Main Page
- Related Pages
- Modules
- Data Structures
- Files

# 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 @*/