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