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

unix.superglobalmegacorp.com

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