[BACK]Return to fts.c CVS log [TXT][DIR] Up to [cvs.NetBSD.org] / src / lib / libc / gen

Annotation of src/lib/libc/gen/fts.c, Revision 1.51

1.51    ! christos    1: /*     $NetBSD: fts.c,v 1.50 2021/11/02 08:39:20 nia Exp $     */
1.12      cgd         2:
1.25      christos    3: /*-
                      4:  * Copyright (c) 1990, 1993, 1994
                      5:  *     The Regents of the University of California.  All rights reserved.
                      6:  *
                      7:  * Redistribution and use in source and binary forms, with or without
                      8:  * modification, are permitted provided that the following conditions
                      9:  * are met:
                     10:  * 1. Redistributions of source code must retain the above copyright
                     11:  *    notice, this list of conditions and the following disclaimer.
                     12:  * 2. Redistributions in binary form must reproduce the above copyright
                     13:  *    notice, this list of conditions and the following disclaimer in the
                     14:  *    documentation and/or other materials provided with the distribution.
                     15:  * 3. Neither the name of the University nor the names of its contributors
                     16:  *    may be used to endorse or promote products derived from this software
                     17:  *    without specific prior written permission.
                     18:  *
                     19:  * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
                     20:  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
                     21:  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
                     22:  * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
                     23:  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
                     24:  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
                     25:  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
                     26:  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
                     27:  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
                     28:  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
                     29:  * SUCH DAMAGE.
1.1       cgd        30:  */
                     31:
1.25      christos   32: #if HAVE_NBTOOL_CONFIG_H
                     33: #include "nbtool_config.h"
                     34: #endif
                     35:
                     36: #include <sys/cdefs.h>
                     37: #if defined(LIBC_SCCS) && !defined(lint)
                     38: #if 0
                     39: static char sccsid[] = "@(#)fts.c      8.6 (Berkeley) 8/14/94";
                     40: #else
1.51    ! christos   41: __RCSID("$NetBSD: fts.c,v 1.50 2021/11/02 08:39:20 nia Exp $");
1.25      christos   42: #endif
                     43: #endif /* LIBC_SCCS and not lint */
                     44:
1.24      christos   45: #include "namespace.h"
1.25      christos   46: #include <sys/param.h>
                     47: #include <sys/stat.h>
                     48:
                     49: #include <assert.h>
1.24      christos   50: #include <dirent.h>
1.25      christos   51: #include <errno.h>
                     52: #include <fcntl.h>
                     53: #include <fts.h>
                     54: #include <stdlib.h>
                     55: #include <string.h>
                     56: #include <unistd.h>
                     57:
                     58: #if ! HAVE_NBTOOL_CONFIG_H
1.32      lukem      59: #define        HAVE_STRUCT_DIRENT_D_NAMLEN
1.25      christos   60: #endif
                     61:
1.28      christos   62: static FTSENT  *fts_alloc(FTS *, const char *, size_t);
                     63: static FTSENT  *fts_build(FTS *, int);
1.33      lukem      64: static void     fts_free(FTSENT *);
1.28      christos   65: static void     fts_lfree(FTSENT *);
                     66: static void     fts_load(FTS *, FTSENT *);
                     67: static size_t   fts_maxarglen(char * const *);
                     68: static size_t   fts_pow2(size_t);
                     69: static int      fts_palloc(FTS *, size_t);
                     70: static void     fts_padjust(FTS *, FTSENT *);
                     71: static FTSENT  *fts_sort(FTS *, FTSENT *, size_t);
1.32      lukem      72: static unsigned short fts_stat(FTS *, FTSENT *, int);
1.28      christos   73: static int      fts_safe_changedir(const FTS *, const FTSENT *, int,
                     74:     const char *);
1.25      christos   75:
1.33      lukem      76: #if defined(ALIGNBYTES) && defined(ALIGN)
                     77: #define        FTS_ALLOC_ALIGNED       1
                     78: #else
                     79: #undef FTS_ALLOC_ALIGNED
                     80: #endif
                     81:
1.44      christos   82: #ifndef ftsent_namelen_truncate
                     83: #define ftsent_namelen_truncate(a)     \
                     84:     ((a) > UINT_MAX ? UINT_MAX : (unsigned int)(a))
                     85: #endif
                     86: #ifndef ftsent_pathlen_truncate
                     87: #define ftsent_pathlen_truncate(a) \
1.43      christos   88:     ((a) > UINT_MAX ? UINT_MAX : (unsigned int)(a))
                     89: #endif
                     90: #ifndef fts_pathlen_truncate
                     91: #define fts_pathlen_truncate(a)        \
                     92:     ((a) > UINT_MAX ? UINT_MAX : (unsigned int)(a))
                     93: #endif
1.44      christos   94: #ifndef fts_nitems_truncate
                     95: #define fts_nitems_truncate(a) \
1.43      christos   96:     ((a) > UINT_MAX ? UINT_MAX : (unsigned int)(a))
                     97: #endif
                     98:
1.25      christos   99: #define        ISDOT(a)        (a[0] == '.' && (!a[1] || (a[1] == '.' && !a[2])))
                    100:
                    101: #define        CLR(opt)        (sp->fts_options &= ~(opt))
                    102: #define        ISSET(opt)      (sp->fts_options & (opt))
                    103: #define        SET(opt)        (sp->fts_options |= (opt))
                    104:
                    105: #define        CHDIR(sp, path) (!ISSET(FTS_NOCHDIR) && chdir(path))
                    106: #define        FCHDIR(sp, fd)  (!ISSET(FTS_NOCHDIR) && fchdir(fd))
                    107:
                    108: /* fts_build flags */
                    109: #define        BCHILD          1               /* fts_children */
                    110: #define        BNAMES          2               /* fts_children, names only */
                    111: #define        BREAD           3               /* fts_read */
                    112:
                    113: #ifndef DTF_HIDEW
                    114: #undef FTS_WHITEOUT
                    115: #endif
                    116:
                    117: FTS *
1.28      christos  118: fts_open(char * const *argv, int options,
                    119:     int (*compar)(const FTSENT **, const FTSENT **))
