1fec65c98Schristos #include "config.h"
2fec65c98Schristos
3fec65c98Schristos #if HAVE_FTS
4fec65c98Schristos
5fec65c98Schristos int dummy;
6fec65c98Schristos
7fec65c98Schristos #else
8fec65c98Schristos
9*9508192eSchristos /* Id: compat_fts.c,v 1.14 2017/02/18 12:24:24 schwarze Exp */
10*9508192eSchristos /* $OpenBSD: fts.c,v 1.56 2016/09/21 04:38:56 guenther Exp $ */
11fec65c98Schristos
12fec65c98Schristos /*-
13fec65c98Schristos * Copyright (c) 1990, 1993, 1994
14fec65c98Schristos * The Regents of the University of California. All rights reserved.
15fec65c98Schristos *
16fec65c98Schristos * Redistribution and use in source and binary forms, with or without
17fec65c98Schristos * modification, are permitted provided that the following conditions
18fec65c98Schristos * are met:
19fec65c98Schristos * 1. Redistributions of source code must retain the above copyright
20fec65c98Schristos * notice, this list of conditions and the following disclaimer.
21fec65c98Schristos * 2. Redistributions in binary form must reproduce the above copyright
22fec65c98Schristos * notice, this list of conditions and the following disclaimer in the
23fec65c98Schristos * documentation and/or other materials provided with the distribution.
24fec65c98Schristos * 3. Neither the name of the University nor the names of its contributors
25fec65c98Schristos * may be used to endorse or promote products derived from this software
26fec65c98Schristos * without specific prior written permission.
27fec65c98Schristos *
28fec65c98Schristos * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
29fec65c98Schristos * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
30fec65c98Schristos * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
31fec65c98Schristos * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
32fec65c98Schristos * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
33fec65c98Schristos * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
34fec65c98Schristos * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
35fec65c98Schristos * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
36fec65c98Schristos * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
37fec65c98Schristos * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
38fec65c98Schristos * SUCH DAMAGE.
39fec65c98Schristos */
40fec65c98Schristos
41fec65c98Schristos #include <sys/stat.h>
42fec65c98Schristos #include <sys/types.h>
43fec65c98Schristos
44fec65c98Schristos #include <dirent.h>
45fec65c98Schristos #include <errno.h>
46fec65c98Schristos #include <fcntl.h>
47fec65c98Schristos #include <limits.h>
48fec65c98Schristos #include <stdlib.h>
49fec65c98Schristos #include <string.h>
50fec65c98Schristos #include <unistd.h>
51fec65c98Schristos #include "compat_fts.h"
52fec65c98Schristos
53fec65c98Schristos #define MAXIMUM(a, b) (((a) > (b)) ? (a) : (b))
54fec65c98Schristos
55fec65c98Schristos static FTSENT *fts_alloc(FTS *, const char *, size_t);
56fec65c98Schristos static FTSENT *fts_build(FTS *);
57fec65c98Schristos static void fts_lfree(FTSENT *);
58fec65c98Schristos static void fts_load(FTS *, FTSENT *);
59fec65c98Schristos static size_t fts_maxarglen(char * const *);
60fec65c98Schristos static void fts_padjust(FTS *, FTSENT *);
61fec65c98Schristos static int fts_palloc(FTS *, size_t);
62*9508192eSchristos static FTSENT *fts_sort(FTS *, FTSENT *, int);
63fec65c98Schristos static unsigned short fts_stat(FTS *, FTSENT *);
64fec65c98Schristos
65fec65c98Schristos #define ISDOT(a) (a[0] == '.' && (!a[1] || (a[1] == '.' && !a[2])))
66fec65c98Schristos #ifndef O_CLOEXEC
67fec65c98Schristos #define O_CLOEXEC 0
68fec65c98Schristos #endif
69fec65c98Schristos
70fec65c98Schristos #define CLR(opt) (sp->fts_options &= ~(opt))
71fec65c98Schristos #define ISSET(opt) (sp->fts_options & (opt))
72fec65c98Schristos #define SET(opt) (sp->fts_options |= (opt))
73fec65c98Schristos
74fec65c98Schristos FTS *
fts_open(char * const * argv,int options,int (* compar)(const FTSENT **,const FTSENT **))75*9508192eSchristos fts_open(char * const *argv, int options,
76*9508192eSchristos int (*compar)(const FTSENT **, const FTSENT **))
77fec65c98Schristos {
78fec65c98Schristos FTS *sp;
79fec65c98Schristos FTSENT *p, *root;
80fec65c98Schristos int nitems;
81*9508192eSchristos FTSENT *parent, *prev;
82fec65c98Schristos
83fec65c98Schristos /* Options check. */
84fec65c98Schristos if (options & ~FTS_OPTIONMASK) {
85fec65c98Schristos errno = EINVAL;
86fec65c98Schristos return (NULL);
87fec65c98Schristos }
88fec65c98Schristos
89*9508192eSchristos /* At least one path must be specified. */
90*9508192eSchristos if (*argv == NULL) {
91*9508192eSchristos errno = EINVAL;
92*9508192eSchristos return (NULL);
93*9508192eSchristos }
94*9508192eSchristos
95fec65c98Schristos /* Allocate/initialize the stream */
96fec65c98Schristos if ((sp = calloc(1, sizeof(FTS))) == NULL)
97fec65c98Schristos return (NULL);
98*9508192eSchristos sp->fts_compar = compar;
99fec65c98Schristos sp->fts_options = options;
100fec65c98Schristos
101fec65c98Schristos /*
102fec65c98Schristos * Start out with 1K of path space, and enough, in any case,
103fec65c98Schristos * to hold the user's paths.
104fec65c98Schristos */
105fec65c98Schristos if (fts_palloc(sp, MAXIMUM(fts_maxarglen(argv), PATH_MAX)))
106fec65c98Schristos goto mem1;
107fec65c98Schristos
108fec65c98Schristos /* Allocate/initialize root's parent. */
109fec65c98Schristos if ((parent = fts_alloc(sp, "", 0)) == NULL)
110fec65c98Schristos goto mem2;
111fec65c98Schristos parent->fts_level = FTS_ROOTPARENTLEVEL;
112fec65c98Schristos
113fec65c98Schristos /* Allocate/initialize root(s). */
114*9508192eSchristos for (root = prev = NULL, nitems = 0; *argv; ++argv, ++nitems) {
115*9508192eSchristos if ((p = fts_alloc(sp, *argv, strlen(*argv))) == NULL)
116fec65c98Schristos goto mem3;
117fec65c98Schristos p->fts_level = FTS_ROOTLEVEL;
118fec65c98Schristos p->fts_parent = parent;
119fec65c98Schristos p->fts_accpath = p->fts_name;
120fec65c98Schristos p->fts_info = fts_stat(sp, p);
121fec65c98Schristos
122fec65c98Schristos /* Command-line "." and ".." are real directories. */
123fec65c98Schristos if (p->fts_info == FTS_DOT)
124fec65c98Schristos p->fts_info = FTS_D;
125fec65c98Schristos
126*9508192eSchristos /*
127*9508192eSchristos * If comparison routine supplied, traverse in sorted
128*9508192eSchristos * order; otherwise traverse in the order specified.
129*9508192eSchristos */
130*9508192eSchristos if (compar) {
131*9508192eSchristos p->fts_link = root;
132*9508192eSchristos root = p;
133*9508192eSchristos } else {
134fec65c98Schristos p->fts_link = NULL;
135fec65c98Schristos if (root == NULL)
136*9508192eSchristos root = p;
137*9508192eSchristos else
138*9508192eSchristos prev->fts_link = p;
139*9508192eSchristos prev = p;
140fec65c98Schristos }
141fec65c98Schristos }
142*9508192eSchristos if (compar && nitems > 1)
143*9508192eSchristos root = fts_sort(sp, root, nitems);
144fec65c98Schristos
145fec65c98Schristos /*
146fec65c98Schristos * Allocate a dummy pointer and make fts_read think that we've just
147fec65c98Schristos * finished the node before the root(s); set p->fts_info to FTS_INIT
148fec65c98Schristos * so that everything about the "current" node is ignored.
149fec65c98Schristos */
150fec65c98Schristos if ((sp->fts_cur = fts_alloc(sp, "", 0)) == NULL)
151fec65c98Schristos goto mem3;
152fec65c98Schristos sp->fts_cur->fts_link = root;
153fec65c98Schristos sp->fts_cur->fts_info = FTS_INIT;
154fec65c98Schristos
155fec65c98Schristos if (nitems == 0)
156fec65c98Schristos free(parent);
157fec65c98Schristos
158fec65c98Schristos return (sp);
159fec65c98Schristos
160fec65c98Schristos mem3: fts_lfree(root);
161fec65c98Schristos free(parent);
162fec65c98Schristos mem2: free(sp->fts_path);
163fec65c98Schristos mem1: free(sp);
164fec65c98Schristos return (NULL);
165fec65c98Schristos }
166fec65c98Schristos
167fec65c98Schristos static void
fts_load(FTS * sp,FTSENT * p)168fec65c98Schristos fts_load(FTS *sp, FTSENT *p)
169fec65c98Schristos {
170fec65c98Schristos size_t len;
171fec65c98Schristos char *cp;
172fec65c98Schristos
173fec65c98Schristos /*
174fec65c98Schristos * Load the stream structure for the next traversal. Since we don't
175fec65c98Schristos * actually enter the directory until after the preorder visit, set
176fec65c98Schristos * the fts_accpath field specially so the chdir gets done to the right
177fec65c98Schristos * place and the user can access the first node. From fts_open it's
178fec65c98Schristos * known that the path will fit.
179fec65c98Schristos */
180fec65c98Schristos len = p->fts_pathlen = p->fts_namelen;
181fec65c98Schristos memmove(sp->fts_path, p->fts_name, len + 1);
182fec65c98Schristos if ((cp = strrchr(p->fts_name, '/')) && (cp != p->fts_name || cp[1])) {
183fec65c98Schristos len = strlen(++cp);
184fec65c98Schristos memmove(p->fts_name, cp, len + 1);
185fec65c98Schristos p->fts_namelen = len;
186fec65c98Schristos }
187fec65c98Schristos p->fts_accpath = p->fts_path = sp->fts_path;
188fec65c98Schristos sp->fts_dev = p->fts_dev;
189fec65c98Schristos }
190fec65c98Schristos
191fec65c98Schristos int
fts_close(FTS * sp)192fec65c98Schristos fts_close(FTS *sp)
193fec65c98Schristos {
194fec65c98Schristos FTSENT *freep, *p;
195fec65c98Schristos
196fec65c98Schristos /*
197fec65c98Schristos * This still works if we haven't read anything -- the dummy structure
198fec65c98Schristos * points to the root list, so we step through to the end of the root
199fec65c98Schristos * list which has a valid parent pointer.
200fec65c98Schristos */
201fec65c98Schristos if (sp->fts_cur) {
202fec65c98Schristos for (p = sp->fts_cur; p->fts_level >= FTS_ROOTLEVEL;) {
203fec65c98Schristos freep = p;
204fec65c98Schristos p = p->fts_link ? p->fts_link : p->fts_parent;
205fec65c98Schristos free(freep);
206fec65c98Schristos }
207fec65c98Schristos free(p);
208fec65c98Schristos }
209fec65c98Schristos
210fec65c98Schristos /* Free up child linked list, sort array, path buffer, stream ptr.*/
211fec65c98Schristos if (sp->fts_child)
212fec65c98Schristos fts_lfree(sp->fts_child);
213*9508192eSchristos free(sp->fts_array);
214fec65c98Schristos free(sp->fts_path);
215fec65c98Schristos free(sp);
216fec65c98Schristos
2179ff1f2acSchristos return (0);
218fec65c98Schristos }
219fec65c98Schristos
220fec65c98Schristos /*
221fec65c98Schristos * Special case of "/" at the end of the path so that slashes aren't
222fec65c98Schristos * appended which would cause paths to be written as "....//foo".
223fec65c98Schristos */
224fec65c98Schristos #define NAPPEND(p) \
225fec65c98Schristos (p->fts_path[p->fts_pathlen - 1] == '/' \
226fec65c98Schristos ? p->fts_pathlen - 1 : p->fts_pathlen)
227fec65c98Schristos
228fec65c98Schristos FTSENT *
fts_read(FTS * sp)229fec65c98Schristos fts_read(FTS *sp)
230fec65c98Schristos {
231fec65c98Schristos FTSENT *p, *tmp;
232fec65c98Schristos int instr;
233fec65c98Schristos char *t;
234fec65c98Schristos
235fec65c98Schristos /* If finished or unrecoverable error, return NULL. */
236fec65c98Schristos if (sp->fts_cur == NULL || ISSET(FTS_STOP))
237fec65c98Schristos return (NULL);
238fec65c98Schristos
239fec65c98Schristos /* Set current node pointer. */
240fec65c98Schristos p = sp->fts_cur;
241fec65c98Schristos
242fec65c98Schristos /* Save and zero out user instructions. */
243fec65c98Schristos instr = p->fts_instr;
244fec65c98Schristos p->fts_instr = FTS_NOINSTR;
245fec65c98Schristos
246fec65c98Schristos /* Directory in pre-order. */
247fec65c98Schristos if (p->fts_info == FTS_D) {
248fec65c98Schristos /* If skipped or crossed mount point, do post-order visit. */
249fec65c98Schristos if (instr == FTS_SKIP ||
250fec65c98Schristos (ISSET(FTS_XDEV) && p->fts_dev != sp->fts_dev)) {
251fec65c98Schristos if (sp->fts_child) {
252fec65c98Schristos fts_lfree(sp->fts_child);
253fec65c98Schristos sp->fts_child = NULL;
254fec65c98Schristos }
255fec65c98Schristos p->fts_info = FTS_DP;
256fec65c98Schristos return (p);
257fec65c98Schristos }
258fec65c98Schristos
259fec65c98Schristos /*
260fec65c98Schristos * If haven't read do so. If the read fails, fts_build sets
261fec65c98Schristos * FTS_STOP or the fts_info field of the node.
262fec65c98Schristos */
263fec65c98Schristos if (sp->fts_child) {
2649ff1f2acSchristos /* nothing */
265fec65c98Schristos } else if ((sp->fts_child = fts_build(sp)) == NULL) {
266fec65c98Schristos if (ISSET(FTS_STOP))
267fec65c98Schristos return (NULL);
268fec65c98Schristos return (p);
269fec65c98Schristos }
270fec65c98Schristos p = sp->fts_child;
271fec65c98Schristos sp->fts_child = NULL;
272fec65c98Schristos goto name;
273fec65c98Schristos }
274fec65c98Schristos
275fec65c98Schristos /* Move to the next node on this level. */
276fec65c98Schristos next: tmp = p;
277fec65c98Schristos if ((p = p->fts_link)) {
278fec65c98Schristos free(tmp);
279fec65c98Schristos
280fec65c98Schristos /*
281fec65c98Schristos * If reached the top, return to the original directory (or
282fec65c98Schristos * the root of the tree), and load the paths for the next root.
283fec65c98Schristos */
284fec65c98Schristos if (p->fts_level == FTS_ROOTLEVEL) {
285fec65c98Schristos fts_load(sp, p);
286fec65c98Schristos return (sp->fts_cur = p);
287fec65c98Schristos }
288fec65c98Schristos
289fec65c98Schristos /*
290fec65c98Schristos * User may have called fts_set on the node. If skipped,
291fec65c98Schristos * ignore. If followed, get a file descriptor so we can
292fec65c98Schristos * get back if necessary.
293fec65c98Schristos */
294fec65c98Schristos if (p->fts_instr == FTS_SKIP)
295fec65c98Schristos goto next;
296fec65c98Schristos
297fec65c98Schristos name: t = sp->fts_path + NAPPEND(p->fts_parent);
298fec65c98Schristos *t++ = '/';
299fec65c98Schristos memmove(t, p->fts_name, p->fts_namelen + 1);
300fec65c98Schristos return (sp->fts_cur = p);
301fec65c98Schristos }
302fec65c98Schristos
303fec65c98Schristos /* Move up to the parent node. */
304fec65c98Schristos p = tmp->fts_parent;
305fec65c98Schristos free(tmp);
306fec65c98Schristos
307fec65c98Schristos if (p->fts_level == FTS_ROOTPARENTLEVEL) {
308fec65c98Schristos /*
309fec65c98Schristos * Done; free everything up and set errno to 0 so the user
310fec65c98Schristos * can distinguish between error and EOF.
311fec65c98Schristos */
312fec65c98Schristos free(p);
313fec65c98Schristos errno = 0;
314fec65c98Schristos return (sp->fts_cur = NULL);
315fec65c98Schristos }
316fec65c98Schristos
317fec65c98Schristos /* NUL terminate the pathname. */
318fec65c98Schristos sp->fts_path[p->fts_pathlen] = '\0';
319fec65c98Schristos
320fec65c98Schristos p->fts_info = p->fts_errno ? FTS_ERR : FTS_DP;
321fec65c98Schristos return (sp->fts_cur = p);
322fec65c98Schristos }
323fec65c98Schristos
324fec65c98Schristos /*
325fec65c98Schristos * Fts_set takes the stream as an argument although it's not used in this
326fec65c98Schristos * implementation; it would be necessary if anyone wanted to add global
327fec65c98Schristos * semantics to fts using fts_set. An error return is allowed for similar
328fec65c98Schristos * reasons.
329fec65c98Schristos */
330fec65c98Schristos int
fts_set(FTS * sp,FTSENT * p,int instr)331fec65c98Schristos fts_set(FTS *sp, FTSENT *p, int instr)
332fec65c98Schristos {
333fec65c98Schristos if (instr && instr != FTS_NOINSTR && instr != FTS_SKIP) {
334fec65c98Schristos errno = EINVAL;
335fec65c98Schristos return (1);
336fec65c98Schristos }
337fec65c98Schristos p->fts_instr = instr;
338fec65c98Schristos return (0);
339fec65c98Schristos }
340fec65c98Schristos
341fec65c98Schristos /*
342fec65c98Schristos * This is the tricky part -- do not casually change *anything* in here. The
343fec65c98Schristos * idea is to build the linked list of entries that are used by fts_children
344fec65c98Schristos * and fts_read. There are lots of special cases.
345fec65c98Schristos *
346fec65c98Schristos * The real slowdown in walking the tree is the stat calls. If FTS_NOSTAT is
347fec65c98Schristos * set and it's a physical walk (so that symbolic links can't be directories),
348fec65c98Schristos * we can do things quickly. First, if it's a 4.4BSD file system, the type
349fec65c98Schristos * of the file is in the directory entry. Otherwise, we assume that the number
350fec65c98Schristos * of subdirectories in a node is equal to the number of links to the parent.
351fec65c98Schristos * The former skips all stat calls. The latter skips stat calls in any leaf
352fec65c98Schristos * directories and for any files after the subdirectories in the directory have
353fec65c98Schristos * been found, cutting the stat calls by about 2/3.
354fec65c98Schristos */
355fec65c98Schristos static FTSENT *
fts_build(FTS * sp)356fec65c98Schristos fts_build(FTS *sp)
357fec65c98Schristos {
358fec65c98Schristos struct dirent *dp;
359fec65c98Schristos FTSENT *p, *head;
360fec65c98Schristos FTSENT *cur, *tail;
361fec65c98Schristos DIR *dirp;
362fec65c98Schristos void *oldaddr;
363fec65c98Schristos size_t dlen, len, maxlen;
3649ff1f2acSchristos int nitems, level, doadjust;
365fec65c98Schristos int saved_errno;
366fec65c98Schristos char *cp;
367fec65c98Schristos
368fec65c98Schristos /* Set current node pointer. */
369fec65c98Schristos cur = sp->fts_cur;
370fec65c98Schristos
371fec65c98Schristos /*
372fec65c98Schristos * Open the directory for reading. If this fails, we're done.
373fec65c98Schristos * If being called from fts_read, set the fts_info field.
374fec65c98Schristos */
375fec65c98Schristos if ((dirp = opendir(cur->fts_accpath)) == NULL) {
376fec65c98Schristos cur->fts_info = FTS_DNR;
377fec65c98Schristos cur->fts_errno = errno;
378fec65c98Schristos return (NULL);
379fec65c98Schristos }
380fec65c98Schristos
381fec65c98Schristos /*
382fec65c98Schristos * Figure out the max file name length that can be stored in the
383fec65c98Schristos * current path -- the inner loop allocates more path as necessary.
384fec65c98Schristos * We really wouldn't have to do the maxlen calculations here, we
385fec65c98Schristos * could do them in fts_read before returning the path, but it's a
386fec65c98Schristos * lot easier here since the length is part of the dirent structure.
387fec65c98Schristos *
388fec65c98Schristos * If not changing directories set a pointer so that can just append
389fec65c98Schristos * each new name into the path.
390fec65c98Schristos */
391fec65c98Schristos len = NAPPEND(cur);
392fec65c98Schristos cp = sp->fts_path + len;
393fec65c98Schristos *cp++ = '/';
394fec65c98Schristos len++;
395fec65c98Schristos maxlen = sp->fts_pathlen - len;
396fec65c98Schristos
397fec65c98Schristos /*
398fec65c98Schristos * fts_level is signed so we must prevent it from wrapping
399fec65c98Schristos * around to FTS_ROOTLEVEL and FTS_ROOTPARENTLEVEL.
400fec65c98Schristos */
401fec65c98Schristos level = cur->fts_level;
402fec65c98Schristos if (level < FTS_MAXLEVEL)
403fec65c98Schristos level++;
404fec65c98Schristos
405fec65c98Schristos /* Read the directory, attaching each entry to the `link' pointer. */
406fec65c98Schristos doadjust = 0;
407fec65c98Schristos for (head = tail = NULL, nitems = 0; dirp && (dp = readdir(dirp));) {
408fec65c98Schristos if (ISDOT(dp->d_name))
409fec65c98Schristos continue;
410fec65c98Schristos
411fec65c98Schristos #if HAVE_DIRENT_NAMLEN
412fec65c98Schristos dlen = dp->d_namlen;
413fec65c98Schristos #else
414fec65c98Schristos dlen = strlen(dp->d_name);
415fec65c98Schristos #endif
416fec65c98Schristos
417fec65c98Schristos if (!(p = fts_alloc(sp, dp->d_name, dlen)))
418fec65c98Schristos goto mem1;
419fec65c98Schristos if (dlen >= maxlen) { /* include space for NUL */
420fec65c98Schristos oldaddr = sp->fts_path;
421fec65c98Schristos if (fts_palloc(sp, dlen + len + 1)) {
422fec65c98Schristos /*
423fec65c98Schristos * No more memory for path or structures. Save
424fec65c98Schristos * errno, free up the current structure and the
425fec65c98Schristos * structures already allocated.
426fec65c98Schristos */
427fec65c98Schristos mem1: saved_errno = errno;
428fec65c98Schristos free(p);
429fec65c98Schristos fts_lfree(head);
430fec65c98Schristos (void)closedir(dirp);
431fec65c98Schristos cur->fts_info = FTS_ERR;
432fec65c98Schristos SET(FTS_STOP);
433fec65c98Schristos errno = saved_errno;
434fec65c98Schristos return (NULL);
435fec65c98Schristos }
436fec65c98Schristos /* Did realloc() change the pointer? */
437fec65c98Schristos if (oldaddr != sp->fts_path) {
438fec65c98Schristos doadjust = 1;
439fec65c98Schristos cp = sp->fts_path + len;
440fec65c98Schristos }
441fec65c98Schristos maxlen = sp->fts_pathlen - len;
442fec65c98Schristos }
443fec65c98Schristos
444fec65c98Schristos p->fts_level = level;
445fec65c98Schristos p->fts_parent = sp->fts_cur;
446fec65c98Schristos p->fts_pathlen = len + dlen;
447fec65c98Schristos if (p->fts_pathlen < len) {
448fec65c98Schristos /*
449fec65c98Schristos * If we wrap, free up the current structure and
450fec65c98Schristos * the structures already allocated, then error
451fec65c98Schristos * out with ENAMETOOLONG.
452fec65c98Schristos */
453fec65c98Schristos free(p);
454fec65c98Schristos fts_lfree(head);
455fec65c98Schristos (void)closedir(dirp);
456fec65c98Schristos cur->fts_info = FTS_ERR;
457fec65c98Schristos SET(FTS_STOP);
458fec65c98Schristos errno = ENAMETOOLONG;
459fec65c98Schristos return (NULL);
460fec65c98Schristos }
461fec65c98Schristos
462fec65c98Schristos /* Build a file name for fts_stat to stat. */
463fec65c98Schristos p->fts_accpath = p->fts_path;
464fec65c98Schristos memmove(cp, p->fts_name, p->fts_namelen + 1);
465fec65c98Schristos /* Stat it. */
466fec65c98Schristos p->fts_info = fts_stat(sp, p);
467fec65c98Schristos
468fec65c98Schristos /* We walk in directory order so "ls -f" doesn't get upset. */
469fec65c98Schristos p->fts_link = NULL;
470fec65c98Schristos if (head == NULL)
471fec65c98Schristos head = tail = p;
472fec65c98Schristos else {
473fec65c98Schristos tail->fts_link = p;
474fec65c98Schristos tail = p;
475fec65c98Schristos }
476fec65c98Schristos ++nitems;
477fec65c98Schristos }
478fec65c98Schristos if (dirp)
479fec65c98Schristos (void)closedir(dirp);
480fec65c98Schristos
481fec65c98Schristos /*
482fec65c98Schristos * If realloc() changed the address of the path, adjust the
483fec65c98Schristos * addresses for the rest of the tree and the dir list.
484fec65c98Schristos */
485fec65c98Schristos if (doadjust)
486fec65c98Schristos fts_padjust(sp, head);
487fec65c98Schristos
488fec65c98Schristos /*
489fec65c98Schristos * If not changing directories, reset the path back to original
490fec65c98Schristos * state.
491fec65c98Schristos */
492fec65c98Schristos if (len == sp->fts_pathlen || nitems == 0)
493fec65c98Schristos --cp;
494fec65c98Schristos *cp = '\0';
495fec65c98Schristos
496fec65c98Schristos /* If didn't find anything, return NULL. */
497fec65c98Schristos if (!nitems) {
498fec65c98Schristos cur->fts_info = FTS_DP;
499fec65c98Schristos return (NULL);
500fec65c98Schristos }
501*9508192eSchristos
502*9508192eSchristos /* Sort the entries. */
503*9508192eSchristos if (sp->fts_compar && nitems > 1)
504*9508192eSchristos head = fts_sort(sp, head, nitems);
505fec65c98Schristos return (head);
506fec65c98Schristos }
507fec65c98Schristos
508fec65c98Schristos static unsigned short
fts_stat(FTS * sp,FTSENT * p)509fec65c98Schristos fts_stat(FTS *sp, FTSENT *p)
510fec65c98Schristos {
511fec65c98Schristos FTSENT *t;
512fec65c98Schristos dev_t dev;
513fec65c98Schristos ino_t ino;
514fec65c98Schristos struct stat *sbp;
515fec65c98Schristos
516fec65c98Schristos /* If user needs stat info, stat buffer already allocated. */
517fec65c98Schristos sbp = p->fts_statp;
518fec65c98Schristos
519fec65c98Schristos if (lstat(p->fts_accpath, sbp)) {
520fec65c98Schristos p->fts_errno = errno;
521fec65c98Schristos memset(sbp, 0, sizeof(struct stat));
522fec65c98Schristos return (FTS_NS);
523fec65c98Schristos }
524fec65c98Schristos
525fec65c98Schristos if (S_ISDIR(sbp->st_mode)) {
526fec65c98Schristos /*
527fec65c98Schristos * Set the device/inode. Used to find cycles and check for
528fec65c98Schristos * crossing mount points. Also remember the link count, used
529fec65c98Schristos * in fts_build to limit the number of stat calls. It is
530fec65c98Schristos * understood that these fields are only referenced if fts_info
531fec65c98Schristos * is set to FTS_D.
532fec65c98Schristos */
533fec65c98Schristos dev = p->fts_dev = sbp->st_dev;
534fec65c98Schristos ino = p->fts_ino = sbp->st_ino;
535fec65c98Schristos p->fts_nlink = sbp->st_nlink;
536fec65c98Schristos
537fec65c98Schristos if (ISDOT(p->fts_name))
538fec65c98Schristos return (FTS_DOT);
539fec65c98Schristos
540fec65c98Schristos /*
541fec65c98Schristos * Cycle detection is done by brute force when the directory
542fec65c98Schristos * is first encountered. If the tree gets deep enough or the
543fec65c98Schristos * number of symbolic links to directories is high enough,
544fec65c98Schristos * something faster might be worthwhile.
545fec65c98Schristos */
546fec65c98Schristos for (t = p->fts_parent;
547fec65c98Schristos t->fts_level >= FTS_ROOTLEVEL; t = t->fts_parent)
548fec65c98Schristos if (ino == t->fts_ino && dev == t->fts_dev) {
549fec65c98Schristos p->fts_cycle = t;
550fec65c98Schristos return (FTS_DC);
551fec65c98Schristos }
552fec65c98Schristos return (FTS_D);
553fec65c98Schristos }
554fec65c98Schristos if (S_ISLNK(sbp->st_mode))
555fec65c98Schristos return (FTS_SL);
556fec65c98Schristos if (S_ISREG(sbp->st_mode))
557fec65c98Schristos return (FTS_F);
558fec65c98Schristos return (FTS_DEFAULT);
559fec65c98Schristos }
560fec65c98Schristos
561fec65c98Schristos static FTSENT *
fts_sort(FTS * sp,FTSENT * head,int nitems)562*9508192eSchristos fts_sort(FTS *sp, FTSENT *head, int nitems)
563*9508192eSchristos {
564*9508192eSchristos FTSENT **ap, *p;
565*9508192eSchristos
566*9508192eSchristos /*
567*9508192eSchristos * Construct an array of pointers to the structures and call qsort(3).
568*9508192eSchristos * Reassemble the array in the order returned by qsort. If unable to
569*9508192eSchristos * sort for memory reasons, return the directory entries in their
570*9508192eSchristos * current order. Allocate enough space for the current needs plus
571*9508192eSchristos * 40 so don't realloc one entry at a time.
572*9508192eSchristos */
573*9508192eSchristos if (nitems > sp->fts_nitems) {
574*9508192eSchristos struct _ftsent **a;
575*9508192eSchristos
576*9508192eSchristos sp->fts_nitems = nitems + 40;
577*9508192eSchristos if ((a = reallocarray(sp->fts_array,
578*9508192eSchristos sp->fts_nitems, sizeof(FTSENT *))) == NULL) {
579*9508192eSchristos free(sp->fts_array);
580*9508192eSchristos sp->fts_array = NULL;
581*9508192eSchristos sp->fts_nitems = 0;
582*9508192eSchristos return (head);
583*9508192eSchristos }
584*9508192eSchristos sp->fts_array = a;
585*9508192eSchristos }
586*9508192eSchristos for (ap = sp->fts_array, p = head; p; p = p->fts_link)
587*9508192eSchristos *ap++ = p;
588*9508192eSchristos qsort(sp->fts_array, nitems, sizeof(FTSENT *), sp->fts_compar);
589*9508192eSchristos for (head = *(ap = sp->fts_array); --nitems; ++ap)
590*9508192eSchristos ap[0]->fts_link = ap[1];
591*9508192eSchristos ap[0]->fts_link = NULL;
592*9508192eSchristos return (head);
593*9508192eSchristos }
594*9508192eSchristos
595*9508192eSchristos static FTSENT *
fts_alloc(FTS * sp,const char * name,size_t namelen)596fec65c98Schristos fts_alloc(FTS *sp, const char *name, size_t namelen)
597fec65c98Schristos {
598fec65c98Schristos FTSENT *p;
599fec65c98Schristos size_t len;
600fec65c98Schristos
601fec65c98Schristos len = sizeof(FTSENT) + namelen;
602fec65c98Schristos if ((p = calloc(1, len)) == NULL)
603fec65c98Schristos return (NULL);
604fec65c98Schristos
605fec65c98Schristos p->fts_path = sp->fts_path;
606fec65c98Schristos p->fts_namelen = namelen;
607fec65c98Schristos p->fts_instr = FTS_NOINSTR;
608fec65c98Schristos p->fts_statp = malloc(sizeof(struct stat));
609fec65c98Schristos if (p->fts_statp == NULL) {
610fec65c98Schristos free(p);
611fec65c98Schristos return (NULL);
612fec65c98Schristos }
613fec65c98Schristos memcpy(p->fts_name, name, namelen);
614fec65c98Schristos
615fec65c98Schristos return (p);
616fec65c98Schristos }
617fec65c98Schristos
618fec65c98Schristos static void
fts_lfree(FTSENT * head)619fec65c98Schristos fts_lfree(FTSENT *head)
620fec65c98Schristos {
621fec65c98Schristos FTSENT *p;
622fec65c98Schristos
623fec65c98Schristos /* Free a linked list of structures. */
624fec65c98Schristos while ((p = head)) {
625fec65c98Schristos head = head->fts_link;
626fec65c98Schristos free(p);
627fec65c98Schristos }
628fec65c98Schristos }
629fec65c98Schristos
630fec65c98Schristos /*
631fec65c98Schristos * Allow essentially unlimited paths; find, rm, ls should all work on any tree.
632fec65c98Schristos * Most systems will allow creation of paths much longer than PATH_MAX, even
633fec65c98Schristos * though the kernel won't resolve them. Add the size (not just what's needed)
634fec65c98Schristos * plus 256 bytes so don't realloc the path 2 bytes at a time.
635fec65c98Schristos */
636fec65c98Schristos static int
fts_palloc(FTS * sp,size_t more)637fec65c98Schristos fts_palloc(FTS *sp, size_t more)
638fec65c98Schristos {
639fec65c98Schristos char *p;
640fec65c98Schristos
641fec65c98Schristos /*
642fec65c98Schristos * Check for possible wraparound.
643fec65c98Schristos */
644fec65c98Schristos more += 256;
645fec65c98Schristos if (sp->fts_pathlen + more < sp->fts_pathlen) {
646fec65c98Schristos free(sp->fts_path);
647fec65c98Schristos sp->fts_path = NULL;
648fec65c98Schristos errno = ENAMETOOLONG;
649fec65c98Schristos return (1);
650fec65c98Schristos }
651fec65c98Schristos sp->fts_pathlen += more;
652fec65c98Schristos p = realloc(sp->fts_path, sp->fts_pathlen);
653fec65c98Schristos if (p == NULL) {
654fec65c98Schristos free(sp->fts_path);
655fec65c98Schristos sp->fts_path = NULL;
656fec65c98Schristos return (1);
657fec65c98Schristos }
658fec65c98Schristos sp->fts_path = p;
659fec65c98Schristos return (0);
660fec65c98Schristos }
661fec65c98Schristos
662fec65c98Schristos /*
663fec65c98Schristos * When the path is realloc'd, have to fix all of the pointers in structures
664fec65c98Schristos * already returned.
665fec65c98Schristos */
666fec65c98Schristos static void
fts_padjust(FTS * sp,FTSENT * head)667fec65c98Schristos fts_padjust(FTS *sp, FTSENT *head)
668fec65c98Schristos {
669fec65c98Schristos FTSENT *p;
670fec65c98Schristos char *addr = sp->fts_path;
671fec65c98Schristos
672fec65c98Schristos #define ADJUST(p) { \
673fec65c98Schristos if ((p)->fts_accpath != (p)->fts_name) { \
674fec65c98Schristos (p)->fts_accpath = \
675fec65c98Schristos (char *)addr + ((p)->fts_accpath - (p)->fts_path); \
676fec65c98Schristos } \
677fec65c98Schristos (p)->fts_path = addr; \
678fec65c98Schristos }
679fec65c98Schristos /* Adjust the current set of children. */
680fec65c98Schristos for (p = sp->fts_child; p; p = p->fts_link)
681fec65c98Schristos ADJUST(p);
682fec65c98Schristos
683fec65c98Schristos /* Adjust the rest of the tree, including the current level. */
684fec65c98Schristos for (p = head; p->fts_level >= FTS_ROOTLEVEL;) {
685fec65c98Schristos ADJUST(p);
686fec65c98Schristos p = p->fts_link ? p->fts_link : p->fts_parent;
687fec65c98Schristos }
688fec65c98Schristos }
689fec65c98Schristos
690fec65c98Schristos static size_t
fts_maxarglen(char * const * argv)691fec65c98Schristos fts_maxarglen(char * const *argv)
692fec65c98Schristos {
693fec65c98Schristos size_t len, max;
694fec65c98Schristos
695fec65c98Schristos for (max = 0; *argv; ++argv)
696fec65c98Schristos if ((len = strlen(*argv)) > max)
697fec65c98Schristos max = len;
698fec65c98Schristos return (max + 1);
699fec65c98Schristos }
700fec65c98Schristos
701fec65c98Schristos #endif
702