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*42281Sbostic static char sccsid[] = "@(#)fts.c 5.6 (Berkeley) 05/22/90"; 1039800Sbostic #endif /* LIBC_SCCS and not lint */ 1139800Sbostic 1239800Sbostic #include <sys/param.h> 1339800Sbostic #include <sys/stat.h> 1439800Sbostic #include <dirent.h> 1539800Sbostic #include <errno.h> 1639800Sbostic #include <fts.h> 1742273Sbostic #include <string.h> 18*42281Sbostic #include <stdlib.h> 1939800Sbostic 2039800Sbostic extern int errno; 2139800Sbostic 2239800Sbostic FTSENT *fts_alloc(), *fts_build(), *fts_cycle(), *fts_sort(), *fts_root(); 2339800Sbostic 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 3342273Sbostic #define CHDIR(sp, path) (!(sp->fts_options & FTS_NOCHDIR) && chdir(path)) 3442273Sbostic #define FCHDIR(sp, fd) (!(sp->fts_options & FTS_NOCHDIR) && fchdir(fd)) 3539800Sbostic 3639800Sbostic #define ROOTLEVEL 0 3739800Sbostic #define ROOTPARENTLEVEL -1 3839800Sbostic 3939800Sbostic static FTS *stream; /* current stream pointer */ 4039800Sbostic 4139800Sbostic FTS * 4239800Sbostic ftsopen(argv, options, compar) 4339800Sbostic char *argv[]; 4439800Sbostic register int options; 4539800Sbostic int (*compar)(); 4639800Sbostic { 4739800Sbostic register FTS *sp; 4839800Sbostic register FTSENT *p, *root; 4939800Sbostic register int nitems, maxlen; 5039800Sbostic FTSENT *parent, *tmp; 5139800Sbostic char wd[MAXPATHLEN], *getwd(), *strdup(); 5239800Sbostic 5339800Sbostic /* allocate/initialize the stream */ 5439800Sbostic if (!(stream = sp = (FTS *)malloc((u_int)sizeof(FTS)))) 5539800Sbostic return(NULL); 5639800Sbostic bzero(sp, sizeof(FTS)); 5742273Sbostic sp->fts_compar = compar; 5842273Sbostic sp->fts_options = options; 5939800Sbostic 6039800Sbostic /* allocate/initialize root's parent */ 6139800Sbostic if (!(parent = fts_alloc("", 0))) 6239800Sbostic goto mem1; 6342273Sbostic parent->fts_level = ROOTPARENTLEVEL; 6439800Sbostic 6539800Sbostic /* allocate/initialize root(s) */ 6639800Sbostic if (options & FTS_MULTIPLE) { 6739800Sbostic maxlen = -1; 6839800Sbostic for (root = NULL, nitems = 0; *argv; ++argv, ++nitems) { 6939800Sbostic if (!(p = fts_root(*argv))) 7039800Sbostic goto mem2; 7142273Sbostic if (maxlen < p->fts_namelen) 7242273Sbostic maxlen = p->fts_namelen; 7339800Sbostic /* 7439800Sbostic * if comparison routine supplied, traverse in sorted 7539800Sbostic * order; otherwise traverse in the order specified. 7639800Sbostic */ 7739800Sbostic if (compar) { 7842273Sbostic p->fts_link = root; 7939800Sbostic root = p; 8042273Sbostic p->fts_accpath = p->fts_name; 8142273Sbostic p->fts_info = fts_stat(p, 0); 8239800Sbostic } else { 8342273Sbostic p->fts_link = NULL; 8439800Sbostic if (!root) 8539800Sbostic tmp = root = p; 8639800Sbostic else { 8742273Sbostic tmp->fts_link = p; 8839800Sbostic tmp = p; 8939800Sbostic } 9039800Sbostic } 9142273Sbostic p->fts_level = ROOTLEVEL; 9242273Sbostic p->fts_parent = parent; 9339800Sbostic } 9439800Sbostic if (compar && nitems > 1) 9539800Sbostic root = fts_sort(root, nitems); 9639800Sbostic } else { 9739800Sbostic if (!(root = fts_root((char *)argv))) 9839800Sbostic goto mem2; 9942273Sbostic maxlen = root->fts_namelen; 10042273Sbostic root->fts_link = NULL; 10142273Sbostic root->fts_level = ROOTLEVEL; 10242273Sbostic root->fts_parent = parent; 10339800Sbostic } 10439800Sbostic 105*42281Sbostic /* 106*42281Sbostic * allocate a dummy pointer and make ftsread think that we've just 107*42281Sbostic * finished the node before the root(s); set p->fts_info to FTS_NS 108*42281Sbostic * so that everything about the "current" node is ignored. 109*42281Sbostic */ 110*42281Sbostic if (!(sp->fts_cur = fts_alloc("", 0))) 111*42281Sbostic goto mem2; 112*42281Sbostic sp->fts_cur->fts_link = root; 113*42281Sbostic sp->fts_cur->fts_info = FTS_NS; 114*42281Sbostic 11539800Sbostic /* start out with at least 1K+ of path space */ 11639800Sbostic if (!fts_path(MAX(maxlen, MAXPATHLEN))) 117*42281Sbostic goto mem3; 11839800Sbostic 11939800Sbostic /* 12039800Sbostic * if using chdir(2), do a getwd() to insure that we can get back 12139800Sbostic * here; this could be avoided for some paths, but probably not 12239800Sbostic * worth the effort. Slashes, symbolic links, and ".." are all 12339800Sbostic * fairly nasty problems. Note, if we can't get the current 12439800Sbostic * working directory we run anyway, just more slowly. 12539800Sbostic */ 12642273Sbostic if (!(options & FTS_NOCHDIR) && 12742273Sbostic (!getwd(wd) || !(sp->fts_wd = strdup(wd)))) 12842273Sbostic sp->fts_options |= FTS_NOCHDIR; 12939800Sbostic 13039800Sbostic return(sp); 13139800Sbostic 132*42281Sbostic mem3: (void)free((char *)sp->fts_cur); 13339800Sbostic mem2: fts_lfree(root); 13439800Sbostic (void)free((char *)parent); 13539800Sbostic mem1: (void)free((char *)sp); 13639800Sbostic return(NULL); 13739800Sbostic } 13839800Sbostic 13939800Sbostic static 14039800Sbostic fts_load(p) 14139800Sbostic register FTSENT *p; 14239800Sbostic { 14339800Sbostic register int len; 14439800Sbostic register char *cp; 14539800Sbostic 14639800Sbostic /* 14739800Sbostic * load the stream structure for the next traversal; set the 14839800Sbostic * accpath field specially so the chdir gets done to the right 14939800Sbostic * place and the user can access the first node. 15039800Sbostic */ 15142273Sbostic len = p->fts_pathlen = p->fts_namelen; 15242273Sbostic bcopy(p->fts_name, stream->fts_path, len + 1); 15342273Sbostic if ((cp = rindex(p->fts_name, '/')) && (cp != p->fts_name || cp[1])) { 15439800Sbostic len = strlen(++cp); 15542273Sbostic bcopy(cp, p->fts_name, len + 1); 15642273Sbostic p->fts_namelen = len; 15739800Sbostic } 15842273Sbostic p->fts_accpath = p->fts_path = stream->fts_path; 15939800Sbostic } 16039800Sbostic 16139800Sbostic ftsclose(sp) 16239800Sbostic FTS *sp; 16339800Sbostic { 16439800Sbostic register FTSENT *freep, *p; 16539800Sbostic int saved_errno; 16639800Sbostic 167*42281Sbostic if (sp->fts_cur) { 168*42281Sbostic /* 169*42281Sbostic * this still works if we haven't read anything -- the dummy 170*42281Sbostic * structure points to the root list, so we step through to 171*42281Sbostic * the end of the root list which has a valid parent pointer. 172*42281Sbostic */ 173*42281Sbostic for (p = sp->fts_cur; p->fts_level > ROOTPARENTLEVEL;) { 174*42281Sbostic freep = p; 175*42281Sbostic p = p->fts_link ? p->fts_link : p->fts_parent; 176*42281Sbostic (void)free((char *)freep); 17739800Sbostic } 178*42281Sbostic (void)free((char *)p); 179*42281Sbostic } 18039800Sbostic 18139800Sbostic /* free up child linked list, sort array, path buffer */ 18242273Sbostic if (sp->fts_child) 18342273Sbostic fts_lfree(sp->fts_child); 18442273Sbostic if (sp->fts_array) 18542273Sbostic (void)free((char *)sp->fts_array); 18642273Sbostic (void)free((char *)sp->fts_path); 18739800Sbostic 18839800Sbostic /* 18939800Sbostic * return to original directory, save errno if necessary; 19039800Sbostic * free up the directory buffer 19139800Sbostic */ 19242273Sbostic if (!(sp->fts_options & FTS_NOCHDIR)) { 19342273Sbostic saved_errno = chdir(sp->fts_wd) ? errno : 0; 19442273Sbostic (void)free(sp->fts_wd); 19539800Sbostic } 19639800Sbostic 19739800Sbostic /* free up the stream pointer */ 19839800Sbostic (void)free((char *)sp); 19939800Sbostic 20039800Sbostic /* set errno and return */ 20142273Sbostic if (!(sp->fts_options & FTS_NOCHDIR) && saved_errno) { 20239800Sbostic errno = saved_errno; 20339800Sbostic return(-1); 20439800Sbostic } 20539800Sbostic return(0); 20639800Sbostic } 20739800Sbostic 20839800Sbostic FTSENT * 20939800Sbostic ftsread(sp) 21039800Sbostic register FTS *sp; 21139800Sbostic { 21239800Sbostic register FTSENT *p; 21339800Sbostic register int instr; 21439800Sbostic static int cd; 21539800Sbostic FTSENT *tmp; 21639800Sbostic char *cp; 21739800Sbostic 21839800Sbostic /* if finished or unrecoverable error, return NULL */ 21942273Sbostic if (!sp->fts_cur || sp->fts_options & FTS__STOP) 22039800Sbostic return(NULL); 22139800Sbostic 22239800Sbostic /* set global stream pointer, and current node pointer */ 22339800Sbostic stream = sp; 22442273Sbostic p = sp->fts_cur; 22539800Sbostic 22639800Sbostic /* save and zero out user instructions */ 22742273Sbostic instr = p->fts_instr; 22842273Sbostic p->fts_instr = 0; 22939800Sbostic 23039800Sbostic /* if used 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 23639800Sbostic /* any type of file may be re-visited; re-stat and return */ 23739800Sbostic if (instr == FTS_AGAIN) { 23842273Sbostic p->fts_info = fts_stat(p, 0); 23939800Sbostic return(p); 24039800Sbostic } 24139800Sbostic 24242273Sbostic if (p->fts_info == FTS_D) 24342280Sbostic if (instr == FTS_SKIP || sp->fts_options & FTS_XDEV && 24442280Sbostic p->fts_statb.st_dev != sp->sdev) { 24542273Sbostic if (sp->fts_child) { 24642273Sbostic fts_lfree(sp->fts_child); 24742273Sbostic sp->fts_child = NULL; 24839800Sbostic } 24939800Sbostic } else { 25042273Sbostic if (!sp->fts_child && 25142273Sbostic (!(sp->fts_child = fts_build(sp, 1)))) 25239800Sbostic return(p); 25342273Sbostic p = sp->fts_child; 25442273Sbostic sp->fts_child = NULL; 25542273Sbostic return(sp->fts_cur = p); 25639800Sbostic } 25742273Sbostic else if (p->fts_info == FTS_SL && instr == FTS_FOLLOW) { 25842273Sbostic p->fts_info = fts_stat(p, 1); 25939800Sbostic return(p); 26039800Sbostic } 26139800Sbostic 26239800Sbostic /* 263*42281Sbostic * user may have called ftsset on pointer returned by ftschildren; 264*42281Sbostic * handle it here. 26539800Sbostic */ 26642273Sbostic for (p = p->fts_link; p;) { 26742273Sbostic instr = p->fts_instr; 26839800Sbostic if (instr == FTS_FOLLOW) { 26942273Sbostic p->fts_info = fts_stat(p, 1); 27042273Sbostic p->fts_instr = 0; 27139800Sbostic break; 27239800Sbostic } 27339800Sbostic if (instr == FTS_SKIP) { 27439800Sbostic tmp = p; 27542273Sbostic p = p->fts_link; 27639800Sbostic (void)free((char *)tmp); 27739800Sbostic continue; 27839800Sbostic } 27942273Sbostic p->fts_instr = 0; 28039800Sbostic break; 28139800Sbostic } 28239800Sbostic 28339800Sbostic /* go to next node on this level */ 28439800Sbostic if (p) { 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 28839800Sbostic * to wherever we started from. 28939800Sbostic */ 29042273Sbostic if (p->fts_level == ROOTLEVEL) { 29139800Sbostic fts_load(p); 292*42281Sbostic (void)free((char *)sp->fts_cur); 293*42281Sbostic if (cd && 294*42281Sbostic p->fts_path[0] != '/' && CHDIR(sp, sp->fts_wd)) { 295*42281Sbostic /* return target path for error msg */ 296*42281Sbostic p->fts_path = sp->fts_wd; 297*42281Sbostic p->fts_info = FTS_ERR; 298*42281Sbostic sp->fts_options |= FTS__STOP; 299*42281Sbostic return(sp->fts_cur = p); 300*42281Sbostic } 301*42281Sbostic cd = 1; 30242273Sbostic p->fts_info = fts_stat(p, 0); 30342280Sbostic sp->sdev = p->fts_statb.st_dev; 30439800Sbostic } else { 30542273Sbostic (void)free((char *)sp->fts_cur); 30642273Sbostic cp = sp->fts_path + NAPPEND(p->fts_parent); 30739800Sbostic *cp++ = '/'; 30842273Sbostic bcopy(p->fts_name, cp, p->fts_namelen + 1); 30942273Sbostic if (p->fts_info == FTS_D && (tmp = fts_cycle(p))) { 31042273Sbostic sp->fts_savelink = p->fts_link; 31142273Sbostic p->fts_link = tmp; 31239800Sbostic } 31339800Sbostic } 31442273Sbostic return(sp->fts_cur = p); 31539800Sbostic } 31639800Sbostic 31739800Sbostic /* go to parent */ 31842273Sbostic p = sp->fts_cur->fts_parent; 31942273Sbostic (void)free((char *)sp->fts_cur); 32042273Sbostic if (p->fts_level == ROOTPARENTLEVEL) { 32139800Sbostic /* 32239800Sbostic * done; free everything up and set errno to 0 so the user 32339800Sbostic * can distinguish between error and EOF. 32439800Sbostic */ 32539800Sbostic (void)free((char *)p); 32639800Sbostic errno = 0; 32742273Sbostic return(sp->fts_cur = NULL); 32839800Sbostic } 32939800Sbostic 33042273Sbostic sp->fts_path[p->fts_pathlen] = '\0'; 33139800Sbostic if (CHDIR(sp, "..")) { 33242273Sbostic sp->fts_options |= FTS__STOP; 33342273Sbostic p->fts_info = FTS_ERR; 33439800Sbostic } else 33542273Sbostic p->fts_info = FTS_DP; 33642273Sbostic return(sp->fts_cur = p); 33739800Sbostic } 33839800Sbostic 33939800Sbostic /* 34039800Sbostic * ftsset takes the stream as an argument although it's not used in this 34139800Sbostic * implementation; it would be necessary if anyone wanted to add global 34239800Sbostic * semantics to fts using ftsset. A possible error return is allowed for 34339800Sbostic * similar reasons. 34439800Sbostic */ 34539800Sbostic /* ARGSUSED */ 34639800Sbostic ftsset(sp, p, instr) 34739800Sbostic FTS *sp; 34839800Sbostic FTSENT *p; 34939800Sbostic int instr; 35039800Sbostic { 35142273Sbostic p->fts_instr = instr; 35239800Sbostic return(0); 35339800Sbostic } 35439800Sbostic 35539800Sbostic FTSENT * 35639800Sbostic ftschildren(sp) 35739800Sbostic register FTS *sp; 35839800Sbostic { 35939800Sbostic /* 36039800Sbostic * set errno to 0 so that user can tell the difference between an 36139800Sbostic * error and a directory without entries. 36239800Sbostic */ 36339800Sbostic errno = 0; 36442273Sbostic if (sp->fts_cur->fts_info != FTS_D || sp->fts_options & FTS__STOP) 36539800Sbostic return(NULL); 36642273Sbostic if (sp->fts_child) 36742273Sbostic fts_lfree(sp->fts_child); 36842273Sbostic return(sp->fts_child = fts_build(sp, 0)); 36939800Sbostic } 37039800Sbostic 37139800Sbostic #define ISDOT(a) (a[0] == '.' && (!a[1] || a[1] == '.' && !a[2])) 37239800Sbostic 37339800Sbostic static FTSENT * 37439962Sbostic fts_build(sp, set_node) 37539800Sbostic register FTS *sp; 37639962Sbostic int set_node; 37739800Sbostic { 37839800Sbostic register struct dirent *dp; 37939800Sbostic register FTSENT *p, *head; 38039800Sbostic register int nitems; 38139800Sbostic DIR *dirp; 38239962Sbostic int descend, len, level, maxlen, nlinks, saved_errno; 38339800Sbostic char *cp; 38439800Sbostic 38542273Sbostic p = sp->fts_cur; 38642273Sbostic if (!(dirp = opendir(p->fts_accpath))) { 38739962Sbostic if (set_node) { 38839800Sbostic errno = 0; 38942273Sbostic p->fts_info = FTS_DNR; 39039800Sbostic } 39139800Sbostic return(NULL); 39239800Sbostic } 39339800Sbostic 39439800Sbostic /* 39539800Sbostic * the real slowdown in walking the tree is the stat calls. If 39639800Sbostic * FTS_NOSTAT is set and it's a physical walk (so that symbolic 39739800Sbostic * links can't be directories), fts assumes that the number of 39839800Sbostic * subdirectories in a node is equal to the number of links to 39939800Sbostic * the parent. This allows stat calls to be skipped in any leaf 40039800Sbostic * directories and for any nodes after the directories in the 40139800Sbostic * parent node have been found. This empirically cuts the stat 40239800Sbostic * calls by about 2/3. 40339800Sbostic */ 40442273Sbostic nlinks = 40542273Sbostic sp->fts_options & FTS_NOSTAT && sp->fts_options & FTS_PHYSICAL ? 40642273Sbostic p->fts_statb.st_nlink - (sp->fts_options & FTS_SEEDOT ? 0 : 2) : -1; 40739800Sbostic 40839962Sbostic if (nlinks || set_node) { 40939962Sbostic if (FCHDIR(sp, dirfd(dirp))) { 41039962Sbostic if (set_node) { 41139962Sbostic errno = 0; 41242273Sbostic p->fts_info = FTS_DNX; 41339962Sbostic } 41439962Sbostic return(NULL); 41539962Sbostic } 41639962Sbostic descend = 1; 41739962Sbostic } else 41839962Sbostic descend = 0; 41939962Sbostic 42040939Sbostic /* get max file name length that can be stored in current path */ 42142273Sbostic maxlen = sp->fts_pathlen - p->fts_pathlen - 1; 42240939Sbostic 42342273Sbostic cp = sp->fts_path + (len = NAPPEND(p)); 42440939Sbostic *cp++ = '/'; 42540939Sbostic 42642273Sbostic level = p->fts_level + 1; 42740939Sbostic 42839800Sbostic /* read the directory, attching each new entry to the `link' pointer */ 42939800Sbostic for (head = NULL, nitems = 0; dp = readdir(dirp);) { 43042273Sbostic if (ISDOT(dp->d_name) && !(sp->fts_options & FTS_SEEDOT)) 43139800Sbostic continue; 43239800Sbostic 43339800Sbostic if (!(p = fts_alloc(dp->d_name, dp->d_namlen))) { 43439800Sbostic saved_errno = errno; 43539800Sbostic goto mem1; 43639800Sbostic } 43739800Sbostic if (dp->d_namlen > maxlen) { 43839800Sbostic if (!fts_path((int)dp->d_namlen)) { 43939800Sbostic /* quit: this stream no longer has a path */ 44042273Sbostic sp->fts_options |= FTS__STOP; 44139800Sbostic saved_errno = errno; 44239800Sbostic (void)free((char *)p); 44339800Sbostic mem1: fts_lfree(head); 44439962Sbostic if (set_node) 44542273Sbostic p->fts_info = FTS_ERR; 44639962Sbostic if (descend && CHDIR(sp, "..")) { 44739800Sbostic saved_errno = errno; 44842273Sbostic sp->fts_options |= FTS__STOP; 44939800Sbostic } 45039800Sbostic errno = saved_errno; 45139800Sbostic return(NULL); 45239800Sbostic } 45342273Sbostic maxlen = sp->fts_pathlen - sp->fts_cur->fts_pathlen - 1; 45439800Sbostic } 45539800Sbostic 45642273Sbostic p->fts_pathlen = len + dp->d_namlen + 1; 45742273Sbostic p->fts_accpath = 45842273Sbostic sp->fts_options & FTS_NOCHDIR ? p->fts_path : p->fts_name; 45939800Sbostic 46042273Sbostic p->fts_parent = sp->fts_cur; 46142273Sbostic p->fts_level = level; 46239800Sbostic 46339800Sbostic if (nlinks) { 46439800Sbostic /* make sure fts_stat has a filename to stat */ 46542273Sbostic if (sp->fts_options & FTS_NOCHDIR) 46642273Sbostic bcopy(p->fts_name, cp, p->fts_namelen + 1); 46742273Sbostic p->fts_info = fts_stat(p, 0); 46842273Sbostic if (nlinks > 0 && (p->fts_info == FTS_D || 46942273Sbostic p->fts_info == FTS_DNR || p->fts_info == FTS_DNX)) 47039800Sbostic --nlinks; 47139800Sbostic } else 47242273Sbostic p->fts_info = FTS_NS; 47339800Sbostic 47442273Sbostic p->fts_link = head; 47539800Sbostic head = p; 47639800Sbostic ++nitems; 47739800Sbostic } 47839800Sbostic (void)closedir(dirp); 47939800Sbostic 48039962Sbostic if (descend && (!nitems || !set_node) && CHDIR(sp, "..")) { 48142273Sbostic sp->fts_options |= FTS__STOP; 48242273Sbostic p->fts_info = FTS_ERR; 48339962Sbostic *--cp = '\0'; 48439962Sbostic return(NULL); 48539962Sbostic } 48639962Sbostic 48739800Sbostic if (!nitems) { 48839962Sbostic if (set_node) 48942273Sbostic p->fts_info = FTS_DP; 49039800Sbostic *--cp = '\0'; 49139800Sbostic return(NULL); 49239800Sbostic } 49339800Sbostic 49442273Sbostic if (sp->fts_compar && nitems > 1) 49539800Sbostic head = fts_sort(head, nitems); 49639800Sbostic 49739962Sbostic if (set_node) 49842273Sbostic bcopy(head->fts_name, cp, head->fts_namelen + 1); 49939800Sbostic else 50039800Sbostic *--cp = '\0'; 50139800Sbostic return(head); 50239800Sbostic } 50339800Sbostic 50439800Sbostic static short 50539800Sbostic fts_stat(p, symflag) 50639800Sbostic register FTSENT *p; 50739800Sbostic int symflag; 50839800Sbostic { 50939800Sbostic /* 51039800Sbostic * detection of symbolic links w/o targets. If FTS_FOLLOW is set, 51139800Sbostic * the symlink structure is overwritten with the stat structure of 51239800Sbostic * the target. 51339800Sbostic */ 51442273Sbostic if (stream->fts_options & FTS_LOGICAL || symflag) { 51542273Sbostic if (stat(p->fts_accpath, &p->fts_statb)) 51642273Sbostic return(symflag || !lstat(p->fts_accpath, 51742273Sbostic &p->fts_statb) ? FTS_SLNONE : FTS_ERR); 51842273Sbostic } else if (lstat(p->fts_accpath, &p->fts_statb)) 51939800Sbostic return(FTS_ERR); 52039800Sbostic 52142273Sbostic switch(p->fts_statb.st_mode&S_IFMT) { 52239800Sbostic case S_IFDIR: 52339800Sbostic return(FTS_D); 52439800Sbostic case S_IFLNK: 52539800Sbostic return(FTS_SL); 52639800Sbostic case S_IFREG: 52739800Sbostic return(FTS_F); 52839800Sbostic } 52940939Sbostic return(FTS_DEFAULT); 53039800Sbostic } 53139800Sbostic 53239800Sbostic static FTSENT * 53339800Sbostic fts_cycle(p) 53439800Sbostic register FTSENT *p; 53539800Sbostic { 53639800Sbostic register dev_t dev; 53739800Sbostic register ino_t ino; 53839800Sbostic 53939800Sbostic /* 54039800Sbostic * cycle detection is brute force; if the tree gets deep enough or 54139800Sbostic * the number of symbolic links to directories is really high 54239800Sbostic * something faster might be worthwhile. 54339800Sbostic */ 54442273Sbostic dev = p->fts_statb.st_dev; 54542273Sbostic ino = p->fts_statb.st_ino; 54642273Sbostic for (p = p->fts_parent; p->fts_level > ROOTLEVEL; p = p->fts_parent) 54742273Sbostic if (ino == p->fts_statb.st_ino && dev == p->fts_statb.st_dev) 54839800Sbostic return(p); 54939800Sbostic return(NULL); 55039800Sbostic } 55139800Sbostic 55239800Sbostic #define R(type, nelem, ptr) \ 55339800Sbostic (type *)realloc((char *)ptr, (u_int)((nelem) * sizeof(type))) 55439800Sbostic 55539800Sbostic static FTSENT * 55639800Sbostic fts_sort(head, nitems) 55739800Sbostic FTSENT *head; 55839800Sbostic register int nitems; 55939800Sbostic { 56039800Sbostic register FTSENT **ap, *p; 56139800Sbostic 56239800Sbostic /* 56339800Sbostic * construct an array of pointers to the structures and call qsort(3). 56439800Sbostic * Reassemble the array in the order returned by qsort. If unable to 56539800Sbostic * sort for memory reasons, return the directory entries in their 56639800Sbostic * current order. Allocate enough space for the current needs plus 56739800Sbostic * 40 so we don't realloc one entry at a time. 56839800Sbostic */ 56942273Sbostic if (nitems > stream->fts_nitems) { 57042273Sbostic stream->fts_nitems = nitems + 40; 57142273Sbostic if (!(stream->fts_array = 57242273Sbostic R(FTSENT *, stream->fts_nitems, stream->fts_array))) { 57342273Sbostic stream->fts_nitems = 0; 57439800Sbostic return(head); 57539800Sbostic } 57639800Sbostic } 57742273Sbostic for (ap = stream->fts_array, p = head; p; p = p->fts_link) 57839800Sbostic *ap++ = p; 57942273Sbostic qsort((char *)stream->fts_array, nitems, sizeof(FTSENT *), 58042273Sbostic stream->fts_compar); 58142273Sbostic for (head = *(ap = stream->fts_array); --nitems; ++ap) 58242273Sbostic ap[0]->fts_link = ap[1]; 58342273Sbostic ap[0]->fts_link = NULL; 58439800Sbostic return(head); 58539800Sbostic } 58639800Sbostic 58739800Sbostic static FTSENT * 58839800Sbostic fts_alloc(name, len) 58939800Sbostic char *name; 59039800Sbostic register int len; 59139800Sbostic { 59239800Sbostic register FTSENT *p; 59339800Sbostic 59439800Sbostic /* 59539800Sbostic * variable sized structures; the name is the last element so 59639800Sbostic * allocate enough extra space after the structure to hold it. 59739800Sbostic */ 59839800Sbostic if (!(p = (FTSENT *)malloc((u_int)(sizeof(FTSENT) + len)))) 59939800Sbostic return(NULL); 60042273Sbostic bcopy(name, p->fts_name, len + 1); 60142273Sbostic p->fts_namelen = len; 60242273Sbostic p->fts_path = stream->fts_path; 60342273Sbostic p->fts_instr = 0; 60442273Sbostic p->fts_local.number = 0; 60542273Sbostic p->fts_local.pointer = NULL; 60639800Sbostic return(p); 60739800Sbostic } 60839800Sbostic 60939800Sbostic static 61039800Sbostic fts_lfree(head) 61139800Sbostic register FTSENT *head; 61239800Sbostic { 61339800Sbostic register FTSENT *p; 61439800Sbostic 61539800Sbostic while (p = head) { 61642273Sbostic head = head->fts_link; 61739800Sbostic (void)free((char *)p); 61839800Sbostic } 61939800Sbostic } 62039800Sbostic 62139800Sbostic /* 62239800Sbostic * allow essentially unlimited paths; certain programs (find, remove, ls) 62339800Sbostic * need to work on any tree. Most systems will allow creation of paths 62439800Sbostic * much longer than MAXPATHLEN, even though the kernel won't resolve them. 62539800Sbostic * Add an extra 128 bytes to the requested size so that we don't realloc 62639800Sbostic * the path 2 bytes at a time. 62739800Sbostic */ 62839800Sbostic static 62939800Sbostic fts_path(size) 63039800Sbostic int size; 63139800Sbostic { 63242273Sbostic stream->fts_pathlen += size + 128; 63342273Sbostic return((int)(stream->fts_path = 63442273Sbostic R(char, stream->fts_pathlen, stream->fts_path))); 63539800Sbostic } 63639800Sbostic 63739800Sbostic static FTSENT * 63839800Sbostic fts_root(name) 63939800Sbostic register char *name; 64039800Sbostic { 64139800Sbostic register char *cp; 64239800Sbostic 64339800Sbostic /* 64439800Sbostic * rip trailing slashes; it's somewhat unclear in POSIX 1003.1 what 64539800Sbostic * /a/b/ really is, they don't talk about what a null path component 64639800Sbostic * resolves to. This hopefully does what the user intended. Don't 64739800Sbostic * allow null pathnames. 64839800Sbostic */ 64939800Sbostic for (cp = name; *cp; ++cp); 65039800Sbostic if (cp == name) { 65139800Sbostic errno = ENOENT; 65239800Sbostic return(NULL); 65339800Sbostic } 65439800Sbostic while (--cp > name && *cp == '/'); 65539800Sbostic *++cp = '\0'; 65639800Sbostic return(fts_alloc(name, cp - name)); 65739800Sbostic } 658