Annotation of researchv10dc/cmd/worm/scsi/tcl/tclGlob.c, revision 1.1.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.