Annotation of researchv10no/cmd/worm/scsi/tcl/tclGlob.c, revision 1.1

1.1     ! root        1: /* 
        !             2:  * tclGlob.c --
        !             3:  *
        !             4:  *     This file provides procedures and commands for file name
        !             5:  *     manipulation, such as tilde expansion and globbing.
        !             6:  *
        !             7:  * Copyright 1990 Regents of the University of California
        !             8:  * Permission to use, copy, modify, and distribute this
        !             9:  * software and its documentation for any purpose and without
        !            10:  * fee is hereby granted, provided that the above copyright
        !            11:  * notice appear in all copies.  The University of California
        !            12:  * makes no representations about the suitability of this
        !            13:  * software for any purpose.  It is provided "as is" without
        !            14:  * express or implied warranty.
        !            15:  */
        !            16: 
        !            17: #ifndef lint
        !            18: static char rcsid[] = "$Header: /sprite/src/lib/tcl/RCS/tclGlob.c,v 1.4 90/04/19 14:53:59 ouster Exp $ SPRITE (Berkeley)";
        !            19: #pragma ref rcsid
        !            20: #endif not lint
        !            21: 
        !            22: #define        _POSIX_SOURCE
        !            23: 
        !            24: #include <stdio.h>
        !            25: #include <errno.h>
        !            26: #include <pwd.h>
        !            27: #include <stdlib.h>
        !            28: #include <string.h>
        !            29: #include <sys/types.h>
        !            30: #include <dirent.h>
        !            31: #include <sys/stat.h>
        !            32: #include <tcl.h>
        !            33: #include "tclInt.h"
        !            34: 
        !            35: /*
        !            36:  * The structure below is used to keep track of a globbing result
        !            37:  * being built up (i.e. a partial list of file names).  The list
        !            38:  * grows dynamically to be as big as needed.
        !            39:  */
        !            40: 
        !            41: typedef struct {
        !            42:     char *result;              /* Pointer to result area. */
        !            43:     int totalSpace;            /* Total number of characters allocated
        !            44:                                 * for result. */
        !            45:     int spaceUsed;             /* Number of characters currently in use
        !            46:                                 * to hold the partial result (not including
        !            47:                                 * the terminating NULL). */
        !            48:     int dynamic;               /* 0 means result is static space, 1 means
        !            49:                                 * it's dynamic. */
        !            50: } GlobResult;
        !            51: 
        !            52: /*
        !            53:  *----------------------------------------------------------------------
        !            54:  *
        !            55:  * AppendResult --
        !            56:  *
        !            57:  *     Given two parts of a file name (directory and element within
        !            58:  *     directory), concatenate the two together and add them to a
        !            59:  *     partially-formed result.
        !            60:  *
        !            61:  * Results:
        !            62:  *     There is no return value.  The structure at *resPtr is modified
        !            63:  *     to hold more information.
        !            64:  *
        !            65:  * Side effects:
        !            66:  *     Storage may be allocated if we run out of space in *resPtr.
        !            67:  *
        !            68:  *----------------------------------------------------------------------
        !            69:  */
        !            70: 
        !            71: static void
        !            72: AppendResult(dir, name, nameLength, resPtr)
        !            73:     char *dir;                 /* Name of directory (without trailing
        !            74:                                 * slash). */
        !            75:     char *name;                        /* Name of file withing directory (NOT
        !            76:                                 * necessarily null-terminated!). */
        !            77:     int nameLength;            /* Number of characters in name. */
        !            78:     register GlobResult *resPtr;/* Structure in which to append info. */
        !            79: {
        !            80:     int dirLength, totalLength;
        !            81:     char *p;
        !            82: 
        !            83:     /*
        !            84:      * Make sure there's enough space in the result area for this
        !            85:      * new name (need two extra chars. besides what's in dir and
        !            86:      * name, for a separating space after the last name and for a
        !            87:      * terminating NULL).
        !            88:      */
        !            89: 
        !            90:     dirLength = strlen(dir);
        !            91:     totalLength = resPtr->spaceUsed + dirLength + nameLength + 2;
        !            92:     if (totalLength > resPtr->totalSpace) {
        !            93:        char *newSpace;
        !            94:        int newSize;
        !            95: 
        !            96:        newSize = 2*resPtr->totalSpace;
        !            97:        if (newSize < totalLength) {
        !            98:            newSize = totalLength;
        !            99:        }
        !           100:        newSpace = malloc((unsigned) newSize);
        !           101:        bcopy(resPtr->result, newSpace, resPtr->spaceUsed);
        !           102:        if (resPtr->dynamic) {
        !           103:            free(resPtr->result);
        !           104:        }
        !           105:        resPtr->result = newSpace;
        !           106:        resPtr->totalSpace = newSize;
        !           107:        resPtr->dynamic = 1;
        !           108:     }
        !           109: 
        !           110:     /*
        !           111:      * Now append the new information onto the end of the result.
        !           112:      */
        !           113: 
        !           114:     p = resPtr->result + resPtr->spaceUsed;
        !           115:     if (resPtr->spaceUsed != 0) {
        !           116:        *p = ' ';
        !           117:        p++;
        !           118:        resPtr->spaceUsed++;
        !           119:     }
        !           120:     strcpy(p, dir);
        !           121:     p += dirLength;
        !           122:     strncpy(p, name, nameLength);
        !           123:     p[nameLength] = 0;
        !           124:     resPtr->spaceUsed += nameLength+dirLength;
        !           125: }
        !           126: 
        !           127: /*
        !           128:  *----------------------------------------------------------------------
        !           129:  *
        !           130:  * DoGlob --
        !           131:  *
        !           132:  *     This recursive procedure forms the heart of the globbing
        !           133:  *     code.  It performs a depth-first traversal of the tree
        !           134:  *     given by the path name to be globbed.
        !           135:  *
        !           136:  * Results:
        !           137:  *     The return value is a standard Tcl result indicating whether
        !           138:  *     an error occurred in globbing.  The result in interp will be
        !           139:  *     set to hold an error message, if any.  The result pointed
        !           140:  *     to by resPtr is updated to hold all file names given by
        !           141:  *     the dir and rem arguments.
        !           142:  *
        !           143:  * Side effects:
        !           144:  *     None.
        !           145:  *
        !           146:  *----------------------------------------------------------------------
        !           147:  */
        !           148: 
        !           149: static int
        !           150: DoGlob(interp, dir, rem, resPtr)
        !           151:     Tcl_Interp *interp;                        /* Interpreter to use for error
        !           152:                                         * reporting (e.g. unmatched brace). */
        !           153:     char *dir;                         /* Name of a directory at which to
        !           154:                                         * start glob expansion.  This name
        !           155:                                         * is fixed: it doesn't contain any
        !           156:                                         * globbing chars.  If it's non-empty
        !           157:                                         * then it should end with a slash. */
        !           158:     char *rem;                         /* Path to glob-expand. */
        !           159:     GlobResult *resPtr;                        /* Where to store fully-expanded file
        !           160:                                         * names.*/
        !           161: {
        !           162:     /*
        !           163:      * When this procedure is entered, the name to be globbed may
        !           164:      * already have been partly expanded by ancestor invocations of
        !           165:      * DoGlob.  The part that's already been expanded is in "dir"
        !           166:      * (this may initially be empty), and the part still to expand
        !           167:      * is in "rem".  This procedure expands "rem" one level, making
        !           168:      * recursive calls to itself if there's still more stuff left
        !           169:      * in the remainder.
        !           170:      */
        !           171: 
        !           172:     register char *p;
        !           173:     register char c;
        !           174:     char *openBrace, *closeBrace;
        !           175:     int gotSpecial, result;
        !           176: 
        !           177:     /*
        !           178:      * When generating information for the next lower call,
        !           179:      * use static areas if the name is short, and malloc if the name
        !           180:      * is longer.
        !           181:      */
        !           182: 
        !           183: #define STATIC_SIZE 200
        !           184: 
        !           185:     /*
        !           186:      * First, find the end of the next element in rem, checking
        !           187:      * along the way for special globbing characters.
        !           188:      */
        !           189: 
        !           190:     gotSpecial = 0;
        !           191:     openBrace = closeBrace = NULL;
        !           192:     for (p = rem; ; p++) {
        !           193:        c = *p;
        !           194:        if ((c == '\0') || (c == '/')) {
        !           195:            break;
        !           196:        }
        !           197:        if ((c == '{') && (openBrace == NULL)) {
        !           198:            openBrace = p;
        !           199:        }
        !           200:        if ((c == '}') && (closeBrace == NULL)) {
        !           201:            closeBrace = p;
        !           202:        }
        !           203:        if ((c == '*') || (c == '[') || (c == '\\') || (c == '?')) {
        !           204:            gotSpecial = 1;
        !           205:        }
        !           206:     }
        !           207: 
        !           208:     /*
        !           209:      * If there is an open brace in the argument, then make a recursive
        !           210:      * call for each element between the braces.  In this case, the
        !           211:      * recursive call to DoGlob uses the same "dir" that we got.
        !           212:      * If there are several brace-pairs in a single name, we just handle
        !           213:      * one here, and the others will be handled in recursive calls.
        !           214:      */
        !           215: 
        !           216:     if (openBrace != NULL) {
        !           217:        int remLength, l1, l2;
        !           218:        char static1[STATIC_SIZE];
        !           219:        char *element, *newRem;
        !           220: 
        !           221:        if (closeBrace == NULL) {
        !           222:            interp->result = "unmatched open-brace in file name";
        !           223:            return TCL_ERROR;
        !           224:        }
        !           225:        remLength = strlen(rem) + 1;
        !           226:        if (remLength <= STATIC_SIZE) {
        !           227:            newRem = static1;
        !           228:        } else {
        !           229:            newRem = malloc((unsigned) remLength);
        !           230:        }
        !           231:        l1 = openBrace-rem;
        !           232:        strncpy(newRem, rem, l1);
        !           233:        p = openBrace;
        !           234:        for (p = openBrace; *p != '}'; ) {
        !           235:            element = p+1;
        !           236:            for (p = element; ((*p != '}') && (*p != ',')); p++) {
        !           237:                /* Empty body:  just find end of this element. */
        !           238:            }
        !           239:            l2 = p - element;
        !           240:            strncpy(newRem+l1, element, l2);
        !           241:            strcpy(newRem+l1+l2, closeBrace+1);
        !           242:            if (DoGlob(interp, dir, newRem, resPtr) != TCL_OK) {
        !           243:                return TCL_ERROR;
        !           244:            }
        !           245:        }
        !           246:        if (remLength > STATIC_SIZE) {
        !           247:            free(newRem);
        !           248:        }
        !           249:        return TCL_OK;
        !           250:     }
        !           251: 
        !           252:     /*
        !           253:      * If there were any pattern-matching characters, then scan through
        !           254:      * the directory to find all the matching names.
        !           255:      */
        !           256: 
        !           257:     if (gotSpecial) {
        !           258:        DIR *d;
        !           259:        struct dirent *entryPtr;
        !           260:        int l1, l2;
        !           261:        char *pattern, *newDir;
        !           262:        char static1[STATIC_SIZE], static2[STATIC_SIZE];
        !           263:        struct stat statBuf;
        !           264: 
        !           265:        if ((stat(dir, &statBuf) != 0)
        !           266:                || !S_ISDIR(statBuf.st_mode)) {
        !           267:            return TCL_OK;
        !           268:        }
        !           269:        d = opendir(dir);
        !           270:        if (d == NULL) {
        !           271:            sprintf(interp->result,
        !           272:                    "couldn't read directory \"%.50s\": %.50s",
        !           273:                    dir, strerror(errno));
        !           274:            return TCL_ERROR;
        !           275:        }
        !           276:        l1 = strlen(dir);
        !           277:        l2 = (p - rem);
        !           278:        if (l2 < STATIC_SIZE) {
        !           279:            pattern = static2;
        !           280:        } else {
        !           281:            pattern = malloc((unsigned) (l2+1));
        !           282:        }
        !           283:        strncpy(pattern, rem, l2);
        !           284:        pattern[l2] = '\0';
        !           285:        result = TCL_OK;
        !           286:        while (1) {
        !           287:            entryPtr = readdir(d);
        !           288:            if (entryPtr == NULL) {
        !           289:                break;
        !           290:            }
        !           291: 
        !           292:            /*
        !           293:             * Don't match names starting with "." unless the "." is
        !           294:             * present in the pattern.
        !           295:             */
        !           296: 
        !           297:            if ((*entryPtr->d_name == '.') && (*pattern != '.')) {
        !           298:                continue;
        !           299:            }
        !           300:            if (Tcl_StringMatch(entryPtr->d_name, pattern)) {
        !           301:                if (*p == 0) {
        !           302:                    AppendResult(dir, entryPtr->d_name,
        !           303:                            (int) entryPtr->d_namlen, resPtr);
        !           304:                } else {
        !           305:                    if ((l1+entryPtr->d_namlen+2) <= STATIC_SIZE) {
        !           306:                        newDir = static1;
        !           307:                    } else {
        !           308:                        newDir = malloc((unsigned) (l1+entryPtr->d_namlen+2));
        !           309:                    }
        !           310:                    sprintf(newDir, "%s%s/", dir, entryPtr->d_name);
        !           311:                    result = DoGlob(interp, newDir, p+1, resPtr);
        !           312:                    if (newDir != static1) {
        !           313:                        free(newDir);
        !           314:                    }
        !           315:                    if (result != TCL_OK) {
        !           316:                        break;
        !           317:                    }
        !           318:                }
        !           319:            }
        !           320:        }
        !           321:        if (pattern != static2) {
        !           322:            free(pattern);
        !           323:        }
        !           324:        return result;
        !           325:     }
        !           326: 
        !           327:     /*
        !           328:      * This is the simplest case:  just another path element.  Move
        !           329:      * it to the dir side and recurse (or just add the name to the
        !           330:      * list, if we're at the end of the path).
        !           331:      */
        !           332: 
        !           333:     if (*p == 0) {
        !           334:        AppendResult(dir, rem, p-rem, resPtr);
        !           335:     } else {
        !           336:        int l1, l2;
        !           337:        char *newDir;
        !           338:        char static1[STATIC_SIZE];
        !           339: 
        !           340:        l1 = strlen(dir);
        !           341:        l2 = l1 + (p - rem) + 2;
        !           342:        if (l2 <= STATIC_SIZE) {
        !           343:            newDir = static1;
        !           344:        } else {
        !           345:            newDir = malloc((unsigned) l2);
        !           346:        }
        !           347:        strcpy(newDir, dir);
        !           348:        strncpy(newDir+l1, rem, p-rem);
        !           349:        newDir[l2-2] = '/';
        !           350:        newDir[l2-1] = 0;
        !           351:        result = DoGlob(interp, newDir, p+1, resPtr);
        !           352:        if (newDir != static1) {
        !           353:            free(newDir);
        !           354:        }
        !           355:        if (result != TCL_OK) {
        !           356:            return TCL_ERROR;
        !           357:        }
        !           358:     }
        !           359:     return TCL_OK;
        !           360: }
        !           361: 
        !           362: /*
        !           363:  *----------------------------------------------------------------------
        !           364:  *
        !           365:  * Tcl_TildeSubst --
        !           366:  *
        !           367:  *     Given a name starting with a tilde, produce a name where
        !           368:  *     the tilde and following characters have been replaced by
        !           369:  *     the home directory location for the named user.
        !           370:  *
        !           371:  * Results:
        !           372:  *     The result is a pointer to a static string containing
        !           373:  *     the new name.  This name will only persist until the next
        !           374:  *     call to Tcl_TildeSubst;  save it if you care about it for
        !           375:  *     the long term.  If there was an error in processing the
        !           376:  *     tilde, then an error message is left in interp->result
        !           377:  *     and the return value is NULL.
        !           378:  *
        !           379:  * Side effects:
        !           380:  *     None that the caller needs to worry about.
        !           381:  *
        !           382:  *----------------------------------------------------------------------
        !           383:  */
        !           384: 
        !           385: char *
        !           386: Tcl_TildeSubst(interp, name)
        !           387:     Tcl_Interp *interp;                /* Interpreter in which to store error
        !           388:                                 * message (if necessary). */
        !           389:     char *name;                        /* File name, which may begin with "~/"
        !           390:                                 * (to indicate current user's home directory)
        !           391:                                 * or "~<user>/" (to indicate any user's
        !           392:                                 * home directory). */
        !           393: {
        !           394: #define STATIC_BUF_SIZE 50
        !           395:     static char staticBuf[STATIC_BUF_SIZE];
        !           396:     static int curSize = STATIC_BUF_SIZE;
        !           397:     static char *curBuf = staticBuf;
        !           398:     char *dir;
        !           399:     int length;
        !           400:     int fromPw = 0;
        !           401:     register char *p;
        !           402: 
        !           403:     if (name[0] != '~') {
        !           404:        return name;
        !           405:     }
        !           406: 
        !           407:     /*
        !           408:      * First, find the directory name corresponding to the tilde entry.
        !           409:      */
        !           410: 
        !           411:     if ((name[1] == '/') || (name[1] == '\0')) {
        !           412:        dir = getenv("HOME");
        !           413:        if (dir == NULL) {
        !           414:            sprintf(interp->result,
        !           415:                    "couldn't find HOME env. variable to expand \"%.100s\"",
        !           416:                    name);
        !           417:            return NULL;
        !           418:        }
        !           419:        p = name+1;
        !           420:     } else {
        !           421:        struct passwd *pwPtr;
        !           422: 
        !           423:        for (p = &name[1]; (*p != 0) && (*p != '/'); p++) {
        !           424:            /* Null body;  just find end of name. */
        !           425:        }
        !           426:        length = p-&name[1];
        !           427:        if (length >= curSize) {
        !           428:            length = curSize-1;
        !           429:        }
        !           430:        bcopy(name+1, curBuf, length);
        !           431:        curBuf[length] = '\0';
        !           432:        pwPtr = getpwnam(curBuf);
        !           433:        if (pwPtr == NULL) {
        !           434:            sprintf(interp->result, "user \"%.50s\" doesn't exist", curBuf);
        !           435:            return NULL;
        !           436:        }
        !           437:        dir = pwPtr->pw_dir;
        !           438:        fromPw = 1;
        !           439:     }
        !           440: 
        !           441:     /*
        !           442:      * Grow the buffer if necessary to make enough space for the
        !           443:      * full file name.
        !           444:      */
        !           445: 
        !           446:     length = strlen(dir) + strlen(p);
        !           447:     if (length >= curSize) {
        !           448:        if (curBuf != staticBuf) {
        !           449:            free(curBuf);
        !           450:        }
        !           451:        curSize = length + 1;
        !           452:        curBuf = malloc((unsigned) curSize);
        !           453:     }
        !           454: 
        !           455:     /*
        !           456:      * Finally, concatenate the directory name with the remainder
        !           457:      * of the path in the buffer.
        !           458:      */
        !           459: 
        !           460:     strcpy(curBuf, dir);
        !           461:     strcat(curBuf, p);
        !           462:     if (fromPw) {
        !           463:        endpwent();
        !           464:     }
        !           465:     return curBuf;
        !           466: }
        !           467: 
        !           468: /*
        !           469:  *----------------------------------------------------------------------
        !           470:  *
        !           471:  * Tcl_GlobCmd --
        !           472:  *
        !           473:  *     This procedure is invoked to process the "glob" Tcl command.
        !           474:  *     See the user documentation for details on what it does.
        !           475:  *
        !           476:  * Results:
        !           477:  *     A standard Tcl result.
        !           478:  *
        !           479:  * Side effects:
        !           480:  *     See the user documentation.
        !           481:  *
        !           482:  *----------------------------------------------------------------------
        !           483:  */
        !           484: 
        !           485:        /* ARGSUSED */
        !           486: int
        !           487: Tcl_GlobCmd(dummy, interp, argc, argv)
        !           488:     ClientData dummy;                  /* Not used. */
        !           489:     Tcl_Interp *interp;                        /* Current interpreter. */
        !           490:     int argc;                          /* Number of arguments. */
        !           491:     char **argv;                       /* Argument strings. */
        !           492: {
        !           493: #pragma ref dummy
        !           494:     GlobResult globRes;
        !           495:     char staticSpace[TCL_RESULT_SIZE];
        !           496:     int i, result;
        !           497: 
        !           498:     globRes.result = staticSpace;
        !           499:     globRes.totalSpace = TCL_RESULT_SIZE;
        !           500:     globRes.spaceUsed = 0;
        !           501:     globRes.dynamic = 0;
        !           502:     for (i = 1; i < argc; i++) {
        !           503:        char *thisName;
        !           504: 
        !           505:        /*
        !           506:         * Do special checks for names starting at the root and for
        !           507:         * names beginning with ~.  Then let DoGlob do the rest.
        !           508:         */
        !           509: 
        !           510:        thisName = argv[i];
        !           511:        if (*thisName == '~') {
        !           512:            thisName = Tcl_TildeSubst(interp, thisName);
        !           513:            if (thisName == NULL) {
        !           514:                return TCL_ERROR;
        !           515:            }
        !           516:        }
        !           517:        if (*thisName == '/') {
        !           518:            result = DoGlob(interp, "/", thisName+1, &globRes);
        !           519:        } else {
        !           520:            result = DoGlob(interp, "", thisName, &globRes);
        !           521:        }
        !           522:        if (result != TCL_OK) {
        !           523:            goto error;
        !           524:        }
        !           525:     }
        !           526:     if (globRes.spaceUsed == 0) {
        !           527:        sprintf(interp->result, "no files matched glob pattern(s)");
        !           528:        result = TCL_ERROR;
        !           529:        goto error;
        !           530:     }
        !           531:     if (globRes.dynamic) {
        !           532:        interp->result = globRes.result;
        !           533:        interp->dynamic = 1;
        !           534:     } else {
        !           535:        strcpy(interp->result, globRes.result);
        !           536:     }
        !           537:     return TCL_OK;
        !           538: 
        !           539:     error:
        !           540:     if (globRes.dynamic) {
        !           541:        free(globRes.result);
        !           542:     }
        !           543:     return result;
        !           544: }

unix.superglobalmegacorp.com

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