1.25      christos  120: {
                    121:        FTS *sp;
                    122:        FTSENT *p, *root;
                    123:        size_t nitems;
                    124:        FTSENT *parent, *tmp = NULL;    /* pacify gcc */
                    125:        size_t len;
                    126:
                    127:        _DIAGASSERT(argv != NULL);
                    128:
                    129:        /* Options check. */
                    130:        if (options & ~FTS_OPTIONMASK) {
                    131:                errno = EINVAL;
                    132:                return (NULL);
                    133:        }
                    134:
                    135:        /* Allocate/initialize the stream */
1.49      pgoyette  136:        if ((sp = calloc(1, sizeof(FTS))) == NULL)
1.25      christos  137:                return (NULL);
                    138:        sp->fts_compar = compar;
                    139:        sp->fts_options = options;
                    140:
                    141:        /* Logical walks turn on NOCHDIR; symbolic links are too hard. */
                    142:        if (ISSET(FTS_LOGICAL))
                    143:                SET(FTS_NOCHDIR);
                    144:
                    145:        /*
                    146:         * Start out with 1K of path space, and enough, in any case,
                    147:         * to hold the user's paths.
                    148:         */
                    149:        if (fts_palloc(sp, MAX(fts_maxarglen(argv), MAXPATHLEN)))
                    150:                goto mem1;
                    151:
                    152:        /* Allocate/initialize root's parent. */
                    153:        if ((parent = fts_alloc(sp, "", 0)) == NULL)
                    154:                goto mem2;
                    155:        parent->fts_level = FTS_ROOTPARENTLEVEL;
                    156:
                    157:        /* Allocate/initialize root(s). */
                    158:        for (root = NULL, nitems = 0; *argv; ++argv, ++nitems) {
                    159:                /* Don't allow zero-length paths. */
                    160:                if ((len = strlen(*argv)) == 0) {
                    161:                        errno = ENOENT;
                    162:                        goto mem3;
                    163:                }
                    164:
                    165:                if ((p = fts_alloc(sp, *argv, len)) == NULL)
                    166:                        goto mem3;
                    167:                p->fts_level = FTS_ROOTLEVEL;
                    168:                p->fts_parent = parent;
                    169:                p->fts_accpath = p->fts_name;
                    170:                p->fts_info = fts_stat(sp, p, ISSET(FTS_COMFOLLOW));
                    171:
                    172:                /* Command-line "." and ".." are real directories. */
                    173:                if (p->fts_info == FTS_DOT)
                    174:                        p->fts_info = FTS_D;
                    175:
                    176:                /*
                    177:                 * If comparison routine supplied, traverse in sorted
                    178:                 * order; otherwise traverse in the order specified.
                    179:                 */
                    180:                if (compar) {
                    181:                        p->fts_link = root;
                    182:                        root = p;
                    183:                } else {
                    184:                        p->fts_link = NULL;
                    185:                        if (root == NULL)
                    186:                                tmp = root = p;
                    187:                        else {
                    188:                                tmp->fts_link = p;
                    189:                                tmp = p;
                    190:                        }
                    191:                }
                    192:        }
                    193:        if (compar && nitems > 1)
                    194:                root = fts_sort(sp, root, nitems);
                    195:
                    196:        /*
                    197:         * Allocate a dummy pointer and make fts_read think that we've just
                    198:         * finished the node before the root(s); set p->fts_info to FTS_INIT
                    199:         * so that everything about the "current" node is ignored.
                    200:         */
                    201:        if ((sp->fts_cur = fts_alloc(sp, "", 0)) == NULL)
                    202:                goto mem3;
                    203:        sp->fts_cur->fts_link = root;
                    204:        sp->fts_cur->fts_info = FTS_INIT;
                    205:
                    206:        /*
1.46      msaitoh   207:         * If using chdir(2), grab a file descriptor pointing to dot to ensure
1.25      christos  208:         * that we can get back here; this could be avoided for some paths,
                    209:         * but almost certainly not worth the effort.  Slashes, symbolic links,
                    210:         * and ".." are all fairly nasty problems.  Note, if we can't get the
                    211:         * descriptor we run anyway, just more slowly.
                    212:         */
1.42      mrg       213: #ifndef O_CLOEXEC
                    214: #define O_CLOEXEC 0
                    215: #endif
1.25      christos  216:        if (!ISSET(FTS_NOCHDIR)) {
1.41      christos  217:                if ((sp->fts_rfd = open(".", O_RDONLY | O_CLOEXEC, 0)) == -1)
1.25      christos  218:                        SET(FTS_NOCHDIR);
                    219:        }
                    220:
1.30      christos  221:        if (nitems == 0)
1.33      lukem     222:                fts_free(parent);
1.30      christos  223:
1.25      christos  224:        return (sp);
                    225:
                    226: mem3:  fts_lfree(root);
1.33      lukem     227:        fts_free(parent);
1.25      christos  228: mem2:  free(sp->fts_path);
                    229: mem1:  free(sp);
                    230:        return (NULL);
                    231: }
                    232:
                    233: static void
1.28      christos  234: fts_load(FTS *sp, FTSENT *p)
1.25      christos  235: {
                    236:        size_t len;
                    237:        char *cp;
                    238:
                    239:        _DIAGASSERT(sp != NULL);
                    240:        _DIAGASSERT(p != NULL);
                    241:
                    242:        /*
                    243:         * Load the stream structure for the next traversal.  Since we don't
                    244:         * actually enter the directory until after the preorder visit, set
                    245:         * the fts_accpath field specially so the chdir gets done to the right
                    246:         * place and the user can access the first node.  From fts_open it's
                    247:         * known that the path will fit.
                    248:         */
                    249:        len = p->fts_pathlen = p->fts_namelen;
                    250:        memmove(sp->fts_path, p->fts_name, len + 1);
                    251:        if ((cp = strrchr(p->fts_name, '/')) && (cp != p->fts_name || cp[1])) {
                    252:                len = strlen(++cp);
                    253:                memmove(p->fts_name, cp, len + 1);
1.44      christos  254:                p->fts_namelen = ftsent_namelen_truncate(len);
1.25      christos  255:        }
                    256:        p->fts_accpath = p->fts_path = sp->fts_path;
                    257:        sp->fts_dev = p->fts_dev;
                    258: }
                    259:
                    260: int
1.28      christos  261: fts_close(FTS *sp)
1.25      christos  262: {
                    263:        FTSENT *freep, *p;
1.29      christos  264:        int saved_errno = 0;
1.25      christos  265:
                    266:        _DIAGASSERT(sp != NULL);
                    267:
                    268:        /*
                    269:         * This still works if we haven't read anything -- the dummy structure
                    270:         * points to the root list, so we step through to the end of the root
                    271:         * list which has a valid parent pointer.
                    272:         */
                    273:        if (sp->fts_cur) {
1.35      lukem     274:                if (sp->fts_cur->fts_flags & FTS_SYMFOLLOW)
1.25      christos  275:                        (void)close(sp->fts_cur->fts_symfd);
                    276:                for (p = sp->fts_cur; p->fts_level >= FTS_ROOTLEVEL;) {
                    277:                        freep = p;
                    278:                        p = p->fts_link ? p->fts_link : p->fts_parent;
1.33      lukem     279:                        fts_free(freep);
1.25      christos  280:                }
1.33      lukem     281:                fts_free(p);
1.25      christos  282:        }
                    283:
                    284:        /* Free up child linked list, sort array, path buffer. */
                    285:        if (sp->fts_child)
                    286:                fts_lfree(sp->fts_child);
                    287:        if (sp->fts_array)
                    288:                free(sp->fts_array);
                    289:        free(sp->fts_path);
                    290:
                    291:        /* Return to original directory, save errno if necessary. */
                    292:        if (!ISSET(FTS_NOCHDIR)) {
1.29      christos  293:                if (fchdir(sp->fts_rfd) == -1)
                    294:                        saved_errno = errno;
1.25      christos  295:                (void)close(sp->fts_rfd);
                    296:        }
                    297:
                    298:        /* Free up the stream pointer. */
                    299:        free(sp);
1.29      christos  300:        if (saved_errno) {
                    301:                errno = saved_errno;
                    302:                return -1;
                    303:        }
1.25      christos  304:
1.29      christos  305:        return 0;
1.25      christos  306: }
                    307:
