/* * Copyright (c) 1988 Regents of the University of California. * All rights reserved. * * Redistribution and use in source and binary forms are permitted * provided that the above copyright notice and this paragraph are * duplicated in all such forms and that any documentation, * advertising materials, and other materials related to such * distribution and use acknowledge that the software was developed * by the University of California, Berkeley. The name of the * University may not be used to endorse or promote products derived * from this software without specific prior written permission. * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE. */ #if defined(LIBC_SCCS) && !defined(lint) static char sccsid[] = "@(#)ftw.c 5.3 (Berkeley) 8/5/88"; #endif /* LIBC_SCCS and not lint */ #include "../src/config.h" #ifdef HAVE_DIRENT #include #else #include #endif #include #include #include #include #include "ftw.h" #define NODESC -1 #ifdef HAVE_SYMLINK # define ISLINK(sb) ((sb.st_mode&S_IFMT) == S_IFLNK) #else # define lstat stat #endif #define ISDIR(sb) ((sb.st_mode&S_IFMT) == S_IFDIR) #define ISDOT(dp) \ (dp->d_name[0] == '.' && (!dp->d_name[1] || \ (dp->d_name[1] == '.' && !dp->d_name[2]))) extern int errno; static int g_fds, (*g_fn) (), g_opts; static char *bp; int treewalk(char *path, int (*fn) ( /* ??? */ ), int maxfds, int opts); /* * cycle through the directories at the top of the tree, otherwise, once * you run out of descriptors you have to keep reusing the same one and * it gets *real* slow. */ typedef struct d_fd { struct d_fd *next; DIR *dirp; off_t off; } FD; static FD *freep, *node; static int walk(register char *name) { #ifdef HAVE_DIRENT register struct dirent *dp; #else register struct direct *dp; #endif register int rval; struct stat sb; FD cur; char *save, *strcpy(char *, const char *); if (!freep) freep = &cur; else node->next = &cur; node = &cur; cur.off = 0; getfd:if (!g_fds) { freep->off = telldir(freep->dirp); closedir(freep->dirp); freep = freep->next; ++g_fds; } if ( !(cur.dirp = opendir(bp)) ) { if (errno == EMFILE) { g_fds = 0; goto getfd; } return (errno == EACCES ? (*g_fn) (bp, &sb, FTW_DNR) : -1); } else --g_fds; for (; *name; ++name) ; *name++ = '/'; for (rval = 0, dp = readdir(cur.dirp); dp; dp = readdir(cur.dirp)) { if (ISDOT(dp)) continue; (void) strcpy(name, dp->d_name); if (lstat(bp, &sb)) { rval = errno == EACCES ? (*g_fn) (bp, &sb, FTW_NS) : -1; if (rval) break; } #ifdef HAVE_SYMLINK if (ISLINK(sb) && g_opts & FTW_SYMLINK) if (stat(bp, &sb)) continue; #endif if (!ISDIR(sb)) { rval = (*g_fn) (bp, &sb, FTW_F); if (rval) break; continue; } if (g_opts & FTW_DIRLAST) #ifdef HAVE_D_NAMLEN save = name + dp->d_namlen; #else save = name + strlen(dp->d_name); #endif rval = (*g_fn) (bp, &sb, FTW_D); if ( (rval && rval != NODESC) || (rval = walk(name))) break; if (g_opts & FTW_DIRLAST) { *save = '\0'; rval = (*g_fn) (dp->d_name, &sb, FTW_D2); if (rval) if (rval == NODESC) rval = 0; else break; } if (cur.off) { *name = NULL; if ( (cur.dirp = opendir(bp)) ) { seekdir(cur.dirp, cur.off); /* tricky; if we have to reset the directory pointer we know * it's the next one to reuse */ freep = &cur; --g_fds; } /* directory moved from under us!!! */ else { rval = -1; break; } } } closedir(cur.dirp); ++g_fds; return (rval); } static int chwalk(register char *name) { #ifdef HAVE_DIRENT register struct dirent *dp; #else register struct direct *dp; #endif register int rval; struct stat sb; FD cur; char *pwd, *getwd(char *), #ifndef NO_MALLOC_PROTO *malloc(size_t), #endif *strcpy(char *, const char *); if (!freep) freep = &cur; else node->next = &cur; node = &cur; cur.off = 0; if (chdir(name)) return (errno == EACCES ? (*g_fn) (name, &sb, FTW_DNR) : -1); getfd:if (!g_fds) { freep->off = telldir(freep->dirp); closedir(freep->dirp); freep = freep->next; ++g_fds; } if ( !(cur.dirp = opendir(".")) ) { if (errno == EMFILE) { g_fds = 0; goto getfd; } return (errno == EACCES ? (*g_fn) (".", &sb, FTW_DNR) : -1); } else --g_fds; for (rval = 0, dp = readdir(cur.dirp); dp; dp = readdir(cur.dirp)) { if (ISDOT(dp)) continue; if (lstat(dp->d_name, &sb)) { rval = errno == EACCES ? (*g_fn) (dp->d_name, &sb, FTW_NS) : -1; if (rval) break; } pwd = NULL; #ifdef HAVE_SYMLINK if (ISLINK(sb) && g_opts & FTW_SYMLINK) { if (stat(dp->d_name, &sb)) continue; if (ISDIR(sb)) { /* NOSTRICT */ if (!(pwd = malloc((u_int) MAXPATHLEN))) { rval = -1; break; } if (!getwd(pwd)) { rval = -1; break; } } } #endif if (!ISDIR(sb)) { rval = (*g_fn) (dp->d_name, &sb, FTW_F); if (rval) break; continue; } rval = (*g_fn) (dp->d_name, &sb, FTW_D); if ((rval && rval != NODESC) || (rval = chwalk(dp->d_name))) break; if (g_opts & FTW_DIRLAST) { rval = (*g_fn) (dp->d_name, &sb, FTW_D2); if (rval) if (rval == NODESC) rval = 0; else break; } if (pwd && chdir(pwd)) { rval = -1; break; } if (cur.off) { if ( (cur.dirp = opendir(".")) ) { seekdir(cur.dirp, cur.off); /* tricky; if we have to reset the directory pointer we know * it's the next one to reuse */ freep = &cur; --g_fds; } /* directory moved from under us!!! */ else { rval = -1; break; } } } closedir(cur.dirp); ++g_fds; if (chdir("..")) return (-1); return (rval); } /* S5 compatible ftw(BA_LIB) */ int ftw(char *path, int (*fn) ( /* ??? */ ), int maxfds) { return (treewalk(path, fn, maxfds, 0)); } int treewalk(char *path, int (*fn) ( /* ??? */ ), int maxfds, int opts) { struct stat sb; int rval; char *pwd, *getwd(char *), #ifndef NO_MALLOC_PROTO *malloc(size_t), #endif *strcpy(char *, const char *); if (lstat(path, &sb)) return (errno == EACCES ? (*fn) (path, &sb, FTW_NS) : -1); pwd = NULL; #ifdef HAVE_SYMLINK if (ISLINK(sb) && opts & FTW_SYMLINK) { if (stat(path, &sb)) return (0); if (ISDIR(sb)) { /* NOSTRICT */ if (!(pwd = malloc((u_int) MAXPATHLEN))) return (-1); if (!getwd(pwd)) return (-1); } } #endif if (!ISDIR(sb)) return ((*fn) (path, &sb, FTW_F)); if (!maxfds) return (-1); g_fds = maxfds == -1 ? getdtablesize(): maxfds; g_fn = fn; g_opts = opts; if (!(opts & FTW_CHDIR) && !(bp = malloc((u_int) MAXPATHLEN))) { errno = ENOMEM; return (-1); } rval = (*fn) (path, &sb, FTW_D); if (rval == NODESC) rval = 0; else if (!rval) { if (opts & FTW_CHDIR) rval = chwalk(path); else rval = walk(strcpy(bp, path)); if (!rval && opts & FTW_DIRLAST) { rval = (*fn) (path, &sb, FTW_D2); if (rval == NODESC) rval = 0; } } if (pwd && chdir(pwd)) return (-1); return (rval); }