📄 fts.c
字号:
/* 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; /* * 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);}/* * This is the tricky part -- do not casually change *anything* in here. The * idea is to build the linked list of entries that are used by fts_children * and fts_read. There are lots of special cases. * * The real slowdown in walking the tree is the stat calls. If FTS_NOSTAT is * set and it's a physical walk (so that symbolic links can't be directories), * we can do things quickly. First, if it's a 4.4BSD file system, the type * of the file is in the directory entry. Otherwise, we assume that the number * of subdirectories in a node is equal to the number of links to the parent. * The former skips all stat calls. The latter skips stat calls in any leaf * directories and for any files after the subdirectories in the directory have * been found, cutting the stat calls by about 2/3. */static FTSENT *fts_build(sp, type) register FTS *sp; int type;{ struct dirent *dp; register FTSENT *p, *head; register int nitems; FTSENT *cur, *tail; DIR *dirp; void *adjaddr; int cderrno, descend, len, level, maxlen, nlinks, saved_errno; char *cp=NULL;#ifdef DTF_HIDEW int oflag;#endif /* Set current node pointer. */ cur = sp->fts_cur; /* * Open the directory for reading. If this fails, we're done. * If being called from fts_read, set the fts_info field. */#ifdef DTF_HIDEW if (ISSET (FTS_WHITEOUT)) oflag = DTF_NODUP|DTF_REWIND; else oflag = DTF_HIDEW|DTF_NODUP|DTF_REWIND;#else# define opendir2(path, flag) opendir(path)#endif if ((dirp = opendir2 (cur->fts_accpath, oflag)) == NULL) { if (type == BREAD) { cur->fts_info = FTS_DNR; cur->fts_errno = errno; } return (NULL); } /* * Nlinks is the number of possible entries of type directory in the * directory if we're cheating on stat calls, 0 if we're not doing * any stat calls at all, -1 if we're doing stats on everything. */ if (type == BNAMES) nlinks = 0; else if (ISSET(FTS_NOSTAT) && ISSET(FTS_PHYSICAL)) nlinks = cur->fts_nlink - (ISSET(FTS_SEEDOT) ? 0 : 2); else nlinks = -1;#ifdef notdef (void)printf("nlinks == %d (cur: %d)\n", nlinks, cur->fts_nlink); (void)printf("NOSTAT %d PHYSICAL %d SEEDOT %d\n", ISSET(FTS_NOSTAT), ISSET(FTS_PHYSICAL), ISSET(FTS_SEEDOT));#endif /* * If we're going to need to stat anything or we want to descend * and stay in the directory, chdir. If this fails we keep going, * but set a flag so we don't chdir after the post-order visit. * We won't be able to stat anything, but we can still return the * names themselves. Note, that since fts_read won't be able to * chdir into the directory, it will have to return different path * names than before, i.e. "a/b" instead of "b". Since the node * has already been visited in pre-order, have to wait until the * post-order visit to return the error. There is a special case * here, if there was nothing to stat then it's not an error to * not be able to stat. This is all fairly nasty. If a program * needed sorted entries or stat information, they had better be * checking FTS_NS on the returned nodes. */ cderrno = 0; if (nlinks || type == BREAD) { if (FCHDIR(sp, dirfd(dirp))) { if (nlinks && type == BREAD) cur->fts_errno = errno; cur->fts_flags |= FTS_DONTCHDIR; descend = 0; cderrno = errno; } else descend = 1; } else descend = 0; /* * Figure out the max file name length that can be stored in the * current path -- the inner loop allocates more path as necessary. * We really wouldn't have to do the maxlen calculations here, we * could do them in fts_read before returning the path, but it's a * lot easier here since the length is part of the dirent structure. * * If not changing directories set a pointer so that can just append * each new name into the path. */ maxlen = sp->fts_pathlen - cur->fts_pathlen - 1; len = NAPPEND(cur); if (ISSET(FTS_NOCHDIR)) { cp = sp->fts_path + len; *cp++ = '/'; } level = cur->fts_level + 1; /* Read the directory, attaching each entry to the `link' pointer. */ adjaddr = NULL; head = tail = NULL; nitems = 0; while((dp = readdir(dirp))) { int namlen; if (!ISSET(FTS_SEEDOT) && ISDOT(dp->d_name)) continue; namlen = _D_EXACT_NAMLEN (dp); if ((p = fts_alloc(sp, dp->d_name, namlen)) == NULL) goto mem1; if (namlen > maxlen) { if (fts_palloc(sp, (size_t)namlen)) { /* * No more memory for path or structures. Save * errno, free up the current structure and the * structures already allocated. */mem1: saved_errno = errno; if (p) free(p); fts_lfree(head); (void)closedir(dirp); errno = saved_errno; cur->fts_info = FTS_ERR; SET(FTS_STOP); return (NULL); } adjaddr = sp->fts_path; maxlen = sp->fts_pathlen - sp->fts_cur->fts_pathlen - 1; } p->fts_pathlen = len + namlen + 1; p->fts_parent = sp->fts_cur; p->fts_level = level; if (cderrno) { if (nlinks) { p->fts_info = FTS_NS; p->fts_errno = cderrno; } else p->fts_info = FTS_NSOK; p->fts_accpath = cur->fts_accpath; } else if (nlinks == 0#if defined DT_DIR && defined _DIRENT_HAVE_D_TYPE || (nlinks > 0 && dp->d_type != DT_DIR && dp->d_type != DT_UNKNOWN)#endif ) { p->fts_accpath = ISSET(FTS_NOCHDIR) ? p->fts_path : p->fts_name; p->fts_info = FTS_NSOK; } else { /* Build a file name for fts_stat to stat. */ if (ISSET(FTS_NOCHDIR)) { p->fts_accpath = p->fts_path; memmove(p->fts_name, cp, p->fts_namelen + 1); } else p->fts_accpath = p->fts_name; /* Stat it. */ p->fts_info = fts_stat(sp, dp, p, 0); /* Decrement link count if applicable. */ if (nlinks > 0 && (p->fts_info == FTS_D || p->fts_info == FTS_DC || p->fts_info == FTS_DOT)) --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; } ++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);}static u_shortfts_stat(sp, dp, p, follow) 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;#if defined(DT_WHT) && defined(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); }#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; } } else if (lstat(p->fts_accpath, sbp)) { p->fts_errno = errno;err: memset(sbp, 0, sizeof(struct stat)); return (FTS_NS); } if (S_ISDIR(sbp->st_mode)) { /* * 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. */ dev = p->fts_dev = sbp->st_dev; ino = p->fts_ino = sbp->st_ino; p->fts_nlink = sbp->st_nlink; if (ISDOT(p->fts_name)) return (FTS_DOT); /* * 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. */ 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); } 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; /* * 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 (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);}static FTSENT *fts_alloc(sp, name, namelen) FTS *sp; const char *name; register int namelen;{ register FTSENT *p; size_t len; /* * 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); if ((p = malloc(len)) == NULL) return (NULL); /* Copy the name plus the trailing NULL. */ memmove(p->fts_name, name, namelen + 1); if (!ISSET(FTS_NOSTAT)) p->fts_statp = (struct stat *)(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);}static voidfts_lfree(head) register FTSENT *head;{ register FTSENT *p; /* Free a linked list of structures. */ while ((p = head)) { head = head->fts_link; 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 intfts_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);}/* * When the path is realloc'd, have to fix all of the pointers in structures * already returned. */static voidfts_padjust(sp, addr) 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; }}static size_tfts_maxarglen(argv) char * const *argv;{ size_t len, max; for (max = 0; *argv; ++argv) if ((len = strlen(*argv)) > max) max = len; return (max);}
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -