142273Sbostic /*- 242273Sbostic * Copyright (c) 1990 The Regents of the University of California. 339800Sbostic * All rights reserved. 439800Sbostic * 542273Sbostic * %sccs.include.redist.c% 639800Sbostic */ 739800Sbostic 839800Sbostic #if defined(LIBC_SCCS) && !defined(lint) 9*58532Sbostic static char sccsid[] = "@(#)fts.c 5.41 (Berkeley) 03/07/93"; 1039800Sbostic #endif /* LIBC_SCCS and not lint */ 1139800Sbostic 1239800Sbostic #include <sys/param.h> 1339800Sbostic #include <sys/stat.h> 1442299Sbostic #include <fcntl.h> 1539800Sbostic #include <dirent.h> 1639800Sbostic #include <errno.h> 1752209Sbostic #include <fts.h> 1847194Sbostic #include <stdlib.h> 1942273Sbostic #include <string.h> 2047155Sbostic #include <unistd.h> 2139800Sbostic 2252061Sbostic static FTSENT *fts_alloc __P((FTS *, char *, int)); 2352061Sbostic static FTSENT *fts_build __P((FTS *, int)); 2452061Sbostic static void fts_lfree __P((FTSENT *)); 2552061Sbostic static void fts_load __P((FTS *, FTSENT *)); 2655557Sbostic static size_t fts_maxarglen __P((char * const *)); 2752209Sbostic static void fts_padjust __P((FTS *, void *)); 2853801Sbostic static int fts_palloc __P((FTS *, size_t)); 2952061Sbostic static FTSENT *fts_sort __P((FTS *, FTSENT *, int)); 3052061Sbostic static u_short fts_stat __P((FTS *, FTSENT *, int)); 3139800Sbostic 3252209Sbostic #define ISDOT(a) (a[0] == '.' && (!a[1] || a[1] == '.' && !a[2])) 3352209Sbostic 3445600Sbostic #define ISSET(opt) (sp->fts_options & opt) 3545600Sbostic #define SET(opt) (sp->fts_options |= opt) 3639800Sbostic 3745600Sbostic #define CHDIR(sp, path) (!ISSET(FTS_NOCHDIR) && chdir(path)) 3845600Sbostic #define FCHDIR(sp, fd) (!ISSET(FTS_NOCHDIR) && fchdir(fd)) 3939800Sbostic 4042299Sbostic /* fts_build flags */ 4152770Sbostic #define BCHILD 1 /* fts_children */ 4252770Sbostic #define BNAMES 2 /* fts_children, names only */ 4352770Sbostic #define BREAD 3 /* fts_read */ 4442299Sbostic 4539800Sbostic FTS * 4645600Sbostic fts_open(argv, options, compar) 4747155Sbostic char * const *argv; 4839800Sbostic register int options; 4947194Sbostic int (*compar)(); 5039800Sbostic { 5139800Sbostic register FTS *sp; 5239800Sbostic register FTSENT *p, *root; 5355557Sbostic register int nitems; 5439800Sbostic FTSENT *parent, *tmp; 5547194Sbostic int len; 5639800Sbostic 5752770Sbostic /* Options check. */ 5852770Sbostic if (options & ~FTS_OPTIONMASK) { 5952770Sbostic errno = EINVAL; 6052770Sbostic return (NULL); 6152770Sbostic } 6252770Sbostic 6345600Sbostic /* Allocate/initialize the stream */ 6452150Sbostic if ((sp = malloc((u_int)sizeof(FTS))) == NULL) 6552150Sbostic return (NULL); 6639800Sbostic bzero(sp, sizeof(FTS)); 6742273Sbostic sp->fts_compar = compar; 6842273Sbostic sp->fts_options = options; 6939800Sbostic 7045600Sbostic /* Logical walks turn on NOCHDIR; symbolic links are too hard. */ 7145600Sbostic if (ISSET(FTS_LOGICAL)) 7245600Sbostic SET(FTS_NOCHDIR); 7345600Sbostic 7453801Sbostic /* 7555557Sbostic * Start out with 1K of path space, and enough, in any case, 7655557Sbostic * to hold the user's paths. 7753801Sbostic */ 7855557Sbostic if (fts_palloc(sp, MAX(fts_maxarglen(argv), MAXPATHLEN))) 7953801Sbostic goto mem1; 8053801Sbostic 8145600Sbostic /* Allocate/initialize root's parent. */ 8252155Sbostic if ((parent = fts_alloc(sp, "", 0)) == NULL) 8353801Sbostic goto mem2; 8447212Sbostic parent->fts_level = FTS_ROOTPARENTLEVEL; 8539800Sbostic 8645600Sbostic /* Allocate/initialize root(s). */ 8755557Sbostic for (root = NULL, nitems = 0; *argv; ++argv, ++nitems) { 8852160Sbostic /* Don't allow zero-length paths. */ 8952209Sbostic if ((len = strlen(*argv)) == 0) { 9047194Sbostic errno = ENOENT; 9153801Sbostic goto mem3; 9247194Sbostic } 9352209Sbostic 9447194Sbostic p = fts_alloc(sp, *argv, len); 9549519Sbostic p->fts_level = FTS_ROOTLEVEL; 9649519Sbostic p->fts_parent = parent; 9752218Sbostic p->fts_accpath = p->fts_name; 9852344Sbostic p->fts_info = fts_stat(sp, p, ISSET(FTS_COMFOLLOW)); 9952209Sbostic 10052209Sbostic /* Command-line "." and ".." are real directories. */ 10152209Sbostic if (p->fts_info == FTS_DOT) 10252209Sbostic p->fts_info = FTS_D; 10352209Sbostic 10443070Sbostic /* 10545600Sbostic * If comparison routine supplied, traverse in sorted 10643070Sbostic * order; otherwise traverse in the order specified. 10743070Sbostic */ 10843070Sbostic if (compar) { 10943070Sbostic p->fts_link = root; 11043070Sbostic root = p; 11143070Sbostic } else { 11243070Sbostic p->fts_link = NULL; 11352155Sbostic if (root == NULL) 11443070Sbostic tmp = root = p; 11543070Sbostic else { 11643070Sbostic tmp->fts_link = p; 11743070Sbostic tmp = p; 11839800Sbostic } 11939800Sbostic } 12039800Sbostic } 12143070Sbostic if (compar && nitems > 1) 12245600Sbostic root = fts_sort(sp, root, nitems); 12339800Sbostic 12442281Sbostic /* 12545600Sbostic * Allocate a dummy pointer and make fts_read think that we've just 12652160Sbostic * finished the node before the root(s); set p->fts_info to FTS_INIT 12742281Sbostic * so that everything about the "current" node is ignored. 12842281Sbostic */ 12952155Sbostic if ((sp->fts_cur = fts_alloc(sp, "", 0)) == NULL) 13053801Sbostic goto mem3; 13142281Sbostic sp->fts_cur->fts_link = root; 13252160Sbostic sp->fts_cur->fts_info = FTS_INIT; 13342281Sbostic 13452160Sbostic /* 13545600Sbostic * If using chdir(2), grab a file descriptor pointing to dot to insure 13642299Sbostic * that we can get back here; this could be avoided for some paths, 13742299Sbostic * but almost certainly not worth the effort. Slashes, symbolic links, 13842299Sbostic * and ".." are all fairly nasty problems. Note, if we can't get the 13942299Sbostic * descriptor we run anyway, just more slowly. 14039800Sbostic */ 14147194Sbostic if (!ISSET(FTS_NOCHDIR) && (sp->fts_rfd = open(".", O_RDONLY, 0)) < 0) 14245600Sbostic SET(FTS_NOCHDIR); 14339800Sbostic 14452150Sbostic return (sp); 14539800Sbostic 14653801Sbostic mem3: fts_lfree(root); 14747194Sbostic free(parent); 14853801Sbostic mem2: free(sp->fts_path); 14947194Sbostic mem1: free(sp); 15052150Sbostic return (NULL); 15139800Sbostic } 15239800Sbostic 15347404Sbostic static void 15445600Sbostic fts_load(sp, p) 15545600Sbostic FTS *sp; 15639800Sbostic register FTSENT *p; 15739800Sbostic { 15839800Sbostic register int len; 15939800Sbostic register char *cp; 16039800Sbostic 16139800Sbostic /* 16247220Sbostic * Load the stream structure for the next traversal. Since we don't 16347220Sbostic * actually enter the directory until after the preorder visit, set 16447220Sbostic * the fts_accpath field specially so the chdir gets done to the right 16552209Sbostic * place and the user can access the first node. From fts_open it's 16652209Sbostic * known that the path will fit. 16739800Sbostic */ 16842273Sbostic len = p->fts_pathlen = p->fts_namelen; 16945600Sbostic bcopy(p->fts_name, sp->fts_path, len + 1); 17042273Sbostic if ((cp = rindex(p->fts_name, '/')) && (cp != p->fts_name || cp[1])) { 17139800Sbostic len = strlen(++cp); 17242273Sbostic bcopy(cp, p->fts_name, len + 1); 17342273Sbostic p->fts_namelen = len; 17439800Sbostic } 17545600Sbostic p->fts_accpath = p->fts_path = sp->fts_path; 17652209Sbostic sp->fts_dev = p->fts_dev; 17739800Sbostic } 17839800Sbostic 17952209Sbostic int 18045600Sbostic fts_close(sp) 18139800Sbostic FTS *sp; 18239800Sbostic { 18339800Sbostic register FTSENT *freep, *p; 18439800Sbostic int saved_errno; 18539800Sbostic 18652209Sbostic /* 18752209Sbostic * This still works if we haven't read anything -- the dummy structure 18852209Sbostic * points to the root list, so we step through to the end of the root 18952209Sbostic * list which has a valid parent pointer. 19052209Sbostic */ 19142281Sbostic if (sp->fts_cur) { 19252209Sbostic for (p = sp->fts_cur; p->fts_level >= FTS_ROOTLEVEL;) { 19342281Sbostic freep = p; 19442281Sbostic p = p->fts_link ? p->fts_link : p->fts_parent; 19547194Sbostic free(freep); 19639800Sbostic } 19747194Sbostic free(p); 19842281Sbostic } 19939800Sbostic 20045600Sbostic /* Free up child linked list, sort array, path buffer. */ 20142273Sbostic if (sp->fts_child) 20242273Sbostic fts_lfree(sp->fts_child); 20342273Sbostic if (sp->fts_array) 20447194Sbostic free(sp->fts_array); 20547194Sbostic free(sp->fts_path); 20639800Sbostic 20747194Sbostic /* Return to original directory, save errno if necessary. */ 20845600Sbostic if (!ISSET(FTS_NOCHDIR)) { 20947194Sbostic saved_errno = fchdir(sp->fts_rfd) ? errno : 0; 21047194Sbostic (void)close(sp->fts_rfd); 21139800Sbostic } 21239800Sbostic 21345600Sbostic /* Free up the stream pointer. */ 21447194Sbostic free(sp); 21539800Sbostic 21645600Sbostic /* Set errno and return. */ 21745600Sbostic if (!ISSET(FTS_NOCHDIR) && saved_errno) { 21839800Sbostic errno = saved_errno; 21952150Sbostic return (-1); 22039800Sbostic } 22152150Sbostic return (0); 22239800Sbostic } 22339800Sbostic 22447194Sbostic /* 22552160Sbostic * Special case a root of "/" so that slashes aren't appended which would 22652160Sbostic * cause paths to be written as "//foo". 22747194Sbostic */ 22855557Sbostic #define NAPPEND(p) \ 22955557Sbostic (p->fts_level == FTS_ROOTLEVEL && p->fts_pathlen == 1 && \ 23047194Sbostic p->fts_path[0] == '/' ? 0 : p->fts_pathlen) 23147194Sbostic 23239800Sbostic FTSENT * 23345600Sbostic fts_read(sp) 23439800Sbostic register FTS *sp; 23539800Sbostic { 23642299Sbostic register FTSENT *p, *tmp; 23739800Sbostic register int instr; 23847194Sbostic register char *t; 23953032Sbostic int saved_errno; 24039800Sbostic 24145600Sbostic /* If finished or unrecoverable error, return NULL. */ 24252155Sbostic if (sp->fts_cur == NULL || ISSET(FTS_STOP)) 24352150Sbostic return (NULL); 24439800Sbostic 24545600Sbostic /* Set current node pointer. */ 24642273Sbostic p = sp->fts_cur; 24739800Sbostic 24845600Sbostic /* Save and zero out user instructions. */ 24942273Sbostic instr = p->fts_instr; 25047194Sbostic p->fts_instr = FTS_NOINSTR; 25139800Sbostic 25247194Sbostic /* Any type of file may be re-visited; re-stat and re-turn. */ 25339800Sbostic if (instr == FTS_AGAIN) { 25445600Sbostic p->fts_info = fts_stat(sp, p, 0); 25552150Sbostic return (p); 25639800Sbostic } 25739800Sbostic 25847194Sbostic /* 25947194Sbostic * Following a symlink -- SLNONE test allows application to see 26052290Sbostic * SLNONE and recover. If indirecting through a symlink, have 26152290Sbostic * keep a pointer to current location. If unable to get that 26252290Sbostic * pointer, follow fails. 26347194Sbostic */ 26447194Sbostic if (instr == FTS_FOLLOW && 26547194Sbostic (p->fts_info == FTS_SL || p->fts_info == FTS_SLNONE)) { 26645600Sbostic p->fts_info = fts_stat(sp, p, 1); 26752290Sbostic if (p->fts_info == FTS_D && !ISSET(FTS_NOCHDIR)) 26852290Sbostic if ((p->fts_symfd = open(".", O_RDONLY, 0)) < 0) { 26952290Sbostic p->fts_errno = errno; 27052290Sbostic p->fts_info = FTS_ERR; 27152290Sbostic } else 27252290Sbostic p->fts_flags |= FTS_SYMFOLLOW; 27352150Sbostic return (p); 27442299Sbostic } 27542299Sbostic 27645600Sbostic /* Directory in pre-order. */ 27742299Sbostic if (p->fts_info == FTS_D) { 27845600Sbostic /* If skipped or crossed mount point, do post-order visit. */ 27947194Sbostic if (instr == FTS_SKIP || 28052209Sbostic ISSET(FTS_XDEV) && p->fts_dev != sp->fts_dev) { 28152290Sbostic if (p->fts_flags & FTS_SYMFOLLOW) 28252290Sbostic (void)close(p->fts_symfd); 28342273Sbostic if (sp->fts_child) { 28442273Sbostic fts_lfree(sp->fts_child); 28542273Sbostic sp->fts_child = NULL; 28639800Sbostic } 28742301Sbostic p->fts_info = FTS_DP; 28852150Sbostic return (p); 28942299Sbostic } 29042299Sbostic 29153096Sbostic /* Rebuild if only read the names and now traversing. */ 29252770Sbostic if (sp->fts_child && sp->fts_options & FTS_NAMEONLY) { 29352770Sbostic sp->fts_options &= ~FTS_NAMEONLY; 29452770Sbostic fts_lfree(sp->fts_child); 29552770Sbostic sp->fts_child = NULL; 29652770Sbostic } 29752770Sbostic 29847194Sbostic /* 29953096Sbostic * Cd to the subdirectory. 30053096Sbostic * 30147220Sbostic * If have already read and now fail to chdir, whack the list 30253096Sbostic * to make the names come out right, and set the parent errno 30347220Sbostic * so the application will eventually get an error condition. 30453096Sbostic * Set the FTS_DONTCHDIR flag so that when we logically change 30553096Sbostic * directories back to the parent we don't do a chdir. 30653096Sbostic * 30753096Sbostic * If haven't read do so. If the read fails, fts_build sets 30853096Sbostic * FTS_STOP or the fts_info field of the node. 30947194Sbostic */ 31047194Sbostic if (sp->fts_child) { 31142299Sbostic if (CHDIR(sp, p->fts_accpath)) { 31252278Sbostic p->fts_errno = errno; 31352290Sbostic p->fts_flags |= FTS_DONTCHDIR; 31447194Sbostic for (p = sp->fts_child; p; p = p->fts_link) 31547194Sbostic p->fts_accpath = 31647194Sbostic p->fts_parent->fts_accpath; 31742299Sbostic } 31852155Sbostic } else if ((sp->fts_child = fts_build(sp, BREAD)) == NULL) { 31952290Sbostic if (ISSET(FTS_STOP)) 32052150Sbostic return (NULL); 32152150Sbostic return (p); 32247220Sbostic } 32347194Sbostic p = sp->fts_child; 32442299Sbostic sp->fts_child = NULL; 32547194Sbostic goto name; 32639800Sbostic } 32739800Sbostic 32852278Sbostic /* Move to the next node on this level. */ 32942299Sbostic next: tmp = p; 33042299Sbostic if (p = p->fts_link) { 33147194Sbostic free(tmp); 33247194Sbostic 333*58532Sbostic /* 334*58532Sbostic * If reached the top, return to the original directory, and 335*58532Sbostic * load the paths for the next root. 336*58532Sbostic */ 33747212Sbostic if (p->fts_level == FTS_ROOTLEVEL) { 338*58532Sbostic if (!ISSET(FTS_NOCHDIR) && FCHDIR(sp, sp->fts_rfd)) { 339*58532Sbostic SET(FTS_STOP); 340*58532Sbostic return (NULL); 341*58532Sbostic } 34247404Sbostic fts_load(sp, p); 34352150Sbostic return (sp->fts_cur = p); 34447194Sbostic } 34542299Sbostic 34652290Sbostic /* 34752290Sbostic * User may have called fts_set on the node. If skipped, 34852290Sbostic * ignore. If followed, get a file descriptor so we can 34952290Sbostic * get back if necessary. 35052290Sbostic */ 35147194Sbostic if (p->fts_instr == FTS_SKIP) 35247194Sbostic goto next; 35347194Sbostic if (p->fts_instr == FTS_FOLLOW) { 35447194Sbostic p->fts_info = fts_stat(sp, p, 1); 35552290Sbostic if (p->fts_info == FTS_D && !ISSET(FTS_NOCHDIR)) 35652290Sbostic if ((p->fts_symfd = 35752290Sbostic open(".", O_RDONLY, 0)) < 0) { 35852290Sbostic p->fts_errno = errno; 35952290Sbostic p->fts_info = FTS_ERR; 36052290Sbostic } else 36152290Sbostic p->fts_flags |= FTS_SYMFOLLOW; 36247194Sbostic p->fts_instr = FTS_NOINSTR; 36347194Sbostic } 36442299Sbostic 36547194Sbostic name: t = sp->fts_path + NAPPEND(p->fts_parent); 36647194Sbostic *t++ = '/'; 36747194Sbostic bcopy(p->fts_name, t, p->fts_namelen + 1); 36852150Sbostic return (sp->fts_cur = p); 36939800Sbostic } 37039800Sbostic 37147220Sbostic /* Move up to the parent node. */ 37242299Sbostic p = tmp->fts_parent; 37347194Sbostic free(tmp); 37442299Sbostic 37547212Sbostic if (p->fts_level == FTS_ROOTPARENTLEVEL) { 37639800Sbostic /* 37745600Sbostic * Done; free everything up and set errno to 0 so the user 37839800Sbostic * can distinguish between error and EOF. 37939800Sbostic */ 38047194Sbostic free(p); 38139800Sbostic errno = 0; 38252150Sbostic return (sp->fts_cur = NULL); 38339800Sbostic } 38439800Sbostic 38553096Sbostic /* Nul terminate the pathname. */ 38642273Sbostic sp->fts_path[p->fts_pathlen] = '\0'; 38747194Sbostic 38847194Sbostic /* 38953096Sbostic * Return to the parent directory. If at a root node or came through 39053096Sbostic * a symlink, go back through the file descriptor. Otherwise, cd up 39153096Sbostic * one directory. 39247194Sbostic */ 39347220Sbostic if (p->fts_level == FTS_ROOTLEVEL) { 39453096Sbostic if (!ISSET(FTS_NOCHDIR) && FCHDIR(sp, sp->fts_rfd)) { 39547220Sbostic SET(FTS_STOP); 39652150Sbostic return (NULL); 39747220Sbostic } 39852290Sbostic } else if (p->fts_flags & FTS_SYMFOLLOW) { 39952290Sbostic if (FCHDIR(sp, p->fts_symfd)) { 40053032Sbostic saved_errno = errno; 40152290Sbostic (void)close(p->fts_symfd); 40253032Sbostic errno = saved_errno; 40352290Sbostic SET(FTS_STOP); 40452290Sbostic return (NULL); 40552290Sbostic } 40652290Sbostic (void)close(p->fts_symfd); 40752290Sbostic } else if (!(p->fts_flags & FTS_DONTCHDIR)) { 40852278Sbostic if (CHDIR(sp, "..")) { 40952278Sbostic SET(FTS_STOP); 41052278Sbostic return (NULL); 41152278Sbostic } 41252278Sbostic } 41352290Sbostic p->fts_info = p->fts_errno ? FTS_ERR : FTS_DP; 41452150Sbostic return (sp->fts_cur = p); 41539800Sbostic } 41639800Sbostic 41739800Sbostic /* 41847220Sbostic * Fts_set takes the stream as an argument although it's not used in this 41939800Sbostic * implementation; it would be necessary if anyone wanted to add global 42045600Sbostic * semantics to fts using fts_set. An error return is allowed for similar 42145600Sbostic * reasons. 42239800Sbostic */ 42339800Sbostic /* ARGSUSED */ 42452209Sbostic int 42545600Sbostic fts_set(sp, p, instr) 42639800Sbostic FTS *sp; 42739800Sbostic FTSENT *p; 42839800Sbostic int instr; 42939800Sbostic { 43052770Sbostic if (instr && instr != FTS_AGAIN && instr != FTS_FOLLOW && 43152770Sbostic instr != FTS_NOINSTR && instr != FTS_SKIP) { 43252770Sbostic errno = EINVAL; 43352770Sbostic return (1); 43452770Sbostic } 43542273Sbostic p->fts_instr = instr; 43652150Sbostic return (0); 43739800Sbostic } 43839800Sbostic 43939800Sbostic FTSENT * 44052770Sbostic fts_children(sp, instr) 44139800Sbostic register FTS *sp; 44252770Sbostic int instr; 44339800Sbostic { 44442299Sbostic register FTSENT *p; 44542299Sbostic int fd; 44642299Sbostic 44752770Sbostic if (instr && instr != FTS_NAMEONLY) { 44852770Sbostic errno = EINVAL; 44952770Sbostic return (NULL); 45052770Sbostic } 45152770Sbostic 45245600Sbostic /* Set current node pointer. */ 45345600Sbostic p = sp->fts_cur; 45445600Sbostic 45539800Sbostic /* 45652160Sbostic * Errno set to 0 so user can distinguish empty directory from 45752160Sbostic * an error. 45839800Sbostic */ 45939800Sbostic errno = 0; 46052160Sbostic 46152160Sbostic /* Fatal errors stop here. */ 46252160Sbostic if (ISSET(FTS_STOP)) 46352150Sbostic return (NULL); 46445600Sbostic 46552160Sbostic /* Return logical hierarchy of user's arguments. */ 46652160Sbostic if (p->fts_info == FTS_INIT) 46752160Sbostic return (p->fts_link); 46852160Sbostic 46952290Sbostic /* 47052290Sbostic * If not a directory being visited in pre-order, stop here. Could 47152290Sbostic * allow FTS_DNR, assuming the user has fixed the problem, but the 47252290Sbostic * same effect is available with FTS_AGAIN. 47352290Sbostic */ 47452290Sbostic if (p->fts_info != FTS_D /* && p->fts_info != FTS_DNR */) 47552160Sbostic return (NULL); 47652160Sbostic 47745600Sbostic /* Free up any previous child list. */ 47842273Sbostic if (sp->fts_child) 47942273Sbostic fts_lfree(sp->fts_child); 48042299Sbostic 48152770Sbostic if (instr == FTS_NAMEONLY) { 48252770Sbostic sp->fts_options |= FTS_NAMEONLY; 48352770Sbostic instr = BNAMES; 48452770Sbostic } else 48552770Sbostic instr = BCHILD; 48652770Sbostic 48742299Sbostic /* 48845600Sbostic * If using chdir on a relative path and called BEFORE fts_read does 48947194Sbostic * its chdir to the root of a traversal, we can lose -- we need to 49047194Sbostic * chdir into the subdirectory, and we don't know where the current 49147194Sbostic * directory is, so we can't get back so that the upcoming chdir by 49247194Sbostic * fts_read will work. 49342299Sbostic */ 49447212Sbostic if (p->fts_level != FTS_ROOTLEVEL || p->fts_accpath[0] == '/' || 49545600Sbostic ISSET(FTS_NOCHDIR)) 49652770Sbostic return (sp->fts_child = fts_build(sp, instr)); 49742299Sbostic 49842299Sbostic if ((fd = open(".", O_RDONLY, 0)) < 0) 49952150Sbostic return (NULL); 50052770Sbostic sp->fts_child = fts_build(sp, instr); 50142299Sbostic if (fchdir(fd)) 50252150Sbostic return (NULL); 50342299Sbostic (void)close(fd); 50452150Sbostic return (sp->fts_child); 50539800Sbostic } 50639800Sbostic 50747194Sbostic /* 50847194Sbostic * This is the tricky part -- do not casually change *anything* in here. The 50947194Sbostic * idea is to build the linked list of entries that are used by fts_children 51047194Sbostic * and fts_read. There are lots of special cases. 51147194Sbostic * 51247194Sbostic * The real slowdown in walking the tree is the stat calls. If FTS_NOSTAT is 51347194Sbostic * set and it's a physical walk (so that symbolic links can't be directories), 51447194Sbostic * we assume that the number of subdirectories in a node is equal to the number 51547194Sbostic * of links to the parent. This allows stat calls to be skipped in any leaf 51647194Sbostic * directories and for any nodes after the directories in the parent node have 51747194Sbostic * been found. This empirically cuts the stat calls by about 2/3. 51847194Sbostic */ 51947155Sbostic static FTSENT * 52042299Sbostic fts_build(sp, type) 52139800Sbostic register FTS *sp; 52242299Sbostic int type; 52339800Sbostic { 52439800Sbostic register struct dirent *dp; 52539800Sbostic register FTSENT *p, *head; 52639800Sbostic register int nitems; 52752290Sbostic FTSENT *cur, *tail; 52839800Sbostic DIR *dirp; 52952209Sbostic void *adjaddr; 53052278Sbostic int cderrno, descend, len, level, maxlen, nlinks, saved_errno; 53139800Sbostic char *cp; 53239800Sbostic 53345600Sbostic /* Set current node pointer. */ 53447194Sbostic cur = sp->fts_cur; 53545600Sbostic 53647194Sbostic /* 53747194Sbostic * Open the directory for reading. If this fails, we're done. 53847194Sbostic * If being called from fts_read, set the fts_info field. 53947194Sbostic */ 54052155Sbostic if ((dirp = opendir(cur->fts_accpath)) == NULL) { 54152206Sbostic if (type == BREAD) { 54247194Sbostic cur->fts_info = FTS_DNR; 54352206Sbostic cur->fts_errno = errno; 54452206Sbostic } 54552150Sbostic return (NULL); 54639800Sbostic } 54739800Sbostic 54839800Sbostic /* 54947194Sbostic * Nlinks is the number of possible entries of type directory in the 55047194Sbostic * directory if we're cheating on stat calls, 0 if we're not doing 55147194Sbostic * any stat calls at all, -1 if we're doing stats on everything. 55239800Sbostic */ 55352770Sbostic if (type == BNAMES) 55452770Sbostic nlinks = 0; 55552770Sbostic else if (ISSET(FTS_NOSTAT) && ISSET(FTS_PHYSICAL)) 55652770Sbostic nlinks = cur->fts_nlink - (ISSET(FTS_SEEDOT) ? 0 : 2); 55752770Sbostic else 55852770Sbostic nlinks = -1; 55939800Sbostic 56052290Sbostic #ifdef notdef 56152290Sbostic (void)printf("nlinks == %d (cur: %d)\n", nlinks, cur->fts_nlink); 56252290Sbostic (void)printf("NOSTAT %d PHYSICAL %d SEEDOT %d\n", 56352290Sbostic ISSET(FTS_NOSTAT), ISSET(FTS_PHYSICAL), ISSET(FTS_SEEDOT)); 56452290Sbostic #endif 56547194Sbostic /* 56647194Sbostic * If we're going to need to stat anything or we want to descend 56753379Sbostic * and stay in the directory, chdir. If this fails we keep going, 56853379Sbostic * but set a flag so we don't chdir after the post-order visit. 56947194Sbostic * We won't be able to stat anything, but we can still return the 57047194Sbostic * names themselves. Note, that since fts_read won't be able to 57147194Sbostic * chdir into the directory, it will have to return different path 57247194Sbostic * names than before, i.e. "a/b" instead of "b". Since the node 57347194Sbostic * has already been visited in pre-order, have to wait until the 57452290Sbostic * post-order visit to return the error. There is a special case 57552290Sbostic * here, if there was nothing to stat then it's not an error to 57652290Sbostic * not be able to stat. This is all fairly nasty. If a program 57752290Sbostic * needed sorted entries or stat information, they had better be 57852290Sbostic * checking FTS_NS on the returned nodes. 57947194Sbostic */ 580*58532Sbostic cderrno = 0; 58145600Sbostic if (nlinks || type == BREAD) 58247194Sbostic if (FCHDIR(sp, dirfd(dirp))) { 58353380Sbostic if (nlinks && type == BREAD) 58452160Sbostic cur->fts_errno = errno; 58553380Sbostic cur->fts_flags |= FTS_DONTCHDIR; 58652290Sbostic descend = 0; 58752278Sbostic cderrno = errno; 588*58532Sbostic } else 58945600Sbostic descend = 1; 59047194Sbostic else 59147194Sbostic descend = 0; 59239962Sbostic 59347194Sbostic /* 59447194Sbostic * Figure out the max file name length that can be stored in the 59547194Sbostic * current path -- the inner loop allocates more path as necessary. 59647194Sbostic * We really wouldn't have to do the maxlen calculations here, we 59747194Sbostic * could do them in fts_read before returning the path, but it's a 59847194Sbostic * lot easier here since the length is part of the dirent structure. 59947194Sbostic * 60052209Sbostic * If not changing directories set a pointer so that can just append 60152209Sbostic * each new name into the path. 60247194Sbostic */ 60347194Sbostic maxlen = sp->fts_pathlen - cur->fts_pathlen - 1; 60447194Sbostic len = NAPPEND(cur); 60547194Sbostic if (ISSET(FTS_NOCHDIR)) { 60647194Sbostic cp = sp->fts_path + len; 60747194Sbostic *cp++ = '/'; 60847194Sbostic } 60940939Sbostic 61047194Sbostic level = cur->fts_level + 1; 61140939Sbostic 61247194Sbostic /* Read the directory, attaching each entry to the `link' pointer. */ 61352209Sbostic adjaddr = NULL; 61452290Sbostic for (head = tail = NULL, nitems = 0; dp = readdir(dirp);) { 61547194Sbostic if (!ISSET(FTS_SEEDOT) && ISDOT(dp->d_name)) 61639800Sbostic continue; 61739800Sbostic 61852155Sbostic if ((p = fts_alloc(sp, dp->d_name, (int)dp->d_namlen)) == NULL) 61939800Sbostic goto mem1; 62039800Sbostic if (dp->d_namlen > maxlen) { 62153801Sbostic if (fts_palloc(sp, (size_t)dp->d_namlen)) { 62247194Sbostic /* 62347194Sbostic * No more memory for path or structures. Save 62447194Sbostic * errno, free up the current structure and the 62547194Sbostic * structures already allocated. 62647194Sbostic */ 62747194Sbostic mem1: saved_errno = errno; 62847194Sbostic if (p) 62947194Sbostic free(p); 63047194Sbostic fts_lfree(head); 63147194Sbostic (void)closedir(dirp); 63239800Sbostic errno = saved_errno; 63347194Sbostic cur->fts_info = FTS_ERR; 63447194Sbostic SET(FTS_STOP); 63552150Sbostic return (NULL); 63639800Sbostic } 63752209Sbostic adjaddr = sp->fts_path; 63842273Sbostic maxlen = sp->fts_pathlen - sp->fts_cur->fts_pathlen - 1; 63939800Sbostic } 64039800Sbostic 64142273Sbostic p->fts_pathlen = len + dp->d_namlen + 1; 64242273Sbostic p->fts_parent = sp->fts_cur; 64342273Sbostic p->fts_level = level; 64439800Sbostic 64552290Sbostic if (cderrno) { 64652290Sbostic if (nlinks) { 64752290Sbostic p->fts_info = FTS_NS; 64852290Sbostic p->fts_errno = cderrno; 64952290Sbostic } else 65052290Sbostic p->fts_info = FTS_NSOK; 65152290Sbostic p->fts_accpath = cur->fts_accpath; 65252290Sbostic } else if (nlinks) { 65347194Sbostic /* Build a file name for fts_stat to stat. */ 65447194Sbostic if (ISSET(FTS_NOCHDIR)) { 65547194Sbostic p->fts_accpath = p->fts_path; 65642273Sbostic bcopy(p->fts_name, cp, p->fts_namelen + 1); 65747194Sbostic } else 65847194Sbostic p->fts_accpath = p->fts_name; 65945600Sbostic p->fts_info = fts_stat(sp, p, 0); 66052209Sbostic if (nlinks > 0 && (p->fts_info == FTS_D || 66152209Sbostic p->fts_info == FTS_DC || p->fts_info == FTS_DOT)) 66239800Sbostic --nlinks; 66347194Sbostic } else { 66447194Sbostic p->fts_accpath = 66547194Sbostic ISSET(FTS_NOCHDIR) ? p->fts_path : p->fts_name; 66647194Sbostic p->fts_info = FTS_NSOK; 66747194Sbostic } 66839800Sbostic 66952290Sbostic /* We walk in directory order so "ls -f" doesn't get upset. */ 67052290Sbostic p->fts_link = NULL; 67152290Sbostic if (head == NULL) 67252290Sbostic head = tail = p; 67352290Sbostic else { 67452290Sbostic tail->fts_link = p; 67552290Sbostic tail = p; 67652290Sbostic } 67739800Sbostic ++nitems; 67839800Sbostic } 67939800Sbostic (void)closedir(dirp); 68039800Sbostic 68147194Sbostic /* 68252209Sbostic * If had to realloc the path, adjust the addresses for the rest 68352209Sbostic * of the tree. 68452209Sbostic */ 68552209Sbostic if (adjaddr) 68652209Sbostic fts_padjust(sp, adjaddr); 68752209Sbostic 68852209Sbostic /* 68947194Sbostic * If not changing directories, reset the path back to original 69047194Sbostic * state. 69147194Sbostic */ 69247194Sbostic if (ISSET(FTS_NOCHDIR)) { 69347194Sbostic if (cp - 1 > sp->fts_path) 69447194Sbostic --cp; 69547194Sbostic *cp = '\0'; 69647194Sbostic } 69742299Sbostic 69842299Sbostic /* 69947194Sbostic * If descended after called from fts_children or called from 70047194Sbostic * fts_read and didn't find anything, get back. If can't get 70152209Sbostic * back, done. 70242299Sbostic */ 70342299Sbostic if (descend && (!nitems || type == BCHILD) && CHDIR(sp, "..")) { 70447194Sbostic cur->fts_info = FTS_ERR; 70547194Sbostic SET(FTS_STOP); 70652150Sbostic return (NULL); 70739962Sbostic } 70839962Sbostic 70953032Sbostic /* If didn't find anything, return NULL. */ 71053096Sbostic if (!nitems) { 71153096Sbostic if (type == BREAD) 71253096Sbostic cur->fts_info = FTS_DP; 71352150Sbostic return (NULL); 71453096Sbostic } 71539800Sbostic 71647194Sbostic /* Sort the entries. */ 71742273Sbostic if (sp->fts_compar && nitems > 1) 71845600Sbostic head = fts_sort(sp, head, nitems); 71952150Sbostic return (head); 72039800Sbostic } 72139800Sbostic 72245600Sbostic static u_short 72345600Sbostic fts_stat(sp, p, follow) 72445600Sbostic FTS *sp; 72539800Sbostic register FTSENT *p; 72645600Sbostic int follow; 72739800Sbostic { 72852160Sbostic register FTSENT *t; 72952160Sbostic register dev_t dev; 73052160Sbostic register ino_t ino; 73152209Sbostic struct stat *sbp, sb; 73247194Sbostic int saved_errno; 73347194Sbostic 73452209Sbostic /* If user needs stat info, stat buffer already allocated. */ 73552209Sbostic sbp = ISSET(FTS_NOSTAT) ? &sb : p->fts_statp; 73652209Sbostic 73739800Sbostic /* 73845600Sbostic * If doing a logical walk, or application requested FTS_FOLLOW, do 73947194Sbostic * a stat(2). If that fails, check for a non-existent symlink. If 74052160Sbostic * fail, set the errno from the stat call. 74139800Sbostic */ 74245600Sbostic if (ISSET(FTS_LOGICAL) || follow) { 74352209Sbostic if (stat(p->fts_accpath, sbp)) { 74447194Sbostic saved_errno = errno; 74552209Sbostic if (!lstat(p->fts_accpath, sbp)) { 74647194Sbostic errno = 0; 74752150Sbostic return (FTS_SLNONE); 74847194Sbostic } 74952160Sbostic p->fts_errno = saved_errno; 75052209Sbostic goto err; 75145600Sbostic } 75252209Sbostic } else if (lstat(p->fts_accpath, sbp)) { 75352160Sbostic p->fts_errno = errno; 75452209Sbostic err: bzero(sbp, sizeof(struct stat)); 75552150Sbostic return (FTS_NS); 75645600Sbostic } 75739800Sbostic 75852209Sbostic if (S_ISDIR(sbp->st_mode)) { 75952209Sbostic /* 76052209Sbostic * Set the device/inode. Used to find cycles and check for 76152209Sbostic * crossing mount points. Also remember the link count, used 76252209Sbostic * in fts_build to limit the number of stat calls. It is 76352209Sbostic * understood that these fields are only referenced if fts_info 76452209Sbostic * is set to FTS_D. 76552209Sbostic */ 76652209Sbostic dev = p->fts_dev = sbp->st_dev; 76752209Sbostic ino = p->fts_ino = sbp->st_ino; 76852209Sbostic p->fts_nlink = sbp->st_nlink; 76952209Sbostic 77052290Sbostic if (ISDOT(p->fts_name)) 77152290Sbostic return (FTS_DOT); 77252290Sbostic 77352209Sbostic /* 77452209Sbostic * Cycle detection is done by brute force when the directory 77552209Sbostic * is first encountered. If the tree gets deep enough or the 77652209Sbostic * number of symbolic links to directories is high enough, 77752209Sbostic * something faster might be worthwhile. 77852209Sbostic */ 77952209Sbostic for (t = p->fts_parent; 78052209Sbostic t->fts_level >= FTS_ROOTLEVEL; t = t->fts_parent) 78152209Sbostic if (ino == t->fts_ino && dev == t->fts_dev) { 78252073Sbostic p->fts_cycle = t; 78352150Sbostic return (FTS_DC); 78447194Sbostic } 78552150Sbostic return (FTS_D); 78647194Sbostic } 78752209Sbostic if (S_ISLNK(sbp->st_mode)) 78852150Sbostic return (FTS_SL); 78952209Sbostic if (S_ISREG(sbp->st_mode)) 79052150Sbostic return (FTS_F); 79152150Sbostic return (FTS_DEFAULT); 79239800Sbostic } 79339800Sbostic 79439800Sbostic static FTSENT * 79545600Sbostic fts_sort(sp, head, nitems) 79645600Sbostic FTS *sp; 79739800Sbostic FTSENT *head; 79839800Sbostic register int nitems; 79939800Sbostic { 80039800Sbostic register FTSENT **ap, *p; 80139800Sbostic 80239800Sbostic /* 80345600Sbostic * Construct an array of pointers to the structures and call qsort(3). 80439800Sbostic * Reassemble the array in the order returned by qsort. If unable to 80539800Sbostic * sort for memory reasons, return the directory entries in their 80639800Sbostic * current order. Allocate enough space for the current needs plus 80752209Sbostic * 40 so don't realloc one entry at a time. 80839800Sbostic */ 80945600Sbostic if (nitems > sp->fts_nitems) { 81045600Sbostic sp->fts_nitems = nitems + 40; 81152209Sbostic if ((sp->fts_array = realloc(sp->fts_array, 81252209Sbostic (size_t)(sp->fts_nitems * sizeof(FTSENT *)))) == NULL) { 81345600Sbostic sp->fts_nitems = 0; 81452150Sbostic return (head); 81539800Sbostic } 81639800Sbostic } 81745600Sbostic for (ap = sp->fts_array, p = head; p; p = p->fts_link) 81839800Sbostic *ap++ = p; 81945600Sbostic qsort((void *)sp->fts_array, nitems, sizeof(FTSENT *), sp->fts_compar); 82045600Sbostic for (head = *(ap = sp->fts_array); --nitems; ++ap) 82142273Sbostic ap[0]->fts_link = ap[1]; 82242273Sbostic ap[0]->fts_link = NULL; 82352150Sbostic return (head); 82439800Sbostic } 82539800Sbostic 82639800Sbostic static FTSENT * 82753801Sbostic fts_alloc(sp, name, namelen) 82845600Sbostic FTS *sp; 82939800Sbostic char *name; 83053801Sbostic register int namelen; 83139800Sbostic { 83239800Sbostic register FTSENT *p; 83353801Sbostic size_t len; 83439800Sbostic 83539800Sbostic /* 83653801Sbostic * The file name is a variable length array and no stat structure is 83753801Sbostic * necessary if the user has set the nostat bit. Allocate the FTSENT 83853801Sbostic * structure, the file name and the stat structure in one chunk, but 83953801Sbostic * be careful that the stat structure is reasonably aligned. Since the 84053801Sbostic * fts_name field is declared to be of size 1, the fts_name pointer is 84153801Sbostic * namelen + 2 before the first possible address of the stat structure. 84239800Sbostic */ 84353801Sbostic len = sizeof(FTSENT) + namelen; 84453801Sbostic if (!ISSET(FTS_NOSTAT)) 84553801Sbostic len += sizeof(struct stat) + ALIGNBYTES; 84653801Sbostic if ((p = malloc(len)) == NULL) 84752150Sbostic return (NULL); 84853801Sbostic 84953801Sbostic /* Copy the name plus the trailing NULL. */ 85053801Sbostic bcopy(name, p->fts_name, namelen + 1); 85153801Sbostic 85253801Sbostic if (!ISSET(FTS_NOSTAT)) 85353801Sbostic p->fts_statp = (struct stat *)ALIGN(p->fts_name + namelen + 2); 85453801Sbostic p->fts_namelen = namelen; 85545600Sbostic p->fts_path = sp->fts_path; 85652290Sbostic p->fts_errno = 0; 85752290Sbostic p->fts_flags = 0; 85847194Sbostic p->fts_instr = FTS_NOINSTR; 85945600Sbostic p->fts_number = 0; 86045600Sbostic p->fts_pointer = NULL; 86152150Sbostic return (p); 86239800Sbostic } 86339800Sbostic 86445600Sbostic static void 86539800Sbostic fts_lfree(head) 86639800Sbostic register FTSENT *head; 86739800Sbostic { 86839800Sbostic register FTSENT *p; 86939800Sbostic 87045600Sbostic /* Free a linked list of structures. */ 87139800Sbostic while (p = head) { 87242273Sbostic head = head->fts_link; 87347194Sbostic free(p); 87439800Sbostic } 87539800Sbostic } 87639800Sbostic 87739800Sbostic /* 87852209Sbostic * Allow essentially unlimited paths; find, rm, ls should all work on any tree. 87952209Sbostic * Most systems will allow creation of paths much longer than MAXPATHLEN, even 88052209Sbostic * though the kernel won't resolve them. Add the size (not just what's needed) 88152209Sbostic * plus 256 bytes so don't realloc the path 2 bytes at a time. 88239800Sbostic */ 88352209Sbostic static int 88452209Sbostic fts_palloc(sp, more) 88545600Sbostic FTS *sp; 88653801Sbostic size_t more; 88739800Sbostic { 88852209Sbostic sp->fts_pathlen += more + 256; 88952209Sbostic sp->fts_path = realloc(sp->fts_path, (size_t)sp->fts_pathlen); 89052209Sbostic return (sp->fts_path == NULL); 89147155Sbostic } 89252209Sbostic 89352209Sbostic /* 89452209Sbostic * When the path is realloc'd, have to fix all of the pointers in structures 89552209Sbostic * already returned. 89652209Sbostic */ 89752209Sbostic static void 89852209Sbostic fts_padjust(sp, addr) 89952209Sbostic FTS *sp; 90052209Sbostic void *addr; 90152209Sbostic { 90252209Sbostic FTSENT *p; 90352209Sbostic 90455557Sbostic #define ADJUST(p) { \ 90555557Sbostic (p)->fts_accpath = addr + ((p)->fts_accpath - (p)->fts_path); \ 90655557Sbostic (p)->fts_path = addr; \ 90752209Sbostic } 90852209Sbostic /* Adjust the current set of children. */ 90952209Sbostic for (p = sp->fts_child; p; p = p->fts_link) 91052209Sbostic ADJUST(p); 91152209Sbostic 91252209Sbostic /* Adjust the rest of the tree. */ 91352209Sbostic for (p = sp->fts_cur; p->fts_level >= FTS_ROOTLEVEL;) { 91452209Sbostic ADJUST(p); 91552209Sbostic p = p->fts_link ? p->fts_link : p->fts_parent; 91652209Sbostic } 91752209Sbostic } 91855557Sbostic 91955557Sbostic static size_t 92055557Sbostic fts_maxarglen(argv) 92155557Sbostic char * const *argv; 92255557Sbostic { 92355557Sbostic size_t len, max; 92455557Sbostic 92555557Sbostic for (max = 0; *argv; ++argv) 92655557Sbostic if ((len = strlen(*argv)) > max) 92755557Sbostic max = len; 92855557Sbostic return (max); 92955557Sbostic } 930