xref: /csrg-svn/lib/libc/gen/fts.c (revision 39800)
1*39800Sbostic /*
2*39800Sbostic  * Copyright (c) 1989 The Regents of the University of California.
3*39800Sbostic  * All rights reserved.
4*39800Sbostic  *
5*39800Sbostic  * Redistribution and use in source and binary forms are permitted
6*39800Sbostic  * provided that the above copyright notice and this paragraph are
7*39800Sbostic  * duplicated in all such forms and that any documentation,
8*39800Sbostic  * advertising materials, and other materials related to such
9*39800Sbostic  * distribution and use acknowledge that the software was developed
10*39800Sbostic  * by the University of California, Berkeley.  The name of the
11*39800Sbostic  * University may not be used to endorse or promote products derived
12*39800Sbostic  * from this software without specific prior written permission.
13*39800Sbostic  * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR
14*39800Sbostic  * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
15*39800Sbostic  * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
16*39800Sbostic  */
17*39800Sbostic 
18*39800Sbostic #if defined(LIBC_SCCS) && !defined(lint)
19*39800Sbostic static char sccsid[] = "@(#)fts.c	5.1 (Berkeley) 12/30/89";
20*39800Sbostic #endif /* LIBC_SCCS and not lint */
21*39800Sbostic 
22*39800Sbostic #include <sys/param.h>
23*39800Sbostic #include <sys/stat.h>
24*39800Sbostic #include <dirent.h>
25*39800Sbostic #include <errno.h>
26*39800Sbostic #include <fts.h>
27*39800Sbostic #include <strings.h>
28*39800Sbostic 
29*39800Sbostic extern int errno;
30*39800Sbostic 
31*39800Sbostic FTSENT *fts_alloc(), *fts_build(), *fts_cycle(), *fts_sort(), *fts_root();
32*39800Sbostic short fts_stat();
33*39800Sbostic char *malloc(), *realloc();
34*39800Sbostic 
35*39800Sbostic /*
36*39800Sbostic  * Special case a root of "/" so that slashes aren't appended causing
37*39800Sbostic  * paths to be written as "//foo".
38*39800Sbostic  */
39*39800Sbostic #define	NAPPEND(p) \
40*39800Sbostic 	(p->level == ROOTLEVEL && p->pathlen == 1 && \
41*39800Sbostic 	    p->path[0] == '/' ? 0 : p->pathlen)
42*39800Sbostic 
43*39800Sbostic #define	CHDIR(sp, path)	(!(sp->options & FTS_NOCHDIR) && chdir(path))
44*39800Sbostic 
45*39800Sbostic #define	ROOTLEVEL	0
46*39800Sbostic #define	ROOTPARENTLEVEL	-1
47*39800Sbostic 
48*39800Sbostic static FTS *stream;			/* current stream pointer */
49*39800Sbostic 
50*39800Sbostic FTS *
51*39800Sbostic ftsopen(argv, options, compar)
52*39800Sbostic 	char *argv[];
53*39800Sbostic 	register int options;
54*39800Sbostic 	int (*compar)();
55*39800Sbostic {
56*39800Sbostic 	register FTS *sp;
57*39800Sbostic 	register FTSENT *p, *root;
58*39800Sbostic 	register int nitems, maxlen;
59*39800Sbostic 	FTSENT *parent, *tmp;
60*39800Sbostic 	char wd[MAXPATHLEN], *getwd(), *strdup();
61*39800Sbostic 
62*39800Sbostic 	/* allocate/initialize the stream */
63*39800Sbostic 	if (!(stream = sp = (FTS *)malloc((u_int)sizeof(FTS))))
64*39800Sbostic 		return(NULL);
65*39800Sbostic 	bzero(sp, sizeof(FTS));
66*39800Sbostic 	sp->compar = compar;
67*39800Sbostic 	sp->options = options;
68*39800Sbostic 
69*39800Sbostic 	/* allocate/initialize root's parent */
70*39800Sbostic 	if (!(parent = fts_alloc("", 0)))
71*39800Sbostic 		goto mem1;
72*39800Sbostic 	parent->level = ROOTPARENTLEVEL;
73*39800Sbostic 
74*39800Sbostic 	/* allocate/initialize root(s) */
75*39800Sbostic 	if (options & FTS_MULTIPLE) {
76*39800Sbostic 		maxlen = -1;
77*39800Sbostic 		for (root = NULL, nitems = 0; *argv; ++argv, ++nitems) {
78*39800Sbostic 			if (!(p = fts_root(*argv)))
79*39800Sbostic 				goto mem2;
80*39800Sbostic 			if (maxlen < p->namelen)
81*39800Sbostic 				maxlen = p->namelen;
82*39800Sbostic 			/*
83*39800Sbostic 			 * if comparison routine supplied, traverse in sorted
84*39800Sbostic 			 * order; otherwise traverse in the order specified.
85*39800Sbostic 			 */
86*39800Sbostic 			if (compar) {
87*39800Sbostic 				p->link = root;
88*39800Sbostic 				root = p;
89*39800Sbostic 				p->accpath = p->name;
90*39800Sbostic 				p->info = fts_stat(p, 0);
91*39800Sbostic 			} else {
92*39800Sbostic 				p->link = NULL;
93*39800Sbostic 				if (!root)
94*39800Sbostic 					tmp = root = p;
95*39800Sbostic 				else {
96*39800Sbostic 					tmp->link = p;
97*39800Sbostic 					tmp = p;
98*39800Sbostic 				}
99*39800Sbostic 			}
100*39800Sbostic 			p->level = ROOTLEVEL;
101*39800Sbostic 			p->parent = parent;
102*39800Sbostic 		}
103*39800Sbostic 		if (compar && nitems > 1)
104*39800Sbostic 			root = fts_sort(root, nitems);
105*39800Sbostic 	} else {
106*39800Sbostic 		if (!(root = fts_root((char *)argv)))
107*39800Sbostic 			goto mem2;
108*39800Sbostic 		maxlen = root->namelen;
109*39800Sbostic 		root->link = NULL;
110*39800Sbostic 		root->level = ROOTLEVEL;
111*39800Sbostic 		root->parent = parent;
112*39800Sbostic 	}
113*39800Sbostic 
114*39800Sbostic 	/* start out with at least 1K+ of path space */
115*39800Sbostic 	if (!fts_path(MAX(maxlen, MAXPATHLEN)))
116*39800Sbostic 		goto mem2;
117*39800Sbostic 
118*39800Sbostic 	/*
119*39800Sbostic 	 * some minor trickiness.  Set the pointers so that ftsread thinks
120*39800Sbostic 	 * we've just finished the node before the root(s); set p->info to
121*39800Sbostic 	 * FTS_NS so that everything about the "current" node is ignored.
122*39800Sbostic 	 * Rather than allocate a dummy node use the root's parent's link
123*39800Sbostic 	 * pointer.  This is handled specially in ftsclose() as well.
124*39800Sbostic 	 */
125*39800Sbostic 	sp->cur = parent;
126*39800Sbostic 	parent->link = root;
127*39800Sbostic 	parent->info = FTS_NS;
128*39800Sbostic 
129*39800Sbostic 	/*
130*39800Sbostic 	 * if using chdir(2), do a getwd() to insure that we can get back
131*39800Sbostic 	 * here; this could be avoided for some paths, but probably not
132*39800Sbostic 	 * worth the effort.  Slashes, symbolic links, and ".." are all
133*39800Sbostic 	 * fairly nasty problems.  Note, if we can't get the current
134*39800Sbostic 	 * working directory we run anyway, just more slowly.
135*39800Sbostic 	 */
136*39800Sbostic 	if (!(options & FTS_NOCHDIR) && (!getwd(wd) || !(sp->wd = strdup(wd))))
137*39800Sbostic 		sp->options |= FTS_NOCHDIR;
138*39800Sbostic 
139*39800Sbostic 	return(sp);
140*39800Sbostic 
141*39800Sbostic mem2:	fts_lfree(root);
142*39800Sbostic 	(void)free((char *)parent);
143*39800Sbostic mem1:	(void)free((char *)sp);
144*39800Sbostic 	return(NULL);
145*39800Sbostic }
146*39800Sbostic 
147*39800Sbostic static
148*39800Sbostic fts_load(p)
149*39800Sbostic 	register FTSENT *p;
150*39800Sbostic {
151*39800Sbostic 	register int len;
152*39800Sbostic 	register char *cp;
153*39800Sbostic 
154*39800Sbostic 	/*
155*39800Sbostic 	 * load the stream structure for the next traversal; set the
156*39800Sbostic 	 * accpath field specially so the chdir gets done to the right
157*39800Sbostic 	 * place and the user can access the first node.
158*39800Sbostic 	 */
159*39800Sbostic 	len = p->pathlen = p->namelen;
160*39800Sbostic 	bcopy(p->name, stream->path, len + 1);
161*39800Sbostic 	if ((cp = rindex(p->name, '/')) && (cp != p->name || cp[1])) {
162*39800Sbostic 		len = strlen(++cp);
163*39800Sbostic 		bcopy(cp, p->name, len + 1);
164*39800Sbostic 		p->namelen = len;
165*39800Sbostic 	}
166*39800Sbostic 	p->accpath = p->path = stream->path;
167*39800Sbostic }
168*39800Sbostic 
169*39800Sbostic ftsclose(sp)
170*39800Sbostic 	FTS *sp;
171*39800Sbostic {
172*39800Sbostic 	register FTSENT *freep, *p;
173*39800Sbostic 	int saved_errno;
174*39800Sbostic 
175*39800Sbostic 	if (sp->cur)
176*39800Sbostic 		/* check for never having read anything */
177*39800Sbostic 		if (sp->cur->level == ROOTPARENTLEVEL)
178*39800Sbostic 			fts_lfree(sp->cur);
179*39800Sbostic 		else {
180*39800Sbostic 			for (p = sp->cur; p->level > ROOTPARENTLEVEL;) {
181*39800Sbostic 				freep = p;
182*39800Sbostic 				p = p->link ? p->link : p->parent;
183*39800Sbostic 				(void)free((char *)freep);
184*39800Sbostic 			}
185*39800Sbostic 			(void)free((char *)p);
186*39800Sbostic 		}
187*39800Sbostic 
188*39800Sbostic 	/* free up child linked list, sort array, path buffer */
189*39800Sbostic 	if (sp->child)
190*39800Sbostic 		fts_lfree(sp->child);
191*39800Sbostic 	if (sp->array)
192*39800Sbostic 		(void)free((char *)sp->array);
193*39800Sbostic 	(void)free((char *)sp->path);
194*39800Sbostic 
195*39800Sbostic 	/*
196*39800Sbostic 	 * return to original directory, save errno if necessary;
197*39800Sbostic 	 * free up the directory buffer
198*39800Sbostic 	 */
199*39800Sbostic 	if (!(sp->options & FTS_NOCHDIR)) {
200*39800Sbostic 		saved_errno = chdir(sp->wd) ? errno : 0;
201*39800Sbostic 		(void)free(sp->wd);
202*39800Sbostic 	}
203*39800Sbostic 
204*39800Sbostic 	/* free up the stream pointer */
205*39800Sbostic 	(void)free((char *)sp);
206*39800Sbostic 
207*39800Sbostic 	/* set errno and return */
208*39800Sbostic 	if (!(sp->options & FTS_NOCHDIR) && saved_errno) {
209*39800Sbostic 		errno = saved_errno;
210*39800Sbostic 		return(-1);
211*39800Sbostic 	}
212*39800Sbostic 	return(0);
213*39800Sbostic }
214*39800Sbostic 
215*39800Sbostic FTSENT *
216*39800Sbostic ftsread(sp)
217*39800Sbostic 	register FTS *sp;
218*39800Sbostic {
219*39800Sbostic 	register FTSENT *p;
220*39800Sbostic 	register int instr;
221*39800Sbostic 	static int cd;
222*39800Sbostic 	FTSENT *tmp;
223*39800Sbostic 	char *cp;
224*39800Sbostic 
225*39800Sbostic 	/* if finished or unrecoverable error, return NULL */
226*39800Sbostic 	if (!sp->cur || sp->options & FTS__STOP)
227*39800Sbostic 		return(NULL);
228*39800Sbostic 
229*39800Sbostic 	/* set global stream pointer, and current node pointer */
230*39800Sbostic 	stream = sp;
231*39800Sbostic 	p = sp->cur;
232*39800Sbostic 
233*39800Sbostic 	/* save and zero out user instructions */
234*39800Sbostic 	instr = p->instr;
235*39800Sbostic 	p->instr = 0;
236*39800Sbostic 
237*39800Sbostic 	/* if used link pointer for cycle detection, restore it */
238*39800Sbostic 	if (sp->savelink) {
239*39800Sbostic 		p->link = sp->savelink;
240*39800Sbostic 		sp->savelink = NULL;
241*39800Sbostic 	}
242*39800Sbostic 
243*39800Sbostic 	/* any type of file may be re-visited; re-stat and return */
244*39800Sbostic 	if (instr == FTS_AGAIN) {
245*39800Sbostic 		p->info = fts_stat(p, 0);
246*39800Sbostic 		return(p);
247*39800Sbostic 	}
248*39800Sbostic 
249*39800Sbostic 	if (p->info == FTS_D)
250*39800Sbostic 		if (instr == FTS_SKIP) {
251*39800Sbostic 			if (sp->child) {
252*39800Sbostic 				fts_lfree(sp->child);
253*39800Sbostic 				sp->child = NULL;
254*39800Sbostic 			}
255*39800Sbostic 		} else {
256*39800Sbostic 			if (sp->child) {
257*39800Sbostic 				/* cd into child directory */
258*39800Sbostic 				if (CHDIR(sp, p->accpath)) {
259*39800Sbostic 					sp->options |= FTS__STOP;
260*39800Sbostic 					p->info = FTS_ERR;
261*39800Sbostic 					return(p);
262*39800Sbostic 				}
263*39800Sbostic 			} else if (!(sp->child = fts_build(sp, 1)))
264*39800Sbostic 				return(p);
265*39800Sbostic 			p = sp->child;
266*39800Sbostic 			sp->child = NULL;
267*39800Sbostic 			cp = sp->path + NAPPEND(p->parent);
268*39800Sbostic 			*cp++ = '/';
269*39800Sbostic 			bcopy(p->name, cp, p->namelen + 1);
270*39800Sbostic 			return(sp->cur = p);
271*39800Sbostic 		}
272*39800Sbostic 	else if (p->info == FTS_SL && instr == FTS_FOLLOW) {
273*39800Sbostic 		p->info = fts_stat(p, 1);
274*39800Sbostic 		return(p);
275*39800Sbostic 	}
276*39800Sbostic 
277*39800Sbostic 	/*
278*39800Sbostic 	 * user may have called ftsset on pointer returned by
279*39800Sbostic 	 * ftschildren; handle it here.
280*39800Sbostic 	 */
281*39800Sbostic 	for (p = p->link; p;) {
282*39800Sbostic 		instr = p->instr;
283*39800Sbostic 		if (instr == FTS_FOLLOW) {
284*39800Sbostic 			p->info = fts_stat(p, 1);
285*39800Sbostic 			p->instr = 0;
286*39800Sbostic 			break;
287*39800Sbostic 		}
288*39800Sbostic 		if (instr == FTS_SKIP) {
289*39800Sbostic 			tmp = p;
290*39800Sbostic 			p = p->link;
291*39800Sbostic 			(void)free((char *)tmp);
292*39800Sbostic 			continue;
293*39800Sbostic 		}
294*39800Sbostic 		p->instr = 0;
295*39800Sbostic 		break;
296*39800Sbostic 	}
297*39800Sbostic 
298*39800Sbostic 	/* go to next node on this level */
299*39800Sbostic 	if (p) {
300*39800Sbostic 		/*
301*39800Sbostic 		 * if root level node, set up paths and return.  If not the
302*39800Sbostic 		 * first time, and it's not an absolute pathname, get back
303*39800Sbostic 		 * to wherever we started from.
304*39800Sbostic 		 */
305*39800Sbostic 		if (p->level == ROOTLEVEL) {
306*39800Sbostic 			fts_load(p);
307*39800Sbostic 			if (cd) {
308*39800Sbostic 				(void)free((char *)sp->cur);
309*39800Sbostic 				if (p->path[0] != '/' && CHDIR(sp, sp->wd)) {
310*39800Sbostic 					/* return target path for error msg */
311*39800Sbostic 					p->path = sp->wd;
312*39800Sbostic 					p->info = FTS_ERR;
313*39800Sbostic 					sp->options |= FTS__STOP;
314*39800Sbostic 					return(sp->cur = p);
315*39800Sbostic 				}
316*39800Sbostic 			} else
317*39800Sbostic 				cd = 1;
318*39800Sbostic 			p->info = fts_stat(p, 0);
319*39800Sbostic 		} else {
320*39800Sbostic 			(void)free((char *)sp->cur);
321*39800Sbostic 			cp = sp->path + NAPPEND(p->parent);
322*39800Sbostic 			*cp++ = '/';
323*39800Sbostic 			bcopy(p->name, cp, p->namelen + 1);
324*39800Sbostic 			if (p->info == FTS_D && (tmp = fts_cycle(p))) {
325*39800Sbostic 				sp->savelink = p->link;
326*39800Sbostic 				p->link = tmp;
327*39800Sbostic 			}
328*39800Sbostic 		}
329*39800Sbostic 		return(sp->cur = p);
330*39800Sbostic 	}
331*39800Sbostic 
332*39800Sbostic 	/* go to parent */
333*39800Sbostic 	p = sp->cur->parent;
334*39800Sbostic 	(void)free((char *)sp->cur);
335*39800Sbostic 	if (p->level == ROOTPARENTLEVEL) {
336*39800Sbostic 		/*
337*39800Sbostic 		 * done; free everything up and set errno to 0 so the user
338*39800Sbostic 		 * can distinguish between error and EOF.
339*39800Sbostic 		 */
340*39800Sbostic 		(void)free((char *)p);
341*39800Sbostic 		errno = 0;
342*39800Sbostic 		return(sp->cur = NULL);
343*39800Sbostic 	}
344*39800Sbostic 
345*39800Sbostic 	sp->path[p->pathlen] = '\0';
346*39800Sbostic 	if (CHDIR(sp, "..")) {
347*39800Sbostic 		sp->options |= FTS__STOP;
348*39800Sbostic 		p->info = FTS_ERR;
349*39800Sbostic 	} else
350*39800Sbostic 		p->info = FTS_DP;
351*39800Sbostic 	return(sp->cur = p);
352*39800Sbostic }
353*39800Sbostic 
354*39800Sbostic /*
355*39800Sbostic  * ftsset takes the stream as an argument although it's not used in this
356*39800Sbostic  * implementation; it would be necessary if anyone wanted to add global
357*39800Sbostic  * semantics to fts using ftsset.  A possible error return is allowed for
358*39800Sbostic  * similar reasons.
359*39800Sbostic  */
360*39800Sbostic /* ARGSUSED */
361*39800Sbostic ftsset(sp, p, instr)
362*39800Sbostic 	FTS *sp;
363*39800Sbostic 	FTSENT *p;
364*39800Sbostic 	int instr;
365*39800Sbostic {
366*39800Sbostic 	p->instr = instr;
367*39800Sbostic 	return(0);
368*39800Sbostic }
369*39800Sbostic 
370*39800Sbostic FTSENT *
371*39800Sbostic ftschildren(sp)
372*39800Sbostic 	register FTS *sp;
373*39800Sbostic {
374*39800Sbostic 	/*
375*39800Sbostic 	 * set errno to 0 so that user can tell the difference between an
376*39800Sbostic 	 * error and a directory without entries.
377*39800Sbostic 	 */
378*39800Sbostic 	errno = 0;
379*39800Sbostic 	if (sp->cur->info != FTS_D || sp->options & FTS__STOP)
380*39800Sbostic 		return(NULL);
381*39800Sbostic 	if (sp->child)
382*39800Sbostic 		fts_lfree(sp->child);
383*39800Sbostic 	if (sp->child = fts_build(sp, 0)) {
384*39800Sbostic 		/*
385*39800Sbostic 		 * have to cd up so that the fields of the current node
386*39800Sbostic 		 * as returned from readfts will be correct.
387*39800Sbostic 		 */
388*39800Sbostic 		if (CHDIR(sp, "..")) {
389*39800Sbostic 			sp->options |= FTS__STOP;
390*39800Sbostic 			return(NULL);
391*39800Sbostic 		}
392*39800Sbostic 	}
393*39800Sbostic 	return(sp->child);
394*39800Sbostic }
395*39800Sbostic 
396*39800Sbostic #define	ISDOT(a)	(a[0] == '.' && (!a[1] || a[1] == '.' && !a[2]))
397*39800Sbostic 
398*39800Sbostic static FTSENT *
399*39800Sbostic fts_build(sp, set_node_errors)
400*39800Sbostic 	register FTS *sp;
401*39800Sbostic 	int set_node_errors;
402*39800Sbostic {
403*39800Sbostic 	register struct dirent *dp;
404*39800Sbostic 	register FTSENT *p, *head;
405*39800Sbostic 	register int nitems;
406*39800Sbostic 	DIR *dirp;
407*39800Sbostic 	int len, level, maxlen, nlinks, saved_errno;
408*39800Sbostic 	char *cp;
409*39800Sbostic 
410*39800Sbostic 	p = sp->cur;
411*39800Sbostic 	if (!(dirp = opendir(p->accpath))) {
412*39800Sbostic 		if (set_node_errors) {
413*39800Sbostic 			errno = 0;
414*39800Sbostic 			p->info = FTS_DNR;
415*39800Sbostic 		}
416*39800Sbostic 		return(NULL);
417*39800Sbostic 	}
418*39800Sbostic 	if (CHDIR(sp, p->accpath)) {
419*39800Sbostic 		if (set_node_errors) {
420*39800Sbostic 			errno = 0;
421*39800Sbostic 			p->info = FTS_DNX;
422*39800Sbostic 		}
423*39800Sbostic 		return(NULL);
424*39800Sbostic 	}
425*39800Sbostic 
426*39800Sbostic 	/*
427*39800Sbostic 	 * the real slowdown in walking the tree is the stat calls.  If
428*39800Sbostic 	 * FTS_NOSTAT is set and it's a physical walk (so that symbolic
429*39800Sbostic 	 * links can't be directories), fts assumes that the number of
430*39800Sbostic 	 * subdirectories in a node is equal to the number of links to
431*39800Sbostic 	 * the parent.  This allows stat calls to be skipped in any leaf
432*39800Sbostic 	 * directories and for any nodes after the directories in the
433*39800Sbostic 	 * parent node have been found.  This empirically cuts the stat
434*39800Sbostic 	 * calls by about 2/3.
435*39800Sbostic 	 */
436*39800Sbostic 	nlinks = sp->options & FTS_NOSTAT && sp->options & FTS_PHYSICAL ?
437*39800Sbostic 	    p->statb.st_nlink - (sp->options & FTS_SEEDOT ? 0 : 2) : -1;
438*39800Sbostic 
439*39800Sbostic 	/* get max file name length that can be stored in current path */
440*39800Sbostic 	maxlen = sp->pathlen - p->pathlen - 1;
441*39800Sbostic 
442*39800Sbostic 	cp = sp->path + (len = NAPPEND(p));
443*39800Sbostic 	*cp++ = '/';
444*39800Sbostic 
445*39800Sbostic 	level = p->level + 1;
446*39800Sbostic 
447*39800Sbostic 	/* read the directory, attching each new entry to the `link' pointer */
448*39800Sbostic 	for (head = NULL, nitems = 0; dp = readdir(dirp);) {
449*39800Sbostic 		if (ISDOT(dp->d_name) && !(sp->options & FTS_SEEDOT))
450*39800Sbostic 			continue;
451*39800Sbostic 
452*39800Sbostic 		if (!(p = fts_alloc(dp->d_name, dp->d_namlen))) {
453*39800Sbostic 			saved_errno = errno;
454*39800Sbostic 			goto mem1;
455*39800Sbostic 		}
456*39800Sbostic 		if (dp->d_namlen > maxlen) {
457*39800Sbostic 			if (!fts_path((int)dp->d_namlen)) {
458*39800Sbostic 				/* quit: this stream no longer has a path */
459*39800Sbostic 				sp->options |= FTS__STOP;
460*39800Sbostic 				saved_errno = errno;
461*39800Sbostic 				(void)free((char *)p);
462*39800Sbostic mem1:				fts_lfree(head);
463*39800Sbostic 				if (set_node_errors)
464*39800Sbostic 					p->info = FTS_ERR;
465*39800Sbostic 				if (CHDIR(sp, "..")) {
466*39800Sbostic 					saved_errno = errno;
467*39800Sbostic 					sp->options |= FTS__STOP;
468*39800Sbostic 				}
469*39800Sbostic 				errno = saved_errno;
470*39800Sbostic 				return(NULL);
471*39800Sbostic 			}
472*39800Sbostic 			maxlen = sp->pathlen - sp->cur->pathlen - 1;
473*39800Sbostic 		}
474*39800Sbostic 
475*39800Sbostic 		p->pathlen = len + dp->d_namlen + 1;
476*39800Sbostic 		p->accpath = sp->options & FTS_NOCHDIR ? p->path : p->name;
477*39800Sbostic 
478*39800Sbostic 		p->parent = sp->cur;
479*39800Sbostic 		p->level = level;
480*39800Sbostic 
481*39800Sbostic 		if (nlinks) {
482*39800Sbostic 			/* make sure fts_stat has a filename to stat */
483*39800Sbostic 			if (sp->options & FTS_NOCHDIR)
484*39800Sbostic 				bcopy(p->name, cp, p->namelen + 1);
485*39800Sbostic 			p->info = fts_stat(p, 0);
486*39800Sbostic 			if (nlinks > 0 && (p->info == FTS_D ||
487*39800Sbostic 			    p->info == FTS_DNR || p->info == FTS_DNX))
488*39800Sbostic 				--nlinks;
489*39800Sbostic 		} else
490*39800Sbostic 			p->info = FTS_NS;
491*39800Sbostic 
492*39800Sbostic 		p->link = head;
493*39800Sbostic 		head = p;
494*39800Sbostic 		++nitems;
495*39800Sbostic 	}
496*39800Sbostic 	(void)closedir(dirp);
497*39800Sbostic 
498*39800Sbostic 	if (!nitems) {
499*39800Sbostic 		if (CHDIR(sp, "..")) {
500*39800Sbostic 			sp->options |= FTS__STOP;
501*39800Sbostic 			p->info = FTS_ERR;
502*39800Sbostic 		} else if (set_node_errors)
503*39800Sbostic 			p->info = FTS_DP;
504*39800Sbostic 		*--cp = '\0';
505*39800Sbostic 		return(NULL);
506*39800Sbostic 	}
507*39800Sbostic 
508*39800Sbostic 	if (sp->compar && nitems > 1)
509*39800Sbostic 		head = fts_sort(head, nitems);
510*39800Sbostic 
511*39800Sbostic 	if (set_node_errors)
512*39800Sbostic 		bcopy(head->name, cp, head->namelen + 1);
513*39800Sbostic 	else
514*39800Sbostic 		*--cp = '\0';
515*39800Sbostic 	return(head);
516*39800Sbostic }
517*39800Sbostic 
518*39800Sbostic static short
519*39800Sbostic fts_stat(p, symflag)
520*39800Sbostic 	register FTSENT *p;
521*39800Sbostic 	int symflag;
522*39800Sbostic {
523*39800Sbostic 	register int ngroups, *gp;
524*39800Sbostic 	int gidset[NGROUPS];
525*39800Sbostic 
526*39800Sbostic 	/*
527*39800Sbostic 	 * detection of symbolic links w/o targets.  If FTS_FOLLOW is set,
528*39800Sbostic 	 * the symlink structure is overwritten with the stat structure of
529*39800Sbostic 	 * the target.
530*39800Sbostic 	 */
531*39800Sbostic 	if (stream->options & FTS_LOGICAL || symflag) {
532*39800Sbostic 		if (stat(p->accpath, &p->statb))
533*39800Sbostic 			return(symflag || !lstat(p->accpath, &p->statb) ?
534*39800Sbostic 			    FTS_SLNONE : FTS_ERR);
535*39800Sbostic 	} else if (lstat(p->accpath, &p->statb))
536*39800Sbostic 		return(FTS_ERR);
537*39800Sbostic 
538*39800Sbostic 	switch(p->statb.st_mode&S_IFMT) {
539*39800Sbostic 	case S_IFDIR:
540*39800Sbostic 		/* get new uid/groups each time, they may have changed */
541*39800Sbostic 		if (getuid() == p->statb.st_uid) {
542*39800Sbostic 			if (!(p->statb.st_mode&S_IRUSR))
543*39800Sbostic 				return(FTS_DNR);
544*39800Sbostic 			if (!(p->statb.st_mode&S_IXUSR))
545*39800Sbostic 				return(FTS_DNX);
546*39800Sbostic 			return(FTS_D);
547*39800Sbostic 		}
548*39800Sbostic 		if ((ngroups = getgroups(NGROUPS, gidset)) == -1)
549*39800Sbostic 			return(FTS_ERR);
550*39800Sbostic 		for (gp = gidset; ngroups--;)
551*39800Sbostic 			if (*gp++ == p->statb.st_gid) {
552*39800Sbostic 				if (!(p->statb.st_mode&S_IRGRP))
553*39800Sbostic 					return(FTS_DNR);
554*39800Sbostic 				if (!(p->statb.st_mode&S_IXGRP))
555*39800Sbostic 					return(FTS_DNX);
556*39800Sbostic 				return(FTS_D);
557*39800Sbostic 			}
558*39800Sbostic 		if (!(p->statb.st_mode&S_IROTH))
559*39800Sbostic 			return(FTS_DNR);
560*39800Sbostic 		if (!(p->statb.st_mode&S_IXOTH))
561*39800Sbostic 			return(FTS_DNX);
562*39800Sbostic 		return(FTS_D);
563*39800Sbostic 	case S_IFLNK:
564*39800Sbostic 		return(FTS_SL);
565*39800Sbostic 	case S_IFREG:
566*39800Sbostic 		return(FTS_F);
567*39800Sbostic 	default:
568*39800Sbostic 		return(FTS_DEFAULT);
569*39800Sbostic 	}
570*39800Sbostic 	/* NOTREACHED */
571*39800Sbostic }
572*39800Sbostic 
573*39800Sbostic static FTSENT *
574*39800Sbostic fts_cycle(p)
575*39800Sbostic 	register FTSENT *p;
576*39800Sbostic {
577*39800Sbostic 	register dev_t dev;
578*39800Sbostic 	register ino_t ino;
579*39800Sbostic 
580*39800Sbostic 	/*
581*39800Sbostic 	 * cycle detection is brute force; if the tree gets deep enough or
582*39800Sbostic 	 * the number of symbolic links to directories is really high
583*39800Sbostic 	 * something faster might be worthwhile.
584*39800Sbostic 	 */
585*39800Sbostic 	dev = p->statb.st_dev;
586*39800Sbostic 	ino = p->statb.st_ino;
587*39800Sbostic 	for (p = p->parent; p->level > ROOTLEVEL; p = p->parent)
588*39800Sbostic 		if (ino == p->statb.st_ino && dev == p->statb.st_dev)
589*39800Sbostic 			return(p);
590*39800Sbostic 	return(NULL);
591*39800Sbostic }
592*39800Sbostic 
593*39800Sbostic #define	R(type, nelem, ptr) \
594*39800Sbostic 	(type *)realloc((char *)ptr, (u_int)((nelem) * sizeof(type)))
595*39800Sbostic 
596*39800Sbostic static FTSENT *
597*39800Sbostic fts_sort(head, nitems)
598*39800Sbostic 	FTSENT *head;
599*39800Sbostic 	register int nitems;
600*39800Sbostic {
601*39800Sbostic 	register FTSENT **ap, *p;
602*39800Sbostic 
603*39800Sbostic 	/*
604*39800Sbostic 	 * construct an array of pointers to the structures and call qsort(3).
605*39800Sbostic 	 * Reassemble the array in the order returned by qsort.  If unable to
606*39800Sbostic 	 * sort for memory reasons, return the directory entries in their
607*39800Sbostic 	 * current order.  Allocate enough space for the current needs plus
608*39800Sbostic 	 * 40 so we don't realloc one entry at a time.
609*39800Sbostic 	 */
610*39800Sbostic 	if (nitems > stream->nitems) {
611*39800Sbostic 		stream->nitems = nitems + 40;
612*39800Sbostic 		if (!(stream->array =
613*39800Sbostic 		    R(FTSENT *, stream->nitems, stream->array))) {
614*39800Sbostic 			stream->nitems = 0;
615*39800Sbostic 			return(head);
616*39800Sbostic 		}
617*39800Sbostic 	}
618*39800Sbostic 	for (ap = stream->array, p = head; p; p = p->link)
619*39800Sbostic 		*ap++ = p;
620*39800Sbostic 	qsort((char *)stream->array, nitems, sizeof(FTSENT *), stream->compar);
621*39800Sbostic 	for (head = *(ap = stream->array); --nitems; ++ap)
622*39800Sbostic 		ap[0]->link = ap[1];
623*39800Sbostic 	ap[0]->link = NULL;
624*39800Sbostic 	return(head);
625*39800Sbostic }
626*39800Sbostic 
627*39800Sbostic static FTSENT *
628*39800Sbostic fts_alloc(name, len)
629*39800Sbostic 	char *name;
630*39800Sbostic 	register int len;
631*39800Sbostic {
632*39800Sbostic 	register FTSENT *p;
633*39800Sbostic 
634*39800Sbostic 	/*
635*39800Sbostic 	 * variable sized structures; the name is the last element so
636*39800Sbostic 	 * allocate enough extra space after the structure to hold it.
637*39800Sbostic 	 */
638*39800Sbostic 	if (!(p = (FTSENT *)malloc((u_int)(sizeof(FTSENT) + len))))
639*39800Sbostic 		return(NULL);
640*39800Sbostic 	bcopy(name, p->name, len + 1);
641*39800Sbostic 	p->namelen = len;
642*39800Sbostic 	p->path = stream->path;
643*39800Sbostic 	p->instr = 0;
644*39800Sbostic 	p->local.number = 0;
645*39800Sbostic 	p->local.pointer = NULL;
646*39800Sbostic 	return(p);
647*39800Sbostic }
648*39800Sbostic 
649*39800Sbostic static
650*39800Sbostic fts_lfree(head)
651*39800Sbostic 	register FTSENT *head;
652*39800Sbostic {
653*39800Sbostic 	register FTSENT *p;
654*39800Sbostic 
655*39800Sbostic 	while (p = head) {
656*39800Sbostic 		head = head->link;
657*39800Sbostic 		(void)free((char *)p);
658*39800Sbostic 	}
659*39800Sbostic }
660*39800Sbostic 
661*39800Sbostic /*
662*39800Sbostic  * allow essentially unlimited paths; certain programs (find, remove, ls)
663*39800Sbostic  * need to work on any tree.  Most systems will allow creation of paths
664*39800Sbostic  * much longer than MAXPATHLEN, even though the kernel won't resolve them.
665*39800Sbostic  * Add an extra 128 bytes to the requested size so that we don't realloc
666*39800Sbostic  * the path 2 bytes at a time.
667*39800Sbostic  */
668*39800Sbostic static
669*39800Sbostic fts_path(size)
670*39800Sbostic 	int size;
671*39800Sbostic {
672*39800Sbostic 	stream->pathlen += size + 128;
673*39800Sbostic 	return((int)(stream->path = R(char, stream->pathlen, stream->path)));
674*39800Sbostic }
675*39800Sbostic 
676*39800Sbostic static FTSENT *
677*39800Sbostic fts_root(name)
678*39800Sbostic 	register char *name;
679*39800Sbostic {
680*39800Sbostic 	register char *cp;
681*39800Sbostic 
682*39800Sbostic 	/*
683*39800Sbostic 	 * rip trailing slashes; it's somewhat unclear in POSIX 1003.1 what
684*39800Sbostic 	 * /a/b/ really is, they don't talk about what a null path component
685*39800Sbostic 	 * resolves to.  This hopefully does what the user intended.  Don't
686*39800Sbostic 	 * allow null pathnames.
687*39800Sbostic 	 */
688*39800Sbostic 	for (cp = name; *cp; ++cp);
689*39800Sbostic 	if (cp == name) {
690*39800Sbostic 		errno = ENOENT;
691*39800Sbostic 		return(NULL);
692*39800Sbostic 	}
693*39800Sbostic 	while (--cp > name && *cp == '/');
694*39800Sbostic 	*++cp = '\0';
695*39800Sbostic 	return(fts_alloc(name, cp - name));
696*39800Sbostic }
697