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*42299Sbostic static char sccsid[] = "@(#)fts.c 5.7 (Berkeley) 05/23/90"; 1039800Sbostic #endif /* LIBC_SCCS and not lint */ 1139800Sbostic 1239800Sbostic #include <sys/param.h> 1339800Sbostic #include <sys/stat.h> 14*42299Sbostic #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 38*42299Sbostic /* fts_build flags */ 39*42299Sbostic #define BCHILD 1 /* from ftschildren */ 40*42299Sbostic #define BREAD 2 /* from ftsread */ 41*42299Sbostic 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; 54*42299Sbostic 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; 61*42299Sbostic 62*42299Sbostic /* 63*42299Sbostic * logical walks turn on NOCHDIR; symbolic links are just too 64*42299Sbostic * hard to deal with. 65*42299Sbostic */ 6642273Sbostic sp->fts_options = options; 67*42299Sbostic if (options & FTS_LOGICAL) 68*42299Sbostic 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) */ 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; 8142273Sbostic if (maxlen < p->fts_namelen) 8242273Sbostic maxlen = p->fts_namelen; 8339800Sbostic /* 8439800Sbostic * if comparison routine supplied, traverse in sorted 8539800Sbostic * order; otherwise traverse in the order specified. 8639800Sbostic */ 8739800Sbostic if (compar) { 8842273Sbostic p->fts_link = root; 8939800Sbostic root = p; 9042273Sbostic p->fts_accpath = p->fts_name; 9142273Sbostic p->fts_info = fts_stat(p, 0); 9239800Sbostic } else { 9342273Sbostic p->fts_link = NULL; 9439800Sbostic if (!root) 9539800Sbostic tmp = root = p; 9639800Sbostic else { 9742273Sbostic tmp->fts_link = p; 9839800Sbostic tmp = p; 9939800Sbostic } 10039800Sbostic } 10142273Sbostic p->fts_level = ROOTLEVEL; 10242273Sbostic p->fts_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; 10942273Sbostic maxlen = root->fts_namelen; 11042273Sbostic root->fts_link = NULL; 11142273Sbostic root->fts_level = ROOTLEVEL; 11242273Sbostic root->fts_parent = parent; 11339800Sbostic } 11439800Sbostic 11542281Sbostic /* 11642281Sbostic * allocate a dummy pointer and make ftsread think that we've just 11742281Sbostic * finished the node before the root(s); set p->fts_info to FTS_NS 11842281Sbostic * so that everything about the "current" node is ignored. 11942281Sbostic */ 12042281Sbostic if (!(sp->fts_cur = fts_alloc("", 0))) 12142281Sbostic goto mem2; 12242281Sbostic sp->fts_cur->fts_link = root; 12342281Sbostic sp->fts_cur->fts_info = FTS_NS; 12442281Sbostic 12539800Sbostic /* start out with at least 1K+ of path space */ 12639800Sbostic if (!fts_path(MAX(maxlen, MAXPATHLEN))) 12742281Sbostic goto mem3; 12839800Sbostic 12939800Sbostic /* 130*42299Sbostic * if using chdir(2), grab a file descriptor pointing to dot to insure 131*42299Sbostic * that we can get back here; this could be avoided for some paths, 132*42299Sbostic * but almost certainly not worth the effort. Slashes, symbolic links, 133*42299Sbostic * and ".." are all fairly nasty problems. Note, if we can't get the 134*42299Sbostic * descriptor we run anyway, just more slowly. 13539800Sbostic */ 13642273Sbostic if (!(options & FTS_NOCHDIR) && 137*42299Sbostic (sp->fts_sd = open(".", O_RDONLY, 0)) < 0) 13842273Sbostic sp->fts_options |= FTS_NOCHDIR; 13939800Sbostic 14039800Sbostic return(sp); 14139800Sbostic 142*42299Sbostic mem3: (void)free((void *)sp->fts_cur); 14339800Sbostic mem2: fts_lfree(root); 144*42299Sbostic (void)free((void *)parent); 145*42299Sbostic mem1: (void)free((void *)sp); 14639800Sbostic return(NULL); 14739800Sbostic } 14839800Sbostic 14939800Sbostic static 15039800Sbostic fts_load(p) 15139800Sbostic register FTSENT *p; 15239800Sbostic { 15339800Sbostic register int len; 15439800Sbostic register char *cp; 15539800Sbostic 15639800Sbostic /* 15739800Sbostic * load the stream structure for the next traversal; set the 15839800Sbostic * accpath field specially so the chdir gets done to the right 15939800Sbostic * place and the user can access the first node. 16039800Sbostic */ 16142273Sbostic len = p->fts_pathlen = p->fts_namelen; 16242273Sbostic bcopy(p->fts_name, stream->fts_path, len + 1); 16342273Sbostic if ((cp = rindex(p->fts_name, '/')) && (cp != p->fts_name || cp[1])) { 16439800Sbostic len = strlen(++cp); 16542273Sbostic bcopy(cp, p->fts_name, len + 1); 16642273Sbostic p->fts_namelen = len; 16739800Sbostic } 16842273Sbostic p->fts_accpath = p->fts_path = stream->fts_path; 16939800Sbostic } 17039800Sbostic 17139800Sbostic ftsclose(sp) 17239800Sbostic FTS *sp; 17339800Sbostic { 17439800Sbostic register FTSENT *freep, *p; 17539800Sbostic int saved_errno; 17639800Sbostic 17742281Sbostic if (sp->fts_cur) { 17842281Sbostic /* 17942281Sbostic * this still works if we haven't read anything -- the dummy 18042281Sbostic * structure points to the root list, so we step through to 18142281Sbostic * the end of the root list which has a valid parent pointer. 18242281Sbostic */ 18342281Sbostic for (p = sp->fts_cur; p->fts_level > ROOTPARENTLEVEL;) { 18442281Sbostic freep = p; 18542281Sbostic p = p->fts_link ? p->fts_link : p->fts_parent; 186*42299Sbostic (void)free((void *)freep); 18739800Sbostic } 188*42299Sbostic (void)free((void *)p); 18942281Sbostic } 19039800Sbostic 19139800Sbostic /* free up child linked list, sort array, path buffer */ 19242273Sbostic if (sp->fts_child) 19342273Sbostic fts_lfree(sp->fts_child); 19442273Sbostic if (sp->fts_array) 195*42299Sbostic (void)free((void *)sp->fts_array); 19642273Sbostic (void)free((char *)sp->fts_path); 19739800Sbostic 19839800Sbostic /* 19939800Sbostic * return to original directory, save errno if necessary; 20039800Sbostic * free up the directory buffer 20139800Sbostic */ 20242273Sbostic if (!(sp->fts_options & FTS_NOCHDIR)) { 203*42299Sbostic saved_errno = fchdir(sp->fts_sd) ? errno : 0; 204*42299Sbostic (void)close(sp->fts_sd); 20539800Sbostic } 20639800Sbostic 20739800Sbostic /* free up the stream pointer */ 208*42299Sbostic (void)free((void *)sp); 20939800Sbostic 21039800Sbostic /* set errno and return */ 21142273Sbostic if (!(sp->fts_options & FTS_NOCHDIR) && saved_errno) { 21239800Sbostic errno = saved_errno; 21339800Sbostic return(-1); 21439800Sbostic } 21539800Sbostic return(0); 21639800Sbostic } 21739800Sbostic 21839800Sbostic FTSENT * 21939800Sbostic ftsread(sp) 22039800Sbostic register FTS *sp; 22139800Sbostic { 222*42299Sbostic register FTSENT *p, *tmp; 22339800Sbostic register int instr; 224*42299Sbostic register char *cp; 22539800Sbostic static int cd; 22639800Sbostic 22739800Sbostic /* if finished or unrecoverable error, return NULL */ 22842273Sbostic if (!sp->fts_cur || sp->fts_options & FTS__STOP) 22939800Sbostic return(NULL); 23039800Sbostic 23139800Sbostic /* set global stream pointer, and current node pointer */ 23239800Sbostic stream = sp; 23342273Sbostic p = sp->fts_cur; 23439800Sbostic 23539800Sbostic /* save and zero out user instructions */ 23642273Sbostic instr = p->fts_instr; 23742273Sbostic p->fts_instr = 0; 23839800Sbostic 23939800Sbostic /* if used link pointer for cycle detection, restore it */ 24042273Sbostic if (sp->fts_savelink) { 24142273Sbostic p->fts_link = sp->fts_savelink; 24242273Sbostic sp->fts_savelink = NULL; 24339800Sbostic } 24439800Sbostic 24539800Sbostic /* any type of file may be re-visited; re-stat and return */ 24639800Sbostic if (instr == FTS_AGAIN) { 24742273Sbostic p->fts_info = fts_stat(p, 0); 24839800Sbostic return(p); 24939800Sbostic } 25039800Sbostic 251*42299Sbostic /* following a symbolic link */ 252*42299Sbostic if (p->fts_info == FTS_SL && instr == FTS_FOLLOW) { 253*42299Sbostic p->fts_info = fts_stat(p, 1); 254*42299Sbostic return(p); 255*42299Sbostic } 256*42299Sbostic 257*42299Sbostic /* directory in pre-order */ 258*42299Sbostic if (p->fts_info == FTS_D) { 259*42299Sbostic /* may have been skipped or crossed mount point */ 26042280Sbostic if (instr == FTS_SKIP || sp->fts_options & FTS_XDEV && 26142280Sbostic p->fts_statb.st_dev != sp->sdev) { 26242273Sbostic if (sp->fts_child) { 26342273Sbostic fts_lfree(sp->fts_child); 26442273Sbostic sp->fts_child = NULL; 26539800Sbostic } 266*42299Sbostic goto next; 267*42299Sbostic } 268*42299Sbostic 269*42299Sbostic /* read the directory if necessary, and return first entry */ 270*42299Sbostic if (sp->fts_child) 271*42299Sbostic if (CHDIR(sp, p->fts_accpath)) { 272*42299Sbostic fts_lfree(sp->fts_child); 273*42299Sbostic p->fts_info = FTS_DNX; 274*42299Sbostic } else { 275*42299Sbostic p = sp->fts_child; 276*42299Sbostic cp = sp->fts_path + NAPPEND(p->fts_parent); 277*42299Sbostic *cp++ = '/'; 278*42299Sbostic bcopy(p->fts_name, cp, p->fts_namelen + 1); 279*42299Sbostic } 280*42299Sbostic else { 281*42299Sbostic if (!(sp->fts_child = fts_build(sp, BREAD))) 28239800Sbostic return(p); 28342273Sbostic p = sp->fts_child; 28439800Sbostic } 285*42299Sbostic sp->fts_child = NULL; 286*42299Sbostic return(sp->fts_cur = p); 28739800Sbostic } 28839800Sbostic 289*42299Sbostic /* move to next node on this level */ 290*42299Sbostic next: tmp = p; 291*42299Sbostic if (p = p->fts_link) { 29239800Sbostic /* 29339800Sbostic * if root level node, set up paths and return. If not the 29439800Sbostic * first time, and it's not an absolute pathname, get back 295*42299Sbostic * to starting directory. 29639800Sbostic */ 29742273Sbostic if (p->fts_level == ROOTLEVEL) { 29839800Sbostic fts_load(p); 299*42299Sbostic (void)free((void *)tmp); 30042281Sbostic if (cd && 301*42299Sbostic p->fts_path[0] != '/' && FCHDIR(sp, sp->fts_sd)) { 302*42299Sbostic /* should never happen... */ 303*42299Sbostic p->fts_path = "starting directory"; 30442281Sbostic p->fts_info = FTS_ERR; 30542281Sbostic sp->fts_options |= FTS__STOP; 30642281Sbostic return(sp->fts_cur = p); 307*42299Sbostic } 30842281Sbostic cd = 1; 30942273Sbostic p->fts_info = fts_stat(p, 0); 31042280Sbostic sp->sdev = p->fts_statb.st_dev; 31139800Sbostic } else { 312*42299Sbostic (void)free((void *)tmp); 313*42299Sbostic 314*42299Sbostic /* user may have called ftsset on node */ 315*42299Sbostic if (p->fts_instr == FTS_SKIP) 316*42299Sbostic goto next; 317*42299Sbostic if (p->fts_instr == FTS_FOLLOW) { 318*42299Sbostic p->fts_info = fts_stat(p, 1); 319*42299Sbostic p->fts_instr = 0; 320*42299Sbostic } 321*42299Sbostic 322*42299Sbostic /* fill in the paths */ 32342273Sbostic cp = sp->fts_path + NAPPEND(p->fts_parent); 32439800Sbostic *cp++ = '/'; 32542273Sbostic bcopy(p->fts_name, cp, p->fts_namelen + 1); 326*42299Sbostic 327*42299Sbostic /* check for directory cycles */ 32842273Sbostic if (p->fts_info == FTS_D && (tmp = fts_cycle(p))) { 32942273Sbostic sp->fts_savelink = p->fts_link; 33042273Sbostic p->fts_link = tmp; 331*42299Sbostic p->fts_info = FTS_DC; 33239800Sbostic } 33339800Sbostic } 33442273Sbostic return(sp->fts_cur = p); 33539800Sbostic } 33639800Sbostic 337*42299Sbostic /* move to parent */ 338*42299Sbostic p = tmp->fts_parent; 339*42299Sbostic (void)free((void *)tmp); 340*42299Sbostic 34142273Sbostic if (p->fts_level == ROOTPARENTLEVEL) { 34239800Sbostic /* 34339800Sbostic * done; free everything up and set errno to 0 so the user 34439800Sbostic * can distinguish between error and EOF. 34539800Sbostic */ 346*42299Sbostic (void)free((void *)p); 34739800Sbostic errno = 0; 34842273Sbostic return(sp->fts_cur = NULL); 34939800Sbostic } 35039800Sbostic 35142273Sbostic sp->fts_path[p->fts_pathlen] = '\0'; 35239800Sbostic if (CHDIR(sp, "..")) { 35342273Sbostic sp->fts_options |= FTS__STOP; 35442273Sbostic p->fts_info = FTS_ERR; 35539800Sbostic } else 35642273Sbostic p->fts_info = FTS_DP; 35742273Sbostic return(sp->fts_cur = p); 35839800Sbostic } 35939800Sbostic 36039800Sbostic /* 36139800Sbostic * ftsset takes the stream as an argument although it's not used in this 36239800Sbostic * implementation; it would be necessary if anyone wanted to add global 36339800Sbostic * semantics to fts using ftsset. A possible error return is allowed for 36439800Sbostic * similar reasons. 36539800Sbostic */ 36639800Sbostic /* ARGSUSED */ 36739800Sbostic ftsset(sp, p, instr) 36839800Sbostic FTS *sp; 36939800Sbostic FTSENT *p; 37039800Sbostic int instr; 37139800Sbostic { 37242273Sbostic p->fts_instr = instr; 37339800Sbostic return(0); 37439800Sbostic } 37539800Sbostic 37639800Sbostic FTSENT * 37739800Sbostic ftschildren(sp) 37839800Sbostic register FTS *sp; 37939800Sbostic { 380*42299Sbostic register FTSENT *p; 381*42299Sbostic int fd; 382*42299Sbostic 38339800Sbostic /* 38439800Sbostic * set errno to 0 so that user can tell the difference between an 38539800Sbostic * error and a directory without entries. 38639800Sbostic */ 38739800Sbostic errno = 0; 388*42299Sbostic p = sp->fts_cur; 389*42299Sbostic if (p->fts_info != FTS_D || sp->fts_options & FTS__STOP) 39039800Sbostic return(NULL); 39142273Sbostic if (sp->fts_child) 39242273Sbostic fts_lfree(sp->fts_child); 393*42299Sbostic 394*42299Sbostic /* 395*42299Sbostic * if using chdir on a relative path and called BEFORE ftsread on the 396*42299Sbostic * root of a traversal, we can lose because we need to chdir into the 397*42299Sbostic * subdirectory, and we don't know where the current directory is to 398*42299Sbostic * get back so that the upcoming chdir by ftsread will work. 399*42299Sbostic */ 400*42299Sbostic if (p->fts_level != ROOTLEVEL || p->fts_accpath[0] == '/' || 401*42299Sbostic sp->fts_options & FTS_NOCHDIR) 402*42299Sbostic return(sp->fts_child = fts_build(sp, BCHILD)); 403*42299Sbostic 404*42299Sbostic if ((fd = open(".", O_RDONLY, 0)) < 0) 405*42299Sbostic return(NULL); 406*42299Sbostic sp->fts_child = fts_build(sp, BCHILD); 407*42299Sbostic if (fchdir(fd)) 408*42299Sbostic return(NULL); 409*42299Sbostic (void)close(fd); 410*42299Sbostic return(sp->fts_child); 41139800Sbostic } 41239800Sbostic 41339800Sbostic #define ISDOT(a) (a[0] == '.' && (!a[1] || a[1] == '.' && !a[2])) 41439800Sbostic 415*42299Sbostic FTSENT * 416*42299Sbostic fts_build(sp, type) 41739800Sbostic register FTS *sp; 418*42299Sbostic int type; 41939800Sbostic { 42039800Sbostic register struct dirent *dp; 42139800Sbostic register FTSENT *p, *head; 42239800Sbostic register int nitems; 42339800Sbostic DIR *dirp; 42439962Sbostic int descend, len, level, maxlen, nlinks, saved_errno; 42539800Sbostic char *cp; 42639800Sbostic 42742273Sbostic p = sp->fts_cur; 42842273Sbostic if (!(dirp = opendir(p->fts_accpath))) { 429*42299Sbostic if (type == BREAD) { 43039800Sbostic errno = 0; 43142273Sbostic p->fts_info = FTS_DNR; 43239800Sbostic } 43339800Sbostic return(NULL); 43439800Sbostic } 43539800Sbostic 43639800Sbostic /* 43739800Sbostic * the real slowdown in walking the tree is the stat calls. If 43839800Sbostic * FTS_NOSTAT is set and it's a physical walk (so that symbolic 43939800Sbostic * links can't be directories), fts assumes that the number of 44039800Sbostic * subdirectories in a node is equal to the number of links to 44139800Sbostic * the parent. This allows stat calls to be skipped in any leaf 44239800Sbostic * directories and for any nodes after the directories in the 44339800Sbostic * parent node have been found. This empirically cuts the stat 44439800Sbostic * calls by about 2/3. 44539800Sbostic */ 44642273Sbostic nlinks = 44742273Sbostic sp->fts_options & FTS_NOSTAT && sp->fts_options & FTS_PHYSICAL ? 44842273Sbostic p->fts_statb.st_nlink - (sp->fts_options & FTS_SEEDOT ? 0 : 2) : -1; 44939800Sbostic 450*42299Sbostic /* if told to descend or found links and not told not to descend. */ 451*42299Sbostic if (nlinks || type == BREAD) { 45239962Sbostic if (FCHDIR(sp, dirfd(dirp))) { 453*42299Sbostic if (type == BREAD) { 45439962Sbostic errno = 0; 45542273Sbostic p->fts_info = FTS_DNX; 45639962Sbostic } 457*42299Sbostic (void)closedir(dirp); 45839962Sbostic return(NULL); 45939962Sbostic } 46039962Sbostic descend = 1; 46139962Sbostic } else 46239962Sbostic descend = 0; 46339962Sbostic 46440939Sbostic /* get max file name length that can be stored in current path */ 46542273Sbostic maxlen = sp->fts_pathlen - p->fts_pathlen - 1; 46640939Sbostic 46742273Sbostic cp = sp->fts_path + (len = NAPPEND(p)); 46840939Sbostic *cp++ = '/'; 46940939Sbostic 47042273Sbostic level = p->fts_level + 1; 47140939Sbostic 47239800Sbostic /* read the directory, attching each new entry to the `link' pointer */ 47339800Sbostic for (head = NULL, nitems = 0; dp = readdir(dirp);) { 47442273Sbostic if (ISDOT(dp->d_name) && !(sp->fts_options & FTS_SEEDOT)) 47539800Sbostic continue; 47639800Sbostic 47739800Sbostic if (!(p = fts_alloc(dp->d_name, dp->d_namlen))) { 47839800Sbostic saved_errno = errno; 47939800Sbostic goto mem1; 48039800Sbostic } 48139800Sbostic if (dp->d_namlen > maxlen) { 48239800Sbostic if (!fts_path((int)dp->d_namlen)) { 48339800Sbostic /* quit: this stream no longer has a path */ 48442273Sbostic sp->fts_options |= FTS__STOP; 48539800Sbostic saved_errno = errno; 486*42299Sbostic (void)free((void *)p); 48739800Sbostic mem1: fts_lfree(head); 488*42299Sbostic if (type == BREAD) 48942273Sbostic p->fts_info = FTS_ERR; 49039962Sbostic if (descend && CHDIR(sp, "..")) { 49139800Sbostic saved_errno = errno; 49242273Sbostic sp->fts_options |= FTS__STOP; 49339800Sbostic } 49439800Sbostic errno = saved_errno; 495*42299Sbostic (void)closedir(dirp); 49639800Sbostic return(NULL); 49739800Sbostic } 49842273Sbostic maxlen = sp->fts_pathlen - sp->fts_cur->fts_pathlen - 1; 49939800Sbostic } 50039800Sbostic 50142273Sbostic p->fts_pathlen = len + dp->d_namlen + 1; 50242273Sbostic p->fts_accpath = 50342273Sbostic sp->fts_options & FTS_NOCHDIR ? p->fts_path : p->fts_name; 50439800Sbostic 50542273Sbostic p->fts_parent = sp->fts_cur; 50642273Sbostic p->fts_level = level; 50739800Sbostic 50839800Sbostic if (nlinks) { 50939800Sbostic /* make sure fts_stat has a filename to stat */ 51042273Sbostic if (sp->fts_options & FTS_NOCHDIR) 51142273Sbostic bcopy(p->fts_name, cp, p->fts_namelen + 1); 51242273Sbostic p->fts_info = fts_stat(p, 0); 51342273Sbostic if (nlinks > 0 && (p->fts_info == FTS_D || 51442273Sbostic p->fts_info == FTS_DNR || p->fts_info == FTS_DNX)) 51539800Sbostic --nlinks; 51639800Sbostic } else 51742273Sbostic p->fts_info = FTS_NS; 51839800Sbostic 51942273Sbostic p->fts_link = head; 52039800Sbostic head = p; 52139800Sbostic ++nitems; 52239800Sbostic } 52339800Sbostic (void)closedir(dirp); 52439800Sbostic 525*42299Sbostic /* reset the path */ 526*42299Sbostic if (cp - 1 > sp->fts_path) 527*42299Sbostic --cp; 528*42299Sbostic *cp = '\0'; 529*42299Sbostic 530*42299Sbostic /* 531*42299Sbostic * if descended: if were called from ftsread and didn't find anything, 532*42299Sbostic * or were called from ftschildren, get back. 533*42299Sbostic */ 534*42299Sbostic if (descend && (!nitems || type == BCHILD) && CHDIR(sp, "..")) { 53542273Sbostic sp->fts_options |= FTS__STOP; 53642273Sbostic p->fts_info = FTS_ERR; 53739962Sbostic return(NULL); 53839962Sbostic } 53939962Sbostic 54039800Sbostic if (!nitems) { 541*42299Sbostic if (type == BREAD) 54242273Sbostic p->fts_info = FTS_DP; 54339800Sbostic return(NULL); 54439800Sbostic } 54539800Sbostic 54642273Sbostic if (sp->fts_compar && nitems > 1) 54739800Sbostic head = fts_sort(head, nitems); 54839800Sbostic 549*42299Sbostic if (type == BREAD) { 550*42299Sbostic *cp = '/'; 551*42299Sbostic bcopy(head->fts_name, cp + 1, head->fts_namelen + 1); 552*42299Sbostic } 55339800Sbostic return(head); 55439800Sbostic } 55539800Sbostic 55639800Sbostic static short 55739800Sbostic fts_stat(p, symflag) 55839800Sbostic register FTSENT *p; 55939800Sbostic int symflag; 56039800Sbostic { 56139800Sbostic /* 56239800Sbostic * detection of symbolic links w/o targets. If FTS_FOLLOW is set, 56339800Sbostic * the symlink structure is overwritten with the stat structure of 56439800Sbostic * the target. 56539800Sbostic */ 56642273Sbostic if (stream->fts_options & FTS_LOGICAL || symflag) { 56742273Sbostic if (stat(p->fts_accpath, &p->fts_statb)) 56842273Sbostic return(symflag || !lstat(p->fts_accpath, 56942273Sbostic &p->fts_statb) ? FTS_SLNONE : FTS_ERR); 57042273Sbostic } else if (lstat(p->fts_accpath, &p->fts_statb)) 57139800Sbostic return(FTS_ERR); 57239800Sbostic 57342273Sbostic switch(p->fts_statb.st_mode&S_IFMT) { 57439800Sbostic case S_IFDIR: 57539800Sbostic return(FTS_D); 57639800Sbostic case S_IFLNK: 57739800Sbostic return(FTS_SL); 57839800Sbostic case S_IFREG: 57939800Sbostic return(FTS_F); 58039800Sbostic } 58140939Sbostic return(FTS_DEFAULT); 58239800Sbostic } 58339800Sbostic 58439800Sbostic static FTSENT * 58539800Sbostic fts_cycle(p) 58639800Sbostic register FTSENT *p; 58739800Sbostic { 58839800Sbostic register dev_t dev; 58939800Sbostic register ino_t ino; 59039800Sbostic 59139800Sbostic /* 59239800Sbostic * cycle detection is brute force; if the tree gets deep enough or 59339800Sbostic * the number of symbolic links to directories is really high 59439800Sbostic * something faster might be worthwhile. 59539800Sbostic */ 59642273Sbostic dev = p->fts_statb.st_dev; 59742273Sbostic ino = p->fts_statb.st_ino; 59842273Sbostic for (p = p->fts_parent; p->fts_level > ROOTLEVEL; p = p->fts_parent) 59942273Sbostic if (ino == p->fts_statb.st_ino && dev == p->fts_statb.st_dev) 60039800Sbostic return(p); 60139800Sbostic return(NULL); 60239800Sbostic } 60339800Sbostic 60439800Sbostic #define R(type, nelem, ptr) \ 605*42299Sbostic (type *)realloc((void *)ptr, (u_int)((nelem) * sizeof(type))) 60639800Sbostic 60739800Sbostic static FTSENT * 60839800Sbostic fts_sort(head, nitems) 60939800Sbostic FTSENT *head; 61039800Sbostic register int nitems; 61139800Sbostic { 61239800Sbostic register FTSENT **ap, *p; 61339800Sbostic 61439800Sbostic /* 61539800Sbostic * construct an array of pointers to the structures and call qsort(3). 61639800Sbostic * Reassemble the array in the order returned by qsort. If unable to 61739800Sbostic * sort for memory reasons, return the directory entries in their 61839800Sbostic * current order. Allocate enough space for the current needs plus 61939800Sbostic * 40 so we don't realloc one entry at a time. 62039800Sbostic */ 62142273Sbostic if (nitems > stream->fts_nitems) { 62242273Sbostic stream->fts_nitems = nitems + 40; 62342273Sbostic if (!(stream->fts_array = 62442273Sbostic R(FTSENT *, stream->fts_nitems, stream->fts_array))) { 62542273Sbostic stream->fts_nitems = 0; 62639800Sbostic return(head); 62739800Sbostic } 62839800Sbostic } 62942273Sbostic for (ap = stream->fts_array, p = head; p; p = p->fts_link) 63039800Sbostic *ap++ = p; 631*42299Sbostic qsort((void *)stream->fts_array, nitems, sizeof(FTSENT *), 63242273Sbostic stream->fts_compar); 63342273Sbostic for (head = *(ap = stream->fts_array); --nitems; ++ap) 63442273Sbostic ap[0]->fts_link = ap[1]; 63542273Sbostic ap[0]->fts_link = NULL; 63639800Sbostic return(head); 63739800Sbostic } 63839800Sbostic 63939800Sbostic static FTSENT * 64039800Sbostic fts_alloc(name, len) 64139800Sbostic char *name; 64239800Sbostic register int len; 64339800Sbostic { 64439800Sbostic register FTSENT *p; 64539800Sbostic 64639800Sbostic /* 64739800Sbostic * variable sized structures; the name is the last element so 64839800Sbostic * allocate enough extra space after the structure to hold it. 64939800Sbostic */ 650*42299Sbostic if (!(p = (FTSENT *)malloc((size_t)(sizeof(FTSENT) + len)))) 65139800Sbostic return(NULL); 65242273Sbostic bcopy(name, p->fts_name, len + 1); 65342273Sbostic p->fts_namelen = len; 65442273Sbostic p->fts_path = stream->fts_path; 65542273Sbostic p->fts_instr = 0; 65642273Sbostic p->fts_local.number = 0; 65742273Sbostic p->fts_local.pointer = NULL; 65839800Sbostic return(p); 65939800Sbostic } 66039800Sbostic 66139800Sbostic static 66239800Sbostic fts_lfree(head) 66339800Sbostic register FTSENT *head; 66439800Sbostic { 66539800Sbostic register FTSENT *p; 66639800Sbostic 66739800Sbostic while (p = head) { 66842273Sbostic head = head->fts_link; 669*42299Sbostic (void)free((void *)p); 67039800Sbostic } 67139800Sbostic } 67239800Sbostic 67339800Sbostic /* 67439800Sbostic * allow essentially unlimited paths; certain programs (find, remove, ls) 67539800Sbostic * need to work on any tree. Most systems will allow creation of paths 67639800Sbostic * much longer than MAXPATHLEN, even though the kernel won't resolve them. 67739800Sbostic * Add an extra 128 bytes to the requested size so that we don't realloc 67839800Sbostic * the path 2 bytes at a time. 67939800Sbostic */ 680*42299Sbostic static char * 68139800Sbostic fts_path(size) 68239800Sbostic int size; 68339800Sbostic { 68442273Sbostic stream->fts_pathlen += size + 128; 685*42299Sbostic return(stream->fts_path = 686*42299Sbostic R(char, stream->fts_pathlen, stream->fts_path)); 68739800Sbostic } 68839800Sbostic 68939800Sbostic static FTSENT * 69039800Sbostic fts_root(name) 69139800Sbostic register char *name; 69239800Sbostic { 69339800Sbostic register char *cp; 69439800Sbostic 69539800Sbostic /* 69639800Sbostic * rip trailing slashes; it's somewhat unclear in POSIX 1003.1 what 69739800Sbostic * /a/b/ really is, they don't talk about what a null path component 69839800Sbostic * resolves to. This hopefully does what the user intended. Don't 69939800Sbostic * allow null pathnames. 70039800Sbostic */ 70139800Sbostic for (cp = name; *cp; ++cp); 70239800Sbostic if (cp == name) { 70339800Sbostic errno = ENOENT; 70439800Sbostic return(NULL); 70539800Sbostic } 70639800Sbostic while (--cp > name && *cp == '/'); 70739800Sbostic *++cp = '\0'; 70839800Sbostic return(fts_alloc(name, cp - name)); 70939800Sbostic } 710