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