Annotation of micropolis/src/tcl/tclproc.c, revision 1.1.1.1

1.1       root        1: /* 
                      2:  * tclProc.c --
                      3:  *
                      4:  *     This file contains routines that implement Tcl procedures,
                      5:  *     including the "proc" and "uplevel" commands.
                      6:  *
                      7:  * Copyright 1987-1991 Regents of the University of California
                      8:  * Permission to use, copy, modify, and distribute this
                      9:  * software and its documentation for any purpose and without
                     10:  * fee is hereby granted, provided that the above copyright
                     11:  * notice appear in all copies.  The University of California
                     12:  * makes no representations about the suitability of this
                     13:  * software for any purpose.  It is provided "as is" without
                     14:  * express or implied warranty.
                     15:  */
                     16: 
                     17: #ifndef lint
                     18: static char rcsid[] = "$Header: /sprite/src/lib/tcl/RCS/tclProc.c,v 1.59 91/09/30 16:59:54 ouster Exp $ SPRITE (Berkeley)";
                     19: #endif
                     20: 
                     21: #include "tclint.h"
                     22: 
                     23: /*
                     24:  * Forward references to procedures defined later in this file:
                     25:  */
                     26: 
                     27: static  int    InterpProc _ANSI_ARGS_((ClientData clientData,
                     28:                    Tcl_Interp *interp, int argc, char **argv));
                     29: static  void   ProcDeleteProc _ANSI_ARGS_((ClientData clientData));
                     30: 
                     31: /*
                     32:  *----------------------------------------------------------------------
                     33:  *
                     34:  * Tcl_ProcCmd --
                     35:  *
                     36:  *     This procedure is invoked to process the "proc" Tcl command.
                     37:  *     See the user documentation for details on what it does.
                     38:  *
                     39:  * Results:
                     40:  *     A standard Tcl result value.
                     41:  *
                     42:  * Side effects:
                     43:  *     A new procedure gets created.
                     44:  *
                     45:  *----------------------------------------------------------------------
                     46:  */
                     47: 
                     48:        /* ARGSUSED */
                     49: int
                     50: Tcl_ProcCmd(dummy, interp, argc, argv)
                     51:     ClientData dummy;                  /* Not used. */
                     52:     Tcl_Interp *interp;                        /* Current interpreter. */
                     53:     int argc;                          /* Number of arguments. */
                     54:     char **argv;                       /* Argument strings. */
                     55: {
                     56:     register Interp *iPtr = (Interp *) interp;
                     57:     register Proc *procPtr;
                     58:     int result, argCount, i;
                     59:     char **argArray = NULL;
                     60:     Arg *lastArgPtr;
                     61:     register Arg *argPtr = NULL;       /* Initialization not needed, but
                     62:                                         * prevents compiler warning. */
                     63: 
                     64:     if (argc != 4) {
                     65:        Tcl_AppendResult(interp, "wrong # args: should be \"", argv[0],
                     66:                " name args body\"", (char *) NULL);
                     67:        return TCL_ERROR;
                     68:     }
                     69: 
                     70:     procPtr = (Proc *) ckalloc(sizeof(Proc));
                     71:     procPtr->iPtr = iPtr;
                     72:     procPtr->command = (char *) ckalloc((unsigned) strlen(argv[3]) + 1);
                     73:     strcpy(procPtr->command, argv[3]);
                     74:     procPtr->argPtr = NULL;
                     75: 
                     76:     /*
                     77:      * Break up the argument list into argument specifiers, then process
                     78:      * each argument specifier.
                     79:      */
                     80: 
                     81:     result = Tcl_SplitList(interp, argv[2], &argCount, &argArray);
                     82:     if (result != TCL_OK) {
                     83:        goto procError;
                     84:     }
                     85:     lastArgPtr = NULL;
                     86:     for (i = 0; i < argCount; i++) {
                     87:        int fieldCount, nameLength, valueLength;
                     88:        char **fieldValues;
                     89: 
                     90:        /*
                     91:         * Now divide the specifier up into name and default.
                     92:         */
                     93: 
                     94:        result = Tcl_SplitList(interp, argArray[i], &fieldCount,
                     95:                &fieldValues);
                     96:        if (result != TCL_OK) {
                     97:            goto procError;
                     98:        }
                     99:        if (fieldCount > 2) {
                    100:            ckfree((char *) fieldValues);
                    101:            Tcl_AppendResult(interp,
                    102:                    "too many fields in argument specifier \"",
                    103:                    argArray[i], "\"", (char *) NULL);
                    104:            result = TCL_ERROR;
                    105:            goto procError;
                    106:        }
                    107:        if ((fieldCount == 0) || (*fieldValues[0] == 0)) {
                    108:            ckfree((char *) fieldValues);
                    109:            Tcl_AppendResult(interp, "procedure \"", argv[1],
                    110:                    "\" has argument with no name", (char *) NULL);
                    111:            result = TCL_ERROR;
                    112:            goto procError;
                    113:        }
                    114:        nameLength = strlen(fieldValues[0]) + 1;
                    115:        if (fieldCount == 2) {
                    116:            valueLength = strlen(fieldValues[1]) + 1;
                    117:        } else {
                    118:            valueLength = 0;
                    119:        }
                    120:        argPtr = (Arg *) ckalloc((unsigned)
                    121:                (sizeof(Arg) - sizeof(argPtr->name) + nameLength
                    122:                + valueLength));
                    123:        if (lastArgPtr == NULL) {
                    124:            procPtr->argPtr = argPtr;
                    125:        } else {
                    126:            lastArgPtr->nextPtr = argPtr;
                    127:        }
                    128:        lastArgPtr = argPtr;
                    129:        argPtr->nextPtr = NULL;
                    130:        strcpy(argPtr->name, fieldValues[0]);
                    131:        if (fieldCount == 2) {
                    132:            argPtr->defValue = argPtr->name + nameLength;
                    133:            strcpy(argPtr->defValue, fieldValues[1]);
                    134:        } else {
                    135:            argPtr->defValue = NULL;
                    136:        }
                    137:        ckfree((char *) fieldValues);
                    138:     }
                    139: 
                    140:     Tcl_CreateCommand(interp, argv[1], InterpProc, (ClientData) procPtr,
                    141:            ProcDeleteProc);
                    142:     ckfree((char *) argArray);
                    143:     return TCL_OK;
                    144: 
                    145:     procError:
                    146:     ckfree(procPtr->command);
                    147:     while (procPtr->argPtr != NULL) {
                    148:        argPtr = procPtr->argPtr;
                    149:        procPtr->argPtr = argPtr->nextPtr;
                    150:        ckfree((char *) argPtr);
                    151:     }
                    152:     ckfree((char *) procPtr);
                    153:     if (argArray != NULL) {
                    154:        ckfree((char *) argArray);
                    155:     }
                    156:     return result;
                    157: }
                    158: 
                    159: /*
                    160:  *----------------------------------------------------------------------
                    161:  *
                    162:  * TclGetFrame --
                    163:  *
                    164:  *     Given a description of a procedure frame, such as the first
                    165:  *     argument to an "uplevel" or "upvar" command, locate the
                    166:  *     call frame for the appropriate level of procedure.
                    167:  *
                    168:  * Results:
                    169:  *     The return value is -1 if an error occurred in finding the
                    170:  *     frame (in this case an error message is left in interp->result).
                    171:  *     1 is returned if string was either a number or a number preceded
                    172:  *     by "#" and it specified a valid frame.  0 is returned if string
                    173:  *     isn't one of the two things above (in this case, the lookup
                    174:  *     acts as if string were "1").  The variable pointed to by
                    175:  *     framePtrPtr is filled in with the address of the desired frame
                    176:  *     (unless an error occurs, in which case it isn't modified).
                    177:  *
                    178:  * Side effects:
                    179:  *     None.
                    180:  *
                    181:  *----------------------------------------------------------------------
                    182:  */
                    183: 
                    184: int
                    185: TclGetFrame(interp, string, framePtrPtr)
                    186:     Tcl_Interp *interp;                /* Interpreter in which to find frame. */
                    187:     char *string;              /* String describing frame. */
                    188:     CallFrame **framePtrPtr;   /* Store pointer to frame here (or NULL
                    189:                                 * if global frame indicated). */
                    190: {
                    191:     register Interp *iPtr = (Interp *) interp;
                    192:     int level, result;
                    193:     CallFrame *framePtr;
                    194: 
                    195:     if (iPtr->varFramePtr == NULL) {
                    196:        iPtr->result = "already at top level";
                    197:        return -1;
                    198:     }
                    199: 
                    200:     /*
                    201:      * Parse string to figure out which level number to go to.
                    202:      */
                    203: 
                    204:     result = 1;
                    205:     if (*string == '#') {
                    206:        if (Tcl_GetInt(interp, string+1, &level) != TCL_OK) {
                    207:            return -1;
                    208:        }
                    209:        if (level < 0) {
                    210:            levelError:
                    211:            Tcl_AppendResult(interp, "bad level \"", string, "\"",
                    212:                    (char *) NULL);
                    213:            return -1;
                    214:        }
                    215:     } else if (isdigit(*string)) {
                    216:        if (Tcl_GetInt(interp, string, &level) != TCL_OK) {
                    217:            return -1;
                    218:        }
                    219:        level = iPtr->varFramePtr->level - level;
                    220:     } else {
                    221:        level = iPtr->varFramePtr->level - 1;
                    222:        result = 0;
                    223:     }
                    224: 
                    225:     /*
                    226:      * Figure out which frame to use, and modify the interpreter so
                    227:      * its variables come from that frame.
                    228:      */
                    229: 
                    230:     if (level == 0) {
                    231:        framePtr = NULL;
                    232:     } else {
                    233:        for (framePtr = iPtr->varFramePtr; framePtr != NULL;
                    234:                framePtr = framePtr->callerVarPtr) {
                    235:            if (framePtr->level == level) {
                    236:                break;
                    237:            }
                    238:        }
                    239:        if (framePtr == NULL) {
                    240:            goto levelError;
                    241:        }
                    242:     }
                    243:     *framePtrPtr = framePtr;
                    244:     return result;
                    245: }
                    246: 
                    247: /*
                    248:  *----------------------------------------------------------------------
                    249:  *
                    250:  * Tcl_UplevelCmd --
                    251:  *
                    252:  *     This procedure is invoked to process the "uplevel" Tcl command.
                    253:  *     See the user documentation for details on what it does.
                    254:  *
                    255:  * Results:
                    256:  *     A standard Tcl result value.
                    257:  *
                    258:  * Side effects:
                    259:  *     See the user documentation.
                    260:  *
                    261:  *----------------------------------------------------------------------
                    262:  */
                    263: 
                    264:        /* ARGSUSED */
                    265: int
                    266: Tcl_UplevelCmd(dummy, interp, argc, argv)
                    267:     ClientData dummy;                  /* Not used. */
                    268:     Tcl_Interp *interp;                        /* Current interpreter. */
                    269:     int argc;                          /* Number of arguments. */
                    270:     char **argv;                       /* Argument strings. */
                    271: {
                    272:     register Interp *iPtr = (Interp *) interp;
                    273:     int result;
                    274:     CallFrame *savedVarFramePtr, *framePtr;
                    275: 
                    276:     if (argc < 2) {
                    277:        uplevelSyntax:
                    278:        Tcl_AppendResult(interp, "wrong # args: should be \"", argv[0],
                    279:                " ?level? command ?command ...?\"", (char *) NULL);
                    280:        return TCL_ERROR;
                    281:     }
                    282: 
                    283:     /*
                    284:      * Find the level to use for executing the command.
                    285:      */
                    286: 
                    287:     result = TclGetFrame(interp, argv[1], &framePtr);
                    288:     if (result == -1) {
                    289:        return TCL_ERROR;
                    290:     }
                    291:     argc -= (result+1);
                    292:     argv += (result+1);
                    293: 
                    294:     /*
                    295:      * Modify the interpreter state to execute in the given frame.
                    296:      */
                    297: 
                    298:     savedVarFramePtr = iPtr->varFramePtr;
                    299:     iPtr->varFramePtr = framePtr;
                    300: 
                    301:     /*
                    302:      * Execute the residual arguments as a command.
                    303:      */
                    304: 
                    305:     if (argc == 0) {
                    306:        goto uplevelSyntax;
                    307:     }
                    308:     if (argc == 1) {
                    309:        result = Tcl_Eval(interp, argv[0], 0, (char **) NULL);
                    310:     } else {
                    311:        char *cmd;
                    312: 
                    313:        cmd = Tcl_Concat(argc, argv);
                    314:        result = Tcl_Eval(interp, cmd, 0, (char **) NULL);
                    315:        ckfree(cmd);
                    316:     }
                    317:     if (result == TCL_ERROR) {
                    318:        char msg[60];
                    319:        sprintf(msg, "\n    (\"uplevel\" body line %d)", interp->errorLine);
                    320:        Tcl_AddErrorInfo(interp, msg);
                    321:     }
                    322: 
                    323:     /*
                    324:      * Restore the variable frame, and return.
                    325:      */
                    326: 
                    327:     iPtr->varFramePtr = savedVarFramePtr;
                    328:     return result;
                    329: }
                    330: 
                    331: /*
                    332:  *----------------------------------------------------------------------
                    333:  *
                    334:  * TclFindProc --
                    335:  *
                    336:  *     Given the name of a procedure, return a pointer to the
                    337:  *     record describing the procedure.
                    338:  *
                    339:  * Results:
                    340:  *     NULL is returned if the name doesn't correspond to any
                    341:  *     procedure.  Otherwise the return value is a pointer to
                    342:  *     the procedure's record.
                    343:  *
                    344:  * Side effects:
                    345:  *     None.
                    346:  *
                    347:  *----------------------------------------------------------------------
                    348:  */
                    349: 
                    350: Proc *
                    351: TclFindProc(iPtr, procName)
                    352:     Interp *iPtr;              /* Interpreter in which to look. */
                    353:     char *procName;            /* Name of desired procedure. */
                    354: {
                    355:     Tcl_HashEntry *hPtr;
                    356:     Command *cmdPtr;
                    357: 
                    358:     hPtr = Tcl_FindHashEntry(&iPtr->commandTable, procName);
                    359:     if (hPtr == NULL) {
                    360:        return NULL;
                    361:     }
                    362:     cmdPtr = (Command *) Tcl_GetHashValue(hPtr);
                    363:     if (cmdPtr->proc != InterpProc) {
                    364:        return NULL;
                    365:     }
                    366:     return (Proc *) cmdPtr->clientData;
                    367: }
                    368: 
                    369: /*
                    370:  *----------------------------------------------------------------------
                    371:  *
                    372:  * TclIsProc --
                    373:  *
                    374:  *     Tells whether a command is a Tcl procedure or not.
                    375:  *
                    376:  * Results:
                    377:  *     If the given command is actuall a Tcl procedure, the
                    378:  *     return value is the address of the record describing
                    379:  *     the procedure.  Otherwise the return value is 0.
                    380:  *
                    381:  * Side effects:
                    382:  *     None.
                    383:  *
                    384:  *----------------------------------------------------------------------
                    385:  */
                    386: 
                    387: Proc *
                    388: TclIsProc(cmdPtr)
                    389:     Command *cmdPtr;           /* Command to test. */
                    390: {
                    391:     if (cmdPtr->proc == InterpProc) {
                    392:        return (Proc *) cmdPtr->clientData;
                    393:     }
                    394:     return (Proc *) 0;
                    395: }
                    396: 
                    397: /*
                    398:  *----------------------------------------------------------------------
                    399:  *
                    400:  * InterpProc --
                    401:  *
                    402:  *     When a Tcl procedure gets invoked, this routine gets invoked
                    403:  *     to interpret the procedure.
                    404:  *
                    405:  * Results:
                    406:  *     A standard Tcl result value, usually TCL_OK.
                    407:  *
                    408:  * Side effects:
                    409:  *     Depends on the commands in the procedure.
                    410:  *
                    411:  *----------------------------------------------------------------------
                    412:  */
                    413: 
                    414: static int
                    415: InterpProc(clientData, interp, argc, argv)
                    416:     ClientData clientData;     /* Record describing procedure to be
                    417:                                 * interpreted. */
                    418:     Tcl_Interp *interp;                /* Interpreter in which procedure was
                    419:                                 * invoked. */
                    420:     int argc;                  /* Count of number of arguments to this
                    421:                                 * procedure. */
                    422:     char **argv;               /* Argument values. */
                    423: {
                    424:     register Proc *procPtr = (Proc *) clientData;
                    425:     register Arg *argPtr;
                    426:     register Interp *iPtr = (Interp *) interp;
                    427:     char **args;
                    428:     CallFrame frame;
                    429:     char *value, *end;
                    430:     int result;
                    431: 
                    432:     /*
                    433:      * Set up a call frame for the new procedure invocation.
                    434:      */
                    435: 
                    436:     iPtr = procPtr->iPtr;
                    437:     Tcl_InitHashTable(&frame.varTable, TCL_STRING_KEYS);
                    438:     if (iPtr->varFramePtr != NULL) {
                    439:        frame.level = iPtr->varFramePtr->level + 1;
                    440:     } else {
                    441:        frame.level = 1;
                    442:     }
                    443:     frame.argc = argc;
                    444:     frame.argv = argv;
                    445:     frame.callerPtr = iPtr->framePtr;
                    446:     frame.callerVarPtr = iPtr->varFramePtr;
                    447:     iPtr->framePtr = &frame;
                    448:     iPtr->varFramePtr = &frame;
                    449: 
                    450:     /*
                    451:      * Match the actual arguments against the procedure's formal
                    452:      * parameters to compute local variables.
                    453:      */
                    454: 
                    455:     for (argPtr = procPtr->argPtr, args = argv+1, argc -= 1;
                    456:            argPtr != NULL;
                    457:            argPtr = argPtr->nextPtr, args++, argc--) {
                    458: 
                    459:        /*
                    460:         * Handle the special case of the last formal being "args".  When
                    461:         * it occurs, assign it a list consisting of all the remaining
                    462:         * actual arguments.
                    463:         */
                    464: 
                    465:        if ((argPtr->nextPtr == NULL)
                    466:                && (strcmp(argPtr->name, "args") == 0)) {
                    467:            if (argc < 0) {
                    468:                argc = 0;
                    469:            }
                    470:            value = Tcl_Merge(argc, args);
                    471:            Tcl_SetVar(interp, argPtr->name, value, 0);
                    472:            ckfree(value);
                    473:            argc = 0;
                    474:            break;
                    475:        } else if (argc > 0) {
                    476:            value = *args;
                    477:        } else if (argPtr->defValue != NULL) {
                    478:            value = argPtr->defValue;
                    479:        } else {
                    480:            Tcl_AppendResult(interp, "no value given for parameter \"",
                    481:                    argPtr->name, "\" to \"", argv[0], "\"",
                    482:                    (char *) NULL);
                    483:            result = TCL_ERROR;
                    484:            goto procDone;
                    485:        }
                    486:        Tcl_SetVar(interp, argPtr->name, value, 0);
                    487:     }
                    488:     if (argc > 0) {
                    489:        Tcl_AppendResult(interp, "called \"", argv[0],
                    490:                "\" with too many arguments", (char *) NULL);
                    491:        result = TCL_ERROR;
                    492:        goto procDone;
                    493:     }
                    494: 
                    495:     /*
                    496:      * Invoke the commands in the procedure's body.
                    497:      */
                    498: 
                    499:     result = Tcl_Eval(interp, procPtr->command, 0, &end);
                    500:     if (result == TCL_RETURN) {
                    501:        result = TCL_OK;
                    502:     } else if (result == TCL_ERROR) {
                    503:        char msg[100];
                    504: 
                    505:        /*
                    506:         * Record information telling where the error occurred.
                    507:         */
                    508: 
                    509:        sprintf(msg, "\n    (procedure \"%.50s\" line %d)", argv[0],
                    510:                iPtr->errorLine);
                    511:        Tcl_AddErrorInfo(interp, msg);
                    512:     } else if (result == TCL_BREAK) {
                    513:        iPtr->result = "invoked \"break\" outside of a loop";
                    514:        result = TCL_ERROR;
                    515:     } else if (result == TCL_CONTINUE) {
                    516:        iPtr->result = "invoked \"continue\" outside of a loop";
                    517:        result = TCL_ERROR;
                    518:     }
                    519: 
                    520:     /*
                    521:      * Delete the call frame for this procedure invocation (it's
                    522:      * important to remove the call frame from the interpreter
                    523:      * before deleting it, so that traces invoked during the
                    524:      * deletion don't see the partially-deleted frame).
                    525:      */
                    526: 
                    527:     procDone:
                    528:     iPtr->framePtr = frame.callerPtr;
                    529:     iPtr->varFramePtr = frame.callerVarPtr;
                    530:     TclDeleteVars(iPtr, &frame.varTable);
                    531:     return result;
                    532: }
                    533: 
                    534: /*
                    535:  *----------------------------------------------------------------------
                    536:  *
                    537:  * ProcDeleteProc --
                    538:  *
                    539:  *     This procedure is invoked just before a command procedure is
                    540:  *     removed from an interpreter.  Its job is to release all the
                    541:  *     resources allocated to the procedure.
                    542:  *
                    543:  * Results:
                    544:  *     None.
                    545:  *
                    546:  * Side effects:
                    547:  *     Memory gets freed.
                    548:  *
                    549:  *----------------------------------------------------------------------
                    550:  */
                    551: 
                    552: static void
                    553: ProcDeleteProc(clientData)
                    554:     ClientData clientData;             /* Procedure to be deleted. */
                    555: {
                    556:     register Proc *procPtr = (Proc *) clientData;
                    557:     register Arg *argPtr;
                    558: 
                    559:     ckfree((char *) procPtr->command);
                    560:     for (argPtr = procPtr->argPtr; argPtr != NULL; ) {
                    561:        Arg *nextPtr = argPtr->nextPtr;
                    562: 
                    563:        ckfree((char *) argPtr);
                    564:        argPtr = nextPtr;
                    565:     }
                    566:     ckfree((char *) procPtr);
                    567: }

unix.superglobalmegacorp.com

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