Annotation of researchv10no/cmd/dist/lib/ftwlk.c, revision 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.