xref: /csrg-svn/lib/libc/gen/fts.c (revision 47155)
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*47155Sbostic static char sccsid[] = "@(#)fts.c	5.13 (Berkeley) 03/08/91";
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>
20*47155Sbostic #include <unistd.h>
2139800Sbostic 
22*47155Sbostic static FTSENT *fts_alloc(), *fts_build(), *fts_cycle(), *fts_root(),
23*47155Sbostic 	*fts_sort();
24*47155Sbostic static void fts_lfree(), fts_load();
25*47155Sbostic static u_short fts_stat();
26*47155Sbostic static char *fts_path();
2739800Sbostic 
2839800Sbostic /*
2939800Sbostic  * Special case a root of "/" so that slashes aren't appended causing
3039800Sbostic  * paths to be written as "//foo".
3139800Sbostic  */
3239800Sbostic #define	NAPPEND(p) \
3342273Sbostic 	(p->fts_level == ROOTLEVEL && p->fts_pathlen == 1 && \
3442273Sbostic 	    p->fts_path[0] == '/' ? 0 : p->fts_pathlen)
3539800Sbostic 
3645600Sbostic #define	ISSET(opt)	(sp->fts_options & opt)
3745600Sbostic #define	SET(opt)	(sp->fts_options |= opt)
3839800Sbostic 
3945600Sbostic #define	CHDIR(sp, path)	(!ISSET(FTS_NOCHDIR) && chdir(path))
4045600Sbostic #define	FCHDIR(sp, fd)	(!ISSET(FTS_NOCHDIR) && fchdir(fd))
4139800Sbostic 
4242299Sbostic /* fts_build flags */
4345600Sbostic #define	BCHILD		1		/* from fts_children */
4445600Sbostic #define	BREAD		2		/* from fts_read */
4542299Sbostic 
4645600Sbostic /* fts_level values */
4745600Sbostic #define	ROOTLEVEL	0
4845600Sbostic #define	ROOTPARENTLEVEL	-1
4939800Sbostic 
5039800Sbostic FTS *
5145600Sbostic fts_open(argv, options, compar)
52*47155Sbostic 	char * const *argv;
5339800Sbostic 	register int options;
54*47155Sbostic 	int (*compar) __P((const FTSENT *, const FTSENT *));
5539800Sbostic {
5639800Sbostic 	register FTS *sp;
5739800Sbostic 	register FTSENT *p, *root;
5839800Sbostic 	register int nitems, maxlen;
5939800Sbostic 	FTSENT *parent, *tmp;
6039800Sbostic 
6145600Sbostic 	/* Allocate/initialize the stream */
6245600Sbostic 	if (!(sp = (FTS *)malloc((u_int)sizeof(FTS))))
6339800Sbostic 		return(NULL);
6439800Sbostic 	bzero(sp, sizeof(FTS));
6542273Sbostic 	sp->fts_compar = compar;
6642273Sbostic 	sp->fts_options = options;
6739800Sbostic 
6845600Sbostic 	/* Logical walks turn on NOCHDIR; symbolic links are too hard. */
6945600Sbostic 	if (ISSET(FTS_LOGICAL))
7045600Sbostic 		SET(FTS_NOCHDIR);
7145600Sbostic 
7245600Sbostic 	/* Allocate/initialize root's parent. */
7345600Sbostic 	if (!(parent = fts_alloc(sp, "", 0)))
7439800Sbostic 		goto mem1;
7542273Sbostic 	parent->fts_level = ROOTPARENTLEVEL;
7639800Sbostic 
7745600Sbostic 	/* Allocate/initialize root(s). */
7843070Sbostic 	maxlen = -1;
7943070Sbostic 	for (root = NULL, nitems = 0; *argv; ++argv, ++nitems) {
8045600Sbostic 		if (!(p = fts_root(sp, *argv)))
8143070Sbostic 			goto mem2;
8243070Sbostic 		if (maxlen < p->fts_namelen)
8343070Sbostic 			maxlen = p->fts_namelen;
8443070Sbostic 		/*
8545600Sbostic 		 * If comparison routine supplied, traverse in sorted
8643070Sbostic 		 * order; otherwise traverse in the order specified.
8743070Sbostic 		 */
8843070Sbostic 		if (compar) {
8943070Sbostic 			p->fts_link = root;
9043070Sbostic 			root = p;
9143070Sbostic 			p->fts_accpath = p->fts_name;
9243075Sbostic 			if (!(options & FTS_NOSTAT))
9345600Sbostic 				p->fts_info = fts_stat(sp, p, 0);
9443070Sbostic 		} else {
9543070Sbostic 			p->fts_link = NULL;
9643070Sbostic 			if (!root)
9743070Sbostic 				tmp = root = p;
9843070Sbostic 			else {
9943070Sbostic 				tmp->fts_link = p;
10043070Sbostic 				tmp = p;
10139800Sbostic 			}
10239800Sbostic 		}
10343070Sbostic 		p->fts_level = ROOTLEVEL;
10443070Sbostic 		p->fts_parent = parent;
10539800Sbostic 	}
10643070Sbostic 	if (compar && nitems > 1)
10745600Sbostic 		root = fts_sort(sp, root, nitems);
10839800Sbostic 
10942281Sbostic 	/*
11045600Sbostic 	 * Allocate a dummy pointer and make fts_read think that we've just
11142281Sbostic 	 * finished the node before the root(s); set p->fts_info to FTS_NS
11242281Sbostic 	 * so that everything about the "current" node is ignored.
11342281Sbostic 	 */
11445600Sbostic 	if (!(sp->fts_cur = fts_alloc(sp, "", 0)))
11542281Sbostic 		goto mem2;
11642281Sbostic 	sp->fts_cur->fts_link = root;
11742281Sbostic 	sp->fts_cur->fts_info = FTS_NS;
11842281Sbostic 
11945600Sbostic 	/* Start out with at least 1K+ of path space. */
12045600Sbostic 	if (!fts_path(sp, MAX(maxlen, MAXPATHLEN)))
12142281Sbostic 		goto mem3;
12239800Sbostic 
12339800Sbostic 	/*
12445600Sbostic 	 * If using chdir(2), grab a file descriptor pointing to dot to insure
12542299Sbostic 	 * that we can get back here; this could be avoided for some paths,
12642299Sbostic 	 * but almost certainly not worth the effort.  Slashes, symbolic links,
12742299Sbostic 	 * and ".." are all fairly nasty problems.  Note, if we can't get the
12842299Sbostic 	 * descriptor we run anyway, just more slowly.
12939800Sbostic 	 */
13045600Sbostic 	if (!ISSET(FTS_NOCHDIR) && (sp->fts_sd = open(".", O_RDONLY, 0)) < 0)
13145600Sbostic 		SET(FTS_NOCHDIR);
13239800Sbostic 
13339800Sbostic 	return(sp);
13439800Sbostic 
13545600Sbostic mem3:	free((void *)sp->fts_cur);
13639800Sbostic mem2:	fts_lfree(root);
13745600Sbostic 	free((void *)parent);
13845600Sbostic mem1:	free((void *)sp);
13939800Sbostic 	return(NULL);
14039800Sbostic }
14139800Sbostic 
14245600Sbostic static void
14345600Sbostic fts_load(sp, p)
14445600Sbostic 	FTS *sp;
14539800Sbostic 	register FTSENT *p;
14639800Sbostic {
14739800Sbostic 	register int len;
14839800Sbostic 	register char *cp;
14939800Sbostic 
15039800Sbostic 	/*
15145600Sbostic 	 * Load the stream structure for the next traversal; set the
15245600Sbostic 	 * fts_accpath field specially so the chdir gets done to the
15345600Sbostic 	 * right place and the user can access the first node.
15439800Sbostic 	 */
15542273Sbostic 	len = p->fts_pathlen = p->fts_namelen;
15645600Sbostic 	bcopy(p->fts_name, sp->fts_path, len + 1);
15742273Sbostic 	if ((cp = rindex(p->fts_name, '/')) && (cp != p->fts_name || cp[1])) {
15839800Sbostic 		len = strlen(++cp);
15942273Sbostic 		bcopy(cp, p->fts_name, len + 1);
16042273Sbostic 		p->fts_namelen = len;
16139800Sbostic 	}
16245600Sbostic 	p->fts_accpath = p->fts_path = sp->fts_path;
16339800Sbostic }
16439800Sbostic 
165*47155Sbostic int
16645600Sbostic fts_close(sp)
16739800Sbostic 	FTS *sp;
16839800Sbostic {
16939800Sbostic 	register FTSENT *freep, *p;
17039800Sbostic 	int saved_errno;
17139800Sbostic 
17242281Sbostic 	if (sp->fts_cur) {
17342281Sbostic 		/*
17445600Sbostic 		 * This still works if we haven't read anything -- the dummy
17542281Sbostic 		 * structure points to the root list, so we step through to
17642281Sbostic 		 * the end of the root list which has a valid parent pointer.
17742281Sbostic 		 */
17842281Sbostic 		for (p = sp->fts_cur; p->fts_level > ROOTPARENTLEVEL;) {
17942281Sbostic 			freep = p;
18042281Sbostic 			p = p->fts_link ? p->fts_link : p->fts_parent;
18145600Sbostic 			free((void *)freep);
18239800Sbostic 		}
18345600Sbostic 		free((void *)p);
18442281Sbostic 	}
18539800Sbostic 
18645600Sbostic 	/* Free up child linked list, sort array, path buffer. */
18742273Sbostic 	if (sp->fts_child)
18842273Sbostic 		fts_lfree(sp->fts_child);
18942273Sbostic 	if (sp->fts_array)
19045600Sbostic 		free((void *)sp->fts_array);
19145600Sbostic 	free((char *)sp->fts_path);
19239800Sbostic 
19339800Sbostic 	/*
19445600Sbostic 	 * Return to original directory, save errno if necessary; free up
19545600Sbostic 	 * the directory buffer.
19639800Sbostic 	 */
19745600Sbostic 	if (!ISSET(FTS_NOCHDIR)) {
19842299Sbostic 		saved_errno = fchdir(sp->fts_sd) ? errno : 0;
19942299Sbostic 		(void)close(sp->fts_sd);
20039800Sbostic 	}
20139800Sbostic 
20245600Sbostic 	/* Free up the stream pointer. */
20345600Sbostic 	free((void *)sp);
20439800Sbostic 
20545600Sbostic 	/* Set errno and return. */
20645600Sbostic 	if (!ISSET(FTS_NOCHDIR) && saved_errno) {
20739800Sbostic 		errno = saved_errno;
20839800Sbostic 		return(-1);
20939800Sbostic 	}
21039800Sbostic 	return(0);
21139800Sbostic }
21239800Sbostic 
21339800Sbostic FTSENT *
21445600Sbostic fts_read(sp)
21539800Sbostic 	register FTS *sp;
21639800Sbostic {
21742299Sbostic 	register FTSENT *p, *tmp;
21839800Sbostic 	register int instr;
21942299Sbostic 	register char *cp;
22039800Sbostic 	static int cd;
22139800Sbostic 
22245600Sbostic 	/* If finished or unrecoverable error, return NULL. */
22345600Sbostic 	if (!sp->fts_cur || ISSET(FTS__STOP))
22439800Sbostic 		return(NULL);
22539800Sbostic 
22645600Sbostic 	/* Set current node pointer. */
22742273Sbostic 	p = sp->fts_cur;
22839800Sbostic 
22945600Sbostic 	/* Save and zero out user instructions. */
23042273Sbostic 	instr = p->fts_instr;
23145600Sbostic 	p->fts_instr = FTS__NOINSTR;
23239800Sbostic 
23345600Sbostic 	/* If used fts_link pointer for cycle detection, restore it. */
23442273Sbostic 	if (sp->fts_savelink) {
23542273Sbostic 		p->fts_link = sp->fts_savelink;
23642273Sbostic 		sp->fts_savelink = NULL;
23739800Sbostic 	}
23839800Sbostic 
23945600Sbostic 	/* Any type of file may be re-visited; re-stat and return. */
24039800Sbostic 	if (instr == FTS_AGAIN) {
24145600Sbostic 		p->fts_info = fts_stat(sp, p, 0);
24239800Sbostic 		return(p);
24339800Sbostic 	}
24439800Sbostic 
24545600Sbostic 	/* Following a symbolic link. */
24642299Sbostic 	if (p->fts_info == FTS_SL && instr == FTS_FOLLOW) {
24745600Sbostic 		p->fts_info = fts_stat(sp, p, 1);
24842299Sbostic 		return(p);
24942299Sbostic 	}
25042299Sbostic 
25145600Sbostic 	/* Directory in pre-order. */
25242299Sbostic 	if (p->fts_info == FTS_D) {
25345600Sbostic 		/* If skipped or crossed mount point, do post-order visit. */
25445600Sbostic 		if (instr == FTS_SKIP || ISSET(FTS_XDEV) &&
25542280Sbostic 		    p->fts_statb.st_dev != sp->sdev) {
25642273Sbostic 			if (sp->fts_child) {
25742273Sbostic 				fts_lfree(sp->fts_child);
25842273Sbostic 				sp->fts_child = NULL;
25939800Sbostic 			}
26042301Sbostic 			p->fts_info = FTS_DP;
26142301Sbostic 			return(p);
26242299Sbostic 		}
26342299Sbostic 
26445600Sbostic 		/* Read the directory if necessary, and return first entry. */
26542299Sbostic 		if (sp->fts_child)
26642299Sbostic 			if (CHDIR(sp, p->fts_accpath)) {
26742299Sbostic 				fts_lfree(sp->fts_child);
26842299Sbostic 				p->fts_info = FTS_DNX;
26942299Sbostic 			} else {
27042299Sbostic 				p = sp->fts_child;
27142299Sbostic 				cp = sp->fts_path + NAPPEND(p->fts_parent);
27242299Sbostic 				*cp++ = '/';
27342299Sbostic 				bcopy(p->fts_name, cp, p->fts_namelen + 1);
27442299Sbostic 			}
27542299Sbostic 		else {
27642299Sbostic 			if (!(sp->fts_child = fts_build(sp, BREAD)))
27739800Sbostic 				return(p);
27842273Sbostic 			p = sp->fts_child;
27939800Sbostic 		}
28042299Sbostic 		sp->fts_child = NULL;
28142299Sbostic 		return(sp->fts_cur = p);
28239800Sbostic 	}
28339800Sbostic 
28445600Sbostic 	/* Move to next node on this level. */
28542299Sbostic next:	tmp = p;
28642299Sbostic 	if (p = p->fts_link) {
28739800Sbostic 		/*
28845600Sbostic 		 * If root level node, set up paths and return.  If not the
28939800Sbostic 		 * first time, and it's not an absolute pathname, get back
29045600Sbostic 		 * to starting directory.  If that fails, we're dead.
29139800Sbostic 		 */
29242273Sbostic 		if (p->fts_level == ROOTLEVEL) {
29345600Sbostic 			fts_load(sp, p);
29445600Sbostic 			free((void *)tmp);
29542281Sbostic 			if (cd &&
29642299Sbostic 			    p->fts_path[0] != '/' && FCHDIR(sp, sp->fts_sd)) {
29745600Sbostic 				/* Can't get back to start; we're dead. */
29842299Sbostic 				p->fts_path = "starting directory";
29942281Sbostic 				p->fts_info = FTS_ERR;
30045600Sbostic 				SET(FTS__STOP);
30142281Sbostic 				return(sp->fts_cur = p);
30242299Sbostic 			}
30342281Sbostic 			cd = 1;
30445600Sbostic 			p->fts_info = fts_stat(sp, p, 0);
30542280Sbostic 			sp->sdev = p->fts_statb.st_dev;
30639800Sbostic 		} else {
30745600Sbostic 			free((void *)tmp);
30842299Sbostic 
30945600Sbostic 			/* User may have called fts_set on node. */
31042299Sbostic 			if (p->fts_instr == FTS_SKIP)
31142299Sbostic 				goto next;
31242299Sbostic 			if (p->fts_instr == FTS_FOLLOW) {
31345600Sbostic 				p->fts_info = fts_stat(sp, p, 1);
31445600Sbostic 				p->fts_instr = FTS__NOINSTR;
31542299Sbostic 			}
31642299Sbostic 
31745600Sbostic 			/* Fill in the paths. */
31842273Sbostic 			cp = sp->fts_path + NAPPEND(p->fts_parent);
31939800Sbostic 			*cp++ = '/';
32042273Sbostic 			bcopy(p->fts_name, cp, p->fts_namelen + 1);
32142299Sbostic 
32245600Sbostic 			/* Check for directory cycles. */
32342273Sbostic 			if (p->fts_info == FTS_D && (tmp = fts_cycle(p))) {
32442273Sbostic 				sp->fts_savelink = p->fts_link;
32542273Sbostic 				p->fts_link = tmp;
32642299Sbostic 				p->fts_info = FTS_DC;
32739800Sbostic 			}
32839800Sbostic 		}
32942273Sbostic 		return(sp->fts_cur = p);
33039800Sbostic 	}
33139800Sbostic 
33245600Sbostic 	/* Move to parent. */
33342299Sbostic 	p = tmp->fts_parent;
33445600Sbostic 	free((void *)tmp);
33542299Sbostic 
33642273Sbostic 	if (p->fts_level == ROOTPARENTLEVEL) {
33739800Sbostic 		/*
33845600Sbostic 		 * Done; free everything up and set errno to 0 so the user
33939800Sbostic 		 * can distinguish between error and EOF.
34039800Sbostic 		 */
34145600Sbostic 		free((void *)p);
34239800Sbostic 		errno = 0;
34342273Sbostic 		return(sp->fts_cur = NULL);
34439800Sbostic 	}
34539800Sbostic 
34642273Sbostic 	sp->fts_path[p->fts_pathlen] = '\0';
34739800Sbostic 	if (CHDIR(sp, "..")) {
34845600Sbostic 		SET(FTS__STOP);
34942273Sbostic 		p->fts_info = FTS_ERR;
35039800Sbostic 	} else
35142273Sbostic 		p->fts_info = FTS_DP;
35242273Sbostic 	return(sp->fts_cur = p);
35339800Sbostic }
35439800Sbostic 
35539800Sbostic /*
35645600Sbostic  * fts_set takes the stream as an argument although it's not used in this
35739800Sbostic  * implementation; it would be necessary if anyone wanted to add global
35845600Sbostic  * semantics to fts using fts_set.  An error return is allowed for similar
35945600Sbostic  * reasons.
36039800Sbostic  */
36139800Sbostic /* ARGSUSED */
362*47155Sbostic int
36345600Sbostic fts_set(sp, p, instr)
36439800Sbostic 	FTS *sp;
36539800Sbostic 	FTSENT *p;
36639800Sbostic 	int instr;
36739800Sbostic {
36842273Sbostic 	p->fts_instr = instr;
36939800Sbostic 	return(0);
37039800Sbostic }
37139800Sbostic 
37239800Sbostic FTSENT *
37345600Sbostic fts_children(sp)
37439800Sbostic 	register FTS *sp;
37539800Sbostic {
37642299Sbostic 	register FTSENT *p;
37742299Sbostic 	int fd;
37842299Sbostic 
37945600Sbostic 	/* Set current node pointer. */
38045600Sbostic 	p = sp->fts_cur;
38145600Sbostic 
38239800Sbostic 	/*
38345600Sbostic 	 * Set errno to 0 so that user can tell the difference between an
38439800Sbostic 	 * error and a directory without entries.
38539800Sbostic 	 */
38639800Sbostic 	errno = 0;
38745600Sbostic 	if (ISSET(FTS__STOP) ||
38845600Sbostic 	    p->fts_info != FTS_D && p->fts_info != FTS_DNX &&
38945600Sbostic 	    p->fts_info != FTS_DNR)
39039800Sbostic 		return(NULL);
39145600Sbostic 
39245600Sbostic 	/* Free up any previous child list. */
39342273Sbostic 	if (sp->fts_child)
39442273Sbostic 		fts_lfree(sp->fts_child);
39542299Sbostic 
39642299Sbostic 	/*
39745600Sbostic 	 * If using chdir on a relative path and called BEFORE fts_read does
39845600Sbostic 	 * its chdir to the root of a traversal, we can lose because we need
39945600Sbostic 	 * to chdir into the subdirectory, and we don't know where the current
40045600Sbostic 	 * directory is to get back so that the upcoming chdir by fts_read
40145600Sbostic 	 * will work.
40242299Sbostic 	 */
40342299Sbostic 	if (p->fts_level != ROOTLEVEL || p->fts_accpath[0] == '/' ||
40445600Sbostic 	    ISSET(FTS_NOCHDIR))
40542299Sbostic 		return(sp->fts_child = fts_build(sp, BCHILD));
40642299Sbostic 
40742299Sbostic 	if ((fd = open(".", O_RDONLY, 0)) < 0)
40842299Sbostic 		return(NULL);
40942299Sbostic 	sp->fts_child = fts_build(sp, BCHILD);
41042299Sbostic 	if (fchdir(fd))
41142299Sbostic 		return(NULL);
41242299Sbostic 	(void)close(fd);
41342299Sbostic 	return(sp->fts_child);
41439800Sbostic }
41539800Sbostic 
41639800Sbostic #define	ISDOT(a)	(a[0] == '.' && (!a[1] || a[1] == '.' && !a[2]))
41739800Sbostic 
418*47155Sbostic static FTSENT *
41942299Sbostic fts_build(sp, type)
42039800Sbostic 	register FTS *sp;
42142299Sbostic 	int type;
42239800Sbostic {
42339800Sbostic 	register struct dirent *dp;
42439800Sbostic 	register FTSENT *p, *head;
42539800Sbostic 	register int nitems;
42639800Sbostic 	DIR *dirp;
42739962Sbostic 	int descend, len, level, maxlen, nlinks, saved_errno;
42839800Sbostic 	char *cp;
42939800Sbostic 
43045600Sbostic 	/* Set current node pointer. */
43142273Sbostic 	p = sp->fts_cur;
43245600Sbostic 
43342273Sbostic 	if (!(dirp = opendir(p->fts_accpath))) {
43442299Sbostic 		if (type == BREAD) {
43545600Sbostic 			p->fts_info = FTS_DNR;
43639800Sbostic 			errno = 0;
43739800Sbostic 		}
43839800Sbostic 		return(NULL);
43939800Sbostic 	}
44039800Sbostic 
44139800Sbostic 	/*
44245600Sbostic 	 * The real slowdown in walking the tree is the stat calls.  If
44339800Sbostic 	 * FTS_NOSTAT is set and it's a physical walk (so that symbolic
44439800Sbostic 	 * links can't be directories), fts assumes that the number of
44539800Sbostic 	 * subdirectories in a node is equal to the number of links to
44639800Sbostic 	 * the parent.  This allows stat calls to be skipped in any leaf
44739800Sbostic 	 * directories and for any nodes after the directories in the
44839800Sbostic 	 * parent node have been found.  This empirically cuts the stat
44939800Sbostic 	 * calls by about 2/3.
45039800Sbostic 	 */
45142273Sbostic 	nlinks =
45245600Sbostic 	    ISSET(FTS_NOSTAT) && ISSET(FTS_PHYSICAL) ?
45345600Sbostic 	    p->fts_statb.st_nlink - (ISSET(FTS_SEEDOT) ? 0 : 2) : -1;
45439800Sbostic 
45545600Sbostic 	/* If told to descend or found links and not told not to descend. */
45645600Sbostic 	descend = 0;
45745600Sbostic 	if (nlinks || type == BREAD)
45845600Sbostic 		if (!FCHDIR(sp, dirfd(dirp)))
45945600Sbostic 			descend = 1;
46045600Sbostic 		/*
46145627Sbostic 		 * Return all the information possible; an fts_read doing a
46245627Sbostic 		 * relative walk of the tree will have to descend, so it can't
46345627Sbostic 		 * succeed.  Fts_children or absolute walks of the tree can
46445627Sbostic 		 * succeed, but no stat information will be available.  Reset
46545627Sbostic 		 * errno as necessary.
46645600Sbostic 		 */
46745600Sbostic 		else {
46845627Sbostic 			errno = 0;
46942299Sbostic 			if (type == BREAD) {
47045600Sbostic 				(void)closedir(dirp);
47145600Sbostic 				p->fts_info = FTS_DNX;
47245600Sbostic 				return(NULL);
47339962Sbostic 			}
47445600Sbostic 			nlinks = 0;
47539962Sbostic 		}
47639962Sbostic 
47745600Sbostic 	/* Get max file name length that can be stored in current path. */
47842273Sbostic 	maxlen = sp->fts_pathlen - p->fts_pathlen - 1;
47940939Sbostic 
48042273Sbostic 	cp = sp->fts_path + (len = NAPPEND(p));
48140939Sbostic 	*cp++ = '/';
48240939Sbostic 
48342273Sbostic 	level = p->fts_level + 1;
48440939Sbostic 
48545600Sbostic 	/* Read the directory, attching each new entry to the `link' pointer. */
48639800Sbostic 	for (head = NULL, nitems = 0; dp = readdir(dirp);) {
48745600Sbostic 		if (ISDOT(dp->d_name) && !ISSET(FTS_SEEDOT))
48839800Sbostic 			continue;
48939800Sbostic 
49045600Sbostic 		if (!(p = fts_alloc(sp, dp->d_name, (int)dp->d_namlen))) {
49139800Sbostic 			saved_errno = errno;
49239800Sbostic 			goto mem1;
49339800Sbostic 		}
49439800Sbostic 		if (dp->d_namlen > maxlen) {
49545600Sbostic 			if (!fts_path(sp, (int)dp->d_namlen)) {
49645600Sbostic 				/* Quit: this stream no longer has a path. */
49745600Sbostic 				SET(FTS__STOP);
49839800Sbostic 				saved_errno = errno;
49945600Sbostic 				free((void *)p);
50039800Sbostic mem1:				fts_lfree(head);
50142299Sbostic 				if (type == BREAD)
50242273Sbostic 					p->fts_info = FTS_ERR;
50339962Sbostic 				if (descend && CHDIR(sp, "..")) {
50445600Sbostic 					/*
50545600Sbostic 					 * chdir error is more interesting
50645600Sbostic 					 * than memory error, since it stops
50745600Sbostic 					 * everything.
50845600Sbostic 					 */
50939800Sbostic 					saved_errno = errno;
51045600Sbostic 					SET(FTS__STOP);
51139800Sbostic 				}
51239800Sbostic 				errno = saved_errno;
51342299Sbostic 				(void)closedir(dirp);
51439800Sbostic 				return(NULL);
51539800Sbostic 			}
51642273Sbostic 			maxlen = sp->fts_pathlen - sp->fts_cur->fts_pathlen - 1;
51739800Sbostic 		}
51839800Sbostic 
51942273Sbostic 		p->fts_pathlen = len + dp->d_namlen + 1;
52045600Sbostic 		p->fts_accpath = ISSET(FTS_NOCHDIR) ? p->fts_path : p->fts_name;
52139800Sbostic 
52242273Sbostic 		p->fts_parent = sp->fts_cur;
52342273Sbostic 		p->fts_level = level;
52439800Sbostic 
52539800Sbostic 		if (nlinks) {
52645600Sbostic 			/* Make sure fts_stat has a filename to stat. */
52745600Sbostic 			if (ISSET(FTS_NOCHDIR))
52842273Sbostic 				bcopy(p->fts_name, cp, p->fts_namelen + 1);
52945600Sbostic 			p->fts_info = fts_stat(sp, p, 0);
53042273Sbostic 			if (nlinks > 0 && (p->fts_info == FTS_D ||
53142273Sbostic 			    p->fts_info == FTS_DNR || p->fts_info == FTS_DNX))
53239800Sbostic 				--nlinks;
53339800Sbostic 		} else
53442273Sbostic 			p->fts_info = FTS_NS;
53539800Sbostic 
53642273Sbostic 		p->fts_link = head;
53739800Sbostic 		head = p;
53839800Sbostic 		++nitems;
53939800Sbostic 	}
54039800Sbostic 	(void)closedir(dirp);
54139800Sbostic 
54245600Sbostic 	/* Reset the path. */
54342299Sbostic 	if (cp - 1 > sp->fts_path)
54442299Sbostic 		--cp;
54542299Sbostic 	*cp = '\0';
54642299Sbostic 
54742299Sbostic 	/*
54845600Sbostic 	 * If descended: if were called from fts_read and didn't find anything,
54945600Sbostic 	 * or were called from fts_children, get back.
55042299Sbostic 	 */
55142299Sbostic 	if (descend && (!nitems || type == BCHILD) && CHDIR(sp, "..")) {
55245600Sbostic 		SET(FTS__STOP);
55342273Sbostic 		p->fts_info = FTS_ERR;
55439962Sbostic 		return(NULL);
55539962Sbostic 	}
55639962Sbostic 
55739800Sbostic 	if (!nitems) {
55842299Sbostic 		if (type == BREAD)
55942273Sbostic 			p->fts_info = FTS_DP;
56039800Sbostic 		return(NULL);
56139800Sbostic 	}
56239800Sbostic 
56342273Sbostic 	if (sp->fts_compar && nitems > 1)
56445600Sbostic 		head = fts_sort(sp, head, nitems);
56539800Sbostic 
56642299Sbostic 	if (type == BREAD) {
56742299Sbostic 		*cp = '/';
56842299Sbostic 		bcopy(head->fts_name, cp + 1, head->fts_namelen + 1);
56942299Sbostic 	}
57039800Sbostic 	return(head);
57139800Sbostic }
57239800Sbostic 
57345600Sbostic static u_short
57445600Sbostic fts_stat(sp, p, follow)
57545600Sbostic 	FTS *sp;
57639800Sbostic 	register FTSENT *p;
57745600Sbostic 	int follow;
57839800Sbostic {
57939800Sbostic 	/*
58045600Sbostic 	 * If doing a logical walk, or application requested FTS_FOLLOW, do
58145600Sbostic 	 * a stat(2).  If that fails, either fail or do an lstat(2) for a
58245600Sbostic 	 * non-existent symlink.  (The check has to be done, or we wouldn't
58345600Sbostic 	 * detect a symlink being deleted.)
58445600Sbostic 	 *
58545600Sbostic 	 * Don't leave errno set for FTS_NS cases.		XXX
58639800Sbostic 	 */
58745600Sbostic 	if (ISSET(FTS_LOGICAL) || follow) {
58845600Sbostic 		if (stat(p->fts_accpath, &p->fts_statb)) {
58945600Sbostic 			errno = 0;
59045600Sbostic 			if (follow && !lstat(p->fts_accpath, &p->fts_statb))
59145600Sbostic 				return(FTS_SLNONE);
59245600Sbostic 			else {
59345600Sbostic 				errno = 0;
59445600Sbostic 				return(FTS_NS);
59545600Sbostic 			}
59645600Sbostic 		}
59745600Sbostic 	} else if (lstat(p->fts_accpath, &p->fts_statb)) {
59845600Sbostic 		errno = 0;
59945600Sbostic 		return(FTS_NS);
60045600Sbostic 	}
60139800Sbostic 
60245600Sbostic 	if (S_ISDIR(p->fts_statb.st_mode))
60339800Sbostic 		return(FTS_D);
60445600Sbostic 	if (S_ISLNK(p->fts_statb.st_mode))
60539800Sbostic 		return(FTS_SL);
60645600Sbostic 	if (S_ISREG(p->fts_statb.st_mode))
60739800Sbostic 		return(FTS_F);
60840939Sbostic 	return(FTS_DEFAULT);
60939800Sbostic }
61039800Sbostic 
61139800Sbostic static FTSENT *
61239800Sbostic fts_cycle(p)
61339800Sbostic 	register FTSENT *p;
61439800Sbostic {
61539800Sbostic 	register dev_t dev;
61639800Sbostic 	register ino_t ino;
61739800Sbostic 
61839800Sbostic 	/*
61945600Sbostic 	 * Cycle detection is brute force; if the tree gets deep enough or
62039800Sbostic 	 * the number of symbolic links to directories is really high
62139800Sbostic 	 * something faster might be worthwhile.
62239800Sbostic 	 */
62342273Sbostic 	dev = p->fts_statb.st_dev;
62442273Sbostic 	ino = p->fts_statb.st_ino;
62542273Sbostic 	for (p = p->fts_parent; p->fts_level > ROOTLEVEL; p = p->fts_parent)
62642273Sbostic 		if (ino == p->fts_statb.st_ino && dev == p->fts_statb.st_dev)
62739800Sbostic 			return(p);
62839800Sbostic 	return(NULL);
62939800Sbostic }
63039800Sbostic 
63139800Sbostic #define	R(type, nelem, ptr) \
63242299Sbostic 	(type *)realloc((void *)ptr, (u_int)((nelem) * sizeof(type)))
63339800Sbostic 
63439800Sbostic static FTSENT *
63545600Sbostic fts_sort(sp, head, nitems)
63645600Sbostic 	FTS *sp;
63739800Sbostic 	FTSENT *head;
63839800Sbostic 	register int nitems;
63939800Sbostic {
64039800Sbostic 	register FTSENT **ap, *p;
64139800Sbostic 
64239800Sbostic 	/*
64345600Sbostic 	 * Construct an array of pointers to the structures and call qsort(3).
64439800Sbostic 	 * Reassemble the array in the order returned by qsort.  If unable to
64539800Sbostic 	 * sort for memory reasons, return the directory entries in their
64639800Sbostic 	 * current order.  Allocate enough space for the current needs plus
64739800Sbostic 	 * 40 so we don't realloc one entry at a time.
64839800Sbostic 	 */
64945600Sbostic 	if (nitems > sp->fts_nitems) {
65045600Sbostic 		sp->fts_nitems = nitems + 40;
65145600Sbostic 		if (!(sp->fts_array =
65245600Sbostic 		    R(FTSENT *, sp->fts_nitems, sp->fts_array))) {
65345600Sbostic 			sp->fts_nitems = 0;
65439800Sbostic 			return(head);
65539800Sbostic 		}
65639800Sbostic 	}
65745600Sbostic 	for (ap = sp->fts_array, p = head; p; p = p->fts_link)
65839800Sbostic 		*ap++ = p;
65945600Sbostic 	qsort((void *)sp->fts_array, nitems, sizeof(FTSENT *), sp->fts_compar);
66045600Sbostic 	for (head = *(ap = sp->fts_array); --nitems; ++ap)
66142273Sbostic 		ap[0]->fts_link = ap[1];
66242273Sbostic 	ap[0]->fts_link = NULL;
66339800Sbostic 	return(head);
66439800Sbostic }
66539800Sbostic 
66639800Sbostic static FTSENT *
66745600Sbostic fts_alloc(sp, name, len)
66845600Sbostic 	FTS *sp;
66939800Sbostic 	char *name;
67039800Sbostic 	register int len;
67139800Sbostic {
67239800Sbostic 	register FTSENT *p;
67339800Sbostic 
67439800Sbostic 	/*
67545600Sbostic 	 * Variable sized structures; the name is the last element so
67639800Sbostic 	 * allocate enough extra space after the structure to hold it.
67739800Sbostic 	 */
67842299Sbostic 	if (!(p = (FTSENT *)malloc((size_t)(sizeof(FTSENT) + len))))
67939800Sbostic 		return(NULL);
68042273Sbostic 	bcopy(name, p->fts_name, len + 1);
68142273Sbostic 	p->fts_namelen = len;
68245600Sbostic 	p->fts_path = sp->fts_path;
68345600Sbostic 	p->fts_instr = FTS__NOINSTR;
68445600Sbostic 	p->fts_number = 0;
68545600Sbostic 	p->fts_pointer = NULL;
68639800Sbostic 	return(p);
68739800Sbostic }
68839800Sbostic 
68945600Sbostic static void
69039800Sbostic fts_lfree(head)
69139800Sbostic 	register FTSENT *head;
69239800Sbostic {
69339800Sbostic 	register FTSENT *p;
69439800Sbostic 
69545600Sbostic 	/* Free a linked list of structures. */
69639800Sbostic 	while (p = head) {
69742273Sbostic 		head = head->fts_link;
69845600Sbostic 		free((void *)p);
69939800Sbostic 	}
70039800Sbostic }
70139800Sbostic 
70239800Sbostic /*
70345600Sbostic  * Allow essentially unlimited paths; certain programs (find, rm, ls) need to
70445600Sbostic  * work on any tree.  Most systems will allow creation of paths much longer
70545600Sbostic  * than MAXPATHLEN, even though the kernel won't resolve them.  Add an extra
70645600Sbostic  * 128 bytes to the requested size so that we don't realloc the path 2 bytes
70745600Sbostic  * at a time.
70839800Sbostic  */
70942299Sbostic static char *
71045600Sbostic fts_path(sp, size)
71145600Sbostic 	FTS *sp;
71239800Sbostic 	int size;
71339800Sbostic {
71445600Sbostic 	sp->fts_pathlen += size + 128;
715*47155Sbostic 	return(sp->fts_path = R(char, sp->fts_pathlen, sp->fts_path));
716*47155Sbostic }
71739800Sbostic 
71839800Sbostic static FTSENT *
71945600Sbostic fts_root(sp, name)
72045600Sbostic 	FTS *sp;
72139800Sbostic 	register char *name;
72239800Sbostic {
72339800Sbostic 	register char *cp;
72439800Sbostic 
72539800Sbostic 	/*
72645600Sbostic 	 * Rip trailing slashes; it's somewhat unclear in POSIX 1003.1 what
72739800Sbostic 	 * /a/b/ really is, they don't talk about what a null path component
72839800Sbostic 	 * resolves to.  This hopefully does what the user intended.  Don't
72939800Sbostic 	 * allow null pathnames.
73039800Sbostic 	 */
73139800Sbostic 	for (cp = name; *cp; ++cp);
73239800Sbostic 	if (cp == name) {
73339800Sbostic 		errno = ENOENT;
73439800Sbostic 		return(NULL);
73539800Sbostic 	}
736*47155Sbostic #ifdef notdef
73739800Sbostic 	while (--cp > name && *cp == '/');
73839800Sbostic 	*++cp = '\0';
739*47155Sbostic #endif
740*47155Sbostic 	return(fts_alloc(sp, name, cp - name + 1));
74139800Sbostic }
742