Annotation of researchv10no/cmd/worm/scsi/tcl/tclCmdAH.c, revision 1.1.1.1

1.1       root        1: /* 
                      2:  * tclCmdAH.c --
                      3:  *
                      4:  *     This file contains the top-level command routines for most of
                      5:  *     the Tcl built-in commands whose names begin with the letters
                      6:  *     A to H.
                      7:  *
                      8:  * Copyright 1987 Regents of the University of California
                      9:  * Permission to use, copy, modify, and distribute this
                     10:  * software and its documentation for any purpose and without
                     11:  * fee is hereby granted, provided that the above copyright
                     12:  * notice appear in all copies.  The University of California
                     13:  * makes no representations about the suitability of this
                     14:  * software for any purpose.  It is provided "as is" without
                     15:  * express or implied warranty.
                     16:  */
                     17: 
                     18: #ifndef lint
                     19: static char rcsid[] = "$Header: /sprite/src/lib/tcl/RCS/tclCmdAH.c,v 1.45 90/04/18 17:09:19 ouster Exp $ SPRITE (Berkeley)";
                     20: #pragma ref rcsid
                     21: #endif not lint
                     22: 
                     23: #define        _POSIX_SOURCE
                     24: 
                     25: #include <ctype.h>
                     26: #include <errno.h>
                     27: #include <signal.h>
                     28: #include <stdio.h>
                     29: #include <stdlib.h>
                     30: #include <unistd.h>
                     31: #include <string.h>
                     32: #include <sys/types.h>
                     33: #include <fcntl.h>
                     34: #include <sys/stat.h>
                     35: #include <sys/wait.h>
                     36: #include "tclInt.h"
                     37: 
                     38: extern long lseek();
                     39: extern char *mktemp();
                     40: 
                     41: /*
                     42:  *----------------------------------------------------------------------
                     43:  *
                     44:  * Tcl_BreakCmd --
                     45:  *
                     46:  *     This procedure is invoked to process the "break" Tcl command.
                     47:  *     See the user documentation for details on what it does.
                     48:  *
                     49:  * Results:
                     50:  *     A standard Tcl result.
                     51:  *
                     52:  * Side effects:
                     53:  *     See the user documentation.
                     54:  *
                     55:  *----------------------------------------------------------------------
                     56:  */
                     57: 
                     58:        /* ARGSUSED */
                     59: int
                     60: Tcl_BreakCmd(dummy, interp, argc, argv)
                     61:     ClientData dummy;                  /* Not used. */
                     62:     Tcl_Interp *interp;                        /* Current interpreter. */
                     63:     int argc;                          /* Number of arguments. */
                     64:     char **argv;                       /* Argument strings. */
                     65: {
                     66: #pragma ref dummy
                     67:     if (argc != 1) {
                     68:        sprintf(interp->result, "too many args: should be \"%.50s\"", argv[0]);
                     69:        return TCL_ERROR;
                     70:     }
                     71:     return TCL_BREAK;
                     72: }
                     73: 
                     74: /*
                     75:  *----------------------------------------------------------------------
                     76:  *
                     77:  * Tcl_CaseCmd --
                     78:  *
                     79:  *     This procedure is invoked to process the "case" Tcl command.
                     80:  *     See the user documentation for details on what it does.
                     81:  *
                     82:  * Results:
                     83:  *     A standard Tcl result.
                     84:  *
                     85:  * Side effects:
                     86:  *     See the user documentation.
                     87:  *
                     88:  *----------------------------------------------------------------------
                     89:  */
                     90: 
                     91:        /* ARGSUSED */
                     92: int
                     93: Tcl_CaseCmd(dummy, interp, argc, argv)
                     94:     ClientData dummy;                  /* Not used. */
                     95:     Tcl_Interp *interp;                        /* Current interpreter. */
                     96:     int argc;                          /* Number of arguments. */
                     97:     char **argv;                       /* Argument strings. */
                     98: {
                     99: #pragma ref dummy
                    100:     int i, result;
                    101:     int body;
                    102:     char *string;
                    103: 
                    104:     if (argc < 4) {
                    105:        sprintf(interp->result,
                    106:                "%s \"%.50s string [in] patList body ... [default body]\"",
                    107:                "not enough args:  should be", argv[0]);
                    108:        return TCL_ERROR;
                    109:     }
                    110:     string = argv[1];
                    111:     body = NULL;
                    112:     if (strcmp(argv[2], "in") == 0) {
                    113:        i = 3;
                    114:     } else {
                    115:        i = 2;
                    116:     }
                    117:     for (; i < argc; i += 2) {
                    118:        int patArgc, j;
                    119:        char **patArgv;
                    120:        register char *p;
                    121: 
                    122:        if (i == (argc-1)) {
                    123:            sprintf(interp->result, "extra pattern with no body in \"%.50s\"",
                    124:                    argv[0]);
                    125:            return TCL_ERROR;
                    126:        }
                    127: 
                    128:        /*
                    129:         * Check for special case of single pattern (no list) with
                    130:         * no backslash sequences.
                    131:         */
                    132: 
                    133:        for (p = argv[i]; *p != 0; p++) {
                    134:            if (isspace(*p) || (*p == '\\')) {
                    135:                break;
                    136:            }
                    137:        }
                    138:        if (*p == 0) {
                    139:            if ((*argv[i] == 'd') && (strcmp(argv[i], "default") == 0)) {
                    140:                body = i+1;
                    141:            }
                    142:            if (Tcl_StringMatch(string, argv[i])) {
                    143:                body = i+1;
                    144:                goto match;
                    145:            }
                    146:            continue;
                    147:        }
                    148: 
                    149:        /*
                    150:         * Break up pattern lists, then check each of the patterns
                    151:         * in the list.
                    152:         */
                    153: 
                    154:        result = Tcl_SplitList(interp, argv[i], &patArgc, &patArgv);
                    155:        if (result != TCL_OK) {
                    156:            return result;
                    157:        }
                    158:        for (j = 0; j < patArgc; j++) {
                    159:            if (Tcl_StringMatch(string, patArgv[j])) {
                    160:                body = i+1;
                    161:                break;
                    162:            }
                    163:        }
                    164:        free((char *) patArgv);
                    165:        if (j < patArgc) {
                    166:            break;
                    167:        }
                    168:     }
                    169: 
                    170:     match:
                    171:     if (body != NULL) {
                    172:        result = Tcl_Eval(interp, argv[body], 0, (char **) NULL);
                    173:        if (result == TCL_ERROR) {
                    174:            char msg[100];
                    175:            sprintf(msg, " (\"%.50s\" arm line %d)", argv[i],
                    176:                    interp->errorLine);
                    177:            Tcl_AddErrorInfo(interp, msg);
                    178:        }
                    179:        return result;
                    180:     }
                    181: 
                    182:     /*
                    183:      * Nothing matched:  return nothing.
                    184:      */
                    185:     return TCL_OK;
                    186: }
                    187: 
                    188: /*
                    189:  *----------------------------------------------------------------------
                    190:  *
                    191:  * Tcl_CatchCmd --
                    192:  *
                    193:  *     This procedure is invoked to process the "catch" Tcl command.
                    194:  *     See the user documentation for details on what it does.
                    195:  *
                    196:  * Results:
                    197:  *     A standard Tcl result.
                    198:  *
                    199:  * Side effects:
                    200:  *     See the user documentation.
                    201:  *
                    202:  *----------------------------------------------------------------------
                    203:  */
                    204: 
                    205:        /* ARGSUSED */
                    206: int
                    207: Tcl_CatchCmd(dummy, interp, argc, argv)
                    208:     ClientData dummy;                  /* Not used. */
                    209:     Tcl_Interp *interp;                        /* Current interpreter. */
                    210:     int argc;                          /* Number of arguments. */
                    211:     char **argv;                       /* Argument strings. */
                    212: {
                    213: #pragma ref dummy
                    214:     int result;
                    215: 
                    216:     if ((argc != 2) && (argc != 3)) {
                    217:        sprintf(interp->result,
                    218:                "wrong # args: should be \"%.50s command [varName]\"",
                    219:                argv[0]);
                    220:        return TCL_ERROR;
                    221:     }
                    222:     result = Tcl_Eval(interp, argv[1], 0, (char **) NULL);
                    223:     if (argc == 3) {
                    224:        Tcl_SetVar(interp, argv[2], interp->result, 0);
                    225:     }
                    226:     Tcl_Return(interp, (char *) NULL, TCL_STATIC);
                    227:     sprintf(interp->result, "%d", result);
                    228:     return TCL_OK;
                    229: }
                    230: 
                    231: /*
                    232:  *----------------------------------------------------------------------
                    233:  *
                    234:  * Tcl_ConcatCmd --
                    235:  *
                    236:  *     This procedure is invoked to process the "concat" Tcl command.
                    237:  *     See the user documentation for details on what it does.
                    238:  *
                    239:  * Results:
                    240:  *     A standard Tcl result.
                    241:  *
                    242:  * Side effects:
                    243:  *     See the user documentation.
                    244:  *
                    245:  *----------------------------------------------------------------------
                    246:  */
                    247: 
                    248:        /* ARGSUSED */
                    249: int
                    250: Tcl_ConcatCmd(dummy, interp, argc, argv)
                    251:     ClientData dummy;                  /* Not used. */
                    252:     Tcl_Interp *interp;                        /* Current interpreter. */
                    253:     int argc;                          /* Number of arguments. */
                    254:     char **argv;                       /* Argument strings. */
                    255: {
                    256: #pragma ref dummy
                    257:     if (argc == 1) {
                    258:        sprintf(interp->result,
                    259:                "not enough args:  should be \"%.50s arg [arg ...]\"",
                    260:                argv[0]);
                    261:        return TCL_ERROR;
                    262:     }
                    263: 
                    264:     interp->result = Tcl_Concat(argc-1, argv+1);
                    265:     interp->dynamic = 1;
                    266:     return TCL_OK;
                    267: }
                    268: 
                    269: /*
                    270:  *----------------------------------------------------------------------
                    271:  *
                    272:  * Tcl_ContinueCmd --
                    273:  *
                    274:  *     This procedure is invoked to process the "continue" Tcl command.
                    275:  *     See the user documentation for details on what it does.
                    276:  *
                    277:  * Results:
                    278:  *     A standard Tcl result.
                    279:  *
                    280:  * Side effects:
                    281:  *     See the user documentation.
                    282:  *
                    283:  *----------------------------------------------------------------------
                    284:  */
                    285: 
                    286:        /* ARGSUSED */
                    287: int
                    288: Tcl_ContinueCmd(dummy, interp, argc, argv)
                    289:     ClientData dummy;                  /* Not used. */
                    290:     Tcl_Interp *interp;                        /* Current interpreter. */
                    291:     int argc;                          /* Number of arguments. */
                    292:     char **argv;                       /* Argument strings. */
                    293: {
                    294: #pragma ref dummy
                    295:     if (argc != 1) {
                    296:        sprintf(interp->result, "too many args: should be \"%.50s\"", argv[0]);
                    297:        return TCL_ERROR;
                    298:     }
                    299:     return TCL_CONTINUE;
                    300: }
                    301: 
                    302: /*
                    303:  *----------------------------------------------------------------------
                    304:  *
                    305:  * Tcl_ErrorCmd --
                    306:  *
                    307:  *     This procedure is invoked to process the "error" Tcl command.
                    308:  *     See the user documentation for details on what it does.
                    309:  *
                    310:  * Results:
                    311:  *     A standard Tcl result.
                    312:  *
                    313:  * Side effects:
                    314:  *     See the user documentation.
                    315:  *
                    316:  *----------------------------------------------------------------------
                    317:  */
                    318: 
                    319:        /* ARGSUSED */
                    320: int
                    321: Tcl_ErrorCmd(dummy, interp, argc, argv)
                    322:     ClientData dummy;                  /* Not used. */
                    323:     Tcl_Interp *interp;                        /* Current interpreter. */
                    324:     int argc;                          /* Number of arguments. */
                    325:     char **argv;                       /* Argument strings. */
                    326: {
                    327: #pragma ref dummy
                    328:     Interp *iPtr = (Interp *) interp;
                    329: 
                    330:     if ((argc != 2) && (argc != 3)) {
                    331:        sprintf(interp->result, "wrong # args: should be \"%.50s message [errorInfo]\"",
                    332:                argv[0]);
                    333:        return TCL_ERROR;
                    334:     }
                    335:     if (argc == 3) {
                    336:        Tcl_AddErrorInfo(interp, argv[2]);
                    337:        iPtr->flags |= ERR_ALREADY_LOGGED;
                    338:     }
                    339:     Tcl_Return(interp, argv[1], TCL_VOLATILE);
                    340:     return TCL_ERROR;
                    341: }
                    342: 
                    343: /*
                    344:  *----------------------------------------------------------------------
                    345:  *
                    346:  * Tcl_EvalCmd --
                    347:  *
                    348:  *     This procedure is invoked to process the "eval" Tcl command.
                    349:  *     See the user documentation for details on what it does.
                    350:  *
                    351:  * Results:
                    352:  *     A standard Tcl result.
                    353:  *
                    354:  * Side effects:
                    355:  *     See the user documentation.
                    356:  *
                    357:  *----------------------------------------------------------------------
                    358:  */
                    359: 
                    360:        /* ARGSUSED */
                    361: int
                    362: Tcl_EvalCmd(dummy, interp, argc, argv)
                    363:     ClientData dummy;                  /* Not used. */
                    364:     Tcl_Interp *interp;                        /* Current interpreter. */
                    365:     int argc;                          /* Number of arguments. */
                    366:     char **argv;                       /* Argument strings. */
                    367: {
                    368: #pragma ref dummy
                    369:     int result;
                    370:     char *cmd;
                    371: 
                    372:     if (argc < 2) {
                    373:        sprintf(interp->result,
                    374:                "not enough args:  should be \"%.50s arg [arg ...]\"",
                    375:                argv[0]);
                    376:        return TCL_ERROR;
                    377:     }
                    378:     if (argc == 2) {
                    379:        result = Tcl_Eval(interp, argv[1], 0, (char **) NULL);
                    380:     } else {
                    381:     
                    382:        /*
                    383:         * More than one argument:  concatenate them together with spaces
                    384:         * between, then evaluate the result.
                    385:         */
                    386:     
                    387:        cmd = Tcl_Concat(argc-1, argv+1);
                    388:        result = Tcl_Eval(interp, cmd, 0, (char **) NULL);
                    389:        free(cmd);
                    390:     }
                    391:     if (result == TCL_ERROR) {
                    392:        char msg[60];
                    393:        sprintf(msg, " (\"eval\" body line %d)", interp->errorLine);
                    394:        Tcl_AddErrorInfo(interp, msg);
                    395:     }
                    396:     return result;
                    397: }
                    398: 
                    399: /*
                    400:  *----------------------------------------------------------------------
                    401:  *
                    402:  * Tcl_ExecCmd --
                    403:  *
                    404:  *     This procedure is invoked to process the "exec" Tcl command.
                    405:  *     See the user documentation for details on what it does.
                    406:  *
                    407:  * Results:
                    408:  *     A standard Tcl result.
                    409:  *
                    410:  * Side effects:
                    411:  *     See the user documentation.
                    412:  *
                    413:  *----------------------------------------------------------------------
                    414:  */
                    415: 
                    416:        /* ARGSUSED */
                    417: int
                    418: Tcl_ExecCmd(dummy, interp, argc, argv)
                    419:     ClientData dummy;                  /* Not used. */
                    420:     Tcl_Interp *interp;                        /* Current interpreter. */
                    421:     int argc;                          /* Number of arguments. */
                    422:     char **argv;                       /* Argument strings. */
                    423: {
                    424: #pragma ref dummy
                    425:     char *input = "";                  /* Points to the input remaining to
                    426:                                         * send to the child process. */
                    427:     int inputSize;                     /* # of bytes of input. */
                    428: #define MAX_PIPE_INPUT 4095
                    429: #define TMP_FILE_NAME "/tmp/tcl.XXXXXX"
                    430:     char *output = NULL;               /* Output received from child. */
                    431:     int outputSize;                    /* Number of valid bytes at output. */
                    432:     int outputSpace;                   /* Total space available at output. */
                    433:     int stdIn[2], stdOut[2], count, result, i;
                    434:     int pid = -1;                      /* -1 means child process doesn't
                    435:                                         * exist (yet).  Non-zero gives its
                    436:                                         * id (0 only in child). */
                    437:     int status;
                    438:     char *cmdName, *execName;
                    439: 
                    440:     /*
                    441:      * Look through the arguments for a standard input specification
                    442:      * ("< value" in two arguments).  If found, collapse it out.
                    443:      * Shuffle all the arguments back over the "exec" argument, so that
                    444:      * there's room for a NULL argument at the end.
                    445:      */
                    446: 
                    447:     cmdName = argv[0];
                    448:     for (i = 1; i < argc; i++) {
                    449:        argv[i-1] = argv[i];
                    450:        if ((argv[i][0] != '<') || (argv[i][1] != 0)) {
                    451:            continue;
                    452:        }
                    453:        i++;
                    454:        if (i >= argc) {
                    455:            sprintf(interp->result,
                    456:                    "specified \"<\" but no input in \"%.50s\" command",
                    457:                    cmdName);
                    458:            return TCL_ERROR;
                    459:        }
                    460:        input = argv[i];
                    461:        for (i++; i < argc; i++) {
                    462:            argv[i-3] = argv[i];
                    463:        }
                    464:        argc -= 2;
                    465:     }
                    466: 
                    467:     argc -= 1;                 /* Drop "exec" argument. */
                    468:     argv[argc] = NULL;
                    469:     if (argc < 1) {
                    470:        sprintf(interp->result, "not enough arguments to \"%.50s\" command",
                    471:                cmdName);
                    472:        return TCL_ERROR;
                    473:     }
                    474:     execName = Tcl_TildeSubst(interp, argv[0]);
                    475:     if (execName == NULL) {
                    476:        return TCL_ERROR;
                    477:     }
                    478: 
                    479:     /*
                    480:      * Set up the input stream for child.  Use a pipe if the amount of
                    481:      * input data is small enough for us to write it to the pipe without
                    482:      * overflowing the pipe and blocking.  If there's too much input data,
                    483:      * then write it to a temporary file.
                    484:      */
                    485: 
                    486:     stdIn[0] = stdIn[1] = stdOut[0] = stdOut[1] = -1;
                    487:     inputSize = strlen(input);
                    488:     if (inputSize <= MAX_PIPE_INPUT) {
                    489:        if (pipe(stdIn) < 0) {
                    490:            sprintf(interp->result,
                    491:                    "couldn't create input pipe for \"%.50s\" command: %.50s",
                    492:                    cmdName, strerror(errno));
                    493:            result = TCL_ERROR;
                    494:            goto cleanup;
                    495:        }
                    496:        if (write(stdIn[1], input, inputSize) != inputSize) {
                    497:            sprintf(interp->result,
                    498:                    "couldn't write pipe input for command: %.50s",
                    499:                    strerror(errno));
                    500:            result = TCL_ERROR;
                    501:            goto cleanup;
                    502:        }
                    503:        close(stdIn[1]);
                    504:        stdIn[1] = -1;
                    505:     } else {
                    506:        char tmp[L_tmpnam];
                    507:        tmpnam(tmp);
                    508:        stdIn[0] = open(tmp, O_RDWR|O_CREAT, 0);
                    509:        if (stdIn[0] < 0) {
                    510:            sprintf(interp->result,
                    511:                    "couldn't create input file for \"%.50s\" command: %.50s",
                    512:                    cmdName, strerror(errno));
                    513:            result = TCL_ERROR;
                    514:            goto cleanup;
                    515:        }
                    516:        if (write(stdIn[0], input, inputSize) != inputSize) {
                    517:            sprintf(interp->result,
                    518:                    "couldn't write file input for command: %.50s",
                    519:                    strerror(errno));
                    520:            result = TCL_ERROR;
                    521:            goto cleanup;
                    522:        }
                    523:        if ((lseek(stdIn[0], 0L, 0) == -1) || (unlink(tmp) == -1)) {
                    524:            sprintf(interp->result,
                    525:                    "couldn't reset or close input file for command: %.50s",
                    526:                    strerror(errno));
                    527:            result = TCL_ERROR;
                    528:            goto cleanup;
                    529:        }
                    530:     }
                    531: 
                    532:     /*
                    533:      * Set up an output pipe from the child's stdout/stderr back to
                    534:      * us, then fork the child.
                    535:      */
                    536: 
                    537:     if (pipe(stdOut) < 0) {
                    538:        sprintf(interp->result,
                    539:                "couldn't create output pipe for \"%.50s\" command",
                    540:                cmdName);
                    541:        result = TCL_ERROR;
                    542:        goto cleanup;
                    543:     }
                    544:     pid = fork();
                    545:     if (pid == -1) {
                    546:        sprintf(interp->result,
                    547:                "couldn't fork child for \"%.50s\" command: %.50s",
                    548:                cmdName, strerror(errno));
                    549:        result = TCL_ERROR;
                    550:        goto cleanup;
                    551:     }
                    552:     if (pid == 0) {
                    553:        char errSpace[100];
                    554: 
                    555:        if ((dup2(stdIn[0], 0) == -1) || (dup2(stdOut[1], 1) == -1)
                    556:                || (dup2(stdOut[1], 2) == -1)) {
                    557:            char *err;
                    558:            err = "forked process couldn't set up input/output";
                    559:            write(stdOut[1], err, strlen(err));
                    560:            _exit(1);
                    561:        }
                    562:        close(stdIn[0]);
                    563:        close(stdOut[0]);
                    564:        close(stdOut[1]);
                    565:        execvp(execName, argv);
                    566:        sprintf(errSpace, "couldn't find a \"%.50s\" to execute", argv[0]);
                    567:        write(1, errSpace, strlen(errSpace));
                    568:        _exit(1);
                    569:     }
                    570: 
                    571:     /*
                    572:      * In the parent, read output from the child until end of file
                    573:      * (this should mean that the child has completed and died).
                    574:      */
                    575: 
                    576:     close(stdIn[0]);
                    577:     stdIn[0] = -1;
                    578:     close(stdOut[1]);
                    579:     stdOut[1] = -1;
                    580:     outputSize = 0;
                    581:     outputSpace = 0;
                    582:     result = -1;
                    583:     while (1) {
                    584:        if ((outputSpace - outputSize) < 100) {
                    585:            char *newOutput;
                    586: 
                    587:            if (outputSpace == 0) {
                    588:                outputSpace = 200;
                    589:            } else {
                    590:                outputSpace = 2*outputSpace;
                    591:            }
                    592:            newOutput = (char *) malloc((unsigned) outputSpace);
                    593:            if (output != 0) {
                    594:                bcopy(output, newOutput, outputSize);
                    595:                free(output);
                    596:            }
                    597:            output = newOutput;
                    598:        }
                    599:        count = read(stdOut[0], output+outputSize,
                    600:                outputSpace-outputSize-1);
                    601: 
                    602:        if (count == 0) {
                    603:            break;
                    604:        }
                    605:        if (count < 0) {
                    606:            sprintf(interp->result,
                    607:                    "error reading stdout during \"%.50s\": %.50s",
                    608:                    cmdName, strerror(errno));
                    609:            result = TCL_ERROR;
                    610:            goto cleanup;
                    611:        }
                    612:        outputSize += count;
                    613:     }
                    614: 
                    615:     /*
                    616:      * The command is supposedly done now.  Terminate the result
                    617:      * string and wait for the process really to complete.
                    618:      */
                    619: 
                    620:     output[outputSize] = 0;
                    621:     interp->result = output;
                    622:     interp->dynamic = 1;
                    623: 
                    624:     cleanup:
                    625:     if (pid != -1) {
                    626:        while (1) {
                    627:            int child;
                    628: 
                    629:            child = wait(&status);
                    630:            if (child == -1) {
                    631:                sprintf(interp->result,
                    632:                        "child process disappeared mysteriously");
                    633:                result = TCL_ERROR;
                    634:                break;
                    635:            }
                    636:            if (child == pid) {
                    637:                break;
                    638:            }
                    639:        }
                    640:        if (!WIFEXITED(status)) {
                    641:            sprintf(interp->result, "command terminated abnormally");
                    642:            result = TCL_ERROR;
                    643:        }
                    644:        result = status;
                    645:     }
                    646:     if (stdIn[0] != -1) {
                    647:        close(stdIn[0]);
                    648:     }
                    649:     if (stdIn[1] != -1) {
                    650:        close(stdIn[1]);
                    651:     }
                    652:     if (stdOut[0] != -1) {
                    653:        close(stdOut[0]);
                    654:     }
                    655:     if (stdOut[1] != -1) {
                    656:        close(stdOut[1]);
                    657:     }
                    658:     return result;
                    659: }
                    660: 
                    661: /*
                    662:  *----------------------------------------------------------------------
                    663:  *
                    664:  * Tcl_ExprCmd --
                    665:  *
                    666:  *     This procedure is invoked to process the "expr" Tcl command.
                    667:  *     See the user documentation for details on what it does.
                    668:  *
                    669:  * Results:
                    670:  *     A standard Tcl result.
                    671:  *
                    672:  * Side effects:
                    673:  *     See the user documentation.
                    674:  *
                    675:  *----------------------------------------------------------------------
                    676:  */
                    677: 
                    678:        /* ARGSUSED */
                    679: int
                    680: Tcl_ExprCmd(dummy, interp, argc, argv)
                    681:     ClientData dummy;                  /* Not used. */
                    682:     Tcl_Interp *interp;                        /* Current interpreter. */
                    683:     int argc;                          /* Number of arguments. */
                    684:     char **argv;                       /* Argument strings. */
                    685: {
                    686: #pragma ref dummy
                    687:     int result, value;
                    688: 
                    689:     if (argc != 2) {
                    690:        sprintf(interp->result,
                    691:                "wrong # args: should be \"%.50s expression\"", argv[0]);
                    692:        return TCL_ERROR;
                    693:     }
                    694: 
                    695:     result = Tcl_Expr(interp, argv[1], &value);
                    696:     if (result != TCL_OK) {
                    697:        return result;
                    698:     }
                    699: 
                    700:     /*
                    701:      * Turn the integer result back into a string.
                    702:      */
                    703: 
                    704:     sprintf(interp->result, "%d", value);
                    705:     return TCL_OK;
                    706: }
                    707: 
                    708: /*
                    709:  *----------------------------------------------------------------------
                    710:  *
                    711:  * Tcl_FileCmd --
                    712:  *
                    713:  *     This procedure is invoked to process the "file" Tcl command.
                    714:  *     See the user documentation for details on what it does.
                    715:  *
                    716:  * Results:
                    717:  *     A standard Tcl result.
                    718:  *
                    719:  * Side effects:
                    720:  *     See the user documentation.
                    721:  *
                    722:  *----------------------------------------------------------------------
                    723:  */
                    724: 
                    725:        /* ARGSUSED */
                    726: int
                    727: Tcl_FileCmd(dummy, interp, argc, argv)
                    728:     ClientData dummy;                  /* Not used. */
                    729:     Tcl_Interp *interp;                        /* Current interpreter. */
                    730:     int argc;                          /* Number of arguments. */
                    731:     char **argv;                       /* Argument strings. */
                    732: {
                    733: #pragma ref dummy
                    734:     char *p;
                    735:     int length, mode, statOp;
                    736:     struct stat statBuf;
                    737:     char *fileName;
                    738: 
                    739:     if (argc != 3) {
                    740:        sprintf(interp->result,
                    741:                "wrong # args: should be \"%.50s name option\"", argv[0]);
                    742:        return TCL_ERROR;
                    743:     }
                    744:     length = strlen(argv[2]);
                    745: 
                    746:     /*
                    747:      * First handle operations on the file name.
                    748:      */
                    749: 
                    750:     fileName = Tcl_TildeSubst(interp, argv[1]);
                    751:     if ((argv[2][0] == 'd') && (strncmp(argv[2], "dirname", length) == 0)) {
                    752:        p = strrchr(fileName, '/');
                    753:        if (p == NULL) {
                    754:            interp->result = ".";
                    755:        } else if (p == fileName) {
                    756:            interp->result = "/";
                    757:        } else {
                    758:            *p = 0;
                    759:            Tcl_Return(interp, fileName, TCL_VOLATILE);
                    760:            *p = '/';
                    761:        }
                    762:        return TCL_OK;
                    763:     } else if ((argv[2][0] == 'r') && (length >= 2)
                    764:            && (strncmp(argv[2], "rootname", length) == 0)) {
                    765:        p = strrchr(fileName, '.');
                    766:        if (p == NULL) {
                    767:            Tcl_Return(interp, fileName, TCL_VOLATILE);
                    768:        } else {
                    769:            *p = 0;
                    770:            Tcl_Return(interp, fileName, TCL_VOLATILE);
                    771:            *p = '.';
                    772:        }
                    773:        return TCL_OK;
                    774:     } else if ((argv[2][0] == 'e') && (length >= 3)
                    775:            && (strncmp(argv[2], "extension", length) == 0)) {
                    776:        char *lastSlash;
                    777: 
                    778:        p = strrchr(fileName, '.');
                    779:        lastSlash = strrchr(fileName, '/');
                    780:        if ((p != NULL) && ((lastSlash == NULL) || (lastSlash < p))) {
                    781:            Tcl_Return(interp, p, TCL_VOLATILE);
                    782:        }
                    783:        return TCL_OK;
                    784:     } else if ((argv[2][0] == 't') && (strncmp(argv[2], "tail", length) == 0)) {
                    785:        p = strrchr(fileName, '/');
                    786:        if (p != NULL) {
                    787:            Tcl_Return(interp, p+1, TCL_VOLATILE);
                    788:        } else {
                    789:            Tcl_Return(interp, fileName, TCL_VOLATILE);
                    790:        }
                    791:        return TCL_OK;
                    792:     }
                    793: 
                    794:     /*
                    795:      * Next, handle operations that can be satisfied with the "access"
                    796:      * kernel call.
                    797:      */
                    798: 
                    799:     if (fileName == NULL) {
                    800:        return TCL_ERROR;
                    801:     }
                    802:     if ((argv[2][0] == 'r') && (length >= 2)
                    803:            && (strncmp(argv[2], "readable", length) == 0)) {
                    804:        mode = R_OK;
                    805:        checkAccess:
                    806:        if (access(fileName, mode) == -1) {
                    807:            interp->result = "0";
                    808:        } else {
                    809:            interp->result = "1";
                    810:        }
                    811:        return TCL_OK;
                    812:     } else if ((argv[2][0] == 'w')
                    813:            && (strncmp(argv[2], "writable", length) == 0)) {
                    814:        mode = W_OK;
                    815:        goto checkAccess;
                    816:     } else if ((argv[2][0] == 'e') && (length >= 3)
                    817:            && (strncmp(argv[2], "executable", length) == 0)) {
                    818:        mode = X_OK;
                    819:        goto checkAccess;
                    820:     } else if ((argv[2][0] == 'e') && (length >= 3)
                    821:            && (strncmp(argv[2], "exists", length) == 0)) {
                    822:        mode = F_OK;
                    823:        goto checkAccess;
                    824:     }
                    825: 
                    826:     /*
                    827:      * Lastly, check stuff that requires the file to be stat-ed.
                    828:      */
                    829: 
                    830:     if ((argv[2][0] == 'o') && (strncmp(argv[2], "owned", length) == 0)) {
                    831:        statOp = 0;
                    832:     } else if ((argv[2][0] == 'i') && (length >= 3)
                    833:            && (strncmp(argv[2], "isfile", length) == 0)) {
                    834:        statOp = 1;
                    835:     } else if ((argv[2][0] == 'i') && (length >= 3)
                    836:            && (strncmp(argv[2], "isdirectory", length) == 0)) {
                    837:        statOp = 2;
                    838:     } else {
                    839:        sprintf(interp->result, "bad \"%.30s\" option \"%.30s\": must be dirname, executable, exists, extension, isdirectory, isfile, owned, readable, root, tail, or writable",
                    840:                argv[0], argv[2]);
                    841:        return TCL_ERROR;
                    842:     }
                    843:     if (stat(fileName, &statBuf) == -1) {
                    844:        interp->result = "0";
                    845:        return TCL_OK;
                    846:     }
                    847:     switch (statOp) {
                    848:        case 0:
                    849:            mode = (geteuid() == statBuf.st_uid);
                    850:            break;
                    851:        case 1:
                    852:            mode = S_ISREG(statBuf.st_mode);
                    853:            break;
                    854:        case 2:
                    855:            mode = S_ISDIR(statBuf.st_mode);
                    856:            break;
                    857:     }
                    858:     if (mode) {
                    859:        interp->result = "1";
                    860:     } else {
                    861:        interp->result = "0";
                    862:     }
                    863:     return TCL_OK;
                    864: }
                    865: 
                    866: /*
                    867:  *----------------------------------------------------------------------
                    868:  *
                    869:  * Tcl_ForCmd --
                    870:  *
                    871:  *     This procedure is invoked to process the "for" Tcl command.
                    872:  *     See the user documentation for details on what it does.
                    873:  *
                    874:  * Results:
                    875:  *     A standard Tcl result.
                    876:  *
                    877:  * Side effects:
                    878:  *     See the user documentation.
                    879:  *
                    880:  *----------------------------------------------------------------------
                    881:  */
                    882: 
                    883:        /* ARGSUSED */
                    884: int
                    885: Tcl_ForCmd(dummy, interp, argc, argv)
                    886:     ClientData dummy;                  /* Not used. */
                    887:     Tcl_Interp *interp;                        /* Current interpreter. */
                    888:     int argc;                          /* Number of arguments. */
                    889:     char **argv;                       /* Argument strings. */
                    890: {
                    891: #pragma ref dummy
                    892:     int result, value;
                    893: 
                    894:     if (argc != 5) {
                    895:        sprintf(interp->result,
                    896:                "wrong # args: should be \"%.50s start test next command\"",
                    897:                argv[0]);
                    898:        return TCL_ERROR;
                    899:     }
                    900: 
                    901:     result = Tcl_Eval(interp, argv[1], 0, (char **) NULL);
                    902:     if (result != TCL_OK) {
                    903:        if (result == TCL_ERROR) {
                    904:            Tcl_AddErrorInfo(interp, " (\"for\" initial command)");
                    905:        }
                    906:        return result;
                    907:     }
                    908:     while (1) {
                    909:        result = Tcl_Expr(interp, argv[2], &value);
                    910:        if (result != TCL_OK) {
                    911:            return result;
                    912:        }
                    913:        if (!value) {
                    914:            break;
                    915:        }
                    916:        result = Tcl_Eval(interp, argv[4], 0, (char **) NULL);
                    917:        if (result == TCL_CONTINUE) {
                    918:            result = TCL_OK;
                    919:        } else if (result != TCL_OK) {
                    920:            if (result == TCL_ERROR) {
                    921:                char msg[60];
                    922:                sprintf(msg, " (\"for\" body line %d)", interp->errorLine);
                    923:                Tcl_AddErrorInfo(interp, msg);
                    924:            }
                    925:            break;
                    926:        }
                    927:        result = Tcl_Eval(interp, argv[3], 0, (char **) NULL);
                    928:        if (result == TCL_BREAK) {
                    929:            break;
                    930:        } else if (result != TCL_OK) {
                    931:            if (result == TCL_ERROR) {
                    932:                Tcl_AddErrorInfo(interp, " (\"for\" loop-end command)");
                    933:            }
                    934:            return result;
                    935:        }
                    936:     }
                    937:     if (result == TCL_BREAK) {
                    938:        result = TCL_OK;
                    939:     }
                    940:     if (result == TCL_OK) {
                    941:        Tcl_Return(interp, (char *) NULL, TCL_STATIC);
                    942:     }
                    943:     return result;
                    944: }
                    945: 
                    946: /*
                    947:  *----------------------------------------------------------------------
                    948:  *
                    949:  * Tcl_ForeachCmd --
                    950:  *
                    951:  *     This procedure is invoked to process the "foreach" Tcl command.
                    952:  *     See the user documentation for details on what it does.
                    953:  *
                    954:  * Results:
                    955:  *     A standard Tcl result.
                    956:  *
                    957:  * Side effects:
                    958:  *     See the user documentation.
                    959:  *
                    960:  *----------------------------------------------------------------------
                    961:  */
                    962: 
                    963:        /* ARGSUSED */
                    964: int
                    965: Tcl_ForeachCmd(dummy, interp, argc, argv)
                    966:     ClientData dummy;                  /* Not used. */
                    967:     Tcl_Interp *interp;                        /* Current interpreter. */
                    968:     int argc;                          /* Number of arguments. */
                    969:     char **argv;                       /* Argument strings. */
                    970: {
                    971: #pragma ref dummy
                    972:     int listArgc, i, result;
                    973:     char **listArgv;
                    974: 
                    975:     if (argc != 4) {
                    976:        sprintf(interp->result,
                    977:                "wrong # args: should be \"%.50s varName list command\"",
                    978:                argv[0]);
                    979:        return TCL_ERROR;
                    980:     }
                    981: 
                    982:     /*
                    983:      * Break the list up into elements, and execute the command once
                    984:      * for each value of the element.
                    985:      */
                    986: 
                    987:     result = Tcl_SplitList(interp, argv[2], &listArgc, &listArgv);
                    988:     if (result != TCL_OK) {
                    989:        return result;
                    990:     }
                    991:     for (i = 0; i < listArgc; i++) {
                    992:        Tcl_SetVar(interp, argv[1], listArgv[i], 0);
                    993: 
                    994:        result = Tcl_Eval(interp, argv[3], 0, (char **) NULL);
                    995:        if (result != TCL_OK) {
                    996:            if (result == TCL_CONTINUE) {
                    997:                result = TCL_OK;
                    998:            } else if (result == TCL_BREAK) {
                    999:                result = TCL_OK;
                   1000:                break;
                   1001:            } else if (result == TCL_ERROR) {
                   1002:                char msg[100];
                   1003:                sprintf(msg, " (\"foreach\" body line %d)", interp->errorLine);
                   1004:                Tcl_AddErrorInfo(interp, msg);
                   1005:                break;
                   1006:            } else {
                   1007:                break;
                   1008:            }
                   1009:        }
                   1010:     }
                   1011:     free((char *) listArgv);
                   1012:     if (result == TCL_OK) {
                   1013:        Tcl_Return(interp, (char *) NULL, TCL_STATIC);
                   1014:     }
                   1015:     return result;
                   1016: }
                   1017: 
                   1018: /*
                   1019:  *----------------------------------------------------------------------
                   1020:  *
                   1021:  * Tcl_FormatCmd --
                   1022:  *
                   1023:  *     This procedure is invoked to process the "format" Tcl command.
                   1024:  *     See the user documentation for details on what it does.
                   1025:  *
                   1026:  * Results:
                   1027:  *     A standard Tcl result.
                   1028:  *
                   1029:  * Side effects:
                   1030:  *     See the user documentation.
                   1031:  *
                   1032:  *----------------------------------------------------------------------
                   1033:  */
                   1034: 
                   1035:        /* ARGSUSED */
                   1036: int
                   1037: Tcl_FormatCmd(dummy, interp, argc, argv)
                   1038:     ClientData dummy;                  /* Not used. */
                   1039:     Tcl_Interp *interp;                        /* Current interpreter. */
                   1040:     int argc;                          /* Number of arguments. */
                   1041:     char **argv;                       /* Argument strings. */
                   1042: {
                   1043: #pragma ref dummy
                   1044:     register char *format;     /* Used to read characters from the format
                   1045:                                 * string. */
                   1046:     char newFormat[40];                /* A new format specifier is generated here. */
                   1047:     int width;                 /* Field width from field specifier, or 0 if
                   1048:                                 * no width given. */
                   1049:     int precision;             /* Field precision from field specifier, or 0
                   1050:                                 * if no precision given. */
                   1051:     int size;                  /* Number of bytes needed for result of
                   1052:                                 * conversion, based on type of conversion
                   1053:                                 * ("e", "s", etc.) and width from above. */
                   1054:     char *oneWordValue;                /* Used to hold value to pass to sprintf, if
                   1055:                                 * it's a one-word value. */
                   1056:     double twoWordValue;       /* Used to hold value to pass to sprintf if
                   1057:                                 * it's a two-word value. */
                   1058:     int useTwoWords;           /* 0 means use oneWordValue, 1 means use
                   1059:                                 * twoWordValue. */
                   1060:     char *dst = interp->result;        /* Where result is stored.  Starts off at
                   1061:                                 * interp->resultSpace, but may get dynamically
                   1062:                                 * re-allocated if this isn't enough. */
                   1063:     int dstSize = 0;           /* Number of non-null characters currently
                   1064:                                 * stored at dst. */
                   1065:     int dstSpace = TCL_RESULT_SIZE;
                   1066:                                /* Total amount of storage space available
                   1067:                                 * in dst (not including null terminator. */
                   1068:     int noPercent;             /* Special case for speed:  indicates there's
                   1069:                                 * no field specifier, just a string to copy. */
                   1070:     char **curArg;             /* Remainder of argv array. */
                   1071: 
                   1072:     /*
                   1073:      * This procedure is a bit nasty.  The goal is to use sprintf to
                   1074:      * do most of the dirty work.  There are several problems:
                   1075:      * 1. this procedure can't trust its arguments.
                   1076:      * 2. we must be able to provide a large enough result area to hold
                   1077:      *    whatever's generated.  This is hard to estimate.
                   1078:      * 2. there's no way to move the arguments from argv to the call
                   1079:      *    to sprintf in a reasonable way.  This is particularly nasty
                   1080:      *    because some of the arguments may be two-word values (doubles).
                   1081:      * So, what happens here is to scan the format string one % group
                   1082:      * at a time, making many individual calls to sprintf.
                   1083:      */
                   1084: 
                   1085:     if (argc < 2) {
                   1086:        sprintf(interp->result,
                   1087:                "too few args: should be \"%.50s formatString [arg arg ...]\"",
                   1088:                argv[0]);
                   1089:        return TCL_ERROR;
                   1090:     }
                   1091:     curArg = argv+2;
                   1092:     argc -= 2;
                   1093:     for (format = argv[1]; *format != 0; ) {
                   1094:        register char *newPtr = newFormat;
                   1095: 
                   1096:        width = precision = useTwoWords = noPercent = 0;
                   1097: 
                   1098:        /*
                   1099:         * Get rid of any characters before the next field specifier.
                   1100:         * Collapse backslash sequences found along the way.
                   1101:         */
                   1102: 
                   1103:        if (*format != '%') {
                   1104:            register char *p;
                   1105:            int bsSize;
                   1106: 
                   1107:            oneWordValue = format;
                   1108:            for (p = format; (*format != '%') && (*format != 0); p++) {
                   1109:                if (*format == '\\') {
                   1110:                    *p = Tcl_Backslash(format, &bsSize);
                   1111:                    format += bsSize;
                   1112:                } else {
                   1113:                    *p = *format;
                   1114:                    format++;
                   1115:                }
                   1116:            }
                   1117:            size = p - oneWordValue;
                   1118:            noPercent = 1;
                   1119:            goto doField;
                   1120:        }
                   1121: 
                   1122:        if (format[1] == '%') {
                   1123:            oneWordValue = format;
                   1124:            size = 1;
                   1125:            noPercent = 1;
                   1126:            format += 2;
                   1127:            goto doField;
                   1128:        }
                   1129: 
                   1130:        /*
                   1131:         * Parse off a field specifier, compute how many characters
                   1132:         * will be needed to store the result, and substitute for
                   1133:         * "*" size specifiers.
                   1134:         */
                   1135: 
                   1136:        *newPtr = '%';
                   1137:        newPtr++;
                   1138:        format++;
                   1139:        if (*format == '-') {
                   1140:            *newPtr = '-';
                   1141:            newPtr++;
                   1142:            format++;
                   1143:        }
                   1144:        if (*format == '0') {
                   1145:            *newPtr = '0';
                   1146:            newPtr++;
                   1147:            format++;
                   1148:        }
                   1149:        if (isdigit(*format)) {
                   1150:            width = atoi(format);
                   1151:            do {
                   1152:                format++;
                   1153:            } while (isdigit(*format));
                   1154:        } else if (*format == '*') {
                   1155:            if (argc <= 0) {
                   1156:                goto notEnoughArgs;
                   1157:            }
                   1158:            width = atoi(*curArg);
                   1159:            argc--;
                   1160:            curArg++;
                   1161:            format++;
                   1162:        }
                   1163:        if (width != 0) {
                   1164:            sprintf(newPtr, "%d", width);
                   1165:            while (*newPtr != 0) {
                   1166:                newPtr++;
                   1167:            }
                   1168:        }
                   1169:        if (*format == '.') {
                   1170:            *newPtr = '.';
                   1171:            newPtr++;
                   1172:            format++;
                   1173:        }
                   1174:        if (isdigit(*format)) {
                   1175:            precision = atoi(format);
                   1176:            do {
                   1177:                format++;
                   1178:            } while (isdigit(*format));
                   1179:        } else if (*format == '*') {
                   1180:            if (argc <= 0) {
                   1181:                goto notEnoughArgs;
                   1182:            }
                   1183:            precision = atoi(*curArg);
                   1184:            argc--;
                   1185:            curArg++;
                   1186:            format++;
                   1187:        }
                   1188:        if (precision != 0) {
                   1189:            sprintf(newPtr, "%d", precision);
                   1190:            while (*newPtr != 0) {
                   1191:                newPtr++;
                   1192:            }
                   1193:        }
                   1194:        if (*format == '#') {
                   1195:            *newPtr = '#';
                   1196:            newPtr++;
                   1197:            format++;
                   1198:        }
                   1199:        if (*format == 'l') {
                   1200:            format++;
                   1201:        }
                   1202:        *newPtr = *format;
                   1203:        newPtr++;
                   1204:        *newPtr = 0;
                   1205:        if (argc <= 0) {
                   1206:            goto notEnoughArgs;
                   1207:        }
                   1208:        switch (*format) {
                   1209:            case 'D':
                   1210:            case 'd':
                   1211:            case 'O':
                   1212:            case 'o':
                   1213:            case 'X':
                   1214:            case 'x':
                   1215:            case 'U':
                   1216:            case 'u': {
                   1217:                char *end;
                   1218: 
                   1219:                oneWordValue = (char *) strtol(*curArg, &end, 0);
                   1220:                if ((*curArg == 0) || (*end != 0)) {
                   1221:                    sprintf(interp->result,
                   1222:                            "expected integer but got \"%.50s\" instead",
                   1223:                            *curArg);
                   1224:                    goto fmtError;
                   1225:                }
                   1226:                size = 40;
                   1227:                break;
                   1228:            }
                   1229:            case 's':
                   1230:                oneWordValue = *curArg;
                   1231:                size = strlen(*curArg);
                   1232:                break;
                   1233:            case 'c': {
                   1234:                char *end;
                   1235: 
                   1236:                oneWordValue = (char *) strtol(*curArg, &end, 0);
                   1237:                if ((*curArg == 0) || (*end != 0)) {
                   1238:                    sprintf(interp->result,
                   1239:                            "expected integer but got \"%.50s\" instead",
                   1240:                            *curArg);
                   1241:                    goto fmtError;
                   1242:                }
                   1243:                size = 1;
                   1244:                break;
                   1245:            }
                   1246:            case 'F':
                   1247:            case 'f':
                   1248:            case 'E':
                   1249:            case 'e':
                   1250:            case 'G':
                   1251:            case 'g':
                   1252:                if (sscanf(*curArg, "%F", &twoWordValue) != 1) {
                   1253:                    sprintf(interp->result,
                   1254:                            "expected floating-point number but got \"%.50s\" instead",
                   1255:                            *curArg);
                   1256:                    goto fmtError;
                   1257:                }
                   1258:                useTwoWords = 1;
                   1259:                size = 320;
                   1260:                if (precision > 10) {
                   1261:                    size += precision;
                   1262:                }
                   1263:                break;
                   1264:            case 0:
                   1265:                interp->result = "format string ended in middle of field specifier";
                   1266:                goto fmtError;
                   1267:            default:
                   1268:                sprintf(interp->result, "bad field specifier \"%c\"", *format);
                   1269:                goto fmtError;
                   1270:        }
                   1271:        argc--;
                   1272:        curArg++;
                   1273:        format++;
                   1274: 
                   1275:        /*
                   1276:         * Make sure that there's enough space to hold the formatted
                   1277:         * result, then format it.
                   1278:         */
                   1279: 
                   1280:        doField:
                   1281:        if (width > size) {
                   1282:            size = width;
                   1283:        }
                   1284:        if ((dstSize + size) > dstSpace) {
                   1285:            char *newDst;
                   1286:            int newSpace;
                   1287: 
                   1288:            newSpace = 2*(dstSize + size);
                   1289:            newDst = (char *) malloc((unsigned) newSpace+1);
                   1290:            if (dstSize != 0) {
                   1291:                bcopy(dst, newDst, dstSize);
                   1292:            }
                   1293:            if (dstSpace != TCL_RESULT_SIZE) {
                   1294:                free(dst);
                   1295:            }
                   1296:            dst = newDst;
                   1297:            dstSpace = newSpace;
                   1298:        }
                   1299:        if (noPercent) {
                   1300:            bcopy(oneWordValue, dst+dstSize, size);
                   1301:            dstSize += size;
                   1302:            dst[dstSize] = 0;
                   1303:        } else {
                   1304:            if (useTwoWords) {
                   1305:                sprintf(dst+dstSize, newFormat, twoWordValue);
                   1306:            } else {
                   1307:                sprintf(dst+dstSize, newFormat, oneWordValue);
                   1308:            }
                   1309:            dstSize += strlen(dst+dstSize);
                   1310:        }
                   1311:     }
                   1312: 
                   1313:     interp->result = dst;
                   1314:     interp->dynamic = !(dstSpace == TCL_RESULT_SIZE);
                   1315:     return TCL_OK;
                   1316: 
                   1317:     notEnoughArgs:
                   1318:     sprintf(interp->result,
                   1319:            "invoked \"%.50s\" without enough arguments", argv[0]);
                   1320:     fmtError:
                   1321:     if (dstSpace != TCL_RESULT_SIZE) {
                   1322:        free(dst);
                   1323:     }
                   1324:     return TCL_ERROR;
                   1325: }

unix.superglobalmegacorp.com

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