|
|
1.1 ! root 1: /* ! 2: * ftw - file tree walk ! 3: * ! 4: * int ftw (path, fn, depth) char *path; int (*fn)(); int depth; ! 5: * ! 6: * Given a path name, ftw starts from the file given by that path ! 7: * name and visits each file and directory in the tree beneath ! 8: * that file. If a single file has multiple links within the ! 9: * structure, it will be visited once for each such link. ! 10: * For each object visited, fn is called with four arguments. ! 11: * The fourth can often be ignored; it is a pointer, say S, ! 12: * declared "struct FTW *S", discussed in more detail below. ! 13: * The first contains the path name of the object, the second ! 14: * contains a pointer to a stat buffer which will usually hold ! 15: * appropriate information for the object and the third contains ! 16: * an integer value giving additional information about the ! 17: * object, as follows: ! 18: * ! 19: * FTW_F The object is a file for which stat was ! 20: * successful. It does not guarantee that the ! 21: * file can actually be read. ! 22: * ! 23: * FTW_D The object is a directory for which stat and ! 24: * open for read were both successful. This is ! 25: * a preorder visit -- objects in the directory ! 26: * are yet to be visited. ! 27: * ! 28: * FTW_DNR The object is a directory for which stat ! 29: * succeeded, but which cannot be read. Because ! 30: * the directory cannot be read, fn will not be ! 31: * called for any descendants of this directory. ! 32: * ! 33: * FTW_DP The object is a directory for which stat and ! 34: * open for read were both successful. This is ! 35: * a postorder visit -- everything in the directory ! 36: * has already been visited. ! 37: * ! 38: * FTW_NS Stat failed on the object because of lack of ! 39: * appropriate permission. This indication will ! 40: * be given, for example, for each file in a directory ! 41: * with read but no execute permission. Because ! 42: * stat failed, it is not possible to determine ! 43: * whether this object is a file or a directory. ! 44: * the stat buffer passed to fn will contain garbage. ! 45: * Stat failure for any reason other than lack of ! 46: * permission will be considered an error and will ! 47: * cause ftw to stop and return -1 to its caller. ! 48: * ! 49: * FTW_SL The object is a symbolic link. Set S->quit ! 50: * (a component of the structure pointed to by ! 51: * the fourth parameter to fn) to FTW_FOLLOW to ! 52: * have the link followed and the object to which ! 53: * it points visited. ! 54: * ! 55: * Among the components of the structure to which the fourth ! 56: * parameter, S, to fn points is S->quit. If the caller sets ! 57: * S->quit to FTW_SKR, then no more files in the current directory ! 58: * will be visited. (The current directory is the one containing ! 59: * the object being visited.) If the third parameter to fn is ! 60: * FTW_D and the caller sets S->quit to FTW_SKD, then this directory ! 61: * (the one named in the first parameter to fn) will be skipped. ! 62: * ! 63: * Other components pointed to by the fourth parameter S are ! 64: * the current recursion level S->level (top level = 0) and ! 65: * the offset S->base in the pathname of the current object ! 66: * (the first parameter to fn) of the object's base name. ! 67: * By expanding the definition of struct FTW given below and ! 68: * including the files included below, one can arrange for ! 69: * S to point to a larger structure, components of which can ! 70: * be initialized (for example) on calls to fn with third ! 71: * parameter FTW_D. ! 72: * ! 73: * If fn returns nonzero, ftw stops and returns the same value ! 74: * to its caller. If ftw gets into other trouble along the way, ! 75: * it returns -1 and leaves an indication of the cause in errno. ! 76: * ! 77: * The third argument to ftw does not limit the depth to which ! 78: * ftw will go. Rather, it limits the depth to which ftw will ! 79: * go before it starts recycling file descriptors. In general, ! 80: * it is necessary to use a file descriptor for each level of the ! 81: * tree, but they can be recycled for deep trees by saving the position, ! 82: * closing, re-opening, and seeking. It is possible to start ! 83: * recycling file descriptors by sensing when we have run out, but ! 84: * in general this will not be terribly useful if fn expects to be ! 85: * able to open files. We could also figure out how many file descriptors ! 86: * are available and guarantee a certain number to fn, but we would not ! 87: * know how many to guarantee, and we do not want to impose the extra ! 88: * overhead on a caller who knows how many are available without ! 89: * having to figure it out. ! 90: * ! 91: * It is possible for ftw to die with a memory fault in the event ! 92: * of a file system so deeply nested that the stack overflows. ! 93: */ ! 94: ! 95: #include <sys/types.h> ! 96: #include <sys/stat.h> ! 97: #include <ftw.h> ! 98: /* ! 99: * Struct FTW (whose definition starts at the end of ftw.h) must ! 100: * must include at least the integers quit, base, and level. ! 101: */ ! 102: ! 103: #define FTW_PATHLEN0 1000 ! 104: #define FTW_PATHINC 1000 ! 105: #ifndef S_IFLNK ! 106: #define lstat stat ! 107: #endif ! 108: #ifdef S_IFSOCK ! 109: #include <sys/dir.h> ! 110: #else ! 111: #include "ndir.h" ! 112: #endif ! 113: #ifndef ENOMEM ! 114: #include <errno.h> ! 115: #endif ! 116: ! 117: extern int errno; ! 118: ! 119: /* ! 120: * Each generation of ftw1 (the real ftw) allocates one copy, R, of the ! 121: * following structure; it passes a pointer to this structure when it ! 122: * recursively invokes itself. These structures are chained together, ! 123: * so that if it becomes necessary to recycle file descriptors, then ! 124: * the oldest descriptor (the one at the shallowest depth still open) ! 125: * can be recycled. ! 126: */ ! 127: ! 128: struct FTW_rec { ! 129: struct FTW_rec *prev; ! 130: long here; /* seek to here when reopening at this level */ ! 131: DIR *fd; /* file descriptor at this level */ ! 132: }; ! 133: ! 134: /* ! 135: * One instance, T, of the following structure is allocated by ftw; a ! 136: * pointer to it is passed to all generations of ftw1 (the real ftw). ! 137: * T could often be a global variable, but this way the parameter fn ! 138: * can invoke ftw for an independent tree walk. ! 139: * Component T->path points to storage for the object path-names; ! 140: * this storage may be relocated by realloc if T->path needs to be ! 141: * more than T->pathlast characters long. ! 142: * T->path[T->pathnext] is the next free character in the pathnames. ! 143: * T->depth = parameter depth to ftw. T->lastout is the deepest level at ! 144: * which a file descriptor has been recycled. ! 145: */ ! 146: ! 147: struct FTW_top { ! 148: int (*fn)(); ! 149: char *path; ! 150: unsigned pathlast, pathnext; ! 151: int lastout; ! 152: int depth; ! 153: }; ! 154: ! 155: int ! 156: ftw (path, fn, depth) ! 157: char *path; ! 158: int (*fn)(); ! 159: int depth; ! 160: { ! 161: struct FTW_top T; ! 162: struct FTW_rec R; ! 163: struct FTW S; ! 164: int rc; ! 165: char *malloc(), *strcpy(); ! 166: ! 167: T.depth = depth; ! 168: T.lastout = -1; ! 169: T.fn = fn; ! 170: S.quit = 0; ! 171: S.level = -1; ! 172: ! 173: /* initialize S.base, T.pathnext... */ ! 174: { ! 175: register char c, *p, *q; ! 176: for (p = q = path; c = *p; p++) if (c == '/') q = p + 1; ! 177: S.base = q - path; ! 178: T.pathnext = p - path; ! 179: } ! 180: ! 181: T.pathlast = T.pathnext + FTW_PATHLEN0; ! 182: T.path = malloc(T.pathlast); ! 183: if (!T.path) { errno = ENOMEM; return -1; } ! 184: strcpy(T.path, path); ! 185: rc = ftw_1_(&R, &T, 0, &S); ! 186: free(T.path); ! 187: return rc; ! 188: } ! 189: ! 190: int ! 191: ftw_1_ (R, T, level, S1) ! 192: register struct FTW_rec *R; ! 193: register struct FTW_top *T; ! 194: int level; ! 195: struct FTW *S1; ! 196: { ! 197: int rc, n; ! 198: DIR *fd; ! 199: struct direct *dirp; ! 200: char *component, *path; ! 201: struct stat sb; ! 202: struct FTW_rec mr; ! 203: unsigned nextsave; ! 204: struct FTW S; ! 205: char *realloc(); ! 206: long lseek(); ! 207: ! 208: mr.prev = R; ! 209: path = T->path; ! 210: S.level = level; ! 211: S.quit = 0; ! 212: S.base = S1->base; ! 213: ! 214: /* Try to get file status. If unsuccessful, errno will say why. */ ! 215: if (lstat(path, &sb) < 0) { ! 216: badstat: rc = -1; ! 217: if (errno == EACCES) { ! 218: rc = (*T->fn) (path, &sb, FTW_NS, &S); ! 219: S1->quit = S.quit; ! 220: } ! 221: return rc; ! 222: }; ! 223: ! 224: /* ! 225: * The stat succeeded, so we know the object exists. ! 226: * If not a directory, call the user function and return. ! 227: */ ! 228: #ifdef S_IFLNK ! 229: if ((sb.st_mode & S_IFMT) == S_IFLNK) { ! 230: rc = (*T->fn) (path, &sb, FTW_SL, &S); ! 231: S1->quit = S.quit; ! 232: if (rc || S.quit == FTW_SKR) return rc; ! 233: if (S.quit != FTW_FOLLOW) return 0; ! 234: S1->quit = S.quit = 0; ! 235: if (stat(path, &sb) < 0) goto badstat; ! 236: } ! 237: #endif ! 238: ! 239: if ((sb.st_mode & S_IFMT) != S_IFDIR) { ! 240: rc = (*T->fn) (path, &sb, FTW_F, &S); ! 241: S1->quit = S.quit; ! 242: return rc; ! 243: } ! 244: ! 245: /* ! 246: * The object was a directory. ! 247: * ! 248: * Open a file to read the directory ! 249: */ ! 250: mr.fd = fd = opendir(path); ! 251: ! 252: /* ! 253: * Call the user function, telling it whether ! 254: * the directory can be read. If it can't be read ! 255: * call the user function or indicate an error, ! 256: * depending on the reason it couldn't be read. ! 257: */ ! 258: if (!fd) { ! 259: rc = -1; ! 260: if (errno == EACCES) { ! 261: rc = (*T->fn) (path, &sb, FTW_DNR, &S); ! 262: S1->quit = S.quit; ! 263: } ! 264: return rc; ! 265: } ! 266: ! 267: /* We could read the directory. Call user function. */ ! 268: rc = (*T->fn) (path, &sb, FTW_D, &S); ! 269: if (rc != 0) ! 270: return rc; ! 271: if (S.quit == FTW_SKD) return 0; ! 272: if (S.quit == FTW_SKR) {S1->quit = FTW_SKR; return 0;} ! 273: ! 274: /* Make sure path is big enough to hold generated pathnames. */ ! 275: ! 276: n = nextsave = T->pathnext; ! 277: if (n + MAXNAMLEN + 1 >= T->pathlast) { ! 278: T->pathlast += FTW_PATHINC; ! 279: path = T->path = realloc(T->path, T->pathlast); ! 280: if (!path) { ! 281: (void) closedir(fd); ! 282: errno = ENOMEM; ! 283: return -1; ! 284: } ! 285: } ! 286: ! 287: /* Create a prefix to which we will append component names */ ! 288: ! 289: if (n > 0 && path[n-1] != '/') path[n++] = '/'; ! 290: component = path + n; ! 291: ! 292: /* ! 293: * Read the directory one component at a time. ! 294: * We must ignore "." and "..", but other than that, ! 295: * just create a path name and call self to check it out. ! 296: */ ! 297: while (dirp = readdir(fd)) { ! 298: if (dirp->d_ino != 0 ! 299: && strcmp (dirp->d_name, ".") != 0 ! 300: && strcmp (dirp->d_name, "..") != 0) { ! 301: int i; ! 302: struct FTW_rec *pr; ! 303: ! 304: /* Append the component name to the working path */ ! 305: strcpy(component, dirp->d_name); ! 306: T->pathnext = n + strlen(dirp->d_name); ! 307: ! 308: /* ! 309: * If we are about to exceed our depth, ! 310: * remember where we are and close the file. ! 311: */ ! 312: if (level - T->lastout >= T->depth) { ! 313: pr = &mr; ! 314: i = T->lastout++; ! 315: while (++i < level) pr = pr->prev; ! 316: pr->here = telldir(pr->fd); ! 317: closedir(pr->fd); ! 318: } ! 319: ! 320: /* ! 321: * Do a recursive call to process the file. ! 322: */ ! 323: S.quit = 0; ! 324: S.base = n; ! 325: rc = ftw_1_(&mr, T, level+1, &S); ! 326: if (rc != 0 || S.quit == FTW_SKR) { ! 327: if (level > T->lastout) closedir(fd); ! 328: T->pathnext = nextsave; ! 329: return rc; ! 330: } ! 331: ! 332: /* ! 333: * If we closed the file, try to reopen it. ! 334: */ ! 335: if (level <= T->lastout) { ! 336: char c = path[nextsave]; ! 337: path[nextsave] = 0; ! 338: T->lastout = level - 1; ! 339: mr.fd = fd = opendir(path); ! 340: if (!fd) return -1; ! 341: path[nextsave] = c; ! 342: seekdir(fd, mr.here); ! 343: } ! 344: } ! 345: } ! 346: T->pathnext = nextsave; ! 347: path[nextsave] = 0; ! 348: ! 349: /* ! 350: * We got out of the subdirectory loop. Call the user ! 351: * function again at the end and clean up. ! 352: */ ! 353: ! 354: rc = (*T->fn) (path, &sb, FTW_DP, &S); ! 355: closedir(fd); ! 356: S1->quit = S.quit; ! 357: return rc; ! 358: }
This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.