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