Annotation of researchv10dc/cmd/worm/scsi/tcl/tclUtil.c, revision 1.1.1.1

1.1       root        1: /* 
                      2:  * tclUtil.c --
                      3:  *
                      4:  *     This file contains utility procedures that are used by many Tcl
                      5:  *     commands.
                      6:  *
                      7:  * Copyright 1987, 1989 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/tclUtil.c,v 1.30 90/03/25 11:04:25 ouster Exp $ SPRITE (Berkeley)";
                     19: #pragma ref rcsid
                     20: #endif not lint
                     21: 
                     22: #define        _POSIX_SOURCE
                     23: 
                     24: #include <ctype.h>
                     25: #include <stdio.h>
                     26: #include <stdlib.h>
                     27: #include <string.h>
                     28: #include "tcl.h"
                     29: #include "tclInt.h"
                     30: 
                     31: /*
                     32:  *----------------------------------------------------------------------
                     33:  *
                     34:  * TclFindElement --
                     35:  *
                     36:  *     Given a pointer into a Tcl list, locate the first (or next)
                     37:  *     element in the list.
                     38:  *
                     39:  * Results:
                     40:  *     The return value is normally TCL_OK, which means that the
                     41:  *     element was successfully located.  If TCL_ERROR is returned
                     42:  *     it means that list didn't have proper list structure;
                     43:  *     interp->result contains a more detailed error message.
                     44:  *
                     45:  *     If TCL_OK is returned, then *elementPtr will be set to point
                     46:  *     to the first element of list, and *nextPtr will be set to point
                     47:  *     to the character just after any white space following the last
                     48:  *     character that's part of the element.  If this is the last argument
                     49:  *     in the list, then *nextPtr will point to the NULL character at the
                     50:  *     end of list.  If sizePtr is non-NULL, *sizePtr is filled in with
                     51:  *     the number of characters in the element.  If the element is in
                     52:  *     braces, then *elementPtr will point to the character after the
                     53:  *     opening brace and *sizePtr will not include either of the braces.
                     54:  *     If there isn't an element in the list, *sizePtr will be zero, and
                     55:  *     both *elementPtr and *termPtr will refer to the null character at
                     56:  *     the end of list.  Note:  this procedure does NOT collapse backslash
                     57:  *     sequences.
                     58:  *
                     59:  * Side effects:
                     60:  *     None.
                     61:  *
                     62:  *----------------------------------------------------------------------
                     63:  */
                     64: 
                     65: int
                     66: TclFindElement(interp, list, elementPtr, nextPtr, sizePtr, bracePtr)
                     67:     Tcl_Interp *interp;                /* Interpreter to use for error reporting. */
                     68:     register char *list;       /* String containing Tcl list with zero
                     69:                                 * or more elements (possibly in braces). */
                     70:     char **elementPtr;         /* Fill in with location of first significant
                     71:                                 * character in first element of list. */
                     72:     char **nextPtr;            /* Fill in with location of character just
                     73:                                 * after all white space following end of
                     74:                                 * argument (i.e. next argument or end of
                     75:                                 * list). */
                     76:     int *sizePtr;              /* If non-zero, fill in with size of
                     77:                                 * element. */
                     78:     int *bracePtr;             /* If non-zero fill in with non-zero/zero
                     79:                                 * to indicate that arg was/wasn't
                     80:                                 * in braces. */
                     81: {
                     82:     register char *p;
                     83:     int openBraces = 0;
                     84:     int size;
                     85: 
                     86:     /*
                     87:      * Skim off leading white space and check for an opening brace.
                     88:      */
                     89: 
                     90:     while (isspace(*list)) {
                     91:        list++;
                     92:     }
                     93:     if (*list == '{') {
                     94:        openBraces = 1;
                     95:        list++;
                     96:     }
                     97:     if (bracePtr != 0) {
                     98:        *bracePtr = openBraces;
                     99:     }
                    100:     p = list;
                    101: 
                    102:     /*
                    103:      * Find the end of the element (either a space or a close brace or
                    104:      * the end of the string).
                    105:      */
                    106: 
                    107:     while (1) {
                    108:        switch (*p) {
                    109: 
                    110:            /*
                    111:             * Open brace: don't treat specially unless the element is
                    112:             * in braces.  In this case, keep a nesting count.
                    113:             */
                    114: 
                    115:            case '{':
                    116:                if (openBraces != 0) {
                    117:                    openBraces++;
                    118:                }
                    119:                break;
                    120: 
                    121:            /*
                    122:             * Close brace: if element is in braces, keep nesting
                    123:             * count and quit when the last close brace is seen.
                    124:             */
                    125: 
                    126:            case '}':
                    127:                if (openBraces == 1) {
                    128:                    char *p2;
                    129: 
                    130:                    size = p - list;
                    131:                    p++;
                    132:                    if (isspace(*p) || (*p == 0)) {
                    133:                        goto done;
                    134:                    }
                    135:                    for (p2 = p; (*p2 != 0) && (!isspace(*p2)) && (p2 < p+20);
                    136:                            p2++) {
                    137:                        /* null body */
                    138:                    }
                    139:                    Tcl_Return(interp, (char *) NULL, TCL_STATIC);
                    140:                    sprintf(interp->result,
                    141:                            "list element in braces followed by \"%.*s\" instead of space",
                    142:                            p2-p, p);
                    143:                    return TCL_ERROR;
                    144:                } else if (openBraces != 0) {
                    145:                    openBraces--;
                    146:                }
                    147:                break;
                    148: 
                    149:            /*
                    150:             * Backslash:  skip over everything up to the end of the
                    151:             * backslash sequence.
                    152:             */
                    153: 
                    154:            case '\\': {
                    155:                int size;
                    156: 
                    157:                (void) Tcl_Backslash(p, &size);
                    158:                p += size - 1;
                    159:                break;
                    160:            }
                    161: 
                    162:            /*
                    163:             * Space: ignore if element is in braces;  otherwise
                    164:             * terminate element.
                    165:             */
                    166: 
                    167:            case ' ':
                    168:            case '\t':
                    169:            case '\n':
                    170:                if (openBraces == 0) {
                    171:                    size = p - list;
                    172:                    goto done;
                    173:                }
                    174:                break;
                    175: 
                    176:            /*
                    177:             * End of list:  terminate element.
                    178:             */
                    179: 
                    180:            case 0:
                    181:                if (openBraces != 0) {
                    182:                    Tcl_Return(interp, "unmatched open brace in list",
                    183:                            TCL_STATIC);
                    184:                    return TCL_ERROR;
                    185:                }
                    186:                size = p - list;
                    187:                goto done;
                    188: 
                    189:        }
                    190:        p++;
                    191:     }
                    192: 
                    193:     done:
                    194:     while (isspace(*p)) {
                    195:        p++;
                    196:     }
                    197:     *elementPtr = list;
                    198:     *nextPtr = p;
                    199:     if (sizePtr != 0) {
                    200:        *sizePtr = size;
                    201:     }
                    202:     return TCL_OK;
                    203: }
                    204: 
                    205: /*
                    206:  *----------------------------------------------------------------------
                    207:  *
                    208:  * TclCopyAndCollapse --
                    209:  *
                    210:  *     Copy a string and eliminate any backslashes that aren't in braces.
                    211:  *
                    212:  * Results:
                    213:  *     There is no return value.  Count chars. get copied from src
                    214:  *     to dst.  Along the way, if backslash sequences are found outside
                    215:  *     braces, the backslashes are eliminated in the copy.
                    216:  *     After scanning count chars. from source, a null character is
                    217:  *     placed at the end of dst.
                    218:  *
                    219:  * Side effects:
                    220:  *     None.
                    221:  *
                    222:  *----------------------------------------------------------------------
                    223:  */
                    224: 
                    225: void
                    226: TclCopyAndCollapse(count, src, dst)
                    227:     register char *src;                /* Copy from here... */
                    228:     register char *dst;                /* ... to here. */
                    229: {
                    230:     register char c;
                    231:     int numRead;
                    232: 
                    233:     for (c = *src; count > 0; dst++, src++, c = *src, count--) {
                    234:        if (c == '\\') {
                    235:            *dst = Tcl_Backslash(src, &numRead);
                    236:            src += numRead-1;
                    237:            count -= numRead-1;
                    238:        } else {
                    239:            *dst = c;
                    240:        }
                    241:     }
                    242:     *dst = 0;
                    243: }
                    244: 
                    245: /*
                    246:  *----------------------------------------------------------------------
                    247:  *
                    248:  * Tcl_Merge --
                    249:  *
                    250:  *     Given a collection of strings, merge them together into a
                    251:  *     single string that has proper Tcl list structured (i.e.
                    252:  *     TclFindElement and TclCopyAndCollapse may be used to retrieve
                    253:  *     strings equal to the original elements, and Tcl_Eval will
                    254:  *     parse the string back into its original elements).
                    255:  *
                    256:  * Results:
                    257:  *     The return value is the address of a dynamically-allocated
                    258:  *     string containing the merged list.
                    259:  *
                    260:  * Side effects:
                    261:  *     None.
                    262:  *
                    263:  *----------------------------------------------------------------------
                    264:  */
                    265: 
                    266: char *
                    267: Tcl_Merge(argc, argv)
                    268:     int argc;                  /* How many strings to merge. */
                    269:     char **argv;               /* Array of string values. */
                    270: {
                    271:     /*
                    272:      * This procedure operates in two passes.  In the first pass it figures
                    273:      * out how many bytes will be needed to store the result (actually,
                    274:      * it overestimates slightly).  The first pass also collects information
                    275:      * about each element in the form of a flags word.  If there are only
                    276:      * a few elements, local storage gets used for the flags;  if there are
                    277:      * a lot of elements, a new array is dynamically allocated.
                    278:      *
                    279:      * In the second pass this procedure copies the arguments into the
                    280:      * result string.  The special cases to worry about are:
                    281:      *
                    282:      * 1. Argument contains embedded spaces, or starts with a brace:  must
                    283:      * add another level of braces when copying to the result.
                    284:      *
                    285:      * 2. Argument contains unbalanced braces:  backslash all of the
                    286:      * braces when copying to the result.  In this case, don't add another
                    287:      * level of braces (they would prevent the backslash from
                    288:      * being removed when the argument is extracted from the list later).
                    289:      *
                    290:      * 3. Argument contains backslashed brace/bracket:  if possible,
                    291:      * group the argument in braces:  then no special action needs to be taken
                    292:      * with the backslashes.  If the argument can't be put in braces, then
                    293:      * add another backslash in front of the sequence, so that upon
                    294:      * extraction the original sequence will be restored.
                    295:      *
                    296:      * These potential problems are the reasons why particular information
                    297:      * is gathered during pass 1.
                    298:      */
                    299: #   define WANT_PARENS                 1
                    300: #   define PARENS_UNBALANCED           2
                    301: #   define PARENTHESIZED               4
                    302: #   define CANT_PARENTHESIZE           8
                    303: 
                    304: #   define LOCAL_SIZE 20
                    305:     int localFlags[LOCAL_SIZE];
                    306:     int *flagPtr;
                    307:     int numChars;
                    308:     char *result;
                    309:     register char *src, *dst;
                    310:     register int curFlags;
                    311:     int i;
                    312: 
                    313:     /*
                    314:      * Pass 1: estimate space, gather information.
                    315:      */
                    316: 
                    317:     if (argc <= LOCAL_SIZE) {
                    318:        flagPtr = localFlags;
                    319:     } else {
                    320:        flagPtr = (int *) malloc((unsigned) argc*sizeof(int));
                    321:     }
                    322:     numChars = 0;
                    323:     for (i = 0; i < argc; i++) {
                    324:        int braceCount, nestingLevel, nestedBS, whiteSpace, brackets, dollars;
                    325: 
                    326:        curFlags = braceCount = nestingLevel = nestedBS = whiteSpace = 0;
                    327:        brackets = dollars = 0;
                    328:        src = argv[i];
                    329:        if (*src == '{') {
                    330:            curFlags |= PARENTHESIZED|WANT_PARENS;
                    331:        }
                    332:        if (*src == 0) {
                    333:            curFlags |= WANT_PARENS;
                    334:        } else {
                    335:            for (; ; src++) {
                    336:                switch (*src) {
                    337:                    case '{':
                    338:                        braceCount++;
                    339:                        nestingLevel++;
                    340:                        break;
                    341:                    case '}':
                    342:                        braceCount++;
                    343:                        nestingLevel--;
                    344:                        break;
                    345:                    case ']':
                    346:                    case '[':
                    347:                        curFlags |= WANT_PARENS;
                    348:                        brackets++;
                    349:                        break;
                    350:                    case '$':
                    351:                        curFlags |= WANT_PARENS;
                    352:                        dollars++;
                    353:                        break;
                    354:                    case ' ':
                    355:                    case '\n':
                    356:                    case '\t':
                    357:                        curFlags |= WANT_PARENS;
                    358:                        whiteSpace++;
                    359:                        break;
                    360:                    case '\\':
                    361:                        src++;
                    362:                        if (*src == 0) {
                    363:                            goto elementDone;
                    364:                        } else if ((*src == '{') || (*src == '}')
                    365:                                || (*src == '[') || (*src == ']')) {
                    366:                            curFlags |= WANT_PARENS;
                    367:                            nestedBS++;
                    368:                        }
                    369:                        break;
                    370:                    case 0:
                    371:                        goto elementDone;
                    372:                }
                    373:            }
                    374:        }
                    375:        elementDone:
                    376:        numChars += src - argv[i];
                    377:        if (nestingLevel != 0) {
                    378:            numChars += braceCount + nestedBS + whiteSpace
                    379:                    + brackets + dollars;
                    380:            curFlags = CANT_PARENTHESIZE;
                    381:        }
                    382:        if (curFlags & WANT_PARENS) {
                    383:            numChars += 2;
                    384:        }
                    385:        numChars++;             /* Space to separate arguments. */
                    386:        flagPtr[i] = curFlags;
                    387:     }
                    388: 
                    389:     /*
                    390:      * Pass two: copy into the result area.
                    391:      */
                    392: 
                    393:     result = (char *) malloc((unsigned) numChars + 1);
                    394:     dst = result;
                    395:     for (i = 0; i < argc; i++) {
                    396:        curFlags = flagPtr[i];
                    397:        if (curFlags & WANT_PARENS) {
                    398:            *dst = '{';
                    399:            dst++;
                    400:        }
                    401:        for (src = argv[i]; *src != 0 ; src++) {
                    402:            if (curFlags & CANT_PARENTHESIZE) {
                    403:                switch (*src) {
                    404:                    case '{':
                    405:                    case '}':
                    406:                    case ']':
                    407:                    case '[':
                    408:                    case '$':
                    409:                    case ' ':
                    410:                        *dst = '\\';
                    411:                        dst++;
                    412:                        break;
                    413:                    case '\n':
                    414:                        *dst = '\\';
                    415:                        dst++;
                    416:                        *dst = 'n';
                    417:                        goto loopBottom;
                    418:                    case '\t':
                    419:                        *dst = '\\';
                    420:                        dst++;
                    421:                        *dst = 't';
                    422:                        goto loopBottom;
                    423:                    case '\\':
                    424:                        *dst = '\\';
                    425:                        dst++;
                    426:                        src++;
                    427:                        if ((*src == '{') || (*src == '}') || (*src == '[')
                    428:                                || (*src == ']')) {
                    429:                            *dst = '\\';
                    430:                            dst++;
                    431:                        } else if (*src == 0) {
                    432:                            goto pass2ElementDone;
                    433:                        }
                    434:                        break;
                    435:                }
                    436:            }
                    437:            *dst = *src;
                    438:            loopBottom:
                    439:            dst++;
                    440:        }
                    441:        pass2ElementDone:
                    442:        if (curFlags & WANT_PARENS) {
                    443:            *dst = '}';
                    444:            dst++;
                    445:        }
                    446:        *dst = ' ';
                    447:        dst++;
                    448:     }
                    449:     if (dst == result) {
                    450:        *dst = 0;
                    451:     } else {
                    452:        dst[-1] = 0;
                    453:     }
                    454: 
                    455:     if (flagPtr != localFlags) {
                    456:        free((char *) flagPtr);
                    457:     }
                    458:     return result;
                    459: }
                    460: 
                    461: /*
                    462:  *----------------------------------------------------------------------
                    463:  *
                    464:  * Tcl_Concat --
                    465:  *
                    466:  *     Concatenate a set of strings into a single large string.
                    467:  *
                    468:  * Results:
                    469:  *     The return value is dynamically-allocated string containing
                    470:  *     a concatenation of all the strings in argv, with spaces between
                    471:  *     the original argv elements.
                    472:  *
                    473:  * Side effects:
                    474:  *     Memory is allocated for the result;  the caller is responsible
                    475:  *     for freeing the memory.
                    476:  *
                    477:  *----------------------------------------------------------------------
                    478:  */
                    479: 
                    480: char *
                    481: Tcl_Concat(argc, argv)
                    482:     int argc;                  /* Number of strings to concatenate. */
                    483:     char **argv;               /* Array of strings to concatenate. */
                    484: {
                    485:     int totalSize, i;
                    486:     register char *p;
                    487:     char *result;
                    488: 
                    489:     for (totalSize = 1, i = 0; i < argc; i++) {
                    490:        totalSize += strlen(argv[i]) + 1;
                    491:     }
                    492:     result = malloc((unsigned) totalSize);
                    493:     for (p = result, i = 0; i < argc; i++) {
                    494:        (void) strcpy(p, argv[i]);
                    495:        p += strlen(argv[i]);
                    496:        *p = ' ';
                    497:        p++;
                    498:     }
                    499:     p[-1] = 0;
                    500:     return result;
                    501: }
                    502: 
                    503: /*
                    504:  *----------------------------------------------------------------------
                    505:  *
                    506:  * Tcl_Return --
                    507:  *
                    508:  *     Arrange for "string" to be the Tcl return value.
                    509:  *
                    510:  * Results:
                    511:  *     None.
                    512:  *
                    513:  * Side effects:
                    514:  *     interp->result is left pointing either to "string" (if "copy" is 0)
                    515:  *     or to a copy of string.
                    516:  *
                    517:  *----------------------------------------------------------------------
                    518:  */
                    519: 
                    520: void
                    521: Tcl_Return(interp, string, status)
                    522:     Tcl_Interp *interp;                /* Interpreter with which to associate the
                    523:                                 * return value. */
                    524:     char *string;              /* Value to be returned.  If NULL,
                    525:                                 * the result is set to an empty string. */
                    526:     int status;                        /* Gives information about the string:
                    527:                                 * TCL_STATIC, TCL_DYNAMIC, TCL_VOLATILE.
                    528:                                 * Ignored if string is NULL. */
                    529: {
                    530:     register Interp *iPtr = (Interp *) interp;
                    531:     int length;
                    532:     int wasDynamic = iPtr->dynamic;
                    533:     char *oldResult = iPtr->result;
                    534: 
                    535:     if (string == NULL) {
                    536:        iPtr->resultSpace[0] = 0;
                    537:        iPtr->result = iPtr->resultSpace;
                    538:        iPtr->dynamic = 0;
                    539:     } else if (status == TCL_STATIC) {
                    540:        iPtr->result = string;
                    541:        iPtr->dynamic = 0;
                    542:     } else if (status == TCL_DYNAMIC) {
                    543:        iPtr->result = string;
                    544:        iPtr->dynamic = 1;
                    545:     } else {
                    546:        length = strlen(string);
                    547:        if (length > TCL_RESULT_SIZE) {
                    548:            iPtr->result = (char *) malloc((unsigned) length+1);
                    549:            iPtr->dynamic = 1;
                    550:        } else {
                    551:            iPtr->result = iPtr->resultSpace;
                    552:            iPtr->dynamic = 0;
                    553:        }
                    554:        strcpy(iPtr->result, string);
                    555:     }
                    556: 
                    557:     /*
                    558:      * If the old result was dynamically-allocated, free it up.  Do it
                    559:      * here, rather than at the beginning, in case the new result value
                    560:      * was part of the old result value.
                    561:      */
                    562: 
                    563:     if (wasDynamic) {
                    564:        free(oldResult);
                    565:     }
                    566: }
                    567: 
                    568: /*
                    569:  *----------------------------------------------------------------------
                    570:  *
                    571:  * Tcl_Backslash --
                    572:  *
                    573:  *     Figure out how to handle a backslash sequence.
                    574:  *
                    575:  * Results:
                    576:  *     The return value is the character that should be substituted
                    577:  *     in place of the backslash sequence that starts at src.  If
                    578:  *     readPtr isn't NULL then it is filled in with a count of the
                    579:  *     number of characters in the backslash sequence.  Note:  if
                    580:  *     the backslash isn't followed by characters that are understood
                    581:  *     here, then the backslash sequence is only considered to be
                    582:  *     one character long, and it is replaced by a backslash char.
                    583:  *
                    584:  * Side effects:
                    585:  *     None.
                    586:  *
                    587:  *----------------------------------------------------------------------
                    588:  */
                    589: 
                    590: char
                    591: Tcl_Backslash(src, readPtr)
                    592:     char *src;                 /* Points to the backslash character of
                    593:                                 * a backslash sequence. */
                    594:     int *readPtr;              /* Fill in with number of characters read
                    595:                                 * from src, unless NULL. */
                    596: {
                    597:     register char *p = src+1;
                    598:     char result;
                    599:     int count;
                    600: 
                    601:     count = 2;
                    602: 
                    603:     switch (*p) {
                    604:        case 'b':
                    605:            result = '\b';
                    606:            break;
                    607:        case 'e':
                    608:            result = 033;
                    609:            break;
                    610:        case 'n':
                    611:            result = '\n';
                    612:            break;
                    613:        case 'r':
                    614:            result = '\r';
                    615:            break;
                    616:        case 't':
                    617:            result = '\t';
                    618:            break;
                    619:        case 'C':
                    620:            p++;
                    621:            if (isspace(*p) || (*p == 0)) {
                    622:                result = 'C';
                    623:                count = 1;
                    624:                break;
                    625:            }
                    626:            count = 3;
                    627:            if (*p == 'M') {
                    628:                p++;
                    629:                if (isspace(*p) || (*p == 0)) {
                    630:                    result = 'M' & 037;
                    631:                    break;
                    632:                }
                    633:                count = 4;
                    634:                result = (*p & 037) | 0200;
                    635:                break;
                    636:            }
                    637:            count = 3;
                    638:            result = *p & 037;
                    639:            break;
                    640:        case 'M':
                    641:            p++;
                    642:            if (isspace(*p) || (*p == 0)) {
                    643:                result = 'M';
                    644:                count = 1;
                    645:                break;
                    646:            }
                    647:            count = 3;
                    648:            result = *p + 0200;
                    649:            break;
                    650:        case '}':
                    651:        case '{':
                    652:        case ']':
                    653:        case '[':
                    654:        case '$':
                    655:        case ' ':
                    656:        case ';':
                    657:        case '"':
                    658:        case '\\':
                    659:            result = *p;
                    660:            break;
                    661:        default:
                    662:            if (isdigit(*p)) {
                    663:                result = *p - '0';
                    664:                p++;
                    665:                if (!isdigit(*p)) {
                    666:                    break;
                    667:                }
                    668:                count = 3;
                    669:                result = (result << 3) + (*p - '0');
                    670:                p++;
                    671:                if (!isdigit(*p)) {
                    672:                    break;
                    673:                }
                    674:                count = 4;
                    675:                result = (result << 3) + (*p - '0');
                    676:                break;
                    677:            }
                    678:            result = '\\';
                    679:            count = 1;
                    680:            break;
                    681:     }
                    682: 
                    683:     if (readPtr != NULL) {
                    684:        *readPtr = count;
                    685:     }
                    686:     return result;
                    687: }
                    688: 
                    689: /*
                    690:  *----------------------------------------------------------------------
                    691:  *
                    692:  * Tcl_SplitList --
                    693:  *
                    694:  *     Splits a list up into its constituent fields.
                    695:  *
                    696:  * Results
                    697:  *     The return value is normally TCL_OK, which means that
                    698:  *     the list was successfully split up.  If TCL_ERROR is
                    699:  *     returned, it means that "list" didn't have proper list
                    700:  *     structure;  interp->result will contain a more detailed
                    701:  *     error message.
                    702:  *
                    703:  *     *argvPtr will be filled in with the address of an array
                    704:  *     whose elements point to the elements of list, in order.
                    705:  *     *argcPtr will get filled in with the number of valid elements
                    706:  *     in the array.  A single block of memory is dynamically allocated
                    707:  *     to hold both the argv array and a copy of the list (with
                    708:  *     backslashes and braces removed in the standard way).
                    709:  *     The caller must eventually free this memory by calling free()
                    710:  *     on *argvPtr.  Note:  *argvPtr and *argcPtr are only modified
                    711:  *     if the procedure returns normally.
                    712:  *
                    713:  * Side effects:
                    714:  *     Memory is allocated.
                    715:  *
                    716:  *----------------------------------------------------------------------
                    717:  */
                    718: 
                    719: int
                    720: Tcl_SplitList(interp, list, argcPtr, argvPtr)
                    721:     Tcl_Interp *interp;                /* Interpreter to use for error reporting. */
                    722:     char *list;                        /* Pointer to string with list structure. */
                    723:     int *argcPtr;              /* Pointer to location to fill in with
                    724:                                 * the number of elements in the list. */
                    725:     char ***argvPtr;           /* Pointer to place to store pointer to array
                    726:                                 * of pointers to list elements. */
                    727: {
                    728:     char **argv;
                    729:     register char *p;
                    730:     int size, i, result, elSize, brace;
                    731:     char *element;
                    732: 
                    733:     /*
                    734:      * Figure out how much space to allocate.  There must be enough
                    735:      * space for both the array of pointers and also for a copy of
                    736:      * the list.  To estimate the number of pointers needed, count
                    737:      * the number of space characters in the list.
                    738:      */
                    739: 
                    740:     for (size = 1, p = list; *p != 0; p++) {
                    741:        if (isspace(*p)) {
                    742:            size++;
                    743:        }
                    744:     }
                    745:     argv = (char **) malloc((unsigned)
                    746:            ((size * sizeof(char *)) + (p - list) + 1));
                    747:     for (i = 0, p = ((char *) argv) + size*sizeof(char *);
                    748:            *list != 0; i++) {
                    749:        result = TclFindElement(interp, list, &element, &list, &elSize, &brace);
                    750:        if (result != TCL_OK) {
                    751:            free((char *) argv);
                    752:            return result;
                    753:        }
                    754:        if (*element == 0) {
                    755:            break;
                    756:        }
                    757:        if (i >= size) {
                    758:            Tcl_Return(interp, "internal error in Tcl_SplitList", TCL_STATIC);
                    759:            return TCL_ERROR;
                    760:        }
                    761:        argv[i] = p;
                    762:        if (brace) {
                    763:            strncpy(p, element, elSize);
                    764:            p += elSize;
                    765:            *p = 0;
                    766:            p++;
                    767:        } else {
                    768:            TclCopyAndCollapse(elSize, element, p);
                    769:            p += elSize+1;
                    770:        }
                    771:     }
                    772: 
                    773:     *argvPtr = argv;
                    774:     *argcPtr = i;
                    775:     return TCL_OK;
                    776: }
                    777: 
                    778: /*
                    779:  *----------------------------------------------------------------------
                    780:  *
                    781:  * Tcl_StringMatch --
                    782:  *
                    783:  *     See if a particular string matches a particular pattern.
                    784:  *
                    785:  * Results:
                    786:  *     The return value is 1 if string matches pattern, and
                    787:  *     0 otherwise.  The matching operation permits the following
                    788:  *     special characters in the pattern: *?\[] (see the manual
                    789:  *     entry for details on what these mean).
                    790:  *
                    791:  * Side effects:
                    792:  *     None.
                    793:  *
                    794:  *----------------------------------------------------------------------
                    795:  */
                    796: 
                    797: int
                    798: Tcl_StringMatch(string, pattern)
                    799:     register char *string;     /* String. */
                    800:     register char *pattern;    /* Pattern, which may contain
                    801:                                 * special characters. */
                    802: {
                    803:     char c2;
                    804: 
                    805:     while (1) {
                    806:        /* See if we're at the end of both the pattern and the string.
                    807:         * If so, we succeeded.  If we're at the end of the pattern
                    808:         * but not at the end of the string, we failed.
                    809:         */
                    810:        
                    811:        if (*pattern == 0) {
                    812:            if (*string == 0) {
                    813:                return 1;
                    814:            } else {
                    815:                return 0;
                    816:            }
                    817:        }
                    818:        if ((*string == 0) && (*pattern != '*')) {
                    819:            return 0;
                    820:        }
                    821: 
                    822:        /* Check for a "*" as the next pattern character.  It matches
                    823:         * any substring.  We handle this by calling ourselves
                    824:         * recursively for each postfix of string, until either we
                    825:         * match or we reach the end of the string.
                    826:         */
                    827:        
                    828:        if (*pattern == '*') {
                    829:            pattern += 1;
                    830:            if (*pattern == 0) {
                    831:                return 1;
                    832:            }
                    833:            while (*string != 0) {
                    834:                if (Tcl_StringMatch(string, pattern)) {
                    835:                    return 1;
                    836:                }
                    837:                string += 1;
                    838:            }
                    839:            return 0;
                    840:        }
                    841:     
                    842:        /* Check for a "?" as the next pattern character.  It matches
                    843:         * any single character.
                    844:         */
                    845: 
                    846:        if (*pattern == '?') {
                    847:            goto thisCharOK;
                    848:        }
                    849: 
                    850:        /* Check for a "[" as the next pattern character.  It is followed
                    851:         * by a list of characters that are acceptable, or by a range
                    852:         * (two characters separated by "-").
                    853:         */
                    854:        
                    855:        if (*pattern == '[') {
                    856:            pattern += 1;
                    857:            while (1) {
                    858:                if ((*pattern == ']') || (*pattern == 0)) {
                    859:                    return 0;
                    860:                }
                    861:                if (*pattern == *string) {
                    862:                    break;
                    863:                }
                    864:                if (pattern[1] == '-') {
                    865:                    c2 = pattern[2];
                    866:                    if (c2 == 0) {
                    867:                        return 0;
                    868:                    }
                    869:                    if ((*pattern <= *string) && (c2 >= *string)) {
                    870:                        break;
                    871:                    }
                    872:                    if ((*pattern >= *string) && (c2 <= *string)) {
                    873:                        break;
                    874:                    }
                    875:                    pattern += 2;
                    876:                }
                    877:                pattern += 1;
                    878:            }
                    879:            while ((*pattern != ']') && (*pattern != 0)) {
                    880:                pattern += 1;
                    881:            }
                    882:            goto thisCharOK;
                    883:        }
                    884:     
                    885:        /* If the next pattern character is '/', just strip off the '/'
                    886:         * so we do exact matching on the character that follows.
                    887:         */
                    888:        
                    889:        if (*pattern == '\\') {
                    890:            pattern += 1;
                    891:            if (*pattern == 0) {
                    892:                return 0;
                    893:            }
                    894:        }
                    895: 
                    896:        /* There's no special character.  Just make sure that the next
                    897:         * characters of each string match.
                    898:         */
                    899:        
                    900:        if (*pattern != *string) {
                    901:            return 0;
                    902:        }
                    903: 
                    904:        thisCharOK: pattern += 1;
                    905:        string += 1;
                    906:     }
                    907: }
                    908: 
                    909: /*
                    910:  *----------------------------------------------------------------------
                    911:  *
                    912:  * TclWordEnd --
                    913:  *
                    914:  *     Given a pointer into a Tcl command, find the end of the next
                    915:  *     word of the command.
                    916:  *
                    917:  * Results:
                    918:  *     The return value is a pointer to the character just after the
                    919:  *     last one that's part of the word pointed to by "start".  This
                    920:  *     may be the address of the NULL character at the end of the
                    921:  *     string.
                    922:  *
                    923:  * Side effects:
                    924:  *     None.
                    925:  *
                    926:  *----------------------------------------------------------------------
                    927:  */
                    928: 
                    929: char *
                    930: TclWordEnd(start, nested)
                    931:     char *start;               /* Beginning of a word of a Tcl command. */
                    932:     int nested;                        /* Zero means this is a top-level command.
                    933:                                 * One means this is a nested command (close
                    934:                                 * brace is a word terminator). */
                    935: {
                    936:     register char *p;
                    937:     int count;
                    938: 
                    939:     p = start;
                    940:     while (isspace(*p)) {
                    941:        p++;
                    942:     }
                    943: 
                    944:     /*
                    945:      * Handle words beginning with a double-quote or a brace.
                    946:      */
                    947: 
                    948:     if (*p == '"') {
                    949:        while (1) {
                    950:            p++;
                    951:            while (*p == '\\') {
                    952:                (void) Tcl_Backslash(p, &count);
                    953:                p += count;
                    954:            }
                    955:            if (*p == '"') {
                    956:                break;
                    957:            }
                    958:        }
                    959:     } else if (*p == '{') {
                    960:        int braces = 1;
                    961:        while (braces != 0) {
                    962:            p++;
                    963:            while (*p == '\\') {
                    964:                (void) Tcl_Backslash(p, &count);
                    965:                p += count;
                    966:            }
                    967:            if (*p == '}') {
                    968:                braces--;
                    969:            } else if (*p == '{') {
                    970:                braces++;
                    971:            } else if (*p == 0) {
                    972:                return p;
                    973:            }
                    974:        }
                    975:     }
                    976: 
                    977:     /*
                    978:      * Handle words that don't start with a brace or double-quote.
                    979:      * This code is also invoked if the word starts with a brace or
                    980:      * double-quote and there is garbage after the closing brace or
                    981:      * quote.  This is an error as far as Tcl_Eval is concerned, but
                    982:      * for here the garbage is treated as part of the word.
                    983:      */
                    984: 
                    985:     while (1) {
                    986: 
                    987:        /*
                    988:         * Handle nested commands.
                    989:         */
                    990: 
                    991:        while (*p == '[') {
                    992:            p++;
                    993:            while ((*p != ']') && (*p != 0)) {
                    994:                p = TclWordEnd(p, 1);
                    995:                if (*p == ';') {
                    996:                    p++;
                    997:                }
                    998:            }
                    999:            if (*p == ']') {
                   1000:                p++;
                   1001:            }
                   1002:        }
                   1003: 
                   1004:        /*
                   1005:         * Handle backslash sequences.  Backslash-newline isn't handled
                   1006:         * by Tcl_Backslash, so it must be checked for explicitly.
                   1007:         */
                   1008: 
                   1009:        while (*p == '\\') {
                   1010:            if (p[1] == '\n') {
                   1011:                p += 2;
                   1012:            } else {
                   1013:                (void) Tcl_Backslash(p, &count);
                   1014:                p += count;
                   1015:            }
                   1016:        }
                   1017: 
                   1018:        /*
                   1019:         * Check for end of word.  Note:  semi-colon terminates a word
                   1020:         * and also counts as a word by itself.
                   1021:         */
                   1022: 
                   1023:        if (*p == ';') {
                   1024:            if (p == start) {
                   1025:                p++;
                   1026:            }
                   1027:            break;
                   1028:        }
                   1029:        if (isspace(*p) || (*p == 0)) {
                   1030:            break;
                   1031:        }
                   1032:        if ((*p == ']') && nested) {
                   1033:            break;
                   1034:        }
                   1035: 
                   1036:        p++;
                   1037:     }
                   1038:     return p;
                   1039: }

unix.superglobalmegacorp.com

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