1.29      christos  308: #if !defined(__FTS_COMPAT_TAILINGSLASH)
                    309:
1.25      christos  310: /*
1.26      christos  311:  * Special case of "/" at the end of the path so that slashes aren't
                    312:  * appended which would cause paths to be written as "....//foo".
1.25      christos  313:  */
                    314: #define        NAPPEND(p)                                                      \
1.26      christos  315:        (p->fts_path[p->fts_pathlen - 1] == '/'                         \
                    316:            ? p->fts_pathlen - 1 : p->fts_pathlen)
1.25      christos  317:
1.29      christos  318: #else /* !defined(__FTS_COMPAT_TAILINGSLASH) */
                    319:
                    320: /*
                    321:  * compatibility with the old behaviour.
                    322:  *
                    323:  * Special case a root of "/" so that slashes aren't appended which would
                    324:  * cause paths to be written as "//foo".
                    325:  */
                    326:
                    327: #define        NAPPEND(p)                                                      \
                    328:        (p->fts_level == FTS_ROOTLEVEL && p->fts_pathlen == 1 &&        \
                    329:            p->fts_path[0] == '/' ? 0 : p->fts_pathlen)
                    330:
                    331: #endif /* !defined(__FTS_COMPAT_TAILINGSLASH) */
                    332:
1.25      christos  333: FTSENT *
1.28      christos  334: fts_read(FTS *sp)
1.25      christos  335: {
                    336:        FTSENT *p, *tmp;
                    337:        int instr;
                    338:        char *t;
                    339:        int saved_errno;
                    340:
                    341:        _DIAGASSERT(sp != NULL);
                    342:
                    343:        /* If finished or unrecoverable error, return NULL. */
                    344:        if (sp->fts_cur == NULL || ISSET(FTS_STOP))
                    345:                return (NULL);
                    346:
                    347:        /* Set current node pointer. */
                    348:        p = sp->fts_cur;
                    349:
                    350:        /* Save and zero out user instructions. */
                    351:        instr = p->fts_instr;
                    352:        p->fts_instr = FTS_NOINSTR;
                    353:
                    354:        /* Any type of file may be re-visited; re-stat and re-turn. */
                    355:        if (instr == FTS_AGAIN) {
                    356:                p->fts_info = fts_stat(sp, p, 0);
                    357:                return (p);
                    358:        }
                    359:
                    360:        /*
                    361:         * Following a symlink -- SLNONE test allows application to see
                    362:         * SLNONE and recover.  If indirecting through a symlink, have
                    363:         * keep a pointer to current location.  If unable to get that
                    364:         * pointer, follow fails.
                    365:         */
                    366:        if (instr == FTS_FOLLOW &&
                    367:            (p->fts_info == FTS_SL || p->fts_info == FTS_SLNONE)) {
                    368:                p->fts_info = fts_stat(sp, p, 1);
                    369:                if (p->fts_info == FTS_D && !ISSET(FTS_NOCHDIR)) {
1.41      christos  370:                        if ((p->fts_symfd = open(".", O_RDONLY | O_CLOEXEC, 0))
                    371:                            == -1) {
1.25      christos  372:                                p->fts_errno = errno;
                    373:                                p->fts_info = FTS_ERR;
                    374:                        } else
                    375:                                p->fts_flags |= FTS_SYMFOLLOW;
                    376:                }
                    377:                return (p);
                    378:        }
                    379:
                    380:        /* Directory in pre-order. */
                    381:        if (p->fts_info == FTS_D) {
                    382:                /* If skipped or crossed mount point, do post-order visit. */
                    383:                if (instr == FTS_SKIP ||
                    384:                    (ISSET(FTS_XDEV) && p->fts_dev != sp->fts_dev)) {
                    385:                        if (p->fts_flags & FTS_SYMFOLLOW)
                    386:                                (void)close(p->fts_symfd);
                    387:                        if (sp->fts_child) {
                    388:                                fts_lfree(sp->fts_child);
                    389:                                sp->fts_child = NULL;
                    390:                        }
                    391:                        p->fts_info = FTS_DP;
                    392:                        return (p);
1.32      lukem     393:                }
1.25      christos  394:
                    395:                /* Rebuild if only read the names and now traversing. */
                    396:                if (sp->fts_child && ISSET(FTS_NAMEONLY)) {
                    397:                        CLR(FTS_NAMEONLY);
                    398:                        fts_lfree(sp->fts_child);
                    399:                        sp->fts_child = NULL;
                    400:                }
                    401:
                    402:                /*
                    403:                 * Cd to the subdirectory.
                    404:                 *
                    405:                 * If have already read and now fail to chdir, whack the list
                    406:                 * to make the names come out right, and set the parent errno
                    407:                 * so the application will eventually get an error condition.
                    408:                 * Set the FTS_DONTCHDIR flag so that when we logically change
                    409:                 * directories back to the parent we don't do a chdir.
                    410:                 *
                    411:                 * If haven't read do so.  If the read fails, fts_build sets
                    412:                 * FTS_STOP or the fts_info field of the node.
                    413:                 */
                    414:                if (sp->fts_child) {
                    415:                        if (fts_safe_changedir(sp, p, -1, p->fts_accpath)) {
                    416:                                p->fts_errno = errno;
                    417:                                p->fts_flags |= FTS_DONTCHDIR;
                    418:                                for (p = sp->fts_child; p; p = p->fts_link)
                    419:                                        p->fts_accpath =
                    420:                                            p->fts_parent->fts_accpath;
                    421:                        }
                    422:                } else if ((sp->fts_child = fts_build(sp, BREAD)) == NULL) {
                    423:                        if (ISSET(FTS_STOP))
                    424:                                return (NULL);
                    425:                        return (p);
                    426:                }
                    427:                p = sp->fts_child;
                    428:                sp->fts_child = NULL;
                    429:                goto name;
                    430:        }
                    431:
1.48      manu      432: next:
1.25      christos  433:        /* Move to the next node on this level. */
1.48      manu      434:        tmp = p;
                    435:
                    436:        /*
                    437:         * We are going to free sp->fts_cur, set it to NULL so
                    438:         * that fts_close() does not attempt to free it again
                    439:         * if we exit without setting it to a new value because
                    440:         * FCHDIR() failed below.
                    441:         */
                    442:        assert(tmp == sp->fts_cur);
                    443:        sp->fts_cur = NULL;
                    444:
1.25      christos  445:        if ((p = p->fts_link) != NULL) {
1.33      lukem     446:                fts_free(tmp);
1.25      christos  447:
                    448:                /*
                    449:                 * If reached the top, return to the original directory, and
                    450:                 * load the paths for the next root.
                    451:                 */
                    452:                if (p->fts_level == FTS_ROOTLEVEL) {
                    453:                        if (FCHDIR(sp, sp->fts_rfd)) {
                    454:                                SET(FTS_STOP);
                    455:                                return (NULL);
                    456:                        }
                    457:                        fts_load(sp, p);
                    458:                        return (sp->fts_cur = p);
                    459:                }
                    460:
                    461:                /*
                    462:                 * User may have called fts_set on the node.  If skipped,
                    463:                 * ignore.  If followed, get a file descriptor so we can
                    464:                 * get back if necessary.
                    465:                 */
                    466:                if (p->fts_instr == FTS_SKIP)
                    467:                        goto next;
                    468:                if (p->fts_instr == FTS_FOLLOW) {
                    469:                        p->fts_info = fts_stat(sp, p, 1);
                    470:                        if (p->fts_info == FTS_D && !ISSET(FTS_NOCHDIR)) {
                    471:                                if ((p->fts_symfd =
1.41      christos  472:                                    open(".", O_RDONLY | O_CLOEXEC, 0)) == -1) {
1.25      christos  473:                                        p->fts_errno = errno;
                    474:                                        p->fts_info = FTS_ERR;
                    475:                                } else
                    476:                                        p->fts_flags |= FTS_SYMFOLLOW;
                    477:                        }
                    478:                        p->fts_instr = FTS_NOINSTR;
                    479:                }
                    480:
                    481: name:          t = sp->fts_path + NAPPEND(p->fts_parent);
                    482:                *t++ = '/';
                    483:                memmove(t, p->fts_name, (size_t)(p->fts_namelen + 1));
                    484:                return (sp->fts_cur = p);
                    485:        }
                    486:
                    487:        /* Move up to the parent node. */
                    488:        p = tmp->fts_parent;
1.33      lukem     489:        fts_free(tmp);
1.25      christos  490:
                    491:        if (p->fts_level == FTS_ROOTPARENTLEVEL) {
                    492:                /*
                    493:                 * Done; free everything up and set errno to 0 so the user
                    494:                 * can distinguish between error and EOF.
                    495:                 */
1.33      lukem     496:                fts_free(p);
1.25      christos  497:                errno = 0;
                    498:                return (sp->fts_cur = NULL);
                    499:        }
                    500:
1.46      msaitoh   501:        /* NUL terminate the pathname. */
1.25      christos  502:        sp->fts_path[p->fts_pathlen] = '\0';
                    503:
                    504:        /*
                    505:         * Return to the parent directory.  If at a root node or came through
                    506:         * a symlink, go back through the file descriptor.  Otherwise, cd up
                    507:         * one directory.
                    508:         */
                    509:        if (p->fts_level == FTS_ROOTLEVEL) {
                    510:                if (FCHDIR(sp, sp->fts_rfd)) {
                    511:                        SET(FTS_STOP);
                    512:                        return (NULL);
                    513:                }
                    514:        } else if (p->fts_flags & FTS_SYMFOLLOW) {
                    515:                if (FCHDIR(sp, p->fts_symfd)) {
                    516:                        saved_errno = errno;
                    517:                        (void)close(p->fts_symfd);
                    518:                        errno = saved_errno;
                    519:                        SET(FTS_STOP);
                    520:                        return (NULL);
                    521:                }
                    522:                (void)close(p->fts_symfd);
                    523:        } else if (!(p->fts_flags & FTS_DONTCHDIR) &&
                    524:            fts_safe_changedir(sp, p->fts_parent, -1, "..")) {
                    525:                SET(FTS_STOP);
                    526:                return (NULL);
                    527:        }
                    528:        p->fts_info = p->fts_errno ? FTS_ERR : FTS_DP;
                    529:        return (sp->fts_cur = p);
                    530: }
                    531:
                    532: /*
                    533:  * Fts_set takes the stream as an argument although it's not used in this
                    534:  * implementation; it would be necessary if anyone wanted to add global
                    535:  * semantics to fts using fts_set.  An error return is allowed for similar
                    536:  * reasons.
                    537:  */
                    538: /* ARGSUSED */
                    539: int
