Annotation of researchv10no/cmd/dist/lib/ftwlk.c, revision 1.1.1.1

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: }

unix.superglobalmegacorp.com

This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.