139800Sbostic /* 239800Sbostic * Copyright (c) 1989 The Regents of the University of California. 339800Sbostic * All rights reserved. 439800Sbostic * 539800Sbostic * Redistribution and use in source and binary forms are permitted 639800Sbostic * provided that the above copyright notice and this paragraph are 739800Sbostic * duplicated in all such forms and that any documentation, 839800Sbostic * advertising materials, and other materials related to such 939800Sbostic * distribution and use acknowledge that the software was developed 1039800Sbostic * by the University of California, Berkeley. The name of the 1139800Sbostic * University may not be used to endorse or promote products derived 1239800Sbostic * from this software without specific prior written permission. 1339800Sbostic * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR 1439800Sbostic * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED 1539800Sbostic * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. 1639800Sbostic */ 1739800Sbostic 1839800Sbostic #if defined(LIBC_SCCS) && !defined(lint) 19*39962Sbostic static char sccsid[] = "@(#)fts.c 5.2 (Berkeley) 01/31/90"; 2039800Sbostic #endif /* LIBC_SCCS and not lint */ 2139800Sbostic 2239800Sbostic #include <sys/param.h> 2339800Sbostic #include <sys/stat.h> 2439800Sbostic #include <dirent.h> 2539800Sbostic #include <errno.h> 2639800Sbostic #include <fts.h> 2739800Sbostic #include <strings.h> 2839800Sbostic 2939800Sbostic extern int errno; 3039800Sbostic 3139800Sbostic FTSENT *fts_alloc(), *fts_build(), *fts_cycle(), *fts_sort(), *fts_root(); 3239800Sbostic short fts_stat(); 3339800Sbostic char *malloc(), *realloc(); 3439800Sbostic 3539800Sbostic /* 3639800Sbostic * Special case a root of "/" so that slashes aren't appended causing 3739800Sbostic * paths to be written as "//foo". 3839800Sbostic */ 3939800Sbostic #define NAPPEND(p) \ 4039800Sbostic (p->level == ROOTLEVEL && p->pathlen == 1 && \ 4139800Sbostic p->path[0] == '/' ? 0 : p->pathlen) 4239800Sbostic 4339800Sbostic #define CHDIR(sp, path) (!(sp->options & FTS_NOCHDIR) && chdir(path)) 44*39962Sbostic #define FCHDIR(sp, fd) (!(sp->options & FTS_NOCHDIR) && fchdir(fd)) 4539800Sbostic 4639800Sbostic #define ROOTLEVEL 0 4739800Sbostic #define ROOTPARENTLEVEL -1 4839800Sbostic 4939800Sbostic static FTS *stream; /* current stream pointer */ 5039800Sbostic 5139800Sbostic FTS * 5239800Sbostic ftsopen(argv, options, compar) 5339800Sbostic char *argv[]; 5439800Sbostic register int options; 5539800Sbostic int (*compar)(); 5639800Sbostic { 5739800Sbostic register FTS *sp; 5839800Sbostic register FTSENT *p, *root; 5939800Sbostic register int nitems, maxlen; 6039800Sbostic FTSENT *parent, *tmp; 6139800Sbostic char wd[MAXPATHLEN], *getwd(), *strdup(); 6239800Sbostic 6339800Sbostic /* allocate/initialize the stream */ 6439800Sbostic if (!(stream = sp = (FTS *)malloc((u_int)sizeof(FTS)))) 6539800Sbostic return(NULL); 6639800Sbostic bzero(sp, sizeof(FTS)); 6739800Sbostic sp->compar = compar; 6839800Sbostic sp->options = options; 6939800Sbostic 7039800Sbostic /* allocate/initialize root's parent */ 7139800Sbostic if (!(parent = fts_alloc("", 0))) 7239800Sbostic goto mem1; 7339800Sbostic parent->level = ROOTPARENTLEVEL; 7439800Sbostic 7539800Sbostic /* allocate/initialize root(s) */ 7639800Sbostic if (options & FTS_MULTIPLE) { 7739800Sbostic maxlen = -1; 7839800Sbostic for (root = NULL, nitems = 0; *argv; ++argv, ++nitems) { 7939800Sbostic if (!(p = fts_root(*argv))) 8039800Sbostic goto mem2; 8139800Sbostic if (maxlen < p->namelen) 8239800Sbostic maxlen = p->namelen; 8339800Sbostic /* 8439800Sbostic * if comparison routine supplied, traverse in sorted 8539800Sbostic * order; otherwise traverse in the order specified. 8639800Sbostic */ 8739800Sbostic if (compar) { 8839800Sbostic p->link = root; 8939800Sbostic root = p; 9039800Sbostic p->accpath = p->name; 9139800Sbostic p->info = fts_stat(p, 0); 9239800Sbostic } else { 9339800Sbostic p->link = NULL; 9439800Sbostic if (!root) 9539800Sbostic tmp = root = p; 9639800Sbostic else { 9739800Sbostic tmp->link = p; 9839800Sbostic tmp = p; 9939800Sbostic } 10039800Sbostic } 10139800Sbostic p->level = ROOTLEVEL; 10239800Sbostic p->parent = parent; 10339800Sbostic } 10439800Sbostic if (compar && nitems > 1) 10539800Sbostic root = fts_sort(root, nitems); 10639800Sbostic } else { 10739800Sbostic if (!(root = fts_root((char *)argv))) 10839800Sbostic goto mem2; 10939800Sbostic maxlen = root->namelen; 11039800Sbostic root->link = NULL; 11139800Sbostic root->level = ROOTLEVEL; 11239800Sbostic root->parent = parent; 11339800Sbostic } 11439800Sbostic 11539800Sbostic /* start out with at least 1K+ of path space */ 11639800Sbostic if (!fts_path(MAX(maxlen, MAXPATHLEN))) 11739800Sbostic goto mem2; 11839800Sbostic 11939800Sbostic /* 12039800Sbostic * some minor trickiness. Set the pointers so that ftsread thinks 12139800Sbostic * we've just finished the node before the root(s); set p->info to 12239800Sbostic * FTS_NS so that everything about the "current" node is ignored. 12339800Sbostic * Rather than allocate a dummy node use the root's parent's link 12439800Sbostic * pointer. This is handled specially in ftsclose() as well. 12539800Sbostic */ 12639800Sbostic sp->cur = parent; 12739800Sbostic parent->link = root; 12839800Sbostic parent->info = FTS_NS; 12939800Sbostic 13039800Sbostic /* 13139800Sbostic * if using chdir(2), do a getwd() to insure that we can get back 13239800Sbostic * here; this could be avoided for some paths, but probably not 13339800Sbostic * worth the effort. Slashes, symbolic links, and ".." are all 13439800Sbostic * fairly nasty problems. Note, if we can't get the current 13539800Sbostic * working directory we run anyway, just more slowly. 13639800Sbostic */ 13739800Sbostic if (!(options & FTS_NOCHDIR) && (!getwd(wd) || !(sp->wd = strdup(wd)))) 13839800Sbostic sp->options |= FTS_NOCHDIR; 13939800Sbostic 14039800Sbostic return(sp); 14139800Sbostic 14239800Sbostic mem2: fts_lfree(root); 14339800Sbostic (void)free((char *)parent); 14439800Sbostic mem1: (void)free((char *)sp); 14539800Sbostic return(NULL); 14639800Sbostic } 14739800Sbostic 14839800Sbostic static 14939800Sbostic fts_load(p) 15039800Sbostic register FTSENT *p; 15139800Sbostic { 15239800Sbostic register int len; 15339800Sbostic register char *cp; 15439800Sbostic 15539800Sbostic /* 15639800Sbostic * load the stream structure for the next traversal; set the 15739800Sbostic * accpath field specially so the chdir gets done to the right 15839800Sbostic * place and the user can access the first node. 15939800Sbostic */ 16039800Sbostic len = p->pathlen = p->namelen; 16139800Sbostic bcopy(p->name, stream->path, len + 1); 16239800Sbostic if ((cp = rindex(p->name, '/')) && (cp != p->name || cp[1])) { 16339800Sbostic len = strlen(++cp); 16439800Sbostic bcopy(cp, p->name, len + 1); 16539800Sbostic p->namelen = len; 16639800Sbostic } 16739800Sbostic p->accpath = p->path = stream->path; 16839800Sbostic } 16939800Sbostic 17039800Sbostic ftsclose(sp) 17139800Sbostic FTS *sp; 17239800Sbostic { 17339800Sbostic register FTSENT *freep, *p; 17439800Sbostic int saved_errno; 17539800Sbostic 17639800Sbostic if (sp->cur) 17739800Sbostic /* check for never having read anything */ 17839800Sbostic if (sp->cur->level == ROOTPARENTLEVEL) 17939800Sbostic fts_lfree(sp->cur); 18039800Sbostic else { 18139800Sbostic for (p = sp->cur; p->level > ROOTPARENTLEVEL;) { 18239800Sbostic freep = p; 18339800Sbostic p = p->link ? p->link : p->parent; 18439800Sbostic (void)free((char *)freep); 18539800Sbostic } 18639800Sbostic (void)free((char *)p); 18739800Sbostic } 18839800Sbostic 18939800Sbostic /* free up child linked list, sort array, path buffer */ 19039800Sbostic if (sp->child) 19139800Sbostic fts_lfree(sp->child); 19239800Sbostic if (sp->array) 19339800Sbostic (void)free((char *)sp->array); 19439800Sbostic (void)free((char *)sp->path); 19539800Sbostic 19639800Sbostic /* 19739800Sbostic * return to original directory, save errno if necessary; 19839800Sbostic * free up the directory buffer 19939800Sbostic */ 20039800Sbostic if (!(sp->options & FTS_NOCHDIR)) { 20139800Sbostic saved_errno = chdir(sp->wd) ? errno : 0; 20239800Sbostic (void)free(sp->wd); 20339800Sbostic } 20439800Sbostic 20539800Sbostic /* free up the stream pointer */ 20639800Sbostic (void)free((char *)sp); 20739800Sbostic 20839800Sbostic /* set errno and return */ 20939800Sbostic if (!(sp->options & FTS_NOCHDIR) && saved_errno) { 21039800Sbostic errno = saved_errno; 21139800Sbostic return(-1); 21239800Sbostic } 21339800Sbostic return(0); 21439800Sbostic } 21539800Sbostic 21639800Sbostic FTSENT * 21739800Sbostic ftsread(sp) 21839800Sbostic register FTS *sp; 21939800Sbostic { 22039800Sbostic register FTSENT *p; 22139800Sbostic register int instr; 22239800Sbostic static int cd; 22339800Sbostic FTSENT *tmp; 22439800Sbostic char *cp; 22539800Sbostic 22639800Sbostic /* if finished or unrecoverable error, return NULL */ 22739800Sbostic if (!sp->cur || sp->options & FTS__STOP) 22839800Sbostic return(NULL); 22939800Sbostic 23039800Sbostic /* set global stream pointer, and current node pointer */ 23139800Sbostic stream = sp; 23239800Sbostic p = sp->cur; 23339800Sbostic 23439800Sbostic /* save and zero out user instructions */ 23539800Sbostic instr = p->instr; 23639800Sbostic p->instr = 0; 23739800Sbostic 23839800Sbostic /* if used link pointer for cycle detection, restore it */ 23939800Sbostic if (sp->savelink) { 24039800Sbostic p->link = sp->savelink; 24139800Sbostic sp->savelink = NULL; 24239800Sbostic } 24339800Sbostic 24439800Sbostic /* any type of file may be re-visited; re-stat and return */ 24539800Sbostic if (instr == FTS_AGAIN) { 24639800Sbostic p->info = fts_stat(p, 0); 24739800Sbostic return(p); 24839800Sbostic } 24939800Sbostic 25039800Sbostic if (p->info == FTS_D) 25139800Sbostic if (instr == FTS_SKIP) { 25239800Sbostic if (sp->child) { 25339800Sbostic fts_lfree(sp->child); 25439800Sbostic sp->child = NULL; 25539800Sbostic } 25639800Sbostic } else { 257*39962Sbostic if (!sp->child && (!(sp->child = fts_build(sp, 1)))) 25839800Sbostic return(p); 25939800Sbostic p = sp->child; 26039800Sbostic sp->child = NULL; 26139800Sbostic return(sp->cur = p); 26239800Sbostic } 26339800Sbostic else if (p->info == FTS_SL && instr == FTS_FOLLOW) { 26439800Sbostic p->info = fts_stat(p, 1); 26539800Sbostic return(p); 26639800Sbostic } 26739800Sbostic 26839800Sbostic /* 26939800Sbostic * user may have called ftsset on pointer returned by 27039800Sbostic * ftschildren; handle it here. 27139800Sbostic */ 27239800Sbostic for (p = p->link; p;) { 27339800Sbostic instr = p->instr; 27439800Sbostic if (instr == FTS_FOLLOW) { 27539800Sbostic p->info = fts_stat(p, 1); 27639800Sbostic p->instr = 0; 27739800Sbostic break; 27839800Sbostic } 27939800Sbostic if (instr == FTS_SKIP) { 28039800Sbostic tmp = p; 28139800Sbostic p = p->link; 28239800Sbostic (void)free((char *)tmp); 28339800Sbostic continue; 28439800Sbostic } 28539800Sbostic p->instr = 0; 28639800Sbostic break; 28739800Sbostic } 28839800Sbostic 28939800Sbostic /* go to next node on this level */ 29039800Sbostic if (p) { 29139800Sbostic /* 29239800Sbostic * if root level node, set up paths and return. If not the 29339800Sbostic * first time, and it's not an absolute pathname, get back 29439800Sbostic * to wherever we started from. 29539800Sbostic */ 29639800Sbostic if (p->level == ROOTLEVEL) { 29739800Sbostic fts_load(p); 29839800Sbostic if (cd) { 29939800Sbostic (void)free((char *)sp->cur); 30039800Sbostic if (p->path[0] != '/' && CHDIR(sp, sp->wd)) { 30139800Sbostic /* return target path for error msg */ 30239800Sbostic p->path = sp->wd; 30339800Sbostic p->info = FTS_ERR; 30439800Sbostic sp->options |= FTS__STOP; 30539800Sbostic return(sp->cur = p); 30639800Sbostic } 30739800Sbostic } else 30839800Sbostic cd = 1; 30939800Sbostic p->info = fts_stat(p, 0); 31039800Sbostic } else { 31139800Sbostic (void)free((char *)sp->cur); 31239800Sbostic cp = sp->path + NAPPEND(p->parent); 31339800Sbostic *cp++ = '/'; 31439800Sbostic bcopy(p->name, cp, p->namelen + 1); 31539800Sbostic if (p->info == FTS_D && (tmp = fts_cycle(p))) { 31639800Sbostic sp->savelink = p->link; 31739800Sbostic p->link = tmp; 31839800Sbostic } 31939800Sbostic } 32039800Sbostic return(sp->cur = p); 32139800Sbostic } 32239800Sbostic 32339800Sbostic /* go to parent */ 32439800Sbostic p = sp->cur->parent; 32539800Sbostic (void)free((char *)sp->cur); 32639800Sbostic if (p->level == ROOTPARENTLEVEL) { 32739800Sbostic /* 32839800Sbostic * done; free everything up and set errno to 0 so the user 32939800Sbostic * can distinguish between error and EOF. 33039800Sbostic */ 33139800Sbostic (void)free((char *)p); 33239800Sbostic errno = 0; 33339800Sbostic return(sp->cur = NULL); 33439800Sbostic } 33539800Sbostic 33639800Sbostic sp->path[p->pathlen] = '\0'; 33739800Sbostic if (CHDIR(sp, "..")) { 33839800Sbostic sp->options |= FTS__STOP; 33939800Sbostic p->info = FTS_ERR; 34039800Sbostic } else 34139800Sbostic p->info = FTS_DP; 34239800Sbostic return(sp->cur = p); 34339800Sbostic } 34439800Sbostic 34539800Sbostic /* 34639800Sbostic * ftsset takes the stream as an argument although it's not used in this 34739800Sbostic * implementation; it would be necessary if anyone wanted to add global 34839800Sbostic * semantics to fts using ftsset. A possible error return is allowed for 34939800Sbostic * similar reasons. 35039800Sbostic */ 35139800Sbostic /* ARGSUSED */ 35239800Sbostic ftsset(sp, p, instr) 35339800Sbostic FTS *sp; 35439800Sbostic FTSENT *p; 35539800Sbostic int instr; 35639800Sbostic { 35739800Sbostic p->instr = instr; 35839800Sbostic return(0); 35939800Sbostic } 36039800Sbostic 36139800Sbostic FTSENT * 36239800Sbostic ftschildren(sp) 36339800Sbostic register FTS *sp; 36439800Sbostic { 36539800Sbostic /* 36639800Sbostic * set errno to 0 so that user can tell the difference between an 36739800Sbostic * error and a directory without entries. 36839800Sbostic */ 36939800Sbostic errno = 0; 37039800Sbostic if (sp->cur->info != FTS_D || sp->options & FTS__STOP) 37139800Sbostic return(NULL); 37239800Sbostic if (sp->child) 37339800Sbostic fts_lfree(sp->child); 374*39962Sbostic return(sp->child = fts_build(sp, 0)); 37539800Sbostic } 37639800Sbostic 37739800Sbostic #define ISDOT(a) (a[0] == '.' && (!a[1] || a[1] == '.' && !a[2])) 37839800Sbostic 37939800Sbostic static FTSENT * 380*39962Sbostic fts_build(sp, set_node) 38139800Sbostic register FTS *sp; 382*39962Sbostic int set_node; 38339800Sbostic { 38439800Sbostic register struct dirent *dp; 38539800Sbostic register FTSENT *p, *head; 38639800Sbostic register int nitems; 38739800Sbostic DIR *dirp; 388*39962Sbostic int descend, len, level, maxlen, nlinks, saved_errno; 38939800Sbostic char *cp; 39039800Sbostic 39139800Sbostic p = sp->cur; 39239800Sbostic if (!(dirp = opendir(p->accpath))) { 393*39962Sbostic if (set_node) { 39439800Sbostic errno = 0; 39539800Sbostic p->info = FTS_DNR; 39639800Sbostic } 39739800Sbostic return(NULL); 39839800Sbostic } 39939800Sbostic 40039800Sbostic /* 40139800Sbostic * the real slowdown in walking the tree is the stat calls. If 40239800Sbostic * FTS_NOSTAT is set and it's a physical walk (so that symbolic 40339800Sbostic * links can't be directories), fts assumes that the number of 40439800Sbostic * subdirectories in a node is equal to the number of links to 40539800Sbostic * the parent. This allows stat calls to be skipped in any leaf 40639800Sbostic * directories and for any nodes after the directories in the 40739800Sbostic * parent node have been found. This empirically cuts the stat 40839800Sbostic * calls by about 2/3. 40939800Sbostic */ 41039800Sbostic nlinks = sp->options & FTS_NOSTAT && sp->options & FTS_PHYSICAL ? 41139800Sbostic p->statb.st_nlink - (sp->options & FTS_SEEDOT ? 0 : 2) : -1; 41239800Sbostic 41339800Sbostic /* get max file name length that can be stored in current path */ 41439800Sbostic maxlen = sp->pathlen - p->pathlen - 1; 41539800Sbostic 41639800Sbostic cp = sp->path + (len = NAPPEND(p)); 41739800Sbostic *cp++ = '/'; 41839800Sbostic 41939800Sbostic level = p->level + 1; 42039800Sbostic 421*39962Sbostic if (nlinks || set_node) { 422*39962Sbostic if (FCHDIR(sp, dirfd(dirp))) { 423*39962Sbostic if (set_node) { 424*39962Sbostic errno = 0; 425*39962Sbostic p->info = FTS_DNX; 426*39962Sbostic } 427*39962Sbostic return(NULL); 428*39962Sbostic } 429*39962Sbostic descend = 1; 430*39962Sbostic } else 431*39962Sbostic descend = 0; 432*39962Sbostic 43339800Sbostic /* read the directory, attching each new entry to the `link' pointer */ 43439800Sbostic for (head = NULL, nitems = 0; dp = readdir(dirp);) { 43539800Sbostic if (ISDOT(dp->d_name) && !(sp->options & FTS_SEEDOT)) 43639800Sbostic continue; 43739800Sbostic 43839800Sbostic if (!(p = fts_alloc(dp->d_name, dp->d_namlen))) { 43939800Sbostic saved_errno = errno; 44039800Sbostic goto mem1; 44139800Sbostic } 44239800Sbostic if (dp->d_namlen > maxlen) { 44339800Sbostic if (!fts_path((int)dp->d_namlen)) { 44439800Sbostic /* quit: this stream no longer has a path */ 44539800Sbostic sp->options |= FTS__STOP; 44639800Sbostic saved_errno = errno; 44739800Sbostic (void)free((char *)p); 44839800Sbostic mem1: fts_lfree(head); 449*39962Sbostic if (set_node) 45039800Sbostic p->info = FTS_ERR; 451*39962Sbostic if (descend && CHDIR(sp, "..")) { 45239800Sbostic saved_errno = errno; 45339800Sbostic sp->options |= FTS__STOP; 45439800Sbostic } 45539800Sbostic errno = saved_errno; 45639800Sbostic return(NULL); 45739800Sbostic } 45839800Sbostic maxlen = sp->pathlen - sp->cur->pathlen - 1; 45939800Sbostic } 46039800Sbostic 46139800Sbostic p->pathlen = len + dp->d_namlen + 1; 46239800Sbostic p->accpath = sp->options & FTS_NOCHDIR ? p->path : p->name; 46339800Sbostic 46439800Sbostic p->parent = sp->cur; 46539800Sbostic p->level = level; 46639800Sbostic 46739800Sbostic if (nlinks) { 46839800Sbostic /* make sure fts_stat has a filename to stat */ 46939800Sbostic if (sp->options & FTS_NOCHDIR) 47039800Sbostic bcopy(p->name, cp, p->namelen + 1); 47139800Sbostic p->info = fts_stat(p, 0); 47239800Sbostic if (nlinks > 0 && (p->info == FTS_D || 47339800Sbostic p->info == FTS_DNR || p->info == FTS_DNX)) 47439800Sbostic --nlinks; 47539800Sbostic } else 47639800Sbostic p->info = FTS_NS; 47739800Sbostic 47839800Sbostic p->link = head; 47939800Sbostic head = p; 48039800Sbostic ++nitems; 48139800Sbostic } 48239800Sbostic (void)closedir(dirp); 48339800Sbostic 484*39962Sbostic if (descend && (!nitems || !set_node) && CHDIR(sp, "..")) { 485*39962Sbostic sp->options |= FTS__STOP; 486*39962Sbostic p->info = FTS_ERR; 487*39962Sbostic *--cp = '\0'; 488*39962Sbostic return(NULL); 489*39962Sbostic } 490*39962Sbostic 49139800Sbostic if (!nitems) { 492*39962Sbostic if (set_node) 49339800Sbostic p->info = FTS_DP; 49439800Sbostic *--cp = '\0'; 49539800Sbostic return(NULL); 49639800Sbostic } 49739800Sbostic 49839800Sbostic if (sp->compar && nitems > 1) 49939800Sbostic head = fts_sort(head, nitems); 50039800Sbostic 501*39962Sbostic if (set_node) 50239800Sbostic bcopy(head->name, cp, head->namelen + 1); 50339800Sbostic else 50439800Sbostic *--cp = '\0'; 50539800Sbostic return(head); 50639800Sbostic } 50739800Sbostic 50839800Sbostic static short 50939800Sbostic fts_stat(p, symflag) 51039800Sbostic register FTSENT *p; 51139800Sbostic int symflag; 51239800Sbostic { 51339800Sbostic register int ngroups, *gp; 51439800Sbostic int gidset[NGROUPS]; 51539800Sbostic 51639800Sbostic /* 51739800Sbostic * detection of symbolic links w/o targets. If FTS_FOLLOW is set, 51839800Sbostic * the symlink structure is overwritten with the stat structure of 51939800Sbostic * the target. 52039800Sbostic */ 52139800Sbostic if (stream->options & FTS_LOGICAL || symflag) { 52239800Sbostic if (stat(p->accpath, &p->statb)) 52339800Sbostic return(symflag || !lstat(p->accpath, &p->statb) ? 52439800Sbostic FTS_SLNONE : FTS_ERR); 52539800Sbostic } else if (lstat(p->accpath, &p->statb)) 52639800Sbostic return(FTS_ERR); 52739800Sbostic 52839800Sbostic switch(p->statb.st_mode&S_IFMT) { 52939800Sbostic case S_IFDIR: 53039800Sbostic /* get new uid/groups each time, they may have changed */ 53139800Sbostic if (getuid() == p->statb.st_uid) { 53239800Sbostic if (!(p->statb.st_mode&S_IRUSR)) 53339800Sbostic return(FTS_DNR); 53439800Sbostic if (!(p->statb.st_mode&S_IXUSR)) 53539800Sbostic return(FTS_DNX); 53639800Sbostic return(FTS_D); 53739800Sbostic } 53839800Sbostic if ((ngroups = getgroups(NGROUPS, gidset)) == -1) 53939800Sbostic return(FTS_ERR); 54039800Sbostic for (gp = gidset; ngroups--;) 54139800Sbostic if (*gp++ == p->statb.st_gid) { 54239800Sbostic if (!(p->statb.st_mode&S_IRGRP)) 54339800Sbostic return(FTS_DNR); 54439800Sbostic if (!(p->statb.st_mode&S_IXGRP)) 54539800Sbostic return(FTS_DNX); 54639800Sbostic return(FTS_D); 54739800Sbostic } 54839800Sbostic if (!(p->statb.st_mode&S_IROTH)) 54939800Sbostic return(FTS_DNR); 55039800Sbostic if (!(p->statb.st_mode&S_IXOTH)) 55139800Sbostic return(FTS_DNX); 55239800Sbostic return(FTS_D); 55339800Sbostic case S_IFLNK: 55439800Sbostic return(FTS_SL); 55539800Sbostic case S_IFREG: 55639800Sbostic return(FTS_F); 55739800Sbostic default: 55839800Sbostic return(FTS_DEFAULT); 55939800Sbostic } 56039800Sbostic /* NOTREACHED */ 56139800Sbostic } 56239800Sbostic 56339800Sbostic static FTSENT * 56439800Sbostic fts_cycle(p) 56539800Sbostic register FTSENT *p; 56639800Sbostic { 56739800Sbostic register dev_t dev; 56839800Sbostic register ino_t ino; 56939800Sbostic 57039800Sbostic /* 57139800Sbostic * cycle detection is brute force; if the tree gets deep enough or 57239800Sbostic * the number of symbolic links to directories is really high 57339800Sbostic * something faster might be worthwhile. 57439800Sbostic */ 57539800Sbostic dev = p->statb.st_dev; 57639800Sbostic ino = p->statb.st_ino; 57739800Sbostic for (p = p->parent; p->level > ROOTLEVEL; p = p->parent) 57839800Sbostic if (ino == p->statb.st_ino && dev == p->statb.st_dev) 57939800Sbostic return(p); 58039800Sbostic return(NULL); 58139800Sbostic } 58239800Sbostic 58339800Sbostic #define R(type, nelem, ptr) \ 58439800Sbostic (type *)realloc((char *)ptr, (u_int)((nelem) * sizeof(type))) 58539800Sbostic 58639800Sbostic static FTSENT * 58739800Sbostic fts_sort(head, nitems) 58839800Sbostic FTSENT *head; 58939800Sbostic register int nitems; 59039800Sbostic { 59139800Sbostic register FTSENT **ap, *p; 59239800Sbostic 59339800Sbostic /* 59439800Sbostic * construct an array of pointers to the structures and call qsort(3). 59539800Sbostic * Reassemble the array in the order returned by qsort. If unable to 59639800Sbostic * sort for memory reasons, return the directory entries in their 59739800Sbostic * current order. Allocate enough space for the current needs plus 59839800Sbostic * 40 so we don't realloc one entry at a time. 59939800Sbostic */ 60039800Sbostic if (nitems > stream->nitems) { 60139800Sbostic stream->nitems = nitems + 40; 60239800Sbostic if (!(stream->array = 60339800Sbostic R(FTSENT *, stream->nitems, stream->array))) { 60439800Sbostic stream->nitems = 0; 60539800Sbostic return(head); 60639800Sbostic } 60739800Sbostic } 60839800Sbostic for (ap = stream->array, p = head; p; p = p->link) 60939800Sbostic *ap++ = p; 61039800Sbostic qsort((char *)stream->array, nitems, sizeof(FTSENT *), stream->compar); 61139800Sbostic for (head = *(ap = stream->array); --nitems; ++ap) 61239800Sbostic ap[0]->link = ap[1]; 61339800Sbostic ap[0]->link = NULL; 61439800Sbostic return(head); 61539800Sbostic } 61639800Sbostic 61739800Sbostic static FTSENT * 61839800Sbostic fts_alloc(name, len) 61939800Sbostic char *name; 62039800Sbostic register int len; 62139800Sbostic { 62239800Sbostic register FTSENT *p; 62339800Sbostic 62439800Sbostic /* 62539800Sbostic * variable sized structures; the name is the last element so 62639800Sbostic * allocate enough extra space after the structure to hold it. 62739800Sbostic */ 62839800Sbostic if (!(p = (FTSENT *)malloc((u_int)(sizeof(FTSENT) + len)))) 62939800Sbostic return(NULL); 63039800Sbostic bcopy(name, p->name, len + 1); 63139800Sbostic p->namelen = len; 63239800Sbostic p->path = stream->path; 63339800Sbostic p->instr = 0; 63439800Sbostic p->local.number = 0; 63539800Sbostic p->local.pointer = NULL; 63639800Sbostic return(p); 63739800Sbostic } 63839800Sbostic 63939800Sbostic static 64039800Sbostic fts_lfree(head) 64139800Sbostic register FTSENT *head; 64239800Sbostic { 64339800Sbostic register FTSENT *p; 64439800Sbostic 64539800Sbostic while (p = head) { 64639800Sbostic head = head->link; 64739800Sbostic (void)free((char *)p); 64839800Sbostic } 64939800Sbostic } 65039800Sbostic 65139800Sbostic /* 65239800Sbostic * allow essentially unlimited paths; certain programs (find, remove, ls) 65339800Sbostic * need to work on any tree. Most systems will allow creation of paths 65439800Sbostic * much longer than MAXPATHLEN, even though the kernel won't resolve them. 65539800Sbostic * Add an extra 128 bytes to the requested size so that we don't realloc 65639800Sbostic * the path 2 bytes at a time. 65739800Sbostic */ 65839800Sbostic static 65939800Sbostic fts_path(size) 66039800Sbostic int size; 66139800Sbostic { 66239800Sbostic stream->pathlen += size + 128; 66339800Sbostic return((int)(stream->path = R(char, stream->pathlen, stream->path))); 66439800Sbostic } 66539800Sbostic 66639800Sbostic static FTSENT * 66739800Sbostic fts_root(name) 66839800Sbostic register char *name; 66939800Sbostic { 67039800Sbostic register char *cp; 67139800Sbostic 67239800Sbostic /* 67339800Sbostic * rip trailing slashes; it's somewhat unclear in POSIX 1003.1 what 67439800Sbostic * /a/b/ really is, they don't talk about what a null path component 67539800Sbostic * resolves to. This hopefully does what the user intended. Don't 67639800Sbostic * allow null pathnames. 67739800Sbostic */ 67839800Sbostic for (cp = name; *cp; ++cp); 67939800Sbostic if (cp == name) { 68039800Sbostic errno = ENOENT; 68139800Sbostic return(NULL); 68239800Sbostic } 68339800Sbostic while (--cp > name && *cp == '/'); 68439800Sbostic *++cp = '\0'; 68539800Sbostic return(fts_alloc(name, cp - name)); 68639800Sbostic } 687