1.28      christos  540: fts_set(FTS *sp, FTSENT *p, int instr)
1.25      christos  541: {
                    542:
                    543:        _DIAGASSERT(sp != NULL);
                    544:        _DIAGASSERT(p != NULL);
                    545:
                    546:        if (instr && instr != FTS_AGAIN && instr != FTS_FOLLOW &&
                    547:            instr != FTS_NOINSTR && instr != FTS_SKIP) {
                    548:                errno = EINVAL;
                    549:                return (1);
                    550:        }
                    551:        p->fts_instr = instr;
                    552:        return (0);
                    553: }
                    554:
                    555: FTSENT *
1.28      christos  556: fts_children(FTS *sp, int instr)
1.25      christos  557: {
                    558:        FTSENT *p;
                    559:        int fd;
                    560:
                    561:        _DIAGASSERT(sp != NULL);
                    562:
                    563:        if (instr && instr != FTS_NAMEONLY) {
                    564:                errno = EINVAL;
                    565:                return (NULL);
                    566:        }
                    567:
                    568:        /* Set current node pointer. */
                    569:        p = sp->fts_cur;
                    570:
                    571:        /*
                    572:         * Errno set to 0 so user can distinguish empty directory from
                    573:         * an error.
                    574:         */
                    575:        errno = 0;
                    576:
                    577:        /* Fatal errors stop here. */
                    578:        if (ISSET(FTS_STOP))
                    579:                return (NULL);
                    580:
                    581:        /* Return logical hierarchy of user's arguments. */
                    582:        if (p->fts_info == FTS_INIT)
                    583:                return (p->fts_link);
                    584:
                    585:        /*
                    586:         * If not a directory being visited in pre-order, stop here.  Could
                    587:         * allow FTS_DNR, assuming the user has fixed the problem, but the
                    588:         * same effect is available with FTS_AGAIN.
                    589:         */
                    590:        if (p->fts_info != FTS_D /* && p->fts_info != FTS_DNR */)
                    591:                return (NULL);
                    592:
                    593:        /* Free up any previous child list. */
                    594:        if (sp->fts_child)
                    595:                fts_lfree(sp->fts_child);
                    596:
                    597:        if (instr == FTS_NAMEONLY) {
                    598:                SET(FTS_NAMEONLY);
                    599:                instr = BNAMES;
1.32      lukem     600:        } else
1.25      christos  601:                instr = BCHILD;
                    602:
                    603:        /*
                    604:         * If using chdir on a relative path and called BEFORE fts_read does
                    605:         * its chdir to the root of a traversal, we can lose -- we need to
                    606:         * chdir into the subdirectory, and we don't know where the current
                    607:         * directory is, so we can't get back so that the upcoming chdir by
                    608:         * fts_read will work.
                    609:         */
                    610:        if (p->fts_level != FTS_ROOTLEVEL || p->fts_accpath[0] == '/' ||
                    611:            ISSET(FTS_NOCHDIR))
                    612:                return (sp->fts_child = fts_build(sp, instr));
                    613:
1.47      christos  614:        if ((fd = open(".", O_RDONLY | O_CLOEXEC, 0)) == -1)
1.25      christos  615:                return (sp->fts_child = NULL);
                    616:        sp->fts_child = fts_build(sp, instr);
                    617:        if (fchdir(fd)) {
                    618:                (void)close(fd);
                    619:                return (NULL);
                    620:        }
                    621:        (void)close(fd);
                    622:        return (sp->fts_child);
                    623: }
                    624:
                    625: /*
                    626:  * This is the tricky part -- do not casually change *anything* in here.  The
                    627:  * idea is to build the linked list of entries that are used by fts_children
                    628:  * and fts_read.  There are lots of special cases.
                    629:  *
                    630:  * The real slowdown in walking the tree is the stat calls.  If FTS_NOSTAT is
                    631:  * set and it's a physical walk (so that symbolic links can't be directories),
                    632:  * we can do things quickly.  First, if it's a 4.4BSD file system, the type
                    633:  * of the file is in the directory entry.  Otherwise, we assume that the number
                    634:  * of subdirectories in a node is equal to the number of links to the parent.
                    635:  * The former skips all stat calls.  The latter skips stat calls in any leaf
                    636:  * directories and for any files after the subdirectories in the directory have
                    637:  * been found, cutting the stat calls by about 2/3.
                    638:  */
                    639: static FTSENT *
