tclTrace.c

Go to the documentation of this file.
00001 /*
00002  * tclTrace.c --
00003  *
00004  *      This file contains code to handle most trace management.
00005  *
00006  * Copyright (c) 1987-1993 The Regents of the University of California.
00007  * Copyright (c) 1994-1997 Sun Microsystems, Inc.
00008  * Copyright (c) 1998-2000 Scriptics Corporation.
00009  * Copyright (c) 2002 ActiveState Corporation.
00010  *
00011  * See the file "license.terms" for information on usage and redistribution of
00012  * this file, and for a DISCLAIMER OF ALL WARRANTIES.
00013  *
00014  * RCS: @(#) $Id: tclTrace.c,v 1.47 2007/12/13 15:23:20 dgp Exp $
00015  */
00016 
00017 #include "tclInt.h"
00018 
00019 /*
00020  * Structures used to hold information about variable traces:
00021  */
00022 
00023 typedef struct {
00024     int flags;                  /* Operations for which Tcl command is to be
00025                                  * invoked. */
00026     size_t length;              /* Number of non-NUL chars. in command. */
00027     char command[4];            /* Space for Tcl command to invoke. Actual
00028                                  * size will be as large as necessary to hold
00029                                  * command. This field must be the last in the
00030                                  * structure, so that it can be larger than 4
00031                                  * bytes. */
00032 } TraceVarInfo;
00033 
00034 typedef struct {
00035     VarTrace traceInfo;
00036     TraceVarInfo traceCmdInfo;
00037 } CombinedTraceVarInfo;
00038 
00039 /*
00040  * Structure used to hold information about command traces:
00041  */
00042 
00043 typedef struct {
00044     int flags;                  /* Operations for which Tcl command is to be
00045                                  * invoked. */
00046     size_t length;              /* Number of non-NUL chars. in command. */
00047     Tcl_Trace stepTrace;        /* Used for execution traces, when tracing
00048                                  * inside the given command */
00049     int startLevel;             /* Used for bookkeeping with step execution
00050                                  * traces, store the level at which the step
00051                                  * trace was invoked */
00052     char *startCmd;             /* Used for bookkeeping with step execution
00053                                  * traces, store the command name which
00054                                  * invoked step trace */
00055     int curFlags;               /* Trace flags for the current command */
00056     int curCode;                /* Return code for the current command */
00057     int refCount;               /* Used to ensure this structure is not
00058                                  * deleted too early. Keeps track of how many
00059                                  * pieces of code have a pointer to this
00060                                  * structure. */
00061     char command[4];            /* Space for Tcl command to invoke. Actual
00062                                  * size will be as large as necessary to hold
00063                                  * command. This field must be the last in the
00064                                  * structure, so that it can be larger than 4
00065                                  * bytes. */
00066 } TraceCommandInfo;
00067 
00068 /*
00069  * Used by command execution traces. Note that we assume in the code that
00070  * TCL_TRACE_ENTER_DURING_EXEC == 4 * TCL_TRACE_ENTER_EXEC and that
00071  * TCL_TRACE_LEAVE_DURING_EXEC == 4 * TCL_TRACE_LEAVE_EXEC.
00072  *
00073  * TCL_TRACE_ENTER_DURING_EXEC  - Trace each command inside the command
00074  *                                currently being traced, before execution.
00075  * TCL_TRACE_LEAVE_DURING_EXEC  - Trace each command inside the command
00076  *                                currently being traced, after execution.
00077  * TCL_TRACE_ANY_EXEC           - OR'd combination of all EXEC flags.
00078  * TCL_TRACE_EXEC_IN_PROGRESS   - The callback function on this trace is
00079  *                                currently executing. Therefore we don't let
00080  *                                further traces execute.
00081  * TCL_TRACE_EXEC_DIRECT        - This execution trace is triggered directly
00082  *                                by the command being traced, not because of
00083  *                                an internal trace.
00084  * The flags 'TCL_TRACE_DESTROYED' and 'TCL_INTERP_DESTROYED' may also be used
00085  * in command execution traces.
00086  */
00087 
00088 #define TCL_TRACE_ENTER_DURING_EXEC     4
00089 #define TCL_TRACE_LEAVE_DURING_EXEC     8
00090 #define TCL_TRACE_ANY_EXEC              15
00091 #define TCL_TRACE_EXEC_IN_PROGRESS      0x10
00092 #define TCL_TRACE_EXEC_DIRECT           0x20
00093 
00094 /*
00095  * Forward declarations for functions defined in this file:
00096  */
00097 
00098 typedef int (Tcl_TraceTypeObjCmd)(Tcl_Interp *interp, int optionIndex,
00099         int objc, Tcl_Obj *const objv[]);
00100 
00101 static Tcl_TraceTypeObjCmd TraceVariableObjCmd;
00102 static Tcl_TraceTypeObjCmd TraceCommandObjCmd;
00103 static Tcl_TraceTypeObjCmd TraceExecutionObjCmd;
00104 
00105 /*
00106  * Each subcommand has a number of 'types' to which it can apply. Currently
00107  * 'execution', 'command' and 'variable' are the only types supported. These
00108  * three arrays MUST be kept in sync! In the future we may provide an API to
00109  * add to the list of supported trace types.
00110  */
00111 
00112 static const char *traceTypeOptions[] = {
00113     "execution", "command", "variable", NULL
00114 };
00115 static Tcl_TraceTypeObjCmd *traceSubCmds[] = {
00116     TraceExecutionObjCmd,
00117     TraceCommandObjCmd,
00118     TraceVariableObjCmd,
00119 };
00120 
00121 /*
00122  * Declarations for local functions to this file:
00123  */
00124 
00125 static int              CallTraceFunction(Tcl_Interp *interp, Trace *tracePtr,
00126                             Command *cmdPtr, const char *command, int numChars,
00127                             int objc, Tcl_Obj *const objv[]);
00128 static char *           TraceVarProc(ClientData clientData, Tcl_Interp *interp,
00129                             const char *name1, const char *name2, int flags);
00130 static void             TraceCommandProc(ClientData clientData,
00131                             Tcl_Interp *interp, const char *oldName,
00132                             const char *newName, int flags);
00133 static Tcl_CmdObjTraceProc TraceExecutionProc;
00134 static int              StringTraceProc(ClientData clientData,
00135                             Tcl_Interp *interp, int level,
00136                             const char *command, Tcl_Command commandInfo,
00137                             int objc, Tcl_Obj *const objv[]);
00138 static void             StringTraceDeleteProc(ClientData clientData);
00139 static void             DisposeTraceResult(int flags, char *result);
00140 static int              TraceVarEx(Tcl_Interp *interp, const char *part1,
00141                             const char *part2, register VarTrace *tracePtr);
00142 
00143 /*
00144  * The following structure holds the client data for string-based
00145  * trace procs
00146  */
00147 
00148 typedef struct StringTraceData {
00149     ClientData clientData;      /* Client data from Tcl_CreateTrace */
00150     Tcl_CmdTraceProc *proc;     /* Trace function from Tcl_CreateTrace */
00151 } StringTraceData;
00152 
00153 /*
00154  *----------------------------------------------------------------------
00155  *
00156  * Tcl_TraceObjCmd --
00157  *
00158  *      This function is invoked to process the "trace" Tcl command. See the
00159  *      user documentation for details on what it does.
00160  *
00161  *      Standard syntax as of Tcl 8.4 is:
00162  *          trace {add|info|remove} {command|variable} name ops cmd
00163  *
00164  * Results:
00165  *      A standard Tcl result.
00166  *
00167  * Side effects:
00168  *      See the user documentation.
00169  *----------------------------------------------------------------------
00170  */
00171 
00172         /* ARGSUSED */
00173 int
00174 Tcl_TraceObjCmd(
00175     ClientData dummy,           /* Not used. */
00176     Tcl_Interp *interp,         /* Current interpreter. */
00177     int objc,                   /* Number of arguments. */
00178     Tcl_Obj *const objv[])      /* Argument objects. */
00179 {
00180     int optionIndex;
00181     char *name, *flagOps, *p;
00182     /* Main sub commands to 'trace' */
00183     static const char *traceOptions[] = {
00184         "add", "info", "remove",
00185 #ifndef TCL_REMOVE_OBSOLETE_TRACES
00186         "variable", "vdelete", "vinfo",
00187 #endif
00188         NULL
00189     };
00190     /* 'OLD' options are pre-Tcl-8.4 style */
00191     enum traceOptions {
00192         TRACE_ADD, TRACE_INFO, TRACE_REMOVE,
00193 #ifndef TCL_REMOVE_OBSOLETE_TRACES
00194         TRACE_OLD_VARIABLE, TRACE_OLD_VDELETE, TRACE_OLD_VINFO
00195 #endif
00196     };
00197 
00198     if (objc < 2) {
00199         Tcl_WrongNumArgs(interp, 1, objv, "option ?arg arg ...?");
00200         return TCL_ERROR;
00201     }
00202 
00203     if (Tcl_GetIndexFromObj(interp, objv[1], traceOptions,
00204                 "option", 0, &optionIndex) != TCL_OK) {
00205         return TCL_ERROR;
00206     }
00207     switch ((enum traceOptions) optionIndex) {
00208     case TRACE_ADD:
00209     case TRACE_REMOVE: {
00210         /*
00211          * All sub commands of trace add/remove must take at least one more
00212          * argument. Beyond that we let the subcommand itself control the
00213          * argument structure.
00214          */
00215 
00216         int typeIndex;
00217 
00218         if (objc < 3) {
00219             Tcl_WrongNumArgs(interp, 2, objv, "type ?arg arg ...?");
00220             return TCL_ERROR;
00221         }
00222         if (Tcl_GetIndexFromObj(interp, objv[2], traceTypeOptions, "option",
00223                 0, &typeIndex) != TCL_OK) {
00224             return TCL_ERROR;
00225         }
00226         return (traceSubCmds[typeIndex])(interp, optionIndex, objc, objv);
00227     }
00228     case TRACE_INFO: {
00229         /*
00230          * All sub commands of trace info must take exactly two more arguments
00231          * which name the type of thing being traced and the name of the thing
00232          * being traced.
00233          */
00234 
00235         int typeIndex;
00236         if (objc < 3) {
00237             /*
00238              * Delegate other complaints to the type-specific code which can
00239              * give a better error message.
00240              */
00241 
00242             Tcl_WrongNumArgs(interp, 2, objv, "type name");
00243             return TCL_ERROR;
00244         }
00245         if (Tcl_GetIndexFromObj(interp, objv[2], traceTypeOptions, "option",
00246                 0, &typeIndex) != TCL_OK) {
00247             return TCL_ERROR;
00248         }
00249         return (traceSubCmds[typeIndex])(interp, optionIndex, objc, objv);
00250         break;
00251     }
00252 
00253 #ifndef TCL_REMOVE_OBSOLETE_TRACES
00254     case TRACE_OLD_VARIABLE:
00255     case TRACE_OLD_VDELETE: {
00256         Tcl_Obj *copyObjv[6];
00257         Tcl_Obj *opsList;
00258         int code, numFlags;
00259 
00260         if (objc != 5) {
00261             Tcl_WrongNumArgs(interp, 2, objv, "name ops command");
00262             return TCL_ERROR;
00263         }
00264 
00265         opsList = Tcl_NewObj();
00266         Tcl_IncrRefCount(opsList);
00267         flagOps = Tcl_GetStringFromObj(objv[3], &numFlags);
00268         if (numFlags == 0) {
00269             Tcl_DecrRefCount(opsList);
00270             goto badVarOps;
00271         }
00272         for (p = flagOps; *p != 0; p++) {
00273             Tcl_Obj *opObj;
00274 
00275             if (*p == 'r') {
00276                 TclNewLiteralStringObj(opObj, "read");
00277             } else if (*p == 'w') {
00278                 TclNewLiteralStringObj(opObj, "write");
00279             } else if (*p == 'u') {
00280                 TclNewLiteralStringObj(opObj, "unset");
00281             } else if (*p == 'a') {
00282                 TclNewLiteralStringObj(opObj, "array");
00283             } else {
00284                 Tcl_DecrRefCount(opsList);
00285                 goto badVarOps;
00286             }
00287             Tcl_ListObjAppendElement(NULL, opsList, opObj);
00288         }
00289         copyObjv[0] = NULL;
00290         memcpy(copyObjv+1, objv, objc*sizeof(Tcl_Obj *));
00291         copyObjv[4] = opsList;
00292         if (optionIndex == TRACE_OLD_VARIABLE) {
00293             code = (traceSubCmds[2])(interp, TRACE_ADD, objc+1, copyObjv);
00294         } else {
00295             code = (traceSubCmds[2])(interp, TRACE_REMOVE, objc+1, copyObjv);
00296         }
00297         Tcl_DecrRefCount(opsList);
00298         return code;
00299     }
00300     case TRACE_OLD_VINFO: {
00301         ClientData clientData;
00302         char ops[5];
00303         Tcl_Obj *resultListPtr, *pairObjPtr, *elemObjPtr;
00304 
00305         if (objc != 3) {
00306             Tcl_WrongNumArgs(interp, 2, objv, "name");
00307             return TCL_ERROR;
00308         }
00309         resultListPtr = Tcl_NewObj();
00310         clientData = 0;
00311         name = Tcl_GetString(objv[2]);
00312         while ((clientData = Tcl_VarTraceInfo(interp, name, 0,
00313                 TraceVarProc, clientData)) != 0) {
00314 
00315             TraceVarInfo *tvarPtr = (TraceVarInfo *) clientData;
00316 
00317             pairObjPtr = Tcl_NewListObj(0, NULL);
00318             p = ops;
00319             if (tvarPtr->flags & TCL_TRACE_READS) {
00320                 *p = 'r';
00321                 p++;
00322             }
00323             if (tvarPtr->flags & TCL_TRACE_WRITES) {
00324                 *p = 'w';
00325                 p++;
00326             }
00327             if (tvarPtr->flags & TCL_TRACE_UNSETS) {
00328                 *p = 'u';
00329                 p++;
00330             }
00331             if (tvarPtr->flags & TCL_TRACE_ARRAY) {
00332                 *p = 'a';
00333                 p++;
00334             }
00335             *p = '\0';
00336 
00337             /*
00338              * Build a pair (2-item list) with the ops string as the first obj
00339              * element and the tvarPtr->command string as the second obj
00340              * element. Append the pair (as an element) to the end of the
00341              * result object list.
00342              */
00343 
00344             elemObjPtr = Tcl_NewStringObj(ops, -1);
00345             Tcl_ListObjAppendElement(NULL, pairObjPtr, elemObjPtr);
00346             elemObjPtr = Tcl_NewStringObj(tvarPtr->command, -1);
00347             Tcl_ListObjAppendElement(NULL, pairObjPtr, elemObjPtr);
00348             Tcl_ListObjAppendElement(interp, resultListPtr, pairObjPtr);
00349         }
00350         Tcl_SetObjResult(interp, resultListPtr);
00351         break;
00352     }
00353 #endif /* TCL_REMOVE_OBSOLETE_TRACES */
00354     }
00355     return TCL_OK;
00356 
00357   badVarOps:
00358     Tcl_AppendResult(interp, "bad operations \"", flagOps,
00359             "\": should be one or more of rwua", NULL);
00360     return TCL_ERROR;
00361 }
00362 
00363 /*
00364  *----------------------------------------------------------------------
00365  *
00366  * TraceExecutionObjCmd --
00367  *
00368  *      Helper function for Tcl_TraceObjCmd; implements the [trace
00369  *      {add|remove|info} execution ...] subcommands. See the user
00370  *      documentation for details on what these do.
00371  *
00372  * Results:
00373  *      Standard Tcl result.
00374  *
00375  * Side effects:
00376  *      Depends on the operation (add, remove, or info) being performed; may
00377  *      add or remove command traces on a command.
00378  *
00379  *----------------------------------------------------------------------
00380  */
00381 
00382 static int
00383 TraceExecutionObjCmd(
00384     Tcl_Interp *interp,         /* Current interpreter. */
00385     int optionIndex,            /* Add, info or remove */
00386     int objc,                   /* Number of arguments. */
00387     Tcl_Obj *const objv[])      /* Argument objects. */
00388 {
00389     int commandLength, index;
00390     char *name, *command;
00391     size_t length;
00392     enum traceOptions {
00393         TRACE_ADD, TRACE_INFO, TRACE_REMOVE
00394     };
00395     static const char *opStrings[] = {
00396         "enter", "leave", "enterstep", "leavestep", NULL
00397     };
00398     enum operations {
00399         TRACE_EXEC_ENTER, TRACE_EXEC_LEAVE,
00400         TRACE_EXEC_ENTER_STEP, TRACE_EXEC_LEAVE_STEP
00401     };
00402 
00403     switch ((enum traceOptions) optionIndex) {
00404     case TRACE_ADD:
00405     case TRACE_REMOVE: {
00406         int flags = 0;
00407         int i, listLen, result;
00408         Tcl_Obj **elemPtrs;
00409 
00410         if (objc != 6) {
00411             Tcl_WrongNumArgs(interp, 3, objv, "name opList command");
00412             return TCL_ERROR;
00413         }
00414 
00415         /*
00416          * Make sure the ops argument is a list object; get its length and a
00417          * pointer to its array of element pointers.
00418          */
00419 
00420         result = Tcl_ListObjGetElements(interp, objv[4], &listLen, &elemPtrs);
00421         if (result != TCL_OK) {
00422             return result;
00423         }
00424         if (listLen == 0) {
00425             Tcl_SetResult(interp, "bad operation list \"\": must be "
00426                     "one or more of enter, leave, enterstep, or leavestep",
00427                     TCL_STATIC);
00428             return TCL_ERROR;
00429         }
00430         for (i = 0; i < listLen; i++) {
00431             if (Tcl_GetIndexFromObj(interp, elemPtrs[i], opStrings,
00432                     "operation", TCL_EXACT, &index) != TCL_OK) {
00433                 return TCL_ERROR;
00434             }
00435             switch ((enum operations) index) {
00436             case TRACE_EXEC_ENTER:
00437                 flags |= TCL_TRACE_ENTER_EXEC;
00438                 break;
00439             case TRACE_EXEC_LEAVE:
00440                 flags |= TCL_TRACE_LEAVE_EXEC;
00441                 break;
00442             case TRACE_EXEC_ENTER_STEP:
00443                 flags |= TCL_TRACE_ENTER_DURING_EXEC;
00444                 break;
00445             case TRACE_EXEC_LEAVE_STEP:
00446                 flags |= TCL_TRACE_LEAVE_DURING_EXEC;
00447                 break;
00448             }
00449         }
00450         command = Tcl_GetStringFromObj(objv[5], &commandLength);
00451         length = (size_t) commandLength;
00452         if ((enum traceOptions) optionIndex == TRACE_ADD) {
00453             TraceCommandInfo *tcmdPtr;
00454 
00455             tcmdPtr = (TraceCommandInfo *) ckalloc((unsigned)
00456                     (sizeof(TraceCommandInfo) - sizeof(tcmdPtr->command)
00457                             + length + 1));
00458             tcmdPtr->flags = flags;
00459             tcmdPtr->stepTrace = NULL;
00460             tcmdPtr->startLevel = 0;
00461             tcmdPtr->startCmd = NULL;
00462             tcmdPtr->length = length;
00463             tcmdPtr->refCount = 1;
00464             flags |= TCL_TRACE_DELETE;
00465             if (flags & (TCL_TRACE_ENTER_DURING_EXEC |
00466                     TCL_TRACE_LEAVE_DURING_EXEC)) {
00467                 flags |= (TCL_TRACE_ENTER_EXEC | TCL_TRACE_LEAVE_EXEC);
00468             }
00469             strcpy(tcmdPtr->command, command);
00470             name = Tcl_GetString(objv[3]);
00471             if (Tcl_TraceCommand(interp, name, flags, TraceCommandProc,
00472                     (ClientData) tcmdPtr) != TCL_OK) {
00473                 ckfree((char *) tcmdPtr);
00474                 return TCL_ERROR;
00475             }
00476         } else {
00477             /*
00478              * Search through all of our traces on this command to see if
00479              * there's one with the given command. If so, then delete the
00480              * first one that matches.
00481              */
00482 
00483             TraceCommandInfo *tcmdPtr;
00484             ClientData clientData = NULL;
00485             name = Tcl_GetString(objv[3]);
00486 
00487             /*
00488              * First ensure the name given is valid.
00489              */
00490 
00491             if (Tcl_FindCommand(interp,name,NULL,TCL_LEAVE_ERR_MSG) == NULL) {
00492                 return TCL_ERROR;
00493             }
00494 
00495             while ((clientData = Tcl_CommandTraceInfo(interp, name, 0,
00496                     TraceCommandProc, clientData)) != NULL) {
00497                 tcmdPtr = (TraceCommandInfo *) clientData;
00498 
00499                 /*
00500                  * In checking the 'flags' field we must remove any extraneous
00501                  * flags which may have been temporarily added by various
00502                  * pieces of the trace mechanism.
00503                  */
00504 
00505                 if ((tcmdPtr->length == length)
00506                         && ((tcmdPtr->flags & (TCL_TRACE_ANY_EXEC |
00507                                 TCL_TRACE_RENAME | TCL_TRACE_DELETE)) == flags)
00508                         && (strncmp(command, tcmdPtr->command,
00509                                 (size_t) length) == 0)) {
00510                     flags |= TCL_TRACE_DELETE;
00511                     if (flags & (TCL_TRACE_ENTER_DURING_EXEC |
00512                             TCL_TRACE_LEAVE_DURING_EXEC)) {
00513                         flags |= (TCL_TRACE_ENTER_EXEC | TCL_TRACE_LEAVE_EXEC);
00514                     }
00515                     Tcl_UntraceCommand(interp, name, flags,
00516                             TraceCommandProc, clientData);
00517                     if (tcmdPtr->stepTrace != NULL) {
00518                         /*
00519                          * We need to remove the interpreter-wide trace which
00520                          * we created to allow 'step' traces.
00521                          */
00522 
00523                         Tcl_DeleteTrace(interp, tcmdPtr->stepTrace);
00524                         tcmdPtr->stepTrace = NULL;
00525                         if (tcmdPtr->startCmd != NULL) {
00526                             ckfree((char *) tcmdPtr->startCmd);
00527                         }
00528                     }
00529                     if (tcmdPtr->flags & TCL_TRACE_EXEC_IN_PROGRESS) {
00530                         /*
00531                          * Postpone deletion.
00532                          */
00533 
00534                         tcmdPtr->flags = 0;
00535                     }
00536                     if ((--tcmdPtr->refCount) <= 0) {
00537                         ckfree((char *) tcmdPtr);
00538                     }
00539                     break;
00540                 }
00541             }
00542         }
00543         break;
00544     }
00545     case TRACE_INFO: {
00546         ClientData clientData;
00547         Tcl_Obj *resultListPtr, *eachTraceObjPtr, *elemObjPtr;
00548 
00549         if (objc != 4) {
00550             Tcl_WrongNumArgs(interp, 3, objv, "name");
00551             return TCL_ERROR;
00552         }
00553 
00554         clientData = NULL;
00555         name = Tcl_GetString(objv[3]);
00556 
00557         /*
00558          * First ensure the name given is valid.
00559          */
00560 
00561         if (Tcl_FindCommand(interp, name, NULL, TCL_LEAVE_ERR_MSG) == NULL) {
00562             return TCL_ERROR;
00563         }
00564 
00565         resultListPtr = Tcl_NewListObj(0, NULL);
00566         while ((clientData = Tcl_CommandTraceInfo(interp, name, 0,
00567                 TraceCommandProc, clientData)) != NULL) {
00568             int numOps = 0;
00569             Tcl_Obj *opObj;
00570             TraceCommandInfo *tcmdPtr = (TraceCommandInfo *) clientData;
00571 
00572             /*
00573              * Build a list with the ops list as the first obj element and the
00574              * tcmdPtr->command string as the second obj element. Append this
00575              * list (as an element) to the end of the result object list.
00576              */
00577 
00578             elemObjPtr = Tcl_NewListObj(0, NULL);
00579             Tcl_IncrRefCount(elemObjPtr);
00580             if (tcmdPtr->flags & TCL_TRACE_ENTER_EXEC) {
00581                 TclNewLiteralStringObj(opObj, "enter");
00582                 Tcl_ListObjAppendElement(NULL, elemObjPtr, opObj);
00583             }
00584             if (tcmdPtr->flags & TCL_TRACE_LEAVE_EXEC) {
00585                 TclNewLiteralStringObj(opObj, "leave");
00586                 Tcl_ListObjAppendElement(NULL, elemObjPtr, opObj);
00587             }
00588             if (tcmdPtr->flags & TCL_TRACE_ENTER_DURING_EXEC) {
00589                 TclNewLiteralStringObj(opObj, "enterstep");
00590                 Tcl_ListObjAppendElement(NULL, elemObjPtr, opObj);
00591             }
00592             if (tcmdPtr->flags & TCL_TRACE_LEAVE_DURING_EXEC) {
00593                 TclNewLiteralStringObj(opObj, "leavestep");
00594                 Tcl_ListObjAppendElement(NULL, elemObjPtr, opObj);
00595             }
00596             Tcl_ListObjLength(NULL, elemObjPtr, &numOps);
00597             if (0 == numOps) {
00598                 Tcl_DecrRefCount(elemObjPtr);
00599                 continue;
00600             }
00601             eachTraceObjPtr = Tcl_NewListObj(0, NULL);
00602             Tcl_ListObjAppendElement(NULL, eachTraceObjPtr, elemObjPtr);
00603             Tcl_DecrRefCount(elemObjPtr);
00604             elemObjPtr = NULL;
00605 
00606             Tcl_ListObjAppendElement(NULL, eachTraceObjPtr,
00607                     Tcl_NewStringObj(tcmdPtr->command, -1));
00608             Tcl_ListObjAppendElement(interp, resultListPtr, eachTraceObjPtr);
00609         }
00610         Tcl_SetObjResult(interp, resultListPtr);
00611         break;
00612     }
00613     }
00614     return TCL_OK;
00615 }
00616 
00617 /*
00618  *----------------------------------------------------------------------
00619  *
00620  * TraceCommandObjCmd --
00621  *
00622  *      Helper function for Tcl_TraceObjCmd; implements the [trace
00623  *      {add|info|remove} command ...] subcommands. See the user documentation
00624  *      for details on what these do.
00625  *
00626  * Results:
00627  *      Standard Tcl result.
00628  *
00629  * Side effects:
00630  *      Depends on the operation (add, remove, or info) being performed; may
00631  *      add or remove command traces on a command.
00632  *
00633  *----------------------------------------------------------------------
00634  */
00635 
00636 static int
00637 TraceCommandObjCmd(
00638     Tcl_Interp *interp,         /* Current interpreter. */
00639     int optionIndex,            /* Add, info or remove */
00640     int objc,                   /* Number of arguments. */
00641     Tcl_Obj *const objv[])      /* Argument objects. */
00642 {
00643     int commandLength, index;
00644     char *name, *command;
00645     size_t length;
00646     enum traceOptions { TRACE_ADD, TRACE_INFO, TRACE_REMOVE };
00647     static const char *opStrings[] = { "delete", "rename", NULL };
00648     enum operations { TRACE_CMD_DELETE, TRACE_CMD_RENAME };
00649 
00650     switch ((enum traceOptions) optionIndex) {
00651     case TRACE_ADD:
00652     case TRACE_REMOVE: {
00653         int flags = 0;
00654         int i, listLen, result;
00655         Tcl_Obj **elemPtrs;
00656 
00657         if (objc != 6) {
00658             Tcl_WrongNumArgs(interp, 3, objv, "name opList command");
00659             return TCL_ERROR;
00660         }
00661 
00662         /*
00663          * Make sure the ops argument is a list object; get its length and a
00664          * pointer to its array of element pointers.
00665          */
00666 
00667         result = Tcl_ListObjGetElements(interp, objv[4], &listLen, &elemPtrs);
00668         if (result != TCL_OK) {
00669             return result;
00670         }
00671         if (listLen == 0) {
00672             Tcl_SetResult(interp, "bad operation list \"\": must be "
00673                     "one or more of delete or rename", TCL_STATIC);
00674             return TCL_ERROR;
00675         }
00676 
00677         for (i = 0; i < listLen; i++) {
00678             if (Tcl_GetIndexFromObj(interp, elemPtrs[i], opStrings,
00679                     "operation", TCL_EXACT, &index) != TCL_OK) {
00680                 return TCL_ERROR;
00681             }
00682             switch ((enum operations) index) {
00683             case TRACE_CMD_RENAME:
00684                 flags |= TCL_TRACE_RENAME;
00685                 break;
00686             case TRACE_CMD_DELETE:
00687                 flags |= TCL_TRACE_DELETE;
00688                 break;
00689             }
00690         }
00691 
00692         command = Tcl_GetStringFromObj(objv[5], &commandLength);
00693         length = (size_t) commandLength;
00694         if ((enum traceOptions) optionIndex == TRACE_ADD) {
00695             TraceCommandInfo *tcmdPtr;
00696 
00697             tcmdPtr = (TraceCommandInfo *) ckalloc((unsigned)
00698                     (sizeof(TraceCommandInfo) - sizeof(tcmdPtr->command)
00699                             + length + 1));
00700             tcmdPtr->flags = flags;
00701             tcmdPtr->stepTrace = NULL;
00702             tcmdPtr->startLevel = 0;
00703             tcmdPtr->startCmd = NULL;
00704             tcmdPtr->length = length;
00705             tcmdPtr->refCount = 1;
00706             flags |= TCL_TRACE_DELETE;
00707             strcpy(tcmdPtr->command, command);
00708             name = Tcl_GetString(objv[3]);
00709             if (Tcl_TraceCommand(interp, name, flags, TraceCommandProc,
00710                     (ClientData) tcmdPtr) != TCL_OK) {
00711                 ckfree((char *) tcmdPtr);
00712                 return TCL_ERROR;
00713             }
00714         } else {
00715             /*
00716              * Search through all of our traces on this command to see if
00717              * there's one with the given command. If so, then delete the
00718              * first one that matches.
00719              */
00720 
00721             TraceCommandInfo *tcmdPtr;
00722             ClientData clientData = NULL;
00723             name = Tcl_GetString(objv[3]);
00724 
00725             /*
00726              * First ensure the name given is valid.
00727              */
00728 
00729             if (Tcl_FindCommand(interp,name,NULL,TCL_LEAVE_ERR_MSG) == NULL) {
00730                 return TCL_ERROR;
00731             }
00732 
00733             while ((clientData = Tcl_CommandTraceInfo(interp, name, 0,
00734                     TraceCommandProc, clientData)) != NULL) {
00735                 tcmdPtr = (TraceCommandInfo *) clientData;
00736                 if ((tcmdPtr->length == length)
00737                         && (tcmdPtr->flags == flags)
00738                         && (strncmp(command, tcmdPtr->command,
00739                                 (size_t) length) == 0)) {
00740                     Tcl_UntraceCommand(interp, name, flags | TCL_TRACE_DELETE,
00741                             TraceCommandProc, clientData);
00742                     tcmdPtr->flags |= TCL_TRACE_DESTROYED;
00743                     if ((--tcmdPtr->refCount) <= 0) {
00744                         ckfree((char *) tcmdPtr);
00745                     }
00746                     break;
00747                 }
00748             }
00749         }
00750         break;
00751     }
00752     case TRACE_INFO: {
00753         ClientData clientData;
00754         Tcl_Obj *resultListPtr, *eachTraceObjPtr, *elemObjPtr;
00755 
00756         if (objc != 4) {
00757             Tcl_WrongNumArgs(interp, 3, objv, "name");
00758             return TCL_ERROR;
00759         }
00760 
00761         clientData = NULL;
00762         name = Tcl_GetString(objv[3]);
00763 
00764         /*
00765          * First ensure the name given is valid.
00766          */
00767 
00768         if (Tcl_FindCommand(interp, name, NULL, TCL_LEAVE_ERR_MSG) == NULL) {
00769             return TCL_ERROR;
00770         }
00771 
00772         resultListPtr = Tcl_NewListObj(0, NULL);
00773         while ((clientData = Tcl_CommandTraceInfo(interp, name, 0,
00774                 TraceCommandProc, clientData)) != NULL) {
00775             int numOps = 0;
00776             Tcl_Obj *opObj;
00777             TraceCommandInfo *tcmdPtr = (TraceCommandInfo *) clientData;
00778 
00779             /*
00780              * Build a list with the ops list as the first obj element and the
00781              * tcmdPtr->command string as the second obj element. Append this
00782              * list (as an element) to the end of the result object list.
00783              */
00784 
00785             elemObjPtr = Tcl_NewListObj(0, NULL);
00786             Tcl_IncrRefCount(elemObjPtr);
00787             if (tcmdPtr->flags & TCL_TRACE_RENAME) {
00788                 TclNewLiteralStringObj(opObj, "rename");
00789                 Tcl_ListObjAppendElement(NULL, elemObjPtr, opObj);
00790             }
00791             if (tcmdPtr->flags & TCL_TRACE_DELETE) {
00792                 TclNewLiteralStringObj(opObj, "delete");
00793                 Tcl_ListObjAppendElement(NULL, elemObjPtr, opObj);
00794             }
00795             Tcl_ListObjLength(NULL, elemObjPtr, &numOps);
00796             if (0 == numOps) {
00797                 Tcl_DecrRefCount(elemObjPtr);
00798                 continue;
00799             }
00800             eachTraceObjPtr = Tcl_NewListObj(0, NULL);
00801             Tcl_ListObjAppendElement(NULL, eachTraceObjPtr, elemObjPtr);
00802             Tcl_DecrRefCount(elemObjPtr);
00803 
00804             elemObjPtr = Tcl_NewStringObj(tcmdPtr->command, -1);
00805             Tcl_ListObjAppendElement(NULL, eachTraceObjPtr, elemObjPtr);
00806             Tcl_ListObjAppendElement(interp, resultListPtr, eachTraceObjPtr);
00807         }
00808         Tcl_SetObjResult(interp, resultListPtr);
00809         break;
00810     }
00811     }
00812     return TCL_OK;
00813 }
00814 
00815 /*
00816  *----------------------------------------------------------------------
00817  *
00818  * TraceVariableObjCmd --
00819  *
00820  *      Helper function for Tcl_TraceObjCmd; implements the [trace
00821  *      {add|info|remove} variable ...] subcommands. See the user
00822  *      documentation for details on what these do.
00823  *
00824  * Results:
00825  *      Standard Tcl result.
00826  *
00827  * Side effects:
00828  *      Depends on the operation (add, remove, or info) being performed; may
00829  *      add or remove variable traces on a variable.
00830  *
00831  *----------------------------------------------------------------------
00832  */
00833 
00834 static int
00835 TraceVariableObjCmd(
00836     Tcl_Interp *interp,         /* Current interpreter. */
00837     int optionIndex,            /* Add, info or remove */
00838     int objc,                   /* Number of arguments. */
00839     Tcl_Obj *const objv[])      /* Argument objects. */
00840 {
00841     int commandLength, index;
00842     char *name, *command;
00843     size_t length;
00844     enum traceOptions { TRACE_ADD, TRACE_INFO, TRACE_REMOVE };
00845     static const char *opStrings[] = {
00846         "array", "read", "unset", "write", NULL
00847     };
00848     enum operations {
00849         TRACE_VAR_ARRAY, TRACE_VAR_READ, TRACE_VAR_UNSET, TRACE_VAR_WRITE
00850     };
00851 
00852     switch ((enum traceOptions) optionIndex) {
00853     case TRACE_ADD:
00854     case TRACE_REMOVE: {
00855         int flags = 0;
00856         int i, listLen, result;
00857         Tcl_Obj **elemPtrs;
00858 
00859         if (objc != 6) {
00860             Tcl_WrongNumArgs(interp, 3, objv, "name opList command");
00861             return TCL_ERROR;
00862         }
00863 
00864         /*
00865          * Make sure the ops argument is a list object; get its length and a
00866          * pointer to its array of element pointers.
00867          */
00868 
00869         result = Tcl_ListObjGetElements(interp, objv[4], &listLen, &elemPtrs);
00870         if (result != TCL_OK) {
00871             return result;
00872         }
00873         if (listLen == 0) {
00874             Tcl_SetResult(interp, "bad operation list \"\": must be "
00875                     "one or more of array, read, unset, or write", TCL_STATIC);
00876             return TCL_ERROR;
00877         }
00878         for (i = 0; i < listLen ; i++) {
00879             if (Tcl_GetIndexFromObj(interp, elemPtrs[i], opStrings,
00880                     "operation", TCL_EXACT, &index) != TCL_OK) {
00881                 return TCL_ERROR;
00882             }
00883             switch ((enum operations) index) {
00884             case TRACE_VAR_ARRAY:
00885                 flags |= TCL_TRACE_ARRAY;
00886                 break;
00887             case TRACE_VAR_READ:
00888                 flags |= TCL_TRACE_READS;
00889                 break;
00890             case TRACE_VAR_UNSET:
00891                 flags |= TCL_TRACE_UNSETS;
00892                 break;
00893             case TRACE_VAR_WRITE:
00894                 flags |= TCL_TRACE_WRITES;
00895                 break;
00896             }
00897         }
00898         command = Tcl_GetStringFromObj(objv[5], &commandLength);
00899         length = (size_t) commandLength;
00900         if ((enum traceOptions) optionIndex == TRACE_ADD) {
00901             CombinedTraceVarInfo *ctvarPtr;
00902 
00903             ctvarPtr = (CombinedTraceVarInfo *) ckalloc((unsigned)
00904                     (sizeof(CombinedTraceVarInfo) + length + 1
00905                     - sizeof(ctvarPtr->traceCmdInfo.command)));
00906             ctvarPtr->traceCmdInfo.flags = flags;
00907             if (objv[0] == NULL) {
00908                 ctvarPtr->traceCmdInfo.flags |= TCL_TRACE_OLD_STYLE;
00909             }
00910             ctvarPtr->traceCmdInfo.length = length;
00911             flags |= TCL_TRACE_UNSETS | TCL_TRACE_RESULT_OBJECT;
00912             strcpy(ctvarPtr->traceCmdInfo.command, command);
00913             ctvarPtr->traceInfo.traceProc = TraceVarProc;
00914             ctvarPtr->traceInfo.clientData = (ClientData)
00915                     &ctvarPtr->traceCmdInfo;
00916             ctvarPtr->traceInfo.flags = flags;
00917             name = Tcl_GetString(objv[3]);
00918             if (TraceVarEx(interp,name,NULL,(VarTrace*)ctvarPtr) != TCL_OK) {
00919                 ckfree((char *) ctvarPtr);
00920                 return TCL_ERROR;
00921             }
00922         } else {
00923             /*
00924              * Search through all of our traces on this variable to see if
00925              * there's one with the given command. If so, then delete the
00926              * first one that matches.
00927              */
00928 
00929             TraceVarInfo *tvarPtr;
00930             ClientData clientData = 0;
00931             name = Tcl_GetString(objv[3]);
00932             while ((clientData = Tcl_VarTraceInfo(interp, name, 0,
00933                     TraceVarProc, clientData)) != 0) {
00934                 tvarPtr = (TraceVarInfo *) clientData;
00935                 if ((tvarPtr->length == length)
00936                         && ((tvarPtr->flags & ~TCL_TRACE_OLD_STYLE)==flags)
00937                         && (strncmp(command, tvarPtr->command,
00938                                 (size_t) length) == 0)) {
00939                     Tcl_UntraceVar2(interp, name, NULL,
00940                             flags | TCL_TRACE_UNSETS | TCL_TRACE_RESULT_OBJECT,
00941                             TraceVarProc, clientData);
00942                     break;
00943                 }
00944             }
00945         }
00946         break;
00947     }
00948     case TRACE_INFO: {
00949         ClientData clientData;
00950         Tcl_Obj *resultListPtr, *eachTraceObjPtr, *elemObjPtr;
00951 
00952         if (objc != 4) {
00953             Tcl_WrongNumArgs(interp, 3, objv, "name");
00954             return TCL_ERROR;
00955         }
00956 
00957         resultListPtr = Tcl_NewObj();
00958         clientData = 0;
00959         name = Tcl_GetString(objv[3]);
00960         while ((clientData = Tcl_VarTraceInfo(interp, name, 0, TraceVarProc,
00961                 clientData)) != 0) {
00962             Tcl_Obj *opObj;
00963             TraceVarInfo *tvarPtr = (TraceVarInfo *) clientData;
00964 
00965             /*
00966              * Build a list with the ops list as the first obj element and the
00967              * tcmdPtr->command string as the second obj element. Append this
00968              * list (as an element) to the end of the result object list.
00969              */
00970 
00971             elemObjPtr = Tcl_NewListObj(0, NULL);
00972             if (tvarPtr->flags & TCL_TRACE_ARRAY) {
00973                 TclNewLiteralStringObj(opObj, "array");
00974                 Tcl_ListObjAppendElement(NULL, elemObjPtr, opObj);
00975             }
00976             if (tvarPtr->flags & TCL_TRACE_READS) {
00977                 TclNewLiteralStringObj(opObj, "read");
00978                 Tcl_ListObjAppendElement(NULL, elemObjPtr, opObj);
00979             }
00980             if (tvarPtr->flags & TCL_TRACE_WRITES) {
00981                 TclNewLiteralStringObj(opObj, "write");
00982                 Tcl_ListObjAppendElement(NULL, elemObjPtr, opObj);
00983             }
00984             if (tvarPtr->flags & TCL_TRACE_UNSETS) {
00985                 TclNewLiteralStringObj(opObj, "unset");
00986                 Tcl_ListObjAppendElement(NULL, elemObjPtr, opObj);
00987             }
00988             eachTraceObjPtr = Tcl_NewListObj(0, NULL);
00989             Tcl_ListObjAppendElement(NULL, eachTraceObjPtr, elemObjPtr);
00990 
00991             elemObjPtr = Tcl_NewStringObj(tvarPtr->command, -1);
00992             Tcl_ListObjAppendElement(NULL, eachTraceObjPtr, elemObjPtr);
00993             Tcl_ListObjAppendElement(interp, resultListPtr,
00994                     eachTraceObjPtr);
00995         }
00996         Tcl_SetObjResult(interp, resultListPtr);
00997         break;
00998     }
00999     }
01000     return TCL_OK;
01001 }
01002 
01003 /*
01004  *----------------------------------------------------------------------
01005  *
01006  * Tcl_CommandTraceInfo --
01007  *
01008  *      Return the clientData value associated with a trace on a command.
01009  *      This function can also be used to step through all of the traces on a
01010  *      particular command that have the same trace function.
01011  *
01012  * Results:
01013  *      The return value is the clientData value associated with a trace on
01014  *      the given command. Information will only be returned for a trace with
01015  *      proc as trace function. If the clientData argument is NULL then the
01016  *      first such trace is returned; otherwise, the next relevant one after
01017  *      the one given by clientData will be returned. If the command doesn't
01018  *      exist then an error message is left in the interpreter and NULL is
01019  *      returned. Also, if there are no (more) traces for the given command,
01020  *      NULL is returned.
01021  *
01022  * Side effects:
01023  *      None.
01024  *
01025  *----------------------------------------------------------------------
01026  */
01027 
01028 ClientData
01029 Tcl_CommandTraceInfo(
01030     Tcl_Interp *interp,         /* Interpreter containing command. */
01031     const char *cmdName,        /* Name of command. */
01032     int flags,                  /* OR-ed combo or TCL_GLOBAL_ONLY,
01033                                  * TCL_NAMESPACE_ONLY (can be 0). */
01034     Tcl_CommandTraceProc *proc, /* Function assocated with trace. */
01035     ClientData prevClientData)  /* If non-NULL, gives last value returned by
01036                                  * this function, so this call will return the
01037                                  * next trace after that one. If NULL, this
01038                                  * call will return the first trace. */
01039 {
01040     Command *cmdPtr;
01041     register CommandTrace *tracePtr;
01042 
01043     cmdPtr = (Command *) Tcl_FindCommand(interp, cmdName, NULL,
01044             TCL_LEAVE_ERR_MSG);
01045     if (cmdPtr == NULL) {
01046         return NULL;
01047     }
01048 
01049     /*
01050      * Find the relevant trace, if any, and return its clientData.
01051      */
01052 
01053     tracePtr = cmdPtr->tracePtr;
01054     if (prevClientData != NULL) {
01055         for (; tracePtr!=NULL ; tracePtr=tracePtr->nextPtr) {
01056             if ((tracePtr->clientData == prevClientData)
01057                     && (tracePtr->traceProc == proc)) {
01058                 tracePtr = tracePtr->nextPtr;
01059                 break;
01060             }
01061         }
01062     }
01063     for (; tracePtr!=NULL ; tracePtr=tracePtr->nextPtr) {
01064         if (tracePtr->traceProc == proc) {
01065             return tracePtr->clientData;
01066         }
01067     }
01068     return NULL;
01069 }
01070 
01071 /*
01072  *----------------------------------------------------------------------
01073  *
01074  * Tcl_TraceCommand --
01075  *
01076  *      Arrange for rename/deletes to a command to cause a function to be
01077  *      invoked, which can monitor the operations.
01078  *
01079  *      Also optionally arrange for execution of that command to cause a
01080  *      function to be invoked.
01081  *
01082  * Results:
01083  *      A standard Tcl return value.
01084  *
01085  * Side effects:
01086  *      A trace is set up on the command given by cmdName, such that future
01087  *      changes to the command will be intermediated by proc. See the manual
01088  *      entry for complete details on the calling sequence for proc.
01089  *
01090  *----------------------------------------------------------------------
01091  */
01092 
01093 int
01094 Tcl_TraceCommand(
01095     Tcl_Interp *interp,         /* Interpreter in which command is to be
01096                                  * traced. */
01097     const char *cmdName,        /* Name of command. */
01098     int flags,                  /* OR-ed collection of bits, including any of
01099                                  * TCL_TRACE_RENAME, TCL_TRACE_DELETE, and any
01100                                  * of the TRACE_*_EXEC flags */
01101     Tcl_CommandTraceProc *proc, /* Function to call when specified ops are
01102                                  * invoked upon cmdName. */
01103     ClientData clientData)      /* Arbitrary argument to pass to proc. */
01104 {
01105     Command *cmdPtr;
01106     register CommandTrace *tracePtr;
01107 
01108     cmdPtr = (Command *) Tcl_FindCommand(interp, cmdName, NULL,
01109             TCL_LEAVE_ERR_MSG);
01110     if (cmdPtr == NULL) {
01111         return TCL_ERROR;
01112     }
01113 
01114     /*
01115      * Set up trace information.
01116      */
01117 
01118     tracePtr = (CommandTrace *) ckalloc(sizeof(CommandTrace));
01119     tracePtr->traceProc = proc;
01120     tracePtr->clientData = clientData;
01121     tracePtr->flags = flags &
01122             (TCL_TRACE_RENAME | TCL_TRACE_DELETE | TCL_TRACE_ANY_EXEC);
01123     tracePtr->nextPtr = cmdPtr->tracePtr;
01124     tracePtr->refCount = 1;
01125     cmdPtr->tracePtr = tracePtr;
01126     if (tracePtr->flags & TCL_TRACE_ANY_EXEC) {
01127         cmdPtr->flags |= CMD_HAS_EXEC_TRACES;
01128     }
01129     return TCL_OK;
01130 }
01131 
01132 /*
01133  *----------------------------------------------------------------------
01134  *
01135  * Tcl_UntraceCommand --
01136  *
01137  *      Remove a previously-created trace for a command.
01138  *
01139  * Results:
01140  *      None.
01141  *
01142  * Side effects:
01143  *      If there exists a trace for the command given by cmdName with the
01144  *      given flags, proc, and clientData, then that trace is removed.
01145  *
01146  *----------------------------------------------------------------------
01147  */
01148 
01149 void
01150 Tcl_UntraceCommand(
01151     Tcl_Interp *interp,         /* Interpreter containing command. */
01152     const char *cmdName,        /* Name of command. */
01153     int flags,                  /* OR-ed collection of bits, including any of
01154                                  * TCL_TRACE_RENAME, TCL_TRACE_DELETE, and any
01155                                  * of the TRACE_*_EXEC flags */
01156     Tcl_CommandTraceProc *proc, /* Function assocated with trace. */
01157     ClientData clientData)      /* Arbitrary argument to pass to proc. */
01158 {
01159     register CommandTrace *tracePtr;
01160     CommandTrace *prevPtr;
01161     Command *cmdPtr;
01162     Interp *iPtr = (Interp *) interp;
01163     ActiveCommandTrace *activePtr;
01164     int hasExecTraces = 0;
01165 
01166     cmdPtr = (Command *) Tcl_FindCommand(interp, cmdName, NULL,
01167             TCL_LEAVE_ERR_MSG);
01168     if (cmdPtr == NULL) {
01169         return;
01170     }
01171 
01172     flags &= (TCL_TRACE_RENAME | TCL_TRACE_DELETE | TCL_TRACE_ANY_EXEC);
01173 
01174     for (tracePtr = cmdPtr->tracePtr, prevPtr = NULL; ;
01175             prevPtr = tracePtr, tracePtr = tracePtr->nextPtr) {
01176         if (tracePtr == NULL) {
01177             return;
01178         }
01179         if ((tracePtr->traceProc == proc)
01180                 && ((tracePtr->flags & (TCL_TRACE_RENAME | TCL_TRACE_DELETE |
01181                         TCL_TRACE_ANY_EXEC)) == flags)
01182                 && (tracePtr->clientData == clientData)) {
01183             if (tracePtr->flags & TCL_TRACE_ANY_EXEC) {
01184                 hasExecTraces = 1;
01185             }
01186             break;
01187         }
01188     }
01189 
01190     /*
01191      * The code below makes it possible to delete traces while traces are
01192      * active: it makes sure that the deleted trace won't be processed by
01193      * CallCommandTraces.
01194      */
01195 
01196     for (activePtr = iPtr->activeCmdTracePtr;  activePtr != NULL;
01197             activePtr = activePtr->nextPtr) {
01198         if (activePtr->nextTracePtr == tracePtr) {
01199             if (activePtr->reverseScan) {
01200                 activePtr->nextTracePtr = prevPtr;
01201             } else {
01202                 activePtr->nextTracePtr = tracePtr->nextPtr;
01203             }
01204         }
01205     }
01206     if (prevPtr == NULL) {
01207         cmdPtr->tracePtr = tracePtr->nextPtr;
01208     } else {
01209         prevPtr->nextPtr = tracePtr->nextPtr;
01210     }
01211     tracePtr->flags = 0;
01212 
01213     if ((--tracePtr->refCount) <= 0) {
01214         ckfree((char *) tracePtr);
01215     }
01216 
01217     if (hasExecTraces) {
01218         for (tracePtr = cmdPtr->tracePtr, prevPtr = NULL; tracePtr != NULL ;
01219                 prevPtr = tracePtr, tracePtr = tracePtr->nextPtr) {
01220             if (tracePtr->flags & TCL_TRACE_ANY_EXEC) {
01221                 return;
01222             }
01223         }
01224 
01225         /*
01226          * None of the remaining traces on this command are execution traces.
01227          * We therefore remove this flag:
01228          */
01229 
01230         cmdPtr->flags &= ~CMD_HAS_EXEC_TRACES;
01231     }
01232 }
01233 
01234 /*
01235  *----------------------------------------------------------------------
01236  *
01237  * TraceCommandProc --
01238  *
01239  *      This function is called to handle command changes that have been
01240  *      traced using the "trace" command, when using the 'rename' or 'delete'
01241  *      options.
01242  *
01243  * Results:
01244  *      None.
01245  *
01246  * Side effects:
01247  *      Depends on the command associated with the trace.
01248  *
01249  *----------------------------------------------------------------------
01250  */
01251 
01252         /* ARGSUSED */
01253 static void
01254 TraceCommandProc(
01255     ClientData clientData,      /* Information about the command trace. */
01256     Tcl_Interp *interp,         /* Interpreter containing command. */
01257     const char *oldName,        /* Name of command being changed. */
01258     const char *newName,        /* New name of command. Empty string or NULL
01259                                  * means command is being deleted (renamed to
01260                                  * ""). */
01261     int flags)                  /* OR-ed bits giving operation and other
01262                                  * information. */
01263 {
01264     TraceCommandInfo *tcmdPtr = (TraceCommandInfo *) clientData;
01265     int code;
01266     Tcl_DString cmd;
01267 
01268     tcmdPtr->refCount++;
01269 
01270     if ((tcmdPtr->flags & flags) && !Tcl_InterpDeleted(interp)
01271             && !Tcl_LimitExceeded(interp)) {
01272         /*
01273          * Generate a command to execute by appending list elements for the
01274          * old and new command name and the operation.
01275          */
01276 
01277         Tcl_DStringInit(&cmd);
01278         Tcl_DStringAppend(&cmd, tcmdPtr->command, (int) tcmdPtr->length);
01279         Tcl_DStringAppendElement(&cmd, oldName);
01280         Tcl_DStringAppendElement(&cmd, (newName ? newName : ""));
01281         if (flags & TCL_TRACE_RENAME) {
01282             Tcl_DStringAppend(&cmd, " rename", 7);
01283         } else if (flags & TCL_TRACE_DELETE) {
01284             Tcl_DStringAppend(&cmd, " delete", 7);
01285         }
01286 
01287         /*
01288          * Execute the command. We discard any object result the command
01289          * returns.
01290          *
01291          * Add the TCL_TRACE_DESTROYED flag to tcmdPtr to indicate to other
01292          * areas that this will be destroyed by us, otherwise a double-free
01293          * might occur depending on what the eval does.
01294          */
01295 
01296         if (flags & TCL_TRACE_DESTROYED) {
01297             tcmdPtr->flags |= TCL_TRACE_DESTROYED;
01298         }
01299         code = Tcl_EvalEx(interp, Tcl_DStringValue(&cmd),
01300                 Tcl_DStringLength(&cmd), 0);
01301         if (code != TCL_OK) {
01302             /* We ignore errors in these traced commands */
01303             /*** QUESTION: Use Tcl_BackgroundError(interp); instead? ***/
01304         }
01305         Tcl_DStringFree(&cmd);
01306     }
01307 
01308     /*
01309      * We delete when the trace was destroyed or if this is a delete trace,
01310      * because command deletes are unconditional, so the trace must go away.
01311      */
01312 
01313     if (flags & (TCL_TRACE_DESTROYED | TCL_TRACE_DELETE)) {
01314         int untraceFlags = tcmdPtr->flags;
01315         Tcl_InterpState state;
01316 
01317         if (tcmdPtr->stepTrace != NULL) {
01318             Tcl_DeleteTrace(interp, tcmdPtr->stepTrace);
01319             tcmdPtr->stepTrace = NULL;
01320             if (tcmdPtr->startCmd != NULL) {
01321                 ckfree((char *) tcmdPtr->startCmd);
01322             }
01323         }
01324         if (tcmdPtr->flags & TCL_TRACE_EXEC_IN_PROGRESS) {
01325             /*
01326              * Postpone deletion, until exec trace returns.
01327              */
01328 
01329             tcmdPtr->flags = 0;
01330         }
01331 
01332         /*
01333          * We need to construct the same flags for Tcl_UntraceCommand as were
01334          * passed to Tcl_TraceCommand. Reproduce the processing of [trace add
01335          * execution/command]. Be careful to keep this code in sync with that.
01336          */
01337 
01338         if (untraceFlags & TCL_TRACE_ANY_EXEC) {
01339             untraceFlags |= TCL_TRACE_DELETE;
01340             if (untraceFlags & (TCL_TRACE_ENTER_DURING_EXEC
01341                     | TCL_TRACE_LEAVE_DURING_EXEC)) {
01342                 untraceFlags |= (TCL_TRACE_ENTER_EXEC | TCL_TRACE_LEAVE_EXEC);
01343             }
01344         } else if (untraceFlags & TCL_TRACE_RENAME) {
01345             untraceFlags |= TCL_TRACE_DELETE;
01346         }
01347 
01348         /*
01349          * Remove the trace since TCL_TRACE_DESTROYED tells us to, or the
01350          * command we're tracing has just gone away. Then decrement the
01351          * clientData refCount that was set up by trace creation.
01352          *
01353          * Note that we save the (return) state of the interpreter to prevent
01354          * bizarre error messages.
01355          */
01356 
01357         state = Tcl_SaveInterpState(interp, TCL_OK);
01358         Tcl_UntraceCommand(interp, oldName, untraceFlags,
01359                 TraceCommandProc, clientData);
01360         (void) Tcl_RestoreInterpState(interp, state);
01361         tcmdPtr->refCount--;
01362     }
01363     if ((--tcmdPtr->refCount) <= 0) {
01364         ckfree((char *) tcmdPtr);
01365     }
01366 }
01367 
01368 /*
01369  *----------------------------------------------------------------------
01370  *
01371  * TclCheckExecutionTraces --
01372  *
01373  *      Checks on all current command execution traces, and invokes functions
01374  *      which have been registered. This function can be used by other code
01375  *      which performs execution to unify the tracing system, so that
01376  *      execution traces will function for that other code.
01377  *
01378  *      For instance extensions like [incr Tcl] which use their own execution
01379  *      technique can make use of Tcl's tracing.
01380  *
01381  *      This function is called by 'TclEvalObjvInternal'
01382  *
01383  * Results:
01384  *      The return value is a standard Tcl completion code such as TCL_OK or
01385  *      TCL_ERROR, etc.
01386  *
01387  * Side effects:
01388  *      Those side effects made by any trace functions called.
01389  *
01390  *----------------------------------------------------------------------
01391  */
01392 
01393 int
01394 TclCheckExecutionTraces(
01395     Tcl_Interp *interp,         /* The current interpreter. */
01396     const char *command,        /* Pointer to beginning of the current command
01397                                  * string. */
01398     int numChars,               /* The number of characters in 'command' which
01399                                  * are part of the command string. */
01400     Command *cmdPtr,            /* Points to command's Command struct. */
01401     int code,                   /* The current result code. */
01402     int traceFlags,             /* Current tracing situation. */
01403     int objc,                   /* Number of arguments for the command. */
01404     Tcl_Obj *const objv[])      /* Pointers to Tcl_Obj of each argument. */
01405 {
01406     Interp *iPtr = (Interp *) interp;
01407     CommandTrace *tracePtr, *lastTracePtr;
01408     ActiveCommandTrace active;
01409     int curLevel;
01410     int traceCode = TCL_OK;
01411     Tcl_InterpState state = NULL;
01412 
01413     if (cmdPtr->tracePtr == NULL) {
01414         return traceCode;
01415     }
01416 
01417     curLevel = iPtr->varFramePtr->level;
01418 
01419     active.nextPtr = iPtr->activeCmdTracePtr;
01420     iPtr->activeCmdTracePtr = &active;
01421 
01422     active.cmdPtr = cmdPtr;
01423     lastTracePtr = NULL;
01424     for (tracePtr = cmdPtr->tracePtr;
01425             (traceCode == TCL_OK) && (tracePtr != NULL);
01426             tracePtr = active.nextTracePtr) {
01427         if (traceFlags & TCL_TRACE_LEAVE_EXEC) {
01428             /*
01429              * Execute the trace command in order of creation for "leave".
01430              */
01431 
01432             active.reverseScan = 1;
01433             active.nextTracePtr = NULL;
01434             tracePtr = cmdPtr->tracePtr;
01435             while (tracePtr->nextPtr != lastTracePtr) {
01436                 active.nextTracePtr = tracePtr;
01437                 tracePtr = tracePtr->nextPtr;
01438             }
01439         } else {
01440             active.reverseScan = 0;
01441             active.nextTracePtr = tracePtr->nextPtr;
01442         }
01443         if (tracePtr->traceProc == TraceCommandProc) {
01444             TraceCommandInfo *tcmdPtr = (TraceCommandInfo *)
01445                     tracePtr->clientData;
01446 
01447             if (tcmdPtr->flags != 0) {
01448                 tcmdPtr->curFlags = traceFlags | TCL_TRACE_EXEC_DIRECT;
01449                 tcmdPtr->curCode  = code;
01450                 tcmdPtr->refCount++;
01451                 if (state == NULL) {
01452                     state = Tcl_SaveInterpState(interp, code);
01453                 }
01454                 traceCode = TraceExecutionProc((ClientData) tcmdPtr, interp,
01455                         curLevel, command, (Tcl_Command) cmdPtr, objc, objv);
01456                 if ((--tcmdPtr->refCount) <= 0) {
01457                     ckfree((char *) tcmdPtr);
01458                 }
01459             }
01460         }
01461         if (active.nextTracePtr) {
01462             lastTracePtr = active.nextTracePtr->nextPtr;
01463         }
01464     }
01465     iPtr->activeCmdTracePtr = active.nextPtr;
01466     if (state) {
01467         (void) Tcl_RestoreInterpState(interp, state);
01468     }
01469 
01470     return(traceCode);
01471 }
01472 
01473 /*
01474  *----------------------------------------------------------------------
01475  *
01476  * TclCheckInterpTraces --
01477  *
01478  *      Checks on all current traces, and invokes functions which have been
01479  *      registered. This function can be used by other code which performs
01480  *      execution to unify the tracing system. For instance extensions like
01481  *      [incr Tcl] which use their own execution technique can make use of
01482  *      Tcl's tracing.
01483  *
01484  *      This function is called by 'TclEvalObjvInternal'
01485  *
01486  * Results:
01487  *      The return value is a standard Tcl completion code such as TCL_OK or
01488  *      TCL_ERROR, etc.
01489  *
01490  * Side effects:
01491  *      Those side effects made by any trace functions called.
01492  *
01493  *----------------------------------------------------------------------
01494  */
01495 
01496 int
01497 TclCheckInterpTraces(
01498     Tcl_Interp *interp,         /* The current interpreter. */
01499     const char *command,        /* Pointer to beginning of the current command
01500                                  * string. */
01501     int numChars,               /* The number of characters in 'command' which
01502                                  * are part of the command string. */
01503     Command *cmdPtr,            /* Points to command's Command struct. */
01504     int code,                   /* The current result code. */
01505     int traceFlags,             /* Current tracing situation. */
01506     int objc,                   /* Number of arguments for the command. */
01507     Tcl_Obj *const objv[])      /* Pointers to Tcl_Obj of each argument. */
01508 {
01509     Interp *iPtr = (Interp *) interp;
01510     Trace *tracePtr, *lastTracePtr;
01511     ActiveInterpTrace active;
01512     int curLevel;
01513     int traceCode = TCL_OK;
01514     Tcl_InterpState state = NULL;
01515 
01516     if ((iPtr->tracePtr == NULL)
01517             || (iPtr->flags & INTERP_TRACE_IN_PROGRESS)) {
01518         return(traceCode);
01519     }
01520 
01521     curLevel = iPtr->numLevels;
01522 
01523     active.nextPtr = iPtr->activeInterpTracePtr;
01524     iPtr->activeInterpTracePtr = &active;
01525 
01526     lastTracePtr = NULL;
01527     for (tracePtr = iPtr->tracePtr;
01528             (traceCode == TCL_OK) && (tracePtr != NULL);
01529             tracePtr = active.nextTracePtr) {
01530         if (traceFlags & TCL_TRACE_ENTER_EXEC) {
01531             /*
01532              * Execute the trace command in reverse order of creation for
01533              * "enterstep" operation. The order is changed for "enterstep"
01534              * instead of for "leavestep" as was done in
01535              * TclCheckExecutionTraces because for step traces,
01536              * Tcl_CreateObjTrace creates one more linked list of traces which
01537              * results in one more reversal of trace invocation.
01538              */
01539 
01540             active.reverseScan = 1;
01541             active.nextTracePtr = NULL;
01542             tracePtr = iPtr->tracePtr;
01543             while (tracePtr->nextPtr != lastTracePtr) {
01544                 active.nextTracePtr = tracePtr;
01545                 tracePtr = tracePtr->nextPtr;
01546             }
01547             if (active.nextTracePtr) {
01548                 lastTracePtr = active.nextTracePtr->nextPtr;
01549             }
01550         } else {
01551             active.reverseScan = 0;
01552             active.nextTracePtr = tracePtr->nextPtr;
01553         }
01554 
01555         if (tracePtr->level > 0 && curLevel > tracePtr->level) {
01556             continue;
01557         }
01558 
01559         if (!(tracePtr->flags & TCL_TRACE_EXEC_IN_PROGRESS)) {
01560             /*
01561              * The proc invoked might delete the traced command which which
01562              * might try to free tracePtr. We want to use tracePtr until the
01563              * end of this if section, so we use Tcl_Preserve() and
01564              * Tcl_Release() to be sure it is not freed while we still need
01565              * it.
01566              */
01567 
01568             Tcl_Preserve((ClientData) tracePtr);
01569             tracePtr->flags |= TCL_TRACE_EXEC_IN_PROGRESS;
01570             if (state == NULL) {
01571                 state = Tcl_SaveInterpState(interp, code);
01572             }
01573 
01574             if (tracePtr->flags &
01575                     (TCL_TRACE_ENTER_EXEC | TCL_TRACE_LEAVE_EXEC)) {
01576                 /*
01577                  * New style trace.
01578                  */
01579 
01580                 if (tracePtr->flags & traceFlags) {
01581                     if (tracePtr->proc == TraceExecutionProc) {
01582                         TraceCommandInfo *tcmdPtr = (TraceCommandInfo *)
01583                                 tracePtr->clientData;
01584 
01585                         tcmdPtr->curFlags = traceFlags;
01586                         tcmdPtr->curCode = code;
01587                     }
01588                     traceCode = (tracePtr->proc)(tracePtr->clientData,
01589                             interp, curLevel, command, (Tcl_Command) cmdPtr,
01590                             objc, objv);
01591                 }
01592             } else {
01593                 /*
01594                  * Old-style trace.
01595                  */
01596 
01597                 if (traceFlags & TCL_TRACE_ENTER_EXEC) {
01598                     /*
01599                      * Old-style interpreter-wide traces only trigger before
01600                      * the command is executed.
01601                      */
01602 
01603                     traceCode = CallTraceFunction(interp, tracePtr, cmdPtr,
01604                             command, numChars, objc, objv);
01605                 }
01606             }
01607             tracePtr->flags &= ~TCL_TRACE_EXEC_IN_PROGRESS;
01608             Tcl_Release((ClientData) tracePtr);
01609         }
01610     }
01611     iPtr->activeInterpTracePtr = active.nextPtr;
01612     if (state) {
01613         if (traceCode == TCL_OK) {
01614             (void) Tcl_RestoreInterpState(interp, state);
01615         } else {
01616             Tcl_DiscardInterpState(state);
01617         }
01618     }
01619 
01620     return(traceCode);
01621 }
01622 
01623 /*
01624  *----------------------------------------------------------------------
01625  *
01626  * CallTraceFunction --
01627  *
01628  *      Invokes a trace function registered with an interpreter. These
01629  *      functions trace command execution. Currently this trace function is
01630  *      called with the address of the string-based Tcl_CmdProc for the
01631  *      command, not the Tcl_ObjCmdProc.
01632  *
01633  * Results:
01634  *      None.
01635  *
01636  * Side effects:
01637  *      Those side effects made by the trace function.
01638  *
01639  *----------------------------------------------------------------------
01640  */
01641 
01642 static int
01643 CallTraceFunction(
01644     Tcl_Interp *interp,         /* The current interpreter. */
01645     register Trace *tracePtr,   /* Describes the trace function to call. */
01646     Command *cmdPtr,            /* Points to command's Command struct. */
01647     const char *command,        /* Points to the first character of the
01648                                  * command's source before substitutions. */
01649     int numChars,               /* The number of characters in the command's
01650                                  * source. */
01651     register int objc,          /* Number of arguments for the command. */
01652     Tcl_Obj *const objv[])      /* Pointers to Tcl_Obj of each argument. */
01653 {
01654     Interp *iPtr = (Interp *) interp;
01655     char *commandCopy;
01656     int traceCode;
01657 
01658     /*
01659      * Copy the command characters into a new string.
01660      */
01661 
01662     commandCopy = TclStackAlloc(interp, (unsigned) (numChars + 1));
01663     memcpy(commandCopy, command, (size_t) numChars);
01664     commandCopy[numChars] = '\0';
01665 
01666     /*
01667      * Call the trace function then free allocated storage.
01668      */
01669 
01670     traceCode = (tracePtr->proc)(tracePtr->clientData, (Tcl_Interp *) iPtr,
01671             iPtr->numLevels, commandCopy, (Tcl_Command) cmdPtr, objc, objv);
01672 
01673     TclStackFree(interp, commandCopy);
01674     return traceCode;
01675 }
01676 
01677 /*
01678  *----------------------------------------------------------------------
01679  *
01680  * CommandObjTraceDeleted --
01681  *
01682  *      Ensure the trace is correctly deleted by decrementing its refCount and
01683  *      only deleting if no other references exist.
01684  *
01685  * Results:
01686  *      None.
01687  *
01688  * Side effects:
01689  *      May release memory.
01690  *
01691  *----------------------------------------------------------------------
01692  */
01693 
01694 static void
01695 CommandObjTraceDeleted(
01696     ClientData clientData)
01697 {
01698     TraceCommandInfo *tcmdPtr = (TraceCommandInfo *) clientData;
01699 
01700     if ((--tcmdPtr->refCount) <= 0) {
01701         ckfree((char *) tcmdPtr);
01702     }
01703 }
01704 
01705 /*
01706  *----------------------------------------------------------------------
01707  *
01708  * TraceExecutionProc --
01709  *
01710  *      This function is invoked whenever code relevant to a 'trace execution'
01711  *      command is executed. It is called in one of two ways in Tcl's core:
01712  *
01713  *      (i) by the TclCheckExecutionTraces, when an execution trace has been
01714  *      triggered.
01715  *      (ii) by TclCheckInterpTraces, when a prior execution trace has created
01716  *      a trace of the internals of a procedure, passing in this function as
01717  *      the one to be called.
01718  *
01719  * Results:
01720  *      The return value is a standard Tcl completion code such as TCL_OK or
01721  *      TCL_ERROR, etc.
01722  *
01723  * Side effects:
01724  *      May invoke an arbitrary Tcl procedure, and may create or delete an
01725  *      interpreter-wide trace.
01726  *
01727  *----------------------------------------------------------------------
01728  */
01729 
01730 static int
01731 TraceExecutionProc(
01732     ClientData clientData,
01733     Tcl_Interp *interp,
01734     int level,
01735     const char *command,
01736     Tcl_Command cmdInfo,
01737     int objc,
01738     struct Tcl_Obj *const objv[])
01739 {
01740     int call = 0;
01741     Interp *iPtr = (Interp *) interp;
01742     TraceCommandInfo *tcmdPtr = (TraceCommandInfo *) clientData;
01743     int flags = tcmdPtr->curFlags;
01744     int code = tcmdPtr->curCode;
01745     int traceCode = TCL_OK;
01746 
01747     if (tcmdPtr->flags & TCL_TRACE_EXEC_IN_PROGRESS) {
01748         /*
01749          * Inside any kind of execution trace callback, we do not allow any
01750          * further execution trace callbacks to be called for the same trace.
01751          */
01752 
01753         return traceCode;
01754     }
01755 
01756     if (!Tcl_InterpDeleted(interp) && !Tcl_LimitExceeded(interp)) {
01757         /*
01758          * Check whether the current call is going to eval arbitrary Tcl code
01759          * with a generated trace, or whether we are only going to setup
01760          * interpreter-wide traces to implement the 'step' traces. This latter
01761          * situation can happen if we create a command trace without either
01762          * before or after operations, but with either of the step operations.
01763          */
01764 
01765         if (flags & TCL_TRACE_EXEC_DIRECT) {
01766             call = flags & tcmdPtr->flags &
01767                     (TCL_TRACE_ENTER_EXEC | TCL_TRACE_LEAVE_EXEC);
01768         } else {
01769             call = 1;
01770         }
01771 
01772         /*
01773          * First, if we have returned back to the level at which we created an
01774          * interpreter trace for enterstep and/or leavestep execution traces,
01775          * we remove it here.
01776          */
01777 
01778         if ((flags & TCL_TRACE_LEAVE_EXEC) && (tcmdPtr->stepTrace != NULL)
01779                 && (level == tcmdPtr->startLevel)
01780                 && (strcmp(command, tcmdPtr->startCmd) == 0)) {
01781             Tcl_DeleteTrace(interp, tcmdPtr->stepTrace);
01782             tcmdPtr->stepTrace = NULL;
01783             if (tcmdPtr->startCmd != NULL) {
01784                 ckfree((char *) tcmdPtr->startCmd);
01785             }
01786         }
01787 
01788         /*
01789          * Second, create the tcl callback, if required.
01790          */
01791 
01792         if (call) {
01793             Tcl_DString cmd;
01794             Tcl_DString sub;
01795             int i, saveInterpFlags;
01796 
01797             Tcl_DStringInit(&cmd);
01798             Tcl_DStringAppend(&cmd, tcmdPtr->command, (int)tcmdPtr->length);
01799 
01800             /*
01801              * Append command with arguments.
01802              */
01803 
01804             Tcl_DStringInit(&sub);
01805             for (i = 0; i < objc; i++) {
01806                 Tcl_DStringAppendElement(&sub, Tcl_GetString(objv[i]));
01807             }
01808             Tcl_DStringAppendElement(&cmd, Tcl_DStringValue(&sub));
01809             Tcl_DStringFree(&sub);
01810 
01811             if (flags & TCL_TRACE_ENTER_EXEC) {
01812                 /*
01813                  * Append trace operation.
01814                  */
01815 
01816                 if (flags & TCL_TRACE_EXEC_DIRECT) {
01817                     Tcl_DStringAppendElement(&cmd, "enter");
01818                 } else {
01819                     Tcl_DStringAppendElement(&cmd, "enterstep");
01820                 }
01821             } else if (flags & TCL_TRACE_LEAVE_EXEC) {
01822                 Tcl_Obj *resultCode;
01823                 char *resultCodeStr;
01824 
01825                 /*
01826                  * Append result code.
01827                  */
01828 
01829                 resultCode = Tcl_NewIntObj(code);
01830                 resultCodeStr = Tcl_GetString(resultCode);
01831                 Tcl_DStringAppendElement(&cmd, resultCodeStr);
01832                 Tcl_DecrRefCount(resultCode);
01833 
01834                 /*
01835                  * Append result string.
01836                  */
01837 
01838                 Tcl_DStringAppendElement(&cmd, Tcl_GetStringResult(interp));
01839 
01840                 /*
01841                  * Append trace operation.
01842                  */
01843 
01844                 if (flags & TCL_TRACE_EXEC_DIRECT) {
01845                     Tcl_DStringAppendElement(&cmd, "leave");
01846                 } else {
01847                     Tcl_DStringAppendElement(&cmd, "leavestep");
01848                 }
01849             } else {
01850                 Tcl_Panic("TraceExecutionProc: bad flag combination");
01851             }
01852 
01853             /*
01854              * Execute the command. We discard any object result the command
01855              * returns.
01856              */
01857 
01858             saveInterpFlags = iPtr->flags;
01859             iPtr->flags    |= INTERP_TRACE_IN_PROGRESS;
01860             tcmdPtr->flags |= TCL_TRACE_EXEC_IN_PROGRESS;
01861             tcmdPtr->refCount++;
01862 
01863             /*
01864              * This line can have quite arbitrary side-effects, including
01865              * deleting the trace, the command being traced, or even the
01866              * interpreter.
01867              */
01868 
01869             traceCode = Tcl_Eval(interp, Tcl_DStringValue(&cmd));
01870             tcmdPtr->flags &= ~TCL_TRACE_EXEC_IN_PROGRESS;
01871 
01872             /*
01873              * Restore the interp tracing flag to prevent cmd traces from
01874              * affecting interp traces.
01875              */
01876 
01877             iPtr->flags = saveInterpFlags;
01878             if (tcmdPtr->flags == 0) {
01879                 flags |= TCL_TRACE_DESTROYED;
01880             }
01881             Tcl_DStringFree(&cmd);
01882         }
01883 
01884         /*
01885          * Third, if there are any step execution traces for this proc, we
01886          * register an interpreter trace to invoke enterstep and/or leavestep
01887          * traces. We also need to save the current stack level and the proc
01888          * string in startLevel and startCmd so that we can delete this
01889          * interpreter trace when it reaches the end of this proc.
01890          */
01891 
01892         if ((flags & TCL_TRACE_ENTER_EXEC) && (tcmdPtr->stepTrace == NULL)
01893                 && (tcmdPtr->flags & (TCL_TRACE_ENTER_DURING_EXEC |
01894                         TCL_TRACE_LEAVE_DURING_EXEC))) {
01895             register unsigned len = strlen(command) + 1;
01896 
01897             tcmdPtr->startLevel = level;
01898             tcmdPtr->startCmd = ckalloc(len);
01899             memcpy(tcmdPtr->startCmd, command, len);
01900             tcmdPtr->refCount++;
01901             tcmdPtr->stepTrace = Tcl_CreateObjTrace(interp, 0,
01902                    (tcmdPtr->flags & TCL_TRACE_ANY_EXEC) >> 2,
01903                    TraceExecutionProc, (ClientData)tcmdPtr,
01904                    CommandObjTraceDeleted);
01905         }
01906     }
01907     if (flags & TCL_TRACE_DESTROYED) {
01908         if (tcmdPtr->stepTrace != NULL) {
01909             Tcl_DeleteTrace(interp, tcmdPtr->stepTrace);
01910             tcmdPtr->stepTrace = NULL;
01911             if (tcmdPtr->startCmd != NULL) {
01912                 ckfree(tcmdPtr->startCmd);
01913             }
01914         }
01915     }
01916     if (call) {
01917         if ((--tcmdPtr->refCount) <= 0) {
01918             ckfree((char *) tcmdPtr);
01919         }
01920     }
01921     return traceCode;
01922 }
01923 
01924 /*
01925  *----------------------------------------------------------------------
01926  *
01927  * TraceVarProc --
01928  *
01929  *      This function is called to handle variable accesses that have been
01930  *      traced using the "trace" command.
01931  *
01932  * Results:
01933  *      Normally returns NULL. If the trace command returns an error, then
01934  *      this function returns an error string.
01935  *
01936  * Side effects:
01937  *      Depends on the command associated with the trace.
01938  *
01939  *----------------------------------------------------------------------
01940  */
01941 
01942         /* ARGSUSED */
01943 static char *
01944 TraceVarProc(
01945     ClientData clientData,      /* Information about the variable trace. */
01946     Tcl_Interp *interp,         /* Interpreter containing variable. */
01947     const char *name1,          /* Name of variable or array. */
01948     const char *name2,          /* Name of element within array; NULL means
01949                                  * scalar variable is being referenced. */
01950     int flags)                  /* OR-ed bits giving operation and other
01951                                  * information. */
01952 {
01953     TraceVarInfo *tvarPtr = (TraceVarInfo *) clientData;
01954     char *result;
01955     int code, destroy = 0;
01956     Tcl_DString cmd;
01957 
01958     /*
01959      * We might call Tcl_Eval() below, and that might evaluate [trace vdelete]
01960      * which might try to free tvarPtr. We want to use tvarPtr until the end
01961      * of this function, so we use Tcl_Preserve() and Tcl_Release() to be sure
01962      * it is not freed while we still need it.
01963      */
01964 
01965     result = NULL;
01966     if ((tvarPtr->flags & flags) && !Tcl_InterpDeleted(interp)
01967             && !Tcl_LimitExceeded(interp)) {
01968         if (tvarPtr->length != (size_t) 0) {
01969             /*
01970              * Generate a command to execute by appending list elements for
01971              * the two variable names and the operation.
01972              */
01973 
01974             Tcl_DStringInit(&cmd);
01975             Tcl_DStringAppend(&cmd, tvarPtr->command, (int) tvarPtr->length);
01976             Tcl_DStringAppendElement(&cmd, name1);
01977             Tcl_DStringAppendElement(&cmd, (name2 ? name2 : ""));
01978 #ifndef TCL_REMOVE_OBSOLETE_TRACES
01979             if (tvarPtr->flags & TCL_TRACE_OLD_STYLE) {
01980                 if (flags & TCL_TRACE_ARRAY) {
01981                     Tcl_DStringAppend(&cmd, " a", 2);
01982                 } else if (flags & TCL_TRACE_READS) {
01983                     Tcl_DStringAppend(&cmd, " r", 2);
01984                 } else if (flags & TCL_TRACE_WRITES) {
01985                     Tcl_DStringAppend(&cmd, " w", 2);
01986                 } else if (flags & TCL_TRACE_UNSETS) {
01987                     Tcl_DStringAppend(&cmd, " u", 2);
01988                 }
01989             } else {
01990 #endif
01991                 if (flags & TCL_TRACE_ARRAY) {
01992                     Tcl_DStringAppend(&cmd, " array", 6);
01993                 } else if (flags & TCL_TRACE_READS) {
01994                     Tcl_DStringAppend(&cmd, " read", 5);
01995                 } else if (flags & TCL_TRACE_WRITES) {
01996                     Tcl_DStringAppend(&cmd, " write", 6);
01997                 } else if (flags & TCL_TRACE_UNSETS) {
01998                     Tcl_DStringAppend(&cmd, " unset", 6);
01999                 }
02000 #ifndef TCL_REMOVE_OBSOLETE_TRACES
02001             }
02002 #endif
02003 
02004             /*
02005              * Execute the command. We discard any object result the command
02006              * returns.
02007              *
02008              * Add the TCL_TRACE_DESTROYED flag to tvarPtr to indicate to
02009              * other areas that this will be destroyed by us, otherwise a
02010              * double-free might occur depending on what the eval does.
02011              */
02012 
02013             if ((flags & TCL_TRACE_DESTROYED)
02014                     && !(tvarPtr->flags & TCL_TRACE_DESTROYED)) {
02015                 destroy = 1;
02016                 tvarPtr->flags |= TCL_TRACE_DESTROYED;
02017             }
02018             code = Tcl_EvalEx(interp, Tcl_DStringValue(&cmd),
02019                     Tcl_DStringLength(&cmd), 0);
02020             if (code != TCL_OK) {               /* copy error msg to result */
02021                 Tcl_Obj *errMsgObj = Tcl_GetObjResult(interp);
02022                 Tcl_IncrRefCount(errMsgObj);
02023                 result = (char *) errMsgObj;
02024             }
02025             Tcl_DStringFree(&cmd);
02026         }
02027     }
02028     if (destroy && result != NULL) {
02029         register Tcl_Obj *errMsgObj = (Tcl_Obj *) result;
02030 
02031         Tcl_DecrRefCount(errMsgObj);
02032         result = NULL;
02033     }
02034     return result;
02035 }
02036 
02037 /*
02038  *----------------------------------------------------------------------
02039  *
02040  * Tcl_CreateObjTrace --
02041  *
02042  *      Arrange for a function to be called to trace command execution.
02043  *
02044  * Results:
02045  *      The return value is a token for the trace, which may be passed to
02046  *      Tcl_DeleteTrace to eliminate the trace.
02047  *
02048  * Side effects:
02049  *      From now on, proc will be called just before a command function is
02050  *      called to execute a Tcl command. Calls to proc will have the following
02051  *      form:
02052  *
02053  *      void proc(ClientData     clientData,
02054  *                Tcl_Interp *   interp,
02055  *                int            level,
02056  *                const char *   command,
02057  *                Tcl_Command    commandInfo,
02058  *                int            objc,
02059  *                Tcl_Obj *const objv[]);
02060  *
02061  *      The 'clientData' and 'interp' arguments to 'proc' will be the same as
02062  *      the arguments to Tcl_CreateObjTrace. The 'level' argument gives the
02063  *      nesting depth of command interpretation within the interpreter. The
02064  *      'command' argument is the ASCII text of the command being evaluated -
02065  *      before any substitutions are performed. The 'commandInfo' argument
02066  *      gives a handle to the command procedure that will be evaluated. The
02067  *      'objc' and 'objv' parameters give the parameter vector that will be
02068  *      passed to the command procedure. Proc does not return a value.
02069  *
02070  *      It is permissible for 'proc' to call Tcl_SetCommandTokenInfo to change
02071  *      the command procedure or client data for the command being evaluated,
02072  *      and these changes will take effect with the current evaluation.
02073  *
02074  *      The 'level' argument specifies the maximum nesting level of calls to
02075  *      be traced. If the execution depth of the interpreter exceeds 'level',
02076  *      the trace callback is not executed.
02077  *
02078  *      The 'flags' argument is either zero or the value,
02079  *      TCL_ALLOW_INLINE_COMPILATION. If the TCL_ALLOW_INLINE_COMPILATION flag
02080  *      is not present, the bytecode compiler will not generate inline code
02081  *      for Tcl's built-in commands. This behavior will have a significant
02082  *      impact on performance, but will ensure that all command evaluations
02083  *      are traced. If the TCL_ALLOW_INLINE_COMPILATION flag is present, the
02084  *      bytecode compiler will have its normal behavior of compiling in-line
02085  *      code for some of Tcl's built-in commands. In this case, the tracing
02086  *      will be imprecise - in-line code will not be traced - but run-time
02087  *      performance will be improved. The latter behavior is desired for many
02088  *      applications such as profiling of run time.
02089  *
02090  *      When the trace is deleted, the 'delProc' function will be invoked,
02091  *      passing it the original client data.
02092  *
02093  *----------------------------------------------------------------------
02094  */
02095 
02096 Tcl_Trace
02097 Tcl_CreateObjTrace(
02098     Tcl_Interp *interp,         /* Tcl interpreter */
02099     int level,                  /* Maximum nesting level */
02100     int flags,                  /* Flags, see above */
02101     Tcl_CmdObjTraceProc *proc,  /* Trace callback */
02102     ClientData clientData,      /* Client data for the callback */
02103     Tcl_CmdObjTraceDeleteProc *delProc)
02104                                 /* Function to call when trace is deleted */
02105 {
02106     register Trace *tracePtr;
02107     register Interp *iPtr = (Interp *) interp;
02108 
02109     /*
02110      * Test if this trace allows inline compilation of commands.
02111      */
02112 
02113     if (!(flags & TCL_ALLOW_INLINE_COMPILATION)) {
02114         if (iPtr->tracesForbiddingInline == 0) {
02115             /*
02116              * When the first trace forbidding inline compilation is created,
02117              * invalidate existing compiled code for this interpreter and
02118              * arrange (by setting the DONT_COMPILE_CMDS_INLINE flag) that
02119              * when compiling new code, no commands will be compiled inline
02120              * (i.e., into an inline sequence of instructions). We do this
02121              * because commands that were compiled inline will never result in
02122              * a command trace being called.
02123              */
02124 
02125             iPtr->compileEpoch++;
02126             iPtr->flags |= DONT_COMPILE_CMDS_INLINE;
02127         }
02128         iPtr->tracesForbiddingInline++;
02129     }
02130 
02131     tracePtr = (Trace *) ckalloc(sizeof(Trace));
02132     tracePtr->level = level;
02133     tracePtr->proc = proc;
02134     tracePtr->clientData = clientData;
02135     tracePtr->delProc = delProc;
02136     tracePtr->nextPtr = iPtr->tracePtr;
02137     tracePtr->flags = flags;
02138     iPtr->tracePtr = tracePtr;
02139 
02140     return (Tcl_Trace) tracePtr;
02141 }
02142 
02143 /*
02144  *----------------------------------------------------------------------
02145  *
02146  * Tcl_CreateTrace --
02147  *
02148  *      Arrange for a function to be called to trace command execution.
02149  *
02150  * Results:
02151  *      The return value is a token for the trace, which may be passed to
02152  *      Tcl_DeleteTrace to eliminate the trace.
02153  *
02154  * Side effects:
02155  *      From now on, proc will be called just before a command procedure is
02156  *      called to execute a Tcl command. Calls to proc will have the following
02157  *      form:
02158  *
02159  *      void
02160  *      proc(clientData, interp, level, command, cmdProc, cmdClientData,
02161  *              argc, argv)
02162  *          ClientData clientData;
02163  *          Tcl_Interp *interp;
02164  *          int level;
02165  *          char *command;
02166  *          int (*cmdProc)();
02167  *          ClientData cmdClientData;
02168  *          int argc;
02169  *          char **argv;
02170  *      {
02171  *      }
02172  *
02173  *      The clientData and interp arguments to proc will be the same as the
02174  *      corresponding arguments to this function. Level gives the nesting
02175  *      level of command interpretation for this interpreter (0 corresponds to
02176  *      top level). Command gives the ASCII text of the raw command, cmdProc
02177  *      and cmdClientData give the function that will be called to process the
02178  *      command and the ClientData value it will receive, and argc and argv
02179  *      give the arguments to the command, after any argument parsing and
02180  *      substitution. Proc does not return a value.
02181  *
02182  *----------------------------------------------------------------------
02183  */
02184 
02185 Tcl_Trace
02186 Tcl_CreateTrace(
02187     Tcl_Interp *interp,         /* Interpreter in which to create trace. */
02188     int level,                  /* Only call proc for commands at nesting
02189                                  * level<=argument level (1=>top level). */
02190     Tcl_CmdTraceProc *proc,     /* Function to call before executing each
02191                                  * command. */
02192     ClientData clientData)      /* Arbitrary value word to pass to proc. */
02193 {
02194     StringTraceData *data = (StringTraceData *)
02195             ckalloc(sizeof(StringTraceData));
02196 
02197     data->clientData = clientData;
02198     data->proc = proc;
02199     return Tcl_CreateObjTrace(interp, level, 0, StringTraceProc,
02200             (ClientData) data, StringTraceDeleteProc);
02201 }
02202 
02203 /*
02204  *----------------------------------------------------------------------
02205  *
02206  * StringTraceProc --
02207  *
02208  *      Invoke a string-based trace function from an object-based callback.
02209  *
02210  * Results:
02211  *      None.
02212  *
02213  * Side effects:
02214  *      Whatever the string-based trace function does.
02215  *
02216  *----------------------------------------------------------------------
02217  */
02218 
02219 static int
02220 StringTraceProc(
02221     ClientData clientData,
02222     Tcl_Interp *interp,
02223     int level,
02224     const char *command,
02225     Tcl_Command commandInfo,
02226     int objc,
02227     Tcl_Obj *const *objv)
02228 {
02229     StringTraceData *data = (StringTraceData *) clientData;
02230     Command *cmdPtr = (Command *) commandInfo;
02231     const char **argv;          /* Args to pass to string trace proc */
02232     int i;
02233 
02234     /*
02235      * This is a bit messy because we have to emulate the old trace interface,
02236      * which uses strings for everything.
02237      */
02238 
02239     argv = (const char **) TclStackAlloc(interp,
02240             (unsigned) ((objc + 1) * sizeof(const char *)));
02241     for (i = 0; i < objc; i++) {
02242         argv[i] = Tcl_GetString(objv[i]);
02243     }
02244     argv[objc] = 0;
02245 
02246     /*
02247      * Invoke the command function. Note that we cast away const-ness on two
02248      * parameters for compatibility with legacy code; the code MUST NOT modify
02249      * either command or argv.
02250      */
02251 
02252     (data->proc)(data->clientData, interp, level, (char *) command,
02253             cmdPtr->proc, cmdPtr->clientData, objc, argv);
02254     TclStackFree(interp, (void *) argv);
02255 
02256     return TCL_OK;
02257 }
02258 
02259 /*
02260  *----------------------------------------------------------------------
02261  *
02262  * StringTraceDeleteProc --
02263  *
02264  *      Clean up memory when a string-based trace is deleted.
02265  *
02266  * Results:
02267  *      None.
02268  *
02269  * Side effects:
02270  *      Allocated memory is returned to the system.
02271  *
02272  *----------------------------------------------------------------------
02273  */
02274 
02275 static void
02276 StringTraceDeleteProc(
02277     ClientData clientData)
02278 {
02279     ckfree((char *) clientData);
02280 }
02281 
02282 /*
02283  *----------------------------------------------------------------------
02284  *
02285  * Tcl_DeleteTrace --
02286  *
02287  *      Remove a trace.
02288  *
02289  * Results:
02290  *      None.
02291  *
02292  * Side effects:
02293  *      From now on there will be no more calls to the function given in
02294  *      trace.
02295  *
02296  *----------------------------------------------------------------------
02297  */
02298 
02299 void
02300 Tcl_DeleteTrace(
02301     Tcl_Interp *interp,         /* Interpreter that contains trace. */
02302     Tcl_Trace trace)            /* Token for trace (returned previously by
02303                                  * Tcl_CreateTrace). */
02304 {
02305     Interp *iPtr = (Interp *) interp;
02306     Trace *prevPtr, *tracePtr = (Trace *) trace;
02307     register Trace **tracePtr2 = &(iPtr->tracePtr);
02308     ActiveInterpTrace *activePtr;
02309 
02310     /*
02311      * Locate the trace entry in the interpreter's trace list, and remove it
02312      * from the list.
02313      */
02314 
02315     prevPtr = NULL;
02316     while ((*tracePtr2) != NULL && (*tracePtr2) != tracePtr) {
02317         prevPtr = *tracePtr2;
02318         tracePtr2 = &((*tracePtr2)->nextPtr);
02319     }
02320     if (*tracePtr2 == NULL) {
02321         return;
02322     }
02323     (*tracePtr2) = (*tracePtr2)->nextPtr;
02324 
02325     /*
02326      * The code below makes it possible to delete traces while traces are
02327      * active: it makes sure that the deleted trace won't be processed by
02328      * TclCheckInterpTraces.
02329      */
02330 
02331     for (activePtr = iPtr->activeInterpTracePtr;  activePtr != NULL;
02332             activePtr = activePtr->nextPtr) {
02333         if (activePtr->nextTracePtr == tracePtr) {
02334             if (activePtr->reverseScan) {
02335                 activePtr->nextTracePtr = prevPtr;
02336             } else {
02337                 activePtr->nextTracePtr = tracePtr->nextPtr;
02338             }
02339         }
02340     }
02341 
02342     /*
02343      * If the trace forbids bytecode compilation, change the interpreter's
02344      * state. If bytecode compilation is now permitted, flag the fact and
02345      * advance the compilation epoch so that procs will be recompiled to take
02346      * advantage of it.
02347      */
02348 
02349     if (!(tracePtr->flags & TCL_ALLOW_INLINE_COMPILATION)) {
02350         iPtr->tracesForbiddingInline--;
02351         if (iPtr->tracesForbiddingInline == 0) {
02352             iPtr->flags &= ~DONT_COMPILE_CMDS_INLINE;
02353             iPtr->compileEpoch++;
02354         }
02355     }
02356 
02357     /*
02358      * Execute any delete callback.
02359      */
02360 
02361     if (tracePtr->delProc != NULL) {
02362         (tracePtr->delProc)(tracePtr->clientData);
02363     }
02364 
02365     /*
02366      * Delete the trace object.
02367      */
02368 
02369     Tcl_EventuallyFree((char *) tracePtr, TCL_DYNAMIC);
02370 }
02371 
02372 /*
02373  *----------------------------------------------------------------------
02374  *
02375  * TclTraceVarExists --
02376  *
02377  *      This is called from info exists. We need to trigger read and/or array
02378  *      traces because they may end up creating a variable that doesn't
02379  *      currently exist.
02380  *
02381  * Results:
02382  *      A pointer to the Var structure, or NULL.
02383  *
02384  * Side effects:
02385  *      May fill in error messages in the interp.
02386  *
02387  *----------------------------------------------------------------------
02388  */
02389 
02390 Var *
02391 TclVarTraceExists(
02392     Tcl_Interp *interp,         /* The interpreter */
02393     const char *varName)        /* The variable name */
02394 {
02395     Var *varPtr;
02396     Var *arrayPtr;
02397 
02398     /*
02399      * The choice of "create" flag values is delicate here, and matches the
02400      * semantics of GetVar. Things are still not perfect, however, because if
02401      * you do "info exists x" you get a varPtr and therefore trigger traces.
02402      * However, if you do "info exists x(i)", then you only get a varPtr if x
02403      * is already known to be an array. Otherwise you get NULL, and no trace
02404      * is triggered. This matches Tcl 7.6 semantics.
02405      */
02406 
02407     varPtr = TclLookupVar(interp, varName, NULL, 0, "access",
02408             /*createPart1*/ 0, /*createPart2*/ 1, &arrayPtr);
02409 
02410     if (varPtr == NULL) {
02411         return NULL;
02412     }
02413 
02414     if ((varPtr->flags & VAR_TRACED_READ)
02415             || (arrayPtr && (arrayPtr->flags & VAR_TRACED_READ))) {
02416         TclCallVarTraces((Interp *)interp, arrayPtr, varPtr, varName, NULL,
02417                 TCL_TRACE_READS, /* leaveErrMsg */ 0);
02418     }
02419 
02420     /*
02421      * If the variable doesn't exist anymore and no-one's using it, then free
02422      * up the relevant structures and hash table entries.
02423      */
02424 
02425     if (TclIsVarUndefined(varPtr)) {
02426         TclCleanupVar(varPtr, arrayPtr);
02427         return NULL;
02428     }
02429 
02430     return varPtr;
02431 }
02432 
02433 /*
02434  *----------------------------------------------------------------------
02435  *
02436  * TclCallVarTraces --
02437  *
02438  *      This function is invoked to find and invoke relevant trace functions
02439  *      associated with a particular operation on a variable. This function
02440  *      invokes traces both on the variable and on its containing array (where
02441  *      relevant).
02442  *
02443  * Results:
02444  *      Returns TCL_OK to indicate normal operation. Returns TCL_ERROR if
02445  *      invocation of a trace function indicated an error. When TCL_ERROR is
02446  *      returned and leaveErrMsg is true, then the errorInfo field of iPtr has
02447  *      information about the error placed in it.
02448  *
02449  * Side effects:
02450  *      Almost anything can happen, depending on trace; this function itself
02451  *      doesn't have any side effects.
02452  *
02453  *----------------------------------------------------------------------
02454  */
02455 
02456 int
02457 TclObjCallVarTraces(
02458     Interp *iPtr,               /* Interpreter containing variable. */
02459     register Var *arrayPtr,     /* Pointer to array variable that contains the
02460                                  * variable, or NULL if the variable isn't an
02461                                  * element of an array. */
02462     Var *varPtr,                /* Variable whose traces are to be invoked. */
02463     Tcl_Obj *part1Ptr,
02464     Tcl_Obj *part2Ptr,          /* Variable's two-part name. */
02465     int flags,                  /* Flags passed to trace functions: indicates
02466                                  * what's happening to variable, plus maybe
02467                                  * TCL_GLOBAL_ONLY or TCL_NAMESPACE_ONLY */
02468     int leaveErrMsg,            /* If true, and one of the traces indicates an
02469                                  * error, then leave an error message and
02470                                  * stack trace information in *iPTr. */
02471     int index)                  /* Index into the local variable table of the
02472                                  * variable, or -1. Only used when part1Ptr is
02473                                  * NULL. */
02474 {
02475     char *part1, *part2;
02476 
02477     if (!part1Ptr) {
02478         part1Ptr = localName(iPtr->varFramePtr, index);
02479     }
02480     part1 = TclGetString(part1Ptr);
02481     part2 = part2Ptr? TclGetString(part2Ptr) : NULL;
02482 
02483     return TclCallVarTraces(iPtr, arrayPtr, varPtr, part1, part2, flags,
02484             leaveErrMsg);
02485 }
02486 
02487 int
02488 TclCallVarTraces(
02489     Interp *iPtr,               /* Interpreter containing variable. */
02490     register Var *arrayPtr,     /* Pointer to array variable that contains the
02491                                  * variable, or NULL if the variable isn't an
02492                                  * element of an array. */
02493     Var *varPtr,                /* Variable whose traces are to be invoked. */
02494     const char *part1,
02495     const char *part2,          /* Variable's two-part name. */
02496     int flags,                  /* Flags passed to trace functions: indicates
02497                                  * what's happening to variable, plus maybe
02498                                  * TCL_GLOBAL_ONLY or TCL_NAMESPACE_ONLY */
02499     int leaveErrMsg)            /* If true, and one of the traces indicates an
02500                                  * error, then leave an error message and
02501                                  * stack trace information in *iPTr. */
02502 {
02503     register VarTrace *tracePtr;
02504     ActiveVarTrace active;
02505     char *result;
02506     const char *openParen, *p;
02507     Tcl_DString nameCopy;
02508     int copiedName;
02509     int code = TCL_OK;
02510     int disposeFlags = 0;
02511     Tcl_InterpState state = NULL;
02512     Tcl_HashEntry *hPtr;
02513     int traceflags = flags & VAR_ALL_TRACES;
02514 
02515     /*
02516      * If there are already similar trace functions active for the variable,
02517      * don't call them again.
02518      */
02519 
02520     if (TclIsVarTraceActive(varPtr)) {
02521         return code;
02522     }
02523     TclSetVarTraceActive(varPtr);
02524     if (TclIsVarInHash(varPtr)) {
02525         VarHashRefCount(varPtr)++;
02526     }
02527     if (arrayPtr && TclIsVarInHash(arrayPtr)) {
02528         VarHashRefCount(arrayPtr)++;
02529     }
02530 
02531     /*
02532      * If the variable name hasn't been parsed into array name and element, do
02533      * it here. If there really is an array element, make a copy of the
02534      * original name so that NULLs can be inserted into it to separate the
02535      * names (can't modify the name string in place, because the string might
02536      * get used by the callbacks we invoke).
02537      */
02538 
02539     copiedName = 0;
02540     if (part2 == NULL) {
02541         for (p = part1; *p ; p++) {
02542             if (*p == '(') {
02543                 openParen = p;
02544                 do {
02545                     p++;
02546                 } while (*p != '\0');
02547                 p--;
02548                 if (*p == ')') {
02549                     int offset = (openParen - part1);
02550                     char *newPart1;
02551 
02552                     Tcl_DStringInit(&nameCopy);
02553                     Tcl_DStringAppend(&nameCopy, part1, (p-part1));
02554                     newPart1 = Tcl_DStringValue(&nameCopy);
02555                     newPart1[offset] = 0;
02556                     part1 = newPart1;
02557                     part2 = newPart1 + offset + 1;
02558                     copiedName = 1;
02559                 }
02560                 break;
02561             }
02562         }
02563     }
02564 
02565     /*
02566      * Ignore any caller-provided TCL_INTERP_DESTROYED flag.  Only we can
02567      * set it correctly.
02568      */
02569 
02570     flags &= ~TCL_INTERP_DESTROYED;
02571 
02572     /*
02573      * Invoke traces on the array containing the variable, if relevant.
02574      */
02575 
02576     result = NULL;
02577     active.nextPtr = iPtr->activeVarTracePtr;
02578     iPtr->activeVarTracePtr = &active;
02579     Tcl_Preserve((ClientData) iPtr);
02580     if (arrayPtr && !TclIsVarTraceActive(arrayPtr)
02581             && (arrayPtr->flags & traceflags)) {
02582         hPtr = Tcl_FindHashEntry(&iPtr->varTraces, (char *) arrayPtr);
02583         active.varPtr = arrayPtr;
02584         for (tracePtr = (VarTrace *) Tcl_GetHashValue(hPtr);
02585              tracePtr != NULL; tracePtr = active.nextTracePtr) {
02586             active.nextTracePtr = tracePtr->nextPtr;
02587             if (!(tracePtr->flags & flags)) {
02588                 continue;
02589             }
02590             Tcl_Preserve((ClientData) tracePtr);
02591             if (state == NULL) {
02592                 state = Tcl_SaveInterpState((Tcl_Interp *)iPtr, code);
02593             }
02594             if (Tcl_InterpDeleted((Tcl_Interp *)iPtr)) {
02595                 flags |= TCL_INTERP_DESTROYED;
02596             }
02597             result = (*tracePtr->traceProc)(tracePtr->clientData,
02598                     (Tcl_Interp *) iPtr, part1, part2, flags);
02599             if (result != NULL) {
02600                 if (flags & TCL_TRACE_UNSETS) {
02601                     /*
02602                      * Ignore errors in unset traces.
02603                      */
02604 
02605                     DisposeTraceResult(tracePtr->flags, result);
02606                 } else {
02607                     disposeFlags = tracePtr->flags;
02608                     code = TCL_ERROR;
02609                 }
02610             }
02611             Tcl_Release((ClientData) tracePtr);
02612             if (code == TCL_ERROR) {
02613                 goto done;
02614             }
02615         }
02616     }
02617 
02618     /*
02619      * Invoke traces on the variable itself.
02620      */
02621 
02622     if (flags & TCL_TRACE_UNSETS) {
02623         flags |= TCL_TRACE_DESTROYED;
02624     }
02625     active.varPtr = varPtr;
02626     if (varPtr->flags & traceflags) {
02627         hPtr = Tcl_FindHashEntry(&iPtr->varTraces, (char *) varPtr);
02628         for (tracePtr = (VarTrace *) Tcl_GetHashValue(hPtr);
02629              tracePtr != NULL; tracePtr = active.nextTracePtr) {
02630             active.nextTracePtr = tracePtr->nextPtr;
02631             if (!(tracePtr->flags & flags)) {
02632                 continue;
02633             }
02634             Tcl_Preserve((ClientData) tracePtr);
02635             if (state == NULL) {
02636                 state = Tcl_SaveInterpState((Tcl_Interp *)iPtr, code);
02637             }
02638             if (Tcl_InterpDeleted((Tcl_Interp *)iPtr)) {
02639                 flags |= TCL_INTERP_DESTROYED;
02640             }
02641             result = (*tracePtr->traceProc)(tracePtr->clientData,
02642                     (Tcl_Interp *) iPtr, part1, part2, flags);
02643             if (result != NULL) {
02644                 if (flags & TCL_TRACE_UNSETS) {
02645                     /*
02646                      * Ignore errors in unset traces.
02647                      */
02648 
02649                     DisposeTraceResult(tracePtr->flags, result);
02650                 } else {
02651                     disposeFlags = tracePtr->flags;
02652                     code = TCL_ERROR;
02653                 }
02654             }
02655             Tcl_Release((ClientData) tracePtr);
02656             if (code == TCL_ERROR) {
02657                 goto done;
02658             }
02659         }
02660     }
02661 
02662     /*
02663      * Restore the variable's flags, remove the record of our active traces,
02664      * and then return.
02665      */
02666 
02667   done:
02668     if (code == TCL_ERROR) {
02669         if (leaveErrMsg) {
02670             const char *type = "";
02671             Tcl_Obj *options = Tcl_GetReturnOptions((Tcl_Interp *)iPtr, code);
02672             Tcl_Obj *errorInfoKey, *errorInfo;
02673 
02674             TclNewLiteralStringObj(errorInfoKey, "-errorinfo");
02675             Tcl_IncrRefCount(errorInfoKey);
02676             Tcl_DictObjGet(NULL, options, errorInfoKey, &errorInfo);
02677             Tcl_IncrRefCount(errorInfo);
02678             Tcl_DictObjRemove(NULL, options, errorInfoKey);
02679             if (Tcl_IsShared(errorInfo)) {
02680                 Tcl_DecrRefCount(errorInfo);
02681                 errorInfo = Tcl_DuplicateObj(errorInfo);
02682                 Tcl_IncrRefCount(errorInfo);
02683             }
02684             Tcl_AppendToObj(errorInfo, "\n    (", -1);
02685             switch (flags&(TCL_TRACE_READS|TCL_TRACE_WRITES|TCL_TRACE_ARRAY)) {
02686             case TCL_TRACE_READS:
02687                 type = "read";
02688                 Tcl_AppendToObj(errorInfo, type, -1);
02689                 break;
02690             case TCL_TRACE_WRITES:
02691                 type = "set";
02692                 Tcl_AppendToObj(errorInfo, "write", -1);
02693                 break;
02694             case TCL_TRACE_ARRAY:
02695                 type = "trace array";
02696                 Tcl_AppendToObj(errorInfo, "array", -1);
02697                 break;
02698             }
02699             if (disposeFlags & TCL_TRACE_RESULT_OBJECT) {
02700                 TclVarErrMsg((Tcl_Interp *) iPtr, part1, part2, type,
02701                         Tcl_GetString((Tcl_Obj *) result));
02702             } else {
02703                 TclVarErrMsg((Tcl_Interp *) iPtr, part1, part2, type, result);
02704             }
02705             Tcl_AppendToObj(errorInfo, " trace on \"", -1);
02706             Tcl_AppendToObj(errorInfo, part1, -1);
02707             if (part2 != NULL) {
02708                 Tcl_AppendToObj(errorInfo, "(", -1);
02709                 Tcl_AppendToObj(errorInfo, part1, -1);
02710                 Tcl_AppendToObj(errorInfo, ")", -1);
02711             }
02712             Tcl_AppendToObj(errorInfo, "\")", -1);
02713             Tcl_DictObjPut(NULL, options, errorInfoKey, errorInfo);
02714             Tcl_DecrRefCount(errorInfoKey);
02715             Tcl_DecrRefCount(errorInfo);
02716             code = Tcl_SetReturnOptions((Tcl_Interp *)iPtr, options);
02717             iPtr->flags &= ~(ERR_ALREADY_LOGGED);
02718             Tcl_DiscardInterpState(state);
02719         } else {
02720             (void) Tcl_RestoreInterpState((Tcl_Interp *)iPtr, state);
02721         }
02722         DisposeTraceResult(disposeFlags,result);
02723     } else if (state) {
02724         if (code == TCL_OK) {
02725             code = Tcl_RestoreInterpState((Tcl_Interp *)iPtr, state);
02726         } else {
02727             Tcl_DiscardInterpState(state);
02728         }
02729     }
02730 
02731     if (arrayPtr && TclIsVarInHash(arrayPtr)) {
02732         VarHashRefCount(arrayPtr)--;
02733     }
02734     if (copiedName) {
02735         Tcl_DStringFree(&nameCopy);
02736     }
02737     TclClearVarTraceActive(varPtr);
02738     if (TclIsVarInHash(varPtr)) {
02739         VarHashRefCount(varPtr)--;
02740     }
02741     iPtr->activeVarTracePtr = active.nextPtr;
02742     Tcl_Release((ClientData) iPtr);
02743     return code;
02744 }
02745 
02746 /*
02747  *----------------------------------------------------------------------
02748  *
02749  * DisposeTraceResult--
02750  *
02751  *      This function is called to dispose of the result returned from a trace
02752  *      function. The disposal method appropriate to the type of result is
02753  *      determined by flags.
02754  *
02755  * Results:
02756  *      None.
02757  *
02758  * Side effects:
02759  *      The memory allocated for the trace result may be freed.
02760  *
02761  *----------------------------------------------------------------------
02762  */
02763 
02764 static void
02765 DisposeTraceResult(
02766     int flags,                  /* Indicates type of result to determine
02767                                  * proper disposal method. */
02768     char *result)               /* The result returned from a trace function
02769                                  * to be disposed. */
02770 {
02771     if (flags & TCL_TRACE_RESULT_DYNAMIC) {
02772         ckfree(result);
02773     } else if (flags & TCL_TRACE_RESULT_OBJECT) {
02774         Tcl_DecrRefCount((Tcl_Obj *) result);
02775     }
02776 }
02777 
02778 /*
02779  *----------------------------------------------------------------------
02780  *
02781  * Tcl_UntraceVar --
02782  *
02783  *      Remove a previously-created trace for a variable.
02784  *
02785  * Results:
02786  *      None.
02787  *
02788  * Side effects:
02789  *      If there exists a trace for the variable given by varName with the
02790  *      given flags, proc, and clientData, then that trace is removed.
02791  *
02792  *----------------------------------------------------------------------
02793  */
02794 
02795 void
02796 Tcl_UntraceVar(
02797     Tcl_Interp *interp,         /* Interpreter containing variable. */
02798     const char *varName,        /* Name of variable; may end with "(index)" to
02799                                  * signify an array reference. */
02800     int flags,                  /* OR-ed collection of bits describing current
02801                                  * trace, including any of TCL_TRACE_READS,
02802                                  * TCL_TRACE_WRITES, TCL_TRACE_UNSETS,
02803                                  * TCL_GLOBAL_ONLY and TCL_NAMESPACE_ONLY. */
02804     Tcl_VarTraceProc *proc,     /* Function assocated with trace. */
02805     ClientData clientData)      /* Arbitrary argument to pass to proc. */
02806 {
02807     Tcl_UntraceVar2(interp, varName, NULL, flags, proc, clientData);
02808 }
02809 
02810 /*
02811  *----------------------------------------------------------------------
02812  *
02813  * Tcl_UntraceVar2 --
02814  *
02815  *      Remove a previously-created trace for a variable.
02816  *
02817  * Results:
02818  *      None.
02819  *
02820  * Side effects:
02821  *      If there exists a trace for the variable given by part1 and part2 with
02822  *      the given flags, proc, and clientData, then that trace is removed.
02823  *
02824  *----------------------------------------------------------------------
02825  */
02826 
02827 void
02828 Tcl_UntraceVar2(
02829     Tcl_Interp *interp,         /* Interpreter containing variable. */
02830     const char *part1,          /* Name of variable or array. */
02831     const char *part2,          /* Name of element within array; NULL means
02832                                  * trace applies to scalar variable or array
02833                                  * as-a-whole. */
02834     int flags,                  /* OR-ed collection of bits describing current
02835                                  * trace, including any of TCL_TRACE_READS,
02836                                  * TCL_TRACE_WRITES, TCL_TRACE_UNSETS,
02837                                  * TCL_GLOBAL_ONLY, and TCL_NAMESPACE_ONLY. */
02838     Tcl_VarTraceProc *proc,     /* Function assocated with trace. */
02839     ClientData clientData)      /* Arbitrary argument to pass to proc. */
02840 {
02841     register VarTrace *tracePtr;
02842     VarTrace *prevPtr, *nextPtr;
02843     Var *varPtr, *arrayPtr;
02844     Interp *iPtr = (Interp *) interp;
02845     ActiveVarTrace *activePtr;
02846     int flagMask, allFlags = 0;
02847     Tcl_HashEntry *hPtr;
02848 
02849     /*
02850      * Set up a mask to mask out the parts of the flags that we are not
02851      * interested in now.
02852      */
02853 
02854     flagMask = TCL_GLOBAL_ONLY | TCL_NAMESPACE_ONLY;
02855     varPtr = TclLookupVar(interp, part1, part2, flags & flagMask, /*msg*/ NULL,
02856             /*createPart1*/ 0, /*createPart2*/ 0, &arrayPtr);
02857     if (varPtr == NULL || !(varPtr->flags & VAR_ALL_TRACES & flags)) {
02858         return;
02859     }
02860 
02861     /*
02862      * Set up a mask to mask out the parts of the flags that we are not
02863      * interested in now.
02864      */
02865 
02866     flagMask = TCL_TRACE_READS | TCL_TRACE_WRITES | TCL_TRACE_UNSETS |
02867           TCL_TRACE_ARRAY | TCL_TRACE_RESULT_DYNAMIC | TCL_TRACE_RESULT_OBJECT;
02868 #ifndef TCL_REMOVE_OBSOLETE_TRACES
02869     flagMask |= TCL_TRACE_OLD_STYLE;
02870 #endif
02871     flags &= flagMask;
02872 
02873     hPtr = Tcl_FindHashEntry(&iPtr->varTraces,
02874             (char *) varPtr);
02875     for (tracePtr = (VarTrace *) Tcl_GetHashValue(hPtr), prevPtr = NULL; ;
02876             prevPtr = tracePtr, tracePtr = tracePtr->nextPtr) {
02877         if (tracePtr == NULL) {
02878             goto updateFlags;
02879         }
02880         if ((tracePtr->traceProc == proc) && (tracePtr->flags == flags)
02881                 && (tracePtr->clientData == clientData)) {
02882             break;
02883         }
02884         allFlags |= tracePtr->flags;
02885     }
02886 
02887     /*
02888      * The code below makes it possible to delete traces while traces are
02889      * active: it makes sure that the deleted trace won't be processed by
02890      * TclCallVarTraces.
02891      */
02892 
02893     for (activePtr = iPtr->activeVarTracePtr;  activePtr != NULL;
02894             activePtr = activePtr->nextPtr) {
02895         if (activePtr->nextTracePtr == tracePtr) {
02896             activePtr->nextTracePtr = tracePtr->nextPtr;
02897         }
02898     }
02899     nextPtr = tracePtr->nextPtr;
02900     if (prevPtr == NULL) {
02901         if (nextPtr) {
02902             Tcl_SetHashValue(hPtr, nextPtr);
02903         } else {
02904             Tcl_DeleteHashEntry(hPtr);
02905         }
02906     } else {
02907         prevPtr->nextPtr = nextPtr;
02908     }
02909     Tcl_EventuallyFree((ClientData) tracePtr, TCL_DYNAMIC);
02910 
02911     for (tracePtr = nextPtr; tracePtr != NULL;
02912             tracePtr = tracePtr->nextPtr) {
02913         allFlags |= tracePtr->flags;
02914     }
02915 
02916   updateFlags:
02917     varPtr->flags &= ~VAR_ALL_TRACES;
02918     if (allFlags & VAR_ALL_TRACES) {
02919         varPtr->flags |= (allFlags & VAR_ALL_TRACES);
02920     } else if (TclIsVarUndefined(varPtr)) {
02921         /*
02922          * If this is the last trace on the variable, and the variable is
02923          * unset and unused, then free up the variable.
02924          */
02925 
02926         TclCleanupVar(varPtr, NULL);
02927     }
02928 }
02929 
02930 /*
02931  *----------------------------------------------------------------------
02932  *
02933  * Tcl_VarTraceInfo --
02934  *
02935  *      Return the clientData value associated with a trace on a variable.
02936  *      This function can also be used to step through all of the traces on a
02937  *      particular variable that have the same trace function.
02938  *
02939  * Results:
02940  *      The return value is the clientData value associated with a trace on
02941  *      the given variable. Information will only be returned for a trace with
02942  *      proc as trace function. If the clientData argument is NULL then the
02943  *      first such trace is returned; otherwise, the next relevant one after
02944  *      the one given by clientData will be returned. If the variable doesn't
02945  *      exist, or if there are no (more) traces for it, then NULL is returned.
02946  *
02947  * Side effects:
02948  *      None.
02949  *
02950  *----------------------------------------------------------------------
02951  */
02952 
02953 ClientData
02954 Tcl_VarTraceInfo(
02955     Tcl_Interp *interp,         /* Interpreter containing variable. */
02956     const char *varName,        /* Name of variable; may end with "(index)" to
02957                                  * signify an array reference. */
02958     int flags,                  /* OR-ed combo or TCL_GLOBAL_ONLY,
02959                                  * TCL_NAMESPACE_ONLY (can be 0). */
02960     Tcl_VarTraceProc *proc,     /* Function assocated with trace. */
02961     ClientData prevClientData)  /* If non-NULL, gives last value returned by
02962                                  * this function, so this call will return the
02963                                  * next trace after that one. If NULL, this
02964                                  * call will return the first trace. */
02965 {
02966     return Tcl_VarTraceInfo2(interp, varName, NULL, flags, proc,
02967             prevClientData);
02968 }
02969 
02970 /*
02971  *----------------------------------------------------------------------
02972  *
02973  * Tcl_VarTraceInfo2 --
02974  *
02975  *      Same as Tcl_VarTraceInfo, except takes name in two pieces instead of
02976  *      one.
02977  *
02978  * Results:
02979  *      Same as Tcl_VarTraceInfo.
02980  *
02981  * Side effects:
02982  *      None.
02983  *
02984  *----------------------------------------------------------------------
02985  */
02986 
02987 ClientData
02988 Tcl_VarTraceInfo2(
02989     Tcl_Interp *interp,         /* Interpreter containing variable. */
02990     const char *part1,          /* Name of variable or array. */
02991     const char *part2,          /* Name of element within array; NULL means
02992                                  * trace applies to scalar variable or array
02993                                  * as-a-whole. */
02994     int flags,                  /* OR-ed combination of TCL_GLOBAL_ONLY,
02995                                  * TCL_NAMESPACE_ONLY. */
02996     Tcl_VarTraceProc *proc,     /* Function assocated with trace. */
02997     ClientData prevClientData)  /* If non-NULL, gives last value returned by
02998                                  * this function, so this call will return the
02999                                  * next trace after that one. If NULL, this
03000                                  * call will return the first trace. */
03001 {
03002     Interp *iPtr = (Interp *) interp;
03003     register VarTrace *tracePtr;
03004     Var *varPtr, *arrayPtr;
03005     Tcl_HashEntry *hPtr;
03006 
03007     varPtr = TclLookupVar(interp, part1, part2,
03008             flags & (TCL_GLOBAL_ONLY|TCL_NAMESPACE_ONLY), /*msg*/ NULL,
03009             /*createPart1*/ 0, /*createPart2*/ 0, &arrayPtr);
03010     if (varPtr == NULL) {
03011         return NULL;
03012     }
03013 
03014     /*
03015      * Find the relevant trace, if any, and return its clientData.
03016      */
03017 
03018     hPtr = Tcl_FindHashEntry(&iPtr->varTraces,
03019             (char *) varPtr);
03020 
03021     if (hPtr) {
03022         tracePtr = Tcl_GetHashValue(hPtr);
03023 
03024         if (prevClientData != NULL) {
03025             for ( ;  tracePtr != NULL;  tracePtr = tracePtr->nextPtr) {
03026                 if ((tracePtr->clientData == prevClientData)
03027                         && (tracePtr->traceProc == proc)) {
03028                     tracePtr = tracePtr->nextPtr;
03029                     break;
03030                 }
03031             }
03032         }
03033         for (; tracePtr!=NULL ; tracePtr=tracePtr->nextPtr) {
03034             if (tracePtr->traceProc == proc) {
03035                 return tracePtr->clientData;
03036             }
03037         }
03038     }
03039     return NULL;
03040 }
03041 
03042 /*
03043  *----------------------------------------------------------------------
03044  *
03045  * Tcl_TraceVar --
03046  *
03047  *      Arrange for reads and/or writes to a variable to cause a function to
03048  *      be invoked, which can monitor the operations and/or change their
03049  *      actions.
03050  *
03051  * Results:
03052  *      A standard Tcl return value.
03053  *
03054  * Side effects:
03055  *      A trace is set up on the variable given by varName, such that future
03056  *      references to the variable will be intermediated by proc. See the
03057  *      manual entry for complete details on the calling sequence for proc.
03058  *     The variable's flags are updated.
03059  *
03060  *----------------------------------------------------------------------
03061  */
03062 
03063 int
03064 Tcl_TraceVar(
03065     Tcl_Interp *interp,         /* Interpreter in which variable is to be
03066                                  * traced. */
03067     const char *varName,        /* Name of variable; may end with "(index)" to
03068                                  * signify an array reference. */
03069     int flags,                  /* OR-ed collection of bits, including any of
03070                                  * TCL_TRACE_READS, TCL_TRACE_WRITES,
03071                                  * TCL_TRACE_UNSETS, TCL_GLOBAL_ONLY, and
03072                                  * TCL_NAMESPACE_ONLY. */
03073     Tcl_VarTraceProc *proc,     /* Function to call when specified ops are
03074                                  * invoked upon varName. */
03075     ClientData clientData)      /* Arbitrary argument to pass to proc. */
03076 {
03077     return Tcl_TraceVar2(interp, varName, NULL, flags, proc, clientData);
03078 }
03079 
03080 /*
03081  *----------------------------------------------------------------------
03082  *
03083  * Tcl_TraceVar2 --
03084  *
03085  *      Arrange for reads and/or writes to a variable to cause a function to
03086  *      be invoked, which can monitor the operations and/or change their
03087  *      actions.
03088  *
03089  * Results:
03090  *      A standard Tcl return value.
03091  *
03092  * Side effects:
03093  *      A trace is set up on the variable given by part1 and part2, such that
03094  *      future references to the variable will be intermediated by proc. See
03095  *      the manual entry for complete details on the calling sequence for
03096  *      proc. The variable's flags are updated.
03097  *
03098  *----------------------------------------------------------------------
03099  */
03100 
03101 int
03102 Tcl_TraceVar2(
03103     Tcl_Interp *interp,         /* Interpreter in which variable is to be
03104                                  * traced. */
03105     const char *part1,          /* Name of scalar variable or array. */
03106     const char *part2,          /* Name of element within array; NULL means
03107                                  * trace applies to scalar variable or array
03108                                  * as-a-whole. */
03109     int flags,                  /* OR-ed collection of bits, including any of
03110                                  * TCL_TRACE_READS, TCL_TRACE_WRITES,
03111                                  * TCL_TRACE_UNSETS, TCL_GLOBAL_ONLY, and
03112                                  * TCL_NAMESPACE_ONLY. */
03113     Tcl_VarTraceProc *proc,     /* Function to call when specified ops are
03114                                  * invoked upon varName. */
03115     ClientData clientData)      /* Arbitrary argument to pass to proc. */
03116 {
03117     register VarTrace *tracePtr;
03118     int result;
03119 
03120     tracePtr = (VarTrace *) ckalloc(sizeof(VarTrace));
03121     tracePtr->traceProc = proc;
03122     tracePtr->clientData = clientData;
03123     tracePtr->flags = flags;
03124 
03125     result = TraceVarEx(interp, part1, part2, tracePtr);
03126 
03127     if (result != TCL_OK) {
03128         ckfree((char *) tracePtr);
03129     }
03130     return result;
03131 }
03132 
03133 /*
03134  *----------------------------------------------------------------------
03135  *
03136  * TraceVarEx --
03137  *
03138  *      Arrange for reads and/or writes to a variable to cause a function to
03139  *      be invoked, which can monitor the operations and/or change their
03140  *      actions.
03141  *
03142  * Results:
03143  *      A standard Tcl return value.
03144  *
03145  * Side effects:
03146  *      A trace is set up on the variable given by part1 and part2, such that
03147  *      future references to the variable will be intermediated by the
03148  *      traceProc listed in tracePtr. See the manual entry for complete
03149  *      details on the calling sequence for proc.
03150  *
03151  *----------------------------------------------------------------------
03152  */
03153 
03154 static int
03155 TraceVarEx(
03156     Tcl_Interp *interp,         /* Interpreter in which variable is to be
03157                                  * traced. */
03158     const char *part1,          /* Name of scalar variable or array. */
03159     const char *part2,          /* Name of element within array; NULL means
03160                                  * trace applies to scalar variable or array
03161                                  * as-a-whole. */
03162     register VarTrace *tracePtr)/* Structure containing flags, traceProc and
03163                                  * clientData fields. Others should be left
03164                                  * blank. Will be ckfree()d (eventually) if
03165                                  * this function returns TCL_OK, and up to
03166                                  * caller to free if this function returns
03167                                  * TCL_ERROR. */
03168 {
03169     Interp *iPtr = (Interp *) interp;
03170     Var *varPtr, *arrayPtr;
03171     int flagMask, isNew;
03172     Tcl_HashEntry *hPtr;
03173 
03174     /*
03175      * We strip 'flags' down to just the parts which are relevant to
03176      * TclLookupVar, to avoid conflicts between trace flags and internal
03177      * namespace flags such as 'TCL_FIND_ONLY_NS'. This can now occur since we
03178      * have trace flags with values 0x1000 and higher.
03179      */
03180 
03181     flagMask = TCL_GLOBAL_ONLY | TCL_NAMESPACE_ONLY;
03182     varPtr = TclLookupVar(interp, part1, part2,
03183             (tracePtr->flags & flagMask) | TCL_LEAVE_ERR_MSG,
03184             "trace", /*createPart1*/ 1, /*createPart2*/ 1, &arrayPtr);
03185     if (varPtr == NULL) {
03186         return TCL_ERROR;
03187     }
03188 
03189     /*
03190      * Check for a nonsense flag combination. Note that this is a Tcl_Panic()
03191      * because there should be no code path that ever sets both flags.
03192      */
03193 
03194     if ((tracePtr->flags&TCL_TRACE_RESULT_DYNAMIC)
03195             && (tracePtr->flags&TCL_TRACE_RESULT_OBJECT)) {
03196         Tcl_Panic("bad result flag combination");
03197     }
03198 
03199     /*
03200      * Set up trace information.
03201      */
03202 
03203     flagMask = TCL_TRACE_READS | TCL_TRACE_WRITES | TCL_TRACE_UNSETS |
03204           TCL_TRACE_ARRAY | TCL_TRACE_RESULT_DYNAMIC | TCL_TRACE_RESULT_OBJECT;
03205 #ifndef TCL_REMOVE_OBSOLETE_TRACES
03206     flagMask |= TCL_TRACE_OLD_STYLE;
03207 #endif
03208     tracePtr->flags = tracePtr->flags & flagMask;
03209 
03210     hPtr = Tcl_CreateHashEntry(&iPtr->varTraces, (char *) varPtr, &isNew);
03211     if (isNew) {
03212         tracePtr->nextPtr = NULL;
03213     } else {
03214         tracePtr->nextPtr = (VarTrace *) Tcl_GetHashValue(hPtr);
03215     }
03216     Tcl_SetHashValue(hPtr, (char *) tracePtr);
03217 
03218     /*
03219      * Mark the variable as traced so we know to call them.
03220      */
03221 
03222     varPtr->flags |= (tracePtr->flags & VAR_ALL_TRACES);
03223 
03224     return TCL_OK;
03225 }
03226 
03227 /*
03228  * Local Variables:
03229  * mode: c
03230  * c-basic-offset: 4
03231  * fill-column: 78
03232  * End:
03233  */



Generated on Wed Mar 12 12:18:23 2008 by  doxygen 1.5.1