[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.10

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

CVSweb <webmaster@jp.NetBSD.org>