1.28      christos  640: fts_build(FTS *sp, int type)
1.25      christos  641: {
                    642:        struct dirent *dp;
                    643:        FTSENT *p, *head;
                    644:        size_t nitems;
                    645:        FTSENT *cur, *tail;
                    646:        DIR *dirp;
1.27      christos  647:        void *oldaddr;
                    648:        size_t dnamlen;
1.37      lukem     649:        int cderrno, descend, level, nlinks, saved_errno, nostat, doadjust;
                    650:        size_t len, maxlen;
1.25      christos  651: #ifdef FTS_WHITEOUT
                    652:        int oflag;
                    653: #endif
                    654:        char *cp = NULL;        /* pacify gcc */
                    655:
                    656:        _DIAGASSERT(sp != NULL);
                    657:
                    658:        /* Set current node pointer. */
                    659:        cur = sp->fts_cur;
                    660:
                    661:        /*
                    662:         * Open the directory for reading.  If this fails, we're done.
                    663:         * If being called from fts_read, set the fts_info field.
                    664:         */
                    665: #ifdef FTS_WHITEOUT
                    666:        if (ISSET(FTS_WHITEOUT))
                    667:                oflag = DTF_NODUP|DTF_REWIND;
                    668:        else
                    669:                oflag = DTF_HIDEW|DTF_NODUP|DTF_REWIND;
                    670: #else
1.32      lukem     671: #define        __opendir2(path, flag) opendir(path)
1.25      christos  672: #endif
                    673:        if ((dirp = __opendir2(cur->fts_accpath, oflag)) == NULL) {
                    674:                if (type == BREAD) {
                    675:                        cur->fts_info = FTS_DNR;
                    676:                        cur->fts_errno = errno;
                    677:                }
                    678:                return (NULL);
                    679:        }
                    680:
                    681:        /*
                    682:         * Nlinks is the number of possible entries of type directory in the
                    683:         * directory if we're cheating on stat calls, 0 if we're not doing
                    684:         * any stat calls at all, -1 if we're doing stats on everything.
                    685:         */
                    686:        if (type == BNAMES) {
                    687:                nlinks = 0;
                    688:                nostat = 1;
                    689:        } else if (ISSET(FTS_NOSTAT) && ISSET(FTS_PHYSICAL)) {
                    690:                nlinks = cur->fts_nlink - (ISSET(FTS_SEEDOT) ? 0 : 2);
                    691:                nostat = 1;
                    692:        } else {
                    693:                nlinks = -1;
                    694:                nostat = 0;
                    695:        }
                    696:
                    697: #ifdef notdef
                    698:        (void)printf("nlinks == %d (cur: %d)\n", nlinks, cur->fts_nlink);
                    699:        (void)printf("NOSTAT %d PHYSICAL %d SEEDOT %d\n",
                    700:            ISSET(FTS_NOSTAT), ISSET(FTS_PHYSICAL), ISSET(FTS_SEEDOT));
                    701: #endif
                    702:        /*
                    703:         * If we're going to need to stat anything or we want to descend
                    704:         * and stay in the directory, chdir.  If this fails we keep going,
                    705:         * but set a flag so we don't chdir after the post-order visit.
                    706:         * We won't be able to stat anything, but we can still return the
                    707:         * names themselves.  Note, that since fts_read won't be able to
                    708:         * chdir into the directory, it will have to return different path
                    709:         * names than before, i.e. "a/b" instead of "b".  Since the node
                    710:         * has already been visited in pre-order, have to wait until the
                    711:         * post-order visit to return the error.  There is a special case
                    712:         * here, if there was nothing to stat then it's not an error to
                    713:         * not be able to stat.  This is all fairly nasty.  If a program
                    714:         * needed sorted entries or stat information, they had better be
                    715:         * checking FTS_NS on the returned nodes.
                    716:         */
                    717:        cderrno = 0;
                    718:        if (nlinks || type == BREAD) {
                    719:                if (fts_safe_changedir(sp, cur, dirfd(dirp), NULL)) {
                    720:                        if (nlinks && type == BREAD)
                    721:                                cur->fts_errno = errno;
                    722:                        cur->fts_flags |= FTS_DONTCHDIR;
                    723:                        descend = 0;
                    724:                        cderrno = errno;
                    725:                } else
                    726:                        descend = 1;
                    727:        } else
                    728:                descend = 0;
                    729:
                    730:        /*
                    731:         * Figure out the max file name length that can be stored in the
                    732:         * current path -- the inner loop allocates more path as necessary.
                    733:         * We really wouldn't have to do the maxlen calculations here, we
                    734:         * could do them in fts_read before returning the path, but it's a
                    735:         * lot easier here since the length is part of the dirent structure.
                    736:         *
                    737:         * If not changing directories set a pointer so that can just append
                    738:         * each new name into the path.
                    739:         */
                    740:        len = NAPPEND(cur);
                    741:        if (ISSET(FTS_NOCHDIR)) {
                    742:                cp = sp->fts_path + len;
                    743:                *cp++ = '/';
                    744:        }
                    745:        len++;
                    746:        maxlen = sp->fts_pathlen - len;
                    747:
1.39      christos  748: #if defined(__FTS_COMPAT_LEVEL)
1.38      pgoyette  749:        if (cur->fts_level == SHRT_MAX) {
                    750:                (void)closedir(dirp);
                    751:                cur->fts_info = FTS_ERR;
                    752:                SET(FTS_STOP);
                    753:                errno = ENAMETOOLONG;
                    754:                return (NULL);
                    755:        }
1.39      christos  756: #endif
1.38      pgoyette  757:
1.25      christos  758:        level = cur->fts_level + 1;
                    759:
                    760:        /* Read the directory, attaching each entry to the `link' pointer. */
1.27      christos  761:        doadjust = 0;
1.25      christos  762:        for (head = tail = NULL, nitems = 0; (dp = readdir(dirp)) != NULL;) {
                    763:
                    764:                if (!ISSET(FTS_SEEDOT) && ISDOT(dp->d_name))
                    765:                        continue;
                    766:
1.32      lukem     767: #if defined(HAVE_STRUCT_DIRENT_D_NAMLEN)
1.27      christos  768:                dnamlen = dp->d_namlen;
1.25      christos  769: #else
1.27      christos  770:                dnamlen = strlen(dp->d_name);
1.25      christos  771: #endif
1.27      christos  772:                if ((p = fts_alloc(sp, dp->d_name, dnamlen)) == NULL)
1.25      christos  773:                        goto mem1;
1.27      christos  774:                if (dnamlen >= maxlen) {        /* include space for NUL */
                    775:                        oldaddr = sp->fts_path;
                    776:                        if (fts_palloc(sp, dnamlen + len + 1)) {
1.25      christos  777:                                /*
                    778:                                 * No more memory for path or structures.  Save
                    779:                                 * errno, free up the current structure and the
                    780:                                 * structures already allocated.
                    781:                                 */
                    782: mem1:                          saved_errno = errno;
                    783:                                if (p)
1.33      lukem     784:                                        fts_free(p);
1.25      christos  785:                                fts_lfree(head);
                    786:                                (void)closedir(dirp);
                    787:                                errno = saved_errno;
                    788:                                cur->fts_info = FTS_ERR;
                    789:                                SET(FTS_STOP);
                    790:                                return (NULL);
                    791:                        }
1.27      christos  792:                        /* Did realloc() change the pointer? */
                    793:                        if (oldaddr != sp->fts_path) {
                    794:                                doadjust = 1;
                    795:                                if (ISSET(FTS_NOCHDIR))
                    796:                                        cp = sp->fts_path + len;
                    797:                        }
1.25      christos  798:                        maxlen = sp->fts_pathlen - len;
                    799:                }
                    800:
1.31      christos  801: #if defined(__FTS_COMPAT_LENGTH)
1.27      christos  802:                if (len + dnamlen >= USHRT_MAX) {
                    803:                        /*
1.32      lukem     804:                         * In an FTSENT, fts_pathlen is an unsigned short
                    805:                         * so it is possible to wraparound here.
                    806:                         * If we do, free up the current structure and the
                    807:                         * structures already allocated, then error out
                    808:                         * with ENAMETOOLONG.
1.27      christos  809:                         */
1.33      lukem     810:                        fts_free(p);
1.27      christos  811:                        fts_lfree(head);
                    812:                        (void)closedir(dirp);
                    813:                        cur->fts_info = FTS_ERR;
                    814:                        SET(FTS_STOP);
                    815:                        errno = ENAMETOOLONG;
                    816:                        return (NULL);
                    817:                }
1.31      christos  818: #endif
1.27      christos  819:                p->fts_level = level;
1.43      christos  820:                p->fts_pathlen = ftsent_pathlen_truncate(len + dnamlen);
1.25      christos  821:                p->fts_parent = sp->fts_cur;
                    822:
                    823: #ifdef FTS_WHITEOUT
                    824:                if (dp->d_type == DT_WHT)
                    825:                        p->fts_flags |= FTS_ISW;
                    826: #endif
                    827:
                    828:                if (cderrno) {
                    829:                        if (nlinks) {
                    830:                                p->fts_info = FTS_NS;
                    831:                                p->fts_errno = cderrno;
                    832:                        } else
                    833:                                p->fts_info = FTS_NSOK;
                    834:                        p->fts_accpath = cur->fts_accpath;
                    835:                } else if (nlinks == 0
                    836: #ifdef DT_DIR
1.32      lukem     837:                    || (nostat &&
1.25      christos  838:                    dp->d_type != DT_DIR && dp->d_type != DT_UNKNOWN)
                    839: #endif
                    840:                    ) {
                    841:                        p->fts_accpath =
                    842:                            ISSET(FTS_NOCHDIR) ? p->fts_path : p->fts_name;
                    843:                        p->fts_info = FTS_NSOK;
                    844:                } else {
                    845:                        /* Build a file name for fts_stat to stat. */
                    846:                        if (ISSET(FTS_NOCHDIR)) {
                    847:                                p->fts_accpath = p->fts_path;
                    848:                                memmove(cp, p->fts_name,
                    849:                                        (size_t)(p->fts_namelen + 1));
                    850:                        } else
                    851:                                p->fts_accpath = p->fts_name;
                    852:                        /* Stat it. */
                    853:                        p->fts_info = fts_stat(sp, p, 0);
                    854:
                    855:                        /* Decrement link count if applicable. */
                    856:                        if (nlinks > 0 && (p->fts_info == FTS_D ||
                    857:                            p->fts_info == FTS_DC || p->fts_info == FTS_DOT))
                    858:                                --nlinks;
                    859:                }
                    860:
                    861:                /* We walk in directory order so "ls -f" doesn't get upset. */
                    862:                p->fts_link = NULL;
                    863:                if (head == NULL)
                    864:                        head = tail = p;
                    865:                else {
                    866:                        tail->fts_link = p;
                    867:                        tail = p;
                    868:                }
                    869:                ++nitems;
                    870:        }
                    871:        (void)closedir(dirp);
                    872:
                    873:        /*
                    874:         * If had to realloc the path, adjust the addresses for the rest
                    875:         * of the tree.
                    876:         */
1.27      christos  877:        if (doadjust)
1.25      christos  878:                fts_padjust(sp, head);
                    879:
                    880:        /*
                    881:         * If not changing directories, reset the path back to original
                    882:         * state.
                    883:         */
                    884:        if (ISSET(FTS_NOCHDIR)) {
1.27      christos  885:                if (len == sp->fts_pathlen || nitems == 0)
1.25      christos  886:                        --cp;
                    887:                *cp = '\0';
                    888:        }
                    889:
                    890:        /*
                    891:         * If descended after called from fts_children or after called from
                    892:         * fts_read and nothing found, get back.  At the root level we use
                    893:         * the saved fd; if one of fts_open()'s arguments is a relative path
                    894:         * to an empty directory, we wind up here with no other way back.  If
                    895:         * can't get back, we're done.
                    896:         */
                    897:        if (descend && (type == BCHILD || !nitems) &&
                    898:            (cur->fts_level == FTS_ROOTLEVEL ?
                    899:            FCHDIR(sp, sp->fts_rfd) :
                    900:            fts_safe_changedir(sp, cur->fts_parent, -1, ".."))) {
                    901:                cur->fts_info = FTS_ERR;
                    902:                SET(FTS_STOP);
                    903:                return (NULL);
                    904:        }
                    905:
                    906:        /* If didn't find anything, return NULL. */
                    907:        if (!nitems) {
                    908:                if (type == BREAD)
                    909:                        cur->fts_info = FTS_DP;
                    910:                return (NULL);
                    911:        }
                    912:
                    913:        /* Sort the entries. */
                    914:        if (sp->fts_compar && nitems > 1)
                    915:                head = fts_sort(sp, head, nitems);
                    916:        return (head);
                    917: }
                    918:
