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*53032Sbostic static char sccsid[] = "@(#)fts.c 5.34 (Berkeley) 03/20/92"; 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 *)); 2652209Sbostic static void fts_padjust __P((FTS *, void *)); 2752209Sbostic static int fts_palloc __P((FTS *, int)); 2852061Sbostic static FTSENT *fts_sort __P((FTS *, FTSENT *, int)); 2952061Sbostic static u_short fts_stat __P((FTS *, FTSENT *, int)); 3039800Sbostic 3152209Sbostic #define ISDOT(a) (a[0] == '.' && (!a[1] || a[1] == '.' && !a[2])) 3252209Sbostic 3345600Sbostic #define ISSET(opt) (sp->fts_options & opt) 3445600Sbostic #define SET(opt) (sp->fts_options |= opt) 3539800Sbostic 3645600Sbostic #define CHDIR(sp, path) (!ISSET(FTS_NOCHDIR) && chdir(path)) 3745600Sbostic #define FCHDIR(sp, fd) (!ISSET(FTS_NOCHDIR) && fchdir(fd)) 3839800Sbostic 3942299Sbostic /* fts_build flags */ 4052770Sbostic #define BCHILD 1 /* fts_children */ 4152770Sbostic #define BNAMES 2 /* fts_children, names only */ 4252770Sbostic #define BREAD 3 /* fts_read */ 4342299Sbostic 4439800Sbostic FTS * 4545600Sbostic fts_open(argv, options, compar) 4647155Sbostic char * const *argv; 4739800Sbostic register int options; 4847194Sbostic int (*compar)(); 4939800Sbostic { 5039800Sbostic register FTS *sp; 5139800Sbostic register FTSENT *p, *root; 5239800Sbostic register int nitems, maxlen; 5339800Sbostic FTSENT *parent, *tmp; 5447194Sbostic int len; 5539800Sbostic 5652770Sbostic /* Options check. */ 5752770Sbostic if (options & ~FTS_OPTIONMASK) { 5852770Sbostic errno = EINVAL; 5952770Sbostic return (NULL); 6052770Sbostic } 6152770Sbostic 6245600Sbostic /* Allocate/initialize the stream */ 6352150Sbostic if ((sp = malloc((u_int)sizeof(FTS))) == NULL) 6452150Sbostic return (NULL); 6539800Sbostic bzero(sp, sizeof(FTS)); 6642273Sbostic sp->fts_compar = compar; 6742273Sbostic sp->fts_options = options; 6839800Sbostic 6945600Sbostic /* Logical walks turn on NOCHDIR; symbolic links are too hard. */ 7045600Sbostic if (ISSET(FTS_LOGICAL)) 7145600Sbostic SET(FTS_NOCHDIR); 7245600Sbostic 7345600Sbostic /* Allocate/initialize root's parent. */ 7452155Sbostic if ((parent = fts_alloc(sp, "", 0)) == NULL) 7539800Sbostic goto mem1; 7647212Sbostic parent->fts_level = FTS_ROOTPARENTLEVEL; 7739800Sbostic 7845600Sbostic /* Allocate/initialize root(s). */ 7952160Sbostic for (root = NULL, maxlen = nitems = 0; *argv; ++argv, ++nitems) { 8052160Sbostic /* Don't allow zero-length paths. */ 8152209Sbostic if ((len = strlen(*argv)) == 0) { 8247194Sbostic errno = ENOENT; 8343070Sbostic goto mem2; 8447194Sbostic } 8547194Sbostic if (maxlen < len) 8647194Sbostic maxlen = len; 8752209Sbostic 8847194Sbostic p = fts_alloc(sp, *argv, len); 8949519Sbostic p->fts_level = FTS_ROOTLEVEL; 9049519Sbostic p->fts_parent = parent; 9152218Sbostic p->fts_accpath = p->fts_name; 9252344Sbostic p->fts_info = fts_stat(sp, p, ISSET(FTS_COMFOLLOW)); 9352209Sbostic 9452209Sbostic /* Command-line "." and ".." are real directories. */ 9552209Sbostic if (p->fts_info == FTS_DOT) 9652209Sbostic p->fts_info = FTS_D; 9752209Sbostic 9843070Sbostic /* 9945600Sbostic * If comparison routine supplied, traverse in sorted 10043070Sbostic * order; otherwise traverse in the order specified. 10143070Sbostic */ 10243070Sbostic if (compar) { 10343070Sbostic p->fts_link = root; 10443070Sbostic root = p; 10543070Sbostic } else { 10643070Sbostic p->fts_link = NULL; 10752155Sbostic if (root == NULL) 10843070Sbostic tmp = root = p; 10943070Sbostic else { 11043070Sbostic tmp->fts_link = p; 11143070Sbostic tmp = p; 11239800Sbostic } 11339800Sbostic } 11439800Sbostic } 11543070Sbostic if (compar && nitems > 1) 11645600Sbostic root = fts_sort(sp, root, nitems); 11739800Sbostic 11842281Sbostic /* 11945600Sbostic * Allocate a dummy pointer and make fts_read think that we've just 12052160Sbostic * finished the node before the root(s); set p->fts_info to FTS_INIT 12142281Sbostic * so that everything about the "current" node is ignored. 12242281Sbostic */ 12352155Sbostic if ((sp->fts_cur = fts_alloc(sp, "", 0)) == NULL) 12442281Sbostic goto mem2; 12542281Sbostic sp->fts_cur->fts_link = root; 12652160Sbostic sp->fts_cur->fts_info = FTS_INIT; 12742281Sbostic 12852160Sbostic /* 12952209Sbostic * Start out with more than 1K of path space, and enough, in any 13052160Sbostic * case, to hold the user's paths. 13152160Sbostic */ 13252209Sbostic if (fts_palloc(sp, MAX(maxlen, MAXPATHLEN))) 13342281Sbostic goto mem3; 13439800Sbostic 13539800Sbostic /* 13645600Sbostic * If using chdir(2), grab a file descriptor pointing to dot to insure 13742299Sbostic * that we can get back here; this could be avoided for some paths, 13842299Sbostic * but almost certainly not worth the effort. Slashes, symbolic links, 13942299Sbostic * and ".." are all fairly nasty problems. Note, if we can't get the 14042299Sbostic * descriptor we run anyway, just more slowly. 14139800Sbostic */ 14247194Sbostic if (!ISSET(FTS_NOCHDIR) && (sp->fts_rfd = open(".", O_RDONLY, 0)) < 0) 14345600Sbostic SET(FTS_NOCHDIR); 14439800Sbostic 14552150Sbostic return (sp); 14639800Sbostic 14747194Sbostic mem3: free(sp->fts_cur); 14839800Sbostic mem2: fts_lfree(root); 14947194Sbostic free(parent); 15047194Sbostic mem1: free(sp); 15152150Sbostic return (NULL); 15239800Sbostic } 15339800Sbostic 15447404Sbostic static void 15545600Sbostic fts_load(sp, p) 15645600Sbostic FTS *sp; 15739800Sbostic register FTSENT *p; 15839800Sbostic { 15939800Sbostic register int len; 16039800Sbostic register char *cp; 16139800Sbostic 16239800Sbostic /* 16347220Sbostic * Load the stream structure for the next traversal. Since we don't 16447220Sbostic * actually enter the directory until after the preorder visit, set 16547220Sbostic * the fts_accpath field specially so the chdir gets done to the right 16652209Sbostic * place and the user can access the first node. From fts_open it's 16752209Sbostic * known that the path will fit. 16839800Sbostic */ 16942273Sbostic len = p->fts_pathlen = p->fts_namelen; 17045600Sbostic bcopy(p->fts_name, sp->fts_path, len + 1); 17142273Sbostic if ((cp = rindex(p->fts_name, '/')) && (cp != p->fts_name || cp[1])) { 17239800Sbostic len = strlen(++cp); 17342273Sbostic bcopy(cp, p->fts_name, len + 1); 17442273Sbostic p->fts_namelen = len; 17539800Sbostic } 17645600Sbostic p->fts_accpath = p->fts_path = sp->fts_path; 17752209Sbostic sp->fts_dev = p->fts_dev; 17839800Sbostic } 17939800Sbostic 18052209Sbostic int 18145600Sbostic fts_close(sp) 18239800Sbostic FTS *sp; 18339800Sbostic { 18439800Sbostic register FTSENT *freep, *p; 18539800Sbostic int saved_errno; 18639800Sbostic 18752209Sbostic /* 18852209Sbostic * This still works if we haven't read anything -- the dummy structure 18952209Sbostic * points to the root list, so we step through to the end of the root 19052209Sbostic * list which has a valid parent pointer. 19152209Sbostic */ 19242281Sbostic if (sp->fts_cur) { 19352209Sbostic for (p = sp->fts_cur; p->fts_level >= FTS_ROOTLEVEL;) { 19442281Sbostic freep = p; 19542281Sbostic p = p->fts_link ? p->fts_link : p->fts_parent; 19647194Sbostic free(freep); 19739800Sbostic } 19847194Sbostic free(p); 19942281Sbostic } 20039800Sbostic 20145600Sbostic /* Free up child linked list, sort array, path buffer. */ 20242273Sbostic if (sp->fts_child) 20342273Sbostic fts_lfree(sp->fts_child); 20442273Sbostic if (sp->fts_array) 20547194Sbostic free(sp->fts_array); 20647194Sbostic free(sp->fts_path); 20739800Sbostic 20847194Sbostic /* Return to original directory, save errno if necessary. */ 20945600Sbostic if (!ISSET(FTS_NOCHDIR)) { 21047194Sbostic saved_errno = fchdir(sp->fts_rfd) ? errno : 0; 21147194Sbostic (void)close(sp->fts_rfd); 21239800Sbostic } 21339800Sbostic 21445600Sbostic /* Free up the stream pointer. */ 21547194Sbostic free(sp); 21639800Sbostic 21745600Sbostic /* Set errno and return. */ 21845600Sbostic if (!ISSET(FTS_NOCHDIR) && saved_errno) { 21939800Sbostic errno = saved_errno; 22052150Sbostic return (-1); 22139800Sbostic } 22252150Sbostic return (0); 22339800Sbostic } 22439800Sbostic 22547194Sbostic /* 22652160Sbostic * Special case a root of "/" so that slashes aren't appended which would 22752160Sbostic * cause paths to be written as "//foo". 22847194Sbostic */ 22947194Sbostic #define NAPPEND(p) \ 23047212Sbostic (p->fts_level == FTS_ROOTLEVEL && p->fts_pathlen == 1 && \ 23147194Sbostic p->fts_path[0] == '/' ? 0 : p->fts_pathlen) 23247194Sbostic 23339800Sbostic FTSENT * 23445600Sbostic fts_read(sp) 23539800Sbostic register FTS *sp; 23639800Sbostic { 23742299Sbostic register FTSENT *p, *tmp; 23839800Sbostic register int instr; 23947194Sbostic register char *t; 240*53032Sbostic int saved_errno; 24139800Sbostic 24245600Sbostic /* If finished or unrecoverable error, return NULL. */ 24352155Sbostic if (sp->fts_cur == NULL || ISSET(FTS_STOP)) 24452150Sbostic return (NULL); 24539800Sbostic 24645600Sbostic /* Set current node pointer. */ 24742273Sbostic p = sp->fts_cur; 24839800Sbostic 24945600Sbostic /* Save and zero out user instructions. */ 25042273Sbostic instr = p->fts_instr; 25147194Sbostic p->fts_instr = FTS_NOINSTR; 25239800Sbostic 25347194Sbostic /* Any type of file may be re-visited; re-stat and re-turn. */ 25439800Sbostic if (instr == FTS_AGAIN) { 25545600Sbostic p->fts_info = fts_stat(sp, p, 0); 25652150Sbostic return (p); 25739800Sbostic } 25839800Sbostic 25947194Sbostic /* 26047194Sbostic * Following a symlink -- SLNONE test allows application to see 26152290Sbostic * SLNONE and recover. If indirecting through a symlink, have 26252290Sbostic * keep a pointer to current location. If unable to get that 26352290Sbostic * pointer, follow fails. 26447194Sbostic */ 26547194Sbostic if (instr == FTS_FOLLOW && 26647194Sbostic (p->fts_info == FTS_SL || p->fts_info == FTS_SLNONE)) { 26745600Sbostic p->fts_info = fts_stat(sp, p, 1); 26852290Sbostic if (p->fts_info == FTS_D && !ISSET(FTS_NOCHDIR)) 26952290Sbostic if ((p->fts_symfd = open(".", O_RDONLY, 0)) < 0) { 27052290Sbostic p->fts_errno = errno; 27152290Sbostic p->fts_info = FTS_ERR; 27252290Sbostic } else 27352290Sbostic p->fts_flags |= FTS_SYMFOLLOW; 27452150Sbostic return (p); 27542299Sbostic } 27642299Sbostic 27745600Sbostic /* Directory in pre-order. */ 27842299Sbostic if (p->fts_info == FTS_D) { 27945600Sbostic /* If skipped or crossed mount point, do post-order visit. */ 28047194Sbostic if (instr == FTS_SKIP || 28152209Sbostic ISSET(FTS_XDEV) && p->fts_dev != sp->fts_dev) { 28252290Sbostic if (p->fts_flags & FTS_SYMFOLLOW) 28352290Sbostic (void)close(p->fts_symfd); 28442273Sbostic if (sp->fts_child) { 28542273Sbostic fts_lfree(sp->fts_child); 28642273Sbostic sp->fts_child = NULL; 28739800Sbostic } 28842301Sbostic p->fts_info = FTS_DP; 28952150Sbostic return (p); 29042299Sbostic } 29142299Sbostic 29252770Sbostic /* Rebuild if only got the names and now traversing. */ 29352770Sbostic if (sp->fts_child && sp->fts_options & FTS_NAMEONLY) { 29452770Sbostic sp->fts_options &= ~FTS_NAMEONLY; 29552770Sbostic fts_lfree(sp->fts_child); 29652770Sbostic sp->fts_child = NULL; 29752770Sbostic } 29852770Sbostic 29947194Sbostic /* 30047194Sbostic * Cd to the subdirectory, reading it if haven't already. If 30147194Sbostic * the read fails for any reason, or the directory is empty, 30247194Sbostic * the fts_info field of the current node is set by fts_build. 30347220Sbostic * If have already read and now fail to chdir, whack the list 30447220Sbostic * to make the names come out right, and set the parent state 30547220Sbostic * so the application will eventually get an error condition. 30647220Sbostic * If haven't read and fail to chdir, check to see if we're 30747220Sbostic * at the root node -- if so, we have to get back or the root 30847220Sbostic * node may be inaccessible. 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); 32147220Sbostic if (p->fts_level == FTS_ROOTLEVEL && 322*53032Sbostic !ISSET(FTS_NOCHDIR) && FCHDIR(sp, sp->fts_rfd)) { 323*53032Sbostic saved_errno = errno; 32452290Sbostic (void)close(sp->fts_rfd); 325*53032Sbostic errno = saved_errno; 32647220Sbostic SET(FTS_STOP); 32752150Sbostic return (NULL); 32847220Sbostic } 329*53032Sbostic p->fts_flags |= FTS_DONTCHDIR; 330*53032Sbostic p->fts_info = FTS_DP; 33152150Sbostic return (p); 33247220Sbostic } 33347194Sbostic p = sp->fts_child; 33442299Sbostic sp->fts_child = NULL; 33547194Sbostic goto name; 33639800Sbostic } 33739800Sbostic 33852278Sbostic /* Move to the next node on this level. */ 33942299Sbostic next: tmp = p; 34042299Sbostic if (p = p->fts_link) { 34147194Sbostic free(tmp); 34247194Sbostic 34347194Sbostic /* If reached the top, load the paths for the next root. */ 34447212Sbostic if (p->fts_level == FTS_ROOTLEVEL) { 34547404Sbostic fts_load(sp, p); 34652150Sbostic return (sp->fts_cur = p); 34747194Sbostic } 34842299Sbostic 34952290Sbostic /* 35052290Sbostic * User may have called fts_set on the node. If skipped, 35152290Sbostic * ignore. If followed, get a file descriptor so we can 35252290Sbostic * get back if necessary. 35352290Sbostic */ 35447194Sbostic if (p->fts_instr == FTS_SKIP) 35547194Sbostic goto next; 35647194Sbostic if (p->fts_instr == FTS_FOLLOW) { 35747194Sbostic p->fts_info = fts_stat(sp, p, 1); 35852290Sbostic if (p->fts_info == FTS_D && !ISSET(FTS_NOCHDIR)) 35952290Sbostic if ((p->fts_symfd = 36052290Sbostic open(".", O_RDONLY, 0)) < 0) { 36152290Sbostic p->fts_errno = errno; 36252290Sbostic p->fts_info = FTS_ERR; 36352290Sbostic } else 36452290Sbostic p->fts_flags |= FTS_SYMFOLLOW; 36547194Sbostic p->fts_instr = FTS_NOINSTR; 36647194Sbostic } 36742299Sbostic 36847194Sbostic name: t = sp->fts_path + NAPPEND(p->fts_parent); 36947194Sbostic *t++ = '/'; 37047194Sbostic bcopy(p->fts_name, t, p->fts_namelen + 1); 37152150Sbostic return (sp->fts_cur = p); 37239800Sbostic } 37339800Sbostic 37447220Sbostic /* Move up to the parent node. */ 37542299Sbostic p = tmp->fts_parent; 37647194Sbostic free(tmp); 37742299Sbostic 37847212Sbostic if (p->fts_level == FTS_ROOTPARENTLEVEL) { 37939800Sbostic /* 38045600Sbostic * Done; free everything up and set errno to 0 so the user 38139800Sbostic * can distinguish between error and EOF. 38239800Sbostic */ 38347194Sbostic free(p); 38439800Sbostic errno = 0; 38552150Sbostic return (sp->fts_cur = NULL); 38639800Sbostic } 38739800Sbostic 38842273Sbostic sp->fts_path[p->fts_pathlen] = '\0'; 38947194Sbostic 39047194Sbostic /* 39152290Sbostic * Change to starting directory. If at a root node or came through a 39252290Sbostic * symlink, go back through the file descriptor. Otherwise, just cd 39352290Sbostic * up one directory. 39447194Sbostic */ 39547220Sbostic if (p->fts_level == FTS_ROOTLEVEL) { 39647220Sbostic if (FCHDIR(sp, sp->fts_rfd)) { 397*53032Sbostic saved_errno = errno; 39852290Sbostic (void)close(sp->fts_rfd); 399*53032Sbostic errno = saved_errno; 40047220Sbostic SET(FTS_STOP); 40152150Sbostic return (NULL); 40247220Sbostic } 40352290Sbostic (void)close(sp->fts_rfd); 40452290Sbostic } else if (p->fts_flags & FTS_SYMFOLLOW) { 40552290Sbostic if (FCHDIR(sp, p->fts_symfd)) { 406*53032Sbostic saved_errno = errno; 40752290Sbostic (void)close(p->fts_symfd); 408*53032Sbostic errno = saved_errno; 40952290Sbostic SET(FTS_STOP); 41052290Sbostic return (NULL); 41152290Sbostic } 41252290Sbostic (void)close(p->fts_symfd); 41352290Sbostic } else if (!(p->fts_flags & FTS_DONTCHDIR)) { 41452278Sbostic if (CHDIR(sp, "..")) { 41552278Sbostic SET(FTS_STOP); 41652278Sbostic return (NULL); 41752278Sbostic } 41852278Sbostic } 41952290Sbostic p->fts_info = p->fts_errno ? FTS_ERR : FTS_DP; 42052150Sbostic return (sp->fts_cur = p); 42139800Sbostic } 42239800Sbostic 42339800Sbostic /* 42447220Sbostic * Fts_set takes the stream as an argument although it's not used in this 42539800Sbostic * implementation; it would be necessary if anyone wanted to add global 42645600Sbostic * semantics to fts using fts_set. An error return is allowed for similar 42745600Sbostic * reasons. 42839800Sbostic */ 42939800Sbostic /* ARGSUSED */ 43052209Sbostic int 43145600Sbostic fts_set(sp, p, instr) 43239800Sbostic FTS *sp; 43339800Sbostic FTSENT *p; 43439800Sbostic int instr; 43539800Sbostic { 43652770Sbostic if (instr && instr != FTS_AGAIN && instr != FTS_FOLLOW && 43752770Sbostic instr != FTS_NOINSTR && instr != FTS_SKIP) { 43852770Sbostic errno = EINVAL; 43952770Sbostic return (1); 44052770Sbostic } 44142273Sbostic p->fts_instr = instr; 44252150Sbostic return (0); 44339800Sbostic } 44439800Sbostic 44539800Sbostic FTSENT * 44652770Sbostic fts_children(sp, instr) 44739800Sbostic register FTS *sp; 44852770Sbostic int instr; 44939800Sbostic { 45042299Sbostic register FTSENT *p; 45142299Sbostic int fd; 45242299Sbostic 45352770Sbostic if (instr && instr != FTS_NAMEONLY) { 45452770Sbostic errno = EINVAL; 45552770Sbostic return (NULL); 45652770Sbostic } 45752770Sbostic 45845600Sbostic /* Set current node pointer. */ 45945600Sbostic p = sp->fts_cur; 46045600Sbostic 46139800Sbostic /* 46252160Sbostic * Errno set to 0 so user can distinguish empty directory from 46352160Sbostic * an error. 46439800Sbostic */ 46539800Sbostic errno = 0; 46652160Sbostic 46752160Sbostic /* Fatal errors stop here. */ 46852160Sbostic if (ISSET(FTS_STOP)) 46952150Sbostic return (NULL); 47045600Sbostic 47152160Sbostic /* Return logical hierarchy of user's arguments. */ 47252160Sbostic if (p->fts_info == FTS_INIT) 47352160Sbostic return (p->fts_link); 47452160Sbostic 47552290Sbostic /* 47652290Sbostic * If not a directory being visited in pre-order, stop here. Could 47752290Sbostic * allow FTS_DNR, assuming the user has fixed the problem, but the 47852290Sbostic * same effect is available with FTS_AGAIN. 47952290Sbostic */ 48052290Sbostic if (p->fts_info != FTS_D /* && p->fts_info != FTS_DNR */) 48152160Sbostic return (NULL); 48252160Sbostic 48345600Sbostic /* Free up any previous child list. */ 48442273Sbostic if (sp->fts_child) 48542273Sbostic fts_lfree(sp->fts_child); 48642299Sbostic 48752770Sbostic if (instr == FTS_NAMEONLY) { 48852770Sbostic sp->fts_options |= FTS_NAMEONLY; 48952770Sbostic instr = BNAMES; 49052770Sbostic } else 49152770Sbostic instr = BCHILD; 49252770Sbostic 49342299Sbostic /* 49445600Sbostic * If using chdir on a relative path and called BEFORE fts_read does 49547194Sbostic * its chdir to the root of a traversal, we can lose -- we need to 49647194Sbostic * chdir into the subdirectory, and we don't know where the current 49747194Sbostic * directory is, so we can't get back so that the upcoming chdir by 49847194Sbostic * fts_read will work. 49942299Sbostic */ 50047212Sbostic if (p->fts_level != FTS_ROOTLEVEL || p->fts_accpath[0] == '/' || 50145600Sbostic ISSET(FTS_NOCHDIR)) 50252770Sbostic return (sp->fts_child = fts_build(sp, instr)); 50342299Sbostic 50442299Sbostic if ((fd = open(".", O_RDONLY, 0)) < 0) 50552150Sbostic return (NULL); 50652770Sbostic sp->fts_child = fts_build(sp, instr); 50742299Sbostic if (fchdir(fd)) 50852150Sbostic return (NULL); 50942299Sbostic (void)close(fd); 51052150Sbostic return (sp->fts_child); 51139800Sbostic } 51239800Sbostic 51347194Sbostic /* 51447194Sbostic * This is the tricky part -- do not casually change *anything* in here. The 51547194Sbostic * idea is to build the linked list of entries that are used by fts_children 51647194Sbostic * and fts_read. There are lots of special cases. 51747194Sbostic * 51847194Sbostic * The real slowdown in walking the tree is the stat calls. If FTS_NOSTAT is 51947194Sbostic * set and it's a physical walk (so that symbolic links can't be directories), 52047194Sbostic * we assume that the number of subdirectories in a node is equal to the number 52147194Sbostic * of links to the parent. This allows stat calls to be skipped in any leaf 52247194Sbostic * directories and for any nodes after the directories in the parent node have 52347194Sbostic * been found. This empirically cuts the stat calls by about 2/3. 52447194Sbostic */ 52547155Sbostic static FTSENT * 52642299Sbostic fts_build(sp, type) 52739800Sbostic register FTS *sp; 52842299Sbostic int type; 52939800Sbostic { 53039800Sbostic register struct dirent *dp; 53139800Sbostic register FTSENT *p, *head; 53239800Sbostic register int nitems; 53352290Sbostic FTSENT *cur, *tail; 53439800Sbostic DIR *dirp; 53552209Sbostic void *adjaddr; 53652278Sbostic int cderrno, descend, len, level, maxlen, nlinks, saved_errno; 53739800Sbostic char *cp; 53839800Sbostic 53945600Sbostic /* Set current node pointer. */ 54047194Sbostic cur = sp->fts_cur; 54145600Sbostic 54247194Sbostic /* 54347194Sbostic * Open the directory for reading. If this fails, we're done. 54447194Sbostic * If being called from fts_read, set the fts_info field. 54547194Sbostic */ 54652155Sbostic if ((dirp = opendir(cur->fts_accpath)) == NULL) { 54752206Sbostic if (type == BREAD) { 54847194Sbostic cur->fts_info = FTS_DNR; 54952206Sbostic cur->fts_errno = errno; 55052206Sbostic } 55152150Sbostic return (NULL); 55239800Sbostic } 55339800Sbostic 55439800Sbostic /* 55547194Sbostic * Nlinks is the number of possible entries of type directory in the 55647194Sbostic * directory if we're cheating on stat calls, 0 if we're not doing 55747194Sbostic * any stat calls at all, -1 if we're doing stats on everything. 55839800Sbostic */ 55952770Sbostic if (type == BNAMES) 56052770Sbostic nlinks = 0; 56152770Sbostic else if (ISSET(FTS_NOSTAT) && ISSET(FTS_PHYSICAL)) 56252770Sbostic nlinks = cur->fts_nlink - (ISSET(FTS_SEEDOT) ? 0 : 2); 56352770Sbostic else 56452770Sbostic nlinks = -1; 56539800Sbostic 56652290Sbostic #ifdef notdef 56752290Sbostic (void)printf("nlinks == %d (cur: %d)\n", nlinks, cur->fts_nlink); 56852290Sbostic (void)printf("NOSTAT %d PHYSICAL %d SEEDOT %d\n", 56952290Sbostic ISSET(FTS_NOSTAT), ISSET(FTS_PHYSICAL), ISSET(FTS_SEEDOT)); 57052290Sbostic #endif 57147194Sbostic /* 57247194Sbostic * If we're going to need to stat anything or we want to descend 57347194Sbostic * and stay in the directory, chdir. If this fails we keep going. 57447194Sbostic * We won't be able to stat anything, but we can still return the 57547194Sbostic * names themselves. Note, that since fts_read won't be able to 57647194Sbostic * chdir into the directory, it will have to return different path 57747194Sbostic * names than before, i.e. "a/b" instead of "b". Since the node 57847194Sbostic * has already been visited in pre-order, have to wait until the 57952290Sbostic * post-order visit to return the error. There is a special case 58052290Sbostic * here, if there was nothing to stat then it's not an error to 58152290Sbostic * not be able to stat. This is all fairly nasty. If a program 58252290Sbostic * needed sorted entries or stat information, they had better be 58352290Sbostic * checking FTS_NS on the returned nodes. 58447194Sbostic */ 58545600Sbostic if (nlinks || type == BREAD) 58647194Sbostic if (FCHDIR(sp, dirfd(dirp))) { 58752290Sbostic if (nlinks && type == BREAD) 58852160Sbostic cur->fts_errno = errno; 58952290Sbostic descend = 0; 59052278Sbostic cderrno = errno; 59147194Sbostic } else { 59245600Sbostic descend = 1; 59352278Sbostic cderrno = 0; 59439962Sbostic } 59547194Sbostic else 59647194Sbostic descend = 0; 59739962Sbostic 59847194Sbostic /* 59947194Sbostic * Figure out the max file name length that can be stored in the 60047194Sbostic * current path -- the inner loop allocates more path as necessary. 60147194Sbostic * We really wouldn't have to do the maxlen calculations here, we 60247194Sbostic * could do them in fts_read before returning the path, but it's a 60347194Sbostic * lot easier here since the length is part of the dirent structure. 60447194Sbostic * 60552209Sbostic * If not changing directories set a pointer so that can just append 60652209Sbostic * each new name into the path. 60747194Sbostic */ 60847194Sbostic maxlen = sp->fts_pathlen - cur->fts_pathlen - 1; 60947194Sbostic len = NAPPEND(cur); 61047194Sbostic if (ISSET(FTS_NOCHDIR)) { 61147194Sbostic cp = sp->fts_path + len; 61247194Sbostic *cp++ = '/'; 61347194Sbostic } 61440939Sbostic 61547194Sbostic level = cur->fts_level + 1; 61640939Sbostic 61747194Sbostic /* Read the directory, attaching each entry to the `link' pointer. */ 61852209Sbostic adjaddr = NULL; 61952290Sbostic for (head = tail = NULL, nitems = 0; dp = readdir(dirp);) { 62047194Sbostic if (!ISSET(FTS_SEEDOT) && ISDOT(dp->d_name)) 62139800Sbostic continue; 62239800Sbostic 62352155Sbostic if ((p = fts_alloc(sp, dp->d_name, (int)dp->d_namlen)) == NULL) 62439800Sbostic goto mem1; 62539800Sbostic if (dp->d_namlen > maxlen) { 62652209Sbostic if (fts_palloc(sp, (int)dp->d_namlen)) { 62747194Sbostic /* 62847194Sbostic * No more memory for path or structures. Save 62947194Sbostic * errno, free up the current structure and the 63047194Sbostic * structures already allocated. 63147194Sbostic */ 63247194Sbostic mem1: saved_errno = errno; 63347194Sbostic if (p) 63447194Sbostic free(p); 63547194Sbostic fts_lfree(head); 63647194Sbostic (void)closedir(dirp); 63739800Sbostic errno = saved_errno; 63847194Sbostic cur->fts_info = FTS_ERR; 63947194Sbostic SET(FTS_STOP); 64052150Sbostic return (NULL); 64139800Sbostic } 64252209Sbostic adjaddr = sp->fts_path; 64342273Sbostic maxlen = sp->fts_pathlen - sp->fts_cur->fts_pathlen - 1; 64439800Sbostic } 64539800Sbostic 64642273Sbostic p->fts_pathlen = len + dp->d_namlen + 1; 64742273Sbostic p->fts_parent = sp->fts_cur; 64842273Sbostic p->fts_level = level; 64939800Sbostic 65052290Sbostic if (cderrno) { 65152290Sbostic if (nlinks) { 65252290Sbostic p->fts_info = FTS_NS; 65352290Sbostic p->fts_errno = cderrno; 65452290Sbostic } else 65552290Sbostic p->fts_info = FTS_NSOK; 65652290Sbostic p->fts_accpath = cur->fts_accpath; 65752290Sbostic } else if (nlinks) { 65847194Sbostic /* Build a file name for fts_stat to stat. */ 65947194Sbostic if (ISSET(FTS_NOCHDIR)) { 66047194Sbostic p->fts_accpath = p->fts_path; 66142273Sbostic bcopy(p->fts_name, cp, p->fts_namelen + 1); 66247194Sbostic } else 66347194Sbostic p->fts_accpath = p->fts_name; 66445600Sbostic p->fts_info = fts_stat(sp, p, 0); 66552209Sbostic if (nlinks > 0 && (p->fts_info == FTS_D || 66652209Sbostic p->fts_info == FTS_DC || p->fts_info == FTS_DOT)) 66739800Sbostic --nlinks; 66847194Sbostic } else { 66947194Sbostic p->fts_accpath = 67047194Sbostic ISSET(FTS_NOCHDIR) ? p->fts_path : p->fts_name; 67147194Sbostic p->fts_info = FTS_NSOK; 67247194Sbostic } 67339800Sbostic 67452290Sbostic /* We walk in directory order so "ls -f" doesn't get upset. */ 67552290Sbostic p->fts_link = NULL; 67652290Sbostic if (head == NULL) 67752290Sbostic head = tail = p; 67852290Sbostic else { 67952290Sbostic tail->fts_link = p; 68052290Sbostic tail = p; 68152290Sbostic } 68239800Sbostic ++nitems; 68339800Sbostic } 68439800Sbostic (void)closedir(dirp); 68539800Sbostic 68647194Sbostic /* 68752209Sbostic * If had to realloc the path, adjust the addresses for the rest 68852209Sbostic * of the tree. 68952209Sbostic */ 69052209Sbostic if (adjaddr) 69152209Sbostic fts_padjust(sp, adjaddr); 69252209Sbostic 69352209Sbostic /* 69447194Sbostic * If not changing directories, reset the path back to original 69547194Sbostic * state. 69647194Sbostic */ 69747194Sbostic if (ISSET(FTS_NOCHDIR)) { 69847194Sbostic if (cp - 1 > sp->fts_path) 69947194Sbostic --cp; 70047194Sbostic *cp = '\0'; 70147194Sbostic } 70242299Sbostic 70342299Sbostic /* 70447194Sbostic * If descended after called from fts_children or called from 70547194Sbostic * fts_read and didn't find anything, get back. If can't get 70652209Sbostic * back, done. 70742299Sbostic */ 70842299Sbostic if (descend && (!nitems || type == BCHILD) && CHDIR(sp, "..")) { 70947194Sbostic cur->fts_info = FTS_ERR; 71047194Sbostic SET(FTS_STOP); 71152150Sbostic return (NULL); 71239962Sbostic } 71339962Sbostic 714*53032Sbostic /* If didn't find anything, return NULL. */ 715*53032Sbostic if (!nitems) 71652150Sbostic return (NULL); 71739800Sbostic 71847194Sbostic /* Sort the entries. */ 71942273Sbostic if (sp->fts_compar && nitems > 1) 72045600Sbostic head = fts_sort(sp, head, nitems); 72152150Sbostic return (head); 72239800Sbostic } 72339800Sbostic 72445600Sbostic static u_short 72545600Sbostic fts_stat(sp, p, follow) 72645600Sbostic FTS *sp; 72739800Sbostic register FTSENT *p; 72845600Sbostic int follow; 72939800Sbostic { 73052160Sbostic register FTSENT *t; 73152160Sbostic register dev_t dev; 73252160Sbostic register ino_t ino; 73352209Sbostic struct stat *sbp, sb; 73447194Sbostic int saved_errno; 73547194Sbostic 73652209Sbostic /* If user needs stat info, stat buffer already allocated. */ 73752209Sbostic sbp = ISSET(FTS_NOSTAT) ? &sb : p->fts_statp; 73852209Sbostic 73939800Sbostic /* 74045600Sbostic * If doing a logical walk, or application requested FTS_FOLLOW, do 74147194Sbostic * a stat(2). If that fails, check for a non-existent symlink. If 74252160Sbostic * fail, set the errno from the stat call. 74339800Sbostic */ 74445600Sbostic if (ISSET(FTS_LOGICAL) || follow) { 74552209Sbostic if (stat(p->fts_accpath, sbp)) { 74647194Sbostic saved_errno = errno; 74752209Sbostic if (!lstat(p->fts_accpath, sbp)) { 74847194Sbostic errno = 0; 74952150Sbostic return (FTS_SLNONE); 75047194Sbostic } 75152160Sbostic p->fts_errno = saved_errno; 75252209Sbostic goto err; 75345600Sbostic } 75452209Sbostic } else if (lstat(p->fts_accpath, sbp)) { 75552160Sbostic p->fts_errno = errno; 75652209Sbostic err: bzero(sbp, sizeof(struct stat)); 75752150Sbostic return (FTS_NS); 75845600Sbostic } 75939800Sbostic 76052209Sbostic if (S_ISDIR(sbp->st_mode)) { 76152209Sbostic /* 76252209Sbostic * Set the device/inode. Used to find cycles and check for 76352209Sbostic * crossing mount points. Also remember the link count, used 76452209Sbostic * in fts_build to limit the number of stat calls. It is 76552209Sbostic * understood that these fields are only referenced if fts_info 76652209Sbostic * is set to FTS_D. 76752209Sbostic */ 76852209Sbostic dev = p->fts_dev = sbp->st_dev; 76952209Sbostic ino = p->fts_ino = sbp->st_ino; 77052209Sbostic p->fts_nlink = sbp->st_nlink; 77152209Sbostic 77252290Sbostic if (ISDOT(p->fts_name)) 77352290Sbostic return (FTS_DOT); 77452290Sbostic 77552209Sbostic /* 77652209Sbostic * Cycle detection is done by brute force when the directory 77752209Sbostic * is first encountered. If the tree gets deep enough or the 77852209Sbostic * number of symbolic links to directories is high enough, 77952209Sbostic * something faster might be worthwhile. 78052209Sbostic */ 78152209Sbostic for (t = p->fts_parent; 78252209Sbostic t->fts_level >= FTS_ROOTLEVEL; t = t->fts_parent) 78352209Sbostic if (ino == t->fts_ino && dev == t->fts_dev) { 78452073Sbostic p->fts_cycle = t; 78552150Sbostic return (FTS_DC); 78647194Sbostic } 78752150Sbostic return (FTS_D); 78847194Sbostic } 78952209Sbostic if (S_ISLNK(sbp->st_mode)) 79052150Sbostic return (FTS_SL); 79152209Sbostic if (S_ISREG(sbp->st_mode)) 79252150Sbostic return (FTS_F); 79352150Sbostic return (FTS_DEFAULT); 79439800Sbostic } 79539800Sbostic 79639800Sbostic static FTSENT * 79745600Sbostic fts_sort(sp, head, nitems) 79845600Sbostic FTS *sp; 79939800Sbostic FTSENT *head; 80039800Sbostic register int nitems; 80139800Sbostic { 80239800Sbostic register FTSENT **ap, *p; 80339800Sbostic 80439800Sbostic /* 80545600Sbostic * Construct an array of pointers to the structures and call qsort(3). 80639800Sbostic * Reassemble the array in the order returned by qsort. If unable to 80739800Sbostic * sort for memory reasons, return the directory entries in their 80839800Sbostic * current order. Allocate enough space for the current needs plus 80952209Sbostic * 40 so don't realloc one entry at a time. 81039800Sbostic */ 81145600Sbostic if (nitems > sp->fts_nitems) { 81245600Sbostic sp->fts_nitems = nitems + 40; 81352209Sbostic if ((sp->fts_array = realloc(sp->fts_array, 81452209Sbostic (size_t)(sp->fts_nitems * sizeof(FTSENT *)))) == NULL) { 81545600Sbostic sp->fts_nitems = 0; 81652150Sbostic return (head); 81739800Sbostic } 81839800Sbostic } 81945600Sbostic for (ap = sp->fts_array, p = head; p; p = p->fts_link) 82039800Sbostic *ap++ = p; 82145600Sbostic qsort((void *)sp->fts_array, nitems, sizeof(FTSENT *), sp->fts_compar); 82245600Sbostic for (head = *(ap = sp->fts_array); --nitems; ++ap) 82342273Sbostic ap[0]->fts_link = ap[1]; 82442273Sbostic ap[0]->fts_link = NULL; 82552150Sbostic return (head); 82639800Sbostic } 82739800Sbostic 82839800Sbostic static FTSENT * 82945600Sbostic fts_alloc(sp, name, len) 83045600Sbostic FTS *sp; 83139800Sbostic char *name; 83239800Sbostic register int len; 83339800Sbostic { 83439800Sbostic register FTSENT *p; 83552209Sbostic int needstat; 83639800Sbostic 83739800Sbostic /* 83852209Sbostic * Variable sized structures. The stat structure isn't necessary 83952209Sbostic * if the user doesn't need it, and the name is variable length. 84052209Sbostic * Allocate enough extra space after the structure to store them. 84139800Sbostic */ 84252579Sbostic needstat = ISSET(FTS_NOSTAT) ? 0 : ALIGN(sizeof(struct stat)); 84352209Sbostic if ((p = malloc((size_t)(sizeof(FTSENT) + len + 1 + needstat))) == NULL) 84452150Sbostic return (NULL); 84542273Sbostic bcopy(name, p->fts_name, len + 1); 84652209Sbostic if (needstat) 84752579Sbostic p->fts_statp = (struct stat *)ALIGN(p->fts_name + len + 1); 84842273Sbostic p->fts_namelen = len; 84945600Sbostic p->fts_path = sp->fts_path; 85052290Sbostic p->fts_errno = 0; 85152290Sbostic p->fts_flags = 0; 85247194Sbostic p->fts_instr = FTS_NOINSTR; 85345600Sbostic p->fts_number = 0; 85445600Sbostic p->fts_pointer = NULL; 85552150Sbostic return (p); 85639800Sbostic } 85739800Sbostic 85845600Sbostic static void 85939800Sbostic fts_lfree(head) 86039800Sbostic register FTSENT *head; 86139800Sbostic { 86239800Sbostic register FTSENT *p; 86339800Sbostic 86445600Sbostic /* Free a linked list of structures. */ 86539800Sbostic while (p = head) { 86642273Sbostic head = head->fts_link; 86747194Sbostic free(p); 86839800Sbostic } 86939800Sbostic } 87039800Sbostic 87139800Sbostic /* 87252209Sbostic * Allow essentially unlimited paths; find, rm, ls should all work on any tree. 87352209Sbostic * Most systems will allow creation of paths much longer than MAXPATHLEN, even 87452209Sbostic * though the kernel won't resolve them. Add the size (not just what's needed) 87552209Sbostic * plus 256 bytes so don't realloc the path 2 bytes at a time. 87639800Sbostic */ 87752209Sbostic static int 87852209Sbostic fts_palloc(sp, more) 87945600Sbostic FTS *sp; 88052209Sbostic int more; 88139800Sbostic { 88252209Sbostic 88352209Sbostic sp->fts_pathlen += more + 256; 88452209Sbostic sp->fts_path = realloc(sp->fts_path, (size_t)sp->fts_pathlen); 88552209Sbostic return (sp->fts_path == NULL); 88647155Sbostic } 88752209Sbostic 88852209Sbostic /* 88952209Sbostic * When the path is realloc'd, have to fix all of the pointers in structures 89052209Sbostic * already returned. 89152209Sbostic */ 89252209Sbostic static void 89352209Sbostic fts_padjust(sp, addr) 89452209Sbostic FTS *sp; 89552209Sbostic void *addr; 89652209Sbostic { 89752209Sbostic FTSENT *p; 89852209Sbostic 89952209Sbostic #define ADJUST(p) { \ 90052209Sbostic (p)->fts_accpath = addr + ((p)->fts_accpath - (p)->fts_path); \ 90152209Sbostic (p)->fts_path = addr; \ 90252209Sbostic } 90352209Sbostic /* Adjust the current set of children. */ 90452209Sbostic for (p = sp->fts_child; p; p = p->fts_link) 90552209Sbostic ADJUST(p); 90652209Sbostic 90752209Sbostic /* Adjust the rest of the tree. */ 90852209Sbostic for (p = sp->fts_cur; p->fts_level >= FTS_ROOTLEVEL;) { 90952209Sbostic ADJUST(p); 91052209Sbostic p = p->fts_link ? p->fts_link : p->fts_parent; 91152209Sbostic } 91252209Sbostic } 913