142273Sbostic /*- 2*61111Sbostic * Copyright (c) 1990, 1993 3*61111Sbostic * The Regents of the University of California. All rights reserved. 439800Sbostic * 542273Sbostic * %sccs.include.redist.c% 639800Sbostic */ 739800Sbostic 839800Sbostic #if defined(LIBC_SCCS) && !defined(lint) 9*61111Sbostic static char sccsid[] = "@(#)fts.c 8.1 (Berkeley) 06/04/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 33358532Sbostic /* 33458532Sbostic * If reached the top, return to the original directory, and 33558532Sbostic * load the paths for the next root. 33658532Sbostic */ 33747212Sbostic if (p->fts_level == FTS_ROOTLEVEL) { 33858532Sbostic if (!ISSET(FTS_NOCHDIR) && FCHDIR(sp, sp->fts_rfd)) { 33958532Sbostic SET(FTS_STOP); 34058532Sbostic return (NULL); 34158532Sbostic } 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), 51459307Sbostic * we can do things quickly. First, if it's a 4.4BSD file system, the type 51559307Sbostic * of the file is in the directory entry. Otherwise, we assume that the number 51659307Sbostic * of subdirectories in a node is equal to the number of links to the parent. 51759307Sbostic * The former skips all stat calls. The latter skips stat calls in any leaf 51859307Sbostic * directories and for any files after the subdirectories in the directory have 51959307Sbostic * been found, cutting the stat calls by about 2/3. 52047194Sbostic */ 52147155Sbostic static FTSENT * 52242299Sbostic fts_build(sp, type) 52339800Sbostic register FTS *sp; 52442299Sbostic int type; 52539800Sbostic { 52639800Sbostic register struct dirent *dp; 52739800Sbostic register FTSENT *p, *head; 52839800Sbostic register int nitems; 52952290Sbostic FTSENT *cur, *tail; 53039800Sbostic DIR *dirp; 53152209Sbostic void *adjaddr; 53252278Sbostic int cderrno, descend, len, level, maxlen, nlinks, saved_errno; 53339800Sbostic char *cp; 53439800Sbostic 53545600Sbostic /* Set current node pointer. */ 53647194Sbostic cur = sp->fts_cur; 53745600Sbostic 53847194Sbostic /* 53947194Sbostic * Open the directory for reading. If this fails, we're done. 54047194Sbostic * If being called from fts_read, set the fts_info field. 54147194Sbostic */ 54252155Sbostic if ((dirp = opendir(cur->fts_accpath)) == NULL) { 54352206Sbostic if (type == BREAD) { 54447194Sbostic cur->fts_info = FTS_DNR; 54552206Sbostic cur->fts_errno = errno; 54652206Sbostic } 54752150Sbostic return (NULL); 54839800Sbostic } 54939800Sbostic 55039800Sbostic /* 55147194Sbostic * Nlinks is the number of possible entries of type directory in the 55247194Sbostic * directory if we're cheating on stat calls, 0 if we're not doing 55347194Sbostic * any stat calls at all, -1 if we're doing stats on everything. 55439800Sbostic */ 55552770Sbostic if (type == BNAMES) 55652770Sbostic nlinks = 0; 55752770Sbostic else if (ISSET(FTS_NOSTAT) && ISSET(FTS_PHYSICAL)) 55852770Sbostic nlinks = cur->fts_nlink - (ISSET(FTS_SEEDOT) ? 0 : 2); 55952770Sbostic else 56052770Sbostic nlinks = -1; 56139800Sbostic 56252290Sbostic #ifdef notdef 56352290Sbostic (void)printf("nlinks == %d (cur: %d)\n", nlinks, cur->fts_nlink); 56452290Sbostic (void)printf("NOSTAT %d PHYSICAL %d SEEDOT %d\n", 56552290Sbostic ISSET(FTS_NOSTAT), ISSET(FTS_PHYSICAL), ISSET(FTS_SEEDOT)); 56652290Sbostic #endif 56747194Sbostic /* 56847194Sbostic * If we're going to need to stat anything or we want to descend 56953379Sbostic * and stay in the directory, chdir. If this fails we keep going, 57053379Sbostic * but set a flag so we don't chdir after the post-order visit. 57147194Sbostic * We won't be able to stat anything, but we can still return the 57247194Sbostic * names themselves. Note, that since fts_read won't be able to 57347194Sbostic * chdir into the directory, it will have to return different path 57447194Sbostic * names than before, i.e. "a/b" instead of "b". Since the node 57547194Sbostic * has already been visited in pre-order, have to wait until the 57652290Sbostic * post-order visit to return the error. There is a special case 57752290Sbostic * here, if there was nothing to stat then it's not an error to 57852290Sbostic * not be able to stat. This is all fairly nasty. If a program 57952290Sbostic * needed sorted entries or stat information, they had better be 58052290Sbostic * checking FTS_NS on the returned nodes. 58147194Sbostic */ 58258532Sbostic cderrno = 0; 58345600Sbostic if (nlinks || type == BREAD) 58447194Sbostic if (FCHDIR(sp, dirfd(dirp))) { 58553380Sbostic if (nlinks && type == BREAD) 58652160Sbostic cur->fts_errno = errno; 58753380Sbostic cur->fts_flags |= FTS_DONTCHDIR; 58852290Sbostic descend = 0; 58952278Sbostic cderrno = errno; 59058532Sbostic } else 59145600Sbostic descend = 1; 59247194Sbostic else 59347194Sbostic descend = 0; 59439962Sbostic 59547194Sbostic /* 59647194Sbostic * Figure out the max file name length that can be stored in the 59747194Sbostic * current path -- the inner loop allocates more path as necessary. 59847194Sbostic * We really wouldn't have to do the maxlen calculations here, we 59947194Sbostic * could do them in fts_read before returning the path, but it's a 60047194Sbostic * lot easier here since the length is part of the dirent structure. 60147194Sbostic * 60252209Sbostic * If not changing directories set a pointer so that can just append 60352209Sbostic * each new name into the path. 60447194Sbostic */ 60547194Sbostic maxlen = sp->fts_pathlen - cur->fts_pathlen - 1; 60647194Sbostic len = NAPPEND(cur); 60747194Sbostic if (ISSET(FTS_NOCHDIR)) { 60847194Sbostic cp = sp->fts_path + len; 60947194Sbostic *cp++ = '/'; 61047194Sbostic } 61140939Sbostic 61247194Sbostic level = cur->fts_level + 1; 61340939Sbostic 61447194Sbostic /* Read the directory, attaching each entry to the `link' pointer. */ 61552209Sbostic adjaddr = NULL; 61652290Sbostic for (head = tail = NULL, nitems = 0; dp = readdir(dirp);) { 61747194Sbostic if (!ISSET(FTS_SEEDOT) && ISDOT(dp->d_name)) 61839800Sbostic continue; 61939800Sbostic 62052155Sbostic if ((p = fts_alloc(sp, dp->d_name, (int)dp->d_namlen)) == NULL) 62139800Sbostic goto mem1; 62239800Sbostic if (dp->d_namlen > maxlen) { 62353801Sbostic if (fts_palloc(sp, (size_t)dp->d_namlen)) { 62447194Sbostic /* 62547194Sbostic * No more memory for path or structures. Save 62647194Sbostic * errno, free up the current structure and the 62747194Sbostic * structures already allocated. 62847194Sbostic */ 62947194Sbostic mem1: saved_errno = errno; 63047194Sbostic if (p) 63147194Sbostic free(p); 63247194Sbostic fts_lfree(head); 63347194Sbostic (void)closedir(dirp); 63439800Sbostic errno = saved_errno; 63547194Sbostic cur->fts_info = FTS_ERR; 63647194Sbostic SET(FTS_STOP); 63752150Sbostic return (NULL); 63839800Sbostic } 63952209Sbostic adjaddr = sp->fts_path; 64042273Sbostic maxlen = sp->fts_pathlen - sp->fts_cur->fts_pathlen - 1; 64139800Sbostic } 64239800Sbostic 64342273Sbostic p->fts_pathlen = len + dp->d_namlen + 1; 64442273Sbostic p->fts_parent = sp->fts_cur; 64542273Sbostic p->fts_level = level; 64639800Sbostic 64752290Sbostic if (cderrno) { 64852290Sbostic if (nlinks) { 64952290Sbostic p->fts_info = FTS_NS; 65052290Sbostic p->fts_errno = cderrno; 65152290Sbostic } else 65252290Sbostic p->fts_info = FTS_NSOK; 65352290Sbostic p->fts_accpath = cur->fts_accpath; 65459307Sbostic } else if (nlinks == 0 65559307Sbostic #ifdef DT_DIR 65659307Sbostic || nlinks > 0 && 65759307Sbostic dp->d_type != DT_DIR && dp->d_type != DT_UNKNOWN 65859307Sbostic #endif 65959307Sbostic ) { 66059307Sbostic p->fts_accpath = 66159307Sbostic ISSET(FTS_NOCHDIR) ? p->fts_path : p->fts_name; 66259307Sbostic p->fts_info = FTS_NSOK; 66359307Sbostic } else { 66447194Sbostic /* Build a file name for fts_stat to stat. */ 66547194Sbostic if (ISSET(FTS_NOCHDIR)) { 66647194Sbostic p->fts_accpath = p->fts_path; 66742273Sbostic bcopy(p->fts_name, cp, p->fts_namelen + 1); 66847194Sbostic } else 66947194Sbostic p->fts_accpath = p->fts_name; 67059307Sbostic /* Stat it. */ 67145600Sbostic p->fts_info = fts_stat(sp, p, 0); 67259307Sbostic 67359307Sbostic /* Decrement link count if applicable. */ 67452209Sbostic if (nlinks > 0 && (p->fts_info == FTS_D || 67552209Sbostic p->fts_info == FTS_DC || p->fts_info == FTS_DOT)) 67639800Sbostic --nlinks; 67747194Sbostic } 67839800Sbostic 67952290Sbostic /* We walk in directory order so "ls -f" doesn't get upset. */ 68052290Sbostic p->fts_link = NULL; 68152290Sbostic if (head == NULL) 68252290Sbostic head = tail = p; 68352290Sbostic else { 68452290Sbostic tail->fts_link = p; 68552290Sbostic tail = p; 68652290Sbostic } 68739800Sbostic ++nitems; 68839800Sbostic } 68939800Sbostic (void)closedir(dirp); 69039800Sbostic 69147194Sbostic /* 69252209Sbostic * If had to realloc the path, adjust the addresses for the rest 69352209Sbostic * of the tree. 69452209Sbostic */ 69552209Sbostic if (adjaddr) 69652209Sbostic fts_padjust(sp, adjaddr); 69752209Sbostic 69852209Sbostic /* 69947194Sbostic * If not changing directories, reset the path back to original 70047194Sbostic * state. 70147194Sbostic */ 70247194Sbostic if (ISSET(FTS_NOCHDIR)) { 70347194Sbostic if (cp - 1 > sp->fts_path) 70447194Sbostic --cp; 70547194Sbostic *cp = '\0'; 70647194Sbostic } 70742299Sbostic 70842299Sbostic /* 70947194Sbostic * If descended after called from fts_children or called from 71047194Sbostic * fts_read and didn't find anything, get back. If can't get 71152209Sbostic * back, done. 71242299Sbostic */ 71342299Sbostic if (descend && (!nitems || type == BCHILD) && CHDIR(sp, "..")) { 71447194Sbostic cur->fts_info = FTS_ERR; 71547194Sbostic SET(FTS_STOP); 71652150Sbostic return (NULL); 71739962Sbostic } 71839962Sbostic 71953032Sbostic /* If didn't find anything, return NULL. */ 72053096Sbostic if (!nitems) { 72153096Sbostic if (type == BREAD) 72253096Sbostic cur->fts_info = FTS_DP; 72352150Sbostic return (NULL); 72453096Sbostic } 72539800Sbostic 72647194Sbostic /* Sort the entries. */ 72742273Sbostic if (sp->fts_compar && nitems > 1) 72845600Sbostic head = fts_sort(sp, head, nitems); 72952150Sbostic return (head); 73039800Sbostic } 73139800Sbostic 73245600Sbostic static u_short 73345600Sbostic fts_stat(sp, p, follow) 73445600Sbostic FTS *sp; 73539800Sbostic register FTSENT *p; 73645600Sbostic int follow; 73739800Sbostic { 73852160Sbostic register FTSENT *t; 73952160Sbostic register dev_t dev; 74052160Sbostic register ino_t ino; 74152209Sbostic struct stat *sbp, sb; 74247194Sbostic int saved_errno; 74347194Sbostic 74452209Sbostic /* If user needs stat info, stat buffer already allocated. */ 74552209Sbostic sbp = ISSET(FTS_NOSTAT) ? &sb : p->fts_statp; 74652209Sbostic 74739800Sbostic /* 74845600Sbostic * If doing a logical walk, or application requested FTS_FOLLOW, do 74947194Sbostic * a stat(2). If that fails, check for a non-existent symlink. If 75052160Sbostic * fail, set the errno from the stat call. 75139800Sbostic */ 75245600Sbostic if (ISSET(FTS_LOGICAL) || follow) { 75352209Sbostic if (stat(p->fts_accpath, sbp)) { 75447194Sbostic saved_errno = errno; 75552209Sbostic if (!lstat(p->fts_accpath, sbp)) { 75647194Sbostic errno = 0; 75752150Sbostic return (FTS_SLNONE); 75847194Sbostic } 75952160Sbostic p->fts_errno = saved_errno; 76052209Sbostic goto err; 76145600Sbostic } 76252209Sbostic } else if (lstat(p->fts_accpath, sbp)) { 76352160Sbostic p->fts_errno = errno; 76452209Sbostic err: bzero(sbp, sizeof(struct stat)); 76552150Sbostic return (FTS_NS); 76645600Sbostic } 76739800Sbostic 76852209Sbostic if (S_ISDIR(sbp->st_mode)) { 76952209Sbostic /* 77052209Sbostic * Set the device/inode. Used to find cycles and check for 77152209Sbostic * crossing mount points. Also remember the link count, used 77252209Sbostic * in fts_build to limit the number of stat calls. It is 77352209Sbostic * understood that these fields are only referenced if fts_info 77452209Sbostic * is set to FTS_D. 77552209Sbostic */ 77652209Sbostic dev = p->fts_dev = sbp->st_dev; 77752209Sbostic ino = p->fts_ino = sbp->st_ino; 77852209Sbostic p->fts_nlink = sbp->st_nlink; 77952209Sbostic 78052290Sbostic if (ISDOT(p->fts_name)) 78152290Sbostic return (FTS_DOT); 78252290Sbostic 78352209Sbostic /* 78452209Sbostic * Cycle detection is done by brute force when the directory 78552209Sbostic * is first encountered. If the tree gets deep enough or the 78652209Sbostic * number of symbolic links to directories is high enough, 78752209Sbostic * something faster might be worthwhile. 78852209Sbostic */ 78952209Sbostic for (t = p->fts_parent; 79052209Sbostic t->fts_level >= FTS_ROOTLEVEL; t = t->fts_parent) 79152209Sbostic if (ino == t->fts_ino && dev == t->fts_dev) { 79252073Sbostic p->fts_cycle = t; 79352150Sbostic return (FTS_DC); 79447194Sbostic } 79552150Sbostic return (FTS_D); 79647194Sbostic } 79752209Sbostic if (S_ISLNK(sbp->st_mode)) 79852150Sbostic return (FTS_SL); 79952209Sbostic if (S_ISREG(sbp->st_mode)) 80052150Sbostic return (FTS_F); 80152150Sbostic return (FTS_DEFAULT); 80239800Sbostic } 80339800Sbostic 80439800Sbostic static FTSENT * 80545600Sbostic fts_sort(sp, head, nitems) 80645600Sbostic FTS *sp; 80739800Sbostic FTSENT *head; 80839800Sbostic register int nitems; 80939800Sbostic { 81039800Sbostic register FTSENT **ap, *p; 81139800Sbostic 81239800Sbostic /* 81345600Sbostic * Construct an array of pointers to the structures and call qsort(3). 81439800Sbostic * Reassemble the array in the order returned by qsort. If unable to 81539800Sbostic * sort for memory reasons, return the directory entries in their 81639800Sbostic * current order. Allocate enough space for the current needs plus 81752209Sbostic * 40 so don't realloc one entry at a time. 81839800Sbostic */ 81945600Sbostic if (nitems > sp->fts_nitems) { 82045600Sbostic sp->fts_nitems = nitems + 40; 82152209Sbostic if ((sp->fts_array = realloc(sp->fts_array, 82252209Sbostic (size_t)(sp->fts_nitems * sizeof(FTSENT *)))) == NULL) { 82345600Sbostic sp->fts_nitems = 0; 82452150Sbostic return (head); 82539800Sbostic } 82639800Sbostic } 82745600Sbostic for (ap = sp->fts_array, p = head; p; p = p->fts_link) 82839800Sbostic *ap++ = p; 82945600Sbostic qsort((void *)sp->fts_array, nitems, sizeof(FTSENT *), sp->fts_compar); 83045600Sbostic for (head = *(ap = sp->fts_array); --nitems; ++ap) 83142273Sbostic ap[0]->fts_link = ap[1]; 83242273Sbostic ap[0]->fts_link = NULL; 83352150Sbostic return (head); 83439800Sbostic } 83539800Sbostic 83639800Sbostic static FTSENT * 83753801Sbostic fts_alloc(sp, name, namelen) 83845600Sbostic FTS *sp; 83939800Sbostic char *name; 84053801Sbostic register int namelen; 84139800Sbostic { 84239800Sbostic register FTSENT *p; 84353801Sbostic size_t len; 84439800Sbostic 84539800Sbostic /* 84653801Sbostic * The file name is a variable length array and no stat structure is 84753801Sbostic * necessary if the user has set the nostat bit. Allocate the FTSENT 84853801Sbostic * structure, the file name and the stat structure in one chunk, but 84953801Sbostic * be careful that the stat structure is reasonably aligned. Since the 85053801Sbostic * fts_name field is declared to be of size 1, the fts_name pointer is 85153801Sbostic * namelen + 2 before the first possible address of the stat structure. 85239800Sbostic */ 85353801Sbostic len = sizeof(FTSENT) + namelen; 85453801Sbostic if (!ISSET(FTS_NOSTAT)) 85553801Sbostic len += sizeof(struct stat) + ALIGNBYTES; 85653801Sbostic if ((p = malloc(len)) == NULL) 85752150Sbostic return (NULL); 85853801Sbostic 85953801Sbostic /* Copy the name plus the trailing NULL. */ 86053801Sbostic bcopy(name, p->fts_name, namelen + 1); 86153801Sbostic 86253801Sbostic if (!ISSET(FTS_NOSTAT)) 86353801Sbostic p->fts_statp = (struct stat *)ALIGN(p->fts_name + namelen + 2); 86453801Sbostic p->fts_namelen = namelen; 86545600Sbostic p->fts_path = sp->fts_path; 86652290Sbostic p->fts_errno = 0; 86752290Sbostic p->fts_flags = 0; 86847194Sbostic p->fts_instr = FTS_NOINSTR; 86945600Sbostic p->fts_number = 0; 87045600Sbostic p->fts_pointer = NULL; 87152150Sbostic return (p); 87239800Sbostic } 87339800Sbostic 87445600Sbostic static void 87539800Sbostic fts_lfree(head) 87639800Sbostic register FTSENT *head; 87739800Sbostic { 87839800Sbostic register FTSENT *p; 87939800Sbostic 88045600Sbostic /* Free a linked list of structures. */ 88139800Sbostic while (p = head) { 88242273Sbostic head = head->fts_link; 88347194Sbostic free(p); 88439800Sbostic } 88539800Sbostic } 88639800Sbostic 88739800Sbostic /* 88852209Sbostic * Allow essentially unlimited paths; find, rm, ls should all work on any tree. 88952209Sbostic * Most systems will allow creation of paths much longer than MAXPATHLEN, even 89052209Sbostic * though the kernel won't resolve them. Add the size (not just what's needed) 89152209Sbostic * plus 256 bytes so don't realloc the path 2 bytes at a time. 89239800Sbostic */ 89352209Sbostic static int 89452209Sbostic fts_palloc(sp, more) 89545600Sbostic FTS *sp; 89653801Sbostic size_t more; 89739800Sbostic { 89852209Sbostic sp->fts_pathlen += more + 256; 89952209Sbostic sp->fts_path = realloc(sp->fts_path, (size_t)sp->fts_pathlen); 90052209Sbostic return (sp->fts_path == NULL); 90147155Sbostic } 90252209Sbostic 90352209Sbostic /* 90452209Sbostic * When the path is realloc'd, have to fix all of the pointers in structures 90552209Sbostic * already returned. 90652209Sbostic */ 90752209Sbostic static void 90852209Sbostic fts_padjust(sp, addr) 90952209Sbostic FTS *sp; 91052209Sbostic void *addr; 91152209Sbostic { 91252209Sbostic FTSENT *p; 91352209Sbostic 91455557Sbostic #define ADJUST(p) { \ 91555557Sbostic (p)->fts_accpath = addr + ((p)->fts_accpath - (p)->fts_path); \ 91655557Sbostic (p)->fts_path = addr; \ 91752209Sbostic } 91852209Sbostic /* Adjust the current set of children. */ 91952209Sbostic for (p = sp->fts_child; p; p = p->fts_link) 92052209Sbostic ADJUST(p); 92152209Sbostic 92252209Sbostic /* Adjust the rest of the tree. */ 92352209Sbostic for (p = sp->fts_cur; p->fts_level >= FTS_ROOTLEVEL;) { 92452209Sbostic ADJUST(p); 92552209Sbostic p = p->fts_link ? p->fts_link : p->fts_parent; 92652209Sbostic } 92752209Sbostic } 92855557Sbostic 92955557Sbostic static size_t 93055557Sbostic fts_maxarglen(argv) 93155557Sbostic char * const *argv; 93255557Sbostic { 93355557Sbostic size_t len, max; 93455557Sbostic 93555557Sbostic for (max = 0; *argv; ++argv) 93655557Sbostic if ((len = strlen(*argv)) > max) 93755557Sbostic max = len; 93855557Sbostic return (max); 93955557Sbostic } 940