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