xref: /csrg-svn/lib/libc/gen/fts.c (revision 39962)
139800Sbostic /*
239800Sbostic  * Copyright (c) 1989 The Regents of the University of California.
339800Sbostic  * All rights reserved.
439800Sbostic  *
539800Sbostic  * Redistribution and use in source and binary forms are permitted
639800Sbostic  * provided that the above copyright notice and this paragraph are
739800Sbostic  * duplicated in all such forms and that any documentation,
839800Sbostic  * advertising materials, and other materials related to such
939800Sbostic  * distribution and use acknowledge that the software was developed
1039800Sbostic  * by the University of California, Berkeley.  The name of the
1139800Sbostic  * University may not be used to endorse or promote products derived
1239800Sbostic  * from this software without specific prior written permission.
1339800Sbostic  * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR
1439800Sbostic  * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
1539800Sbostic  * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
1639800Sbostic  */
1739800Sbostic 
1839800Sbostic #if defined(LIBC_SCCS) && !defined(lint)
19*39962Sbostic static char sccsid[] = "@(#)fts.c	5.2 (Berkeley) 01/31/90";
2039800Sbostic #endif /* LIBC_SCCS and not lint */
2139800Sbostic 
2239800Sbostic #include <sys/param.h>
2339800Sbostic #include <sys/stat.h>
2439800Sbostic #include <dirent.h>
2539800Sbostic #include <errno.h>
2639800Sbostic #include <fts.h>
2739800Sbostic #include <strings.h>
2839800Sbostic 
2939800Sbostic extern int errno;
3039800Sbostic 
3139800Sbostic FTSENT *fts_alloc(), *fts_build(), *fts_cycle(), *fts_sort(), *fts_root();
3239800Sbostic short fts_stat();
3339800Sbostic char *malloc(), *realloc();
3439800Sbostic 
3539800Sbostic /*
3639800Sbostic  * Special case a root of "/" so that slashes aren't appended causing
3739800Sbostic  * paths to be written as "//foo".
3839800Sbostic  */
3939800Sbostic #define	NAPPEND(p) \
4039800Sbostic 	(p->level == ROOTLEVEL && p->pathlen == 1 && \
4139800Sbostic 	    p->path[0] == '/' ? 0 : p->pathlen)
4239800Sbostic 
4339800Sbostic #define	CHDIR(sp, path)	(!(sp->options & FTS_NOCHDIR) && chdir(path))
44*39962Sbostic #define	FCHDIR(sp, fd)	(!(sp->options & FTS_NOCHDIR) && fchdir(fd))
4539800Sbostic 
4639800Sbostic #define	ROOTLEVEL	0
4739800Sbostic #define	ROOTPARENTLEVEL	-1
4839800Sbostic 
4939800Sbostic static FTS *stream;			/* current stream pointer */
5039800Sbostic 
5139800Sbostic FTS *
5239800Sbostic ftsopen(argv, options, compar)
5339800Sbostic 	char *argv[];
5439800Sbostic 	register int options;
5539800Sbostic 	int (*compar)();
5639800Sbostic {
5739800Sbostic 	register FTS *sp;
5839800Sbostic 	register FTSENT *p, *root;
5939800Sbostic 	register int nitems, maxlen;
6039800Sbostic 	FTSENT *parent, *tmp;
6139800Sbostic 	char wd[MAXPATHLEN], *getwd(), *strdup();
6239800Sbostic 
6339800Sbostic 	/* allocate/initialize the stream */
6439800Sbostic 	if (!(stream = sp = (FTS *)malloc((u_int)sizeof(FTS))))
6539800Sbostic 		return(NULL);
6639800Sbostic 	bzero(sp, sizeof(FTS));
6739800Sbostic 	sp->compar = compar;
6839800Sbostic 	sp->options = options;
6939800Sbostic 
7039800Sbostic 	/* allocate/initialize root's parent */
7139800Sbostic 	if (!(parent = fts_alloc("", 0)))
7239800Sbostic 		goto mem1;
7339800Sbostic 	parent->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;
8139800Sbostic 			if (maxlen < p->namelen)
8239800Sbostic 				maxlen = p->namelen;
8339800Sbostic 			/*
8439800Sbostic 			 * if comparison routine supplied, traverse in sorted
8539800Sbostic 			 * order; otherwise traverse in the order specified.
8639800Sbostic 			 */
8739800Sbostic 			if (compar) {
8839800Sbostic 				p->link = root;
8939800Sbostic 				root = p;
9039800Sbostic 				p->accpath = p->name;
9139800Sbostic 				p->info = fts_stat(p, 0);
9239800Sbostic 			} else {
9339800Sbostic 				p->link = NULL;
9439800Sbostic 				if (!root)
9539800Sbostic 					tmp = root = p;
9639800Sbostic 				else {
9739800Sbostic 					tmp->link = p;
9839800Sbostic 					tmp = p;
9939800Sbostic 				}
10039800Sbostic 			}
10139800Sbostic 			p->level = ROOTLEVEL;
10239800Sbostic 			p->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;
10939800Sbostic 		maxlen = root->namelen;
11039800Sbostic 		root->link = NULL;
11139800Sbostic 		root->level = ROOTLEVEL;
11239800Sbostic 		root->parent = parent;
11339800Sbostic 	}
11439800Sbostic 
11539800Sbostic 	/* start out with at least 1K+ of path space */
11639800Sbostic 	if (!fts_path(MAX(maxlen, MAXPATHLEN)))
11739800Sbostic 		goto mem2;
11839800Sbostic 
11939800Sbostic 	/*
12039800Sbostic 	 * some minor trickiness.  Set the pointers so that ftsread thinks
12139800Sbostic 	 * we've just finished the node before the root(s); set p->info to
12239800Sbostic 	 * FTS_NS so that everything about the "current" node is ignored.
12339800Sbostic 	 * Rather than allocate a dummy node use the root's parent's link
12439800Sbostic 	 * pointer.  This is handled specially in ftsclose() as well.
12539800Sbostic 	 */
12639800Sbostic 	sp->cur = parent;
12739800Sbostic 	parent->link = root;
12839800Sbostic 	parent->info = FTS_NS;
12939800Sbostic 
13039800Sbostic 	/*
13139800Sbostic 	 * if using chdir(2), do a getwd() to insure that we can get back
13239800Sbostic 	 * here; this could be avoided for some paths, but probably not
13339800Sbostic 	 * worth the effort.  Slashes, symbolic links, and ".." are all
13439800Sbostic 	 * fairly nasty problems.  Note, if we can't get the current
13539800Sbostic 	 * working directory we run anyway, just more slowly.
13639800Sbostic 	 */
13739800Sbostic 	if (!(options & FTS_NOCHDIR) && (!getwd(wd) || !(sp->wd = strdup(wd))))
13839800Sbostic 		sp->options |= FTS_NOCHDIR;
13939800Sbostic 
14039800Sbostic 	return(sp);
14139800Sbostic 
14239800Sbostic mem2:	fts_lfree(root);
14339800Sbostic 	(void)free((char *)parent);
14439800Sbostic mem1:	(void)free((char *)sp);
14539800Sbostic 	return(NULL);
14639800Sbostic }
14739800Sbostic 
14839800Sbostic static
14939800Sbostic fts_load(p)
15039800Sbostic 	register FTSENT *p;
15139800Sbostic {
15239800Sbostic 	register int len;
15339800Sbostic 	register char *cp;
15439800Sbostic 
15539800Sbostic 	/*
15639800Sbostic 	 * load the stream structure for the next traversal; set the
15739800Sbostic 	 * accpath field specially so the chdir gets done to the right
15839800Sbostic 	 * place and the user can access the first node.
15939800Sbostic 	 */
16039800Sbostic 	len = p->pathlen = p->namelen;
16139800Sbostic 	bcopy(p->name, stream->path, len + 1);
16239800Sbostic 	if ((cp = rindex(p->name, '/')) && (cp != p->name || cp[1])) {
16339800Sbostic 		len = strlen(++cp);
16439800Sbostic 		bcopy(cp, p->name, len + 1);
16539800Sbostic 		p->namelen = len;
16639800Sbostic 	}
16739800Sbostic 	p->accpath = p->path = stream->path;
16839800Sbostic }
16939800Sbostic 
17039800Sbostic ftsclose(sp)
17139800Sbostic 	FTS *sp;
17239800Sbostic {
17339800Sbostic 	register FTSENT *freep, *p;
17439800Sbostic 	int saved_errno;
17539800Sbostic 
17639800Sbostic 	if (sp->cur)
17739800Sbostic 		/* check for never having read anything */
17839800Sbostic 		if (sp->cur->level == ROOTPARENTLEVEL)
17939800Sbostic 			fts_lfree(sp->cur);
18039800Sbostic 		else {
18139800Sbostic 			for (p = sp->cur; p->level > ROOTPARENTLEVEL;) {
18239800Sbostic 				freep = p;
18339800Sbostic 				p = p->link ? p->link : p->parent;
18439800Sbostic 				(void)free((char *)freep);
18539800Sbostic 			}
18639800Sbostic 			(void)free((char *)p);
18739800Sbostic 		}
18839800Sbostic 
18939800Sbostic 	/* free up child linked list, sort array, path buffer */
19039800Sbostic 	if (sp->child)
19139800Sbostic 		fts_lfree(sp->child);
19239800Sbostic 	if (sp->array)
19339800Sbostic 		(void)free((char *)sp->array);
19439800Sbostic 	(void)free((char *)sp->path);
19539800Sbostic 
19639800Sbostic 	/*
19739800Sbostic 	 * return to original directory, save errno if necessary;
19839800Sbostic 	 * free up the directory buffer
19939800Sbostic 	 */
20039800Sbostic 	if (!(sp->options & FTS_NOCHDIR)) {
20139800Sbostic 		saved_errno = chdir(sp->wd) ? errno : 0;
20239800Sbostic 		(void)free(sp->wd);
20339800Sbostic 	}
20439800Sbostic 
20539800Sbostic 	/* free up the stream pointer */
20639800Sbostic 	(void)free((char *)sp);
20739800Sbostic 
20839800Sbostic 	/* set errno and return */
20939800Sbostic 	if (!(sp->options & FTS_NOCHDIR) && saved_errno) {
21039800Sbostic 		errno = saved_errno;
21139800Sbostic 		return(-1);
21239800Sbostic 	}
21339800Sbostic 	return(0);
21439800Sbostic }
21539800Sbostic 
21639800Sbostic FTSENT *
21739800Sbostic ftsread(sp)
21839800Sbostic 	register FTS *sp;
21939800Sbostic {
22039800Sbostic 	register FTSENT *p;
22139800Sbostic 	register int instr;
22239800Sbostic 	static int cd;
22339800Sbostic 	FTSENT *tmp;
22439800Sbostic 	char *cp;
22539800Sbostic 
22639800Sbostic 	/* if finished or unrecoverable error, return NULL */
22739800Sbostic 	if (!sp->cur || sp->options & FTS__STOP)
22839800Sbostic 		return(NULL);
22939800Sbostic 
23039800Sbostic 	/* set global stream pointer, and current node pointer */
23139800Sbostic 	stream = sp;
23239800Sbostic 	p = sp->cur;
23339800Sbostic 
23439800Sbostic 	/* save and zero out user instructions */
23539800Sbostic 	instr = p->instr;
23639800Sbostic 	p->instr = 0;
23739800Sbostic 
23839800Sbostic 	/* if used link pointer for cycle detection, restore it */
23939800Sbostic 	if (sp->savelink) {
24039800Sbostic 		p->link = sp->savelink;
24139800Sbostic 		sp->savelink = NULL;
24239800Sbostic 	}
24339800Sbostic 
24439800Sbostic 	/* any type of file may be re-visited; re-stat and return */
24539800Sbostic 	if (instr == FTS_AGAIN) {
24639800Sbostic 		p->info = fts_stat(p, 0);
24739800Sbostic 		return(p);
24839800Sbostic 	}
24939800Sbostic 
25039800Sbostic 	if (p->info == FTS_D)
25139800Sbostic 		if (instr == FTS_SKIP) {
25239800Sbostic 			if (sp->child) {
25339800Sbostic 				fts_lfree(sp->child);
25439800Sbostic 				sp->child = NULL;
25539800Sbostic 			}
25639800Sbostic 		} else {
257*39962Sbostic 			if (!sp->child && (!(sp->child = fts_build(sp, 1))))
25839800Sbostic 				return(p);
25939800Sbostic 			p = sp->child;
26039800Sbostic 			sp->child = NULL;
26139800Sbostic 			return(sp->cur = p);
26239800Sbostic 		}
26339800Sbostic 	else if (p->info == FTS_SL && instr == FTS_FOLLOW) {
26439800Sbostic 		p->info = fts_stat(p, 1);
26539800Sbostic 		return(p);
26639800Sbostic 	}
26739800Sbostic 
26839800Sbostic 	/*
26939800Sbostic 	 * user may have called ftsset on pointer returned by
27039800Sbostic 	 * ftschildren; handle it here.
27139800Sbostic 	 */
27239800Sbostic 	for (p = p->link; p;) {
27339800Sbostic 		instr = p->instr;
27439800Sbostic 		if (instr == FTS_FOLLOW) {
27539800Sbostic 			p->info = fts_stat(p, 1);
27639800Sbostic 			p->instr = 0;
27739800Sbostic 			break;
27839800Sbostic 		}
27939800Sbostic 		if (instr == FTS_SKIP) {
28039800Sbostic 			tmp = p;
28139800Sbostic 			p = p->link;
28239800Sbostic 			(void)free((char *)tmp);
28339800Sbostic 			continue;
28439800Sbostic 		}
28539800Sbostic 		p->instr = 0;
28639800Sbostic 		break;
28739800Sbostic 	}
28839800Sbostic 
28939800Sbostic 	/* go to next node on this level */
29039800Sbostic 	if (p) {
29139800Sbostic 		/*
29239800Sbostic 		 * if root level node, set up paths and return.  If not the
29339800Sbostic 		 * first time, and it's not an absolute pathname, get back
29439800Sbostic 		 * to wherever we started from.
29539800Sbostic 		 */
29639800Sbostic 		if (p->level == ROOTLEVEL) {
29739800Sbostic 			fts_load(p);
29839800Sbostic 			if (cd) {
29939800Sbostic 				(void)free((char *)sp->cur);
30039800Sbostic 				if (p->path[0] != '/' && CHDIR(sp, sp->wd)) {
30139800Sbostic 					/* return target path for error msg */
30239800Sbostic 					p->path = sp->wd;
30339800Sbostic 					p->info = FTS_ERR;
30439800Sbostic 					sp->options |= FTS__STOP;
30539800Sbostic 					return(sp->cur = p);
30639800Sbostic 				}
30739800Sbostic 			} else
30839800Sbostic 				cd = 1;
30939800Sbostic 			p->info = fts_stat(p, 0);
31039800Sbostic 		} else {
31139800Sbostic 			(void)free((char *)sp->cur);
31239800Sbostic 			cp = sp->path + NAPPEND(p->parent);
31339800Sbostic 			*cp++ = '/';
31439800Sbostic 			bcopy(p->name, cp, p->namelen + 1);
31539800Sbostic 			if (p->info == FTS_D && (tmp = fts_cycle(p))) {
31639800Sbostic 				sp->savelink = p->link;
31739800Sbostic 				p->link = tmp;
31839800Sbostic 			}
31939800Sbostic 		}
32039800Sbostic 		return(sp->cur = p);
32139800Sbostic 	}
32239800Sbostic 
32339800Sbostic 	/* go to parent */
32439800Sbostic 	p = sp->cur->parent;
32539800Sbostic 	(void)free((char *)sp->cur);
32639800Sbostic 	if (p->level == ROOTPARENTLEVEL) {
32739800Sbostic 		/*
32839800Sbostic 		 * done; free everything up and set errno to 0 so the user
32939800Sbostic 		 * can distinguish between error and EOF.
33039800Sbostic 		 */
33139800Sbostic 		(void)free((char *)p);
33239800Sbostic 		errno = 0;
33339800Sbostic 		return(sp->cur = NULL);
33439800Sbostic 	}
33539800Sbostic 
33639800Sbostic 	sp->path[p->pathlen] = '\0';
33739800Sbostic 	if (CHDIR(sp, "..")) {
33839800Sbostic 		sp->options |= FTS__STOP;
33939800Sbostic 		p->info = FTS_ERR;
34039800Sbostic 	} else
34139800Sbostic 		p->info = FTS_DP;
34239800Sbostic 	return(sp->cur = p);
34339800Sbostic }
34439800Sbostic 
34539800Sbostic /*
34639800Sbostic  * ftsset takes the stream as an argument although it's not used in this
34739800Sbostic  * implementation; it would be necessary if anyone wanted to add global
34839800Sbostic  * semantics to fts using ftsset.  A possible error return is allowed for
34939800Sbostic  * similar reasons.
35039800Sbostic  */
35139800Sbostic /* ARGSUSED */
35239800Sbostic ftsset(sp, p, instr)
35339800Sbostic 	FTS *sp;
35439800Sbostic 	FTSENT *p;
35539800Sbostic 	int instr;
35639800Sbostic {
35739800Sbostic 	p->instr = instr;
35839800Sbostic 	return(0);
35939800Sbostic }
36039800Sbostic 
36139800Sbostic FTSENT *
36239800Sbostic ftschildren(sp)
36339800Sbostic 	register FTS *sp;
36439800Sbostic {
36539800Sbostic 	/*
36639800Sbostic 	 * set errno to 0 so that user can tell the difference between an
36739800Sbostic 	 * error and a directory without entries.
36839800Sbostic 	 */
36939800Sbostic 	errno = 0;
37039800Sbostic 	if (sp->cur->info != FTS_D || sp->options & FTS__STOP)
37139800Sbostic 		return(NULL);
37239800Sbostic 	if (sp->child)
37339800Sbostic 		fts_lfree(sp->child);
374*39962Sbostic 	return(sp->child = fts_build(sp, 0));
37539800Sbostic }
37639800Sbostic 
37739800Sbostic #define	ISDOT(a)	(a[0] == '.' && (!a[1] || a[1] == '.' && !a[2]))
37839800Sbostic 
37939800Sbostic static FTSENT *
380*39962Sbostic fts_build(sp, set_node)
38139800Sbostic 	register FTS *sp;
382*39962Sbostic 	int set_node;
38339800Sbostic {
38439800Sbostic 	register struct dirent *dp;
38539800Sbostic 	register FTSENT *p, *head;
38639800Sbostic 	register int nitems;
38739800Sbostic 	DIR *dirp;
388*39962Sbostic 	int descend, len, level, maxlen, nlinks, saved_errno;
38939800Sbostic 	char *cp;
39039800Sbostic 
39139800Sbostic 	p = sp->cur;
39239800Sbostic 	if (!(dirp = opendir(p->accpath))) {
393*39962Sbostic 		if (set_node) {
39439800Sbostic 			errno = 0;
39539800Sbostic 			p->info = FTS_DNR;
39639800Sbostic 		}
39739800Sbostic 		return(NULL);
39839800Sbostic 	}
39939800Sbostic 
40039800Sbostic 	/*
40139800Sbostic 	 * the real slowdown in walking the tree is the stat calls.  If
40239800Sbostic 	 * FTS_NOSTAT is set and it's a physical walk (so that symbolic
40339800Sbostic 	 * links can't be directories), fts assumes that the number of
40439800Sbostic 	 * subdirectories in a node is equal to the number of links to
40539800Sbostic 	 * the parent.  This allows stat calls to be skipped in any leaf
40639800Sbostic 	 * directories and for any nodes after the directories in the
40739800Sbostic 	 * parent node have been found.  This empirically cuts the stat
40839800Sbostic 	 * calls by about 2/3.
40939800Sbostic 	 */
41039800Sbostic 	nlinks = sp->options & FTS_NOSTAT && sp->options & FTS_PHYSICAL ?
41139800Sbostic 	    p->statb.st_nlink - (sp->options & FTS_SEEDOT ? 0 : 2) : -1;
41239800Sbostic 
41339800Sbostic 	/* get max file name length that can be stored in current path */
41439800Sbostic 	maxlen = sp->pathlen - p->pathlen - 1;
41539800Sbostic 
41639800Sbostic 	cp = sp->path + (len = NAPPEND(p));
41739800Sbostic 	*cp++ = '/';
41839800Sbostic 
41939800Sbostic 	level = p->level + 1;
42039800Sbostic 
421*39962Sbostic 	if (nlinks || set_node) {
422*39962Sbostic 		if (FCHDIR(sp, dirfd(dirp))) {
423*39962Sbostic 			if (set_node) {
424*39962Sbostic 				errno = 0;
425*39962Sbostic 				p->info = FTS_DNX;
426*39962Sbostic 			}
427*39962Sbostic 			return(NULL);
428*39962Sbostic 		}
429*39962Sbostic 		descend = 1;
430*39962Sbostic 	} else
431*39962Sbostic 		descend = 0;
432*39962Sbostic 
43339800Sbostic 	/* read the directory, attching each new entry to the `link' pointer */
43439800Sbostic 	for (head = NULL, nitems = 0; dp = readdir(dirp);) {
43539800Sbostic 		if (ISDOT(dp->d_name) && !(sp->options & FTS_SEEDOT))
43639800Sbostic 			continue;
43739800Sbostic 
43839800Sbostic 		if (!(p = fts_alloc(dp->d_name, dp->d_namlen))) {
43939800Sbostic 			saved_errno = errno;
44039800Sbostic 			goto mem1;
44139800Sbostic 		}
44239800Sbostic 		if (dp->d_namlen > maxlen) {
44339800Sbostic 			if (!fts_path((int)dp->d_namlen)) {
44439800Sbostic 				/* quit: this stream no longer has a path */
44539800Sbostic 				sp->options |= FTS__STOP;
44639800Sbostic 				saved_errno = errno;
44739800Sbostic 				(void)free((char *)p);
44839800Sbostic mem1:				fts_lfree(head);
449*39962Sbostic 				if (set_node)
45039800Sbostic 					p->info = FTS_ERR;
451*39962Sbostic 				if (descend && CHDIR(sp, "..")) {
45239800Sbostic 					saved_errno = errno;
45339800Sbostic 					sp->options |= FTS__STOP;
45439800Sbostic 				}
45539800Sbostic 				errno = saved_errno;
45639800Sbostic 				return(NULL);
45739800Sbostic 			}
45839800Sbostic 			maxlen = sp->pathlen - sp->cur->pathlen - 1;
45939800Sbostic 		}
46039800Sbostic 
46139800Sbostic 		p->pathlen = len + dp->d_namlen + 1;
46239800Sbostic 		p->accpath = sp->options & FTS_NOCHDIR ? p->path : p->name;
46339800Sbostic 
46439800Sbostic 		p->parent = sp->cur;
46539800Sbostic 		p->level = level;
46639800Sbostic 
46739800Sbostic 		if (nlinks) {
46839800Sbostic 			/* make sure fts_stat has a filename to stat */
46939800Sbostic 			if (sp->options & FTS_NOCHDIR)
47039800Sbostic 				bcopy(p->name, cp, p->namelen + 1);
47139800Sbostic 			p->info = fts_stat(p, 0);
47239800Sbostic 			if (nlinks > 0 && (p->info == FTS_D ||
47339800Sbostic 			    p->info == FTS_DNR || p->info == FTS_DNX))
47439800Sbostic 				--nlinks;
47539800Sbostic 		} else
47639800Sbostic 			p->info = FTS_NS;
47739800Sbostic 
47839800Sbostic 		p->link = head;
47939800Sbostic 		head = p;
48039800Sbostic 		++nitems;
48139800Sbostic 	}
48239800Sbostic 	(void)closedir(dirp);
48339800Sbostic 
484*39962Sbostic 	if (descend && (!nitems || !set_node) && CHDIR(sp, "..")) {
485*39962Sbostic 		sp->options |= FTS__STOP;
486*39962Sbostic 		p->info = FTS_ERR;
487*39962Sbostic 		*--cp = '\0';
488*39962Sbostic 		return(NULL);
489*39962Sbostic 	}
490*39962Sbostic 
49139800Sbostic 	if (!nitems) {
492*39962Sbostic 		if (set_node)
49339800Sbostic 			p->info = FTS_DP;
49439800Sbostic 		*--cp = '\0';
49539800Sbostic 		return(NULL);
49639800Sbostic 	}
49739800Sbostic 
49839800Sbostic 	if (sp->compar && nitems > 1)
49939800Sbostic 		head = fts_sort(head, nitems);
50039800Sbostic 
501*39962Sbostic 	if (set_node)
50239800Sbostic 		bcopy(head->name, cp, head->namelen + 1);
50339800Sbostic 	else
50439800Sbostic 		*--cp = '\0';
50539800Sbostic 	return(head);
50639800Sbostic }
50739800Sbostic 
50839800Sbostic static short
50939800Sbostic fts_stat(p, symflag)
51039800Sbostic 	register FTSENT *p;
51139800Sbostic 	int symflag;
51239800Sbostic {
51339800Sbostic 	register int ngroups, *gp;
51439800Sbostic 	int gidset[NGROUPS];
51539800Sbostic 
51639800Sbostic 	/*
51739800Sbostic 	 * detection of symbolic links w/o targets.  If FTS_FOLLOW is set,
51839800Sbostic 	 * the symlink structure is overwritten with the stat structure of
51939800Sbostic 	 * the target.
52039800Sbostic 	 */
52139800Sbostic 	if (stream->options & FTS_LOGICAL || symflag) {
52239800Sbostic 		if (stat(p->accpath, &p->statb))
52339800Sbostic 			return(symflag || !lstat(p->accpath, &p->statb) ?
52439800Sbostic 			    FTS_SLNONE : FTS_ERR);
52539800Sbostic 	} else if (lstat(p->accpath, &p->statb))
52639800Sbostic 		return(FTS_ERR);
52739800Sbostic 
52839800Sbostic 	switch(p->statb.st_mode&S_IFMT) {
52939800Sbostic 	case S_IFDIR:
53039800Sbostic 		/* get new uid/groups each time, they may have changed */
53139800Sbostic 		if (getuid() == p->statb.st_uid) {
53239800Sbostic 			if (!(p->statb.st_mode&S_IRUSR))
53339800Sbostic 				return(FTS_DNR);
53439800Sbostic 			if (!(p->statb.st_mode&S_IXUSR))
53539800Sbostic 				return(FTS_DNX);
53639800Sbostic 			return(FTS_D);
53739800Sbostic 		}
53839800Sbostic 		if ((ngroups = getgroups(NGROUPS, gidset)) == -1)
53939800Sbostic 			return(FTS_ERR);
54039800Sbostic 		for (gp = gidset; ngroups--;)
54139800Sbostic 			if (*gp++ == p->statb.st_gid) {
54239800Sbostic 				if (!(p->statb.st_mode&S_IRGRP))
54339800Sbostic 					return(FTS_DNR);
54439800Sbostic 				if (!(p->statb.st_mode&S_IXGRP))
54539800Sbostic 					return(FTS_DNX);
54639800Sbostic 				return(FTS_D);
54739800Sbostic 			}
54839800Sbostic 		if (!(p->statb.st_mode&S_IROTH))
54939800Sbostic 			return(FTS_DNR);
55039800Sbostic 		if (!(p->statb.st_mode&S_IXOTH))
55139800Sbostic 			return(FTS_DNX);
55239800Sbostic 		return(FTS_D);
55339800Sbostic 	case S_IFLNK:
55439800Sbostic 		return(FTS_SL);
55539800Sbostic 	case S_IFREG:
55639800Sbostic 		return(FTS_F);
55739800Sbostic 	default:
55839800Sbostic 		return(FTS_DEFAULT);
55939800Sbostic 	}
56039800Sbostic 	/* NOTREACHED */
56139800Sbostic }
56239800Sbostic 
56339800Sbostic static FTSENT *
56439800Sbostic fts_cycle(p)
56539800Sbostic 	register FTSENT *p;
56639800Sbostic {
56739800Sbostic 	register dev_t dev;
56839800Sbostic 	register ino_t ino;
56939800Sbostic 
57039800Sbostic 	/*
57139800Sbostic 	 * cycle detection is brute force; if the tree gets deep enough or
57239800Sbostic 	 * the number of symbolic links to directories is really high
57339800Sbostic 	 * something faster might be worthwhile.
57439800Sbostic 	 */
57539800Sbostic 	dev = p->statb.st_dev;
57639800Sbostic 	ino = p->statb.st_ino;
57739800Sbostic 	for (p = p->parent; p->level > ROOTLEVEL; p = p->parent)
57839800Sbostic 		if (ino == p->statb.st_ino && dev == p->statb.st_dev)
57939800Sbostic 			return(p);
58039800Sbostic 	return(NULL);
58139800Sbostic }
58239800Sbostic 
58339800Sbostic #define	R(type, nelem, ptr) \
58439800Sbostic 	(type *)realloc((char *)ptr, (u_int)((nelem) * sizeof(type)))
58539800Sbostic 
58639800Sbostic static FTSENT *
58739800Sbostic fts_sort(head, nitems)
58839800Sbostic 	FTSENT *head;
58939800Sbostic 	register int nitems;
59039800Sbostic {
59139800Sbostic 	register FTSENT **ap, *p;
59239800Sbostic 
59339800Sbostic 	/*
59439800Sbostic 	 * construct an array of pointers to the structures and call qsort(3).
59539800Sbostic 	 * Reassemble the array in the order returned by qsort.  If unable to
59639800Sbostic 	 * sort for memory reasons, return the directory entries in their
59739800Sbostic 	 * current order.  Allocate enough space for the current needs plus
59839800Sbostic 	 * 40 so we don't realloc one entry at a time.
59939800Sbostic 	 */
60039800Sbostic 	if (nitems > stream->nitems) {
60139800Sbostic 		stream->nitems = nitems + 40;
60239800Sbostic 		if (!(stream->array =
60339800Sbostic 		    R(FTSENT *, stream->nitems, stream->array))) {
60439800Sbostic 			stream->nitems = 0;
60539800Sbostic 			return(head);
60639800Sbostic 		}
60739800Sbostic 	}
60839800Sbostic 	for (ap = stream->array, p = head; p; p = p->link)
60939800Sbostic 		*ap++ = p;
61039800Sbostic 	qsort((char *)stream->array, nitems, sizeof(FTSENT *), stream->compar);
61139800Sbostic 	for (head = *(ap = stream->array); --nitems; ++ap)
61239800Sbostic 		ap[0]->link = ap[1];
61339800Sbostic 	ap[0]->link = NULL;
61439800Sbostic 	return(head);
61539800Sbostic }
61639800Sbostic 
61739800Sbostic static FTSENT *
61839800Sbostic fts_alloc(name, len)
61939800Sbostic 	char *name;
62039800Sbostic 	register int len;
62139800Sbostic {
62239800Sbostic 	register FTSENT *p;
62339800Sbostic 
62439800Sbostic 	/*
62539800Sbostic 	 * variable sized structures; the name is the last element so
62639800Sbostic 	 * allocate enough extra space after the structure to hold it.
62739800Sbostic 	 */
62839800Sbostic 	if (!(p = (FTSENT *)malloc((u_int)(sizeof(FTSENT) + len))))
62939800Sbostic 		return(NULL);
63039800Sbostic 	bcopy(name, p->name, len + 1);
63139800Sbostic 	p->namelen = len;
63239800Sbostic 	p->path = stream->path;
63339800Sbostic 	p->instr = 0;
63439800Sbostic 	p->local.number = 0;
63539800Sbostic 	p->local.pointer = NULL;
63639800Sbostic 	return(p);
63739800Sbostic }
63839800Sbostic 
63939800Sbostic static
64039800Sbostic fts_lfree(head)
64139800Sbostic 	register FTSENT *head;
64239800Sbostic {
64339800Sbostic 	register FTSENT *p;
64439800Sbostic 
64539800Sbostic 	while (p = head) {
64639800Sbostic 		head = head->link;
64739800Sbostic 		(void)free((char *)p);
64839800Sbostic 	}
64939800Sbostic }
65039800Sbostic 
65139800Sbostic /*
65239800Sbostic  * allow essentially unlimited paths; certain programs (find, remove, ls)
65339800Sbostic  * need to work on any tree.  Most systems will allow creation of paths
65439800Sbostic  * much longer than MAXPATHLEN, even though the kernel won't resolve them.
65539800Sbostic  * Add an extra 128 bytes to the requested size so that we don't realloc
65639800Sbostic  * the path 2 bytes at a time.
65739800Sbostic  */
65839800Sbostic static
65939800Sbostic fts_path(size)
66039800Sbostic 	int size;
66139800Sbostic {
66239800Sbostic 	stream->pathlen += size + 128;
66339800Sbostic 	return((int)(stream->path = R(char, stream->pathlen, stream->path)));
66439800Sbostic }
66539800Sbostic 
66639800Sbostic static FTSENT *
66739800Sbostic fts_root(name)
66839800Sbostic 	register char *name;
66939800Sbostic {
67039800Sbostic 	register char *cp;
67139800Sbostic 
67239800Sbostic 	/*
67339800Sbostic 	 * rip trailing slashes; it's somewhat unclear in POSIX 1003.1 what
67439800Sbostic 	 * /a/b/ really is, they don't talk about what a null path component
67539800Sbostic 	 * resolves to.  This hopefully does what the user intended.  Don't
67639800Sbostic 	 * allow null pathnames.
67739800Sbostic 	 */
67839800Sbostic 	for (cp = name; *cp; ++cp);
67939800Sbostic 	if (cp == name) {
68039800Sbostic 		errno = ENOENT;
68139800Sbostic 		return(NULL);
68239800Sbostic 	}
68339800Sbostic 	while (--cp > name && *cp == '/');
68439800Sbostic 	*++cp = '\0';
68539800Sbostic 	return(fts_alloc(name, cp - name));
68639800Sbostic }
687