fts-compat.c revision 1573
1237651Sbschmidt/*- 2237651Sbschmidt * Copyright (c) 1990, 1993, 1994 3237651Sbschmidt * The Regents of the University of California. All rights reserved. 4237651Sbschmidt * 5237651Sbschmidt * Redistribution and use in source and binary forms, with or without 6237651Sbschmidt * modification, are permitted provided that the following conditions 7237651Sbschmidt * are met: 8237651Sbschmidt * 1. Redistributions of source code must retain the above copyright 9237651Sbschmidt * notice, this list of conditions and the following disclaimer. 10237651Sbschmidt * 2. Redistributions in binary form must reproduce the above copyright 11237651Sbschmidt * notice, this list of conditions and the following disclaimer in the 12237651Sbschmidt * documentation and/or other materials provided with the distribution. 13237651Sbschmidt * 3. All advertising materials mentioning features or use of this software 14237651Sbschmidt * must display the following acknowledgement: 15237651Sbschmidt * This product includes software developed by the University of 16237651Sbschmidt * California, Berkeley and its contributors. 17237651Sbschmidt * 4. Neither the name of the University nor the names of its contributors 18237651Sbschmidt * may be used to endorse or promote products derived from this software 19237651Sbschmidt * without specific prior written permission. 20237651Sbschmidt * 21237651Sbschmidt * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND 22237651Sbschmidt * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 23237651Sbschmidt * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 24237651Sbschmidt * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE 25237651Sbschmidt * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 26237651Sbschmidt * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 27237651Sbschmidt * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 28237651Sbschmidt * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 29237651Sbschmidt * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 30237651Sbschmidt * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 31237651Sbschmidt * SUCH DAMAGE. 32237651Sbschmidt */ 33237651Sbschmidt 34237651Sbschmidt#if defined(LIBC_SCCS) && !defined(lint) 35237651Sbschmidtstatic char sccsid[] = "@(#)fts.c 8.4 (Berkeley) 4/16/94"; 36237651Sbschmidt#endif /* LIBC_SCCS and not lint */ 37237651Sbschmidt 38237651Sbschmidt#include <sys/param.h> 39237651Sbschmidt#include <sys/stat.h> 40237651Sbschmidt 41237651Sbschmidt#include <dirent.h> 42237651Sbschmidt#include <errno.h> 43237651Sbschmidt#include <fcntl.h> 44237651Sbschmidt#include <fts.h> 45237651Sbschmidt#include <stdlib.h> 46237651Sbschmidt#include <string.h> 47237651Sbschmidt#include <unistd.h> 48237651Sbschmidt 49237651Sbschmidtstatic FTSENT *fts_alloc __P((FTS *, char *, int)); 50237651Sbschmidtstatic FTSENT *fts_build __P((FTS *, int)); 51237651Sbschmidtstatic void fts_lfree __P((FTSENT *)); 52237651Sbschmidtstatic void fts_load __P((FTS *, FTSENT *)); 53237651Sbschmidtstatic size_t fts_maxarglen __P((char * const *)); 54237651Sbschmidtstatic void fts_padjust __P((FTS *, void *)); 55237651Sbschmidtstatic int fts_palloc __P((FTS *, size_t)); 56237651Sbschmidtstatic FTSENT *fts_sort __P((FTS *, FTSENT *, int)); 57237651Sbschmidtstatic u_short fts_stat __P((FTS *, FTSENT *, int)); 58237651Sbschmidt 59237651Sbschmidt#define ISDOT(a) (a[0] == '.' && (!a[1] || a[1] == '.' && !a[2])) 60237651Sbschmidt 61237651Sbschmidt#define ISSET(opt) (sp->fts_options & opt) 62237651Sbschmidt#define SET(opt) (sp->fts_options |= opt) 63237651Sbschmidt 64237651Sbschmidt#define CHDIR(sp, path) (!ISSET(FTS_NOCHDIR) && chdir(path)) 65237651Sbschmidt#define FCHDIR(sp, fd) (!ISSET(FTS_NOCHDIR) && fchdir(fd)) 66237651Sbschmidt 67237651Sbschmidt/* fts_build flags */ 68237651Sbschmidt#define BCHILD 1 /* fts_children */ 69237651Sbschmidt#define BNAMES 2 /* fts_children, names only */ 70237651Sbschmidt#define BREAD 3 /* fts_read */ 71237651Sbschmidt 72237651SbschmidtFTS * 73237651Sbschmidtfts_open(argv, options, compar) 74237651Sbschmidt char * const *argv; 75237651Sbschmidt register int options; 76237651Sbschmidt int (*compar)(); 77237651Sbschmidt{ 78237651Sbschmidt register FTS *sp; 79237651Sbschmidt register FTSENT *p, *root; 80237651Sbschmidt register int nitems; 81237651Sbschmidt FTSENT *parent, *tmp; 82237651Sbschmidt int len; 83237651Sbschmidt 84237651Sbschmidt /* Options check. */ 85237651Sbschmidt if (options & ~FTS_OPTIONMASK) { 86237651Sbschmidt errno = EINVAL; 87237651Sbschmidt return (NULL); 88237651Sbschmidt } 89237651Sbschmidt 90237651Sbschmidt /* Allocate/initialize the stream */ 91237651Sbschmidt if ((sp = malloc((u_int)sizeof(FTS))) == NULL) 92237651Sbschmidt return (NULL); 93237651Sbschmidt memset(sp, 0, sizeof(FTS)); 94237651Sbschmidt sp->fts_compar = compar; 95237651Sbschmidt sp->fts_options = options; 96237651Sbschmidt 97237651Sbschmidt /* Logical walks turn on NOCHDIR; symbolic links are too hard. */ 98237651Sbschmidt if (ISSET(FTS_LOGICAL)) 99237651Sbschmidt SET(FTS_NOCHDIR); 100237651Sbschmidt 101237651Sbschmidt /* 102237651Sbschmidt * Start out with 1K of path space, and enough, in any case, 103237651Sbschmidt * to hold the user's paths. 104237651Sbschmidt */ 105237651Sbschmidt if (fts_palloc(sp, MAX(fts_maxarglen(argv), MAXPATHLEN))) 106237651Sbschmidt goto mem1; 107237651Sbschmidt 108237651Sbschmidt /* Allocate/initialize root's parent. */ 109237651Sbschmidt if ((parent = fts_alloc(sp, "", 0)) == NULL) 110237651Sbschmidt goto mem2; 111237651Sbschmidt parent->fts_level = FTS_ROOTPARENTLEVEL; 112237651Sbschmidt 113237651Sbschmidt /* Allocate/initialize root(s). */ 114237651Sbschmidt for (root = NULL, nitems = 0; *argv; ++argv, ++nitems) { 115237651Sbschmidt /* Don't allow zero-length paths. */ 116237651Sbschmidt if ((len = strlen(*argv)) == 0) { 117237651Sbschmidt errno = ENOENT; 118237651Sbschmidt goto mem3; 119237651Sbschmidt } 120237651Sbschmidt 121237651Sbschmidt p = fts_alloc(sp, *argv, len); 122237651Sbschmidt p->fts_level = FTS_ROOTLEVEL; 123237651Sbschmidt p->fts_parent = parent; 124237651Sbschmidt p->fts_accpath = p->fts_name; 125237651Sbschmidt p->fts_info = fts_stat(sp, p, ISSET(FTS_COMFOLLOW)); 126237651Sbschmidt 127237651Sbschmidt /* Command-line "." and ".." are real directories. */ 128237651Sbschmidt if (p->fts_info == FTS_DOT) 129237651Sbschmidt p->fts_info = FTS_D; 130237651Sbschmidt 131237651Sbschmidt /* 132237651Sbschmidt * If comparison routine supplied, traverse in sorted 133237651Sbschmidt * order; otherwise traverse in the order specified. 134237651Sbschmidt */ 135237651Sbschmidt if (compar) { 136237651Sbschmidt p->fts_link = root; 137237651Sbschmidt root = p; 138237651Sbschmidt } else { 139237651Sbschmidt p->fts_link = NULL; 140237651Sbschmidt if (root == NULL) 141237651Sbschmidt tmp = root = p; 142237651Sbschmidt else { 143237651Sbschmidt tmp->fts_link = p; 144237651Sbschmidt tmp = p; 145237651Sbschmidt } 146237651Sbschmidt } 147237651Sbschmidt } 148237651Sbschmidt if (compar && nitems > 1) 149237651Sbschmidt root = fts_sort(sp, root, nitems); 150237651Sbschmidt 151237651Sbschmidt /* 152237651Sbschmidt * Allocate a dummy pointer and make fts_read think that we've just 153237651Sbschmidt * finished the node before the root(s); set p->fts_info to FTS_INIT 154237651Sbschmidt * so that everything about the "current" node is ignored. 155237651Sbschmidt */ 156237651Sbschmidt if ((sp->fts_cur = fts_alloc(sp, "", 0)) == NULL) 157237651Sbschmidt goto mem3; 158237651Sbschmidt sp->fts_cur->fts_link = root; 159237651Sbschmidt sp->fts_cur->fts_info = FTS_INIT; 160237651Sbschmidt 161237651Sbschmidt /* 162237651Sbschmidt * If using chdir(2), grab a file descriptor pointing to dot to insure 163237651Sbschmidt * that we can get back here; this could be avoided for some paths, 164237651Sbschmidt * but almost certainly not worth the effort. Slashes, symbolic links, 165237651Sbschmidt * and ".." are all fairly nasty problems. Note, if we can't get the 166237651Sbschmidt * descriptor we run anyway, just more slowly. 167237651Sbschmidt */ 168237651Sbschmidt if (!ISSET(FTS_NOCHDIR) && (sp->fts_rfd = open(".", O_RDONLY, 0)) < 0) 169237651Sbschmidt SET(FTS_NOCHDIR); 170237651Sbschmidt 171237651Sbschmidt return (sp); 172237651Sbschmidt 173237651Sbschmidtmem3: fts_lfree(root); 174237651Sbschmidt free(parent); 175237651Sbschmidtmem2: free(sp->fts_path); 176237651Sbschmidtmem1: free(sp); 177237651Sbschmidt return (NULL); 178237651Sbschmidt} 179237651Sbschmidt 180237651Sbschmidtstatic void 181237651Sbschmidtfts_load(sp, p) 182237651Sbschmidt FTS *sp; 183237651Sbschmidt register FTSENT *p; 184237651Sbschmidt{ 185237651Sbschmidt register int len; 186237651Sbschmidt register char *cp; 187237651Sbschmidt 188237651Sbschmidt /* 189237651Sbschmidt * Load the stream structure for the next traversal. Since we don't 190237651Sbschmidt * actually enter the directory until after the preorder visit, set 191237651Sbschmidt * the fts_accpath field specially so the chdir gets done to the right 192237651Sbschmidt * place and the user can access the first node. From fts_open it's 193237651Sbschmidt * known that the path will fit. 194237651Sbschmidt */ 195237651Sbschmidt len = p->fts_pathlen = p->fts_namelen; 196237651Sbschmidt memmove(sp->fts_path, p->fts_name, len + 1); 197237651Sbschmidt if ((cp = strrchr(p->fts_name, '/')) && (cp != p->fts_name || cp[1])) { 198237651Sbschmidt len = strlen(++cp); 199237651Sbschmidt memmove(p->fts_name, cp, len + 1); 200237651Sbschmidt p->fts_namelen = len; 201237651Sbschmidt } 202237651Sbschmidt p->fts_accpath = p->fts_path = sp->fts_path; 203237651Sbschmidt sp->fts_dev = p->fts_dev; 204237651Sbschmidt} 205237651Sbschmidt 206237651Sbschmidtint 207237651Sbschmidtfts_close(sp) 208237651Sbschmidt FTS *sp; 209237651Sbschmidt{ 210237651Sbschmidt register FTSENT *freep, *p; 211237651Sbschmidt int saved_errno; 212237651Sbschmidt 213237651Sbschmidt /* 214237651Sbschmidt * This still works if we haven't read anything -- the dummy structure 215237651Sbschmidt * points to the root list, so we step through to the end of the root 216237651Sbschmidt * list which has a valid parent pointer. 217237651Sbschmidt */ 218237651Sbschmidt if (sp->fts_cur) { 219237651Sbschmidt for (p = sp->fts_cur; p->fts_level >= FTS_ROOTLEVEL;) { 220237651Sbschmidt freep = p; 221237651Sbschmidt p = p->fts_link ? p->fts_link : p->fts_parent; 222237651Sbschmidt free(freep); 223237651Sbschmidt } 224237651Sbschmidt free(p); 225237651Sbschmidt } 226237651Sbschmidt 227237651Sbschmidt /* Free up child linked list, sort array, path buffer. */ 228237651Sbschmidt if (sp->fts_child) 229237651Sbschmidt fts_lfree(sp->fts_child); 230237651Sbschmidt if (sp->fts_array) 231237651Sbschmidt free(sp->fts_array); 232237651Sbschmidt free(sp->fts_path); 233237651Sbschmidt 234237651Sbschmidt /* Return to original directory, save errno if necessary. */ 235237651Sbschmidt if (!ISSET(FTS_NOCHDIR)) { 236237651Sbschmidt saved_errno = fchdir(sp->fts_rfd) ? errno : 0; 237237651Sbschmidt (void)close(sp->fts_rfd); 238237651Sbschmidt } 239237651Sbschmidt 240237651Sbschmidt /* Free up the stream pointer. */ 241237651Sbschmidt free(sp); 242237651Sbschmidt 243237651Sbschmidt /* Set errno and return. */ 244237651Sbschmidt if (!ISSET(FTS_NOCHDIR) && saved_errno) { 245237651Sbschmidt errno = saved_errno; 246237651Sbschmidt return (-1); 247237651Sbschmidt } 248237651Sbschmidt return (0); 249237651Sbschmidt} 250237651Sbschmidt 251237651Sbschmidt/* 252237651Sbschmidt * Special case a root of "/" so that slashes aren't appended which would 253237651Sbschmidt * cause paths to be written as "//foo". 254237651Sbschmidt */ 255237651Sbschmidt#define NAPPEND(p) \ 256237651Sbschmidt (p->fts_level == FTS_ROOTLEVEL && p->fts_pathlen == 1 && \ 257237651Sbschmidt p->fts_path[0] == '/' ? 0 : p->fts_pathlen) 258237651Sbschmidt 259237651SbschmidtFTSENT * 260237651Sbschmidtfts_read(sp) 261237651Sbschmidt register FTS *sp; 262237651Sbschmidt{ 263237651Sbschmidt register FTSENT *p, *tmp; 264237651Sbschmidt register int instr; 265237651Sbschmidt register char *t; 266237651Sbschmidt int saved_errno; 267237651Sbschmidt 268237651Sbschmidt /* If finished or unrecoverable error, return NULL. */ 269237651Sbschmidt if (sp->fts_cur == NULL || ISSET(FTS_STOP)) 270237651Sbschmidt return (NULL); 271237651Sbschmidt 272237651Sbschmidt /* Set current node pointer. */ 273237651Sbschmidt p = sp->fts_cur; 274237651Sbschmidt 275237651Sbschmidt /* Save and zero out user instructions. */ 276237651Sbschmidt instr = p->fts_instr; 277237651Sbschmidt p->fts_instr = FTS_NOINSTR; 278237651Sbschmidt 279237651Sbschmidt /* Any type of file may be re-visited; re-stat and re-turn. */ 280237651Sbschmidt if (instr == FTS_AGAIN) { 281237651Sbschmidt p->fts_info = fts_stat(sp, p, 0); 282237651Sbschmidt return (p); 283237651Sbschmidt } 284237651Sbschmidt 285237651Sbschmidt /* 286237651Sbschmidt * Following a symlink -- SLNONE test allows application to see 287237651Sbschmidt * SLNONE and recover. If indirecting through a symlink, have 288237651Sbschmidt * keep a pointer to current location. If unable to get that 289237651Sbschmidt * pointer, follow fails. 290237651Sbschmidt */ 291237651Sbschmidt if (instr == FTS_FOLLOW && 292237651Sbschmidt (p->fts_info == FTS_SL || p->fts_info == FTS_SLNONE)) { 293237651Sbschmidt p->fts_info = fts_stat(sp, p, 1); 294237651Sbschmidt if (p->fts_info == FTS_D && !ISSET(FTS_NOCHDIR)) 295237651Sbschmidt if ((p->fts_symfd = open(".", O_RDONLY, 0)) < 0) { 296237651Sbschmidt p->fts_errno = errno; 297237651Sbschmidt p->fts_info = FTS_ERR; 298237651Sbschmidt } else 299237651Sbschmidt p->fts_flags |= FTS_SYMFOLLOW; 300237651Sbschmidt return (p); 301237651Sbschmidt } 302237651Sbschmidt 303237651Sbschmidt /* Directory in pre-order. */ 304237651Sbschmidt if (p->fts_info == FTS_D) { 305237651Sbschmidt /* If skipped or crossed mount point, do post-order visit. */ 306237651Sbschmidt if (instr == FTS_SKIP || 307237651Sbschmidt ISSET(FTS_XDEV) && p->fts_dev != sp->fts_dev) { 308237651Sbschmidt if (p->fts_flags & FTS_SYMFOLLOW) 309237651Sbschmidt (void)close(p->fts_symfd); 310237651Sbschmidt if (sp->fts_child) { 311237651Sbschmidt fts_lfree(sp->fts_child); 312237651Sbschmidt sp->fts_child = NULL; 313237651Sbschmidt } 314237651Sbschmidt p->fts_info = FTS_DP; 315237651Sbschmidt return (p); 316237651Sbschmidt } 317237651Sbschmidt 318237651Sbschmidt /* Rebuild if only read the names and now traversing. */ 319237651Sbschmidt if (sp->fts_child && sp->fts_options & FTS_NAMEONLY) { 320237651Sbschmidt sp->fts_options &= ~FTS_NAMEONLY; 321237651Sbschmidt fts_lfree(sp->fts_child); 322237651Sbschmidt sp->fts_child = NULL; 323237651Sbschmidt } 324237651Sbschmidt 325237651Sbschmidt /* 326237651Sbschmidt * Cd to the subdirectory. 327237651Sbschmidt * 328237651Sbschmidt * If have already read and now fail to chdir, whack the list 329237651Sbschmidt * to make the names come out right, and set the parent errno 330237651Sbschmidt * so the application will eventually get an error condition. 331237651Sbschmidt * Set the FTS_DONTCHDIR flag so that when we logically change 332237651Sbschmidt * directories back to the parent we don't do a chdir. 333237651Sbschmidt * 334237651Sbschmidt * If haven't read do so. If the read fails, fts_build sets 335237651Sbschmidt * FTS_STOP or the fts_info field of the node. 336237651Sbschmidt */ 337237651Sbschmidt if (sp->fts_child) { 338237651Sbschmidt if (CHDIR(sp, p->fts_accpath)) { 339237651Sbschmidt p->fts_errno = errno; 340237651Sbschmidt p->fts_flags |= FTS_DONTCHDIR; 341237651Sbschmidt for (p = sp->fts_child; p; p = p->fts_link) 342237651Sbschmidt p->fts_accpath = 343237651Sbschmidt p->fts_parent->fts_accpath; 344237651Sbschmidt } 345237651Sbschmidt } else if ((sp->fts_child = fts_build(sp, BREAD)) == NULL) { 346237651Sbschmidt if (ISSET(FTS_STOP)) 347237651Sbschmidt return (NULL); 348237651Sbschmidt return (p); 349237651Sbschmidt } 350237651Sbschmidt p = sp->fts_child; 351237651Sbschmidt sp->fts_child = NULL; 352237651Sbschmidt goto name; 353237651Sbschmidt } 354237651Sbschmidt 355237651Sbschmidt /* Move to the next node on this level. */ 356237651Sbschmidtnext: tmp = p; 357237651Sbschmidt if (p = p->fts_link) { 358237651Sbschmidt free(tmp); 359237651Sbschmidt 360237651Sbschmidt /* 361237651Sbschmidt * If reached the top, return to the original directory, and 362237651Sbschmidt * load the paths for the next root. 363237651Sbschmidt */ 364237651Sbschmidt if (p->fts_level == FTS_ROOTLEVEL) { 365237651Sbschmidt if (!ISSET(FTS_NOCHDIR) && FCHDIR(sp, sp->fts_rfd)) { 366237651Sbschmidt SET(FTS_STOP); 367237651Sbschmidt return (NULL); 368237651Sbschmidt } 369237651Sbschmidt fts_load(sp, p); 370237651Sbschmidt return (sp->fts_cur = p); 371237651Sbschmidt } 372237651Sbschmidt 373237651Sbschmidt /* 374237651Sbschmidt * User may have called fts_set on the node. If skipped, 375237651Sbschmidt * ignore. If followed, get a file descriptor so we can 376237651Sbschmidt * get back if necessary. 377237651Sbschmidt */ 378237651Sbschmidt if (p->fts_instr == FTS_SKIP) 379237651Sbschmidt goto next; 380237651Sbschmidt if (p->fts_instr == FTS_FOLLOW) { 381237651Sbschmidt p->fts_info = fts_stat(sp, p, 1); 382237651Sbschmidt if (p->fts_info == FTS_D && !ISSET(FTS_NOCHDIR)) 383237651Sbschmidt if ((p->fts_symfd = 384237651Sbschmidt open(".", O_RDONLY, 0)) < 0) { 385237651Sbschmidt p->fts_errno = errno; 386237651Sbschmidt p->fts_info = FTS_ERR; 387237651Sbschmidt } else 388237651Sbschmidt p->fts_flags |= FTS_SYMFOLLOW; 389237651Sbschmidt p->fts_instr = FTS_NOINSTR; 390237651Sbschmidt } 391237651Sbschmidt 392237651Sbschmidtname: t = sp->fts_path + NAPPEND(p->fts_parent); 393237651Sbschmidt *t++ = '/'; 394237651Sbschmidt memmove(t, p->fts_name, p->fts_namelen + 1); 395237651Sbschmidt return (sp->fts_cur = p); 396237651Sbschmidt } 397237651Sbschmidt 398237651Sbschmidt /* Move up to the parent node. */ 399237651Sbschmidt p = tmp->fts_parent; 400237651Sbschmidt free(tmp); 401237651Sbschmidt 402237651Sbschmidt if (p->fts_level == FTS_ROOTPARENTLEVEL) { 403237651Sbschmidt /* 404237651Sbschmidt * Done; free everything up and set errno to 0 so the user 405237651Sbschmidt * can distinguish between error and EOF. 406237651Sbschmidt */ 407237651Sbschmidt free(p); 408237651Sbschmidt errno = 0; 409237651Sbschmidt return (sp->fts_cur = NULL); 410237651Sbschmidt } 411237651Sbschmidt 412237651Sbschmidt /* Nul terminate the pathname. */ 413237651Sbschmidt sp->fts_path[p->fts_pathlen] = '\0'; 414237651Sbschmidt 415237651Sbschmidt /* 416237651Sbschmidt * Return to the parent directory. If at a root node or came through 417237651Sbschmidt * a symlink, go back through the file descriptor. Otherwise, cd up 418237651Sbschmidt * one directory. 419237651Sbschmidt */ 420237651Sbschmidt if (p->fts_level == FTS_ROOTLEVEL) { 421237651Sbschmidt if (!ISSET(FTS_NOCHDIR) && FCHDIR(sp, sp->fts_rfd)) { 422237651Sbschmidt SET(FTS_STOP); 423237651Sbschmidt return (NULL); 424237651Sbschmidt } 425237651Sbschmidt } else if (p->fts_flags & FTS_SYMFOLLOW) { 426237651Sbschmidt if (FCHDIR(sp, p->fts_symfd)) { 427237651Sbschmidt saved_errno = errno; 428237651Sbschmidt (void)close(p->fts_symfd); 429237651Sbschmidt errno = saved_errno; 430237651Sbschmidt SET(FTS_STOP); 431237651Sbschmidt return (NULL); 432237651Sbschmidt } 433237651Sbschmidt (void)close(p->fts_symfd); 434237651Sbschmidt } else if (!(p->fts_flags & FTS_DONTCHDIR)) { 435237651Sbschmidt if (CHDIR(sp, "..")) { 436237651Sbschmidt SET(FTS_STOP); 437237651Sbschmidt return (NULL); 438237651Sbschmidt } 439237651Sbschmidt } 440237651Sbschmidt p->fts_info = p->fts_errno ? FTS_ERR : FTS_DP; 441237651Sbschmidt return (sp->fts_cur = p); 442237651Sbschmidt} 443237651Sbschmidt 444237651Sbschmidt/* 445237651Sbschmidt * Fts_set takes the stream as an argument although it's not used in this 446237651Sbschmidt * implementation; it would be necessary if anyone wanted to add global 447237651Sbschmidt * semantics to fts using fts_set. An error return is allowed for similar 448237651Sbschmidt * reasons. 449237651Sbschmidt */ 450237651Sbschmidt/* ARGSUSED */ 451237651Sbschmidtint 452237651Sbschmidtfts_set(sp, p, instr) 453237651Sbschmidt FTS *sp; 454237651Sbschmidt FTSENT *p; 455237651Sbschmidt int instr; 456237651Sbschmidt{ 457237651Sbschmidt if (instr && instr != FTS_AGAIN && instr != FTS_FOLLOW && 458237651Sbschmidt instr != FTS_NOINSTR && instr != FTS_SKIP) { 459237651Sbschmidt errno = EINVAL; 460237651Sbschmidt return (1); 461237651Sbschmidt } 462237651Sbschmidt p->fts_instr = instr; 463237651Sbschmidt return (0); 464237651Sbschmidt} 465237651Sbschmidt 466237651SbschmidtFTSENT * 467237651Sbschmidtfts_children(sp, instr) 468237651Sbschmidt register FTS *sp; 469237651Sbschmidt int instr; 470237651Sbschmidt{ 471237651Sbschmidt register FTSENT *p; 472237651Sbschmidt int fd; 473237651Sbschmidt 474237651Sbschmidt if (instr && instr != FTS_NAMEONLY) { 475237651Sbschmidt errno = EINVAL; 476237651Sbschmidt return (NULL); 477237651Sbschmidt } 478237651Sbschmidt 479237651Sbschmidt /* Set current node pointer. */ 480237651Sbschmidt p = sp->fts_cur; 481237651Sbschmidt 482237651Sbschmidt /* 483237651Sbschmidt * Errno set to 0 so user can distinguish empty directory from 484237651Sbschmidt * an error. 485237651Sbschmidt */ 486237651Sbschmidt errno = 0; 487237651Sbschmidt 488237651Sbschmidt /* Fatal errors stop here. */ 489237651Sbschmidt if (ISSET(FTS_STOP)) 490237651Sbschmidt return (NULL); 491237651Sbschmidt 492237651Sbschmidt /* Return logical hierarchy of user's arguments. */ 493237651Sbschmidt if (p->fts_info == FTS_INIT) 494237651Sbschmidt return (p->fts_link); 495237651Sbschmidt 496237651Sbschmidt /* 497237651Sbschmidt * If not a directory being visited in pre-order, stop here. Could 498237651Sbschmidt * allow FTS_DNR, assuming the user has fixed the problem, but the 499237651Sbschmidt * same effect is available with FTS_AGAIN. 500237651Sbschmidt */ 501237651Sbschmidt if (p->fts_info != FTS_D /* && p->fts_info != FTS_DNR */) 502237651Sbschmidt return (NULL); 503237651Sbschmidt 504237651Sbschmidt /* Free up any previous child list. */ 505237651Sbschmidt if (sp->fts_child) 506237651Sbschmidt fts_lfree(sp->fts_child); 507237651Sbschmidt 508237651Sbschmidt if (instr == FTS_NAMEONLY) { 509237651Sbschmidt sp->fts_options |= FTS_NAMEONLY; 510237651Sbschmidt instr = BNAMES; 511237651Sbschmidt } else 512237651Sbschmidt instr = BCHILD; 513237651Sbschmidt 514237651Sbschmidt /* 515237651Sbschmidt * If using chdir on a relative path and called BEFORE fts_read does 516237651Sbschmidt * its chdir to the root of a traversal, we can lose -- we need to 517237651Sbschmidt * chdir into the subdirectory, and we don't know where the current 518237651Sbschmidt * directory is, so we can't get back so that the upcoming chdir by 519237651Sbschmidt * fts_read will work. 520237651Sbschmidt */ 521237651Sbschmidt if (p->fts_level != FTS_ROOTLEVEL || p->fts_accpath[0] == '/' || 522237651Sbschmidt ISSET(FTS_NOCHDIR)) 523237651Sbschmidt return (sp->fts_child = fts_build(sp, instr)); 524237651Sbschmidt 525237651Sbschmidt if ((fd = open(".", O_RDONLY, 0)) < 0) 526237651Sbschmidt return (NULL); 527237651Sbschmidt sp->fts_child = fts_build(sp, instr); 528237651Sbschmidt if (fchdir(fd)) 529237651Sbschmidt return (NULL); 530237651Sbschmidt (void)close(fd); 531237651Sbschmidt return (sp->fts_child); 532237651Sbschmidt} 533237651Sbschmidt 534237651Sbschmidt/* 535237651Sbschmidt * This is the tricky part -- do not casually change *anything* in here. The 536237651Sbschmidt * idea is to build the linked list of entries that are used by fts_children 537237651Sbschmidt * and fts_read. There are lots of special cases. 538237651Sbschmidt * 539237651Sbschmidt * The real slowdown in walking the tree is the stat calls. If FTS_NOSTAT is 540237651Sbschmidt * set and it's a physical walk (so that symbolic links can't be directories), 541237651Sbschmidt * we can do things quickly. First, if it's a 4.4BSD file system, the type 542237651Sbschmidt * of the file is in the directory entry. Otherwise, we assume that the number 543237651Sbschmidt * of subdirectories in a node is equal to the number of links to the parent. 544237651Sbschmidt * The former skips all stat calls. The latter skips stat calls in any leaf 545237651Sbschmidt * directories and for any files after the subdirectories in the directory have 546237651Sbschmidt * been found, cutting the stat calls by about 2/3. 547237651Sbschmidt */ 548237651Sbschmidtstatic FTSENT * 549237651Sbschmidtfts_build(sp, type) 550237651Sbschmidt register FTS *sp; 551237651Sbschmidt int type; 552237651Sbschmidt{ 553237651Sbschmidt register struct dirent *dp; 554237651Sbschmidt register FTSENT *p, *head; 555237651Sbschmidt register int nitems; 556237651Sbschmidt FTSENT *cur, *tail; 557237651Sbschmidt DIR *dirp; 558237651Sbschmidt void *adjaddr; 559237651Sbschmidt int cderrno, descend, len, level, maxlen, nlinks, saved_errno; 560237651Sbschmidt char *cp; 561237651Sbschmidt 562237651Sbschmidt /* Set current node pointer. */ 563237651Sbschmidt cur = sp->fts_cur; 564237651Sbschmidt 565237651Sbschmidt /* 566237651Sbschmidt * Open the directory for reading. If this fails, we're done. 567237651Sbschmidt * If being called from fts_read, set the fts_info field. 568237651Sbschmidt */ 569237651Sbschmidt if ((dirp = opendir(cur->fts_accpath)) == NULL) { 570237651Sbschmidt if (type == BREAD) { 571237651Sbschmidt cur->fts_info = FTS_DNR; 572237651Sbschmidt cur->fts_errno = errno; 573237651Sbschmidt } 574237651Sbschmidt return (NULL); 575237651Sbschmidt } 576237651Sbschmidt 577237651Sbschmidt /* 578237651Sbschmidt * Nlinks is the number of possible entries of type directory in the 579237651Sbschmidt * directory if we're cheating on stat calls, 0 if we're not doing 580237651Sbschmidt * any stat calls at all, -1 if we're doing stats on everything. 581237651Sbschmidt */ 582237651Sbschmidt if (type == BNAMES) 583237651Sbschmidt nlinks = 0; 584237651Sbschmidt else if (ISSET(FTS_NOSTAT) && ISSET(FTS_PHYSICAL)) 585237651Sbschmidt nlinks = cur->fts_nlink - (ISSET(FTS_SEEDOT) ? 0 : 2); 586237651Sbschmidt else 587237651Sbschmidt nlinks = -1; 588237651Sbschmidt 589237651Sbschmidt#ifdef notdef 590237651Sbschmidt (void)printf("nlinks == %d (cur: %d)\n", nlinks, cur->fts_nlink); 591237651Sbschmidt (void)printf("NOSTAT %d PHYSICAL %d SEEDOT %d\n", 592237651Sbschmidt ISSET(FTS_NOSTAT), ISSET(FTS_PHYSICAL), ISSET(FTS_SEEDOT)); 593237651Sbschmidt#endif 594237651Sbschmidt /* 595237651Sbschmidt * If we're going to need to stat anything or we want to descend 596237651Sbschmidt * and stay in the directory, chdir. If this fails we keep going, 597237651Sbschmidt * but set a flag so we don't chdir after the post-order visit. 598237651Sbschmidt * We won't be able to stat anything, but we can still return the 599237651Sbschmidt * names themselves. Note, that since fts_read won't be able to 600237651Sbschmidt * chdir into the directory, it will have to return different path 601237651Sbschmidt * names than before, i.e. "a/b" instead of "b". Since the node 602237651Sbschmidt * has already been visited in pre-order, have to wait until the 603237651Sbschmidt * post-order visit to return the error. There is a special case 604237651Sbschmidt * here, if there was nothing to stat then it's not an error to 605237651Sbschmidt * not be able to stat. This is all fairly nasty. If a program 606237651Sbschmidt * needed sorted entries or stat information, they had better be 607237651Sbschmidt * checking FTS_NS on the returned nodes. 608237651Sbschmidt */ 609237651Sbschmidt cderrno = 0; 610237651Sbschmidt if (nlinks || type == BREAD) 611237651Sbschmidt if (FCHDIR(sp, dirfd(dirp))) { 612237651Sbschmidt if (nlinks && type == BREAD) 613237651Sbschmidt cur->fts_errno = errno; 614237651Sbschmidt cur->fts_flags |= FTS_DONTCHDIR; 615237651Sbschmidt descend = 0; 616237651Sbschmidt cderrno = errno; 617237651Sbschmidt } else 618237651Sbschmidt descend = 1; 619237651Sbschmidt else 620237651Sbschmidt descend = 0; 621237651Sbschmidt 622237651Sbschmidt /* 623237651Sbschmidt * Figure out the max file name length that can be stored in the 624237651Sbschmidt * current path -- the inner loop allocates more path as necessary. 625237651Sbschmidt * We really wouldn't have to do the maxlen calculations here, we 626237651Sbschmidt * could do them in fts_read before returning the path, but it's a 627237651Sbschmidt * lot easier here since the length is part of the dirent structure. 628237651Sbschmidt * 629237651Sbschmidt * If not changing directories set a pointer so that can just append 630237651Sbschmidt * each new name into the path. 631237651Sbschmidt */ 632237651Sbschmidt maxlen = sp->fts_pathlen - cur->fts_pathlen - 1; 633237651Sbschmidt len = NAPPEND(cur); 634237651Sbschmidt if (ISSET(FTS_NOCHDIR)) { 635237651Sbschmidt cp = sp->fts_path + len; 636237651Sbschmidt *cp++ = '/'; 637237651Sbschmidt } 638237651Sbschmidt 639237651Sbschmidt level = cur->fts_level + 1; 640237651Sbschmidt 641237651Sbschmidt /* Read the directory, attaching each entry to the `link' pointer. */ 642237651Sbschmidt adjaddr = NULL; 643237651Sbschmidt for (head = tail = NULL, nitems = 0; dp = readdir(dirp);) { 644237651Sbschmidt if (!ISSET(FTS_SEEDOT) && ISDOT(dp->d_name)) 645237651Sbschmidt continue; 646237651Sbschmidt 647237651Sbschmidt if ((p = fts_alloc(sp, dp->d_name, (int)dp->d_namlen)) == NULL) 648237651Sbschmidt goto mem1; 649237651Sbschmidt if (dp->d_namlen > maxlen) { 650237651Sbschmidt if (fts_palloc(sp, (size_t)dp->d_namlen)) { 651237651Sbschmidt /* 652237651Sbschmidt * No more memory for path or structures. Save 653237651Sbschmidt * errno, free up the current structure and the 654237651Sbschmidt * structures already allocated. 655237651Sbschmidt */ 656237651Sbschmidtmem1: saved_errno = errno; 657237651Sbschmidt if (p) 658237651Sbschmidt free(p); 659237651Sbschmidt fts_lfree(head); 660237651Sbschmidt (void)closedir(dirp); 661237651Sbschmidt errno = saved_errno; 662237651Sbschmidt cur->fts_info = FTS_ERR; 663237651Sbschmidt SET(FTS_STOP); 664237651Sbschmidt return (NULL); 665237651Sbschmidt } 666237651Sbschmidt adjaddr = sp->fts_path; 667237651Sbschmidt maxlen = sp->fts_pathlen - sp->fts_cur->fts_pathlen - 1; 668237651Sbschmidt } 669237651Sbschmidt 670237651Sbschmidt p->fts_pathlen = len + dp->d_namlen + 1; 671237651Sbschmidt p->fts_parent = sp->fts_cur; 672237651Sbschmidt p->fts_level = level; 673237651Sbschmidt 674237651Sbschmidt if (cderrno) { 675237651Sbschmidt if (nlinks) { 676237651Sbschmidt p->fts_info = FTS_NS; 677237651Sbschmidt p->fts_errno = cderrno; 678237651Sbschmidt } else 679237651Sbschmidt p->fts_info = FTS_NSOK; 680237651Sbschmidt p->fts_accpath = cur->fts_accpath; 681237651Sbschmidt } else if (nlinks == 0 682237651Sbschmidt#ifdef DT_DIR 683237651Sbschmidt || nlinks > 0 && 684237651Sbschmidt dp->d_type != DT_DIR && dp->d_type != DT_UNKNOWN 685237651Sbschmidt#endif 686237651Sbschmidt ) { 687237651Sbschmidt p->fts_accpath = 688237651Sbschmidt ISSET(FTS_NOCHDIR) ? p->fts_path : p->fts_name; 689237651Sbschmidt p->fts_info = FTS_NSOK; 690237651Sbschmidt } else { 691237651Sbschmidt /* Build a file name for fts_stat to stat. */ 692237651Sbschmidt if (ISSET(FTS_NOCHDIR)) { 693237651Sbschmidt p->fts_accpath = p->fts_path; 694237651Sbschmidt memmove(cp, p->fts_name, p->fts_namelen + 1); 695237651Sbschmidt } else 696237651Sbschmidt p->fts_accpath = p->fts_name; 697237651Sbschmidt /* Stat it. */ 698237651Sbschmidt p->fts_info = fts_stat(sp, p, 0); 699237651Sbschmidt 700237651Sbschmidt /* Decrement link count if applicable. */ 701237651Sbschmidt if (nlinks > 0 && (p->fts_info == FTS_D || 702237651Sbschmidt p->fts_info == FTS_DC || p->fts_info == FTS_DOT)) 703237651Sbschmidt --nlinks; 704237651Sbschmidt } 705237651Sbschmidt 706237651Sbschmidt /* We walk in directory order so "ls -f" doesn't get upset. */ 707237651Sbschmidt p->fts_link = NULL; 708237651Sbschmidt if (head == NULL) 709237651Sbschmidt head = tail = p; 710237651Sbschmidt else { 711237651Sbschmidt tail->fts_link = p; 712237651Sbschmidt tail = p; 713237651Sbschmidt } 714237651Sbschmidt ++nitems; 715237651Sbschmidt } 716237651Sbschmidt (void)closedir(dirp); 717237651Sbschmidt 718237651Sbschmidt /* 719237651Sbschmidt * If had to realloc the path, adjust the addresses for the rest 720237651Sbschmidt * of the tree. 721237651Sbschmidt */ 722237651Sbschmidt if (adjaddr) 723237651Sbschmidt fts_padjust(sp, adjaddr); 724237651Sbschmidt 725237651Sbschmidt /* 726237651Sbschmidt * If not changing directories, reset the path back to original 727237651Sbschmidt * state. 728237651Sbschmidt */ 729237651Sbschmidt if (ISSET(FTS_NOCHDIR)) { 730237651Sbschmidt if (cp - 1 > sp->fts_path) 731237651Sbschmidt --cp; 732237651Sbschmidt *cp = '\0'; 733237651Sbschmidt } 734237651Sbschmidt 735237651Sbschmidt /* 736237651Sbschmidt * If descended after called from fts_children or after called from 737237651Sbschmidt * fts_read and nothing found, get back. At the root level we use 738237651Sbschmidt * the saved fd; if one of fts_open()'s arguments is a relative path 739237651Sbschmidt * to an empty directory, we wind up here with no other way back. If 740237651Sbschmidt * can't get back, we're done. 741237651Sbschmidt */ 742237651Sbschmidt if (descend && (type == BCHILD || !nitems) && 743237651Sbschmidt (cur->fts_level == FTS_ROOTLEVEL ? 744237651Sbschmidt FCHDIR(sp, sp->fts_rfd) : CHDIR(sp, ".."))) { 745237651Sbschmidt cur->fts_info = FTS_ERR; 746237651Sbschmidt SET(FTS_STOP); 747237651Sbschmidt return (NULL); 748237651Sbschmidt } 749237651Sbschmidt 750237651Sbschmidt /* If didn't find anything, return NULL. */ 751237651Sbschmidt if (!nitems) { 752237651Sbschmidt if (type == BREAD) 753237651Sbschmidt cur->fts_info = FTS_DP; 754237651Sbschmidt return (NULL); 755237651Sbschmidt } 756237651Sbschmidt 757237651Sbschmidt /* Sort the entries. */ 758237651Sbschmidt if (sp->fts_compar && nitems > 1) 759237651Sbschmidt head = fts_sort(sp, head, nitems); 760237651Sbschmidt return (head); 761237651Sbschmidt} 762237651Sbschmidt 763237651Sbschmidtstatic u_short 764237651Sbschmidtfts_stat(sp, p, follow) 765237651Sbschmidt FTS *sp; 766237651Sbschmidt register FTSENT *p; 767237651Sbschmidt int follow; 768237651Sbschmidt{ 769237651Sbschmidt register FTSENT *t; 770237651Sbschmidt register dev_t dev; 771237651Sbschmidt register ino_t ino; 772237651Sbschmidt struct stat *sbp, sb; 773237651Sbschmidt int saved_errno; 774237651Sbschmidt 775237651Sbschmidt /* If user needs stat info, stat buffer already allocated. */ 776237651Sbschmidt sbp = ISSET(FTS_NOSTAT) ? &sb : p->fts_statp; 777237651Sbschmidt 778237651Sbschmidt /* 779237651Sbschmidt * If doing a logical walk, or application requested FTS_FOLLOW, do 780237651Sbschmidt * a stat(2). If that fails, check for a non-existent symlink. If 781237651Sbschmidt * fail, set the errno from the stat call. 782237651Sbschmidt */ 783237651Sbschmidt if (ISSET(FTS_LOGICAL) || follow) { 784237651Sbschmidt if (stat(p->fts_accpath, sbp)) { 785237651Sbschmidt saved_errno = errno; 786237651Sbschmidt if (!lstat(p->fts_accpath, sbp)) { 787237651Sbschmidt errno = 0; 788237651Sbschmidt return (FTS_SLNONE); 789237651Sbschmidt } 790237651Sbschmidt p->fts_errno = saved_errno; 791237651Sbschmidt goto err; 792237651Sbschmidt } 793237651Sbschmidt } else if (lstat(p->fts_accpath, sbp)) { 794237651Sbschmidt p->fts_errno = errno; 795237651Sbschmidterr: memset(sbp, 0, sizeof(struct stat)); 796237651Sbschmidt return (FTS_NS); 797237651Sbschmidt } 798237651Sbschmidt 799237651Sbschmidt if (S_ISDIR(sbp->st_mode)) { 800237651Sbschmidt /* 801237651Sbschmidt * Set the device/inode. Used to find cycles and check for 802237651Sbschmidt * crossing mount points. Also remember the link count, used 803237651Sbschmidt * in fts_build to limit the number of stat calls. It is 804237651Sbschmidt * understood that these fields are only referenced if fts_info 805237651Sbschmidt * is set to FTS_D. 806237651Sbschmidt */ 807237651Sbschmidt dev = p->fts_dev = sbp->st_dev; 808237651Sbschmidt ino = p->fts_ino = sbp->st_ino; 809237651Sbschmidt p->fts_nlink = sbp->st_nlink; 810237651Sbschmidt 811237651Sbschmidt if (ISDOT(p->fts_name)) 812237651Sbschmidt return (FTS_DOT); 813237651Sbschmidt 814237651Sbschmidt /* 815237651Sbschmidt * Cycle detection is done by brute force when the directory 816237651Sbschmidt * is first encountered. If the tree gets deep enough or the 817237651Sbschmidt * number of symbolic links to directories is high enough, 818237651Sbschmidt * something faster might be worthwhile. 819237651Sbschmidt */ 820237651Sbschmidt for (t = p->fts_parent; 821237651Sbschmidt t->fts_level >= FTS_ROOTLEVEL; t = t->fts_parent) 822237651Sbschmidt if (ino == t->fts_ino && dev == t->fts_dev) { 823237651Sbschmidt p->fts_cycle = t; 824237651Sbschmidt return (FTS_DC); 825237651Sbschmidt } 826237651Sbschmidt return (FTS_D); 827237651Sbschmidt } 828237651Sbschmidt if (S_ISLNK(sbp->st_mode)) 829237651Sbschmidt return (FTS_SL); 830237651Sbschmidt if (S_ISREG(sbp->st_mode)) 831237651Sbschmidt return (FTS_F); 832237651Sbschmidt return (FTS_DEFAULT); 833237651Sbschmidt} 834237651Sbschmidt 835237651Sbschmidtstatic FTSENT * 836237651Sbschmidtfts_sort(sp, head, nitems) 837237651Sbschmidt FTS *sp; 838237651Sbschmidt FTSENT *head; 839237651Sbschmidt register int nitems; 840237651Sbschmidt{ 841237651Sbschmidt register FTSENT **ap, *p; 842237651Sbschmidt 843237651Sbschmidt /* 844237651Sbschmidt * Construct an array of pointers to the structures and call qsort(3). 845237651Sbschmidt * Reassemble the array in the order returned by qsort. If unable to 846237651Sbschmidt * sort for memory reasons, return the directory entries in their 847237651Sbschmidt * current order. Allocate enough space for the current needs plus 848237651Sbschmidt * 40 so don't realloc one entry at a time. 849237651Sbschmidt */ 850237651Sbschmidt if (nitems > sp->fts_nitems) { 851237651Sbschmidt sp->fts_nitems = nitems + 40; 852237651Sbschmidt if ((sp->fts_array = realloc(sp->fts_array, 853237651Sbschmidt (size_t)(sp->fts_nitems * sizeof(FTSENT *)))) == NULL) { 854237651Sbschmidt sp->fts_nitems = 0; 855237651Sbschmidt return (head); 856237651Sbschmidt } 857237651Sbschmidt } 858237651Sbschmidt for (ap = sp->fts_array, p = head; p; p = p->fts_link) 859237651Sbschmidt *ap++ = p; 860237651Sbschmidt qsort((void *)sp->fts_array, nitems, sizeof(FTSENT *), sp->fts_compar); 861237651Sbschmidt for (head = *(ap = sp->fts_array); --nitems; ++ap) 862237651Sbschmidt ap[0]->fts_link = ap[1]; 863237651Sbschmidt ap[0]->fts_link = NULL; 864237651Sbschmidt return (head); 865237651Sbschmidt} 866237651Sbschmidt 867237651Sbschmidtstatic FTSENT * 868237651Sbschmidtfts_alloc(sp, name, namelen) 869237651Sbschmidt FTS *sp; 870237651Sbschmidt char *name; 871237651Sbschmidt register int namelen; 872237651Sbschmidt{ 873237651Sbschmidt register FTSENT *p; 874237651Sbschmidt size_t len; 875237651Sbschmidt 876237651Sbschmidt /* 877237651Sbschmidt * The file name is a variable length array and no stat structure is 878237651Sbschmidt * necessary if the user has set the nostat bit. Allocate the FTSENT 879237651Sbschmidt * structure, the file name and the stat structure in one chunk, but 880237651Sbschmidt * be careful that the stat structure is reasonably aligned. Since the 881237651Sbschmidt * fts_name field is declared to be of size 1, the fts_name pointer is 882237651Sbschmidt * namelen + 2 before the first possible address of the stat structure. 883237651Sbschmidt */ 884237651Sbschmidt len = sizeof(FTSENT) + namelen; 885237651Sbschmidt if (!ISSET(FTS_NOSTAT)) 886237651Sbschmidt len += sizeof(struct stat) + ALIGNBYTES; 887237651Sbschmidt if ((p = malloc(len)) == NULL) 888237651Sbschmidt return (NULL); 889237651Sbschmidt 890237651Sbschmidt /* Copy the name plus the trailing NULL. */ 891237651Sbschmidt memmove(p->fts_name, name, namelen + 1); 892237651Sbschmidt 893237651Sbschmidt if (!ISSET(FTS_NOSTAT)) 894237651Sbschmidt p->fts_statp = (struct stat *)ALIGN(p->fts_name + namelen + 2); 895237651Sbschmidt p->fts_namelen = namelen; 896237651Sbschmidt p->fts_path = sp->fts_path; 897237651Sbschmidt p->fts_errno = 0; 898237651Sbschmidt p->fts_flags = 0; 899237651Sbschmidt p->fts_instr = FTS_NOINSTR; 900237651Sbschmidt p->fts_number = 0; 901237651Sbschmidt p->fts_pointer = NULL; 902237651Sbschmidt return (p); 903237651Sbschmidt} 904237651Sbschmidt 905237651Sbschmidtstatic void 906237651Sbschmidtfts_lfree(head) 907237651Sbschmidt register FTSENT *head; 908237651Sbschmidt{ 909237651Sbschmidt register FTSENT *p; 910237651Sbschmidt 911237651Sbschmidt /* Free a linked list of structures. */ 912237651Sbschmidt while (p = head) { 913237651Sbschmidt head = head->fts_link; 914237651Sbschmidt free(p); 915237651Sbschmidt } 916237651Sbschmidt} 917237651Sbschmidt 918237651Sbschmidt/* 919237651Sbschmidt * Allow essentially unlimited paths; find, rm, ls should all work on any tree. 920237651Sbschmidt * Most systems will allow creation of paths much longer than MAXPATHLEN, even 921237651Sbschmidt * though the kernel won't resolve them. Add the size (not just what's needed) 922237651Sbschmidt * plus 256 bytes so don't realloc the path 2 bytes at a time. 923237651Sbschmidt */ 924237651Sbschmidtstatic int 925237651Sbschmidtfts_palloc(sp, more) 926237651Sbschmidt FTS *sp; 927237651Sbschmidt size_t more; 928237651Sbschmidt{ 929237651Sbschmidt sp->fts_pathlen += more + 256; 930237651Sbschmidt sp->fts_path = realloc(sp->fts_path, (size_t)sp->fts_pathlen); 931237651Sbschmidt return (sp->fts_path == NULL); 932237651Sbschmidt} 933237651Sbschmidt 934237651Sbschmidt/* 935237651Sbschmidt * When the path is realloc'd, have to fix all of the pointers in structures 936237651Sbschmidt * already returned. 937237651Sbschmidt */ 938237651Sbschmidtstatic void 939237651Sbschmidtfts_padjust(sp, addr) 940237651Sbschmidt FTS *sp; 941237651Sbschmidt void *addr; 942237651Sbschmidt{ 943237651Sbschmidt FTSENT *p; 944237651Sbschmidt 945237651Sbschmidt#define ADJUST(p) { \ 946237651Sbschmidt (p)->fts_accpath = \ 947237651Sbschmidt (char *)addr + ((p)->fts_accpath - (p)->fts_path); \ 948237651Sbschmidt (p)->fts_path = addr; \ 949237651Sbschmidt} 950237651Sbschmidt /* Adjust the current set of children. */ 951237651Sbschmidt for (p = sp->fts_child; p; p = p->fts_link) 952237651Sbschmidt ADJUST(p); 953237651Sbschmidt 954237651Sbschmidt /* Adjust the rest of the tree. */ 955237651Sbschmidt for (p = sp->fts_cur; p->fts_level >= FTS_ROOTLEVEL;) { 956237651Sbschmidt ADJUST(p); 957237651Sbschmidt p = p->fts_link ? p->fts_link : p->fts_parent; 958237651Sbschmidt } 959237651Sbschmidt} 960237651Sbschmidt 961237651Sbschmidtstatic size_t 962237651Sbschmidtfts_maxarglen(argv) 963237651Sbschmidt char * const *argv; 964237651Sbschmidt{ 965237651Sbschmidt size_t len, max; 966237651Sbschmidt 967237651Sbschmidt for (max = 0; *argv; ++argv) 968237651Sbschmidt if ((len = strlen(*argv)) > max) 969237651Sbschmidt max = len; 970237651Sbschmidt return (max); 971237651Sbschmidt} 972237651Sbschmidt