tclCmdMZ.c
上传用户:rrhhcc
上传日期:2015-12-11
资源大小:54129k
文件大小:139k
源码类别:

通讯编程

开发平台:

Visual C++

  1.     case SUBST_NOCOMMANDS: {
  2. flags &= ~TCL_SUBST_COMMANDS;
  3. break;
  4.     }
  5.     case SUBST_NOVARS: {
  6. flags &= ~TCL_SUBST_VARIABLES;
  7. break;
  8.     }
  9.     default: {
  10. panic("Tcl_SubstObjCmd: bad option index to SubstOptions");
  11.     }
  12. }
  13.     }
  14.     if (i != (objc-1)) {
  15. Tcl_WrongNumArgs(interp, 1, objv,
  16. "?-nobackslashes? ?-nocommands? ?-novariables? string");
  17. return TCL_ERROR;
  18.     }
  19.     /*
  20.      * Perform the substitution.
  21.      */
  22.     resultPtr = Tcl_SubstObj(interp, objv[i], flags);
  23.     if (resultPtr == NULL) {
  24. return TCL_ERROR;
  25.     }
  26.     Tcl_SetObjResult(interp, resultPtr);
  27.     return TCL_OK;
  28. }
  29. /*
  30.  *----------------------------------------------------------------------
  31.  *
  32.  * Tcl_SubstObj --
  33.  *
  34.  * This function performs the substitutions specified on the
  35.  * given string as described in the user documentation for the
  36.  * "subst" Tcl command.  This code is heavily based on an
  37.  * implementation by Andrew Payne.  Note that if a command
  38.  * substitution returns TCL_CONTINUE or TCL_RETURN from its
  39.  * evaluation and is not completely well-formed, the results are
  40.  * not defined (or at least hard to characterise.)  This fault
  41.  * will be fixed at some point, but the cost of the only sane
  42.  * fix (well-formedness check first) is such that you need to
  43.  * "precompile and cache" to stop everyone from being hit with
  44.  * the consequences every time through.  Note that the current
  45.  * behaviour is not a security hole; it just restarts parsing
  46.  * the string following the substitution in a mildly surprising
  47.  * place, and it is a very bad idea to count on this remaining
  48.  * the same in future...
  49.  *
  50.  * Results:
  51.  * A Tcl_Obj* containing the substituted string, or NULL to
  52.  * indicate that an error occurred.
  53.  *
  54.  * Side effects:
  55.  * See the user documentation.
  56.  *
  57.  *----------------------------------------------------------------------
  58.  */
  59. Tcl_Obj *
  60. Tcl_SubstObj(interp, objPtr, flags)
  61.     Tcl_Interp *interp;
  62.     Tcl_Obj *objPtr;
  63.     int flags;
  64. {
  65.     Tcl_Obj *resultObj;
  66.     char *p, *old;
  67.     int length;
  68.     old = p = Tcl_GetStringFromObj(objPtr, &length);
  69.     resultObj = Tcl_NewStringObj("", 0);
  70.     while (length) {
  71. switch (*p) {
  72. case '\':
  73.     if (flags & TCL_SUBST_BACKSLASHES) {
  74. char buf[TCL_UTF_MAX];
  75. int count;
  76. if (p != old) {
  77.     Tcl_AppendToObj(resultObj, old, p-old);
  78. }
  79. Tcl_AppendToObj(resultObj, buf,
  80. Tcl_UtfBackslash(p, &count, buf));
  81. p += count; length -= count;
  82. old = p;
  83.     } else {
  84. p++; length--;
  85.     }
  86.     break;
  87. case '$':
  88.     if (flags & TCL_SUBST_VARIABLES) {
  89. Tcl_Parse parse;
  90. int code;
  91. /*
  92.  * Code is simpler overall if we (effectively) inline
  93.  * Tcl_ParseVar, particularly as that allows us to use
  94.  * a non-string interface when we come to appending
  95.  * the variable contents to the result object.  There
  96.  * are a few other optimisations that doing this
  97.  * enables (like being able to continue the run of
  98.  * unsubstituted characters straight through if a '$'
  99.  * does not precede a variable name.)
  100.  */
  101. if (Tcl_ParseVarName(interp, p, -1, &parse, 0) != TCL_OK) {
  102.     goto errorResult;
  103. }
  104. if (parse.numTokens == 1) {
  105.     /*
  106.      * There isn't a variable name after all: the $ is
  107.      * just a $.
  108.      */
  109.     p++; length--;
  110.     break;
  111. }
  112. if (p != old) {
  113.     Tcl_AppendToObj(resultObj, old, p-old);
  114. }
  115. p += parse.tokenPtr->size;
  116. length -= parse.tokenPtr->size;
  117. code = Tcl_EvalTokensStandard(interp, parse.tokenPtr,
  118.         parse.numTokens);
  119. if (code == TCL_ERROR) {
  120.     goto errorResult;
  121. }
  122. if (code == TCL_BREAK) {
  123.     Tcl_ResetResult(interp);
  124.     return resultObj;
  125. }
  126. if (code != TCL_CONTINUE) {
  127.     Tcl_AppendObjToObj(resultObj, Tcl_GetObjResult(interp));
  128. }
  129. Tcl_ResetResult(interp);
  130. old = p;
  131.     } else {
  132. p++; length--;
  133.     }
  134.     break;
  135. case '[':
  136.     if (flags & TCL_SUBST_COMMANDS) {
  137. Interp *iPtr = (Interp *) interp;
  138. int code;
  139. if (p != old) {
  140.     Tcl_AppendToObj(resultObj, old, p-old);
  141. }
  142. iPtr->evalFlags = TCL_BRACKET_TERM;
  143. iPtr->numLevels++;
  144. code = TclInterpReady(interp);
  145. if (code == TCL_OK) {
  146.     code = Tcl_EvalEx(interp, p+1, -1, 0);
  147. }
  148. iPtr->numLevels--;
  149. switch (code) {
  150. case TCL_ERROR:
  151.     goto errorResult;
  152. case TCL_BREAK:
  153.     Tcl_ResetResult(interp);
  154.     return resultObj;
  155. default:
  156.     Tcl_AppendObjToObj(resultObj, Tcl_GetObjResult(interp));
  157. case TCL_CONTINUE:
  158.     Tcl_ResetResult(interp);
  159.     old = p = (p+1 + iPtr->termOffset + 1);
  160.     length -= (iPtr->termOffset + 2);
  161. }
  162.     } else {
  163. p++; length--;
  164.     }
  165.     break;
  166. default:
  167.     p++; length--;
  168.     break;
  169. }
  170.     }
  171.     if (p != old) {
  172. Tcl_AppendToObj(resultObj, old, p-old);
  173.     }
  174.     return resultObj;
  175.  errorResult:
  176.     Tcl_DecrRefCount(resultObj);
  177.     return NULL;
  178. }
  179. /*
  180.  *----------------------------------------------------------------------
  181.  *
  182.  * Tcl_SwitchObjCmd --
  183.  *
  184.  * This object-based procedure is invoked to process the "switch" Tcl
  185.  * command. See the user documentation for details on what it does.
  186.  *
  187.  * Results:
  188.  * A standard Tcl object result.
  189.  *
  190.  * Side effects:
  191.  * See the user documentation.
  192.  *
  193.  *----------------------------------------------------------------------
  194.  */
  195. /* ARGSUSED */
  196. int
  197. Tcl_SwitchObjCmd(dummy, interp, objc, objv)
  198.     ClientData dummy; /* Not used. */
  199.     Tcl_Interp *interp; /* Current interpreter. */
  200.     int objc; /* Number of arguments. */
  201.     Tcl_Obj *CONST objv[]; /* Argument objects. */
  202. {
  203.     int i, j, index, mode, matched, result, splitObjs;
  204.     char *string, *pattern;
  205.     Tcl_Obj *stringObj;
  206.     Tcl_Obj *CONST *savedObjv = objv;
  207. #ifdef TCL_TIP280
  208.     Interp*  iPtr  = (Interp*) interp;
  209.     int      pc    = 0;
  210.     int      bidx  = 0;    /* Index of body argument */
  211.     Tcl_Obj* blist = NULL; /* List obj which is the body */
  212.     CmdFrame ctx;          /* Copy of the topmost cmdframe,
  213.     * to allow us to mess with the
  214.     * line information */
  215. #endif
  216.     static CONST char *options[] = {
  217. "-exact", "-glob", "-regexp", "--", 
  218. NULL
  219.     };
  220.     enum options {
  221. OPT_EXACT, OPT_GLOB, OPT_REGEXP, OPT_LAST
  222.     };
  223.     mode = OPT_EXACT;
  224.     for (i = 1; i < objc; i++) {
  225. string = Tcl_GetString(objv[i]);
  226. if (string[0] != '-') {
  227.     break;
  228. }
  229. if (Tcl_GetIndexFromObj(interp, objv[i], options, "option", 0, 
  230. &index) != TCL_OK) {
  231.     return TCL_ERROR;
  232. }
  233. if (index == OPT_LAST) {
  234.     i++;
  235.     break;
  236. }
  237. mode = index;
  238.     }
  239.     if (objc - i < 2) {
  240. Tcl_WrongNumArgs(interp, 1, objv,
  241. "?switches? string pattern body ... ?default body?");
  242. return TCL_ERROR;
  243.     }
  244.     stringObj = objv[i];
  245.     objc -= i + 1;
  246.     objv += i + 1;
  247. #ifdef TCL_TIP280
  248.     bidx = i+1; /* First after the match string */
  249. #endif
  250.     /*
  251.      * If all of the pattern/command pairs are lumped into a single
  252.      * argument, split them out again.
  253.      *
  254.      * TIP #280: Determine the lines the words in the list start at, based on
  255.      * the same data for the list word itself. The cmdFramePtr line information
  256.      * is manipulated directly.
  257.      */
  258.     splitObjs = 0;
  259.     if (objc == 1) {
  260. Tcl_Obj **listv;
  261. #ifdef TCL_TIP280
  262. blist = objv[0];
  263. #endif
  264. if (Tcl_ListObjGetElements(interp, objv[0], &objc, &listv) != TCL_OK) {
  265.     return TCL_ERROR;
  266. }
  267. /*
  268.  * Ensure that the list is non-empty.
  269.  */
  270. if (objc < 1) {
  271.     Tcl_WrongNumArgs(interp, 1, savedObjv,
  272.     "?switches? string {pattern body ... ?default body?}");
  273.     return TCL_ERROR;
  274. }
  275. objv = listv;
  276. splitObjs = 1;
  277.     }
  278.     /*
  279.      * Complain if there is an odd number of words in the list of
  280.      * patterns and bodies.
  281.      */
  282.     if (objc % 2) {
  283. Tcl_ResetResult(interp);
  284. Tcl_AppendResult(interp, "extra switch pattern with no body", NULL);
  285. /*
  286.  * Check if this can be due to a badly placed comment
  287.  * in the switch block.
  288.  *
  289.  * The following is an heuristic to detect the infamous
  290.  * "comment in switch" error: just check if a pattern
  291.  * begins with '#'.
  292.  */
  293. if (splitObjs) {
  294.     for (i=0 ; i<objc ; i+=2) {
  295. if (Tcl_GetString(objv[i])[0] == '#') {
  296.     Tcl_AppendResult(interp, ", this may be due to a ",
  297.     "comment incorrectly placed outside of a ",
  298.     "switch body - see the "switch" ",
  299.     "documentation", NULL);
  300.     break;
  301. }
  302.     }
  303. }
  304. return TCL_ERROR;
  305.     }
  306.     /*
  307.      * Complain if the last body is a continuation.  Note that this
  308.      * check assumes that the list is non-empty!
  309.      */
  310.     if (strcmp(Tcl_GetString(objv[objc-1]), "-") == 0) {
  311. Tcl_ResetResult(interp);
  312. Tcl_AppendResult(interp, "no body specified for pattern "",
  313. Tcl_GetString(objv[objc-2]), """, NULL);
  314. return TCL_ERROR;
  315.     }
  316.     for (i = 0; i < objc; i += 2) {
  317. /*
  318.  * See if the pattern matches the string.
  319.  */
  320. pattern = Tcl_GetString(objv[i]);
  321. matched = 0;
  322. if ((i == objc - 2) 
  323. && (*pattern == 'd') 
  324. && (strcmp(pattern, "default") == 0)) {
  325.     matched = 1;
  326. } else {
  327.     switch (mode) {
  328. case OPT_EXACT:
  329.     matched = (strcmp(Tcl_GetString(stringObj), pattern) == 0);
  330.     break;
  331. case OPT_GLOB:
  332.     matched = Tcl_StringMatch(Tcl_GetString(stringObj),
  333.     pattern);
  334.     break;
  335. case OPT_REGEXP:
  336.     matched = Tcl_RegExpMatchObj(interp, stringObj, objv[i]);
  337.     if (matched < 0) {
  338. return TCL_ERROR;
  339.     }
  340.     break;
  341.     }
  342. }
  343. if (matched == 0) {
  344.     continue;
  345. }
  346. /*
  347.  * We've got a match. Find a body to execute, skipping bodies
  348.  * that are "-".
  349.  *
  350.  * TIP#280: Now is also the time to determine a line number for the
  351.  * single-word case.
  352.  */
  353. #ifdef TCL_TIP280
  354. ctx = *iPtr->cmdFramePtr;
  355. if (splitObjs) {
  356.     /* We have to perform the GetSrc and other type dependent handling
  357.      * of the frame here because we are munging with the line numbers,
  358.      * something the other commands like if, etc. are not doing. Them
  359.      * are fine with simply passing the CmdFrame through and having
  360.      * the special handling done in 'info frame', or the bc compiler
  361.      */
  362.     if (ctx.type == TCL_LOCATION_BC) {
  363. /* Note: Type BC => ctx.data.eval.path    is not used.
  364.  *                  ctx.data.tebc.codePtr is used instead.
  365.  */
  366. TclGetSrcInfoForPc (&ctx);
  367. pc = 1;
  368. /* The line information in the cmdFrame is now a copy we do
  369.  * not own */
  370.     }
  371.     if (ctx.type == TCL_LOCATION_SOURCE) {
  372. int bline = ctx.line [bidx];
  373. if (bline >= 0) {
  374.     ctx.line  = (int*) ckalloc (objc * sizeof(int));
  375.     ctx.nline = objc;
  376.     ListLines (Tcl_GetString (blist), bline, objc, ctx.line);
  377. } else {
  378.     int k;
  379.     /* Dynamic code word ... All elements are relative to themselves */
  380.     ctx.line  = (int*) ckalloc (objc * sizeof(int));
  381.     ctx.nline = objc;
  382.     for (k=0; k < objc; k++) {ctx.line[k] = -1;}
  383. }
  384.     } else {
  385. int k;
  386. /* Anything else ... No information, or dynamic ... */
  387. ctx.line  = (int*) ckalloc (objc * sizeof(int));
  388. ctx.nline = objc;
  389. for (k=0; k < objc; k++) {ctx.line[k] = -1;}
  390.     }
  391. }
  392. #endif
  393. for (j = i + 1; ; j += 2) {
  394.     if (j >= objc) {
  395. /*
  396.  * This shouldn't happen since we've checked that the
  397.  * last body is not a continuation...
  398.  */
  399. panic("fall-out when searching for body to match pattern");
  400.     }
  401.     if (strcmp(Tcl_GetString(objv[j]), "-") != 0) {
  402. break;
  403.     }
  404. }
  405. #ifndef TCL_TIP280
  406. result = Tcl_EvalObjEx(interp, objv[j], 0);
  407. #else
  408. /* TIP #280. Make invoking context available to switch branch */
  409. result = TclEvalObjEx(interp, objv[j], 0, &ctx, j);
  410. if (splitObjs) {
  411.     ckfree ((char*) ctx.line);
  412.     if (pc && (ctx.type == TCL_LOCATION_SOURCE)) {
  413. /* Death of SrcInfo reference */
  414. Tcl_DecrRefCount (ctx.data.eval.path);
  415.     }
  416. }
  417. #endif
  418. if (result == TCL_ERROR) {
  419.     char msg[100 + TCL_INTEGER_SPACE];
  420.     sprintf(msg, "n    ("%.50s" arm line %d)", pattern,
  421.     interp->errorLine);
  422.     Tcl_AddObjErrorInfo(interp, msg, -1);
  423. }
  424. return result;
  425.     }
  426.     return TCL_OK;
  427. }
  428. /*
  429.  *----------------------------------------------------------------------
  430.  *
  431.  * Tcl_TimeObjCmd --
  432.  *
  433.  * This object-based procedure is invoked to process the "time" Tcl
  434.  * command.  See the user documentation for details on what it does.
  435.  *
  436.  * Results:
  437.  * A standard Tcl object result.
  438.  *
  439.  * Side effects:
  440.  * See the user documentation.
  441.  *
  442.  *----------------------------------------------------------------------
  443.  */
  444. /* ARGSUSED */
  445. int
  446. Tcl_TimeObjCmd(dummy, interp, objc, objv)
  447.     ClientData dummy; /* Not used. */
  448.     Tcl_Interp *interp; /* Current interpreter. */
  449.     int objc; /* Number of arguments. */
  450.     Tcl_Obj *CONST objv[]; /* Argument objects. */
  451. {
  452.     register Tcl_Obj *objPtr;
  453.     Tcl_Obj *objs[4];
  454.     register int i, result;
  455.     int count;
  456.     double totalMicroSec;
  457.     Tcl_Time start, stop;
  458.     if (objc == 2) {
  459. count = 1;
  460.     } else if (objc == 3) {
  461. result = Tcl_GetIntFromObj(interp, objv[2], &count);
  462. if (result != TCL_OK) {
  463.     return result;
  464. }
  465.     } else {
  466. Tcl_WrongNumArgs(interp, 1, objv, "command ?count?");
  467. return TCL_ERROR;
  468.     }
  469.     
  470.     objPtr = objv[1];
  471.     i = count;
  472.     Tcl_GetTime(&start);
  473.     while (i-- > 0) {
  474. result = Tcl_EvalObjEx(interp, objPtr, 0);
  475. if (result != TCL_OK) {
  476.     return result;
  477. }
  478.     }
  479.     Tcl_GetTime(&stop);
  480.     
  481.     totalMicroSec = ( ( (double) ( stop.sec - start.sec ) ) * 1.0e6
  482.       + ( stop.usec - start.usec ) );
  483.     if (count <= 1) {
  484. /* Use int obj since we know time is not fractional [Bug 1202178] */
  485. objs[0] = Tcl_NewIntObj((count <= 0) ? 0 : (int) totalMicroSec);
  486.     } else {
  487. objs[0] = Tcl_NewDoubleObj(totalMicroSec/count);
  488.     }
  489.     objs[1] = Tcl_NewStringObj("microseconds", -1);
  490.     objs[2] = Tcl_NewStringObj("per", -1);
  491.     objs[3] = Tcl_NewStringObj("iteration", -1);
  492.     Tcl_SetObjResult(interp, Tcl_NewListObj(4, objs));
  493.     return TCL_OK;
  494. }
  495. /*
  496.  *----------------------------------------------------------------------
  497.  *
  498.  * Tcl_TraceObjCmd --
  499.  *
  500.  * This procedure is invoked to process the "trace" Tcl command.
  501.  * See the user documentation for details on what it does.
  502.  *
  503.  * Standard syntax as of Tcl 8.4 is
  504.  *
  505.  *  trace {add|info|remove} {command|variable} name ops cmd
  506.  *
  507.  *
  508.  * Results:
  509.  * A standard Tcl result.
  510.  *
  511.  * Side effects:
  512.  * See the user documentation.
  513.  *----------------------------------------------------------------------
  514.  */
  515. /* ARGSUSED */
  516. int
  517. Tcl_TraceObjCmd(dummy, interp, objc, objv)
  518.     ClientData dummy; /* Not used. */
  519.     Tcl_Interp *interp; /* Current interpreter. */
  520.     int objc; /* Number of arguments. */
  521.     Tcl_Obj *CONST objv[]; /* Argument objects. */
  522. {
  523.     int optionIndex;
  524.     char *name, *flagOps, *p;
  525.     /* Main sub commands to 'trace' */
  526.     static CONST char *traceOptions[] = {
  527. "add", "info", "remove", 
  528. #ifndef TCL_REMOVE_OBSOLETE_TRACES
  529. "variable", "vdelete", "vinfo", 
  530. #endif
  531. (char *) NULL
  532.     };
  533.     /* 'OLD' options are pre-Tcl-8.4 style */
  534.     enum traceOptions {
  535. TRACE_ADD, TRACE_INFO, TRACE_REMOVE, 
  536. #ifndef TCL_REMOVE_OBSOLETE_TRACES
  537. TRACE_OLD_VARIABLE, TRACE_OLD_VDELETE, TRACE_OLD_VINFO
  538. #endif
  539.     };
  540.     if (objc < 2) {
  541. Tcl_WrongNumArgs(interp, 1, objv, "option ?arg arg ...?");
  542. return TCL_ERROR;
  543.     }
  544.     if (Tcl_GetIndexFromObj(interp, objv[1], traceOptions,
  545. "option", 0, &optionIndex) != TCL_OK) {
  546. return TCL_ERROR;
  547.     }
  548.     switch ((enum traceOptions) optionIndex) {
  549. case TRACE_ADD: 
  550. case TRACE_REMOVE:
  551. case TRACE_INFO: {
  552.     /* 
  553.      * All sub commands of trace add/remove must take at least
  554.      * one more argument.  Beyond that we let the subcommand itself
  555.      * control the argument structure.
  556.      */
  557.     int typeIndex;
  558.     if (objc < 3) {
  559. Tcl_WrongNumArgs(interp, 2, objv, "type ?arg arg ...?");
  560. return TCL_ERROR;
  561.     }
  562.     if (Tcl_GetIndexFromObj(interp, objv[2], traceTypeOptions,
  563. "option", 0, &typeIndex) != TCL_OK) {
  564. return TCL_ERROR;
  565.     }
  566.     return (traceSubCmds[typeIndex])(interp, optionIndex, objc, objv);
  567. }
  568. #ifndef TCL_REMOVE_OBSOLETE_TRACES
  569.         case TRACE_OLD_VARIABLE:
  570. case TRACE_OLD_VDELETE: {
  571.     Tcl_Obj *copyObjv[6];
  572.     Tcl_Obj *opsList;
  573.     int code, numFlags;
  574.     if (objc != 5) {
  575. Tcl_WrongNumArgs(interp, 2, objv, "name ops command");
  576. return TCL_ERROR;
  577.     }
  578.     opsList = Tcl_NewObj();
  579.     Tcl_IncrRefCount(opsList);
  580.     flagOps = Tcl_GetStringFromObj(objv[3], &numFlags);
  581.     if (numFlags == 0) {
  582. Tcl_DecrRefCount(opsList);
  583. goto badVarOps;
  584.     }
  585.     for (p = flagOps; *p != 0; p++) {
  586. if (*p == 'r') {
  587.     Tcl_ListObjAppendElement(NULL, opsList,
  588.     Tcl_NewStringObj("read", -1));
  589. } else if (*p == 'w') {
  590.     Tcl_ListObjAppendElement(NULL, opsList,
  591.     Tcl_NewStringObj("write", -1));
  592. } else if (*p == 'u') {
  593.     Tcl_ListObjAppendElement(NULL, opsList,
  594.     Tcl_NewStringObj("unset", -1));
  595. } else if (*p == 'a') {
  596.     Tcl_ListObjAppendElement(NULL, opsList,
  597.     Tcl_NewStringObj("array", -1));
  598. } else {
  599.     Tcl_DecrRefCount(opsList);
  600.     goto badVarOps;
  601. }
  602.     }
  603.     copyObjv[0] = NULL;
  604.     memcpy(copyObjv+1, objv, objc*sizeof(Tcl_Obj *));
  605.     copyObjv[4] = opsList;
  606.     if  (optionIndex == TRACE_OLD_VARIABLE) {
  607. code = (traceSubCmds[2])(interp,TRACE_ADD,objc+1,copyObjv);
  608.     } else {
  609. code = (traceSubCmds[2])(interp,TRACE_REMOVE,objc+1,copyObjv);
  610.     }
  611.     Tcl_DecrRefCount(opsList);
  612.     return code;
  613. }
  614. case TRACE_OLD_VINFO: {
  615.     ClientData clientData;
  616.     char ops[5];
  617.     Tcl_Obj *resultListPtr, *pairObjPtr, *elemObjPtr;
  618.     if (objc != 3) {
  619. Tcl_WrongNumArgs(interp, 2, objv, "name");
  620. return TCL_ERROR;
  621.     }
  622.     resultListPtr = Tcl_GetObjResult(interp);
  623.     clientData = 0;
  624.     name = Tcl_GetString(objv[2]);
  625.     while ((clientData = Tcl_VarTraceInfo(interp, name, 0,
  626.     TraceVarProc, clientData)) != 0) {
  627. TraceVarInfo *tvarPtr = (TraceVarInfo *) clientData;
  628. pairObjPtr = Tcl_NewListObj(0, (Tcl_Obj **) NULL);
  629. p = ops;
  630. if (tvarPtr->flags & TCL_TRACE_READS) {
  631.     *p = 'r';
  632.     p++;
  633. }
  634. if (tvarPtr->flags & TCL_TRACE_WRITES) {
  635.     *p = 'w';
  636.     p++;
  637. }
  638. if (tvarPtr->flags & TCL_TRACE_UNSETS) {
  639.     *p = 'u';
  640.     p++;
  641. }
  642. if (tvarPtr->flags & TCL_TRACE_ARRAY) {
  643.     *p = 'a';
  644.     p++;
  645. }
  646. *p = '';
  647. /*
  648.  * Build a pair (2-item list) with the ops string as
  649.  * the first obj element and the tvarPtr->command string
  650.  * as the second obj element.  Append the pair (as an
  651.  * element) to the end of the result object list.
  652.  */
  653. elemObjPtr = Tcl_NewStringObj(ops, -1);
  654. Tcl_ListObjAppendElement(NULL, pairObjPtr, elemObjPtr);
  655. elemObjPtr = Tcl_NewStringObj(tvarPtr->command, -1);
  656. Tcl_ListObjAppendElement(NULL, pairObjPtr, elemObjPtr);
  657. Tcl_ListObjAppendElement(interp, resultListPtr, pairObjPtr);
  658.     }
  659.     Tcl_SetObjResult(interp, resultListPtr);
  660.     break;
  661. }
  662. #endif /* TCL_REMOVE_OBSOLETE_TRACES */
  663.     }
  664.     return TCL_OK;
  665.     badVarOps:
  666.     Tcl_AppendResult(interp, "bad operations "", flagOps,
  667.     "": should be one or more of rwua", (char *) NULL);
  668.     return TCL_ERROR;
  669. }
  670. /*
  671.  *----------------------------------------------------------------------
  672.  *
  673.  * TclTraceExecutionObjCmd --
  674.  *
  675.  * Helper function for Tcl_TraceObjCmd; implements the
  676.  * [trace {add|remove|info} execution ...] subcommands.
  677.  * See the user documentation for details on what these do.
  678.  *
  679.  * Results:
  680.  * Standard Tcl result.
  681.  *
  682.  * Side effects:
  683.  * Depends on the operation (add, remove, or info) being performed;
  684.  * may add or remove command traces on a command.
  685.  *
  686.  *----------------------------------------------------------------------
  687.  */
  688. int
  689. TclTraceExecutionObjCmd(interp, optionIndex, objc, objv)
  690.     Tcl_Interp *interp; /* Current interpreter. */
  691.     int optionIndex; /* Add, info or remove */
  692.     int objc; /* Number of arguments. */
  693.     Tcl_Obj *CONST objv[]; /* Argument objects. */
  694. {
  695.     int commandLength, index;
  696.     char *name, *command;
  697.     size_t length;
  698.     enum traceOptions { TRACE_ADD, TRACE_INFO, TRACE_REMOVE };
  699.     static CONST char *opStrings[] = { "enter", "leave", 
  700.                                  "enterstep", "leavestep", (char *) NULL };
  701.     enum operations { TRACE_EXEC_ENTER, TRACE_EXEC_LEAVE,
  702.                       TRACE_EXEC_ENTER_STEP, TRACE_EXEC_LEAVE_STEP };
  703.     
  704.     switch ((enum traceOptions) optionIndex) {
  705. case TRACE_ADD: 
  706. case TRACE_REMOVE: {
  707.     int flags = 0;
  708.     int i, listLen, result;
  709.     Tcl_Obj **elemPtrs;
  710.     if (objc != 6) {
  711. Tcl_WrongNumArgs(interp, 3, objv, "name opList command");
  712. return TCL_ERROR;
  713.     }
  714.     /*
  715.      * Make sure the ops argument is a list object; get its length and
  716.      * a pointer to its array of element pointers.
  717.      */
  718.     result = Tcl_ListObjGetElements(interp, objv[4], &listLen,
  719.     &elemPtrs);
  720.     if (result != TCL_OK) {
  721. return result;
  722.     }
  723.     if (listLen == 0) {
  724. Tcl_SetResult(interp, "bad operation list "": must be "
  725.           "one or more of enter, leave, enterstep, or leavestep", 
  726.   TCL_STATIC);
  727. return TCL_ERROR;
  728.     }
  729.     for (i = 0; i < listLen; i++) {
  730. if (Tcl_GetIndexFromObj(interp, elemPtrs[i], opStrings,
  731. "operation", TCL_EXACT, &index) != TCL_OK) {
  732.     return TCL_ERROR;
  733. }
  734. switch ((enum operations) index) {
  735.     case TRACE_EXEC_ENTER:
  736. flags |= TCL_TRACE_ENTER_EXEC;
  737. break;
  738.     case TRACE_EXEC_LEAVE:
  739. flags |= TCL_TRACE_LEAVE_EXEC;
  740. break;
  741.     case TRACE_EXEC_ENTER_STEP:
  742. flags |= TCL_TRACE_ENTER_DURING_EXEC;
  743. break;
  744.     case TRACE_EXEC_LEAVE_STEP:
  745. flags |= TCL_TRACE_LEAVE_DURING_EXEC;
  746. break;
  747. }
  748.     }
  749.     command = Tcl_GetStringFromObj(objv[5], &commandLength);
  750.     length = (size_t) commandLength;
  751.     if ((enum traceOptions) optionIndex == TRACE_ADD) {
  752. TraceCommandInfo *tcmdPtr;
  753. tcmdPtr = (TraceCommandInfo *) ckalloc((unsigned)
  754. (sizeof(TraceCommandInfo) - sizeof(tcmdPtr->command)
  755. + length + 1));
  756. tcmdPtr->flags = flags;
  757. tcmdPtr->stepTrace = NULL;
  758. tcmdPtr->startLevel = 0;
  759. tcmdPtr->startCmd = NULL;
  760. tcmdPtr->length = length;
  761. tcmdPtr->refCount = 1;
  762. flags |= TCL_TRACE_DELETE;
  763. if (flags & (TCL_TRACE_ENTER_DURING_EXEC |
  764.      TCL_TRACE_LEAVE_DURING_EXEC)) {
  765.     flags |= (TCL_TRACE_ENTER_EXEC | 
  766.       TCL_TRACE_LEAVE_EXEC);
  767. }
  768. strcpy(tcmdPtr->command, command);
  769. name = Tcl_GetString(objv[3]);
  770. if (Tcl_TraceCommand(interp, name, flags, TraceCommandProc,
  771. (ClientData) tcmdPtr) != TCL_OK) {
  772.     ckfree((char *) tcmdPtr);
  773.     return TCL_ERROR;
  774. }
  775.     } else {
  776. /*
  777.  * Search through all of our traces on this command to
  778.  * see if there's one with the given command.  If so, then
  779.  * delete the first one that matches.
  780.  */
  781. TraceCommandInfo *tcmdPtr;
  782. ClientData clientData = NULL;
  783. name = Tcl_GetString(objv[3]);
  784. /* First ensure the name given is valid */
  785. if (Tcl_FindCommand(interp, name, NULL, 
  786.     TCL_LEAVE_ERR_MSG) == NULL) {
  787.     return TCL_ERROR;
  788. }
  789.     
  790. while ((clientData = Tcl_CommandTraceInfo(interp, name, 0,
  791. TraceCommandProc, clientData)) != NULL) {
  792.     tcmdPtr = (TraceCommandInfo *) clientData;
  793.     /* 
  794.      * In checking the 'flags' field we must remove any
  795.      * extraneous flags which may have been temporarily
  796.      * added by various pieces of the trace mechanism.
  797.      */
  798.     if ((tcmdPtr->length == length)
  799.     && ((tcmdPtr->flags & (TCL_TRACE_ANY_EXEC | 
  800.    TCL_TRACE_RENAME | 
  801.    TCL_TRACE_DELETE)) == flags)
  802.     && (strncmp(command, tcmdPtr->command,
  803.     (size_t) length) == 0)) {
  804. flags |= TCL_TRACE_DELETE;
  805. if (flags & (TCL_TRACE_ENTER_DURING_EXEC |
  806.      TCL_TRACE_LEAVE_DURING_EXEC)) {
  807.     flags |= (TCL_TRACE_ENTER_EXEC | 
  808.       TCL_TRACE_LEAVE_EXEC);
  809. }
  810. Tcl_UntraceCommand(interp, name,
  811. flags, TraceCommandProc, clientData);
  812. if (tcmdPtr->stepTrace != NULL) {
  813.     /* 
  814.      * We need to remove the interpreter-wide trace 
  815.      * which we created to allow 'step' traces.
  816.      */
  817.     Tcl_DeleteTrace(interp, tcmdPtr->stepTrace);
  818.     tcmdPtr->stepTrace = NULL;
  819.                             if (tcmdPtr->startCmd != NULL) {
  820.         ckfree((char *)tcmdPtr->startCmd);
  821.     }
  822. }
  823. if (tcmdPtr->flags & TCL_TRACE_EXEC_IN_PROGRESS) {
  824.     /* Postpone deletion */
  825.     tcmdPtr->flags = 0;
  826. }
  827. tcmdPtr->refCount--;
  828. if (tcmdPtr->refCount < 0) {
  829.     Tcl_Panic("TclTraceExecutionObjCmd: negative TraceCommandInfo refCount");
  830. }
  831. if (tcmdPtr->refCount == 0) {
  832.     ckfree((char*)tcmdPtr);
  833. }
  834. break;
  835.     }
  836. }
  837.     }
  838.     break;
  839. }
  840. case TRACE_INFO: {
  841.     ClientData clientData;
  842.     Tcl_Obj *resultListPtr, *eachTraceObjPtr, *elemObjPtr;
  843.     if (objc != 4) {
  844. Tcl_WrongNumArgs(interp, 3, objv, "name");
  845. return TCL_ERROR;
  846.     }
  847.     clientData = NULL;
  848.     name = Tcl_GetString(objv[3]);
  849.     
  850.     /* First ensure the name given is valid */
  851.     if (Tcl_FindCommand(interp, name, NULL, 
  852. TCL_LEAVE_ERR_MSG) == NULL) {
  853. return TCL_ERROR;
  854.     }
  855.     resultListPtr = Tcl_NewListObj(0, (Tcl_Obj **) NULL);
  856.     while ((clientData = Tcl_CommandTraceInfo(interp, name, 0,
  857.     TraceCommandProc, clientData)) != NULL) {
  858. int numOps = 0;
  859. TraceCommandInfo *tcmdPtr = (TraceCommandInfo *) clientData;
  860. /*
  861.  * Build a list with the ops list as the first obj
  862.  * element and the tcmdPtr->command string as the
  863.  * second obj element.  Append this list (as an
  864.  * element) to the end of the result object list.
  865.  */
  866. elemObjPtr = Tcl_NewListObj(0, (Tcl_Obj **) NULL);
  867. Tcl_IncrRefCount(elemObjPtr);
  868. if (tcmdPtr->flags & TCL_TRACE_ENTER_EXEC) {
  869.     Tcl_ListObjAppendElement(NULL, elemObjPtr,
  870.     Tcl_NewStringObj("enter",5));
  871. }
  872. if (tcmdPtr->flags & TCL_TRACE_LEAVE_EXEC) {
  873.     Tcl_ListObjAppendElement(NULL, elemObjPtr,
  874.     Tcl_NewStringObj("leave",5));
  875. }
  876. if (tcmdPtr->flags & TCL_TRACE_ENTER_DURING_EXEC) {
  877.     Tcl_ListObjAppendElement(NULL, elemObjPtr,
  878.     Tcl_NewStringObj("enterstep",9));
  879. }
  880. if (tcmdPtr->flags & TCL_TRACE_LEAVE_DURING_EXEC) {
  881.     Tcl_ListObjAppendElement(NULL, elemObjPtr,
  882.     Tcl_NewStringObj("leavestep",9));
  883. }
  884. Tcl_ListObjLength(NULL, elemObjPtr, &numOps);
  885. if (0 == numOps) {
  886.     Tcl_DecrRefCount(elemObjPtr);
  887.                     continue;
  888.                 }
  889. eachTraceObjPtr = Tcl_NewListObj(0, (Tcl_Obj **) NULL);
  890. Tcl_ListObjAppendElement(NULL, eachTraceObjPtr, elemObjPtr);
  891. Tcl_DecrRefCount(elemObjPtr);
  892. elemObjPtr = NULL;
  893. Tcl_ListObjAppendElement(NULL, eachTraceObjPtr, 
  894. Tcl_NewStringObj(tcmdPtr->command, -1));
  895. Tcl_ListObjAppendElement(interp, resultListPtr,
  896. eachTraceObjPtr);
  897.     }
  898.     Tcl_SetObjResult(interp, resultListPtr);
  899.     break;
  900. }
  901.     }
  902.     return TCL_OK;
  903. }
  904. /*
  905.  *----------------------------------------------------------------------
  906.  *
  907.  * TclTraceCommandObjCmd --
  908.  *
  909.  * Helper function for Tcl_TraceObjCmd; implements the
  910.  * [trace {add|info|remove} command ...] subcommands.
  911.  * See the user documentation for details on what these do.
  912.  *
  913.  * Results:
  914.  * Standard Tcl result.
  915.  *
  916.  * Side effects:
  917.  * Depends on the operation (add, remove, or info) being performed;
  918.  * may add or remove command traces on a command.
  919.  *
  920.  *----------------------------------------------------------------------
  921.  */
  922. int
  923. TclTraceCommandObjCmd(interp, optionIndex, objc, objv)
  924.     Tcl_Interp *interp; /* Current interpreter. */
  925.     int optionIndex; /* Add, info or remove */
  926.     int objc; /* Number of arguments. */
  927.     Tcl_Obj *CONST objv[]; /* Argument objects. */
  928. {
  929.     int commandLength, index;
  930.     char *name, *command;
  931.     size_t length;
  932.     enum traceOptions { TRACE_ADD, TRACE_INFO, TRACE_REMOVE };
  933.     static CONST char *opStrings[] = { "delete", "rename", (char *) NULL };
  934.     enum operations { TRACE_CMD_DELETE, TRACE_CMD_RENAME };
  935.     
  936.     switch ((enum traceOptions) optionIndex) {
  937. case TRACE_ADD: 
  938. case TRACE_REMOVE: {
  939.     int flags = 0;
  940.     int i, listLen, result;
  941.     Tcl_Obj **elemPtrs;
  942.     if (objc != 6) {
  943. Tcl_WrongNumArgs(interp, 3, objv, "name opList command");
  944. return TCL_ERROR;
  945.     }
  946.     /*
  947.      * Make sure the ops argument is a list object; get its length and
  948.      * a pointer to its array of element pointers.
  949.      */
  950.     result = Tcl_ListObjGetElements(interp, objv[4], &listLen,
  951.     &elemPtrs);
  952.     if (result != TCL_OK) {
  953. return result;
  954.     }
  955.     if (listLen == 0) {
  956. Tcl_SetResult(interp, "bad operation list "": must be "
  957. "one or more of delete or rename", TCL_STATIC);
  958. return TCL_ERROR;
  959.     }
  960.     for (i = 0; i < listLen; i++) {
  961. if (Tcl_GetIndexFromObj(interp, elemPtrs[i], opStrings,
  962. "operation", TCL_EXACT, &index) != TCL_OK) {
  963.     return TCL_ERROR;
  964. }
  965. switch ((enum operations) index) {
  966.     case TRACE_CMD_RENAME:
  967. flags |= TCL_TRACE_RENAME;
  968. break;
  969.     case TRACE_CMD_DELETE:
  970. flags |= TCL_TRACE_DELETE;
  971. break;
  972. }
  973.     }
  974.     command = Tcl_GetStringFromObj(objv[5], &commandLength);
  975.     length = (size_t) commandLength;
  976.     if ((enum traceOptions) optionIndex == TRACE_ADD) {
  977. TraceCommandInfo *tcmdPtr;
  978. tcmdPtr = (TraceCommandInfo *) ckalloc((unsigned)
  979. (sizeof(TraceCommandInfo) - sizeof(tcmdPtr->command)
  980. + length + 1));
  981. tcmdPtr->flags = flags;
  982. tcmdPtr->stepTrace = NULL;
  983. tcmdPtr->startLevel = 0;
  984. tcmdPtr->startCmd = NULL;
  985. tcmdPtr->length = length;
  986. tcmdPtr->refCount = 1;
  987. flags |= TCL_TRACE_DELETE;
  988. strcpy(tcmdPtr->command, command);
  989. name = Tcl_GetString(objv[3]);
  990. if (Tcl_TraceCommand(interp, name, flags, TraceCommandProc,
  991. (ClientData) tcmdPtr) != TCL_OK) {
  992.     ckfree((char *) tcmdPtr);
  993.     return TCL_ERROR;
  994. }
  995.     } else {
  996. /*
  997.  * Search through all of our traces on this command to
  998.  * see if there's one with the given command.  If so, then
  999.  * delete the first one that matches.
  1000.  */
  1001. TraceCommandInfo *tcmdPtr;
  1002. ClientData clientData = NULL;
  1003. name = Tcl_GetString(objv[3]);
  1004. /* First ensure the name given is valid */
  1005. if (Tcl_FindCommand(interp, name, NULL, 
  1006.     TCL_LEAVE_ERR_MSG) == NULL) {
  1007.     return TCL_ERROR;
  1008. }
  1009.     
  1010. while ((clientData = Tcl_CommandTraceInfo(interp, name, 0,
  1011. TraceCommandProc, clientData)) != NULL) {
  1012.     tcmdPtr = (TraceCommandInfo *) clientData;
  1013.     if ((tcmdPtr->length == length)
  1014.     && (tcmdPtr->flags == flags)
  1015.     && (strncmp(command, tcmdPtr->command,
  1016.     (size_t) length) == 0)) {
  1017. Tcl_UntraceCommand(interp, name,
  1018. flags | TCL_TRACE_DELETE,
  1019. TraceCommandProc, clientData);
  1020. tcmdPtr->flags |= TCL_TRACE_DESTROYED;
  1021. tcmdPtr->refCount--;
  1022. if (tcmdPtr->refCount < 0) {
  1023.     Tcl_Panic("TclTraceCommandObjCmd: negative TraceCommandInfo refCount");
  1024. }
  1025. if (tcmdPtr->refCount == 0) {
  1026.     ckfree((char *) tcmdPtr);
  1027. }
  1028. break;
  1029.     }
  1030. }
  1031.     }
  1032.     break;
  1033. }
  1034. case TRACE_INFO: {
  1035.     ClientData clientData;
  1036.     Tcl_Obj *resultListPtr, *eachTraceObjPtr, *elemObjPtr;
  1037.     if (objc != 4) {
  1038. Tcl_WrongNumArgs(interp, 3, objv, "name");
  1039. return TCL_ERROR;
  1040.     }
  1041.     clientData = NULL;
  1042.     name = Tcl_GetString(objv[3]);
  1043.     
  1044.     /* First ensure the name given is valid */
  1045.     if (Tcl_FindCommand(interp, name, NULL, 
  1046. TCL_LEAVE_ERR_MSG) == NULL) {
  1047. return TCL_ERROR;
  1048.     }
  1049.     resultListPtr = Tcl_NewListObj(0, (Tcl_Obj **) NULL);
  1050.     while ((clientData = Tcl_CommandTraceInfo(interp, name, 0,
  1051.     TraceCommandProc, clientData)) != NULL) {
  1052. int numOps = 0;
  1053. TraceCommandInfo *tcmdPtr = (TraceCommandInfo *) clientData;
  1054. /*
  1055.  * Build a list with the ops list as
  1056.  * the first obj element and the tcmdPtr->command string
  1057.  * as the second obj element.  Append this list (as an
  1058.  * element) to the end of the result object list.
  1059.  */
  1060. elemObjPtr = Tcl_NewListObj(0, (Tcl_Obj **) NULL);
  1061. Tcl_IncrRefCount(elemObjPtr);
  1062. if (tcmdPtr->flags & TCL_TRACE_RENAME) {
  1063.     Tcl_ListObjAppendElement(NULL, elemObjPtr,
  1064.     Tcl_NewStringObj("rename",6));
  1065. }
  1066. if (tcmdPtr->flags & TCL_TRACE_DELETE) {
  1067.     Tcl_ListObjAppendElement(NULL, elemObjPtr,
  1068.     Tcl_NewStringObj("delete",6));
  1069. }
  1070. Tcl_ListObjLength(NULL, elemObjPtr, &numOps);
  1071. if (0 == numOps) {
  1072.     Tcl_DecrRefCount(elemObjPtr);
  1073.                     continue;
  1074.                 }
  1075. eachTraceObjPtr = Tcl_NewListObj(0, (Tcl_Obj **) NULL);
  1076. Tcl_ListObjAppendElement(NULL, eachTraceObjPtr, elemObjPtr);
  1077. Tcl_DecrRefCount(elemObjPtr);
  1078. elemObjPtr = Tcl_NewStringObj(tcmdPtr->command, -1);
  1079. Tcl_ListObjAppendElement(NULL, eachTraceObjPtr, elemObjPtr);
  1080. Tcl_ListObjAppendElement(interp, resultListPtr,
  1081. eachTraceObjPtr);
  1082.     }
  1083.     Tcl_SetObjResult(interp, resultListPtr);
  1084.     break;
  1085. }
  1086.     }
  1087.     return TCL_OK;
  1088. }
  1089. /*
  1090.  *----------------------------------------------------------------------
  1091.  *
  1092.  * TclTraceVariableObjCmd --
  1093.  *
  1094.  * Helper function for Tcl_TraceObjCmd; implements the
  1095.  * [trace {add|info|remove} variable ...] subcommands.
  1096.  * See the user documentation for details on what these do.
  1097.  *
  1098.  * Results:
  1099.  * Standard Tcl result.
  1100.  *
  1101.  * Side effects:
  1102.  * Depends on the operation (add, remove, or info) being performed;
  1103.  * may add or remove variable traces on a variable.
  1104.  *
  1105.  *----------------------------------------------------------------------
  1106.  */
  1107. int
  1108. TclTraceVariableObjCmd(interp, optionIndex, objc, objv)
  1109.     Tcl_Interp *interp; /* Current interpreter. */
  1110.     int optionIndex; /* Add, info or remove */
  1111.     int objc; /* Number of arguments. */
  1112.     Tcl_Obj *CONST objv[]; /* Argument objects. */
  1113. {
  1114.     int commandLength, index;
  1115.     char *name, *command;
  1116.     size_t length;
  1117.     enum traceOptions { TRACE_ADD, TRACE_INFO, TRACE_REMOVE };
  1118.     static CONST char *opStrings[] = { "array", "read", "unset", "write",
  1119.      (char *) NULL };
  1120.     enum operations { TRACE_VAR_ARRAY, TRACE_VAR_READ, TRACE_VAR_UNSET,
  1121.   TRACE_VAR_WRITE };
  1122.         
  1123.     switch ((enum traceOptions) optionIndex) {
  1124. case TRACE_ADD: 
  1125. case TRACE_REMOVE: {
  1126.     int flags = 0;
  1127.     int i, listLen, result;
  1128.     Tcl_Obj **elemPtrs;
  1129.     if (objc != 6) {
  1130. Tcl_WrongNumArgs(interp, 3, objv, "name opList command");
  1131. return TCL_ERROR;
  1132.     }
  1133.     /*
  1134.      * Make sure the ops argument is a list object; get its length and
  1135.      * a pointer to its array of element pointers.
  1136.      */
  1137.     result = Tcl_ListObjGetElements(interp, objv[4], &listLen,
  1138.     &elemPtrs);
  1139.     if (result != TCL_OK) {
  1140. return result;
  1141.     }
  1142.     if (listLen == 0) {
  1143. Tcl_SetResult(interp, "bad operation list "": must be "
  1144. "one or more of array, read, unset, or write",
  1145. TCL_STATIC);
  1146. return TCL_ERROR;
  1147.     }
  1148.     for (i = 0; i < listLen ; i++) {
  1149. if (Tcl_GetIndexFromObj(interp, elemPtrs[i], opStrings,
  1150. "operation", TCL_EXACT, &index) != TCL_OK) {
  1151.     return TCL_ERROR;
  1152. }
  1153. switch ((enum operations) index) {
  1154.     case TRACE_VAR_ARRAY:
  1155. flags |= TCL_TRACE_ARRAY;
  1156. break;
  1157.     case TRACE_VAR_READ:
  1158. flags |= TCL_TRACE_READS;
  1159. break;
  1160.     case TRACE_VAR_UNSET:
  1161. flags |= TCL_TRACE_UNSETS;
  1162. break;
  1163.     case TRACE_VAR_WRITE:
  1164. flags |= TCL_TRACE_WRITES;
  1165. break;
  1166. }
  1167.     }
  1168.     command = Tcl_GetStringFromObj(objv[5], &commandLength);
  1169.     length = (size_t) commandLength;
  1170.     if ((enum traceOptions) optionIndex == TRACE_ADD) {
  1171. /*
  1172.  * This code essentially mallocs together the VarTrace and the
  1173.  * TraceVarInfo, then inlines the Tcl_TraceVar(). This is
  1174.  * necessary in order to have the TraceVarInfo to be freed 
  1175.  * automatically when the VarTrace is freed [Bug 1348775]
  1176.  */
  1177. CompoundVarTrace *compTracePtr;
  1178. TraceVarInfo *tvarPtr;
  1179. Var *varPtr, *arrayPtr;
  1180. VarTrace *tracePtr;
  1181. int flagMask;
  1182. compTracePtr = (CompoundVarTrace *) ckalloc((unsigned)
  1183. (sizeof(CompoundVarTrace) - sizeof(tvarPtr->command)
  1184. + length + 1));
  1185. tracePtr = &(compTracePtr->trace);
  1186. tvarPtr = &(compTracePtr->tvar);
  1187. tvarPtr->flags = flags;
  1188. if (objv[0] == NULL) {
  1189.     tvarPtr->flags |= TCL_TRACE_OLD_STYLE;
  1190. }
  1191. tvarPtr->length = length;
  1192. flags |= TCL_TRACE_UNSETS | TCL_TRACE_RESULT_OBJECT;
  1193. strcpy(tvarPtr->command, command);
  1194. name = Tcl_GetString(objv[3]);
  1195. flagMask = TCL_GLOBAL_ONLY | TCL_NAMESPACE_ONLY;
  1196. varPtr = TclLookupVar(interp, name, NULL,
  1197. (flags & flagMask) | TCL_LEAVE_ERR_MSG, "trace",
  1198. /*createPart1*/ 1, /*createPart2*/ 1, &arrayPtr);
  1199. if (varPtr == NULL) {
  1200.     ckfree((char *) tracePtr);
  1201.     return TCL_ERROR;
  1202. }
  1203. flagMask = TCL_TRACE_READS | TCL_TRACE_WRITES
  1204. | TCL_TRACE_UNSETS | TCL_TRACE_ARRAY
  1205. | TCL_TRACE_RESULT_DYNAMIC | TCL_TRACE_RESULT_OBJECT;
  1206. #ifndef TCL_REMOVE_OBSOLETE_TRACES
  1207. flagMask |= TCL_TRACE_OLD_STYLE;
  1208. #endif
  1209. tracePtr->traceProc = TraceVarProc;
  1210. tracePtr->clientData = (ClientData) tvarPtr;
  1211. tracePtr->flags = flags & flagMask;
  1212. tracePtr->nextPtr = varPtr->tracePtr;
  1213. varPtr->tracePtr = tracePtr;
  1214.     } else {
  1215. /*
  1216.  * Search through all of our traces on this variable to
  1217.  * see if there's one with the given command.  If so, then
  1218.  * delete the first one that matches.
  1219.  */
  1220. TraceVarInfo *tvarPtr;
  1221. ClientData clientData = 0;
  1222. name = Tcl_GetString(objv[3]);
  1223. while ((clientData = Tcl_VarTraceInfo(interp, name, 0,
  1224. TraceVarProc, clientData)) != 0) {
  1225.     tvarPtr = (TraceVarInfo *) clientData;
  1226.     if ((tvarPtr->length == length)
  1227.     && ((tvarPtr->flags & ~TCL_TRACE_OLD_STYLE)==flags)
  1228.     && (strncmp(command, tvarPtr->command,
  1229.     (size_t) length) == 0)) {
  1230. Tcl_UntraceVar2(interp, name, NULL, 
  1231.   flags | TCL_TRACE_UNSETS | TCL_TRACE_RESULT_OBJECT,
  1232. TraceVarProc, clientData);
  1233. break;
  1234.     }
  1235. }
  1236.     }
  1237.     break;
  1238. }
  1239. case TRACE_INFO: {
  1240.     ClientData clientData;
  1241.     Tcl_Obj *resultListPtr, *eachTraceObjPtr, *elemObjPtr;
  1242.     if (objc != 4) {
  1243. Tcl_WrongNumArgs(interp, 3, objv, "name");
  1244. return TCL_ERROR;
  1245.     }
  1246.     resultListPtr = Tcl_GetObjResult(interp);
  1247.     clientData = 0;
  1248.     name = Tcl_GetString(objv[3]);
  1249.     while ((clientData = Tcl_VarTraceInfo(interp, name, 0,
  1250.     TraceVarProc, clientData)) != 0) {
  1251. TraceVarInfo *tvarPtr = (TraceVarInfo *) clientData;
  1252. /*
  1253.  * Build a list with the ops list as
  1254.  * the first obj element and the tcmdPtr->command string
  1255.  * as the second obj element.  Append this list (as an
  1256.  * element) to the end of the result object list.
  1257.  */
  1258. elemObjPtr = Tcl_NewListObj(0, (Tcl_Obj **) NULL);
  1259. if (tvarPtr->flags & TCL_TRACE_ARRAY) {
  1260.     Tcl_ListObjAppendElement(NULL, elemObjPtr,
  1261.     Tcl_NewStringObj("array", 5));
  1262. }
  1263. if (tvarPtr->flags & TCL_TRACE_READS) {
  1264.     Tcl_ListObjAppendElement(NULL, elemObjPtr,
  1265.     Tcl_NewStringObj("read", 4));
  1266. }
  1267. if (tvarPtr->flags & TCL_TRACE_WRITES) {
  1268.     Tcl_ListObjAppendElement(NULL, elemObjPtr,
  1269.     Tcl_NewStringObj("write", 5));
  1270. }
  1271. if (tvarPtr->flags & TCL_TRACE_UNSETS) {
  1272.     Tcl_ListObjAppendElement(NULL, elemObjPtr,
  1273.     Tcl_NewStringObj("unset", 5));
  1274. }
  1275. eachTraceObjPtr = Tcl_NewListObj(0, (Tcl_Obj **) NULL);
  1276. Tcl_ListObjAppendElement(NULL, eachTraceObjPtr, elemObjPtr);
  1277. elemObjPtr = Tcl_NewStringObj(tvarPtr->command, -1);
  1278. Tcl_ListObjAppendElement(NULL, eachTraceObjPtr, elemObjPtr);
  1279. Tcl_ListObjAppendElement(interp, resultListPtr,
  1280. eachTraceObjPtr);
  1281.     }
  1282.     Tcl_SetObjResult(interp, resultListPtr);
  1283.     break;
  1284. }
  1285.     }
  1286.     return TCL_OK;
  1287. }
  1288. /*
  1289.  *----------------------------------------------------------------------
  1290.  *
  1291.  * Tcl_CommandTraceInfo --
  1292.  *
  1293.  * Return the clientData value associated with a trace on a
  1294.  * command.  This procedure can also be used to step through
  1295.  * all of the traces on a particular command that have the
  1296.  * same trace procedure.
  1297.  *
  1298.  * Results:
  1299.  * The return value is the clientData value associated with
  1300.  * a trace on the given command.  Information will only be
  1301.  * returned for a trace with proc as trace procedure.  If
  1302.  * the clientData argument is NULL then the first such trace is
  1303.  * returned;  otherwise, the next relevant one after the one
  1304.  * given by clientData will be returned.  If the command
  1305.  * doesn't exist then an error message is left in the interpreter
  1306.  * and NULL is returned.  Also, if there are no (more) traces for 
  1307.  * the given command, NULL is returned.
  1308.  *
  1309.  * Side effects:
  1310.  * None.
  1311.  *
  1312.  *----------------------------------------------------------------------
  1313.  */
  1314. ClientData
  1315. Tcl_CommandTraceInfo(interp, cmdName, flags, proc, prevClientData)
  1316.     Tcl_Interp *interp; /* Interpreter containing command. */
  1317.     CONST char *cmdName; /* Name of command. */
  1318.     int flags; /* OR-ed combo or TCL_GLOBAL_ONLY,
  1319.  * TCL_NAMESPACE_ONLY (can be 0). */
  1320.     Tcl_CommandTraceProc *proc; /* Procedure assocated with trace. */
  1321.     ClientData prevClientData; /* If non-NULL, gives last value returned
  1322.  * by this procedure, so this call will
  1323.  * return the next trace after that one.
  1324.  * If NULL, this call will return the
  1325.  * first trace. */
  1326. {
  1327.     Command *cmdPtr;
  1328.     register CommandTrace *tracePtr;
  1329.     cmdPtr = (Command*)Tcl_FindCommand(interp, cmdName, 
  1330. NULL, TCL_LEAVE_ERR_MSG);
  1331.     if (cmdPtr == NULL) {
  1332. return NULL;
  1333.     }
  1334.     /*
  1335.      * Find the relevant trace, if any, and return its clientData.
  1336.      */
  1337.     tracePtr = cmdPtr->tracePtr;
  1338.     if (prevClientData != NULL) {
  1339. for ( ;  tracePtr != NULL;  tracePtr = tracePtr->nextPtr) {
  1340.     if ((tracePtr->clientData == prevClientData)
  1341.     && (tracePtr->traceProc == proc)) {
  1342. tracePtr = tracePtr->nextPtr;
  1343. break;
  1344.     }
  1345. }
  1346.     }
  1347.     for ( ;  tracePtr != NULL;  tracePtr = tracePtr->nextPtr) {
  1348. if (tracePtr->traceProc == proc) {
  1349.     return tracePtr->clientData;
  1350. }
  1351.     }
  1352.     return NULL;
  1353. }
  1354. /*
  1355.  *----------------------------------------------------------------------
  1356.  *
  1357.  * Tcl_TraceCommand --
  1358.  *
  1359.  * Arrange for rename/deletes to a command to cause a
  1360.  * procedure to be invoked, which can monitor the operations.
  1361.  *
  1362.  * Also optionally arrange for execution of that command
  1363.  * to cause a procedure to be invoked.
  1364.  *
  1365.  * Results:
  1366.  * A standard Tcl return value.
  1367.  *
  1368.  * Side effects:
  1369.  * A trace is set up on the command given by cmdName, such that
  1370.  * future changes to the command will be intermediated by
  1371.  * proc.  See the manual entry for complete details on the calling
  1372.  * sequence for proc.
  1373.  *
  1374.  *----------------------------------------------------------------------
  1375.  */
  1376. int
  1377. Tcl_TraceCommand(interp, cmdName, flags, proc, clientData)
  1378.     Tcl_Interp *interp; /* Interpreter in which command is
  1379.  * to be traced. */
  1380.     CONST char *cmdName; /* Name of command. */
  1381.     int flags; /* OR-ed collection of bits, including any
  1382.  * of TCL_TRACE_RENAME, TCL_TRACE_DELETE,
  1383.  * and any of the TRACE_*_EXEC flags */
  1384.     Tcl_CommandTraceProc *proc; /* Procedure to call when specified ops are
  1385.  * invoked upon varName. */
  1386.     ClientData clientData; /* Arbitrary argument to pass to proc. */
  1387. {
  1388.     Command *cmdPtr;
  1389.     register CommandTrace *tracePtr;
  1390.     cmdPtr = (Command*)Tcl_FindCommand(interp, cmdName,
  1391.     NULL, TCL_LEAVE_ERR_MSG);
  1392.     if (cmdPtr == NULL) {
  1393. return TCL_ERROR;
  1394.     }
  1395.     /*
  1396.      * Set up trace information.
  1397.      */
  1398.     tracePtr = (CommandTrace *) ckalloc(sizeof(CommandTrace));
  1399.     tracePtr->traceProc = proc;
  1400.     tracePtr->clientData = clientData;
  1401.     tracePtr->flags = flags & (TCL_TRACE_RENAME | TCL_TRACE_DELETE
  1402.        | TCL_TRACE_ANY_EXEC);
  1403.     tracePtr->nextPtr = cmdPtr->tracePtr;
  1404.     tracePtr->refCount = 1;
  1405.     cmdPtr->tracePtr = tracePtr;
  1406.     if (tracePtr->flags & TCL_TRACE_ANY_EXEC) {
  1407.         cmdPtr->flags |= CMD_HAS_EXEC_TRACES;
  1408.     }
  1409.     return TCL_OK;
  1410. }
  1411. /*
  1412.  *----------------------------------------------------------------------
  1413.  *
  1414.  * Tcl_UntraceCommand --
  1415.  *
  1416.  * Remove a previously-created trace for a command.
  1417.  *
  1418.  * Results:
  1419.  * None.
  1420.  *
  1421.  * Side effects:
  1422.  * If there exists a trace for the command given by cmdName
  1423.  * with the given flags, proc, and clientData, then that trace
  1424.  * is removed.
  1425.  *
  1426.  *----------------------------------------------------------------------
  1427.  */
  1428. void
  1429. Tcl_UntraceCommand(interp, cmdName, flags, proc, clientData)
  1430.     Tcl_Interp *interp; /* Interpreter containing command. */
  1431.     CONST char *cmdName; /* Name of command. */
  1432.     int flags; /* OR-ed collection of bits, including any
  1433.  * of TCL_TRACE_RENAME, TCL_TRACE_DELETE,
  1434.  * and any of the TRACE_*_EXEC flags */
  1435.     Tcl_CommandTraceProc *proc; /* Procedure assocated with trace. */
  1436.     ClientData clientData; /* Arbitrary argument to pass to proc. */
  1437. {
  1438.     register CommandTrace *tracePtr;
  1439.     CommandTrace *prevPtr;
  1440.     Command *cmdPtr;
  1441.     Interp *iPtr = (Interp *) interp;
  1442.     ActiveCommandTrace *activePtr;
  1443.     int hasExecTraces = 0;
  1444.     
  1445.     cmdPtr = (Command*)Tcl_FindCommand(interp, cmdName, 
  1446. NULL, TCL_LEAVE_ERR_MSG);
  1447.     if (cmdPtr == NULL) {
  1448. return;
  1449.     }
  1450.     flags &= (TCL_TRACE_RENAME | TCL_TRACE_DELETE | TCL_TRACE_ANY_EXEC);
  1451.     for (tracePtr = cmdPtr->tracePtr, prevPtr = NULL;  ;
  1452.  prevPtr = tracePtr, tracePtr = tracePtr->nextPtr) {
  1453. if (tracePtr == NULL) {
  1454.     return;
  1455. }
  1456. if ((tracePtr->traceProc == proc) 
  1457.     && ((tracePtr->flags & (TCL_TRACE_RENAME | TCL_TRACE_DELETE | 
  1458.     TCL_TRACE_ANY_EXEC)) == flags)
  1459. && (tracePtr->clientData == clientData)) {
  1460.     if (tracePtr->flags & TCL_TRACE_ANY_EXEC) {
  1461. hasExecTraces = 1;
  1462.     }
  1463.     break;
  1464. }
  1465.     }
  1466.     
  1467.     /*
  1468.      * The code below makes it possible to delete traces while traces
  1469.      * are active: it makes sure that the deleted trace won't be
  1470.      * processed by CallCommandTraces.
  1471.      */
  1472.     for (activePtr = iPtr->activeCmdTracePtr;  activePtr != NULL;
  1473.  activePtr = activePtr->nextPtr) {
  1474. if (activePtr->nextTracePtr == tracePtr) {
  1475.     if (activePtr->reverseScan) {
  1476. activePtr->nextTracePtr = prevPtr;
  1477.     } else {
  1478. activePtr->nextTracePtr = tracePtr->nextPtr;
  1479.     }
  1480. }
  1481.     }
  1482.     if (prevPtr == NULL) {
  1483. cmdPtr->tracePtr = tracePtr->nextPtr;
  1484.     } else {
  1485. prevPtr->nextPtr = tracePtr->nextPtr;
  1486.     }
  1487.     tracePtr->flags = 0;
  1488.     
  1489.     if ((--tracePtr->refCount) <= 0) {
  1490. ckfree((char*)tracePtr);
  1491.     }
  1492.     
  1493.     if (hasExecTraces) {
  1494. for (tracePtr = cmdPtr->tracePtr, prevPtr = NULL; tracePtr != NULL ;
  1495.      prevPtr = tracePtr, tracePtr = tracePtr->nextPtr) {
  1496.     if (tracePtr->flags & TCL_TRACE_ANY_EXEC) {
  1497.         return;
  1498.     }
  1499. }
  1500. /* 
  1501.  * None of the remaining traces on this command are execution
  1502.  * traces.  We therefore remove this flag:
  1503.  */
  1504. cmdPtr->flags &= ~CMD_HAS_EXEC_TRACES;
  1505.     }
  1506. }
  1507. /*
  1508.  *----------------------------------------------------------------------
  1509.  *
  1510.  * TraceCommandProc --
  1511.  *
  1512.  * This procedure is called to handle command changes that have
  1513.  * been traced using the "trace" command, when using the 
  1514.  * 'rename' or 'delete' options.
  1515.  *
  1516.  * Results:
  1517.  * None.
  1518.  *
  1519.  * Side effects:
  1520.  * Depends on the command associated with the trace.
  1521.  *
  1522.  *----------------------------------------------------------------------
  1523.  */
  1524. /* ARGSUSED */
  1525. static void
  1526. TraceCommandProc(clientData, interp, oldName, newName, flags)
  1527.     ClientData clientData; /* Information about the command trace. */
  1528.     Tcl_Interp *interp; /* Interpreter containing command. */
  1529.     CONST char *oldName; /* Name of command being changed. */
  1530.     CONST char *newName; /* New name of command.  Empty string
  1531.                     * or NULL means command is being deleted
  1532.                     * (renamed to ""). */
  1533.     int flags; /* OR-ed bits giving operation and other
  1534.  * information. */
  1535. {
  1536.     Interp *iPtr = (Interp *) interp;
  1537.     int stateCode;
  1538.     Tcl_SavedResult state;
  1539.     TraceCommandInfo *tcmdPtr = (TraceCommandInfo *) clientData;
  1540.     int code;
  1541.     Tcl_DString cmd;
  1542.     
  1543.     tcmdPtr->refCount++;
  1544.     
  1545.     if ((tcmdPtr->flags & flags) && !Tcl_InterpDeleted(interp)) {
  1546. /*
  1547.  * Generate a command to execute by appending list elements
  1548.  * for the old and new command name and the operation.
  1549.  */
  1550. Tcl_DStringInit(&cmd);
  1551. Tcl_DStringAppend(&cmd, tcmdPtr->command, (int) tcmdPtr->length);
  1552. Tcl_DStringAppendElement(&cmd, oldName);
  1553. Tcl_DStringAppendElement(&cmd, (newName ? newName : ""));
  1554. if (flags & TCL_TRACE_RENAME) {
  1555.     Tcl_DStringAppend(&cmd, " rename", 7);
  1556. } else if (flags & TCL_TRACE_DELETE) {
  1557.     Tcl_DStringAppend(&cmd, " delete", 7);
  1558. }
  1559. /*
  1560.  * Execute the command.  Save the interp's result used for the
  1561.  * command, including the value of iPtr->returnCode which may be
  1562.  * modified when Tcl_Eval is invoked. We discard any object
  1563.  * result the command returns.
  1564.  *
  1565.  * Add the TCL_TRACE_DESTROYED flag to tcmdPtr to indicate to
  1566.  * other areas that this will be destroyed by us, otherwise a
  1567.  * double-free might occur depending on what the eval does.
  1568.  */
  1569. Tcl_SaveResult(interp, &state);
  1570. stateCode = iPtr->returnCode;
  1571. if (flags & TCL_TRACE_DESTROYED) {
  1572.     tcmdPtr->flags |= TCL_TRACE_DESTROYED;
  1573. }
  1574. code = Tcl_EvalEx(interp, Tcl_DStringValue(&cmd),
  1575. Tcl_DStringLength(&cmd), 0);
  1576. if (code != TCL_OK) {      
  1577.     /* We ignore errors in these traced commands */
  1578. }
  1579. Tcl_RestoreResult(interp, &state);
  1580. iPtr->returnCode = stateCode;
  1581. Tcl_DStringFree(&cmd);
  1582.     }
  1583.     /*
  1584.      * We delete when the trace was destroyed or if this is a delete trace,
  1585.      * because command deletes are unconditional, so the trace must go away.
  1586.      */
  1587.     if (flags & (TCL_TRACE_DESTROYED | TCL_TRACE_DELETE)) {
  1588. int untraceFlags = tcmdPtr->flags;
  1589. if (tcmdPtr->stepTrace != NULL) {
  1590.     Tcl_DeleteTrace(interp, tcmdPtr->stepTrace);
  1591.     tcmdPtr->stepTrace = NULL;
  1592.             if (tcmdPtr->startCmd != NULL) {
  1593.         ckfree((char *)tcmdPtr->startCmd);
  1594.     }
  1595. }
  1596. if (tcmdPtr->flags & TCL_TRACE_EXEC_IN_PROGRESS) {
  1597.     /* Postpone deletion, until exec trace returns */
  1598.     tcmdPtr->flags = 0;
  1599. }
  1600. /*
  1601.  * We need to construct the same flags for Tcl_UntraceCommand
  1602.  * as were passed to Tcl_TraceCommand.  Reproduce the processing
  1603.  * of [trace add execution/command].  Be careful to keep this
  1604.  * code in sync with that.
  1605.  */
  1606. if (untraceFlags & TCL_TRACE_ANY_EXEC) {
  1607.     untraceFlags |= TCL_TRACE_DELETE;
  1608.     if (untraceFlags & (TCL_TRACE_ENTER_DURING_EXEC 
  1609.     | TCL_TRACE_LEAVE_DURING_EXEC)) {
  1610. untraceFlags |= (TCL_TRACE_ENTER_EXEC | TCL_TRACE_LEAVE_EXEC);
  1611.     }
  1612. } else if (untraceFlags & TCL_TRACE_RENAME) {
  1613.     untraceFlags |= TCL_TRACE_DELETE;
  1614. }
  1615. /* 
  1616.  * Remove the trace since TCL_TRACE_DESTROYED tells us to, or the
  1617.  * command we're tracing has just gone away.  Then decrement the
  1618.  * clientData refCount that was set up by trace creation.
  1619.  *
  1620.  * Note that we save the (return) state of the interpreter to prevent
  1621.  * bizarre error messages.
  1622.  */
  1623. Tcl_SaveResult(interp, &state);
  1624. stateCode = iPtr->returnCode;
  1625. Tcl_UntraceCommand(interp, oldName, untraceFlags,
  1626. TraceCommandProc, clientData);
  1627. Tcl_RestoreResult(interp, &state);
  1628. iPtr->returnCode = stateCode;
  1629. tcmdPtr->refCount--;
  1630.     }
  1631.     tcmdPtr->refCount--;
  1632.     if (tcmdPtr->refCount < 0) {
  1633. Tcl_Panic("TraceCommandProc: negative TraceCommandInfo refCount");
  1634.     }
  1635.     if (tcmdPtr->refCount == 0) {
  1636.         ckfree((char*)tcmdPtr);
  1637.     }
  1638.     return;
  1639. }
  1640. /*
  1641.  *----------------------------------------------------------------------
  1642.  *
  1643.  * TclCheckExecutionTraces --
  1644.  *
  1645.  * Checks on all current command execution traces, and invokes
  1646.  * procedures which have been registered.  This procedure can be
  1647.  * used by other code which performs execution to unify the
  1648.  * tracing system, so that execution traces will function for that
  1649.  * other code.
  1650.  *
  1651.  * For instance extensions like [incr Tcl] which use their
  1652.  * own execution technique can make use of Tcl's tracing.
  1653.  *
  1654.  * This procedure is called by 'TclEvalObjvInternal'
  1655.  *
  1656.  * Results:
  1657.  *      The return value is a standard Tcl completion code such as
  1658.  *      TCL_OK or TCL_ERROR, etc.
  1659.  *
  1660.  * Side effects:
  1661.  * Those side effects made by any trace procedures called.
  1662.  *
  1663.  *----------------------------------------------------------------------
  1664.  */
  1665. int 
  1666. TclCheckExecutionTraces(interp, command, numChars, cmdPtr, code, 
  1667. traceFlags, objc, objv)
  1668.     Tcl_Interp *interp; /* The current interpreter. */
  1669.     CONST char *command;        /* Pointer to beginning of the current 
  1670.  * command string. */
  1671.     int numChars;               /* The number of characters in 'command' 
  1672.  * which are part of the command string. */
  1673.     Command *cmdPtr; /* Points to command's Command struct. */
  1674.     int code;                   /* The current result code. */
  1675.     int traceFlags;             /* Current tracing situation. */
  1676.     int objc; /* Number of arguments for the command. */
  1677.     Tcl_Obj *CONST objv[]; /* Pointers to Tcl_Obj of each argument. */
  1678. {
  1679.     Interp *iPtr = (Interp *) interp;
  1680.     CommandTrace *tracePtr, *lastTracePtr;
  1681.     ActiveCommandTrace active;
  1682.     int curLevel;
  1683.     int traceCode = TCL_OK;
  1684.     TraceCommandInfo* tcmdPtr;
  1685.     
  1686.     if (command == NULL || cmdPtr->tracePtr == NULL) {
  1687. return traceCode;
  1688.     }
  1689.     
  1690.     curLevel = ((iPtr->varFramePtr == NULL) ? 0 : iPtr->varFramePtr->level);
  1691.     
  1692.     active.nextPtr = iPtr->activeCmdTracePtr;
  1693.     iPtr->activeCmdTracePtr = &active;
  1694.     active.cmdPtr = cmdPtr;
  1695.     lastTracePtr = NULL;
  1696.     for (tracePtr = cmdPtr->tracePtr; 
  1697.  (traceCode == TCL_OK) && (tracePtr != NULL);
  1698.  tracePtr = active.nextTracePtr) {
  1699.         if (traceFlags & TCL_TRACE_LEAVE_EXEC) {
  1700.             /* execute the trace command in order of creation for "leave" */
  1701.     active.reverseScan = 1;
  1702.     active.nextTracePtr = NULL;
  1703.             tracePtr = cmdPtr->tracePtr;
  1704.             while (tracePtr->nextPtr != lastTracePtr) {
  1705.         active.nextTracePtr = tracePtr;
  1706.         tracePtr = tracePtr->nextPtr;
  1707.             }
  1708.         } else {
  1709.     active.reverseScan = 0;
  1710.     active.nextTracePtr = tracePtr->nextPtr;
  1711.         }
  1712. if (tracePtr->traceProc == TraceCommandProc) {
  1713.     tcmdPtr = (TraceCommandInfo*)tracePtr->clientData;
  1714.     if (tcmdPtr->flags != 0) {
  1715.          tcmdPtr->curFlags = traceFlags | TCL_TRACE_EXEC_DIRECT;
  1716.          tcmdPtr->curCode  = code;
  1717. tcmdPtr->refCount++;
  1718. traceCode = TraceExecutionProc((ClientData)tcmdPtr, interp, 
  1719. curLevel, command, (Tcl_Command)cmdPtr, objc, objv);
  1720. tcmdPtr->refCount--;
  1721. if (tcmdPtr->refCount < 0) {
  1722.     Tcl_Panic("TclCheckExecutionTraces: negative TraceCommandInfo refCount");
  1723. }
  1724. if (tcmdPtr->refCount == 0) {
  1725.     ckfree((char*)tcmdPtr);
  1726. }
  1727.     }
  1728. }
  1729. if (active.nextTracePtr) {
  1730.     lastTracePtr = active.nextTracePtr->nextPtr;
  1731. }
  1732.     }
  1733.     iPtr->activeCmdTracePtr = active.nextPtr;
  1734.     return(traceCode);
  1735. }
  1736. /*
  1737.  *----------------------------------------------------------------------
  1738.  *
  1739.  * TclCheckInterpTraces --
  1740.  *
  1741.  * Checks on all current traces, and invokes procedures which
  1742.  * have been registered.  This procedure can be used by other
  1743.  * code which performs execution to unify the tracing system.
  1744.  * For instance extensions like [incr Tcl] which use their
  1745.  * own execution technique can make use of Tcl's tracing.
  1746.  *
  1747.  * This procedure is called by 'TclEvalObjvInternal'
  1748.  *
  1749.  * Results:
  1750.  *      The return value is a standard Tcl completion code such as
  1751.  *      TCL_OK or TCL_ERROR, etc.
  1752.  *
  1753.  * Side effects:
  1754.  * Those side effects made by any trace procedures called.
  1755.  *
  1756.  *----------------------------------------------------------------------
  1757.  */
  1758. int 
  1759. TclCheckInterpTraces(interp, command, numChars, cmdPtr, code, 
  1760.      traceFlags, objc, objv)
  1761.     Tcl_Interp *interp; /* The current interpreter. */
  1762.     CONST char *command;        /* Pointer to beginning of the current 
  1763.  * command string. */
  1764.     int numChars;               /* The number of characters in 'command' 
  1765.  * which are part of the command string. */
  1766.     Command *cmdPtr; /* Points to command's Command struct. */
  1767.     int code;                   /* The current result code. */
  1768.     int traceFlags;             /* Current tracing situation. */
  1769.     int objc; /* Number of arguments for the command. */
  1770.     Tcl_Obj *CONST objv[]; /* Pointers to Tcl_Obj of each argument. */
  1771. {
  1772.     Interp *iPtr = (Interp *) interp;
  1773.     Trace *tracePtr, *lastTracePtr;
  1774.     ActiveInterpTrace active;
  1775.     int curLevel;
  1776.     int traceCode = TCL_OK;
  1777.     
  1778.     if (command == NULL || iPtr->tracePtr == NULL ||
  1779.            (iPtr->flags & INTERP_TRACE_IN_PROGRESS)) {
  1780. return(traceCode);
  1781.     }
  1782.     
  1783.     curLevel = iPtr->numLevels;
  1784.     
  1785.     active.nextPtr = iPtr->activeInterpTracePtr;
  1786.     iPtr->activeInterpTracePtr = &active;
  1787.     lastTracePtr = NULL;
  1788.     for ( tracePtr = iPtr->tracePtr;
  1789.           (traceCode == TCL_OK) && (tracePtr != NULL);
  1790.   tracePtr = active.nextTracePtr) {
  1791.         if (traceFlags & TCL_TRACE_ENTER_EXEC) {
  1792.             /* 
  1793.              * Execute the trace command in reverse order of creation
  1794.              * for "enterstep" operation. The order is changed for
  1795.              * "enterstep" instead of for "leavestep" as was done in 
  1796.              * TclCheckExecutionTraces because for step traces,
  1797.              * Tcl_CreateObjTrace creates one more linked list of traces
  1798.              * which results in one more reversal of trace invocation.
  1799.              */
  1800.     active.reverseScan = 1;
  1801.     active.nextTracePtr = NULL;
  1802.             tracePtr = iPtr->tracePtr;
  1803.             while (tracePtr->nextPtr != lastTracePtr) {
  1804.         active.nextTracePtr = tracePtr;
  1805.         tracePtr = tracePtr->nextPtr;
  1806.             }
  1807.     if (active.nextTracePtr) {
  1808. lastTracePtr = active.nextTracePtr->nextPtr;
  1809.     }
  1810.         } else {
  1811.     active.reverseScan = 0;
  1812.     active.nextTracePtr = tracePtr->nextPtr;
  1813.         }
  1814. if (tracePtr->level > 0 && curLevel > tracePtr->level) {
  1815.     continue;
  1816. }
  1817. if (!(tracePtr->flags & TCL_TRACE_EXEC_IN_PROGRESS)) {
  1818.             /*
  1819.      * The proc invoked might delete the traced command which 
  1820.      * which might try to free tracePtr.  We want to use tracePtr
  1821.      * until the end of this if section, so we use
  1822.      * Tcl_Preserve() and Tcl_Release() to be sure it is not
  1823.      * freed while we still need it.
  1824.      */
  1825.     Tcl_Preserve((ClientData) tracePtr);
  1826.     tracePtr->flags |= TCL_TRACE_EXEC_IN_PROGRESS;
  1827.     
  1828.     if (tracePtr->flags & (TCL_TRACE_ENTER_EXEC | TCL_TRACE_LEAVE_EXEC)) {
  1829.         /* New style trace */
  1830. if (tracePtr->flags & traceFlags) {
  1831.     if (tracePtr->proc == TraceExecutionProc) {
  1832. TraceCommandInfo *tcmdPtr =
  1833. (TraceCommandInfo *) tracePtr->clientData;
  1834. tcmdPtr->curFlags = traceFlags;
  1835. tcmdPtr->curCode  = code;
  1836.     }
  1837.     traceCode = (tracePtr->proc)(tracePtr->clientData, 
  1838.     interp, curLevel, command, (Tcl_Command)cmdPtr,
  1839.     objc, objv);
  1840. }
  1841.     } else {
  1842. /* Old-style trace */
  1843. if (traceFlags & TCL_TRACE_ENTER_EXEC) {
  1844.     /* 
  1845.      * Old-style interpreter-wide traces only trigger
  1846.      * before the command is executed.
  1847.      */
  1848.     traceCode = CallTraceProcedure(interp, tracePtr, cmdPtr,
  1849.        command, numChars, objc, objv);
  1850. }
  1851.     }
  1852.     tracePtr->flags &= ~TCL_TRACE_EXEC_IN_PROGRESS;
  1853.     Tcl_Release((ClientData) tracePtr);
  1854. }
  1855.     }
  1856.     iPtr->activeInterpTracePtr = active.nextPtr;
  1857.     return(traceCode);
  1858. }
  1859. /*
  1860.  *----------------------------------------------------------------------
  1861.  *
  1862.  * CallTraceProcedure --
  1863.  *
  1864.  * Invokes a trace procedure registered with an interpreter. These
  1865.  * procedures trace command execution. Currently this trace procedure
  1866.  * is called with the address of the string-based Tcl_CmdProc for the
  1867.  * command, not the Tcl_ObjCmdProc.
  1868.  *
  1869.  * Results:
  1870.  * None.
  1871.  *
  1872.  * Side effects:
  1873.  * Those side effects made by the trace procedure.
  1874.  *
  1875.  *----------------------------------------------------------------------
  1876.  */
  1877. static int
  1878. CallTraceProcedure(interp, tracePtr, cmdPtr, command, numChars, objc, objv)
  1879.     Tcl_Interp *interp; /* The current interpreter. */
  1880.     register Trace *tracePtr; /* Describes the trace procedure to call. */
  1881.     Command *cmdPtr; /* Points to command's Command struct. */
  1882.     CONST char *command; /* Points to the first character of the
  1883.  * command's source before substitutions. */
  1884.     int numChars; /* The number of characters in the
  1885.  * command's source. */
  1886.     register int objc; /* Number of arguments for the command. */
  1887.     Tcl_Obj *CONST objv[]; /* Pointers to Tcl_Obj of each argument. */
  1888. {
  1889.     Interp *iPtr = (Interp *) interp;
  1890.     char *commandCopy;
  1891.     int traceCode;
  1892.    /*
  1893.      * Copy the command characters into a new string.
  1894.      */
  1895.     commandCopy = (char *) ckalloc((unsigned) (numChars + 1));
  1896.     memcpy((VOID *) commandCopy, (VOID *) command, (size_t) numChars);
  1897.     commandCopy[numChars] = '';
  1898.     
  1899.     /*
  1900.      * Call the trace procedure then free allocated storage.
  1901.      */
  1902.     
  1903.     traceCode = (tracePtr->proc)( tracePtr->clientData, (Tcl_Interp*) iPtr,
  1904.                               iPtr->numLevels, commandCopy,
  1905.                               (Tcl_Command) cmdPtr, objc, objv );
  1906.     ckfree((char *) commandCopy);
  1907.     return(traceCode);
  1908. }
  1909. /*
  1910.  *----------------------------------------------------------------------
  1911.  *
  1912.  * CommandObjTraceDeleted --
  1913.  *
  1914.  * Ensure the trace is correctly deleted by decrementing its
  1915.  * refCount and only deleting if no other references exist.
  1916.  *
  1917.  * Results:
  1918.  *      None.
  1919.  *
  1920.  * Side effects:
  1921.  * May release memory.
  1922.  *
  1923.  *----------------------------------------------------------------------
  1924.  */
  1925. static void 
  1926. CommandObjTraceDeleted(ClientData clientData) {
  1927.     TraceCommandInfo* tcmdPtr = (TraceCommandInfo*)clientData;
  1928.     tcmdPtr->refCount--;
  1929.     if (tcmdPtr->refCount < 0) {
  1930. Tcl_Panic("CommandObjTraceDeleted: negative TraceCommandInfo refCount");
  1931.     }
  1932.     if (tcmdPtr->refCount == 0) {
  1933.         ckfree((char*)tcmdPtr);
  1934.     }
  1935. }
  1936. /*
  1937.  *----------------------------------------------------------------------
  1938.  *
  1939.  * TraceExecutionProc --
  1940.  *
  1941.  * This procedure is invoked whenever code relevant to a
  1942.  * 'trace execution' command is executed.  It is called in one
  1943.  * of two ways in Tcl's core:
  1944.  *
  1945.  * (i) by the TclCheckExecutionTraces, when an execution trace 
  1946.  * has been triggered.
  1947.  * (ii) by TclCheckInterpTraces, when a prior execution trace has
  1948.  * created a trace of the internals of a procedure, passing in
  1949.  * this procedure as the one to be called.
  1950.  *
  1951.  * Results:
  1952.  *      The return value is a standard Tcl completion code such as
  1953.  *      TCL_OK or TCL_ERROR, etc.
  1954.  *
  1955.  * Side effects:
  1956.  * May invoke an arbitrary Tcl procedure, and may create or
  1957.  * delete an interpreter-wide trace.
  1958.  *
  1959.  *----------------------------------------------------------------------
  1960.  */
  1961. static int
  1962. TraceExecutionProc(ClientData clientData, Tcl_Interp *interp, 
  1963.       int level, CONST char* command, Tcl_Command cmdInfo,
  1964.       int objc, struct Tcl_Obj *CONST objv[]) {
  1965.     int call = 0;
  1966.     Interp *iPtr = (Interp *) interp;
  1967.     TraceCommandInfo* tcmdPtr = (TraceCommandInfo*)clientData;
  1968.     int flags = tcmdPtr->curFlags;
  1969.     int code  = tcmdPtr->curCode;
  1970.     int traceCode  = TCL_OK;
  1971.     
  1972.     if (tcmdPtr->flags & TCL_TRACE_EXEC_IN_PROGRESS) {
  1973. /* 
  1974.  * Inside any kind of execution trace callback, we do
  1975.  * not allow any further execution trace callbacks to
  1976.  * be called for the same trace.
  1977.  */
  1978. return traceCode;
  1979.     }
  1980.     
  1981.     if (!Tcl_InterpDeleted(interp)) {
  1982. /*
  1983.  * Check whether the current call is going to eval arbitrary
  1984.  * Tcl code with a generated trace, or whether we are only
  1985.  * going to setup interpreter-wide traces to implement the
  1986.  * 'step' traces.  This latter situation can happen if
  1987.  * we create a command trace without either before or after
  1988.  * operations, but with either of the step operations.
  1989.  */
  1990. if (flags & TCL_TRACE_EXEC_DIRECT) {
  1991.     call = flags & tcmdPtr->flags 
  1992.     & (TCL_TRACE_ENTER_EXEC | TCL_TRACE_LEAVE_EXEC);
  1993. } else {
  1994.     call = 1;
  1995. }
  1996. /*
  1997.  * First, if we have returned back to the level at which we
  1998.  * created an interpreter trace for enterstep and/or leavestep
  1999.          * execution traces, we remove it here.
  2000.  */
  2001. if (flags & TCL_TRACE_LEAVE_EXEC) {
  2002.     if ((tcmdPtr->stepTrace != NULL) && (level == tcmdPtr->startLevel)
  2003.                 && (strcmp(command, tcmdPtr->startCmd) == 0)) {
  2004. Tcl_DeleteTrace(interp, tcmdPtr->stepTrace);
  2005. tcmdPtr->stepTrace = NULL;
  2006.                 if (tcmdPtr->startCmd != NULL) {
  2007.             ckfree((char *)tcmdPtr->startCmd);
  2008.         }
  2009.     }
  2010. }
  2011. /*
  2012.  * Second, create the tcl callback, if required.
  2013.  */
  2014. if (call) {
  2015.     Tcl_SavedResult state;
  2016.     int stateCode, i, saveInterpFlags;
  2017.     Tcl_DString cmd;
  2018.     Tcl_DString sub;
  2019.     Tcl_DStringInit(&cmd);
  2020.     Tcl_DStringAppend(&cmd, tcmdPtr->command, (int)tcmdPtr->length);
  2021.     /* Append command with arguments */
  2022.     Tcl_DStringInit(&sub);
  2023.     for (i = 0; i < objc; i++) {
  2024.         char* str;
  2025.         int len;
  2026.         str = Tcl_GetStringFromObj(objv[i],&len);
  2027.         Tcl_DStringAppendElement(&sub, str);
  2028.     }
  2029.     Tcl_DStringAppendElement(&cmd, Tcl_DStringValue(&sub));
  2030.     Tcl_DStringFree(&sub);
  2031.     if (flags & TCL_TRACE_ENTER_EXEC) {
  2032. /* Append trace operation */
  2033. if (flags & TCL_TRACE_EXEC_DIRECT) {
  2034.     Tcl_DStringAppendElement(&cmd, "enter");
  2035. } else {
  2036.     Tcl_DStringAppendElement(&cmd, "enterstep");
  2037. }
  2038.     } else if (flags & TCL_TRACE_LEAVE_EXEC) {
  2039. Tcl_Obj* resultCode;
  2040. char* resultCodeStr;
  2041. /* Append result code */
  2042. resultCode = Tcl_NewIntObj(code);
  2043. resultCodeStr = Tcl_GetString(resultCode);
  2044. Tcl_DStringAppendElement(&cmd, resultCodeStr);
  2045. Tcl_DecrRefCount(resultCode);
  2046. /* Append result string */
  2047. Tcl_DStringAppendElement(&cmd, Tcl_GetStringResult(interp));
  2048. /* Append trace operation */
  2049. if (flags & TCL_TRACE_EXEC_DIRECT) {
  2050.     Tcl_DStringAppendElement(&cmd, "leave");
  2051. } else {
  2052.     Tcl_DStringAppendElement(&cmd, "leavestep");
  2053. }
  2054.     } else {
  2055. panic("TraceExecutionProc: bad flag combination");
  2056.     }
  2057.     
  2058.     /*
  2059.      * Execute the command.  Save the interp's result used for
  2060.      * the command, including the value of iPtr->returnCode which
  2061.      * may be modified when Tcl_Eval is invoked.  We discard any
  2062.      * object result the command returns.
  2063.      */
  2064.     Tcl_SaveResult(interp, &state);
  2065.     stateCode = iPtr->returnCode;
  2066.     saveInterpFlags = iPtr->flags;
  2067.     iPtr->flags    |= INTERP_TRACE_IN_PROGRESS;
  2068.     tcmdPtr->flags |= TCL_TRACE_EXEC_IN_PROGRESS;
  2069.     tcmdPtr->refCount++;
  2070.     /* 
  2071.      * This line can have quite arbitrary side-effects,
  2072.      * including deleting the trace, the command being
  2073.      * traced, or even the interpreter.
  2074.      */
  2075.     traceCode = Tcl_Eval(interp, Tcl_DStringValue(&cmd));
  2076.     tcmdPtr->flags &= ~TCL_TRACE_EXEC_IN_PROGRESS;
  2077.     /*
  2078.      * Restore the interp tracing flag to prevent cmd traces
  2079.      * from affecting interp traces
  2080.      */
  2081.     iPtr->flags = saveInterpFlags;;
  2082.     if (tcmdPtr->flags == 0) {
  2083. flags |= TCL_TRACE_DESTROYED;
  2084.     }
  2085.     
  2086.             if (traceCode == TCL_OK) {
  2087. /* Restore result if trace execution was successful */
  2088. Tcl_RestoreResult(interp, &state);
  2089. iPtr->returnCode = stateCode;
  2090.             } else {
  2091. Tcl_DiscardResult(&state);
  2092.     }
  2093.     Tcl_DStringFree(&cmd);
  2094. }
  2095. /*
  2096.  * Third, if there are any step execution traces for this proc,
  2097.          * we register an interpreter trace to invoke enterstep and/or
  2098.  * leavestep traces.
  2099.  * We also need to save the current stack level and the proc
  2100.          * string in startLevel and startCmd so that we can delete this
  2101.          * interpreter trace when it reaches the end of this proc.
  2102.  */
  2103. if ((flags & TCL_TRACE_ENTER_EXEC) && (tcmdPtr->stepTrace == NULL)
  2104.     && (tcmdPtr->flags & (TCL_TRACE_ENTER_DURING_EXEC | 
  2105.   TCL_TRACE_LEAVE_DURING_EXEC))) {
  2106. tcmdPtr->startLevel = level;
  2107. tcmdPtr->startCmd = 
  2108.     (char *) ckalloc((unsigned) (strlen(command) + 1));
  2109. strcpy(tcmdPtr->startCmd, command);
  2110. tcmdPtr->refCount++;
  2111. tcmdPtr->stepTrace = Tcl_CreateObjTrace(interp, 0,
  2112.    (tcmdPtr->flags & TCL_TRACE_ANY_EXEC) >> 2, 
  2113.    TraceExecutionProc, (ClientData)tcmdPtr, 
  2114.    CommandObjTraceDeleted);
  2115. }
  2116.     }
  2117.     if (flags & TCL_TRACE_DESTROYED) {
  2118. if (tcmdPtr->stepTrace != NULL) {
  2119.     Tcl_DeleteTrace(interp, tcmdPtr->stepTrace);
  2120.     tcmdPtr->stepTrace = NULL;
  2121.             if (tcmdPtr->startCmd != NULL) {
  2122.         ckfree((char *)tcmdPtr->startCmd);
  2123.     }
  2124. }
  2125.     }
  2126.     if (call) {
  2127. tcmdPtr->refCount--;
  2128. if (tcmdPtr->refCount < 0) {
  2129.     Tcl_Panic("TraceExecutionProc: negative TraceCommandInfo refCount");
  2130. }
  2131. if (tcmdPtr->refCount == 0) {
  2132.     ckfree((char*)tcmdPtr);
  2133. }
  2134.     }
  2135.     return traceCode;
  2136. }
  2137. /*
  2138.  *----------------------------------------------------------------------
  2139.  *
  2140.  * TraceVarProc --
  2141.  *
  2142.  * This procedure is called to handle variable accesses that have
  2143.  * been traced using the "trace" command.
  2144.  *
  2145.  * Results:
  2146.  * Normally returns NULL.  If the trace command returns an error,
  2147.  * then this procedure returns an error string.
  2148.  *
  2149.  * Side effects:
  2150.  * Depends on the command associated with the trace.
  2151.  *
  2152.  *----------------------------------------------------------------------
  2153.  */
  2154. /* ARGSUSED */
  2155. static char *
  2156. TraceVarProc(clientData, interp, name1, name2, flags)
  2157.     ClientData clientData; /* Information about the variable trace. */
  2158.     Tcl_Interp *interp; /* Interpreter containing variable. */
  2159.     CONST char *name1; /* Name of variable or array. */
  2160.     CONST char *name2; /* Name of element within array;  NULL means
  2161.  * scalar variable is being referenced. */
  2162.     int flags; /* OR-ed bits giving operation and other
  2163.  * information. */
  2164. {
  2165.     Tcl_SavedResult state;
  2166.     TraceVarInfo *tvarPtr = (TraceVarInfo *) clientData;
  2167.     char *result;
  2168.     int code, destroy = 0;
  2169.     Tcl_DString cmd;
  2170.     /* 
  2171.      * We might call Tcl_Eval() below, and that might evaluate [trace
  2172.      * vdelete] which might try to free tvarPtr. However we do not
  2173.      * need to protect anything here; it's done by our caller because
  2174.      * the TraceVarInfo is really part of a CompoundVarTrace. [Bug 1348775]
  2175.      */
  2176.     result = NULL;
  2177.     if ((tvarPtr->flags & flags) && !Tcl_InterpDeleted(interp)) {
  2178. if (tvarPtr->length != (size_t) 0) {
  2179.     /*
  2180.      * Generate a command to execute by appending list elements
  2181.      * for the two variable names and the operation. 
  2182.      */
  2183.     Tcl_DStringInit(&cmd);
  2184.     Tcl_DStringAppend(&cmd, tvarPtr->command, (int) tvarPtr->length);
  2185.     Tcl_DStringAppendElement(&cmd, name1);
  2186.     Tcl_DStringAppendElement(&cmd, (name2 ? name2 : ""));
  2187. #ifndef TCL_REMOVE_OBSOLETE_TRACES
  2188.     if (tvarPtr->flags & TCL_TRACE_OLD_STYLE) {
  2189. if (flags & TCL_TRACE_ARRAY) {
  2190.     Tcl_DStringAppend(&cmd, " a", 2);
  2191. } else if (flags & TCL_TRACE_READS) {
  2192.     Tcl_DStringAppend(&cmd, " r", 2);
  2193. } else if (flags & TCL_TRACE_WRITES) {
  2194.     Tcl_DStringAppend(&cmd, " w", 2);
  2195. } else if (flags & TCL_TRACE_UNSETS) {
  2196.     Tcl_DStringAppend(&cmd, " u", 2);
  2197. }
  2198.     } else {
  2199. #endif
  2200. if (flags & TCL_TRACE_ARRAY) {
  2201.     Tcl_DStringAppend(&cmd, " array", 6);
  2202. } else if (flags & TCL_TRACE_READS) {
  2203.     Tcl_DStringAppend(&cmd, " read", 5);
  2204. } else if (flags & TCL_TRACE_WRITES) {
  2205.     Tcl_DStringAppend(&cmd, " write", 6);
  2206. } else if (flags & TCL_TRACE_UNSETS) {
  2207.     Tcl_DStringAppend(&cmd, " unset", 6);
  2208. }
  2209. #ifndef TCL_REMOVE_OBSOLETE_TRACES
  2210.     }
  2211. #endif
  2212.     
  2213.     /*
  2214.      * Execute the command.  Save the interp's result used for
  2215.      * the command. We discard any object result the command returns.
  2216.      *
  2217.      * Add the TCL_TRACE_DESTROYED flag to tvarPtr to indicate to
  2218.      * other areas that this will be destroyed by us, otherwise a
  2219.      * double-free might occur depending on what the eval does.
  2220.      */
  2221.     Tcl_SaveResult(interp, &state);
  2222.     if ((flags & TCL_TRACE_DESTROYED)
  2223.     && !(tvarPtr->flags & TCL_TRACE_DESTROYED)) {
  2224. destroy = 1;
  2225. tvarPtr->flags |= TCL_TRACE_DESTROYED;
  2226.     }
  2227.     code = Tcl_EvalEx(interp, Tcl_DStringValue(&cmd),
  2228.     Tcl_DStringLength(&cmd), 0);
  2229.     if (code != TCL_OK) {      /* copy error msg to result */
  2230. register Tcl_Obj *errMsgObj = Tcl_GetObjResult(interp);
  2231. Tcl_IncrRefCount(errMsgObj);
  2232. result = (char *) errMsgObj;
  2233.     }
  2234.     Tcl_RestoreResult(interp, &state);
  2235.     Tcl_DStringFree(&cmd);
  2236. }
  2237.     }
  2238.     if (destroy) {
  2239. if (result != NULL) {
  2240.     register Tcl_Obj *errMsgObj = (Tcl_Obj *) result;
  2241.     Tcl_DecrRefCount(errMsgObj);
  2242.     result = NULL;
  2243. }
  2244.     }
  2245.     return result;
  2246. }
  2247. /*
  2248.  *----------------------------------------------------------------------
  2249.  *
  2250.  * Tcl_WhileObjCmd --
  2251.  *
  2252.  *      This procedure is invoked to process the "while" Tcl command.
  2253.  *      See the user documentation for details on what it does.
  2254.  *
  2255.  * With the bytecode compiler, this procedure is only called when
  2256.  * a command name is computed at runtime, and is "while" or the name
  2257.  * to which "while" was renamed: e.g., "set z while; $z {$i<100} {}"
  2258.  *
  2259.  * Results:
  2260.  *      A standard Tcl result.
  2261.  *
  2262.  * Side effects:
  2263.  *      See the user documentation.
  2264.  *
  2265.  *----------------------------------------------------------------------
  2266.  */
  2267.         /* ARGSUSED */
  2268. int
  2269. Tcl_WhileObjCmd(dummy, interp, objc, objv)
  2270.     ClientData dummy;                   /* Not used. */
  2271.     Tcl_Interp *interp;                 /* Current interpreter. */
  2272.     int objc;                           /* Number of arguments. */
  2273.     Tcl_Obj *CONST objv[];        /* Argument objects. */
  2274. {
  2275.     int result, value;
  2276. #ifdef TCL_TIP280
  2277.     Interp* iPtr = (Interp*) interp;
  2278. #endif
  2279.     if (objc != 3) {
  2280. Tcl_WrongNumArgs(interp, 1, objv, "test command");
  2281.         return TCL_ERROR;
  2282.     }
  2283.     while (1) {
  2284.         result = Tcl_ExprBooleanObj(interp, objv[1], &value);
  2285.         if (result != TCL_OK) {
  2286.             return result;
  2287.         }
  2288.         if (!value) {
  2289.             break;
  2290.         }
  2291. #ifndef TCL_TIP280
  2292.         result = Tcl_EvalObjEx(interp, objv[2], 0);
  2293. #else
  2294. /* TIP #280. */
  2295.         result = TclEvalObjEx(interp, objv[2], 0, iPtr->cmdFramePtr,2);
  2296. #endif
  2297.         if ((result != TCL_OK) && (result != TCL_CONTINUE)) {
  2298.             if (result == TCL_ERROR) {
  2299.                 char msg[32 + TCL_INTEGER_SPACE];
  2300.                 sprintf(msg, "n    ("while" body line %d)",
  2301.                         interp->errorLine);
  2302.                 Tcl_AddErrorInfo(interp, msg);
  2303.             }
  2304.             break;
  2305.         }
  2306.     }
  2307.     if (result == TCL_BREAK) {
  2308.         result = TCL_OK;
  2309.     }
  2310.     if (result == TCL_OK) {
  2311.         Tcl_ResetResult(interp);
  2312.     }
  2313.     return result;
  2314. }
  2315. #ifdef TCL_TIP280
  2316. static void
  2317. ListLines(listStr, line, n, lines)
  2318.      CONST char* listStr; /* Pointer to string with list structure.
  2319.    * Assumed to be valid. Assumed to contain
  2320.    * n elements.
  2321.    */
  2322.      int  line;           /* line the list as a whole starts on */
  2323.      int  n;              /* #elements in lines */
  2324.      int* lines;          /* Array of line numbers, to fill */
  2325. {
  2326.     int         i;
  2327.     int         length  = strlen( listStr);
  2328.     CONST char *element = NULL;
  2329.     CONST char* next    = NULL;
  2330.     for (i = 0; i < n; i++) {
  2331. TclFindElement(NULL, listStr, length, &element, &next, NULL, NULL);
  2332. TclAdvanceLines (&line, listStr, element); /* Leading whitespace */
  2333. lines [i] = line;
  2334. length   -= (next - listStr);
  2335. TclAdvanceLines (&line, element, next); /* Element */
  2336. listStr   = next;
  2337. if (*element == 0) {
  2338.     /* ASSERT i == n */
  2339.     break;
  2340. }
  2341.     }
  2342. }
  2343. #endif
  2344. /*
  2345.  * Local Variables:
  2346.  * mode: c
  2347.  * c-basic-offset: 4
  2348.  * fill-column: 78
  2349.  * End:
  2350.  */