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