1.32      lukem     919: static unsigned short
1.28      christos  920: fts_stat(FTS *sp, FTSENT *p, int follow)
1.25      christos  921: {
                    922:        FTSENT *t;
                    923:        dev_t dev;
                    924:        __fts_ino_t ino;
                    925:        __fts_stat_t *sbp, sb;
                    926:        int saved_errno;
                    927:
                    928:        _DIAGASSERT(sp != NULL);
                    929:        _DIAGASSERT(p != NULL);
                    930:
                    931:        /* If user needs stat info, stat buffer already allocated. */
                    932:        sbp = ISSET(FTS_NOSTAT) ? &sb : p->fts_statp;
                    933:
                    934: #ifdef FTS_WHITEOUT
                    935:        /* check for whiteout */
                    936:        if (p->fts_flags & FTS_ISW) {
                    937:                if (sbp != &sb) {
                    938:                        memset(sbp, '\0', sizeof (*sbp));
                    939:                        sbp->st_mode = S_IFWHT;
                    940:                }
                    941:                return (FTS_W);
                    942:        }
                    943: #endif
                    944:
                    945:        /*
                    946:         * If doing a logical walk, or application requested FTS_FOLLOW, do
                    947:         * a stat(2).  If that fails, check for a non-existent symlink.  If
                    948:         * fail, set the errno from the stat call.
                    949:         */
                    950:        if (ISSET(FTS_LOGICAL) || follow) {
                    951:                if (stat(p->fts_accpath, sbp)) {
                    952:                        saved_errno = errno;
                    953:                        if (!lstat(p->fts_accpath, sbp)) {
                    954:                                errno = 0;
                    955:                                return (FTS_SLNONE);
1.32      lukem     956:                        }
1.25      christos  957:                        p->fts_errno = saved_errno;
                    958:                        goto err;
                    959:                }
                    960:        } else if (lstat(p->fts_accpath, sbp)) {
                    961:                p->fts_errno = errno;
                    962: err:           memset(sbp, 0, sizeof(*sbp));
                    963:                return (FTS_NS);
                    964:        }
                    965:
                    966:        if (S_ISDIR(sbp->st_mode)) {
                    967:                /*
                    968:                 * Set the device/inode.  Used to find cycles and check for
                    969:                 * crossing mount points.  Also remember the link count, used
                    970:                 * in fts_build to limit the number of stat calls.  It is
                    971:                 * understood that these fields are only referenced if fts_info
                    972:                 * is set to FTS_D.
                    973:                 */
                    974:                dev = p->fts_dev = sbp->st_dev;
                    975:                ino = p->fts_ino = sbp->st_ino;
                    976:                p->fts_nlink = sbp->st_nlink;
                    977:
                    978:                if (ISDOT(p->fts_name))
                    979:                        return (FTS_DOT);
                    980:
                    981:                /*
                    982:                 * Cycle detection is done by brute force when the directory
                    983:                 * is first encountered.  If the tree gets deep enough or the
                    984:                 * number of symbolic links to directories is high enough,
                    985:                 * something faster might be worthwhile.
                    986:                 */
                    987:                for (t = p->fts_parent;
                    988:                    t->fts_level >= FTS_ROOTLEVEL; t = t->fts_parent)
                    989:                        if (ino == t->fts_ino && dev == t->fts_dev) {
                    990:                                p->fts_cycle = t;
                    991:                                return (FTS_DC);
                    992:                        }
                    993:                return (FTS_D);
                    994:        }
                    995:        if (S_ISLNK(sbp->st_mode))
                    996:                return (FTS_SL);
                    997:        if (S_ISREG(sbp->st_mode))
                    998:                return (FTS_F);
                    999:        return (FTS_DEFAULT);
                   1000: }
                   1001:
                   1002: static FTSENT *
