Annotation of micropolis/src/tcl/tclproc.c, revision 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.