|
|
1.1 ! root 1: /* ! 2: * tclBasic.c -- ! 3: * ! 4: * Contains the basic facilities for TCL command interpretation, ! 5: * including interpreter creation and deletion, command creation ! 6: * and deletion, and command parsing and execution. ! 7: * ! 8: * Copyright 1987-1992 Regents of the University of California ! 9: * Permission to use, copy, modify, and distribute this ! 10: * software and its documentation for any purpose and without ! 11: * fee is hereby granted, provided that the above copyright ! 12: * notice appear in all copies. The University of California ! 13: * makes no representations about the suitability of this ! 14: * software for any purpose. It is provided "as is" without ! 15: * express or implied warranty. ! 16: */ ! 17: ! 18: #ifndef lint ! 19: static char rcsid[] = "$Header: /user6/ouster/tcl/RCS/tclBasic.c,v 1.131 92/06/21 14:09:41 ouster Exp $ SPRITE (Berkeley)"; ! 20: #endif ! 21: ! 22: #include "tclint.h" ! 23: ! 24: /* ! 25: * The following structure defines all of the commands in the Tcl core, ! 26: * and the C procedures that execute them. ! 27: */ ! 28: ! 29: typedef struct { ! 30: char *name; /* Name of command. */ ! 31: Tcl_CmdProc *proc; /* Procedure that executes command. */ ! 32: } CmdInfo; ! 33: ! 34: /* ! 35: * Built-in commands, and the procedures associated with them: ! 36: */ ! 37: ! 38: static CmdInfo builtInCmds[] = { ! 39: /* ! 40: * Commands in the generic core: ! 41: */ ! 42: ! 43: {"append", Tcl_AppendCmd}, ! 44: {"array", Tcl_ArrayCmd}, ! 45: {"break", Tcl_BreakCmd}, ! 46: {"case", Tcl_CaseCmd}, ! 47: {"catch", Tcl_CatchCmd}, ! 48: {"concat", Tcl_ConcatCmd}, ! 49: {"continue", Tcl_ContinueCmd}, ! 50: {"error", Tcl_ErrorCmd}, ! 51: {"eval", Tcl_EvalCmd}, ! 52: {"expr", Tcl_ExprCmd}, ! 53: {"for", Tcl_ForCmd}, ! 54: {"foreach", Tcl_ForeachCmd}, ! 55: {"format", Tcl_FormatCmd}, ! 56: {"global", Tcl_GlobalCmd}, ! 57: {"if", Tcl_IfCmd}, ! 58: {"incr", Tcl_IncrCmd}, ! 59: {"info", Tcl_InfoCmd}, ! 60: {"join", Tcl_JoinCmd}, ! 61: {"lappend", Tcl_LappendCmd}, ! 62: {"lindex", Tcl_LindexCmd}, ! 63: {"linsert", Tcl_LinsertCmd}, ! 64: {"list", Tcl_ListCmd}, ! 65: {"llength", Tcl_LlengthCmd}, ! 66: {"lrange", Tcl_LrangeCmd}, ! 67: {"lreplace", Tcl_LreplaceCmd}, ! 68: {"lsearch", Tcl_LsearchCmd}, ! 69: {"lsort", Tcl_LsortCmd}, ! 70: {"proc", Tcl_ProcCmd}, ! 71: {"regexp", Tcl_RegexpCmd}, ! 72: {"regsub", Tcl_RegsubCmd}, ! 73: {"rename", Tcl_RenameCmd}, ! 74: {"return", Tcl_ReturnCmd}, ! 75: {"scan", Tcl_ScanCmd}, ! 76: {"set", Tcl_SetCmd}, ! 77: {"split", Tcl_SplitCmd}, ! 78: {"string", Tcl_StringCmd}, ! 79: {"trace", Tcl_TraceCmd}, ! 80: {"unset", Tcl_UnsetCmd}, ! 81: {"uplevel", Tcl_UplevelCmd}, ! 82: {"upvar", Tcl_UpvarCmd}, ! 83: {"while", Tcl_WhileCmd}, ! 84: ! 85: /* ! 86: * Commands in the UNIX core: ! 87: */ ! 88: ! 89: #ifndef TCL_GENERIC_ONLY ! 90: {"cd", Tcl_CdCmd}, ! 91: {"close", Tcl_CloseCmd}, ! 92: {"eof", Tcl_EofCmd}, ! 93: {"exec", Tcl_ExecCmd}, ! 94: {"exit", Tcl_ExitCmd}, ! 95: {"file", Tcl_FileCmd}, ! 96: {"flush", Tcl_FlushCmd}, ! 97: {"gets", Tcl_GetsCmd}, ! 98: {"glob", Tcl_GlobCmd}, ! 99: {"open", Tcl_OpenCmd}, ! 100: {"puts", Tcl_PutsCmd}, ! 101: {"pwd", Tcl_PwdCmd}, ! 102: {"read", Tcl_ReadCmd}, ! 103: {"seek", Tcl_SeekCmd}, ! 104: {"source", Tcl_SourceCmd}, ! 105: {"tell", Tcl_TellCmd}, ! 106: {"time", Tcl_TimeCmd}, ! 107: #endif /* TCL_GENERIC_ONLY */ ! 108: {NULL, (Tcl_CmdProc *) NULL} ! 109: }; ! 110: ! 111: /* ! 112: *---------------------------------------------------------------------- ! 113: * ! 114: * Tcl_CreateInterp -- ! 115: * ! 116: * Create a new TCL command interpreter. ! 117: * ! 118: * Results: ! 119: * The return value is a token for the interpreter, which may be ! 120: * used in calls to procedures like Tcl_CreateCmd, Tcl_Eval, or ! 121: * Tcl_DeleteInterp. ! 122: * ! 123: * Side effects: ! 124: * The command interpreter is initialized with an empty variable ! 125: * table and the built-in commands. ! 126: * ! 127: *---------------------------------------------------------------------- ! 128: */ ! 129: ! 130: Tcl_Interp * ! 131: Tcl_CreateInterp() ! 132: { ! 133: register Interp *iPtr; ! 134: register Command *cmdPtr; ! 135: register CmdInfo *cmdInfoPtr; ! 136: int i; ! 137: ! 138: iPtr = (Interp *) ckalloc(sizeof(Interp)); ! 139: iPtr->result = iPtr->resultSpace; ! 140: iPtr->freeProc = 0; ! 141: iPtr->errorLine = 0; ! 142: Tcl_InitHashTable(&iPtr->commandTable, TCL_STRING_KEYS); ! 143: Tcl_InitHashTable(&iPtr->globalTable, TCL_STRING_KEYS); ! 144: iPtr->numLevels = 0; ! 145: iPtr->framePtr = NULL; ! 146: iPtr->varFramePtr = NULL; ! 147: iPtr->activeTracePtr = NULL; ! 148: iPtr->numEvents = 0; ! 149: iPtr->events = NULL; ! 150: iPtr->curEvent = 0; ! 151: iPtr->curEventNum = 0; ! 152: iPtr->revPtr = NULL; ! 153: iPtr->historyFirst = NULL; ! 154: iPtr->revDisables = 1; ! 155: iPtr->evalFirst = iPtr->evalLast = NULL; ! 156: iPtr->appendResult = NULL; ! 157: iPtr->appendAvl = 0; ! 158: iPtr->appendUsed = 0; ! 159: iPtr->numFiles = 0; ! 160: iPtr->filePtrArray = NULL; ! 161: for (i = 0; i < NUM_REGEXPS; i++) { ! 162: iPtr->patterns[i] = NULL; ! 163: iPtr->patLengths[i] = -1; ! 164: iPtr->regexps[i] = NULL; ! 165: } ! 166: iPtr->cmdCount = 0; ! 167: iPtr->noEval = 0; ! 168: iPtr->scriptFile = NULL; ! 169: iPtr->flags = 0; ! 170: iPtr->tracePtr = NULL; ! 171: iPtr->resultSpace[0] = 0; ! 172: ! 173: /* ! 174: * Create the built-in commands. Do it here, rather than calling ! 175: * Tcl_CreateCommand, because it's faster (there's no need to ! 176: * check for a pre-existing command by the same name). ! 177: */ ! 178: ! 179: for (cmdInfoPtr = builtInCmds; cmdInfoPtr->name != NULL; cmdInfoPtr++) { ! 180: int new; ! 181: Tcl_HashEntry *hPtr; ! 182: ! 183: hPtr = Tcl_CreateHashEntry(&iPtr->commandTable, ! 184: cmdInfoPtr->name, &new); ! 185: if (new) { ! 186: cmdPtr = (Command *) ckalloc(sizeof(Command)); ! 187: cmdPtr->proc = cmdInfoPtr->proc; ! 188: cmdPtr->clientData = (ClientData) NULL; ! 189: cmdPtr->deleteProc = NULL; ! 190: Tcl_SetHashValue(hPtr, cmdPtr); ! 191: } ! 192: } ! 193: ! 194: #ifndef TCL_GENERIC_ONLY ! 195: TclSetupEnv((Tcl_Interp *) iPtr); ! 196: #endif ! 197: ! 198: return (Tcl_Interp *) iPtr; ! 199: } ! 200: ! 201: /* ! 202: *---------------------------------------------------------------------- ! 203: * ! 204: * Tcl_DeleteInterp -- ! 205: * ! 206: * Delete an interpreter and free up all of the resources associated ! 207: * with it. ! 208: * ! 209: * Results: ! 210: * None. ! 211: * ! 212: * Side effects: ! 213: * The interpreter is destroyed. The caller should never again ! 214: * use the interp token. ! 215: * ! 216: *---------------------------------------------------------------------- ! 217: */ ! 218: ! 219: void ! 220: Tcl_DeleteInterp(interp) ! 221: Tcl_Interp *interp; /* Token for command interpreter (returned ! 222: * by a previous call to Tcl_CreateInterp). */ ! 223: { ! 224: Interp *iPtr = (Interp *) interp; ! 225: Tcl_HashEntry *hPtr; ! 226: Tcl_HashSearch search; ! 227: register Command *cmdPtr; ! 228: int i; ! 229: ! 230: /* ! 231: * If the interpreter is in use, delay the deletion until later. ! 232: */ ! 233: ! 234: iPtr->flags |= DELETED; ! 235: if (iPtr->numLevels != 0) { ! 236: return; ! 237: } ! 238: ! 239: /* ! 240: * Free up any remaining resources associated with the ! 241: * interpreter. ! 242: */ ! 243: ! 244: for (hPtr = Tcl_FirstHashEntry(&iPtr->commandTable, &search); ! 245: hPtr != NULL; hPtr = Tcl_NextHashEntry(&search)) { ! 246: cmdPtr = (Command *) Tcl_GetHashValue(hPtr); ! 247: if (cmdPtr->deleteProc != NULL) { ! 248: (*cmdPtr->deleteProc)(cmdPtr->clientData); ! 249: } ! 250: ckfree((char *) cmdPtr); ! 251: } ! 252: Tcl_DeleteHashTable(&iPtr->commandTable); ! 253: TclDeleteVars(iPtr, &iPtr->globalTable); ! 254: if (iPtr->events != NULL) { ! 255: int i; ! 256: ! 257: for (i = 0; i < iPtr->numEvents; i++) { ! 258: ckfree(iPtr->events[i].command); ! 259: } ! 260: ckfree((char *) iPtr->events); ! 261: } ! 262: while (iPtr->revPtr != NULL) { ! 263: HistoryRev *nextPtr = iPtr->revPtr->nextPtr; ! 264: ! 265: ckfree((char *) iPtr->revPtr); ! 266: iPtr->revPtr = nextPtr; ! 267: } ! 268: if (iPtr->appendResult != NULL) { ! 269: ckfree(iPtr->appendResult); ! 270: } ! 271: #ifndef TCL_GENERIC_ONLY ! 272: if (iPtr->numFiles > 0) { ! 273: for (i = 0; i < iPtr->numFiles; i++) { ! 274: OpenFile *filePtr; ! 275: ! 276: filePtr = iPtr->filePtrArray[i]; ! 277: if (filePtr == NULL) { ! 278: continue; ! 279: } ! 280: if (i >= 3) { ! 281: fclose(filePtr->f); ! 282: if (filePtr->f2 != NULL) { ! 283: fclose(filePtr->f2); ! 284: } ! 285: if (filePtr->numPids > 0) { ! 286: Tcl_DetachPids(filePtr->numPids, filePtr->pidPtr); ! 287: ckfree((char *) filePtr->pidPtr); ! 288: } ! 289: } ! 290: ckfree((char *) filePtr); ! 291: } ! 292: ckfree((char *) iPtr->filePtrArray); ! 293: } ! 294: #endif ! 295: for (i = 0; i < NUM_REGEXPS; i++) { ! 296: if (iPtr->patterns[i] == NULL) { ! 297: break; ! 298: } ! 299: ckfree(iPtr->patterns[i]); ! 300: ckfree((char *) iPtr->regexps[i]); ! 301: } ! 302: while (iPtr->tracePtr != NULL) { ! 303: Trace *nextPtr = iPtr->tracePtr->nextPtr; ! 304: ! 305: ckfree((char *) iPtr->tracePtr); ! 306: iPtr->tracePtr = nextPtr; ! 307: } ! 308: ckfree((char *) iPtr); ! 309: } ! 310: ! 311: /* ! 312: *---------------------------------------------------------------------- ! 313: * ! 314: * Tcl_CreateCommand -- ! 315: * ! 316: * Define a new command in a command table. ! 317: * ! 318: * Results: ! 319: * None. ! 320: * ! 321: * Side effects: ! 322: * If a command named cmdName already exists for interp, it is ! 323: * deleted. In the future, when cmdName is seen as the name of ! 324: * a command by Tcl_Eval, proc will be called. When the command ! 325: * is deleted from the table, deleteProc will be called. See the ! 326: * manual entry for details on the calling sequence. ! 327: * ! 328: *---------------------------------------------------------------------- ! 329: */ ! 330: ! 331: void ! 332: Tcl_CreateCommand(interp, cmdName, proc, clientData, deleteProc) ! 333: Tcl_Interp *interp; /* Token for command interpreter (returned ! 334: * by a previous call to Tcl_CreateInterp). */ ! 335: char *cmdName; /* Name of command. */ ! 336: Tcl_CmdProc *proc; /* Command procedure to associate with ! 337: * cmdName. */ ! 338: ClientData clientData; /* Arbitrary one-word value to pass to proc. */ ! 339: Tcl_CmdDeleteProc *deleteProc; ! 340: /* If not NULL, gives a procedure to call when ! 341: * this command is deleted. */ ! 342: { ! 343: Interp *iPtr = (Interp *) interp; ! 344: register Command *cmdPtr; ! 345: Tcl_HashEntry *hPtr; ! 346: int new; ! 347: ! 348: hPtr = Tcl_CreateHashEntry(&iPtr->commandTable, cmdName, &new); ! 349: if (!new) { ! 350: /* ! 351: * Command already exists: delete the old one. ! 352: */ ! 353: ! 354: cmdPtr = (Command *) Tcl_GetHashValue(hPtr); ! 355: if (cmdPtr->deleteProc != NULL) { ! 356: (*cmdPtr->deleteProc)(cmdPtr->clientData); ! 357: } ! 358: } else { ! 359: cmdPtr = (Command *) ckalloc(sizeof(Command)); ! 360: Tcl_SetHashValue(hPtr, cmdPtr); ! 361: } ! 362: cmdPtr->proc = proc; ! 363: cmdPtr->clientData = clientData; ! 364: cmdPtr->deleteProc = deleteProc; ! 365: } ! 366: ! 367: /* ! 368: *---------------------------------------------------------------------- ! 369: * ! 370: * Tcl_DeleteCommand -- ! 371: * ! 372: * Remove the given command from the given interpreter. ! 373: * ! 374: * Results: ! 375: * 0 is returned if the command was deleted successfully. ! 376: * -1 is returned if there didn't exist a command by that ! 377: * name. ! 378: * ! 379: * Side effects: ! 380: * CmdName will no longer be recognized as a valid command for ! 381: * interp. ! 382: * ! 383: *---------------------------------------------------------------------- ! 384: */ ! 385: ! 386: int ! 387: Tcl_DeleteCommand(interp, cmdName) ! 388: Tcl_Interp *interp; /* Token for command interpreter (returned ! 389: * by a previous call to Tcl_CreateInterp). */ ! 390: char *cmdName; /* Name of command to remove. */ ! 391: { ! 392: Interp *iPtr = (Interp *) interp; ! 393: Tcl_HashEntry *hPtr; ! 394: Command *cmdPtr; ! 395: ! 396: hPtr = Tcl_FindHashEntry(&iPtr->commandTable, cmdName); ! 397: if (hPtr == NULL) { ! 398: return -1; ! 399: } ! 400: cmdPtr = (Command *) Tcl_GetHashValue(hPtr); ! 401: if (cmdPtr->deleteProc != NULL) { ! 402: (*cmdPtr->deleteProc)(cmdPtr->clientData); ! 403: } ! 404: ckfree((char *) cmdPtr); ! 405: Tcl_DeleteHashEntry(hPtr); ! 406: return 0; ! 407: } ! 408: ! 409: /* ! 410: *----------------------------------------------------------------- ! 411: * ! 412: * Tcl_Eval -- ! 413: * ! 414: * Parse and execute a command in the Tcl language. ! 415: * ! 416: * Results: ! 417: * The return value is one of the return codes defined in tcl.hd ! 418: * (such as TCL_OK), and interp->result contains a string value ! 419: * to supplement the return code. The value of interp->result ! 420: * will persist only until the next call to Tcl_Eval: copy it or ! 421: * lose it! *TermPtr is filled in with the character just after ! 422: * the last one that was part of the command (usually a NULL ! 423: * character or a closing bracket). ! 424: * ! 425: * Side effects: ! 426: * Almost certainly; depends on the command. ! 427: * ! 428: *----------------------------------------------------------------- ! 429: */ ! 430: ! 431: int ! 432: Tcl_Eval(interp, cmd, flags, termPtr) ! 433: Tcl_Interp *interp; /* Token for command interpreter (returned ! 434: * by a previous call to Tcl_CreateInterp). */ ! 435: char *cmd; /* Pointer to TCL command to interpret. */ ! 436: int flags; /* OR-ed combination of flags like ! 437: * TCL_BRACKET_TERM and TCL_RECORD_BOUNDS. */ ! 438: char **termPtr; /* If non-NULL, fill in the address it points ! 439: * to with the address of the char. just after ! 440: * the last one that was part of cmd. See ! 441: * the man page for details on this. */ ! 442: { ! 443: /* ! 444: * The storage immediately below is used to generate a copy ! 445: * of the command, after all argument substitutions. Pv will ! 446: * contain the argv values passed to the command procedure. ! 447: */ ! 448: ! 449: # define NUM_CHARS 200 ! 450: char copyStorage[NUM_CHARS]; ! 451: ParseValue pv; ! 452: char *oldBuffer; ! 453: ! 454: /* ! 455: * This procedure generates an (argv, argc) array for the command, ! 456: * It starts out with stack-allocated space but uses dynamically- ! 457: * allocated storage to increase it if needed. ! 458: */ ! 459: ! 460: # define NUM_ARGS 10 ! 461: char *(argStorage[NUM_ARGS]); ! 462: char **argv = argStorage; ! 463: int argc; ! 464: int argSize = NUM_ARGS; ! 465: ! 466: register char *src; /* Points to current character ! 467: * in cmd. */ ! 468: char termChar; /* Return when this character is found ! 469: * (either ']' or '\0'). Zero means ! 470: * that newlines terminate commands. */ ! 471: int result; /* Return value. */ ! 472: register Interp *iPtr = (Interp *) interp; ! 473: Tcl_HashEntry *hPtr; ! 474: Command *cmdPtr; ! 475: char *dummy; /* Make termPtr point here if it was ! 476: * originally NULL. */ ! 477: char *cmdStart; /* Points to first non-blank char. in ! 478: * command (used in calling trace ! 479: * procedures). */ ! 480: char *ellipsis = ""; /* Used in setting errorInfo variable; ! 481: * set to "..." to indicate that not ! 482: * all of offending command is included ! 483: * in errorInfo. "" means that the ! 484: * command is all there. */ ! 485: register Trace *tracePtr; ! 486: ! 487: /* ! 488: * Initialize the result to an empty string and clear out any ! 489: * error information. This makes sure that we return an empty ! 490: * result if there are no commands in the command string. ! 491: */ ! 492: ! 493: Tcl_FreeResult((Tcl_Interp *) iPtr); ! 494: iPtr->result = iPtr->resultSpace; ! 495: iPtr->resultSpace[0] = 0; ! 496: result = TCL_OK; ! 497: ! 498: /* ! 499: * Check depth of nested calls to Tcl_Eval: if this gets too large, ! 500: * it's probably because of an infinite loop somewhere. ! 501: */ ! 502: ! 503: iPtr->numLevels++; ! 504: if (iPtr->numLevels > MAX_NESTING_DEPTH) { ! 505: iPtr->numLevels--; ! 506: iPtr->result = "too many nested calls to Tcl_Eval (infinite loop?)"; ! 507: return TCL_ERROR; ! 508: } ! 509: ! 510: /* ! 511: * Initialize the area in which command copies will be assembled. ! 512: */ ! 513: ! 514: pv.buffer = copyStorage; ! 515: pv.end = copyStorage + NUM_CHARS - 1; ! 516: pv.expandProc = TclExpandParseValue; ! 517: pv.clientData = (ClientData) NULL; ! 518: ! 519: src = cmd; ! 520: if (flags & TCL_BRACKET_TERM) { ! 521: termChar = ']'; ! 522: } else { ! 523: termChar = 0; ! 524: } ! 525: if (termPtr == NULL) { ! 526: termPtr = &dummy; ! 527: } ! 528: *termPtr = src; ! 529: cmdStart = src; ! 530: ! 531: /* ! 532: * There can be many sub-commands (separated by semi-colons or ! 533: * newlines) in one command string. This outer loop iterates over ! 534: * individual commands. ! 535: */ ! 536: ! 537: while (*src != termChar) { ! 538: iPtr->flags &= ~(ERR_IN_PROGRESS | ERROR_CODE_SET); ! 539: ! 540: /* ! 541: * Skim off leading white space and semi-colons, and skip ! 542: * comments. ! 543: */ ! 544: ! 545: while (1) { ! 546: register char c = *src; ! 547: ! 548: if ((CHAR_TYPE(c) != TCL_SPACE) && (c != ';') && (c != '\n')) { ! 549: break; ! 550: } ! 551: src += 1; ! 552: } ! 553: if (*src == '#') { ! 554: for (src++; *src != 0; src++) { ! 555: if (*src == '\n') { ! 556: src++; ! 557: break; ! 558: } ! 559: } ! 560: continue; ! 561: } ! 562: cmdStart = src; ! 563: ! 564: /* ! 565: * Parse the words of the command, generating the argc and ! 566: * argv for the command procedure. May have to call ! 567: * TclParseWords several times, expanding the argv array ! 568: * between calls. ! 569: */ ! 570: ! 571: pv.next = oldBuffer = pv.buffer; ! 572: argc = 0; ! 573: while (1) { ! 574: int newArgs, maxArgs; ! 575: char **newArgv; ! 576: int i; ! 577: ! 578: /* ! 579: * Note: the "- 2" below guarantees that we won't use the ! 580: * last two argv slots here. One is for a NULL pointer to ! 581: * mark the end of the list, and the other is to leave room ! 582: * for inserting the command name "unknown" as the first ! 583: * argument (see below). ! 584: */ ! 585: ! 586: maxArgs = argSize - argc - 2; ! 587: result = TclParseWords((Tcl_Interp *) iPtr, src, flags, ! 588: maxArgs, termPtr, &newArgs, &argv[argc], &pv); ! 589: src = *termPtr; ! 590: if (result != TCL_OK) { ! 591: ellipsis = "..."; ! 592: goto done; ! 593: } ! 594: ! 595: /* ! 596: * Careful! Buffer space may have gotten reallocated while ! 597: * parsing words. If this happened, be sure to update all ! 598: * of the older argv pointers to refer to the new space. ! 599: */ ! 600: ! 601: if (oldBuffer != pv.buffer) { ! 602: int i; ! 603: ! 604: for (i = 0; i < argc; i++) { ! 605: argv[i] = pv.buffer + (argv[i] - oldBuffer); ! 606: } ! 607: oldBuffer = pv.buffer; ! 608: } ! 609: argc += newArgs; ! 610: if (newArgs < maxArgs) { ! 611: argv[argc] = (char *) NULL; ! 612: break; ! 613: } ! 614: ! 615: /* ! 616: * Args didn't all fit in the current array. Make it bigger. ! 617: */ ! 618: ! 619: argSize *= 2; ! 620: newArgv = (char **) ! 621: ckalloc((unsigned) argSize * sizeof(char *)); ! 622: for (i = 0; i < argc; i++) { ! 623: newArgv[i] = argv[i]; ! 624: } ! 625: if (argv != argStorage) { ! 626: ckfree((char *) argv); ! 627: } ! 628: argv = newArgv; ! 629: } ! 630: ! 631: /* ! 632: * If this is an empty command (or if we're just parsing ! 633: * commands without evaluating them), then just skip to the ! 634: * next command. ! 635: */ ! 636: ! 637: if ((argc == 0) || iPtr->noEval) { ! 638: continue; ! 639: } ! 640: argv[argc] = NULL; ! 641: ! 642: /* ! 643: * Save information for the history module, if needed. ! 644: */ ! 645: ! 646: if (flags & TCL_RECORD_BOUNDS) { ! 647: iPtr->evalFirst = cmdStart; ! 648: iPtr->evalLast = src-1; ! 649: } ! 650: ! 651: /* ! 652: * Find the procedure to execute this command. If there isn't ! 653: * one, then see if there is a command "unknown". If so, ! 654: * invoke it instead, passing it the words of the original ! 655: * command as arguments. ! 656: */ ! 657: ! 658: hPtr = Tcl_FindHashEntry(&iPtr->commandTable, argv[0]); ! 659: if (hPtr == NULL) { ! 660: int i; ! 661: ! 662: hPtr = Tcl_FindHashEntry(&iPtr->commandTable, "unknown"); ! 663: if (hPtr == NULL) { ! 664: Tcl_ResetResult(interp); ! 665: Tcl_AppendResult(interp, "invalid command name: \"", ! 666: argv[0], "\"", (char *) NULL); ! 667: result = TCL_ERROR; ! 668: goto done; ! 669: } ! 670: for (i = argc; i >= 0; i--) { ! 671: argv[i+1] = argv[i]; ! 672: } ! 673: argv[0] = "unknown"; ! 674: argc++; ! 675: } ! 676: cmdPtr = (Command *) Tcl_GetHashValue(hPtr); ! 677: ! 678: /* ! 679: * Call trace procedures, if any. ! 680: */ ! 681: ! 682: for (tracePtr = iPtr->tracePtr; tracePtr != NULL; ! 683: tracePtr = tracePtr->nextPtr) { ! 684: char saved; ! 685: ! 686: if (tracePtr->level < iPtr->numLevels) { ! 687: continue; ! 688: } ! 689: saved = *src; ! 690: *src = 0; ! 691: (*tracePtr->proc)(tracePtr->clientData, interp, iPtr->numLevels, ! 692: cmdStart, cmdPtr->proc, cmdPtr->clientData, argc, argv); ! 693: *src = saved; ! 694: } ! 695: ! 696: /* ! 697: * At long last, invoke the command procedure. Reset the ! 698: * result to its default empty value first (it could have ! 699: * gotten changed by earlier commands in the same command ! 700: * string). ! 701: */ ! 702: ! 703: iPtr->cmdCount++; ! 704: Tcl_FreeResult(iPtr); ! 705: iPtr->result = iPtr->resultSpace; ! 706: iPtr->resultSpace[0] = 0; ! 707: result = (*cmdPtr->proc)(cmdPtr->clientData, interp, argc, argv); ! 708: if (result != TCL_OK) { ! 709: break; ! 710: } ! 711: } ! 712: ! 713: /* ! 714: * Free up any extra resources that were allocated. ! 715: */ ! 716: ! 717: done: ! 718: if (pv.buffer != copyStorage) { ! 719: ckfree((char *) pv.buffer); ! 720: } ! 721: if (argv != argStorage) { ! 722: ckfree((char *) argv); ! 723: } ! 724: iPtr->numLevels--; ! 725: if (iPtr->numLevels == 0) { ! 726: if (result == TCL_RETURN) { ! 727: result = TCL_OK; ! 728: } ! 729: if ((result != TCL_OK) && (result != TCL_ERROR)) { ! 730: Tcl_ResetResult(interp); ! 731: if (result == TCL_BREAK) { ! 732: iPtr->result = "invoked \"break\" outside of a loop"; ! 733: } else if (result == TCL_CONTINUE) { ! 734: iPtr->result = "invoked \"continue\" outside of a loop"; ! 735: } else { ! 736: iPtr->result = iPtr->resultSpace; ! 737: sprintf(iPtr->resultSpace, "command returned bad code: %d", ! 738: result); ! 739: } ! 740: result = TCL_ERROR; ! 741: } ! 742: if (iPtr->flags & DELETED) { ! 743: Tcl_DeleteInterp(interp); ! 744: } ! 745: } ! 746: ! 747: /* ! 748: * If an error occurred, record information about what was being ! 749: * executed when the error occurred. ! 750: */ ! 751: ! 752: if ((result == TCL_ERROR) && !(iPtr->flags & ERR_ALREADY_LOGGED)) { ! 753: int numChars; ! 754: register char *p; ! 755: ! 756: /* ! 757: * Compute the line number where the error occurred. ! 758: */ ! 759: ! 760: iPtr->errorLine = 1; ! 761: for (p = cmd; p != cmdStart; p++) { ! 762: if (*p == '\n') { ! 763: iPtr->errorLine++; ! 764: } ! 765: } ! 766: for ( ; isspace(*p) || (*p == ';'); p++) { ! 767: if (*p == '\n') { ! 768: iPtr->errorLine++; ! 769: } ! 770: } ! 771: ! 772: /* ! 773: * Figure out how much of the command to print in the error ! 774: * message (up to a certain number of characters, or up to ! 775: * the first new-line). ! 776: */ ! 777: ! 778: numChars = src - cmdStart; ! 779: if (numChars > (NUM_CHARS-50)) { ! 780: numChars = NUM_CHARS-50; ! 781: ellipsis = " ..."; ! 782: } ! 783: ! 784: if (!(iPtr->flags & ERR_IN_PROGRESS)) { ! 785: sprintf(copyStorage, "\n while executing\n\"%.*s%s\"", ! 786: numChars, cmdStart, ellipsis); ! 787: } else { ! 788: sprintf(copyStorage, "\n invoked from within\n\"%.*s%s\"", ! 789: numChars, cmdStart, ellipsis); ! 790: } ! 791: Tcl_AddErrorInfo(interp, copyStorage); ! 792: iPtr->flags &= ~ERR_ALREADY_LOGGED; ! 793: } else { ! 794: iPtr->flags &= ~ERR_ALREADY_LOGGED; ! 795: } ! 796: return result; ! 797: } ! 798: ! 799: /* ! 800: *---------------------------------------------------------------------- ! 801: * ! 802: * Tcl_CreateTrace -- ! 803: * ! 804: * Arrange for a procedure to be called to trace command execution. ! 805: * ! 806: * Results: ! 807: * The return value is a token for the trace, which may be passed ! 808: * to Tcl_DeleteTrace to eliminate the trace. ! 809: * ! 810: * Side effects: ! 811: * From now on, proc will be called just before a command procedure ! 812: * is called to execute a Tcl command. Calls to proc will have the ! 813: * following form: ! 814: * ! 815: * void ! 816: * proc(clientData, interp, level, command, cmdProc, cmdClientData, ! 817: * argc, argv) ! 818: * ClientData clientData; ! 819: * Tcl_Interp *interp; ! 820: * int level; ! 821: * char *command; ! 822: * int (*cmdProc)(); ! 823: * ClientData cmdClientData; ! 824: * int argc; ! 825: * char **argv; ! 826: * { ! 827: * } ! 828: * ! 829: * The clientData and interp arguments to proc will be the same ! 830: * as the corresponding arguments to this procedure. Level gives ! 831: * the nesting level of command interpretation for this interpreter ! 832: * (0 corresponds to top level). Command gives the ASCII text of ! 833: * the raw command, cmdProc and cmdClientData give the procedure that ! 834: * will be called to process the command and the ClientData value it ! 835: * will receive, and argc and argv give the arguments to the ! 836: * command, after any argument parsing and substitution. Proc ! 837: * does not return a value. ! 838: * ! 839: *---------------------------------------------------------------------- ! 840: */ ! 841: ! 842: Tcl_Trace ! 843: Tcl_CreateTrace(interp, level, proc, clientData) ! 844: Tcl_Interp *interp; /* Interpreter in which to create the trace. */ ! 845: int level; /* Only call proc for commands at nesting level ! 846: * <= level (1 => top level). */ ! 847: Tcl_CmdTraceProc *proc; /* Procedure to call before executing each ! 848: * command. */ ! 849: ClientData clientData; /* Arbitrary one-word value to pass to proc. */ ! 850: { ! 851: register Trace *tracePtr; ! 852: register Interp *iPtr = (Interp *) interp; ! 853: ! 854: tracePtr = (Trace *) ckalloc(sizeof(Trace)); ! 855: tracePtr->level = level; ! 856: tracePtr->proc = proc; ! 857: tracePtr->clientData = clientData; ! 858: tracePtr->nextPtr = iPtr->tracePtr; ! 859: iPtr->tracePtr = tracePtr; ! 860: ! 861: return (Tcl_Trace) tracePtr; ! 862: } ! 863: ! 864: /* ! 865: *---------------------------------------------------------------------- ! 866: * ! 867: * Tcl_DeleteTrace -- ! 868: * ! 869: * Remove a trace. ! 870: * ! 871: * Results: ! 872: * None. ! 873: * ! 874: * Side effects: ! 875: * From now on there will be no more calls to the procedure given ! 876: * in trace. ! 877: * ! 878: *---------------------------------------------------------------------- ! 879: */ ! 880: ! 881: void ! 882: Tcl_DeleteTrace(interp, trace) ! 883: Tcl_Interp *interp; /* Interpreter that contains trace. */ ! 884: Tcl_Trace trace; /* Token for trace (returned previously by ! 885: * Tcl_CreateTrace). */ ! 886: { ! 887: register Interp *iPtr = (Interp *) interp; ! 888: register Trace *tracePtr = (Trace *) trace; ! 889: register Trace *tracePtr2; ! 890: ! 891: if (iPtr->tracePtr == tracePtr) { ! 892: iPtr->tracePtr = tracePtr->nextPtr; ! 893: ckfree((char *) tracePtr); ! 894: } else { ! 895: for (tracePtr2 = iPtr->tracePtr; tracePtr2 != NULL; ! 896: tracePtr2 = tracePtr2->nextPtr) { ! 897: if (tracePtr2->nextPtr == tracePtr) { ! 898: tracePtr2->nextPtr = tracePtr->nextPtr; ! 899: ckfree((char *) tracePtr); ! 900: return; ! 901: } ! 902: } ! 903: } ! 904: } ! 905: ! 906: /* ! 907: *---------------------------------------------------------------------- ! 908: * ! 909: * Tcl_AddErrorInfo -- ! 910: * ! 911: * Add information to a message being accumulated that describes ! 912: * the current error. ! 913: * ! 914: * Results: ! 915: * None. ! 916: * ! 917: * Side effects: ! 918: * The contents of message are added to the "errorInfo" variable. ! 919: * If Tcl_Eval has been called since the current value of errorInfo ! 920: * was set, errorInfo is cleared before adding the new message. ! 921: * ! 922: *---------------------------------------------------------------------- ! 923: */ ! 924: ! 925: void ! 926: Tcl_AddErrorInfo(interp, message) ! 927: Tcl_Interp *interp; /* Interpreter to which error information ! 928: * pertains. */ ! 929: char *message; /* Message to record. */ ! 930: { ! 931: register Interp *iPtr = (Interp *) interp; ! 932: ! 933: /* ! 934: * If an error is already being logged, then the new errorInfo ! 935: * is the concatenation of the old info and the new message. ! 936: * If this is the first piece of info for the error, then the ! 937: * new errorInfo is the concatenation of the message in ! 938: * interp->result and the new message. ! 939: */ ! 940: ! 941: if (!(iPtr->flags & ERR_IN_PROGRESS)) { ! 942: Tcl_SetVar2(interp, "errorInfo", (char *) NULL, interp->result, ! 943: TCL_GLOBAL_ONLY); ! 944: iPtr->flags |= ERR_IN_PROGRESS; ! 945: ! 946: /* ! 947: * If the errorCode variable wasn't set by the code that generated ! 948: * the error, set it to "NONE". ! 949: */ ! 950: ! 951: if (!(iPtr->flags & ERROR_CODE_SET)) { ! 952: (void) Tcl_SetVar2(interp, "errorCode", (char *) NULL, "NONE", ! 953: TCL_GLOBAL_ONLY); ! 954: } ! 955: } ! 956: Tcl_SetVar2(interp, "errorInfo", (char *) NULL, message, ! 957: TCL_GLOBAL_ONLY|TCL_APPEND_VALUE); ! 958: } ! 959: ! 960: /* ! 961: *---------------------------------------------------------------------- ! 962: * ! 963: * Tcl_VarEval -- ! 964: * ! 965: * Given a variable number of string arguments, concatenate them ! 966: * all together and execute the result as a Tcl command. ! 967: * ! 968: * Results: ! 969: * A standard Tcl return result. An error message or other ! 970: * result may be left in interp->result. ! 971: * ! 972: * Side effects: ! 973: * Depends on what was done by the command. ! 974: * ! 975: *---------------------------------------------------------------------- ! 976: */ ! 977: int ! 978: Tcl_VarEval(Tcl_Interp *interp, ...) ! 979: { ! 980: va_list argList; ! 981: #define FIXED_SIZE 200 ! 982: char fixedSpace[FIXED_SIZE+1]; ! 983: int spaceAvl, spaceUsed, length; ! 984: char *string, *cmd; ! 985: int result; ! 986: ! 987: /* ! 988: * Copy the strings one after the other into a single larger ! 989: * string. Use stack-allocated space for small commands, but if ! 990: * the commands gets too large than call ckalloc to create the ! 991: * space. ! 992: */ ! 993: ! 994: va_start(argList, interp); ! 995: spaceAvl = FIXED_SIZE; ! 996: spaceUsed = 0; ! 997: cmd = fixedSpace; ! 998: while (1) { ! 999: string = va_arg(argList, char *); ! 1000: if (string == NULL) { ! 1001: break; ! 1002: } ! 1003: length = strlen(string); ! 1004: if ((spaceUsed + length) > spaceAvl) { ! 1005: char *new; ! 1006: ! 1007: spaceAvl = spaceUsed + length; ! 1008: spaceAvl += spaceAvl/2; ! 1009: new = ckalloc((unsigned) spaceAvl); ! 1010: memcpy((VOID *) new, (VOID *) cmd, spaceUsed); ! 1011: if (cmd != fixedSpace) { ! 1012: ckfree(cmd); ! 1013: } ! 1014: cmd = new; ! 1015: } ! 1016: strcpy(cmd + spaceUsed, string); ! 1017: spaceUsed += length; ! 1018: } ! 1019: va_end(argList); ! 1020: cmd[spaceUsed] = '\0'; ! 1021: ! 1022: result = Tcl_Eval(interp, cmd, 0, (char **) NULL); ! 1023: if (cmd != fixedSpace) { ! 1024: ckfree(cmd); ! 1025: } ! 1026: return result; ! 1027: } ! 1028: ! 1029: /* ! 1030: *---------------------------------------------------------------------- ! 1031: * ! 1032: * Tcl_GlobalEval -- ! 1033: * ! 1034: * Evaluate a command at global level in an interpreter. ! 1035: * ! 1036: * Results: ! 1037: * A standard Tcl result is returned, and interp->result is ! 1038: * modified accordingly. ! 1039: * ! 1040: * Side effects: ! 1041: * The command string is executed in interp, and the execution ! 1042: * is carried out in the variable context of global level (no ! 1043: * procedures active), just as if an "uplevel #0" command were ! 1044: * being executed. ! 1045: * ! 1046: *---------------------------------------------------------------------- ! 1047: */ ! 1048: ! 1049: int ! 1050: Tcl_GlobalEval(interp, command) ! 1051: Tcl_Interp *interp; /* Interpreter in which to evaluate command. */ ! 1052: char *command; /* Command to evaluate. */ ! 1053: { ! 1054: register Interp *iPtr = (Interp *) interp; ! 1055: int result; ! 1056: CallFrame *savedVarFramePtr; ! 1057: ! 1058: savedVarFramePtr = iPtr->varFramePtr; ! 1059: iPtr->varFramePtr = NULL; ! 1060: result = Tcl_Eval(interp, command, 0, (char **) NULL); ! 1061: iPtr->varFramePtr = savedVarFramePtr; ! 1062: return result; ! 1063: }
This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.