|
|
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:
76: #define NULL 0
77:
78: char *malloc(), *strcpy();
79: long lseek();
80: extern int errno;
81:
82: int
83: ftw (path, fn, depth)
84: char *path;
85: int (*fn)();
86: int depth;
87: {
88: int rc, n;
89: DIR *fd;
90: unsigned blen;
91: char *subpath, *component;
92: struct stat sb;
93: struct direct dir, *dirp;
94:
95: /* Try to get file status. If unsuccessful, errno will say why. */
96: #ifdef S_IFLNK
97: if (lstat (path, &sb) < 0)
98: #else
99: if (stat (path, &sb) < 0)
100: #endif
101: return errno == EACCES? (*fn) (path, &sb, FTW_NS): -1;
102:
103: /*
104: * The stat succeeded, so we know the object exists.
105: * If not a directory, call the user function and return.
106: */
107: if ((sb.st_mode & S_IFMT) != S_IFDIR)
108: switch(sb.st_mode & S_IFMT) {
109: case S_IFDIR: break;
110: #ifdef S_IFLNK
111: case S_IFLNK:
112: return (*fn) (path, &sb, FTW_SL);
113: #endif
114: default:
115: return (*fn) (path, &sb, FTW_F);
116: }
117:
118: /*
119: * The object was a directory.
120: *
121: * Open a file to read the directory
122: */
123: fd = opendir(path);
124:
125: /*
126: * Call the user function, telling it whether
127: * the directory can be read. If it can't be read
128: * call the user function or indicate an error,
129: * depending on the reason it couldn't be read.
130: */
131: if (fd == NULL)
132: return errno == EACCES? (*fn) (path, &sb, FTW_DNR): -1;
133:
134: /* We could read the directory. Call user function. */
135: rc = (*fn) (path, &sb, FTW_D);
136: if (rc != 0)
137: return rc;
138:
139: /* Allocate a buffer to hold generated pathnames. */
140: n = strlen (path);
141: blen = n + 14 + 2; /* nominal size */
142: subpath = malloc (blen);
143: if (subpath == NULL) {
144: closedir(fd);
145: errno = ENOMEM;
146: return -1;
147: }
148:
149: /* Create a prefix to which we will append component names */
150: (void) strcpy (subpath, path);
151: if (subpath[0] != '\0' && subpath[n - 1] != '/')
152: subpath[n++] = '/';
153: component = &subpath[n];
154:
155: /*
156: * Read the directory one component at a time.
157: * We must ignore "." and "..", but other than that,
158: * just create a path name and call self to check it out.
159: */
160: while ((dirp = readdir(fd)) != NULL) {
161: if (dirp->d_ino != 0
162: && strcmp (dirp->d_name, ".") != 0
163: && strcmp (dirp->d_name, "..") != 0) {
164: int i;
165: char *p, *q;
166: long here;
167:
168: /* Append the component name to the working path */
169: if ((n + dirp->d_namlen + 2) > blen) {
170: char *np;
171: blen = n + dirp->d_namlen + 2;
172: np = malloc(blen);
173: if (np == NULL) {
174: closedir(fd);
175: free(subpath);
176: errno = ENOMEM;
177: return -1;
178: }
179: strcpy(np, subpath);
180: free(subpath);
181: subpath = np;
182: component = &subpath[n];
183: }
184: p = component;
185: q = dirp->d_name;
186: for (i = 0; i < dirp->d_namlen && *q != '\0'; i++)
187: *p++ = *q++;
188: *p = '\0';
189:
190: /*
191: * If we are about to exceed our depth,
192: * remember where we are and close the file.
193: */
194: if (depth <= 1) {
195: here = telldir(fd);
196: closedir(fd);
197: }
198:
199: /*
200: * Do a recursive call to process the file.
201: * (watch this, sports fans)
202: */
203: rc = ftw (subpath, fn, depth - 1);
204: if (rc != 0) {
205: free (subpath);
206: if (depth > 1)
207: closedir (fd);
208: return rc;
209: }
210:
211: /*
212: * If we closed the file, try to reopen it.
213: */
214: if (depth <= 1) {
215: fd = opendir(path);
216: if (fd == NULL) {
217: free (subpath);
218: return -1;
219: }
220: seekdir (fd, here);
221: }
222: }
223: }
224:
225: /*
226: * We got out of the subdirectory loop. The return from the
227: * final iread is in rl. Call the user function again at the
228: * end, clean up, and then check that the final
229: * iread was successful. If not, give an error return.
230: */
231: free (subpath);
232: closedir(fd);
233:
234: rc = (*fn) (path, &sb, FTW_DP);
235: if (rc != 0)
236: return rc;
237:
238: return 0;
239: }
This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.