| Forward port the sources to be able to compile with c99/gcc5 |
| |
| Signed-off-by: Khem Raj <raj.khem@gmail.com> |
| Upstream-Status: Inappropriate |
| |
| Index: fts/fts.c |
| =================================================================== |
| --- fts.orig/fts.c |
| +++ fts/fts.c |
| @@ -51,16 +51,6 @@ static char sccsid[] = "@(#)fts.c 8.6 (B |
| #include <string.h> |
| #include <unistd.h> |
| |
| -static FTSENT *fts_alloc __P(FTS *, char *, int); |
| -static FTSENT *fts_build __P(FTS *, int); |
| -static void fts_lfree __P(FTSENT *); |
| -static void fts_load __P(FTS *, FTSENT *); |
| -static size_t fts_maxarglen __P(char * const *); |
| -static void fts_padjust __P(FTS *, void *); |
| -static int fts_palloc __P(FTS *, size_t); |
| -static FTSENT *fts_sort __P(FTS *, FTSENT *, int); |
| -static u_short fts_stat __P(FTS *, struct dirent *, FTSENT *, int); |
| - |
| #define ISDOT(a) (a[0] == '.' && (!a[1] || a[1] == '.' && !a[2])) |
| |
| #define ISSET(opt) (sp->fts_options & opt) |
| @@ -73,119 +63,16 @@ static u_short fts_stat __P(FTS *, stru |
| #define BCHILD 1 /* fts_children */ |
| #define BNAMES 2 /* fts_children, names only */ |
| #define BREAD 3 /* fts_read */ |
| - |
| -FTS * |
| -fts_open(argv, options, compar) |
| - char * const *argv; |
| - register int options; |
| - int (*compar)(); |
| -{ |
| - register FTS *sp; |
| - register FTSENT *p, *root; |
| - register int nitems; |
| - FTSENT *parent, *tmp; |
| - int len; |
| - |
| - /* Options check. */ |
| - if (options & ~FTS_OPTIONMASK) { |
| - errno = EINVAL; |
| - return (NULL); |
| - } |
| - |
| - /* Allocate/initialize the stream */ |
| - if ((sp = malloc((u_int)sizeof(FTS))) == NULL) |
| - return (NULL); |
| - memset(sp, 0, sizeof(FTS)); |
| - sp->fts_compar = compar; |
| - sp->fts_options = options; |
| - |
| - /* Logical walks turn on NOCHDIR; symbolic links are too hard. */ |
| - if (ISSET(FTS_LOGICAL)) |
| - SET(FTS_NOCHDIR); |
| - |
| - /* |
| - * Start out with 1K of path space, and enough, in any case, |
| - * to hold the user's paths. |
| - */ |
| - if (fts_palloc(sp, MAX(fts_maxarglen(argv), MAXPATHLEN))) |
| - goto mem1; |
| - |
| - /* Allocate/initialize root's parent. */ |
| - if ((parent = fts_alloc(sp, "", 0)) == NULL) |
| - goto mem2; |
| - parent->fts_level = FTS_ROOTPARENTLEVEL; |
| - |
| - /* Allocate/initialize root(s). */ |
| - for (root = NULL, nitems = 0; *argv; ++argv, ++nitems) { |
| - /* Don't allow zero-length paths. */ |
| - if ((len = strlen(*argv)) == 0) { |
| - errno = EINVAL; |
| - goto mem3; |
| - } |
| - |
| - p = fts_alloc(sp, *argv, len); |
| - p->fts_level = FTS_ROOTLEVEL; |
| - p->fts_parent = parent; |
| - p->fts_accpath = p->fts_name; |
| - p->fts_info = fts_stat(sp, NULL, p, ISSET(FTS_COMFOLLOW)); |
| - |
| - /* Command-line "." and ".." are real directories. */ |
| - if (p->fts_info == FTS_DOT) |
| - p->fts_info = FTS_D; |
| - |
| - /* |
| - * If comparison routine supplied, traverse in sorted |
| - * order; otherwise traverse in the order specified. |
| - */ |
| - if (compar) { |
| - p->fts_link = root; |
| - root = p; |
| - } else { |
| - p->fts_link = NULL; |
| - if (root == NULL) |
| - tmp = root = p; |
| - else { |
| - tmp->fts_link = p; |
| - tmp = p; |
| - } |
| - } |
| - } |
| - if (compar && nitems > 1) |
| - root = fts_sort(sp, root, nitems); |
| - |
| - /* |
| - * Allocate a dummy pointer and make fts_read think that we've just |
| - * finished the node before the root(s); set p->fts_info to FTS_INIT |
| - * so that everything about the "current" node is ignored. |
| - */ |
| - if ((sp->fts_cur = fts_alloc(sp, "", 0)) == NULL) |
| - goto mem3; |
| - sp->fts_cur->fts_link = root; |
| - sp->fts_cur->fts_info = FTS_INIT; |
| - |
| - /* |
| - * If using chdir(2), grab a file descriptor pointing to dot to insure |
| - * that we can get back here; this could be avoided for some paths, |
| - * but almost certainly not worth the effort. Slashes, symbolic links, |
| - * and ".." are all fairly nasty problems. Note, if we can't get the |
| - * descriptor we run anyway, just more slowly. |
| - */ |
| - if (!ISSET(FTS_NOCHDIR) && (sp->fts_rfd = open(".", O_RDONLY, 0)) < 0) |
| - SET(FTS_NOCHDIR); |
| - |
| - return (sp); |
| - |
| -mem3: fts_lfree(root); |
| - free(parent); |
| -mem2: free(sp->fts_path); |
| -mem1: free(sp); |
| - return (NULL); |
| -} |
| +/* |
| + * Special case a root of "/" so that slashes aren't appended which would |
| + * cause paths to be written as "//foo". |
| + */ |
| +#define NAPPEND(p) \ |
| + (p->fts_level == FTS_ROOTLEVEL && p->fts_pathlen == 1 && \ |
| + p->fts_path[0] == '/' ? 0 : p->fts_pathlen) |
| |
| static void |
| -fts_load(sp, p) |
| - FTS *sp; |
| - register FTSENT *p; |
| +fts_load(FTS *sp, register FTSENT *p) |
| { |
| register int len; |
| register char *cp; |
| @@ -208,332 +95,214 @@ fts_load(sp, p) |
| sp->fts_dev = p->fts_dev; |
| } |
| |
| -int |
| -fts_close(sp) |
| - FTS *sp; |
| +static void |
| +fts_lfree(register FTSENT *head) |
| { |
| - register FTSENT *freep, *p; |
| - int saved_errno; |
| + register FTSENT *p; |
| |
| - /* |
| - * This still works if we haven't read anything -- the dummy structure |
| - * points to the root list, so we step through to the end of the root |
| - * list which has a valid parent pointer. |
| - */ |
| - if (sp->fts_cur) { |
| - for (p = sp->fts_cur; p->fts_level >= FTS_ROOTLEVEL;) { |
| - freep = p; |
| - p = p->fts_link ? p->fts_link : p->fts_parent; |
| - free(freep); |
| - } |
| + /* Free a linked list of structures. */ |
| + while (p = head) { |
| + head = head->fts_link; |
| free(p); |
| } |
| +} |
| |
| - /* Free up child linked list, sort array, path buffer. */ |
| - if (sp->fts_child) |
| - fts_lfree(sp->fts_child); |
| - if (sp->fts_array) |
| - free(sp->fts_array); |
| - free(sp->fts_path); |
| +static size_t |
| +fts_maxarglen(char * const *argv) |
| +{ |
| + size_t len, max; |
| |
| - /* Return to original directory, save errno if necessary. */ |
| - if (!ISSET(FTS_NOCHDIR)) { |
| - saved_errno = fchdir(sp->fts_rfd) ? errno : 0; |
| - (void)close(sp->fts_rfd); |
| - } |
| + for (max = 0; *argv; ++argv) |
| + if ((len = strlen(*argv)) > max) |
| + max = len; |
| + return (max); |
| +} |
| |
| - /* Free up the stream pointer. */ |
| - free(sp); |
| |
| - /* Set errno and return. */ |
| - if (!ISSET(FTS_NOCHDIR) && saved_errno) { |
| - errno = saved_errno; |
| - return (-1); |
| +/* |
| + * When the path is realloc'd, have to fix all of the pointers in structures |
| + * already returned. |
| + */ |
| +static void |
| +fts_padjust(FTS *sp, void *addr) |
| +{ |
| + FTSENT *p; |
| + |
| +#define ADJUST(p) { \ |
| + (p)->fts_accpath = \ |
| + (char *)addr + ((p)->fts_accpath - (p)->fts_path); \ |
| + (p)->fts_path = addr; \ |
| +} |
| + /* Adjust the current set of children. */ |
| + for (p = sp->fts_child; p; p = p->fts_link) |
| + ADJUST(p); |
| + |
| + /* Adjust the rest of the tree. */ |
| + for (p = sp->fts_cur; p->fts_level >= FTS_ROOTLEVEL;) { |
| + ADJUST(p); |
| + p = p->fts_link ? p->fts_link : p->fts_parent; |
| } |
| - return (0); |
| } |
| |
| /* |
| - * Special case a root of "/" so that slashes aren't appended which would |
| - * cause paths to be written as "//foo". |
| + * Allow essentially unlimited paths; find, rm, ls should all work on any tree. |
| + * Most systems will allow creation of paths much longer than MAXPATHLEN, even |
| + * though the kernel won't resolve them. Add the size (not just what's needed) |
| + * plus 256 bytes so don't realloc the path 2 bytes at a time. |
| */ |
| -#define NAPPEND(p) \ |
| - (p->fts_level == FTS_ROOTLEVEL && p->fts_pathlen == 1 && \ |
| - p->fts_path[0] == '/' ? 0 : p->fts_pathlen) |
| +static int |
| +fts_palloc(FTS *sp, size_t more) |
| +{ |
| + sp->fts_pathlen += more + 256; |
| + sp->fts_path = realloc(sp->fts_path, (size_t)sp->fts_pathlen); |
| + return (sp->fts_path == NULL); |
| +} |
| |
| -FTSENT * |
| -fts_read(sp) |
| - register FTS *sp; |
| +static FTSENT * |
| +fts_alloc(FTS *sp, char *name, register int namelen) |
| { |
| - register FTSENT *p, *tmp; |
| - register int instr; |
| - register char *t; |
| - int saved_errno; |
| + register FTSENT *p; |
| + size_t len; |
| |
| - /* If finished or unrecoverable error, return NULL. */ |
| - if (sp->fts_cur == NULL || ISSET(FTS_STOP)) |
| + /* |
| + * The file name is a variable length array and no stat structure is |
| + * necessary if the user has set the nostat bit. Allocate the FTSENT |
| + * structure, the file name and the stat structure in one chunk, but |
| + * be careful that the stat structure is reasonably aligned. Since the |
| + * fts_name field is declared to be of size 1, the fts_name pointer is |
| + * namelen + 2 before the first possible address of the stat structure. |
| + */ |
| + len = sizeof(FTSENT) + namelen; |
| + if (!ISSET(FTS_NOSTAT)) |
| + len += sizeof(struct stat) + ALIGNBYTES; |
| + if ((p = malloc(len)) == NULL) |
| return (NULL); |
| |
| - /* Set current node pointer. */ |
| - p = sp->fts_cur; |
| + /* Copy the name plus the trailing NULL. */ |
| + memmove(p->fts_name, name, namelen + 1); |
| |
| - /* Save and zero out user instructions. */ |
| - instr = p->fts_instr; |
| + if (!ISSET(FTS_NOSTAT)) |
| + p->fts_statp = (struct stat *)ALIGN(p->fts_name + namelen + 2); |
| + p->fts_namelen = namelen; |
| + p->fts_path = sp->fts_path; |
| + p->fts_errno = 0; |
| + p->fts_flags = 0; |
| p->fts_instr = FTS_NOINSTR; |
| + p->fts_number = 0; |
| + p->fts_pointer = NULL; |
| + return (p); |
| +} |
| |
| - /* Any type of file may be re-visited; re-stat and re-turn. */ |
| - if (instr == FTS_AGAIN) { |
| - p->fts_info = fts_stat(sp, NULL, p, 0); |
| - return (p); |
| - } |
| |
| +static u_short |
| +fts_stat(FTS *sp, register FTSENT *p, struct dirent *dp, int follow) |
| +{ |
| + register FTSENT *t; |
| + register dev_t dev; |
| + register ino_t ino; |
| + struct stat *sbp, sb; |
| + int saved_errno; |
| + |
| + /* If user needs stat info, stat buffer already allocated. */ |
| + sbp = ISSET(FTS_NOSTAT) ? &sb : p->fts_statp; |
| + |
| +#ifdef S_IFWHT |
| /* |
| - * Following a symlink -- SLNONE test allows application to see |
| - * SLNONE and recover. If indirecting through a symlink, have |
| - * keep a pointer to current location. If unable to get that |
| - * pointer, follow fails. |
| - */ |
| - if (instr == FTS_FOLLOW && |
| - (p->fts_info == FTS_SL || p->fts_info == FTS_SLNONE)) { |
| - p->fts_info = fts_stat(sp, NULL, p, 1); |
| - if (p->fts_info == FTS_D && !ISSET(FTS_NOCHDIR)) |
| - if ((p->fts_symfd = open(".", O_RDONLY, 0)) < 0) { |
| - p->fts_errno = errno; |
| - p->fts_info = FTS_ERR; |
| - } else |
| - p->fts_flags |= FTS_SYMFOLLOW; |
| - return (p); |
| + * Whited-out files don't really exist. However, there's stat(2) file |
| + * mask for them, so we set it so that programs (i.e., find) don't have |
| + * to test FTS_W separately from other file types. |
| + */ |
| + if (dp != NULL && dp->d_type == DT_WHT) { |
| + memset(sbp, 0, sizeof(struct stat)); |
| + sbp->st_mode = S_IFWHT; |
| + return (FTS_W); |
| } |
| - |
| - /* Directory in pre-order. */ |
| - if (p->fts_info == FTS_D) { |
| - /* If skipped or crossed mount point, do post-order visit. */ |
| - if (instr == FTS_SKIP || |
| - ISSET(FTS_XDEV) && p->fts_dev != sp->fts_dev) { |
| - if (p->fts_flags & FTS_SYMFOLLOW) |
| - (void)close(p->fts_symfd); |
| - if (sp->fts_child) { |
| - fts_lfree(sp->fts_child); |
| - sp->fts_child = NULL; |
| - } |
| - p->fts_info = FTS_DP; |
| - return (p); |
| - } |
| - |
| - /* Rebuild if only read the names and now traversing. */ |
| - if (sp->fts_child && sp->fts_options & FTS_NAMEONLY) { |
| - sp->fts_options &= ~FTS_NAMEONLY; |
| - fts_lfree(sp->fts_child); |
| - sp->fts_child = NULL; |
| - } |
| - |
| - /* |
| - * Cd to the subdirectory. |
| - * |
| - * If have already read and now fail to chdir, whack the list |
| - * to make the names come out right, and set the parent errno |
| - * so the application will eventually get an error condition. |
| - * Set the FTS_DONTCHDIR flag so that when we logically change |
| - * directories back to the parent we don't do a chdir. |
| - * |
| - * If haven't read do so. If the read fails, fts_build sets |
| - * FTS_STOP or the fts_info field of the node. |
| - */ |
| - if (sp->fts_child) { |
| - if (CHDIR(sp, p->fts_accpath)) { |
| - p->fts_errno = errno; |
| - p->fts_flags |= FTS_DONTCHDIR; |
| - for (p = sp->fts_child; p; p = p->fts_link) |
| - p->fts_accpath = |
| - p->fts_parent->fts_accpath; |
| - } |
| - } else if ((sp->fts_child = fts_build(sp, BREAD)) == NULL) { |
| - if (ISSET(FTS_STOP)) |
| - return (NULL); |
| - return (p); |
| +#endif |
| + |
| + /* |
| + * If doing a logical walk, or application requested FTS_FOLLOW, do |
| + * a stat(2). If that fails, check for a non-existent symlink. If |
| + * fail, set the errno from the stat call. |
| + */ |
| + if (ISSET(FTS_LOGICAL) || follow) { |
| + if (stat(p->fts_accpath, sbp)) { |
| + saved_errno = errno; |
| + if (!lstat(p->fts_accpath, sbp)) { |
| + errno = 0; |
| + return (FTS_SLNONE); |
| + } |
| + p->fts_errno = saved_errno; |
| + goto err; |
| } |
| - p = sp->fts_child; |
| - sp->fts_child = NULL; |
| - goto name; |
| + } else if (lstat(p->fts_accpath, sbp)) { |
| + p->fts_errno = errno; |
| +err: memset(sbp, 0, sizeof(struct stat)); |
| + return (FTS_NS); |
| } |
| |
| - /* Move to the next node on this level. */ |
| -next: tmp = p; |
| - if (p = p->fts_link) { |
| - free(tmp); |
| - |
| - /* |
| - * If reached the top, return to the original directory, and |
| - * load the paths for the next root. |
| - */ |
| - if (p->fts_level == FTS_ROOTLEVEL) { |
| - if (!ISSET(FTS_NOCHDIR) && FCHDIR(sp, sp->fts_rfd)) { |
| - SET(FTS_STOP); |
| - return (NULL); |
| - } |
| - fts_load(sp, p); |
| - return (sp->fts_cur = p); |
| - } |
| - |
| + if (S_ISDIR(sbp->st_mode)) { |
| /* |
| - * User may have called fts_set on the node. If skipped, |
| - * ignore. If followed, get a file descriptor so we can |
| - * get back if necessary. |
| + * Set the device/inode. Used to find cycles and check for |
| + * crossing mount points. Also remember the link count, used |
| + * in fts_build to limit the number of stat calls. It is |
| + * understood that these fields are only referenced if fts_info |
| + * is set to FTS_D. |
| */ |
| - if (p->fts_instr == FTS_SKIP) |
| - goto next; |
| - if (p->fts_instr == FTS_FOLLOW) { |
| - p->fts_info = fts_stat(sp, NULL, p, 1); |
| - if (p->fts_info == FTS_D && !ISSET(FTS_NOCHDIR)) |
| - if ((p->fts_symfd = |
| - open(".", O_RDONLY, 0)) < 0) { |
| - p->fts_errno = errno; |
| - p->fts_info = FTS_ERR; |
| - } else |
| - p->fts_flags |= FTS_SYMFOLLOW; |
| - p->fts_instr = FTS_NOINSTR; |
| - } |
| - |
| -name: t = sp->fts_path + NAPPEND(p->fts_parent); |
| - *t++ = '/'; |
| - memmove(t, p->fts_name, p->fts_namelen + 1); |
| - return (sp->fts_cur = p); |
| - } |
| + dev = p->fts_dev = sbp->st_dev; |
| + ino = p->fts_ino = sbp->st_ino; |
| + p->fts_nlink = sbp->st_nlink; |
| |
| - /* Move up to the parent node. */ |
| - p = tmp->fts_parent; |
| - free(tmp); |
| + if (ISDOT(p->fts_name)) |
| + return (FTS_DOT); |
| |
| - if (p->fts_level == FTS_ROOTPARENTLEVEL) { |
| /* |
| - * Done; free everything up and set errno to 0 so the user |
| - * can distinguish between error and EOF. |
| + * Cycle detection is done by brute force when the directory |
| + * is first encountered. If the tree gets deep enough or the |
| + * number of symbolic links to directories is high enough, |
| + * something faster might be worthwhile. |
| */ |
| - free(p); |
| - errno = 0; |
| - return (sp->fts_cur = NULL); |
| - } |
| - |
| - /* Nul terminate the pathname. */ |
| - sp->fts_path[p->fts_pathlen] = '\0'; |
| - |
| - /* |
| - * Return to the parent directory. If at a root node or came through |
| - * a symlink, go back through the file descriptor. Otherwise, cd up |
| - * one directory. |
| - */ |
| - if (p->fts_level == FTS_ROOTLEVEL) { |
| - if (!ISSET(FTS_NOCHDIR) && FCHDIR(sp, sp->fts_rfd)) { |
| - SET(FTS_STOP); |
| - return (NULL); |
| - } |
| - } else if (p->fts_flags & FTS_SYMFOLLOW) { |
| - if (FCHDIR(sp, p->fts_symfd)) { |
| - saved_errno = errno; |
| - (void)close(p->fts_symfd); |
| - errno = saved_errno; |
| - SET(FTS_STOP); |
| - return (NULL); |
| - } |
| - (void)close(p->fts_symfd); |
| - } else if (!(p->fts_flags & FTS_DONTCHDIR)) { |
| - if (CHDIR(sp, "..")) { |
| - SET(FTS_STOP); |
| - return (NULL); |
| - } |
| - } |
| - p->fts_info = p->fts_errno ? FTS_ERR : FTS_DP; |
| - return (sp->fts_cur = p); |
| -} |
| - |
| -/* |
| - * Fts_set takes the stream as an argument although it's not used in this |
| - * implementation; it would be necessary if anyone wanted to add global |
| - * semantics to fts using fts_set. An error return is allowed for similar |
| - * reasons. |
| - */ |
| -/* ARGSUSED */ |
| -int |
| -fts_set(sp, p, instr) |
| - FTS *sp; |
| - FTSENT *p; |
| - int instr; |
| -{ |
| - if (instr && instr != FTS_AGAIN && instr != FTS_FOLLOW && |
| - instr != FTS_NOINSTR && instr != FTS_SKIP) { |
| - errno = EINVAL; |
| - return (1); |
| + for (t = p->fts_parent; |
| + t->fts_level >= FTS_ROOTLEVEL; t = t->fts_parent) |
| + if (ino == t->fts_ino && dev == t->fts_dev) { |
| + p->fts_cycle = t; |
| + return (FTS_DC); |
| + } |
| + return (FTS_D); |
| } |
| - p->fts_instr = instr; |
| - return (0); |
| + if (S_ISLNK(sbp->st_mode)) |
| + return (FTS_SL); |
| + if (S_ISREG(sbp->st_mode)) |
| + return (FTS_F); |
| + return (FTS_DEFAULT); |
| } |
| |
| -FTSENT * |
| -fts_children(sp, instr) |
| - register FTS *sp; |
| - int instr; |
| +static FTSENT * |
| +fts_sort(FTS *sp, FTSENT *head, register int nitems) |
| { |
| - register FTSENT *p; |
| - int fd; |
| - |
| - if (instr && instr != FTS_NAMEONLY) { |
| - errno = EINVAL; |
| - return (NULL); |
| - } |
| - |
| - /* Set current node pointer. */ |
| - p = sp->fts_cur; |
| - |
| - /* |
| - * Errno set to 0 so user can distinguish empty directory from |
| - * an error. |
| - */ |
| - errno = 0; |
| - |
| - /* Fatal errors stop here. */ |
| - if (ISSET(FTS_STOP)) |
| - return (NULL); |
| - |
| - /* Return logical hierarchy of user's arguments. */ |
| - if (p->fts_info == FTS_INIT) |
| - return (p->fts_link); |
| - |
| - /* |
| - * If not a directory being visited in pre-order, stop here. Could |
| - * allow FTS_DNR, assuming the user has fixed the problem, but the |
| - * same effect is available with FTS_AGAIN. |
| - */ |
| - if (p->fts_info != FTS_D /* && p->fts_info != FTS_DNR */) |
| - return (NULL); |
| - |
| - /* Free up any previous child list. */ |
| - if (sp->fts_child) |
| - fts_lfree(sp->fts_child); |
| - |
| - if (instr == FTS_NAMEONLY) { |
| - sp->fts_options |= FTS_NAMEONLY; |
| - instr = BNAMES; |
| - } else |
| - instr = BCHILD; |
| + register FTSENT **ap, *p; |
| |
| /* |
| - * If using chdir on a relative path and called BEFORE fts_read does |
| - * its chdir to the root of a traversal, we can lose -- we need to |
| - * chdir into the subdirectory, and we don't know where the current |
| - * directory is, so we can't get back so that the upcoming chdir by |
| - * fts_read will work. |
| + * Construct an array of pointers to the structures and call qsort(3). |
| + * Reassemble the array in the order returned by qsort. If unable to |
| + * sort for memory reasons, return the directory entries in their |
| + * current order. Allocate enough space for the current needs plus |
| + * 40 so don't realloc one entry at a time. |
| */ |
| - if (p->fts_level != FTS_ROOTLEVEL || p->fts_accpath[0] == '/' || |
| - ISSET(FTS_NOCHDIR)) |
| - return (sp->fts_child = fts_build(sp, instr)); |
| - |
| - if ((fd = open(".", O_RDONLY, 0)) < 0) |
| - return (NULL); |
| - sp->fts_child = fts_build(sp, instr); |
| - if (fchdir(fd)) |
| - return (NULL); |
| - (void)close(fd); |
| - return (sp->fts_child); |
| + if (nitems > sp->fts_nitems) { |
| + sp->fts_nitems = nitems + 40; |
| + if ((sp->fts_array = realloc(sp->fts_array, |
| + (size_t)(sp->fts_nitems * sizeof(FTSENT *)))) == NULL) { |
| + sp->fts_nitems = 0; |
| + return (head); |
| + } |
| + } |
| + for (ap = sp->fts_array, p = head; p; p = p->fts_link) |
| + *ap++ = p; |
| + qsort((void *)sp->fts_array, nitems, sizeof(FTSENT *), sp->fts_compar); |
| + for (head = *(ap = sp->fts_array); --nitems; ++ap) |
| + ap[0]->fts_link = ap[1]; |
| + ap[0]->fts_link = NULL; |
| + return (head); |
| } |
| |
| /* |
| @@ -551,9 +320,7 @@ fts_children(sp, instr) |
| * been found, cutting the stat calls by about 2/3. |
| */ |
| static FTSENT * |
| -fts_build(sp, type) |
| - register FTS *sp; |
| - int type; |
| +fts_build(register FTS *sp, int type) |
| { |
| register struct dirent *dp; |
| register FTSENT *p, *head; |
| @@ -716,283 +483,479 @@ mem1: saved_errno = errno; |
| --nlinks; |
| } |
| |
| - /* We walk in directory order so "ls -f" doesn't get upset. */ |
| - p->fts_link = NULL; |
| - if (head == NULL) |
| - head = tail = p; |
| - else { |
| - tail->fts_link = p; |
| - tail = p; |
| + /* We walk in directory order so "ls -f" doesn't get upset. */ |
| + p->fts_link = NULL; |
| + if (head == NULL) |
| + head = tail = p; |
| + else { |
| + tail->fts_link = p; |
| + tail = p; |
| + } |
| + ++nitems; |
| + } |
| + (void)closedir(dirp); |
| + |
| + /* |
| + * If had to realloc the path, adjust the addresses for the rest |
| + * of the tree. |
| + */ |
| + if (adjaddr) |
| + fts_padjust(sp, adjaddr); |
| + |
| + /* |
| + * If not changing directories, reset the path back to original |
| + * state. |
| + */ |
| + if (ISSET(FTS_NOCHDIR)) { |
| + if (cp - 1 > sp->fts_path) |
| + --cp; |
| + *cp = '\0'; |
| + } |
| + |
| + /* |
| + * If descended after called from fts_children or after called from |
| + * fts_read and nothing found, get back. At the root level we use |
| + * the saved fd; if one of fts_open()'s arguments is a relative path |
| + * to an empty directory, we wind up here with no other way back. If |
| + * can't get back, we're done. |
| + */ |
| + if (descend && (type == BCHILD || !nitems) && |
| + (cur->fts_level == FTS_ROOTLEVEL ? |
| + FCHDIR(sp, sp->fts_rfd) : CHDIR(sp, ".."))) { |
| + cur->fts_info = FTS_ERR; |
| + SET(FTS_STOP); |
| + return (NULL); |
| + } |
| + |
| + /* If didn't find anything, return NULL. */ |
| + if (!nitems) { |
| + if (type == BREAD) |
| + cur->fts_info = FTS_DP; |
| + return (NULL); |
| + } |
| + |
| + /* Sort the entries. */ |
| + if (sp->fts_compar && nitems > 1) |
| + head = fts_sort(sp, head, nitems); |
| + return (head); |
| +} |
| + |
| + |
| +FTS * |
| +fts_open(char * const *argv, register int options, int (*compar)()) |
| +{ |
| + register FTS *sp; |
| + register FTSENT *p, *root; |
| + register int nitems; |
| + FTSENT *parent, *tmp; |
| + int len; |
| + |
| + /* Options check. */ |
| + if (options & ~FTS_OPTIONMASK) { |
| + errno = EINVAL; |
| + return (NULL); |
| + } |
| + |
| + /* Allocate/initialize the stream */ |
| + if ((sp = malloc((u_int)sizeof(FTS))) == NULL) |
| + return (NULL); |
| + memset(sp, 0, sizeof(FTS)); |
| + sp->fts_compar = compar; |
| + sp->fts_options = options; |
| + |
| + /* Logical walks turn on NOCHDIR; symbolic links are too hard. */ |
| + if (ISSET(FTS_LOGICAL)) |
| + SET(FTS_NOCHDIR); |
| + |
| + /* |
| + * Start out with 1K of path space, and enough, in any case, |
| + * to hold the user's paths. |
| + */ |
| + if (fts_palloc(sp, MAX(fts_maxarglen(argv), MAXPATHLEN))) |
| + goto mem1; |
| + |
| + /* Allocate/initialize root's parent. */ |
| + if ((parent = fts_alloc(sp, "", 0)) == NULL) |
| + goto mem2; |
| + parent->fts_level = FTS_ROOTPARENTLEVEL; |
| + |
| + /* Allocate/initialize root(s). */ |
| + for (root = NULL, nitems = 0; *argv; ++argv, ++nitems) { |
| + /* Don't allow zero-length paths. */ |
| + if ((len = strlen(*argv)) == 0) { |
| + errno = EINVAL; |
| + goto mem3; |
| + } |
| + |
| + p = fts_alloc(sp, *argv, len); |
| + p->fts_level = FTS_ROOTLEVEL; |
| + p->fts_parent = parent; |
| + p->fts_accpath = p->fts_name; |
| + p->fts_info = fts_stat(sp, NULL, p, ISSET(FTS_COMFOLLOW)); |
| + |
| + /* Command-line "." and ".." are real directories. */ |
| + if (p->fts_info == FTS_DOT) |
| + p->fts_info = FTS_D; |
| + |
| + /* |
| + * If comparison routine supplied, traverse in sorted |
| + * order; otherwise traverse in the order specified. |
| + */ |
| + if (compar) { |
| + p->fts_link = root; |
| + root = p; |
| + } else { |
| + p->fts_link = NULL; |
| + if (root == NULL) |
| + tmp = root = p; |
| + else { |
| + tmp->fts_link = p; |
| + tmp = p; |
| + } |
| } |
| - ++nitems; |
| } |
| - (void)closedir(dirp); |
| - |
| - /* |
| - * If had to realloc the path, adjust the addresses for the rest |
| - * of the tree. |
| - */ |
| - if (adjaddr) |
| - fts_padjust(sp, adjaddr); |
| + if (compar && nitems > 1) |
| + root = fts_sort(sp, root, nitems); |
| |
| /* |
| - * If not changing directories, reset the path back to original |
| - * state. |
| + * Allocate a dummy pointer and make fts_read think that we've just |
| + * finished the node before the root(s); set p->fts_info to FTS_INIT |
| + * so that everything about the "current" node is ignored. |
| */ |
| - if (ISSET(FTS_NOCHDIR)) { |
| - if (cp - 1 > sp->fts_path) |
| - --cp; |
| - *cp = '\0'; |
| - } |
| + if ((sp->fts_cur = fts_alloc(sp, "", 0)) == NULL) |
| + goto mem3; |
| + sp->fts_cur->fts_link = root; |
| + sp->fts_cur->fts_info = FTS_INIT; |
| |
| /* |
| - * If descended after called from fts_children or after called from |
| - * fts_read and nothing found, get back. At the root level we use |
| - * the saved fd; if one of fts_open()'s arguments is a relative path |
| - * to an empty directory, we wind up here with no other way back. If |
| - * can't get back, we're done. |
| + * If using chdir(2), grab a file descriptor pointing to dot to insure |
| + * that we can get back here; this could be avoided for some paths, |
| + * but almost certainly not worth the effort. Slashes, symbolic links, |
| + * and ".." are all fairly nasty problems. Note, if we can't get the |
| + * descriptor we run anyway, just more slowly. |
| */ |
| - if (descend && (type == BCHILD || !nitems) && |
| - (cur->fts_level == FTS_ROOTLEVEL ? |
| - FCHDIR(sp, sp->fts_rfd) : CHDIR(sp, ".."))) { |
| - cur->fts_info = FTS_ERR; |
| - SET(FTS_STOP); |
| - return (NULL); |
| - } |
| + if (!ISSET(FTS_NOCHDIR) && (sp->fts_rfd = open(".", O_RDONLY, 0)) < 0) |
| + SET(FTS_NOCHDIR); |
| |
| - /* If didn't find anything, return NULL. */ |
| - if (!nitems) { |
| - if (type == BREAD) |
| - cur->fts_info = FTS_DP; |
| - return (NULL); |
| - } |
| + return (sp); |
| |
| - /* Sort the entries. */ |
| - if (sp->fts_compar && nitems > 1) |
| - head = fts_sort(sp, head, nitems); |
| - return (head); |
| +mem3: fts_lfree(root); |
| + free(parent); |
| +mem2: free(sp->fts_path); |
| +mem1: free(sp); |
| + return (NULL); |
| } |
| |
| -static u_short |
| -fts_stat(sp, dp, p, follow) |
| - FTS *sp; |
| - register FTSENT *p; |
| - struct dirent *dp; |
| - int follow; |
| +FTSENT * |
| +fts_read(register FTS *sp) |
| { |
| - register FTSENT *t; |
| - register dev_t dev; |
| - register ino_t ino; |
| - struct stat *sbp, sb; |
| + register FTSENT *p, *tmp; |
| + register int instr; |
| + register char *t; |
| int saved_errno; |
| |
| - /* If user needs stat info, stat buffer already allocated. */ |
| - sbp = ISSET(FTS_NOSTAT) ? &sb : p->fts_statp; |
| + /* If finished or unrecoverable error, return NULL. */ |
| + if (sp->fts_cur == NULL || ISSET(FTS_STOP)) |
| + return (NULL); |
| |
| -#ifdef S_IFWHT |
| - /* |
| - * Whited-out files don't really exist. However, there's stat(2) file |
| - * mask for them, so we set it so that programs (i.e., find) don't have |
| - * to test FTS_W separately from other file types. |
| - */ |
| - if (dp != NULL && dp->d_type == DT_WHT) { |
| - memset(sbp, 0, sizeof(struct stat)); |
| - sbp->st_mode = S_IFWHT; |
| - return (FTS_W); |
| + /* Set current node pointer. */ |
| + p = sp->fts_cur; |
| + |
| + /* Save and zero out user instructions. */ |
| + instr = p->fts_instr; |
| + p->fts_instr = FTS_NOINSTR; |
| + |
| + /* Any type of file may be re-visited; re-stat and re-turn. */ |
| + if (instr == FTS_AGAIN) { |
| + p->fts_info = fts_stat(sp, NULL, p, 0); |
| + return (p); |
| } |
| -#endif |
| - |
| + |
| /* |
| - * If doing a logical walk, or application requested FTS_FOLLOW, do |
| - * a stat(2). If that fails, check for a non-existent symlink. If |
| - * fail, set the errno from the stat call. |
| + * Following a symlink -- SLNONE test allows application to see |
| + * SLNONE and recover. If indirecting through a symlink, have |
| + * keep a pointer to current location. If unable to get that |
| + * pointer, follow fails. |
| */ |
| - if (ISSET(FTS_LOGICAL) || follow) { |
| - if (stat(p->fts_accpath, sbp)) { |
| - saved_errno = errno; |
| - if (!lstat(p->fts_accpath, sbp)) { |
| - errno = 0; |
| - return (FTS_SLNONE); |
| - } |
| - p->fts_errno = saved_errno; |
| - goto err; |
| + if (instr == FTS_FOLLOW && |
| + (p->fts_info == FTS_SL || p->fts_info == FTS_SLNONE)) { |
| + p->fts_info = fts_stat(sp, NULL, p, 1); |
| + if (p->fts_info == FTS_D && !ISSET(FTS_NOCHDIR)) |
| + if ((p->fts_symfd = open(".", O_RDONLY, 0)) < 0) { |
| + p->fts_errno = errno; |
| + p->fts_info = FTS_ERR; |
| + } else |
| + p->fts_flags |= FTS_SYMFOLLOW; |
| + return (p); |
| + } |
| + |
| + /* Directory in pre-order. */ |
| + if (p->fts_info == FTS_D) { |
| + /* If skipped or crossed mount point, do post-order visit. */ |
| + if (instr == FTS_SKIP || |
| + ISSET(FTS_XDEV) && p->fts_dev != sp->fts_dev) { |
| + if (p->fts_flags & FTS_SYMFOLLOW) |
| + (void)close(p->fts_symfd); |
| + if (sp->fts_child) { |
| + fts_lfree(sp->fts_child); |
| + sp->fts_child = NULL; |
| + } |
| + p->fts_info = FTS_DP; |
| + return (p); |
| } |
| - } else if (lstat(p->fts_accpath, sbp)) { |
| - p->fts_errno = errno; |
| -err: memset(sbp, 0, sizeof(struct stat)); |
| - return (FTS_NS); |
| + |
| + /* Rebuild if only read the names and now traversing. */ |
| + if (sp->fts_child && sp->fts_options & FTS_NAMEONLY) { |
| + sp->fts_options &= ~FTS_NAMEONLY; |
| + fts_lfree(sp->fts_child); |
| + sp->fts_child = NULL; |
| + } |
| + |
| + /* |
| + * Cd to the subdirectory. |
| + * |
| + * If have already read and now fail to chdir, whack the list |
| + * to make the names come out right, and set the parent errno |
| + * so the application will eventually get an error condition. |
| + * Set the FTS_DONTCHDIR flag so that when we logically change |
| + * directories back to the parent we don't do a chdir. |
| + * |
| + * If haven't read do so. If the read fails, fts_build sets |
| + * FTS_STOP or the fts_info field of the node. |
| + */ |
| + if (sp->fts_child) { |
| + if (CHDIR(sp, p->fts_accpath)) { |
| + p->fts_errno = errno; |
| + p->fts_flags |= FTS_DONTCHDIR; |
| + for (p = sp->fts_child; p; p = p->fts_link) |
| + p->fts_accpath = |
| + p->fts_parent->fts_accpath; |
| + } |
| + } else if ((sp->fts_child = fts_build(sp, BREAD)) == NULL) { |
| + if (ISSET(FTS_STOP)) |
| + return (NULL); |
| + return (p); |
| + } |
| + p = sp->fts_child; |
| + sp->fts_child = NULL; |
| + goto name; |
| } |
| |
| - if (S_ISDIR(sbp->st_mode)) { |
| + /* Move to the next node on this level. */ |
| +next: tmp = p; |
| + if (p = p->fts_link) { |
| + free(tmp); |
| + |
| /* |
| - * Set the device/inode. Used to find cycles and check for |
| - * crossing mount points. Also remember the link count, used |
| - * in fts_build to limit the number of stat calls. It is |
| - * understood that these fields are only referenced if fts_info |
| - * is set to FTS_D. |
| + * If reached the top, return to the original directory, and |
| + * load the paths for the next root. |
| */ |
| - dev = p->fts_dev = sbp->st_dev; |
| - ino = p->fts_ino = sbp->st_ino; |
| - p->fts_nlink = sbp->st_nlink; |
| + if (p->fts_level == FTS_ROOTLEVEL) { |
| + if (!ISSET(FTS_NOCHDIR) && FCHDIR(sp, sp->fts_rfd)) { |
| + SET(FTS_STOP); |
| + return (NULL); |
| + } |
| + fts_load(sp, p); |
| + return (sp->fts_cur = p); |
| + } |
| + |
| + /* |
| + * User may have called fts_set on the node. If skipped, |
| + * ignore. If followed, get a file descriptor so we can |
| + * get back if necessary. |
| + */ |
| + if (p->fts_instr == FTS_SKIP) |
| + goto next; |
| + if (p->fts_instr == FTS_FOLLOW) { |
| + p->fts_info = fts_stat(sp, NULL, p, 1); |
| + if (p->fts_info == FTS_D && !ISSET(FTS_NOCHDIR)) |
| + if ((p->fts_symfd = |
| + open(".", O_RDONLY, 0)) < 0) { |
| + p->fts_errno = errno; |
| + p->fts_info = FTS_ERR; |
| + } else |
| + p->fts_flags |= FTS_SYMFOLLOW; |
| + p->fts_instr = FTS_NOINSTR; |
| + } |
| |
| - if (ISDOT(p->fts_name)) |
| - return (FTS_DOT); |
| +name: t = sp->fts_path + NAPPEND(p->fts_parent); |
| + *t++ = '/'; |
| + memmove(t, p->fts_name, p->fts_namelen + 1); |
| + return (sp->fts_cur = p); |
| + } |
| |
| + /* Move up to the parent node. */ |
| + p = tmp->fts_parent; |
| + free(tmp); |
| + |
| + if (p->fts_level == FTS_ROOTPARENTLEVEL) { |
| /* |
| - * Cycle detection is done by brute force when the directory |
| - * is first encountered. If the tree gets deep enough or the |
| - * number of symbolic links to directories is high enough, |
| - * something faster might be worthwhile. |
| + * Done; free everything up and set errno to 0 so the user |
| + * can distinguish between error and EOF. |
| */ |
| - for (t = p->fts_parent; |
| - t->fts_level >= FTS_ROOTLEVEL; t = t->fts_parent) |
| - if (ino == t->fts_ino && dev == t->fts_dev) { |
| - p->fts_cycle = t; |
| - return (FTS_DC); |
| - } |
| - return (FTS_D); |
| + free(p); |
| + errno = 0; |
| + return (sp->fts_cur = NULL); |
| } |
| - if (S_ISLNK(sbp->st_mode)) |
| - return (FTS_SL); |
| - if (S_ISREG(sbp->st_mode)) |
| - return (FTS_F); |
| - return (FTS_DEFAULT); |
| -} |
| |
| -static FTSENT * |
| -fts_sort(sp, head, nitems) |
| - FTS *sp; |
| - FTSENT *head; |
| - register int nitems; |
| -{ |
| - register FTSENT **ap, *p; |
| + /* Nul terminate the pathname. */ |
| + sp->fts_path[p->fts_pathlen] = '\0'; |
| |
| /* |
| - * Construct an array of pointers to the structures and call qsort(3). |
| - * Reassemble the array in the order returned by qsort. If unable to |
| - * sort for memory reasons, return the directory entries in their |
| - * current order. Allocate enough space for the current needs plus |
| - * 40 so don't realloc one entry at a time. |
| + * Return to the parent directory. If at a root node or came through |
| + * a symlink, go back through the file descriptor. Otherwise, cd up |
| + * one directory. |
| */ |
| - if (nitems > sp->fts_nitems) { |
| - sp->fts_nitems = nitems + 40; |
| - if ((sp->fts_array = realloc(sp->fts_array, |
| - (size_t)(sp->fts_nitems * sizeof(FTSENT *)))) == NULL) { |
| - sp->fts_nitems = 0; |
| - return (head); |
| + if (p->fts_level == FTS_ROOTLEVEL) { |
| + if (!ISSET(FTS_NOCHDIR) && FCHDIR(sp, sp->fts_rfd)) { |
| + SET(FTS_STOP); |
| + return (NULL); |
| + } |
| + } else if (p->fts_flags & FTS_SYMFOLLOW) { |
| + if (FCHDIR(sp, p->fts_symfd)) { |
| + saved_errno = errno; |
| + (void)close(p->fts_symfd); |
| + errno = saved_errno; |
| + SET(FTS_STOP); |
| + return (NULL); |
| + } |
| + (void)close(p->fts_symfd); |
| + } else if (!(p->fts_flags & FTS_DONTCHDIR)) { |
| + if (CHDIR(sp, "..")) { |
| + SET(FTS_STOP); |
| + return (NULL); |
| } |
| } |
| - for (ap = sp->fts_array, p = head; p; p = p->fts_link) |
| - *ap++ = p; |
| - qsort((void *)sp->fts_array, nitems, sizeof(FTSENT *), sp->fts_compar); |
| - for (head = *(ap = sp->fts_array); --nitems; ++ap) |
| - ap[0]->fts_link = ap[1]; |
| - ap[0]->fts_link = NULL; |
| - return (head); |
| + p->fts_info = p->fts_errno ? FTS_ERR : FTS_DP; |
| + return (sp->fts_cur = p); |
| } |
| |
| -static FTSENT * |
| -fts_alloc(sp, name, namelen) |
| - FTS *sp; |
| - char *name; |
| - register int namelen; |
| +/* |
| + * Fts_set takes the stream as an argument although it's not used in this |
| + * implementation; it would be necessary if anyone wanted to add global |
| + * semantics to fts using fts_set. An error return is allowed for similar |
| + * reasons. |
| + */ |
| +/* ARGSUSED */ |
| +int |
| +fts_set(FTS *sp, FTSENT *p, int instr) |
| +{ |
| + if (instr && instr != FTS_AGAIN && instr != FTS_FOLLOW && |
| + instr != FTS_NOINSTR && instr != FTS_SKIP) { |
| + errno = EINVAL; |
| + return (1); |
| + } |
| + p->fts_instr = instr; |
| + return (0); |
| +} |
| + |
| +FTSENT * |
| +fts_children(register FTS *sp, int instr) |
| { |
| register FTSENT *p; |
| - size_t len; |
| + int fd; |
| + |
| + if (instr && instr != FTS_NAMEONLY) { |
| + errno = EINVAL; |
| + return (NULL); |
| + } |
| + |
| + /* Set current node pointer. */ |
| + p = sp->fts_cur; |
| |
| /* |
| - * The file name is a variable length array and no stat structure is |
| - * necessary if the user has set the nostat bit. Allocate the FTSENT |
| - * structure, the file name and the stat structure in one chunk, but |
| - * be careful that the stat structure is reasonably aligned. Since the |
| - * fts_name field is declared to be of size 1, the fts_name pointer is |
| - * namelen + 2 before the first possible address of the stat structure. |
| + * Errno set to 0 so user can distinguish empty directory from |
| + * an error. |
| */ |
| - len = sizeof(FTSENT) + namelen; |
| - if (!ISSET(FTS_NOSTAT)) |
| - len += sizeof(struct stat) + ALIGNBYTES; |
| - if ((p = malloc(len)) == NULL) |
| + errno = 0; |
| + |
| + /* Fatal errors stop here. */ |
| + if (ISSET(FTS_STOP)) |
| return (NULL); |
| |
| - /* Copy the name plus the trailing NULL. */ |
| - memmove(p->fts_name, name, namelen + 1); |
| + /* Return logical hierarchy of user's arguments. */ |
| + if (p->fts_info == FTS_INIT) |
| + return (p->fts_link); |
| |
| - if (!ISSET(FTS_NOSTAT)) |
| - p->fts_statp = (struct stat *)ALIGN(p->fts_name + namelen + 2); |
| - p->fts_namelen = namelen; |
| - p->fts_path = sp->fts_path; |
| - p->fts_errno = 0; |
| - p->fts_flags = 0; |
| - p->fts_instr = FTS_NOINSTR; |
| - p->fts_number = 0; |
| - p->fts_pointer = NULL; |
| - return (p); |
| + /* |
| + * If not a directory being visited in pre-order, stop here. Could |
| + * allow FTS_DNR, assuming the user has fixed the problem, but the |
| + * same effect is available with FTS_AGAIN. |
| + */ |
| + if (p->fts_info != FTS_D /* && p->fts_info != FTS_DNR */) |
| + return (NULL); |
| + |
| + /* Free up any previous child list. */ |
| + if (sp->fts_child) |
| + fts_lfree(sp->fts_child); |
| + |
| + if (instr == FTS_NAMEONLY) { |
| + sp->fts_options |= FTS_NAMEONLY; |
| + instr = BNAMES; |
| + } else |
| + instr = BCHILD; |
| + |
| + /* |
| + * If using chdir on a relative path and called BEFORE fts_read does |
| + * its chdir to the root of a traversal, we can lose -- we need to |
| + * chdir into the subdirectory, and we don't know where the current |
| + * directory is, so we can't get back so that the upcoming chdir by |
| + * fts_read will work. |
| + */ |
| + if (p->fts_level != FTS_ROOTLEVEL || p->fts_accpath[0] == '/' || |
| + ISSET(FTS_NOCHDIR)) |
| + return (sp->fts_child = fts_build(sp, instr)); |
| + |
| + if ((fd = open(".", O_RDONLY, 0)) < 0) |
| + return (NULL); |
| + sp->fts_child = fts_build(sp, instr); |
| + if (fchdir(fd)) |
| + return (NULL); |
| + (void)close(fd); |
| + return (sp->fts_child); |
| } |
| |
| -static void |
| -fts_lfree(head) |
| - register FTSENT *head; |
| +int |
| +fts_close(FTS *sp) |
| { |
| - register FTSENT *p; |
| + register FTSENT *freep, *p; |
| + int saved_errno; |
| |
| - /* Free a linked list of structures. */ |
| - while (p = head) { |
| - head = head->fts_link; |
| + /* |
| + * This still works if we haven't read anything -- the dummy structure |
| + * points to the root list, so we step through to the end of the root |
| + * list which has a valid parent pointer. |
| + */ |
| + if (sp->fts_cur) { |
| + for (p = sp->fts_cur; p->fts_level >= FTS_ROOTLEVEL;) { |
| + freep = p; |
| + p = p->fts_link ? p->fts_link : p->fts_parent; |
| + free(freep); |
| + } |
| free(p); |
| } |
| -} |
| |
| -/* |
| - * Allow essentially unlimited paths; find, rm, ls should all work on any tree. |
| - * Most systems will allow creation of paths much longer than MAXPATHLEN, even |
| - * though the kernel won't resolve them. Add the size (not just what's needed) |
| - * plus 256 bytes so don't realloc the path 2 bytes at a time. |
| - */ |
| -static int |
| -fts_palloc(sp, more) |
| - FTS *sp; |
| - size_t more; |
| -{ |
| - sp->fts_pathlen += more + 256; |
| - sp->fts_path = realloc(sp->fts_path, (size_t)sp->fts_pathlen); |
| - return (sp->fts_path == NULL); |
| -} |
| + /* Free up child linked list, sort array, path buffer. */ |
| + if (sp->fts_child) |
| + fts_lfree(sp->fts_child); |
| + if (sp->fts_array) |
| + free(sp->fts_array); |
| + free(sp->fts_path); |
| |
| -/* |
| - * When the path is realloc'd, have to fix all of the pointers in structures |
| - * already returned. |
| - */ |
| -static void |
| -fts_padjust(sp, addr) |
| - FTS *sp; |
| - void *addr; |
| -{ |
| - FTSENT *p; |
| + /* Return to original directory, save errno if necessary. */ |
| + if (!ISSET(FTS_NOCHDIR)) { |
| + saved_errno = fchdir(sp->fts_rfd) ? errno : 0; |
| + (void)close(sp->fts_rfd); |
| + } |
| |
| -#define ADJUST(p) { \ |
| - (p)->fts_accpath = \ |
| - (char *)addr + ((p)->fts_accpath - (p)->fts_path); \ |
| - (p)->fts_path = addr; \ |
| -} |
| - /* Adjust the current set of children. */ |
| - for (p = sp->fts_child; p; p = p->fts_link) |
| - ADJUST(p); |
| + /* Free up the stream pointer. */ |
| + free(sp); |
| |
| - /* Adjust the rest of the tree. */ |
| - for (p = sp->fts_cur; p->fts_level >= FTS_ROOTLEVEL;) { |
| - ADJUST(p); |
| - p = p->fts_link ? p->fts_link : p->fts_parent; |
| + /* Set errno and return. */ |
| + if (!ISSET(FTS_NOCHDIR) && saved_errno) { |
| + errno = saved_errno; |
| + return (-1); |
| } |
| + return (0); |
| } |
| |
| -static size_t |
| -fts_maxarglen(argv) |
| - char * const *argv; |
| -{ |
| - size_t len, max; |
| - |
| - for (max = 0; *argv; ++argv) |
| - if ((len = strlen(*argv)) > max) |
| - max = len; |
| - return (max); |
| -} |