14887Schin /***********************************************************************
24887Schin *                                                                      *
34887Schin *               This software is part of the ast package               *
4*8462SApril.Chin@Sun.COM *          Copyright (c) 1985-2008 AT&T Intellectual Property          *
54887Schin *                      and is licensed under the                       *
64887Schin *                  Common Public License, Version 1.0                  *
7*8462SApril.Chin@Sun.COM *                    by AT&T Intellectual Property                     *
84887Schin *                                                                      *
94887Schin *                A copy of the License is available at                 *
104887Schin *            http://www.opensource.org/licenses/cpl1.0.txt             *
114887Schin *         (with md5 checksum 059e8cd6165cb4c31e351f2b69388fd9)         *
124887Schin *                                                                      *
134887Schin *              Information and Software Systems Research               *
144887Schin *                            AT&T Research                             *
154887Schin *                           Florham Park NJ                            *
164887Schin *                                                                      *
174887Schin *                 Glenn Fowler <gsf@research.att.com>                  *
184887Schin *                  David Korn <dgk@research.att.com>                   *
194887Schin *                   Phong Vo <kpv@research.att.com>                    *
204887Schin *                                                                      *
214887Schin ***********************************************************************/
224887Schin #pragma prototyped
234887Schin /*
244887Schin  * Phong Vo
254887Schin  * Glenn Fowler
264887Schin  * AT&T Research
274887Schin  *
284887Schin  * fts implementation unwound from the kpv ftwalk() of 1988-10-30
294887Schin  */
304887Schin 
314887Schin #include <ast.h>
324887Schin #include <ast_dir.h>
334887Schin #include <error.h>
344887Schin #include <fs3d.h>
35*8462SApril.Chin@Sun.COM #include <ls.h>
364887Schin 
374887Schin struct Ftsent;
384887Schin 
394887Schin typedef int (*Compar_f)(struct Ftsent* const*, struct Ftsent* const*);
404887Schin typedef int (*Stat_f)(const char*, struct stat*);
414887Schin 
424887Schin #define _FTS_PRIVATE_ \
434887Schin 	FTSENT*		parent;			/* top parent		*/ \
444887Schin 	FTSENT*		todo;			/* todo list		*/ \
454887Schin 	FTSENT*		top;			/* top element		*/ \
464887Schin 	FTSENT*		root;						   \
474887Schin 	FTSENT*		bot;			/* bottom element	*/ \
484887Schin 	FTSENT*		free;			/* free element		*/ \
494887Schin 	FTSENT*		diroot;						   \
504887Schin 	FTSENT*		curdir;						   \
514887Schin 	FTSENT*		current;		/* current element	*/ \
524887Schin 	FTSENT*		previous;		/* previous current	*/ \
534887Schin 	FTSENT*		dotdot;						   \
544887Schin 	FTSENT*		link;			/* real current fts_link*/ \
554887Schin 	FTSENT*		pwd;			/* pwd parent		*/ \
564887Schin 	DIR*		dir;			/* current dir stream	*/ \
574887Schin 	Compar_f	comparf;		/* node comparison func	*/ \
584887Schin 	int		baselen;		/* current strlen(base)	*/ \
594887Schin 	int		cd;			/* chdir status		*/ \
604887Schin 	int		cpname;						   \
614887Schin 	int		flags;			/* fts_open() flags	*/ \
624887Schin 	int		homesize;		/* sizeof(home)		*/ \
634887Schin 	int		nd;						   \
644887Schin 	unsigned char	children;					   \
654887Schin 	unsigned char	fs3d;						   \
664887Schin 	unsigned char	nostat;					   	   \
674887Schin 	unsigned char	state;			/* fts_read() state	*/ \
684887Schin 	char*		base;			/* basename in path	*/ \
694887Schin 	char*		name;						   \
704887Schin 	char*		path;			/* path workspace	*/ \
714887Schin 	char*		home;			/* home/path buffer	*/ \
724887Schin 	char*		endbase;		/* space to build paths */ \
734887Schin 	char*		endbuf;			/* space to build paths */ \
74*8462SApril.Chin@Sun.COM 	char*		pad[2];			/* $0.02 to splain this	*/
754887Schin 
764887Schin /*
774887Schin  * NOTE: <ftwalk.h> relies on status and statb being the first two elements
784887Schin  */
794887Schin 
804887Schin #define _FTSENT_PRIVATE_ \
814887Schin 	short		status;			/* internal status	*/ \
824887Schin 	struct stat	statb;			/* fts_statp data	*/ \
83*8462SApril.Chin@Sun.COM 	FTS*		fts;			/* fts_open() handle	*/ \
844887Schin 	int		nd;			/* popdir() count	*/ \
854887Schin 	FTSENT*		left;			/* left child		*/ \
864887Schin 	FTSENT*		right;			/* right child		*/ \
874887Schin 	FTSENT*		pwd;			/* pwd parent		*/ \
884887Schin 	FTSENT*		stack;			/* getlist() stack	*/ \
894887Schin 	long		nlink;			/* FTS_D link count	*/ \
904887Schin 	unsigned char	must;			/* must stat		*/ \
914887Schin 	unsigned char	type;			/* DT_* type		*/ \
924887Schin 	unsigned char	symlink;		/* originally a symlink	*/ \
934887Schin 	char		name[sizeof(int)];	/* fts_name data	*/
944887Schin 
954887Schin #include <fts.h>
964887Schin 
974887Schin #ifndef ENOSYS
984887Schin #define ENOSYS		EINVAL
994887Schin #endif
1004887Schin 
1014887Schin 
1024887Schin #if MAXNAMLEN > 16
1034887Schin #define MINNAME		32
1044887Schin #else
1054887Schin #define MINNAME		16
1064887Schin #endif
1074887Schin 
1084887Schin #define drop(p,f)	(((f)->fts_namelen < MINNAME) ? ((f)->fts_link = (p)->free, (p)->free = (f)) : (free(f), (p)->free))
1094887Schin 
1104887Schin #define ACCESS(p,f)	((p)->cd==0?(f)->fts_name:(f)->fts_path)
1114887Schin #define PATH(f,p,l)	((!((f)->flags&FTS_SEEDOTDIR)&&(l)>0&&(p)[0]=='.'&&(p)[1]=='/')?((p)+2):(p))
1124887Schin #define SAME(one,two)	((one)->st_ino==(two)->st_ino&&(one)->st_dev==(two)->st_dev)
1134887Schin #define SKIPLINK(p,f)	((f)->fts_parent->nlink == 0)
1144887Schin 
1154887Schin #ifdef D_TYPE
1164887Schin #define ISTYPE(f,t)	((f)->type == (t))
1174887Schin #define TYPE(f,t)	((f)->type = (t))
1184887Schin #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)))))
1194887Schin #else
1204887Schin #undef	DT_UNKNOWN
1214887Schin #define DT_UNKNOWN	0
1224887Schin #undef	DT_LNK
1234887Schin #define DT_LNK		1
1244887Schin #define ISTYPE(f,t)	((t)==DT_UNKNOWN)
1254887Schin #define TYPE(f,d)
1264887Schin #define SKIP(p,f)	((f)->fts_parent->must == 0 && SKIPLINK(p,f))
1274887Schin #endif
1284887Schin 
1294887Schin #ifndef D_FILENO
1304887Schin #define D_FILENO(d)	(1)
1314887Schin #endif
1324887Schin 
1334887Schin /*
1344887Schin  * NOTE: a malicious dir rename() could change .. underfoot so we
1354887Schin  *	 must always verify; undef verify to enable the unsafe code
1364887Schin  */
1374887Schin 
1384887Schin #define verify		1
1394887Schin 
1404887Schin /*
1414887Schin  * FTS_NOSTAT requires a dir with
1424887Schin  *	D_TYPE(&dirent_t)!=DT_UNKNOWN
1434887Schin  *	    OR
1444887Schin  *	st_nlink>=2
1454887Schin  */
1464887Schin 
1474887Schin #define FTS_children_resume	1
1484887Schin #define FTS_children_return	2
1494887Schin #define FTS_error		3
1504887Schin #define FTS_popstack		4
1514887Schin #define FTS_popstack_resume	5
1524887Schin #define FTS_popstack_return	6
1534887Schin #define FTS_preorder		7
1544887Schin #define FTS_preorder_resume	8
1554887Schin #define FTS_preorder_return	9
1564887Schin #define FTS_readdir		10
1574887Schin #define FTS_terminal		11
1584887Schin #define FTS_todo		12
1594887Schin #define FTS_top_return		13
1604887Schin 
1614887Schin typedef int (*Notify_f)(FTS*, FTSENT*, void*);
1624887Schin 
1634887Schin typedef struct Notify_s
1644887Schin {
1654887Schin 	struct Notify_s*	next;
1664887Schin 	Notify_f		notifyf;
1674887Schin 	void*			context;
1684887Schin } Notify_t;
1694887Schin 
1704887Schin static Notify_t*		notify;
1714887Schin 
1724887Schin /*
1734887Schin  * allocate an FTSENT node
1744887Schin  */
1754887Schin 
1764887Schin static FTSENT*
1774887Schin node(FTS* fts, FTSENT* parent, register char* name, register int namelen)
1784887Schin {
1794887Schin 	register FTSENT*	f;
1804887Schin 	register int		n;
1814887Schin 
1824887Schin 	if (fts->free && namelen < MINNAME)
1834887Schin 	{
1844887Schin 		f = fts->free;
1854887Schin 		fts->free = f->fts_link;
1864887Schin 	}
1874887Schin 	else
1884887Schin 	{
1894887Schin 		n = (namelen < MINNAME ? MINNAME : namelen + 1) - sizeof(int);
1904887Schin 		if (!(f = newof(0, FTSENT, 1, n)))
1914887Schin 		{
1924887Schin 			fts->fts_errno = errno;
1934887Schin 			fts->state = FTS_error;
1944887Schin 			return 0;
1954887Schin 		}
1964887Schin 		f->fts = fts;
1974887Schin 	}
1984887Schin 	TYPE(f, DT_UNKNOWN);
1994887Schin 	f->status = 0;
2004887Schin 	f->symlink = 0;
2014887Schin 	f->fts_level = (f->fts_parent = parent)->fts_level + 1;
2024887Schin 	f->fts_link = 0;
2034887Schin 	f->fts_pointer = 0;
2044887Schin 	f->fts_number = 0;
2054887Schin 	f->fts_errno = 0;
2064887Schin 	f->fts_namelen = namelen;
2074887Schin 	f->fts_name = f->name;
2084887Schin 	f->fts_statp = &f->statb;
2094887Schin 	memcpy(f->fts_name, name, namelen + 1);
2104887Schin 	return f;
2114887Schin }
2124887Schin 
2134887Schin /*
2144887Schin  * compare directories by device/inode
2154887Schin  */
2164887Schin 
2174887Schin static int
2184887Schin statcmp(FTSENT* const* pf1, FTSENT* const* pf2)
2194887Schin {
2204887Schin 	register const FTSENT*	f1 = *pf1;
2214887Schin 	register const FTSENT*	f2 = *pf2;
2224887Schin 
2234887Schin 	if (f1->statb.st_ino < f2->statb.st_ino)
2244887Schin 		return -1;
2254887Schin 	if (f1->statb.st_ino > f2->statb.st_ino)
2264887Schin 		return 1;
2274887Schin 	if (f1->statb.st_dev < f2->statb.st_dev)
2284887Schin 		return -1;
2294887Schin 	if (f1->statb.st_dev > f2->statb.st_dev)
2304887Schin 		return 1;
2314887Schin 
2324887Schin 	/*
2334887Schin 	 * hack for NFS where <dev,ino> may not uniquely identify objects
2344887Schin 	 */
2354887Schin 
2364887Schin 	if (f1->statb.st_mtime < f2->statb.st_mtime)
2374887Schin 		return -1;
2384887Schin 	if (f1->statb.st_mtime > f2->statb.st_mtime)
2394887Schin 		return 1;
2404887Schin 	return 0;
2414887Schin }
2424887Schin 
2434887Schin /*
2444887Schin  * search trees with top-down splaying (a la Tarjan and Sleator)
2454887Schin  * when used for insertion sort, this implements a stable sort
2464887Schin  */
2474887Schin 
2484887Schin #define RROTATE(r)	(t = r->left, r->left = t->right, t->right = r, r = t)
2494887Schin #define LROTATE(r)	(t = r->right, r->right = t->left, t->left = r, r = t)
2504887Schin 
2514887Schin static FTSENT*
2524887Schin search(FTSENT* e, FTSENT* root, int(*comparf)(FTSENT* const*, FTSENT* const*), int insert)
2534887Schin {
2544887Schin 	register int		cmp;
2554887Schin 	register FTSENT*	t;
2564887Schin 	register FTSENT*	left;
2574887Schin 	register FTSENT*	right;
2584887Schin 	register FTSENT*	lroot;
2594887Schin 	register FTSENT*	rroot;
2604887Schin 
2614887Schin 	left = right = lroot = rroot = 0;
2624887Schin 	while (root)
2634887Schin 	{
2644887Schin 		if (!(cmp = (*comparf)(&e, &root)) && !insert)
2654887Schin 			break;
2664887Schin 		if (cmp < 0)
2674887Schin 		{
2684887Schin 			/*
2694887Schin 			 * this is the left zig-zig case
2704887Schin 			 */
2714887Schin 
2724887Schin 			if (root->left && (cmp = (*comparf)(&e, &root->left)) <= 0)
2734887Schin 			{
2744887Schin 				RROTATE(root);
2754887Schin 				if (!cmp && !insert)
2764887Schin 					break;
2774887Schin 			}
2784887Schin 
2794887Schin 			/*
2804887Schin 			 * stick all things > e to the right tree
2814887Schin 			 */
2824887Schin 
2834887Schin 			if (right)
2844887Schin 				right->left = root;
2854887Schin 			else
2864887Schin 				rroot = root;
2874887Schin 			right = root;
2884887Schin 			root = root->left;
2894887Schin 			right->left = 0;
2904887Schin 		}
2914887Schin 		else
2924887Schin 		{
2934887Schin 			/*
2944887Schin 			 * this is the right zig-zig case
2954887Schin 			 */
2964887Schin 
2974887Schin 			if (root->right && (cmp = (*comparf)(&e, &root->right)) >= 0)
2984887Schin 			{
2994887Schin 				LROTATE(root);
3004887Schin 				if (!cmp && !insert)
3014887Schin 					break;
3024887Schin 			}
3034887Schin 
3044887Schin 			/*
3054887Schin 			 * stick all things <= e to the left tree
3064887Schin 			 */
3074887Schin 
3084887Schin 			if (left)
3094887Schin 				left->right = root;
3104887Schin 			else
3114887Schin 				lroot = root;
3124887Schin 			left = root;
3134887Schin 			root = root->right;
3144887Schin 			left->right = 0;
3154887Schin 		}
3164887Schin 	}
3174887Schin 	if (!root)
3184887Schin 		root = e;
3194887Schin 	else
3204887Schin 	{
3214887Schin 		if (right)
3224887Schin 			right->left = root->right;
3234887Schin 		else
3244887Schin 			rroot = root->right;
3254887Schin 		if (left)
3264887Schin 			left->right = root->left;
3274887Schin 		else
3284887Schin 			lroot = root->left;
3294887Schin 	}
3304887Schin 	root->left = lroot;
3314887Schin 	root->right = rroot;
3324887Schin 	return root;
3334887Schin }
3344887Schin 
3354887Schin /*
3364887Schin  * delete the root element from the tree
3374887Schin  */
3384887Schin 
3394887Schin static FTSENT*
3404887Schin deleteroot(register FTSENT* root)
3414887Schin {
3424887Schin 	register FTSENT*	t;
3434887Schin 	register FTSENT*	left;
3444887Schin 	register FTSENT*	right;
3454887Schin 
3464887Schin 	right = root->right;
3474887Schin 	if (!(left = root->left))
3484887Schin 		root = right;
3494887Schin 	else
3504887Schin 	{
3514887Schin 		while (left->right)
3524887Schin 			LROTATE(left);
3534887Schin 		left->right = right;
3544887Schin 		root = left;
3554887Schin 	}
3564887Schin 	return root;
3574887Schin }
3584887Schin 
3594887Schin /*
3604887Schin  * generate ordered fts_link list from binary tree at root
3614887Schin  * FTSENT.stack instead of recursion to avoid blowing the real
3624887Schin  * stack on big directories
3634887Schin  */
3644887Schin 
3654887Schin static void
3664887Schin getlist(register FTSENT** top, register FTSENT** bot, register FTSENT* root)
3674887Schin {
3684887Schin 	register FTSENT*	stack = 0;
3694887Schin 
3704887Schin 	for (;;)
3714887Schin 	{
3724887Schin 		if (root->left)
3734887Schin 		{
3744887Schin 			root->stack = stack;
3754887Schin 			stack = root;
3764887Schin 			root = root->left;
3774887Schin 		}
3784887Schin 		else
3794887Schin 		{
3804887Schin 			for (;;)
3814887Schin 			{
3824887Schin 				if (*top)
3834887Schin 					*bot = (*bot)->fts_link = root;
3844887Schin 				else
3854887Schin 					*bot = *top = root;
3864887Schin 				if (root->right)
3874887Schin 				{
3884887Schin 					root = root->right;
3894887Schin 					break;
3904887Schin 				}
3914887Schin 				if (!(root = stack))
3924887Schin 					return;
3934887Schin 				stack = stack->stack;
3944887Schin 			}
3954887Schin 		}
3964887Schin 	}
3974887Schin }
3984887Schin 
3994887Schin /*
4004887Schin  * set directory when curdir is lost in space
4014887Schin  */
4024887Schin 
4034887Schin static int
4044887Schin setdir(register char* home, register char* path)
4054887Schin {
4064887Schin 	register int	cdrv;
4074887Schin 
4084887Schin 	if (path[0] == '/')
4094887Schin 		cdrv = pathcd(path, NiL);
4104887Schin 	else
4114887Schin 	{
4124887Schin 		/*
4134887Schin 		 * note that path and home are in the same buffer
4144887Schin 		 */
4154887Schin 
4164887Schin 		path[-1] = '/';
4174887Schin 		cdrv = pathcd(home, NiL);
4184887Schin 		path[-1] = 0;
4194887Schin 	}
4204887Schin 	if (cdrv < 0)
4214887Schin 		pathcd(home, NiL);
4224887Schin 	return cdrv;
4234887Schin }
4244887Schin 
4254887Schin /*
4264887Schin  * set to parent dir
4274887Schin  */
4284887Schin 
4294887Schin static int
4304887Schin setpdir(register char* home, register char* path, register char* base)
4314887Schin {
4324887Schin 	register int	c;
4334887Schin 	register int	cdrv;
4344887Schin 
4354887Schin 	if (base > path)
4364887Schin 	{
4374887Schin 		c = base[0];
4384887Schin 		base[0] = 0;
4394887Schin 		cdrv = setdir(home, path);
4404887Schin 		base[0] = c;
4414887Schin 	}
4424887Schin 	else
4434887Schin 		cdrv = pathcd(home, NiL);
4444887Schin 	return cdrv;
4454887Schin }
4464887Schin 
4474887Schin /*
4484887Schin  * pop a set of directories
4494887Schin  */
4504887Schin static int
4514887Schin popdirs(FTS* fts)
4524887Schin {
4534887Schin 	register FTSENT*f;
4544887Schin 	register char*	s;
4554887Schin 	register char*	e;
4564887Schin #ifndef verify
4574887Schin 	register int	verify;
4584887Schin #endif
4594887Schin 	struct stat	sb;
4604887Schin 	char		buf[PATH_MAX];
4614887Schin 
4624887Schin 	if (!(f = fts->curdir) || f->fts_level < 0)
4634887Schin 		return -1;
4644887Schin 	e = buf + sizeof(buf) - 4;
4654887Schin #ifndef verify
4664887Schin 	verify = 0;
4674887Schin #endif
4684887Schin 	while (fts->nd > 0)
4694887Schin 	{
4704887Schin 		for (s = buf; s < e && fts->nd > 0; fts->nd--)
4714887Schin 		{
4724887Schin 			if (fts->pwd)
4734887Schin 			{
4744887Schin #ifndef verify
4754887Schin 				verify |= fts->pwd->symlink;
4764887Schin #endif
4774887Schin 				fts->pwd = fts->pwd->pwd;
4784887Schin 			}
4794887Schin 			*s++ = '.';
4804887Schin 			*s++ = '.';
4814887Schin 			*s++ = '/';
4824887Schin 		}
4834887Schin 		*s = 0;
4844887Schin 		if (chdir(buf))
4854887Schin 			return -1;
4864887Schin 	}
4874887Schin 	return (verify && (stat(".", &sb) < 0 || !SAME(&sb, f->fts_statp))) ? -1 : 0;
4884887Schin }
4894887Schin 
4904887Schin /*
4914887Schin  * initialize st from path and fts_info from st
4924887Schin  */
4934887Schin 
4944887Schin static int
4954887Schin info(FTS* fts, register FTSENT* f, const char* path, struct stat* sp, int flags)
4964887Schin {
4974887Schin 	if (path)
4984887Schin 	{
4994887Schin #ifdef S_ISLNK
5004887Schin 		if (!f->symlink && (ISTYPE(f, DT_UNKNOWN) || ISTYPE(f, DT_LNK)))
5014887Schin 		{
5024887Schin 			if (lstat(path, sp) < 0)
5034887Schin 				goto bad;
5044887Schin 		}
5054887Schin 		else
5064887Schin #endif
5074887Schin 			if (stat(path, sp) < 0)
5084887Schin 				goto bad;
5094887Schin 	}
5104887Schin #ifdef S_ISLNK
5114887Schin  again:
5124887Schin #endif
5134887Schin 	if (S_ISDIR(sp->st_mode))
5144887Schin 	{
5154887Schin 		if ((flags & FTS_NOSTAT) && !fts->fs3d)
5164887Schin 		{
5174887Schin 			f->fts_parent->nlink--;
5184887Schin #ifdef D_TYPE
5194887Schin 			if ((f->nlink = sp->st_nlink) < 2)
520*8462SApril.Chin@Sun.COM 			{
521*8462SApril.Chin@Sun.COM 				f->must = 2;
5224887Schin 				f->nlink = 2;
523*8462SApril.Chin@Sun.COM 			}
524*8462SApril.Chin@Sun.COM 			else
525*8462SApril.Chin@Sun.COM 				f->must = 0;
5264887Schin #else
5274887Schin 			if ((f->nlink = sp->st_nlink) >= 2)
5284887Schin 				f->must = 1;
5294887Schin 			else
5304887Schin 				f->must = 2;
5314887Schin #endif
5324887Schin 		}
5334887Schin 		else
5344887Schin 			f->must = 2;
5354887Schin 		TYPE(f, DT_DIR);
5364887Schin 		f->fts_info = FTS_D;
5374887Schin 	}
5384887Schin #ifdef S_ISLNK
5394887Schin 	else if (S_ISLNK((sp)->st_mode))
5404887Schin 	{
5414887Schin 		struct stat	sb;
5424887Schin 
5434887Schin 		f->symlink = 1;
5444887Schin 		if (!(flags & FTS_PHYSICAL) && stat(path, &sb) >= 0)
5454887Schin 		{
5464887Schin 			*sp = sb;
5474887Schin 			flags = FTS_PHYSICAL;
5484887Schin 			goto again;
5494887Schin 		}
5504887Schin 		TYPE(f, DT_LNK);
5514887Schin 		f->fts_info = FTS_SL;
5524887Schin 	}
5534887Schin #endif
5544887Schin 	else
5554887Schin 	{
5564887Schin 		TYPE(f, DT_REG);
5574887Schin 		f->fts_info = FTS_F;
5584887Schin 	}
5594887Schin 	return 0;
5604887Schin  bad:
5614887Schin 	TYPE(f, DT_UNKNOWN);
5624887Schin 	f->fts_info = FTS_NS;
5634887Schin 	return -1;
5644887Schin }
5654887Schin 
5664887Schin /*
5674887Schin  * get top list of elements to process
5684887Schin  */
5694887Schin 
5704887Schin static FTSENT*
5714887Schin toplist(FTS* fts, register char* const* pathnames)
5724887Schin {
5734887Schin 	register char*		path;
5744887Schin 	register struct stat*	sb;
5754887Schin 	register FTSENT*	f;
5764887Schin 	register FTSENT*	root;
5774887Schin 	int			physical;
5784887Schin 	int			metaphysical;
5794887Schin 	char*			s;
5804887Schin 	FTSENT*			top;
5814887Schin 	FTSENT*			bot;
5824887Schin 	struct stat		st;
5834887Schin 
5844887Schin 	if (fts->flags & FTS_NOSEEDOTDIR)
5854887Schin 		fts->flags &= ~FTS_SEEDOTDIR;
5864887Schin 	physical = (fts->flags & FTS_PHYSICAL);
5874887Schin 	metaphysical = (fts->flags & (FTS_META|FTS_PHYSICAL)) == (FTS_META|FTS_PHYSICAL);
5884887Schin 	top = bot = root = 0;
5894887Schin 	while (path = *pathnames++)
5904887Schin 	{
5914887Schin 		/*
5924887Schin 		 * make elements
5934887Schin 		 */
5944887Schin 
5954887Schin 		if (!(f = node(fts, fts->parent, path, strlen(path))))
5964887Schin 			break;
5974887Schin 		path = f->fts_name;
5984887Schin 		if (!physical)
5994887Schin 			f->fts_namelen = (fts->flags & FTS_SEEDOTDIR) ? strlen(path) : (pathcanon(path, 0) - path);
6004887Schin 		else if (*path != '.')
6014887Schin 		{
6024887Schin 			f->fts_namelen = strlen(path);
6034887Schin 			fts->flags |= FTS_SEEDOTDIR;
6044887Schin 		}
6054887Schin 		else
6064887Schin 		{
6074887Schin 			if (fts->flags & FTS_NOSEEDOTDIR)
6084887Schin 			{
6094887Schin 				fts->flags &= ~FTS_SEEDOTDIR;
6104887Schin 				s = path;
6114887Schin 				while (*s++ == '.' && *s++ == '/')
6124887Schin 				{
6134887Schin 					while (*s == '/')
6144887Schin 						s++;
6154887Schin 					if (!*s)
6164887Schin 						break;
6174887Schin 					path = f->fts_name;
6184887Schin 					while (*path++ = *s++);
6194887Schin 					path = f->fts_name;
6204887Schin 				}
6214887Schin 			}
6224887Schin 			else
6234887Schin 				fts->flags |= FTS_SEEDOTDIR;
6244887Schin 			for (s = path + strlen(path); s > path && *(s - 1) == '/'; s--);
6254887Schin 			*s = 0;
6264887Schin 			f->fts_namelen = s - path;
6274887Schin 		}
6284887Schin 		sb = f->fts_statp;
6294887Schin 		if (!*path)
6304887Schin 		{
6314887Schin 			errno = ENOENT;
6324887Schin 			f->fts_info = FTS_NS;
6334887Schin 		}
6344887Schin 		else
6354887Schin 			info(fts, f, path, sb, fts->flags);
6364887Schin #ifdef S_ISLNK
6374887Schin 
6384887Schin 		/*
6394887Schin 		 * don't let any standards committee get
6404887Schin 		 * away with calling your idea a hack
6414887Schin 		 */
6424887Schin 
6434887Schin 		if (metaphysical && f->fts_info == FTS_SL && stat(path, &st) >= 0)
6444887Schin 		{
6454887Schin 			*sb = st;
6464887Schin 			info(fts, f, NiL, sb, 0);
6474887Schin 		}
6484887Schin #endif
6494887Schin 		if (fts->comparf)
6504887Schin 			root = search(f, root, fts->comparf, 1);
6514887Schin 		else if (bot)
6524887Schin 		{
6534887Schin 			bot->fts_link = f;
6544887Schin 			bot = f;
6554887Schin 		}
6564887Schin 		else
6574887Schin 			top = bot = f;
6584887Schin 	}
6594887Schin 	if (fts->comparf)
6604887Schin 		getlist(&top, &bot, root);
6614887Schin 	return top;
6624887Schin }
6634887Schin 
6644887Schin /*
6654887Schin  * resize the path buffer
6664887Schin  * note that free() is not used because we may need to chdir(fts->home)
6674887Schin  * if there isn't enough space to continue
6684887Schin  */
6694887Schin 
6704887Schin static int
6714887Schin resize(register FTS* fts, int inc)
6724887Schin {
6734887Schin 	register char*	old;
6744887Schin 	register char*	newp;
6754887Schin 	register int	n_old;
6764887Schin 
6774887Schin 	/*
6784887Schin 	 * add space for "/." used in testing FTS_DNX
6794887Schin 	 */
6804887Schin 
6814887Schin 	n_old = fts->homesize;
6824887Schin 	fts->homesize = ((fts->homesize + inc + 4) / PATH_MAX + 1) * PATH_MAX;
6834887Schin 	if (!(newp = newof(0, char, fts->homesize, 0)))
6844887Schin 	{
6854887Schin 		fts->fts_errno = errno;
6864887Schin 		fts->state = FTS_error;
6874887Schin 		return -1;
6884887Schin 	}
6894887Schin 	old = fts->home;
6904887Schin 	fts->home = newp;
6914887Schin 	memcpy(newp, old, n_old);
6924887Schin 	if (fts->endbuf)
6934887Schin 		fts->endbuf = newp + fts->homesize - 4;
6944887Schin 	if (fts->path)
6954887Schin 		fts->path = newp + (fts->path - old);
6964887Schin 	if (fts->base)
6974887Schin 		fts->base = newp + (fts->base - old);
6984887Schin 	free(old);
6994887Schin 	return 0;
7004887Schin }
7014887Schin 
7024887Schin /*
7034887Schin  * open a new fts stream on pathnames
7044887Schin  */
7054887Schin 
7064887Schin FTS*
7074887Schin fts_open(char* const* pathnames, int flags, int (*comparf)(FTSENT* const*, FTSENT* const*))
7084887Schin {
7094887Schin 	register FTS*	fts;
7104887Schin 
7114887Schin 	if (!(fts = newof(0, FTS, 1, sizeof(FTSENT))))
7124887Schin 		return 0;
7134887Schin 	fts->flags = flags;
7144887Schin 	fts->cd = (flags & FTS_NOCHDIR) ? 1 : -1;
7154887Schin 	fts->comparf = comparf;
7164887Schin 	fts->fs3d = fs3d(FS3D_TEST);
7174887Schin 
7184887Schin 	/*
7194887Schin 	 * set up the path work buffer
7204887Schin 	 */
7214887Schin 
7224887Schin 	fts->homesize = 2 * PATH_MAX;
7234887Schin 	for (;;)
7244887Schin 	{
7254887Schin 		if (!(fts->home = newof(fts->home, char, fts->homesize, 0)))
7264887Schin 		{
7274887Schin 			free(fts);
7284887Schin 			return 0;
7294887Schin 		}
7304887Schin 		if (fts->cd > 0 || getcwd(fts->home, fts->homesize))
7314887Schin 			break;
7324887Schin 		if (errno == ERANGE)
7334887Schin 			fts->homesize += PATH_MAX;
7344887Schin 		else
7354887Schin 			fts->cd = 1;
7364887Schin 	}
7374887Schin 	fts->endbuf = fts->home + fts->homesize - 4;
7384887Schin 
7394887Schin 	/*
7404887Schin 	 * initialize the tippity-top
7414887Schin 	 */
7424887Schin 
7434887Schin 	fts->parent = (FTSENT*)(fts + 1);
7444887Schin 	fts->parent->fts_info = FTS_D;
7454887Schin 	memcpy(fts->parent->fts_accpath = fts->parent->fts_path = fts->parent->fts_name = fts->parent->name, ".", 2);
7464887Schin 	fts->parent->fts_level = -1;
7474887Schin 	fts->parent->fts_statp = &fts->parent->statb;
7484887Schin 	fts->parent->must = 2;
7494887Schin 	fts->parent->type = DT_UNKNOWN;
7504887Schin 	fts->path = fts->home + strlen(fts->home) + 1;
7514887Schin 
7524887Schin 	/*
7534887Schin 	 * make the list of top elements
7544887Schin 	 */
7554887Schin 
7564887Schin 	if (!pathnames || (flags & FTS_ONEPATH) || !*pathnames)
7574887Schin 	{
7584887Schin 		char*	v[2];
7594887Schin 
7604887Schin 		v[0] = pathnames && (flags & FTS_ONEPATH) ? (char*)pathnames : ".";
7614887Schin 		v[1] = 0;
7624887Schin 		fts->todo = toplist(fts, v);
7634887Schin 	}
7644887Schin 	else
7654887Schin 		fts->todo = toplist(fts, pathnames);
7664887Schin 	if (!fts->todo || fts->todo->fts_info == FTS_NS && !fts->todo->fts_link)
7674887Schin 	{
7684887Schin 		fts_close(fts);
7694887Schin 		return 0;
7704887Schin 	}
7714887Schin 	return fts;
7724887Schin }
7734887Schin 
7744887Schin /*
7754887Schin  * return the next FTS entry
7764887Schin  */
7774887Schin 
7784887Schin FTSENT*
7794887Schin fts_read(register FTS* fts)
7804887Schin {
7814887Schin 	register char*		s;
7824887Schin 	register int		n;
7834887Schin 	register FTSENT*	f;
7844887Schin 	struct dirent*		d;
7854887Schin 	int			i;
7864887Schin 	FTSENT*			t;
7874887Schin 	Notify_t*		p;
7884887Schin #ifdef verify
7894887Schin 	struct stat		sb;
7904887Schin #endif
7914887Schin 
7924887Schin 	for (;;) switch (fts->state)
7934887Schin 	{
7944887Schin 
7954887Schin 	case FTS_top_return:
7964887Schin 
7974887Schin 		f = fts->todo;
7984887Schin 		t = 0;
7994887Schin 		while (f)
8004887Schin 			if (f->status == FTS_SKIP)
8014887Schin 			{
8024887Schin 				if (t)
8034887Schin 				{
8044887Schin 					t->fts_link = f->fts_link;
8054887Schin 					drop(fts, f);
8064887Schin 					f = t->fts_link;
8074887Schin 				}
8084887Schin 				else
8094887Schin 				{
8104887Schin 					fts->todo = f->fts_link;
8114887Schin 					drop(fts, f);
8124887Schin 					f = fts->todo;
8134887Schin 				}
8144887Schin 			}
8154887Schin 			else
8164887Schin 			{
8174887Schin 				t = f;
8184887Schin 				f = f->fts_link;
8194887Schin 			}
8204887Schin 		/*FALLTHROUGH*/
8214887Schin 
8224887Schin 	case 0:
8234887Schin 
8244887Schin 		if (!(f = fts->todo))
8254887Schin 			return 0;
8264887Schin 		/*FALLTHROUGH*/
8274887Schin 
8284887Schin 	case FTS_todo:
8294887Schin 
8304887Schin 		/*
8314887Schin 		 * process the top object on the stack
8324887Schin 		 */
8334887Schin 
8344887Schin 		fts->root = fts->top = fts->bot = 0;
8354887Schin 
8364887Schin 		/*
8374887Schin 		 * initialize the top level
8384887Schin 		 */
8394887Schin 
8404887Schin 		if (f->fts_level == 0)
8414887Schin 		{
8424887Schin 			fts->parent->fts_number = f->fts_number;
8434887Schin 			fts->parent->fts_pointer = f->fts_pointer;
8444887Schin 			fts->parent->fts_statp = f->fts_statp;
8454887Schin 			fts->parent->statb = *f->fts_statp;
8464887Schin 			f->fts_parent = fts->parent;
8474887Schin 			fts->diroot = 0;
8484887Schin 			if (fts->cd == 0)
8494887Schin 				pathcd(fts->home, NiL);
8504887Schin 			else if (fts->cd < 0)
8514887Schin 				fts->cd = 0;
8524887Schin 			fts->pwd = f->fts_parent;
8534887Schin 			fts->curdir = fts->cd ? 0 : f->fts_parent;
8544887Schin 			*(fts->base = fts->path) = 0;
8554887Schin 		}
8564887Schin 
8574887Schin 		/*
8584887Schin 		 * chdir to parent if asked for
8594887Schin 		 */
8604887Schin 
8614887Schin 		if (fts->cd < 0)
8624887Schin 		{
8634887Schin 			fts->cd = setdir(fts->home, fts->path);
8644887Schin 			fts->pwd = f->fts_parent;
8654887Schin 			fts->curdir = fts->cd ? 0 : f->fts_parent;
8664887Schin 		}
8674887Schin 
8684887Schin 		/*
8694887Schin 		 * add object's name to the path
8704887Schin 		 */
8714887Schin 
8724887Schin 		if ((fts->baselen = f->fts_namelen) >= (fts->endbuf - fts->base) && resize(fts, fts->baselen))
8734887Schin 			return 0;
8744887Schin 		memcpy(fts->base, f->name, fts->baselen + 1);
8754887Schin 		fts->name = fts->cd ? fts->path : fts->base;
8764887Schin 		/*FALLTHROUGH*/
8774887Schin 
8784887Schin 	case FTS_preorder:
8794887Schin 
8804887Schin 		/*
8814887Schin 		 * check for cycle and open dir
8824887Schin 		 */
8834887Schin 
8844887Schin 		if (f->fts_info == FTS_D)
8854887Schin 		{
8864887Schin 			if ((fts->diroot = search(f, fts->diroot, statcmp, 0)) != f || f->fts_level > 0 && (t = f) && statcmp(&t, &f->fts_parent) == 0)
8874887Schin 			{
8884887Schin 				f->fts_info = FTS_DC;
8894887Schin 				f->fts_cycle = fts->diroot;
8904887Schin 			}
8914887Schin 			else if (!(fts->flags & FTS_TOP) && (!(fts->flags & FTS_XDEV) || f->statb.st_dev == f->fts_parent->statb.st_dev))
8924887Schin 			{
8934887Schin 				/*
8944887Schin 				 * buffer is known to be large enough here!
8954887Schin 				 */
8964887Schin 
8974887Schin 				if (fts->base[fts->baselen - 1] != '/')
8984887Schin 					memcpy(fts->base + fts->baselen, "/.", 3);
8994887Schin 				if (!(fts->dir = opendir(fts->name)))
9004887Schin 					f->fts_info = FTS_DNX;
9014887Schin 				fts->base[fts->baselen] = 0;
9024887Schin 				if (!fts->dir && !(fts->dir = opendir(fts->name)))
9034887Schin 					f->fts_info = FTS_DNR;
9044887Schin 			}
9054887Schin 		}
9064887Schin 		f->nd = f->fts_info & ~FTS_DNX;
9074887Schin 		if (f->nd || !(fts->flags & FTS_NOPREORDER))
9084887Schin 		{
9094887Schin 			fts->current = f;
9104887Schin 			fts->link = f->fts_link;
9114887Schin 			f->fts_link = 0;
9124887Schin 			f->fts_path = PATH(fts, fts->path, f->fts_level);
9134887Schin 			f->fts_pathlen = (fts->base - f->fts_path) + fts->baselen;
9144887Schin 			f->fts_accpath = ACCESS(fts, f);
9154887Schin 			fts->state = FTS_preorder_return;
9164887Schin 			goto note;
9174887Schin 		}
9184887Schin 		/*FALLTHROUGH*/
9194887Schin 
9204887Schin 	case FTS_preorder_resume:
9214887Schin 
9224887Schin 		/*
9234887Schin 		 * prune
9244887Schin 		 */
9254887Schin 
9264887Schin 		if (!fts->dir || f->nd || f->status == FTS_SKIP)
9274887Schin 		{
9284887Schin 			if (fts->dir)
9294887Schin 			{
9304887Schin 				closedir(fts->dir);
9314887Schin 				fts->dir = 0;
9324887Schin 			}
9334887Schin 			fts->state = FTS_popstack;
9344887Schin 			continue;
9354887Schin 		}
9364887Schin 
9374887Schin 		/*
9384887Schin 		 * FTS_D or FTS_DNX, about to read children
9394887Schin 		 */
9404887Schin 
9414887Schin 		if (fts->cd == 0)
9424887Schin 		{
9434887Schin 			if ((fts->cd = chdir(fts->name)) < 0)
9444887Schin 				pathcd(fts->home, NiL);
9454887Schin 			else if (fts->pwd != f)
9464887Schin 			{
9474887Schin 				f->pwd = fts->pwd;
9484887Schin 				fts->pwd = f;
9494887Schin 			}
9504887Schin 			fts->curdir = fts->cd < 0 ? 0 : f;
9514887Schin 		}
9524887Schin 		fts->nostat = fts->children > 1 || f->fts_info == FTS_DNX;
9534887Schin 		fts->cpname = fts->cd && !fts->nostat || !fts->children && !fts->comparf;
9544887Schin 		fts->dotdot = 0;
9554887Schin 		fts->endbase = fts->base + fts->baselen;
9564887Schin 		if (fts->endbase[-1] != '/')
9574887Schin 			*fts->endbase++ = '/';
9584887Schin 		fts->current = f;
9594887Schin 		/*FALLTHROUGH*/
9604887Schin 
9614887Schin 	case FTS_readdir:
9624887Schin 
9634887Schin 		while (d = readdir(fts->dir))
9644887Schin 		{
9654887Schin 			s = d->d_name;
9664887Schin 			if (s[0] == '.')
9674887Schin 			{
9684887Schin 				if (s[1] == 0)
9694887Schin 				{
9704887Schin 					fts->current->nlink--;
9714887Schin 					if (!(fts->flags & FTS_SEEDOT))
9724887Schin 						continue;
9734887Schin 					n = 1;
9744887Schin 				}
9754887Schin 				else if (s[1] == '.' && s[2] == 0)
9764887Schin 				{
9774887Schin 					fts->current->nlink--;
9784887Schin 					if (fts->current->must == 1)
9794887Schin 						fts->current->must = 0;
9804887Schin 					if (!(fts->flags & FTS_SEEDOT))
9814887Schin 						continue;
9824887Schin 					n = 2;
9834887Schin 				}
9844887Schin 				else
9854887Schin 					n = 0;
9864887Schin 			}
9874887Schin 			else
9884887Schin 				n = 0;
9894887Schin 
9904887Schin 			/*
9914887Schin 			 * make a new entry
9924887Schin 			 */
9934887Schin 
9944887Schin 			i = D_NAMLEN(d);
9954887Schin 			if (!(f = node(fts, fts->current, s, i)))
9964887Schin 				return 0;
9974887Schin 			TYPE(f, D_TYPE(d));
9984887Schin 
9994887Schin 			/*
10004887Schin 			 * check for space
10014887Schin 			 */
10024887Schin 
10034887Schin 			if (i >= fts->endbuf - fts->endbase)
10044887Schin 			{
10054887Schin 	   	   		if (resize(fts, i))
10064887Schin 					return 0;
10074887Schin 				fts->endbase = fts->base + fts->baselen;
10084887Schin 				if (fts->endbase[-1] != '/')
10094887Schin 					fts->endbase++;
10104887Schin 			}
10114887Schin 			if (fts->cpname)
10124887Schin 			{
10134887Schin 				memcpy(fts->endbase, s, i + 1);
10144887Schin 				if (fts->cd)
10154887Schin 					s = fts->path;
10164887Schin 			}
10174887Schin 			if (n)
10184887Schin 			{
10194887Schin 				/*
10204887Schin 				 * don't recurse on . and ..
10214887Schin 				 */
10224887Schin 
10234887Schin 				if (n == 1)
10244887Schin 					f->fts_statp = fts->current->fts_statp;
10254887Schin 				else
10264887Schin 				{
10274887Schin 					if (f->fts_info != FTS_NS)
10284887Schin 						fts->dotdot = f;
10294887Schin 					if (fts->current->fts_parent->fts_level < 0)
10304887Schin 					{
10314887Schin 						f->fts_statp = &fts->current->fts_parent->statb;
10324887Schin 						info(fts, f, s, f->fts_statp, 0);
10334887Schin 					}
10344887Schin 					else
10354887Schin 						f->fts_statp = fts->current->fts_parent->fts_statp;
10364887Schin 				}
10374887Schin 				f->fts_info = FTS_DOT;
10384887Schin 			}
10394887Schin 			else if ((fts->nostat || SKIP(fts, f)) && (f->fts_info = FTS_NSOK) || info(fts, f, s, &f->statb, fts->flags))
10404887Schin 				f->statb.st_ino = D_FILENO(d);
10414887Schin 			if (fts->comparf)
10424887Schin 				fts->root = search(f, fts->root, fts->comparf, 1);
10434887Schin 			else if (fts->children || f->fts_info == FTS_D || f->fts_info == FTS_SL)
10444887Schin 			{
10454887Schin 				if (fts->top)
10464887Schin 					fts->bot = fts->bot->fts_link = f;
10474887Schin 				else
10484887Schin 					fts->top = fts->bot = f;
10494887Schin 			}
10504887Schin 			else
10514887Schin 			{
10524887Schin 				/*
10534887Schin 				 * terminal node
10544887Schin 				 */
10554887Schin 
10564887Schin 				f->fts_path = PATH(fts, fts->path, 1);
10574887Schin 				f->fts_pathlen = fts->endbase - f->fts_path + f->fts_namelen;
10584887Schin 				f->fts_accpath = ACCESS(fts, f);
10594887Schin 				fts->previous = fts->current;
10604887Schin 				fts->current = f;
10614887Schin 				fts->state = FTS_terminal;
10624887Schin 				goto note;
10634887Schin 			}
10644887Schin 		}
10654887Schin 
10664887Schin 		/*
10674887Schin 		 * done with the directory
10684887Schin 		 */
10694887Schin 
10704887Schin 		closedir(fts->dir);
10714887Schin 		fts->dir = 0;
10724887Schin 		if (fts->root)
10734887Schin 			getlist(&fts->top, &fts->bot, fts->root);
10744887Schin 		if (fts->children)
10754887Schin 		{
10764887Schin 			/*
10774887Schin 			 * try moving back to parent dir
10784887Schin 			 */
10794887Schin 
10804887Schin 			fts->base[fts->baselen] = 0;
10814887Schin 			if (fts->cd <= 0)
10824887Schin 			{
10834887Schin 				f = fts->current->fts_parent;
10844887Schin 				if (fts->cd < 0
10854887Schin 				    || f != fts->curdir
10864887Schin 				    || !fts->dotdot
10874887Schin 				    || !SAME(f->fts_statp, fts->dotdot->fts_statp)
10884887Schin 				    || fts->pwd && fts->pwd->symlink
10894887Schin 				    || (fts->cd = chdir("..")) < 0
10904887Schin #ifdef verify
10914887Schin 				    || stat(".", &sb) < 0
10924887Schin 				    || !SAME(&sb, fts->dotdot->fts_statp)
10934887Schin #endif
10944887Schin 				    )
10954887Schin 					fts->cd = setpdir(fts->home, fts->path, fts->base);
10964887Schin 				if (fts->pwd)
10974887Schin 					fts->pwd = fts->pwd->pwd;
10984887Schin 				fts->curdir = fts->cd ? 0 : f;
10994887Schin 			}
11004887Schin 			f = fts->current;
11014887Schin 			fts->link = f->fts_link;
11024887Schin 			f->fts_link = fts->top;
11034887Schin 			f->fts_path = PATH(fts, fts->path, f->fts_level);
11044887Schin 			f->fts_pathlen = (fts->base - f->fts_path) + f->fts_namelen;
11054887Schin 			f->fts_accpath = ACCESS(fts, f);
11064887Schin 			fts->state = FTS_children_return;
11074887Schin 			goto note;
11084887Schin 		}
11094887Schin 		/*FALLTHROUGH*/
11104887Schin 
11114887Schin 	case FTS_children_resume:
11124887Schin 
11134887Schin 		fts->base[fts->baselen] = 0;
11144887Schin 		if (fts->top)
11154887Schin 		{
11164887Schin 			fts->bot->fts_link = fts->todo;
11174887Schin 			fts->todo = fts->top;
11184887Schin 			fts->top = 0;
11194887Schin 		}
11204887Schin 		/*FALLTHROUGH*/
11214887Schin 
11224887Schin 	case FTS_popstack:
11234887Schin 
11244887Schin 		/*
11254887Schin 		 * pop objects completely processed
11264887Schin 		 */
11274887Schin 
11284887Schin 		fts->nd = 0;
11294887Schin 		f = fts->current;
11304887Schin 		/*FALLTHROUGH*/
11314887Schin 
11324887Schin 	case FTS_popstack_resume:
11334887Schin 
11344887Schin 		while (fts->todo && f == fts->todo)
11354887Schin 		{
11364887Schin 			t = f->fts_parent;
11374887Schin 			if ((f->fts_info & FTS_DP) == FTS_D)
11384887Schin 			{
11394887Schin 				/*
11404887Schin 				 * delete from <dev,ino> tree
11414887Schin 				 */
11424887Schin 
11434887Schin 				if (f != fts->diroot)
11444887Schin 					fts->diroot = search(f, fts->diroot, statcmp, 0);
11454887Schin 				fts->diroot = deleteroot(fts->diroot);
11464887Schin 				if (f == fts->curdir)
11474887Schin 				{
11484887Schin 					fts->nd++;
11494887Schin 					fts->curdir = t;
11504887Schin 				}
11514887Schin 
11524887Schin 				/*
11534887Schin 				 * perform post-order processing
11544887Schin 				 */
11554887Schin 
11564887Schin 				if (!(fts->flags & FTS_NOPOSTORDER) &&
11574887Schin 				    f->status != FTS_SKIP &&
11584887Schin 				    f->status != FTS_NOPOSTORDER)
11594887Schin 				{
11604887Schin 					/*
11614887Schin 					 * move to parent dir
11624887Schin 					 */
11634887Schin 
11644887Schin 					if (fts->nd > 0)
11654887Schin 						fts->cd = popdirs(fts);
11664887Schin 					if (fts->cd < 0)
11674887Schin 						fts->cd = setpdir(fts->home, fts->path, fts->base);
11684887Schin 					fts->curdir = fts->cd ? 0 : t;
11694887Schin 					f->fts_info = FTS_DP;
11704887Schin 					f->fts_path = PATH(fts, fts->path, f->fts_level);
11714887Schin 					f->fts_pathlen = (fts->base - f->fts_path) + f->fts_namelen;
11724887Schin 					f->fts_accpath = ACCESS(fts, f);
11734887Schin 
11744887Schin 					/*
11754887Schin 					 * re-stat to update nlink/times
11764887Schin 					 */
11774887Schin 
11784887Schin 					stat(f->fts_accpath, f->fts_statp);
11794887Schin 					fts->link = f->fts_link;
11804887Schin 					f->fts_link = 0;
11814887Schin 					fts->state = FTS_popstack_return;
11824887Schin 					goto note;
11834887Schin 				}
11844887Schin 			}
11854887Schin 
11864887Schin 			/*
11874887Schin 			 * reset base
11884887Schin 			 */
11894887Schin 
11904887Schin 			if (fts->base > fts->path + t->fts_namelen)
11914887Schin 				fts->base--;
11924887Schin 			*fts->base = 0;
11934887Schin 			fts->base -= t->fts_namelen;
11944887Schin 
11954887Schin 			/*
11964887Schin 			 * try again or delete from top of stack
11974887Schin 			 */
11984887Schin 
11994887Schin 			if (f->status == FTS_AGAIN)
12004887Schin 			{
12014887Schin 				f->fts_info = FTS_D;
12024887Schin 				f->status = 0;
12034887Schin 			}
12044887Schin 			else
12054887Schin 			{
12064887Schin 				fts->todo = fts->todo->fts_link;
12074887Schin 				drop(fts, f);
12084887Schin 			}
12094887Schin 			f = t;
12104887Schin 		}
12114887Schin 
12124887Schin 		/*
12134887Schin 		 * reset current directory
12144887Schin 		 */
12154887Schin 
12164887Schin 		if (fts->nd > 0 && popdirs(fts) < 0)
12174887Schin 		{
12184887Schin 			pathcd(fts->home, NiL);
12194887Schin 			fts->curdir = 0;
12204887Schin 			fts->cd = -1;
12214887Schin 		}
12224887Schin 		if (fts->todo)
12234887Schin 		{
12244887Schin 			if (*fts->base)
12254887Schin 				fts->base += f->fts_namelen;
12264887Schin 			if (*(fts->base - 1) != '/')
12274887Schin 				*fts->base++ = '/';
12284887Schin 			*fts->base = 0;
12294887Schin 			f = fts->todo;
12304887Schin 			fts->state = FTS_todo;
12314887Schin 			continue;
12324887Schin 		}
12334887Schin 		return 0;
12344887Schin 
12354887Schin 	case FTS_children_return:
12364887Schin 
12374887Schin 		f = fts->current;
12384887Schin 		f->fts_link = fts->link;
12394887Schin 
12404887Schin 		/*
12414887Schin 		 * chdir down again
12424887Schin 		 */
12434887Schin 
12444887Schin 		i = f->fts_info != FTS_DNX;
12454887Schin 		n = f->status == FTS_SKIP;
12464887Schin 		if (!n && fts->cd == 0)
12474887Schin 		{
12484887Schin 			if ((fts->cd = chdir(fts->base)) < 0)
12494887Schin 				pathcd(fts->home, NiL);
12504887Schin 			else if (fts->pwd != f)
12514887Schin 			{
12524887Schin 				f->pwd = fts->pwd;
12534887Schin 				fts->pwd = f;
12544887Schin 			}
12554887Schin 			fts->curdir = fts->cd ? 0 : f;
12564887Schin 		}
12574887Schin 
12584887Schin 		/*
12594887Schin 		 * prune
12604887Schin 		 */
12614887Schin 
12624887Schin 		if (fts->base[fts->baselen - 1] != '/')
12634887Schin 			fts->base[fts->baselen] = '/';
12644887Schin 		for (fts->bot = 0, f = fts->top; f; )
12654887Schin 			if (n || f->status == FTS_SKIP)
12664887Schin 			{
12674887Schin 				if (fts->bot)
12684887Schin 					fts->bot->fts_link = f->fts_link;
12694887Schin 				else
12704887Schin 					fts->top = f->fts_link;
12714887Schin 				drop(fts, f);
12724887Schin 				f = fts->bot ? fts->bot->fts_link : fts->top;
12734887Schin 			}
12744887Schin 			else
12754887Schin 			{
12764887Schin 				if (fts->children > 1 && i)
12774887Schin 				{
12784887Schin 					if (f->status == FTS_STAT)
12794887Schin 						info(fts, f, NiL, f->fts_statp, 0);
12804887Schin 					else if (f->fts_info == FTS_NSOK && !SKIP(fts, f))
12814887Schin 					{
12824887Schin 						s = f->fts_name;
12834887Schin 						if (fts->cd)
12844887Schin 						{
12854887Schin 							memcpy(fts->endbase, s, f->fts_namelen + 1);
12864887Schin 							s = fts->path;
12874887Schin 						}
12884887Schin 						info(fts, f, s, f->fts_statp, fts->flags);
12894887Schin 					}
12904887Schin 				}
12914887Schin 				fts->bot = f;
12924887Schin 				f = f->fts_link;
12934887Schin 			}
12944887Schin 		fts->children = 0;
12954887Schin 		fts->state = FTS_children_resume;
12964887Schin 		continue;
12974887Schin 
12984887Schin 	case FTS_popstack_return:
12994887Schin 
13004887Schin 		f = fts->todo;
13014887Schin 		f->fts_link = fts->link;
13024887Schin 		f->fts_info = f->status == FTS_AGAIN ? FTS_DP : 0;
13034887Schin 		fts->state = FTS_popstack_resume;
13044887Schin 		continue;
13054887Schin 
13064887Schin 	case FTS_preorder_return:
13074887Schin 
13084887Schin 		f = fts->current;
13094887Schin 		f->fts_link = fts->link;
13104887Schin 
13114887Schin 		/*
13124887Schin 		 * follow symlink if asked to
13134887Schin 		 */
13144887Schin 
13154887Schin 		if (f->status == FTS_FOLLOW)
13164887Schin 		{
13174887Schin 			f->status = 0;
13184887Schin 			if (f->fts_info == FTS_SL || ISTYPE(f, DT_LNK) || f->fts_info == FTS_NSOK)
13194887Schin 			{
13204887Schin 				info(fts, f, f->fts_accpath, f->fts_statp, 0);
13214887Schin 				if (f->fts_info != FTS_SL)
13224887Schin 				{
13234887Schin 					fts->state = FTS_preorder;
13244887Schin 					continue;
13254887Schin 				}
13264887Schin 			}
13274887Schin 		}
13284887Schin 
13294887Schin 		/*
13304887Schin 		 * about to prune this f and already at home
13314887Schin 		 */
13324887Schin 
13334887Schin 		if (fts->cd == 0 && f->fts_level == 0 && f->nd)
13344887Schin 			fts->cd = -1;
13354887Schin 		fts->state = FTS_preorder_resume;
13364887Schin 		continue;
13374887Schin 
13384887Schin 	case FTS_terminal:
13394887Schin 
13404887Schin 		f = fts->current;
13414887Schin 		if (f->status == FTS_FOLLOW)
13424887Schin 		{
13434887Schin 			f->status = 0;
13444887Schin 			if (f->fts_info == FTS_SL || ISTYPE(f, DT_LNK) || f->fts_info == FTS_NSOK)
13454887Schin 			{
13464887Schin 				info(fts, f, f->fts_accpath, f->fts_statp, 0);
13474887Schin 				if (f->symlink && f->fts_info != FTS_SL)
13484887Schin 				{
13494887Schin 					if (!(f->fts_link = fts->top))
13504887Schin 						fts->bot = f;
13514887Schin 					fts->top = f;
13524887Schin 					fts->current = fts->previous;
13534887Schin 					fts->state = FTS_readdir;
13544887Schin 					continue;
13554887Schin 				}
13564887Schin 			}
13574887Schin 		}
13584887Schin 		f = f->fts_parent;
13594887Schin 		drop(fts, fts->current);
13604887Schin 		fts->current = f;
13614887Schin 		fts->state = FTS_readdir;
13624887Schin 		continue;
13634887Schin 
13644887Schin 	case FTS_error:
13654887Schin 
13664887Schin 		return 0;
13674887Schin 
13684887Schin 	default:
13694887Schin 
13704887Schin 		fts->fts_errno = EINVAL;
13714887Schin 		fts->state = FTS_error;
13724887Schin 		return 0;
13734887Schin 
13744887Schin 	}
13754887Schin  note:
13764887Schin 	for (p = notify; p; p = p->next)
13774887Schin 		if ((n = (*p->notifyf)(fts, f, p->context)) > 0)
13784887Schin 			break;
13794887Schin 		else if (n < 0)
13804887Schin 		{
13814887Schin 			fts->fts_errno = EINVAL;
13824887Schin 			fts->state = FTS_error;
13834887Schin 			return 0;
13844887Schin 		}
13854887Schin 	return f;
13864887Schin }
13874887Schin 
13884887Schin /*
13894887Schin  * set stream or entry flags
13904887Schin  */
13914887Schin 
13924887Schin int
13934887Schin fts_set(register FTS* fts, register FTSENT* f, int status)
13944887Schin {
13954887Schin 	if (fts || !f || f->fts->current != f)
13964887Schin 		return -1;
13974887Schin 	switch (status)
13984887Schin 	{
13994887Schin 	case FTS_AGAIN:
14004887Schin 		break;
14014887Schin 	case FTS_FOLLOW:
14024887Schin 		if (!(f->fts_info & FTS_SL))
14034887Schin 			return -1;
14044887Schin 		break;
14054887Schin 	case FTS_NOPOSTORDER:
14064887Schin 		break;
14074887Schin 	case FTS_SKIP:
14084887Schin 		if ((f->fts_info & (FTS_D|FTS_P)) != FTS_D)
14094887Schin 			return -1;
14104887Schin 		break;
14114887Schin 	default:
14124887Schin 		return -1;
14134887Schin 	}
14144887Schin 	f->status = status;
14154887Schin 	return 0;
14164887Schin }
14174887Schin 
14184887Schin /*
14194887Schin  * return the list of child entries
14204887Schin  */
14214887Schin 
14224887Schin FTSENT*
14234887Schin fts_children(register FTS* fts, int flags)
14244887Schin {
14254887Schin 	register FTSENT*	f;
14264887Schin 
14274887Schin 	switch (fts->state)
14284887Schin 	{
14294887Schin 
14304887Schin 	case 0:
14314887Schin 
14324887Schin 		fts->state = FTS_top_return;
14334887Schin 		return fts->todo;
14344887Schin 
14354887Schin 	case FTS_preorder_return:
14364887Schin 
14374887Schin 		fts->children = ((flags | fts->flags) & FTS_NOSTAT) ? 2 : 1;
14384887Schin 		if (f = fts_read(fts))
14394887Schin 			f = f->fts_link;
14404887Schin 		return f;
14414887Schin 
14424887Schin 	}
14434887Schin 	return 0;
14444887Schin }
14454887Schin 
14464887Schin /*
14474887Schin  * return default (FTS_LOGICAL|FTS_META|FTS_PHYSICAL|FTS_SEEDOTDIR) flags
14484887Schin  * conditioned by astconf()
14494887Schin  */
14504887Schin 
14514887Schin int
14524887Schin fts_flags(void)
14534887Schin {
14544887Schin 	register char*	s;
14554887Schin 
14564887Schin 	s = astconf("PATH_RESOLVE", NiL, NiL);
14574887Schin 	if (streq(s, "logical"))
14584887Schin 		return FTS_LOGICAL;
14594887Schin 	if (streq(s, "physical"))
14604887Schin 		return FTS_PHYSICAL|FTS_SEEDOTDIR;
14614887Schin 	return FTS_META|FTS_PHYSICAL|FTS_SEEDOTDIR;
14624887Schin }
14634887Schin 
14644887Schin /*
1465*8462SApril.Chin@Sun.COM  * return 1 if ent is mounted on a local filesystem
1466*8462SApril.Chin@Sun.COM  */
1467*8462SApril.Chin@Sun.COM 
1468*8462SApril.Chin@Sun.COM int
1469*8462SApril.Chin@Sun.COM fts_local(FTSENT* ent)
1470*8462SApril.Chin@Sun.COM {
1471*8462SApril.Chin@Sun.COM #ifdef ST_LOCAL
1472*8462SApril.Chin@Sun.COM 	struct statvfs	fs;
1473*8462SApril.Chin@Sun.COM 
1474*8462SApril.Chin@Sun.COM 	return statvfs(ent->fts_path, &fs) || (fs.f_flag & ST_LOCAL);
1475*8462SApril.Chin@Sun.COM #else
1476*8462SApril.Chin@Sun.COM 	return !strgrpmatch(fmtfs(ent->fts_statp), "([an]fs|samb)", NiL, 0, STR_LEFT|STR_ICASE);
1477*8462SApril.Chin@Sun.COM #endif
1478*8462SApril.Chin@Sun.COM }
1479*8462SApril.Chin@Sun.COM 
1480*8462SApril.Chin@Sun.COM /*
14814887Schin  * close an open fts stream
14824887Schin  */
14834887Schin 
14844887Schin int
14854887Schin fts_close(register FTS* fts)
14864887Schin {
14874887Schin 	register FTSENT*	f;
14884887Schin 	register FTSENT*	x;
14894887Schin 
14904887Schin 	if (fts->dir)
14914887Schin 		closedir(fts->dir);
14924887Schin 	if (fts->cd == 0)
14934887Schin 		pathcd(fts->home, NiL);
14944887Schin 	free(fts->home);
14954887Schin 	if (fts->state == FTS_children_return)
14964887Schin 		fts->current->fts_link = fts->link;
14974887Schin 	if (fts->top)
14984887Schin 	{
14994887Schin 		fts->bot->fts_link = fts->todo;
15004887Schin 		fts->todo = fts->top;
15014887Schin 	}
15024887Schin 	for (f = fts->todo; f; f = x)
15034887Schin 	{
15044887Schin 		x = f->fts_link;
15054887Schin 		free(f);
15064887Schin 	}
15074887Schin 	for (f = fts->free; f; f = x)
15084887Schin 	{
15094887Schin 		x = f->fts_link;
15104887Schin 		free(f);
15114887Schin 	}
1512*8462SApril.Chin@Sun.COM 	free(fts);
15134887Schin 	return 0;
15144887Schin }
15154887Schin 
15164887Schin /*
15174887Schin  * register function to be called for each fts_read() entry
1518*8462SApril.Chin@Sun.COM  * context==0 => unregister notifyf
15194887Schin  */
15204887Schin 
15214887Schin int
15224887Schin fts_notify(Notify_f notifyf, void* context)
15234887Schin {
15244887Schin 	register Notify_t*	np;
1525*8462SApril.Chin@Sun.COM 	register Notify_t*	pp;
15264887Schin 
1527*8462SApril.Chin@Sun.COM 	if (context)
1528*8462SApril.Chin@Sun.COM 	{
1529*8462SApril.Chin@Sun.COM 		if (!(np = newof(0, Notify_t, 1, 0)))
1530*8462SApril.Chin@Sun.COM 			return -1;
1531*8462SApril.Chin@Sun.COM 		np->notifyf = notifyf;
1532*8462SApril.Chin@Sun.COM 		np->context = context;
1533*8462SApril.Chin@Sun.COM 		np->next = notify;
1534*8462SApril.Chin@Sun.COM 		notify = np;
1535*8462SApril.Chin@Sun.COM 	}
1536*8462SApril.Chin@Sun.COM 	else
1537*8462SApril.Chin@Sun.COM 	{
1538*8462SApril.Chin@Sun.COM 		for (np = notify, pp = 0; np; pp = np, np = np->next)
1539*8462SApril.Chin@Sun.COM 			if (np->notifyf == notifyf)
1540*8462SApril.Chin@Sun.COM 			{
1541*8462SApril.Chin@Sun.COM 				if (pp)
1542*8462SApril.Chin@Sun.COM 					pp->next = np->next;
1543*8462SApril.Chin@Sun.COM 				else
1544*8462SApril.Chin@Sun.COM 					notify = np->next;
1545*8462SApril.Chin@Sun.COM 				free(np);
1546*8462SApril.Chin@Sun.COM 				return 0;
1547*8462SApril.Chin@Sun.COM 			}
15484887Schin 		return -1;
1549*8462SApril.Chin@Sun.COM 	}
15504887Schin 	return 0;
15514887Schin }
1552