|
|
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 three arguments. ! 11: * The first contains the path name of the object, the second ! 12: * contains a pointer to a stat buffer which will usually hold ! 13: * appropriate information for the object and the third will contain ! 14: * an integer value giving additional information about the ! 15: * object, as follows: ! 16: * ! 17: * FTW_F The object is a file for which stat was ! 18: * successful. It does not guarantee that the ! 19: * file can actually be read. ! 20: * ! 21: * FTW_D The object is a directory for which stat and ! 22: * open for read were both successful. This is ! 23: * a preorder visit -- objects in the directory ! 24: * are yet to be visited. ! 25: * ! 26: * FTW_DNR The object is a directory for which stat ! 27: * succeeded, but which cannot be read. Because ! 28: * the directory cannot be read, fn will not be ! 29: * called for any descendants of this directory. ! 30: * ! 31: * FTW_DP The object is a directory for which stat and ! 32: * open for read were both successful. This is ! 33: * a postorder visit -- everything in the directory ! 34: * has already been visited. ! 35: * ! 36: * FTW_SL The object is a symbolic link. ! 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: * If fn returns nonzero, ftw stops and returns the same value ! 50: * to its caller. If ftw gets into other trouble along the way, ! 51: * it returns -1 and leaves an indication of the cause in errno. ! 52: * ! 53: * The third argument to ftw does not limit the depth to which ! 54: * ftw will go. Rather, it limits the depth to which ftw will ! 55: * go before it starts recycling file descriptors. In general, ! 56: * it is necessary to use a file descriptor for each level of the ! 57: * tree, but they can be recycled for deep trees by saving the position, ! 58: * closing, re-opening, and seeking. It is possible to start ! 59: * recycling file descriptors by sensing when we have run out, but ! 60: * in general this will not be terribly useful if fn expects to be ! 61: * able to open files. We could also figure out how many file descriptors ! 62: * are available and guarantee a certain number to fn, but we would not ! 63: * know how many to guarantee, and we do not want to impose the extra ! 64: * overhead on a caller who knows how many are available without ! 65: * having to figure it out. ! 66: * ! 67: * It is possible for ftw to die with a memory fault in the event ! 68: * of a file system so deeply nested that the stack overflows. ! 69: */ ! 70: ! 71: #include <sys/types.h> ! 72: #include <sys/stat.h> ! 73: #include <errno.h> ! 74: #include <ftw.h> ! 75: #include <ndir.h> ! 76: ! 77: #define NULL 0 ! 78: ! 79: char *malloc(), *strcpy(); ! 80: long lseek(); ! 81: extern int errno; ! 82: ! 83: int ! 84: ftw (path, fn, depth) ! 85: char *path; ! 86: int (*fn)(); ! 87: int depth; ! 88: { ! 89: int rc, n; ! 90: DIR *fd; ! 91: unsigned blen; ! 92: char *subpath, *component; ! 93: struct stat sb; ! 94: struct direct dir, *dirp; ! 95: ! 96: /* Try to get file status. If unsuccessful, errno will say why. */ ! 97: if (lstat (path, &sb) < 0) ! 98: return errno == EACCES? (*fn) (path, &sb, FTW_NS): -1; ! 99: ! 100: /* ! 101: * The stat succeeded, so we know the object exists. ! 102: * If not a directory, call the user function and return. ! 103: */ ! 104: if ((sb.st_mode & S_IFMT) != S_IFDIR) ! 105: switch(sb.st_mode & S_IFMT) { ! 106: case S_IFDIR: ! 107: break; ! 108: case S_IFLNK: ! 109: return (*fn) (path, &sb, FTW_SL); ! 110: default: ! 111: return (*fn) (path, &sb, FTW_F); ! 112: } ! 113: ! 114: /* ! 115: * The object was a directory. ! 116: * ! 117: * Open a file to read the directory ! 118: */ ! 119: fd = opendir(path); ! 120: ! 121: /* ! 122: * Call the user function, telling it whether ! 123: * the directory can be read. If it can't be read ! 124: * call the user function or indicate an error, ! 125: * depending on the reason it couldn't be read. ! 126: */ ! 127: if (fd == NULL) ! 128: return errno == EACCES? (*fn) (path, &sb, FTW_DNR): -1; ! 129: ! 130: /* We could read the directory. Call user function. */ ! 131: rc = (*fn) (path, &sb, FTW_D); ! 132: if (rc != 0) ! 133: return rc; ! 134: ! 135: /* Allocate a buffer to hold generated pathnames. */ ! 136: n = strlen (path); ! 137: blen = n + 14 + 2; /* nominal size */ ! 138: subpath = malloc (blen); ! 139: if (subpath == NULL) { ! 140: closedir(fd); ! 141: errno = ENOMEM; ! 142: return -1; ! 143: } ! 144: ! 145: /* Create a prefix to which we will append component names */ ! 146: (void) strcpy (subpath, path); ! 147: if (subpath[0] != '\0' && subpath[n - 1] != '/') ! 148: subpath[n++] = '/'; ! 149: component = &subpath[n]; ! 150: ! 151: /* ! 152: * Read the directory one component at a time. ! 153: * We must ignore "." and "..", but other than that, ! 154: * just create a path name and call self to check it out. ! 155: */ ! 156: while ((dirp = readdir(fd)) != NULL) { ! 157: if (dirp->d_ino != 0 ! 158: && strcmp (dirp->d_name, ".") != 0 ! 159: && strcmp (dirp->d_name, "..") != 0) { ! 160: int i; ! 161: char *p, *q; ! 162: long here; ! 163: ! 164: /* Append the component name to the working path */ ! 165: if ((n + dirp->d_namlen + 2) > blen) { ! 166: char *np; ! 167: blen = n + dirp->d_namlen + 2; ! 168: np = malloc(blen); ! 169: if (np == NULL) { ! 170: closedir(fd); ! 171: free(subpath); ! 172: errno = ENOMEM; ! 173: return -1; ! 174: } ! 175: strcpy(np, subpath); ! 176: free(subpath); ! 177: subpath = np; ! 178: component = &subpath[n]; ! 179: } ! 180: p = component; ! 181: q = dirp->d_name; ! 182: for (i = 0; i < dirp->d_namlen && *q != '\0'; i++) ! 183: *p++ = *q++; ! 184: *p = '\0'; ! 185: ! 186: /* ! 187: * If we are about to exceed our depth, ! 188: * remember where we are and close the file. ! 189: */ ! 190: if (depth <= 1) { ! 191: here = telldir(fd); ! 192: closedir(fd); ! 193: } ! 194: ! 195: /* ! 196: * Do a recursive call to process the file. ! 197: * (watch this, sports fans) ! 198: */ ! 199: rc = ftw (subpath, fn, depth - 1); ! 200: if (rc != 0) { ! 201: free (subpath); ! 202: if (depth > 1) ! 203: closedir (fd); ! 204: return rc; ! 205: } ! 206: ! 207: /* ! 208: * If we closed the file, try to reopen it. ! 209: */ ! 210: if (depth <= 1) { ! 211: fd = opendir(path); ! 212: if (fd == NULL) { ! 213: free (subpath); ! 214: return -1; ! 215: } ! 216: seekdir (fd, here); ! 217: } ! 218: } ! 219: } ! 220: ! 221: /* ! 222: * We got out of the subdirectory loop. The return from the ! 223: * final iread is in rl. Call the user function again at the ! 224: * end, clean up, and then check that the final ! 225: * iread was successful. If not, give an error return. ! 226: */ ! 227: free (subpath); ! 228: closedir(fd); ! 229: ! 230: rc = (*fn) (path, &sb, FTW_DP); ! 231: if (rc != 0) ! 232: return rc; ! 233: ! 234: return 0; ! 235: }
This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.