|
|
1.1 ! root 1: /* ! 2: * tclProc.c -- ! 3: * ! 4: * This file contains routines that implement Tcl procedures and ! 5: * variables. ! 6: * ! 7: * Copyright 1987 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.35 90/03/29 10:55:16 ouster Exp $ SPRITE (Berkeley)"; ! 19: #pragma ref rcsid ! 20: #endif not lint ! 21: ! 22: #include <stdio.h> ! 23: #include <stdlib.h> ! 24: #include <string.h> ! 25: #include <ctype.h> ! 26: #include "tclInt.h" ! 27: ! 28: /* ! 29: * Forward references to procedures defined later in this file: ! 30: */ ! 31: ! 32: extern Var * FindVar(); ! 33: extern int InterpProc(); ! 34: extern Var * NewVar(); ! 35: extern void ProcDeleteProc(); ! 36: ! 37: /* ! 38: *---------------------------------------------------------------------- ! 39: * ! 40: * Tcl_ProcCmd -- ! 41: * ! 42: * This procedure is invoked to process the "proc" Tcl command. ! 43: * See the user documentation for details on what it does. ! 44: * ! 45: * Results: ! 46: * A standard Tcl result value. ! 47: * ! 48: * Side effects: ! 49: * A new procedure gets created. ! 50: * ! 51: *---------------------------------------------------------------------- ! 52: */ ! 53: ! 54: /* ARGSUSED */ ! 55: int ! 56: Tcl_ProcCmd(dummy, interp, argc, argv) ! 57: ClientData dummy; /* Not used. */ ! 58: Tcl_Interp *interp; /* Current interpreter. */ ! 59: int argc; /* Number of arguments. */ ! 60: char **argv; /* Argument strings. */ ! 61: { ! 62: #pragma ref dummy ! 63: register Interp *iPtr = (Interp *) interp; ! 64: register Proc *procPtr; ! 65: int result, argCount, i; ! 66: char **argArray; ! 67: ! 68: if (argc != 4) { ! 69: sprintf(iPtr->result, ! 70: "wrong # args: should be \"%.50s name args body\"", ! 71: argv[0]); ! 72: return TCL_ERROR; ! 73: } ! 74: ! 75: procPtr = (Proc *) malloc(sizeof(Proc)); ! 76: procPtr->iPtr = iPtr; ! 77: procPtr->command = (char *) malloc((unsigned) strlen(argv[3]) + 1); ! 78: strcpy(procPtr->command, argv[3]); ! 79: procPtr->argPtr = NULL; ! 80: Tcl_CreateCommand(interp, argv[1], InterpProc, ! 81: (ClientData) procPtr, ProcDeleteProc); ! 82: ! 83: /* ! 84: * Break up the argument list into argument specifiers, then process ! 85: * each argument specifier. ! 86: */ ! 87: ! 88: result = Tcl_SplitList(interp, argv[2], &argCount, &argArray); ! 89: if (result != TCL_OK) { ! 90: return result; ! 91: } ! 92: for (i = 0; i < argCount; i++) { ! 93: int fieldCount, nameLength, valueLength; ! 94: char **fieldValues; ! 95: register Var *argPtr; ! 96: ! 97: /* ! 98: * Now divide the specifier up into name and default. ! 99: */ ! 100: ! 101: result = Tcl_SplitList(interp, argArray[i], &fieldCount, ! 102: &fieldValues); ! 103: if (result != TCL_OK) { ! 104: goto procError; ! 105: } ! 106: if (fieldCount > 2) { ! 107: sprintf(iPtr->result, ! 108: "too many fields in argument specifier \"%.50s\"", ! 109: argArray[i]); ! 110: result = TCL_ERROR; ! 111: goto procError; ! 112: } ! 113: if ((fieldCount == 0) || (*fieldValues[0] == 0)) { ! 114: sprintf(iPtr->result, ! 115: "procedure \"%.50s\" has argument with no name", argv[1]); ! 116: result = TCL_ERROR; ! 117: goto procError; ! 118: } ! 119: nameLength = strlen(fieldValues[0]); ! 120: if (fieldCount == 2) { ! 121: valueLength = strlen(fieldValues[1]); ! 122: } else { ! 123: valueLength = 0; ! 124: } ! 125: if (procPtr->argPtr == NULL) { ! 126: argPtr = (Var *) malloc(VAR_SIZE(nameLength, valueLength)); ! 127: procPtr->argPtr = argPtr; ! 128: } else { ! 129: argPtr->nextPtr = (Var *) malloc(VAR_SIZE(nameLength, valueLength)); ! 130: argPtr = argPtr->nextPtr; ! 131: } ! 132: strcpy(argPtr->name, fieldValues[0]); ! 133: if (fieldCount == 2) { ! 134: argPtr->value = argPtr->name + nameLength + 1; ! 135: strcpy(argPtr->value, fieldValues[1]); ! 136: } else { ! 137: argPtr->value = NULL; ! 138: } ! 139: argPtr->valueLength = valueLength; ! 140: argPtr->flags = 0; ! 141: argPtr->nextPtr = NULL; ! 142: free((char *) fieldValues); ! 143: } ! 144: ! 145: free((char *) argArray); ! 146: return TCL_OK; ! 147: ! 148: procError: ! 149: free((char *) argArray); ! 150: return result; ! 151: } ! 152: ! 153: /*1 ! 154: *---------------------------------------------------------------------- ! 155: * ! 156: * Tcl_GetVar -- ! 157: * ! 158: * Return the value of a Tcl variable. ! 159: * ! 160: * Results: ! 161: * The return value points to the current value of varName. If ! 162: * the variable is not defined in interp, either as a local or ! 163: * global variable, then a NULL pointer is returned. Note: the ! 164: * return value is only valid up until the next call to Tcl_SetVar; ! 165: * if you depend on the value lasting longer than that, then make ! 166: * yourself a private copy. ! 167: * ! 168: * Side effects: ! 169: * None. ! 170: * ! 171: *---------------------------------------------------------------------- ! 172: */ ! 173: ! 174: char * ! 175: Tcl_GetVar(interp, varName, global) ! 176: Tcl_Interp *interp; /* Command interpreter in which varName is ! 177: * to be looked up. */ ! 178: char *varName; /* Name of a variable in interp. */ ! 179: int global; /* If non-zero, use only a global variable */ ! 180: { ! 181: Var *varPtr; ! 182: Interp *iPtr = (Interp *) interp; ! 183: ! 184: if (global || (iPtr->varFramePtr == NULL)) { ! 185: varPtr = FindVar(&iPtr->globalPtr, varName); ! 186: } else { ! 187: varPtr = FindVar(&iPtr->varFramePtr->varPtr, varName); ! 188: } ! 189: if (varPtr == NULL) { ! 190: return NULL; ! 191: } ! 192: if (varPtr->flags & VAR_GLOBAL) { ! 193: varPtr = varPtr->globalPtr; ! 194: } ! 195: if (varPtr->flags & VAR_DOESNT_EXIST) { ! 196: return NULL; ! 197: } ! 198: return varPtr->value; ! 199: } ! 200: ! 201: /* ! 202: *---------------------------------------------------------------------- ! 203: * ! 204: * Tcl_SetVar -- ! 205: * ! 206: * Change the value of a variable. ! 207: * ! 208: * Results: ! 209: * None. ! 210: * ! 211: * Side effects: ! 212: * If varName is defined as a local or global variable in interp, ! 213: * its value is changed to newValue. If varName isn't currently ! 214: * defined, then a new global variable by that name is created. ! 215: * ! 216: *---------------------------------------------------------------------- ! 217: */ ! 218: ! 219: void ! 220: Tcl_SetVar(interp, varName, newValue, global) ! 221: Tcl_Interp *interp; /* Command interpreter in which varName is ! 222: * to be looked up. */ ! 223: char *varName; /* Name of a variable in interp. */ ! 224: char *newValue; /* New value for varName. */ ! 225: int global; /* If non-zero, use only a global variable. */ ! 226: { ! 227: register Var *varPtr, **varListPtr; ! 228: register Interp *iPtr = (Interp *) interp; ! 229: int valueLength; ! 230: ! 231: if (global || (iPtr->varFramePtr == NULL)) { ! 232: varListPtr = &iPtr->globalPtr; ! 233: } else { ! 234: varListPtr = &iPtr->varFramePtr->varPtr; ! 235: } ! 236: varPtr = FindVar(varListPtr, varName); ! 237: if (varPtr == NULL) { ! 238: varPtr = NewVar(varName, newValue); ! 239: varPtr->nextPtr = *varListPtr; ! 240: *varListPtr = varPtr; ! 241: } else { ! 242: if (varPtr->flags & VAR_GLOBAL) { ! 243: varPtr = varPtr->globalPtr; ! 244: } ! 245: valueLength = strlen(newValue); ! 246: if (valueLength > varPtr->valueLength) { ! 247: if (varPtr->flags & VAR_DYNAMIC) { ! 248: free(varPtr->value); ! 249: } ! 250: varPtr->value = (char *) malloc((unsigned) valueLength + 1); ! 251: varPtr->flags |= VAR_DYNAMIC; ! 252: varPtr->valueLength = valueLength; ! 253: } ! 254: strcpy(varPtr->value, newValue); ! 255: varPtr->flags &= ~VAR_DOESNT_EXIST; ! 256: } ! 257: } ! 258: ! 259: /* ! 260: *---------------------------------------------------------------------- ! 261: * ! 262: * Tcl_ParseVar -- ! 263: * ! 264: * Given a string starting with a $ sign, parse off a variable ! 265: * name and return its value. ! 266: * ! 267: * Results: ! 268: * The return value is the contents of the variable given by ! 269: * the leading characters of string. If termPtr isn't NULL, ! 270: * *termPtr gets filled in with the address of the character ! 271: * just after the last one in the variable specifier. If the ! 272: * variable doesn't exist, then the return value is NULL and ! 273: * an error message will be left in interp->result. ! 274: * ! 275: * Side effects: ! 276: * None. ! 277: * ! 278: *---------------------------------------------------------------------- ! 279: */ ! 280: ! 281: char * ! 282: Tcl_ParseVar(interp, string, termPtr) ! 283: Tcl_Interp *interp; /* Context for looking up variable. */ ! 284: register char *string; /* String containing variable name. ! 285: * First character must be "$". */ ! 286: char **termPtr; /* If non-NULL, points to word to fill ! 287: * in with character just after last ! 288: * one in the variable specifier. */ ! 289: ! 290: { ! 291: char *name, c, *result; ! 292: ! 293: /* ! 294: * There are two cases: ! 295: * 1. The $ sign is followed by an open curly brace. Then the variable ! 296: * name is everything up to the next close curly brace. ! 297: * 2. The $ sign is not followed by an open curly brace. Then the ! 298: * variable name is everything up to the next character that isn't ! 299: * a letter, digit, or underscore. ! 300: * 3. The $ sign is followed by something that isn't a letter, digit, ! 301: * or underscore: in this case, there is no variable name, and "$" ! 302: * is returned. ! 303: */ ! 304: ! 305: string++; ! 306: if (*string == '{') { ! 307: string++; ! 308: name = string; ! 309: while ((*string != '}') && (*string != 0)) { ! 310: string++; ! 311: } ! 312: if (termPtr != 0) { ! 313: if (*string != 0) { ! 314: *termPtr = string+1; ! 315: } else { ! 316: *termPtr = string; ! 317: } ! 318: } ! 319: } else { ! 320: name = string; ! 321: while (isalnum(*string) || (*string == '_')) { ! 322: string++; ! 323: } ! 324: if (termPtr != 0) { ! 325: *termPtr = string; ! 326: } ! 327: if (string == name) { ! 328: return "$"; ! 329: } ! 330: } ! 331: ! 332: c = *string; ! 333: *string = 0; ! 334: result = Tcl_GetVar(interp, name, 0); ! 335: if (result == NULL) { ! 336: Tcl_Return(interp, (char *) NULL, TCL_STATIC); ! 337: sprintf(interp->result, "couldn't find variable \"%.50s\"", name); ! 338: } ! 339: *string = c; ! 340: return result; ! 341: } ! 342: ! 343: /* ! 344: *---------------------------------------------------------------------- ! 345: * ! 346: * Tcl_SetCmd -- ! 347: * ! 348: * This procedure is invoked to process the "set" Tcl command. ! 349: * See the user documentation for details on what it does. ! 350: * ! 351: * Results: ! 352: * A standard Tcl result value. ! 353: * ! 354: * Side effects: ! 355: * A variable's value may be changed. ! 356: * ! 357: *---------------------------------------------------------------------- ! 358: */ ! 359: ! 360: /* ARGSUSED */ ! 361: int ! 362: Tcl_SetCmd(dummy, interp, argc, argv) ! 363: ClientData dummy; /* Not used. */ ! 364: register Tcl_Interp *interp; /* Current interpreter. */ ! 365: int argc; /* Number of arguments. */ ! 366: char **argv; /* Argument strings. */ ! 367: { ! 368: #pragma ref dummy ! 369: if (argc == 2) { ! 370: char *value; ! 371: ! 372: value = Tcl_GetVar(interp, argv[1], 0); ! 373: if (value == NULL) { ! 374: sprintf(interp->result, "couldn't find variable \"%.50s\"", ! 375: argv[1]); ! 376: return TCL_ERROR; ! 377: } ! 378: interp->result = value; ! 379: return TCL_OK; ! 380: } else if (argc == 3) { ! 381: Tcl_SetVar(interp, argv[1], argv[2], 0); ! 382: return TCL_OK; ! 383: } else { ! 384: sprintf(interp->result, ! 385: "wrong # args: should be \"%.50s varName [newValue]\"", ! 386: argv[0]); ! 387: return TCL_ERROR; ! 388: } ! 389: } ! 390: ! 391: /* ! 392: *---------------------------------------------------------------------- ! 393: * ! 394: * Tcl_GlobalCmd -- ! 395: * ! 396: * This procedure is invoked to process the "global" Tcl command. ! 397: * See the user documentation for details on what it does. ! 398: * ! 399: * Results: ! 400: * A standard Tcl result value. ! 401: * ! 402: * Side effects: ! 403: * See the user documentation. ! 404: * ! 405: *---------------------------------------------------------------------- ! 406: */ ! 407: ! 408: /* ARGSUSED */ ! 409: int ! 410: Tcl_GlobalCmd(dummy, interp, argc, argv) ! 411: ClientData dummy; /* Not used. */ ! 412: Tcl_Interp *interp; /* Current interpreter. */ ! 413: int argc; /* Number of arguments. */ ! 414: char **argv; /* Argument strings. */ ! 415: { ! 416: #pragma ref dummy ! 417: register Var *varPtr; ! 418: register Interp *iPtr = (Interp *) interp; ! 419: Var *gVarPtr; ! 420: ! 421: if (argc < 2) { ! 422: sprintf(iPtr->result, ! 423: "too few args: should be \"%.50s varName varName ...\"", ! 424: argv[0]); ! 425: return TCL_ERROR; ! 426: } ! 427: if (iPtr->varFramePtr == NULL) { ! 428: return TCL_OK; ! 429: } ! 430: ! 431: for (argc--, argv++; argc > 0; argc--, argv++) { ! 432: gVarPtr = FindVar(&iPtr->globalPtr, *argv); ! 433: if (gVarPtr == NULL) { ! 434: gVarPtr = NewVar(*argv, ""); ! 435: gVarPtr->nextPtr = iPtr->globalPtr; ! 436: iPtr->globalPtr = gVarPtr; ! 437: gVarPtr->flags |= VAR_DOESNT_EXIST; ! 438: } ! 439: varPtr = NewVar(*argv, ""); ! 440: varPtr->flags |= VAR_GLOBAL; ! 441: varPtr->globalPtr = gVarPtr; ! 442: varPtr->nextPtr = iPtr->varFramePtr->varPtr; ! 443: iPtr->varFramePtr->varPtr = varPtr; ! 444: } ! 445: return TCL_OK; ! 446: } ! 447: ! 448: /* ! 449: *---------------------------------------------------------------------- ! 450: * ! 451: * Tcl_UplevelCmd -- ! 452: * ! 453: * This procedure is invoked to process the "uplevel" Tcl command. ! 454: * See the user documentation for details on what it does. ! 455: * ! 456: * Results: ! 457: * A standard Tcl result value. ! 458: * ! 459: * Side effects: ! 460: * See the user documentation. ! 461: * ! 462: *---------------------------------------------------------------------- ! 463: */ ! 464: ! 465: /* ARGSUSED */ ! 466: int ! 467: Tcl_UplevelCmd(dummy, interp, argc, argv) ! 468: ClientData dummy; /* Not used. */ ! 469: Tcl_Interp *interp; /* Current interpreter. */ ! 470: int argc; /* Number of arguments. */ ! 471: char **argv; /* Argument strings. */ ! 472: { ! 473: #pragma ref dummy ! 474: register Interp *iPtr = (Interp *) interp; ! 475: int level, result; ! 476: char *end, *levelArg; ! 477: CallFrame *savedVarFramePtr, *framePtr; ! 478: ! 479: if (argc < 2) { ! 480: uplevelSyntax: ! 481: sprintf(iPtr->result, ! 482: "too few args: should be \"%.50s [level] command ...\"", ! 483: argv[0]); ! 484: return TCL_ERROR; ! 485: } ! 486: ! 487: /* ! 488: * Parse arguments to figure out which level to go to, and set ! 489: * argv and argc to refer to the command to execute at that level. ! 490: */ ! 491: ! 492: levelArg = argv[1]; ! 493: if (*levelArg == '#') { ! 494: level = strtoul(levelArg+1, &end, 10); ! 495: if ((end == (levelArg+1)) || (*end != '\0')) { ! 496: goto levelError; ! 497: } ! 498: argc -= 2; ! 499: argv += 2; ! 500: } else if (isdigit(*levelArg)) { ! 501: level = strtoul(levelArg, &end, 10); ! 502: if ((end == levelArg) || (*end != '\0')) { ! 503: goto levelError; ! 504: } ! 505: if (iPtr->varFramePtr == NULL) { ! 506: goto levelError; ! 507: } ! 508: level = iPtr->varFramePtr->level - level; ! 509: argc -= 2; ! 510: argv += 2; ! 511: } else { ! 512: if (iPtr->varFramePtr == NULL) { ! 513: goto levelError; ! 514: } ! 515: level = iPtr->varFramePtr->level - 1; ! 516: argc--; ! 517: argv++; ! 518: } ! 519: ! 520: /* ! 521: * Figure out which frame to use, and modify the interpreter so ! 522: * its variables come from that frame. ! 523: */ ! 524: ! 525: savedVarFramePtr = iPtr->varFramePtr; ! 526: if (level == 0) { ! 527: iPtr->varFramePtr = NULL; ! 528: } else { ! 529: for (framePtr = savedVarFramePtr; framePtr != NULL; ! 530: framePtr = framePtr->callerVarPtr) { ! 531: if (framePtr->level == level) { ! 532: break; ! 533: } ! 534: } ! 535: if (framePtr == NULL) { ! 536: goto levelError; ! 537: } ! 538: iPtr->varFramePtr = framePtr; ! 539: } ! 540: ! 541: /* ! 542: * Execute the residual arguments as a command. ! 543: */ ! 544: ! 545: if (argc == 0) { ! 546: goto uplevelSyntax; ! 547: } ! 548: if (argc == 1) { ! 549: result = Tcl_Eval(interp, argv[0], 0, (char **) NULL); ! 550: } else { ! 551: char *cmd; ! 552: ! 553: cmd = Tcl_Concat(argc, argv); ! 554: result = Tcl_Eval(interp, cmd, 0, (char **) NULL); ! 555: } ! 556: if (result == TCL_ERROR) { ! 557: char msg[60]; ! 558: sprintf(msg, " (\"uplevel\" body line %d)", interp->errorLine); ! 559: Tcl_AddErrorInfo(interp, msg); ! 560: } ! 561: ! 562: /* ! 563: * Restore the variable frame, and return. ! 564: */ ! 565: ! 566: iPtr->varFramePtr = savedVarFramePtr; ! 567: return result; ! 568: ! 569: levelError: ! 570: sprintf(iPtr->result, "bad level \"%.50s\"", levelArg); ! 571: return TCL_ERROR; ! 572: } ! 573: ! 574: /* ! 575: *---------------------------------------------------------------------- ! 576: * ! 577: * TclFindProc -- ! 578: * ! 579: * Given the name of a procedure, return a pointer to the ! 580: * record describing the procedure. ! 581: * ! 582: * Results: ! 583: * NULL is returned if the name doesn't correspond to any ! 584: * procedure. Otherwise the return value is a pointer to ! 585: * the procedure's record. ! 586: * ! 587: * Side effects: ! 588: * None. ! 589: * ! 590: *---------------------------------------------------------------------- ! 591: */ ! 592: ! 593: Proc * ! 594: TclFindProc(iPtr, procName) ! 595: Interp *iPtr; /* Interpreter in which to look. */ ! 596: char *procName; /* Name of desired procedure. */ ! 597: { ! 598: Command *cmdPtr; ! 599: ! 600: cmdPtr = TclFindCmd(iPtr, procName, 0); ! 601: if (cmdPtr == NULL) { ! 602: return NULL; ! 603: } ! 604: if (cmdPtr->proc != InterpProc) { ! 605: return NULL; ! 606: } ! 607: return (Proc *) cmdPtr->clientData; ! 608: } ! 609: ! 610: /* ! 611: *---------------------------------------------------------------------- ! 612: * ! 613: * TclIsProc -- ! 614: * ! 615: * Tells whether a command is a Tcl procedure or not. ! 616: * ! 617: * Results: ! 618: * If the given command is actuall a Tcl procedure, the ! 619: * return value is the address of the record describing ! 620: * the procedure. Otherwise the return value is 0. ! 621: * ! 622: * Side effects: ! 623: * None. ! 624: * ! 625: *---------------------------------------------------------------------- ! 626: */ ! 627: ! 628: Proc * ! 629: TclIsProc(cmdPtr) ! 630: Command *cmdPtr; /* Command to test. */ ! 631: { ! 632: if (cmdPtr->proc == InterpProc) { ! 633: return (Proc *) cmdPtr->clientData; ! 634: } ! 635: return (Proc *) 0; ! 636: } ! 637: ! 638: /* ! 639: *---------------------------------------------------------------------- ! 640: * ! 641: * TclDeleteVars -- ! 642: * ! 643: * This procedure is called as part of deleting an interpreter: ! 644: * it recycles all the storage space associated with global ! 645: * variables (the local ones should already have been deleted). ! 646: * ! 647: * Results: ! 648: * None. ! 649: * ! 650: * Side effects: ! 651: * Variables are deleted. ! 652: * ! 653: *---------------------------------------------------------------------- ! 654: */ ! 655: ! 656: void ! 657: TclDeleteVars(iPtr) ! 658: Interp *iPtr; /* Interpreter to nuke. */ ! 659: { ! 660: register Var *varPtr; ! 661: ! 662: for (varPtr = iPtr->globalPtr; varPtr != NULL; varPtr = varPtr->nextPtr) { ! 663: if (varPtr->flags & VAR_DYNAMIC) { ! 664: free(varPtr->value); ! 665: } ! 666: free((char *) varPtr); ! 667: } ! 668: } ! 669: ! 670: /* ! 671: *---------------------------------------------------------------------- ! 672: * ! 673: * InterpProc -- ! 674: * ! 675: * When a Tcl procedure gets invoked, this routine gets invoked ! 676: * to interpret the procedure. ! 677: * ! 678: * Results: ! 679: * A standard Tcl result value, usually TCL_OK. ! 680: * ! 681: * Side effects: ! 682: * Depends on the commands in the procedure. ! 683: * ! 684: *---------------------------------------------------------------------- ! 685: */ ! 686: ! 687: int ! 688: InterpProc(procPtr, interp, argc, argv) ! 689: register Proc *procPtr; /* Record describing procedure to be ! 690: * interpreted. */ ! 691: Tcl_Interp *interp; /* Interpreter in which procedure was ! 692: * invoked. */ ! 693: int argc; /* Count of number of arguments to this ! 694: * procedure. */ ! 695: char **argv; /* Argument values. */ ! 696: { ! 697: char **args; ! 698: register Var *formalPtr, *argPtr; ! 699: register Interp *iPtr = (Interp *) interp; ! 700: CallFrame frame; ! 701: char *value, *end; ! 702: int result; ! 703: ! 704: /* ! 705: * Set up a call frame for the new procedure invocation. ! 706: */ ! 707: ! 708: iPtr = procPtr->iPtr; ! 709: frame.varPtr = NULL; ! 710: if (iPtr->varFramePtr != NULL) { ! 711: frame.level = iPtr->varFramePtr->level + 1; ! 712: } else { ! 713: frame.level = 1; ! 714: } ! 715: frame.argc = argc; ! 716: frame.argv = argv; ! 717: frame.callerPtr = iPtr->framePtr; ! 718: frame.callerVarPtr = iPtr->varFramePtr; ! 719: iPtr->framePtr = &frame; ! 720: iPtr->varFramePtr = &frame; ! 721: ! 722: /* ! 723: * Match the actual arguments against the procedure's formal ! 724: * parameters to compute local variables. ! 725: */ ! 726: ! 727: for (formalPtr = procPtr->argPtr, args = argv+1, argc -= 1; ! 728: formalPtr != NULL; ! 729: formalPtr = formalPtr->nextPtr, args++, argc--) { ! 730: ! 731: /* ! 732: * Handle the special case of the last formal being "args". When ! 733: * it occurs, assign it a list consisting of all the remaining ! 734: * actual arguments. ! 735: */ ! 736: ! 737: if ((formalPtr->nextPtr == NULL) ! 738: && (strcmp(formalPtr->name, "args") == 0)) { ! 739: if (argc < 0) { ! 740: argc = 0; ! 741: } ! 742: value = Tcl_Merge(argc, args); ! 743: argPtr = NewVar(formalPtr->name, value); ! 744: free(value); ! 745: argPtr->nextPtr = frame.varPtr; ! 746: frame.varPtr = argPtr; ! 747: argc = 0; ! 748: break; ! 749: } else if (argc > 0) { ! 750: value = *args; ! 751: } else if (formalPtr->value != NULL) { ! 752: value = formalPtr->value; ! 753: } else { ! 754: sprintf(iPtr->result, ! 755: "no value given for parameter \"%s\" to \"%s\"", ! 756: formalPtr->name, argv[0]); ! 757: result = TCL_ERROR; ! 758: goto procDone; ! 759: } ! 760: argPtr = NewVar(formalPtr->name, value); ! 761: argPtr->nextPtr = frame.varPtr; ! 762: frame.varPtr = argPtr; ! 763: } ! 764: if (argc > 0) { ! 765: sprintf(iPtr->result, "called \"%s\" with too many arguments", ! 766: argv[0]); ! 767: result = TCL_ERROR; ! 768: goto procDone; ! 769: } ! 770: ! 771: /* ! 772: * Invoke the commands in the procedure's body. ! 773: */ ! 774: ! 775: result = Tcl_Eval(interp, procPtr->command, 0, &end); ! 776: if (result == TCL_RETURN) { ! 777: result = TCL_OK; ! 778: } else if (result == TCL_ERROR) { ! 779: char msg[100]; ! 780: ! 781: /* ! 782: * Record information telling where the error occurred. ! 783: */ ! 784: ! 785: sprintf(msg, " (procedure \"%.50s\" line %d)", argv[0], ! 786: iPtr->errorLine); ! 787: Tcl_AddErrorInfo(interp, msg); ! 788: } else if (result == TCL_BREAK) { ! 789: iPtr->result = "invoked \"break\" outside of a loop"; ! 790: result = TCL_ERROR; ! 791: } else if (result == TCL_CONTINUE) { ! 792: iPtr->result = "invoked \"continue\" outside of a loop"; ! 793: result = TCL_ERROR; ! 794: } ! 795: ! 796: /* ! 797: * Delete the call frame for this procedure invocation. ! 798: */ ! 799: ! 800: procDone: ! 801: for (argPtr = frame.varPtr; argPtr != NULL; argPtr = argPtr->nextPtr) { ! 802: if (argPtr->flags & VAR_DYNAMIC) { ! 803: free(argPtr->value); ! 804: } ! 805: free((char *) argPtr); ! 806: } ! 807: iPtr->framePtr = frame.callerPtr; ! 808: iPtr->varFramePtr = frame.callerVarPtr; ! 809: return result; ! 810: } ! 811: ! 812: /* ! 813: *---------------------------------------------------------------------- ! 814: * ! 815: * ProcDeleteProc -- ! 816: * ! 817: * This procedure is invoked just before a command procedure is ! 818: * removed from an interpreter. Its job is to release all the ! 819: * resources allocated to the procedure. ! 820: * ! 821: * Results: ! 822: * None. ! 823: * ! 824: * Side effects: ! 825: * Memory gets freed. ! 826: * ! 827: *---------------------------------------------------------------------- ! 828: */ ! 829: ! 830: void ! 831: ProcDeleteProc(procPtr) ! 832: register Proc *procPtr; /* Procedure to be deleted. */ ! 833: { ! 834: register Var *argPtr; ! 835: ! 836: free((char *) procPtr->command); ! 837: for (argPtr = procPtr->argPtr; argPtr != NULL; argPtr = argPtr->nextPtr) { ! 838: if (argPtr->flags & VAR_DYNAMIC) { ! 839: free(argPtr->value); ! 840: } ! 841: free((char *) argPtr); ! 842: } ! 843: free((char *) procPtr); ! 844: } ! 845: ! 846: /* ! 847: *---------------------------------------------------------------------- ! 848: * ! 849: * FindVar -- ! 850: * ! 851: * Locate the Var structure corresponding to varName, if there ! 852: * is one defined in a given list. ! 853: * ! 854: * Results: ! 855: * The return value points to the Var structure corresponding to ! 856: * the current value of varName in varListPtr, or NULL if varName ! 857: * isn't currently defined in the list. ! 858: * ! 859: * Side effects: ! 860: * If the variable is found, it is moved to the front of the list. ! 861: * ! 862: *---------------------------------------------------------------------- ! 863: */ ! 864: ! 865: Var * ! 866: FindVar(varListPtr, varName) ! 867: Var **varListPtr; /* Pointer to head of list. The value pointed ! 868: * to will be modified to bring the found ! 869: * variable to the front of the list. */ ! 870: char *varName; /* Desired variable. */ ! 871: { ! 872: register Var *prev, *cur; ! 873: register char c; ! 874: ! 875: c = *varName; ! 876: ! 877: /* ! 878: * Local variables take precedence over global ones. Check the ! 879: * first character immediately, before wasting time calling strcmp. ! 880: */ ! 881: ! 882: for (prev = NULL, cur = *varListPtr; cur != NULL; ! 883: prev = cur, cur = cur->nextPtr) { ! 884: if ((cur->name[0] == c) && (strcmp(cur->name, varName) == 0)) { ! 885: if (prev != NULL) { ! 886: prev->nextPtr = cur->nextPtr; ! 887: cur->nextPtr = *varListPtr; ! 888: *varListPtr = cur; ! 889: } ! 890: return cur; ! 891: } ! 892: } ! 893: return NULL; ! 894: } ! 895: ! 896: /* ! 897: *---------------------------------------------------------------------- ! 898: * ! 899: * NewVar -- ! 900: * ! 901: * Create a new variable with the given name and initial value. ! 902: * ! 903: * Results: ! 904: * The return value is a pointer to the new variable. The variable ! 905: * will not have been linked into any particular list, and its ! 906: * nextPtr field will be NULL. ! 907: * ! 908: * Side effects: ! 909: * Storage gets allocated. ! 910: * ! 911: *---------------------------------------------------------------------- ! 912: */ ! 913: ! 914: Var * ! 915: NewVar(name, value) ! 916: char *name; /* Name for variable. */ ! 917: char *value; /* Value for variable. */ ! 918: { ! 919: register Var *varPtr; ! 920: int nameLength, valueLength; ! 921: ! 922: nameLength = strlen(name); ! 923: valueLength = strlen(value); ! 924: if (valueLength < 20) { ! 925: valueLength = 20; ! 926: } ! 927: varPtr = (Var *) malloc(VAR_SIZE(nameLength, valueLength)); ! 928: strcpy(varPtr->name, name); ! 929: varPtr->value = varPtr->name + nameLength + 1; ! 930: strcpy(varPtr->value, value); ! 931: varPtr->valueLength = valueLength; ! 932: varPtr->flags = 0; ! 933: varPtr->globalPtr = NULL; ! 934: varPtr->nextPtr = NULL; ! 935: return varPtr; ! 936: }
This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.