|
|
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: }
This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.