1.28      christos 1003: fts_sort(FTS *sp, FTSENT *head, size_t nitems)
1.25      christos 1004: {
                   1005:        FTSENT **ap, *p;
                   1006:
                   1007:        _DIAGASSERT(sp != NULL);
                   1008:        _DIAGASSERT(head != NULL);
                   1009:
                   1010:        /*
                   1011:         * Construct an array of pointers to the structures and call qsort(3).
                   1012:         * Reassemble the array in the order returned by qsort.  If unable to
                   1013:         * sort for memory reasons, return the directory entries in their
                   1014:         * current order.  Allocate enough space for the current needs plus
                   1015:         * 40 so don't realloc one entry at a time.
                   1016:         */
                   1017:        if (nitems > sp->fts_nitems) {
1.51    ! christos 1018:                errno = reallocarr(&sp->fts_array,
        !          1019:                    nitems + 40, sizeof(*sp->fts_array));
        !          1020:                if (errno)
        !          1021:                        return head;
1.44      christos 1022:                sp->fts_nitems = fts_nitems_truncate(nitems + 40);
1.25      christos 1023:        }
                   1024:        for (ap = sp->fts_array, p = head; p; p = p->fts_link)
                   1025:                *ap++ = p;
1.32      lukem    1026:        qsort((void *)sp->fts_array, nitems, sizeof(FTSENT *),
1.28      christos 1027:                (int (*)(const void *, const void *))sp->fts_compar);
1.25      christos 1028:        for (head = *(ap = sp->fts_array); --nitems; ++ap)
                   1029:                ap[0]->fts_link = ap[1];
                   1030:        ap[0]->fts_link = NULL;
                   1031:        return (head);
                   1032: }
                   1033:
                   1034: static FTSENT *
1.28      christos 1035: fts_alloc(FTS *sp, const char *name, size_t namelen)
1.25      christos 1036: {
                   1037:        FTSENT *p;
1.34      lukem    1038: #if defined(FTS_ALLOC_ALIGNED)
1.25      christos 1039:        size_t len;
1.34      lukem    1040: #endif
1.25      christos 1041:
                   1042:        _DIAGASSERT(sp != NULL);
                   1043:        _DIAGASSERT(name != NULL);
                   1044:
1.33      lukem    1045: #if defined(FTS_ALLOC_ALIGNED)
1.25      christos 1046:        /*
                   1047:         * The file name is a variable length array and no stat structure is
                   1048:         * necessary if the user has set the nostat bit.  Allocate the FTSENT
                   1049:         * structure, the file name and the stat structure in one chunk, but
                   1050:         * be careful that the stat structure is reasonably aligned.  Since the
                   1051:         * fts_name field is declared to be of size 1, the fts_name pointer is
                   1052:         * namelen + 2 before the first possible address of the stat structure.
                   1053:         */
                   1054:        len = sizeof(FTSENT) + namelen;
                   1055:        if (!ISSET(FTS_NOSTAT))
                   1056:                len += sizeof(*(p->fts_statp)) + ALIGNBYTES;
                   1057:        if ((p = malloc(len)) == NULL)
                   1058:                return (NULL);
                   1059:
                   1060:        if (!ISSET(FTS_NOSTAT))
1.32      lukem    1061:                p->fts_statp = (__fts_stat_t *)ALIGN(
                   1062:                    (unsigned long)(p->fts_name + namelen + 2));
1.25      christos 1063: #else
                   1064:        if ((p = malloc(sizeof(FTSENT) + namelen)) == NULL)
                   1065:                return (NULL);
                   1066:
                   1067:        if (!ISSET(FTS_NOSTAT))
                   1068:                if ((p->fts_statp = malloc(sizeof(*(p->fts_statp)))) == NULL) {
                   1069:                        free(p);
                   1070:                        return (NULL);
                   1071:                }
                   1072: #endif
                   1073:
1.40      stacktic 1074:         if (ISSET(FTS_NOSTAT))
                   1075:                 p->fts_statp = NULL;
                   1076:
1.25      christos 1077:        /* Copy the name plus the trailing NULL. */
                   1078:        memmove(p->fts_name, name, namelen + 1);
                   1079:
1.44      christos 1080:        p->fts_namelen = ftsent_namelen_truncate(namelen);
1.25      christos 1081:        p->fts_path = sp->fts_path;
                   1082:        p->fts_errno = 0;
                   1083:        p->fts_flags = 0;
                   1084:        p->fts_instr = FTS_NOINSTR;
                   1085:        p->fts_number = 0;
                   1086:        p->fts_pointer = NULL;
                   1087:        return (p);
                   1088: }
                   1089:
                   1090: static void
