xref: /onnv-gate/usr/src/lib/libast/common/misc/fts.c (revision 4887:feebf9260c2e)
1*4887Schin /***********************************************************************
2*4887Schin *                                                                      *
3*4887Schin *               This software is part of the ast package               *
4*4887Schin *           Copyright (c) 1985-2007 AT&T Knowledge Ventures            *
5*4887Schin *                      and is licensed under the                       *
6*4887Schin *                  Common Public License, Version 1.0                  *
7*4887Schin *                      by AT&T Knowledge Ventures                      *
8*4887Schin *                                                                      *
9*4887Schin *                A copy of the License is available at                 *
10*4887Schin *            http://www.opensource.org/licenses/cpl1.0.txt             *
11*4887Schin *         (with md5 checksum 059e8cd6165cb4c31e351f2b69388fd9)         *
12*4887Schin *                                                                      *
13*4887Schin *              Information and Software Systems Research               *
14*4887Schin *                            AT&T Research                             *
15*4887Schin *                           Florham Park NJ                            *
16*4887Schin *                                                                      *
17*4887Schin *                 Glenn Fowler <gsf@research.att.com>                  *
18*4887Schin *                  David Korn <dgk@research.att.com>                   *
19*4887Schin *                   Phong Vo <kpv@research.att.com>                    *
20*4887Schin *                                                                      *
21*4887Schin ***********************************************************************/
22*4887Schin #pragma prototyped
23*4887Schin /*
24*4887Schin  * Phong Vo
25*4887Schin  * Glenn Fowler
26*4887Schin  * AT&T Research
27*4887Schin  *
28*4887Schin  * fts implementation unwound from the kpv ftwalk() of 1988-10-30
29*4887Schin  */
30*4887Schin 
31*4887Schin #include <ast.h>
32*4887Schin #include <ast_dir.h>
33*4887Schin #include <error.h>
34*4887Schin #include <fs3d.h>
35*4887Schin 
36*4887Schin struct Ftsent;
37*4887Schin 
38*4887Schin typedef int (*Compar_f)(struct Ftsent* const*, struct Ftsent* const*);
39*4887Schin typedef int (*Stat_f)(const char*, struct stat*);
40*4887Schin 
41*4887Schin #define _FTS_PRIVATE_ \
42*4887Schin 	FTSENT*		parent;			/* top parent		*/ \
43*4887Schin 	FTSENT*		todo;			/* todo list		*/ \
44*4887Schin 	FTSENT*		top;			/* top element		*/ \
45*4887Schin 	FTSENT*		root;						   \
46*4887Schin 	FTSENT*		bot;			/* bottom element	*/ \
47*4887Schin 	FTSENT*		free;			/* free element		*/ \
48*4887Schin 	FTSENT*		diroot;						   \
49*4887Schin 	FTSENT*		curdir;						   \
50*4887Schin 	FTSENT*		current;		/* current element	*/ \
51*4887Schin 	FTSENT*		previous;		/* previous current	*/ \
52*4887Schin 	FTSENT*		dotdot;						   \
53*4887Schin 	FTSENT*		link;			/* real current fts_link*/ \
54*4887Schin 	FTSENT*		pwd;			/* pwd parent		*/ \
55*4887Schin 	DIR*		dir;			/* current dir stream	*/ \
56*4887Schin 	Compar_f	comparf;		/* node comparison func	*/ \
57*4887Schin 	int		baselen;		/* current strlen(base)	*/ \
58*4887Schin 	int		cd;			/* chdir status		*/ \
59*4887Schin 	int		cpname;						   \
60*4887Schin 	int		flags;			/* fts_open() flags	*/ \
61*4887Schin 	int		homesize;		/* sizeof(home)		*/ \
62*4887Schin 	int		nd;						   \
63*4887Schin 	unsigned char	children;					   \
64*4887Schin 	unsigned char	fs3d;						   \
65*4887Schin 	unsigned char	nostat;					   	   \
66*4887Schin 	unsigned char	state;			/* fts_read() state	*/ \
67*4887Schin 	char*		base;			/* basename in path	*/ \
68*4887Schin 	char*		name;						   \
69*4887Schin 	char*		path;			/* path workspace	*/ \
70*4887Schin 	char*		home;			/* home/path buffer	*/ \
71*4887Schin 	char*		endbase;		/* space to build paths */ \
72*4887Schin 	char*		endbuf;			/* space to build paths */ \
73*4887Schin 	char*		pad;
74*4887Schin 
75*4887Schin /*
76*4887Schin  * NOTE: <ftwalk.h> relies on status and statb being the first two elements
77*4887Schin  */
78*4887Schin 
79*4887Schin #define _FTSENT_PRIVATE_ \
80*4887Schin 	short		status;			/* internal status	*/ \
81*4887Schin 	struct stat	statb;			/* fts_statp data	*/ \
82*4887Schin 	int		nd;			/* popdir() count	*/ \
83*4887Schin 	FTSENT*		left;			/* left child		*/ \
84*4887Schin 	FTSENT*		right;			/* right child		*/ \
85*4887Schin 	FTSENT*		pwd;			/* pwd parent		*/ \
86*4887Schin 	FTSENT*		stack;			/* getlist() stack	*/ \
87*4887Schin 	FTS*		fts;			/* for fts verification	*/ \
88*4887Schin 	long		nlink;			/* FTS_D link count	*/ \
89*4887Schin 	unsigned char	must;			/* must stat		*/ \
90*4887Schin 	unsigned char	type;			/* DT_* type		*/ \
91*4887Schin 	unsigned char	symlink;		/* originally a symlink	*/ \
92*4887Schin 	char		name[sizeof(int)];	/* fts_name data	*/
93*4887Schin 
94*4887Schin #include <fts.h>
95*4887Schin 
96*4887Schin #ifndef ENOSYS
97*4887Schin #define ENOSYS		EINVAL
98*4887Schin #endif
99*4887Schin 
100*4887Schin 
101*4887Schin #if MAXNAMLEN > 16
102*4887Schin #define MINNAME		32
103*4887Schin #else
104*4887Schin #define MINNAME		16
105*4887Schin #endif
106*4887Schin 
107*4887Schin #define drop(p,f)	(((f)->fts_namelen < MINNAME) ? ((f)->fts_link = (p)->free, (p)->free = (f)) : (free(f), (p)->free))
108*4887Schin 
109*4887Schin #define ACCESS(p,f)	((p)->cd==0?(f)->fts_name:(f)->fts_path)
110*4887Schin #define PATH(f,p,l)	((!((f)->flags&FTS_SEEDOTDIR)&&(l)>0&&(p)[0]=='.'&&(p)[1]=='/')?((p)+2):(p))
111*4887Schin #define SAME(one,two)	((one)->st_ino==(two)->st_ino&&(one)->st_dev==(two)->st_dev)
112*4887Schin #define SKIPLINK(p,f)	((f)->fts_parent->nlink == 0)
113*4887Schin 
114*4887Schin #ifdef D_TYPE
115*4887Schin #define ISTYPE(f,t)	((f)->type == (t))
116*4887Schin #define TYPE(f,t)	((f)->type = (t))
117*4887Schin #define SKIP(p,f)	((f)->fts_parent->must == 0 && (((f)->type == DT_UNKNOWN) ? SKIPLINK(p,f) : ((f)->type != DT_DIR && ((f)->type != DT_LNK || ((p)->flags & FTS_PHYSICAL)))))
118*4887Schin #else
119*4887Schin #undef	DT_UNKNOWN
120*4887Schin #define DT_UNKNOWN	0
121*4887Schin #undef	DT_LNK
122*4887Schin #define DT_LNK		1
123*4887Schin #define ISTYPE(f,t)	((t)==DT_UNKNOWN)
124*4887Schin #define TYPE(f,d)
125*4887Schin #define SKIP(p,f)	((f)->fts_parent->must == 0 && SKIPLINK(p,f))
126*4887Schin #endif
127*4887Schin 
128*4887Schin #ifndef D_FILENO
129*4887Schin #define D_FILENO(d)	(1)
130*4887Schin #endif
131*4887Schin 
132*4887Schin /*
133*4887Schin  * NOTE: a malicious dir rename() could change .. underfoot so we
134*4887Schin  *	 must always verify; undef verify to enable the unsafe code
135*4887Schin  */
136*4887Schin 
137*4887Schin #define verify		1
138*4887Schin 
139*4887Schin /*
140*4887Schin  * FTS_NOSTAT requires a dir with
141*4887Schin  *	D_TYPE(&dirent_t)!=DT_UNKNOWN
142*4887Schin  *	    OR
143*4887Schin  *	st_nlink>=2
144*4887Schin  */
145*4887Schin 
146*4887Schin #define FTS_children_resume	1
147*4887Schin #define FTS_children_return	2
148*4887Schin #define FTS_error		3
149*4887Schin #define FTS_popstack		4
150*4887Schin #define FTS_popstack_resume	5
151*4887Schin #define FTS_popstack_return	6
152*4887Schin #define FTS_preorder		7
153*4887Schin #define FTS_preorder_resume	8
154*4887Schin #define FTS_preorder_return	9
155*4887Schin #define FTS_readdir		10
156*4887Schin #define FTS_terminal		11
157*4887Schin #define FTS_todo		12
158*4887Schin #define FTS_top_return		13
159*4887Schin 
160*4887Schin typedef int (*Notify_f)(FTS*, FTSENT*, void*);
161*4887Schin 
162*4887Schin typedef struct Notify_s
163*4887Schin {
164*4887Schin 	struct Notify_s*	next;
165*4887Schin 	Notify_f		notifyf;
166*4887Schin 	void*			context;
167*4887Schin } Notify_t;
168*4887Schin 
169*4887Schin static Notify_t*		notify;
170*4887Schin 
171*4887Schin /*
172*4887Schin  * allocate an FTSENT node
173*4887Schin  */
174*4887Schin 
175*4887Schin static FTSENT*
176*4887Schin node(FTS* fts, FTSENT* parent, register char* name, register int namelen)
177*4887Schin {
178*4887Schin 	register FTSENT*	f;
179*4887Schin 	register int		n;
180*4887Schin 
181*4887Schin 	if (fts->free && namelen < MINNAME)
182*4887Schin 	{
183*4887Schin 		f = fts->free;
184*4887Schin 		fts->free = f->fts_link;
185*4887Schin 	}
186*4887Schin 	else
187*4887Schin 	{
188*4887Schin 		n = (namelen < MINNAME ? MINNAME : namelen + 1) - sizeof(int);
189*4887Schin 		if (!(f = newof(0, FTSENT, 1, n)))
190*4887Schin 		{
191*4887Schin 			fts->fts_errno = errno;
192*4887Schin 			fts->state = FTS_error;
193*4887Schin 			return 0;
194*4887Schin 		}
195*4887Schin 		f->fts = fts;
196*4887Schin 	}
197*4887Schin 	TYPE(f, DT_UNKNOWN);
198*4887Schin 	f->status = 0;
199*4887Schin 	f->symlink = 0;
200*4887Schin 	f->fts_level = (f->fts_parent = parent)->fts_level + 1;
201*4887Schin 	f->fts_link = 0;
202*4887Schin 	f->fts_pointer = 0;
203*4887Schin 	f->fts_number = 0;
204*4887Schin 	f->fts_errno = 0;
205*4887Schin 	f->fts_namelen = namelen;
206*4887Schin 	f->fts_name = f->name;
207*4887Schin 	f->fts_statp = &f->statb;
208*4887Schin 	memcpy(f->fts_name, name, namelen + 1);
209*4887Schin 	return f;
210*4887Schin }
211*4887Schin 
212*4887Schin /*
213*4887Schin  * compare directories by device/inode
214*4887Schin  */
215*4887Schin 
216*4887Schin static int
217*4887Schin statcmp(FTSENT* const* pf1, FTSENT* const* pf2)
218*4887Schin {
219*4887Schin 	register const FTSENT*	f1 = *pf1;
220*4887Schin 	register const FTSENT*	f2 = *pf2;
221*4887Schin 
222*4887Schin 	if (f1->statb.st_ino < f2->statb.st_ino)
223*4887Schin 		return -1;
224*4887Schin 	if (f1->statb.st_ino > f2->statb.st_ino)
225*4887Schin 		return 1;
226*4887Schin 	if (f1->statb.st_dev < f2->statb.st_dev)
227*4887Schin 		return -1;
228*4887Schin 	if (f1->statb.st_dev > f2->statb.st_dev)
229*4887Schin 		return 1;
230*4887Schin 
231*4887Schin 	/*
232*4887Schin 	 * hack for NFS where <dev,ino> may not uniquely identify objects
233*4887Schin 	 */
234*4887Schin 
235*4887Schin 	if (f1->statb.st_mtime < f2->statb.st_mtime)
236*4887Schin 		return -1;
237*4887Schin 	if (f1->statb.st_mtime > f2->statb.st_mtime)
238*4887Schin 		return 1;
239*4887Schin 	return 0;
240*4887Schin }
241*4887Schin 
242*4887Schin /*
243*4887Schin  * search trees with top-down splaying (a la Tarjan and Sleator)
244*4887Schin  * when used for insertion sort, this implements a stable sort
245*4887Schin  */
246*4887Schin 
247*4887Schin #define RROTATE(r)	(t = r->left, r->left = t->right, t->right = r, r = t)
248*4887Schin #define LROTATE(r)	(t = r->right, r->right = t->left, t->left = r, r = t)
249*4887Schin 
250*4887Schin static FTSENT*
251*4887Schin search(FTSENT* e, FTSENT* root, int(*comparf)(FTSENT* const*, FTSENT* const*), int insert)
252*4887Schin {
253*4887Schin 	register int		cmp;
254*4887Schin 	register FTSENT*	t;
255*4887Schin 	register FTSENT*	left;
256*4887Schin 	register FTSENT*	right;
257*4887Schin 	register FTSENT*	lroot;
258*4887Schin 	register FTSENT*	rroot;
259*4887Schin 
260*4887Schin 	left = right = lroot = rroot = 0;
261*4887Schin 	while (root)
262*4887Schin 	{
263*4887Schin 		if (!(cmp = (*comparf)(&e, &root)) && !insert)
264*4887Schin 			break;
265*4887Schin 		if (cmp < 0)
266*4887Schin 		{
267*4887Schin 			/*
268*4887Schin 			 * this is the left zig-zig case
269*4887Schin 			 */
270*4887Schin 
271*4887Schin 			if (root->left && (cmp = (*comparf)(&e, &root->left)) <= 0)
272*4887Schin 			{
273*4887Schin 				RROTATE(root);
274*4887Schin 				if (!cmp && !insert)
275*4887Schin 					break;
276*4887Schin 			}
277*4887Schin 
278*4887Schin 			/*
279*4887Schin 			 * stick all things > e to the right tree
280*4887Schin 			 */
281*4887Schin 
282*4887Schin 			if (right)
283*4887Schin 				right->left = root;
284*4887Schin 			else
285*4887Schin 				rroot = root;
286*4887Schin 			right = root;
287*4887Schin 			root = root->left;
288*4887Schin 			right->left = 0;
289*4887Schin 		}
290*4887Schin 		else
291*4887Schin 		{
292*4887Schin 			/*
293*4887Schin 			 * this is the right zig-zig case
294*4887Schin 			 */
295*4887Schin 
296*4887Schin 			if (root->right && (cmp = (*comparf)(&e, &root->right)) >= 0)
297*4887Schin 			{
298*4887Schin 				LROTATE(root);
299*4887Schin 				if (!cmp && !insert)
300*4887Schin 					break;
301*4887Schin 			}
302*4887Schin 
303*4887Schin 			/*
304*4887Schin 			 * stick all things <= e to the left tree
305*4887Schin 			 */
306*4887Schin 
307*4887Schin 			if (left)
308*4887Schin 				left->right = root;
309*4887Schin 			else
310*4887Schin 				lroot = root;
311*4887Schin 			left = root;
312*4887Schin 			root = root->right;
313*4887Schin 			left->right = 0;
314*4887Schin 		}
315*4887Schin 	}
316*4887Schin 	if (!root)
317*4887Schin 		root = e;
318*4887Schin 	else
319*4887Schin 	{
320*4887Schin 		if (right)
321*4887Schin 			right->left = root->right;
322*4887Schin 		else
323*4887Schin 			rroot = root->right;
324*4887Schin 		if (left)
325*4887Schin 			left->right = root->left;
326*4887Schin 		else
327*4887Schin 			lroot = root->left;
328*4887Schin 	}
329*4887Schin 	root->left = lroot;
330*4887Schin 	root->right = rroot;
331*4887Schin 	return root;
332*4887Schin }
333*4887Schin 
334*4887Schin /*
335*4887Schin  * delete the root element from the tree
336*4887Schin  */
337*4887Schin 
338*4887Schin static FTSENT*
339*4887Schin deleteroot(register FTSENT* root)
340*4887Schin {
341*4887Schin 	register FTSENT*	t;
342*4887Schin 	register FTSENT*	left;
343*4887Schin 	register FTSENT*	right;
344*4887Schin 
345*4887Schin 	right = root->right;
346*4887Schin 	if (!(left = root->left))
347*4887Schin 		root = right;
348*4887Schin 	else
349*4887Schin 	{
350*4887Schin 		while (left->right)
351*4887Schin 			LROTATE(left);
352*4887Schin 		left->right = right;
353*4887Schin 		root = left;
354*4887Schin 	}
355*4887Schin 	return root;
356*4887Schin }
357*4887Schin 
358*4887Schin /*
359*4887Schin  * generate ordered fts_link list from binary tree at root
360*4887Schin  * FTSENT.stack instead of recursion to avoid blowing the real
361*4887Schin  * stack on big directories
362*4887Schin  */
363*4887Schin 
364*4887Schin static void
365*4887Schin getlist(register FTSENT** top, register FTSENT** bot, register FTSENT* root)
366*4887Schin {
367*4887Schin 	register FTSENT*	stack = 0;
368*4887Schin 
369*4887Schin 	for (;;)
370*4887Schin 	{
371*4887Schin 		if (root->left)
372*4887Schin 		{
373*4887Schin 			root->stack = stack;
374*4887Schin 			stack = root;
375*4887Schin 			root = root->left;
376*4887Schin 		}
377*4887Schin 		else
378*4887Schin 		{
379*4887Schin 			for (;;)
380*4887Schin 			{
381*4887Schin 				if (*top)
382*4887Schin 					*bot = (*bot)->fts_link = root;
383*4887Schin 				else
384*4887Schin 					*bot = *top = root;
385*4887Schin 				if (root->right)
386*4887Schin 				{
387*4887Schin 					root = root->right;
388*4887Schin 					break;
389*4887Schin 				}
390*4887Schin 				if (!(root = stack))
391*4887Schin 					return;
392*4887Schin 				stack = stack->stack;
393*4887Schin 			}
394*4887Schin 		}
395*4887Schin 	}
396*4887Schin }
397*4887Schin 
398*4887Schin /*
399*4887Schin  * set directory when curdir is lost in space
400*4887Schin  */
401*4887Schin 
402*4887Schin static int
403*4887Schin setdir(register char* home, register char* path)
404*4887Schin {
405*4887Schin 	register int	cdrv;
406*4887Schin 
407*4887Schin 	if (path[0] == '/')
408*4887Schin 		cdrv = pathcd(path, NiL);
409*4887Schin 	else
410*4887Schin 	{
411*4887Schin 		/*
412*4887Schin 		 * note that path and home are in the same buffer
413*4887Schin 		 */
414*4887Schin 
415*4887Schin 		path[-1] = '/';
416*4887Schin 		cdrv = pathcd(home, NiL);
417*4887Schin 		path[-1] = 0;
418*4887Schin 	}
419*4887Schin 	if (cdrv < 0)
420*4887Schin 		pathcd(home, NiL);
421*4887Schin 	return cdrv;
422*4887Schin }
423*4887Schin 
424*4887Schin /*
425*4887Schin  * set to parent dir
426*4887Schin  */
427*4887Schin 
428*4887Schin static int
429*4887Schin setpdir(register char* home, register char* path, register char* base)
430*4887Schin {
431*4887Schin 	register int	c;
432*4887Schin 	register int	cdrv;
433*4887Schin 
434*4887Schin 	if (base > path)
435*4887Schin 	{
436*4887Schin 		c = base[0];
437*4887Schin 		base[0] = 0;
438*4887Schin 		cdrv = setdir(home, path);
439*4887Schin 		base[0] = c;
440*4887Schin 	}
441*4887Schin 	else
442*4887Schin 		cdrv = pathcd(home, NiL);
443*4887Schin 	return cdrv;
444*4887Schin }
445*4887Schin 
446*4887Schin /*
447*4887Schin  * pop a set of directories
448*4887Schin  */
449*4887Schin static int
450*4887Schin popdirs(FTS* fts)
451*4887Schin {
452*4887Schin 	register FTSENT*f;
453*4887Schin 	register char*	s;
454*4887Schin 	register char*	e;
455*4887Schin #ifndef verify
456*4887Schin 	register int	verify;
457*4887Schin #endif
458*4887Schin 	struct stat	sb;
459*4887Schin 	char		buf[PATH_MAX];
460*4887Schin 
461*4887Schin 	if (!(f = fts->curdir) || f->fts_level < 0)
462*4887Schin 		return -1;
463*4887Schin 	e = buf + sizeof(buf) - 4;
464*4887Schin #ifndef verify
465*4887Schin 	verify = 0;
466*4887Schin #endif
467*4887Schin 	while (fts->nd > 0)
468*4887Schin 	{
469*4887Schin 		for (s = buf; s < e && fts->nd > 0; fts->nd--)
470*4887Schin 		{
471*4887Schin 			if (fts->pwd)
472*4887Schin 			{
473*4887Schin #ifndef verify
474*4887Schin 				verify |= fts->pwd->symlink;
475*4887Schin #endif
476*4887Schin 				fts->pwd = fts->pwd->pwd;
477*4887Schin 			}
478*4887Schin 			*s++ = '.';
479*4887Schin 			*s++ = '.';
480*4887Schin 			*s++ = '/';
481*4887Schin 		}
482*4887Schin 		*s = 0;
483*4887Schin 		if (chdir(buf))
484*4887Schin 			return -1;
485*4887Schin 	}
486*4887Schin 	return (verify && (stat(".", &sb) < 0 || !SAME(&sb, f->fts_statp))) ? -1 : 0;
487*4887Schin }
488*4887Schin 
489*4887Schin /*
490*4887Schin  * initialize st from path and fts_info from st
491*4887Schin  */
492*4887Schin 
493*4887Schin static int
494*4887Schin info(FTS* fts, register FTSENT* f, const char* path, struct stat* sp, int flags)
495*4887Schin {
496*4887Schin 	if (path)
497*4887Schin 	{
498*4887Schin #ifdef S_ISLNK
499*4887Schin 		if (!f->symlink && (ISTYPE(f, DT_UNKNOWN) || ISTYPE(f, DT_LNK)))
500*4887Schin 		{
501*4887Schin 			if (lstat(path, sp) < 0)
502*4887Schin 				goto bad;
503*4887Schin 		}
504*4887Schin 		else
505*4887Schin #endif
506*4887Schin 			if (stat(path, sp) < 0)
507*4887Schin 				goto bad;
508*4887Schin 	}
509*4887Schin #ifdef S_ISLNK
510*4887Schin  again:
511*4887Schin #endif
512*4887Schin 	if (S_ISDIR(sp->st_mode))
513*4887Schin 	{
514*4887Schin 		if ((flags & FTS_NOSTAT) && !fts->fs3d)
515*4887Schin 		{
516*4887Schin 			f->fts_parent->nlink--;
517*4887Schin #ifdef D_TYPE
518*4887Schin 			f->must = 0;
519*4887Schin 			if ((f->nlink = sp->st_nlink) < 2)
520*4887Schin 				f->nlink = 2;
521*4887Schin #else
522*4887Schin 			if ((f->nlink = sp->st_nlink) >= 2)
523*4887Schin 				f->must = 1;
524*4887Schin 			else
525*4887Schin 				f->must = 2;
526*4887Schin #endif
527*4887Schin 		}
528*4887Schin 		else
529*4887Schin 			f->must = 2;
530*4887Schin 		TYPE(f, DT_DIR);
531*4887Schin 		f->fts_info = FTS_D;
532*4887Schin 	}
533*4887Schin #ifdef S_ISLNK
534*4887Schin 	else if (S_ISLNK((sp)->st_mode))
535*4887Schin 	{
536*4887Schin 		struct stat	sb;
537*4887Schin 
538*4887Schin 		f->symlink = 1;
539*4887Schin 		if (!(flags & FTS_PHYSICAL) && stat(path, &sb) >= 0)
540*4887Schin 		{
541*4887Schin 			*sp = sb;
542*4887Schin 			flags = FTS_PHYSICAL;
543*4887Schin 			goto again;
544*4887Schin 		}
545*4887Schin 		TYPE(f, DT_LNK);
546*4887Schin 		f->fts_info = FTS_SL;
547*4887Schin 	}
548*4887Schin #endif
549*4887Schin 	else
550*4887Schin 	{
551*4887Schin 		TYPE(f, DT_REG);
552*4887Schin 		f->fts_info = FTS_F;
553*4887Schin 	}
554*4887Schin 	return 0;
555*4887Schin  bad:
556*4887Schin 	TYPE(f, DT_UNKNOWN);
557*4887Schin 	f->fts_info = FTS_NS;
558*4887Schin 	return -1;
559*4887Schin }
560*4887Schin 
561*4887Schin /*
562*4887Schin  * get top list of elements to process
563*4887Schin  */
564*4887Schin 
565*4887Schin static FTSENT*
566*4887Schin toplist(FTS* fts, register char* const* pathnames)
567*4887Schin {
568*4887Schin 	register char*		path;
569*4887Schin 	register struct stat*	sb;
570*4887Schin 	register FTSENT*	f;
571*4887Schin 	register FTSENT*	root;
572*4887Schin 	int			physical;
573*4887Schin 	int			metaphysical;
574*4887Schin 	char*			s;
575*4887Schin 	FTSENT*			top;
576*4887Schin 	FTSENT*			bot;
577*4887Schin 	struct stat		st;
578*4887Schin 
579*4887Schin 	if (fts->flags & FTS_NOSEEDOTDIR)
580*4887Schin 		fts->flags &= ~FTS_SEEDOTDIR;
581*4887Schin 	physical = (fts->flags & FTS_PHYSICAL);
582*4887Schin 	metaphysical = (fts->flags & (FTS_META|FTS_PHYSICAL)) == (FTS_META|FTS_PHYSICAL);
583*4887Schin 	top = bot = root = 0;
584*4887Schin 	while (path = *pathnames++)
585*4887Schin 	{
586*4887Schin 		/*
587*4887Schin 		 * make elements
588*4887Schin 		 */
589*4887Schin 
590*4887Schin 		if (!(f = node(fts, fts->parent, path, strlen(path))))
591*4887Schin 			break;
592*4887Schin 		path = f->fts_name;
593*4887Schin 		if (!physical)
594*4887Schin 			f->fts_namelen = (fts->flags & FTS_SEEDOTDIR) ? strlen(path) : (pathcanon(path, 0) - path);
595*4887Schin 		else if (*path != '.')
596*4887Schin 		{
597*4887Schin 			f->fts_namelen = strlen(path);
598*4887Schin 			fts->flags |= FTS_SEEDOTDIR;
599*4887Schin 		}
600*4887Schin 		else
601*4887Schin 		{
602*4887Schin 			if (fts->flags & FTS_NOSEEDOTDIR)
603*4887Schin 			{
604*4887Schin 				fts->flags &= ~FTS_SEEDOTDIR;
605*4887Schin 				s = path;
606*4887Schin 				while (*s++ == '.' && *s++ == '/')
607*4887Schin 				{
608*4887Schin 					while (*s == '/')
609*4887Schin 						s++;
610*4887Schin 					if (!*s)
611*4887Schin 						break;
612*4887Schin 					path = f->fts_name;
613*4887Schin 					while (*path++ = *s++);
614*4887Schin 					path = f->fts_name;
615*4887Schin 				}
616*4887Schin 			}
617*4887Schin 			else
618*4887Schin 				fts->flags |= FTS_SEEDOTDIR;
619*4887Schin 			for (s = path + strlen(path); s > path && *(s - 1) == '/'; s--);
620*4887Schin 			*s = 0;
621*4887Schin 			f->fts_namelen = s - path;
622*4887Schin 		}
623*4887Schin 		sb = f->fts_statp;
624*4887Schin 		if (!*path)
625*4887Schin 		{
626*4887Schin 			errno = ENOENT;
627*4887Schin 			f->fts_info = FTS_NS;
628*4887Schin 		}
629*4887Schin 		else
630*4887Schin 			info(fts, f, path, sb, fts->flags);
631*4887Schin #ifdef S_ISLNK
632*4887Schin 
633*4887Schin 		/*
634*4887Schin 		 * don't let any standards committee get
635*4887Schin 		 * away with calling your idea a hack
636*4887Schin 		 */
637*4887Schin 
638*4887Schin 		if (metaphysical && f->fts_info == FTS_SL && stat(path, &st) >= 0)
639*4887Schin 		{
640*4887Schin 			*sb = st;
641*4887Schin 			info(fts, f, NiL, sb, 0);
642*4887Schin 		}
643*4887Schin #endif
644*4887Schin 		if (fts->comparf)
645*4887Schin 			root = search(f, root, fts->comparf, 1);
646*4887Schin 		else if (bot)
647*4887Schin 		{
648*4887Schin 			bot->fts_link = f;
649*4887Schin 			bot = f;
650*4887Schin 		}
651*4887Schin 		else
652*4887Schin 			top = bot = f;
653*4887Schin 	}
654*4887Schin 	if (fts->comparf)
655*4887Schin 		getlist(&top, &bot, root);
656*4887Schin 	return top;
657*4887Schin }
658*4887Schin 
659*4887Schin /*
660*4887Schin  * resize the path buffer
661*4887Schin  * note that free() is not used because we may need to chdir(fts->home)
662*4887Schin  * if there isn't enough space to continue
663*4887Schin  */
664*4887Schin 
665*4887Schin static int
666*4887Schin resize(register FTS* fts, int inc)
667*4887Schin {
668*4887Schin 	register char*	old;
669*4887Schin 	register char*	newp;
670*4887Schin 	register int	n_old;
671*4887Schin 
672*4887Schin 	/*
673*4887Schin 	 * add space for "/." used in testing FTS_DNX
674*4887Schin 	 */
675*4887Schin 
676*4887Schin 	n_old = fts->homesize;
677*4887Schin 	fts->homesize = ((fts->homesize + inc + 4) / PATH_MAX + 1) * PATH_MAX;
678*4887Schin 	if (!(newp = newof(0, char, fts->homesize, 0)))
679*4887Schin 	{
680*4887Schin 		fts->fts_errno = errno;
681*4887Schin 		fts->state = FTS_error;
682*4887Schin 		return -1;
683*4887Schin 	}
684*4887Schin 	old = fts->home;
685*4887Schin 	fts->home = newp;
686*4887Schin 	memcpy(newp, old, n_old);
687*4887Schin 	if (fts->endbuf)
688*4887Schin 		fts->endbuf = newp + fts->homesize - 4;
689*4887Schin 	if (fts->path)
690*4887Schin 		fts->path = newp + (fts->path - old);
691*4887Schin 	if (fts->base)
692*4887Schin 		fts->base = newp + (fts->base - old);
693*4887Schin 	free(old);
694*4887Schin 	return 0;
695*4887Schin }
696*4887Schin 
697*4887Schin /*
698*4887Schin  * open a new fts stream on pathnames
699*4887Schin  */
700*4887Schin 
701*4887Schin FTS*
702*4887Schin fts_open(char* const* pathnames, int flags, int (*comparf)(FTSENT* const*, FTSENT* const*))
703*4887Schin {
704*4887Schin 	register FTS*	fts;
705*4887Schin 
706*4887Schin 	if (!(fts = newof(0, FTS, 1, sizeof(FTSENT))))
707*4887Schin 		return 0;
708*4887Schin 	fts->flags = flags;
709*4887Schin 	fts->cd = (flags & FTS_NOCHDIR) ? 1 : -1;
710*4887Schin 	fts->comparf = comparf;
711*4887Schin 	fts->fs3d = fs3d(FS3D_TEST);
712*4887Schin 
713*4887Schin 	/*
714*4887Schin 	 * set up the path work buffer
715*4887Schin 	 */
716*4887Schin 
717*4887Schin 	fts->homesize = 2 * PATH_MAX;
718*4887Schin 	for (;;)
719*4887Schin 	{
720*4887Schin 		if (!(fts->home = newof(fts->home, char, fts->homesize, 0)))
721*4887Schin 		{
722*4887Schin 			free(fts);
723*4887Schin 			return 0;
724*4887Schin 		}
725*4887Schin 		if (fts->cd > 0 || getcwd(fts->home, fts->homesize))
726*4887Schin 			break;
727*4887Schin 		if (errno == ERANGE)
728*4887Schin 			fts->homesize += PATH_MAX;
729*4887Schin 		else
730*4887Schin 			fts->cd = 1;
731*4887Schin 	}
732*4887Schin 	fts->endbuf = fts->home + fts->homesize - 4;
733*4887Schin 
734*4887Schin 	/*
735*4887Schin 	 * initialize the tippity-top
736*4887Schin 	 */
737*4887Schin 
738*4887Schin 	fts->parent = (FTSENT*)(fts + 1);
739*4887Schin 	fts->parent->fts_info = FTS_D;
740*4887Schin 	memcpy(fts->parent->fts_accpath = fts->parent->fts_path = fts->parent->fts_name = fts->parent->name, ".", 2);
741*4887Schin 	fts->parent->fts_level = -1;
742*4887Schin 	fts->parent->fts_statp = &fts->parent->statb;
743*4887Schin 	fts->parent->must = 2;
744*4887Schin 	fts->parent->type = DT_UNKNOWN;
745*4887Schin 	fts->path = fts->home + strlen(fts->home) + 1;
746*4887Schin 
747*4887Schin 	/*
748*4887Schin 	 * make the list of top elements
749*4887Schin 	 */
750*4887Schin 
751*4887Schin 	if (!pathnames || (flags & FTS_ONEPATH) || !*pathnames)
752*4887Schin 	{
753*4887Schin 		char*	v[2];
754*4887Schin 
755*4887Schin 		v[0] = pathnames && (flags & FTS_ONEPATH) ? (char*)pathnames : ".";
756*4887Schin 		v[1] = 0;
757*4887Schin 		fts->todo = toplist(fts, v);
758*4887Schin 	}
759*4887Schin 	else
760*4887Schin 		fts->todo = toplist(fts, pathnames);
761*4887Schin 	if (!fts->todo || fts->todo->fts_info == FTS_NS && !fts->todo->fts_link)
762*4887Schin 	{
763*4887Schin 		fts_close(fts);
764*4887Schin 		return 0;
765*4887Schin 	}
766*4887Schin 	return fts;
767*4887Schin }
768*4887Schin 
769*4887Schin /*
770*4887Schin  * return the next FTS entry
771*4887Schin  */
772*4887Schin 
773*4887Schin FTSENT*
774*4887Schin fts_read(register FTS* fts)
775*4887Schin {
776*4887Schin 	register char*		s;
777*4887Schin 	register int		n;
778*4887Schin 	register FTSENT*	f;
779*4887Schin 	struct dirent*		d;
780*4887Schin 	int			i;
781*4887Schin 	FTSENT*			t;
782*4887Schin 	Notify_t*		p;
783*4887Schin #ifdef verify
784*4887Schin 	struct stat		sb;
785*4887Schin #endif
786*4887Schin 
787*4887Schin 	for (;;) switch (fts->state)
788*4887Schin 	{
789*4887Schin 
790*4887Schin 	case FTS_top_return:
791*4887Schin 
792*4887Schin 		f = fts->todo;
793*4887Schin 		t = 0;
794*4887Schin 		while (f)
795*4887Schin 			if (f->status == FTS_SKIP)
796*4887Schin 			{
797*4887Schin 				if (t)
798*4887Schin 				{
799*4887Schin 					t->fts_link = f->fts_link;
800*4887Schin 					drop(fts, f);
801*4887Schin 					f = t->fts_link;
802*4887Schin 				}
803*4887Schin 				else
804*4887Schin 				{
805*4887Schin 					fts->todo = f->fts_link;
806*4887Schin 					drop(fts, f);
807*4887Schin 					f = fts->todo;
808*4887Schin 				}
809*4887Schin 			}
810*4887Schin 			else
811*4887Schin 			{
812*4887Schin 				t = f;
813*4887Schin 				f = f->fts_link;
814*4887Schin 			}
815*4887Schin 		/*FALLTHROUGH*/
816*4887Schin 
817*4887Schin 	case 0:
818*4887Schin 
819*4887Schin 		if (!(f = fts->todo))
820*4887Schin 			return 0;
821*4887Schin 		/*FALLTHROUGH*/
822*4887Schin 
823*4887Schin 	case FTS_todo:
824*4887Schin 
825*4887Schin 		/*
826*4887Schin 		 * process the top object on the stack
827*4887Schin 		 */
828*4887Schin 
829*4887Schin 		fts->root = fts->top = fts->bot = 0;
830*4887Schin 
831*4887Schin 		/*
832*4887Schin 		 * initialize the top level
833*4887Schin 		 */
834*4887Schin 
835*4887Schin 		if (f->fts_level == 0)
836*4887Schin 		{
837*4887Schin 			fts->parent->fts_number = f->fts_number;
838*4887Schin 			fts->parent->fts_pointer = f->fts_pointer;
839*4887Schin 			fts->parent->fts_statp = f->fts_statp;
840*4887Schin 			fts->parent->statb = *f->fts_statp;
841*4887Schin 			f->fts_parent = fts->parent;
842*4887Schin 			fts->diroot = 0;
843*4887Schin 			if (fts->cd == 0)
844*4887Schin 				pathcd(fts->home, NiL);
845*4887Schin 			else if (fts->cd < 0)
846*4887Schin 				fts->cd = 0;
847*4887Schin 			fts->pwd = f->fts_parent;
848*4887Schin 			fts->curdir = fts->cd ? 0 : f->fts_parent;
849*4887Schin 			*(fts->base = fts->path) = 0;
850*4887Schin 		}
851*4887Schin 
852*4887Schin 		/*
853*4887Schin 		 * chdir to parent if asked for
854*4887Schin 		 */
855*4887Schin 
856*4887Schin 		if (fts->cd < 0)
857*4887Schin 		{
858*4887Schin 			fts->cd = setdir(fts->home, fts->path);
859*4887Schin 			fts->pwd = f->fts_parent;
860*4887Schin 			fts->curdir = fts->cd ? 0 : f->fts_parent;
861*4887Schin 		}
862*4887Schin 
863*4887Schin 		/*
864*4887Schin 		 * add object's name to the path
865*4887Schin 		 */
866*4887Schin 
867*4887Schin 		if ((fts->baselen = f->fts_namelen) >= (fts->endbuf - fts->base) && resize(fts, fts->baselen))
868*4887Schin 			return 0;
869*4887Schin 		memcpy(fts->base, f->name, fts->baselen + 1);
870*4887Schin 		fts->name = fts->cd ? fts->path : fts->base;
871*4887Schin 		/*FALLTHROUGH*/
872*4887Schin 
873*4887Schin 	case FTS_preorder:
874*4887Schin 
875*4887Schin 		/*
876*4887Schin 		 * check for cycle and open dir
877*4887Schin 		 */
878*4887Schin 
879*4887Schin 		if (f->fts_info == FTS_D)
880*4887Schin 		{
881*4887Schin 			if ((fts->diroot = search(f, fts->diroot, statcmp, 0)) != f || f->fts_level > 0 && (t = f) && statcmp(&t, &f->fts_parent) == 0)
882*4887Schin 			{
883*4887Schin 				f->fts_info = FTS_DC;
884*4887Schin 				f->fts_cycle = fts->diroot;
885*4887Schin 			}
886*4887Schin 			else if (!(fts->flags & FTS_TOP) && (!(fts->flags & FTS_XDEV) || f->statb.st_dev == f->fts_parent->statb.st_dev))
887*4887Schin 			{
888*4887Schin 				/*
889*4887Schin 				 * buffer is known to be large enough here!
890*4887Schin 				 */
891*4887Schin 
892*4887Schin 				if (fts->base[fts->baselen - 1] != '/')
893*4887Schin 					memcpy(fts->base + fts->baselen, "/.", 3);
894*4887Schin 				if (!(fts->dir = opendir(fts->name)))
895*4887Schin 					f->fts_info = FTS_DNX;
896*4887Schin 				fts->base[fts->baselen] = 0;
897*4887Schin 				if (!fts->dir && !(fts->dir = opendir(fts->name)))
898*4887Schin 					f->fts_info = FTS_DNR;
899*4887Schin 			}
900*4887Schin 		}
901*4887Schin 		f->nd = f->fts_info & ~FTS_DNX;
902*4887Schin 		if (f->nd || !(fts->flags & FTS_NOPREORDER))
903*4887Schin 		{
904*4887Schin 			fts->current = f;
905*4887Schin 			fts->link = f->fts_link;
906*4887Schin 			f->fts_link = 0;
907*4887Schin 			f->fts_path = PATH(fts, fts->path, f->fts_level);
908*4887Schin 			f->fts_pathlen = (fts->base - f->fts_path) + fts->baselen;
909*4887Schin 			f->fts_accpath = ACCESS(fts, f);
910*4887Schin 			fts->state = FTS_preorder_return;
911*4887Schin 			goto note;
912*4887Schin 		}
913*4887Schin 		/*FALLTHROUGH*/
914*4887Schin 
915*4887Schin 	case FTS_preorder_resume:
916*4887Schin 
917*4887Schin 		/*
918*4887Schin 		 * prune
919*4887Schin 		 */
920*4887Schin 
921*4887Schin 		if (!fts->dir || f->nd || f->status == FTS_SKIP)
922*4887Schin 		{
923*4887Schin 			if (fts->dir)
924*4887Schin 			{
925*4887Schin 				closedir(fts->dir);
926*4887Schin 				fts->dir = 0;
927*4887Schin 			}
928*4887Schin 			fts->state = FTS_popstack;
929*4887Schin 			continue;
930*4887Schin 		}
931*4887Schin 
932*4887Schin 		/*
933*4887Schin 		 * FTS_D or FTS_DNX, about to read children
934*4887Schin 		 */
935*4887Schin 
936*4887Schin 		if (fts->cd == 0)
937*4887Schin 		{
938*4887Schin 			if ((fts->cd = chdir(fts->name)) < 0)
939*4887Schin 				pathcd(fts->home, NiL);
940*4887Schin 			else if (fts->pwd != f)
941*4887Schin 			{
942*4887Schin 				f->pwd = fts->pwd;
943*4887Schin 				fts->pwd = f;
944*4887Schin 			}
945*4887Schin 			fts->curdir = fts->cd < 0 ? 0 : f;
946*4887Schin 		}
947*4887Schin 		fts->nostat = fts->children > 1 || f->fts_info == FTS_DNX;
948*4887Schin 		fts->cpname = fts->cd && !fts->nostat || !fts->children && !fts->comparf;
949*4887Schin 		fts->dotdot = 0;
950*4887Schin 		fts->endbase = fts->base + fts->baselen;
951*4887Schin 		if (fts->endbase[-1] != '/')
952*4887Schin 			*fts->endbase++ = '/';
953*4887Schin 		fts->current = f;
954*4887Schin 		/*FALLTHROUGH*/
955*4887Schin 
956*4887Schin 	case FTS_readdir:
957*4887Schin 
958*4887Schin 		while (d = readdir(fts->dir))
959*4887Schin 		{
960*4887Schin 			s = d->d_name;
961*4887Schin 			if (s[0] == '.')
962*4887Schin 			{
963*4887Schin 				if (s[1] == 0)
964*4887Schin 				{
965*4887Schin 					fts->current->nlink--;
966*4887Schin 					if (!(fts->flags & FTS_SEEDOT))
967*4887Schin 						continue;
968*4887Schin 					n = 1;
969*4887Schin 				}
970*4887Schin 				else if (s[1] == '.' && s[2] == 0)
971*4887Schin 				{
972*4887Schin 					fts->current->nlink--;
973*4887Schin 					if (fts->current->must == 1)
974*4887Schin 						fts->current->must = 0;
975*4887Schin 					if (!(fts->flags & FTS_SEEDOT))
976*4887Schin 						continue;
977*4887Schin 					n = 2;
978*4887Schin 				}
979*4887Schin 				else
980*4887Schin 					n = 0;
981*4887Schin 			}
982*4887Schin 			else
983*4887Schin 				n = 0;
984*4887Schin 
985*4887Schin 			/*
986*4887Schin 			 * make a new entry
987*4887Schin 			 */
988*4887Schin 
989*4887Schin 			i = D_NAMLEN(d);
990*4887Schin 			if (!(f = node(fts, fts->current, s, i)))
991*4887Schin 				return 0;
992*4887Schin 			TYPE(f, D_TYPE(d));
993*4887Schin 
994*4887Schin 			/*
995*4887Schin 			 * check for space
996*4887Schin 			 */
997*4887Schin 
998*4887Schin 			if (i >= fts->endbuf - fts->endbase)
999*4887Schin 			{
1000*4887Schin 	   	   		if (resize(fts, i))
1001*4887Schin 					return 0;
1002*4887Schin 				fts->endbase = fts->base + fts->baselen;
1003*4887Schin 				if (fts->endbase[-1] != '/')
1004*4887Schin 					fts->endbase++;
1005*4887Schin 			}
1006*4887Schin 			if (fts->cpname)
1007*4887Schin 			{
1008*4887Schin 				memcpy(fts->endbase, s, i + 1);
1009*4887Schin 				if (fts->cd)
1010*4887Schin 					s = fts->path;
1011*4887Schin 			}
1012*4887Schin 			if (n)
1013*4887Schin 			{
1014*4887Schin 				/*
1015*4887Schin 				 * don't recurse on . and ..
1016*4887Schin 				 */
1017*4887Schin 
1018*4887Schin 				if (n == 1)
1019*4887Schin 					f->fts_statp = fts->current->fts_statp;
1020*4887Schin 				else
1021*4887Schin 				{
1022*4887Schin 					if (f->fts_info != FTS_NS)
1023*4887Schin 						fts->dotdot = f;
1024*4887Schin 					if (fts->current->fts_parent->fts_level < 0)
1025*4887Schin 					{
1026*4887Schin 						f->fts_statp = &fts->current->fts_parent->statb;
1027*4887Schin 						info(fts, f, s, f->fts_statp, 0);
1028*4887Schin 					}
1029*4887Schin 					else
1030*4887Schin 						f->fts_statp = fts->current->fts_parent->fts_statp;
1031*4887Schin 				}
1032*4887Schin 				f->fts_info = FTS_DOT;
1033*4887Schin 			}
1034*4887Schin 			else if ((fts->nostat || SKIP(fts, f)) && (f->fts_info = FTS_NSOK) || info(fts, f, s, &f->statb, fts->flags))
1035*4887Schin 				f->statb.st_ino = D_FILENO(d);
1036*4887Schin 			if (fts->comparf)
1037*4887Schin 				fts->root = search(f, fts->root, fts->comparf, 1);
1038*4887Schin 			else if (fts->children || f->fts_info == FTS_D || f->fts_info == FTS_SL)
1039*4887Schin 			{
1040*4887Schin 				if (fts->top)
1041*4887Schin 					fts->bot = fts->bot->fts_link = f;
1042*4887Schin 				else
1043*4887Schin 					fts->top = fts->bot = f;
1044*4887Schin 			}
1045*4887Schin 			else
1046*4887Schin 			{
1047*4887Schin 				/*
1048*4887Schin 				 * terminal node
1049*4887Schin 				 */
1050*4887Schin 
1051*4887Schin 				f->fts_path = PATH(fts, fts->path, 1);
1052*4887Schin 				f->fts_pathlen = fts->endbase - f->fts_path + f->fts_namelen;
1053*4887Schin 				f->fts_accpath = ACCESS(fts, f);
1054*4887Schin 				fts->previous = fts->current;
1055*4887Schin 				fts->current = f;
1056*4887Schin 				fts->state = FTS_terminal;
1057*4887Schin 				goto note;
1058*4887Schin 			}
1059*4887Schin 		}
1060*4887Schin 
1061*4887Schin 		/*
1062*4887Schin 		 * done with the directory
1063*4887Schin 		 */
1064*4887Schin 
1065*4887Schin 		closedir(fts->dir);
1066*4887Schin 		fts->dir = 0;
1067*4887Schin 		if (fts->root)
1068*4887Schin 			getlist(&fts->top, &fts->bot, fts->root);
1069*4887Schin 		if (fts->children)
1070*4887Schin 		{
1071*4887Schin 			/*
1072*4887Schin 			 * try moving back to parent dir
1073*4887Schin 			 */
1074*4887Schin 
1075*4887Schin 			fts->base[fts->baselen] = 0;
1076*4887Schin 			if (fts->cd <= 0)
1077*4887Schin 			{
1078*4887Schin 				f = fts->current->fts_parent;
1079*4887Schin 				if (fts->cd < 0
1080*4887Schin 				    || f != fts->curdir
1081*4887Schin 				    || !fts->dotdot
1082*4887Schin 				    || !SAME(f->fts_statp, fts->dotdot->fts_statp)
1083*4887Schin 				    || fts->pwd && fts->pwd->symlink
1084*4887Schin 				    || (fts->cd = chdir("..")) < 0
1085*4887Schin #ifdef verify
1086*4887Schin 				    || stat(".", &sb) < 0
1087*4887Schin 				    || !SAME(&sb, fts->dotdot->fts_statp)
1088*4887Schin #endif
1089*4887Schin 				    )
1090*4887Schin 					fts->cd = setpdir(fts->home, fts->path, fts->base);
1091*4887Schin 				if (fts->pwd)
1092*4887Schin 					fts->pwd = fts->pwd->pwd;
1093*4887Schin 				fts->curdir = fts->cd ? 0 : f;
1094*4887Schin 			}
1095*4887Schin 			f = fts->current;
1096*4887Schin 			fts->link = f->fts_link;
1097*4887Schin 			f->fts_link = fts->top;
1098*4887Schin 			f->fts_path = PATH(fts, fts->path, f->fts_level);
1099*4887Schin 			f->fts_pathlen = (fts->base - f->fts_path) + f->fts_namelen;
1100*4887Schin 			f->fts_accpath = ACCESS(fts, f);
1101*4887Schin 			fts->state = FTS_children_return;
1102*4887Schin 			goto note;
1103*4887Schin 		}
1104*4887Schin 		/*FALLTHROUGH*/
1105*4887Schin 
1106*4887Schin 	case FTS_children_resume:
1107*4887Schin 
1108*4887Schin 		fts->base[fts->baselen] = 0;
1109*4887Schin 		if (fts->top)
1110*4887Schin 		{
1111*4887Schin 			fts->bot->fts_link = fts->todo;
1112*4887Schin 			fts->todo = fts->top;
1113*4887Schin 			fts->top = 0;
1114*4887Schin 		}
1115*4887Schin 		/*FALLTHROUGH*/
1116*4887Schin 
1117*4887Schin 	case FTS_popstack:
1118*4887Schin 
1119*4887Schin 		/*
1120*4887Schin 		 * pop objects completely processed
1121*4887Schin 		 */
1122*4887Schin 
1123*4887Schin 		fts->nd = 0;
1124*4887Schin 		f = fts->current;
1125*4887Schin 		/*FALLTHROUGH*/
1126*4887Schin 
1127*4887Schin 	case FTS_popstack_resume:
1128*4887Schin 
1129*4887Schin 		while (fts->todo && f == fts->todo)
1130*4887Schin 		{
1131*4887Schin 			t = f->fts_parent;
1132*4887Schin 			if ((f->fts_info & FTS_DP) == FTS_D)
1133*4887Schin 			{
1134*4887Schin 				/*
1135*4887Schin 				 * delete from <dev,ino> tree
1136*4887Schin 				 */
1137*4887Schin 
1138*4887Schin 				if (f != fts->diroot)
1139*4887Schin 					fts->diroot = search(f, fts->diroot, statcmp, 0);
1140*4887Schin 				fts->diroot = deleteroot(fts->diroot);
1141*4887Schin 				if (f == fts->curdir)
1142*4887Schin 				{
1143*4887Schin 					fts->nd++;
1144*4887Schin 					fts->curdir = t;
1145*4887Schin 				}
1146*4887Schin 
1147*4887Schin 				/*
1148*4887Schin 				 * perform post-order processing
1149*4887Schin 				 */
1150*4887Schin 
1151*4887Schin 				if (!(fts->flags & FTS_NOPOSTORDER) &&
1152*4887Schin 				    f->status != FTS_SKIP &&
1153*4887Schin 				    f->status != FTS_NOPOSTORDER)
1154*4887Schin 				{
1155*4887Schin 					/*
1156*4887Schin 					 * move to parent dir
1157*4887Schin 					 */
1158*4887Schin 
1159*4887Schin 					if (fts->nd > 0)
1160*4887Schin 						fts->cd = popdirs(fts);
1161*4887Schin 					if (fts->cd < 0)
1162*4887Schin 						fts->cd = setpdir(fts->home, fts->path, fts->base);
1163*4887Schin 					fts->curdir = fts->cd ? 0 : t;
1164*4887Schin 					f->fts_info = FTS_DP;
1165*4887Schin 					f->fts_path = PATH(fts, fts->path, f->fts_level);
1166*4887Schin 					f->fts_pathlen = (fts->base - f->fts_path) + f->fts_namelen;
1167*4887Schin 					f->fts_accpath = ACCESS(fts, f);
1168*4887Schin 
1169*4887Schin 					/*
1170*4887Schin 					 * re-stat to update nlink/times
1171*4887Schin 					 */
1172*4887Schin 
1173*4887Schin 					stat(f->fts_accpath, f->fts_statp);
1174*4887Schin 					fts->link = f->fts_link;
1175*4887Schin 					f->fts_link = 0;
1176*4887Schin 					fts->state = FTS_popstack_return;
1177*4887Schin 					goto note;
1178*4887Schin 				}
1179*4887Schin 			}
1180*4887Schin 
1181*4887Schin 			/*
1182*4887Schin 			 * reset base
1183*4887Schin 			 */
1184*4887Schin 
1185*4887Schin 			if (fts->base > fts->path + t->fts_namelen)
1186*4887Schin 				fts->base--;
1187*4887Schin 			*fts->base = 0;
1188*4887Schin 			fts->base -= t->fts_namelen;
1189*4887Schin 
1190*4887Schin 			/*
1191*4887Schin 			 * try again or delete from top of stack
1192*4887Schin 			 */
1193*4887Schin 
1194*4887Schin 			if (f->status == FTS_AGAIN)
1195*4887Schin 			{
1196*4887Schin 				f->fts_info = FTS_D;
1197*4887Schin 				f->status = 0;
1198*4887Schin 			}
1199*4887Schin 			else
1200*4887Schin 			{
1201*4887Schin 				fts->todo = fts->todo->fts_link;
1202*4887Schin 				drop(fts, f);
1203*4887Schin 			}
1204*4887Schin 			f = t;
1205*4887Schin 		}
1206*4887Schin 
1207*4887Schin 		/*
1208*4887Schin 		 * reset current directory
1209*4887Schin 		 */
1210*4887Schin 
1211*4887Schin 		if (fts->nd > 0 && popdirs(fts) < 0)
1212*4887Schin 		{
1213*4887Schin 			pathcd(fts->home, NiL);
1214*4887Schin 			fts->curdir = 0;
1215*4887Schin 			fts->cd = -1;
1216*4887Schin 		}
1217*4887Schin 		if (fts->todo)
1218*4887Schin 		{
1219*4887Schin 			if (*fts->base)
1220*4887Schin 				fts->base += f->fts_namelen;
1221*4887Schin 			if (*(fts->base - 1) != '/')
1222*4887Schin 				*fts->base++ = '/';
1223*4887Schin 			*fts->base = 0;
1224*4887Schin 			f = fts->todo;
1225*4887Schin 			fts->state = FTS_todo;
1226*4887Schin 			continue;
1227*4887Schin 		}
1228*4887Schin 		return 0;
1229*4887Schin 
1230*4887Schin 	case FTS_children_return:
1231*4887Schin 
1232*4887Schin 		f = fts->current;
1233*4887Schin 		f->fts_link = fts->link;
1234*4887Schin 
1235*4887Schin 		/*
1236*4887Schin 		 * chdir down again
1237*4887Schin 		 */
1238*4887Schin 
1239*4887Schin 		i = f->fts_info != FTS_DNX;
1240*4887Schin 		n = f->status == FTS_SKIP;
1241*4887Schin 		if (!n && fts->cd == 0)
1242*4887Schin 		{
1243*4887Schin 			if ((fts->cd = chdir(fts->base)) < 0)
1244*4887Schin 				pathcd(fts->home, NiL);
1245*4887Schin 			else if (fts->pwd != f)
1246*4887Schin 			{
1247*4887Schin 				f->pwd = fts->pwd;
1248*4887Schin 				fts->pwd = f;
1249*4887Schin 			}
1250*4887Schin 			fts->curdir = fts->cd ? 0 : f;
1251*4887Schin 		}
1252*4887Schin 
1253*4887Schin 		/*
1254*4887Schin 		 * prune
1255*4887Schin 		 */
1256*4887Schin 
1257*4887Schin 		if (fts->base[fts->baselen - 1] != '/')
1258*4887Schin 			fts->base[fts->baselen] = '/';
1259*4887Schin 		for (fts->bot = 0, f = fts->top; f; )
1260*4887Schin 			if (n || f->status == FTS_SKIP)
1261*4887Schin 			{
1262*4887Schin 				if (fts->bot)
1263*4887Schin 					fts->bot->fts_link = f->fts_link;
1264*4887Schin 				else
1265*4887Schin 					fts->top = f->fts_link;
1266*4887Schin 				drop(fts, f);
1267*4887Schin 				f = fts->bot ? fts->bot->fts_link : fts->top;
1268*4887Schin 			}
1269*4887Schin 			else
1270*4887Schin 			{
1271*4887Schin 				if (fts->children > 1 && i)
1272*4887Schin 				{
1273*4887Schin 					if (f->status == FTS_STAT)
1274*4887Schin 						info(fts, f, NiL, f->fts_statp, 0);
1275*4887Schin 					else if (f->fts_info == FTS_NSOK && !SKIP(fts, f))
1276*4887Schin 					{
1277*4887Schin 						s = f->fts_name;
1278*4887Schin 						if (fts->cd)
1279*4887Schin 						{
1280*4887Schin 							memcpy(fts->endbase, s, f->fts_namelen + 1);
1281*4887Schin 							s = fts->path;
1282*4887Schin 						}
1283*4887Schin 						info(fts, f, s, f->fts_statp, fts->flags);
1284*4887Schin 					}
1285*4887Schin 				}
1286*4887Schin 				fts->bot = f;
1287*4887Schin 				f = f->fts_link;
1288*4887Schin 			}
1289*4887Schin 		fts->children = 0;
1290*4887Schin 		fts->state = FTS_children_resume;
1291*4887Schin 		continue;
1292*4887Schin 
1293*4887Schin 	case FTS_popstack_return:
1294*4887Schin 
1295*4887Schin 		f = fts->todo;
1296*4887Schin 		f->fts_link = fts->link;
1297*4887Schin 		f->fts_info = f->status == FTS_AGAIN ? FTS_DP : 0;
1298*4887Schin 		fts->state = FTS_popstack_resume;
1299*4887Schin 		continue;
1300*4887Schin 
1301*4887Schin 	case FTS_preorder_return:
1302*4887Schin 
1303*4887Schin 		f = fts->current;
1304*4887Schin 		f->fts_link = fts->link;
1305*4887Schin 
1306*4887Schin 		/*
1307*4887Schin 		 * follow symlink if asked to
1308*4887Schin 		 */
1309*4887Schin 
1310*4887Schin 		if (f->status == FTS_FOLLOW)
1311*4887Schin 		{
1312*4887Schin 			f->status = 0;
1313*4887Schin 			if (f->fts_info == FTS_SL || ISTYPE(f, DT_LNK) || f->fts_info == FTS_NSOK)
1314*4887Schin 			{
1315*4887Schin 				info(fts, f, f->fts_accpath, f->fts_statp, 0);
1316*4887Schin 				if (f->fts_info != FTS_SL)
1317*4887Schin 				{
1318*4887Schin 					fts->state = FTS_preorder;
1319*4887Schin 					continue;
1320*4887Schin 				}
1321*4887Schin 			}
1322*4887Schin 		}
1323*4887Schin 
1324*4887Schin 		/*
1325*4887Schin 		 * about to prune this f and already at home
1326*4887Schin 		 */
1327*4887Schin 
1328*4887Schin 		if (fts->cd == 0 && f->fts_level == 0 && f->nd)
1329*4887Schin 			fts->cd = -1;
1330*4887Schin 		fts->state = FTS_preorder_resume;
1331*4887Schin 		continue;
1332*4887Schin 
1333*4887Schin 	case FTS_terminal:
1334*4887Schin 
1335*4887Schin 		f = fts->current;
1336*4887Schin 		if (f->status == FTS_FOLLOW)
1337*4887Schin 		{
1338*4887Schin 			f->status = 0;
1339*4887Schin 			if (f->fts_info == FTS_SL || ISTYPE(f, DT_LNK) || f->fts_info == FTS_NSOK)
1340*4887Schin 			{
1341*4887Schin 				info(fts, f, f->fts_accpath, f->fts_statp, 0);
1342*4887Schin 				if (f->symlink && f->fts_info != FTS_SL)
1343*4887Schin 				{
1344*4887Schin 					if (!(f->fts_link = fts->top))
1345*4887Schin 						fts->bot = f;
1346*4887Schin 					fts->top = f;
1347*4887Schin 					fts->current = fts->previous;
1348*4887Schin 					fts->state = FTS_readdir;
1349*4887Schin 					continue;
1350*4887Schin 				}
1351*4887Schin 			}
1352*4887Schin 		}
1353*4887Schin 		f = f->fts_parent;
1354*4887Schin 		drop(fts, fts->current);
1355*4887Schin 		fts->current = f;
1356*4887Schin 		fts->state = FTS_readdir;
1357*4887Schin 		continue;
1358*4887Schin 
1359*4887Schin 	case FTS_error:
1360*4887Schin 
1361*4887Schin 		return 0;
1362*4887Schin 
1363*4887Schin 	default:
1364*4887Schin 
1365*4887Schin 		fts->fts_errno = EINVAL;
1366*4887Schin 		fts->state = FTS_error;
1367*4887Schin 		return 0;
1368*4887Schin 
1369*4887Schin 	}
1370*4887Schin  note:
1371*4887Schin 	for (p = notify; p; p = p->next)
1372*4887Schin 		if ((n = (*p->notifyf)(fts, f, p->context)) > 0)
1373*4887Schin 			break;
1374*4887Schin 		else if (n < 0)
1375*4887Schin 		{
1376*4887Schin 			fts->fts_errno = EINVAL;
1377*4887Schin 			fts->state = FTS_error;
1378*4887Schin 			return 0;
1379*4887Schin 		}
1380*4887Schin 	return f;
1381*4887Schin }
1382*4887Schin 
1383*4887Schin /*
1384*4887Schin  * set stream or entry flags
1385*4887Schin  */
1386*4887Schin 
1387*4887Schin int
1388*4887Schin fts_set(register FTS* fts, register FTSENT* f, int status)
1389*4887Schin {
1390*4887Schin 	if (fts || !f || f->fts->current != f)
1391*4887Schin 		return -1;
1392*4887Schin 	switch (status)
1393*4887Schin 	{
1394*4887Schin 	case FTS_AGAIN:
1395*4887Schin 		break;
1396*4887Schin 	case FTS_FOLLOW:
1397*4887Schin 		if (!(f->fts_info & FTS_SL))
1398*4887Schin 			return -1;
1399*4887Schin 		break;
1400*4887Schin 	case FTS_NOPOSTORDER:
1401*4887Schin 		break;
1402*4887Schin 	case FTS_SKIP:
1403*4887Schin 		if ((f->fts_info & (FTS_D|FTS_P)) != FTS_D)
1404*4887Schin 			return -1;
1405*4887Schin 		break;
1406*4887Schin 	default:
1407*4887Schin 		return -1;
1408*4887Schin 	}
1409*4887Schin 	f->status = status;
1410*4887Schin 	return 0;
1411*4887Schin }
1412*4887Schin 
1413*4887Schin /*
1414*4887Schin  * return the list of child entries
1415*4887Schin  */
1416*4887Schin 
1417*4887Schin FTSENT*
1418*4887Schin fts_children(register FTS* fts, int flags)
1419*4887Schin {
1420*4887Schin 	register FTSENT*	f;
1421*4887Schin 
1422*4887Schin 	switch (fts->state)
1423*4887Schin 	{
1424*4887Schin 
1425*4887Schin 	case 0:
1426*4887Schin 
1427*4887Schin 		fts->state = FTS_top_return;
1428*4887Schin 		return fts->todo;
1429*4887Schin 
1430*4887Schin 	case FTS_preorder_return:
1431*4887Schin 
1432*4887Schin 		fts->children = ((flags | fts->flags) & FTS_NOSTAT) ? 2 : 1;
1433*4887Schin 		if (f = fts_read(fts))
1434*4887Schin 			f = f->fts_link;
1435*4887Schin 		return f;
1436*4887Schin 
1437*4887Schin 	}
1438*4887Schin 	return 0;
1439*4887Schin }
1440*4887Schin 
1441*4887Schin /*
1442*4887Schin  * return default (FTS_LOGICAL|FTS_META|FTS_PHYSICAL|FTS_SEEDOTDIR) flags
1443*4887Schin  * conditioned by astconf()
1444*4887Schin  */
1445*4887Schin 
1446*4887Schin int
1447*4887Schin fts_flags(void)
1448*4887Schin {
1449*4887Schin 	register char*	s;
1450*4887Schin 
1451*4887Schin 	s = astconf("PATH_RESOLVE", NiL, NiL);
1452*4887Schin 	if (streq(s, "logical"))
1453*4887Schin 		return FTS_LOGICAL;
1454*4887Schin 	if (streq(s, "physical"))
1455*4887Schin 		return FTS_PHYSICAL|FTS_SEEDOTDIR;
1456*4887Schin 	return FTS_META|FTS_PHYSICAL|FTS_SEEDOTDIR;
1457*4887Schin }
1458*4887Schin 
1459*4887Schin /*
1460*4887Schin  * close an open fts stream
1461*4887Schin  */
1462*4887Schin 
1463*4887Schin int
1464*4887Schin fts_close(register FTS* fts)
1465*4887Schin {
1466*4887Schin 	register FTSENT*	f;
1467*4887Schin 	register FTSENT*	x;
1468*4887Schin 
1469*4887Schin 	if (fts->dir)
1470*4887Schin 		closedir(fts->dir);
1471*4887Schin 	if (fts->cd == 0)
1472*4887Schin 		pathcd(fts->home, NiL);
1473*4887Schin 	free(fts->home);
1474*4887Schin 	if (fts->state == FTS_children_return)
1475*4887Schin 		fts->current->fts_link = fts->link;
1476*4887Schin 	if (fts->top)
1477*4887Schin 	{
1478*4887Schin 		fts->bot->fts_link = fts->todo;
1479*4887Schin 		fts->todo = fts->top;
1480*4887Schin 	}
1481*4887Schin 	for (f = fts->todo; f; f = x)
1482*4887Schin 	{
1483*4887Schin 		x = f->fts_link;
1484*4887Schin 		free(f);
1485*4887Schin 	}
1486*4887Schin 	for (f = fts->free; f; f = x)
1487*4887Schin 	{
1488*4887Schin 		x = f->fts_link;
1489*4887Schin 		free(f);
1490*4887Schin 	}
1491*4887Schin 	return 0;
1492*4887Schin }
1493*4887Schin 
1494*4887Schin /*
1495*4887Schin  * register function to be called for each fts_read() entry
1496*4887Schin  */
1497*4887Schin 
1498*4887Schin int
1499*4887Schin fts_notify(Notify_f notifyf, void* context)
1500*4887Schin {
1501*4887Schin 	register Notify_t*	np;
1502*4887Schin 
1503*4887Schin 	if (!(np = newof(0, Notify_t, 1, 0)))
1504*4887Schin 		return -1;
1505*4887Schin 	np->notifyf = notifyf;
1506*4887Schin 	np->context = context;
1507*4887Schin 	np->next = notify;
1508*4887Schin 	notify = np;
1509*4887Schin 	return 0;
1510*4887Schin }
1511