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