tclTrace.cGo 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 1.5.1 |