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*45600Sbostic static char sccsid[] = "@(#)fts.c 5.11 (Berkeley) 11/14/90"; 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> 2039800Sbostic 21*45600Sbostic FTSENT *fts_alloc(), *fts_build(), *fts_cycle(), *fts_root(), *fts_sort(); 22*45600Sbostic void fts_lfree(), fts_load(); 23*45600Sbostic u_short fts_stat(); 2439800Sbostic 2539800Sbostic /* 2639800Sbostic * Special case a root of "/" so that slashes aren't appended causing 2739800Sbostic * paths to be written as "//foo". 2839800Sbostic */ 2939800Sbostic #define NAPPEND(p) \ 3042273Sbostic (p->fts_level == ROOTLEVEL && p->fts_pathlen == 1 && \ 3142273Sbostic p->fts_path[0] == '/' ? 0 : p->fts_pathlen) 3239800Sbostic 33*45600Sbostic #define ISSET(opt) (sp->fts_options & opt) 34*45600Sbostic #define SET(opt) (sp->fts_options |= opt) 3539800Sbostic 36*45600Sbostic #define CHDIR(sp, path) (!ISSET(FTS_NOCHDIR) && chdir(path)) 37*45600Sbostic #define FCHDIR(sp, fd) (!ISSET(FTS_NOCHDIR) && fchdir(fd)) 3839800Sbostic 3942299Sbostic /* fts_build flags */ 40*45600Sbostic #define BCHILD 1 /* from fts_children */ 41*45600Sbostic #define BREAD 2 /* from fts_read */ 4242299Sbostic 43*45600Sbostic /* fts_level values */ 44*45600Sbostic #define ROOTLEVEL 0 45*45600Sbostic #define ROOTPARENTLEVEL -1 4639800Sbostic 4739800Sbostic FTS * 48*45600Sbostic fts_open(argv, options, compar) 4939800Sbostic char *argv[]; 5039800Sbostic register int options; 5139800Sbostic int (*compar)(); 5239800Sbostic { 5339800Sbostic register FTS *sp; 5439800Sbostic register FTSENT *p, *root; 5539800Sbostic register int nitems, maxlen; 5639800Sbostic FTSENT *parent, *tmp; 5742299Sbostic char *fts_path(); 5839800Sbostic 59*45600Sbostic /* Allocate/initialize the stream */ 60*45600Sbostic if (!(sp = (FTS *)malloc((u_int)sizeof(FTS)))) 6139800Sbostic return(NULL); 6239800Sbostic bzero(sp, sizeof(FTS)); 6342273Sbostic sp->fts_compar = compar; 6442273Sbostic sp->fts_options = options; 6539800Sbostic 66*45600Sbostic /* Logical walks turn on NOCHDIR; symbolic links are too hard. */ 67*45600Sbostic if (ISSET(FTS_LOGICAL)) 68*45600Sbostic SET(FTS_NOCHDIR); 69*45600Sbostic 70*45600Sbostic /* Allocate/initialize root's parent. */ 71*45600Sbostic if (!(parent = fts_alloc(sp, "", 0))) 7239800Sbostic goto mem1; 7342273Sbostic parent->fts_level = ROOTPARENTLEVEL; 7439800Sbostic 75*45600Sbostic /* Allocate/initialize root(s). */ 7643070Sbostic maxlen = -1; 7743070Sbostic for (root = NULL, nitems = 0; *argv; ++argv, ++nitems) { 78*45600Sbostic if (!(p = fts_root(sp, *argv))) 7943070Sbostic goto mem2; 8043070Sbostic if (maxlen < p->fts_namelen) 8143070Sbostic maxlen = p->fts_namelen; 8243070Sbostic /* 83*45600Sbostic * If comparison routine supplied, traverse in sorted 8443070Sbostic * order; otherwise traverse in the order specified. 8543070Sbostic */ 8643070Sbostic if (compar) { 8743070Sbostic p->fts_link = root; 8843070Sbostic root = p; 8943070Sbostic p->fts_accpath = p->fts_name; 9043075Sbostic if (!(options & FTS_NOSTAT)) 91*45600Sbostic p->fts_info = fts_stat(sp, p, 0); 9243070Sbostic } else { 9343070Sbostic p->fts_link = NULL; 9443070Sbostic if (!root) 9543070Sbostic tmp = root = p; 9643070Sbostic else { 9743070Sbostic tmp->fts_link = p; 9843070Sbostic tmp = p; 9939800Sbostic } 10039800Sbostic } 10143070Sbostic p->fts_level = ROOTLEVEL; 10243070Sbostic p->fts_parent = parent; 10339800Sbostic } 10443070Sbostic if (compar && nitems > 1) 105*45600Sbostic root = fts_sort(sp, root, nitems); 10639800Sbostic 10742281Sbostic /* 108*45600Sbostic * Allocate a dummy pointer and make fts_read think that we've just 10942281Sbostic * finished the node before the root(s); set p->fts_info to FTS_NS 11042281Sbostic * so that everything about the "current" node is ignored. 11142281Sbostic */ 112*45600Sbostic if (!(sp->fts_cur = fts_alloc(sp, "", 0))) 11342281Sbostic goto mem2; 11442281Sbostic sp->fts_cur->fts_link = root; 11542281Sbostic sp->fts_cur->fts_info = FTS_NS; 11642281Sbostic 117*45600Sbostic /* Start out with at least 1K+ of path space. */ 118*45600Sbostic if (!fts_path(sp, MAX(maxlen, MAXPATHLEN))) 11942281Sbostic goto mem3; 12039800Sbostic 12139800Sbostic /* 122*45600Sbostic * If using chdir(2), grab a file descriptor pointing to dot to insure 12342299Sbostic * that we can get back here; this could be avoided for some paths, 12442299Sbostic * but almost certainly not worth the effort. Slashes, symbolic links, 12542299Sbostic * and ".." are all fairly nasty problems. Note, if we can't get the 12642299Sbostic * descriptor we run anyway, just more slowly. 12739800Sbostic */ 128*45600Sbostic if (!ISSET(FTS_NOCHDIR) && (sp->fts_sd = open(".", O_RDONLY, 0)) < 0) 129*45600Sbostic SET(FTS_NOCHDIR); 13039800Sbostic 13139800Sbostic return(sp); 13239800Sbostic 133*45600Sbostic mem3: free((void *)sp->fts_cur); 13439800Sbostic mem2: fts_lfree(root); 135*45600Sbostic free((void *)parent); 136*45600Sbostic mem1: free((void *)sp); 13739800Sbostic return(NULL); 13839800Sbostic } 13939800Sbostic 140*45600Sbostic static void 141*45600Sbostic fts_load(sp, p) 142*45600Sbostic FTS *sp; 14339800Sbostic register FTSENT *p; 14439800Sbostic { 14539800Sbostic register int len; 14639800Sbostic register char *cp; 14739800Sbostic 14839800Sbostic /* 149*45600Sbostic * Load the stream structure for the next traversal; set the 150*45600Sbostic * fts_accpath field specially so the chdir gets done to the 151*45600Sbostic * right place and the user can access the first node. 15239800Sbostic */ 15342273Sbostic len = p->fts_pathlen = p->fts_namelen; 154*45600Sbostic bcopy(p->fts_name, sp->fts_path, len + 1); 15542273Sbostic if ((cp = rindex(p->fts_name, '/')) && (cp != p->fts_name || cp[1])) { 15639800Sbostic len = strlen(++cp); 15742273Sbostic bcopy(cp, p->fts_name, len + 1); 15842273Sbostic p->fts_namelen = len; 15939800Sbostic } 160*45600Sbostic p->fts_accpath = p->fts_path = sp->fts_path; 16139800Sbostic } 16239800Sbostic 163*45600Sbostic fts_close(sp) 16439800Sbostic FTS *sp; 16539800Sbostic { 16639800Sbostic register FTSENT *freep, *p; 16739800Sbostic int saved_errno; 16839800Sbostic 16942281Sbostic if (sp->fts_cur) { 17042281Sbostic /* 171*45600Sbostic * This still works if we haven't read anything -- the dummy 17242281Sbostic * structure points to the root list, so we step through to 17342281Sbostic * the end of the root list which has a valid parent pointer. 17442281Sbostic */ 17542281Sbostic for (p = sp->fts_cur; p->fts_level > ROOTPARENTLEVEL;) { 17642281Sbostic freep = p; 17742281Sbostic p = p->fts_link ? p->fts_link : p->fts_parent; 178*45600Sbostic free((void *)freep); 17939800Sbostic } 180*45600Sbostic free((void *)p); 18142281Sbostic } 18239800Sbostic 183*45600Sbostic /* Free up child linked list, sort array, path buffer. */ 18442273Sbostic if (sp->fts_child) 18542273Sbostic fts_lfree(sp->fts_child); 18642273Sbostic if (sp->fts_array) 187*45600Sbostic free((void *)sp->fts_array); 188*45600Sbostic free((char *)sp->fts_path); 18939800Sbostic 19039800Sbostic /* 191*45600Sbostic * Return to original directory, save errno if necessary; free up 192*45600Sbostic * the directory buffer. 19339800Sbostic */ 194*45600Sbostic if (!ISSET(FTS_NOCHDIR)) { 19542299Sbostic saved_errno = fchdir(sp->fts_sd) ? errno : 0; 19642299Sbostic (void)close(sp->fts_sd); 19739800Sbostic } 19839800Sbostic 199*45600Sbostic /* Free up the stream pointer. */ 200*45600Sbostic free((void *)sp); 20139800Sbostic 202*45600Sbostic /* Set errno and return. */ 203*45600Sbostic if (!ISSET(FTS_NOCHDIR) && saved_errno) { 20439800Sbostic errno = saved_errno; 20539800Sbostic return(-1); 20639800Sbostic } 20739800Sbostic return(0); 20839800Sbostic } 20939800Sbostic 21039800Sbostic FTSENT * 211*45600Sbostic fts_read(sp) 21239800Sbostic register FTS *sp; 21339800Sbostic { 21442299Sbostic register FTSENT *p, *tmp; 21539800Sbostic register int instr; 21642299Sbostic register char *cp; 21739800Sbostic static int cd; 21839800Sbostic 219*45600Sbostic /* If finished or unrecoverable error, return NULL. */ 220*45600Sbostic if (!sp->fts_cur || ISSET(FTS__STOP)) 22139800Sbostic return(NULL); 22239800Sbostic 223*45600Sbostic /* Set current node pointer. */ 22442273Sbostic p = sp->fts_cur; 22539800Sbostic 226*45600Sbostic /* Save and zero out user instructions. */ 22742273Sbostic instr = p->fts_instr; 228*45600Sbostic p->fts_instr = FTS__NOINSTR; 22939800Sbostic 230*45600Sbostic /* If used fts_link pointer for cycle detection, restore it. */ 23142273Sbostic if (sp->fts_savelink) { 23242273Sbostic p->fts_link = sp->fts_savelink; 23342273Sbostic sp->fts_savelink = NULL; 23439800Sbostic } 23539800Sbostic 236*45600Sbostic /* Any type of file may be re-visited; re-stat and return. */ 23739800Sbostic if (instr == FTS_AGAIN) { 238*45600Sbostic p->fts_info = fts_stat(sp, p, 0); 23939800Sbostic return(p); 24039800Sbostic } 24139800Sbostic 242*45600Sbostic /* Following a symbolic link. */ 24342299Sbostic if (p->fts_info == FTS_SL && instr == FTS_FOLLOW) { 244*45600Sbostic p->fts_info = fts_stat(sp, p, 1); 24542299Sbostic return(p); 24642299Sbostic } 24742299Sbostic 248*45600Sbostic /* Directory in pre-order. */ 24942299Sbostic if (p->fts_info == FTS_D) { 250*45600Sbostic /* If skipped or crossed mount point, do post-order visit. */ 251*45600Sbostic if (instr == FTS_SKIP || ISSET(FTS_XDEV) && 25242280Sbostic p->fts_statb.st_dev != sp->sdev) { 25342273Sbostic if (sp->fts_child) { 25442273Sbostic fts_lfree(sp->fts_child); 25542273Sbostic sp->fts_child = NULL; 25639800Sbostic } 25742301Sbostic p->fts_info = FTS_DP; 25842301Sbostic return(p); 25942299Sbostic } 26042299Sbostic 261*45600Sbostic /* Read the directory if necessary, and return first entry. */ 26242299Sbostic if (sp->fts_child) 26342299Sbostic if (CHDIR(sp, p->fts_accpath)) { 26442299Sbostic fts_lfree(sp->fts_child); 26542299Sbostic p->fts_info = FTS_DNX; 26642299Sbostic } else { 26742299Sbostic p = sp->fts_child; 26842299Sbostic cp = sp->fts_path + NAPPEND(p->fts_parent); 26942299Sbostic *cp++ = '/'; 27042299Sbostic bcopy(p->fts_name, cp, p->fts_namelen + 1); 27142299Sbostic } 27242299Sbostic else { 27342299Sbostic if (!(sp->fts_child = fts_build(sp, BREAD))) 27439800Sbostic return(p); 27542273Sbostic p = sp->fts_child; 27639800Sbostic } 27742299Sbostic sp->fts_child = NULL; 27842299Sbostic return(sp->fts_cur = p); 27939800Sbostic } 28039800Sbostic 281*45600Sbostic /* Move to next node on this level. */ 28242299Sbostic next: tmp = p; 28342299Sbostic if (p = p->fts_link) { 28439800Sbostic /* 285*45600Sbostic * If root level node, set up paths and return. If not the 28639800Sbostic * first time, and it's not an absolute pathname, get back 287*45600Sbostic * to starting directory. If that fails, we're dead. 28839800Sbostic */ 28942273Sbostic if (p->fts_level == ROOTLEVEL) { 290*45600Sbostic fts_load(sp, p); 291*45600Sbostic free((void *)tmp); 29242281Sbostic if (cd && 29342299Sbostic p->fts_path[0] != '/' && FCHDIR(sp, sp->fts_sd)) { 294*45600Sbostic /* Can't get back to start; we're dead. */ 29542299Sbostic p->fts_path = "starting directory"; 29642281Sbostic p->fts_info = FTS_ERR; 297*45600Sbostic SET(FTS__STOP); 29842281Sbostic return(sp->fts_cur = p); 29942299Sbostic } 30042281Sbostic cd = 1; 301*45600Sbostic p->fts_info = fts_stat(sp, p, 0); 30242280Sbostic sp->sdev = p->fts_statb.st_dev; 30339800Sbostic } else { 304*45600Sbostic free((void *)tmp); 30542299Sbostic 306*45600Sbostic /* User may have called fts_set on node. */ 30742299Sbostic if (p->fts_instr == FTS_SKIP) 30842299Sbostic goto next; 30942299Sbostic if (p->fts_instr == FTS_FOLLOW) { 310*45600Sbostic p->fts_info = fts_stat(sp, p, 1); 311*45600Sbostic p->fts_instr = FTS__NOINSTR; 31242299Sbostic } 31342299Sbostic 314*45600Sbostic /* Fill in the paths. */ 31542273Sbostic cp = sp->fts_path + NAPPEND(p->fts_parent); 31639800Sbostic *cp++ = '/'; 31742273Sbostic bcopy(p->fts_name, cp, p->fts_namelen + 1); 31842299Sbostic 319*45600Sbostic /* Check for directory cycles. */ 32042273Sbostic if (p->fts_info == FTS_D && (tmp = fts_cycle(p))) { 32142273Sbostic sp->fts_savelink = p->fts_link; 32242273Sbostic p->fts_link = tmp; 32342299Sbostic p->fts_info = FTS_DC; 32439800Sbostic } 32539800Sbostic } 32642273Sbostic return(sp->fts_cur = p); 32739800Sbostic } 32839800Sbostic 329*45600Sbostic /* Move to parent. */ 33042299Sbostic p = tmp->fts_parent; 331*45600Sbostic free((void *)tmp); 33242299Sbostic 33342273Sbostic if (p->fts_level == ROOTPARENTLEVEL) { 33439800Sbostic /* 335*45600Sbostic * Done; free everything up and set errno to 0 so the user 33639800Sbostic * can distinguish between error and EOF. 33739800Sbostic */ 338*45600Sbostic free((void *)p); 33939800Sbostic errno = 0; 34042273Sbostic return(sp->fts_cur = NULL); 34139800Sbostic } 34239800Sbostic 34342273Sbostic sp->fts_path[p->fts_pathlen] = '\0'; 34439800Sbostic if (CHDIR(sp, "..")) { 345*45600Sbostic SET(FTS__STOP); 34642273Sbostic p->fts_info = FTS_ERR; 34739800Sbostic } else 34842273Sbostic p->fts_info = FTS_DP; 34942273Sbostic return(sp->fts_cur = p); 35039800Sbostic } 35139800Sbostic 35239800Sbostic /* 353*45600Sbostic * fts_set takes the stream as an argument although it's not used in this 35439800Sbostic * implementation; it would be necessary if anyone wanted to add global 355*45600Sbostic * semantics to fts using fts_set. An error return is allowed for similar 356*45600Sbostic * reasons. 35739800Sbostic */ 35839800Sbostic /* ARGSUSED */ 359*45600Sbostic fts_set(sp, p, instr) 36039800Sbostic FTS *sp; 36139800Sbostic FTSENT *p; 36239800Sbostic int instr; 36339800Sbostic { 36442273Sbostic p->fts_instr = instr; 36539800Sbostic return(0); 36639800Sbostic } 36739800Sbostic 36839800Sbostic FTSENT * 369*45600Sbostic fts_children(sp) 37039800Sbostic register FTS *sp; 37139800Sbostic { 37242299Sbostic register FTSENT *p; 37342299Sbostic int fd; 37442299Sbostic 375*45600Sbostic /* Set current node pointer. */ 376*45600Sbostic p = sp->fts_cur; 377*45600Sbostic 37839800Sbostic /* 379*45600Sbostic * Set errno to 0 so that user can tell the difference between an 38039800Sbostic * error and a directory without entries. 38139800Sbostic */ 38239800Sbostic errno = 0; 383*45600Sbostic if (ISSET(FTS__STOP) || 384*45600Sbostic p->fts_info != FTS_D && p->fts_info != FTS_DNX && 385*45600Sbostic p->fts_info != FTS_DNR) 38639800Sbostic return(NULL); 387*45600Sbostic 388*45600Sbostic /* Free up any previous child list. */ 38942273Sbostic if (sp->fts_child) 39042273Sbostic fts_lfree(sp->fts_child); 39142299Sbostic 39242299Sbostic /* 393*45600Sbostic * If using chdir on a relative path and called BEFORE fts_read does 394*45600Sbostic * its chdir to the root of a traversal, we can lose because we need 395*45600Sbostic * to chdir into the subdirectory, and we don't know where the current 396*45600Sbostic * directory is to get back so that the upcoming chdir by fts_read 397*45600Sbostic * will work. 39842299Sbostic */ 39942299Sbostic if (p->fts_level != ROOTLEVEL || p->fts_accpath[0] == '/' || 400*45600Sbostic ISSET(FTS_NOCHDIR)) 40142299Sbostic return(sp->fts_child = fts_build(sp, BCHILD)); 40242299Sbostic 40342299Sbostic if ((fd = open(".", O_RDONLY, 0)) < 0) 40442299Sbostic return(NULL); 40542299Sbostic sp->fts_child = fts_build(sp, BCHILD); 40642299Sbostic if (fchdir(fd)) 40742299Sbostic return(NULL); 40842299Sbostic (void)close(fd); 40942299Sbostic return(sp->fts_child); 41039800Sbostic } 41139800Sbostic 41239800Sbostic #define ISDOT(a) (a[0] == '.' && (!a[1] || a[1] == '.' && !a[2])) 41339800Sbostic 41442299Sbostic FTSENT * 41542299Sbostic fts_build(sp, type) 41639800Sbostic register FTS *sp; 41742299Sbostic int type; 41839800Sbostic { 41939800Sbostic register struct dirent *dp; 42039800Sbostic register FTSENT *p, *head; 42139800Sbostic register int nitems; 42239800Sbostic DIR *dirp; 42339962Sbostic int descend, len, level, maxlen, nlinks, saved_errno; 42439800Sbostic char *cp; 42539800Sbostic 426*45600Sbostic /* Set current node pointer. */ 42742273Sbostic p = sp->fts_cur; 428*45600Sbostic 42942273Sbostic if (!(dirp = opendir(p->fts_accpath))) { 43042299Sbostic if (type == BREAD) { 431*45600Sbostic p->fts_info = FTS_DNR; 43239800Sbostic errno = 0; 43339800Sbostic } 43439800Sbostic return(NULL); 43539800Sbostic } 43639800Sbostic 43739800Sbostic /* 438*45600Sbostic * The real slowdown in walking the tree is the stat calls. If 43939800Sbostic * FTS_NOSTAT is set and it's a physical walk (so that symbolic 44039800Sbostic * links can't be directories), fts assumes that the number of 44139800Sbostic * subdirectories in a node is equal to the number of links to 44239800Sbostic * the parent. This allows stat calls to be skipped in any leaf 44339800Sbostic * directories and for any nodes after the directories in the 44439800Sbostic * parent node have been found. This empirically cuts the stat 44539800Sbostic * calls by about 2/3. 44639800Sbostic */ 44742273Sbostic nlinks = 448*45600Sbostic ISSET(FTS_NOSTAT) && ISSET(FTS_PHYSICAL) ? 449*45600Sbostic p->fts_statb.st_nlink - (ISSET(FTS_SEEDOT) ? 0 : 2) : -1; 45039800Sbostic 451*45600Sbostic /* If told to descend or found links and not told not to descend. */ 452*45600Sbostic descend = 0; 453*45600Sbostic if (nlinks || type == BREAD) 454*45600Sbostic if (!FCHDIR(sp, dirfd(dirp))) 455*45600Sbostic descend = 1; 456*45600Sbostic /* 457*45600Sbostic * Return all the information possible; fts_read doing a 458*45600Sbostic * relative walk of the tree will have to descend, so it 459*45600Sbostic * can't succeed. Fts_children or absolute walks of the 460*45600Sbostic * tree can succeed, but no stat information will be available. 461*45600Sbostic */ 462*45600Sbostic else { 46342299Sbostic if (type == BREAD) { 464*45600Sbostic (void)closedir(dirp); 465*45600Sbostic p->fts_info = FTS_DNX; 46639962Sbostic errno = 0; 467*45600Sbostic return(NULL); 46839962Sbostic } 469*45600Sbostic nlinks = 0; 47039962Sbostic } 47139962Sbostic 472*45600Sbostic /* Get max file name length that can be stored in current path. */ 47342273Sbostic maxlen = sp->fts_pathlen - p->fts_pathlen - 1; 47440939Sbostic 47542273Sbostic cp = sp->fts_path + (len = NAPPEND(p)); 47640939Sbostic *cp++ = '/'; 47740939Sbostic 47842273Sbostic level = p->fts_level + 1; 47940939Sbostic 480*45600Sbostic /* Read the directory, attching each new entry to the `link' pointer. */ 48139800Sbostic for (head = NULL, nitems = 0; dp = readdir(dirp);) { 482*45600Sbostic if (ISDOT(dp->d_name) && !ISSET(FTS_SEEDOT)) 48339800Sbostic continue; 48439800Sbostic 485*45600Sbostic if (!(p = fts_alloc(sp, dp->d_name, (int)dp->d_namlen))) { 48639800Sbostic saved_errno = errno; 48739800Sbostic goto mem1; 48839800Sbostic } 48939800Sbostic if (dp->d_namlen > maxlen) { 490*45600Sbostic if (!fts_path(sp, (int)dp->d_namlen)) { 491*45600Sbostic /* Quit: this stream no longer has a path. */ 492*45600Sbostic SET(FTS__STOP); 49339800Sbostic saved_errno = errno; 494*45600Sbostic free((void *)p); 49539800Sbostic mem1: fts_lfree(head); 49642299Sbostic if (type == BREAD) 49742273Sbostic p->fts_info = FTS_ERR; 49839962Sbostic if (descend && CHDIR(sp, "..")) { 499*45600Sbostic /* 500*45600Sbostic * chdir error is more interesting 501*45600Sbostic * than memory error, since it stops 502*45600Sbostic * everything. 503*45600Sbostic */ 50439800Sbostic saved_errno = errno; 505*45600Sbostic SET(FTS__STOP); 50639800Sbostic } 50739800Sbostic errno = saved_errno; 50842299Sbostic (void)closedir(dirp); 50939800Sbostic return(NULL); 51039800Sbostic } 51142273Sbostic maxlen = sp->fts_pathlen - sp->fts_cur->fts_pathlen - 1; 51239800Sbostic } 51339800Sbostic 51442273Sbostic p->fts_pathlen = len + dp->d_namlen + 1; 515*45600Sbostic p->fts_accpath = ISSET(FTS_NOCHDIR) ? p->fts_path : p->fts_name; 51639800Sbostic 51742273Sbostic p->fts_parent = sp->fts_cur; 51842273Sbostic p->fts_level = level; 51939800Sbostic 52039800Sbostic if (nlinks) { 521*45600Sbostic /* Make sure fts_stat has a filename to stat. */ 522*45600Sbostic if (ISSET(FTS_NOCHDIR)) 52342273Sbostic bcopy(p->fts_name, cp, p->fts_namelen + 1); 524*45600Sbostic p->fts_info = fts_stat(sp, p, 0); 52542273Sbostic if (nlinks > 0 && (p->fts_info == FTS_D || 52642273Sbostic p->fts_info == FTS_DNR || p->fts_info == FTS_DNX)) 52739800Sbostic --nlinks; 52839800Sbostic } else 52942273Sbostic p->fts_info = FTS_NS; 53039800Sbostic 53142273Sbostic p->fts_link = head; 53239800Sbostic head = p; 53339800Sbostic ++nitems; 53439800Sbostic } 53539800Sbostic (void)closedir(dirp); 53639800Sbostic 537*45600Sbostic /* Reset the path. */ 53842299Sbostic if (cp - 1 > sp->fts_path) 53942299Sbostic --cp; 54042299Sbostic *cp = '\0'; 54142299Sbostic 54242299Sbostic /* 543*45600Sbostic * If descended: if were called from fts_read and didn't find anything, 544*45600Sbostic * or were called from fts_children, get back. 54542299Sbostic */ 54642299Sbostic if (descend && (!nitems || type == BCHILD) && CHDIR(sp, "..")) { 547*45600Sbostic SET(FTS__STOP); 54842273Sbostic p->fts_info = FTS_ERR; 54939962Sbostic return(NULL); 55039962Sbostic } 55139962Sbostic 55239800Sbostic if (!nitems) { 55342299Sbostic if (type == BREAD) 55442273Sbostic p->fts_info = FTS_DP; 55539800Sbostic return(NULL); 55639800Sbostic } 55739800Sbostic 55842273Sbostic if (sp->fts_compar && nitems > 1) 559*45600Sbostic head = fts_sort(sp, head, nitems); 56039800Sbostic 56142299Sbostic if (type == BREAD) { 56242299Sbostic *cp = '/'; 56342299Sbostic bcopy(head->fts_name, cp + 1, head->fts_namelen + 1); 56442299Sbostic } 56539800Sbostic return(head); 56639800Sbostic } 56739800Sbostic 568*45600Sbostic static u_short 569*45600Sbostic fts_stat(sp, p, follow) 570*45600Sbostic FTS *sp; 57139800Sbostic register FTSENT *p; 572*45600Sbostic int follow; 57339800Sbostic { 57439800Sbostic /* 575*45600Sbostic * If doing a logical walk, or application requested FTS_FOLLOW, do 576*45600Sbostic * a stat(2). If that fails, either fail or do an lstat(2) for a 577*45600Sbostic * non-existent symlink. (The check has to be done, or we wouldn't 578*45600Sbostic * detect a symlink being deleted.) 579*45600Sbostic * 580*45600Sbostic * Don't leave errno set for FTS_NS cases. XXX 58139800Sbostic */ 582*45600Sbostic if (ISSET(FTS_LOGICAL) || follow) { 583*45600Sbostic if (stat(p->fts_accpath, &p->fts_statb)) { 584*45600Sbostic errno = 0; 585*45600Sbostic if (follow && !lstat(p->fts_accpath, &p->fts_statb)) 586*45600Sbostic return(FTS_SLNONE); 587*45600Sbostic else { 588*45600Sbostic errno = 0; 589*45600Sbostic return(FTS_NS); 590*45600Sbostic } 591*45600Sbostic } 592*45600Sbostic } else if (lstat(p->fts_accpath, &p->fts_statb)) { 593*45600Sbostic errno = 0; 594*45600Sbostic return(FTS_NS); 595*45600Sbostic } 59639800Sbostic 597*45600Sbostic if (S_ISDIR(p->fts_statb.st_mode)) 59839800Sbostic return(FTS_D); 599*45600Sbostic if (S_ISLNK(p->fts_statb.st_mode)) 60039800Sbostic return(FTS_SL); 601*45600Sbostic if (S_ISREG(p->fts_statb.st_mode)) 60239800Sbostic return(FTS_F); 60340939Sbostic return(FTS_DEFAULT); 60439800Sbostic } 60539800Sbostic 60639800Sbostic static FTSENT * 60739800Sbostic fts_cycle(p) 60839800Sbostic register FTSENT *p; 60939800Sbostic { 61039800Sbostic register dev_t dev; 61139800Sbostic register ino_t ino; 61239800Sbostic 61339800Sbostic /* 614*45600Sbostic * Cycle detection is brute force; if the tree gets deep enough or 61539800Sbostic * the number of symbolic links to directories is really high 61639800Sbostic * something faster might be worthwhile. 61739800Sbostic */ 61842273Sbostic dev = p->fts_statb.st_dev; 61942273Sbostic ino = p->fts_statb.st_ino; 62042273Sbostic for (p = p->fts_parent; p->fts_level > ROOTLEVEL; p = p->fts_parent) 62142273Sbostic if (ino == p->fts_statb.st_ino && dev == p->fts_statb.st_dev) 62239800Sbostic return(p); 62339800Sbostic return(NULL); 62439800Sbostic } 62539800Sbostic 62639800Sbostic #define R(type, nelem, ptr) \ 62742299Sbostic (type *)realloc((void *)ptr, (u_int)((nelem) * sizeof(type))) 62839800Sbostic 62939800Sbostic static FTSENT * 630*45600Sbostic fts_sort(sp, head, nitems) 631*45600Sbostic FTS *sp; 63239800Sbostic FTSENT *head; 63339800Sbostic register int nitems; 63439800Sbostic { 63539800Sbostic register FTSENT **ap, *p; 63639800Sbostic 63739800Sbostic /* 638*45600Sbostic * Construct an array of pointers to the structures and call qsort(3). 63939800Sbostic * Reassemble the array in the order returned by qsort. If unable to 64039800Sbostic * sort for memory reasons, return the directory entries in their 64139800Sbostic * current order. Allocate enough space for the current needs plus 64239800Sbostic * 40 so we don't realloc one entry at a time. 64339800Sbostic */ 644*45600Sbostic if (nitems > sp->fts_nitems) { 645*45600Sbostic sp->fts_nitems = nitems + 40; 646*45600Sbostic if (!(sp->fts_array = 647*45600Sbostic R(FTSENT *, sp->fts_nitems, sp->fts_array))) { 648*45600Sbostic sp->fts_nitems = 0; 64939800Sbostic return(head); 65039800Sbostic } 65139800Sbostic } 652*45600Sbostic for (ap = sp->fts_array, p = head; p; p = p->fts_link) 65339800Sbostic *ap++ = p; 654*45600Sbostic qsort((void *)sp->fts_array, nitems, sizeof(FTSENT *), sp->fts_compar); 655*45600Sbostic for (head = *(ap = sp->fts_array); --nitems; ++ap) 65642273Sbostic ap[0]->fts_link = ap[1]; 65742273Sbostic ap[0]->fts_link = NULL; 65839800Sbostic return(head); 65939800Sbostic } 66039800Sbostic 66139800Sbostic static FTSENT * 662*45600Sbostic fts_alloc(sp, name, len) 663*45600Sbostic FTS *sp; 66439800Sbostic char *name; 66539800Sbostic register int len; 66639800Sbostic { 66739800Sbostic register FTSENT *p; 66839800Sbostic 66939800Sbostic /* 670*45600Sbostic * Variable sized structures; the name is the last element so 67139800Sbostic * allocate enough extra space after the structure to hold it. 67239800Sbostic */ 67342299Sbostic if (!(p = (FTSENT *)malloc((size_t)(sizeof(FTSENT) + len)))) 67439800Sbostic return(NULL); 67542273Sbostic bcopy(name, p->fts_name, len + 1); 67642273Sbostic p->fts_namelen = len; 677*45600Sbostic p->fts_path = sp->fts_path; 678*45600Sbostic p->fts_instr = FTS__NOINSTR; 679*45600Sbostic p->fts_number = 0; 680*45600Sbostic p->fts_pointer = NULL; 68139800Sbostic return(p); 68239800Sbostic } 68339800Sbostic 684*45600Sbostic static void 68539800Sbostic fts_lfree(head) 68639800Sbostic register FTSENT *head; 68739800Sbostic { 68839800Sbostic register FTSENT *p; 68939800Sbostic 690*45600Sbostic /* Free a linked list of structures. */ 69139800Sbostic while (p = head) { 69242273Sbostic head = head->fts_link; 693*45600Sbostic free((void *)p); 69439800Sbostic } 69539800Sbostic } 69639800Sbostic 69739800Sbostic /* 698*45600Sbostic * Allow essentially unlimited paths; certain programs (find, rm, ls) need to 699*45600Sbostic * work on any tree. Most systems will allow creation of paths much longer 700*45600Sbostic * than MAXPATHLEN, even though the kernel won't resolve them. Add an extra 701*45600Sbostic * 128 bytes to the requested size so that we don't realloc the path 2 bytes 702*45600Sbostic * at a time. 70339800Sbostic */ 70442299Sbostic static char * 705*45600Sbostic fts_path(sp, size) 706*45600Sbostic FTS *sp; 70739800Sbostic int size; 70839800Sbostic { 709*45600Sbostic sp->fts_pathlen += size + 128; 710*45600Sbostic return(sp->fts_path = R(char, sp->fts_pathlen, sp->fts_path)); } 71139800Sbostic 71239800Sbostic static FTSENT * 713*45600Sbostic fts_root(sp, name) 714*45600Sbostic FTS *sp; 71539800Sbostic register char *name; 71639800Sbostic { 71739800Sbostic register char *cp; 71839800Sbostic 71939800Sbostic /* 720*45600Sbostic * Rip trailing slashes; it's somewhat unclear in POSIX 1003.1 what 72139800Sbostic * /a/b/ really is, they don't talk about what a null path component 72239800Sbostic * resolves to. This hopefully does what the user intended. Don't 72339800Sbostic * allow null pathnames. 72439800Sbostic */ 72539800Sbostic for (cp = name; *cp; ++cp); 72639800Sbostic if (cp == name) { 72739800Sbostic errno = ENOENT; 72839800Sbostic return(NULL); 72939800Sbostic } 73039800Sbostic while (--cp > name && *cp == '/'); 73139800Sbostic *++cp = '\0'; 732*45600Sbostic return(fts_alloc(sp, name, cp - name)); 73339800Sbostic } 734