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