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*55557Sbostic static char sccsid[] = "@(#)fts.c 5.40 (Berkeley) 07/23/92"; 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> 1752209Sbostic #include <fts.h> 1847194Sbostic #include <stdlib.h> 1942273Sbostic #include <string.h> 2047155Sbostic #include <unistd.h> 2139800Sbostic 2252061Sbostic static FTSENT *fts_alloc __P((FTS *, char *, int)); 2352061Sbostic static FTSENT *fts_build __P((FTS *, int)); 2452061Sbostic static void fts_lfree __P((FTSENT *)); 2552061Sbostic static void fts_load __P((FTS *, FTSENT *)); 26*55557Sbostic static size_t fts_maxarglen __P((char * const *)); 2752209Sbostic static void fts_padjust __P((FTS *, void *)); 2853801Sbostic static int fts_palloc __P((FTS *, size_t)); 2952061Sbostic static FTSENT *fts_sort __P((FTS *, FTSENT *, int)); 3052061Sbostic static u_short fts_stat __P((FTS *, FTSENT *, int)); 3139800Sbostic 3252209Sbostic #define ISDOT(a) (a[0] == '.' && (!a[1] || a[1] == '.' && !a[2])) 3352209Sbostic 3445600Sbostic #define ISSET(opt) (sp->fts_options & opt) 3545600Sbostic #define SET(opt) (sp->fts_options |= opt) 3639800Sbostic 3745600Sbostic #define CHDIR(sp, path) (!ISSET(FTS_NOCHDIR) && chdir(path)) 3845600Sbostic #define FCHDIR(sp, fd) (!ISSET(FTS_NOCHDIR) && fchdir(fd)) 3939800Sbostic 4042299Sbostic /* fts_build flags */ 4152770Sbostic #define BCHILD 1 /* fts_children */ 4252770Sbostic #define BNAMES 2 /* fts_children, names only */ 4352770Sbostic #define BREAD 3 /* fts_read */ 4442299Sbostic 4539800Sbostic FTS * 4645600Sbostic fts_open(argv, options, compar) 4747155Sbostic char * const *argv; 4839800Sbostic register int options; 4947194Sbostic int (*compar)(); 5039800Sbostic { 5139800Sbostic register FTS *sp; 5239800Sbostic register FTSENT *p, *root; 53*55557Sbostic register int nitems; 5439800Sbostic FTSENT *parent, *tmp; 5547194Sbostic int len; 5639800Sbostic 5752770Sbostic /* Options check. */ 5852770Sbostic if (options & ~FTS_OPTIONMASK) { 5952770Sbostic errno = EINVAL; 6052770Sbostic return (NULL); 6152770Sbostic } 6252770Sbostic 6345600Sbostic /* Allocate/initialize the stream */ 6452150Sbostic if ((sp = malloc((u_int)sizeof(FTS))) == NULL) 6552150Sbostic return (NULL); 6639800Sbostic bzero(sp, sizeof(FTS)); 6742273Sbostic sp->fts_compar = compar; 6842273Sbostic sp->fts_options = options; 6939800Sbostic 7045600Sbostic /* Logical walks turn on NOCHDIR; symbolic links are too hard. */ 7145600Sbostic if (ISSET(FTS_LOGICAL)) 7245600Sbostic SET(FTS_NOCHDIR); 7345600Sbostic 7453801Sbostic /* 75*55557Sbostic * Start out with 1K of path space, and enough, in any case, 76*55557Sbostic * to hold the user's paths. 7753801Sbostic */ 78*55557Sbostic if (fts_palloc(sp, MAX(fts_maxarglen(argv), MAXPATHLEN))) 7953801Sbostic goto mem1; 8053801Sbostic 8145600Sbostic /* Allocate/initialize root's parent. */ 8252155Sbostic if ((parent = fts_alloc(sp, "", 0)) == NULL) 8353801Sbostic goto mem2; 8447212Sbostic parent->fts_level = FTS_ROOTPARENTLEVEL; 8539800Sbostic 8645600Sbostic /* Allocate/initialize root(s). */ 87*55557Sbostic for (root = NULL, nitems = 0; *argv; ++argv, ++nitems) { 8852160Sbostic /* Don't allow zero-length paths. */ 8952209Sbostic if ((len = strlen(*argv)) == 0) { 9047194Sbostic errno = ENOENT; 9153801Sbostic goto mem3; 9247194Sbostic } 9352209Sbostic 9447194Sbostic p = fts_alloc(sp, *argv, len); 9549519Sbostic p->fts_level = FTS_ROOTLEVEL; 9649519Sbostic p->fts_parent = parent; 9752218Sbostic p->fts_accpath = p->fts_name; 9852344Sbostic p->fts_info = fts_stat(sp, p, ISSET(FTS_COMFOLLOW)); 9952209Sbostic 10052209Sbostic /* Command-line "." and ".." are real directories. */ 10152209Sbostic if (p->fts_info == FTS_DOT) 10252209Sbostic p->fts_info = FTS_D; 10352209Sbostic 10443070Sbostic /* 10545600Sbostic * If comparison routine supplied, traverse in sorted 10643070Sbostic * order; otherwise traverse in the order specified. 10743070Sbostic */ 10843070Sbostic if (compar) { 10943070Sbostic p->fts_link = root; 11043070Sbostic root = p; 11143070Sbostic } else { 11243070Sbostic p->fts_link = NULL; 11352155Sbostic if (root == NULL) 11443070Sbostic tmp = root = p; 11543070Sbostic else { 11643070Sbostic tmp->fts_link = p; 11743070Sbostic tmp = p; 11839800Sbostic } 11939800Sbostic } 12039800Sbostic } 12143070Sbostic if (compar && nitems > 1) 12245600Sbostic root = fts_sort(sp, root, nitems); 12339800Sbostic 12442281Sbostic /* 12545600Sbostic * Allocate a dummy pointer and make fts_read think that we've just 12652160Sbostic * finished the node before the root(s); set p->fts_info to FTS_INIT 12742281Sbostic * so that everything about the "current" node is ignored. 12842281Sbostic */ 12952155Sbostic if ((sp->fts_cur = fts_alloc(sp, "", 0)) == NULL) 13053801Sbostic goto mem3; 13142281Sbostic sp->fts_cur->fts_link = root; 13252160Sbostic sp->fts_cur->fts_info = FTS_INIT; 13342281Sbostic 13452160Sbostic /* 13545600Sbostic * If using chdir(2), grab a file descriptor pointing to dot to insure 13642299Sbostic * that we can get back here; this could be avoided for some paths, 13742299Sbostic * but almost certainly not worth the effort. Slashes, symbolic links, 13842299Sbostic * and ".." are all fairly nasty problems. Note, if we can't get the 13942299Sbostic * descriptor we run anyway, just more slowly. 14039800Sbostic */ 14147194Sbostic if (!ISSET(FTS_NOCHDIR) && (sp->fts_rfd = open(".", O_RDONLY, 0)) < 0) 14245600Sbostic SET(FTS_NOCHDIR); 14339800Sbostic 14452150Sbostic return (sp); 14539800Sbostic 14653801Sbostic mem3: fts_lfree(root); 14747194Sbostic free(parent); 14853801Sbostic mem2: free(sp->fts_path); 14947194Sbostic mem1: free(sp); 15052150Sbostic return (NULL); 15139800Sbostic } 15239800Sbostic 15347404Sbostic static void 15445600Sbostic fts_load(sp, p) 15545600Sbostic FTS *sp; 15639800Sbostic register FTSENT *p; 15739800Sbostic { 15839800Sbostic register int len; 15939800Sbostic register char *cp; 16039800Sbostic 16139800Sbostic /* 16247220Sbostic * Load the stream structure for the next traversal. Since we don't 16347220Sbostic * actually enter the directory until after the preorder visit, set 16447220Sbostic * the fts_accpath field specially so the chdir gets done to the right 16552209Sbostic * place and the user can access the first node. From fts_open it's 16652209Sbostic * known that the path will fit. 16739800Sbostic */ 16842273Sbostic len = p->fts_pathlen = p->fts_namelen; 16945600Sbostic bcopy(p->fts_name, sp->fts_path, len + 1); 17042273Sbostic if ((cp = rindex(p->fts_name, '/')) && (cp != p->fts_name || cp[1])) { 17139800Sbostic len = strlen(++cp); 17242273Sbostic bcopy(cp, p->fts_name, len + 1); 17342273Sbostic p->fts_namelen = len; 17439800Sbostic } 17545600Sbostic p->fts_accpath = p->fts_path = sp->fts_path; 17652209Sbostic sp->fts_dev = p->fts_dev; 17739800Sbostic } 17839800Sbostic 17952209Sbostic int 18045600Sbostic fts_close(sp) 18139800Sbostic FTS *sp; 18239800Sbostic { 18339800Sbostic register FTSENT *freep, *p; 18439800Sbostic int saved_errno; 18539800Sbostic 18652209Sbostic /* 18752209Sbostic * This still works if we haven't read anything -- the dummy structure 18852209Sbostic * points to the root list, so we step through to the end of the root 18952209Sbostic * list which has a valid parent pointer. 19052209Sbostic */ 19142281Sbostic if (sp->fts_cur) { 19252209Sbostic for (p = sp->fts_cur; p->fts_level >= FTS_ROOTLEVEL;) { 19342281Sbostic freep = p; 19442281Sbostic p = p->fts_link ? p->fts_link : p->fts_parent; 19547194Sbostic free(freep); 19639800Sbostic } 19747194Sbostic free(p); 19842281Sbostic } 19939800Sbostic 20045600Sbostic /* Free up child linked list, sort array, path buffer. */ 20142273Sbostic if (sp->fts_child) 20242273Sbostic fts_lfree(sp->fts_child); 20342273Sbostic if (sp->fts_array) 20447194Sbostic free(sp->fts_array); 20547194Sbostic free(sp->fts_path); 20639800Sbostic 20747194Sbostic /* Return to original directory, save errno if necessary. */ 20845600Sbostic if (!ISSET(FTS_NOCHDIR)) { 20947194Sbostic saved_errno = fchdir(sp->fts_rfd) ? errno : 0; 21047194Sbostic (void)close(sp->fts_rfd); 21139800Sbostic } 21239800Sbostic 21345600Sbostic /* Free up the stream pointer. */ 21447194Sbostic free(sp); 21539800Sbostic 21645600Sbostic /* Set errno and return. */ 21745600Sbostic if (!ISSET(FTS_NOCHDIR) && saved_errno) { 21839800Sbostic errno = saved_errno; 21952150Sbostic return (-1); 22039800Sbostic } 22152150Sbostic return (0); 22239800Sbostic } 22339800Sbostic 22447194Sbostic /* 22552160Sbostic * Special case a root of "/" so that slashes aren't appended which would 22652160Sbostic * cause paths to be written as "//foo". 22747194Sbostic */ 228*55557Sbostic #define NAPPEND(p) \ 229*55557Sbostic (p->fts_level == FTS_ROOTLEVEL && p->fts_pathlen == 1 && \ 23047194Sbostic p->fts_path[0] == '/' ? 0 : p->fts_pathlen) 23147194Sbostic 23239800Sbostic FTSENT * 23345600Sbostic fts_read(sp) 23439800Sbostic register FTS *sp; 23539800Sbostic { 23642299Sbostic register FTSENT *p, *tmp; 23739800Sbostic register int instr; 23847194Sbostic register char *t; 23953032Sbostic int saved_errno; 24039800Sbostic 24145600Sbostic /* If finished or unrecoverable error, return NULL. */ 24252155Sbostic if (sp->fts_cur == NULL || ISSET(FTS_STOP)) 24352150Sbostic return (NULL); 24439800Sbostic 24545600Sbostic /* Set current node pointer. */ 24642273Sbostic p = sp->fts_cur; 24739800Sbostic 24845600Sbostic /* Save and zero out user instructions. */ 24942273Sbostic instr = p->fts_instr; 25047194Sbostic p->fts_instr = FTS_NOINSTR; 25139800Sbostic 25247194Sbostic /* Any type of file may be re-visited; re-stat and re-turn. */ 25339800Sbostic if (instr == FTS_AGAIN) { 25445600Sbostic p->fts_info = fts_stat(sp, p, 0); 25552150Sbostic return (p); 25639800Sbostic } 25739800Sbostic 25847194Sbostic /* 25947194Sbostic * Following a symlink -- SLNONE test allows application to see 26052290Sbostic * SLNONE and recover. If indirecting through a symlink, have 26152290Sbostic * keep a pointer to current location. If unable to get that 26252290Sbostic * pointer, follow fails. 26347194Sbostic */ 26447194Sbostic if (instr == FTS_FOLLOW && 26547194Sbostic (p->fts_info == FTS_SL || p->fts_info == FTS_SLNONE)) { 26645600Sbostic p->fts_info = fts_stat(sp, p, 1); 26752290Sbostic if (p->fts_info == FTS_D && !ISSET(FTS_NOCHDIR)) 26852290Sbostic if ((p->fts_symfd = open(".", O_RDONLY, 0)) < 0) { 26952290Sbostic p->fts_errno = errno; 27052290Sbostic p->fts_info = FTS_ERR; 27152290Sbostic } else 27252290Sbostic p->fts_flags |= FTS_SYMFOLLOW; 27352150Sbostic return (p); 27442299Sbostic } 27542299Sbostic 27645600Sbostic /* Directory in pre-order. */ 27742299Sbostic if (p->fts_info == FTS_D) { 27845600Sbostic /* If skipped or crossed mount point, do post-order visit. */ 27947194Sbostic if (instr == FTS_SKIP || 28052209Sbostic ISSET(FTS_XDEV) && p->fts_dev != sp->fts_dev) { 28152290Sbostic if (p->fts_flags & FTS_SYMFOLLOW) 28252290Sbostic (void)close(p->fts_symfd); 28342273Sbostic if (sp->fts_child) { 28442273Sbostic fts_lfree(sp->fts_child); 28542273Sbostic sp->fts_child = NULL; 28639800Sbostic } 28742301Sbostic p->fts_info = FTS_DP; 28852150Sbostic return (p); 28942299Sbostic } 29042299Sbostic 29153096Sbostic /* Rebuild if only read the names and now traversing. */ 29252770Sbostic if (sp->fts_child && sp->fts_options & FTS_NAMEONLY) { 29352770Sbostic sp->fts_options &= ~FTS_NAMEONLY; 29452770Sbostic fts_lfree(sp->fts_child); 29552770Sbostic sp->fts_child = NULL; 29652770Sbostic } 29752770Sbostic 29847194Sbostic /* 29953096Sbostic * Cd to the subdirectory. 30053096Sbostic * 30147220Sbostic * If have already read and now fail to chdir, whack the list 30253096Sbostic * to make the names come out right, and set the parent errno 30347220Sbostic * so the application will eventually get an error condition. 30453096Sbostic * Set the FTS_DONTCHDIR flag so that when we logically change 30553096Sbostic * directories back to the parent we don't do a chdir. 30653096Sbostic * 30753096Sbostic * If haven't read do so. If the read fails, fts_build sets 30853096Sbostic * FTS_STOP or the fts_info field of the node. 30947194Sbostic */ 31047194Sbostic if (sp->fts_child) { 31142299Sbostic if (CHDIR(sp, p->fts_accpath)) { 31252278Sbostic p->fts_errno = errno; 31352290Sbostic p->fts_flags |= FTS_DONTCHDIR; 31447194Sbostic for (p = sp->fts_child; p; p = p->fts_link) 31547194Sbostic p->fts_accpath = 31647194Sbostic p->fts_parent->fts_accpath; 31742299Sbostic } 31852155Sbostic } else if ((sp->fts_child = fts_build(sp, BREAD)) == NULL) { 31952290Sbostic if (ISSET(FTS_STOP)) 32052150Sbostic return (NULL); 32152150Sbostic return (p); 32247220Sbostic } 32347194Sbostic p = sp->fts_child; 32442299Sbostic sp->fts_child = NULL; 32547194Sbostic goto name; 32639800Sbostic } 32739800Sbostic 32852278Sbostic /* Move to the next node on this level. */ 32942299Sbostic next: tmp = p; 33042299Sbostic if (p = p->fts_link) { 33147194Sbostic free(tmp); 33247194Sbostic 33347194Sbostic /* If reached the top, load the paths for the next root. */ 33447212Sbostic if (p->fts_level == FTS_ROOTLEVEL) { 33547404Sbostic fts_load(sp, p); 33652150Sbostic return (sp->fts_cur = p); 33747194Sbostic } 33842299Sbostic 33952290Sbostic /* 34052290Sbostic * User may have called fts_set on the node. If skipped, 34152290Sbostic * ignore. If followed, get a file descriptor so we can 34252290Sbostic * get back if necessary. 34352290Sbostic */ 34447194Sbostic if (p->fts_instr == FTS_SKIP) 34547194Sbostic goto next; 34647194Sbostic if (p->fts_instr == FTS_FOLLOW) { 34747194Sbostic p->fts_info = fts_stat(sp, p, 1); 34852290Sbostic if (p->fts_info == FTS_D && !ISSET(FTS_NOCHDIR)) 34952290Sbostic if ((p->fts_symfd = 35052290Sbostic open(".", O_RDONLY, 0)) < 0) { 35152290Sbostic p->fts_errno = errno; 35252290Sbostic p->fts_info = FTS_ERR; 35352290Sbostic } else 35452290Sbostic p->fts_flags |= FTS_SYMFOLLOW; 35547194Sbostic p->fts_instr = FTS_NOINSTR; 35647194Sbostic } 35742299Sbostic 35847194Sbostic name: t = sp->fts_path + NAPPEND(p->fts_parent); 35947194Sbostic *t++ = '/'; 36047194Sbostic bcopy(p->fts_name, t, p->fts_namelen + 1); 36152150Sbostic return (sp->fts_cur = p); 36239800Sbostic } 36339800Sbostic 36447220Sbostic /* Move up to the parent node. */ 36542299Sbostic p = tmp->fts_parent; 36647194Sbostic free(tmp); 36742299Sbostic 36847212Sbostic if (p->fts_level == FTS_ROOTPARENTLEVEL) { 36939800Sbostic /* 37045600Sbostic * Done; free everything up and set errno to 0 so the user 37139800Sbostic * can distinguish between error and EOF. 37239800Sbostic */ 37347194Sbostic free(p); 37439800Sbostic errno = 0; 37552150Sbostic return (sp->fts_cur = NULL); 37639800Sbostic } 37739800Sbostic 37853096Sbostic /* Nul terminate the pathname. */ 37942273Sbostic sp->fts_path[p->fts_pathlen] = '\0'; 38047194Sbostic 38147194Sbostic /* 38253096Sbostic * Return to the parent directory. If at a root node or came through 38353096Sbostic * a symlink, go back through the file descriptor. Otherwise, cd up 38453096Sbostic * one directory. 38547194Sbostic */ 38647220Sbostic if (p->fts_level == FTS_ROOTLEVEL) { 38753096Sbostic if (!ISSET(FTS_NOCHDIR) && FCHDIR(sp, sp->fts_rfd)) { 38847220Sbostic SET(FTS_STOP); 38952150Sbostic return (NULL); 39047220Sbostic } 39152290Sbostic } else if (p->fts_flags & FTS_SYMFOLLOW) { 39252290Sbostic if (FCHDIR(sp, p->fts_symfd)) { 39353032Sbostic saved_errno = errno; 39452290Sbostic (void)close(p->fts_symfd); 39553032Sbostic errno = saved_errno; 39652290Sbostic SET(FTS_STOP); 39752290Sbostic return (NULL); 39852290Sbostic } 39952290Sbostic (void)close(p->fts_symfd); 40052290Sbostic } else if (!(p->fts_flags & FTS_DONTCHDIR)) { 40152278Sbostic if (CHDIR(sp, "..")) { 40252278Sbostic SET(FTS_STOP); 40352278Sbostic return (NULL); 40452278Sbostic } 40552278Sbostic } 40652290Sbostic p->fts_info = p->fts_errno ? FTS_ERR : FTS_DP; 40752150Sbostic return (sp->fts_cur = p); 40839800Sbostic } 40939800Sbostic 41039800Sbostic /* 41147220Sbostic * Fts_set takes the stream as an argument although it's not used in this 41239800Sbostic * implementation; it would be necessary if anyone wanted to add global 41345600Sbostic * semantics to fts using fts_set. An error return is allowed for similar 41445600Sbostic * reasons. 41539800Sbostic */ 41639800Sbostic /* ARGSUSED */ 41752209Sbostic int 41845600Sbostic fts_set(sp, p, instr) 41939800Sbostic FTS *sp; 42039800Sbostic FTSENT *p; 42139800Sbostic int instr; 42239800Sbostic { 42352770Sbostic if (instr && instr != FTS_AGAIN && instr != FTS_FOLLOW && 42452770Sbostic instr != FTS_NOINSTR && instr != FTS_SKIP) { 42552770Sbostic errno = EINVAL; 42652770Sbostic return (1); 42752770Sbostic } 42842273Sbostic p->fts_instr = instr; 42952150Sbostic return (0); 43039800Sbostic } 43139800Sbostic 43239800Sbostic FTSENT * 43352770Sbostic fts_children(sp, instr) 43439800Sbostic register FTS *sp; 43552770Sbostic int instr; 43639800Sbostic { 43742299Sbostic register FTSENT *p; 43842299Sbostic int fd; 43942299Sbostic 44052770Sbostic if (instr && instr != FTS_NAMEONLY) { 44152770Sbostic errno = EINVAL; 44252770Sbostic return (NULL); 44352770Sbostic } 44452770Sbostic 44545600Sbostic /* Set current node pointer. */ 44645600Sbostic p = sp->fts_cur; 44745600Sbostic 44839800Sbostic /* 44952160Sbostic * Errno set to 0 so user can distinguish empty directory from 45052160Sbostic * an error. 45139800Sbostic */ 45239800Sbostic errno = 0; 45352160Sbostic 45452160Sbostic /* Fatal errors stop here. */ 45552160Sbostic if (ISSET(FTS_STOP)) 45652150Sbostic return (NULL); 45745600Sbostic 45852160Sbostic /* Return logical hierarchy of user's arguments. */ 45952160Sbostic if (p->fts_info == FTS_INIT) 46052160Sbostic return (p->fts_link); 46152160Sbostic 46252290Sbostic /* 46352290Sbostic * If not a directory being visited in pre-order, stop here. Could 46452290Sbostic * allow FTS_DNR, assuming the user has fixed the problem, but the 46552290Sbostic * same effect is available with FTS_AGAIN. 46652290Sbostic */ 46752290Sbostic if (p->fts_info != FTS_D /* && p->fts_info != FTS_DNR */) 46852160Sbostic return (NULL); 46952160Sbostic 47045600Sbostic /* Free up any previous child list. */ 47142273Sbostic if (sp->fts_child) 47242273Sbostic fts_lfree(sp->fts_child); 47342299Sbostic 47452770Sbostic if (instr == FTS_NAMEONLY) { 47552770Sbostic sp->fts_options |= FTS_NAMEONLY; 47652770Sbostic instr = BNAMES; 47752770Sbostic } else 47852770Sbostic instr = BCHILD; 47952770Sbostic 48042299Sbostic /* 48145600Sbostic * If using chdir on a relative path and called BEFORE fts_read does 48247194Sbostic * its chdir to the root of a traversal, we can lose -- we need to 48347194Sbostic * chdir into the subdirectory, and we don't know where the current 48447194Sbostic * directory is, so we can't get back so that the upcoming chdir by 48547194Sbostic * fts_read will work. 48642299Sbostic */ 48747212Sbostic if (p->fts_level != FTS_ROOTLEVEL || p->fts_accpath[0] == '/' || 48845600Sbostic ISSET(FTS_NOCHDIR)) 48952770Sbostic return (sp->fts_child = fts_build(sp, instr)); 49042299Sbostic 49142299Sbostic if ((fd = open(".", O_RDONLY, 0)) < 0) 49252150Sbostic return (NULL); 49352770Sbostic sp->fts_child = fts_build(sp, instr); 49442299Sbostic if (fchdir(fd)) 49552150Sbostic return (NULL); 49642299Sbostic (void)close(fd); 49752150Sbostic return (sp->fts_child); 49839800Sbostic } 49939800Sbostic 50047194Sbostic /* 50147194Sbostic * This is the tricky part -- do not casually change *anything* in here. The 50247194Sbostic * idea is to build the linked list of entries that are used by fts_children 50347194Sbostic * and fts_read. There are lots of special cases. 50447194Sbostic * 50547194Sbostic * The real slowdown in walking the tree is the stat calls. If FTS_NOSTAT is 50647194Sbostic * set and it's a physical walk (so that symbolic links can't be directories), 50747194Sbostic * we assume that the number of subdirectories in a node is equal to the number 50847194Sbostic * of links to the parent. This allows stat calls to be skipped in any leaf 50947194Sbostic * directories and for any nodes after the directories in the parent node have 51047194Sbostic * been found. This empirically cuts the stat calls by about 2/3. 51147194Sbostic */ 51247155Sbostic static FTSENT * 51342299Sbostic fts_build(sp, type) 51439800Sbostic register FTS *sp; 51542299Sbostic int type; 51639800Sbostic { 51739800Sbostic register struct dirent *dp; 51839800Sbostic register FTSENT *p, *head; 51939800Sbostic register int nitems; 52052290Sbostic FTSENT *cur, *tail; 52139800Sbostic DIR *dirp; 52252209Sbostic void *adjaddr; 52352278Sbostic int cderrno, descend, len, level, maxlen, nlinks, saved_errno; 52439800Sbostic char *cp; 52539800Sbostic 52645600Sbostic /* Set current node pointer. */ 52747194Sbostic cur = sp->fts_cur; 52845600Sbostic 52947194Sbostic /* 53047194Sbostic * Open the directory for reading. If this fails, we're done. 53147194Sbostic * If being called from fts_read, set the fts_info field. 53247194Sbostic */ 53352155Sbostic if ((dirp = opendir(cur->fts_accpath)) == NULL) { 53452206Sbostic if (type == BREAD) { 53547194Sbostic cur->fts_info = FTS_DNR; 53652206Sbostic cur->fts_errno = errno; 53752206Sbostic } 53852150Sbostic return (NULL); 53939800Sbostic } 54039800Sbostic 54139800Sbostic /* 54247194Sbostic * Nlinks is the number of possible entries of type directory in the 54347194Sbostic * directory if we're cheating on stat calls, 0 if we're not doing 54447194Sbostic * any stat calls at all, -1 if we're doing stats on everything. 54539800Sbostic */ 54652770Sbostic if (type == BNAMES) 54752770Sbostic nlinks = 0; 54852770Sbostic else if (ISSET(FTS_NOSTAT) && ISSET(FTS_PHYSICAL)) 54952770Sbostic nlinks = cur->fts_nlink - (ISSET(FTS_SEEDOT) ? 0 : 2); 55052770Sbostic else 55152770Sbostic nlinks = -1; 55239800Sbostic 55352290Sbostic #ifdef notdef 55452290Sbostic (void)printf("nlinks == %d (cur: %d)\n", nlinks, cur->fts_nlink); 55552290Sbostic (void)printf("NOSTAT %d PHYSICAL %d SEEDOT %d\n", 55652290Sbostic ISSET(FTS_NOSTAT), ISSET(FTS_PHYSICAL), ISSET(FTS_SEEDOT)); 55752290Sbostic #endif 55847194Sbostic /* 55947194Sbostic * If we're going to need to stat anything or we want to descend 56053379Sbostic * and stay in the directory, chdir. If this fails we keep going, 56153379Sbostic * but set a flag so we don't chdir after the post-order visit. 56247194Sbostic * We won't be able to stat anything, but we can still return the 56347194Sbostic * names themselves. Note, that since fts_read won't be able to 56447194Sbostic * chdir into the directory, it will have to return different path 56547194Sbostic * names than before, i.e. "a/b" instead of "b". Since the node 56647194Sbostic * has already been visited in pre-order, have to wait until the 56752290Sbostic * post-order visit to return the error. There is a special case 56852290Sbostic * here, if there was nothing to stat then it's not an error to 56952290Sbostic * not be able to stat. This is all fairly nasty. If a program 57052290Sbostic * needed sorted entries or stat information, they had better be 57152290Sbostic * checking FTS_NS on the returned nodes. 57247194Sbostic */ 57345600Sbostic if (nlinks || type == BREAD) 57447194Sbostic if (FCHDIR(sp, dirfd(dirp))) { 57553380Sbostic if (nlinks && type == BREAD) 57652160Sbostic cur->fts_errno = errno; 57753380Sbostic cur->fts_flags |= FTS_DONTCHDIR; 57852290Sbostic descend = 0; 57952278Sbostic cderrno = errno; 58047194Sbostic } else { 58145600Sbostic descend = 1; 58252278Sbostic cderrno = 0; 58339962Sbostic } 58447194Sbostic else 58547194Sbostic descend = 0; 58639962Sbostic 58747194Sbostic /* 58847194Sbostic * Figure out the max file name length that can be stored in the 58947194Sbostic * current path -- the inner loop allocates more path as necessary. 59047194Sbostic * We really wouldn't have to do the maxlen calculations here, we 59147194Sbostic * could do them in fts_read before returning the path, but it's a 59247194Sbostic * lot easier here since the length is part of the dirent structure. 59347194Sbostic * 59452209Sbostic * If not changing directories set a pointer so that can just append 59552209Sbostic * each new name into the path. 59647194Sbostic */ 59747194Sbostic maxlen = sp->fts_pathlen - cur->fts_pathlen - 1; 59847194Sbostic len = NAPPEND(cur); 59947194Sbostic if (ISSET(FTS_NOCHDIR)) { 60047194Sbostic cp = sp->fts_path + len; 60147194Sbostic *cp++ = '/'; 60247194Sbostic } 60340939Sbostic 60447194Sbostic level = cur->fts_level + 1; 60540939Sbostic 60647194Sbostic /* Read the directory, attaching each entry to the `link' pointer. */ 60752209Sbostic adjaddr = NULL; 60852290Sbostic for (head = tail = NULL, nitems = 0; dp = readdir(dirp);) { 60947194Sbostic if (!ISSET(FTS_SEEDOT) && ISDOT(dp->d_name)) 61039800Sbostic continue; 61139800Sbostic 61252155Sbostic if ((p = fts_alloc(sp, dp->d_name, (int)dp->d_namlen)) == NULL) 61339800Sbostic goto mem1; 61439800Sbostic if (dp->d_namlen > maxlen) { 61553801Sbostic if (fts_palloc(sp, (size_t)dp->d_namlen)) { 61647194Sbostic /* 61747194Sbostic * No more memory for path or structures. Save 61847194Sbostic * errno, free up the current structure and the 61947194Sbostic * structures already allocated. 62047194Sbostic */ 62147194Sbostic mem1: saved_errno = errno; 62247194Sbostic if (p) 62347194Sbostic free(p); 62447194Sbostic fts_lfree(head); 62547194Sbostic (void)closedir(dirp); 62639800Sbostic errno = saved_errno; 62747194Sbostic cur->fts_info = FTS_ERR; 62847194Sbostic SET(FTS_STOP); 62952150Sbostic return (NULL); 63039800Sbostic } 63152209Sbostic adjaddr = sp->fts_path; 63242273Sbostic maxlen = sp->fts_pathlen - sp->fts_cur->fts_pathlen - 1; 63339800Sbostic } 63439800Sbostic 63542273Sbostic p->fts_pathlen = len + dp->d_namlen + 1; 63642273Sbostic p->fts_parent = sp->fts_cur; 63742273Sbostic p->fts_level = level; 63839800Sbostic 63952290Sbostic if (cderrno) { 64052290Sbostic if (nlinks) { 64152290Sbostic p->fts_info = FTS_NS; 64252290Sbostic p->fts_errno = cderrno; 64352290Sbostic } else 64452290Sbostic p->fts_info = FTS_NSOK; 64552290Sbostic p->fts_accpath = cur->fts_accpath; 64652290Sbostic } else if (nlinks) { 64747194Sbostic /* Build a file name for fts_stat to stat. */ 64847194Sbostic if (ISSET(FTS_NOCHDIR)) { 64947194Sbostic p->fts_accpath = p->fts_path; 65042273Sbostic bcopy(p->fts_name, cp, p->fts_namelen + 1); 65147194Sbostic } else 65247194Sbostic p->fts_accpath = p->fts_name; 65345600Sbostic p->fts_info = fts_stat(sp, p, 0); 65452209Sbostic if (nlinks > 0 && (p->fts_info == FTS_D || 65552209Sbostic p->fts_info == FTS_DC || p->fts_info == FTS_DOT)) 65639800Sbostic --nlinks; 65747194Sbostic } else { 65847194Sbostic p->fts_accpath = 65947194Sbostic ISSET(FTS_NOCHDIR) ? p->fts_path : p->fts_name; 66047194Sbostic p->fts_info = FTS_NSOK; 66147194Sbostic } 66239800Sbostic 66352290Sbostic /* We walk in directory order so "ls -f" doesn't get upset. */ 66452290Sbostic p->fts_link = NULL; 66552290Sbostic if (head == NULL) 66652290Sbostic head = tail = p; 66752290Sbostic else { 66852290Sbostic tail->fts_link = p; 66952290Sbostic tail = p; 67052290Sbostic } 67139800Sbostic ++nitems; 67239800Sbostic } 67339800Sbostic (void)closedir(dirp); 67439800Sbostic 67547194Sbostic /* 67652209Sbostic * If had to realloc the path, adjust the addresses for the rest 67752209Sbostic * of the tree. 67852209Sbostic */ 67952209Sbostic if (adjaddr) 68052209Sbostic fts_padjust(sp, adjaddr); 68152209Sbostic 68252209Sbostic /* 68347194Sbostic * If not changing directories, reset the path back to original 68447194Sbostic * state. 68547194Sbostic */ 68647194Sbostic if (ISSET(FTS_NOCHDIR)) { 68747194Sbostic if (cp - 1 > sp->fts_path) 68847194Sbostic --cp; 68947194Sbostic *cp = '\0'; 69047194Sbostic } 69142299Sbostic 69242299Sbostic /* 69347194Sbostic * If descended after called from fts_children or called from 69447194Sbostic * fts_read and didn't find anything, get back. If can't get 69552209Sbostic * back, done. 69642299Sbostic */ 69742299Sbostic if (descend && (!nitems || type == BCHILD) && CHDIR(sp, "..")) { 69847194Sbostic cur->fts_info = FTS_ERR; 69947194Sbostic SET(FTS_STOP); 70052150Sbostic return (NULL); 70139962Sbostic } 70239962Sbostic 70353032Sbostic /* If didn't find anything, return NULL. */ 70453096Sbostic if (!nitems) { 70553096Sbostic if (type == BREAD) 70653096Sbostic cur->fts_info = FTS_DP; 70752150Sbostic return (NULL); 70853096Sbostic } 70939800Sbostic 71047194Sbostic /* Sort the entries. */ 71142273Sbostic if (sp->fts_compar && nitems > 1) 71245600Sbostic head = fts_sort(sp, head, nitems); 71352150Sbostic return (head); 71439800Sbostic } 71539800Sbostic 71645600Sbostic static u_short 71745600Sbostic fts_stat(sp, p, follow) 71845600Sbostic FTS *sp; 71939800Sbostic register FTSENT *p; 72045600Sbostic int follow; 72139800Sbostic { 72252160Sbostic register FTSENT *t; 72352160Sbostic register dev_t dev; 72452160Sbostic register ino_t ino; 72552209Sbostic struct stat *sbp, sb; 72647194Sbostic int saved_errno; 72747194Sbostic 72852209Sbostic /* If user needs stat info, stat buffer already allocated. */ 72952209Sbostic sbp = ISSET(FTS_NOSTAT) ? &sb : p->fts_statp; 73052209Sbostic 73139800Sbostic /* 73245600Sbostic * If doing a logical walk, or application requested FTS_FOLLOW, do 73347194Sbostic * a stat(2). If that fails, check for a non-existent symlink. If 73452160Sbostic * fail, set the errno from the stat call. 73539800Sbostic */ 73645600Sbostic if (ISSET(FTS_LOGICAL) || follow) { 73752209Sbostic if (stat(p->fts_accpath, sbp)) { 73847194Sbostic saved_errno = errno; 73952209Sbostic if (!lstat(p->fts_accpath, sbp)) { 74047194Sbostic errno = 0; 74152150Sbostic return (FTS_SLNONE); 74247194Sbostic } 74352160Sbostic p->fts_errno = saved_errno; 74452209Sbostic goto err; 74545600Sbostic } 74652209Sbostic } else if (lstat(p->fts_accpath, sbp)) { 74752160Sbostic p->fts_errno = errno; 74852209Sbostic err: bzero(sbp, sizeof(struct stat)); 74952150Sbostic return (FTS_NS); 75045600Sbostic } 75139800Sbostic 75252209Sbostic if (S_ISDIR(sbp->st_mode)) { 75352209Sbostic /* 75452209Sbostic * Set the device/inode. Used to find cycles and check for 75552209Sbostic * crossing mount points. Also remember the link count, used 75652209Sbostic * in fts_build to limit the number of stat calls. It is 75752209Sbostic * understood that these fields are only referenced if fts_info 75852209Sbostic * is set to FTS_D. 75952209Sbostic */ 76052209Sbostic dev = p->fts_dev = sbp->st_dev; 76152209Sbostic ino = p->fts_ino = sbp->st_ino; 76252209Sbostic p->fts_nlink = sbp->st_nlink; 76352209Sbostic 76452290Sbostic if (ISDOT(p->fts_name)) 76552290Sbostic return (FTS_DOT); 76652290Sbostic 76752209Sbostic /* 76852209Sbostic * Cycle detection is done by brute force when the directory 76952209Sbostic * is first encountered. If the tree gets deep enough or the 77052209Sbostic * number of symbolic links to directories is high enough, 77152209Sbostic * something faster might be worthwhile. 77252209Sbostic */ 77352209Sbostic for (t = p->fts_parent; 77452209Sbostic t->fts_level >= FTS_ROOTLEVEL; t = t->fts_parent) 77552209Sbostic if (ino == t->fts_ino && dev == t->fts_dev) { 77652073Sbostic p->fts_cycle = t; 77752150Sbostic return (FTS_DC); 77847194Sbostic } 77952150Sbostic return (FTS_D); 78047194Sbostic } 78152209Sbostic if (S_ISLNK(sbp->st_mode)) 78252150Sbostic return (FTS_SL); 78352209Sbostic if (S_ISREG(sbp->st_mode)) 78452150Sbostic return (FTS_F); 78552150Sbostic return (FTS_DEFAULT); 78639800Sbostic } 78739800Sbostic 78839800Sbostic static FTSENT * 78945600Sbostic fts_sort(sp, head, nitems) 79045600Sbostic FTS *sp; 79139800Sbostic FTSENT *head; 79239800Sbostic register int nitems; 79339800Sbostic { 79439800Sbostic register FTSENT **ap, *p; 79539800Sbostic 79639800Sbostic /* 79745600Sbostic * Construct an array of pointers to the structures and call qsort(3). 79839800Sbostic * Reassemble the array in the order returned by qsort. If unable to 79939800Sbostic * sort for memory reasons, return the directory entries in their 80039800Sbostic * current order. Allocate enough space for the current needs plus 80152209Sbostic * 40 so don't realloc one entry at a time. 80239800Sbostic */ 80345600Sbostic if (nitems > sp->fts_nitems) { 80445600Sbostic sp->fts_nitems = nitems + 40; 80552209Sbostic if ((sp->fts_array = realloc(sp->fts_array, 80652209Sbostic (size_t)(sp->fts_nitems * sizeof(FTSENT *)))) == NULL) { 80745600Sbostic sp->fts_nitems = 0; 80852150Sbostic return (head); 80939800Sbostic } 81039800Sbostic } 81145600Sbostic for (ap = sp->fts_array, p = head; p; p = p->fts_link) 81239800Sbostic *ap++ = p; 81345600Sbostic qsort((void *)sp->fts_array, nitems, sizeof(FTSENT *), sp->fts_compar); 81445600Sbostic for (head = *(ap = sp->fts_array); --nitems; ++ap) 81542273Sbostic ap[0]->fts_link = ap[1]; 81642273Sbostic ap[0]->fts_link = NULL; 81752150Sbostic return (head); 81839800Sbostic } 81939800Sbostic 82039800Sbostic static FTSENT * 82153801Sbostic fts_alloc(sp, name, namelen) 82245600Sbostic FTS *sp; 82339800Sbostic char *name; 82453801Sbostic register int namelen; 82539800Sbostic { 82639800Sbostic register FTSENT *p; 82753801Sbostic size_t len; 82839800Sbostic 82939800Sbostic /* 83053801Sbostic * The file name is a variable length array and no stat structure is 83153801Sbostic * necessary if the user has set the nostat bit. Allocate the FTSENT 83253801Sbostic * structure, the file name and the stat structure in one chunk, but 83353801Sbostic * be careful that the stat structure is reasonably aligned. Since the 83453801Sbostic * fts_name field is declared to be of size 1, the fts_name pointer is 83553801Sbostic * namelen + 2 before the first possible address of the stat structure. 83639800Sbostic */ 83753801Sbostic len = sizeof(FTSENT) + namelen; 83853801Sbostic if (!ISSET(FTS_NOSTAT)) 83953801Sbostic len += sizeof(struct stat) + ALIGNBYTES; 84053801Sbostic if ((p = malloc(len)) == NULL) 84152150Sbostic return (NULL); 84253801Sbostic 84353801Sbostic /* Copy the name plus the trailing NULL. */ 84453801Sbostic bcopy(name, p->fts_name, namelen + 1); 84553801Sbostic 84653801Sbostic if (!ISSET(FTS_NOSTAT)) 84753801Sbostic p->fts_statp = (struct stat *)ALIGN(p->fts_name + namelen + 2); 84853801Sbostic p->fts_namelen = namelen; 84945600Sbostic p->fts_path = sp->fts_path; 85052290Sbostic p->fts_errno = 0; 85152290Sbostic p->fts_flags = 0; 85247194Sbostic p->fts_instr = FTS_NOINSTR; 85345600Sbostic p->fts_number = 0; 85445600Sbostic p->fts_pointer = NULL; 85552150Sbostic return (p); 85639800Sbostic } 85739800Sbostic 85845600Sbostic static void 85939800Sbostic fts_lfree(head) 86039800Sbostic register FTSENT *head; 86139800Sbostic { 86239800Sbostic register FTSENT *p; 86339800Sbostic 86445600Sbostic /* Free a linked list of structures. */ 86539800Sbostic while (p = head) { 86642273Sbostic head = head->fts_link; 86747194Sbostic free(p); 86839800Sbostic } 86939800Sbostic } 87039800Sbostic 87139800Sbostic /* 87252209Sbostic * Allow essentially unlimited paths; find, rm, ls should all work on any tree. 87352209Sbostic * Most systems will allow creation of paths much longer than MAXPATHLEN, even 87452209Sbostic * though the kernel won't resolve them. Add the size (not just what's needed) 87552209Sbostic * plus 256 bytes so don't realloc the path 2 bytes at a time. 87639800Sbostic */ 87752209Sbostic static int 87852209Sbostic fts_palloc(sp, more) 87945600Sbostic FTS *sp; 88053801Sbostic size_t more; 88139800Sbostic { 88252209Sbostic sp->fts_pathlen += more + 256; 88352209Sbostic sp->fts_path = realloc(sp->fts_path, (size_t)sp->fts_pathlen); 88452209Sbostic return (sp->fts_path == NULL); 88547155Sbostic } 88652209Sbostic 88752209Sbostic /* 88852209Sbostic * When the path is realloc'd, have to fix all of the pointers in structures 88952209Sbostic * already returned. 89052209Sbostic */ 89152209Sbostic static void 89252209Sbostic fts_padjust(sp, addr) 89352209Sbostic FTS *sp; 89452209Sbostic void *addr; 89552209Sbostic { 89652209Sbostic FTSENT *p; 89752209Sbostic 898*55557Sbostic #define ADJUST(p) { \ 899*55557Sbostic (p)->fts_accpath = addr + ((p)->fts_accpath - (p)->fts_path); \ 900*55557Sbostic (p)->fts_path = addr; \ 90152209Sbostic } 90252209Sbostic /* Adjust the current set of children. */ 90352209Sbostic for (p = sp->fts_child; p; p = p->fts_link) 90452209Sbostic ADJUST(p); 90552209Sbostic 90652209Sbostic /* Adjust the rest of the tree. */ 90752209Sbostic for (p = sp->fts_cur; p->fts_level >= FTS_ROOTLEVEL;) { 90852209Sbostic ADJUST(p); 90952209Sbostic p = p->fts_link ? p->fts_link : p->fts_parent; 91052209Sbostic } 91152209Sbostic } 912*55557Sbostic 913*55557Sbostic static size_t 914*55557Sbostic fts_maxarglen(argv) 915*55557Sbostic char * const *argv; 916*55557Sbostic { 917*55557Sbostic size_t len, max; 918*55557Sbostic 919*55557Sbostic for (max = 0; *argv; ++argv) 920*55557Sbostic if ((len = strlen(*argv)) > max) 921*55557Sbostic max = len; 922*55557Sbostic return (max); 923*55557Sbostic } 924