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*47155Sbostic static char sccsid[] = "@(#)fts.c 5.13 (Berkeley) 03/08/91"; 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> 1739800Sbostic #include <fts.h> 1842273Sbostic #include <string.h> 1942281Sbostic #include <stdlib.h> 20*47155Sbostic #include <unistd.h> 2139800Sbostic 22*47155Sbostic static FTSENT *fts_alloc(), *fts_build(), *fts_cycle(), *fts_root(), 23*47155Sbostic *fts_sort(); 24*47155Sbostic static void fts_lfree(), fts_load(); 25*47155Sbostic static u_short fts_stat(); 26*47155Sbostic static char *fts_path(); 2739800Sbostic 2839800Sbostic /* 2939800Sbostic * Special case a root of "/" so that slashes aren't appended causing 3039800Sbostic * paths to be written as "//foo". 3139800Sbostic */ 3239800Sbostic #define NAPPEND(p) \ 3342273Sbostic (p->fts_level == ROOTLEVEL && p->fts_pathlen == 1 && \ 3442273Sbostic p->fts_path[0] == '/' ? 0 : p->fts_pathlen) 3539800Sbostic 3645600Sbostic #define ISSET(opt) (sp->fts_options & opt) 3745600Sbostic #define SET(opt) (sp->fts_options |= opt) 3839800Sbostic 3945600Sbostic #define CHDIR(sp, path) (!ISSET(FTS_NOCHDIR) && chdir(path)) 4045600Sbostic #define FCHDIR(sp, fd) (!ISSET(FTS_NOCHDIR) && fchdir(fd)) 4139800Sbostic 4242299Sbostic /* fts_build flags */ 4345600Sbostic #define BCHILD 1 /* from fts_children */ 4445600Sbostic #define BREAD 2 /* from fts_read */ 4542299Sbostic 4645600Sbostic /* fts_level values */ 4745600Sbostic #define ROOTLEVEL 0 4845600Sbostic #define ROOTPARENTLEVEL -1 4939800Sbostic 5039800Sbostic FTS * 5145600Sbostic fts_open(argv, options, compar) 52*47155Sbostic char * const *argv; 5339800Sbostic register int options; 54*47155Sbostic int (*compar) __P((const FTSENT *, const FTSENT *)); 5539800Sbostic { 5639800Sbostic register FTS *sp; 5739800Sbostic register FTSENT *p, *root; 5839800Sbostic register int nitems, maxlen; 5939800Sbostic FTSENT *parent, *tmp; 6039800Sbostic 6145600Sbostic /* Allocate/initialize the stream */ 6245600Sbostic if (!(sp = (FTS *)malloc((u_int)sizeof(FTS)))) 6339800Sbostic return(NULL); 6439800Sbostic bzero(sp, sizeof(FTS)); 6542273Sbostic sp->fts_compar = compar; 6642273Sbostic sp->fts_options = options; 6739800Sbostic 6845600Sbostic /* Logical walks turn on NOCHDIR; symbolic links are too hard. */ 6945600Sbostic if (ISSET(FTS_LOGICAL)) 7045600Sbostic SET(FTS_NOCHDIR); 7145600Sbostic 7245600Sbostic /* Allocate/initialize root's parent. */ 7345600Sbostic if (!(parent = fts_alloc(sp, "", 0))) 7439800Sbostic goto mem1; 7542273Sbostic parent->fts_level = ROOTPARENTLEVEL; 7639800Sbostic 7745600Sbostic /* Allocate/initialize root(s). */ 7843070Sbostic maxlen = -1; 7943070Sbostic for (root = NULL, nitems = 0; *argv; ++argv, ++nitems) { 8045600Sbostic if (!(p = fts_root(sp, *argv))) 8143070Sbostic goto mem2; 8243070Sbostic if (maxlen < p->fts_namelen) 8343070Sbostic maxlen = p->fts_namelen; 8443070Sbostic /* 8545600Sbostic * If comparison routine supplied, traverse in sorted 8643070Sbostic * order; otherwise traverse in the order specified. 8743070Sbostic */ 8843070Sbostic if (compar) { 8943070Sbostic p->fts_link = root; 9043070Sbostic root = p; 9143070Sbostic p->fts_accpath = p->fts_name; 9243075Sbostic if (!(options & FTS_NOSTAT)) 9345600Sbostic p->fts_info = fts_stat(sp, p, 0); 9443070Sbostic } else { 9543070Sbostic p->fts_link = NULL; 9643070Sbostic if (!root) 9743070Sbostic tmp = root = p; 9843070Sbostic else { 9943070Sbostic tmp->fts_link = p; 10043070Sbostic tmp = p; 10139800Sbostic } 10239800Sbostic } 10343070Sbostic p->fts_level = ROOTLEVEL; 10443070Sbostic p->fts_parent = parent; 10539800Sbostic } 10643070Sbostic if (compar && nitems > 1) 10745600Sbostic root = fts_sort(sp, root, nitems); 10839800Sbostic 10942281Sbostic /* 11045600Sbostic * Allocate a dummy pointer and make fts_read think that we've just 11142281Sbostic * finished the node before the root(s); set p->fts_info to FTS_NS 11242281Sbostic * so that everything about the "current" node is ignored. 11342281Sbostic */ 11445600Sbostic if (!(sp->fts_cur = fts_alloc(sp, "", 0))) 11542281Sbostic goto mem2; 11642281Sbostic sp->fts_cur->fts_link = root; 11742281Sbostic sp->fts_cur->fts_info = FTS_NS; 11842281Sbostic 11945600Sbostic /* Start out with at least 1K+ of path space. */ 12045600Sbostic if (!fts_path(sp, MAX(maxlen, MAXPATHLEN))) 12142281Sbostic goto mem3; 12239800Sbostic 12339800Sbostic /* 12445600Sbostic * If using chdir(2), grab a file descriptor pointing to dot to insure 12542299Sbostic * that we can get back here; this could be avoided for some paths, 12642299Sbostic * but almost certainly not worth the effort. Slashes, symbolic links, 12742299Sbostic * and ".." are all fairly nasty problems. Note, if we can't get the 12842299Sbostic * descriptor we run anyway, just more slowly. 12939800Sbostic */ 13045600Sbostic if (!ISSET(FTS_NOCHDIR) && (sp->fts_sd = open(".", O_RDONLY, 0)) < 0) 13145600Sbostic SET(FTS_NOCHDIR); 13239800Sbostic 13339800Sbostic return(sp); 13439800Sbostic 13545600Sbostic mem3: free((void *)sp->fts_cur); 13639800Sbostic mem2: fts_lfree(root); 13745600Sbostic free((void *)parent); 13845600Sbostic mem1: free((void *)sp); 13939800Sbostic return(NULL); 14039800Sbostic } 14139800Sbostic 14245600Sbostic static void 14345600Sbostic fts_load(sp, p) 14445600Sbostic FTS *sp; 14539800Sbostic register FTSENT *p; 14639800Sbostic { 14739800Sbostic register int len; 14839800Sbostic register char *cp; 14939800Sbostic 15039800Sbostic /* 15145600Sbostic * Load the stream structure for the next traversal; set the 15245600Sbostic * fts_accpath field specially so the chdir gets done to the 15345600Sbostic * right place and the user can access the first node. 15439800Sbostic */ 15542273Sbostic len = p->fts_pathlen = p->fts_namelen; 15645600Sbostic bcopy(p->fts_name, sp->fts_path, len + 1); 15742273Sbostic if ((cp = rindex(p->fts_name, '/')) && (cp != p->fts_name || cp[1])) { 15839800Sbostic len = strlen(++cp); 15942273Sbostic bcopy(cp, p->fts_name, len + 1); 16042273Sbostic p->fts_namelen = len; 16139800Sbostic } 16245600Sbostic p->fts_accpath = p->fts_path = sp->fts_path; 16339800Sbostic } 16439800Sbostic 165*47155Sbostic int 16645600Sbostic fts_close(sp) 16739800Sbostic FTS *sp; 16839800Sbostic { 16939800Sbostic register FTSENT *freep, *p; 17039800Sbostic int saved_errno; 17139800Sbostic 17242281Sbostic if (sp->fts_cur) { 17342281Sbostic /* 17445600Sbostic * This still works if we haven't read anything -- the dummy 17542281Sbostic * structure points to the root list, so we step through to 17642281Sbostic * the end of the root list which has a valid parent pointer. 17742281Sbostic */ 17842281Sbostic for (p = sp->fts_cur; p->fts_level > ROOTPARENTLEVEL;) { 17942281Sbostic freep = p; 18042281Sbostic p = p->fts_link ? p->fts_link : p->fts_parent; 18145600Sbostic free((void *)freep); 18239800Sbostic } 18345600Sbostic free((void *)p); 18442281Sbostic } 18539800Sbostic 18645600Sbostic /* Free up child linked list, sort array, path buffer. */ 18742273Sbostic if (sp->fts_child) 18842273Sbostic fts_lfree(sp->fts_child); 18942273Sbostic if (sp->fts_array) 19045600Sbostic free((void *)sp->fts_array); 19145600Sbostic free((char *)sp->fts_path); 19239800Sbostic 19339800Sbostic /* 19445600Sbostic * Return to original directory, save errno if necessary; free up 19545600Sbostic * the directory buffer. 19639800Sbostic */ 19745600Sbostic if (!ISSET(FTS_NOCHDIR)) { 19842299Sbostic saved_errno = fchdir(sp->fts_sd) ? errno : 0; 19942299Sbostic (void)close(sp->fts_sd); 20039800Sbostic } 20139800Sbostic 20245600Sbostic /* Free up the stream pointer. */ 20345600Sbostic free((void *)sp); 20439800Sbostic 20545600Sbostic /* Set errno and return. */ 20645600Sbostic if (!ISSET(FTS_NOCHDIR) && saved_errno) { 20739800Sbostic errno = saved_errno; 20839800Sbostic return(-1); 20939800Sbostic } 21039800Sbostic return(0); 21139800Sbostic } 21239800Sbostic 21339800Sbostic FTSENT * 21445600Sbostic fts_read(sp) 21539800Sbostic register FTS *sp; 21639800Sbostic { 21742299Sbostic register FTSENT *p, *tmp; 21839800Sbostic register int instr; 21942299Sbostic register char *cp; 22039800Sbostic static int cd; 22139800Sbostic 22245600Sbostic /* If finished or unrecoverable error, return NULL. */ 22345600Sbostic if (!sp->fts_cur || ISSET(FTS__STOP)) 22439800Sbostic return(NULL); 22539800Sbostic 22645600Sbostic /* Set current node pointer. */ 22742273Sbostic p = sp->fts_cur; 22839800Sbostic 22945600Sbostic /* Save and zero out user instructions. */ 23042273Sbostic instr = p->fts_instr; 23145600Sbostic p->fts_instr = FTS__NOINSTR; 23239800Sbostic 23345600Sbostic /* If used fts_link pointer for cycle detection, restore it. */ 23442273Sbostic if (sp->fts_savelink) { 23542273Sbostic p->fts_link = sp->fts_savelink; 23642273Sbostic sp->fts_savelink = NULL; 23739800Sbostic } 23839800Sbostic 23945600Sbostic /* Any type of file may be re-visited; re-stat and return. */ 24039800Sbostic if (instr == FTS_AGAIN) { 24145600Sbostic p->fts_info = fts_stat(sp, p, 0); 24239800Sbostic return(p); 24339800Sbostic } 24439800Sbostic 24545600Sbostic /* Following a symbolic link. */ 24642299Sbostic if (p->fts_info == FTS_SL && instr == FTS_FOLLOW) { 24745600Sbostic p->fts_info = fts_stat(sp, p, 1); 24842299Sbostic return(p); 24942299Sbostic } 25042299Sbostic 25145600Sbostic /* Directory in pre-order. */ 25242299Sbostic if (p->fts_info == FTS_D) { 25345600Sbostic /* If skipped or crossed mount point, do post-order visit. */ 25445600Sbostic if (instr == FTS_SKIP || ISSET(FTS_XDEV) && 25542280Sbostic p->fts_statb.st_dev != sp->sdev) { 25642273Sbostic if (sp->fts_child) { 25742273Sbostic fts_lfree(sp->fts_child); 25842273Sbostic sp->fts_child = NULL; 25939800Sbostic } 26042301Sbostic p->fts_info = FTS_DP; 26142301Sbostic return(p); 26242299Sbostic } 26342299Sbostic 26445600Sbostic /* Read the directory if necessary, and return first entry. */ 26542299Sbostic if (sp->fts_child) 26642299Sbostic if (CHDIR(sp, p->fts_accpath)) { 26742299Sbostic fts_lfree(sp->fts_child); 26842299Sbostic p->fts_info = FTS_DNX; 26942299Sbostic } else { 27042299Sbostic p = sp->fts_child; 27142299Sbostic cp = sp->fts_path + NAPPEND(p->fts_parent); 27242299Sbostic *cp++ = '/'; 27342299Sbostic bcopy(p->fts_name, cp, p->fts_namelen + 1); 27442299Sbostic } 27542299Sbostic else { 27642299Sbostic if (!(sp->fts_child = fts_build(sp, BREAD))) 27739800Sbostic return(p); 27842273Sbostic p = sp->fts_child; 27939800Sbostic } 28042299Sbostic sp->fts_child = NULL; 28142299Sbostic return(sp->fts_cur = p); 28239800Sbostic } 28339800Sbostic 28445600Sbostic /* Move to next node on this level. */ 28542299Sbostic next: tmp = p; 28642299Sbostic if (p = p->fts_link) { 28739800Sbostic /* 28845600Sbostic * If root level node, set up paths and return. If not the 28939800Sbostic * first time, and it's not an absolute pathname, get back 29045600Sbostic * to starting directory. If that fails, we're dead. 29139800Sbostic */ 29242273Sbostic if (p->fts_level == ROOTLEVEL) { 29345600Sbostic fts_load(sp, p); 29445600Sbostic free((void *)tmp); 29542281Sbostic if (cd && 29642299Sbostic p->fts_path[0] != '/' && FCHDIR(sp, sp->fts_sd)) { 29745600Sbostic /* Can't get back to start; we're dead. */ 29842299Sbostic p->fts_path = "starting directory"; 29942281Sbostic p->fts_info = FTS_ERR; 30045600Sbostic SET(FTS__STOP); 30142281Sbostic return(sp->fts_cur = p); 30242299Sbostic } 30342281Sbostic cd = 1; 30445600Sbostic p->fts_info = fts_stat(sp, p, 0); 30542280Sbostic sp->sdev = p->fts_statb.st_dev; 30639800Sbostic } else { 30745600Sbostic free((void *)tmp); 30842299Sbostic 30945600Sbostic /* User may have called fts_set on node. */ 31042299Sbostic if (p->fts_instr == FTS_SKIP) 31142299Sbostic goto next; 31242299Sbostic if (p->fts_instr == FTS_FOLLOW) { 31345600Sbostic p->fts_info = fts_stat(sp, p, 1); 31445600Sbostic p->fts_instr = FTS__NOINSTR; 31542299Sbostic } 31642299Sbostic 31745600Sbostic /* Fill in the paths. */ 31842273Sbostic cp = sp->fts_path + NAPPEND(p->fts_parent); 31939800Sbostic *cp++ = '/'; 32042273Sbostic bcopy(p->fts_name, cp, p->fts_namelen + 1); 32142299Sbostic 32245600Sbostic /* Check for directory cycles. */ 32342273Sbostic if (p->fts_info == FTS_D && (tmp = fts_cycle(p))) { 32442273Sbostic sp->fts_savelink = p->fts_link; 32542273Sbostic p->fts_link = tmp; 32642299Sbostic p->fts_info = FTS_DC; 32739800Sbostic } 32839800Sbostic } 32942273Sbostic return(sp->fts_cur = p); 33039800Sbostic } 33139800Sbostic 33245600Sbostic /* Move to parent. */ 33342299Sbostic p = tmp->fts_parent; 33445600Sbostic free((void *)tmp); 33542299Sbostic 33642273Sbostic if (p->fts_level == ROOTPARENTLEVEL) { 33739800Sbostic /* 33845600Sbostic * Done; free everything up and set errno to 0 so the user 33939800Sbostic * can distinguish between error and EOF. 34039800Sbostic */ 34145600Sbostic free((void *)p); 34239800Sbostic errno = 0; 34342273Sbostic return(sp->fts_cur = NULL); 34439800Sbostic } 34539800Sbostic 34642273Sbostic sp->fts_path[p->fts_pathlen] = '\0'; 34739800Sbostic if (CHDIR(sp, "..")) { 34845600Sbostic SET(FTS__STOP); 34942273Sbostic p->fts_info = FTS_ERR; 35039800Sbostic } else 35142273Sbostic p->fts_info = FTS_DP; 35242273Sbostic return(sp->fts_cur = p); 35339800Sbostic } 35439800Sbostic 35539800Sbostic /* 35645600Sbostic * fts_set takes the stream as an argument although it's not used in this 35739800Sbostic * implementation; it would be necessary if anyone wanted to add global 35845600Sbostic * semantics to fts using fts_set. An error return is allowed for similar 35945600Sbostic * reasons. 36039800Sbostic */ 36139800Sbostic /* ARGSUSED */ 362*47155Sbostic int 36345600Sbostic fts_set(sp, p, instr) 36439800Sbostic FTS *sp; 36539800Sbostic FTSENT *p; 36639800Sbostic int instr; 36739800Sbostic { 36842273Sbostic p->fts_instr = instr; 36939800Sbostic return(0); 37039800Sbostic } 37139800Sbostic 37239800Sbostic FTSENT * 37345600Sbostic fts_children(sp) 37439800Sbostic register FTS *sp; 37539800Sbostic { 37642299Sbostic register FTSENT *p; 37742299Sbostic int fd; 37842299Sbostic 37945600Sbostic /* Set current node pointer. */ 38045600Sbostic p = sp->fts_cur; 38145600Sbostic 38239800Sbostic /* 38345600Sbostic * Set errno to 0 so that user can tell the difference between an 38439800Sbostic * error and a directory without entries. 38539800Sbostic */ 38639800Sbostic errno = 0; 38745600Sbostic if (ISSET(FTS__STOP) || 38845600Sbostic p->fts_info != FTS_D && p->fts_info != FTS_DNX && 38945600Sbostic p->fts_info != FTS_DNR) 39039800Sbostic return(NULL); 39145600Sbostic 39245600Sbostic /* Free up any previous child list. */ 39342273Sbostic if (sp->fts_child) 39442273Sbostic fts_lfree(sp->fts_child); 39542299Sbostic 39642299Sbostic /* 39745600Sbostic * If using chdir on a relative path and called BEFORE fts_read does 39845600Sbostic * its chdir to the root of a traversal, we can lose because we need 39945600Sbostic * to chdir into the subdirectory, and we don't know where the current 40045600Sbostic * directory is to get back so that the upcoming chdir by fts_read 40145600Sbostic * will work. 40242299Sbostic */ 40342299Sbostic if (p->fts_level != ROOTLEVEL || p->fts_accpath[0] == '/' || 40445600Sbostic ISSET(FTS_NOCHDIR)) 40542299Sbostic return(sp->fts_child = fts_build(sp, BCHILD)); 40642299Sbostic 40742299Sbostic if ((fd = open(".", O_RDONLY, 0)) < 0) 40842299Sbostic return(NULL); 40942299Sbostic sp->fts_child = fts_build(sp, BCHILD); 41042299Sbostic if (fchdir(fd)) 41142299Sbostic return(NULL); 41242299Sbostic (void)close(fd); 41342299Sbostic return(sp->fts_child); 41439800Sbostic } 41539800Sbostic 41639800Sbostic #define ISDOT(a) (a[0] == '.' && (!a[1] || a[1] == '.' && !a[2])) 41739800Sbostic 418*47155Sbostic static FTSENT * 41942299Sbostic fts_build(sp, type) 42039800Sbostic register FTS *sp; 42142299Sbostic int type; 42239800Sbostic { 42339800Sbostic register struct dirent *dp; 42439800Sbostic register FTSENT *p, *head; 42539800Sbostic register int nitems; 42639800Sbostic DIR *dirp; 42739962Sbostic int descend, len, level, maxlen, nlinks, saved_errno; 42839800Sbostic char *cp; 42939800Sbostic 43045600Sbostic /* Set current node pointer. */ 43142273Sbostic p = sp->fts_cur; 43245600Sbostic 43342273Sbostic if (!(dirp = opendir(p->fts_accpath))) { 43442299Sbostic if (type == BREAD) { 43545600Sbostic p->fts_info = FTS_DNR; 43639800Sbostic errno = 0; 43739800Sbostic } 43839800Sbostic return(NULL); 43939800Sbostic } 44039800Sbostic 44139800Sbostic /* 44245600Sbostic * The real slowdown in walking the tree is the stat calls. If 44339800Sbostic * FTS_NOSTAT is set and it's a physical walk (so that symbolic 44439800Sbostic * links can't be directories), fts assumes that the number of 44539800Sbostic * subdirectories in a node is equal to the number of links to 44639800Sbostic * the parent. This allows stat calls to be skipped in any leaf 44739800Sbostic * directories and for any nodes after the directories in the 44839800Sbostic * parent node have been found. This empirically cuts the stat 44939800Sbostic * calls by about 2/3. 45039800Sbostic */ 45142273Sbostic nlinks = 45245600Sbostic ISSET(FTS_NOSTAT) && ISSET(FTS_PHYSICAL) ? 45345600Sbostic p->fts_statb.st_nlink - (ISSET(FTS_SEEDOT) ? 0 : 2) : -1; 45439800Sbostic 45545600Sbostic /* If told to descend or found links and not told not to descend. */ 45645600Sbostic descend = 0; 45745600Sbostic if (nlinks || type == BREAD) 45845600Sbostic if (!FCHDIR(sp, dirfd(dirp))) 45945600Sbostic descend = 1; 46045600Sbostic /* 46145627Sbostic * Return all the information possible; an fts_read doing a 46245627Sbostic * relative walk of the tree will have to descend, so it can't 46345627Sbostic * succeed. Fts_children or absolute walks of the tree can 46445627Sbostic * succeed, but no stat information will be available. Reset 46545627Sbostic * errno as necessary. 46645600Sbostic */ 46745600Sbostic else { 46845627Sbostic errno = 0; 46942299Sbostic if (type == BREAD) { 47045600Sbostic (void)closedir(dirp); 47145600Sbostic p->fts_info = FTS_DNX; 47245600Sbostic return(NULL); 47339962Sbostic } 47445600Sbostic nlinks = 0; 47539962Sbostic } 47639962Sbostic 47745600Sbostic /* Get max file name length that can be stored in current path. */ 47842273Sbostic maxlen = sp->fts_pathlen - p->fts_pathlen - 1; 47940939Sbostic 48042273Sbostic cp = sp->fts_path + (len = NAPPEND(p)); 48140939Sbostic *cp++ = '/'; 48240939Sbostic 48342273Sbostic level = p->fts_level + 1; 48440939Sbostic 48545600Sbostic /* Read the directory, attching each new entry to the `link' pointer. */ 48639800Sbostic for (head = NULL, nitems = 0; dp = readdir(dirp);) { 48745600Sbostic if (ISDOT(dp->d_name) && !ISSET(FTS_SEEDOT)) 48839800Sbostic continue; 48939800Sbostic 49045600Sbostic if (!(p = fts_alloc(sp, dp->d_name, (int)dp->d_namlen))) { 49139800Sbostic saved_errno = errno; 49239800Sbostic goto mem1; 49339800Sbostic } 49439800Sbostic if (dp->d_namlen > maxlen) { 49545600Sbostic if (!fts_path(sp, (int)dp->d_namlen)) { 49645600Sbostic /* Quit: this stream no longer has a path. */ 49745600Sbostic SET(FTS__STOP); 49839800Sbostic saved_errno = errno; 49945600Sbostic free((void *)p); 50039800Sbostic mem1: fts_lfree(head); 50142299Sbostic if (type == BREAD) 50242273Sbostic p->fts_info = FTS_ERR; 50339962Sbostic if (descend && CHDIR(sp, "..")) { 50445600Sbostic /* 50545600Sbostic * chdir error is more interesting 50645600Sbostic * than memory error, since it stops 50745600Sbostic * everything. 50845600Sbostic */ 50939800Sbostic saved_errno = errno; 51045600Sbostic SET(FTS__STOP); 51139800Sbostic } 51239800Sbostic errno = saved_errno; 51342299Sbostic (void)closedir(dirp); 51439800Sbostic return(NULL); 51539800Sbostic } 51642273Sbostic maxlen = sp->fts_pathlen - sp->fts_cur->fts_pathlen - 1; 51739800Sbostic } 51839800Sbostic 51942273Sbostic p->fts_pathlen = len + dp->d_namlen + 1; 52045600Sbostic p->fts_accpath = ISSET(FTS_NOCHDIR) ? p->fts_path : p->fts_name; 52139800Sbostic 52242273Sbostic p->fts_parent = sp->fts_cur; 52342273Sbostic p->fts_level = level; 52439800Sbostic 52539800Sbostic if (nlinks) { 52645600Sbostic /* Make sure fts_stat has a filename to stat. */ 52745600Sbostic if (ISSET(FTS_NOCHDIR)) 52842273Sbostic bcopy(p->fts_name, cp, p->fts_namelen + 1); 52945600Sbostic p->fts_info = fts_stat(sp, p, 0); 53042273Sbostic if (nlinks > 0 && (p->fts_info == FTS_D || 53142273Sbostic p->fts_info == FTS_DNR || p->fts_info == FTS_DNX)) 53239800Sbostic --nlinks; 53339800Sbostic } else 53442273Sbostic p->fts_info = FTS_NS; 53539800Sbostic 53642273Sbostic p->fts_link = head; 53739800Sbostic head = p; 53839800Sbostic ++nitems; 53939800Sbostic } 54039800Sbostic (void)closedir(dirp); 54139800Sbostic 54245600Sbostic /* Reset the path. */ 54342299Sbostic if (cp - 1 > sp->fts_path) 54442299Sbostic --cp; 54542299Sbostic *cp = '\0'; 54642299Sbostic 54742299Sbostic /* 54845600Sbostic * If descended: if were called from fts_read and didn't find anything, 54945600Sbostic * or were called from fts_children, get back. 55042299Sbostic */ 55142299Sbostic if (descend && (!nitems || type == BCHILD) && CHDIR(sp, "..")) { 55245600Sbostic SET(FTS__STOP); 55342273Sbostic p->fts_info = FTS_ERR; 55439962Sbostic return(NULL); 55539962Sbostic } 55639962Sbostic 55739800Sbostic if (!nitems) { 55842299Sbostic if (type == BREAD) 55942273Sbostic p->fts_info = FTS_DP; 56039800Sbostic return(NULL); 56139800Sbostic } 56239800Sbostic 56342273Sbostic if (sp->fts_compar && nitems > 1) 56445600Sbostic head = fts_sort(sp, head, nitems); 56539800Sbostic 56642299Sbostic if (type == BREAD) { 56742299Sbostic *cp = '/'; 56842299Sbostic bcopy(head->fts_name, cp + 1, head->fts_namelen + 1); 56942299Sbostic } 57039800Sbostic return(head); 57139800Sbostic } 57239800Sbostic 57345600Sbostic static u_short 57445600Sbostic fts_stat(sp, p, follow) 57545600Sbostic FTS *sp; 57639800Sbostic register FTSENT *p; 57745600Sbostic int follow; 57839800Sbostic { 57939800Sbostic /* 58045600Sbostic * If doing a logical walk, or application requested FTS_FOLLOW, do 58145600Sbostic * a stat(2). If that fails, either fail or do an lstat(2) for a 58245600Sbostic * non-existent symlink. (The check has to be done, or we wouldn't 58345600Sbostic * detect a symlink being deleted.) 58445600Sbostic * 58545600Sbostic * Don't leave errno set for FTS_NS cases. XXX 58639800Sbostic */ 58745600Sbostic if (ISSET(FTS_LOGICAL) || follow) { 58845600Sbostic if (stat(p->fts_accpath, &p->fts_statb)) { 58945600Sbostic errno = 0; 59045600Sbostic if (follow && !lstat(p->fts_accpath, &p->fts_statb)) 59145600Sbostic return(FTS_SLNONE); 59245600Sbostic else { 59345600Sbostic errno = 0; 59445600Sbostic return(FTS_NS); 59545600Sbostic } 59645600Sbostic } 59745600Sbostic } else if (lstat(p->fts_accpath, &p->fts_statb)) { 59845600Sbostic errno = 0; 59945600Sbostic return(FTS_NS); 60045600Sbostic } 60139800Sbostic 60245600Sbostic if (S_ISDIR(p->fts_statb.st_mode)) 60339800Sbostic return(FTS_D); 60445600Sbostic if (S_ISLNK(p->fts_statb.st_mode)) 60539800Sbostic return(FTS_SL); 60645600Sbostic if (S_ISREG(p->fts_statb.st_mode)) 60739800Sbostic return(FTS_F); 60840939Sbostic return(FTS_DEFAULT); 60939800Sbostic } 61039800Sbostic 61139800Sbostic static FTSENT * 61239800Sbostic fts_cycle(p) 61339800Sbostic register FTSENT *p; 61439800Sbostic { 61539800Sbostic register dev_t dev; 61639800Sbostic register ino_t ino; 61739800Sbostic 61839800Sbostic /* 61945600Sbostic * Cycle detection is brute force; if the tree gets deep enough or 62039800Sbostic * the number of symbolic links to directories is really high 62139800Sbostic * something faster might be worthwhile. 62239800Sbostic */ 62342273Sbostic dev = p->fts_statb.st_dev; 62442273Sbostic ino = p->fts_statb.st_ino; 62542273Sbostic for (p = p->fts_parent; p->fts_level > ROOTLEVEL; p = p->fts_parent) 62642273Sbostic if (ino == p->fts_statb.st_ino && dev == p->fts_statb.st_dev) 62739800Sbostic return(p); 62839800Sbostic return(NULL); 62939800Sbostic } 63039800Sbostic 63139800Sbostic #define R(type, nelem, ptr) \ 63242299Sbostic (type *)realloc((void *)ptr, (u_int)((nelem) * sizeof(type))) 63339800Sbostic 63439800Sbostic static FTSENT * 63545600Sbostic fts_sort(sp, head, nitems) 63645600Sbostic FTS *sp; 63739800Sbostic FTSENT *head; 63839800Sbostic register int nitems; 63939800Sbostic { 64039800Sbostic register FTSENT **ap, *p; 64139800Sbostic 64239800Sbostic /* 64345600Sbostic * Construct an array of pointers to the structures and call qsort(3). 64439800Sbostic * Reassemble the array in the order returned by qsort. If unable to 64539800Sbostic * sort for memory reasons, return the directory entries in their 64639800Sbostic * current order. Allocate enough space for the current needs plus 64739800Sbostic * 40 so we don't realloc one entry at a time. 64839800Sbostic */ 64945600Sbostic if (nitems > sp->fts_nitems) { 65045600Sbostic sp->fts_nitems = nitems + 40; 65145600Sbostic if (!(sp->fts_array = 65245600Sbostic R(FTSENT *, sp->fts_nitems, sp->fts_array))) { 65345600Sbostic sp->fts_nitems = 0; 65439800Sbostic return(head); 65539800Sbostic } 65639800Sbostic } 65745600Sbostic for (ap = sp->fts_array, p = head; p; p = p->fts_link) 65839800Sbostic *ap++ = p; 65945600Sbostic qsort((void *)sp->fts_array, nitems, sizeof(FTSENT *), sp->fts_compar); 66045600Sbostic for (head = *(ap = sp->fts_array); --nitems; ++ap) 66142273Sbostic ap[0]->fts_link = ap[1]; 66242273Sbostic ap[0]->fts_link = NULL; 66339800Sbostic return(head); 66439800Sbostic } 66539800Sbostic 66639800Sbostic static FTSENT * 66745600Sbostic fts_alloc(sp, name, len) 66845600Sbostic FTS *sp; 66939800Sbostic char *name; 67039800Sbostic register int len; 67139800Sbostic { 67239800Sbostic register FTSENT *p; 67339800Sbostic 67439800Sbostic /* 67545600Sbostic * Variable sized structures; the name is the last element so 67639800Sbostic * allocate enough extra space after the structure to hold it. 67739800Sbostic */ 67842299Sbostic if (!(p = (FTSENT *)malloc((size_t)(sizeof(FTSENT) + len)))) 67939800Sbostic return(NULL); 68042273Sbostic bcopy(name, p->fts_name, len + 1); 68142273Sbostic p->fts_namelen = len; 68245600Sbostic p->fts_path = sp->fts_path; 68345600Sbostic p->fts_instr = FTS__NOINSTR; 68445600Sbostic p->fts_number = 0; 68545600Sbostic p->fts_pointer = NULL; 68639800Sbostic return(p); 68739800Sbostic } 68839800Sbostic 68945600Sbostic static void 69039800Sbostic fts_lfree(head) 69139800Sbostic register FTSENT *head; 69239800Sbostic { 69339800Sbostic register FTSENT *p; 69439800Sbostic 69545600Sbostic /* Free a linked list of structures. */ 69639800Sbostic while (p = head) { 69742273Sbostic head = head->fts_link; 69845600Sbostic free((void *)p); 69939800Sbostic } 70039800Sbostic } 70139800Sbostic 70239800Sbostic /* 70345600Sbostic * Allow essentially unlimited paths; certain programs (find, rm, ls) need to 70445600Sbostic * work on any tree. Most systems will allow creation of paths much longer 70545600Sbostic * than MAXPATHLEN, even though the kernel won't resolve them. Add an extra 70645600Sbostic * 128 bytes to the requested size so that we don't realloc the path 2 bytes 70745600Sbostic * at a time. 70839800Sbostic */ 70942299Sbostic static char * 71045600Sbostic fts_path(sp, size) 71145600Sbostic FTS *sp; 71239800Sbostic int size; 71339800Sbostic { 71445600Sbostic sp->fts_pathlen += size + 128; 715*47155Sbostic return(sp->fts_path = R(char, sp->fts_pathlen, sp->fts_path)); 716*47155Sbostic } 71739800Sbostic 71839800Sbostic static FTSENT * 71945600Sbostic fts_root(sp, name) 72045600Sbostic FTS *sp; 72139800Sbostic register char *name; 72239800Sbostic { 72339800Sbostic register char *cp; 72439800Sbostic 72539800Sbostic /* 72645600Sbostic * Rip trailing slashes; it's somewhat unclear in POSIX 1003.1 what 72739800Sbostic * /a/b/ really is, they don't talk about what a null path component 72839800Sbostic * resolves to. This hopefully does what the user intended. Don't 72939800Sbostic * allow null pathnames. 73039800Sbostic */ 73139800Sbostic for (cp = name; *cp; ++cp); 73239800Sbostic if (cp == name) { 73339800Sbostic errno = ENOENT; 73439800Sbostic return(NULL); 73539800Sbostic } 736*47155Sbostic #ifdef notdef 73739800Sbostic while (--cp > name && *cp == '/'); 73839800Sbostic *++cp = '\0'; 739*47155Sbostic #endif 740*47155Sbostic return(fts_alloc(sp, name, cp - name + 1)); 74139800Sbostic } 742