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*43075Sbostic static char sccsid[] = "@(#)fts.c 5.10 (Berkeley) 06/09/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 2139800Sbostic FTSENT *fts_alloc(), *fts_build(), *fts_cycle(), *fts_sort(), *fts_root(); 2239800Sbostic short fts_stat(); 2339800Sbostic 2439800Sbostic /* 2539800Sbostic * Special case a root of "/" so that slashes aren't appended causing 2639800Sbostic * paths to be written as "//foo". 2739800Sbostic */ 2839800Sbostic #define NAPPEND(p) \ 2942273Sbostic (p->fts_level == ROOTLEVEL && p->fts_pathlen == 1 && \ 3042273Sbostic p->fts_path[0] == '/' ? 0 : p->fts_pathlen) 3139800Sbostic 3242273Sbostic #define CHDIR(sp, path) (!(sp->fts_options & FTS_NOCHDIR) && chdir(path)) 3342273Sbostic #define FCHDIR(sp, fd) (!(sp->fts_options & FTS_NOCHDIR) && fchdir(fd)) 3439800Sbostic 3539800Sbostic #define ROOTLEVEL 0 3639800Sbostic #define ROOTPARENTLEVEL -1 3739800Sbostic 3842299Sbostic /* fts_build flags */ 3942299Sbostic #define BCHILD 1 /* from ftschildren */ 4042299Sbostic #define BREAD 2 /* from ftsread */ 4142299Sbostic 4239800Sbostic static FTS *stream; /* current stream pointer */ 4339800Sbostic 4439800Sbostic FTS * 4539800Sbostic ftsopen(argv, options, compar) 4639800Sbostic char *argv[]; 4739800Sbostic register int options; 4839800Sbostic int (*compar)(); 4939800Sbostic { 5039800Sbostic register FTS *sp; 5139800Sbostic register FTSENT *p, *root; 5239800Sbostic register int nitems, maxlen; 5339800Sbostic FTSENT *parent, *tmp; 5442299Sbostic char *fts_path(); 5539800Sbostic 5639800Sbostic /* allocate/initialize the stream */ 5739800Sbostic if (!(stream = sp = (FTS *)malloc((u_int)sizeof(FTS)))) 5839800Sbostic return(NULL); 5939800Sbostic bzero(sp, sizeof(FTS)); 6042273Sbostic sp->fts_compar = compar; 6142299Sbostic 6242299Sbostic /* 6342299Sbostic * logical walks turn on NOCHDIR; symbolic links are just too 6442299Sbostic * hard to deal with. 6542299Sbostic */ 6642273Sbostic sp->fts_options = options; 6742299Sbostic if (options & FTS_LOGICAL) 6842299Sbostic sp->fts_options |= FTS_NOCHDIR; 6939800Sbostic 7039800Sbostic /* allocate/initialize root's parent */ 7139800Sbostic if (!(parent = fts_alloc("", 0))) 7239800Sbostic goto mem1; 7342273Sbostic parent->fts_level = ROOTPARENTLEVEL; 7439800Sbostic 7539800Sbostic /* allocate/initialize root(s) */ 7643070Sbostic maxlen = -1; 7743070Sbostic for (root = NULL, nitems = 0; *argv; ++argv, ++nitems) { 7843070Sbostic if (!(p = fts_root(*argv))) 7943070Sbostic goto mem2; 8043070Sbostic if (maxlen < p->fts_namelen) 8143070Sbostic maxlen = p->fts_namelen; 8243070Sbostic /* 8343070Sbostic * 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; 90*43075Sbostic if (!(options & FTS_NOSTAT)) 91*43075Sbostic p->fts_info = fts_stat(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) 10543070Sbostic root = fts_sort(root, nitems); 10639800Sbostic 10742281Sbostic /* 10842281Sbostic * allocate a dummy pointer and make ftsread 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 */ 11242281Sbostic if (!(sp->fts_cur = fts_alloc("", 0))) 11342281Sbostic goto mem2; 11442281Sbostic sp->fts_cur->fts_link = root; 11542281Sbostic sp->fts_cur->fts_info = FTS_NS; 11642281Sbostic 11739800Sbostic /* start out with at least 1K+ of path space */ 11839800Sbostic if (!fts_path(MAX(maxlen, MAXPATHLEN))) 11942281Sbostic goto mem3; 12039800Sbostic 12139800Sbostic /* 12242299Sbostic * 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 */ 12842273Sbostic if (!(options & FTS_NOCHDIR) && 12942299Sbostic (sp->fts_sd = open(".", O_RDONLY, 0)) < 0) 13042273Sbostic sp->fts_options |= FTS_NOCHDIR; 13139800Sbostic 13239800Sbostic return(sp); 13339800Sbostic 13442299Sbostic mem3: (void)free((void *)sp->fts_cur); 13539800Sbostic mem2: fts_lfree(root); 13642299Sbostic (void)free((void *)parent); 13742299Sbostic mem1: (void)free((void *)sp); 13839800Sbostic return(NULL); 13939800Sbostic } 14039800Sbostic 14139800Sbostic static 14239800Sbostic fts_load(p) 14339800Sbostic register FTSENT *p; 14439800Sbostic { 14539800Sbostic register int len; 14639800Sbostic register char *cp; 14739800Sbostic 14839800Sbostic /* 14939800Sbostic * load the stream structure for the next traversal; set the 15039800Sbostic * accpath field specially so the chdir gets done to the right 15139800Sbostic * place and the user can access the first node. 15239800Sbostic */ 15342273Sbostic len = p->fts_pathlen = p->fts_namelen; 15442273Sbostic bcopy(p->fts_name, stream->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 } 16042273Sbostic p->fts_accpath = p->fts_path = stream->fts_path; 16139800Sbostic } 16239800Sbostic 16339800Sbostic ftsclose(sp) 16439800Sbostic FTS *sp; 16539800Sbostic { 16639800Sbostic register FTSENT *freep, *p; 16739800Sbostic int saved_errno; 16839800Sbostic 16942281Sbostic if (sp->fts_cur) { 17042281Sbostic /* 17142281Sbostic * 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; 17842299Sbostic (void)free((void *)freep); 17939800Sbostic } 18042299Sbostic (void)free((void *)p); 18142281Sbostic } 18239800Sbostic 18339800Sbostic /* 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) 18742299Sbostic (void)free((void *)sp->fts_array); 18842273Sbostic (void)free((char *)sp->fts_path); 18939800Sbostic 19039800Sbostic /* 19139800Sbostic * return to original directory, save errno if necessary; 19239800Sbostic * free up the directory buffer 19339800Sbostic */ 19442273Sbostic if (!(sp->fts_options & FTS_NOCHDIR)) { 19542299Sbostic saved_errno = fchdir(sp->fts_sd) ? errno : 0; 19642299Sbostic (void)close(sp->fts_sd); 19739800Sbostic } 19839800Sbostic 19939800Sbostic /* free up the stream pointer */ 20042299Sbostic (void)free((void *)sp); 20139800Sbostic 20239800Sbostic /* set errno and return */ 20342273Sbostic if (!(sp->fts_options & FTS_NOCHDIR) && saved_errno) { 20439800Sbostic errno = saved_errno; 20539800Sbostic return(-1); 20639800Sbostic } 20739800Sbostic return(0); 20839800Sbostic } 20939800Sbostic 21039800Sbostic FTSENT * 21139800Sbostic ftsread(sp) 21239800Sbostic register FTS *sp; 21339800Sbostic { 21442299Sbostic register FTSENT *p, *tmp; 21539800Sbostic register int instr; 21642299Sbostic register char *cp; 21739800Sbostic static int cd; 21839800Sbostic 21939800Sbostic /* if finished or unrecoverable error, return NULL */ 22042273Sbostic if (!sp->fts_cur || sp->fts_options & FTS__STOP) 22139800Sbostic return(NULL); 22239800Sbostic 22339800Sbostic /* set global stream pointer, and current node pointer */ 22439800Sbostic stream = sp; 22542273Sbostic p = sp->fts_cur; 22639800Sbostic 22739800Sbostic /* save and zero out user instructions */ 22842273Sbostic instr = p->fts_instr; 22942273Sbostic p->fts_instr = 0; 23039800Sbostic 23139800Sbostic /* if used link pointer for cycle detection, restore it */ 23242273Sbostic if (sp->fts_savelink) { 23342273Sbostic p->fts_link = sp->fts_savelink; 23442273Sbostic sp->fts_savelink = NULL; 23539800Sbostic } 23639800Sbostic 23739800Sbostic /* any type of file may be re-visited; re-stat and return */ 23839800Sbostic if (instr == FTS_AGAIN) { 23942273Sbostic p->fts_info = fts_stat(p, 0); 24039800Sbostic return(p); 24139800Sbostic } 24239800Sbostic 24342299Sbostic /* following a symbolic link */ 24442299Sbostic if (p->fts_info == FTS_SL && instr == FTS_FOLLOW) { 24542299Sbostic p->fts_info = fts_stat(p, 1); 24642299Sbostic return(p); 24742299Sbostic } 24842299Sbostic 24942299Sbostic /* directory in pre-order */ 25042299Sbostic if (p->fts_info == FTS_D) { 25142301Sbostic /* if skipped or crossed mount point, do post-order visit */ 25242280Sbostic if (instr == FTS_SKIP || sp->fts_options & FTS_XDEV && 25342280Sbostic p->fts_statb.st_dev != sp->sdev) { 25442273Sbostic if (sp->fts_child) { 25542273Sbostic fts_lfree(sp->fts_child); 25642273Sbostic sp->fts_child = NULL; 25739800Sbostic } 25842301Sbostic p->fts_info = FTS_DP; 25942301Sbostic return(p); 26042299Sbostic } 26142299Sbostic 26242299Sbostic /* read the directory if necessary, and return first entry */ 26342299Sbostic if (sp->fts_child) 26442299Sbostic if (CHDIR(sp, p->fts_accpath)) { 26542299Sbostic fts_lfree(sp->fts_child); 26642299Sbostic p->fts_info = FTS_DNX; 26742299Sbostic } else { 26842299Sbostic p = sp->fts_child; 26942299Sbostic cp = sp->fts_path + NAPPEND(p->fts_parent); 27042299Sbostic *cp++ = '/'; 27142299Sbostic bcopy(p->fts_name, cp, p->fts_namelen + 1); 27242299Sbostic } 27342299Sbostic else { 27442299Sbostic if (!(sp->fts_child = fts_build(sp, BREAD))) 27539800Sbostic return(p); 27642273Sbostic p = sp->fts_child; 27739800Sbostic } 27842299Sbostic sp->fts_child = NULL; 27942299Sbostic return(sp->fts_cur = p); 28039800Sbostic } 28139800Sbostic 28242299Sbostic /* move to next node on this level */ 28342299Sbostic next: tmp = p; 28442299Sbostic if (p = p->fts_link) { 28539800Sbostic /* 28639800Sbostic * if root level node, set up paths and return. If not the 28739800Sbostic * first time, and it's not an absolute pathname, get back 28842299Sbostic * to starting directory. 28939800Sbostic */ 29042273Sbostic if (p->fts_level == ROOTLEVEL) { 29139800Sbostic fts_load(p); 29242299Sbostic (void)free((void *)tmp); 29342281Sbostic if (cd && 29442299Sbostic p->fts_path[0] != '/' && FCHDIR(sp, sp->fts_sd)) { 29542299Sbostic /* should never happen... */ 29642299Sbostic p->fts_path = "starting directory"; 29742281Sbostic p->fts_info = FTS_ERR; 29842281Sbostic sp->fts_options |= FTS__STOP; 29942281Sbostic return(sp->fts_cur = p); 30042299Sbostic } 30142281Sbostic cd = 1; 30242273Sbostic p->fts_info = fts_stat(p, 0); 30342280Sbostic sp->sdev = p->fts_statb.st_dev; 30439800Sbostic } else { 30542299Sbostic (void)free((void *)tmp); 30642299Sbostic 30742299Sbostic /* user may have called ftsset on node */ 30842299Sbostic if (p->fts_instr == FTS_SKIP) 30942299Sbostic goto next; 31042299Sbostic if (p->fts_instr == FTS_FOLLOW) { 31142299Sbostic p->fts_info = fts_stat(p, 1); 31242299Sbostic p->fts_instr = 0; 31342299Sbostic } 31442299Sbostic 31542299Sbostic /* fill in the paths */ 31642273Sbostic cp = sp->fts_path + NAPPEND(p->fts_parent); 31739800Sbostic *cp++ = '/'; 31842273Sbostic bcopy(p->fts_name, cp, p->fts_namelen + 1); 31942299Sbostic 32042299Sbostic /* check for directory cycles */ 32142273Sbostic if (p->fts_info == FTS_D && (tmp = fts_cycle(p))) { 32242273Sbostic sp->fts_savelink = p->fts_link; 32342273Sbostic p->fts_link = tmp; 32442299Sbostic p->fts_info = FTS_DC; 32539800Sbostic } 32639800Sbostic } 32742273Sbostic return(sp->fts_cur = p); 32839800Sbostic } 32939800Sbostic 33042299Sbostic /* move to parent */ 33142299Sbostic p = tmp->fts_parent; 33242299Sbostic (void)free((void *)tmp); 33342299Sbostic 33442273Sbostic if (p->fts_level == ROOTPARENTLEVEL) { 33539800Sbostic /* 33639800Sbostic * done; free everything up and set errno to 0 so the user 33739800Sbostic * can distinguish between error and EOF. 33839800Sbostic */ 33942299Sbostic (void)free((void *)p); 34039800Sbostic errno = 0; 34142273Sbostic return(sp->fts_cur = NULL); 34239800Sbostic } 34339800Sbostic 34442273Sbostic sp->fts_path[p->fts_pathlen] = '\0'; 34539800Sbostic if (CHDIR(sp, "..")) { 34642273Sbostic sp->fts_options |= FTS__STOP; 34742273Sbostic p->fts_info = FTS_ERR; 34839800Sbostic } else 34942273Sbostic p->fts_info = FTS_DP; 35042273Sbostic return(sp->fts_cur = p); 35139800Sbostic } 35239800Sbostic 35339800Sbostic /* 35439800Sbostic * ftsset takes the stream as an argument although it's not used in this 35539800Sbostic * implementation; it would be necessary if anyone wanted to add global 35639800Sbostic * semantics to fts using ftsset. A possible error return is allowed for 35739800Sbostic * similar reasons. 35839800Sbostic */ 35939800Sbostic /* ARGSUSED */ 36039800Sbostic ftsset(sp, p, instr) 36139800Sbostic FTS *sp; 36239800Sbostic FTSENT *p; 36339800Sbostic int instr; 36439800Sbostic { 36542273Sbostic p->fts_instr = instr; 36639800Sbostic return(0); 36739800Sbostic } 36839800Sbostic 36939800Sbostic FTSENT * 37039800Sbostic ftschildren(sp) 37139800Sbostic register FTS *sp; 37239800Sbostic { 37342299Sbostic register FTSENT *p; 37442299Sbostic int fd; 37542299Sbostic 37639800Sbostic /* 37739800Sbostic * set errno to 0 so that user can tell the difference between an 37839800Sbostic * error and a directory without entries. 37939800Sbostic */ 38039800Sbostic errno = 0; 38142299Sbostic p = sp->fts_cur; 38242299Sbostic if (p->fts_info != FTS_D || sp->fts_options & FTS__STOP) 38339800Sbostic return(NULL); 38442273Sbostic if (sp->fts_child) 38542273Sbostic fts_lfree(sp->fts_child); 38642299Sbostic 38742299Sbostic /* 38842299Sbostic * if using chdir on a relative path and called BEFORE ftsread on the 38942299Sbostic * root of a traversal, we can lose because we need to chdir into the 39042299Sbostic * subdirectory, and we don't know where the current directory is to 39142299Sbostic * get back so that the upcoming chdir by ftsread will work. 39242299Sbostic */ 39342299Sbostic if (p->fts_level != ROOTLEVEL || p->fts_accpath[0] == '/' || 39442299Sbostic sp->fts_options & FTS_NOCHDIR) 39542299Sbostic return(sp->fts_child = fts_build(sp, BCHILD)); 39642299Sbostic 39742299Sbostic if ((fd = open(".", O_RDONLY, 0)) < 0) 39842299Sbostic return(NULL); 39942299Sbostic sp->fts_child = fts_build(sp, BCHILD); 40042299Sbostic if (fchdir(fd)) 40142299Sbostic return(NULL); 40242299Sbostic (void)close(fd); 40342299Sbostic return(sp->fts_child); 40439800Sbostic } 40539800Sbostic 40639800Sbostic #define ISDOT(a) (a[0] == '.' && (!a[1] || a[1] == '.' && !a[2])) 40739800Sbostic 40842299Sbostic FTSENT * 40942299Sbostic fts_build(sp, type) 41039800Sbostic register FTS *sp; 41142299Sbostic int type; 41239800Sbostic { 41339800Sbostic register struct dirent *dp; 41439800Sbostic register FTSENT *p, *head; 41539800Sbostic register int nitems; 41639800Sbostic DIR *dirp; 41739962Sbostic int descend, len, level, maxlen, nlinks, saved_errno; 41839800Sbostic char *cp; 41939800Sbostic 42042273Sbostic p = sp->fts_cur; 42142273Sbostic if (!(dirp = opendir(p->fts_accpath))) { 42242299Sbostic if (type == BREAD) { 42339800Sbostic errno = 0; 42442273Sbostic p->fts_info = FTS_DNR; 42539800Sbostic } 42639800Sbostic return(NULL); 42739800Sbostic } 42839800Sbostic 42939800Sbostic /* 43039800Sbostic * the real slowdown in walking the tree is the stat calls. If 43139800Sbostic * FTS_NOSTAT is set and it's a physical walk (so that symbolic 43239800Sbostic * links can't be directories), fts assumes that the number of 43339800Sbostic * subdirectories in a node is equal to the number of links to 43439800Sbostic * the parent. This allows stat calls to be skipped in any leaf 43539800Sbostic * directories and for any nodes after the directories in the 43639800Sbostic * parent node have been found. This empirically cuts the stat 43739800Sbostic * calls by about 2/3. 43839800Sbostic */ 43942273Sbostic nlinks = 44042273Sbostic sp->fts_options & FTS_NOSTAT && sp->fts_options & FTS_PHYSICAL ? 44142273Sbostic p->fts_statb.st_nlink - (sp->fts_options & FTS_SEEDOT ? 0 : 2) : -1; 44239800Sbostic 44342299Sbostic /* if told to descend or found links and not told not to descend. */ 44442299Sbostic if (nlinks || type == BREAD) { 44539962Sbostic if (FCHDIR(sp, dirfd(dirp))) { 44642299Sbostic if (type == BREAD) { 44739962Sbostic errno = 0; 44842273Sbostic p->fts_info = FTS_DNX; 44939962Sbostic } 45042299Sbostic (void)closedir(dirp); 45139962Sbostic return(NULL); 45239962Sbostic } 45339962Sbostic descend = 1; 45439962Sbostic } else 45539962Sbostic descend = 0; 45639962Sbostic 45740939Sbostic /* get max file name length that can be stored in current path */ 45842273Sbostic maxlen = sp->fts_pathlen - p->fts_pathlen - 1; 45940939Sbostic 46042273Sbostic cp = sp->fts_path + (len = NAPPEND(p)); 46140939Sbostic *cp++ = '/'; 46240939Sbostic 46342273Sbostic level = p->fts_level + 1; 46440939Sbostic 46539800Sbostic /* read the directory, attching each new entry to the `link' pointer */ 46639800Sbostic for (head = NULL, nitems = 0; dp = readdir(dirp);) { 46742273Sbostic if (ISDOT(dp->d_name) && !(sp->fts_options & FTS_SEEDOT)) 46839800Sbostic continue; 46939800Sbostic 47039800Sbostic if (!(p = fts_alloc(dp->d_name, dp->d_namlen))) { 47139800Sbostic saved_errno = errno; 47239800Sbostic goto mem1; 47339800Sbostic } 47439800Sbostic if (dp->d_namlen > maxlen) { 47539800Sbostic if (!fts_path((int)dp->d_namlen)) { 47639800Sbostic /* quit: this stream no longer has a path */ 47742273Sbostic sp->fts_options |= FTS__STOP; 47839800Sbostic saved_errno = errno; 47942299Sbostic (void)free((void *)p); 48039800Sbostic mem1: fts_lfree(head); 48142299Sbostic if (type == BREAD) 48242273Sbostic p->fts_info = FTS_ERR; 48339962Sbostic if (descend && CHDIR(sp, "..")) { 48439800Sbostic saved_errno = errno; 48542273Sbostic sp->fts_options |= FTS__STOP; 48639800Sbostic } 48739800Sbostic errno = saved_errno; 48842299Sbostic (void)closedir(dirp); 48939800Sbostic return(NULL); 49039800Sbostic } 49142273Sbostic maxlen = sp->fts_pathlen - sp->fts_cur->fts_pathlen - 1; 49239800Sbostic } 49339800Sbostic 49442273Sbostic p->fts_pathlen = len + dp->d_namlen + 1; 49542273Sbostic p->fts_accpath = 49642273Sbostic sp->fts_options & FTS_NOCHDIR ? p->fts_path : p->fts_name; 49739800Sbostic 49842273Sbostic p->fts_parent = sp->fts_cur; 49942273Sbostic p->fts_level = level; 50039800Sbostic 50139800Sbostic if (nlinks) { 50239800Sbostic /* make sure fts_stat has a filename to stat */ 50342273Sbostic if (sp->fts_options & FTS_NOCHDIR) 50442273Sbostic bcopy(p->fts_name, cp, p->fts_namelen + 1); 50542273Sbostic p->fts_info = fts_stat(p, 0); 50642273Sbostic if (nlinks > 0 && (p->fts_info == FTS_D || 50742273Sbostic p->fts_info == FTS_DNR || p->fts_info == FTS_DNX)) 50839800Sbostic --nlinks; 50939800Sbostic } else 51042273Sbostic p->fts_info = FTS_NS; 51139800Sbostic 51242273Sbostic p->fts_link = head; 51339800Sbostic head = p; 51439800Sbostic ++nitems; 51539800Sbostic } 51639800Sbostic (void)closedir(dirp); 51739800Sbostic 51842299Sbostic /* reset the path */ 51942299Sbostic if (cp - 1 > sp->fts_path) 52042299Sbostic --cp; 52142299Sbostic *cp = '\0'; 52242299Sbostic 52342299Sbostic /* 52442299Sbostic * if descended: if were called from ftsread and didn't find anything, 52542299Sbostic * or were called from ftschildren, get back. 52642299Sbostic */ 52742299Sbostic if (descend && (!nitems || type == BCHILD) && CHDIR(sp, "..")) { 52842273Sbostic sp->fts_options |= FTS__STOP; 52942273Sbostic p->fts_info = FTS_ERR; 53039962Sbostic return(NULL); 53139962Sbostic } 53239962Sbostic 53339800Sbostic if (!nitems) { 53442299Sbostic if (type == BREAD) 53542273Sbostic p->fts_info = FTS_DP; 53639800Sbostic return(NULL); 53739800Sbostic } 53839800Sbostic 53942273Sbostic if (sp->fts_compar && nitems > 1) 54039800Sbostic head = fts_sort(head, nitems); 54139800Sbostic 54242299Sbostic if (type == BREAD) { 54342299Sbostic *cp = '/'; 54442299Sbostic bcopy(head->fts_name, cp + 1, head->fts_namelen + 1); 54542299Sbostic } 54639800Sbostic return(head); 54739800Sbostic } 54839800Sbostic 54939800Sbostic static short 55039800Sbostic fts_stat(p, symflag) 55139800Sbostic register FTSENT *p; 55239800Sbostic int symflag; 55339800Sbostic { 55439800Sbostic /* 55539800Sbostic * detection of symbolic links w/o targets. If FTS_FOLLOW is set, 55639800Sbostic * the symlink structure is overwritten with the stat structure of 55739800Sbostic * the target. 55839800Sbostic */ 55942273Sbostic if (stream->fts_options & FTS_LOGICAL || symflag) { 56042273Sbostic if (stat(p->fts_accpath, &p->fts_statb)) 56142273Sbostic return(symflag || !lstat(p->fts_accpath, 56242273Sbostic &p->fts_statb) ? FTS_SLNONE : FTS_ERR); 56342273Sbostic } else if (lstat(p->fts_accpath, &p->fts_statb)) 56439800Sbostic return(FTS_ERR); 56539800Sbostic 56642273Sbostic switch(p->fts_statb.st_mode&S_IFMT) { 56739800Sbostic case S_IFDIR: 56839800Sbostic return(FTS_D); 56939800Sbostic case S_IFLNK: 57039800Sbostic return(FTS_SL); 57139800Sbostic case S_IFREG: 57239800Sbostic return(FTS_F); 57339800Sbostic } 57440939Sbostic return(FTS_DEFAULT); 57539800Sbostic } 57639800Sbostic 57739800Sbostic static FTSENT * 57839800Sbostic fts_cycle(p) 57939800Sbostic register FTSENT *p; 58039800Sbostic { 58139800Sbostic register dev_t dev; 58239800Sbostic register ino_t ino; 58339800Sbostic 58439800Sbostic /* 58539800Sbostic * cycle detection is brute force; if the tree gets deep enough or 58639800Sbostic * the number of symbolic links to directories is really high 58739800Sbostic * something faster might be worthwhile. 58839800Sbostic */ 58942273Sbostic dev = p->fts_statb.st_dev; 59042273Sbostic ino = p->fts_statb.st_ino; 59142273Sbostic for (p = p->fts_parent; p->fts_level > ROOTLEVEL; p = p->fts_parent) 59242273Sbostic if (ino == p->fts_statb.st_ino && dev == p->fts_statb.st_dev) 59339800Sbostic return(p); 59439800Sbostic return(NULL); 59539800Sbostic } 59639800Sbostic 59739800Sbostic #define R(type, nelem, ptr) \ 59842299Sbostic (type *)realloc((void *)ptr, (u_int)((nelem) * sizeof(type))) 59939800Sbostic 60039800Sbostic static FTSENT * 60139800Sbostic fts_sort(head, nitems) 60239800Sbostic FTSENT *head; 60339800Sbostic register int nitems; 60439800Sbostic { 60539800Sbostic register FTSENT **ap, *p; 60639800Sbostic 60739800Sbostic /* 60839800Sbostic * construct an array of pointers to the structures and call qsort(3). 60939800Sbostic * Reassemble the array in the order returned by qsort. If unable to 61039800Sbostic * sort for memory reasons, return the directory entries in their 61139800Sbostic * current order. Allocate enough space for the current needs plus 61239800Sbostic * 40 so we don't realloc one entry at a time. 61339800Sbostic */ 61442273Sbostic if (nitems > stream->fts_nitems) { 61542273Sbostic stream->fts_nitems = nitems + 40; 61642273Sbostic if (!(stream->fts_array = 61742273Sbostic R(FTSENT *, stream->fts_nitems, stream->fts_array))) { 61842273Sbostic stream->fts_nitems = 0; 61939800Sbostic return(head); 62039800Sbostic } 62139800Sbostic } 62242273Sbostic for (ap = stream->fts_array, p = head; p; p = p->fts_link) 62339800Sbostic *ap++ = p; 62442299Sbostic qsort((void *)stream->fts_array, nitems, sizeof(FTSENT *), 62542273Sbostic stream->fts_compar); 62642273Sbostic for (head = *(ap = stream->fts_array); --nitems; ++ap) 62742273Sbostic ap[0]->fts_link = ap[1]; 62842273Sbostic ap[0]->fts_link = NULL; 62939800Sbostic return(head); 63039800Sbostic } 63139800Sbostic 63239800Sbostic static FTSENT * 63339800Sbostic fts_alloc(name, len) 63439800Sbostic char *name; 63539800Sbostic register int len; 63639800Sbostic { 63739800Sbostic register FTSENT *p; 63839800Sbostic 63939800Sbostic /* 64039800Sbostic * variable sized structures; the name is the last element so 64139800Sbostic * allocate enough extra space after the structure to hold it. 64239800Sbostic */ 64342299Sbostic if (!(p = (FTSENT *)malloc((size_t)(sizeof(FTSENT) + len)))) 64439800Sbostic return(NULL); 64542273Sbostic bcopy(name, p->fts_name, len + 1); 64642273Sbostic p->fts_namelen = len; 64742273Sbostic p->fts_path = stream->fts_path; 64842273Sbostic p->fts_instr = 0; 64942273Sbostic p->fts_local.number = 0; 65042273Sbostic p->fts_local.pointer = NULL; 65139800Sbostic return(p); 65239800Sbostic } 65339800Sbostic 65439800Sbostic static 65539800Sbostic fts_lfree(head) 65639800Sbostic register FTSENT *head; 65739800Sbostic { 65839800Sbostic register FTSENT *p; 65939800Sbostic 66039800Sbostic while (p = head) { 66142273Sbostic head = head->fts_link; 66242299Sbostic (void)free((void *)p); 66339800Sbostic } 66439800Sbostic } 66539800Sbostic 66639800Sbostic /* 66739800Sbostic * allow essentially unlimited paths; certain programs (find, remove, ls) 66839800Sbostic * need to work on any tree. Most systems will allow creation of paths 66939800Sbostic * much longer than MAXPATHLEN, even though the kernel won't resolve them. 67039800Sbostic * Add an extra 128 bytes to the requested size so that we don't realloc 67139800Sbostic * the path 2 bytes at a time. 67239800Sbostic */ 67342299Sbostic static char * 67439800Sbostic fts_path(size) 67539800Sbostic int size; 67639800Sbostic { 67742273Sbostic stream->fts_pathlen += size + 128; 67842299Sbostic return(stream->fts_path = 67942299Sbostic R(char, stream->fts_pathlen, stream->fts_path)); 68039800Sbostic } 68139800Sbostic 68239800Sbostic static FTSENT * 68339800Sbostic fts_root(name) 68439800Sbostic register char *name; 68539800Sbostic { 68639800Sbostic register char *cp; 68739800Sbostic 68839800Sbostic /* 68939800Sbostic * rip trailing slashes; it's somewhat unclear in POSIX 1003.1 what 69039800Sbostic * /a/b/ really is, they don't talk about what a null path component 69139800Sbostic * resolves to. This hopefully does what the user intended. Don't 69239800Sbostic * allow null pathnames. 69339800Sbostic */ 69439800Sbostic for (cp = name; *cp; ++cp); 69539800Sbostic if (cp == name) { 69639800Sbostic errno = ENOENT; 69739800Sbostic return(NULL); 69839800Sbostic } 69939800Sbostic while (--cp > name && *cp == '/'); 70039800Sbostic *++cp = '\0'; 70139800Sbostic return(fts_alloc(name, cp - name)); 70239800Sbostic } 703