1.33      lukem    1091: fts_free(FTSENT *p)
                   1092: {
                   1093: #if !defined(FTS_ALLOC_ALIGNED)
                   1094:        if (p->fts_statp)
                   1095:                free(p->fts_statp);
                   1096: #endif
                   1097:        free(p);
                   1098: }
                   1099:
                   1100: static void
1.28      christos 1101: fts_lfree(FTSENT *head)
1.25      christos 1102: {
                   1103:        FTSENT *p;
                   1104:
                   1105:        /* XXX: head may be NULL ? */
                   1106:
                   1107:        /* Free a linked list of structures. */
                   1108:        while ((p = head) != NULL) {
                   1109:                head = head->fts_link;
1.33      lukem    1110:                fts_free(p);
1.25      christos 1111:        }
                   1112: }
                   1113:
                   1114: static size_t
1.28      christos 1115: fts_pow2(size_t x)
1.25      christos 1116: {
                   1117:
                   1118:        x--;
                   1119:        x |= x>>1;
                   1120:        x |= x>>2;
                   1121:        x |= x>>4;
                   1122:        x |= x>>8;
                   1123:        x |= x>>16;
                   1124: #if LONG_BIT > 32
                   1125:        x |= x>>32;
                   1126: #endif
                   1127: #if LONG_BIT > 64
                   1128:        x |= x>>64;
                   1129: #endif
                   1130:        x++;
                   1131:        return (x);
                   1132: }
                   1133:
                   1134: /*
                   1135:  * Allow essentially unlimited paths; find, rm, ls should all work on any tree.
                   1136:  * Most systems will allow creation of paths much longer than MAXPATHLEN, even
                   1137:  * though the kernel won't resolve them.  Round up the new size to a power of 2,
1.32      lukem    1138:  * so we don't realloc the path 2 bytes at a time.
1.25      christos 1139:  */
                   1140: static int
1.28      christos 1141: fts_palloc(FTS *sp, size_t size)
1.25      christos 1142: {
                   1143:        char *new;
                   1144:
                   1145:        _DIAGASSERT(sp != NULL);
                   1146:
1.31      christos 1147: #ifdef __FTS_COMPAT_LENGTH
1.25      christos 1148:        /* Protect against fts_pathlen overflow. */
                   1149:        if (size > USHRT_MAX + 1) {
1.27      christos 1150:                errno = ENAMETOOLONG;
1.25      christos 1151:                return (1);
                   1152:        }
                   1153: #endif
                   1154:        size = fts_pow2(size);
                   1155:        new = realloc(sp->fts_path, size);
                   1156:        if (new == 0)
                   1157:                return (1);
                   1158:        sp->fts_path = new;
1.43      christos 1159:        sp->fts_pathlen = fts_pathlen_truncate(size);
1.25      christos 1160:        return (0);
                   1161: }
                   1162:
                   1163: /*
                   1164:  * When the path is realloc'd, have to fix all of the pointers in structures
                   1165:  * already returned.
                   1166:  */
                   1167: static void
1.28      christos 1168: fts_padjust(FTS *sp, FTSENT *head)
1.25      christos 1169: {
                   1170:        FTSENT *p;
                   1171:        char *addr;
                   1172:
                   1173:        _DIAGASSERT(sp != NULL);
                   1174:
                   1175: #define        ADJUST(p) do {                                                  \
                   1176:        if ((p)->fts_accpath != (p)->fts_name)                          \
                   1177:                (p)->fts_accpath =                                      \
                   1178:                    addr + ((p)->fts_accpath - (p)->fts_path);          \
                   1179:        (p)->fts_path = addr;                                           \
                   1180: } while (/*CONSTCOND*/0)
                   1181:
                   1182:        addr = sp->fts_path;
                   1183:
                   1184:        /* Adjust the current set of children. */
                   1185:        for (p = sp->fts_child; p; p = p->fts_link)
                   1186:                ADJUST(p);
                   1187:
                   1188:        /* Adjust the rest of the tree, including the current level. */
                   1189:        for (p = head; p->fts_level >= FTS_ROOTLEVEL;) {
                   1190:                ADJUST(p);
                   1191:                p = p->fts_link ? p->fts_link : p->fts_parent;
                   1192:        }
                   1193: }
                   1194:
                   1195: static size_t
1.28      christos 1196: fts_maxarglen(char * const *argv)
1.25      christos 1197: {
                   1198:        size_t len, max;
                   1199:
                   1200:        _DIAGASSERT(argv != NULL);
                   1201:
                   1202:        for (max = 0; *argv; ++argv)
                   1203:                if ((len = strlen(*argv)) > max)
                   1204:                        max = len;
                   1205:        return (max + 1);
                   1206: }
                   1207:
                   1208: /*
                   1209:  * Change to dir specified by fd or p->fts_accpath without getting
                   1210:  * tricked by someone changing the world out from underneath us.
                   1211:  * Assumes p->fts_dev and p->fts_ino are filled in.
                   1212:  */
                   1213: static int
1.28      christos 1214: fts_safe_changedir(const FTS *sp, const FTSENT *p, int fd, const char *path)
1.25      christos 1215: {
                   1216:        int oldfd = fd, ret = -1;
                   1217:        __fts_stat_t sb;
                   1218:
                   1219:        if (ISSET(FTS_NOCHDIR))
                   1220:                return 0;
                   1221:
1.47      christos 1222:        if (oldfd < 0 && (fd = open(path, O_RDONLY | O_CLOEXEC)) == -1)
1.25      christos 1223:                return -1;
                   1224:
                   1225:        if (fstat(fd, &sb) == -1)
                   1226:                goto bail;
1.24      christos 1227:
1.25      christos 1228:        if (sb.st_ino != p->fts_ino || sb.st_dev != p->fts_dev) {
                   1229:                errno = ENOENT;
                   1230:                goto bail;
                   1231:        }
1.21      fvdl     1232:
1.25      christos 1233:        ret = fchdir(fd);
1.24      christos 1234:
1.25      christos 1235: bail:
                   1236:        if (oldfd < 0) {
                   1237:                int save_errno = errno;
                   1238:                (void)close(fd);
                   1239:                errno = save_errno;
                   1240:        }
                   1241:        return ret;
                   1242: }

CVSweb <webmaster@jp.NetBSD.org>