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