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*52290Sbostic static char sccsid[] = "@(#)fts.c 5.29 (Berkeley) 02/03/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 */ 4045600Sbostic #define BCHILD 1 /* from fts_children */ 4145600Sbostic #define BREAD 2 /* from fts_read */ 4242299Sbostic 4339800Sbostic FTS * 4445600Sbostic fts_open(argv, options, compar) 4547155Sbostic char * const *argv; 4639800Sbostic register int options; 4747194Sbostic int (*compar)(); 4839800Sbostic { 4939800Sbostic register FTS *sp; 5039800Sbostic register FTSENT *p, *root; 5139800Sbostic register int nitems, maxlen; 5239800Sbostic FTSENT *parent, *tmp; 5347194Sbostic int len; 5439800Sbostic 5545600Sbostic /* Allocate/initialize the stream */ 5652150Sbostic if ((sp = malloc((u_int)sizeof(FTS))) == NULL) 5752150Sbostic return (NULL); 5839800Sbostic bzero(sp, sizeof(FTS)); 5942273Sbostic sp->fts_compar = compar; 6042273Sbostic sp->fts_options = options; 6139800Sbostic 6245600Sbostic /* Logical walks turn on NOCHDIR; symbolic links are too hard. */ 6345600Sbostic if (ISSET(FTS_LOGICAL)) 6445600Sbostic SET(FTS_NOCHDIR); 6545600Sbostic 6645600Sbostic /* Allocate/initialize root's parent. */ 6752155Sbostic if ((parent = fts_alloc(sp, "", 0)) == NULL) 6839800Sbostic goto mem1; 6947212Sbostic parent->fts_level = FTS_ROOTPARENTLEVEL; 7039800Sbostic 7145600Sbostic /* Allocate/initialize root(s). */ 7252160Sbostic for (root = NULL, maxlen = nitems = 0; *argv; ++argv, ++nitems) { 7352160Sbostic /* Don't allow zero-length paths. */ 7452209Sbostic if ((len = strlen(*argv)) == 0) { 7547194Sbostic errno = ENOENT; 7643070Sbostic goto mem2; 7747194Sbostic } 7847194Sbostic if (maxlen < len) 7947194Sbostic maxlen = len; 8052209Sbostic 8147194Sbostic p = fts_alloc(sp, *argv, len); 8249519Sbostic p->fts_level = FTS_ROOTLEVEL; 8349519Sbostic p->fts_parent = parent; 8452218Sbostic p->fts_accpath = p->fts_name; 8552209Sbostic p->fts_info = fts_stat(sp, p, 0); 8652209Sbostic 8752209Sbostic /* Command-line "." and ".." are real directories. */ 8852209Sbostic if (p->fts_info == FTS_DOT) 8952209Sbostic p->fts_info = FTS_D; 9052209Sbostic 9143070Sbostic /* 9245600Sbostic * If comparison routine supplied, traverse in sorted 9343070Sbostic * order; otherwise traverse in the order specified. 9443070Sbostic */ 9543070Sbostic if (compar) { 9643070Sbostic p->fts_link = root; 9743070Sbostic root = p; 9843070Sbostic } else { 9943070Sbostic p->fts_link = NULL; 10052155Sbostic if (root == NULL) 10143070Sbostic tmp = root = p; 10243070Sbostic else { 10343070Sbostic tmp->fts_link = p; 10443070Sbostic tmp = p; 10539800Sbostic } 10639800Sbostic } 10739800Sbostic } 10843070Sbostic if (compar && nitems > 1) 10945600Sbostic root = fts_sort(sp, root, nitems); 11039800Sbostic 11142281Sbostic /* 11245600Sbostic * Allocate a dummy pointer and make fts_read think that we've just 11352160Sbostic * finished the node before the root(s); set p->fts_info to FTS_INIT 11442281Sbostic * so that everything about the "current" node is ignored. 11542281Sbostic */ 11652155Sbostic if ((sp->fts_cur = fts_alloc(sp, "", 0)) == NULL) 11742281Sbostic goto mem2; 11842281Sbostic sp->fts_cur->fts_link = root; 11952160Sbostic sp->fts_cur->fts_info = FTS_INIT; 12042281Sbostic 12152160Sbostic /* 12252209Sbostic * Start out with more than 1K of path space, and enough, in any 12352160Sbostic * case, to hold the user's paths. 12452160Sbostic */ 12552209Sbostic if (fts_palloc(sp, MAX(maxlen, MAXPATHLEN))) 12642281Sbostic goto mem3; 12739800Sbostic 12839800Sbostic /* 12945600Sbostic * If using chdir(2), grab a file descriptor pointing to dot to insure 13042299Sbostic * that we can get back here; this could be avoided for some paths, 13142299Sbostic * but almost certainly not worth the effort. Slashes, symbolic links, 13242299Sbostic * and ".." are all fairly nasty problems. Note, if we can't get the 13342299Sbostic * descriptor we run anyway, just more slowly. 13439800Sbostic */ 13547194Sbostic if (!ISSET(FTS_NOCHDIR) && (sp->fts_rfd = open(".", O_RDONLY, 0)) < 0) 13645600Sbostic SET(FTS_NOCHDIR); 13739800Sbostic 13852150Sbostic return (sp); 13939800Sbostic 14047194Sbostic mem3: free(sp->fts_cur); 14139800Sbostic mem2: fts_lfree(root); 14247194Sbostic free(parent); 14347194Sbostic mem1: free(sp); 14452150Sbostic return (NULL); 14539800Sbostic } 14639800Sbostic 14747404Sbostic static void 14845600Sbostic fts_load(sp, p) 14945600Sbostic FTS *sp; 15039800Sbostic register FTSENT *p; 15139800Sbostic { 15239800Sbostic register int len; 15339800Sbostic register char *cp; 15439800Sbostic 15539800Sbostic /* 15647220Sbostic * Load the stream structure for the next traversal. Since we don't 15747220Sbostic * actually enter the directory until after the preorder visit, set 15847220Sbostic * the fts_accpath field specially so the chdir gets done to the right 15952209Sbostic * place and the user can access the first node. From fts_open it's 16052209Sbostic * known that the path will fit. 16139800Sbostic */ 16242273Sbostic len = p->fts_pathlen = p->fts_namelen; 16345600Sbostic bcopy(p->fts_name, sp->fts_path, len + 1); 16442273Sbostic if ((cp = rindex(p->fts_name, '/')) && (cp != p->fts_name || cp[1])) { 16539800Sbostic len = strlen(++cp); 16642273Sbostic bcopy(cp, p->fts_name, len + 1); 16742273Sbostic p->fts_namelen = len; 16839800Sbostic } 16945600Sbostic p->fts_accpath = p->fts_path = sp->fts_path; 17052209Sbostic sp->fts_dev = p->fts_dev; 17139800Sbostic } 17239800Sbostic 17352209Sbostic int 17445600Sbostic fts_close(sp) 17539800Sbostic FTS *sp; 17639800Sbostic { 17739800Sbostic register FTSENT *freep, *p; 17839800Sbostic int saved_errno; 17939800Sbostic 18052209Sbostic /* 18152209Sbostic * This still works if we haven't read anything -- the dummy structure 18252209Sbostic * points to the root list, so we step through to the end of the root 18352209Sbostic * list which has a valid parent pointer. 18452209Sbostic */ 18542281Sbostic if (sp->fts_cur) { 18652209Sbostic for (p = sp->fts_cur; p->fts_level >= FTS_ROOTLEVEL;) { 18742281Sbostic freep = p; 18842281Sbostic p = p->fts_link ? p->fts_link : p->fts_parent; 18947194Sbostic free(freep); 19039800Sbostic } 19147194Sbostic free(p); 19242281Sbostic } 19339800Sbostic 19445600Sbostic /* Free up child linked list, sort array, path buffer. */ 19542273Sbostic if (sp->fts_child) 19642273Sbostic fts_lfree(sp->fts_child); 19742273Sbostic if (sp->fts_array) 19847194Sbostic free(sp->fts_array); 19947194Sbostic free(sp->fts_path); 20039800Sbostic 20147194Sbostic /* Return to original directory, save errno if necessary. */ 20245600Sbostic if (!ISSET(FTS_NOCHDIR)) { 20347194Sbostic saved_errno = fchdir(sp->fts_rfd) ? errno : 0; 20447194Sbostic (void)close(sp->fts_rfd); 20539800Sbostic } 20639800Sbostic 20745600Sbostic /* Free up the stream pointer. */ 20847194Sbostic free(sp); 20939800Sbostic 21045600Sbostic /* Set errno and return. */ 21145600Sbostic if (!ISSET(FTS_NOCHDIR) && saved_errno) { 21239800Sbostic errno = saved_errno; 21352150Sbostic return (-1); 21439800Sbostic } 21552150Sbostic return (0); 21639800Sbostic } 21739800Sbostic 21847194Sbostic /* 21952160Sbostic * Special case a root of "/" so that slashes aren't appended which would 22052160Sbostic * cause paths to be written as "//foo". 22147194Sbostic */ 22247194Sbostic #define NAPPEND(p) \ 22347212Sbostic (p->fts_level == FTS_ROOTLEVEL && p->fts_pathlen == 1 && \ 22447194Sbostic p->fts_path[0] == '/' ? 0 : p->fts_pathlen) 22547194Sbostic 22639800Sbostic FTSENT * 22745600Sbostic fts_read(sp) 22839800Sbostic register FTS *sp; 22939800Sbostic { 23042299Sbostic register FTSENT *p, *tmp; 23139800Sbostic register int instr; 23247194Sbostic register char *t; 23339800Sbostic 23445600Sbostic /* If finished or unrecoverable error, return NULL. */ 23552155Sbostic if (sp->fts_cur == NULL || ISSET(FTS_STOP)) 23652150Sbostic return (NULL); 23739800Sbostic 23845600Sbostic /* Set current node pointer. */ 23942273Sbostic p = sp->fts_cur; 24039800Sbostic 24145600Sbostic /* Save and zero out user instructions. */ 24242273Sbostic instr = p->fts_instr; 24347194Sbostic p->fts_instr = FTS_NOINSTR; 24439800Sbostic 24547194Sbostic /* Any type of file may be re-visited; re-stat and re-turn. */ 24639800Sbostic if (instr == FTS_AGAIN) { 24745600Sbostic p->fts_info = fts_stat(sp, p, 0); 24852150Sbostic return (p); 24939800Sbostic } 25039800Sbostic 25147194Sbostic /* 25247194Sbostic * Following a symlink -- SLNONE test allows application to see 253*52290Sbostic * SLNONE and recover. If indirecting through a symlink, have 254*52290Sbostic * keep a pointer to current location. If unable to get that 255*52290Sbostic * pointer, follow fails. 25647194Sbostic */ 25747194Sbostic if (instr == FTS_FOLLOW && 25847194Sbostic (p->fts_info == FTS_SL || p->fts_info == FTS_SLNONE)) { 25945600Sbostic p->fts_info = fts_stat(sp, p, 1); 260*52290Sbostic if (p->fts_info == FTS_D && !ISSET(FTS_NOCHDIR)) 261*52290Sbostic if ((p->fts_symfd = open(".", O_RDONLY, 0)) < 0) { 262*52290Sbostic p->fts_errno = errno; 263*52290Sbostic p->fts_info = FTS_ERR; 264*52290Sbostic } else 265*52290Sbostic p->fts_flags |= FTS_SYMFOLLOW; 26652150Sbostic return (p); 26742299Sbostic } 26842299Sbostic 26945600Sbostic /* Directory in pre-order. */ 27042299Sbostic if (p->fts_info == FTS_D) { 27145600Sbostic /* If skipped or crossed mount point, do post-order visit. */ 27247194Sbostic if (instr == FTS_SKIP || 27352209Sbostic ISSET(FTS_XDEV) && p->fts_dev != sp->fts_dev) { 274*52290Sbostic if (p->fts_flags & FTS_SYMFOLLOW) 275*52290Sbostic (void)close(p->fts_symfd); 27642273Sbostic if (sp->fts_child) { 27742273Sbostic fts_lfree(sp->fts_child); 27842273Sbostic sp->fts_child = NULL; 27939800Sbostic } 28042301Sbostic p->fts_info = FTS_DP; 28152150Sbostic return (p); 28242299Sbostic } 28342299Sbostic 28447194Sbostic /* 28547194Sbostic * Cd to the subdirectory, reading it if haven't already. If 28647194Sbostic * the read fails for any reason, or the directory is empty, 28747194Sbostic * the fts_info field of the current node is set by fts_build. 28847220Sbostic * If have already read and now fail to chdir, whack the list 28947220Sbostic * to make the names come out right, and set the parent state 29047220Sbostic * so the application will eventually get an error condition. 29147220Sbostic * If haven't read and fail to chdir, check to see if we're 29247220Sbostic * at the root node -- if so, we have to get back or the root 29347220Sbostic * node may be inaccessible. 29447194Sbostic */ 29547194Sbostic if (sp->fts_child) { 29642299Sbostic if (CHDIR(sp, p->fts_accpath)) { 29752278Sbostic p->fts_errno = errno; 298*52290Sbostic p->fts_flags |= FTS_DONTCHDIR; 29947194Sbostic for (p = sp->fts_child; p; p = p->fts_link) 30047194Sbostic p->fts_accpath = 30147194Sbostic p->fts_parent->fts_accpath; 30242299Sbostic } 30352155Sbostic } else if ((sp->fts_child = fts_build(sp, BREAD)) == NULL) { 304*52290Sbostic p->fts_flags |= FTS_DONTCHDIR; 305*52290Sbostic if (ISSET(FTS_STOP)) 30652150Sbostic return (NULL); 30747220Sbostic if (p->fts_level == FTS_ROOTLEVEL && 30847220Sbostic FCHDIR(sp, sp->fts_rfd)) { 309*52290Sbostic (void)close(sp->fts_rfd); 31047220Sbostic SET(FTS_STOP); 31152150Sbostic return (NULL); 31247220Sbostic } 313*52290Sbostic (void)close(sp->fts_rfd); 31452150Sbostic return (p); 31547220Sbostic } 31647194Sbostic p = sp->fts_child; 31742299Sbostic sp->fts_child = NULL; 31847194Sbostic goto name; 31939800Sbostic } 32039800Sbostic 32152278Sbostic /* Move to the next node on this level. */ 32242299Sbostic next: tmp = p; 32342299Sbostic if (p = p->fts_link) { 32447194Sbostic free(tmp); 32547194Sbostic 32647194Sbostic /* If reached the top, load the paths for the next root. */ 32747212Sbostic if (p->fts_level == FTS_ROOTLEVEL) { 32847404Sbostic fts_load(sp, p); 32952150Sbostic return (sp->fts_cur = p); 33047194Sbostic } 33142299Sbostic 332*52290Sbostic /* 333*52290Sbostic * User may have called fts_set on the node. If skipped, 334*52290Sbostic * ignore. If followed, get a file descriptor so we can 335*52290Sbostic * get back if necessary. 336*52290Sbostic */ 33747194Sbostic if (p->fts_instr == FTS_SKIP) 33847194Sbostic goto next; 33947194Sbostic if (p->fts_instr == FTS_FOLLOW) { 34047194Sbostic p->fts_info = fts_stat(sp, p, 1); 341*52290Sbostic if (p->fts_info == FTS_D && !ISSET(FTS_NOCHDIR)) 342*52290Sbostic if ((p->fts_symfd = 343*52290Sbostic open(".", O_RDONLY, 0)) < 0) { 344*52290Sbostic p->fts_errno = errno; 345*52290Sbostic p->fts_info = FTS_ERR; 346*52290Sbostic } else 347*52290Sbostic p->fts_flags |= FTS_SYMFOLLOW; 34847194Sbostic p->fts_instr = FTS_NOINSTR; 34947194Sbostic } 35042299Sbostic 35147194Sbostic name: t = sp->fts_path + NAPPEND(p->fts_parent); 35247194Sbostic *t++ = '/'; 35347194Sbostic bcopy(p->fts_name, t, p->fts_namelen + 1); 35452150Sbostic return (sp->fts_cur = p); 35539800Sbostic } 35639800Sbostic 35747220Sbostic /* Move up to the parent node. */ 35842299Sbostic p = tmp->fts_parent; 35947194Sbostic free(tmp); 36042299Sbostic 36147212Sbostic if (p->fts_level == FTS_ROOTPARENTLEVEL) { 36239800Sbostic /* 36345600Sbostic * Done; free everything up and set errno to 0 so the user 36439800Sbostic * can distinguish between error and EOF. 36539800Sbostic */ 36647194Sbostic free(p); 36739800Sbostic errno = 0; 36852150Sbostic return (sp->fts_cur = NULL); 36939800Sbostic } 37039800Sbostic 37142273Sbostic sp->fts_path[p->fts_pathlen] = '\0'; 37247194Sbostic 37347194Sbostic /* 374*52290Sbostic * Change to starting directory. If at a root node or came through a 375*52290Sbostic * symlink, go back through the file descriptor. Otherwise, just cd 376*52290Sbostic * up one directory. 37747194Sbostic */ 37847220Sbostic if (p->fts_level == FTS_ROOTLEVEL) { 37947220Sbostic if (FCHDIR(sp, sp->fts_rfd)) { 380*52290Sbostic (void)close(sp->fts_rfd); 38147220Sbostic SET(FTS_STOP); 38252150Sbostic return (NULL); 38347220Sbostic } 384*52290Sbostic (void)close(sp->fts_rfd); 385*52290Sbostic } else if (p->fts_flags & FTS_SYMFOLLOW) { 386*52290Sbostic if (FCHDIR(sp, p->fts_symfd)) { 387*52290Sbostic (void)close(p->fts_symfd); 388*52290Sbostic SET(FTS_STOP); 389*52290Sbostic return (NULL); 390*52290Sbostic } 391*52290Sbostic (void)close(p->fts_symfd); 392*52290Sbostic } else if (!(p->fts_flags & FTS_DONTCHDIR)) { 39352278Sbostic if (CHDIR(sp, "..")) { 39452278Sbostic SET(FTS_STOP); 39552278Sbostic return (NULL); 39652278Sbostic } 39752278Sbostic } 398*52290Sbostic p->fts_info = p->fts_errno ? FTS_ERR : FTS_DP; 39952150Sbostic return (sp->fts_cur = p); 40039800Sbostic } 40139800Sbostic 40239800Sbostic /* 40347220Sbostic * Fts_set takes the stream as an argument although it's not used in this 40439800Sbostic * implementation; it would be necessary if anyone wanted to add global 40545600Sbostic * semantics to fts using fts_set. An error return is allowed for similar 40645600Sbostic * reasons. 40739800Sbostic */ 40839800Sbostic /* ARGSUSED */ 40952209Sbostic int 41045600Sbostic fts_set(sp, p, instr) 41139800Sbostic FTS *sp; 41239800Sbostic FTSENT *p; 41339800Sbostic int instr; 41439800Sbostic { 41542273Sbostic p->fts_instr = instr; 41652150Sbostic return (0); 41739800Sbostic } 41839800Sbostic 41939800Sbostic FTSENT * 42045600Sbostic fts_children(sp) 42139800Sbostic register FTS *sp; 42239800Sbostic { 42342299Sbostic register FTSENT *p; 42442299Sbostic int fd; 42542299Sbostic 42645600Sbostic /* Set current node pointer. */ 42745600Sbostic p = sp->fts_cur; 42845600Sbostic 42939800Sbostic /* 43052160Sbostic * Errno set to 0 so user can distinguish empty directory from 43152160Sbostic * an error. 43239800Sbostic */ 43339800Sbostic errno = 0; 43452160Sbostic 43552160Sbostic /* Fatal errors stop here. */ 43652160Sbostic if (ISSET(FTS_STOP)) 43752150Sbostic return (NULL); 43845600Sbostic 43952160Sbostic /* Return logical hierarchy of user's arguments. */ 44052160Sbostic if (p->fts_info == FTS_INIT) 44152160Sbostic return (p->fts_link); 44252160Sbostic 443*52290Sbostic /* 444*52290Sbostic * If not a directory being visited in pre-order, stop here. Could 445*52290Sbostic * allow FTS_DNR, assuming the user has fixed the problem, but the 446*52290Sbostic * same effect is available with FTS_AGAIN. 447*52290Sbostic */ 448*52290Sbostic if (p->fts_info != FTS_D /* && p->fts_info != FTS_DNR */) 44952160Sbostic return (NULL); 45052160Sbostic 45145600Sbostic /* Free up any previous child list. */ 45242273Sbostic if (sp->fts_child) 45342273Sbostic fts_lfree(sp->fts_child); 45442299Sbostic 45542299Sbostic /* 45645600Sbostic * If using chdir on a relative path and called BEFORE fts_read does 45747194Sbostic * its chdir to the root of a traversal, we can lose -- we need to 45847194Sbostic * chdir into the subdirectory, and we don't know where the current 45947194Sbostic * directory is, so we can't get back so that the upcoming chdir by 46047194Sbostic * fts_read will work. 46142299Sbostic */ 46247212Sbostic if (p->fts_level != FTS_ROOTLEVEL || p->fts_accpath[0] == '/' || 46345600Sbostic ISSET(FTS_NOCHDIR)) 46452150Sbostic return (sp->fts_child = fts_build(sp, BCHILD)); 46542299Sbostic 46642299Sbostic if ((fd = open(".", O_RDONLY, 0)) < 0) 46752150Sbostic return (NULL); 46842299Sbostic sp->fts_child = fts_build(sp, BCHILD); 46942299Sbostic if (fchdir(fd)) 47052150Sbostic return (NULL); 47142299Sbostic (void)close(fd); 47252150Sbostic return (sp->fts_child); 47339800Sbostic } 47439800Sbostic 47547194Sbostic /* 47647194Sbostic * This is the tricky part -- do not casually change *anything* in here. The 47747194Sbostic * idea is to build the linked list of entries that are used by fts_children 47847194Sbostic * and fts_read. There are lots of special cases. 47947194Sbostic * 48047194Sbostic * The real slowdown in walking the tree is the stat calls. If FTS_NOSTAT is 48147194Sbostic * set and it's a physical walk (so that symbolic links can't be directories), 48247194Sbostic * we assume that the number of subdirectories in a node is equal to the number 48347194Sbostic * of links to the parent. This allows stat calls to be skipped in any leaf 48447194Sbostic * directories and for any nodes after the directories in the parent node have 48547194Sbostic * been found. This empirically cuts the stat calls by about 2/3. 48647194Sbostic */ 48747155Sbostic static FTSENT * 48842299Sbostic fts_build(sp, type) 48939800Sbostic register FTS *sp; 49042299Sbostic int type; 49139800Sbostic { 49239800Sbostic register struct dirent *dp; 49339800Sbostic register FTSENT *p, *head; 49439800Sbostic register int nitems; 495*52290Sbostic FTSENT *cur, *tail; 49639800Sbostic DIR *dirp; 49752209Sbostic void *adjaddr; 49852278Sbostic int cderrno, descend, len, level, maxlen, nlinks, saved_errno; 49939800Sbostic char *cp; 50039800Sbostic 50145600Sbostic /* Set current node pointer. */ 50247194Sbostic cur = sp->fts_cur; 50345600Sbostic 50447194Sbostic /* 50547194Sbostic * Open the directory for reading. If this fails, we're done. 50647194Sbostic * If being called from fts_read, set the fts_info field. 50747194Sbostic */ 50852155Sbostic if ((dirp = opendir(cur->fts_accpath)) == NULL) { 50952206Sbostic if (type == BREAD) { 51047194Sbostic cur->fts_info = FTS_DNR; 51152206Sbostic cur->fts_errno = errno; 51252206Sbostic } 51352150Sbostic return (NULL); 51439800Sbostic } 51539800Sbostic 51639800Sbostic /* 51747194Sbostic * Nlinks is the number of possible entries of type directory in the 51847194Sbostic * directory if we're cheating on stat calls, 0 if we're not doing 51947194Sbostic * any stat calls at all, -1 if we're doing stats on everything. 52039800Sbostic */ 521*52290Sbostic nlinks = ISSET(FTS_NOSTAT) && ISSET(FTS_PHYSICAL) ? 52252209Sbostic cur->fts_nlink - (ISSET(FTS_SEEDOT) ? 0 : 2) : -1; 52339800Sbostic 524*52290Sbostic #ifdef notdef 525*52290Sbostic (void)printf("nlinks == %d (cur: %d)\n", nlinks, cur->fts_nlink); 526*52290Sbostic (void)printf("NOSTAT %d PHYSICAL %d SEEDOT %d\n", 527*52290Sbostic ISSET(FTS_NOSTAT), ISSET(FTS_PHYSICAL), ISSET(FTS_SEEDOT)); 528*52290Sbostic #endif 52947194Sbostic /* 53047194Sbostic * If we're going to need to stat anything or we want to descend 53147194Sbostic * and stay in the directory, chdir. If this fails we keep going. 53247194Sbostic * We won't be able to stat anything, but we can still return the 53347194Sbostic * names themselves. Note, that since fts_read won't be able to 53447194Sbostic * chdir into the directory, it will have to return different path 53547194Sbostic * names than before, i.e. "a/b" instead of "b". Since the node 53647194Sbostic * has already been visited in pre-order, have to wait until the 537*52290Sbostic * post-order visit to return the error. There is a special case 538*52290Sbostic * here, if there was nothing to stat then it's not an error to 539*52290Sbostic * not be able to stat. This is all fairly nasty. If a program 540*52290Sbostic * needed sorted entries or stat information, they had better be 541*52290Sbostic * checking FTS_NS on the returned nodes. 54247194Sbostic */ 54345600Sbostic if (nlinks || type == BREAD) 54447194Sbostic if (FCHDIR(sp, dirfd(dirp))) { 545*52290Sbostic if (nlinks && type == BREAD) 54652160Sbostic cur->fts_errno = errno; 547*52290Sbostic descend = 0; 54852278Sbostic cderrno = errno; 54947194Sbostic } else { 55045600Sbostic descend = 1; 55152278Sbostic cderrno = 0; 55239962Sbostic } 55347194Sbostic else 55447194Sbostic descend = 0; 55539962Sbostic 55647194Sbostic /* 55747194Sbostic * Figure out the max file name length that can be stored in the 55847194Sbostic * current path -- the inner loop allocates more path as necessary. 55947194Sbostic * We really wouldn't have to do the maxlen calculations here, we 56047194Sbostic * could do them in fts_read before returning the path, but it's a 56147194Sbostic * lot easier here since the length is part of the dirent structure. 56247194Sbostic * 56352209Sbostic * If not changing directories set a pointer so that can just append 56452209Sbostic * each new name into the path. 56547194Sbostic */ 56647194Sbostic maxlen = sp->fts_pathlen - cur->fts_pathlen - 1; 56747194Sbostic len = NAPPEND(cur); 56847194Sbostic if (ISSET(FTS_NOCHDIR)) { 56947194Sbostic cp = sp->fts_path + len; 57047194Sbostic *cp++ = '/'; 57147194Sbostic } 57240939Sbostic 57347194Sbostic level = cur->fts_level + 1; 57440939Sbostic 57547194Sbostic /* Read the directory, attaching each entry to the `link' pointer. */ 57652209Sbostic adjaddr = NULL; 577*52290Sbostic for (head = tail = NULL, nitems = 0; dp = readdir(dirp);) { 57847194Sbostic if (!ISSET(FTS_SEEDOT) && ISDOT(dp->d_name)) 57939800Sbostic continue; 58039800Sbostic 58152155Sbostic if ((p = fts_alloc(sp, dp->d_name, (int)dp->d_namlen)) == NULL) 58239800Sbostic goto mem1; 58339800Sbostic if (dp->d_namlen > maxlen) { 58452209Sbostic if (fts_palloc(sp, (int)dp->d_namlen)) { 58547194Sbostic /* 58647194Sbostic * No more memory for path or structures. Save 58747194Sbostic * errno, free up the current structure and the 58847194Sbostic * structures already allocated. 58947194Sbostic */ 59047194Sbostic mem1: saved_errno = errno; 59147194Sbostic if (p) 59247194Sbostic free(p); 59347194Sbostic fts_lfree(head); 59447194Sbostic (void)closedir(dirp); 59539800Sbostic errno = saved_errno; 59647194Sbostic cur->fts_info = FTS_ERR; 59747194Sbostic SET(FTS_STOP); 59852150Sbostic return (NULL); 59939800Sbostic } 60052209Sbostic adjaddr = sp->fts_path; 60142273Sbostic maxlen = sp->fts_pathlen - sp->fts_cur->fts_pathlen - 1; 60239800Sbostic } 60339800Sbostic 60442273Sbostic p->fts_pathlen = len + dp->d_namlen + 1; 60542273Sbostic p->fts_parent = sp->fts_cur; 60642273Sbostic p->fts_level = level; 60739800Sbostic 608*52290Sbostic if (cderrno) { 609*52290Sbostic if (nlinks) { 610*52290Sbostic p->fts_info = FTS_NS; 611*52290Sbostic p->fts_errno = cderrno; 612*52290Sbostic } else 613*52290Sbostic p->fts_info = FTS_NSOK; 614*52290Sbostic p->fts_accpath = cur->fts_accpath; 615*52290Sbostic } else if (nlinks) { 61647194Sbostic /* Build a file name for fts_stat to stat. */ 61747194Sbostic if (ISSET(FTS_NOCHDIR)) { 61847194Sbostic p->fts_accpath = p->fts_path; 61942273Sbostic bcopy(p->fts_name, cp, p->fts_namelen + 1); 62047194Sbostic } else 62147194Sbostic p->fts_accpath = p->fts_name; 62245600Sbostic p->fts_info = fts_stat(sp, p, 0); 62352209Sbostic if (nlinks > 0 && (p->fts_info == FTS_D || 62452209Sbostic p->fts_info == FTS_DC || p->fts_info == FTS_DOT)) 62539800Sbostic --nlinks; 62647194Sbostic } else { 62747194Sbostic p->fts_accpath = 62847194Sbostic ISSET(FTS_NOCHDIR) ? p->fts_path : p->fts_name; 62947194Sbostic p->fts_info = FTS_NSOK; 63047194Sbostic } 63139800Sbostic 632*52290Sbostic /* We walk in directory order so "ls -f" doesn't get upset. */ 633*52290Sbostic p->fts_link = NULL; 634*52290Sbostic if (head == NULL) 635*52290Sbostic head = tail = p; 636*52290Sbostic else { 637*52290Sbostic tail->fts_link = p; 638*52290Sbostic tail = p; 639*52290Sbostic } 64039800Sbostic ++nitems; 64139800Sbostic } 64239800Sbostic (void)closedir(dirp); 64339800Sbostic 64447194Sbostic /* 64552209Sbostic * If had to realloc the path, adjust the addresses for the rest 64652209Sbostic * of the tree. 64752209Sbostic */ 64852209Sbostic if (adjaddr) 64952209Sbostic fts_padjust(sp, adjaddr); 65052209Sbostic 65152209Sbostic /* 65247194Sbostic * If not changing directories, reset the path back to original 65347194Sbostic * state. 65447194Sbostic */ 65547194Sbostic if (ISSET(FTS_NOCHDIR)) { 65647194Sbostic if (cp - 1 > sp->fts_path) 65747194Sbostic --cp; 65847194Sbostic *cp = '\0'; 65947194Sbostic } 66042299Sbostic 66142299Sbostic /* 66247194Sbostic * If descended after called from fts_children or called from 66347194Sbostic * fts_read and didn't find anything, get back. If can't get 66452209Sbostic * back, done. 66542299Sbostic */ 66642299Sbostic if (descend && (!nitems || type == BCHILD) && CHDIR(sp, "..")) { 66747194Sbostic cur->fts_info = FTS_ERR; 66847194Sbostic SET(FTS_STOP); 66952150Sbostic return (NULL); 67039962Sbostic } 67139962Sbostic 67252209Sbostic /* If didn't find anything, just do the post-order visit */ 67339800Sbostic if (!nitems) { 67442299Sbostic if (type == BREAD) 67547194Sbostic cur->fts_info = FTS_DP; 67652150Sbostic return (NULL); 67739800Sbostic } 67839800Sbostic 67947194Sbostic /* Sort the entries. */ 68042273Sbostic if (sp->fts_compar && nitems > 1) 68145600Sbostic head = fts_sort(sp, head, nitems); 68252150Sbostic return (head); 68339800Sbostic } 68439800Sbostic 68545600Sbostic static u_short 68645600Sbostic fts_stat(sp, p, follow) 68745600Sbostic FTS *sp; 68839800Sbostic register FTSENT *p; 68945600Sbostic int follow; 69039800Sbostic { 69152160Sbostic register FTSENT *t; 69252160Sbostic register dev_t dev; 69352160Sbostic register ino_t ino; 69452209Sbostic struct stat *sbp, sb; 69547194Sbostic int saved_errno; 69647194Sbostic 69752209Sbostic /* If user needs stat info, stat buffer already allocated. */ 69852209Sbostic sbp = ISSET(FTS_NOSTAT) ? &sb : p->fts_statp; 69952209Sbostic 70039800Sbostic /* 70145600Sbostic * If doing a logical walk, or application requested FTS_FOLLOW, do 70247194Sbostic * a stat(2). If that fails, check for a non-existent symlink. If 70352160Sbostic * fail, set the errno from the stat call. 70439800Sbostic */ 70545600Sbostic if (ISSET(FTS_LOGICAL) || follow) { 70652209Sbostic if (stat(p->fts_accpath, sbp)) { 70747194Sbostic saved_errno = errno; 70852209Sbostic if (!lstat(p->fts_accpath, sbp)) { 70947194Sbostic errno = 0; 71052150Sbostic return (FTS_SLNONE); 71147194Sbostic } 71252160Sbostic p->fts_errno = saved_errno; 71352209Sbostic goto err; 71445600Sbostic } 71552209Sbostic } else if (lstat(p->fts_accpath, sbp)) { 71652160Sbostic p->fts_errno = errno; 71752209Sbostic err: bzero(sbp, sizeof(struct stat)); 71852150Sbostic return (FTS_NS); 71945600Sbostic } 72039800Sbostic 72152209Sbostic if (S_ISDIR(sbp->st_mode)) { 72252209Sbostic /* 72352209Sbostic * Set the device/inode. Used to find cycles and check for 72452209Sbostic * crossing mount points. Also remember the link count, used 72552209Sbostic * in fts_build to limit the number of stat calls. It is 72652209Sbostic * understood that these fields are only referenced if fts_info 72752209Sbostic * is set to FTS_D. 72852209Sbostic */ 72952209Sbostic dev = p->fts_dev = sbp->st_dev; 73052209Sbostic ino = p->fts_ino = sbp->st_ino; 73152209Sbostic p->fts_nlink = sbp->st_nlink; 73252209Sbostic 733*52290Sbostic if (ISDOT(p->fts_name)) 734*52290Sbostic return (FTS_DOT); 735*52290Sbostic 73652209Sbostic /* 73752209Sbostic * Cycle detection is done by brute force when the directory 73852209Sbostic * is first encountered. If the tree gets deep enough or the 73952209Sbostic * number of symbolic links to directories is high enough, 74052209Sbostic * something faster might be worthwhile. 74152209Sbostic */ 74252209Sbostic for (t = p->fts_parent; 74352209Sbostic t->fts_level >= FTS_ROOTLEVEL; t = t->fts_parent) 74452209Sbostic if (ino == t->fts_ino && dev == t->fts_dev) { 74552073Sbostic p->fts_cycle = t; 74652150Sbostic return (FTS_DC); 74747194Sbostic } 74852150Sbostic return (FTS_D); 74947194Sbostic } 75052209Sbostic if (S_ISLNK(sbp->st_mode)) 75152150Sbostic return (FTS_SL); 75252209Sbostic if (S_ISREG(sbp->st_mode)) 75352150Sbostic return (FTS_F); 75452150Sbostic return (FTS_DEFAULT); 75539800Sbostic } 75639800Sbostic 75739800Sbostic static FTSENT * 75845600Sbostic fts_sort(sp, head, nitems) 75945600Sbostic FTS *sp; 76039800Sbostic FTSENT *head; 76139800Sbostic register int nitems; 76239800Sbostic { 76339800Sbostic register FTSENT **ap, *p; 76439800Sbostic 76539800Sbostic /* 76645600Sbostic * Construct an array of pointers to the structures and call qsort(3). 76739800Sbostic * Reassemble the array in the order returned by qsort. If unable to 76839800Sbostic * sort for memory reasons, return the directory entries in their 76939800Sbostic * current order. Allocate enough space for the current needs plus 77052209Sbostic * 40 so don't realloc one entry at a time. 77139800Sbostic */ 77245600Sbostic if (nitems > sp->fts_nitems) { 77345600Sbostic sp->fts_nitems = nitems + 40; 77452209Sbostic if ((sp->fts_array = realloc(sp->fts_array, 77552209Sbostic (size_t)(sp->fts_nitems * sizeof(FTSENT *)))) == NULL) { 77645600Sbostic sp->fts_nitems = 0; 77752150Sbostic return (head); 77839800Sbostic } 77939800Sbostic } 78045600Sbostic for (ap = sp->fts_array, p = head; p; p = p->fts_link) 78139800Sbostic *ap++ = p; 78245600Sbostic qsort((void *)sp->fts_array, nitems, sizeof(FTSENT *), sp->fts_compar); 78345600Sbostic for (head = *(ap = sp->fts_array); --nitems; ++ap) 78442273Sbostic ap[0]->fts_link = ap[1]; 78542273Sbostic ap[0]->fts_link = NULL; 78652150Sbostic return (head); 78739800Sbostic } 78839800Sbostic 78939800Sbostic static FTSENT * 79045600Sbostic fts_alloc(sp, name, len) 79145600Sbostic FTS *sp; 79239800Sbostic char *name; 79339800Sbostic register int len; 79439800Sbostic { 79539800Sbostic register FTSENT *p; 79652209Sbostic int needstat; 79739800Sbostic 79839800Sbostic /* 79952209Sbostic * Variable sized structures. The stat structure isn't necessary 80052209Sbostic * if the user doesn't need it, and the name is variable length. 80152209Sbostic * Allocate enough extra space after the structure to store them. 80239800Sbostic */ 80352209Sbostic needstat = ISSET(FTS_NOSTAT) ? 0 : sizeof(struct stat); 80452209Sbostic if ((p = malloc((size_t)(sizeof(FTSENT) + len + 1 + needstat))) == NULL) 80552150Sbostic return (NULL); 80642273Sbostic bcopy(name, p->fts_name, len + 1); 80752209Sbostic if (needstat) 80852209Sbostic p->fts_statp = (struct stat *)(p->fts_name + len + 1); 80942273Sbostic p->fts_namelen = len; 81045600Sbostic p->fts_path = sp->fts_path; 811*52290Sbostic p->fts_errno = 0; 812*52290Sbostic p->fts_flags = 0; 81347194Sbostic p->fts_instr = FTS_NOINSTR; 81445600Sbostic p->fts_number = 0; 81552209Sbostic #ifdef NOT_NECESSARY 81645600Sbostic p->fts_pointer = NULL; 81752209Sbostic #endif 81852150Sbostic return (p); 81939800Sbostic } 82039800Sbostic 82145600Sbostic static void 82239800Sbostic fts_lfree(head) 82339800Sbostic register FTSENT *head; 82439800Sbostic { 82539800Sbostic register FTSENT *p; 82639800Sbostic 82745600Sbostic /* Free a linked list of structures. */ 82839800Sbostic while (p = head) { 82942273Sbostic head = head->fts_link; 83047194Sbostic free(p); 83139800Sbostic } 83239800Sbostic } 83339800Sbostic 83439800Sbostic /* 83552209Sbostic * Allow essentially unlimited paths; find, rm, ls should all work on any tree. 83652209Sbostic * Most systems will allow creation of paths much longer than MAXPATHLEN, even 83752209Sbostic * though the kernel won't resolve them. Add the size (not just what's needed) 83852209Sbostic * plus 256 bytes so don't realloc the path 2 bytes at a time. 83939800Sbostic */ 84052209Sbostic static int 84152209Sbostic fts_palloc(sp, more) 84245600Sbostic FTS *sp; 84352209Sbostic int more; 84439800Sbostic { 84552209Sbostic 84652209Sbostic sp->fts_pathlen += more + 256; 84752209Sbostic sp->fts_path = realloc(sp->fts_path, (size_t)sp->fts_pathlen); 84852209Sbostic return (sp->fts_path == NULL); 84947155Sbostic } 85052209Sbostic 85152209Sbostic /* 85252209Sbostic * When the path is realloc'd, have to fix all of the pointers in structures 85352209Sbostic * already returned. 85452209Sbostic */ 85552209Sbostic static void 85652209Sbostic fts_padjust(sp, addr) 85752209Sbostic FTS *sp; 85852209Sbostic void *addr; 85952209Sbostic { 86052209Sbostic FTSENT *p; 86152209Sbostic 86252209Sbostic #define ADJUST(p) { \ 86352209Sbostic (p)->fts_accpath = addr + ((p)->fts_accpath - (p)->fts_path); \ 86452209Sbostic (p)->fts_path = addr; \ 86552209Sbostic } 86652209Sbostic /* Adjust the current set of children. */ 86752209Sbostic for (p = sp->fts_child; p; p = p->fts_link) 86852209Sbostic ADJUST(p); 86952209Sbostic 87052209Sbostic /* Adjust the rest of the tree. */ 87152209Sbostic for (p = sp->fts_cur; p->fts_level >= FTS_ROOTLEVEL;) { 87252209Sbostic ADJUST(p); 87352209Sbostic p = p->fts_link ? p->fts_link : p->fts_parent; 87452209Sbostic } 87552209Sbostic } 876