xref: /csrg-svn/lib/libc/gen/fts.c (revision 45600)
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*45600Sbostic static char sccsid[] = "@(#)fts.c	5.11 (Berkeley) 11/14/90";
1039800Sbostic #endif /* LIBC_SCCS and not lint */
1139800Sbostic 
1239800Sbostic #include <sys/param.h>
1339800Sbostic #include <sys/stat.h>
1442299Sbostic #include <fcntl.h>
1539800Sbostic #include <dirent.h>
1639800Sbostic #include <errno.h>
1739800Sbostic #include <fts.h>
1842273Sbostic #include <string.h>
1942281Sbostic #include <stdlib.h>
2039800Sbostic 
21*45600Sbostic FTSENT *fts_alloc(), *fts_build(), *fts_cycle(), *fts_root(), *fts_sort();
22*45600Sbostic void fts_lfree(), fts_load();
23*45600Sbostic u_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 
33*45600Sbostic #define	ISSET(opt)	(sp->fts_options & opt)
34*45600Sbostic #define	SET(opt)	(sp->fts_options |= opt)
3539800Sbostic 
36*45600Sbostic #define	CHDIR(sp, path)	(!ISSET(FTS_NOCHDIR) && chdir(path))
37*45600Sbostic #define	FCHDIR(sp, fd)	(!ISSET(FTS_NOCHDIR) && fchdir(fd))
3839800Sbostic 
3942299Sbostic /* fts_build flags */
40*45600Sbostic #define	BCHILD		1		/* from fts_children */
41*45600Sbostic #define	BREAD		2		/* from fts_read */
4242299Sbostic 
43*45600Sbostic /* fts_level values */
44*45600Sbostic #define	ROOTLEVEL	0
45*45600Sbostic #define	ROOTPARENTLEVEL	-1
4639800Sbostic 
4739800Sbostic FTS *
48*45600Sbostic fts_open(argv, options, compar)
4939800Sbostic 	char *argv[];
5039800Sbostic 	register int options;
5139800Sbostic 	int (*compar)();
5239800Sbostic {
5339800Sbostic 	register FTS *sp;
5439800Sbostic 	register FTSENT *p, *root;
5539800Sbostic 	register int nitems, maxlen;
5639800Sbostic 	FTSENT *parent, *tmp;
5742299Sbostic 	char *fts_path();
5839800Sbostic 
59*45600Sbostic 	/* Allocate/initialize the stream */
60*45600Sbostic 	if (!(sp = (FTS *)malloc((u_int)sizeof(FTS))))
6139800Sbostic 		return(NULL);
6239800Sbostic 	bzero(sp, sizeof(FTS));
6342273Sbostic 	sp->fts_compar = compar;
6442273Sbostic 	sp->fts_options = options;
6539800Sbostic 
66*45600Sbostic 	/* Logical walks turn on NOCHDIR; symbolic links are too hard. */
67*45600Sbostic 	if (ISSET(FTS_LOGICAL))
68*45600Sbostic 		SET(FTS_NOCHDIR);
69*45600Sbostic 
70*45600Sbostic 	/* Allocate/initialize root's parent. */
71*45600Sbostic 	if (!(parent = fts_alloc(sp, "", 0)))
7239800Sbostic 		goto mem1;
7342273Sbostic 	parent->fts_level = ROOTPARENTLEVEL;
7439800Sbostic 
75*45600Sbostic 	/* Allocate/initialize root(s). */
7643070Sbostic 	maxlen = -1;
7743070Sbostic 	for (root = NULL, nitems = 0; *argv; ++argv, ++nitems) {
78*45600Sbostic 		if (!(p = fts_root(sp, *argv)))
7943070Sbostic 			goto mem2;
8043070Sbostic 		if (maxlen < p->fts_namelen)
8143070Sbostic 			maxlen = p->fts_namelen;
8243070Sbostic 		/*
83*45600Sbostic 		 * If comparison routine supplied, traverse in sorted
8443070Sbostic 		 * order; otherwise traverse in the order specified.
8543070Sbostic 		 */
8643070Sbostic 		if (compar) {
8743070Sbostic 			p->fts_link = root;
8843070Sbostic 			root = p;
8943070Sbostic 			p->fts_accpath = p->fts_name;
9043075Sbostic 			if (!(options & FTS_NOSTAT))
91*45600Sbostic 				p->fts_info = fts_stat(sp, p, 0);
9243070Sbostic 		} else {
9343070Sbostic 			p->fts_link = NULL;
9443070Sbostic 			if (!root)
9543070Sbostic 				tmp = root = p;
9643070Sbostic 			else {
9743070Sbostic 				tmp->fts_link = p;
9843070Sbostic 				tmp = p;
9939800Sbostic 			}
10039800Sbostic 		}
10143070Sbostic 		p->fts_level = ROOTLEVEL;
10243070Sbostic 		p->fts_parent = parent;
10339800Sbostic 	}
10443070Sbostic 	if (compar && nitems > 1)
105*45600Sbostic 		root = fts_sort(sp, root, nitems);
10639800Sbostic 
10742281Sbostic 	/*
108*45600Sbostic 	 * Allocate a dummy pointer and make fts_read think that we've just
10942281Sbostic 	 * finished the node before the root(s); set p->fts_info to FTS_NS
11042281Sbostic 	 * so that everything about the "current" node is ignored.
11142281Sbostic 	 */
112*45600Sbostic 	if (!(sp->fts_cur = fts_alloc(sp, "", 0)))
11342281Sbostic 		goto mem2;
11442281Sbostic 	sp->fts_cur->fts_link = root;
11542281Sbostic 	sp->fts_cur->fts_info = FTS_NS;
11642281Sbostic 
117*45600Sbostic 	/* Start out with at least 1K+ of path space. */
118*45600Sbostic 	if (!fts_path(sp, MAX(maxlen, MAXPATHLEN)))
11942281Sbostic 		goto mem3;
12039800Sbostic 
12139800Sbostic 	/*
122*45600Sbostic 	 * If using chdir(2), grab a file descriptor pointing to dot to insure
12342299Sbostic 	 * that we can get back here; this could be avoided for some paths,
12442299Sbostic 	 * but almost certainly not worth the effort.  Slashes, symbolic links,
12542299Sbostic 	 * and ".." are all fairly nasty problems.  Note, if we can't get the
12642299Sbostic 	 * descriptor we run anyway, just more slowly.
12739800Sbostic 	 */
128*45600Sbostic 	if (!ISSET(FTS_NOCHDIR) && (sp->fts_sd = open(".", O_RDONLY, 0)) < 0)
129*45600Sbostic 		SET(FTS_NOCHDIR);
13039800Sbostic 
13139800Sbostic 	return(sp);
13239800Sbostic 
133*45600Sbostic mem3:	free((void *)sp->fts_cur);
13439800Sbostic mem2:	fts_lfree(root);
135*45600Sbostic 	free((void *)parent);
136*45600Sbostic mem1:	free((void *)sp);
13739800Sbostic 	return(NULL);
13839800Sbostic }
13939800Sbostic 
140*45600Sbostic static void
141*45600Sbostic fts_load(sp, p)
142*45600Sbostic 	FTS *sp;
14339800Sbostic 	register FTSENT *p;
14439800Sbostic {
14539800Sbostic 	register int len;
14639800Sbostic 	register char *cp;
14739800Sbostic 
14839800Sbostic 	/*
149*45600Sbostic 	 * Load the stream structure for the next traversal; set the
150*45600Sbostic 	 * fts_accpath field specially so the chdir gets done to the
151*45600Sbostic 	 * right place and the user can access the first node.
15239800Sbostic 	 */
15342273Sbostic 	len = p->fts_pathlen = p->fts_namelen;
154*45600Sbostic 	bcopy(p->fts_name, sp->fts_path, len + 1);
15542273Sbostic 	if ((cp = rindex(p->fts_name, '/')) && (cp != p->fts_name || cp[1])) {
15639800Sbostic 		len = strlen(++cp);
15742273Sbostic 		bcopy(cp, p->fts_name, len + 1);
15842273Sbostic 		p->fts_namelen = len;
15939800Sbostic 	}
160*45600Sbostic 	p->fts_accpath = p->fts_path = sp->fts_path;
16139800Sbostic }
16239800Sbostic 
163*45600Sbostic fts_close(sp)
16439800Sbostic 	FTS *sp;
16539800Sbostic {
16639800Sbostic 	register FTSENT *freep, *p;
16739800Sbostic 	int saved_errno;
16839800Sbostic 
16942281Sbostic 	if (sp->fts_cur) {
17042281Sbostic 		/*
171*45600Sbostic 		 * This still works if we haven't read anything -- the dummy
17242281Sbostic 		 * structure points to the root list, so we step through to
17342281Sbostic 		 * the end of the root list which has a valid parent pointer.
17442281Sbostic 		 */
17542281Sbostic 		for (p = sp->fts_cur; p->fts_level > ROOTPARENTLEVEL;) {
17642281Sbostic 			freep = p;
17742281Sbostic 			p = p->fts_link ? p->fts_link : p->fts_parent;
178*45600Sbostic 			free((void *)freep);
17939800Sbostic 		}
180*45600Sbostic 		free((void *)p);
18142281Sbostic 	}
18239800Sbostic 
183*45600Sbostic 	/* Free up child linked list, sort array, path buffer. */
18442273Sbostic 	if (sp->fts_child)
18542273Sbostic 		fts_lfree(sp->fts_child);
18642273Sbostic 	if (sp->fts_array)
187*45600Sbostic 		free((void *)sp->fts_array);
188*45600Sbostic 	free((char *)sp->fts_path);
18939800Sbostic 
19039800Sbostic 	/*
191*45600Sbostic 	 * Return to original directory, save errno if necessary; free up
192*45600Sbostic 	 * the directory buffer.
19339800Sbostic 	 */
194*45600Sbostic 	if (!ISSET(FTS_NOCHDIR)) {
19542299Sbostic 		saved_errno = fchdir(sp->fts_sd) ? errno : 0;
19642299Sbostic 		(void)close(sp->fts_sd);
19739800Sbostic 	}
19839800Sbostic 
199*45600Sbostic 	/* Free up the stream pointer. */
200*45600Sbostic 	free((void *)sp);
20139800Sbostic 
202*45600Sbostic 	/* Set errno and return. */
203*45600Sbostic 	if (!ISSET(FTS_NOCHDIR) && saved_errno) {
20439800Sbostic 		errno = saved_errno;
20539800Sbostic 		return(-1);
20639800Sbostic 	}
20739800Sbostic 	return(0);
20839800Sbostic }
20939800Sbostic 
21039800Sbostic FTSENT *
211*45600Sbostic fts_read(sp)
21239800Sbostic 	register FTS *sp;
21339800Sbostic {
21442299Sbostic 	register FTSENT *p, *tmp;
21539800Sbostic 	register int instr;
21642299Sbostic 	register char *cp;
21739800Sbostic 	static int cd;
21839800Sbostic 
219*45600Sbostic 	/* If finished or unrecoverable error, return NULL. */
220*45600Sbostic 	if (!sp->fts_cur || ISSET(FTS__STOP))
22139800Sbostic 		return(NULL);
22239800Sbostic 
223*45600Sbostic 	/* Set current node pointer. */
22442273Sbostic 	p = sp->fts_cur;
22539800Sbostic 
226*45600Sbostic 	/* Save and zero out user instructions. */
22742273Sbostic 	instr = p->fts_instr;
228*45600Sbostic 	p->fts_instr = FTS__NOINSTR;
22939800Sbostic 
230*45600Sbostic 	/* If used fts_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 
236*45600Sbostic 	/* Any type of file may be re-visited; re-stat and return. */
23739800Sbostic 	if (instr == FTS_AGAIN) {
238*45600Sbostic 		p->fts_info = fts_stat(sp, p, 0);
23939800Sbostic 		return(p);
24039800Sbostic 	}
24139800Sbostic 
242*45600Sbostic 	/* Following a symbolic link. */
24342299Sbostic 	if (p->fts_info == FTS_SL && instr == FTS_FOLLOW) {
244*45600Sbostic 		p->fts_info = fts_stat(sp, p, 1);
24542299Sbostic 		return(p);
24642299Sbostic 	}
24742299Sbostic 
248*45600Sbostic 	/* Directory in pre-order. */
24942299Sbostic 	if (p->fts_info == FTS_D) {
250*45600Sbostic 		/* If skipped or crossed mount point, do post-order visit. */
251*45600Sbostic 		if (instr == FTS_SKIP || ISSET(FTS_XDEV) &&
25242280Sbostic 		    p->fts_statb.st_dev != sp->sdev) {
25342273Sbostic 			if (sp->fts_child) {
25442273Sbostic 				fts_lfree(sp->fts_child);
25542273Sbostic 				sp->fts_child = NULL;
25639800Sbostic 			}
25742301Sbostic 			p->fts_info = FTS_DP;
25842301Sbostic 			return(p);
25942299Sbostic 		}
26042299Sbostic 
261*45600Sbostic 		/* Read the directory if necessary, and return first entry. */
26242299Sbostic 		if (sp->fts_child)
26342299Sbostic 			if (CHDIR(sp, p->fts_accpath)) {
26442299Sbostic 				fts_lfree(sp->fts_child);
26542299Sbostic 				p->fts_info = FTS_DNX;
26642299Sbostic 			} else {
26742299Sbostic 				p = sp->fts_child;
26842299Sbostic 				cp = sp->fts_path + NAPPEND(p->fts_parent);
26942299Sbostic 				*cp++ = '/';
27042299Sbostic 				bcopy(p->fts_name, cp, p->fts_namelen + 1);
27142299Sbostic 			}
27242299Sbostic 		else {
27342299Sbostic 			if (!(sp->fts_child = fts_build(sp, BREAD)))
27439800Sbostic 				return(p);
27542273Sbostic 			p = sp->fts_child;
27639800Sbostic 		}
27742299Sbostic 		sp->fts_child = NULL;
27842299Sbostic 		return(sp->fts_cur = p);
27939800Sbostic 	}
28039800Sbostic 
281*45600Sbostic 	/* Move to next node on this level. */
28242299Sbostic next:	tmp = p;
28342299Sbostic 	if (p = p->fts_link) {
28439800Sbostic 		/*
285*45600Sbostic 		 * If root level node, set up paths and return.  If not the
28639800Sbostic 		 * first time, and it's not an absolute pathname, get back
287*45600Sbostic 		 * to starting directory.  If that fails, we're dead.
28839800Sbostic 		 */
28942273Sbostic 		if (p->fts_level == ROOTLEVEL) {
290*45600Sbostic 			fts_load(sp, p);
291*45600Sbostic 			free((void *)tmp);
29242281Sbostic 			if (cd &&
29342299Sbostic 			    p->fts_path[0] != '/' && FCHDIR(sp, sp->fts_sd)) {
294*45600Sbostic 				/* Can't get back to start; we're dead. */
29542299Sbostic 				p->fts_path = "starting directory";
29642281Sbostic 				p->fts_info = FTS_ERR;
297*45600Sbostic 				SET(FTS__STOP);
29842281Sbostic 				return(sp->fts_cur = p);
29942299Sbostic 			}
30042281Sbostic 			cd = 1;
301*45600Sbostic 			p->fts_info = fts_stat(sp, p, 0);
30242280Sbostic 			sp->sdev = p->fts_statb.st_dev;
30339800Sbostic 		} else {
304*45600Sbostic 			free((void *)tmp);
30542299Sbostic 
306*45600Sbostic 			/* User may have called fts_set on node. */
30742299Sbostic 			if (p->fts_instr == FTS_SKIP)
30842299Sbostic 				goto next;
30942299Sbostic 			if (p->fts_instr == FTS_FOLLOW) {
310*45600Sbostic 				p->fts_info = fts_stat(sp, p, 1);
311*45600Sbostic 				p->fts_instr = FTS__NOINSTR;
31242299Sbostic 			}
31342299Sbostic 
314*45600Sbostic 			/* Fill in the paths. */
31542273Sbostic 			cp = sp->fts_path + NAPPEND(p->fts_parent);
31639800Sbostic 			*cp++ = '/';
31742273Sbostic 			bcopy(p->fts_name, cp, p->fts_namelen + 1);
31842299Sbostic 
319*45600Sbostic 			/* Check for directory cycles. */
32042273Sbostic 			if (p->fts_info == FTS_D && (tmp = fts_cycle(p))) {
32142273Sbostic 				sp->fts_savelink = p->fts_link;
32242273Sbostic 				p->fts_link = tmp;
32342299Sbostic 				p->fts_info = FTS_DC;
32439800Sbostic 			}
32539800Sbostic 		}
32642273Sbostic 		return(sp->fts_cur = p);
32739800Sbostic 	}
32839800Sbostic 
329*45600Sbostic 	/* Move to parent. */
33042299Sbostic 	p = tmp->fts_parent;
331*45600Sbostic 	free((void *)tmp);
33242299Sbostic 
33342273Sbostic 	if (p->fts_level == ROOTPARENTLEVEL) {
33439800Sbostic 		/*
335*45600Sbostic 		 * Done; free everything up and set errno to 0 so the user
33639800Sbostic 		 * can distinguish between error and EOF.
33739800Sbostic 		 */
338*45600Sbostic 		free((void *)p);
33939800Sbostic 		errno = 0;
34042273Sbostic 		return(sp->fts_cur = NULL);
34139800Sbostic 	}
34239800Sbostic 
34342273Sbostic 	sp->fts_path[p->fts_pathlen] = '\0';
34439800Sbostic 	if (CHDIR(sp, "..")) {
345*45600Sbostic 		SET(FTS__STOP);
34642273Sbostic 		p->fts_info = FTS_ERR;
34739800Sbostic 	} else
34842273Sbostic 		p->fts_info = FTS_DP;
34942273Sbostic 	return(sp->fts_cur = p);
35039800Sbostic }
35139800Sbostic 
35239800Sbostic /*
353*45600Sbostic  * fts_set takes the stream as an argument although it's not used in this
35439800Sbostic  * implementation; it would be necessary if anyone wanted to add global
355*45600Sbostic  * semantics to fts using fts_set.  An error return is allowed for similar
356*45600Sbostic  * reasons.
35739800Sbostic  */
35839800Sbostic /* ARGSUSED */
359*45600Sbostic fts_set(sp, p, instr)
36039800Sbostic 	FTS *sp;
36139800Sbostic 	FTSENT *p;
36239800Sbostic 	int instr;
36339800Sbostic {
36442273Sbostic 	p->fts_instr = instr;
36539800Sbostic 	return(0);
36639800Sbostic }
36739800Sbostic 
36839800Sbostic FTSENT *
369*45600Sbostic fts_children(sp)
37039800Sbostic 	register FTS *sp;
37139800Sbostic {
37242299Sbostic 	register FTSENT *p;
37342299Sbostic 	int fd;
37442299Sbostic 
375*45600Sbostic 	/* Set current node pointer. */
376*45600Sbostic 	p = sp->fts_cur;
377*45600Sbostic 
37839800Sbostic 	/*
379*45600Sbostic 	 * Set errno to 0 so that user can tell the difference between an
38039800Sbostic 	 * error and a directory without entries.
38139800Sbostic 	 */
38239800Sbostic 	errno = 0;
383*45600Sbostic 	if (ISSET(FTS__STOP) ||
384*45600Sbostic 	    p->fts_info != FTS_D && p->fts_info != FTS_DNX &&
385*45600Sbostic 	    p->fts_info != FTS_DNR)
38639800Sbostic 		return(NULL);
387*45600Sbostic 
388*45600Sbostic 	/* Free up any previous child list. */
38942273Sbostic 	if (sp->fts_child)
39042273Sbostic 		fts_lfree(sp->fts_child);
39142299Sbostic 
39242299Sbostic 	/*
393*45600Sbostic 	 * If using chdir on a relative path and called BEFORE fts_read does
394*45600Sbostic 	 * its chdir to the root of a traversal, we can lose because we need
395*45600Sbostic 	 * to chdir into the subdirectory, and we don't know where the current
396*45600Sbostic 	 * directory is to get back so that the upcoming chdir by fts_read
397*45600Sbostic 	 * will work.
39842299Sbostic 	 */
39942299Sbostic 	if (p->fts_level != ROOTLEVEL || p->fts_accpath[0] == '/' ||
400*45600Sbostic 	    ISSET(FTS_NOCHDIR))
40142299Sbostic 		return(sp->fts_child = fts_build(sp, BCHILD));
40242299Sbostic 
40342299Sbostic 	if ((fd = open(".", O_RDONLY, 0)) < 0)
40442299Sbostic 		return(NULL);
40542299Sbostic 	sp->fts_child = fts_build(sp, BCHILD);
40642299Sbostic 	if (fchdir(fd))
40742299Sbostic 		return(NULL);
40842299Sbostic 	(void)close(fd);
40942299Sbostic 	return(sp->fts_child);
41039800Sbostic }
41139800Sbostic 
41239800Sbostic #define	ISDOT(a)	(a[0] == '.' && (!a[1] || a[1] == '.' && !a[2]))
41339800Sbostic 
41442299Sbostic FTSENT *
41542299Sbostic fts_build(sp, type)
41639800Sbostic 	register FTS *sp;
41742299Sbostic 	int type;
41839800Sbostic {
41939800Sbostic 	register struct dirent *dp;
42039800Sbostic 	register FTSENT *p, *head;
42139800Sbostic 	register int nitems;
42239800Sbostic 	DIR *dirp;
42339962Sbostic 	int descend, len, level, maxlen, nlinks, saved_errno;
42439800Sbostic 	char *cp;
42539800Sbostic 
426*45600Sbostic 	/* Set current node pointer. */
42742273Sbostic 	p = sp->fts_cur;
428*45600Sbostic 
42942273Sbostic 	if (!(dirp = opendir(p->fts_accpath))) {
43042299Sbostic 		if (type == BREAD) {
431*45600Sbostic 			p->fts_info = FTS_DNR;
43239800Sbostic 			errno = 0;
43339800Sbostic 		}
43439800Sbostic 		return(NULL);
43539800Sbostic 	}
43639800Sbostic 
43739800Sbostic 	/*
438*45600Sbostic 	 * The real slowdown in walking the tree is the stat calls.  If
43939800Sbostic 	 * FTS_NOSTAT is set and it's a physical walk (so that symbolic
44039800Sbostic 	 * links can't be directories), fts assumes that the number of
44139800Sbostic 	 * subdirectories in a node is equal to the number of links to
44239800Sbostic 	 * the parent.  This allows stat calls to be skipped in any leaf
44339800Sbostic 	 * directories and for any nodes after the directories in the
44439800Sbostic 	 * parent node have been found.  This empirically cuts the stat
44539800Sbostic 	 * calls by about 2/3.
44639800Sbostic 	 */
44742273Sbostic 	nlinks =
448*45600Sbostic 	    ISSET(FTS_NOSTAT) && ISSET(FTS_PHYSICAL) ?
449*45600Sbostic 	    p->fts_statb.st_nlink - (ISSET(FTS_SEEDOT) ? 0 : 2) : -1;
45039800Sbostic 
451*45600Sbostic 	/* If told to descend or found links and not told not to descend. */
452*45600Sbostic 	descend = 0;
453*45600Sbostic 	if (nlinks || type == BREAD)
454*45600Sbostic 		if (!FCHDIR(sp, dirfd(dirp)))
455*45600Sbostic 			descend = 1;
456*45600Sbostic 		/*
457*45600Sbostic 		 * Return all the information possible; fts_read doing a
458*45600Sbostic 		 * relative walk of the tree will have to descend, so it
459*45600Sbostic 		 * can't succeed.  Fts_children or absolute walks of the
460*45600Sbostic 		 * tree can succeed, but no stat information will be available.
461*45600Sbostic 		 */
462*45600Sbostic 		else {
46342299Sbostic 			if (type == BREAD) {
464*45600Sbostic 				(void)closedir(dirp);
465*45600Sbostic 				p->fts_info = FTS_DNX;
46639962Sbostic 				errno = 0;
467*45600Sbostic 				return(NULL);
46839962Sbostic 			}
469*45600Sbostic 			nlinks = 0;
47039962Sbostic 		}
47139962Sbostic 
472*45600Sbostic 	/* Get max file name length that can be stored in current path. */
47342273Sbostic 	maxlen = sp->fts_pathlen - p->fts_pathlen - 1;
47440939Sbostic 
47542273Sbostic 	cp = sp->fts_path + (len = NAPPEND(p));
47640939Sbostic 	*cp++ = '/';
47740939Sbostic 
47842273Sbostic 	level = p->fts_level + 1;
47940939Sbostic 
480*45600Sbostic 	/* Read the directory, attching each new entry to the `link' pointer. */
48139800Sbostic 	for (head = NULL, nitems = 0; dp = readdir(dirp);) {
482*45600Sbostic 		if (ISDOT(dp->d_name) && !ISSET(FTS_SEEDOT))
48339800Sbostic 			continue;
48439800Sbostic 
485*45600Sbostic 		if (!(p = fts_alloc(sp, dp->d_name, (int)dp->d_namlen))) {
48639800Sbostic 			saved_errno = errno;
48739800Sbostic 			goto mem1;
48839800Sbostic 		}
48939800Sbostic 		if (dp->d_namlen > maxlen) {
490*45600Sbostic 			if (!fts_path(sp, (int)dp->d_namlen)) {
491*45600Sbostic 				/* Quit: this stream no longer has a path. */
492*45600Sbostic 				SET(FTS__STOP);
49339800Sbostic 				saved_errno = errno;
494*45600Sbostic 				free((void *)p);
49539800Sbostic mem1:				fts_lfree(head);
49642299Sbostic 				if (type == BREAD)
49742273Sbostic 					p->fts_info = FTS_ERR;
49839962Sbostic 				if (descend && CHDIR(sp, "..")) {
499*45600Sbostic 					/*
500*45600Sbostic 					 * chdir error is more interesting
501*45600Sbostic 					 * than memory error, since it stops
502*45600Sbostic 					 * everything.
503*45600Sbostic 					 */
50439800Sbostic 					saved_errno = errno;
505*45600Sbostic 					SET(FTS__STOP);
50639800Sbostic 				}
50739800Sbostic 				errno = saved_errno;
50842299Sbostic 				(void)closedir(dirp);
50939800Sbostic 				return(NULL);
51039800Sbostic 			}
51142273Sbostic 			maxlen = sp->fts_pathlen - sp->fts_cur->fts_pathlen - 1;
51239800Sbostic 		}
51339800Sbostic 
51442273Sbostic 		p->fts_pathlen = len + dp->d_namlen + 1;
515*45600Sbostic 		p->fts_accpath = ISSET(FTS_NOCHDIR) ? p->fts_path : p->fts_name;
51639800Sbostic 
51742273Sbostic 		p->fts_parent = sp->fts_cur;
51842273Sbostic 		p->fts_level = level;
51939800Sbostic 
52039800Sbostic 		if (nlinks) {
521*45600Sbostic 			/* Make sure fts_stat has a filename to stat. */
522*45600Sbostic 			if (ISSET(FTS_NOCHDIR))
52342273Sbostic 				bcopy(p->fts_name, cp, p->fts_namelen + 1);
524*45600Sbostic 			p->fts_info = fts_stat(sp, p, 0);
52542273Sbostic 			if (nlinks > 0 && (p->fts_info == FTS_D ||
52642273Sbostic 			    p->fts_info == FTS_DNR || p->fts_info == FTS_DNX))
52739800Sbostic 				--nlinks;
52839800Sbostic 		} else
52942273Sbostic 			p->fts_info = FTS_NS;
53039800Sbostic 
53142273Sbostic 		p->fts_link = head;
53239800Sbostic 		head = p;
53339800Sbostic 		++nitems;
53439800Sbostic 	}
53539800Sbostic 	(void)closedir(dirp);
53639800Sbostic 
537*45600Sbostic 	/* Reset the path. */
53842299Sbostic 	if (cp - 1 > sp->fts_path)
53942299Sbostic 		--cp;
54042299Sbostic 	*cp = '\0';
54142299Sbostic 
54242299Sbostic 	/*
543*45600Sbostic 	 * If descended: if were called from fts_read and didn't find anything,
544*45600Sbostic 	 * or were called from fts_children, get back.
54542299Sbostic 	 */
54642299Sbostic 	if (descend && (!nitems || type == BCHILD) && CHDIR(sp, "..")) {
547*45600Sbostic 		SET(FTS__STOP);
54842273Sbostic 		p->fts_info = FTS_ERR;
54939962Sbostic 		return(NULL);
55039962Sbostic 	}
55139962Sbostic 
55239800Sbostic 	if (!nitems) {
55342299Sbostic 		if (type == BREAD)
55442273Sbostic 			p->fts_info = FTS_DP;
55539800Sbostic 		return(NULL);
55639800Sbostic 	}
55739800Sbostic 
55842273Sbostic 	if (sp->fts_compar && nitems > 1)
559*45600Sbostic 		head = fts_sort(sp, head, nitems);
56039800Sbostic 
56142299Sbostic 	if (type == BREAD) {
56242299Sbostic 		*cp = '/';
56342299Sbostic 		bcopy(head->fts_name, cp + 1, head->fts_namelen + 1);
56442299Sbostic 	}
56539800Sbostic 	return(head);
56639800Sbostic }
56739800Sbostic 
568*45600Sbostic static u_short
569*45600Sbostic fts_stat(sp, p, follow)
570*45600Sbostic 	FTS *sp;
57139800Sbostic 	register FTSENT *p;
572*45600Sbostic 	int follow;
57339800Sbostic {
57439800Sbostic 	/*
575*45600Sbostic 	 * If doing a logical walk, or application requested FTS_FOLLOW, do
576*45600Sbostic 	 * a stat(2).  If that fails, either fail or do an lstat(2) for a
577*45600Sbostic 	 * non-existent symlink.  (The check has to be done, or we wouldn't
578*45600Sbostic 	 * detect a symlink being deleted.)
579*45600Sbostic 	 *
580*45600Sbostic 	 * Don't leave errno set for FTS_NS cases.		XXX
58139800Sbostic 	 */
582*45600Sbostic 	if (ISSET(FTS_LOGICAL) || follow) {
583*45600Sbostic 		if (stat(p->fts_accpath, &p->fts_statb)) {
584*45600Sbostic 			errno = 0;
585*45600Sbostic 			if (follow && !lstat(p->fts_accpath, &p->fts_statb))
586*45600Sbostic 				return(FTS_SLNONE);
587*45600Sbostic 			else {
588*45600Sbostic 				errno = 0;
589*45600Sbostic 				return(FTS_NS);
590*45600Sbostic 			}
591*45600Sbostic 		}
592*45600Sbostic 	} else if (lstat(p->fts_accpath, &p->fts_statb)) {
593*45600Sbostic 		errno = 0;
594*45600Sbostic 		return(FTS_NS);
595*45600Sbostic 	}
59639800Sbostic 
597*45600Sbostic 	if (S_ISDIR(p->fts_statb.st_mode))
59839800Sbostic 		return(FTS_D);
599*45600Sbostic 	if (S_ISLNK(p->fts_statb.st_mode))
60039800Sbostic 		return(FTS_SL);
601*45600Sbostic 	if (S_ISREG(p->fts_statb.st_mode))
60239800Sbostic 		return(FTS_F);
60340939Sbostic 	return(FTS_DEFAULT);
60439800Sbostic }
60539800Sbostic 
60639800Sbostic static FTSENT *
60739800Sbostic fts_cycle(p)
60839800Sbostic 	register FTSENT *p;
60939800Sbostic {
61039800Sbostic 	register dev_t dev;
61139800Sbostic 	register ino_t ino;
61239800Sbostic 
61339800Sbostic 	/*
614*45600Sbostic 	 * Cycle detection is brute force; if the tree gets deep enough or
61539800Sbostic 	 * the number of symbolic links to directories is really high
61639800Sbostic 	 * something faster might be worthwhile.
61739800Sbostic 	 */
61842273Sbostic 	dev = p->fts_statb.st_dev;
61942273Sbostic 	ino = p->fts_statb.st_ino;
62042273Sbostic 	for (p = p->fts_parent; p->fts_level > ROOTLEVEL; p = p->fts_parent)
62142273Sbostic 		if (ino == p->fts_statb.st_ino && dev == p->fts_statb.st_dev)
62239800Sbostic 			return(p);
62339800Sbostic 	return(NULL);
62439800Sbostic }
62539800Sbostic 
62639800Sbostic #define	R(type, nelem, ptr) \
62742299Sbostic 	(type *)realloc((void *)ptr, (u_int)((nelem) * sizeof(type)))
62839800Sbostic 
62939800Sbostic static FTSENT *
630*45600Sbostic fts_sort(sp, head, nitems)
631*45600Sbostic 	FTS *sp;
63239800Sbostic 	FTSENT *head;
63339800Sbostic 	register int nitems;
63439800Sbostic {
63539800Sbostic 	register FTSENT **ap, *p;
63639800Sbostic 
63739800Sbostic 	/*
638*45600Sbostic 	 * Construct an array of pointers to the structures and call qsort(3).
63939800Sbostic 	 * Reassemble the array in the order returned by qsort.  If unable to
64039800Sbostic 	 * sort for memory reasons, return the directory entries in their
64139800Sbostic 	 * current order.  Allocate enough space for the current needs plus
64239800Sbostic 	 * 40 so we don't realloc one entry at a time.
64339800Sbostic 	 */
644*45600Sbostic 	if (nitems > sp->fts_nitems) {
645*45600Sbostic 		sp->fts_nitems = nitems + 40;
646*45600Sbostic 		if (!(sp->fts_array =
647*45600Sbostic 		    R(FTSENT *, sp->fts_nitems, sp->fts_array))) {
648*45600Sbostic 			sp->fts_nitems = 0;
64939800Sbostic 			return(head);
65039800Sbostic 		}
65139800Sbostic 	}
652*45600Sbostic 	for (ap = sp->fts_array, p = head; p; p = p->fts_link)
65339800Sbostic 		*ap++ = p;
654*45600Sbostic 	qsort((void *)sp->fts_array, nitems, sizeof(FTSENT *), sp->fts_compar);
655*45600Sbostic 	for (head = *(ap = sp->fts_array); --nitems; ++ap)
65642273Sbostic 		ap[0]->fts_link = ap[1];
65742273Sbostic 	ap[0]->fts_link = NULL;
65839800Sbostic 	return(head);
65939800Sbostic }
66039800Sbostic 
66139800Sbostic static FTSENT *
662*45600Sbostic fts_alloc(sp, name, len)
663*45600Sbostic 	FTS *sp;
66439800Sbostic 	char *name;
66539800Sbostic 	register int len;
66639800Sbostic {
66739800Sbostic 	register FTSENT *p;
66839800Sbostic 
66939800Sbostic 	/*
670*45600Sbostic 	 * Variable sized structures; the name is the last element so
67139800Sbostic 	 * allocate enough extra space after the structure to hold it.
67239800Sbostic 	 */
67342299Sbostic 	if (!(p = (FTSENT *)malloc((size_t)(sizeof(FTSENT) + len))))
67439800Sbostic 		return(NULL);
67542273Sbostic 	bcopy(name, p->fts_name, len + 1);
67642273Sbostic 	p->fts_namelen = len;
677*45600Sbostic 	p->fts_path = sp->fts_path;
678*45600Sbostic 	p->fts_instr = FTS__NOINSTR;
679*45600Sbostic 	p->fts_number = 0;
680*45600Sbostic 	p->fts_pointer = NULL;
68139800Sbostic 	return(p);
68239800Sbostic }
68339800Sbostic 
684*45600Sbostic static void
68539800Sbostic fts_lfree(head)
68639800Sbostic 	register FTSENT *head;
68739800Sbostic {
68839800Sbostic 	register FTSENT *p;
68939800Sbostic 
690*45600Sbostic 	/* Free a linked list of structures. */
69139800Sbostic 	while (p = head) {
69242273Sbostic 		head = head->fts_link;
693*45600Sbostic 		free((void *)p);
69439800Sbostic 	}
69539800Sbostic }
69639800Sbostic 
69739800Sbostic /*
698*45600Sbostic  * Allow essentially unlimited paths; certain programs (find, rm, ls) need to
699*45600Sbostic  * work on any tree.  Most systems will allow creation of paths much longer
700*45600Sbostic  * than MAXPATHLEN, even though the kernel won't resolve them.  Add an extra
701*45600Sbostic  * 128 bytes to the requested size so that we don't realloc the path 2 bytes
702*45600Sbostic  * at a time.
70339800Sbostic  */
70442299Sbostic static char *
705*45600Sbostic fts_path(sp, size)
706*45600Sbostic 	FTS *sp;
70739800Sbostic 	int size;
70839800Sbostic {
709*45600Sbostic 	sp->fts_pathlen += size + 128;
710*45600Sbostic 	return(sp->fts_path = R(char, sp->fts_pathlen, sp->fts_path)); }
71139800Sbostic 
71239800Sbostic static FTSENT *
713*45600Sbostic fts_root(sp, name)
714*45600Sbostic 	FTS *sp;
71539800Sbostic 	register char *name;
71639800Sbostic {
71739800Sbostic 	register char *cp;
71839800Sbostic 
71939800Sbostic 	/*
720*45600Sbostic 	 * Rip trailing slashes; it's somewhat unclear in POSIX 1003.1 what
72139800Sbostic 	 * /a/b/ really is, they don't talk about what a null path component
72239800Sbostic 	 * resolves to.  This hopefully does what the user intended.  Don't
72339800Sbostic 	 * allow null pathnames.
72439800Sbostic 	 */
72539800Sbostic 	for (cp = name; *cp; ++cp);
72639800Sbostic 	if (cp == name) {
72739800Sbostic 		errno = ENOENT;
72839800Sbostic 		return(NULL);
72939800Sbostic 	}
73039800Sbostic 	while (--cp > name && *cp == '/');
73139800Sbostic 	*++cp = '\0';
732*45600Sbostic 	return(fts_alloc(sp, name, cp - name));
73339800Sbostic }
734