|
|
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.