fts-compat.c revision 17141
11573Srgrimes/*- 21573Srgrimes * Copyright (c) 1990, 1993, 1994 31573Srgrimes * The Regents of the University of California. All rights reserved. 41573Srgrimes * 51573Srgrimes * Redistribution and use in source and binary forms, with or without 61573Srgrimes * modification, are permitted provided that the following conditions 71573Srgrimes * are met: 81573Srgrimes * 1. Redistributions of source code must retain the above copyright 91573Srgrimes * notice, this list of conditions and the following disclaimer. 101573Srgrimes * 2. Redistributions in binary form must reproduce the above copyright 111573Srgrimes * notice, this list of conditions and the following disclaimer in the 121573Srgrimes * documentation and/or other materials provided with the distribution. 131573Srgrimes * 3. All advertising materials mentioning features or use of this software 141573Srgrimes * must display the following acknowledgement: 151573Srgrimes * This product includes software developed by the University of 161573Srgrimes * California, Berkeley and its contributors. 171573Srgrimes * 4. Neither the name of the University nor the names of its contributors 181573Srgrimes * may be used to endorse or promote products derived from this software 191573Srgrimes * without specific prior written permission. 201573Srgrimes * 211573Srgrimes * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND 221573Srgrimes * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 231573Srgrimes * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 241573Srgrimes * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE 251573Srgrimes * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 261573Srgrimes * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 271573Srgrimes * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 281573Srgrimes * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 291573Srgrimes * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 301573Srgrimes * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 311573Srgrimes * SUCH DAMAGE. 321573Srgrimes */ 331573Srgrimes 341573Srgrimes#if defined(LIBC_SCCS) && !defined(lint) 351573Srgrimesstatic char sccsid[] = "@(#)fts.c 8.4 (Berkeley) 4/16/94"; 361573Srgrimes#endif /* LIBC_SCCS and not lint */ 371573Srgrimes 381573Srgrimes#include <sys/param.h> 391573Srgrimes#include <sys/stat.h> 401573Srgrimes 411573Srgrimes#include <dirent.h> 421573Srgrimes#include <errno.h> 431573Srgrimes#include <fcntl.h> 441573Srgrimes#include <fts.h> 451573Srgrimes#include <stdlib.h> 461573Srgrimes#include <string.h> 471573Srgrimes#include <unistd.h> 481573Srgrimes 491573Srgrimesstatic FTSENT *fts_alloc __P((FTS *, char *, int)); 501573Srgrimesstatic FTSENT *fts_build __P((FTS *, int)); 511573Srgrimesstatic void fts_lfree __P((FTSENT *)); 521573Srgrimesstatic void fts_load __P((FTS *, FTSENT *)); 531573Srgrimesstatic size_t fts_maxarglen __P((char * const *)); 541573Srgrimesstatic void fts_padjust __P((FTS *, void *)); 551573Srgrimesstatic int fts_palloc __P((FTS *, size_t)); 561573Srgrimesstatic FTSENT *fts_sort __P((FTS *, FTSENT *, int)); 571573Srgrimesstatic u_short fts_stat __P((FTS *, FTSENT *, int)); 581573Srgrimes 5917141Sjkh#define ISDOT(a) (a[0] == '.' && (!a[1] || (a[1] == '.' && !a[2])) ) 601573Srgrimes 611573Srgrimes#define ISSET(opt) (sp->fts_options & opt) 621573Srgrimes#define SET(opt) (sp->fts_options |= opt) 631573Srgrimes 641573Srgrimes#define CHDIR(sp, path) (!ISSET(FTS_NOCHDIR) && chdir(path)) 651573Srgrimes#define FCHDIR(sp, fd) (!ISSET(FTS_NOCHDIR) && fchdir(fd)) 661573Srgrimes 671573Srgrimes/* fts_build flags */ 681573Srgrimes#define BCHILD 1 /* fts_children */ 691573Srgrimes#define BNAMES 2 /* fts_children, names only */ 701573Srgrimes#define BREAD 3 /* fts_read */ 711573Srgrimes 721573SrgrimesFTS * 731573Srgrimesfts_open(argv, options, compar) 741573Srgrimes char * const *argv; 751573Srgrimes register int options; 761573Srgrimes int (*compar)(); 771573Srgrimes{ 781573Srgrimes register FTS *sp; 791573Srgrimes register FTSENT *p, *root; 801573Srgrimes register int nitems; 811573Srgrimes FTSENT *parent, *tmp; 821573Srgrimes int len; 831573Srgrimes 841573Srgrimes /* Options check. */ 851573Srgrimes if (options & ~FTS_OPTIONMASK) { 861573Srgrimes errno = EINVAL; 871573Srgrimes return (NULL); 881573Srgrimes } 891573Srgrimes 901573Srgrimes /* Allocate/initialize the stream */ 911573Srgrimes if ((sp = malloc((u_int)sizeof(FTS))) == NULL) 921573Srgrimes return (NULL); 931573Srgrimes memset(sp, 0, sizeof(FTS)); 941573Srgrimes sp->fts_compar = compar; 951573Srgrimes sp->fts_options = options; 961573Srgrimes 971573Srgrimes /* Logical walks turn on NOCHDIR; symbolic links are too hard. */ 981573Srgrimes if (ISSET(FTS_LOGICAL)) 991573Srgrimes SET(FTS_NOCHDIR); 1001573Srgrimes 1011573Srgrimes /* 1021573Srgrimes * Start out with 1K of path space, and enough, in any case, 1031573Srgrimes * to hold the user's paths. 1041573Srgrimes */ 1051573Srgrimes if (fts_palloc(sp, MAX(fts_maxarglen(argv), MAXPATHLEN))) 1061573Srgrimes goto mem1; 1071573Srgrimes 1081573Srgrimes /* Allocate/initialize root's parent. */ 1091573Srgrimes if ((parent = fts_alloc(sp, "", 0)) == NULL) 1101573Srgrimes goto mem2; 1111573Srgrimes parent->fts_level = FTS_ROOTPARENTLEVEL; 1121573Srgrimes 1131573Srgrimes /* Allocate/initialize root(s). */ 1141573Srgrimes for (root = NULL, nitems = 0; *argv; ++argv, ++nitems) { 1151573Srgrimes /* Don't allow zero-length paths. */ 1161573Srgrimes if ((len = strlen(*argv)) == 0) { 1171573Srgrimes errno = ENOENT; 1181573Srgrimes goto mem3; 1191573Srgrimes } 1201573Srgrimes 1211573Srgrimes p = fts_alloc(sp, *argv, len); 1221573Srgrimes p->fts_level = FTS_ROOTLEVEL; 1231573Srgrimes p->fts_parent = parent; 1241573Srgrimes p->fts_accpath = p->fts_name; 1251573Srgrimes p->fts_info = fts_stat(sp, p, ISSET(FTS_COMFOLLOW)); 1261573Srgrimes 1271573Srgrimes /* Command-line "." and ".." are real directories. */ 1281573Srgrimes if (p->fts_info == FTS_DOT) 1291573Srgrimes p->fts_info = FTS_D; 1301573Srgrimes 1311573Srgrimes /* 1321573Srgrimes * If comparison routine supplied, traverse in sorted 1331573Srgrimes * order; otherwise traverse in the order specified. 1341573Srgrimes */ 1351573Srgrimes if (compar) { 1361573Srgrimes p->fts_link = root; 1371573Srgrimes root = p; 1381573Srgrimes } else { 1391573Srgrimes p->fts_link = NULL; 1401573Srgrimes if (root == NULL) 1411573Srgrimes tmp = root = p; 1421573Srgrimes else { 1431573Srgrimes tmp->fts_link = p; 1441573Srgrimes tmp = p; 1451573Srgrimes } 1461573Srgrimes } 1471573Srgrimes } 1481573Srgrimes if (compar && nitems > 1) 1491573Srgrimes root = fts_sort(sp, root, nitems); 1501573Srgrimes 1511573Srgrimes /* 1521573Srgrimes * Allocate a dummy pointer and make fts_read think that we've just 1531573Srgrimes * finished the node before the root(s); set p->fts_info to FTS_INIT 1541573Srgrimes * so that everything about the "current" node is ignored. 1551573Srgrimes */ 1561573Srgrimes if ((sp->fts_cur = fts_alloc(sp, "", 0)) == NULL) 1571573Srgrimes goto mem3; 1581573Srgrimes sp->fts_cur->fts_link = root; 1591573Srgrimes sp->fts_cur->fts_info = FTS_INIT; 1601573Srgrimes 1611573Srgrimes /* 1621573Srgrimes * If using chdir(2), grab a file descriptor pointing to dot to insure 1631573Srgrimes * that we can get back here; this could be avoided for some paths, 1641573Srgrimes * but almost certainly not worth the effort. Slashes, symbolic links, 1651573Srgrimes * and ".." are all fairly nasty problems. Note, if we can't get the 1661573Srgrimes * descriptor we run anyway, just more slowly. 1671573Srgrimes */ 1681573Srgrimes if (!ISSET(FTS_NOCHDIR) && (sp->fts_rfd = open(".", O_RDONLY, 0)) < 0) 1691573Srgrimes SET(FTS_NOCHDIR); 1701573Srgrimes 1711573Srgrimes return (sp); 1721573Srgrimes 1731573Srgrimesmem3: fts_lfree(root); 1741573Srgrimes free(parent); 1751573Srgrimesmem2: free(sp->fts_path); 1761573Srgrimesmem1: free(sp); 1771573Srgrimes return (NULL); 1781573Srgrimes} 1791573Srgrimes 1801573Srgrimesstatic void 1811573Srgrimesfts_load(sp, p) 1821573Srgrimes FTS *sp; 1831573Srgrimes register FTSENT *p; 1841573Srgrimes{ 1851573Srgrimes register int len; 1861573Srgrimes register char *cp; 1871573Srgrimes 1881573Srgrimes /* 1891573Srgrimes * Load the stream structure for the next traversal. Since we don't 1901573Srgrimes * actually enter the directory until after the preorder visit, set 1911573Srgrimes * the fts_accpath field specially so the chdir gets done to the right 1921573Srgrimes * place and the user can access the first node. From fts_open it's 1931573Srgrimes * known that the path will fit. 1941573Srgrimes */ 1951573Srgrimes len = p->fts_pathlen = p->fts_namelen; 1961573Srgrimes memmove(sp->fts_path, p->fts_name, len + 1); 1971573Srgrimes if ((cp = strrchr(p->fts_name, '/')) && (cp != p->fts_name || cp[1])) { 1981573Srgrimes len = strlen(++cp); 1991573Srgrimes memmove(p->fts_name, cp, len + 1); 2001573Srgrimes p->fts_namelen = len; 2011573Srgrimes } 2021573Srgrimes p->fts_accpath = p->fts_path = sp->fts_path; 2031573Srgrimes sp->fts_dev = p->fts_dev; 2041573Srgrimes} 2051573Srgrimes 2061573Srgrimesint 2071573Srgrimesfts_close(sp) 2081573Srgrimes FTS *sp; 2091573Srgrimes{ 2101573Srgrimes register FTSENT *freep, *p; 2111573Srgrimes int saved_errno; 2121573Srgrimes 2131573Srgrimes /* 2141573Srgrimes * This still works if we haven't read anything -- the dummy structure 2151573Srgrimes * points to the root list, so we step through to the end of the root 2161573Srgrimes * list which has a valid parent pointer. 2171573Srgrimes */ 2181573Srgrimes if (sp->fts_cur) { 2191573Srgrimes for (p = sp->fts_cur; p->fts_level >= FTS_ROOTLEVEL;) { 2201573Srgrimes freep = p; 2211573Srgrimes p = p->fts_link ? p->fts_link : p->fts_parent; 2221573Srgrimes free(freep); 2231573Srgrimes } 2241573Srgrimes free(p); 2251573Srgrimes } 2261573Srgrimes 2271573Srgrimes /* Free up child linked list, sort array, path buffer. */ 2281573Srgrimes if (sp->fts_child) 2291573Srgrimes fts_lfree(sp->fts_child); 2301573Srgrimes if (sp->fts_array) 2311573Srgrimes free(sp->fts_array); 2321573Srgrimes free(sp->fts_path); 2331573Srgrimes 2341573Srgrimes /* Return to original directory, save errno if necessary. */ 2351573Srgrimes if (!ISSET(FTS_NOCHDIR)) { 2361573Srgrimes saved_errno = fchdir(sp->fts_rfd) ? errno : 0; 2371573Srgrimes (void)close(sp->fts_rfd); 2381573Srgrimes } 2391573Srgrimes 2401573Srgrimes /* Free up the stream pointer. */ 2411573Srgrimes free(sp); 2421573Srgrimes 2431573Srgrimes /* Set errno and return. */ 2441573Srgrimes if (!ISSET(FTS_NOCHDIR) && saved_errno) { 2451573Srgrimes errno = saved_errno; 2461573Srgrimes return (-1); 2471573Srgrimes } 2481573Srgrimes return (0); 2491573Srgrimes} 2501573Srgrimes 2511573Srgrimes/* 2521573Srgrimes * Special case a root of "/" so that slashes aren't appended which would 2531573Srgrimes * cause paths to be written as "//foo". 2541573Srgrimes */ 2551573Srgrimes#define NAPPEND(p) \ 2561573Srgrimes (p->fts_level == FTS_ROOTLEVEL && p->fts_pathlen == 1 && \ 2571573Srgrimes p->fts_path[0] == '/' ? 0 : p->fts_pathlen) 2581573Srgrimes 2591573SrgrimesFTSENT * 2601573Srgrimesfts_read(sp) 2611573Srgrimes register FTS *sp; 2621573Srgrimes{ 2631573Srgrimes register FTSENT *p, *tmp; 2641573Srgrimes register int instr; 2651573Srgrimes register char *t; 2661573Srgrimes int saved_errno; 2671573Srgrimes 2681573Srgrimes /* If finished or unrecoverable error, return NULL. */ 2691573Srgrimes if (sp->fts_cur == NULL || ISSET(FTS_STOP)) 2701573Srgrimes return (NULL); 2711573Srgrimes 2721573Srgrimes /* Set current node pointer. */ 2731573Srgrimes p = sp->fts_cur; 2741573Srgrimes 2751573Srgrimes /* Save and zero out user instructions. */ 2761573Srgrimes instr = p->fts_instr; 2771573Srgrimes p->fts_instr = FTS_NOINSTR; 2781573Srgrimes 2791573Srgrimes /* Any type of file may be re-visited; re-stat and re-turn. */ 2801573Srgrimes if (instr == FTS_AGAIN) { 2811573Srgrimes p->fts_info = fts_stat(sp, p, 0); 2821573Srgrimes return (p); 2831573Srgrimes } 2841573Srgrimes 2851573Srgrimes /* 2861573Srgrimes * Following a symlink -- SLNONE test allows application to see 2871573Srgrimes * SLNONE and recover. If indirecting through a symlink, have 2881573Srgrimes * keep a pointer to current location. If unable to get that 2891573Srgrimes * pointer, follow fails. 2901573Srgrimes */ 2911573Srgrimes if (instr == FTS_FOLLOW && 2921573Srgrimes (p->fts_info == FTS_SL || p->fts_info == FTS_SLNONE)) { 2931573Srgrimes p->fts_info = fts_stat(sp, p, 1); 2941573Srgrimes if (p->fts_info == FTS_D && !ISSET(FTS_NOCHDIR)) 2951573Srgrimes if ((p->fts_symfd = open(".", O_RDONLY, 0)) < 0) { 2961573Srgrimes p->fts_errno = errno; 2971573Srgrimes p->fts_info = FTS_ERR; 2981573Srgrimes } else 2991573Srgrimes p->fts_flags |= FTS_SYMFOLLOW; 3001573Srgrimes return (p); 3011573Srgrimes } 3021573Srgrimes 3031573Srgrimes /* Directory in pre-order. */ 3041573Srgrimes if (p->fts_info == FTS_D) { 3051573Srgrimes /* If skipped or crossed mount point, do post-order visit. */ 3061573Srgrimes if (instr == FTS_SKIP || 30717141Sjkh (ISSET(FTS_XDEV) && p->fts_dev != sp->fts_dev) ) { 3081573Srgrimes if (p->fts_flags & FTS_SYMFOLLOW) 3091573Srgrimes (void)close(p->fts_symfd); 3101573Srgrimes if (sp->fts_child) { 3111573Srgrimes fts_lfree(sp->fts_child); 3121573Srgrimes sp->fts_child = NULL; 3131573Srgrimes } 3141573Srgrimes p->fts_info = FTS_DP; 3151573Srgrimes return (p); 3168870Srgrimes } 3171573Srgrimes 3181573Srgrimes /* Rebuild if only read the names and now traversing. */ 3191573Srgrimes if (sp->fts_child && sp->fts_options & FTS_NAMEONLY) { 3201573Srgrimes sp->fts_options &= ~FTS_NAMEONLY; 3211573Srgrimes fts_lfree(sp->fts_child); 3221573Srgrimes sp->fts_child = NULL; 3231573Srgrimes } 3241573Srgrimes 3251573Srgrimes /* 3261573Srgrimes * Cd to the subdirectory. 3271573Srgrimes * 3281573Srgrimes * If have already read and now fail to chdir, whack the list 3291573Srgrimes * to make the names come out right, and set the parent errno 3301573Srgrimes * so the application will eventually get an error condition. 3311573Srgrimes * Set the FTS_DONTCHDIR flag so that when we logically change 3321573Srgrimes * directories back to the parent we don't do a chdir. 3331573Srgrimes * 3341573Srgrimes * If haven't read do so. If the read fails, fts_build sets 3351573Srgrimes * FTS_STOP or the fts_info field of the node. 3361573Srgrimes */ 3371573Srgrimes if (sp->fts_child) { 3381573Srgrimes if (CHDIR(sp, p->fts_accpath)) { 3391573Srgrimes p->fts_errno = errno; 3401573Srgrimes p->fts_flags |= FTS_DONTCHDIR; 3411573Srgrimes for (p = sp->fts_child; p; p = p->fts_link) 3421573Srgrimes p->fts_accpath = 3431573Srgrimes p->fts_parent->fts_accpath; 3441573Srgrimes } 3451573Srgrimes } else if ((sp->fts_child = fts_build(sp, BREAD)) == NULL) { 3461573Srgrimes if (ISSET(FTS_STOP)) 3471573Srgrimes return (NULL); 3481573Srgrimes return (p); 3491573Srgrimes } 3501573Srgrimes p = sp->fts_child; 3511573Srgrimes sp->fts_child = NULL; 3521573Srgrimes goto name; 3531573Srgrimes } 3541573Srgrimes 3551573Srgrimes /* Move to the next node on this level. */ 3561573Srgrimesnext: tmp = p; 35717141Sjkh if ( (p = p->fts_link) ) { 3581573Srgrimes free(tmp); 3591573Srgrimes 3601573Srgrimes /* 3611573Srgrimes * If reached the top, return to the original directory, and 3621573Srgrimes * load the paths for the next root. 3631573Srgrimes */ 3641573Srgrimes if (p->fts_level == FTS_ROOTLEVEL) { 3651573Srgrimes if (!ISSET(FTS_NOCHDIR) && FCHDIR(sp, sp->fts_rfd)) { 3661573Srgrimes SET(FTS_STOP); 3671573Srgrimes return (NULL); 3681573Srgrimes } 3691573Srgrimes fts_load(sp, p); 3701573Srgrimes return (sp->fts_cur = p); 3711573Srgrimes } 3721573Srgrimes 3731573Srgrimes /* 3741573Srgrimes * User may have called fts_set on the node. If skipped, 3751573Srgrimes * ignore. If followed, get a file descriptor so we can 3761573Srgrimes * get back if necessary. 3771573Srgrimes */ 3781573Srgrimes if (p->fts_instr == FTS_SKIP) 3791573Srgrimes goto next; 3801573Srgrimes if (p->fts_instr == FTS_FOLLOW) { 3811573Srgrimes p->fts_info = fts_stat(sp, p, 1); 3821573Srgrimes if (p->fts_info == FTS_D && !ISSET(FTS_NOCHDIR)) 3831573Srgrimes if ((p->fts_symfd = 3841573Srgrimes open(".", O_RDONLY, 0)) < 0) { 3851573Srgrimes p->fts_errno = errno; 3861573Srgrimes p->fts_info = FTS_ERR; 3871573Srgrimes } else 3881573Srgrimes p->fts_flags |= FTS_SYMFOLLOW; 3891573Srgrimes p->fts_instr = FTS_NOINSTR; 3901573Srgrimes } 3911573Srgrimes 3921573Srgrimesname: t = sp->fts_path + NAPPEND(p->fts_parent); 3931573Srgrimes *t++ = '/'; 3941573Srgrimes memmove(t, p->fts_name, p->fts_namelen + 1); 3951573Srgrimes return (sp->fts_cur = p); 3961573Srgrimes } 3971573Srgrimes 3981573Srgrimes /* Move up to the parent node. */ 3991573Srgrimes p = tmp->fts_parent; 4001573Srgrimes free(tmp); 4011573Srgrimes 4021573Srgrimes if (p->fts_level == FTS_ROOTPARENTLEVEL) { 4031573Srgrimes /* 4041573Srgrimes * Done; free everything up and set errno to 0 so the user 4051573Srgrimes * can distinguish between error and EOF. 4061573Srgrimes */ 4071573Srgrimes free(p); 4081573Srgrimes errno = 0; 4091573Srgrimes return (sp->fts_cur = NULL); 4101573Srgrimes } 4111573Srgrimes 4121573Srgrimes /* Nul terminate the pathname. */ 4131573Srgrimes sp->fts_path[p->fts_pathlen] = '\0'; 4141573Srgrimes 4151573Srgrimes /* 4161573Srgrimes * Return to the parent directory. If at a root node or came through 4171573Srgrimes * a symlink, go back through the file descriptor. Otherwise, cd up 4181573Srgrimes * one directory. 4191573Srgrimes */ 4201573Srgrimes if (p->fts_level == FTS_ROOTLEVEL) { 4211573Srgrimes if (!ISSET(FTS_NOCHDIR) && FCHDIR(sp, sp->fts_rfd)) { 4221573Srgrimes SET(FTS_STOP); 4231573Srgrimes return (NULL); 4241573Srgrimes } 4251573Srgrimes } else if (p->fts_flags & FTS_SYMFOLLOW) { 4261573Srgrimes if (FCHDIR(sp, p->fts_symfd)) { 4271573Srgrimes saved_errno = errno; 4281573Srgrimes (void)close(p->fts_symfd); 4291573Srgrimes errno = saved_errno; 4301573Srgrimes SET(FTS_STOP); 4311573Srgrimes return (NULL); 4321573Srgrimes } 4331573Srgrimes (void)close(p->fts_symfd); 4341573Srgrimes } else if (!(p->fts_flags & FTS_DONTCHDIR)) { 4351573Srgrimes if (CHDIR(sp, "..")) { 4361573Srgrimes SET(FTS_STOP); 4371573Srgrimes return (NULL); 4381573Srgrimes } 4391573Srgrimes } 4401573Srgrimes p->fts_info = p->fts_errno ? FTS_ERR : FTS_DP; 4411573Srgrimes return (sp->fts_cur = p); 4421573Srgrimes} 4431573Srgrimes 4441573Srgrimes/* 4451573Srgrimes * Fts_set takes the stream as an argument although it's not used in this 4461573Srgrimes * implementation; it would be necessary if anyone wanted to add global 4471573Srgrimes * semantics to fts using fts_set. An error return is allowed for similar 4481573Srgrimes * reasons. 4491573Srgrimes */ 4501573Srgrimes/* ARGSUSED */ 4511573Srgrimesint 4521573Srgrimesfts_set(sp, p, instr) 4531573Srgrimes FTS *sp; 4541573Srgrimes FTSENT *p; 4551573Srgrimes int instr; 4561573Srgrimes{ 4571573Srgrimes if (instr && instr != FTS_AGAIN && instr != FTS_FOLLOW && 4581573Srgrimes instr != FTS_NOINSTR && instr != FTS_SKIP) { 4591573Srgrimes errno = EINVAL; 4601573Srgrimes return (1); 4611573Srgrimes } 4621573Srgrimes p->fts_instr = instr; 4631573Srgrimes return (0); 4641573Srgrimes} 4651573Srgrimes 4661573SrgrimesFTSENT * 4671573Srgrimesfts_children(sp, instr) 4681573Srgrimes register FTS *sp; 4691573Srgrimes int instr; 4701573Srgrimes{ 4711573Srgrimes register FTSENT *p; 4721573Srgrimes int fd; 4731573Srgrimes 4741573Srgrimes if (instr && instr != FTS_NAMEONLY) { 4751573Srgrimes errno = EINVAL; 4761573Srgrimes return (NULL); 4771573Srgrimes } 4781573Srgrimes 4791573Srgrimes /* Set current node pointer. */ 4801573Srgrimes p = sp->fts_cur; 4811573Srgrimes 4821573Srgrimes /* 4831573Srgrimes * Errno set to 0 so user can distinguish empty directory from 4841573Srgrimes * an error. 4851573Srgrimes */ 4861573Srgrimes errno = 0; 4871573Srgrimes 4881573Srgrimes /* Fatal errors stop here. */ 4891573Srgrimes if (ISSET(FTS_STOP)) 4901573Srgrimes return (NULL); 4911573Srgrimes 4921573Srgrimes /* Return logical hierarchy of user's arguments. */ 4931573Srgrimes if (p->fts_info == FTS_INIT) 4941573Srgrimes return (p->fts_link); 4951573Srgrimes 4961573Srgrimes /* 4971573Srgrimes * If not a directory being visited in pre-order, stop here. Could 4981573Srgrimes * allow FTS_DNR, assuming the user has fixed the problem, but the 4991573Srgrimes * same effect is available with FTS_AGAIN. 5001573Srgrimes */ 5011573Srgrimes if (p->fts_info != FTS_D /* && p->fts_info != FTS_DNR */) 5021573Srgrimes return (NULL); 5031573Srgrimes 5041573Srgrimes /* Free up any previous child list. */ 5051573Srgrimes if (sp->fts_child) 5061573Srgrimes fts_lfree(sp->fts_child); 5071573Srgrimes 5081573Srgrimes if (instr == FTS_NAMEONLY) { 5091573Srgrimes sp->fts_options |= FTS_NAMEONLY; 5101573Srgrimes instr = BNAMES; 5118870Srgrimes } else 5121573Srgrimes instr = BCHILD; 5131573Srgrimes 5141573Srgrimes /* 5151573Srgrimes * If using chdir on a relative path and called BEFORE fts_read does 5161573Srgrimes * its chdir to the root of a traversal, we can lose -- we need to 5171573Srgrimes * chdir into the subdirectory, and we don't know where the current 5181573Srgrimes * directory is, so we can't get back so that the upcoming chdir by 5191573Srgrimes * fts_read will work. 5201573Srgrimes */ 5211573Srgrimes if (p->fts_level != FTS_ROOTLEVEL || p->fts_accpath[0] == '/' || 5221573Srgrimes ISSET(FTS_NOCHDIR)) 5231573Srgrimes return (sp->fts_child = fts_build(sp, instr)); 5241573Srgrimes 5251573Srgrimes if ((fd = open(".", O_RDONLY, 0)) < 0) 5261573Srgrimes return (NULL); 5271573Srgrimes sp->fts_child = fts_build(sp, instr); 5281573Srgrimes if (fchdir(fd)) 5291573Srgrimes return (NULL); 5301573Srgrimes (void)close(fd); 5311573Srgrimes return (sp->fts_child); 5321573Srgrimes} 5331573Srgrimes 5341573Srgrimes/* 5351573Srgrimes * This is the tricky part -- do not casually change *anything* in here. The 5361573Srgrimes * idea is to build the linked list of entries that are used by fts_children 5371573Srgrimes * and fts_read. There are lots of special cases. 5381573Srgrimes * 5391573Srgrimes * The real slowdown in walking the tree is the stat calls. If FTS_NOSTAT is 5401573Srgrimes * set and it's a physical walk (so that symbolic links can't be directories), 5411573Srgrimes * we can do things quickly. First, if it's a 4.4BSD file system, the type 5421573Srgrimes * of the file is in the directory entry. Otherwise, we assume that the number 5431573Srgrimes * of subdirectories in a node is equal to the number of links to the parent. 5441573Srgrimes * The former skips all stat calls. The latter skips stat calls in any leaf 5451573Srgrimes * directories and for any files after the subdirectories in the directory have 5461573Srgrimes * been found, cutting the stat calls by about 2/3. 5471573Srgrimes */ 5481573Srgrimesstatic FTSENT * 5491573Srgrimesfts_build(sp, type) 5501573Srgrimes register FTS *sp; 5511573Srgrimes int type; 5521573Srgrimes{ 5531573Srgrimes register struct dirent *dp; 5541573Srgrimes register FTSENT *p, *head; 5551573Srgrimes register int nitems; 5561573Srgrimes FTSENT *cur, *tail; 5571573Srgrimes DIR *dirp; 5581573Srgrimes void *adjaddr; 5591573Srgrimes int cderrno, descend, len, level, maxlen, nlinks, saved_errno; 5601573Srgrimes char *cp; 5611573Srgrimes 5621573Srgrimes /* Set current node pointer. */ 5631573Srgrimes cur = sp->fts_cur; 5641573Srgrimes 5651573Srgrimes /* 5661573Srgrimes * Open the directory for reading. If this fails, we're done. 5671573Srgrimes * If being called from fts_read, set the fts_info field. 5681573Srgrimes */ 5691573Srgrimes if ((dirp = opendir(cur->fts_accpath)) == NULL) { 5701573Srgrimes if (type == BREAD) { 5711573Srgrimes cur->fts_info = FTS_DNR; 5721573Srgrimes cur->fts_errno = errno; 5731573Srgrimes } 5741573Srgrimes return (NULL); 5751573Srgrimes } 5761573Srgrimes 5771573Srgrimes /* 5781573Srgrimes * Nlinks is the number of possible entries of type directory in the 5791573Srgrimes * directory if we're cheating on stat calls, 0 if we're not doing 5801573Srgrimes * any stat calls at all, -1 if we're doing stats on everything. 5811573Srgrimes */ 5821573Srgrimes if (type == BNAMES) 5831573Srgrimes nlinks = 0; 5841573Srgrimes else if (ISSET(FTS_NOSTAT) && ISSET(FTS_PHYSICAL)) 5851573Srgrimes nlinks = cur->fts_nlink - (ISSET(FTS_SEEDOT) ? 0 : 2); 5861573Srgrimes else 5871573Srgrimes nlinks = -1; 5881573Srgrimes 5891573Srgrimes#ifdef notdef 5901573Srgrimes (void)printf("nlinks == %d (cur: %d)\n", nlinks, cur->fts_nlink); 5911573Srgrimes (void)printf("NOSTAT %d PHYSICAL %d SEEDOT %d\n", 5921573Srgrimes ISSET(FTS_NOSTAT), ISSET(FTS_PHYSICAL), ISSET(FTS_SEEDOT)); 5931573Srgrimes#endif 5941573Srgrimes /* 5951573Srgrimes * If we're going to need to stat anything or we want to descend 5961573Srgrimes * and stay in the directory, chdir. If this fails we keep going, 5971573Srgrimes * but set a flag so we don't chdir after the post-order visit. 5981573Srgrimes * We won't be able to stat anything, but we can still return the 5991573Srgrimes * names themselves. Note, that since fts_read won't be able to 6001573Srgrimes * chdir into the directory, it will have to return different path 6011573Srgrimes * names than before, i.e. "a/b" instead of "b". Since the node 6021573Srgrimes * has already been visited in pre-order, have to wait until the 6031573Srgrimes * post-order visit to return the error. There is a special case 6041573Srgrimes * here, if there was nothing to stat then it's not an error to 6051573Srgrimes * not be able to stat. This is all fairly nasty. If a program 6061573Srgrimes * needed sorted entries or stat information, they had better be 6071573Srgrimes * checking FTS_NS on the returned nodes. 6081573Srgrimes */ 6091573Srgrimes cderrno = 0; 6101573Srgrimes if (nlinks || type == BREAD) 6111573Srgrimes if (FCHDIR(sp, dirfd(dirp))) { 6121573Srgrimes if (nlinks && type == BREAD) 6131573Srgrimes cur->fts_errno = errno; 6141573Srgrimes cur->fts_flags |= FTS_DONTCHDIR; 6151573Srgrimes descend = 0; 6161573Srgrimes cderrno = errno; 6171573Srgrimes } else 6181573Srgrimes descend = 1; 6191573Srgrimes else 6201573Srgrimes descend = 0; 6211573Srgrimes 6221573Srgrimes /* 6231573Srgrimes * Figure out the max file name length that can be stored in the 6241573Srgrimes * current path -- the inner loop allocates more path as necessary. 6251573Srgrimes * We really wouldn't have to do the maxlen calculations here, we 6261573Srgrimes * could do them in fts_read before returning the path, but it's a 6271573Srgrimes * lot easier here since the length is part of the dirent structure. 6281573Srgrimes * 6291573Srgrimes * If not changing directories set a pointer so that can just append 6301573Srgrimes * each new name into the path. 6311573Srgrimes */ 6321573Srgrimes maxlen = sp->fts_pathlen - cur->fts_pathlen - 1; 6331573Srgrimes len = NAPPEND(cur); 6341573Srgrimes if (ISSET(FTS_NOCHDIR)) { 6351573Srgrimes cp = sp->fts_path + len; 6361573Srgrimes *cp++ = '/'; 6371573Srgrimes } 6381573Srgrimes 6391573Srgrimes level = cur->fts_level + 1; 6401573Srgrimes 6411573Srgrimes /* Read the directory, attaching each entry to the `link' pointer. */ 6421573Srgrimes adjaddr = NULL; 64317141Sjkh for (head = tail = NULL, nitems = 0; (dp = readdir(dirp)); ) { 6441573Srgrimes if (!ISSET(FTS_SEEDOT) && ISDOT(dp->d_name)) 6451573Srgrimes continue; 6461573Srgrimes 6471573Srgrimes if ((p = fts_alloc(sp, dp->d_name, (int)dp->d_namlen)) == NULL) 6481573Srgrimes goto mem1; 6491573Srgrimes if (dp->d_namlen > maxlen) { 6501573Srgrimes if (fts_palloc(sp, (size_t)dp->d_namlen)) { 6511573Srgrimes /* 6521573Srgrimes * No more memory for path or structures. Save 6531573Srgrimes * errno, free up the current structure and the 6541573Srgrimes * structures already allocated. 6551573Srgrimes */ 6561573Srgrimesmem1: saved_errno = errno; 6571573Srgrimes if (p) 6581573Srgrimes free(p); 6591573Srgrimes fts_lfree(head); 6601573Srgrimes (void)closedir(dirp); 6611573Srgrimes errno = saved_errno; 6621573Srgrimes cur->fts_info = FTS_ERR; 6631573Srgrimes SET(FTS_STOP); 6641573Srgrimes return (NULL); 6651573Srgrimes } 6661573Srgrimes adjaddr = sp->fts_path; 6671573Srgrimes maxlen = sp->fts_pathlen - sp->fts_cur->fts_pathlen - 1; 6681573Srgrimes } 6691573Srgrimes 6701573Srgrimes p->fts_pathlen = len + dp->d_namlen + 1; 6711573Srgrimes p->fts_parent = sp->fts_cur; 6721573Srgrimes p->fts_level = level; 6731573Srgrimes 6741573Srgrimes if (cderrno) { 6751573Srgrimes if (nlinks) { 6761573Srgrimes p->fts_info = FTS_NS; 6771573Srgrimes p->fts_errno = cderrno; 6781573Srgrimes } else 6791573Srgrimes p->fts_info = FTS_NSOK; 6801573Srgrimes p->fts_accpath = cur->fts_accpath; 6811573Srgrimes } else if (nlinks == 0 6821573Srgrimes#ifdef DT_DIR 68317141Sjkh || (nlinks > 0 && 68417141Sjkh dp->d_type != DT_DIR && dp->d_type != DT_UNKNOWN) 6851573Srgrimes#endif 6861573Srgrimes ) { 6871573Srgrimes p->fts_accpath = 6881573Srgrimes ISSET(FTS_NOCHDIR) ? p->fts_path : p->fts_name; 6891573Srgrimes p->fts_info = FTS_NSOK; 6901573Srgrimes } else { 6911573Srgrimes /* Build a file name for fts_stat to stat. */ 6921573Srgrimes if (ISSET(FTS_NOCHDIR)) { 6931573Srgrimes p->fts_accpath = p->fts_path; 6941573Srgrimes memmove(cp, p->fts_name, p->fts_namelen + 1); 6951573Srgrimes } else 6961573Srgrimes p->fts_accpath = p->fts_name; 6971573Srgrimes /* Stat it. */ 6981573Srgrimes p->fts_info = fts_stat(sp, p, 0); 6991573Srgrimes 7001573Srgrimes /* Decrement link count if applicable. */ 7011573Srgrimes if (nlinks > 0 && (p->fts_info == FTS_D || 7021573Srgrimes p->fts_info == FTS_DC || p->fts_info == FTS_DOT)) 7031573Srgrimes --nlinks; 7041573Srgrimes } 7051573Srgrimes 7061573Srgrimes /* We walk in directory order so "ls -f" doesn't get upset. */ 7071573Srgrimes p->fts_link = NULL; 7081573Srgrimes if (head == NULL) 7091573Srgrimes head = tail = p; 7101573Srgrimes else { 7111573Srgrimes tail->fts_link = p; 7121573Srgrimes tail = p; 7131573Srgrimes } 7141573Srgrimes ++nitems; 7151573Srgrimes } 7161573Srgrimes (void)closedir(dirp); 7171573Srgrimes 7181573Srgrimes /* 7191573Srgrimes * If had to realloc the path, adjust the addresses for the rest 7201573Srgrimes * of the tree. 7211573Srgrimes */ 7221573Srgrimes if (adjaddr) 7231573Srgrimes fts_padjust(sp, adjaddr); 7241573Srgrimes 7251573Srgrimes /* 7261573Srgrimes * If not changing directories, reset the path back to original 7271573Srgrimes * state. 7281573Srgrimes */ 7291573Srgrimes if (ISSET(FTS_NOCHDIR)) { 7301573Srgrimes if (cp - 1 > sp->fts_path) 7311573Srgrimes --cp; 7321573Srgrimes *cp = '\0'; 7331573Srgrimes } 7341573Srgrimes 7351573Srgrimes /* 7361573Srgrimes * If descended after called from fts_children or after called from 7371573Srgrimes * fts_read and nothing found, get back. At the root level we use 7381573Srgrimes * the saved fd; if one of fts_open()'s arguments is a relative path 7391573Srgrimes * to an empty directory, we wind up here with no other way back. If 7401573Srgrimes * can't get back, we're done. 7411573Srgrimes */ 7421573Srgrimes if (descend && (type == BCHILD || !nitems) && 7431573Srgrimes (cur->fts_level == FTS_ROOTLEVEL ? 7441573Srgrimes FCHDIR(sp, sp->fts_rfd) : CHDIR(sp, ".."))) { 7451573Srgrimes cur->fts_info = FTS_ERR; 7461573Srgrimes SET(FTS_STOP); 7471573Srgrimes return (NULL); 7481573Srgrimes } 7491573Srgrimes 7501573Srgrimes /* If didn't find anything, return NULL. */ 7511573Srgrimes if (!nitems) { 7521573Srgrimes if (type == BREAD) 7531573Srgrimes cur->fts_info = FTS_DP; 7541573Srgrimes return (NULL); 7551573Srgrimes } 7561573Srgrimes 7571573Srgrimes /* Sort the entries. */ 7581573Srgrimes if (sp->fts_compar && nitems > 1) 7591573Srgrimes head = fts_sort(sp, head, nitems); 7601573Srgrimes return (head); 7611573Srgrimes} 7621573Srgrimes 7631573Srgrimesstatic u_short 7641573Srgrimesfts_stat(sp, p, follow) 7651573Srgrimes FTS *sp; 7661573Srgrimes register FTSENT *p; 7671573Srgrimes int follow; 7681573Srgrimes{ 7691573Srgrimes register FTSENT *t; 7701573Srgrimes register dev_t dev; 7711573Srgrimes register ino_t ino; 7721573Srgrimes struct stat *sbp, sb; 7731573Srgrimes int saved_errno; 7741573Srgrimes 7751573Srgrimes /* If user needs stat info, stat buffer already allocated. */ 7761573Srgrimes sbp = ISSET(FTS_NOSTAT) ? &sb : p->fts_statp; 7778870Srgrimes 7781573Srgrimes /* 7791573Srgrimes * If doing a logical walk, or application requested FTS_FOLLOW, do 7801573Srgrimes * a stat(2). If that fails, check for a non-existent symlink. If 7811573Srgrimes * fail, set the errno from the stat call. 7821573Srgrimes */ 7831573Srgrimes if (ISSET(FTS_LOGICAL) || follow) { 7841573Srgrimes if (stat(p->fts_accpath, sbp)) { 7851573Srgrimes saved_errno = errno; 7861573Srgrimes if (!lstat(p->fts_accpath, sbp)) { 7871573Srgrimes errno = 0; 7881573Srgrimes return (FTS_SLNONE); 7898870Srgrimes } 7901573Srgrimes p->fts_errno = saved_errno; 7911573Srgrimes goto err; 7921573Srgrimes } 7931573Srgrimes } else if (lstat(p->fts_accpath, sbp)) { 7941573Srgrimes p->fts_errno = errno; 7951573Srgrimeserr: memset(sbp, 0, sizeof(struct stat)); 7961573Srgrimes return (FTS_NS); 7971573Srgrimes } 7981573Srgrimes 7991573Srgrimes if (S_ISDIR(sbp->st_mode)) { 8001573Srgrimes /* 8011573Srgrimes * Set the device/inode. Used to find cycles and check for 8021573Srgrimes * crossing mount points. Also remember the link count, used 8031573Srgrimes * in fts_build to limit the number of stat calls. It is 8041573Srgrimes * understood that these fields are only referenced if fts_info 8051573Srgrimes * is set to FTS_D. 8061573Srgrimes */ 8071573Srgrimes dev = p->fts_dev = sbp->st_dev; 8081573Srgrimes ino = p->fts_ino = sbp->st_ino; 8091573Srgrimes p->fts_nlink = sbp->st_nlink; 8101573Srgrimes 8111573Srgrimes if (ISDOT(p->fts_name)) 8121573Srgrimes return (FTS_DOT); 8131573Srgrimes 8141573Srgrimes /* 8151573Srgrimes * Cycle detection is done by brute force when the directory 8161573Srgrimes * is first encountered. If the tree gets deep enough or the 8171573Srgrimes * number of symbolic links to directories is high enough, 8181573Srgrimes * something faster might be worthwhile. 8191573Srgrimes */ 8201573Srgrimes for (t = p->fts_parent; 8211573Srgrimes t->fts_level >= FTS_ROOTLEVEL; t = t->fts_parent) 8221573Srgrimes if (ino == t->fts_ino && dev == t->fts_dev) { 8231573Srgrimes p->fts_cycle = t; 8241573Srgrimes return (FTS_DC); 8251573Srgrimes } 8261573Srgrimes return (FTS_D); 8271573Srgrimes } 8281573Srgrimes if (S_ISLNK(sbp->st_mode)) 8291573Srgrimes return (FTS_SL); 8301573Srgrimes if (S_ISREG(sbp->st_mode)) 8311573Srgrimes return (FTS_F); 8321573Srgrimes return (FTS_DEFAULT); 8331573Srgrimes} 8341573Srgrimes 8351573Srgrimesstatic FTSENT * 8361573Srgrimesfts_sort(sp, head, nitems) 8371573Srgrimes FTS *sp; 8381573Srgrimes FTSENT *head; 8391573Srgrimes register int nitems; 8401573Srgrimes{ 8411573Srgrimes register FTSENT **ap, *p; 8421573Srgrimes 8431573Srgrimes /* 8441573Srgrimes * Construct an array of pointers to the structures and call qsort(3). 8451573Srgrimes * Reassemble the array in the order returned by qsort. If unable to 8461573Srgrimes * sort for memory reasons, return the directory entries in their 8471573Srgrimes * current order. Allocate enough space for the current needs plus 8481573Srgrimes * 40 so don't realloc one entry at a time. 8491573Srgrimes */ 8501573Srgrimes if (nitems > sp->fts_nitems) { 8511573Srgrimes sp->fts_nitems = nitems + 40; 8521573Srgrimes if ((sp->fts_array = realloc(sp->fts_array, 8531573Srgrimes (size_t)(sp->fts_nitems * sizeof(FTSENT *)))) == NULL) { 8541573Srgrimes sp->fts_nitems = 0; 8551573Srgrimes return (head); 8561573Srgrimes } 8571573Srgrimes } 8581573Srgrimes for (ap = sp->fts_array, p = head; p; p = p->fts_link) 8591573Srgrimes *ap++ = p; 8601573Srgrimes qsort((void *)sp->fts_array, nitems, sizeof(FTSENT *), sp->fts_compar); 8611573Srgrimes for (head = *(ap = sp->fts_array); --nitems; ++ap) 8621573Srgrimes ap[0]->fts_link = ap[1]; 8631573Srgrimes ap[0]->fts_link = NULL; 8641573Srgrimes return (head); 8651573Srgrimes} 8661573Srgrimes 8671573Srgrimesstatic FTSENT * 8681573Srgrimesfts_alloc(sp, name, namelen) 8691573Srgrimes FTS *sp; 8701573Srgrimes char *name; 8711573Srgrimes register int namelen; 8721573Srgrimes{ 8731573Srgrimes register FTSENT *p; 8741573Srgrimes size_t len; 8751573Srgrimes 8761573Srgrimes /* 8771573Srgrimes * The file name is a variable length array and no stat structure is 8781573Srgrimes * necessary if the user has set the nostat bit. Allocate the FTSENT 8791573Srgrimes * structure, the file name and the stat structure in one chunk, but 8801573Srgrimes * be careful that the stat structure is reasonably aligned. Since the 8811573Srgrimes * fts_name field is declared to be of size 1, the fts_name pointer is 8821573Srgrimes * namelen + 2 before the first possible address of the stat structure. 8831573Srgrimes */ 8841573Srgrimes len = sizeof(FTSENT) + namelen; 8851573Srgrimes if (!ISSET(FTS_NOSTAT)) 8861573Srgrimes len += sizeof(struct stat) + ALIGNBYTES; 8871573Srgrimes if ((p = malloc(len)) == NULL) 8881573Srgrimes return (NULL); 8891573Srgrimes 8901573Srgrimes /* Copy the name plus the trailing NULL. */ 8911573Srgrimes memmove(p->fts_name, name, namelen + 1); 8921573Srgrimes 8931573Srgrimes if (!ISSET(FTS_NOSTAT)) 8941573Srgrimes p->fts_statp = (struct stat *)ALIGN(p->fts_name + namelen + 2); 8951573Srgrimes p->fts_namelen = namelen; 8961573Srgrimes p->fts_path = sp->fts_path; 8971573Srgrimes p->fts_errno = 0; 8981573Srgrimes p->fts_flags = 0; 8991573Srgrimes p->fts_instr = FTS_NOINSTR; 9001573Srgrimes p->fts_number = 0; 9011573Srgrimes p->fts_pointer = NULL; 9021573Srgrimes return (p); 9031573Srgrimes} 9041573Srgrimes 9051573Srgrimesstatic void 9061573Srgrimesfts_lfree(head) 9071573Srgrimes register FTSENT *head; 9081573Srgrimes{ 9091573Srgrimes register FTSENT *p; 9101573Srgrimes 9111573Srgrimes /* Free a linked list of structures. */ 91217141Sjkh while ( (p = head) ) { 9131573Srgrimes head = head->fts_link; 9141573Srgrimes free(p); 9151573Srgrimes } 9161573Srgrimes} 9171573Srgrimes 9181573Srgrimes/* 9191573Srgrimes * Allow essentially unlimited paths; find, rm, ls should all work on any tree. 9201573Srgrimes * Most systems will allow creation of paths much longer than MAXPATHLEN, even 9211573Srgrimes * though the kernel won't resolve them. Add the size (not just what's needed) 9228870Srgrimes * plus 256 bytes so don't realloc the path 2 bytes at a time. 9231573Srgrimes */ 9241573Srgrimesstatic int 9251573Srgrimesfts_palloc(sp, more) 9261573Srgrimes FTS *sp; 9271573Srgrimes size_t more; 9281573Srgrimes{ 9291573Srgrimes sp->fts_pathlen += more + 256; 9301573Srgrimes sp->fts_path = realloc(sp->fts_path, (size_t)sp->fts_pathlen); 9311573Srgrimes return (sp->fts_path == NULL); 9321573Srgrimes} 9331573Srgrimes 9341573Srgrimes/* 9351573Srgrimes * When the path is realloc'd, have to fix all of the pointers in structures 9361573Srgrimes * already returned. 9371573Srgrimes */ 9381573Srgrimesstatic void 9391573Srgrimesfts_padjust(sp, addr) 9401573Srgrimes FTS *sp; 9411573Srgrimes void *addr; 9421573Srgrimes{ 9431573Srgrimes FTSENT *p; 9441573Srgrimes 9451573Srgrimes#define ADJUST(p) { \ 9461573Srgrimes (p)->fts_accpath = \ 9471573Srgrimes (char *)addr + ((p)->fts_accpath - (p)->fts_path); \ 9481573Srgrimes (p)->fts_path = addr; \ 9491573Srgrimes} 9501573Srgrimes /* Adjust the current set of children. */ 9511573Srgrimes for (p = sp->fts_child; p; p = p->fts_link) 9521573Srgrimes ADJUST(p); 9531573Srgrimes 9541573Srgrimes /* Adjust the rest of the tree. */ 9551573Srgrimes for (p = sp->fts_cur; p->fts_level >= FTS_ROOTLEVEL;) { 9561573Srgrimes ADJUST(p); 9571573Srgrimes p = p->fts_link ? p->fts_link : p->fts_parent; 9581573Srgrimes } 9591573Srgrimes} 9601573Srgrimes 9611573Srgrimesstatic size_t 9621573Srgrimesfts_maxarglen(argv) 9631573Srgrimes char * const *argv; 9641573Srgrimes{ 9651573Srgrimes size_t len, max; 9661573Srgrimes 9671573Srgrimes for (max = 0; *argv; ++argv) 9681573Srgrimes if ((len = strlen(*argv)) > max) 9691573Srgrimes max = len; 9701573Srgrimes return (max); 9711573Srgrimes} 972