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