Planet
navi homePPSaboutscreenshotsdownloaddevelopmentforum

source: downloads/tcl8.5.2/generic/tclTrace.c @ 64

Last change on this file since 64 was 25, checked in by landauf, 16 years ago

added tcl to libs

File size: 94.0 KB
Line 
1/*
2 * tclTrace.c --
3 *
4 *      This file contains code to handle most trace management.
5 *
6 * Copyright (c) 1987-1993 The Regents of the University of California.
7 * Copyright (c) 1994-1997 Sun Microsystems, Inc.
8 * Copyright (c) 1998-2000 Scriptics Corporation.
9 * Copyright (c) 2002 ActiveState Corporation.
10 *
11 * See the file "license.terms" for information on usage and redistribution of
12 * this file, and for a DISCLAIMER OF ALL WARRANTIES.
13 *
14 * RCS: @(#) $Id: tclTrace.c,v 1.47 2007/12/13 15:23:20 dgp Exp $
15 */
16
17#include "tclInt.h"
18
19/*
20 * Structures used to hold information about variable traces:
21 */
22
23typedef struct {
24    int flags;                  /* Operations for which Tcl command is to be
25                                 * invoked. */
26    size_t length;              /* Number of non-NUL chars. in command. */
27    char command[4];            /* Space for Tcl command to invoke. Actual
28                                 * size will be as large as necessary to hold
29                                 * command. This field must be the last in the
30                                 * structure, so that it can be larger than 4
31                                 * bytes. */
32} TraceVarInfo;
33
34typedef struct {
35    VarTrace traceInfo;
36    TraceVarInfo traceCmdInfo;
37} CombinedTraceVarInfo;
38
39/*
40 * Structure used to hold information about command traces:
41 */
42
43typedef struct {
44    int flags;                  /* Operations for which Tcl command is to be
45                                 * invoked. */
46    size_t length;              /* Number of non-NUL chars. in command. */
47    Tcl_Trace stepTrace;        /* Used for execution traces, when tracing
48                                 * inside the given command */
49    int startLevel;             /* Used for bookkeeping with step execution
50                                 * traces, store the level at which the step
51                                 * trace was invoked */
52    char *startCmd;             /* Used for bookkeeping with step execution
53                                 * traces, store the command name which
54                                 * invoked step trace */
55    int curFlags;               /* Trace flags for the current command */
56    int curCode;                /* Return code for the current command */
57    int refCount;               /* Used to ensure this structure is not
58                                 * deleted too early. Keeps track of how many
59                                 * pieces of code have a pointer to this
60                                 * structure. */
61    char command[4];            /* Space for Tcl command to invoke. Actual
62                                 * size will be as large as necessary to hold
63                                 * command. This field must be the last in the
64                                 * structure, so that it can be larger than 4
65                                 * bytes. */
66} TraceCommandInfo;
67
68/*
69 * Used by command execution traces. Note that we assume in the code that
70 * TCL_TRACE_ENTER_DURING_EXEC == 4 * TCL_TRACE_ENTER_EXEC and that
71 * TCL_TRACE_LEAVE_DURING_EXEC == 4 * TCL_TRACE_LEAVE_EXEC.
72 *
73 * TCL_TRACE_ENTER_DURING_EXEC  - Trace each command inside the command
74 *                                currently being traced, before execution.
75 * TCL_TRACE_LEAVE_DURING_EXEC  - Trace each command inside the command
76 *                                currently being traced, after execution.
77 * TCL_TRACE_ANY_EXEC           - OR'd combination of all EXEC flags.
78 * TCL_TRACE_EXEC_IN_PROGRESS   - The callback function on this trace is
79 *                                currently executing. Therefore we don't let
80 *                                further traces execute.
81 * TCL_TRACE_EXEC_DIRECT        - This execution trace is triggered directly
82 *                                by the command being traced, not because of
83 *                                an internal trace.
84 * The flags 'TCL_TRACE_DESTROYED' and 'TCL_INTERP_DESTROYED' may also be used
85 * in command execution traces.
86 */
87
88#define TCL_TRACE_ENTER_DURING_EXEC     4
89#define TCL_TRACE_LEAVE_DURING_EXEC     8
90#define TCL_TRACE_ANY_EXEC              15
91#define TCL_TRACE_EXEC_IN_PROGRESS      0x10
92#define TCL_TRACE_EXEC_DIRECT           0x20
93
94/*
95 * Forward declarations for functions defined in this file:
96 */
97
98typedef int (Tcl_TraceTypeObjCmd)(Tcl_Interp *interp, int optionIndex,
99        int objc, Tcl_Obj *const objv[]);
100
101static Tcl_TraceTypeObjCmd TraceVariableObjCmd;
102static Tcl_TraceTypeObjCmd TraceCommandObjCmd;
103static Tcl_TraceTypeObjCmd TraceExecutionObjCmd;
104
105/*
106 * Each subcommand has a number of 'types' to which it can apply. Currently
107 * 'execution', 'command' and 'variable' are the only types supported. These
108 * three arrays MUST be kept in sync! In the future we may provide an API to
109 * add to the list of supported trace types.
110 */
111
112static const char *traceTypeOptions[] = {
113    "execution", "command", "variable", NULL
114};
115static Tcl_TraceTypeObjCmd *traceSubCmds[] = {
116    TraceExecutionObjCmd,
117    TraceCommandObjCmd,
118    TraceVariableObjCmd,
119};
120
121/*
122 * Declarations for local functions to this file:
123 */
124
125static int              CallTraceFunction(Tcl_Interp *interp, Trace *tracePtr,
126                            Command *cmdPtr, const char *command, int numChars,
127                            int objc, Tcl_Obj *const objv[]);
128static char *           TraceVarProc(ClientData clientData, Tcl_Interp *interp,
129                            const char *name1, const char *name2, int flags);
130static void             TraceCommandProc(ClientData clientData,
131                            Tcl_Interp *interp, const char *oldName,
132                            const char *newName, int flags);
133static Tcl_CmdObjTraceProc TraceExecutionProc;
134static int              StringTraceProc(ClientData clientData,
135                            Tcl_Interp *interp, int level,
136                            const char *command, Tcl_Command commandInfo,
137                            int objc, Tcl_Obj *const objv[]);
138static void             StringTraceDeleteProc(ClientData clientData);
139static void             DisposeTraceResult(int flags, char *result);
140static int              TraceVarEx(Tcl_Interp *interp, const char *part1,
141                            const char *part2, register VarTrace *tracePtr);
142
143/*
144 * The following structure holds the client data for string-based
145 * trace procs
146 */
147
148typedef struct StringTraceData {
149    ClientData clientData;      /* Client data from Tcl_CreateTrace */
150    Tcl_CmdTraceProc *proc;     /* Trace function from Tcl_CreateTrace */
151} StringTraceData;
152
153/*
154 *----------------------------------------------------------------------
155 *
156 * Tcl_TraceObjCmd --
157 *
158 *      This function is invoked to process the "trace" Tcl command. See the
159 *      user documentation for details on what it does.
160 *
161 *      Standard syntax as of Tcl 8.4 is:
162 *          trace {add|info|remove} {command|variable} name ops cmd
163 *
164 * Results:
165 *      A standard Tcl result.
166 *
167 * Side effects:
168 *      See the user documentation.
169 *----------------------------------------------------------------------
170 */
171
172        /* ARGSUSED */
173int
174Tcl_TraceObjCmd(
175    ClientData dummy,           /* Not used. */
176    Tcl_Interp *interp,         /* Current interpreter. */
177    int objc,                   /* Number of arguments. */
178    Tcl_Obj *const objv[])      /* Argument objects. */
179{
180    int optionIndex;
181    char *name, *flagOps, *p;
182    /* Main sub commands to 'trace' */
183    static const char *traceOptions[] = {
184        "add", "info", "remove",
185#ifndef TCL_REMOVE_OBSOLETE_TRACES
186        "variable", "vdelete", "vinfo",
187#endif
188        NULL
189    };
190    /* 'OLD' options are pre-Tcl-8.4 style */
191    enum traceOptions {
192        TRACE_ADD, TRACE_INFO, TRACE_REMOVE,
193#ifndef TCL_REMOVE_OBSOLETE_TRACES
194        TRACE_OLD_VARIABLE, TRACE_OLD_VDELETE, TRACE_OLD_VINFO
195#endif
196    };
197
198    if (objc < 2) {
199        Tcl_WrongNumArgs(interp, 1, objv, "option ?arg arg ...?");
200        return TCL_ERROR;
201    }
202
203    if (Tcl_GetIndexFromObj(interp, objv[1], traceOptions,
204                "option", 0, &optionIndex) != TCL_OK) {
205        return TCL_ERROR;
206    }
207    switch ((enum traceOptions) optionIndex) {
208    case TRACE_ADD:
209    case TRACE_REMOVE: {
210        /*
211         * All sub commands of trace add/remove must take at least one more
212         * argument. Beyond that we let the subcommand itself control the
213         * argument structure.
214         */
215
216        int typeIndex;
217
218        if (objc < 3) {
219            Tcl_WrongNumArgs(interp, 2, objv, "type ?arg arg ...?");
220            return TCL_ERROR;
221        }
222        if (Tcl_GetIndexFromObj(interp, objv[2], traceTypeOptions, "option",
223                0, &typeIndex) != TCL_OK) {
224            return TCL_ERROR;
225        }
226        return (traceSubCmds[typeIndex])(interp, optionIndex, objc, objv);
227    }
228    case TRACE_INFO: {
229        /*
230         * All sub commands of trace info must take exactly two more arguments
231         * which name the type of thing being traced and the name of the thing
232         * being traced.
233         */
234
235        int typeIndex;
236        if (objc < 3) {
237            /*
238             * Delegate other complaints to the type-specific code which can
239             * give a better error message.
240             */
241
242            Tcl_WrongNumArgs(interp, 2, objv, "type name");
243            return TCL_ERROR;
244        }
245        if (Tcl_GetIndexFromObj(interp, objv[2], traceTypeOptions, "option",
246                0, &typeIndex) != TCL_OK) {
247            return TCL_ERROR;
248        }
249        return (traceSubCmds[typeIndex])(interp, optionIndex, objc, objv);
250        break;
251    }
252
253#ifndef TCL_REMOVE_OBSOLETE_TRACES
254    case TRACE_OLD_VARIABLE:
255    case TRACE_OLD_VDELETE: {
256        Tcl_Obj *copyObjv[6];
257        Tcl_Obj *opsList;
258        int code, numFlags;
259
260        if (objc != 5) {
261            Tcl_WrongNumArgs(interp, 2, objv, "name ops command");
262            return TCL_ERROR;
263        }
264
265        opsList = Tcl_NewObj();
266        Tcl_IncrRefCount(opsList);
267        flagOps = Tcl_GetStringFromObj(objv[3], &numFlags);
268        if (numFlags == 0) {
269            Tcl_DecrRefCount(opsList);
270            goto badVarOps;
271        }
272        for (p = flagOps; *p != 0; p++) {
273            Tcl_Obj *opObj;
274
275            if (*p == 'r') {
276                TclNewLiteralStringObj(opObj, "read");
277            } else if (*p == 'w') {
278                TclNewLiteralStringObj(opObj, "write");
279            } else if (*p == 'u') {
280                TclNewLiteralStringObj(opObj, "unset");
281            } else if (*p == 'a') {
282                TclNewLiteralStringObj(opObj, "array");
283            } else {
284                Tcl_DecrRefCount(opsList);
285                goto badVarOps;
286            }
287            Tcl_ListObjAppendElement(NULL, opsList, opObj);
288        }
289        copyObjv[0] = NULL;
290        memcpy(copyObjv+1, objv, objc*sizeof(Tcl_Obj *));
291        copyObjv[4] = opsList;
292        if (optionIndex == TRACE_OLD_VARIABLE) {
293            code = (traceSubCmds[2])(interp, TRACE_ADD, objc+1, copyObjv);
294        } else {
295            code = (traceSubCmds[2])(interp, TRACE_REMOVE, objc+1, copyObjv);
296        }
297        Tcl_DecrRefCount(opsList);
298        return code;
299    }
300    case TRACE_OLD_VINFO: {
301        ClientData clientData;
302        char ops[5];
303        Tcl_Obj *resultListPtr, *pairObjPtr, *elemObjPtr;
304
305        if (objc != 3) {
306            Tcl_WrongNumArgs(interp, 2, objv, "name");
307            return TCL_ERROR;
308        }
309        resultListPtr = Tcl_NewObj();
310        clientData = 0;
311        name = Tcl_GetString(objv[2]);
312        while ((clientData = Tcl_VarTraceInfo(interp, name, 0,
313                TraceVarProc, clientData)) != 0) {
314
315            TraceVarInfo *tvarPtr = (TraceVarInfo *) clientData;
316
317            pairObjPtr = Tcl_NewListObj(0, NULL);
318            p = ops;
319            if (tvarPtr->flags & TCL_TRACE_READS) {
320                *p = 'r';
321                p++;
322            }
323            if (tvarPtr->flags & TCL_TRACE_WRITES) {
324                *p = 'w';
325                p++;
326            }
327            if (tvarPtr->flags & TCL_TRACE_UNSETS) {
328                *p = 'u';
329                p++;
330            }
331            if (tvarPtr->flags & TCL_TRACE_ARRAY) {
332                *p = 'a';
333                p++;
334            }
335            *p = '\0';
336
337            /*
338             * Build a pair (2-item list) with the ops string as the first obj
339             * element and the tvarPtr->command string as the second obj
340             * element. Append the pair (as an element) to the end of the
341             * result object list.
342             */
343
344            elemObjPtr = Tcl_NewStringObj(ops, -1);
345            Tcl_ListObjAppendElement(NULL, pairObjPtr, elemObjPtr);
346            elemObjPtr = Tcl_NewStringObj(tvarPtr->command, -1);
347            Tcl_ListObjAppendElement(NULL, pairObjPtr, elemObjPtr);
348            Tcl_ListObjAppendElement(interp, resultListPtr, pairObjPtr);
349        }
350        Tcl_SetObjResult(interp, resultListPtr);
351        break;
352    }
353#endif /* TCL_REMOVE_OBSOLETE_TRACES */
354    }
355    return TCL_OK;
356
357  badVarOps:
358    Tcl_AppendResult(interp, "bad operations \"", flagOps,
359            "\": should be one or more of rwua", NULL);
360    return TCL_ERROR;
361}
362
363/*
364 *----------------------------------------------------------------------
365 *
366 * TraceExecutionObjCmd --
367 *
368 *      Helper function for Tcl_TraceObjCmd; implements the [trace
369 *      {add|remove|info} execution ...] subcommands. See the user
370 *      documentation for details on what these do.
371 *
372 * Results:
373 *      Standard Tcl result.
374 *
375 * Side effects:
376 *      Depends on the operation (add, remove, or info) being performed; may
377 *      add or remove command traces on a command.
378 *
379 *----------------------------------------------------------------------
380 */
381
382static int
383TraceExecutionObjCmd(
384    Tcl_Interp *interp,         /* Current interpreter. */
385    int optionIndex,            /* Add, info or remove */
386    int objc,                   /* Number of arguments. */
387    Tcl_Obj *const objv[])      /* Argument objects. */
388{
389    int commandLength, index;
390    char *name, *command;
391    size_t length;
392    enum traceOptions {
393        TRACE_ADD, TRACE_INFO, TRACE_REMOVE
394    };
395    static const char *opStrings[] = {
396        "enter", "leave", "enterstep", "leavestep", NULL
397    };
398    enum operations {
399        TRACE_EXEC_ENTER, TRACE_EXEC_LEAVE,
400        TRACE_EXEC_ENTER_STEP, TRACE_EXEC_LEAVE_STEP
401    };
402
403    switch ((enum traceOptions) optionIndex) {
404    case TRACE_ADD:
405    case TRACE_REMOVE: {
406        int flags = 0;
407        int i, listLen, result;
408        Tcl_Obj **elemPtrs;
409
410        if (objc != 6) {
411            Tcl_WrongNumArgs(interp, 3, objv, "name opList command");
412            return TCL_ERROR;
413        }
414
415        /*
416         * Make sure the ops argument is a list object; get its length and a
417         * pointer to its array of element pointers.
418         */
419
420        result = Tcl_ListObjGetElements(interp, objv[4], &listLen, &elemPtrs);
421        if (result != TCL_OK) {
422            return result;
423        }
424        if (listLen == 0) {
425            Tcl_SetResult(interp, "bad operation list \"\": must be "
426                    "one or more of enter, leave, enterstep, or leavestep",
427                    TCL_STATIC);
428            return TCL_ERROR;
429        }
430        for (i = 0; i < listLen; i++) {
431            if (Tcl_GetIndexFromObj(interp, elemPtrs[i], opStrings,
432                    "operation", TCL_EXACT, &index) != TCL_OK) {
433                return TCL_ERROR;
434            }
435            switch ((enum operations) index) {
436            case TRACE_EXEC_ENTER:
437                flags |= TCL_TRACE_ENTER_EXEC;
438                break;
439            case TRACE_EXEC_LEAVE:
440                flags |= TCL_TRACE_LEAVE_EXEC;
441                break;
442            case TRACE_EXEC_ENTER_STEP:
443                flags |= TCL_TRACE_ENTER_DURING_EXEC;
444                break;
445            case TRACE_EXEC_LEAVE_STEP:
446                flags |= TCL_TRACE_LEAVE_DURING_EXEC;
447                break;
448            }
449        }
450        command = Tcl_GetStringFromObj(objv[5], &commandLength);
451        length = (size_t) commandLength;
452        if ((enum traceOptions) optionIndex == TRACE_ADD) {
453            TraceCommandInfo *tcmdPtr;
454
455            tcmdPtr = (TraceCommandInfo *) ckalloc((unsigned)
456                    (sizeof(TraceCommandInfo) - sizeof(tcmdPtr->command)
457                            + length + 1));
458            tcmdPtr->flags = flags;
459            tcmdPtr->stepTrace = NULL;
460            tcmdPtr->startLevel = 0;
461            tcmdPtr->startCmd = NULL;
462            tcmdPtr->length = length;
463            tcmdPtr->refCount = 1;
464            flags |= TCL_TRACE_DELETE;
465            if (flags & (TCL_TRACE_ENTER_DURING_EXEC |
466                    TCL_TRACE_LEAVE_DURING_EXEC)) {
467                flags |= (TCL_TRACE_ENTER_EXEC | TCL_TRACE_LEAVE_EXEC);
468            }
469            strcpy(tcmdPtr->command, command);
470            name = Tcl_GetString(objv[3]);
471            if (Tcl_TraceCommand(interp, name, flags, TraceCommandProc,
472                    (ClientData) tcmdPtr) != TCL_OK) {
473                ckfree((char *) tcmdPtr);
474                return TCL_ERROR;
475            }
476        } else {
477            /*
478             * Search through all of our traces on this command to see if
479             * there's one with the given command. If so, then delete the
480             * first one that matches.
481             */
482
483            TraceCommandInfo *tcmdPtr;
484            ClientData clientData = NULL;
485            name = Tcl_GetString(objv[3]);
486
487            /*
488             * First ensure the name given is valid.
489             */
490
491            if (Tcl_FindCommand(interp,name,NULL,TCL_LEAVE_ERR_MSG) == NULL) {
492                return TCL_ERROR;
493            }
494
495            while ((clientData = Tcl_CommandTraceInfo(interp, name, 0,
496                    TraceCommandProc, clientData)) != NULL) {
497                tcmdPtr = (TraceCommandInfo *) clientData;
498
499                /*
500                 * In checking the 'flags' field we must remove any extraneous
501                 * flags which may have been temporarily added by various
502                 * pieces of the trace mechanism.
503                 */
504
505                if ((tcmdPtr->length == length)
506                        && ((tcmdPtr->flags & (TCL_TRACE_ANY_EXEC |
507                                TCL_TRACE_RENAME | TCL_TRACE_DELETE)) == flags)
508                        && (strncmp(command, tcmdPtr->command,
509                                (size_t) length) == 0)) {
510                    flags |= TCL_TRACE_DELETE;
511                    if (flags & (TCL_TRACE_ENTER_DURING_EXEC |
512                            TCL_TRACE_LEAVE_DURING_EXEC)) {
513                        flags |= (TCL_TRACE_ENTER_EXEC | TCL_TRACE_LEAVE_EXEC);
514                    }
515                    Tcl_UntraceCommand(interp, name, flags,
516                            TraceCommandProc, clientData);
517                    if (tcmdPtr->stepTrace != NULL) {
518                        /*
519                         * We need to remove the interpreter-wide trace which
520                         * we created to allow 'step' traces.
521                         */
522
523                        Tcl_DeleteTrace(interp, tcmdPtr->stepTrace);
524                        tcmdPtr->stepTrace = NULL;
525                        if (tcmdPtr->startCmd != NULL) {
526                            ckfree((char *) tcmdPtr->startCmd);
527                        }
528                    }
529                    if (tcmdPtr->flags & TCL_TRACE_EXEC_IN_PROGRESS) {
530                        /*
531                         * Postpone deletion.
532                         */
533
534                        tcmdPtr->flags = 0;
535                    }
536                    if ((--tcmdPtr->refCount) <= 0) {
537                        ckfree((char *) tcmdPtr);
538                    }
539                    break;
540                }
541            }
542        }
543        break;
544    }
545    case TRACE_INFO: {
546        ClientData clientData;
547        Tcl_Obj *resultListPtr, *eachTraceObjPtr, *elemObjPtr;
548
549        if (objc != 4) {
550            Tcl_WrongNumArgs(interp, 3, objv, "name");
551            return TCL_ERROR;
552        }
553
554        clientData = NULL;
555        name = Tcl_GetString(objv[3]);
556
557        /*
558         * First ensure the name given is valid.
559         */
560
561        if (Tcl_FindCommand(interp, name, NULL, TCL_LEAVE_ERR_MSG) == NULL) {
562            return TCL_ERROR;
563        }
564
565        resultListPtr = Tcl_NewListObj(0, NULL);
566        while ((clientData = Tcl_CommandTraceInfo(interp, name, 0,
567                TraceCommandProc, clientData)) != NULL) {
568            int numOps = 0;
569            Tcl_Obj *opObj;
570            TraceCommandInfo *tcmdPtr = (TraceCommandInfo *) clientData;
571
572            /*
573             * Build a list with the ops list as the first obj element and the
574             * tcmdPtr->command string as the second obj element. Append this
575             * list (as an element) to the end of the result object list.
576             */
577
578            elemObjPtr = Tcl_NewListObj(0, NULL);
579            Tcl_IncrRefCount(elemObjPtr);
580            if (tcmdPtr->flags & TCL_TRACE_ENTER_EXEC) {
581                TclNewLiteralStringObj(opObj, "enter");
582                Tcl_ListObjAppendElement(NULL, elemObjPtr, opObj);
583            }
584            if (tcmdPtr->flags & TCL_TRACE_LEAVE_EXEC) {
585                TclNewLiteralStringObj(opObj, "leave");
586                Tcl_ListObjAppendElement(NULL, elemObjPtr, opObj);
587            }
588            if (tcmdPtr->flags & TCL_TRACE_ENTER_DURING_EXEC) {
589                TclNewLiteralStringObj(opObj, "enterstep");
590                Tcl_ListObjAppendElement(NULL, elemObjPtr, opObj);
591            }
592            if (tcmdPtr->flags & TCL_TRACE_LEAVE_DURING_EXEC) {
593                TclNewLiteralStringObj(opObj, "leavestep");
594                Tcl_ListObjAppendElement(NULL, elemObjPtr, opObj);
595            }
596            Tcl_ListObjLength(NULL, elemObjPtr, &numOps);
597            if (0 == numOps) {
598                Tcl_DecrRefCount(elemObjPtr);
599                continue;
600            }
601            eachTraceObjPtr = Tcl_NewListObj(0, NULL);
602            Tcl_ListObjAppendElement(NULL, eachTraceObjPtr, elemObjPtr);
603            Tcl_DecrRefCount(elemObjPtr);
604            elemObjPtr = NULL;
605
606            Tcl_ListObjAppendElement(NULL, eachTraceObjPtr,
607                    Tcl_NewStringObj(tcmdPtr->command, -1));
608            Tcl_ListObjAppendElement(interp, resultListPtr, eachTraceObjPtr);
609        }
610        Tcl_SetObjResult(interp, resultListPtr);
611        break;
612    }
613    }
614    return TCL_OK;
615}
616
617/*
618 *----------------------------------------------------------------------
619 *
620 * TraceCommandObjCmd --
621 *
622 *      Helper function for Tcl_TraceObjCmd; implements the [trace
623 *      {add|info|remove} command ...] subcommands. See the user documentation
624 *      for details on what these do.
625 *
626 * Results:
627 *      Standard Tcl result.
628 *
629 * Side effects:
630 *      Depends on the operation (add, remove, or info) being performed; may
631 *      add or remove command traces on a command.
632 *
633 *----------------------------------------------------------------------
634 */
635
636static int
637TraceCommandObjCmd(
638    Tcl_Interp *interp,         /* Current interpreter. */
639    int optionIndex,            /* Add, info or remove */
640    int objc,                   /* Number of arguments. */
641    Tcl_Obj *const objv[])      /* Argument objects. */
642{
643    int commandLength, index;
644    char *name, *command;
645    size_t length;
646    enum traceOptions { TRACE_ADD, TRACE_INFO, TRACE_REMOVE };
647    static const char *opStrings[] = { "delete", "rename", NULL };
648    enum operations { TRACE_CMD_DELETE, TRACE_CMD_RENAME };
649
650    switch ((enum traceOptions) optionIndex) {
651    case TRACE_ADD:
652    case TRACE_REMOVE: {
653        int flags = 0;
654        int i, listLen, result;
655        Tcl_Obj **elemPtrs;
656
657        if (objc != 6) {
658            Tcl_WrongNumArgs(interp, 3, objv, "name opList command");
659            return TCL_ERROR;
660        }
661
662        /*
663         * Make sure the ops argument is a list object; get its length and a
664         * pointer to its array of element pointers.
665         */
666
667        result = Tcl_ListObjGetElements(interp, objv[4], &listLen, &elemPtrs);
668        if (result != TCL_OK) {
669            return result;
670        }
671        if (listLen == 0) {
672            Tcl_SetResult(interp, "bad operation list \"\": must be "
673                    "one or more of delete or rename", TCL_STATIC);
674            return TCL_ERROR;
675        }
676
677        for (i = 0; i < listLen; i++) {
678            if (Tcl_GetIndexFromObj(interp, elemPtrs[i], opStrings,
679                    "operation", TCL_EXACT, &index) != TCL_OK) {
680                return TCL_ERROR;
681            }
682            switch ((enum operations) index) {
683            case TRACE_CMD_RENAME:
684                flags |= TCL_TRACE_RENAME;
685                break;
686            case TRACE_CMD_DELETE:
687                flags |= TCL_TRACE_DELETE;
688                break;
689            }
690        }
691
692        command = Tcl_GetStringFromObj(objv[5], &commandLength);
693        length = (size_t) commandLength;
694        if ((enum traceOptions) optionIndex == TRACE_ADD) {
695            TraceCommandInfo *tcmdPtr;
696
697            tcmdPtr = (TraceCommandInfo *) ckalloc((unsigned)
698                    (sizeof(TraceCommandInfo) - sizeof(tcmdPtr->command)
699                            + length + 1));
700            tcmdPtr->flags = flags;
701            tcmdPtr->stepTrace = NULL;
702            tcmdPtr->startLevel = 0;
703            tcmdPtr->startCmd = NULL;
704            tcmdPtr->length = length;
705            tcmdPtr->refCount = 1;
706            flags |= TCL_TRACE_DELETE;
707            strcpy(tcmdPtr->command, command);
708            name = Tcl_GetString(objv[3]);
709            if (Tcl_TraceCommand(interp, name, flags, TraceCommandProc,
710                    (ClientData) tcmdPtr) != TCL_OK) {
711                ckfree((char *) tcmdPtr);
712                return TCL_ERROR;
713            }
714        } else {
715            /*
716             * Search through all of our traces on this command to see if
717             * there's one with the given command. If so, then delete the
718             * first one that matches.
719             */
720
721            TraceCommandInfo *tcmdPtr;
722            ClientData clientData = NULL;
723            name = Tcl_GetString(objv[3]);
724
725            /*
726             * First ensure the name given is valid.
727             */
728
729            if (Tcl_FindCommand(interp,name,NULL,TCL_LEAVE_ERR_MSG) == NULL) {
730                return TCL_ERROR;
731            }
732
733            while ((clientData = Tcl_CommandTraceInfo(interp, name, 0,
734                    TraceCommandProc, clientData)) != NULL) {
735                tcmdPtr = (TraceCommandInfo *) clientData;
736                if ((tcmdPtr->length == length)
737                        && (tcmdPtr->flags == flags)
738                        && (strncmp(command, tcmdPtr->command,
739                                (size_t) length) == 0)) {
740                    Tcl_UntraceCommand(interp, name, flags | TCL_TRACE_DELETE,
741                            TraceCommandProc, clientData);
742                    tcmdPtr->flags |= TCL_TRACE_DESTROYED;
743                    if ((--tcmdPtr->refCount) <= 0) {
744                        ckfree((char *) tcmdPtr);
745                    }
746                    break;
747                }
748            }
749        }
750        break;
751    }
752    case TRACE_INFO: {
753        ClientData clientData;
754        Tcl_Obj *resultListPtr, *eachTraceObjPtr, *elemObjPtr;
755
756        if (objc != 4) {
757            Tcl_WrongNumArgs(interp, 3, objv, "name");
758            return TCL_ERROR;
759        }
760
761        clientData = NULL;
762        name = Tcl_GetString(objv[3]);
763
764        /*
765         * First ensure the name given is valid.
766         */
767
768        if (Tcl_FindCommand(interp, name, NULL, TCL_LEAVE_ERR_MSG) == NULL) {
769            return TCL_ERROR;
770        }
771
772        resultListPtr = Tcl_NewListObj(0, NULL);
773        while ((clientData = Tcl_CommandTraceInfo(interp, name, 0,
774                TraceCommandProc, clientData)) != NULL) {
775            int numOps = 0;
776            Tcl_Obj *opObj;
777            TraceCommandInfo *tcmdPtr = (TraceCommandInfo *) clientData;
778
779            /*
780             * Build a list with the ops list as the first obj element and the
781             * tcmdPtr->command string as the second obj element. Append this
782             * list (as an element) to the end of the result object list.
783             */
784
785            elemObjPtr = Tcl_NewListObj(0, NULL);
786            Tcl_IncrRefCount(elemObjPtr);
787            if (tcmdPtr->flags & TCL_TRACE_RENAME) {
788                TclNewLiteralStringObj(opObj, "rename");
789                Tcl_ListObjAppendElement(NULL, elemObjPtr, opObj);
790            }
791            if (tcmdPtr->flags & TCL_TRACE_DELETE) {
792                TclNewLiteralStringObj(opObj, "delete");
793                Tcl_ListObjAppendElement(NULL, elemObjPtr, opObj);
794            }
795            Tcl_ListObjLength(NULL, elemObjPtr, &numOps);
796            if (0 == numOps) {
797                Tcl_DecrRefCount(elemObjPtr);
798                continue;
799            }
800            eachTraceObjPtr = Tcl_NewListObj(0, NULL);
801            Tcl_ListObjAppendElement(NULL, eachTraceObjPtr, elemObjPtr);
802            Tcl_DecrRefCount(elemObjPtr);
803
804            elemObjPtr = Tcl_NewStringObj(tcmdPtr->command, -1);
805            Tcl_ListObjAppendElement(NULL, eachTraceObjPtr, elemObjPtr);
806            Tcl_ListObjAppendElement(interp, resultListPtr, eachTraceObjPtr);
807        }
808        Tcl_SetObjResult(interp, resultListPtr);
809        break;
810    }
811    }
812    return TCL_OK;
813}
814
815/*
816 *----------------------------------------------------------------------
817 *
818 * TraceVariableObjCmd --
819 *
820 *      Helper function for Tcl_TraceObjCmd; implements the [trace
821 *      {add|info|remove} variable ...] subcommands. See the user
822 *      documentation for details on what these do.
823 *
824 * Results:
825 *      Standard Tcl result.
826 *
827 * Side effects:
828 *      Depends on the operation (add, remove, or info) being performed; may
829 *      add or remove variable traces on a variable.
830 *
831 *----------------------------------------------------------------------
832 */
833
834static int
835TraceVariableObjCmd(
836    Tcl_Interp *interp,         /* Current interpreter. */
837    int optionIndex,            /* Add, info or remove */
838    int objc,                   /* Number of arguments. */
839    Tcl_Obj *const objv[])      /* Argument objects. */
840{
841    int commandLength, index;
842    char *name, *command;
843    size_t length;
844    enum traceOptions { TRACE_ADD, TRACE_INFO, TRACE_REMOVE };
845    static const char *opStrings[] = {
846        "array", "read", "unset", "write", NULL
847    };
848    enum operations {
849        TRACE_VAR_ARRAY, TRACE_VAR_READ, TRACE_VAR_UNSET, TRACE_VAR_WRITE
850    };
851
852    switch ((enum traceOptions) optionIndex) {
853    case TRACE_ADD:
854    case TRACE_REMOVE: {
855        int flags = 0;
856        int i, listLen, result;
857        Tcl_Obj **elemPtrs;
858
859        if (objc != 6) {
860            Tcl_WrongNumArgs(interp, 3, objv, "name opList command");
861            return TCL_ERROR;
862        }
863
864        /*
865         * Make sure the ops argument is a list object; get its length and a
866         * pointer to its array of element pointers.
867         */
868
869        result = Tcl_ListObjGetElements(interp, objv[4], &listLen, &elemPtrs);
870        if (result != TCL_OK) {
871            return result;
872        }
873        if (listLen == 0) {
874            Tcl_SetResult(interp, "bad operation list \"\": must be "
875                    "one or more of array, read, unset, or write", TCL_STATIC);
876            return TCL_ERROR;
877        }
878        for (i = 0; i < listLen ; i++) {
879            if (Tcl_GetIndexFromObj(interp, elemPtrs[i], opStrings,
880                    "operation", TCL_EXACT, &index) != TCL_OK) {
881                return TCL_ERROR;
882            }
883            switch ((enum operations) index) {
884            case TRACE_VAR_ARRAY:
885                flags |= TCL_TRACE_ARRAY;
886                break;
887            case TRACE_VAR_READ:
888                flags |= TCL_TRACE_READS;
889                break;
890            case TRACE_VAR_UNSET:
891                flags |= TCL_TRACE_UNSETS;
892                break;
893            case TRACE_VAR_WRITE:
894                flags |= TCL_TRACE_WRITES;
895                break;
896            }
897        }
898        command = Tcl_GetStringFromObj(objv[5], &commandLength);
899        length = (size_t) commandLength;
900        if ((enum traceOptions) optionIndex == TRACE_ADD) {
901            CombinedTraceVarInfo *ctvarPtr;
902
903            ctvarPtr = (CombinedTraceVarInfo *) ckalloc((unsigned)
904                    (sizeof(CombinedTraceVarInfo) + length + 1
905                    - sizeof(ctvarPtr->traceCmdInfo.command)));
906            ctvarPtr->traceCmdInfo.flags = flags;
907            if (objv[0] == NULL) {
908                ctvarPtr->traceCmdInfo.flags |= TCL_TRACE_OLD_STYLE;
909            }
910            ctvarPtr->traceCmdInfo.length = length;
911            flags |= TCL_TRACE_UNSETS | TCL_TRACE_RESULT_OBJECT;
912            strcpy(ctvarPtr->traceCmdInfo.command, command);
913            ctvarPtr->traceInfo.traceProc = TraceVarProc;
914            ctvarPtr->traceInfo.clientData = (ClientData)
915                    &ctvarPtr->traceCmdInfo;
916            ctvarPtr->traceInfo.flags = flags;
917            name = Tcl_GetString(objv[3]);
918            if (TraceVarEx(interp,name,NULL,(VarTrace*)ctvarPtr) != TCL_OK) {
919                ckfree((char *) ctvarPtr);
920                return TCL_ERROR;
921            }
922        } else {
923            /*
924             * Search through all of our traces on this variable to see if
925             * there's one with the given command. If so, then delete the
926             * first one that matches.
927             */
928
929            TraceVarInfo *tvarPtr;
930            ClientData clientData = 0;
931            name = Tcl_GetString(objv[3]);
932            while ((clientData = Tcl_VarTraceInfo(interp, name, 0,
933                    TraceVarProc, clientData)) != 0) {
934                tvarPtr = (TraceVarInfo *) clientData;
935                if ((tvarPtr->length == length)
936                        && ((tvarPtr->flags & ~TCL_TRACE_OLD_STYLE)==flags)
937                        && (strncmp(command, tvarPtr->command,
938                                (size_t) length) == 0)) {
939                    Tcl_UntraceVar2(interp, name, NULL,
940                            flags | TCL_TRACE_UNSETS | TCL_TRACE_RESULT_OBJECT,
941                            TraceVarProc, clientData);
942                    break;
943                }
944            }
945        }
946        break;
947    }
948    case TRACE_INFO: {
949        ClientData clientData;
950        Tcl_Obj *resultListPtr, *eachTraceObjPtr, *elemObjPtr;
951
952        if (objc != 4) {
953            Tcl_WrongNumArgs(interp, 3, objv, "name");
954            return TCL_ERROR;
955        }
956
957        resultListPtr = Tcl_NewObj();
958        clientData = 0;
959        name = Tcl_GetString(objv[3]);
960        while ((clientData = Tcl_VarTraceInfo(interp, name, 0, TraceVarProc,
961                clientData)) != 0) {
962            Tcl_Obj *opObj;
963            TraceVarInfo *tvarPtr = (TraceVarInfo *) clientData;
964
965            /*
966             * Build a list with the ops list as the first obj element and the
967             * tcmdPtr->command string as the second obj element. Append this
968             * list (as an element) to the end of the result object list.
969             */
970
971            elemObjPtr = Tcl_NewListObj(0, NULL);
972            if (tvarPtr->flags & TCL_TRACE_ARRAY) {
973                TclNewLiteralStringObj(opObj, "array");
974                Tcl_ListObjAppendElement(NULL, elemObjPtr, opObj);
975            }
976            if (tvarPtr->flags & TCL_TRACE_READS) {
977                TclNewLiteralStringObj(opObj, "read");
978                Tcl_ListObjAppendElement(NULL, elemObjPtr, opObj);
979            }
980            if (tvarPtr->flags & TCL_TRACE_WRITES) {
981                TclNewLiteralStringObj(opObj, "write");
982                Tcl_ListObjAppendElement(NULL, elemObjPtr, opObj);
983            }
984            if (tvarPtr->flags & TCL_TRACE_UNSETS) {
985                TclNewLiteralStringObj(opObj, "unset");
986                Tcl_ListObjAppendElement(NULL, elemObjPtr, opObj);
987            }
988            eachTraceObjPtr = Tcl_NewListObj(0, NULL);
989            Tcl_ListObjAppendElement(NULL, eachTraceObjPtr, elemObjPtr);
990
991            elemObjPtr = Tcl_NewStringObj(tvarPtr->command, -1);
992            Tcl_ListObjAppendElement(NULL, eachTraceObjPtr, elemObjPtr);
993            Tcl_ListObjAppendElement(interp, resultListPtr,
994                    eachTraceObjPtr);
995        }
996        Tcl_SetObjResult(interp, resultListPtr);
997        break;
998    }
999    }
1000    return TCL_OK;
1001}
1002
1003/*
1004 *----------------------------------------------------------------------
1005 *
1006 * Tcl_CommandTraceInfo --
1007 *
1008 *      Return the clientData value associated with a trace on a command.
1009 *      This function can also be used to step through all of the traces on a
1010 *      particular command that have the same trace function.
1011 *
1012 * Results:
1013 *      The return value is the clientData value associated with a trace on
1014 *      the given command. Information will only be returned for a trace with
1015 *      proc as trace function. If the clientData argument is NULL then the
1016 *      first such trace is returned; otherwise, the next relevant one after
1017 *      the one given by clientData will be returned. If the command doesn't
1018 *      exist then an error message is left in the interpreter and NULL is
1019 *      returned. Also, if there are no (more) traces for the given command,
1020 *      NULL is returned.
1021 *
1022 * Side effects:
1023 *      None.
1024 *
1025 *----------------------------------------------------------------------
1026 */
1027
1028ClientData
1029Tcl_CommandTraceInfo(
1030    Tcl_Interp *interp,         /* Interpreter containing command. */
1031    const char *cmdName,        /* Name of command. */
1032    int flags,                  /* OR-ed combo or TCL_GLOBAL_ONLY,
1033                                 * TCL_NAMESPACE_ONLY (can be 0). */
1034    Tcl_CommandTraceProc *proc, /* Function assocated with trace. */
1035    ClientData prevClientData)  /* If non-NULL, gives last value returned by
1036                                 * this function, so this call will return the
1037                                 * next trace after that one. If NULL, this
1038                                 * call will return the first trace. */
1039{
1040    Command *cmdPtr;
1041    register CommandTrace *tracePtr;
1042
1043    cmdPtr = (Command *) Tcl_FindCommand(interp, cmdName, NULL,
1044            TCL_LEAVE_ERR_MSG);
1045    if (cmdPtr == NULL) {
1046        return NULL;
1047    }
1048
1049    /*
1050     * Find the relevant trace, if any, and return its clientData.
1051     */
1052
1053    tracePtr = cmdPtr->tracePtr;
1054    if (prevClientData != NULL) {
1055        for (; tracePtr!=NULL ; tracePtr=tracePtr->nextPtr) {
1056            if ((tracePtr->clientData == prevClientData)
1057                    && (tracePtr->traceProc == proc)) {
1058                tracePtr = tracePtr->nextPtr;
1059                break;
1060            }
1061        }
1062    }
1063    for (; tracePtr!=NULL ; tracePtr=tracePtr->nextPtr) {
1064        if (tracePtr->traceProc == proc) {
1065            return tracePtr->clientData;
1066        }
1067    }
1068    return NULL;
1069}
1070
1071/*
1072 *----------------------------------------------------------------------
1073 *
1074 * Tcl_TraceCommand --
1075 *
1076 *      Arrange for rename/deletes to a command to cause a function to be
1077 *      invoked, which can monitor the operations.
1078 *
1079 *      Also optionally arrange for execution of that command to cause a
1080 *      function to be invoked.
1081 *
1082 * Results:
1083 *      A standard Tcl return value.
1084 *
1085 * Side effects:
1086 *      A trace is set up on the command given by cmdName, such that future
1087 *      changes to the command will be intermediated by proc. See the manual
1088 *      entry for complete details on the calling sequence for proc.
1089 *
1090 *----------------------------------------------------------------------
1091 */
1092
1093int
1094Tcl_TraceCommand(
1095    Tcl_Interp *interp,         /* Interpreter in which command is to be
1096                                 * traced. */
1097    const char *cmdName,        /* Name of command. */
1098    int flags,                  /* OR-ed collection of bits, including any of
1099                                 * TCL_TRACE_RENAME, TCL_TRACE_DELETE, and any
1100                                 * of the TRACE_*_EXEC flags */
1101    Tcl_CommandTraceProc *proc, /* Function to call when specified ops are
1102                                 * invoked upon cmdName. */
1103    ClientData clientData)      /* Arbitrary argument to pass to proc. */
1104{
1105    Command *cmdPtr;
1106    register CommandTrace *tracePtr;
1107
1108    cmdPtr = (Command *) Tcl_FindCommand(interp, cmdName, NULL,
1109            TCL_LEAVE_ERR_MSG);
1110    if (cmdPtr == NULL) {
1111        return TCL_ERROR;
1112    }
1113
1114    /*
1115     * Set up trace information.
1116     */
1117
1118    tracePtr = (CommandTrace *) ckalloc(sizeof(CommandTrace));
1119    tracePtr->traceProc = proc;
1120    tracePtr->clientData = clientData;
1121    tracePtr->flags = flags &
1122            (TCL_TRACE_RENAME | TCL_TRACE_DELETE | TCL_TRACE_ANY_EXEC);
1123    tracePtr->nextPtr = cmdPtr->tracePtr;
1124    tracePtr->refCount = 1;
1125    cmdPtr->tracePtr = tracePtr;
1126    if (tracePtr->flags & TCL_TRACE_ANY_EXEC) {
1127        cmdPtr->flags |= CMD_HAS_EXEC_TRACES;
1128    }
1129    return TCL_OK;
1130}
1131
1132/*
1133 *----------------------------------------------------------------------
1134 *
1135 * Tcl_UntraceCommand --
1136 *
1137 *      Remove a previously-created trace for a command.
1138 *
1139 * Results:
1140 *      None.
1141 *
1142 * Side effects:
1143 *      If there exists a trace for the command given by cmdName with the
1144 *      given flags, proc, and clientData, then that trace is removed.
1145 *
1146 *----------------------------------------------------------------------
1147 */
1148
1149void
1150Tcl_UntraceCommand(
1151    Tcl_Interp *interp,         /* Interpreter containing command. */
1152    const char *cmdName,        /* Name of command. */
1153    int flags,                  /* OR-ed collection of bits, including any of
1154                                 * TCL_TRACE_RENAME, TCL_TRACE_DELETE, and any
1155                                 * of the TRACE_*_EXEC flags */
1156    Tcl_CommandTraceProc *proc, /* Function assocated with trace. */
1157    ClientData clientData)      /* Arbitrary argument to pass to proc. */
1158{
1159    register CommandTrace *tracePtr;
1160    CommandTrace *prevPtr;
1161    Command *cmdPtr;
1162    Interp *iPtr = (Interp *) interp;
1163    ActiveCommandTrace *activePtr;
1164    int hasExecTraces = 0;
1165
1166    cmdPtr = (Command *) Tcl_FindCommand(interp, cmdName, NULL,
1167            TCL_LEAVE_ERR_MSG);
1168    if (cmdPtr == NULL) {
1169        return;
1170    }
1171
1172    flags &= (TCL_TRACE_RENAME | TCL_TRACE_DELETE | TCL_TRACE_ANY_EXEC);
1173
1174    for (tracePtr = cmdPtr->tracePtr, prevPtr = NULL; ;
1175            prevPtr = tracePtr, tracePtr = tracePtr->nextPtr) {
1176        if (tracePtr == NULL) {
1177            return;
1178        }
1179        if ((tracePtr->traceProc == proc)
1180                && ((tracePtr->flags & (TCL_TRACE_RENAME | TCL_TRACE_DELETE |
1181                        TCL_TRACE_ANY_EXEC)) == flags)
1182                && (tracePtr->clientData == clientData)) {
1183            if (tracePtr->flags & TCL_TRACE_ANY_EXEC) {
1184                hasExecTraces = 1;
1185            }
1186            break;
1187        }
1188    }
1189
1190    /*
1191     * The code below makes it possible to delete traces while traces are
1192     * active: it makes sure that the deleted trace won't be processed by
1193     * CallCommandTraces.
1194     */
1195
1196    for (activePtr = iPtr->activeCmdTracePtr;  activePtr != NULL;
1197            activePtr = activePtr->nextPtr) {
1198        if (activePtr->nextTracePtr == tracePtr) {
1199            if (activePtr->reverseScan) {
1200                activePtr->nextTracePtr = prevPtr;
1201            } else {
1202                activePtr->nextTracePtr = tracePtr->nextPtr;
1203            }
1204        }
1205    }
1206    if (prevPtr == NULL) {
1207        cmdPtr->tracePtr = tracePtr->nextPtr;
1208    } else {
1209        prevPtr->nextPtr = tracePtr->nextPtr;
1210    }
1211    tracePtr->flags = 0;
1212
1213    if ((--tracePtr->refCount) <= 0) {
1214        ckfree((char *) tracePtr);
1215    }
1216
1217    if (hasExecTraces) {
1218        for (tracePtr = cmdPtr->tracePtr, prevPtr = NULL; tracePtr != NULL ;
1219                prevPtr = tracePtr, tracePtr = tracePtr->nextPtr) {
1220            if (tracePtr->flags & TCL_TRACE_ANY_EXEC) {
1221                return;
1222            }
1223        }
1224
1225        /*
1226         * None of the remaining traces on this command are execution traces.
1227         * We therefore remove this flag:
1228         */
1229
1230        cmdPtr->flags &= ~CMD_HAS_EXEC_TRACES;
1231    }
1232}
1233
1234/*
1235 *----------------------------------------------------------------------
1236 *
1237 * TraceCommandProc --
1238 *
1239 *      This function is called to handle command changes that have been
1240 *      traced using the "trace" command, when using the 'rename' or 'delete'
1241 *      options.
1242 *
1243 * Results:
1244 *      None.
1245 *
1246 * Side effects:
1247 *      Depends on the command associated with the trace.
1248 *
1249 *----------------------------------------------------------------------
1250 */
1251
1252        /* ARGSUSED */
1253static void
1254TraceCommandProc(
1255    ClientData clientData,      /* Information about the command trace. */
1256    Tcl_Interp *interp,         /* Interpreter containing command. */
1257    const char *oldName,        /* Name of command being changed. */
1258    const char *newName,        /* New name of command. Empty string or NULL
1259                                 * means command is being deleted (renamed to
1260                                 * ""). */
1261    int flags)                  /* OR-ed bits giving operation and other
1262                                 * information. */
1263{
1264    TraceCommandInfo *tcmdPtr = (TraceCommandInfo *) clientData;
1265    int code;
1266    Tcl_DString cmd;
1267
1268    tcmdPtr->refCount++;
1269
1270    if ((tcmdPtr->flags & flags) && !Tcl_InterpDeleted(interp)
1271            && !Tcl_LimitExceeded(interp)) {
1272        /*
1273         * Generate a command to execute by appending list elements for the
1274         * old and new command name and the operation.
1275         */
1276
1277        Tcl_DStringInit(&cmd);
1278        Tcl_DStringAppend(&cmd, tcmdPtr->command, (int) tcmdPtr->length);
1279        Tcl_DStringAppendElement(&cmd, oldName);
1280        Tcl_DStringAppendElement(&cmd, (newName ? newName : ""));
1281        if (flags & TCL_TRACE_RENAME) {
1282            Tcl_DStringAppend(&cmd, " rename", 7);
1283        } else if (flags & TCL_TRACE_DELETE) {
1284            Tcl_DStringAppend(&cmd, " delete", 7);
1285        }
1286
1287        /*
1288         * Execute the command. We discard any object result the command
1289         * returns.
1290         *
1291         * Add the TCL_TRACE_DESTROYED flag to tcmdPtr to indicate to other
1292         * areas that this will be destroyed by us, otherwise a double-free
1293         * might occur depending on what the eval does.
1294         */
1295
1296        if (flags & TCL_TRACE_DESTROYED) {
1297            tcmdPtr->flags |= TCL_TRACE_DESTROYED;
1298        }
1299        code = Tcl_EvalEx(interp, Tcl_DStringValue(&cmd),
1300                Tcl_DStringLength(&cmd), 0);
1301        if (code != TCL_OK) {
1302            /* We ignore errors in these traced commands */
1303            /*** QUESTION: Use Tcl_BackgroundError(interp); instead? ***/
1304        }
1305        Tcl_DStringFree(&cmd);
1306    }
1307
1308    /*
1309     * We delete when the trace was destroyed or if this is a delete trace,
1310     * because command deletes are unconditional, so the trace must go away.
1311     */
1312
1313    if (flags & (TCL_TRACE_DESTROYED | TCL_TRACE_DELETE)) {
1314        int untraceFlags = tcmdPtr->flags;
1315        Tcl_InterpState state;
1316
1317        if (tcmdPtr->stepTrace != NULL) {
1318            Tcl_DeleteTrace(interp, tcmdPtr->stepTrace);
1319            tcmdPtr->stepTrace = NULL;
1320            if (tcmdPtr->startCmd != NULL) {
1321                ckfree((char *) tcmdPtr->startCmd);
1322            }
1323        }
1324        if (tcmdPtr->flags & TCL_TRACE_EXEC_IN_PROGRESS) {
1325            /*
1326             * Postpone deletion, until exec trace returns.
1327             */
1328
1329            tcmdPtr->flags = 0;
1330        }
1331
1332        /*
1333         * We need to construct the same flags for Tcl_UntraceCommand as were
1334         * passed to Tcl_TraceCommand. Reproduce the processing of [trace add
1335         * execution/command]. Be careful to keep this code in sync with that.
1336         */
1337
1338        if (untraceFlags & TCL_TRACE_ANY_EXEC) {
1339            untraceFlags |= TCL_TRACE_DELETE;
1340            if (untraceFlags & (TCL_TRACE_ENTER_DURING_EXEC
1341                    | TCL_TRACE_LEAVE_DURING_EXEC)) {
1342                untraceFlags |= (TCL_TRACE_ENTER_EXEC | TCL_TRACE_LEAVE_EXEC);
1343            }
1344        } else if (untraceFlags & TCL_TRACE_RENAME) {
1345            untraceFlags |= TCL_TRACE_DELETE;
1346        }
1347
1348        /*
1349         * Remove the trace since TCL_TRACE_DESTROYED tells us to, or the
1350         * command we're tracing has just gone away. Then decrement the
1351         * clientData refCount that was set up by trace creation.
1352         *
1353         * Note that we save the (return) state of the interpreter to prevent
1354         * bizarre error messages.
1355         */
1356
1357        state = Tcl_SaveInterpState(interp, TCL_OK);
1358        Tcl_UntraceCommand(interp, oldName, untraceFlags,
1359                TraceCommandProc, clientData);
1360        (void) Tcl_RestoreInterpState(interp, state);
1361        tcmdPtr->refCount--;
1362    }
1363    if ((--tcmdPtr->refCount) <= 0) {
1364        ckfree((char *) tcmdPtr);
1365    }
1366}
1367
1368/*
1369 *----------------------------------------------------------------------
1370 *
1371 * TclCheckExecutionTraces --
1372 *
1373 *      Checks on all current command execution traces, and invokes functions
1374 *      which have been registered. This function can be used by other code
1375 *      which performs execution to unify the tracing system, so that
1376 *      execution traces will function for that other code.
1377 *
1378 *      For instance extensions like [incr Tcl] which use their own execution
1379 *      technique can make use of Tcl's tracing.
1380 *
1381 *      This function is called by 'TclEvalObjvInternal'
1382 *
1383 * Results:
1384 *      The return value is a standard Tcl completion code such as TCL_OK or
1385 *      TCL_ERROR, etc.
1386 *
1387 * Side effects:
1388 *      Those side effects made by any trace functions called.
1389 *
1390 *----------------------------------------------------------------------
1391 */
1392
1393int
1394TclCheckExecutionTraces(
1395    Tcl_Interp *interp,         /* The current interpreter. */
1396    const char *command,        /* Pointer to beginning of the current command
1397                                 * string. */
1398    int numChars,               /* The number of characters in 'command' which
1399                                 * are part of the command string. */
1400    Command *cmdPtr,            /* Points to command's Command struct. */
1401    int code,                   /* The current result code. */
1402    int traceFlags,             /* Current tracing situation. */
1403    int objc,                   /* Number of arguments for the command. */
1404    Tcl_Obj *const objv[])      /* Pointers to Tcl_Obj of each argument. */
1405{
1406    Interp *iPtr = (Interp *) interp;
1407    CommandTrace *tracePtr, *lastTracePtr;
1408    ActiveCommandTrace active;
1409    int curLevel;
1410    int traceCode = TCL_OK;
1411    Tcl_InterpState state = NULL;
1412
1413    if (cmdPtr->tracePtr == NULL) {
1414        return traceCode;
1415    }
1416
1417    curLevel = iPtr->varFramePtr->level;
1418
1419    active.nextPtr = iPtr->activeCmdTracePtr;
1420    iPtr->activeCmdTracePtr = &active;
1421
1422    active.cmdPtr = cmdPtr;
1423    lastTracePtr = NULL;
1424    for (tracePtr = cmdPtr->tracePtr;
1425            (traceCode == TCL_OK) && (tracePtr != NULL);
1426            tracePtr = active.nextTracePtr) {
1427        if (traceFlags & TCL_TRACE_LEAVE_EXEC) {
1428            /*
1429             * Execute the trace command in order of creation for "leave".
1430             */
1431
1432            active.reverseScan = 1;
1433            active.nextTracePtr = NULL;
1434            tracePtr = cmdPtr->tracePtr;
1435            while (tracePtr->nextPtr != lastTracePtr) {
1436                active.nextTracePtr = tracePtr;
1437                tracePtr = tracePtr->nextPtr;
1438            }
1439        } else {
1440            active.reverseScan = 0;
1441            active.nextTracePtr = tracePtr->nextPtr;
1442        }
1443        if (tracePtr->traceProc == TraceCommandProc) {
1444            TraceCommandInfo *tcmdPtr = (TraceCommandInfo *)
1445                    tracePtr->clientData;
1446
1447            if (tcmdPtr->flags != 0) {
1448                tcmdPtr->curFlags = traceFlags | TCL_TRACE_EXEC_DIRECT;
1449                tcmdPtr->curCode  = code;
1450                tcmdPtr->refCount++;
1451                if (state == NULL) {
1452                    state = Tcl_SaveInterpState(interp, code);
1453                }
1454                traceCode = TraceExecutionProc((ClientData) tcmdPtr, interp,
1455                        curLevel, command, (Tcl_Command) cmdPtr, objc, objv);
1456                if ((--tcmdPtr->refCount) <= 0) {
1457                    ckfree((char *) tcmdPtr);
1458                }
1459            }
1460        }
1461        if (active.nextTracePtr) {
1462            lastTracePtr = active.nextTracePtr->nextPtr;
1463        }
1464    }
1465    iPtr->activeCmdTracePtr = active.nextPtr;
1466    if (state) {
1467        (void) Tcl_RestoreInterpState(interp, state);
1468    }
1469
1470    return(traceCode);
1471}
1472
1473/*
1474 *----------------------------------------------------------------------
1475 *
1476 * TclCheckInterpTraces --
1477 *
1478 *      Checks on all current traces, and invokes functions which have been
1479 *      registered. This function can be used by other code which performs
1480 *      execution to unify the tracing system. For instance extensions like
1481 *      [incr Tcl] which use their own execution technique can make use of
1482 *      Tcl's tracing.
1483 *
1484 *      This function is called by 'TclEvalObjvInternal'
1485 *
1486 * Results:
1487 *      The return value is a standard Tcl completion code such as TCL_OK or
1488 *      TCL_ERROR, etc.
1489 *
1490 * Side effects:
1491 *      Those side effects made by any trace functions called.
1492 *
1493 *----------------------------------------------------------------------
1494 */
1495
1496int
1497TclCheckInterpTraces(
1498    Tcl_Interp *interp,         /* The current interpreter. */
1499    const char *command,        /* Pointer to beginning of the current command
1500                                 * string. */
1501    int numChars,               /* The number of characters in 'command' which
1502                                 * are part of the command string. */
1503    Command *cmdPtr,            /* Points to command's Command struct. */
1504    int code,                   /* The current result code. */
1505    int traceFlags,             /* Current tracing situation. */
1506    int objc,                   /* Number of arguments for the command. */
1507    Tcl_Obj *const objv[])      /* Pointers to Tcl_Obj of each argument. */
1508{
1509    Interp *iPtr = (Interp *) interp;
1510    Trace *tracePtr, *lastTracePtr;
1511    ActiveInterpTrace active;
1512    int curLevel;
1513    int traceCode = TCL_OK;
1514    Tcl_InterpState state = NULL;
1515
1516    if ((iPtr->tracePtr == NULL)
1517            || (iPtr->flags & INTERP_TRACE_IN_PROGRESS)) {
1518        return(traceCode);
1519    }
1520
1521    curLevel = iPtr->numLevels;
1522
1523    active.nextPtr = iPtr->activeInterpTracePtr;
1524    iPtr->activeInterpTracePtr = &active;
1525
1526    lastTracePtr = NULL;
1527    for (tracePtr = iPtr->tracePtr;
1528            (traceCode == TCL_OK) && (tracePtr != NULL);
1529            tracePtr = active.nextTracePtr) {
1530        if (traceFlags & TCL_TRACE_ENTER_EXEC) {
1531            /*
1532             * Execute the trace command in reverse order of creation for
1533             * "enterstep" operation. The order is changed for "enterstep"
1534             * instead of for "leavestep" as was done in
1535             * TclCheckExecutionTraces because for step traces,
1536             * Tcl_CreateObjTrace creates one more linked list of traces which
1537             * results in one more reversal of trace invocation.
1538             */
1539
1540            active.reverseScan = 1;
1541            active.nextTracePtr = NULL;
1542            tracePtr = iPtr->tracePtr;
1543            while (tracePtr->nextPtr != lastTracePtr) {
1544                active.nextTracePtr = tracePtr;
1545                tracePtr = tracePtr->nextPtr;
1546            }
1547            if (active.nextTracePtr) {
1548                lastTracePtr = active.nextTracePtr->nextPtr;
1549            }
1550        } else {
1551            active.reverseScan = 0;
1552            active.nextTracePtr = tracePtr->nextPtr;
1553        }
1554
1555        if (tracePtr->level > 0 && curLevel > tracePtr->level) {
1556            continue;
1557        }
1558
1559        if (!(tracePtr->flags & TCL_TRACE_EXEC_IN_PROGRESS)) {
1560            /*
1561             * The proc invoked might delete the traced command which which
1562             * might try to free tracePtr. We want to use tracePtr until the
1563             * end of this if section, so we use Tcl_Preserve() and
1564             * Tcl_Release() to be sure it is not freed while we still need
1565             * it.
1566             */
1567
1568            Tcl_Preserve((ClientData) tracePtr);
1569            tracePtr->flags |= TCL_TRACE_EXEC_IN_PROGRESS;
1570            if (state == NULL) {
1571                state = Tcl_SaveInterpState(interp, code);
1572            }
1573
1574            if (tracePtr->flags &
1575                    (TCL_TRACE_ENTER_EXEC | TCL_TRACE_LEAVE_EXEC)) {
1576                /*
1577                 * New style trace.
1578                 */
1579
1580                if (tracePtr->flags & traceFlags) {
1581                    if (tracePtr->proc == TraceExecutionProc) {
1582                        TraceCommandInfo *tcmdPtr = (TraceCommandInfo *)
1583                                tracePtr->clientData;
1584
1585                        tcmdPtr->curFlags = traceFlags;
1586                        tcmdPtr->curCode = code;
1587                    }
1588                    traceCode = (tracePtr->proc)(tracePtr->clientData,
1589                            interp, curLevel, command, (Tcl_Command) cmdPtr,
1590                            objc, objv);
1591                }
1592            } else {
1593                /*
1594                 * Old-style trace.
1595                 */
1596
1597                if (traceFlags & TCL_TRACE_ENTER_EXEC) {
1598                    /*
1599                     * Old-style interpreter-wide traces only trigger before
1600                     * the command is executed.
1601                     */
1602
1603                    traceCode = CallTraceFunction(interp, tracePtr, cmdPtr,
1604                            command, numChars, objc, objv);
1605                }
1606            }
1607            tracePtr->flags &= ~TCL_TRACE_EXEC_IN_PROGRESS;
1608            Tcl_Release((ClientData) tracePtr);
1609        }
1610    }
1611    iPtr->activeInterpTracePtr = active.nextPtr;
1612    if (state) {
1613        if (traceCode == TCL_OK) {
1614            (void) Tcl_RestoreInterpState(interp, state);
1615        } else {
1616            Tcl_DiscardInterpState(state);
1617        }
1618    }
1619
1620    return(traceCode);
1621}
1622
1623/*
1624 *----------------------------------------------------------------------
1625 *
1626 * CallTraceFunction --
1627 *
1628 *      Invokes a trace function registered with an interpreter. These
1629 *      functions trace command execution. Currently this trace function is
1630 *      called with the address of the string-based Tcl_CmdProc for the
1631 *      command, not the Tcl_ObjCmdProc.
1632 *
1633 * Results:
1634 *      None.
1635 *
1636 * Side effects:
1637 *      Those side effects made by the trace function.
1638 *
1639 *----------------------------------------------------------------------
1640 */
1641
1642static int
1643CallTraceFunction(
1644    Tcl_Interp *interp,         /* The current interpreter. */
1645    register Trace *tracePtr,   /* Describes the trace function to call. */
1646    Command *cmdPtr,            /* Points to command's Command struct. */
1647    const char *command,        /* Points to the first character of the
1648                                 * command's source before substitutions. */
1649    int numChars,               /* The number of characters in the command's
1650                                 * source. */
1651    register int objc,          /* Number of arguments for the command. */
1652    Tcl_Obj *const objv[])      /* Pointers to Tcl_Obj of each argument. */
1653{
1654    Interp *iPtr = (Interp *) interp;
1655    char *commandCopy;
1656    int traceCode;
1657
1658    /*
1659     * Copy the command characters into a new string.
1660     */
1661
1662    commandCopy = TclStackAlloc(interp, (unsigned) (numChars + 1));
1663    memcpy(commandCopy, command, (size_t) numChars);
1664    commandCopy[numChars] = '\0';
1665
1666    /*
1667     * Call the trace function then free allocated storage.
1668     */
1669
1670    traceCode = (tracePtr->proc)(tracePtr->clientData, (Tcl_Interp *) iPtr,
1671            iPtr->numLevels, commandCopy, (Tcl_Command) cmdPtr, objc, objv);
1672
1673    TclStackFree(interp, commandCopy);
1674    return traceCode;
1675}
1676
1677/*
1678 *----------------------------------------------------------------------
1679 *
1680 * CommandObjTraceDeleted --
1681 *
1682 *      Ensure the trace is correctly deleted by decrementing its refCount and
1683 *      only deleting if no other references exist.
1684 *
1685 * Results:
1686 *      None.
1687 *
1688 * Side effects:
1689 *      May release memory.
1690 *
1691 *----------------------------------------------------------------------
1692 */
1693
1694static void
1695CommandObjTraceDeleted(
1696    ClientData clientData)
1697{
1698    TraceCommandInfo *tcmdPtr = (TraceCommandInfo *) clientData;
1699
1700    if ((--tcmdPtr->refCount) <= 0) {
1701        ckfree((char *) tcmdPtr);
1702    }
1703}
1704
1705/*
1706 *----------------------------------------------------------------------
1707 *
1708 * TraceExecutionProc --
1709 *
1710 *      This function is invoked whenever code relevant to a 'trace execution'
1711 *      command is executed. It is called in one of two ways in Tcl's core:
1712 *
1713 *      (i) by the TclCheckExecutionTraces, when an execution trace has been
1714 *      triggered.
1715 *      (ii) by TclCheckInterpTraces, when a prior execution trace has created
1716 *      a trace of the internals of a procedure, passing in this function as
1717 *      the one to be called.
1718 *
1719 * Results:
1720 *      The return value is a standard Tcl completion code such as TCL_OK or
1721 *      TCL_ERROR, etc.
1722 *
1723 * Side effects:
1724 *      May invoke an arbitrary Tcl procedure, and may create or delete an
1725 *      interpreter-wide trace.
1726 *
1727 *----------------------------------------------------------------------
1728 */
1729
1730static int
1731TraceExecutionProc(
1732    ClientData clientData,
1733    Tcl_Interp *interp,
1734    int level,
1735    const char *command,
1736    Tcl_Command cmdInfo,
1737    int objc,
1738    struct Tcl_Obj *const objv[])
1739{
1740    int call = 0;
1741    Interp *iPtr = (Interp *) interp;
1742    TraceCommandInfo *tcmdPtr = (TraceCommandInfo *) clientData;
1743    int flags = tcmdPtr->curFlags;
1744    int code = tcmdPtr->curCode;
1745    int traceCode = TCL_OK;
1746
1747    if (tcmdPtr->flags & TCL_TRACE_EXEC_IN_PROGRESS) {
1748        /*
1749         * Inside any kind of execution trace callback, we do not allow any
1750         * further execution trace callbacks to be called for the same trace.
1751         */
1752
1753        return traceCode;
1754    }
1755
1756    if (!Tcl_InterpDeleted(interp) && !Tcl_LimitExceeded(interp)) {
1757        /*
1758         * Check whether the current call is going to eval arbitrary Tcl code
1759         * with a generated trace, or whether we are only going to setup
1760         * interpreter-wide traces to implement the 'step' traces. This latter
1761         * situation can happen if we create a command trace without either
1762         * before or after operations, but with either of the step operations.
1763         */
1764
1765        if (flags & TCL_TRACE_EXEC_DIRECT) {
1766            call = flags & tcmdPtr->flags &
1767                    (TCL_TRACE_ENTER_EXEC | TCL_TRACE_LEAVE_EXEC);
1768        } else {
1769            call = 1;
1770        }
1771
1772        /*
1773         * First, if we have returned back to the level at which we created an
1774         * interpreter trace for enterstep and/or leavestep execution traces,
1775         * we remove it here.
1776         */
1777
1778        if ((flags & TCL_TRACE_LEAVE_EXEC) && (tcmdPtr->stepTrace != NULL)
1779                && (level == tcmdPtr->startLevel)
1780                && (strcmp(command, tcmdPtr->startCmd) == 0)) {
1781            Tcl_DeleteTrace(interp, tcmdPtr->stepTrace);
1782            tcmdPtr->stepTrace = NULL;
1783            if (tcmdPtr->startCmd != NULL) {
1784                ckfree((char *) tcmdPtr->startCmd);
1785            }
1786        }
1787
1788        /*
1789         * Second, create the tcl callback, if required.
1790         */
1791
1792        if (call) {
1793            Tcl_DString cmd;
1794            Tcl_DString sub;
1795            int i, saveInterpFlags;
1796
1797            Tcl_DStringInit(&cmd);
1798            Tcl_DStringAppend(&cmd, tcmdPtr->command, (int)tcmdPtr->length);
1799
1800            /*
1801             * Append command with arguments.
1802             */
1803
1804            Tcl_DStringInit(&sub);
1805            for (i = 0; i < objc; i++) {
1806                Tcl_DStringAppendElement(&sub, Tcl_GetString(objv[i]));
1807            }
1808            Tcl_DStringAppendElement(&cmd, Tcl_DStringValue(&sub));
1809            Tcl_DStringFree(&sub);
1810
1811            if (flags & TCL_TRACE_ENTER_EXEC) {
1812                /*
1813                 * Append trace operation.
1814                 */
1815
1816                if (flags & TCL_TRACE_EXEC_DIRECT) {
1817                    Tcl_DStringAppendElement(&cmd, "enter");
1818                } else {
1819                    Tcl_DStringAppendElement(&cmd, "enterstep");
1820                }
1821            } else if (flags & TCL_TRACE_LEAVE_EXEC) {
1822                Tcl_Obj *resultCode;
1823                char *resultCodeStr;
1824
1825                /*
1826                 * Append result code.
1827                 */
1828
1829                resultCode = Tcl_NewIntObj(code);
1830                resultCodeStr = Tcl_GetString(resultCode);
1831                Tcl_DStringAppendElement(&cmd, resultCodeStr);
1832                Tcl_DecrRefCount(resultCode);
1833
1834                /*
1835                 * Append result string.
1836                 */
1837
1838                Tcl_DStringAppendElement(&cmd, Tcl_GetStringResult(interp));
1839
1840                /*
1841                 * Append trace operation.
1842                 */
1843
1844                if (flags & TCL_TRACE_EXEC_DIRECT) {
1845                    Tcl_DStringAppendElement(&cmd, "leave");
1846                } else {
1847                    Tcl_DStringAppendElement(&cmd, "leavestep");
1848                }
1849            } else {
1850                Tcl_Panic("TraceExecutionProc: bad flag combination");
1851            }
1852
1853            /*
1854             * Execute the command. We discard any object result the command
1855             * returns.
1856             */
1857
1858            saveInterpFlags = iPtr->flags;
1859            iPtr->flags    |= INTERP_TRACE_IN_PROGRESS;
1860            tcmdPtr->flags |= TCL_TRACE_EXEC_IN_PROGRESS;
1861            tcmdPtr->refCount++;
1862
1863            /*
1864             * This line can have quite arbitrary side-effects, including
1865             * deleting the trace, the command being traced, or even the
1866             * interpreter.
1867             */
1868
1869            traceCode = Tcl_Eval(interp, Tcl_DStringValue(&cmd));
1870            tcmdPtr->flags &= ~TCL_TRACE_EXEC_IN_PROGRESS;
1871
1872            /*
1873             * Restore the interp tracing flag to prevent cmd traces from
1874             * affecting interp traces.
1875             */
1876
1877            iPtr->flags = saveInterpFlags;
1878            if (tcmdPtr->flags == 0) {
1879                flags |= TCL_TRACE_DESTROYED;
1880            }
1881            Tcl_DStringFree(&cmd);
1882        }
1883
1884        /*
1885         * Third, if there are any step execution traces for this proc, we
1886         * register an interpreter trace to invoke enterstep and/or leavestep
1887         * traces. We also need to save the current stack level and the proc
1888         * string in startLevel and startCmd so that we can delete this
1889         * interpreter trace when it reaches the end of this proc.
1890         */
1891
1892        if ((flags & TCL_TRACE_ENTER_EXEC) && (tcmdPtr->stepTrace == NULL)
1893                && (tcmdPtr->flags & (TCL_TRACE_ENTER_DURING_EXEC |
1894                        TCL_TRACE_LEAVE_DURING_EXEC))) {
1895            register unsigned len = strlen(command) + 1;
1896
1897            tcmdPtr->startLevel = level;
1898            tcmdPtr->startCmd = ckalloc(len);
1899            memcpy(tcmdPtr->startCmd, command, len);
1900            tcmdPtr->refCount++;
1901            tcmdPtr->stepTrace = Tcl_CreateObjTrace(interp, 0,
1902                   (tcmdPtr->flags & TCL_TRACE_ANY_EXEC) >> 2,
1903                   TraceExecutionProc, (ClientData)tcmdPtr,
1904                   CommandObjTraceDeleted);
1905        }
1906    }
1907    if (flags & TCL_TRACE_DESTROYED) {
1908        if (tcmdPtr->stepTrace != NULL) {
1909            Tcl_DeleteTrace(interp, tcmdPtr->stepTrace);
1910            tcmdPtr->stepTrace = NULL;
1911            if (tcmdPtr->startCmd != NULL) {
1912                ckfree(tcmdPtr->startCmd);
1913            }
1914        }
1915    }
1916    if (call) {
1917        if ((--tcmdPtr->refCount) <= 0) {
1918            ckfree((char *) tcmdPtr);
1919        }
1920    }
1921    return traceCode;
1922}
1923
1924/*
1925 *----------------------------------------------------------------------
1926 *
1927 * TraceVarProc --
1928 *
1929 *      This function is called to handle variable accesses that have been
1930 *      traced using the "trace" command.
1931 *
1932 * Results:
1933 *      Normally returns NULL. If the trace command returns an error, then
1934 *      this function returns an error string.
1935 *
1936 * Side effects:
1937 *      Depends on the command associated with the trace.
1938 *
1939 *----------------------------------------------------------------------
1940 */
1941
1942        /* ARGSUSED */
1943static char *
1944TraceVarProc(
1945    ClientData clientData,      /* Information about the variable trace. */
1946    Tcl_Interp *interp,         /* Interpreter containing variable. */
1947    const char *name1,          /* Name of variable or array. */
1948    const char *name2,          /* Name of element within array; NULL means
1949                                 * scalar variable is being referenced. */
1950    int flags)                  /* OR-ed bits giving operation and other
1951                                 * information. */
1952{
1953    TraceVarInfo *tvarPtr = (TraceVarInfo *) clientData;
1954    char *result;
1955    int code, destroy = 0;
1956    Tcl_DString cmd;
1957
1958    /*
1959     * We might call Tcl_Eval() below, and that might evaluate [trace vdelete]
1960     * which might try to free tvarPtr. We want to use tvarPtr until the end
1961     * of this function, so we use Tcl_Preserve() and Tcl_Release() to be sure
1962     * it is not freed while we still need it.
1963     */
1964
1965    result = NULL;
1966    if ((tvarPtr->flags & flags) && !Tcl_InterpDeleted(interp)
1967            && !Tcl_LimitExceeded(interp)) {
1968        if (tvarPtr->length != (size_t) 0) {
1969            /*
1970             * Generate a command to execute by appending list elements for
1971             * the two variable names and the operation.
1972             */
1973
1974            Tcl_DStringInit(&cmd);
1975            Tcl_DStringAppend(&cmd, tvarPtr->command, (int) tvarPtr->length);
1976            Tcl_DStringAppendElement(&cmd, name1);
1977            Tcl_DStringAppendElement(&cmd, (name2 ? name2 : ""));
1978#ifndef TCL_REMOVE_OBSOLETE_TRACES
1979            if (tvarPtr->flags & TCL_TRACE_OLD_STYLE) {
1980                if (flags & TCL_TRACE_ARRAY) {
1981                    Tcl_DStringAppend(&cmd, " a", 2);
1982                } else if (flags & TCL_TRACE_READS) {
1983                    Tcl_DStringAppend(&cmd, " r", 2);
1984                } else if (flags & TCL_TRACE_WRITES) {
1985                    Tcl_DStringAppend(&cmd, " w", 2);
1986                } else if (flags & TCL_TRACE_UNSETS) {
1987                    Tcl_DStringAppend(&cmd, " u", 2);
1988                }
1989            } else {
1990#endif
1991                if (flags & TCL_TRACE_ARRAY) {
1992                    Tcl_DStringAppend(&cmd, " array", 6);
1993                } else if (flags & TCL_TRACE_READS) {
1994                    Tcl_DStringAppend(&cmd, " read", 5);
1995                } else if (flags & TCL_TRACE_WRITES) {
1996                    Tcl_DStringAppend(&cmd, " write", 6);
1997                } else if (flags & TCL_TRACE_UNSETS) {
1998                    Tcl_DStringAppend(&cmd, " unset", 6);
1999                }
2000#ifndef TCL_REMOVE_OBSOLETE_TRACES
2001            }
2002#endif
2003
2004            /*
2005             * Execute the command. We discard any object result the command
2006             * returns.
2007             *
2008             * Add the TCL_TRACE_DESTROYED flag to tvarPtr to indicate to
2009             * other areas that this will be destroyed by us, otherwise a
2010             * double-free might occur depending on what the eval does.
2011             */
2012
2013            if ((flags & TCL_TRACE_DESTROYED)
2014                    && !(tvarPtr->flags & TCL_TRACE_DESTROYED)) {
2015                destroy = 1;
2016                tvarPtr->flags |= TCL_TRACE_DESTROYED;
2017            }
2018            code = Tcl_EvalEx(interp, Tcl_DStringValue(&cmd),
2019                    Tcl_DStringLength(&cmd), 0);
2020            if (code != TCL_OK) {               /* copy error msg to result */
2021                Tcl_Obj *errMsgObj = Tcl_GetObjResult(interp);
2022                Tcl_IncrRefCount(errMsgObj);
2023                result = (char *) errMsgObj;
2024            }
2025            Tcl_DStringFree(&cmd);
2026        }
2027    }
2028    if (destroy && result != NULL) {
2029        register Tcl_Obj *errMsgObj = (Tcl_Obj *) result;
2030
2031        Tcl_DecrRefCount(errMsgObj);
2032        result = NULL;
2033    }
2034    return result;
2035}
2036
2037/*
2038 *----------------------------------------------------------------------
2039 *
2040 * Tcl_CreateObjTrace --
2041 *
2042 *      Arrange for a function to be called to trace command execution.
2043 *
2044 * Results:
2045 *      The return value is a token for the trace, which may be passed to
2046 *      Tcl_DeleteTrace to eliminate the trace.
2047 *
2048 * Side effects:
2049 *      From now on, proc will be called just before a command function is
2050 *      called to execute a Tcl command. Calls to proc will have the following
2051 *      form:
2052 *
2053 *      void proc(ClientData     clientData,
2054 *                Tcl_Interp *   interp,
2055 *                int            level,
2056 *                const char *   command,
2057 *                Tcl_Command    commandInfo,
2058 *                int            objc,
2059 *                Tcl_Obj *const objv[]);
2060 *
2061 *      The 'clientData' and 'interp' arguments to 'proc' will be the same as
2062 *      the arguments to Tcl_CreateObjTrace. The 'level' argument gives the
2063 *      nesting depth of command interpretation within the interpreter. The
2064 *      'command' argument is the ASCII text of the command being evaluated -
2065 *      before any substitutions are performed. The 'commandInfo' argument
2066 *      gives a handle to the command procedure that will be evaluated. The
2067 *      'objc' and 'objv' parameters give the parameter vector that will be
2068 *      passed to the command procedure. Proc does not return a value.
2069 *
2070 *      It is permissible for 'proc' to call Tcl_SetCommandTokenInfo to change
2071 *      the command procedure or client data for the command being evaluated,
2072 *      and these changes will take effect with the current evaluation.
2073 *
2074 *      The 'level' argument specifies the maximum nesting level of calls to
2075 *      be traced. If the execution depth of the interpreter exceeds 'level',
2076 *      the trace callback is not executed.
2077 *
2078 *      The 'flags' argument is either zero or the value,
2079 *      TCL_ALLOW_INLINE_COMPILATION. If the TCL_ALLOW_INLINE_COMPILATION flag
2080 *      is not present, the bytecode compiler will not generate inline code
2081 *      for Tcl's built-in commands. This behavior will have a significant
2082 *      impact on performance, but will ensure that all command evaluations
2083 *      are traced. If the TCL_ALLOW_INLINE_COMPILATION flag is present, the
2084 *      bytecode compiler will have its normal behavior of compiling in-line
2085 *      code for some of Tcl's built-in commands. In this case, the tracing
2086 *      will be imprecise - in-line code will not be traced - but run-time
2087 *      performance will be improved. The latter behavior is desired for many
2088 *      applications such as profiling of run time.
2089 *
2090 *      When the trace is deleted, the 'delProc' function will be invoked,
2091 *      passing it the original client data.
2092 *
2093 *----------------------------------------------------------------------
2094 */
2095
2096Tcl_Trace
2097Tcl_CreateObjTrace(
2098    Tcl_Interp *interp,         /* Tcl interpreter */
2099    int level,                  /* Maximum nesting level */
2100    int flags,                  /* Flags, see above */
2101    Tcl_CmdObjTraceProc *proc,  /* Trace callback */
2102    ClientData clientData,      /* Client data for the callback */
2103    Tcl_CmdObjTraceDeleteProc *delProc)
2104                                /* Function to call when trace is deleted */
2105{
2106    register Trace *tracePtr;
2107    register Interp *iPtr = (Interp *) interp;
2108
2109    /*
2110     * Test if this trace allows inline compilation of commands.
2111     */
2112
2113    if (!(flags & TCL_ALLOW_INLINE_COMPILATION)) {
2114        if (iPtr->tracesForbiddingInline == 0) {
2115            /*
2116             * When the first trace forbidding inline compilation is created,
2117             * invalidate existing compiled code for this interpreter and
2118             * arrange (by setting the DONT_COMPILE_CMDS_INLINE flag) that
2119             * when compiling new code, no commands will be compiled inline
2120             * (i.e., into an inline sequence of instructions). We do this
2121             * because commands that were compiled inline will never result in
2122             * a command trace being called.
2123             */
2124
2125            iPtr->compileEpoch++;
2126            iPtr->flags |= DONT_COMPILE_CMDS_INLINE;
2127        }
2128        iPtr->tracesForbiddingInline++;
2129    }
2130
2131    tracePtr = (Trace *) ckalloc(sizeof(Trace));
2132    tracePtr->level = level;
2133    tracePtr->proc = proc;
2134    tracePtr->clientData = clientData;
2135    tracePtr->delProc = delProc;
2136    tracePtr->nextPtr = iPtr->tracePtr;
2137    tracePtr->flags = flags;
2138    iPtr->tracePtr = tracePtr;
2139
2140    return (Tcl_Trace) tracePtr;
2141}
2142
2143/*
2144 *----------------------------------------------------------------------
2145 *
2146 * Tcl_CreateTrace --
2147 *
2148 *      Arrange for a function to be called to trace command execution.
2149 *
2150 * Results:
2151 *      The return value is a token for the trace, which may be passed to
2152 *      Tcl_DeleteTrace to eliminate the trace.
2153 *
2154 * Side effects:
2155 *      From now on, proc will be called just before a command procedure is
2156 *      called to execute a Tcl command. Calls to proc will have the following
2157 *      form:
2158 *
2159 *      void
2160 *      proc(clientData, interp, level, command, cmdProc, cmdClientData,
2161 *              argc, argv)
2162 *          ClientData clientData;
2163 *          Tcl_Interp *interp;
2164 *          int level;
2165 *          char *command;
2166 *          int (*cmdProc)();
2167 *          ClientData cmdClientData;
2168 *          int argc;
2169 *          char **argv;
2170 *      {
2171 *      }
2172 *
2173 *      The clientData and interp arguments to proc will be the same as the
2174 *      corresponding arguments to this function. Level gives the nesting
2175 *      level of command interpretation for this interpreter (0 corresponds to
2176 *      top level). Command gives the ASCII text of the raw command, cmdProc
2177 *      and cmdClientData give the function that will be called to process the
2178 *      command and the ClientData value it will receive, and argc and argv
2179 *      give the arguments to the command, after any argument parsing and
2180 *      substitution. Proc does not return a value.
2181 *
2182 *----------------------------------------------------------------------
2183 */
2184
2185Tcl_Trace
2186Tcl_CreateTrace(
2187    Tcl_Interp *interp,         /* Interpreter in which to create trace. */
2188    int level,                  /* Only call proc for commands at nesting
2189                                 * level<=argument level (1=>top level). */
2190    Tcl_CmdTraceProc *proc,     /* Function to call before executing each
2191                                 * command. */
2192    ClientData clientData)      /* Arbitrary value word to pass to proc. */
2193{
2194    StringTraceData *data = (StringTraceData *)
2195            ckalloc(sizeof(StringTraceData));
2196
2197    data->clientData = clientData;
2198    data->proc = proc;
2199    return Tcl_CreateObjTrace(interp, level, 0, StringTraceProc,
2200            (ClientData) data, StringTraceDeleteProc);
2201}
2202
2203/*
2204 *----------------------------------------------------------------------
2205 *
2206 * StringTraceProc --
2207 *
2208 *      Invoke a string-based trace function from an object-based callback.
2209 *
2210 * Results:
2211 *      None.
2212 *
2213 * Side effects:
2214 *      Whatever the string-based trace function does.
2215 *
2216 *----------------------------------------------------------------------
2217 */
2218
2219static int
2220StringTraceProc(
2221    ClientData clientData,
2222    Tcl_Interp *interp,
2223    int level,
2224    const char *command,
2225    Tcl_Command commandInfo,
2226    int objc,
2227    Tcl_Obj *const *objv)
2228{
2229    StringTraceData *data = (StringTraceData *) clientData;
2230    Command *cmdPtr = (Command *) commandInfo;
2231    const char **argv;          /* Args to pass to string trace proc */
2232    int i;
2233
2234    /*
2235     * This is a bit messy because we have to emulate the old trace interface,
2236     * which uses strings for everything.
2237     */
2238
2239    argv = (const char **) TclStackAlloc(interp,
2240            (unsigned) ((objc + 1) * sizeof(const char *)));
2241    for (i = 0; i < objc; i++) {
2242        argv[i] = Tcl_GetString(objv[i]);
2243    }
2244    argv[objc] = 0;
2245
2246    /*
2247     * Invoke the command function. Note that we cast away const-ness on two
2248     * parameters for compatibility with legacy code; the code MUST NOT modify
2249     * either command or argv.
2250     */
2251
2252    (data->proc)(data->clientData, interp, level, (char *) command,
2253            cmdPtr->proc, cmdPtr->clientData, objc, argv);
2254    TclStackFree(interp, (void *) argv);
2255
2256    return TCL_OK;
2257}
2258
2259/*
2260 *----------------------------------------------------------------------
2261 *
2262 * StringTraceDeleteProc --
2263 *
2264 *      Clean up memory when a string-based trace is deleted.
2265 *
2266 * Results:
2267 *      None.
2268 *
2269 * Side effects:
2270 *      Allocated memory is returned to the system.
2271 *
2272 *----------------------------------------------------------------------
2273 */
2274
2275static void
2276StringTraceDeleteProc(
2277    ClientData clientData)
2278{
2279    ckfree((char *) clientData);
2280}
2281
2282/*
2283 *----------------------------------------------------------------------
2284 *
2285 * Tcl_DeleteTrace --
2286 *
2287 *      Remove a trace.
2288 *
2289 * Results:
2290 *      None.
2291 *
2292 * Side effects:
2293 *      From now on there will be no more calls to the function given in
2294 *      trace.
2295 *
2296 *----------------------------------------------------------------------
2297 */
2298
2299void
2300Tcl_DeleteTrace(
2301    Tcl_Interp *interp,         /* Interpreter that contains trace. */
2302    Tcl_Trace trace)            /* Token for trace (returned previously by
2303                                 * Tcl_CreateTrace). */
2304{
2305    Interp *iPtr = (Interp *) interp;
2306    Trace *prevPtr, *tracePtr = (Trace *) trace;
2307    register Trace **tracePtr2 = &(iPtr->tracePtr);
2308    ActiveInterpTrace *activePtr;
2309
2310    /*
2311     * Locate the trace entry in the interpreter's trace list, and remove it
2312     * from the list.
2313     */
2314
2315    prevPtr = NULL;
2316    while ((*tracePtr2) != NULL && (*tracePtr2) != tracePtr) {
2317        prevPtr = *tracePtr2;
2318        tracePtr2 = &((*tracePtr2)->nextPtr);
2319    }
2320    if (*tracePtr2 == NULL) {
2321        return;
2322    }
2323    (*tracePtr2) = (*tracePtr2)->nextPtr;
2324
2325    /*
2326     * The code below makes it possible to delete traces while traces are
2327     * active: it makes sure that the deleted trace won't be processed by
2328     * TclCheckInterpTraces.
2329     */
2330
2331    for (activePtr = iPtr->activeInterpTracePtr;  activePtr != NULL;
2332            activePtr = activePtr->nextPtr) {
2333        if (activePtr->nextTracePtr == tracePtr) {
2334            if (activePtr->reverseScan) {
2335                activePtr->nextTracePtr = prevPtr;
2336            } else {
2337                activePtr->nextTracePtr = tracePtr->nextPtr;
2338            }
2339        }
2340    }
2341
2342    /*
2343     * If the trace forbids bytecode compilation, change the interpreter's
2344     * state. If bytecode compilation is now permitted, flag the fact and
2345     * advance the compilation epoch so that procs will be recompiled to take
2346     * advantage of it.
2347     */
2348
2349    if (!(tracePtr->flags & TCL_ALLOW_INLINE_COMPILATION)) {
2350        iPtr->tracesForbiddingInline--;
2351        if (iPtr->tracesForbiddingInline == 0) {
2352            iPtr->flags &= ~DONT_COMPILE_CMDS_INLINE;
2353            iPtr->compileEpoch++;
2354        }
2355    }
2356
2357    /*
2358     * Execute any delete callback.
2359     */
2360
2361    if (tracePtr->delProc != NULL) {
2362        (tracePtr->delProc)(tracePtr->clientData);
2363    }
2364
2365    /*
2366     * Delete the trace object.
2367     */
2368
2369    Tcl_EventuallyFree((char *) tracePtr, TCL_DYNAMIC);
2370}
2371
2372/*
2373 *----------------------------------------------------------------------
2374 *
2375 * TclTraceVarExists --
2376 *
2377 *      This is called from info exists. We need to trigger read and/or array
2378 *      traces because they may end up creating a variable that doesn't
2379 *      currently exist.
2380 *
2381 * Results:
2382 *      A pointer to the Var structure, or NULL.
2383 *
2384 * Side effects:
2385 *      May fill in error messages in the interp.
2386 *
2387 *----------------------------------------------------------------------
2388 */
2389
2390Var *
2391TclVarTraceExists(
2392    Tcl_Interp *interp,         /* The interpreter */
2393    const char *varName)        /* The variable name */
2394{
2395    Var *varPtr;
2396    Var *arrayPtr;
2397
2398    /*
2399     * The choice of "create" flag values is delicate here, and matches the
2400     * semantics of GetVar. Things are still not perfect, however, because if
2401     * you do "info exists x" you get a varPtr and therefore trigger traces.
2402     * However, if you do "info exists x(i)", then you only get a varPtr if x
2403     * is already known to be an array. Otherwise you get NULL, and no trace
2404     * is triggered. This matches Tcl 7.6 semantics.
2405     */
2406
2407    varPtr = TclLookupVar(interp, varName, NULL, 0, "access",
2408            /*createPart1*/ 0, /*createPart2*/ 1, &arrayPtr);
2409
2410    if (varPtr == NULL) {
2411        return NULL;
2412    }
2413
2414    if ((varPtr->flags & VAR_TRACED_READ)
2415            || (arrayPtr && (arrayPtr->flags & VAR_TRACED_READ))) {
2416        TclCallVarTraces((Interp *)interp, arrayPtr, varPtr, varName, NULL,
2417                TCL_TRACE_READS, /* leaveErrMsg */ 0);
2418    }
2419
2420    /*
2421     * If the variable doesn't exist anymore and no-one's using it, then free
2422     * up the relevant structures and hash table entries.
2423     */
2424
2425    if (TclIsVarUndefined(varPtr)) {
2426        TclCleanupVar(varPtr, arrayPtr);
2427        return NULL;
2428    }
2429
2430    return varPtr;
2431}
2432
2433/*
2434 *----------------------------------------------------------------------
2435 *
2436 * TclCallVarTraces --
2437 *
2438 *      This function is invoked to find and invoke relevant trace functions
2439 *      associated with a particular operation on a variable. This function
2440 *      invokes traces both on the variable and on its containing array (where
2441 *      relevant).
2442 *
2443 * Results:
2444 *      Returns TCL_OK to indicate normal operation. Returns TCL_ERROR if
2445 *      invocation of a trace function indicated an error. When TCL_ERROR is
2446 *      returned and leaveErrMsg is true, then the errorInfo field of iPtr has
2447 *      information about the error placed in it.
2448 *
2449 * Side effects:
2450 *      Almost anything can happen, depending on trace; this function itself
2451 *      doesn't have any side effects.
2452 *
2453 *----------------------------------------------------------------------
2454 */
2455
2456int
2457TclObjCallVarTraces(
2458    Interp *iPtr,               /* Interpreter containing variable. */
2459    register Var *arrayPtr,     /* Pointer to array variable that contains the
2460                                 * variable, or NULL if the variable isn't an
2461                                 * element of an array. */
2462    Var *varPtr,                /* Variable whose traces are to be invoked. */
2463    Tcl_Obj *part1Ptr,
2464    Tcl_Obj *part2Ptr,          /* Variable's two-part name. */
2465    int flags,                  /* Flags passed to trace functions: indicates
2466                                 * what's happening to variable, plus maybe
2467                                 * TCL_GLOBAL_ONLY or TCL_NAMESPACE_ONLY */
2468    int leaveErrMsg,            /* If true, and one of the traces indicates an
2469                                 * error, then leave an error message and
2470                                 * stack trace information in *iPTr. */
2471    int index)                  /* Index into the local variable table of the
2472                                 * variable, or -1. Only used when part1Ptr is
2473                                 * NULL. */
2474{
2475    char *part1, *part2;
2476
2477    if (!part1Ptr) {
2478        part1Ptr = localName(iPtr->varFramePtr, index);
2479    }
2480    part1 = TclGetString(part1Ptr);
2481    part2 = part2Ptr? TclGetString(part2Ptr) : NULL;
2482
2483    return TclCallVarTraces(iPtr, arrayPtr, varPtr, part1, part2, flags,
2484            leaveErrMsg);
2485}
2486
2487int
2488TclCallVarTraces(
2489    Interp *iPtr,               /* Interpreter containing variable. */
2490    register Var *arrayPtr,     /* Pointer to array variable that contains the
2491                                 * variable, or NULL if the variable isn't an
2492                                 * element of an array. */
2493    Var *varPtr,                /* Variable whose traces are to be invoked. */
2494    const char *part1,
2495    const char *part2,          /* Variable's two-part name. */
2496    int flags,                  /* Flags passed to trace functions: indicates
2497                                 * what's happening to variable, plus maybe
2498                                 * TCL_GLOBAL_ONLY or TCL_NAMESPACE_ONLY */
2499    int leaveErrMsg)            /* If true, and one of the traces indicates an
2500                                 * error, then leave an error message and
2501                                 * stack trace information in *iPTr. */
2502{
2503    register VarTrace *tracePtr;
2504    ActiveVarTrace active;
2505    char *result;
2506    const char *openParen, *p;
2507    Tcl_DString nameCopy;
2508    int copiedName;
2509    int code = TCL_OK;
2510    int disposeFlags = 0;
2511    Tcl_InterpState state = NULL;
2512    Tcl_HashEntry *hPtr;
2513    int traceflags = flags & VAR_ALL_TRACES;
2514
2515    /*
2516     * If there are already similar trace functions active for the variable,
2517     * don't call them again.
2518     */
2519
2520    if (TclIsVarTraceActive(varPtr)) {
2521        return code;
2522    }
2523    TclSetVarTraceActive(varPtr);
2524    if (TclIsVarInHash(varPtr)) {
2525        VarHashRefCount(varPtr)++;
2526    }
2527    if (arrayPtr && TclIsVarInHash(arrayPtr)) {
2528        VarHashRefCount(arrayPtr)++;
2529    }
2530
2531    /*
2532     * If the variable name hasn't been parsed into array name and element, do
2533     * it here. If there really is an array element, make a copy of the
2534     * original name so that NULLs can be inserted into it to separate the
2535     * names (can't modify the name string in place, because the string might
2536     * get used by the callbacks we invoke).
2537     */
2538
2539    copiedName = 0;
2540    if (part2 == NULL) {
2541        for (p = part1; *p ; p++) {
2542            if (*p == '(') {
2543                openParen = p;
2544                do {
2545                    p++;
2546                } while (*p != '\0');
2547                p--;
2548                if (*p == ')') {
2549                    int offset = (openParen - part1);
2550                    char *newPart1;
2551
2552                    Tcl_DStringInit(&nameCopy);
2553                    Tcl_DStringAppend(&nameCopy, part1, (p-part1));
2554                    newPart1 = Tcl_DStringValue(&nameCopy);
2555                    newPart1[offset] = 0;
2556                    part1 = newPart1;
2557                    part2 = newPart1 + offset + 1;
2558                    copiedName = 1;
2559                }
2560                break;
2561            }
2562        }
2563    }
2564
2565    /*
2566     * Ignore any caller-provided TCL_INTERP_DESTROYED flag.  Only we can
2567     * set it correctly.
2568     */
2569
2570    flags &= ~TCL_INTERP_DESTROYED;
2571
2572    /*
2573     * Invoke traces on the array containing the variable, if relevant.
2574     */
2575
2576    result = NULL;
2577    active.nextPtr = iPtr->activeVarTracePtr;
2578    iPtr->activeVarTracePtr = &active;
2579    Tcl_Preserve((ClientData) iPtr);
2580    if (arrayPtr && !TclIsVarTraceActive(arrayPtr)
2581            && (arrayPtr->flags & traceflags)) {
2582        hPtr = Tcl_FindHashEntry(&iPtr->varTraces, (char *) arrayPtr);
2583        active.varPtr = arrayPtr;
2584        for (tracePtr = (VarTrace *) Tcl_GetHashValue(hPtr);
2585             tracePtr != NULL; tracePtr = active.nextTracePtr) {
2586            active.nextTracePtr = tracePtr->nextPtr;
2587            if (!(tracePtr->flags & flags)) {
2588                continue;
2589            }
2590            Tcl_Preserve((ClientData) tracePtr);
2591            if (state == NULL) {
2592                state = Tcl_SaveInterpState((Tcl_Interp *)iPtr, code);
2593            }
2594            if (Tcl_InterpDeleted((Tcl_Interp *)iPtr)) {
2595                flags |= TCL_INTERP_DESTROYED;
2596            }
2597            result = (*tracePtr->traceProc)(tracePtr->clientData,
2598                    (Tcl_Interp *) iPtr, part1, part2, flags);
2599            if (result != NULL) {
2600                if (flags & TCL_TRACE_UNSETS) {
2601                    /*
2602                     * Ignore errors in unset traces.
2603                     */
2604
2605                    DisposeTraceResult(tracePtr->flags, result);
2606                } else {
2607                    disposeFlags = tracePtr->flags;
2608                    code = TCL_ERROR;
2609                }
2610            }
2611            Tcl_Release((ClientData) tracePtr);
2612            if (code == TCL_ERROR) {
2613                goto done;
2614            }
2615        }
2616    }
2617
2618    /*
2619     * Invoke traces on the variable itself.
2620     */
2621
2622    if (flags & TCL_TRACE_UNSETS) {
2623        flags |= TCL_TRACE_DESTROYED;
2624    }
2625    active.varPtr = varPtr;
2626    if (varPtr->flags & traceflags) {
2627        hPtr = Tcl_FindHashEntry(&iPtr->varTraces, (char *) varPtr);
2628        for (tracePtr = (VarTrace *) Tcl_GetHashValue(hPtr);
2629             tracePtr != NULL; tracePtr = active.nextTracePtr) {
2630            active.nextTracePtr = tracePtr->nextPtr;
2631            if (!(tracePtr->flags & flags)) {
2632                continue;
2633            }
2634            Tcl_Preserve((ClientData) tracePtr);
2635            if (state == NULL) {
2636                state = Tcl_SaveInterpState((Tcl_Interp *)iPtr, code);
2637            }
2638            if (Tcl_InterpDeleted((Tcl_Interp *)iPtr)) {
2639                flags |= TCL_INTERP_DESTROYED;
2640            }
2641            result = (*tracePtr->traceProc)(tracePtr->clientData,
2642                    (Tcl_Interp *) iPtr, part1, part2, flags);
2643            if (result != NULL) {
2644                if (flags & TCL_TRACE_UNSETS) {
2645                    /*
2646                     * Ignore errors in unset traces.
2647                     */
2648
2649                    DisposeTraceResult(tracePtr->flags, result);
2650                } else {
2651                    disposeFlags = tracePtr->flags;
2652                    code = TCL_ERROR;
2653                }
2654            }
2655            Tcl_Release((ClientData) tracePtr);
2656            if (code == TCL_ERROR) {
2657                goto done;
2658            }
2659        }
2660    }
2661
2662    /*
2663     * Restore the variable's flags, remove the record of our active traces,
2664     * and then return.
2665     */
2666
2667  done:
2668    if (code == TCL_ERROR) {
2669        if (leaveErrMsg) {
2670            const char *type = "";
2671            Tcl_Obj *options = Tcl_GetReturnOptions((Tcl_Interp *)iPtr, code);
2672            Tcl_Obj *errorInfoKey, *errorInfo;
2673
2674            TclNewLiteralStringObj(errorInfoKey, "-errorinfo");
2675            Tcl_IncrRefCount(errorInfoKey);
2676            Tcl_DictObjGet(NULL, options, errorInfoKey, &errorInfo);
2677            Tcl_IncrRefCount(errorInfo);
2678            Tcl_DictObjRemove(NULL, options, errorInfoKey);
2679            if (Tcl_IsShared(errorInfo)) {
2680                Tcl_DecrRefCount(errorInfo);
2681                errorInfo = Tcl_DuplicateObj(errorInfo);
2682                Tcl_IncrRefCount(errorInfo);
2683            }
2684            Tcl_AppendToObj(errorInfo, "\n    (", -1);
2685            switch (flags&(TCL_TRACE_READS|TCL_TRACE_WRITES|TCL_TRACE_ARRAY)) {
2686            case TCL_TRACE_READS:
2687                type = "read";
2688                Tcl_AppendToObj(errorInfo, type, -1);
2689                break;
2690            case TCL_TRACE_WRITES:
2691                type = "set";
2692                Tcl_AppendToObj(errorInfo, "write", -1);
2693                break;
2694            case TCL_TRACE_ARRAY:
2695                type = "trace array";
2696                Tcl_AppendToObj(errorInfo, "array", -1);
2697                break;
2698            }
2699            if (disposeFlags & TCL_TRACE_RESULT_OBJECT) {
2700                TclVarErrMsg((Tcl_Interp *) iPtr, part1, part2, type,
2701                        Tcl_GetString((Tcl_Obj *) result));
2702            } else {
2703                TclVarErrMsg((Tcl_Interp *) iPtr, part1, part2, type, result);
2704            }
2705            Tcl_AppendToObj(errorInfo, " trace on \"", -1);
2706            Tcl_AppendToObj(errorInfo, part1, -1);
2707            if (part2 != NULL) {
2708                Tcl_AppendToObj(errorInfo, "(", -1);
2709                Tcl_AppendToObj(errorInfo, part1, -1);
2710                Tcl_AppendToObj(errorInfo, ")", -1);
2711            }
2712            Tcl_AppendToObj(errorInfo, "\")", -1);
2713            Tcl_DictObjPut(NULL, options, errorInfoKey, errorInfo);
2714            Tcl_DecrRefCount(errorInfoKey);
2715            Tcl_DecrRefCount(errorInfo);
2716            code = Tcl_SetReturnOptions((Tcl_Interp *)iPtr, options);
2717            iPtr->flags &= ~(ERR_ALREADY_LOGGED);
2718            Tcl_DiscardInterpState(state);
2719        } else {
2720            (void) Tcl_RestoreInterpState((Tcl_Interp *)iPtr, state);
2721        }
2722        DisposeTraceResult(disposeFlags,result);
2723    } else if (state) {
2724        if (code == TCL_OK) {
2725            code = Tcl_RestoreInterpState((Tcl_Interp *)iPtr, state);
2726        } else {
2727            Tcl_DiscardInterpState(state);
2728        }
2729    }
2730
2731    if (arrayPtr && TclIsVarInHash(arrayPtr)) {
2732        VarHashRefCount(arrayPtr)--;
2733    }
2734    if (copiedName) {
2735        Tcl_DStringFree(&nameCopy);
2736    }
2737    TclClearVarTraceActive(varPtr);
2738    if (TclIsVarInHash(varPtr)) {
2739        VarHashRefCount(varPtr)--;
2740    }
2741    iPtr->activeVarTracePtr = active.nextPtr;
2742    Tcl_Release((ClientData) iPtr);
2743    return code;
2744}
2745
2746/*
2747 *----------------------------------------------------------------------
2748 *
2749 * DisposeTraceResult--
2750 *
2751 *      This function is called to dispose of the result returned from a trace
2752 *      function. The disposal method appropriate to the type of result is
2753 *      determined by flags.
2754 *
2755 * Results:
2756 *      None.
2757 *
2758 * Side effects:
2759 *      The memory allocated for the trace result may be freed.
2760 *
2761 *----------------------------------------------------------------------
2762 */
2763
2764static void
2765DisposeTraceResult(
2766    int flags,                  /* Indicates type of result to determine
2767                                 * proper disposal method. */
2768    char *result)               /* The result returned from a trace function
2769                                 * to be disposed. */
2770{
2771    if (flags & TCL_TRACE_RESULT_DYNAMIC) {
2772        ckfree(result);
2773    } else if (flags & TCL_TRACE_RESULT_OBJECT) {
2774        Tcl_DecrRefCount((Tcl_Obj *) result);
2775    }
2776}
2777
2778/*
2779 *----------------------------------------------------------------------
2780 *
2781 * Tcl_UntraceVar --
2782 *
2783 *      Remove a previously-created trace for a variable.
2784 *
2785 * Results:
2786 *      None.
2787 *
2788 * Side effects:
2789 *      If there exists a trace for the variable given by varName with the
2790 *      given flags, proc, and clientData, then that trace is removed.
2791 *
2792 *----------------------------------------------------------------------
2793 */
2794
2795void
2796Tcl_UntraceVar(
2797    Tcl_Interp *interp,         /* Interpreter containing variable. */
2798    const char *varName,        /* Name of variable; may end with "(index)" to
2799                                 * signify an array reference. */
2800    int flags,                  /* OR-ed collection of bits describing current
2801                                 * trace, including any of TCL_TRACE_READS,
2802                                 * TCL_TRACE_WRITES, TCL_TRACE_UNSETS,
2803                                 * TCL_GLOBAL_ONLY and TCL_NAMESPACE_ONLY. */
2804    Tcl_VarTraceProc *proc,     /* Function assocated with trace. */
2805    ClientData clientData)      /* Arbitrary argument to pass to proc. */
2806{
2807    Tcl_UntraceVar2(interp, varName, NULL, flags, proc, clientData);
2808}
2809
2810/*
2811 *----------------------------------------------------------------------
2812 *
2813 * Tcl_UntraceVar2 --
2814 *
2815 *      Remove a previously-created trace for a variable.
2816 *
2817 * Results:
2818 *      None.
2819 *
2820 * Side effects:
2821 *      If there exists a trace for the variable given by part1 and part2 with
2822 *      the given flags, proc, and clientData, then that trace is removed.
2823 *
2824 *----------------------------------------------------------------------
2825 */
2826
2827void
2828Tcl_UntraceVar2(
2829    Tcl_Interp *interp,         /* Interpreter containing variable. */
2830    const char *part1,          /* Name of variable or array. */
2831    const char *part2,          /* Name of element within array; NULL means
2832                                 * trace applies to scalar variable or array
2833                                 * as-a-whole. */
2834    int flags,                  /* OR-ed collection of bits describing current
2835                                 * trace, including any of TCL_TRACE_READS,
2836                                 * TCL_TRACE_WRITES, TCL_TRACE_UNSETS,
2837                                 * TCL_GLOBAL_ONLY, and TCL_NAMESPACE_ONLY. */
2838    Tcl_VarTraceProc *proc,     /* Function assocated with trace. */
2839    ClientData clientData)      /* Arbitrary argument to pass to proc. */
2840{
2841    register VarTrace *tracePtr;
2842    VarTrace *prevPtr, *nextPtr;
2843    Var *varPtr, *arrayPtr;
2844    Interp *iPtr = (Interp *) interp;
2845    ActiveVarTrace *activePtr;
2846    int flagMask, allFlags = 0;
2847    Tcl_HashEntry *hPtr;
2848
2849    /*
2850     * Set up a mask to mask out the parts of the flags that we are not
2851     * interested in now.
2852     */
2853
2854    flagMask = TCL_GLOBAL_ONLY | TCL_NAMESPACE_ONLY;
2855    varPtr = TclLookupVar(interp, part1, part2, flags & flagMask, /*msg*/ NULL,
2856            /*createPart1*/ 0, /*createPart2*/ 0, &arrayPtr);
2857    if (varPtr == NULL || !(varPtr->flags & VAR_ALL_TRACES & flags)) {
2858        return;
2859    }
2860
2861    /*
2862     * Set up a mask to mask out the parts of the flags that we are not
2863     * interested in now.
2864     */
2865
2866    flagMask = TCL_TRACE_READS | TCL_TRACE_WRITES | TCL_TRACE_UNSETS |
2867          TCL_TRACE_ARRAY | TCL_TRACE_RESULT_DYNAMIC | TCL_TRACE_RESULT_OBJECT;
2868#ifndef TCL_REMOVE_OBSOLETE_TRACES
2869    flagMask |= TCL_TRACE_OLD_STYLE;
2870#endif
2871    flags &= flagMask;
2872
2873    hPtr = Tcl_FindHashEntry(&iPtr->varTraces,
2874            (char *) varPtr);
2875    for (tracePtr = (VarTrace *) Tcl_GetHashValue(hPtr), prevPtr = NULL; ;
2876            prevPtr = tracePtr, tracePtr = tracePtr->nextPtr) {
2877        if (tracePtr == NULL) {
2878            goto updateFlags;
2879        }
2880        if ((tracePtr->traceProc == proc) && (tracePtr->flags == flags)
2881                && (tracePtr->clientData == clientData)) {
2882            break;
2883        }
2884        allFlags |= tracePtr->flags;
2885    }
2886
2887    /*
2888     * The code below makes it possible to delete traces while traces are
2889     * active: it makes sure that the deleted trace won't be processed by
2890     * TclCallVarTraces.
2891     */
2892
2893    for (activePtr = iPtr->activeVarTracePtr;  activePtr != NULL;
2894            activePtr = activePtr->nextPtr) {
2895        if (activePtr->nextTracePtr == tracePtr) {
2896            activePtr->nextTracePtr = tracePtr->nextPtr;
2897        }
2898    }
2899    nextPtr = tracePtr->nextPtr;
2900    if (prevPtr == NULL) {
2901        if (nextPtr) {
2902            Tcl_SetHashValue(hPtr, nextPtr);
2903        } else {
2904            Tcl_DeleteHashEntry(hPtr);
2905        }
2906    } else {
2907        prevPtr->nextPtr = nextPtr;
2908    }
2909    Tcl_EventuallyFree((ClientData) tracePtr, TCL_DYNAMIC);
2910
2911    for (tracePtr = nextPtr; tracePtr != NULL;
2912            tracePtr = tracePtr->nextPtr) {
2913        allFlags |= tracePtr->flags;
2914    }
2915
2916  updateFlags:
2917    varPtr->flags &= ~VAR_ALL_TRACES;
2918    if (allFlags & VAR_ALL_TRACES) {
2919        varPtr->flags |= (allFlags & VAR_ALL_TRACES);
2920    } else if (TclIsVarUndefined(varPtr)) {
2921        /*
2922         * If this is the last trace on the variable, and the variable is
2923         * unset and unused, then free up the variable.
2924         */
2925
2926        TclCleanupVar(varPtr, NULL);
2927    }
2928}
2929
2930/*
2931 *----------------------------------------------------------------------
2932 *
2933 * Tcl_VarTraceInfo --
2934 *
2935 *      Return the clientData value associated with a trace on a variable.
2936 *      This function can also be used to step through all of the traces on a
2937 *      particular variable that have the same trace function.
2938 *
2939 * Results:
2940 *      The return value is the clientData value associated with a trace on
2941 *      the given variable. Information will only be returned for a trace with
2942 *      proc as trace function. If the clientData argument is NULL then the
2943 *      first such trace is returned; otherwise, the next relevant one after
2944 *      the one given by clientData will be returned. If the variable doesn't
2945 *      exist, or if there are no (more) traces for it, then NULL is returned.
2946 *
2947 * Side effects:
2948 *      None.
2949 *
2950 *----------------------------------------------------------------------
2951 */
2952
2953ClientData
2954Tcl_VarTraceInfo(
2955    Tcl_Interp *interp,         /* Interpreter containing variable. */
2956    const char *varName,        /* Name of variable; may end with "(index)" to
2957                                 * signify an array reference. */
2958    int flags,                  /* OR-ed combo or TCL_GLOBAL_ONLY,
2959                                 * TCL_NAMESPACE_ONLY (can be 0). */
2960    Tcl_VarTraceProc *proc,     /* Function assocated with trace. */
2961    ClientData prevClientData)  /* If non-NULL, gives last value returned by
2962                                 * this function, so this call will return the
2963                                 * next trace after that one. If NULL, this
2964                                 * call will return the first trace. */
2965{
2966    return Tcl_VarTraceInfo2(interp, varName, NULL, flags, proc,
2967            prevClientData);
2968}
2969
2970/*
2971 *----------------------------------------------------------------------
2972 *
2973 * Tcl_VarTraceInfo2 --
2974 *
2975 *      Same as Tcl_VarTraceInfo, except takes name in two pieces instead of
2976 *      one.
2977 *
2978 * Results:
2979 *      Same as Tcl_VarTraceInfo.
2980 *
2981 * Side effects:
2982 *      None.
2983 *
2984 *----------------------------------------------------------------------
2985 */
2986
2987ClientData
2988Tcl_VarTraceInfo2(
2989    Tcl_Interp *interp,         /* Interpreter containing variable. */
2990    const char *part1,          /* Name of variable or array. */
2991    const char *part2,          /* Name of element within array; NULL means
2992                                 * trace applies to scalar variable or array
2993                                 * as-a-whole. */
2994    int flags,                  /* OR-ed combination of TCL_GLOBAL_ONLY,
2995                                 * TCL_NAMESPACE_ONLY. */
2996    Tcl_VarTraceProc *proc,     /* Function assocated with trace. */
2997    ClientData prevClientData)  /* If non-NULL, gives last value returned by
2998                                 * this function, so this call will return the
2999                                 * next trace after that one. If NULL, this
3000                                 * call will return the first trace. */
3001{
3002    Interp *iPtr = (Interp *) interp;
3003    register VarTrace *tracePtr;
3004    Var *varPtr, *arrayPtr;
3005    Tcl_HashEntry *hPtr;
3006
3007    varPtr = TclLookupVar(interp, part1, part2,
3008            flags & (TCL_GLOBAL_ONLY|TCL_NAMESPACE_ONLY), /*msg*/ NULL,
3009            /*createPart1*/ 0, /*createPart2*/ 0, &arrayPtr);
3010    if (varPtr == NULL) {
3011        return NULL;
3012    }
3013
3014    /*
3015     * Find the relevant trace, if any, and return its clientData.
3016     */
3017
3018    hPtr = Tcl_FindHashEntry(&iPtr->varTraces,
3019            (char *) varPtr);
3020
3021    if (hPtr) {
3022        tracePtr = Tcl_GetHashValue(hPtr);
3023
3024        if (prevClientData != NULL) {
3025            for ( ;  tracePtr != NULL;  tracePtr = tracePtr->nextPtr) {
3026                if ((tracePtr->clientData == prevClientData)
3027                        && (tracePtr->traceProc == proc)) {
3028                    tracePtr = tracePtr->nextPtr;
3029                    break;
3030                }
3031            }
3032        }
3033        for (; tracePtr!=NULL ; tracePtr=tracePtr->nextPtr) {
3034            if (tracePtr->traceProc == proc) {
3035                return tracePtr->clientData;
3036            }
3037        }
3038    }
3039    return NULL;
3040}
3041
3042/*
3043 *----------------------------------------------------------------------
3044 *
3045 * Tcl_TraceVar --
3046 *
3047 *      Arrange for reads and/or writes to a variable to cause a function to
3048 *      be invoked, which can monitor the operations and/or change their
3049 *      actions.
3050 *
3051 * Results:
3052 *      A standard Tcl return value.
3053 *
3054 * Side effects:
3055 *      A trace is set up on the variable given by varName, such that future
3056 *      references to the variable will be intermediated by proc. See the
3057 *      manual entry for complete details on the calling sequence for proc.
3058 *     The variable's flags are updated.
3059 *
3060 *----------------------------------------------------------------------
3061 */
3062
3063int
3064Tcl_TraceVar(
3065    Tcl_Interp *interp,         /* Interpreter in which variable is to be
3066                                 * traced. */
3067    const char *varName,        /* Name of variable; may end with "(index)" to
3068                                 * signify an array reference. */
3069    int flags,                  /* OR-ed collection of bits, including any of
3070                                 * TCL_TRACE_READS, TCL_TRACE_WRITES,
3071                                 * TCL_TRACE_UNSETS, TCL_GLOBAL_ONLY, and
3072                                 * TCL_NAMESPACE_ONLY. */
3073    Tcl_VarTraceProc *proc,     /* Function to call when specified ops are
3074                                 * invoked upon varName. */
3075    ClientData clientData)      /* Arbitrary argument to pass to proc. */
3076{
3077    return Tcl_TraceVar2(interp, varName, NULL, flags, proc, clientData);
3078}
3079
3080/*
3081 *----------------------------------------------------------------------
3082 *
3083 * Tcl_TraceVar2 --
3084 *
3085 *      Arrange for reads and/or writes to a variable to cause a function to
3086 *      be invoked, which can monitor the operations and/or change their
3087 *      actions.
3088 *
3089 * Results:
3090 *      A standard Tcl return value.
3091 *
3092 * Side effects:
3093 *      A trace is set up on the variable given by part1 and part2, such that
3094 *      future references to the variable will be intermediated by proc. See
3095 *      the manual entry for complete details on the calling sequence for
3096 *      proc. The variable's flags are updated.
3097 *
3098 *----------------------------------------------------------------------
3099 */
3100
3101int
3102Tcl_TraceVar2(
3103    Tcl_Interp *interp,         /* Interpreter in which variable is to be
3104                                 * traced. */
3105    const char *part1,          /* Name of scalar variable or array. */
3106    const char *part2,          /* Name of element within array; NULL means
3107                                 * trace applies to scalar variable or array
3108                                 * as-a-whole. */
3109    int flags,                  /* OR-ed collection of bits, including any of
3110                                 * TCL_TRACE_READS, TCL_TRACE_WRITES,
3111                                 * TCL_TRACE_UNSETS, TCL_GLOBAL_ONLY, and
3112                                 * TCL_NAMESPACE_ONLY. */
3113    Tcl_VarTraceProc *proc,     /* Function to call when specified ops are
3114                                 * invoked upon varName. */
3115    ClientData clientData)      /* Arbitrary argument to pass to proc. */
3116{
3117    register VarTrace *tracePtr;
3118    int result;
3119
3120    tracePtr = (VarTrace *) ckalloc(sizeof(VarTrace));
3121    tracePtr->traceProc = proc;
3122    tracePtr->clientData = clientData;
3123    tracePtr->flags = flags;
3124
3125    result = TraceVarEx(interp, part1, part2, tracePtr);
3126
3127    if (result != TCL_OK) {
3128        ckfree((char *) tracePtr);
3129    }
3130    return result;
3131}
3132
3133/*
3134 *----------------------------------------------------------------------
3135 *
3136 * TraceVarEx --
3137 *
3138 *      Arrange for reads and/or writes to a variable to cause a function to
3139 *      be invoked, which can monitor the operations and/or change their
3140 *      actions.
3141 *
3142 * Results:
3143 *      A standard Tcl return value.
3144 *
3145 * Side effects:
3146 *      A trace is set up on the variable given by part1 and part2, such that
3147 *      future references to the variable will be intermediated by the
3148 *      traceProc listed in tracePtr. See the manual entry for complete
3149 *      details on the calling sequence for proc.
3150 *
3151 *----------------------------------------------------------------------
3152 */
3153
3154static int
3155TraceVarEx(
3156    Tcl_Interp *interp,         /* Interpreter in which variable is to be
3157                                 * traced. */
3158    const char *part1,          /* Name of scalar variable or array. */
3159    const char *part2,          /* Name of element within array; NULL means
3160                                 * trace applies to scalar variable or array
3161                                 * as-a-whole. */
3162    register VarTrace *tracePtr)/* Structure containing flags, traceProc and
3163                                 * clientData fields. Others should be left
3164                                 * blank. Will be ckfree()d (eventually) if
3165                                 * this function returns TCL_OK, and up to
3166                                 * caller to free if this function returns
3167                                 * TCL_ERROR. */
3168{
3169    Interp *iPtr = (Interp *) interp;
3170    Var *varPtr, *arrayPtr;
3171    int flagMask, isNew;
3172    Tcl_HashEntry *hPtr;
3173
3174    /*
3175     * We strip 'flags' down to just the parts which are relevant to
3176     * TclLookupVar, to avoid conflicts between trace flags and internal
3177     * namespace flags such as 'TCL_FIND_ONLY_NS'. This can now occur since we
3178     * have trace flags with values 0x1000 and higher.
3179     */
3180
3181    flagMask = TCL_GLOBAL_ONLY | TCL_NAMESPACE_ONLY;
3182    varPtr = TclLookupVar(interp, part1, part2,
3183            (tracePtr->flags & flagMask) | TCL_LEAVE_ERR_MSG,
3184            "trace", /*createPart1*/ 1, /*createPart2*/ 1, &arrayPtr);
3185    if (varPtr == NULL) {
3186        return TCL_ERROR;
3187    }
3188
3189    /*
3190     * Check for a nonsense flag combination. Note that this is a Tcl_Panic()
3191     * because there should be no code path that ever sets both flags.
3192     */
3193
3194    if ((tracePtr->flags&TCL_TRACE_RESULT_DYNAMIC)
3195            && (tracePtr->flags&TCL_TRACE_RESULT_OBJECT)) {
3196        Tcl_Panic("bad result flag combination");
3197    }
3198
3199    /*
3200     * Set up trace information.
3201     */
3202
3203    flagMask = TCL_TRACE_READS | TCL_TRACE_WRITES | TCL_TRACE_UNSETS |
3204          TCL_TRACE_ARRAY | TCL_TRACE_RESULT_DYNAMIC | TCL_TRACE_RESULT_OBJECT;
3205#ifndef TCL_REMOVE_OBSOLETE_TRACES
3206    flagMask |= TCL_TRACE_OLD_STYLE;
3207#endif
3208    tracePtr->flags = tracePtr->flags & flagMask;
3209
3210    hPtr = Tcl_CreateHashEntry(&iPtr->varTraces, (char *) varPtr, &isNew);
3211    if (isNew) {
3212        tracePtr->nextPtr = NULL;
3213    } else {
3214        tracePtr->nextPtr = (VarTrace *) Tcl_GetHashValue(hPtr);
3215    }
3216    Tcl_SetHashValue(hPtr, (char *) tracePtr);
3217
3218    /*
3219     * Mark the variable as traced so we know to call them.
3220     */
3221
3222    varPtr->flags |= (tracePtr->flags & VAR_ALL_TRACES);
3223
3224    return TCL_OK;
3225}
3226
3227/*
3228 * Local Variables:
3229 * mode: c
3230 * c-basic-offset: 4
3231 * fill-column: 78
3232 * End:
3233 */
Note: See TracBrowser for help on using the repository browser.