xref: /csrg-svn/lib/libc/gen/fts.c (revision 42281)
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*42281Sbostic static char sccsid[] = "@(#)fts.c	5.6 (Berkeley) 05/22/90";
1039800Sbostic #endif /* LIBC_SCCS and not lint */
1139800Sbostic 
1239800Sbostic #include <sys/param.h>
1339800Sbostic #include <sys/stat.h>
1439800Sbostic #include <dirent.h>
1539800Sbostic #include <errno.h>
1639800Sbostic #include <fts.h>
1742273Sbostic #include <string.h>
18*42281Sbostic #include <stdlib.h>
1939800Sbostic 
2039800Sbostic extern int errno;
2139800Sbostic 
2239800Sbostic FTSENT *fts_alloc(), *fts_build(), *fts_cycle(), *fts_sort(), *fts_root();
2339800Sbostic 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 
3342273Sbostic #define	CHDIR(sp, path)	(!(sp->fts_options & FTS_NOCHDIR) && chdir(path))
3442273Sbostic #define	FCHDIR(sp, fd)	(!(sp->fts_options & FTS_NOCHDIR) && fchdir(fd))
3539800Sbostic 
3639800Sbostic #define	ROOTLEVEL	0
3739800Sbostic #define	ROOTPARENTLEVEL	-1
3839800Sbostic 
3939800Sbostic static FTS *stream;			/* current stream pointer */
4039800Sbostic 
4139800Sbostic FTS *
4239800Sbostic ftsopen(argv, options, compar)
4339800Sbostic 	char *argv[];
4439800Sbostic 	register int options;
4539800Sbostic 	int (*compar)();
4639800Sbostic {
4739800Sbostic 	register FTS *sp;
4839800Sbostic 	register FTSENT *p, *root;
4939800Sbostic 	register int nitems, maxlen;
5039800Sbostic 	FTSENT *parent, *tmp;
5139800Sbostic 	char wd[MAXPATHLEN], *getwd(), *strdup();
5239800Sbostic 
5339800Sbostic 	/* allocate/initialize the stream */
5439800Sbostic 	if (!(stream = sp = (FTS *)malloc((u_int)sizeof(FTS))))
5539800Sbostic 		return(NULL);
5639800Sbostic 	bzero(sp, sizeof(FTS));
5742273Sbostic 	sp->fts_compar = compar;
5842273Sbostic 	sp->fts_options = options;
5939800Sbostic 
6039800Sbostic 	/* allocate/initialize root's parent */
6139800Sbostic 	if (!(parent = fts_alloc("", 0)))
6239800Sbostic 		goto mem1;
6342273Sbostic 	parent->fts_level = ROOTPARENTLEVEL;
6439800Sbostic 
6539800Sbostic 	/* allocate/initialize root(s) */
6639800Sbostic 	if (options & FTS_MULTIPLE) {
6739800Sbostic 		maxlen = -1;
6839800Sbostic 		for (root = NULL, nitems = 0; *argv; ++argv, ++nitems) {
6939800Sbostic 			if (!(p = fts_root(*argv)))
7039800Sbostic 				goto mem2;
7142273Sbostic 			if (maxlen < p->fts_namelen)
7242273Sbostic 				maxlen = p->fts_namelen;
7339800Sbostic 			/*
7439800Sbostic 			 * if comparison routine supplied, traverse in sorted
7539800Sbostic 			 * order; otherwise traverse in the order specified.
7639800Sbostic 			 */
7739800Sbostic 			if (compar) {
7842273Sbostic 				p->fts_link = root;
7939800Sbostic 				root = p;
8042273Sbostic 				p->fts_accpath = p->fts_name;
8142273Sbostic 				p->fts_info = fts_stat(p, 0);
8239800Sbostic 			} else {
8342273Sbostic 				p->fts_link = NULL;
8439800Sbostic 				if (!root)
8539800Sbostic 					tmp = root = p;
8639800Sbostic 				else {
8742273Sbostic 					tmp->fts_link = p;
8839800Sbostic 					tmp = p;
8939800Sbostic 				}
9039800Sbostic 			}
9142273Sbostic 			p->fts_level = ROOTLEVEL;
9242273Sbostic 			p->fts_parent = parent;
9339800Sbostic 		}
9439800Sbostic 		if (compar && nitems > 1)
9539800Sbostic 			root = fts_sort(root, nitems);
9639800Sbostic 	} else {
9739800Sbostic 		if (!(root = fts_root((char *)argv)))
9839800Sbostic 			goto mem2;
9942273Sbostic 		maxlen = root->fts_namelen;
10042273Sbostic 		root->fts_link = NULL;
10142273Sbostic 		root->fts_level = ROOTLEVEL;
10242273Sbostic 		root->fts_parent = parent;
10339800Sbostic 	}
10439800Sbostic 
105*42281Sbostic 	/*
106*42281Sbostic 	 * allocate a dummy pointer and make ftsread think that we've just
107*42281Sbostic 	 * finished the node before the root(s); set p->fts_info to FTS_NS
108*42281Sbostic 	 * so that everything about the "current" node is ignored.
109*42281Sbostic 	 */
110*42281Sbostic 	if (!(sp->fts_cur = fts_alloc("", 0)))
111*42281Sbostic 		goto mem2;
112*42281Sbostic 	sp->fts_cur->fts_link = root;
113*42281Sbostic 	sp->fts_cur->fts_info = FTS_NS;
114*42281Sbostic 
11539800Sbostic 	/* start out with at least 1K+ of path space */
11639800Sbostic 	if (!fts_path(MAX(maxlen, MAXPATHLEN)))
117*42281Sbostic 		goto mem3;
11839800Sbostic 
11939800Sbostic 	/*
12039800Sbostic 	 * if using chdir(2), do a getwd() to insure that we can get back
12139800Sbostic 	 * here; this could be avoided for some paths, but probably not
12239800Sbostic 	 * worth the effort.  Slashes, symbolic links, and ".." are all
12339800Sbostic 	 * fairly nasty problems.  Note, if we can't get the current
12439800Sbostic 	 * working directory we run anyway, just more slowly.
12539800Sbostic 	 */
12642273Sbostic 	if (!(options & FTS_NOCHDIR) &&
12742273Sbostic 	    (!getwd(wd) || !(sp->fts_wd = strdup(wd))))
12842273Sbostic 		sp->fts_options |= FTS_NOCHDIR;
12939800Sbostic 
13039800Sbostic 	return(sp);
13139800Sbostic 
132*42281Sbostic mem3:	(void)free((char *)sp->fts_cur);
13339800Sbostic mem2:	fts_lfree(root);
13439800Sbostic 	(void)free((char *)parent);
13539800Sbostic mem1:	(void)free((char *)sp);
13639800Sbostic 	return(NULL);
13739800Sbostic }
13839800Sbostic 
13939800Sbostic static
14039800Sbostic fts_load(p)
14139800Sbostic 	register FTSENT *p;
14239800Sbostic {
14339800Sbostic 	register int len;
14439800Sbostic 	register char *cp;
14539800Sbostic 
14639800Sbostic 	/*
14739800Sbostic 	 * load the stream structure for the next traversal; set the
14839800Sbostic 	 * accpath field specially so the chdir gets done to the right
14939800Sbostic 	 * place and the user can access the first node.
15039800Sbostic 	 */
15142273Sbostic 	len = p->fts_pathlen = p->fts_namelen;
15242273Sbostic 	bcopy(p->fts_name, stream->fts_path, len + 1);
15342273Sbostic 	if ((cp = rindex(p->fts_name, '/')) && (cp != p->fts_name || cp[1])) {
15439800Sbostic 		len = strlen(++cp);
15542273Sbostic 		bcopy(cp, p->fts_name, len + 1);
15642273Sbostic 		p->fts_namelen = len;
15739800Sbostic 	}
15842273Sbostic 	p->fts_accpath = p->fts_path = stream->fts_path;
15939800Sbostic }
16039800Sbostic 
16139800Sbostic ftsclose(sp)
16239800Sbostic 	FTS *sp;
16339800Sbostic {
16439800Sbostic 	register FTSENT *freep, *p;
16539800Sbostic 	int saved_errno;
16639800Sbostic 
167*42281Sbostic 	if (sp->fts_cur) {
168*42281Sbostic 		/*
169*42281Sbostic 		 * this still works if we haven't read anything -- the dummy
170*42281Sbostic 		 * structure points to the root list, so we step through to
171*42281Sbostic 		 * the end of the root list which has a valid parent pointer.
172*42281Sbostic 		 */
173*42281Sbostic 		for (p = sp->fts_cur; p->fts_level > ROOTPARENTLEVEL;) {
174*42281Sbostic 			freep = p;
175*42281Sbostic 			p = p->fts_link ? p->fts_link : p->fts_parent;
176*42281Sbostic 			(void)free((char *)freep);
17739800Sbostic 		}
178*42281Sbostic 		(void)free((char *)p);
179*42281Sbostic 	}
18039800Sbostic 
18139800Sbostic 	/* free up child linked list, sort array, path buffer */
18242273Sbostic 	if (sp->fts_child)
18342273Sbostic 		fts_lfree(sp->fts_child);
18442273Sbostic 	if (sp->fts_array)
18542273Sbostic 		(void)free((char *)sp->fts_array);
18642273Sbostic 	(void)free((char *)sp->fts_path);
18739800Sbostic 
18839800Sbostic 	/*
18939800Sbostic 	 * return to original directory, save errno if necessary;
19039800Sbostic 	 * free up the directory buffer
19139800Sbostic 	 */
19242273Sbostic 	if (!(sp->fts_options & FTS_NOCHDIR)) {
19342273Sbostic 		saved_errno = chdir(sp->fts_wd) ? errno : 0;
19442273Sbostic 		(void)free(sp->fts_wd);
19539800Sbostic 	}
19639800Sbostic 
19739800Sbostic 	/* free up the stream pointer */
19839800Sbostic 	(void)free((char *)sp);
19939800Sbostic 
20039800Sbostic 	/* set errno and return */
20142273Sbostic 	if (!(sp->fts_options & FTS_NOCHDIR) && saved_errno) {
20239800Sbostic 		errno = saved_errno;
20339800Sbostic 		return(-1);
20439800Sbostic 	}
20539800Sbostic 	return(0);
20639800Sbostic }
20739800Sbostic 
20839800Sbostic FTSENT *
20939800Sbostic ftsread(sp)
21039800Sbostic 	register FTS *sp;
21139800Sbostic {
21239800Sbostic 	register FTSENT *p;
21339800Sbostic 	register int instr;
21439800Sbostic 	static int cd;
21539800Sbostic 	FTSENT *tmp;
21639800Sbostic 	char *cp;
21739800Sbostic 
21839800Sbostic 	/* if finished or unrecoverable error, return NULL */
21942273Sbostic 	if (!sp->fts_cur || sp->fts_options & FTS__STOP)
22039800Sbostic 		return(NULL);
22139800Sbostic 
22239800Sbostic 	/* set global stream pointer, and current node pointer */
22339800Sbostic 	stream = sp;
22442273Sbostic 	p = sp->fts_cur;
22539800Sbostic 
22639800Sbostic 	/* save and zero out user instructions */
22742273Sbostic 	instr = p->fts_instr;
22842273Sbostic 	p->fts_instr = 0;
22939800Sbostic 
23039800Sbostic 	/* if used 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 
23639800Sbostic 	/* any type of file may be re-visited; re-stat and return */
23739800Sbostic 	if (instr == FTS_AGAIN) {
23842273Sbostic 		p->fts_info = fts_stat(p, 0);
23939800Sbostic 		return(p);
24039800Sbostic 	}
24139800Sbostic 
24242273Sbostic 	if (p->fts_info == FTS_D)
24342280Sbostic 		if (instr == FTS_SKIP || sp->fts_options & FTS_XDEV &&
24442280Sbostic 		    p->fts_statb.st_dev != sp->sdev) {
24542273Sbostic 			if (sp->fts_child) {
24642273Sbostic 				fts_lfree(sp->fts_child);
24742273Sbostic 				sp->fts_child = NULL;
24839800Sbostic 			}
24939800Sbostic 		} else {
25042273Sbostic 			if (!sp->fts_child &&
25142273Sbostic 			    (!(sp->fts_child = fts_build(sp, 1))))
25239800Sbostic 				return(p);
25342273Sbostic 			p = sp->fts_child;
25442273Sbostic 			sp->fts_child = NULL;
25542273Sbostic 			return(sp->fts_cur = p);
25639800Sbostic 		}
25742273Sbostic 	else if (p->fts_info == FTS_SL && instr == FTS_FOLLOW) {
25842273Sbostic 		p->fts_info = fts_stat(p, 1);
25939800Sbostic 		return(p);
26039800Sbostic 	}
26139800Sbostic 
26239800Sbostic 	/*
263*42281Sbostic 	 * user may have called ftsset on pointer returned by ftschildren;
264*42281Sbostic 	 * handle it here.
26539800Sbostic 	 */
26642273Sbostic 	for (p = p->fts_link; p;) {
26742273Sbostic 		instr = p->fts_instr;
26839800Sbostic 		if (instr == FTS_FOLLOW) {
26942273Sbostic 			p->fts_info = fts_stat(p, 1);
27042273Sbostic 			p->fts_instr = 0;
27139800Sbostic 			break;
27239800Sbostic 		}
27339800Sbostic 		if (instr == FTS_SKIP) {
27439800Sbostic 			tmp = p;
27542273Sbostic 			p = p->fts_link;
27639800Sbostic 			(void)free((char *)tmp);
27739800Sbostic 			continue;
27839800Sbostic 		}
27942273Sbostic 		p->fts_instr = 0;
28039800Sbostic 		break;
28139800Sbostic 	}
28239800Sbostic 
28339800Sbostic 	/* go to next node on this level */
28439800Sbostic 	if (p) {
28539800Sbostic 		/*
28639800Sbostic 		 * if root level node, set up paths and return.  If not the
28739800Sbostic 		 * first time, and it's not an absolute pathname, get back
28839800Sbostic 		 * to wherever we started from.
28939800Sbostic 		 */
29042273Sbostic 		if (p->fts_level == ROOTLEVEL) {
29139800Sbostic 			fts_load(p);
292*42281Sbostic 			(void)free((char *)sp->fts_cur);
293*42281Sbostic 			if (cd &&
294*42281Sbostic 			    p->fts_path[0] != '/' && CHDIR(sp, sp->fts_wd)) {
295*42281Sbostic 				/* return target path for error msg */
296*42281Sbostic 				p->fts_path = sp->fts_wd;
297*42281Sbostic 				p->fts_info = FTS_ERR;
298*42281Sbostic 				sp->fts_options |= FTS__STOP;
299*42281Sbostic 				return(sp->fts_cur = p);
300*42281Sbostic 			}
301*42281Sbostic 			cd = 1;
30242273Sbostic 			p->fts_info = fts_stat(p, 0);
30342280Sbostic 			sp->sdev = p->fts_statb.st_dev;
30439800Sbostic 		} else {
30542273Sbostic 			(void)free((char *)sp->fts_cur);
30642273Sbostic 			cp = sp->fts_path + NAPPEND(p->fts_parent);
30739800Sbostic 			*cp++ = '/';
30842273Sbostic 			bcopy(p->fts_name, cp, p->fts_namelen + 1);
30942273Sbostic 			if (p->fts_info == FTS_D && (tmp = fts_cycle(p))) {
31042273Sbostic 				sp->fts_savelink = p->fts_link;
31142273Sbostic 				p->fts_link = tmp;
31239800Sbostic 			}
31339800Sbostic 		}
31442273Sbostic 		return(sp->fts_cur = p);
31539800Sbostic 	}
31639800Sbostic 
31739800Sbostic 	/* go to parent */
31842273Sbostic 	p = sp->fts_cur->fts_parent;
31942273Sbostic 	(void)free((char *)sp->fts_cur);
32042273Sbostic 	if (p->fts_level == ROOTPARENTLEVEL) {
32139800Sbostic 		/*
32239800Sbostic 		 * done; free everything up and set errno to 0 so the user
32339800Sbostic 		 * can distinguish between error and EOF.
32439800Sbostic 		 */
32539800Sbostic 		(void)free((char *)p);
32639800Sbostic 		errno = 0;
32742273Sbostic 		return(sp->fts_cur = NULL);
32839800Sbostic 	}
32939800Sbostic 
33042273Sbostic 	sp->fts_path[p->fts_pathlen] = '\0';
33139800Sbostic 	if (CHDIR(sp, "..")) {
33242273Sbostic 		sp->fts_options |= FTS__STOP;
33342273Sbostic 		p->fts_info = FTS_ERR;
33439800Sbostic 	} else
33542273Sbostic 		p->fts_info = FTS_DP;
33642273Sbostic 	return(sp->fts_cur = p);
33739800Sbostic }
33839800Sbostic 
33939800Sbostic /*
34039800Sbostic  * ftsset takes the stream as an argument although it's not used in this
34139800Sbostic  * implementation; it would be necessary if anyone wanted to add global
34239800Sbostic  * semantics to fts using ftsset.  A possible error return is allowed for
34339800Sbostic  * similar reasons.
34439800Sbostic  */
34539800Sbostic /* ARGSUSED */
34639800Sbostic ftsset(sp, p, instr)
34739800Sbostic 	FTS *sp;
34839800Sbostic 	FTSENT *p;
34939800Sbostic 	int instr;
35039800Sbostic {
35142273Sbostic 	p->fts_instr = instr;
35239800Sbostic 	return(0);
35339800Sbostic }
35439800Sbostic 
35539800Sbostic FTSENT *
35639800Sbostic ftschildren(sp)
35739800Sbostic 	register FTS *sp;
35839800Sbostic {
35939800Sbostic 	/*
36039800Sbostic 	 * set errno to 0 so that user can tell the difference between an
36139800Sbostic 	 * error and a directory without entries.
36239800Sbostic 	 */
36339800Sbostic 	errno = 0;
36442273Sbostic 	if (sp->fts_cur->fts_info != FTS_D || sp->fts_options & FTS__STOP)
36539800Sbostic 		return(NULL);
36642273Sbostic 	if (sp->fts_child)
36742273Sbostic 		fts_lfree(sp->fts_child);
36842273Sbostic 	return(sp->fts_child = fts_build(sp, 0));
36939800Sbostic }
37039800Sbostic 
37139800Sbostic #define	ISDOT(a)	(a[0] == '.' && (!a[1] || a[1] == '.' && !a[2]))
37239800Sbostic 
37339800Sbostic static FTSENT *
37439962Sbostic fts_build(sp, set_node)
37539800Sbostic 	register FTS *sp;
37639962Sbostic 	int set_node;
37739800Sbostic {
37839800Sbostic 	register struct dirent *dp;
37939800Sbostic 	register FTSENT *p, *head;
38039800Sbostic 	register int nitems;
38139800Sbostic 	DIR *dirp;
38239962Sbostic 	int descend, len, level, maxlen, nlinks, saved_errno;
38339800Sbostic 	char *cp;
38439800Sbostic 
38542273Sbostic 	p = sp->fts_cur;
38642273Sbostic 	if (!(dirp = opendir(p->fts_accpath))) {
38739962Sbostic 		if (set_node) {
38839800Sbostic 			errno = 0;
38942273Sbostic 			p->fts_info = FTS_DNR;
39039800Sbostic 		}
39139800Sbostic 		return(NULL);
39239800Sbostic 	}
39339800Sbostic 
39439800Sbostic 	/*
39539800Sbostic 	 * the real slowdown in walking the tree is the stat calls.  If
39639800Sbostic 	 * FTS_NOSTAT is set and it's a physical walk (so that symbolic
39739800Sbostic 	 * links can't be directories), fts assumes that the number of
39839800Sbostic 	 * subdirectories in a node is equal to the number of links to
39939800Sbostic 	 * the parent.  This allows stat calls to be skipped in any leaf
40039800Sbostic 	 * directories and for any nodes after the directories in the
40139800Sbostic 	 * parent node have been found.  This empirically cuts the stat
40239800Sbostic 	 * calls by about 2/3.
40339800Sbostic 	 */
40442273Sbostic 	nlinks =
40542273Sbostic 	    sp->fts_options & FTS_NOSTAT && sp->fts_options & FTS_PHYSICAL ?
40642273Sbostic 	    p->fts_statb.st_nlink - (sp->fts_options & FTS_SEEDOT ? 0 : 2) : -1;
40739800Sbostic 
40839962Sbostic 	if (nlinks || set_node) {
40939962Sbostic 		if (FCHDIR(sp, dirfd(dirp))) {
41039962Sbostic 			if (set_node) {
41139962Sbostic 				errno = 0;
41242273Sbostic 				p->fts_info = FTS_DNX;
41339962Sbostic 			}
41439962Sbostic 			return(NULL);
41539962Sbostic 		}
41639962Sbostic 		descend = 1;
41739962Sbostic 	} else
41839962Sbostic 		descend = 0;
41939962Sbostic 
42040939Sbostic 	/* get max file name length that can be stored in current path */
42142273Sbostic 	maxlen = sp->fts_pathlen - p->fts_pathlen - 1;
42240939Sbostic 
42342273Sbostic 	cp = sp->fts_path + (len = NAPPEND(p));
42440939Sbostic 	*cp++ = '/';
42540939Sbostic 
42642273Sbostic 	level = p->fts_level + 1;
42740939Sbostic 
42839800Sbostic 	/* read the directory, attching each new entry to the `link' pointer */
42939800Sbostic 	for (head = NULL, nitems = 0; dp = readdir(dirp);) {
43042273Sbostic 		if (ISDOT(dp->d_name) && !(sp->fts_options & FTS_SEEDOT))
43139800Sbostic 			continue;
43239800Sbostic 
43339800Sbostic 		if (!(p = fts_alloc(dp->d_name, dp->d_namlen))) {
43439800Sbostic 			saved_errno = errno;
43539800Sbostic 			goto mem1;
43639800Sbostic 		}
43739800Sbostic 		if (dp->d_namlen > maxlen) {
43839800Sbostic 			if (!fts_path((int)dp->d_namlen)) {
43939800Sbostic 				/* quit: this stream no longer has a path */
44042273Sbostic 				sp->fts_options |= FTS__STOP;
44139800Sbostic 				saved_errno = errno;
44239800Sbostic 				(void)free((char *)p);
44339800Sbostic mem1:				fts_lfree(head);
44439962Sbostic 				if (set_node)
44542273Sbostic 					p->fts_info = FTS_ERR;
44639962Sbostic 				if (descend && CHDIR(sp, "..")) {
44739800Sbostic 					saved_errno = errno;
44842273Sbostic 					sp->fts_options |= FTS__STOP;
44939800Sbostic 				}
45039800Sbostic 				errno = saved_errno;
45139800Sbostic 				return(NULL);
45239800Sbostic 			}
45342273Sbostic 			maxlen = sp->fts_pathlen - sp->fts_cur->fts_pathlen - 1;
45439800Sbostic 		}
45539800Sbostic 
45642273Sbostic 		p->fts_pathlen = len + dp->d_namlen + 1;
45742273Sbostic 		p->fts_accpath =
45842273Sbostic 		    sp->fts_options & FTS_NOCHDIR ? p->fts_path : p->fts_name;
45939800Sbostic 
46042273Sbostic 		p->fts_parent = sp->fts_cur;
46142273Sbostic 		p->fts_level = level;
46239800Sbostic 
46339800Sbostic 		if (nlinks) {
46439800Sbostic 			/* make sure fts_stat has a filename to stat */
46542273Sbostic 			if (sp->fts_options & FTS_NOCHDIR)
46642273Sbostic 				bcopy(p->fts_name, cp, p->fts_namelen + 1);
46742273Sbostic 			p->fts_info = fts_stat(p, 0);
46842273Sbostic 			if (nlinks > 0 && (p->fts_info == FTS_D ||
46942273Sbostic 			    p->fts_info == FTS_DNR || p->fts_info == FTS_DNX))
47039800Sbostic 				--nlinks;
47139800Sbostic 		} else
47242273Sbostic 			p->fts_info = FTS_NS;
47339800Sbostic 
47442273Sbostic 		p->fts_link = head;
47539800Sbostic 		head = p;
47639800Sbostic 		++nitems;
47739800Sbostic 	}
47839800Sbostic 	(void)closedir(dirp);
47939800Sbostic 
48039962Sbostic 	if (descend && (!nitems || !set_node) && CHDIR(sp, "..")) {
48142273Sbostic 		sp->fts_options |= FTS__STOP;
48242273Sbostic 		p->fts_info = FTS_ERR;
48339962Sbostic 		*--cp = '\0';
48439962Sbostic 		return(NULL);
48539962Sbostic 	}
48639962Sbostic 
48739800Sbostic 	if (!nitems) {
48839962Sbostic 		if (set_node)
48942273Sbostic 			p->fts_info = FTS_DP;
49039800Sbostic 		*--cp = '\0';
49139800Sbostic 		return(NULL);
49239800Sbostic 	}
49339800Sbostic 
49442273Sbostic 	if (sp->fts_compar && nitems > 1)
49539800Sbostic 		head = fts_sort(head, nitems);
49639800Sbostic 
49739962Sbostic 	if (set_node)
49842273Sbostic 		bcopy(head->fts_name, cp, head->fts_namelen + 1);
49939800Sbostic 	else
50039800Sbostic 		*--cp = '\0';
50139800Sbostic 	return(head);
50239800Sbostic }
50339800Sbostic 
50439800Sbostic static short
50539800Sbostic fts_stat(p, symflag)
50639800Sbostic 	register FTSENT *p;
50739800Sbostic 	int symflag;
50839800Sbostic {
50939800Sbostic 	/*
51039800Sbostic 	 * detection of symbolic links w/o targets.  If FTS_FOLLOW is set,
51139800Sbostic 	 * the symlink structure is overwritten with the stat structure of
51239800Sbostic 	 * the target.
51339800Sbostic 	 */
51442273Sbostic 	if (stream->fts_options & FTS_LOGICAL || symflag) {
51542273Sbostic 		if (stat(p->fts_accpath, &p->fts_statb))
51642273Sbostic 			return(symflag || !lstat(p->fts_accpath,
51742273Sbostic 			    &p->fts_statb) ? FTS_SLNONE : FTS_ERR);
51842273Sbostic 	} else if (lstat(p->fts_accpath, &p->fts_statb))
51939800Sbostic 		return(FTS_ERR);
52039800Sbostic 
52142273Sbostic 	switch(p->fts_statb.st_mode&S_IFMT) {
52239800Sbostic 	case S_IFDIR:
52339800Sbostic 		return(FTS_D);
52439800Sbostic 	case S_IFLNK:
52539800Sbostic 		return(FTS_SL);
52639800Sbostic 	case S_IFREG:
52739800Sbostic 		return(FTS_F);
52839800Sbostic 	}
52940939Sbostic 	return(FTS_DEFAULT);
53039800Sbostic }
53139800Sbostic 
53239800Sbostic static FTSENT *
53339800Sbostic fts_cycle(p)
53439800Sbostic 	register FTSENT *p;
53539800Sbostic {
53639800Sbostic 	register dev_t dev;
53739800Sbostic 	register ino_t ino;
53839800Sbostic 
53939800Sbostic 	/*
54039800Sbostic 	 * cycle detection is brute force; if the tree gets deep enough or
54139800Sbostic 	 * the number of symbolic links to directories is really high
54239800Sbostic 	 * something faster might be worthwhile.
54339800Sbostic 	 */
54442273Sbostic 	dev = p->fts_statb.st_dev;
54542273Sbostic 	ino = p->fts_statb.st_ino;
54642273Sbostic 	for (p = p->fts_parent; p->fts_level > ROOTLEVEL; p = p->fts_parent)
54742273Sbostic 		if (ino == p->fts_statb.st_ino && dev == p->fts_statb.st_dev)
54839800Sbostic 			return(p);
54939800Sbostic 	return(NULL);
55039800Sbostic }
55139800Sbostic 
55239800Sbostic #define	R(type, nelem, ptr) \
55339800Sbostic 	(type *)realloc((char *)ptr, (u_int)((nelem) * sizeof(type)))
55439800Sbostic 
55539800Sbostic static FTSENT *
55639800Sbostic fts_sort(head, nitems)
55739800Sbostic 	FTSENT *head;
55839800Sbostic 	register int nitems;
55939800Sbostic {
56039800Sbostic 	register FTSENT **ap, *p;
56139800Sbostic 
56239800Sbostic 	/*
56339800Sbostic 	 * construct an array of pointers to the structures and call qsort(3).
56439800Sbostic 	 * Reassemble the array in the order returned by qsort.  If unable to
56539800Sbostic 	 * sort for memory reasons, return the directory entries in their
56639800Sbostic 	 * current order.  Allocate enough space for the current needs plus
56739800Sbostic 	 * 40 so we don't realloc one entry at a time.
56839800Sbostic 	 */
56942273Sbostic 	if (nitems > stream->fts_nitems) {
57042273Sbostic 		stream->fts_nitems = nitems + 40;
57142273Sbostic 		if (!(stream->fts_array =
57242273Sbostic 		    R(FTSENT *, stream->fts_nitems, stream->fts_array))) {
57342273Sbostic 			stream->fts_nitems = 0;
57439800Sbostic 			return(head);
57539800Sbostic 		}
57639800Sbostic 	}
57742273Sbostic 	for (ap = stream->fts_array, p = head; p; p = p->fts_link)
57839800Sbostic 		*ap++ = p;
57942273Sbostic 	qsort((char *)stream->fts_array, nitems, sizeof(FTSENT *),
58042273Sbostic 	    stream->fts_compar);
58142273Sbostic 	for (head = *(ap = stream->fts_array); --nitems; ++ap)
58242273Sbostic 		ap[0]->fts_link = ap[1];
58342273Sbostic 	ap[0]->fts_link = NULL;
58439800Sbostic 	return(head);
58539800Sbostic }
58639800Sbostic 
58739800Sbostic static FTSENT *
58839800Sbostic fts_alloc(name, len)
58939800Sbostic 	char *name;
59039800Sbostic 	register int len;
59139800Sbostic {
59239800Sbostic 	register FTSENT *p;
59339800Sbostic 
59439800Sbostic 	/*
59539800Sbostic 	 * variable sized structures; the name is the last element so
59639800Sbostic 	 * allocate enough extra space after the structure to hold it.
59739800Sbostic 	 */
59839800Sbostic 	if (!(p = (FTSENT *)malloc((u_int)(sizeof(FTSENT) + len))))
59939800Sbostic 		return(NULL);
60042273Sbostic 	bcopy(name, p->fts_name, len + 1);
60142273Sbostic 	p->fts_namelen = len;
60242273Sbostic 	p->fts_path = stream->fts_path;
60342273Sbostic 	p->fts_instr = 0;
60442273Sbostic 	p->fts_local.number = 0;
60542273Sbostic 	p->fts_local.pointer = NULL;
60639800Sbostic 	return(p);
60739800Sbostic }
60839800Sbostic 
60939800Sbostic static
61039800Sbostic fts_lfree(head)
61139800Sbostic 	register FTSENT *head;
61239800Sbostic {
61339800Sbostic 	register FTSENT *p;
61439800Sbostic 
61539800Sbostic 	while (p = head) {
61642273Sbostic 		head = head->fts_link;
61739800Sbostic 		(void)free((char *)p);
61839800Sbostic 	}
61939800Sbostic }
62039800Sbostic 
62139800Sbostic /*
62239800Sbostic  * allow essentially unlimited paths; certain programs (find, remove, ls)
62339800Sbostic  * need to work on any tree.  Most systems will allow creation of paths
62439800Sbostic  * much longer than MAXPATHLEN, even though the kernel won't resolve them.
62539800Sbostic  * Add an extra 128 bytes to the requested size so that we don't realloc
62639800Sbostic  * the path 2 bytes at a time.
62739800Sbostic  */
62839800Sbostic static
62939800Sbostic fts_path(size)
63039800Sbostic 	int size;
63139800Sbostic {
63242273Sbostic 	stream->fts_pathlen += size + 128;
63342273Sbostic 	return((int)(stream->fts_path =
63442273Sbostic 	    R(char, stream->fts_pathlen, stream->fts_path)));
63539800Sbostic }
63639800Sbostic 
63739800Sbostic static FTSENT *
63839800Sbostic fts_root(name)
63939800Sbostic 	register char *name;
64039800Sbostic {
64139800Sbostic 	register char *cp;
64239800Sbostic 
64339800Sbostic 	/*
64439800Sbostic 	 * rip trailing slashes; it's somewhat unclear in POSIX 1003.1 what
64539800Sbostic 	 * /a/b/ really is, they don't talk about what a null path component
64639800Sbostic 	 * resolves to.  This hopefully does what the user intended.  Don't
64739800Sbostic 	 * allow null pathnames.
64839800Sbostic 	 */
64939800Sbostic 	for (cp = name; *cp; ++cp);
65039800Sbostic 	if (cp == name) {
65139800Sbostic 		errno = ENOENT;
65239800Sbostic 		return(NULL);
65339800Sbostic 	}
65439800Sbostic 	while (--cp > name && *cp == '/');
65539800Sbostic 	*++cp = '\0';
65639800Sbostic 	return(fts_alloc(name, cp - name));
65739800Sbostic }
658