fts-compat.c revision 1573
1237651Sbschmidt/*-
2237651Sbschmidt * Copyright (c) 1990, 1993, 1994
3237651Sbschmidt *	The Regents of the University of California.  All rights reserved.
4237651Sbschmidt *
5237651Sbschmidt * Redistribution and use in source and binary forms, with or without
6237651Sbschmidt * modification, are permitted provided that the following conditions
7237651Sbschmidt * are met:
8237651Sbschmidt * 1. Redistributions of source code must retain the above copyright
9237651Sbschmidt *    notice, this list of conditions and the following disclaimer.
10237651Sbschmidt * 2. Redistributions in binary form must reproduce the above copyright
11237651Sbschmidt *    notice, this list of conditions and the following disclaimer in the
12237651Sbschmidt *    documentation and/or other materials provided with the distribution.
13237651Sbschmidt * 3. All advertising materials mentioning features or use of this software
14237651Sbschmidt *    must display the following acknowledgement:
15237651Sbschmidt *	This product includes software developed by the University of
16237651Sbschmidt *	California, Berkeley and its contributors.
17237651Sbschmidt * 4. Neither the name of the University nor the names of its contributors
18237651Sbschmidt *    may be used to endorse or promote products derived from this software
19237651Sbschmidt *    without specific prior written permission.
20237651Sbschmidt *
21237651Sbschmidt * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
22237651Sbschmidt * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
23237651Sbschmidt * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
24237651Sbschmidt * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
25237651Sbschmidt * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
26237651Sbschmidt * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
27237651Sbschmidt * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
28237651Sbschmidt * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
29237651Sbschmidt * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
30237651Sbschmidt * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
31237651Sbschmidt * SUCH DAMAGE.
32237651Sbschmidt */
33237651Sbschmidt
34237651Sbschmidt#if defined(LIBC_SCCS) && !defined(lint)
35237651Sbschmidtstatic char sccsid[] = "@(#)fts.c	8.4 (Berkeley) 4/16/94";
36237651Sbschmidt#endif /* LIBC_SCCS and not lint */
37237651Sbschmidt
38237651Sbschmidt#include <sys/param.h>
39237651Sbschmidt#include <sys/stat.h>
40237651Sbschmidt
41237651Sbschmidt#include <dirent.h>
42237651Sbschmidt#include <errno.h>
43237651Sbschmidt#include <fcntl.h>
44237651Sbschmidt#include <fts.h>
45237651Sbschmidt#include <stdlib.h>
46237651Sbschmidt#include <string.h>
47237651Sbschmidt#include <unistd.h>
48237651Sbschmidt
49237651Sbschmidtstatic FTSENT	*fts_alloc __P((FTS *, char *, int));
50237651Sbschmidtstatic FTSENT	*fts_build __P((FTS *, int));
51237651Sbschmidtstatic void	 fts_lfree __P((FTSENT *));
52237651Sbschmidtstatic void	 fts_load __P((FTS *, FTSENT *));
53237651Sbschmidtstatic size_t	 fts_maxarglen __P((char * const *));
54237651Sbschmidtstatic void	 fts_padjust __P((FTS *, void *));
55237651Sbschmidtstatic int	 fts_palloc __P((FTS *, size_t));
56237651Sbschmidtstatic FTSENT	*fts_sort __P((FTS *, FTSENT *, int));
57237651Sbschmidtstatic u_short	 fts_stat __P((FTS *, FTSENT *, int));
58237651Sbschmidt
59237651Sbschmidt#define	ISDOT(a)	(a[0] == '.' && (!a[1] || a[1] == '.' && !a[2]))
60237651Sbschmidt
61237651Sbschmidt#define	ISSET(opt)	(sp->fts_options & opt)
62237651Sbschmidt#define	SET(opt)	(sp->fts_options |= opt)
63237651Sbschmidt
64237651Sbschmidt#define	CHDIR(sp, path)	(!ISSET(FTS_NOCHDIR) && chdir(path))
65237651Sbschmidt#define	FCHDIR(sp, fd)	(!ISSET(FTS_NOCHDIR) && fchdir(fd))
66237651Sbschmidt
67237651Sbschmidt/* fts_build flags */
68237651Sbschmidt#define	BCHILD		1		/* fts_children */
69237651Sbschmidt#define	BNAMES		2		/* fts_children, names only */
70237651Sbschmidt#define	BREAD		3		/* fts_read */
71237651Sbschmidt
72237651SbschmidtFTS *
73237651Sbschmidtfts_open(argv, options, compar)
74237651Sbschmidt	char * const *argv;
75237651Sbschmidt	register int options;
76237651Sbschmidt	int (*compar)();
77237651Sbschmidt{
78237651Sbschmidt	register FTS *sp;
79237651Sbschmidt	register FTSENT *p, *root;
80237651Sbschmidt	register int nitems;
81237651Sbschmidt	FTSENT *parent, *tmp;
82237651Sbschmidt	int len;
83237651Sbschmidt
84237651Sbschmidt	/* Options check. */
85237651Sbschmidt	if (options & ~FTS_OPTIONMASK) {
86237651Sbschmidt		errno = EINVAL;
87237651Sbschmidt		return (NULL);
88237651Sbschmidt	}
89237651Sbschmidt
90237651Sbschmidt	/* Allocate/initialize the stream */
91237651Sbschmidt	if ((sp = malloc((u_int)sizeof(FTS))) == NULL)
92237651Sbschmidt		return (NULL);
93237651Sbschmidt	memset(sp, 0, sizeof(FTS));
94237651Sbschmidt	sp->fts_compar = compar;
95237651Sbschmidt	sp->fts_options = options;
96237651Sbschmidt
97237651Sbschmidt	/* Logical walks turn on NOCHDIR; symbolic links are too hard. */
98237651Sbschmidt	if (ISSET(FTS_LOGICAL))
99237651Sbschmidt		SET(FTS_NOCHDIR);
100237651Sbschmidt
101237651Sbschmidt	/*
102237651Sbschmidt	 * Start out with 1K of path space, and enough, in any case,
103237651Sbschmidt	 * to hold the user's paths.
104237651Sbschmidt	 */
105237651Sbschmidt	if (fts_palloc(sp, MAX(fts_maxarglen(argv), MAXPATHLEN)))
106237651Sbschmidt		goto mem1;
107237651Sbschmidt
108237651Sbschmidt	/* Allocate/initialize root's parent. */
109237651Sbschmidt	if ((parent = fts_alloc(sp, "", 0)) == NULL)
110237651Sbschmidt		goto mem2;
111237651Sbschmidt	parent->fts_level = FTS_ROOTPARENTLEVEL;
112237651Sbschmidt
113237651Sbschmidt	/* Allocate/initialize root(s). */
114237651Sbschmidt	for (root = NULL, nitems = 0; *argv; ++argv, ++nitems) {
115237651Sbschmidt		/* Don't allow zero-length paths. */
116237651Sbschmidt		if ((len = strlen(*argv)) == 0) {
117237651Sbschmidt			errno = ENOENT;
118237651Sbschmidt			goto mem3;
119237651Sbschmidt		}
120237651Sbschmidt
121237651Sbschmidt		p = fts_alloc(sp, *argv, len);
122237651Sbschmidt		p->fts_level = FTS_ROOTLEVEL;
123237651Sbschmidt		p->fts_parent = parent;
124237651Sbschmidt		p->fts_accpath = p->fts_name;
125237651Sbschmidt		p->fts_info = fts_stat(sp, p, ISSET(FTS_COMFOLLOW));
126237651Sbschmidt
127237651Sbschmidt		/* Command-line "." and ".." are real directories. */
128237651Sbschmidt		if (p->fts_info == FTS_DOT)
129237651Sbschmidt			p->fts_info = FTS_D;
130237651Sbschmidt
131237651Sbschmidt		/*
132237651Sbschmidt		 * If comparison routine supplied, traverse in sorted
133237651Sbschmidt		 * order; otherwise traverse in the order specified.
134237651Sbschmidt		 */
135237651Sbschmidt		if (compar) {
136237651Sbschmidt			p->fts_link = root;
137237651Sbschmidt			root = p;
138237651Sbschmidt		} else {
139237651Sbschmidt			p->fts_link = NULL;
140237651Sbschmidt			if (root == NULL)
141237651Sbschmidt				tmp = root = p;
142237651Sbschmidt			else {
143237651Sbschmidt				tmp->fts_link = p;
144237651Sbschmidt				tmp = p;
145237651Sbschmidt			}
146237651Sbschmidt		}
147237651Sbschmidt	}
148237651Sbschmidt	if (compar && nitems > 1)
149237651Sbschmidt		root = fts_sort(sp, root, nitems);
150237651Sbschmidt
151237651Sbschmidt	/*
152237651Sbschmidt	 * Allocate a dummy pointer and make fts_read think that we've just
153237651Sbschmidt	 * finished the node before the root(s); set p->fts_info to FTS_INIT
154237651Sbschmidt	 * so that everything about the "current" node is ignored.
155237651Sbschmidt	 */
156237651Sbschmidt	if ((sp->fts_cur = fts_alloc(sp, "", 0)) == NULL)
157237651Sbschmidt		goto mem3;
158237651Sbschmidt	sp->fts_cur->fts_link = root;
159237651Sbschmidt	sp->fts_cur->fts_info = FTS_INIT;
160237651Sbschmidt
161237651Sbschmidt	/*
162237651Sbschmidt	 * If using chdir(2), grab a file descriptor pointing to dot to insure
163237651Sbschmidt	 * that we can get back here; this could be avoided for some paths,
164237651Sbschmidt	 * but almost certainly not worth the effort.  Slashes, symbolic links,
165237651Sbschmidt	 * and ".." are all fairly nasty problems.  Note, if we can't get the
166237651Sbschmidt	 * descriptor we run anyway, just more slowly.
167237651Sbschmidt	 */
168237651Sbschmidt	if (!ISSET(FTS_NOCHDIR) && (sp->fts_rfd = open(".", O_RDONLY, 0)) < 0)
169237651Sbschmidt		SET(FTS_NOCHDIR);
170237651Sbschmidt
171237651Sbschmidt	return (sp);
172237651Sbschmidt
173237651Sbschmidtmem3:	fts_lfree(root);
174237651Sbschmidt	free(parent);
175237651Sbschmidtmem2:	free(sp->fts_path);
176237651Sbschmidtmem1:	free(sp);
177237651Sbschmidt	return (NULL);
178237651Sbschmidt}
179237651Sbschmidt
180237651Sbschmidtstatic void
181237651Sbschmidtfts_load(sp, p)
182237651Sbschmidt	FTS *sp;
183237651Sbschmidt	register FTSENT *p;
184237651Sbschmidt{
185237651Sbschmidt	register int len;
186237651Sbschmidt	register char *cp;
187237651Sbschmidt
188237651Sbschmidt	/*
189237651Sbschmidt	 * Load the stream structure for the next traversal.  Since we don't
190237651Sbschmidt	 * actually enter the directory until after the preorder visit, set
191237651Sbschmidt	 * the fts_accpath field specially so the chdir gets done to the right
192237651Sbschmidt	 * place and the user can access the first node.  From fts_open it's
193237651Sbschmidt	 * known that the path will fit.
194237651Sbschmidt	 */
195237651Sbschmidt	len = p->fts_pathlen = p->fts_namelen;
196237651Sbschmidt	memmove(sp->fts_path, p->fts_name, len + 1);
197237651Sbschmidt	if ((cp = strrchr(p->fts_name, '/')) && (cp != p->fts_name || cp[1])) {
198237651Sbschmidt		len = strlen(++cp);
199237651Sbschmidt		memmove(p->fts_name, cp, len + 1);
200237651Sbschmidt		p->fts_namelen = len;
201237651Sbschmidt	}
202237651Sbschmidt	p->fts_accpath = p->fts_path = sp->fts_path;
203237651Sbschmidt	sp->fts_dev = p->fts_dev;
204237651Sbschmidt}
205237651Sbschmidt
206237651Sbschmidtint
207237651Sbschmidtfts_close(sp)
208237651Sbschmidt	FTS *sp;
209237651Sbschmidt{
210237651Sbschmidt	register FTSENT *freep, *p;
211237651Sbschmidt	int saved_errno;
212237651Sbschmidt
213237651Sbschmidt	/*
214237651Sbschmidt	 * This still works if we haven't read anything -- the dummy structure
215237651Sbschmidt	 * points to the root list, so we step through to the end of the root
216237651Sbschmidt	 * list which has a valid parent pointer.
217237651Sbschmidt	 */
218237651Sbschmidt	if (sp->fts_cur) {
219237651Sbschmidt		for (p = sp->fts_cur; p->fts_level >= FTS_ROOTLEVEL;) {
220237651Sbschmidt			freep = p;
221237651Sbschmidt			p = p->fts_link ? p->fts_link : p->fts_parent;
222237651Sbschmidt			free(freep);
223237651Sbschmidt		}
224237651Sbschmidt		free(p);
225237651Sbschmidt	}
226237651Sbschmidt
227237651Sbschmidt	/* Free up child linked list, sort array, path buffer. */
228237651Sbschmidt	if (sp->fts_child)
229237651Sbschmidt		fts_lfree(sp->fts_child);
230237651Sbschmidt	if (sp->fts_array)
231237651Sbschmidt		free(sp->fts_array);
232237651Sbschmidt	free(sp->fts_path);
233237651Sbschmidt
234237651Sbschmidt	/* Return to original directory, save errno if necessary. */
235237651Sbschmidt	if (!ISSET(FTS_NOCHDIR)) {
236237651Sbschmidt		saved_errno = fchdir(sp->fts_rfd) ? errno : 0;
237237651Sbschmidt		(void)close(sp->fts_rfd);
238237651Sbschmidt	}
239237651Sbschmidt
240237651Sbschmidt	/* Free up the stream pointer. */
241237651Sbschmidt	free(sp);
242237651Sbschmidt
243237651Sbschmidt	/* Set errno and return. */
244237651Sbschmidt	if (!ISSET(FTS_NOCHDIR) && saved_errno) {
245237651Sbschmidt		errno = saved_errno;
246237651Sbschmidt		return (-1);
247237651Sbschmidt	}
248237651Sbschmidt	return (0);
249237651Sbschmidt}
250237651Sbschmidt
251237651Sbschmidt/*
252237651Sbschmidt * Special case a root of "/" so that slashes aren't appended which would
253237651Sbschmidt * cause paths to be written as "//foo".
254237651Sbschmidt */
255237651Sbschmidt#define	NAPPEND(p)							\
256237651Sbschmidt	(p->fts_level == FTS_ROOTLEVEL && p->fts_pathlen == 1 &&	\
257237651Sbschmidt	    p->fts_path[0] == '/' ? 0 : p->fts_pathlen)
258237651Sbschmidt
259237651SbschmidtFTSENT *
260237651Sbschmidtfts_read(sp)
261237651Sbschmidt	register FTS *sp;
262237651Sbschmidt{
263237651Sbschmidt	register FTSENT *p, *tmp;
264237651Sbschmidt	register int instr;
265237651Sbschmidt	register char *t;
266237651Sbschmidt	int saved_errno;
267237651Sbschmidt
268237651Sbschmidt	/* If finished or unrecoverable error, return NULL. */
269237651Sbschmidt	if (sp->fts_cur == NULL || ISSET(FTS_STOP))
270237651Sbschmidt		return (NULL);
271237651Sbschmidt
272237651Sbschmidt	/* Set current node pointer. */
273237651Sbschmidt	p = sp->fts_cur;
274237651Sbschmidt
275237651Sbschmidt	/* Save and zero out user instructions. */
276237651Sbschmidt	instr = p->fts_instr;
277237651Sbschmidt	p->fts_instr = FTS_NOINSTR;
278237651Sbschmidt
279237651Sbschmidt	/* Any type of file may be re-visited; re-stat and re-turn. */
280237651Sbschmidt	if (instr == FTS_AGAIN) {
281237651Sbschmidt		p->fts_info = fts_stat(sp, p, 0);
282237651Sbschmidt		return (p);
283237651Sbschmidt	}
284237651Sbschmidt
285237651Sbschmidt	/*
286237651Sbschmidt	 * Following a symlink -- SLNONE test allows application to see
287237651Sbschmidt	 * SLNONE and recover.  If indirecting through a symlink, have
288237651Sbschmidt	 * keep a pointer to current location.  If unable to get that
289237651Sbschmidt	 * pointer, follow fails.
290237651Sbschmidt	 */
291237651Sbschmidt	if (instr == FTS_FOLLOW &&
292237651Sbschmidt	    (p->fts_info == FTS_SL || p->fts_info == FTS_SLNONE)) {
293237651Sbschmidt		p->fts_info = fts_stat(sp, p, 1);
294237651Sbschmidt		if (p->fts_info == FTS_D && !ISSET(FTS_NOCHDIR))
295237651Sbschmidt			if ((p->fts_symfd = open(".", O_RDONLY, 0)) < 0) {
296237651Sbschmidt				p->fts_errno = errno;
297237651Sbschmidt				p->fts_info = FTS_ERR;
298237651Sbschmidt			} else
299237651Sbschmidt				p->fts_flags |= FTS_SYMFOLLOW;
300237651Sbschmidt		return (p);
301237651Sbschmidt	}
302237651Sbschmidt
303237651Sbschmidt	/* Directory in pre-order. */
304237651Sbschmidt	if (p->fts_info == FTS_D) {
305237651Sbschmidt		/* If skipped or crossed mount point, do post-order visit. */
306237651Sbschmidt		if (instr == FTS_SKIP ||
307237651Sbschmidt		    ISSET(FTS_XDEV) && p->fts_dev != sp->fts_dev) {
308237651Sbschmidt			if (p->fts_flags & FTS_SYMFOLLOW)
309237651Sbschmidt				(void)close(p->fts_symfd);
310237651Sbschmidt			if (sp->fts_child) {
311237651Sbschmidt				fts_lfree(sp->fts_child);
312237651Sbschmidt				sp->fts_child = NULL;
313237651Sbschmidt			}
314237651Sbschmidt			p->fts_info = FTS_DP;
315237651Sbschmidt			return (p);
316237651Sbschmidt		}
317237651Sbschmidt
318237651Sbschmidt		/* Rebuild if only read the names and now traversing. */
319237651Sbschmidt		if (sp->fts_child && sp->fts_options & FTS_NAMEONLY) {
320237651Sbschmidt			sp->fts_options &= ~FTS_NAMEONLY;
321237651Sbschmidt			fts_lfree(sp->fts_child);
322237651Sbschmidt			sp->fts_child = NULL;
323237651Sbschmidt		}
324237651Sbschmidt
325237651Sbschmidt		/*
326237651Sbschmidt		 * Cd to the subdirectory.
327237651Sbschmidt		 *
328237651Sbschmidt		 * If have already read and now fail to chdir, whack the list
329237651Sbschmidt		 * to make the names come out right, and set the parent errno
330237651Sbschmidt		 * so the application will eventually get an error condition.
331237651Sbschmidt		 * Set the FTS_DONTCHDIR flag so that when we logically change
332237651Sbschmidt		 * directories back to the parent we don't do a chdir.
333237651Sbschmidt		 *
334237651Sbschmidt		 * If haven't read do so.  If the read fails, fts_build sets
335237651Sbschmidt		 * FTS_STOP or the fts_info field of the node.
336237651Sbschmidt		 */
337237651Sbschmidt		if (sp->fts_child) {
338237651Sbschmidt			if (CHDIR(sp, p->fts_accpath)) {
339237651Sbschmidt				p->fts_errno = errno;
340237651Sbschmidt				p->fts_flags |= FTS_DONTCHDIR;
341237651Sbschmidt				for (p = sp->fts_child; p; p = p->fts_link)
342237651Sbschmidt					p->fts_accpath =
343237651Sbschmidt					    p->fts_parent->fts_accpath;
344237651Sbschmidt			}
345237651Sbschmidt		} else if ((sp->fts_child = fts_build(sp, BREAD)) == NULL) {
346237651Sbschmidt			if (ISSET(FTS_STOP))
347237651Sbschmidt				return (NULL);
348237651Sbschmidt			return (p);
349237651Sbschmidt		}
350237651Sbschmidt		p = sp->fts_child;
351237651Sbschmidt		sp->fts_child = NULL;
352237651Sbschmidt		goto name;
353237651Sbschmidt	}
354237651Sbschmidt
355237651Sbschmidt	/* Move to the next node on this level. */
356237651Sbschmidtnext:	tmp = p;
357237651Sbschmidt	if (p = p->fts_link) {
358237651Sbschmidt		free(tmp);
359237651Sbschmidt
360237651Sbschmidt		/*
361237651Sbschmidt		 * If reached the top, return to the original directory, and
362237651Sbschmidt		 * load the paths for the next root.
363237651Sbschmidt		 */
364237651Sbschmidt		if (p->fts_level == FTS_ROOTLEVEL) {
365237651Sbschmidt			if (!ISSET(FTS_NOCHDIR) && FCHDIR(sp, sp->fts_rfd)) {
366237651Sbschmidt				SET(FTS_STOP);
367237651Sbschmidt				return (NULL);
368237651Sbschmidt			}
369237651Sbschmidt			fts_load(sp, p);
370237651Sbschmidt			return (sp->fts_cur = p);
371237651Sbschmidt		}
372237651Sbschmidt
373237651Sbschmidt		/*
374237651Sbschmidt		 * User may have called fts_set on the node.  If skipped,
375237651Sbschmidt		 * ignore.  If followed, get a file descriptor so we can
376237651Sbschmidt		 * get back if necessary.
377237651Sbschmidt		 */
378237651Sbschmidt		if (p->fts_instr == FTS_SKIP)
379237651Sbschmidt			goto next;
380237651Sbschmidt		if (p->fts_instr == FTS_FOLLOW) {
381237651Sbschmidt			p->fts_info = fts_stat(sp, p, 1);
382237651Sbschmidt			if (p->fts_info == FTS_D && !ISSET(FTS_NOCHDIR))
383237651Sbschmidt				if ((p->fts_symfd =
384237651Sbschmidt				    open(".", O_RDONLY, 0)) < 0) {
385237651Sbschmidt					p->fts_errno = errno;
386237651Sbschmidt					p->fts_info = FTS_ERR;
387237651Sbschmidt				} else
388237651Sbschmidt					p->fts_flags |= FTS_SYMFOLLOW;
389237651Sbschmidt			p->fts_instr = FTS_NOINSTR;
390237651Sbschmidt		}
391237651Sbschmidt
392237651Sbschmidtname:		t = sp->fts_path + NAPPEND(p->fts_parent);
393237651Sbschmidt		*t++ = '/';
394237651Sbschmidt		memmove(t, p->fts_name, p->fts_namelen + 1);
395237651Sbschmidt		return (sp->fts_cur = p);
396237651Sbschmidt	}
397237651Sbschmidt
398237651Sbschmidt	/* Move up to the parent node. */
399237651Sbschmidt	p = tmp->fts_parent;
400237651Sbschmidt	free(tmp);
401237651Sbschmidt
402237651Sbschmidt	if (p->fts_level == FTS_ROOTPARENTLEVEL) {
403237651Sbschmidt		/*
404237651Sbschmidt		 * Done; free everything up and set errno to 0 so the user
405237651Sbschmidt		 * can distinguish between error and EOF.
406237651Sbschmidt		 */
407237651Sbschmidt		free(p);
408237651Sbschmidt		errno = 0;
409237651Sbschmidt		return (sp->fts_cur = NULL);
410237651Sbschmidt	}
411237651Sbschmidt
412237651Sbschmidt	/* Nul terminate the pathname. */
413237651Sbschmidt	sp->fts_path[p->fts_pathlen] = '\0';
414237651Sbschmidt
415237651Sbschmidt	/*
416237651Sbschmidt	 * Return to the parent directory.  If at a root node or came through
417237651Sbschmidt	 * a symlink, go back through the file descriptor.  Otherwise, cd up
418237651Sbschmidt	 * one directory.
419237651Sbschmidt	 */
420237651Sbschmidt	if (p->fts_level == FTS_ROOTLEVEL) {
421237651Sbschmidt		if (!ISSET(FTS_NOCHDIR) && FCHDIR(sp, sp->fts_rfd)) {
422237651Sbschmidt			SET(FTS_STOP);
423237651Sbschmidt			return (NULL);
424237651Sbschmidt		}
425237651Sbschmidt	} else if (p->fts_flags & FTS_SYMFOLLOW) {
426237651Sbschmidt		if (FCHDIR(sp, p->fts_symfd)) {
427237651Sbschmidt			saved_errno = errno;
428237651Sbschmidt			(void)close(p->fts_symfd);
429237651Sbschmidt			errno = saved_errno;
430237651Sbschmidt			SET(FTS_STOP);
431237651Sbschmidt			return (NULL);
432237651Sbschmidt		}
433237651Sbschmidt		(void)close(p->fts_symfd);
434237651Sbschmidt	} else if (!(p->fts_flags & FTS_DONTCHDIR)) {
435237651Sbschmidt		if (CHDIR(sp, "..")) {
436237651Sbschmidt			SET(FTS_STOP);
437237651Sbschmidt			return (NULL);
438237651Sbschmidt		}
439237651Sbschmidt	}
440237651Sbschmidt	p->fts_info = p->fts_errno ? FTS_ERR : FTS_DP;
441237651Sbschmidt	return (sp->fts_cur = p);
442237651Sbschmidt}
443237651Sbschmidt
444237651Sbschmidt/*
445237651Sbschmidt * Fts_set takes the stream as an argument although it's not used in this
446237651Sbschmidt * implementation; it would be necessary if anyone wanted to add global
447237651Sbschmidt * semantics to fts using fts_set.  An error return is allowed for similar
448237651Sbschmidt * reasons.
449237651Sbschmidt */
450237651Sbschmidt/* ARGSUSED */
451237651Sbschmidtint
452237651Sbschmidtfts_set(sp, p, instr)
453237651Sbschmidt	FTS *sp;
454237651Sbschmidt	FTSENT *p;
455237651Sbschmidt	int instr;
456237651Sbschmidt{
457237651Sbschmidt	if (instr && instr != FTS_AGAIN && instr != FTS_FOLLOW &&
458237651Sbschmidt	    instr != FTS_NOINSTR && instr != FTS_SKIP) {
459237651Sbschmidt		errno = EINVAL;
460237651Sbschmidt		return (1);
461237651Sbschmidt	}
462237651Sbschmidt	p->fts_instr = instr;
463237651Sbschmidt	return (0);
464237651Sbschmidt}
465237651Sbschmidt
466237651SbschmidtFTSENT *
467237651Sbschmidtfts_children(sp, instr)
468237651Sbschmidt	register FTS *sp;
469237651Sbschmidt	int instr;
470237651Sbschmidt{
471237651Sbschmidt	register FTSENT *p;
472237651Sbschmidt	int fd;
473237651Sbschmidt
474237651Sbschmidt	if (instr && instr != FTS_NAMEONLY) {
475237651Sbschmidt		errno = EINVAL;
476237651Sbschmidt		return (NULL);
477237651Sbschmidt	}
478237651Sbschmidt
479237651Sbschmidt	/* Set current node pointer. */
480237651Sbschmidt	p = sp->fts_cur;
481237651Sbschmidt
482237651Sbschmidt	/*
483237651Sbschmidt	 * Errno set to 0 so user can distinguish empty directory from
484237651Sbschmidt	 * an error.
485237651Sbschmidt	 */
486237651Sbschmidt	errno = 0;
487237651Sbschmidt
488237651Sbschmidt	/* Fatal errors stop here. */
489237651Sbschmidt	if (ISSET(FTS_STOP))
490237651Sbschmidt		return (NULL);
491237651Sbschmidt
492237651Sbschmidt	/* Return logical hierarchy of user's arguments. */
493237651Sbschmidt	if (p->fts_info == FTS_INIT)
494237651Sbschmidt		return (p->fts_link);
495237651Sbschmidt
496237651Sbschmidt	/*
497237651Sbschmidt	 * If not a directory being visited in pre-order, stop here.  Could
498237651Sbschmidt	 * allow FTS_DNR, assuming the user has fixed the problem, but the
499237651Sbschmidt	 * same effect is available with FTS_AGAIN.
500237651Sbschmidt	 */
501237651Sbschmidt	if (p->fts_info != FTS_D /* && p->fts_info != FTS_DNR */)
502237651Sbschmidt		return (NULL);
503237651Sbschmidt
504237651Sbschmidt	/* Free up any previous child list. */
505237651Sbschmidt	if (sp->fts_child)
506237651Sbschmidt		fts_lfree(sp->fts_child);
507237651Sbschmidt
508237651Sbschmidt	if (instr == FTS_NAMEONLY) {
509237651Sbschmidt		sp->fts_options |= FTS_NAMEONLY;
510237651Sbschmidt		instr = BNAMES;
511237651Sbschmidt	} else
512237651Sbschmidt		instr = BCHILD;
513237651Sbschmidt
514237651Sbschmidt	/*
515237651Sbschmidt	 * If using chdir on a relative path and called BEFORE fts_read does
516237651Sbschmidt	 * its chdir to the root of a traversal, we can lose -- we need to
517237651Sbschmidt	 * chdir into the subdirectory, and we don't know where the current
518237651Sbschmidt	 * directory is, so we can't get back so that the upcoming chdir by
519237651Sbschmidt	 * fts_read will work.
520237651Sbschmidt	 */
521237651Sbschmidt	if (p->fts_level != FTS_ROOTLEVEL || p->fts_accpath[0] == '/' ||
522237651Sbschmidt	    ISSET(FTS_NOCHDIR))
523237651Sbschmidt		return (sp->fts_child = fts_build(sp, instr));
524237651Sbschmidt
525237651Sbschmidt	if ((fd = open(".", O_RDONLY, 0)) < 0)
526237651Sbschmidt		return (NULL);
527237651Sbschmidt	sp->fts_child = fts_build(sp, instr);
528237651Sbschmidt	if (fchdir(fd))
529237651Sbschmidt		return (NULL);
530237651Sbschmidt	(void)close(fd);
531237651Sbschmidt	return (sp->fts_child);
532237651Sbschmidt}
533237651Sbschmidt
534237651Sbschmidt/*
535237651Sbschmidt * This is the tricky part -- do not casually change *anything* in here.  The
536237651Sbschmidt * idea is to build the linked list of entries that are used by fts_children
537237651Sbschmidt * and fts_read.  There are lots of special cases.
538237651Sbschmidt *
539237651Sbschmidt * The real slowdown in walking the tree is the stat calls.  If FTS_NOSTAT is
540237651Sbschmidt * set and it's a physical walk (so that symbolic links can't be directories),
541237651Sbschmidt * we can do things quickly.  First, if it's a 4.4BSD file system, the type
542237651Sbschmidt * of the file is in the directory entry.  Otherwise, we assume that the number
543237651Sbschmidt * of subdirectories in a node is equal to the number of links to the parent.
544237651Sbschmidt * The former skips all stat calls.  The latter skips stat calls in any leaf
545237651Sbschmidt * directories and for any files after the subdirectories in the directory have
546237651Sbschmidt * been found, cutting the stat calls by about 2/3.
547237651Sbschmidt */
548237651Sbschmidtstatic FTSENT *
549237651Sbschmidtfts_build(sp, type)
550237651Sbschmidt	register FTS *sp;
551237651Sbschmidt	int type;
552237651Sbschmidt{
553237651Sbschmidt	register struct dirent *dp;
554237651Sbschmidt	register FTSENT *p, *head;
555237651Sbschmidt	register int nitems;
556237651Sbschmidt	FTSENT *cur, *tail;
557237651Sbschmidt	DIR *dirp;
558237651Sbschmidt	void *adjaddr;
559237651Sbschmidt	int cderrno, descend, len, level, maxlen, nlinks, saved_errno;
560237651Sbschmidt	char *cp;
561237651Sbschmidt
562237651Sbschmidt	/* Set current node pointer. */
563237651Sbschmidt	cur = sp->fts_cur;
564237651Sbschmidt
565237651Sbschmidt	/*
566237651Sbschmidt	 * Open the directory for reading.  If this fails, we're done.
567237651Sbschmidt	 * If being called from fts_read, set the fts_info field.
568237651Sbschmidt	 */
569237651Sbschmidt	if ((dirp = opendir(cur->fts_accpath)) == NULL) {
570237651Sbschmidt		if (type == BREAD) {
571237651Sbschmidt			cur->fts_info = FTS_DNR;
572237651Sbschmidt			cur->fts_errno = errno;
573237651Sbschmidt		}
574237651Sbschmidt		return (NULL);
575237651Sbschmidt	}
576237651Sbschmidt
577237651Sbschmidt	/*
578237651Sbschmidt	 * Nlinks is the number of possible entries of type directory in the
579237651Sbschmidt	 * directory if we're cheating on stat calls, 0 if we're not doing
580237651Sbschmidt	 * any stat calls at all, -1 if we're doing stats on everything.
581237651Sbschmidt	 */
582237651Sbschmidt	if (type == BNAMES)
583237651Sbschmidt		nlinks = 0;
584237651Sbschmidt	else if (ISSET(FTS_NOSTAT) && ISSET(FTS_PHYSICAL))
585237651Sbschmidt		nlinks = cur->fts_nlink - (ISSET(FTS_SEEDOT) ? 0 : 2);
586237651Sbschmidt	else
587237651Sbschmidt		nlinks = -1;
588237651Sbschmidt
589237651Sbschmidt#ifdef notdef
590237651Sbschmidt	(void)printf("nlinks == %d (cur: %d)\n", nlinks, cur->fts_nlink);
591237651Sbschmidt	(void)printf("NOSTAT %d PHYSICAL %d SEEDOT %d\n",
592237651Sbschmidt	    ISSET(FTS_NOSTAT), ISSET(FTS_PHYSICAL), ISSET(FTS_SEEDOT));
593237651Sbschmidt#endif
594237651Sbschmidt	/*
595237651Sbschmidt	 * If we're going to need to stat anything or we want to descend
596237651Sbschmidt	 * and stay in the directory, chdir.  If this fails we keep going,
597237651Sbschmidt	 * but set a flag so we don't chdir after the post-order visit.
598237651Sbschmidt	 * We won't be able to stat anything, but we can still return the
599237651Sbschmidt	 * names themselves.  Note, that since fts_read won't be able to
600237651Sbschmidt	 * chdir into the directory, it will have to return different path
601237651Sbschmidt	 * names than before, i.e. "a/b" instead of "b".  Since the node
602237651Sbschmidt	 * has already been visited in pre-order, have to wait until the
603237651Sbschmidt	 * post-order visit to return the error.  There is a special case
604237651Sbschmidt	 * here, if there was nothing to stat then it's not an error to
605237651Sbschmidt	 * not be able to stat.  This is all fairly nasty.  If a program
606237651Sbschmidt	 * needed sorted entries or stat information, they had better be
607237651Sbschmidt	 * checking FTS_NS on the returned nodes.
608237651Sbschmidt	 */
609237651Sbschmidt	cderrno = 0;
610237651Sbschmidt	if (nlinks || type == BREAD)
611237651Sbschmidt		if (FCHDIR(sp, dirfd(dirp))) {
612237651Sbschmidt			if (nlinks && type == BREAD)
613237651Sbschmidt				cur->fts_errno = errno;
614237651Sbschmidt			cur->fts_flags |= FTS_DONTCHDIR;
615237651Sbschmidt			descend = 0;
616237651Sbschmidt			cderrno = errno;
617237651Sbschmidt		} else
618237651Sbschmidt			descend = 1;
619237651Sbschmidt	else
620237651Sbschmidt		descend = 0;
621237651Sbschmidt
622237651Sbschmidt	/*
623237651Sbschmidt	 * Figure out the max file name length that can be stored in the
624237651Sbschmidt	 * current path -- the inner loop allocates more path as necessary.
625237651Sbschmidt	 * We really wouldn't have to do the maxlen calculations here, we
626237651Sbschmidt	 * could do them in fts_read before returning the path, but it's a
627237651Sbschmidt	 * lot easier here since the length is part of the dirent structure.
628237651Sbschmidt	 *
629237651Sbschmidt	 * If not changing directories set a pointer so that can just append
630237651Sbschmidt	 * each new name into the path.
631237651Sbschmidt	 */
632237651Sbschmidt	maxlen = sp->fts_pathlen - cur->fts_pathlen - 1;
633237651Sbschmidt	len = NAPPEND(cur);
634237651Sbschmidt	if (ISSET(FTS_NOCHDIR)) {
635237651Sbschmidt		cp = sp->fts_path + len;
636237651Sbschmidt		*cp++ = '/';
637237651Sbschmidt	}
638237651Sbschmidt
639237651Sbschmidt	level = cur->fts_level + 1;
640237651Sbschmidt
641237651Sbschmidt	/* Read the directory, attaching each entry to the `link' pointer. */
642237651Sbschmidt	adjaddr = NULL;
643237651Sbschmidt	for (head = tail = NULL, nitems = 0; dp = readdir(dirp);) {
644237651Sbschmidt		if (!ISSET(FTS_SEEDOT) && ISDOT(dp->d_name))
645237651Sbschmidt			continue;
646237651Sbschmidt
647237651Sbschmidt		if ((p = fts_alloc(sp, dp->d_name, (int)dp->d_namlen)) == NULL)
648237651Sbschmidt			goto mem1;
649237651Sbschmidt		if (dp->d_namlen > maxlen) {
650237651Sbschmidt			if (fts_palloc(sp, (size_t)dp->d_namlen)) {
651237651Sbschmidt				/*
652237651Sbschmidt				 * No more memory for path or structures.  Save
653237651Sbschmidt				 * errno, free up the current structure and the
654237651Sbschmidt				 * structures already allocated.
655237651Sbschmidt				 */
656237651Sbschmidtmem1:				saved_errno = errno;
657237651Sbschmidt				if (p)
658237651Sbschmidt					free(p);
659237651Sbschmidt				fts_lfree(head);
660237651Sbschmidt				(void)closedir(dirp);
661237651Sbschmidt				errno = saved_errno;
662237651Sbschmidt				cur->fts_info = FTS_ERR;
663237651Sbschmidt				SET(FTS_STOP);
664237651Sbschmidt				return (NULL);
665237651Sbschmidt			}
666237651Sbschmidt			adjaddr = sp->fts_path;
667237651Sbschmidt			maxlen = sp->fts_pathlen - sp->fts_cur->fts_pathlen - 1;
668237651Sbschmidt		}
669237651Sbschmidt
670237651Sbschmidt		p->fts_pathlen = len + dp->d_namlen + 1;
671237651Sbschmidt		p->fts_parent = sp->fts_cur;
672237651Sbschmidt		p->fts_level = level;
673237651Sbschmidt
674237651Sbschmidt		if (cderrno) {
675237651Sbschmidt			if (nlinks) {
676237651Sbschmidt				p->fts_info = FTS_NS;
677237651Sbschmidt				p->fts_errno = cderrno;
678237651Sbschmidt			} else
679237651Sbschmidt				p->fts_info = FTS_NSOK;
680237651Sbschmidt			p->fts_accpath = cur->fts_accpath;
681237651Sbschmidt		} else if (nlinks == 0
682237651Sbschmidt#ifdef DT_DIR
683237651Sbschmidt		    || nlinks > 0 &&
684237651Sbschmidt		    dp->d_type != DT_DIR && dp->d_type != DT_UNKNOWN
685237651Sbschmidt#endif
686237651Sbschmidt		    ) {
687237651Sbschmidt			p->fts_accpath =
688237651Sbschmidt			    ISSET(FTS_NOCHDIR) ? p->fts_path : p->fts_name;
689237651Sbschmidt			p->fts_info = FTS_NSOK;
690237651Sbschmidt		} else {
691237651Sbschmidt			/* Build a file name for fts_stat to stat. */
692237651Sbschmidt			if (ISSET(FTS_NOCHDIR)) {
693237651Sbschmidt				p->fts_accpath = p->fts_path;
694237651Sbschmidt				memmove(cp, p->fts_name, p->fts_namelen + 1);
695237651Sbschmidt			} else
696237651Sbschmidt				p->fts_accpath = p->fts_name;
697237651Sbschmidt			/* Stat it. */
698237651Sbschmidt			p->fts_info = fts_stat(sp, p, 0);
699237651Sbschmidt
700237651Sbschmidt			/* Decrement link count if applicable. */
701237651Sbschmidt			if (nlinks > 0 && (p->fts_info == FTS_D ||
702237651Sbschmidt			    p->fts_info == FTS_DC || p->fts_info == FTS_DOT))
703237651Sbschmidt				--nlinks;
704237651Sbschmidt		}
705237651Sbschmidt
706237651Sbschmidt		/* We walk in directory order so "ls -f" doesn't get upset. */
707237651Sbschmidt		p->fts_link = NULL;
708237651Sbschmidt		if (head == NULL)
709237651Sbschmidt			head = tail = p;
710237651Sbschmidt		else {
711237651Sbschmidt			tail->fts_link = p;
712237651Sbschmidt			tail = p;
713237651Sbschmidt		}
714237651Sbschmidt		++nitems;
715237651Sbschmidt	}
716237651Sbschmidt	(void)closedir(dirp);
717237651Sbschmidt
718237651Sbschmidt	/*
719237651Sbschmidt	 * If had to realloc the path, adjust the addresses for the rest
720237651Sbschmidt	 * of the tree.
721237651Sbschmidt	 */
722237651Sbschmidt	if (adjaddr)
723237651Sbschmidt		fts_padjust(sp, adjaddr);
724237651Sbschmidt
725237651Sbschmidt	/*
726237651Sbschmidt	 * If not changing directories, reset the path back to original
727237651Sbschmidt	 * state.
728237651Sbschmidt	 */
729237651Sbschmidt	if (ISSET(FTS_NOCHDIR)) {
730237651Sbschmidt		if (cp - 1 > sp->fts_path)
731237651Sbschmidt			--cp;
732237651Sbschmidt		*cp = '\0';
733237651Sbschmidt	}
734237651Sbschmidt
735237651Sbschmidt	/*
736237651Sbschmidt	 * If descended after called from fts_children or after called from
737237651Sbschmidt	 * fts_read and nothing found, get back.  At the root level we use
738237651Sbschmidt	 * the saved fd; if one of fts_open()'s arguments is a relative path
739237651Sbschmidt	 * to an empty directory, we wind up here with no other way back.  If
740237651Sbschmidt	 * can't get back, we're done.
741237651Sbschmidt	 */
742237651Sbschmidt	if (descend && (type == BCHILD || !nitems) &&
743237651Sbschmidt	    (cur->fts_level == FTS_ROOTLEVEL ?
744237651Sbschmidt	    FCHDIR(sp, sp->fts_rfd) : CHDIR(sp, ".."))) {
745237651Sbschmidt		cur->fts_info = FTS_ERR;
746237651Sbschmidt		SET(FTS_STOP);
747237651Sbschmidt		return (NULL);
748237651Sbschmidt	}
749237651Sbschmidt
750237651Sbschmidt	/* If didn't find anything, return NULL. */
751237651Sbschmidt	if (!nitems) {
752237651Sbschmidt		if (type == BREAD)
753237651Sbschmidt			cur->fts_info = FTS_DP;
754237651Sbschmidt		return (NULL);
755237651Sbschmidt	}
756237651Sbschmidt
757237651Sbschmidt	/* Sort the entries. */
758237651Sbschmidt	if (sp->fts_compar && nitems > 1)
759237651Sbschmidt		head = fts_sort(sp, head, nitems);
760237651Sbschmidt	return (head);
761237651Sbschmidt}
762237651Sbschmidt
763237651Sbschmidtstatic u_short
764237651Sbschmidtfts_stat(sp, p, follow)
765237651Sbschmidt	FTS *sp;
766237651Sbschmidt	register FTSENT *p;
767237651Sbschmidt	int follow;
768237651Sbschmidt{
769237651Sbschmidt	register FTSENT *t;
770237651Sbschmidt	register dev_t dev;
771237651Sbschmidt	register ino_t ino;
772237651Sbschmidt	struct stat *sbp, sb;
773237651Sbschmidt	int saved_errno;
774237651Sbschmidt
775237651Sbschmidt	/* If user needs stat info, stat buffer already allocated. */
776237651Sbschmidt	sbp = ISSET(FTS_NOSTAT) ? &sb : p->fts_statp;
777237651Sbschmidt
778237651Sbschmidt	/*
779237651Sbschmidt	 * If doing a logical walk, or application requested FTS_FOLLOW, do
780237651Sbschmidt	 * a stat(2).  If that fails, check for a non-existent symlink.  If
781237651Sbschmidt	 * fail, set the errno from the stat call.
782237651Sbschmidt	 */
783237651Sbschmidt	if (ISSET(FTS_LOGICAL) || follow) {
784237651Sbschmidt		if (stat(p->fts_accpath, sbp)) {
785237651Sbschmidt			saved_errno = errno;
786237651Sbschmidt			if (!lstat(p->fts_accpath, sbp)) {
787237651Sbschmidt				errno = 0;
788237651Sbschmidt				return (FTS_SLNONE);
789237651Sbschmidt			}
790237651Sbschmidt			p->fts_errno = saved_errno;
791237651Sbschmidt			goto err;
792237651Sbschmidt		}
793237651Sbschmidt	} else if (lstat(p->fts_accpath, sbp)) {
794237651Sbschmidt		p->fts_errno = errno;
795237651Sbschmidterr:		memset(sbp, 0, sizeof(struct stat));
796237651Sbschmidt		return (FTS_NS);
797237651Sbschmidt	}
798237651Sbschmidt
799237651Sbschmidt	if (S_ISDIR(sbp->st_mode)) {
800237651Sbschmidt		/*
801237651Sbschmidt		 * Set the device/inode.  Used to find cycles and check for
802237651Sbschmidt		 * crossing mount points.  Also remember the link count, used
803237651Sbschmidt		 * in fts_build to limit the number of stat calls.  It is
804237651Sbschmidt		 * understood that these fields are only referenced if fts_info
805237651Sbschmidt		 * is set to FTS_D.
806237651Sbschmidt		 */
807237651Sbschmidt		dev = p->fts_dev = sbp->st_dev;
808237651Sbschmidt		ino = p->fts_ino = sbp->st_ino;
809237651Sbschmidt		p->fts_nlink = sbp->st_nlink;
810237651Sbschmidt
811237651Sbschmidt		if (ISDOT(p->fts_name))
812237651Sbschmidt			return (FTS_DOT);
813237651Sbschmidt
814237651Sbschmidt		/*
815237651Sbschmidt		 * Cycle detection is done by brute force when the directory
816237651Sbschmidt		 * is first encountered.  If the tree gets deep enough or the
817237651Sbschmidt		 * number of symbolic links to directories is high enough,
818237651Sbschmidt		 * something faster might be worthwhile.
819237651Sbschmidt		 */
820237651Sbschmidt		for (t = p->fts_parent;
821237651Sbschmidt		    t->fts_level >= FTS_ROOTLEVEL; t = t->fts_parent)
822237651Sbschmidt			if (ino == t->fts_ino && dev == t->fts_dev) {
823237651Sbschmidt				p->fts_cycle = t;
824237651Sbschmidt				return (FTS_DC);
825237651Sbschmidt			}
826237651Sbschmidt		return (FTS_D);
827237651Sbschmidt	}
828237651Sbschmidt	if (S_ISLNK(sbp->st_mode))
829237651Sbschmidt		return (FTS_SL);
830237651Sbschmidt	if (S_ISREG(sbp->st_mode))
831237651Sbschmidt		return (FTS_F);
832237651Sbschmidt	return (FTS_DEFAULT);
833237651Sbschmidt}
834237651Sbschmidt
835237651Sbschmidtstatic FTSENT *
836237651Sbschmidtfts_sort(sp, head, nitems)
837237651Sbschmidt	FTS *sp;
838237651Sbschmidt	FTSENT *head;
839237651Sbschmidt	register int nitems;
840237651Sbschmidt{
841237651Sbschmidt	register FTSENT **ap, *p;
842237651Sbschmidt
843237651Sbschmidt	/*
844237651Sbschmidt	 * Construct an array of pointers to the structures and call qsort(3).
845237651Sbschmidt	 * Reassemble the array in the order returned by qsort.  If unable to
846237651Sbschmidt	 * sort for memory reasons, return the directory entries in their
847237651Sbschmidt	 * current order.  Allocate enough space for the current needs plus
848237651Sbschmidt	 * 40 so don't realloc one entry at a time.
849237651Sbschmidt	 */
850237651Sbschmidt	if (nitems > sp->fts_nitems) {
851237651Sbschmidt		sp->fts_nitems = nitems + 40;
852237651Sbschmidt		if ((sp->fts_array = realloc(sp->fts_array,
853237651Sbschmidt		    (size_t)(sp->fts_nitems * sizeof(FTSENT *)))) == NULL) {
854237651Sbschmidt			sp->fts_nitems = 0;
855237651Sbschmidt			return (head);
856237651Sbschmidt		}
857237651Sbschmidt	}
858237651Sbschmidt	for (ap = sp->fts_array, p = head; p; p = p->fts_link)
859237651Sbschmidt		*ap++ = p;
860237651Sbschmidt	qsort((void *)sp->fts_array, nitems, sizeof(FTSENT *), sp->fts_compar);
861237651Sbschmidt	for (head = *(ap = sp->fts_array); --nitems; ++ap)
862237651Sbschmidt		ap[0]->fts_link = ap[1];
863237651Sbschmidt	ap[0]->fts_link = NULL;
864237651Sbschmidt	return (head);
865237651Sbschmidt}
866237651Sbschmidt
867237651Sbschmidtstatic FTSENT *
868237651Sbschmidtfts_alloc(sp, name, namelen)
869237651Sbschmidt	FTS *sp;
870237651Sbschmidt	char *name;
871237651Sbschmidt	register int namelen;
872237651Sbschmidt{
873237651Sbschmidt	register FTSENT *p;
874237651Sbschmidt	size_t len;
875237651Sbschmidt
876237651Sbschmidt	/*
877237651Sbschmidt	 * The file name is a variable length array and no stat structure is
878237651Sbschmidt	 * necessary if the user has set the nostat bit.  Allocate the FTSENT
879237651Sbschmidt	 * structure, the file name and the stat structure in one chunk, but
880237651Sbschmidt	 * be careful that the stat structure is reasonably aligned.  Since the
881237651Sbschmidt	 * fts_name field is declared to be of size 1, the fts_name pointer is
882237651Sbschmidt	 * namelen + 2 before the first possible address of the stat structure.
883237651Sbschmidt	 */
884237651Sbschmidt	len = sizeof(FTSENT) + namelen;
885237651Sbschmidt	if (!ISSET(FTS_NOSTAT))
886237651Sbschmidt		len += sizeof(struct stat) + ALIGNBYTES;
887237651Sbschmidt	if ((p = malloc(len)) == NULL)
888237651Sbschmidt		return (NULL);
889237651Sbschmidt
890237651Sbschmidt	/* Copy the name plus the trailing NULL. */
891237651Sbschmidt	memmove(p->fts_name, name, namelen + 1);
892237651Sbschmidt
893237651Sbschmidt	if (!ISSET(FTS_NOSTAT))
894237651Sbschmidt		p->fts_statp = (struct stat *)ALIGN(p->fts_name + namelen + 2);
895237651Sbschmidt	p->fts_namelen = namelen;
896237651Sbschmidt	p->fts_path = sp->fts_path;
897237651Sbschmidt	p->fts_errno = 0;
898237651Sbschmidt	p->fts_flags = 0;
899237651Sbschmidt	p->fts_instr = FTS_NOINSTR;
900237651Sbschmidt	p->fts_number = 0;
901237651Sbschmidt	p->fts_pointer = NULL;
902237651Sbschmidt	return (p);
903237651Sbschmidt}
904237651Sbschmidt
905237651Sbschmidtstatic void
906237651Sbschmidtfts_lfree(head)
907237651Sbschmidt	register FTSENT *head;
908237651Sbschmidt{
909237651Sbschmidt	register FTSENT *p;
910237651Sbschmidt
911237651Sbschmidt	/* Free a linked list of structures. */
912237651Sbschmidt	while (p = head) {
913237651Sbschmidt		head = head->fts_link;
914237651Sbschmidt		free(p);
915237651Sbschmidt	}
916237651Sbschmidt}
917237651Sbschmidt
918237651Sbschmidt/*
919237651Sbschmidt * Allow essentially unlimited paths; find, rm, ls should all work on any tree.
920237651Sbschmidt * Most systems will allow creation of paths much longer than MAXPATHLEN, even
921237651Sbschmidt * though the kernel won't resolve them.  Add the size (not just what's needed)
922237651Sbschmidt * plus 256 bytes so don't realloc the path 2 bytes at a time.
923237651Sbschmidt */
924237651Sbschmidtstatic int
925237651Sbschmidtfts_palloc(sp, more)
926237651Sbschmidt	FTS *sp;
927237651Sbschmidt	size_t more;
928237651Sbschmidt{
929237651Sbschmidt	sp->fts_pathlen += more + 256;
930237651Sbschmidt	sp->fts_path = realloc(sp->fts_path, (size_t)sp->fts_pathlen);
931237651Sbschmidt	return (sp->fts_path == NULL);
932237651Sbschmidt}
933237651Sbschmidt
934237651Sbschmidt/*
935237651Sbschmidt * When the path is realloc'd, have to fix all of the pointers in structures
936237651Sbschmidt * already returned.
937237651Sbschmidt */
938237651Sbschmidtstatic void
939237651Sbschmidtfts_padjust(sp, addr)
940237651Sbschmidt	FTS *sp;
941237651Sbschmidt	void *addr;
942237651Sbschmidt{
943237651Sbschmidt	FTSENT *p;
944237651Sbschmidt
945237651Sbschmidt#define	ADJUST(p) {							\
946237651Sbschmidt	(p)->fts_accpath =						\
947237651Sbschmidt	    (char *)addr + ((p)->fts_accpath - (p)->fts_path);		\
948237651Sbschmidt	(p)->fts_path = addr;						\
949237651Sbschmidt}
950237651Sbschmidt	/* Adjust the current set of children. */
951237651Sbschmidt	for (p = sp->fts_child; p; p = p->fts_link)
952237651Sbschmidt		ADJUST(p);
953237651Sbschmidt
954237651Sbschmidt	/* Adjust the rest of the tree. */
955237651Sbschmidt	for (p = sp->fts_cur; p->fts_level >= FTS_ROOTLEVEL;) {
956237651Sbschmidt		ADJUST(p);
957237651Sbschmidt		p = p->fts_link ? p->fts_link : p->fts_parent;
958237651Sbschmidt	}
959237651Sbschmidt}
960237651Sbschmidt
961237651Sbschmidtstatic size_t
962237651Sbschmidtfts_maxarglen(argv)
963237651Sbschmidt	char * const *argv;
964237651Sbschmidt{
965237651Sbschmidt	size_t len, max;
966237651Sbschmidt
967237651Sbschmidt	for (max = 0; *argv; ++argv)
968237651Sbschmidt		if ((len = strlen(*argv)) > max)
969237651Sbschmidt			max = len;
970237651Sbschmidt	return (max);
971237651Sbschmidt}
972237651Sbschmidt