|
|
1.1 root 1: /*
2: * ftwlk - file tree walk
3: *
4: * int ftwlk (path, fn, depth) char *path; int (*fn)(); int depth;
5: *
6: * Given a path name, ftwlk 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 FTWLK *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: * FTWLK_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: * FTWLK_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: * FTWLK_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: * FTWLK_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: * FTWLK_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: * FTWLK_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 FTWLK_FOLLOW to
50: * have the link followed and the object to which
51: * it points visited.
52: *
53: * FTWLK_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 FTWLK_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: * FTWLK_D and the caller sets S->quit to FTWLK_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 FTWLK 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 FTWLK_D.
74: *
75: * If fn returns nonzero, ftwlk stops and returns the same value
76: * to its caller. Ftw only initiates a nonzero return if malloc
77: * fails; in this case ftwlk sets errno to ENOMEM and returns -1.
78: *
79: * The third argument to ftwlk does not limit the depth to which
80: * ftwlk will go. Rather, it limits the depth to which ftwlk 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 ftwlk 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 <dirent.h>
99: #include <sys/stat.h>
100: #include "ftwlk.h"
101: /*
102: * Struct FTWLK (whose definition starts at the end of ftwlk.h) must
103: * must include at least the integers quit, base, and level.
104: */
105:
106: #define FTWLK_PATHLEN0 1000
107: #define FTWLK_PATHINC 1000
108:
109: #ifndef S_ISDIR
110: #define S_ISDIR(M) (((M) & S_IFMT) == S_IFDIR)
111: #ifdef S_IFLNK
112: #define S_ISLNK(M) (((M) & S_IFMT) == S_IFLNK)
113: #endif
114: #endif
115:
116: #ifndef S_ISLNK
117: #define lstat stat
118: #endif
119:
120: #ifndef ENOMEM
121: #include <errno.h>
122: #endif
123:
124: extern int errno;
125:
126: /*
127: * Each generation of ftwlk1 (the real ftwlk) 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 FTWLK_rec {
136: struct FTWLK_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 ftwlk; a
143: * pointer to it is passed to all generations of ftwlk1 (the real ftwlk).
144: * T could often be a global variable, but this way the parameter fn
145: * can invoke ftwlk 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 ftwlk. T->lastout is the deepest level at
151: * which a file descriptor has been recycled.
152: */
153:
154: struct FTWLK_top {
155: int (*fn)();
156: char *path;
157: unsigned pathlast, pathnext;
158: int lastout;
159: int depth;
160: };
161:
162: static ftwlk_1_();
163:
164: int
165: ftwlk (path, fn, depth)
166: char *path;
167: int (*fn)();
168: int depth;
169: {
170: struct FTWLK_top T;
171: struct FTWLK_rec R;
172: struct FTWLK 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 + FTWLK_PATHLEN0;
191: T.path = malloc(T.pathlast);
192: if (!T.path) { errno = ENOMEM; return -1; }
193: strcpy(T.path, path);
194: rc = ftwlk_1_(&R, &T, 0, &S);
195: free(T.path);
196: return rc;
197: }
198:
199: int
200: static
201: ftwlk_1_ (R, T, level, S1)
202: register struct FTWLK_rec *R;
203: register struct FTWLK_top *T;
204: int level;
205: struct FTWLK *S1;
206: {
207: int rc, n;
208: DIR *fd;
209: struct dirent *dirp;
210: char *component, *path;
211: struct stat sb;
212: struct FTWLK_rec mr;
213: unsigned nextsave;
214: struct FTWLK 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, FTWLK_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_ISLNK
236: if (S_ISLNK(sb.st_mode )) {
237: rc = (*T->fn) (path, &sb, FTWLK_SL, &S);
238: S1->quit = S.quit;
239: if (rc || S.quit == FTWLK_SKR) return rc;
240: if (S.quit != FTWLK_FOLLOW) return 0;
241: S1->quit = S.quit = 0;
242: if (stat(path, &sb) < 0) {
243: rc = (*T->fn) (path, &sb, FTWLK_NSL, &S);
244: S1->quit = S.quit;
245: return rc;
246: };
247: }
248: #endif
249:
250: if (!S_ISDIR(sb.st_mode)) {
251: rc = (*T->fn) (path, &sb, FTWLK_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, FTWLK_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, FTWLK_D, &S);
277: if (rc != 0)
278: goto rtrn;
279: if (S.quit == FTWLK_SKD) goto rtrn;
280: if (S.quit == FTWLK_SKR) {S1->quit = FTWLK_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 += FTWLK_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 FTWLK_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 = ftwlk_1_(&mr, T, level+1, &S);
334: if (rc != 0 || S.quit == FTWLK_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, FTWLK_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, FTWLK_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.