os/persistentdata/persistentstorage/sqlite3api/TEST/TCL/tcldistribution/generic/tclCmdMZ.c
First public contribution.
4 * This file contains the top-level command routines for most of
5 * the Tcl built-in commands whose names begin with the letters
6 * M to Z. It contains only commands in the generic core (i.e.
7 * those that don't depend much upon UNIX facilities).
9 * Copyright (c) 1987-1993 The Regents of the University of California.
10 * Copyright (c) 1994-1997 Sun Microsystems, Inc.
11 * Copyright (c) 1998-2000 Scriptics Corporation.
12 * Copyright (c) 2002 ActiveState Corporation.
13 * Portions Copyright (c) 2007-2008 Nokia Corporation and/or its subsidiaries. All rights reserved.
15 * See the file "license.terms" for information on usage and redistribution
16 * of this file, and for a DISCLAIMER OF ALL WARRANTIES.
18 * RCS: @(#) $Id: tclCmdMZ.c,v 1.82.2.28 2007/05/10 18:23:58 dgp Exp $
23 #include "tclRegexp.h"
24 #include "tclCompile.h"
27 * Structures used to hold information about variable traces:
31 int flags; /* Operations for which Tcl command is
33 size_t length; /* Number of non-NULL chars. in command. */
34 char command[4]; /* Space for Tcl command to invoke. Actual
35 * size will be as large as necessary to
36 * hold command. This field must be the
37 * last in the structure, so that it can
38 * be larger than 4 bytes. */
47 * Structure used to hold information about command traces:
51 int flags; /* Operations for which Tcl command is
53 size_t length; /* Number of non-NULL chars. in command. */
54 Tcl_Trace stepTrace; /* Used for execution traces, when tracing
55 * inside the given command */
56 int startLevel; /* Used for bookkeeping with step execution
57 * traces, store the level at which the step
58 * trace was invoked */
59 char *startCmd; /* Used for bookkeeping with step execution
60 * traces, store the command name which invoked
62 int curFlags; /* Trace flags for the current command */
63 int curCode; /* Return code for the current command */
64 int refCount; /* Used to ensure this structure is
65 * not deleted too early. Keeps track
66 * of how many pieces of code have
67 * a pointer to this structure. */
68 char command[4]; /* Space for Tcl command to invoke. Actual
69 * size will be as large as necessary to
70 * hold command. This field must be the
71 * last in the structure, so that it can
72 * be larger than 4 bytes. */
76 * Used by command execution traces. Note that we assume in the code
77 * that the first two defines are exactly 4 times the
78 * 'TCL_TRACE_ENTER_EXEC' and 'TCL_TRACE_LEAVE_EXEC' constants.
80 * TCL_TRACE_ENTER_DURING_EXEC - Trace each command inside the command
81 * currently being traced, before execution.
82 * TCL_TRACE_LEAVE_DURING_EXEC - Trace each command inside the command
83 * currently being traced, after execution.
84 * TCL_TRACE_ANY_EXEC - OR'd combination of all EXEC flags.
85 * TCL_TRACE_EXEC_IN_PROGRESS - The callback procedure on this trace
86 * is currently executing. Therefore we
87 * don't let further traces execute.
88 * TCL_TRACE_EXEC_DIRECT - This execution trace is triggered directly
89 * by the command being traced, not because
90 * of an internal trace.
91 * The flags 'TCL_TRACE_DESTROYED' and 'TCL_INTERP_DESTROYED' may also
92 * be used in command execution traces.
94 #define TCL_TRACE_ENTER_DURING_EXEC 4
95 #define TCL_TRACE_LEAVE_DURING_EXEC 8
96 #define TCL_TRACE_ANY_EXEC 15
97 #define TCL_TRACE_EXEC_IN_PROGRESS 0x10
98 #define TCL_TRACE_EXEC_DIRECT 0x20
101 * Forward declarations for procedures defined in this file:
104 typedef int (Tcl_TraceTypeObjCmd) _ANSI_ARGS_((Tcl_Interp *interp,
105 int optionIndex, int objc, Tcl_Obj *CONST objv[]));
107 Tcl_TraceTypeObjCmd TclTraceVariableObjCmd;
108 Tcl_TraceTypeObjCmd TclTraceCommandObjCmd;
109 Tcl_TraceTypeObjCmd TclTraceExecutionObjCmd;
112 * Each subcommand has a number of 'types' to which it can apply.
113 * Currently 'execution', 'command' and 'variable' are the only
114 * types supported. These three arrays MUST be kept in sync!
115 * In the future we may provide an API to add to the list of
116 * supported trace types.
118 static CONST char *traceTypeOptions[] = {
119 "execution", "command", "variable", (char*) NULL
121 static Tcl_TraceTypeObjCmd* traceSubCmds[] = {
122 TclTraceExecutionObjCmd,
123 TclTraceCommandObjCmd,
124 TclTraceVariableObjCmd,
128 * Declarations for local procedures to this file:
130 static int CallTraceProcedure _ANSI_ARGS_((Tcl_Interp *interp,
131 Trace *tracePtr, Command *cmdPtr,
132 CONST char *command, int numChars,
133 int objc, Tcl_Obj *CONST objv[]));
134 static char * TraceVarProc _ANSI_ARGS_((ClientData clientData,
135 Tcl_Interp *interp, CONST char *name1,
136 CONST char *name2, int flags));
137 static void TraceCommandProc _ANSI_ARGS_((ClientData clientData,
138 Tcl_Interp *interp, CONST char *oldName,
139 CONST char *newName, int flags));
140 static Tcl_CmdObjTraceProc TraceExecutionProc;
143 static void ListLines _ANSI_ARGS_((CONST char* listStr, int line,
147 *----------------------------------------------------------------------
151 * This procedure is invoked to process the "pwd" Tcl command.
152 * See the user documentation for details on what it does.
155 * A standard Tcl result.
158 * See the user documentation.
160 *----------------------------------------------------------------------
165 Tcl_PwdObjCmd(dummy, interp, objc, objv)
166 ClientData dummy; /* Not used. */
167 Tcl_Interp *interp; /* Current interpreter. */
168 int objc; /* Number of arguments. */
169 Tcl_Obj *CONST objv[]; /* Argument objects. */
174 Tcl_WrongNumArgs(interp, 1, objv, NULL);
178 retVal = Tcl_FSGetCwd(interp);
179 if (retVal == NULL) {
182 Tcl_SetObjResult(interp, retVal);
183 Tcl_DecrRefCount(retVal);
188 *----------------------------------------------------------------------
190 * Tcl_RegexpObjCmd --
192 * This procedure is invoked to process the "regexp" Tcl command.
193 * See the user documentation for details on what it does.
196 * A standard Tcl result.
199 * See the user documentation.
201 *----------------------------------------------------------------------
206 Tcl_RegexpObjCmd(dummy, interp, objc, objv)
207 ClientData dummy; /* Not used. */
208 Tcl_Interp *interp; /* Current interpreter. */
209 int objc; /* Number of arguments. */
210 Tcl_Obj *CONST objv[]; /* Argument objects. */
212 int i, indices, match, about, offset, all, doinline, numMatchesSaved;
213 int cflags, eflags, stringLength;
215 Tcl_Obj *objPtr, *resultPtr;
217 static CONST char *options[] = {
218 "-all", "-about", "-indices", "-inline",
219 "-expanded", "-line", "-linestop", "-lineanchor",
220 "-nocase", "-start", "--", (char *) NULL
223 REGEXP_ALL, REGEXP_ABOUT, REGEXP_INDICES, REGEXP_INLINE,
224 REGEXP_EXPANDED,REGEXP_LINE, REGEXP_LINESTOP,REGEXP_LINEANCHOR,
225 REGEXP_NOCASE, REGEXP_START, REGEXP_LAST
230 cflags = TCL_REG_ADVANCED;
236 for (i = 1; i < objc; i++) {
240 name = Tcl_GetString(objv[i]);
241 if (name[0] != '-') {
244 if (Tcl_GetIndexFromObj(interp, objv[i], options, "switch", TCL_EXACT,
248 switch ((enum options) index) {
253 case REGEXP_INDICES: {
257 case REGEXP_INLINE: {
261 case REGEXP_NOCASE: {
262 cflags |= TCL_REG_NOCASE;
269 case REGEXP_EXPANDED: {
270 cflags |= TCL_REG_EXPANDED;
274 cflags |= TCL_REG_NEWLINE;
277 case REGEXP_LINESTOP: {
278 cflags |= TCL_REG_NLSTOP;
281 case REGEXP_LINEANCHOR: {
282 cflags |= TCL_REG_NLANCH;
289 if (Tcl_GetIntFromObj(interp, objv[i], &offset) != TCL_OK) {
305 if ((objc - i) < (2 - about)) {
306 Tcl_WrongNumArgs(interp, 1, objv,
307 "?switches? exp string ?matchVar? ?subMatchVar subMatchVar ...?");
313 if (doinline && ((objc - 2) != 0)) {
315 * User requested -inline, but specified match variables - a no-no.
317 Tcl_AppendResult(interp, "regexp match variables not allowed",
318 " when using -inline", (char *) NULL);
323 * Handle the odd about case separately.
326 regExpr = Tcl_GetRegExpFromObj(interp, objv[0], cflags);
327 if ((regExpr == NULL) || (TclRegAbout(interp, regExpr) < 0)) {
334 * Get the length of the string that we are matching against so
335 * we can do the termination test for -all matches. Do this before
336 * getting the regexp to avoid shimmering problems.
339 stringLength = Tcl_GetCharLength(objPtr);
341 regExpr = Tcl_GetRegExpFromObj(interp, objv[0], cflags);
342 if (regExpr == NULL) {
348 * Add flag if using offset (string is part of a larger string),
349 * so that "^" won't match.
351 eflags |= TCL_REG_NOTBOL;
356 resultPtr = Tcl_GetObjResult(interp);
360 * Save all the subexpressions, as we will return them as a list
362 numMatchesSaved = -1;
365 * Save only enough subexpressions for matches we want to keep,
366 * expect in the case of -all, where we need to keep at least
367 * one to know where to move the offset.
369 numMatchesSaved = (objc == 0) ? all : objc;
373 * The following loop is to handle multiple matches within the
374 * same source string; each iteration handles one match. If "-all"
375 * hasn't been specified then the loop body only gets executed once.
376 * We terminate the loop when the starting offset is past the end of the
381 match = Tcl_RegExpExecObj(interp, regExpr, objPtr,
382 offset /* offset */, numMatchesSaved, eflags
384 (Tcl_GetUniChar(objPtr,offset-1) != (Tcl_UniChar)'\n'))
385 ? TCL_REG_NOTBOL : 0));
393 * We want to set the value of the intepreter result only when
394 * this is the first time through the loop.
398 * If inlining, set the interpreter's object result to an
399 * empty list, otherwise set it to an integer object w/
403 Tcl_SetListObj(resultPtr, 0, NULL);
405 Tcl_SetIntObj(resultPtr, 0);
413 * If additional variable names have been specified, return
414 * index information in those variables.
417 Tcl_RegExpGetInfo(regExpr, &info);
420 * It's the number of substitutions, plus one for the matchVar
423 objc = info.nsubs + 1;
425 for (i = 0; i < objc; i++) {
433 * Only adjust the match area if there was a match for
434 * that area. (Scriptics Bug 4391/SF Bug #219232)
436 if (i <= info.nsubs && info.matches[i].start >= 0) {
437 start = offset + info.matches[i].start;
438 end = offset + info.matches[i].end;
441 * Adjust index so it refers to the last character in the
442 * match instead of the first character after the match.
453 objs[0] = Tcl_NewLongObj(start);
454 objs[1] = Tcl_NewLongObj(end);
456 newPtr = Tcl_NewListObj(2, objs);
458 if (i <= info.nsubs) {
459 newPtr = Tcl_GetRange(objPtr,
460 offset + info.matches[i].start,
461 offset + info.matches[i].end - 1);
463 newPtr = Tcl_NewObj();
467 if (Tcl_ListObjAppendElement(interp, resultPtr, newPtr)
469 Tcl_DecrRefCount(newPtr);
474 Tcl_IncrRefCount(newPtr);
475 valuePtr = Tcl_ObjSetVar2(interp, objv[i], NULL, newPtr, 0);
476 Tcl_DecrRefCount(newPtr);
477 if (valuePtr == NULL) {
478 Tcl_AppendResult(interp, "couldn't set variable \"",
479 Tcl_GetString(objv[i]), "\"", (char *) NULL);
489 * Adjust the offset to the character just after the last one
490 * in the matchVar and increment all to count how many times
491 * we are making a match. We always increment the offset by at least
492 * one to prevent endless looping (as in the case:
493 * regexp -all {a*} a). Otherwise, when we match the NULL string at
494 * the end of the input string, we will loop indefinately (because the
495 * length of the match is 0, so offset never changes).
497 if (info.matches[0].end == 0) {
500 offset += info.matches[0].end;
502 eflags |= TCL_REG_NOTBOL;
503 if (offset >= stringLength) {
509 * Set the interpreter's object result to an integer object
510 * with value 1 if -all wasn't specified, otherwise it's all-1
511 * (the number of times through the while - 1).
512 * Get the resultPtr again as the Tcl_ObjSetVar2 above may have
513 * cause the result to change. [Patch #558324] (watson).
517 resultPtr = Tcl_GetObjResult(interp);
518 Tcl_SetIntObj(resultPtr, (all ? all-1 : 1));
524 *----------------------------------------------------------------------
526 * Tcl_RegsubObjCmd --
528 * This procedure is invoked to process the "regsub" Tcl command.
529 * See the user documentation for details on what it does.
532 * A standard Tcl result.
535 * See the user documentation.
537 *----------------------------------------------------------------------
542 Tcl_RegsubObjCmd(dummy, interp, objc, objv)
543 ClientData dummy; /* Not used. */
544 Tcl_Interp *interp; /* Current interpreter. */
545 int objc; /* Number of arguments. */
546 Tcl_Obj *CONST objv[]; /* Argument objects. */
548 int idx, result, cflags, all, wlen, wsublen, numMatches, offset;
549 int start, end, subStart, subEnd, match;
552 Tcl_Obj *resultPtr, *subPtr, *objPtr;
553 Tcl_UniChar ch, *wsrc, *wfirstChar, *wstring, *wsubspec, *wend;
555 static CONST char *options[] = {
556 "-all", "-nocase", "-expanded",
557 "-line", "-linestop", "-lineanchor", "-start",
561 REGSUB_ALL, REGSUB_NOCASE, REGSUB_EXPANDED,
562 REGSUB_LINE, REGSUB_LINESTOP, REGSUB_LINEANCHOR, REGSUB_START,
566 cflags = TCL_REG_ADVANCED;
571 for (idx = 1; idx < objc; idx++) {
575 name = Tcl_GetString(objv[idx]);
576 if (name[0] != '-') {
579 if (Tcl_GetIndexFromObj(interp, objv[idx], options, "switch",
580 TCL_EXACT, &index) != TCL_OK) {
583 switch ((enum options) index) {
588 case REGSUB_NOCASE: {
589 cflags |= TCL_REG_NOCASE;
592 case REGSUB_EXPANDED: {
593 cflags |= TCL_REG_EXPANDED;
597 cflags |= TCL_REG_NEWLINE;
600 case REGSUB_LINESTOP: {
601 cflags |= TCL_REG_NLSTOP;
604 case REGSUB_LINEANCHOR: {
605 cflags |= TCL_REG_NLANCH;
612 if (Tcl_GetIntFromObj(interp, objv[idx], &offset) != TCL_OK) {
627 if (objc-idx < 3 || objc-idx > 4) {
628 Tcl_WrongNumArgs(interp, 1, objv,
629 "?switches? exp string subSpec ?varName?");
636 if (all && (offset == 0)
637 && (strpbrk(Tcl_GetString(objv[2]), "&\\") == NULL)
638 && (strpbrk(Tcl_GetString(objv[0]), "*+?{}()[].\\|^$") == NULL)) {
640 * This is a simple one pair string map situation. We make use of
641 * a slightly modified version of the one pair STR_MAP code.
644 int (*strCmpFn)_ANSI_ARGS_((CONST Tcl_UniChar *, CONST Tcl_UniChar *,
646 Tcl_UniChar *p, wsrclc;
649 nocase = (cflags & TCL_REG_NOCASE);
650 strCmpFn = nocase ? Tcl_UniCharNcasecmp : Tcl_UniCharNcmp;
652 wsrc = Tcl_GetUnicodeFromObj(objv[0], &slen);
653 wstring = Tcl_GetUnicodeFromObj(objv[1], &wlen);
654 wsubspec = Tcl_GetUnicodeFromObj(objv[2], &wsublen);
655 wend = wstring + wlen - (slen ? slen - 1 : 0);
660 * regsub behavior for "" matches between each character.
661 * 'string map' skips the "" case.
663 if (wstring < wend) {
664 resultPtr = Tcl_NewUnicodeObj(wstring, 0);
665 Tcl_IncrRefCount(resultPtr);
666 for (; wstring < wend; wstring++) {
667 Tcl_AppendUnicodeToObj(resultPtr, wsubspec, wsublen);
668 Tcl_AppendUnicodeToObj(resultPtr, wstring, 1);
674 wsrclc = Tcl_UniCharToLower(*wsrc);
675 for (p = wfirstChar = wstring; wstring < wend; wstring++) {
676 if (((*wstring == *wsrc) ||
677 (nocase && (Tcl_UniCharToLower(*wstring) ==
679 ((slen == 1) || (strCmpFn(wstring, wsrc,
680 (unsigned long) slen) == 0))) {
681 if (numMatches == 0) {
682 resultPtr = Tcl_NewUnicodeObj(wstring, 0);
683 Tcl_IncrRefCount(resultPtr);
686 Tcl_AppendUnicodeToObj(resultPtr, p, wstring - p);
693 Tcl_AppendUnicodeToObj(resultPtr, wsubspec, wsublen);
698 wlen = wfirstChar + wlen - p;
707 regExpr = Tcl_GetRegExpFromObj(interp, objv[0], cflags);
708 if (regExpr == NULL) {
713 * Make sure to avoid problems where the objects are shared. This
714 * can cause RegExpObj <> UnicodeObj shimmering that causes data
715 * corruption. [Bug #461322]
718 if (objv[1] == objv[0]) {
719 objPtr = Tcl_DuplicateObj(objv[1]);
723 wstring = Tcl_GetUnicodeFromObj(objPtr, &wlen);
724 if (objv[2] == objv[0]) {
725 subPtr = Tcl_DuplicateObj(objv[2]);
729 wsubspec = Tcl_GetUnicodeFromObj(subPtr, &wsublen);
734 * The following loop is to handle multiple matches within the
735 * same source string; each iteration handles one match and its
736 * corresponding substitution. If "-all" hasn't been specified
737 * then the loop body only gets executed once. We must use
738 * 'offset <= wlen' in particular for the case where the regexp
739 * pattern can match the empty string - this is useful when
740 * doing, say, 'regsub -- ^ $str ...' when $str might be empty.
744 for ( ; offset <= wlen; ) {
747 * The flags argument is set if string is part of a larger string,
748 * so that "^" won't match.
751 match = Tcl_RegExpExecObj(interp, regExpr, objPtr, offset,
752 10 /* matches */, ((offset > 0 &&
753 (wstring[offset-1] != (Tcl_UniChar)'\n'))
754 ? TCL_REG_NOTBOL : 0));
763 if (numMatches == 0) {
764 resultPtr = Tcl_NewUnicodeObj(wstring, 0);
765 Tcl_IncrRefCount(resultPtr);
768 * Copy the initial portion of the string in if an offset
771 Tcl_AppendUnicodeToObj(resultPtr, wstring, offset);
777 * Copy the portion of the source string before the match to the
781 Tcl_RegExpGetInfo(regExpr, &info);
782 start = info.matches[0].start;
783 end = info.matches[0].end;
784 Tcl_AppendUnicodeToObj(resultPtr, wstring + offset, start);
787 * Append the subSpec argument to the variable, making appropriate
788 * substitutions. This code is a bit hairy because of the backslash
789 * conventions and because the code saves up ranges of characters in
790 * subSpec to reduce the number of calls to Tcl_SetVar.
793 wsrc = wfirstChar = wsubspec;
794 wend = wsubspec + wsublen;
795 for (ch = *wsrc; wsrc != wend; wsrc++, ch = *wsrc) {
798 } else if (ch == '\\') {
800 if ((ch >= '0') && (ch <= '9')) {
802 } else if ((ch == '\\') || (ch == '&')) {
804 Tcl_AppendUnicodeToObj(resultPtr, wfirstChar,
805 wsrc - wfirstChar + 1);
807 wfirstChar = wsrc + 2;
816 if (wfirstChar != wsrc) {
817 Tcl_AppendUnicodeToObj(resultPtr, wfirstChar,
820 if (idx <= info.nsubs) {
821 subStart = info.matches[idx].start;
822 subEnd = info.matches[idx].end;
823 if ((subStart >= 0) && (subEnd >= 0)) {
824 Tcl_AppendUnicodeToObj(resultPtr,
825 wstring + offset + subStart, subEnd - subStart);
831 wfirstChar = wsrc + 1;
833 if (wfirstChar != wsrc) {
834 Tcl_AppendUnicodeToObj(resultPtr, wfirstChar, wsrc - wfirstChar);
838 * Always consume at least one character of the input string
839 * in order to prevent infinite loops.
843 Tcl_AppendUnicodeToObj(resultPtr, wstring + offset, 1);
850 * We matched an empty string, which means we must go
851 * forward one more step so we don't match again at the
855 Tcl_AppendUnicodeToObj(resultPtr, wstring + offset, 1);
866 * Copy the portion of the source string after the last match to the
870 if (numMatches == 0) {
872 * On zero matches, just ignore the offset, since it shouldn't
873 * matter to us in this case, and the user may have skewed it.
876 Tcl_IncrRefCount(resultPtr);
877 } else if (offset < wlen) {
878 Tcl_AppendUnicodeToObj(resultPtr, wstring + offset, wlen - offset);
881 if (Tcl_ObjSetVar2(interp, objv[3], NULL, resultPtr, 0) == NULL) {
882 Tcl_AppendResult(interp, "couldn't set variable \"",
883 Tcl_GetString(objv[3]), "\"", (char *) NULL);
887 * Set the interpreter's object result to an integer object
888 * holding the number of matches.
891 Tcl_SetIntObj(Tcl_GetObjResult(interp), numMatches);
895 * No varname supplied, so just return the modified string.
897 Tcl_SetObjResult(interp, resultPtr);
901 if (objPtr && (objv[1] == objv[0])) { Tcl_DecrRefCount(objPtr); }
902 if (subPtr && (objv[2] == objv[0])) { Tcl_DecrRefCount(subPtr); }
903 if (resultPtr) { Tcl_DecrRefCount(resultPtr); }
908 *----------------------------------------------------------------------
910 * Tcl_RenameObjCmd --
912 * This procedure is invoked to process the "rename" Tcl command.
913 * See the user documentation for details on what it does.
916 * A standard Tcl object result.
919 * See the user documentation.
921 *----------------------------------------------------------------------
926 Tcl_RenameObjCmd(dummy, interp, objc, objv)
927 ClientData dummy; /* Arbitrary value passed to the command. */
928 Tcl_Interp *interp; /* Current interpreter. */
929 int objc; /* Number of arguments. */
930 Tcl_Obj *CONST objv[]; /* Argument objects. */
932 char *oldName, *newName;
935 Tcl_WrongNumArgs(interp, 1, objv, "oldName newName");
939 oldName = Tcl_GetString(objv[1]);
940 newName = Tcl_GetString(objv[2]);
941 return TclRenameCommand(interp, oldName, newName);
945 *----------------------------------------------------------------------
947 * Tcl_ReturnObjCmd --
949 * This object-based procedure is invoked to process the "return" Tcl
950 * command. See the user documentation for details on what it does.
953 * A standard Tcl object result.
956 * See the user documentation.
958 *----------------------------------------------------------------------
963 Tcl_ReturnObjCmd(dummy, interp, objc, objv)
964 ClientData dummy; /* Not used. */
965 Tcl_Interp *interp; /* Current interpreter. */
966 int objc; /* Number of arguments. */
967 Tcl_Obj *CONST objv[]; /* Argument objects. */
969 Interp *iPtr = (Interp *) interp;
970 int optionLen, argLen, code, result;
972 if (iPtr->errorInfo != NULL) {
973 ckfree(iPtr->errorInfo);
974 iPtr->errorInfo = NULL;
976 if (iPtr->errorCode != NULL) {
977 ckfree(iPtr->errorCode);
978 iPtr->errorCode = NULL;
982 for (objv++, objc--; objc > 1; objv += 2, objc -= 2) {
983 char *option = Tcl_GetStringFromObj(objv[0], &optionLen);
984 char *arg = Tcl_GetStringFromObj(objv[1], &argLen);
986 if (strcmp(option, "-code") == 0) {
987 register int c = arg[0];
988 if ((c == 'o') && (strcmp(arg, "ok") == 0)) {
990 } else if ((c == 'e') && (strcmp(arg, "error") == 0)) {
992 } else if ((c == 'r') && (strcmp(arg, "return") == 0)) {
994 } else if ((c == 'b') && (strcmp(arg, "break") == 0)) {
996 } else if ((c == 'c') && (strcmp(arg, "continue") == 0)) {
999 result = Tcl_GetIntFromObj((Tcl_Interp *) NULL, objv[1],
1001 if (result != TCL_OK) {
1002 Tcl_ResetResult(interp);
1003 Tcl_AppendStringsToObj(Tcl_GetObjResult(interp),
1004 "bad completion code \"",
1005 Tcl_GetString(objv[1]),
1006 "\": must be ok, error, return, break, ",
1007 "continue, or an integer", (char *) NULL);
1011 } else if (strcmp(option, "-errorinfo") == 0) {
1013 (char *) ckalloc((unsigned) (strlen(arg) + 1));
1014 strcpy(iPtr->errorInfo, arg);
1015 } else if (strcmp(option, "-errorcode") == 0) {
1017 (char *) ckalloc((unsigned) (strlen(arg) + 1));
1018 strcpy(iPtr->errorCode, arg);
1020 Tcl_AppendStringsToObj(Tcl_GetObjResult(interp),
1021 "bad option \"", option,
1022 "\": must be -code, -errorcode, or -errorinfo",
1030 * Set the interpreter's object result. An inline version of
1034 Tcl_SetObjResult(interp, objv[0]);
1036 iPtr->returnCode = code;
1041 *----------------------------------------------------------------------
1043 * Tcl_SourceObjCmd --
1045 * This procedure is invoked to process the "source" Tcl command.
1046 * See the user documentation for details on what it does.
1049 * A standard Tcl object result.
1052 * See the user documentation.
1054 *----------------------------------------------------------------------
1059 Tcl_SourceObjCmd(dummy, interp, objc, objv)
1060 ClientData dummy; /* Not used. */
1061 Tcl_Interp *interp; /* Current interpreter. */
1062 int objc; /* Number of arguments. */
1063 Tcl_Obj *CONST objv[]; /* Argument objects. */
1066 Tcl_WrongNumArgs(interp, 1, objv, "fileName");
1070 return Tcl_FSEvalFile(interp, objv[1]);
1074 *----------------------------------------------------------------------
1076 * Tcl_SplitObjCmd --
1078 * This procedure is invoked to process the "split" Tcl command.
1079 * See the user documentation for details on what it does.
1082 * A standard Tcl result.
1085 * See the user documentation.
1087 *----------------------------------------------------------------------
1092 Tcl_SplitObjCmd(dummy, interp, objc, objv)
1093 ClientData dummy; /* Not used. */
1094 Tcl_Interp *interp; /* Current interpreter. */
1095 int objc; /* Number of arguments. */
1096 Tcl_Obj *CONST objv[]; /* Argument objects. */
1100 char *splitChars, *string, *end;
1101 int splitCharLen, stringLen;
1102 Tcl_Obj *listPtr, *objPtr;
1105 splitChars = " \n\t\r";
1107 } else if (objc == 3) {
1108 splitChars = Tcl_GetStringFromObj(objv[2], &splitCharLen);
1110 Tcl_WrongNumArgs(interp, 1, objv, "string ?splitChars?");
1114 string = Tcl_GetStringFromObj(objv[1], &stringLen);
1115 end = string + stringLen;
1116 listPtr = Tcl_GetObjResult(interp);
1118 if (stringLen == 0) {
1122 } else if (splitCharLen == 0) {
1123 Tcl_HashTable charReuseTable;
1124 Tcl_HashEntry *hPtr;
1128 * Handle the special case of splitting on every character.
1130 * Uses a hash table to ensure that each kind of character has
1131 * only one Tcl_Obj instance (multiply-referenced) in the
1132 * final list. This is a *major* win when splitting on a long
1133 * string (especially in the megabyte range!) - DKF
1136 Tcl_InitHashTable(&charReuseTable, TCL_ONE_WORD_KEYS);
1137 for ( ; string < end; string += len) {
1138 len = TclUtfToUniChar(string, &ch);
1139 /* Assume Tcl_UniChar is an integral type... */
1140 hPtr = Tcl_CreateHashEntry(&charReuseTable, (char*)0 + ch, &isNew);
1142 objPtr = Tcl_NewStringObj(string, len);
1143 /* Don't need to fiddle with refcount... */
1144 Tcl_SetHashValue(hPtr, (ClientData) objPtr);
1146 objPtr = (Tcl_Obj*) Tcl_GetHashValue(hPtr);
1148 Tcl_ListObjAppendElement(NULL, listPtr, objPtr);
1150 Tcl_DeleteHashTable(&charReuseTable);
1151 } else if (splitCharLen == 1) {
1155 * Handle the special case of splitting on a single character.
1156 * This is only true for the one-char ASCII case, as one unicode
1157 * char is > 1 byte in length.
1160 while (*string && (p = strchr(string, (int) *splitChars)) != NULL) {
1161 objPtr = Tcl_NewStringObj(string, p - string);
1162 Tcl_ListObjAppendElement(NULL, listPtr, objPtr);
1165 objPtr = Tcl_NewStringObj(string, end - string);
1166 Tcl_ListObjAppendElement(NULL, listPtr, objPtr);
1168 char *element, *p, *splitEnd;
1170 Tcl_UniChar splitChar;
1173 * Normal case: split on any of a given set of characters.
1174 * Discard instances of the split characters.
1177 splitEnd = splitChars + splitCharLen;
1179 for (element = string; string < end; string += len) {
1180 len = TclUtfToUniChar(string, &ch);
1181 for (p = splitChars; p < splitEnd; p += splitLen) {
1182 splitLen = TclUtfToUniChar(p, &splitChar);
1183 if (ch == splitChar) {
1184 objPtr = Tcl_NewStringObj(element, string - element);
1185 Tcl_ListObjAppendElement(NULL, listPtr, objPtr);
1186 element = string + len;
1191 objPtr = Tcl_NewStringObj(element, string - element);
1192 Tcl_ListObjAppendElement(NULL, listPtr, objPtr);
1198 *----------------------------------------------------------------------
1200 * Tcl_StringObjCmd --
1202 * This procedure is invoked to process the "string" Tcl command.
1203 * See the user documentation for details on what it does. Note
1204 * that this command only functions correctly on properly formed
1207 * Note that the primary methods here (equal, compare, match, ...)
1208 * have bytecode equivalents. You will find the code for those in
1209 * tclExecute.c. The code here will only be used in the non-bc
1210 * case (like in an 'eval').
1213 * A standard Tcl result.
1216 * See the user documentation.
1218 *----------------------------------------------------------------------
1223 Tcl_StringObjCmd(dummy, interp, objc, objv)
1224 ClientData dummy; /* Not used. */
1225 Tcl_Interp *interp; /* Current interpreter. */
1226 int objc; /* Number of arguments. */
1227 Tcl_Obj *CONST objv[]; /* Argument objects. */
1229 int index, left, right;
1231 char *string1, *string2;
1232 int length1, length2;
1233 static CONST char *options[] = {
1234 "bytelength", "compare", "equal", "first",
1235 "index", "is", "last", "length",
1236 "map", "match", "range", "repeat",
1237 "replace", "tolower", "toupper", "totitle",
1238 "trim", "trimleft", "trimright",
1239 "wordend", "wordstart", (char *) NULL
1242 STR_BYTELENGTH, STR_COMPARE, STR_EQUAL, STR_FIRST,
1243 STR_INDEX, STR_IS, STR_LAST, STR_LENGTH,
1244 STR_MAP, STR_MATCH, STR_RANGE, STR_REPEAT,
1245 STR_REPLACE, STR_TOLOWER, STR_TOUPPER, STR_TOTITLE,
1246 STR_TRIM, STR_TRIMLEFT, STR_TRIMRIGHT,
1247 STR_WORDEND, STR_WORDSTART
1251 Tcl_WrongNumArgs(interp, 1, objv, "option arg ?arg ...?");
1255 if (Tcl_GetIndexFromObj(interp, objv[1], options, "option", 0,
1256 &index) != TCL_OK) {
1260 resultPtr = Tcl_GetObjResult(interp);
1261 switch ((enum options) index) {
1265 * Remember to keep code here in some sync with the
1266 * byte-compiled versions in tclExecute.c (INST_STR_EQ,
1267 * INST_STR_NEQ and INST_STR_CMP as well as the expr string
1268 * comparison in INST_EQ/INST_NEQ/INST_LT/...).
1270 int i, match, length, nocase = 0, reqlength = -1;
1273 if (objc < 4 || objc > 7) {
1275 Tcl_WrongNumArgs(interp, 2, objv,
1276 "?-nocase? ?-length int? string1 string2");
1280 for (i = 2; i < objc-2; i++) {
1281 string2 = Tcl_GetStringFromObj(objv[i], &length2);
1283 && strncmp(string2, "-nocase", (size_t)length2) == 0) {
1285 } else if ((length2 > 1)
1286 && strncmp(string2, "-length", (size_t)length2) == 0) {
1287 if (i+1 >= objc-2) {
1290 if (Tcl_GetIntFromObj(interp, objv[++i],
1291 &reqlength) != TCL_OK) {
1295 Tcl_AppendStringsToObj(resultPtr, "bad option \"",
1296 string2, "\": must be -nocase or -length",
1303 * From now on, we only access the two objects at the end
1304 * of the argument array.
1308 if ((reqlength == 0) || (objv[0] == objv[1])) {
1310 * Alway match at 0 chars of if it is the same obj.
1313 Tcl_SetBooleanObj(resultPtr,
1314 ((enum options) index == STR_EQUAL));
1316 } else if (!nocase && objv[0]->typePtr == &tclByteArrayType &&
1317 objv[1]->typePtr == &tclByteArrayType) {
1319 * Use binary versions of comparisons since that won't
1320 * cause undue type conversions and it is much faster.
1321 * Only do this if we're case-sensitive (which is all
1322 * that really makes sense with byte arrays anyway, and
1323 * we have no memcasecmp() for some reason... :^)
1325 string1 = (char*) Tcl_GetByteArrayFromObj(objv[0], &length1);
1326 string2 = (char*) Tcl_GetByteArrayFromObj(objv[1], &length2);
1328 } else if ((objv[0]->typePtr == &tclStringType)
1329 && (objv[1]->typePtr == &tclStringType)) {
1331 * Do a unicode-specific comparison if both of the args
1332 * are of String type. In benchmark testing this proved
1333 * the most efficient check between the unicode and
1334 * string comparison operations.
1336 string1 = (char*) Tcl_GetUnicodeFromObj(objv[0], &length1);
1337 string2 = (char*) Tcl_GetUnicodeFromObj(objv[1], &length2);
1338 strCmpFn = nocase ? Tcl_UniCharNcasecmp : Tcl_UniCharNcmp;
1341 * As a catch-all we will work with UTF-8. We cannot use
1342 * memcmp() as that is unsafe with any string containing
1343 * NULL (\xC0\x80 in Tcl's utf rep). We can use the more
1344 * efficient TclpUtfNcmp2 if we are case-sensitive and no
1345 * specific length was requested.
1347 string1 = (char*) Tcl_GetStringFromObj(objv[0], &length1);
1348 string2 = (char*) Tcl_GetStringFromObj(objv[1], &length2);
1349 if ((reqlength < 0) && !nocase) {
1350 strCmpFn = TclpUtfNcmp2;
1352 length1 = Tcl_NumUtfChars(string1, length1);
1353 length2 = Tcl_NumUtfChars(string2, length2);
1354 strCmpFn = nocase ? Tcl_UtfNcasecmp : Tcl_UtfNcmp;
1358 if (((enum options) index == STR_EQUAL)
1359 && (reqlength < 0) && (length1 != length2)) {
1360 match = 1; /* this will be reversed below */
1362 length = (length1 < length2) ? length1 : length2;
1363 if (reqlength > 0 && reqlength < length) {
1365 } else if (reqlength < 0) {
1367 * The requested length is negative, so we ignore it by
1368 * setting it to length + 1 so we correct the match var.
1370 reqlength = length + 1;
1372 match = strCmpFn(string1, string2, (unsigned) length);
1373 if ((match == 0) && (reqlength > length)) {
1374 match = length1 - length2;
1378 if ((enum options) index == STR_EQUAL) {
1379 Tcl_SetBooleanObj(resultPtr, (match) ? 0 : 1);
1381 Tcl_SetIntObj(resultPtr, ((match > 0) ? 1 :
1382 (match < 0) ? -1 : 0));
1387 Tcl_UniChar *ustring1, *ustring2;
1390 if (objc < 4 || objc > 5) {
1391 Tcl_WrongNumArgs(interp, 2, objv,
1392 "subString string ?startIndex?");
1397 * We are searching string2 for the sequence string1.
1404 ustring1 = Tcl_GetUnicodeFromObj(objv[2], &length1);
1405 ustring2 = Tcl_GetUnicodeFromObj(objv[3], &length2);
1409 * If a startIndex is specified, we will need to fast
1410 * forward to that point in the string before we think
1413 if (TclGetIntForIndex(interp, objv[4], length2 - 1,
1414 &start) != TCL_OK) {
1417 if (start >= length2) {
1418 goto str_first_done;
1419 } else if (start > 0) {
1422 } else if (start < 0) {
1424 * Invalid start index mapped to string start;
1432 register Tcl_UniChar *p, *end;
1434 end = ustring2 + length2 - length1 + 1;
1435 for (p = ustring2; p < end; p++) {
1437 * Scan forward to find the first character.
1439 if ((*p == *ustring1) &&
1440 (TclUniCharNcmp(ustring1, p,
1441 (unsigned long) length1) == 0)) {
1442 match = p - ustring2;
1448 * Compute the character index of the matching string by
1449 * counting the number of characters before the match.
1451 if ((match != -1) && (objc == 5)) {
1456 Tcl_SetIntObj(resultPtr, match);
1461 Tcl_WrongNumArgs(interp, 2, objv, "string charIndex");
1466 * If we have a ByteArray object, avoid indexing in the
1467 * Utf string since the byte array contains one byte per
1468 * character. Otherwise, use the Unicode string rep to
1469 * get the index'th char.
1472 if (objv[2]->typePtr == &tclByteArrayType) {
1473 string1 = (char *) Tcl_GetByteArrayFromObj(objv[2], &length1);
1475 if (TclGetIntForIndex(interp, objv[3], length1 - 1,
1476 &index) != TCL_OK) {
1479 if ((index >= 0) && (index < length1)) {
1480 Tcl_SetByteArrayObj(resultPtr,
1481 (unsigned char *)(&string1[index]), 1);
1485 * Get Unicode char length to calulate what 'end' means.
1487 length1 = Tcl_GetCharLength(objv[2]);
1489 if (TclGetIntForIndex(interp, objv[3], length1 - 1,
1490 &index) != TCL_OK) {
1493 if ((index >= 0) && (index < length1)) {
1494 char buf[TCL_UTF_MAX];
1497 ch = Tcl_GetUniChar(objv[2], index);
1498 length1 = Tcl_UniCharToUtf(ch, buf);
1499 Tcl_SetStringObj(resultPtr, buf, length1);
1509 * The UniChar comparison function
1512 int (*chcomp)_ANSI_ARGS_((int)) = NULL;
1513 int i, failat = 0, result = 1, strict = 0;
1514 Tcl_Obj *objPtr, *failVarObj = NULL;
1516 static CONST char *isOptions[] = {
1517 "alnum", "alpha", "ascii", "control",
1518 "boolean", "digit", "double", "false",
1519 "graph", "integer", "lower", "print",
1520 "punct", "space", "true", "upper",
1521 "wordchar", "xdigit", (char *) NULL
1524 STR_IS_ALNUM, STR_IS_ALPHA, STR_IS_ASCII, STR_IS_CONTROL,
1525 STR_IS_BOOL, STR_IS_DIGIT, STR_IS_DOUBLE, STR_IS_FALSE,
1526 STR_IS_GRAPH, STR_IS_INT, STR_IS_LOWER, STR_IS_PRINT,
1527 STR_IS_PUNCT, STR_IS_SPACE, STR_IS_TRUE, STR_IS_UPPER,
1528 STR_IS_WORD, STR_IS_XDIGIT
1531 if (objc < 4 || objc > 7) {
1532 Tcl_WrongNumArgs(interp, 2, objv,
1533 "class ?-strict? ?-failindex var? str");
1536 if (Tcl_GetIndexFromObj(interp, objv[2], isOptions, "class", 0,
1537 &index) != TCL_OK) {
1541 for (i = 3; i < objc-1; i++) {
1542 string2 = Tcl_GetStringFromObj(objv[i], &length2);
1543 if ((length2 > 1) &&
1544 strncmp(string2, "-strict", (size_t) length2) == 0) {
1546 } else if ((length2 > 1) &&
1547 strncmp(string2, "-failindex",
1548 (size_t) length2) == 0) {
1549 if (i+1 >= objc-1) {
1550 Tcl_WrongNumArgs(interp, 3, objv,
1551 "?-strict? ?-failindex var? str");
1554 failVarObj = objv[++i];
1556 Tcl_AppendStringsToObj(resultPtr, "bad option \"",
1557 string2, "\": must be -strict or -failindex",
1565 * We get the objPtr so that we can short-cut for some classes
1566 * by checking the object type (int and double), but we need
1567 * the string otherwise, because we don't want any conversion
1568 * of type occuring (as, for example, Tcl_Get*FromObj would do
1570 objPtr = objv[objc-1];
1571 string1 = Tcl_GetStringFromObj(objPtr, &length1);
1578 end = string1 + length1;
1581 * When entering here, result == 1 and failat == 0
1583 switch ((enum isOptions) index) {
1585 chcomp = Tcl_UniCharIsAlnum;
1588 chcomp = Tcl_UniCharIsAlpha;
1591 for (; string1 < end; string1++, failat++) {
1593 * This is a valid check in unicode, because all
1594 * bytes < 0xC0 are single byte chars (but isascii
1595 * limits that def'n to 0x80).
1597 if (*((unsigned char *)string1) >= 0x80) {
1606 /* Optimizers, beware Bug 1187123 ! */
1607 if ((Tcl_GetBoolean(NULL, string1, &i)
1609 (((enum isOptions) index == STR_IS_TRUE) &&
1611 (((enum isOptions) index == STR_IS_FALSE) &&
1616 case STR_IS_CONTROL:
1617 chcomp = Tcl_UniCharIsControl;
1620 chcomp = Tcl_UniCharIsDigit;
1622 case STR_IS_DOUBLE: {
1625 if ((objPtr->typePtr == &tclDoubleType) ||
1626 (objPtr->typePtr == &tclIntType)) {
1630 * This is adapted from Tcl_GetDouble
1632 * The danger in this function is that
1633 * "12345678901234567890" is an acceptable 'double',
1634 * but will later be interp'd as an int by something
1635 * like [expr]. Therefore, we check to see if it looks
1636 * like an int, and if so we do a range check on it.
1637 * If strtoul gets to the end, we know we either
1638 * received an acceptable int, or over/underflow
1640 if (TclLooksLikeInt(string1, length1)) {
1642 #ifdef TCL_WIDE_INT_IS_LONG
1643 strtoul(string1, &stop, 0); /* INTL: Tcl source. */
1645 strtoull(string1, &stop, 0); /* INTL: Tcl source. */
1648 if (errno == ERANGE) {
1656 strtod(string1, &stop); /* INTL: Tcl source. */
1657 if (errno == ERANGE) {
1659 * if (errno == ERANGE), then it was an over/underflow
1660 * problem, but in this method, we only want to know
1661 * yes or no, so bad flow returns 0 (false) and sets
1662 * the failVarObj to the string length.
1666 } else if (stop == string1) {
1668 * In this case, nothing like a number was found
1674 * Assume we sucked up one char per byte
1675 * and then we go onto SPACE, since we are
1676 * allowed trailing whitespace
1678 failat = stop - string1;
1680 chcomp = Tcl_UniCharIsSpace;
1685 chcomp = Tcl_UniCharIsGraph;
1691 if (TCL_OK == Tcl_GetIntFromObj(NULL, objPtr, &i)) {
1695 * Like STR_IS_DOUBLE, but we use strtoul.
1696 * Since Tcl_GetIntFromObj already failed,
1697 * we set result to 0.
1701 l = strtol(string1, &stop, 0); /* INTL: Tcl source. */
1702 if ((errno == ERANGE) || (l > INT_MAX) || (l < INT_MIN)) {
1704 * if (errno == ERANGE), then it was an over/underflow
1705 * problem, but in this method, we only want to know
1706 * yes or no, so bad flow returns 0 (false) and sets
1707 * the failVarObj to the string length.
1711 } else if (stop == string1) {
1713 * In this case, nothing like a number was found
1718 * Assume we sucked up one char per byte
1719 * and then we go onto SPACE, since we are
1720 * allowed trailing whitespace
1722 failat = stop - string1;
1724 chcomp = Tcl_UniCharIsSpace;
1729 chcomp = Tcl_UniCharIsLower;
1732 chcomp = Tcl_UniCharIsPrint;
1735 chcomp = Tcl_UniCharIsPunct;
1738 chcomp = Tcl_UniCharIsSpace;
1741 chcomp = Tcl_UniCharIsUpper;
1744 chcomp = Tcl_UniCharIsWordChar;
1746 case STR_IS_XDIGIT: {
1747 for (; string1 < end; string1++, failat++) {
1748 /* INTL: We assume unicode is bad for this class */
1749 if ((*((unsigned char *)string1) >= 0xC0) ||
1750 !isxdigit(*(unsigned char *)string1)) {
1758 if (chcomp != NULL) {
1759 for (; string1 < end; string1 += length2, failat++) {
1760 length2 = TclUtfToUniChar(string1, &ch);
1769 * Only set the failVarObj when we will return 0
1770 * and we have indicated a valid fail index (>= 0)
1772 if ((result == 0) && (failVarObj != NULL)) {
1773 Tcl_Obj *resPtr, *tmpPtr = Tcl_NewIntObj(failat);
1775 Tcl_IncrRefCount(tmpPtr);
1776 resPtr = Tcl_ObjSetVar2(interp, failVarObj, NULL, tmpPtr,
1778 Tcl_DecrRefCount(tmpPtr);
1779 if (resPtr == NULL) {
1783 Tcl_SetBooleanObj(resultPtr, result);
1787 Tcl_UniChar *ustring1, *ustring2, *p;
1790 if (objc < 4 || objc > 5) {
1791 Tcl_WrongNumArgs(interp, 2, objv,
1792 "subString string ?startIndex?");
1797 * We are searching string2 for the sequence string1.
1804 ustring1 = Tcl_GetUnicodeFromObj(objv[2], &length1);
1805 ustring2 = Tcl_GetUnicodeFromObj(objv[3], &length2);
1809 * If a startIndex is specified, we will need to restrict
1810 * the string range to that char index in the string
1812 if (TclGetIntForIndex(interp, objv[4], length2 - 1,
1813 &start) != TCL_OK) {
1818 } else if (start < length2) {
1819 p = ustring2 + start + 1 - length1;
1821 p = ustring2 + length2 - length1;
1824 p = ustring2 + length2 - length1;
1828 for (; p >= ustring2; p--) {
1830 * Scan backwards to find the first character.
1832 if ((*p == *ustring1) &&
1833 (memcmp((char *) ustring1, (char *) p, (size_t)
1834 (length1 * sizeof(Tcl_UniChar))) == 0)) {
1835 match = p - ustring2;
1842 Tcl_SetIntObj(resultPtr, match);
1845 case STR_BYTELENGTH:
1848 Tcl_WrongNumArgs(interp, 2, objv, "string");
1852 if ((enum options) index == STR_BYTELENGTH) {
1853 (void) Tcl_GetStringFromObj(objv[2], &length1);
1856 * If we have a ByteArray object, avoid recomputing the
1857 * string since the byte array contains one byte per
1858 * character. Otherwise, use the Unicode string rep to
1859 * calculate the length.
1862 if (objv[2]->typePtr == &tclByteArrayType) {
1863 (void) Tcl_GetByteArrayFromObj(objv[2], &length1);
1865 length1 = Tcl_GetCharLength(objv[2]);
1868 Tcl_SetIntObj(resultPtr, length1);
1872 int mapElemc, nocase = 0, copySource = 0;
1873 Tcl_Obj **mapElemv, *sourceObj;
1874 Tcl_UniChar *ustring1, *ustring2, *p, *end;
1875 int (*strCmpFn)_ANSI_ARGS_((CONST Tcl_UniChar*,
1876 CONST Tcl_UniChar*, unsigned long));
1878 if (objc < 4 || objc > 5) {
1879 Tcl_WrongNumArgs(interp, 2, objv, "?-nocase? charMap string");
1884 string2 = Tcl_GetStringFromObj(objv[2], &length2);
1885 if ((length2 > 1) &&
1886 strncmp(string2, "-nocase", (size_t) length2) == 0) {
1889 Tcl_AppendStringsToObj(resultPtr, "bad option \"",
1890 string2, "\": must be -nocase",
1896 if (Tcl_ListObjGetElements(interp, objv[objc-2], &mapElemc,
1897 &mapElemv) != TCL_OK) {
1900 if (mapElemc == 0) {
1902 * empty charMap, just return whatever string was given
1904 Tcl_SetObjResult(interp, objv[objc-1]);
1906 } else if (mapElemc & 1) {
1908 * The charMap must be an even number of key/value items
1910 Tcl_SetStringObj(resultPtr, "char map list unbalanced", -1);
1915 * Take a copy of the source string object if it is the
1916 * same as the map string to cut out nasty sharing
1917 * crashes. [Bug 1018562]
1919 if (objv[objc-2] == objv[objc-1]) {
1920 sourceObj = Tcl_DuplicateObj(objv[objc-1]);
1923 sourceObj = objv[objc-1];
1925 ustring1 = Tcl_GetUnicodeFromObj(sourceObj, &length1);
1928 * Empty input string, just stop now
1931 Tcl_DecrRefCount(sourceObj);
1935 end = ustring1 + length1;
1937 strCmpFn = nocase ? Tcl_UniCharNcasecmp : Tcl_UniCharNcmp;
1940 * Force result to be Unicode
1942 Tcl_SetUnicodeObj(resultPtr, ustring1, 0);
1944 if (mapElemc == 2) {
1946 * Special case for one map pair which avoids the extra
1947 * for loop and extra calls to get Unicode data. The
1948 * algorithm is otherwise identical to the multi-pair case.
1949 * This will be >30% faster on larger strings.
1952 Tcl_UniChar *mapString, u2lc;
1954 ustring2 = Tcl_GetUnicodeFromObj(mapElemv[0], &length2);
1956 if ((length2 > length1) || (length2 == 0)) {
1957 /* match string is either longer than input or empty */
1960 mapString = Tcl_GetUnicodeFromObj(mapElemv[1], &mapLen);
1961 u2lc = (nocase ? Tcl_UniCharToLower(*ustring2) : 0);
1962 for (; ustring1 < end; ustring1++) {
1963 if (((*ustring1 == *ustring2) ||
1964 (nocase && (Tcl_UniCharToLower(*ustring1) ==
1966 ((length2 == 1) || strCmpFn(ustring1, ustring2,
1967 (unsigned long) length2) == 0)) {
1968 if (p != ustring1) {
1969 Tcl_AppendUnicodeToObj(resultPtr, p,
1971 p = ustring1 + length2;
1977 Tcl_AppendUnicodeToObj(resultPtr, mapString,
1983 Tcl_UniChar **mapStrings, *u2lc = NULL;
1986 * Precompute pointers to the unicode string and length.
1987 * This saves us repeated function calls later,
1988 * significantly speeding up the algorithm. We only need
1989 * the lowercase first char in the nocase case.
1991 mapStrings = (Tcl_UniChar **) ckalloc((mapElemc * 2)
1992 * sizeof(Tcl_UniChar *));
1993 mapLens = (int *) ckalloc((mapElemc * 2) * sizeof(int));
1995 u2lc = (Tcl_UniChar *)
1996 ckalloc((mapElemc) * sizeof(Tcl_UniChar));
1998 for (index = 0; index < mapElemc; index++) {
1999 mapStrings[index] = Tcl_GetUnicodeFromObj(mapElemv[index],
2001 if (nocase && ((index % 2) == 0)) {
2002 u2lc[index/2] = Tcl_UniCharToLower(*mapStrings[index]);
2005 for (p = ustring1; ustring1 < end; ustring1++) {
2006 for (index = 0; index < mapElemc; index += 2) {
2008 * Get the key string to match on.
2010 ustring2 = mapStrings[index];
2011 length2 = mapLens[index];
2012 if ((length2 > 0) && ((*ustring1 == *ustring2) ||
2013 (nocase && (Tcl_UniCharToLower(*ustring1) ==
2015 /* restrict max compare length */
2016 ((end - ustring1) >= length2) &&
2017 ((length2 == 1) || strCmpFn(ustring2, ustring1,
2018 (unsigned long) length2) == 0)) {
2019 if (p != ustring1) {
2021 * Put the skipped chars onto the result first
2023 Tcl_AppendUnicodeToObj(resultPtr, p,
2025 p = ustring1 + length2;
2030 * Adjust len to be full length of matched string
2035 * Append the map value to the unicode string
2037 Tcl_AppendUnicodeToObj(resultPtr,
2038 mapStrings[index+1], mapLens[index+1]);
2043 ckfree((char *) mapStrings);
2044 ckfree((char *) mapLens);
2046 ckfree((char *) u2lc);
2049 if (p != ustring1) {
2051 * Put the rest of the unmapped chars onto result
2053 Tcl_AppendUnicodeToObj(resultPtr, p, ustring1 - p);
2056 Tcl_DecrRefCount(sourceObj);
2061 Tcl_UniChar *ustring1, *ustring2;
2064 if (objc < 4 || objc > 5) {
2065 Tcl_WrongNumArgs(interp, 2, objv, "?-nocase? pattern string");
2070 string2 = Tcl_GetStringFromObj(objv[2], &length2);
2071 if ((length2 > 1) &&
2072 strncmp(string2, "-nocase", (size_t) length2) == 0) {
2075 Tcl_AppendStringsToObj(resultPtr, "bad option \"",
2076 string2, "\": must be -nocase",
2081 ustring1 = Tcl_GetUnicodeFromObj(objv[objc-1], &length1);
2082 ustring2 = Tcl_GetUnicodeFromObj(objv[objc-2], &length2);
2083 Tcl_SetBooleanObj(resultPtr, TclUniCharMatch(ustring1, length1,
2084 ustring2, length2, nocase));
2091 Tcl_WrongNumArgs(interp, 2, objv, "string first last");
2096 * If we have a ByteArray object, avoid indexing in the
2097 * Utf string since the byte array contains one byte per
2098 * character. Otherwise, use the Unicode string rep to
2102 if (objv[2]->typePtr == &tclByteArrayType) {
2103 string1 = (char *)Tcl_GetByteArrayFromObj(objv[2], &length1);
2107 * Get the length in actual characters.
2110 length1 = Tcl_GetCharLength(objv[2]) - 1;
2113 if ((TclGetIntForIndex(interp, objv[3], length1, &first) != TCL_OK)
2114 || (TclGetIntForIndex(interp, objv[4], length1,
2115 &last) != TCL_OK)) {
2122 if (last >= length1) {
2125 if (last >= first) {
2126 if (string1 != NULL) {
2127 int numBytes = last - first + 1;
2128 resultPtr = Tcl_NewByteArrayObj(
2129 (unsigned char *) &string1[first], numBytes);
2130 Tcl_SetObjResult(interp, resultPtr);
2132 Tcl_SetObjResult(interp,
2133 Tcl_GetRange(objv[2], first, last));
2142 Tcl_WrongNumArgs(interp, 2, objv, "string count");
2146 if (Tcl_GetIntFromObj(interp, objv[3], &count) != TCL_OK) {
2151 Tcl_SetObjResult(interp, objv[2]);
2152 } else if (count > 1) {
2153 string1 = Tcl_GetStringFromObj(objv[2], &length1);
2156 * Only build up a string that has data. Instead of
2157 * building it up with repeated appends, we just allocate
2158 * the necessary space once and copy the string value in.
2159 * Check for overflow with back-division. [Bug #714106]
2161 length2 = length1 * count;
2162 if ((length2 / count) != length1) {
2163 char buf[TCL_INTEGER_SPACE+1];
2164 sprintf(buf, "%d", INT_MAX);
2165 Tcl_AppendStringsToObj(resultPtr,
2166 "string size overflow, must be less than ",
2167 buf, (char *) NULL);
2171 * Include space for the NULL
2173 string2 = (char *) ckalloc((size_t) length2+1);
2174 for (index = 0; index < count; index++) {
2175 memcpy(string2 + (length1 * index), string1,
2178 string2[length2] = '\0';
2180 * We have to directly assign this instead of using
2181 * Tcl_SetStringObj (and indirectly TclInitStringRep)
2182 * because that makes another copy of the data.
2184 resultPtr = Tcl_NewObj();
2185 resultPtr->bytes = string2;
2186 resultPtr->length = length2;
2187 Tcl_SetObjResult(interp, resultPtr);
2193 Tcl_UniChar *ustring1;
2196 if (objc < 5 || objc > 6) {
2197 Tcl_WrongNumArgs(interp, 2, objv,
2198 "string first last ?string?");
2202 ustring1 = Tcl_GetUnicodeFromObj(objv[2], &length1);
2205 if ((TclGetIntForIndex(interp, objv[3], length1, &first) != TCL_OK)
2206 || (TclGetIntForIndex(interp, objv[4], length1,
2207 &last) != TCL_OK)) {
2211 if ((last < first) || (last < 0) || (first > length1)) {
2212 Tcl_SetObjResult(interp, objv[2]);
2218 Tcl_SetUnicodeObj(resultPtr, ustring1, first);
2220 Tcl_AppendObjToObj(resultPtr, objv[5]);
2222 if (last < length1) {
2223 Tcl_AppendUnicodeToObj(resultPtr, ustring1 + last + 1,
2232 if (objc < 3 || objc > 5) {
2233 Tcl_WrongNumArgs(interp, 2, objv, "string ?first? ?last?");
2237 string1 = Tcl_GetStringFromObj(objv[2], &length1);
2241 * Since the result object is not a shared object, it is
2242 * safe to copy the string into the result and do the
2243 * conversion in place. The conversion may change the length
2244 * of the string, so reset the length after conversion.
2247 Tcl_SetStringObj(resultPtr, string1, length1);
2248 if ((enum options) index == STR_TOLOWER) {
2249 length1 = Tcl_UtfToLower(Tcl_GetString(resultPtr));
2250 } else if ((enum options) index == STR_TOUPPER) {
2251 length1 = Tcl_UtfToUpper(Tcl_GetString(resultPtr));
2253 length1 = Tcl_UtfToTitle(Tcl_GetString(resultPtr));
2255 Tcl_SetObjLength(resultPtr, length1);
2258 CONST char *start, *end;
2260 length1 = Tcl_NumUtfChars(string1, length1) - 1;
2261 if (TclGetIntForIndex(interp, objv[3], length1,
2262 &first) != TCL_OK) {
2269 if ((objc == 5) && (TclGetIntForIndex(interp, objv[4], length1,
2270 &last) != TCL_OK)) {
2273 if (last >= length1) {
2277 Tcl_SetObjResult(interp, objv[2]);
2280 start = Tcl_UtfAtIndex(string1, first);
2281 end = Tcl_UtfAtIndex(start, last - first + 1);
2282 length2 = end-start;
2283 string2 = ckalloc((size_t) length2+1);
2284 memcpy(string2, start, (size_t) length2);
2285 string2[length2] = '\0';
2286 if ((enum options) index == STR_TOLOWER) {
2287 length2 = Tcl_UtfToLower(string2);
2288 } else if ((enum options) index == STR_TOUPPER) {
2289 length2 = Tcl_UtfToUpper(string2);
2291 length2 = Tcl_UtfToTitle(string2);
2293 Tcl_SetStringObj(resultPtr, string1, start - string1);
2294 Tcl_AppendToObj(resultPtr, string2, length2);
2295 Tcl_AppendToObj(resultPtr, end, -1);
2301 Tcl_UniChar ch, trim;
2302 register CONST char *p, *end;
2303 char *check, *checkEnd;
2311 string2 = Tcl_GetStringFromObj(objv[3], &length2);
2312 } else if (objc == 3) {
2313 string2 = " \t\n\r";
2314 length2 = strlen(string2);
2316 Tcl_WrongNumArgs(interp, 2, objv, "string ?chars?");
2319 string1 = Tcl_GetStringFromObj(objv[2], &length1);
2320 checkEnd = string2 + length2;
2323 end = string1 + length1;
2325 * The outer loop iterates over the string. The inner
2326 * loop iterates over the trim characters. The loops
2327 * terminate as soon as a non-trim character is discovered
2328 * and string1 is left pointing at the first non-trim
2332 for (p = string1; p < end; p += offset) {
2333 offset = TclUtfToUniChar(p, &ch);
2335 for (check = string2; ; ) {
2336 if (check >= checkEnd) {
2340 check += TclUtfToUniChar(check, &trim);
2353 * The outer loop iterates over the string. The inner
2354 * loop iterates over the trim characters. The loops
2355 * terminate as soon as a non-trim character is discovered
2356 * and length1 marks the last non-trim character.
2359 for (p = string1 + length1; p > end; ) {
2360 p = Tcl_UtfPrev(p, string1);
2361 offset = TclUtfToUniChar(p, &ch);
2362 for (check = string2; ; ) {
2363 if (check >= checkEnd) {
2367 check += TclUtfToUniChar(check, &trim);
2375 Tcl_SetStringObj(resultPtr, string1, length1);
2378 case STR_TRIMLEFT: {
2383 case STR_TRIMRIGHT: {
2391 CONST char *p, *end;
2395 Tcl_WrongNumArgs(interp, 2, objv, "string index");
2399 string1 = Tcl_GetStringFromObj(objv[2], &length1);
2400 numChars = Tcl_NumUtfChars(string1, length1);
2401 if (TclGetIntForIndex(interp, objv[3], numChars-1,
2402 &index) != TCL_OK) {
2408 if (index < numChars) {
2409 p = Tcl_UtfAtIndex(string1, index);
2410 end = string1+length1;
2411 for (cur = index; p < end; cur++) {
2412 p += TclUtfToUniChar(p, &ch);
2413 if (!Tcl_UniCharIsWordChar(ch)) {
2423 Tcl_SetIntObj(resultPtr, cur);
2426 case STR_WORDSTART: {
2433 Tcl_WrongNumArgs(interp, 2, objv, "string index");
2437 string1 = Tcl_GetStringFromObj(objv[2], &length1);
2438 numChars = Tcl_NumUtfChars(string1, length1);
2439 if (TclGetIntForIndex(interp, objv[3], numChars-1,
2440 &index) != TCL_OK) {
2443 if (index >= numChars) {
2444 index = numChars - 1;
2448 p = Tcl_UtfAtIndex(string1, index);
2449 for (cur = index; cur >= 0; cur--) {
2450 TclUtfToUniChar(p, &ch);
2451 if (!Tcl_UniCharIsWordChar(ch)) {
2454 p = Tcl_UtfPrev(p, string1);
2460 Tcl_SetIntObj(resultPtr, cur);
2468 *----------------------------------------------------------------------
2470 * Tcl_SubstObjCmd --
2472 * This procedure is invoked to process the "subst" Tcl command.
2473 * See the user documentation for details on what it does. This
2474 * command relies on Tcl_SubstObj() for its implementation.
2477 * A standard Tcl result.
2480 * See the user documentation.
2482 *----------------------------------------------------------------------
2487 Tcl_SubstObjCmd(dummy, interp, objc, objv)
2488 ClientData dummy; /* Not used. */
2489 Tcl_Interp *interp; /* Current interpreter. */
2490 int objc; /* Number of arguments. */
2491 Tcl_Obj *CONST objv[]; /* Argument objects. */
2493 static CONST char *substOptions[] = {
2494 "-nobackslashes", "-nocommands", "-novariables", (char *) NULL
2497 SUBST_NOBACKSLASHES, SUBST_NOCOMMANDS, SUBST_NOVARS
2500 int optionIndex, flags, i;
2503 * Parse command-line options.
2506 flags = TCL_SUBST_ALL;
2507 for (i = 1; i < (objc-1); i++) {
2508 if (Tcl_GetIndexFromObj(interp, objv[i], substOptions,
2509 "switch", 0, &optionIndex) != TCL_OK) {
2513 switch (optionIndex) {
2514 case SUBST_NOBACKSLASHES: {
2515 flags &= ~TCL_SUBST_BACKSLASHES;
2518 case SUBST_NOCOMMANDS: {
2519 flags &= ~TCL_SUBST_COMMANDS;
2522 case SUBST_NOVARS: {
2523 flags &= ~TCL_SUBST_VARIABLES;
2527 panic("Tcl_SubstObjCmd: bad option index to SubstOptions");
2531 if (i != (objc-1)) {
2532 Tcl_WrongNumArgs(interp, 1, objv,
2533 "?-nobackslashes? ?-nocommands? ?-novariables? string");
2538 * Perform the substitution.
2540 resultPtr = Tcl_SubstObj(interp, objv[i], flags);
2542 if (resultPtr == NULL) {
2545 Tcl_SetObjResult(interp, resultPtr);
2550 *----------------------------------------------------------------------
2554 * This function performs the substitutions specified on the
2555 * given string as described in the user documentation for the
2556 * "subst" Tcl command. This code is heavily based on an
2557 * implementation by Andrew Payne. Note that if a command
2558 * substitution returns TCL_CONTINUE or TCL_RETURN from its
2559 * evaluation and is not completely well-formed, the results are
2560 * not defined (or at least hard to characterise.) This fault
2561 * will be fixed at some point, but the cost of the only sane
2562 * fix (well-formedness check first) is such that you need to
2563 * "precompile and cache" to stop everyone from being hit with
2564 * the consequences every time through. Note that the current
2565 * behaviour is not a security hole; it just restarts parsing
2566 * the string following the substitution in a mildly surprising
2567 * place, and it is a very bad idea to count on this remaining
2568 * the same in future...
2571 * A Tcl_Obj* containing the substituted string, or NULL to
2572 * indicate that an error occurred.
2575 * See the user documentation.
2577 *----------------------------------------------------------------------
2581 Tcl_SubstObj(interp, objPtr, flags)
2590 old = p = Tcl_GetStringFromObj(objPtr, &length);
2591 resultObj = Tcl_NewStringObj("", 0);
2595 if (flags & TCL_SUBST_BACKSLASHES) {
2596 char buf[TCL_UTF_MAX];
2600 Tcl_AppendToObj(resultObj, old, p-old);
2602 Tcl_AppendToObj(resultObj, buf,
2603 Tcl_UtfBackslash(p, &count, buf));
2604 p += count; length -= count;
2612 if (flags & TCL_SUBST_VARIABLES) {
2617 * Code is simpler overall if we (effectively) inline
2618 * Tcl_ParseVar, particularly as that allows us to use
2619 * a non-string interface when we come to appending
2620 * the variable contents to the result object. There
2621 * are a few other optimisations that doing this
2622 * enables (like being able to continue the run of
2623 * unsubstituted characters straight through if a '$'
2624 * does not precede a variable name.)
2626 if (Tcl_ParseVarName(interp, p, -1, &parse, 0) != TCL_OK) {
2629 if (parse.numTokens == 1) {
2631 * There isn't a variable name after all: the $ is
2638 Tcl_AppendToObj(resultObj, old, p-old);
2640 p += parse.tokenPtr->size;
2641 length -= parse.tokenPtr->size;
2642 code = Tcl_EvalTokensStandard(interp, parse.tokenPtr,
2644 if (code == TCL_ERROR) {
2647 if (code == TCL_BREAK) {
2648 Tcl_ResetResult(interp);
2651 if (code != TCL_CONTINUE) {
2652 Tcl_AppendObjToObj(resultObj, Tcl_GetObjResult(interp));
2654 Tcl_ResetResult(interp);
2662 if (flags & TCL_SUBST_COMMANDS) {
2663 Interp *iPtr = (Interp *) interp;
2667 Tcl_AppendToObj(resultObj, old, p-old);
2669 iPtr->evalFlags = TCL_BRACKET_TERM;
2671 code = TclInterpReady(interp);
2672 if (code == TCL_OK) {
2673 code = Tcl_EvalEx(interp, p+1, -1, 0);
2680 Tcl_ResetResult(interp);
2683 Tcl_AppendObjToObj(resultObj, Tcl_GetObjResult(interp));
2685 Tcl_ResetResult(interp);
2686 old = p = (p+1 + iPtr->termOffset + 1);
2687 length -= (iPtr->termOffset + 2);
2699 Tcl_AppendToObj(resultObj, old, p-old);
2704 Tcl_DecrRefCount(resultObj);
2709 *----------------------------------------------------------------------
2711 * Tcl_SwitchObjCmd --
2713 * This object-based procedure is invoked to process the "switch" Tcl
2714 * command. See the user documentation for details on what it does.
2717 * A standard Tcl object result.
2720 * See the user documentation.
2722 *----------------------------------------------------------------------
2727 Tcl_SwitchObjCmd(dummy, interp, objc, objv)
2728 ClientData dummy; /* Not used. */
2729 Tcl_Interp *interp; /* Current interpreter. */
2730 int objc; /* Number of arguments. */
2731 Tcl_Obj *CONST objv[]; /* Argument objects. */
2733 int i, j, index, mode, matched, result, splitObjs;
2734 char *string, *pattern;
2736 Tcl_Obj *CONST *savedObjv = objv;
2738 Interp* iPtr = (Interp*) interp;
2740 int bidx = 0; /* Index of body argument */
2741 Tcl_Obj* blist = NULL; /* List obj which is the body */
2742 CmdFrame ctx; /* Copy of the topmost cmdframe,
2743 * to allow us to mess with the
2744 * line information */
2746 static CONST char *options[] = {
2747 "-exact", "-glob", "-regexp", "--",
2751 OPT_EXACT, OPT_GLOB, OPT_REGEXP, OPT_LAST
2755 for (i = 1; i < objc; i++) {
2756 string = Tcl_GetString(objv[i]);
2757 if (string[0] != '-') {
2760 if (Tcl_GetIndexFromObj(interp, objv[i], options, "option", 0,
2761 &index) != TCL_OK) {
2764 if (index == OPT_LAST) {
2772 Tcl_WrongNumArgs(interp, 1, objv,
2773 "?switches? string pattern body ... ?default body?");
2777 stringObj = objv[i];
2781 bidx = i+1; /* First after the match string */
2785 * If all of the pattern/command pairs are lumped into a single
2786 * argument, split them out again.
2788 * TIP #280: Determine the lines the words in the list start at, based on
2789 * the same data for the list word itself. The cmdFramePtr line information
2790 * is manipulated directly.
2799 if (Tcl_ListObjGetElements(interp, objv[0], &objc, &listv) != TCL_OK) {
2804 * Ensure that the list is non-empty.
2808 Tcl_WrongNumArgs(interp, 1, savedObjv,
2809 "?switches? string {pattern body ... ?default body?}");
2817 * Complain if there is an odd number of words in the list of
2818 * patterns and bodies.
2822 Tcl_ResetResult(interp);
2823 Tcl_AppendResult(interp, "extra switch pattern with no body", NULL);
2826 * Check if this can be due to a badly placed comment
2827 * in the switch block.
2829 * The following is an heuristic to detect the infamous
2830 * "comment in switch" error: just check if a pattern
2835 for (i=0 ; i<objc ; i+=2) {
2836 if (Tcl_GetString(objv[i])[0] == '#') {
2837 Tcl_AppendResult(interp, ", this may be due to a ",
2838 "comment incorrectly placed outside of a ",
2839 "switch body - see the \"switch\" ",
2840 "documentation", NULL);
2850 * Complain if the last body is a continuation. Note that this
2851 * check assumes that the list is non-empty!
2854 if (strcmp(Tcl_GetString(objv[objc-1]), "-") == 0) {
2855 Tcl_ResetResult(interp);
2856 Tcl_AppendResult(interp, "no body specified for pattern \"",
2857 Tcl_GetString(objv[objc-2]), "\"", NULL);
2861 for (i = 0; i < objc; i += 2) {
2863 * See if the pattern matches the string.
2866 pattern = Tcl_GetString(objv[i]);
2870 && (*pattern == 'd')
2871 && (strcmp(pattern, "default") == 0)) {
2876 matched = (strcmp(Tcl_GetString(stringObj), pattern) == 0);
2879 matched = Tcl_StringMatch(Tcl_GetString(stringObj),
2883 matched = Tcl_RegExpMatchObj(interp, stringObj, objv[i]);
2895 * We've got a match. Find a body to execute, skipping bodies
2898 * TIP#280: Now is also the time to determine a line number for the
2903 ctx = *iPtr->cmdFramePtr;
2906 /* We have to perform the GetSrc and other type dependent handling
2907 * of the frame here because we are munging with the line numbers,
2908 * something the other commands like if, etc. are not doing. Them
2909 * are fine with simply passing the CmdFrame through and having
2910 * the special handling done in 'info frame', or the bc compiler
2913 if (ctx.type == TCL_LOCATION_BC) {
2914 /* Note: Type BC => ctx.data.eval.path is not used.
2915 * ctx.data.tebc.codePtr is used instead.
2917 TclGetSrcInfoForPc (&ctx);
2919 /* The line information in the cmdFrame is now a copy we do
2923 if (ctx.type == TCL_LOCATION_SOURCE) {
2924 int bline = ctx.line [bidx];
2926 ctx.line = (int*) ckalloc (objc * sizeof(int));
2929 ListLines (Tcl_GetString (blist), bline, objc, ctx.line);
2932 /* Dynamic code word ... All elements are relative to themselves */
2934 ctx.line = (int*) ckalloc (objc * sizeof(int));
2936 for (k=0; k < objc; k++) {ctx.line[k] = -1;}
2940 /* Anything else ... No information, or dynamic ... */
2942 ctx.line = (int*) ckalloc (objc * sizeof(int));
2944 for (k=0; k < objc; k++) {ctx.line[k] = -1;}
2949 for (j = i + 1; ; j += 2) {
2952 * This shouldn't happen since we've checked that the
2953 * last body is not a continuation...
2955 panic("fall-out when searching for body to match pattern");
2957 if (strcmp(Tcl_GetString(objv[j]), "-") != 0) {
2962 result = Tcl_EvalObjEx(interp, objv[j], 0);
2964 /* TIP #280. Make invoking context available to switch branch */
2965 result = TclEvalObjEx(interp, objv[j], 0, &ctx, j);
2967 ckfree ((char*) ctx.line);
2968 if (pc && (ctx.type == TCL_LOCATION_SOURCE)) {
2969 /* Death of SrcInfo reference */
2970 Tcl_DecrRefCount (ctx.data.eval.path);
2974 if (result == TCL_ERROR) {
2975 char msg[100 + TCL_INTEGER_SPACE];
2977 sprintf(msg, "\n (\"%.50s\" arm line %d)", pattern,
2979 Tcl_AddObjErrorInfo(interp, msg, -1);
2987 *----------------------------------------------------------------------
2991 * This object-based procedure is invoked to process the "time" Tcl
2992 * command. See the user documentation for details on what it does.
2995 * A standard Tcl object result.
2998 * See the user documentation.
3000 *----------------------------------------------------------------------
3005 Tcl_TimeObjCmd(dummy, interp, objc, objv)
3006 ClientData dummy; /* Not used. */
3007 Tcl_Interp *interp; /* Current interpreter. */
3008 int objc; /* Number of arguments. */
3009 Tcl_Obj *CONST objv[]; /* Argument objects. */
3011 register Tcl_Obj *objPtr;
3013 register int i, result;
3015 double totalMicroSec;
3016 Tcl_Time start, stop;
3020 } else if (objc == 3) {
3021 result = Tcl_GetIntFromObj(interp, objv[2], &count);
3022 if (result != TCL_OK) {
3026 Tcl_WrongNumArgs(interp, 1, objv, "command ?count?");
3032 Tcl_GetTime(&start);
3034 result = Tcl_EvalObjEx(interp, objPtr, 0);
3035 if (result != TCL_OK) {
3041 totalMicroSec = ( ( (double) ( stop.sec - start.sec ) ) * 1.0e6
3042 + ( stop.usec - start.usec ) );
3044 /* Use int obj since we know time is not fractional [Bug 1202178] */
3045 objs[0] = Tcl_NewIntObj((count <= 0) ? 0 : (int) totalMicroSec);
3047 objs[0] = Tcl_NewDoubleObj(totalMicroSec/count);
3049 objs[1] = Tcl_NewStringObj("microseconds", -1);
3050 objs[2] = Tcl_NewStringObj("per", -1);
3051 objs[3] = Tcl_NewStringObj("iteration", -1);
3052 Tcl_SetObjResult(interp, Tcl_NewListObj(4, objs));
3057 *----------------------------------------------------------------------
3059 * Tcl_TraceObjCmd --
3061 * This procedure is invoked to process the "trace" Tcl command.
3062 * See the user documentation for details on what it does.
3064 * Standard syntax as of Tcl 8.4 is
3066 * trace {add|info|remove} {command|variable} name ops cmd
3070 * A standard Tcl result.
3073 * See the user documentation.
3074 *----------------------------------------------------------------------
3079 Tcl_TraceObjCmd(dummy, interp, objc, objv)
3080 ClientData dummy; /* Not used. */
3081 Tcl_Interp *interp; /* Current interpreter. */
3082 int objc; /* Number of arguments. */
3083 Tcl_Obj *CONST objv[]; /* Argument objects. */
3086 char *name, *flagOps, *p;
3087 /* Main sub commands to 'trace' */
3088 static CONST char *traceOptions[] = {
3089 "add", "info", "remove",
3090 #ifndef TCL_REMOVE_OBSOLETE_TRACES
3091 "variable", "vdelete", "vinfo",
3095 /* 'OLD' options are pre-Tcl-8.4 style */
3097 TRACE_ADD, TRACE_INFO, TRACE_REMOVE,
3098 #ifndef TCL_REMOVE_OBSOLETE_TRACES
3099 TRACE_OLD_VARIABLE, TRACE_OLD_VDELETE, TRACE_OLD_VINFO
3104 Tcl_WrongNumArgs(interp, 1, objv, "option ?arg arg ...?");
3108 if (Tcl_GetIndexFromObj(interp, objv[1], traceOptions,
3109 "option", 0, &optionIndex) != TCL_OK) {
3112 switch ((enum traceOptions) optionIndex) {
3117 * All sub commands of trace add/remove must take at least
3118 * one more argument. Beyond that we let the subcommand itself
3119 * control the argument structure.
3123 Tcl_WrongNumArgs(interp, 2, objv, "type ?arg arg ...?");
3126 if (Tcl_GetIndexFromObj(interp, objv[2], traceTypeOptions,
3127 "option", 0, &typeIndex) != TCL_OK) {
3130 return (traceSubCmds[typeIndex])(interp, optionIndex, objc, objv);
3132 #ifndef TCL_REMOVE_OBSOLETE_TRACES
3133 case TRACE_OLD_VARIABLE:
3134 case TRACE_OLD_VDELETE: {
3135 Tcl_Obj *copyObjv[6];
3140 Tcl_WrongNumArgs(interp, 2, objv, "name ops command");
3144 opsList = Tcl_NewObj();
3145 Tcl_IncrRefCount(opsList);
3146 flagOps = Tcl_GetStringFromObj(objv[3], &numFlags);
3147 if (numFlags == 0) {
3148 Tcl_DecrRefCount(opsList);
3151 for (p = flagOps; *p != 0; p++) {
3153 Tcl_ListObjAppendElement(NULL, opsList,
3154 Tcl_NewStringObj("read", -1));
3155 } else if (*p == 'w') {
3156 Tcl_ListObjAppendElement(NULL, opsList,
3157 Tcl_NewStringObj("write", -1));
3158 } else if (*p == 'u') {
3159 Tcl_ListObjAppendElement(NULL, opsList,
3160 Tcl_NewStringObj("unset", -1));
3161 } else if (*p == 'a') {
3162 Tcl_ListObjAppendElement(NULL, opsList,
3163 Tcl_NewStringObj("array", -1));
3165 Tcl_DecrRefCount(opsList);
3170 memcpy(copyObjv+1, objv, objc*sizeof(Tcl_Obj *));
3171 copyObjv[4] = opsList;
3172 if (optionIndex == TRACE_OLD_VARIABLE) {
3173 code = (traceSubCmds[2])(interp,TRACE_ADD,objc+1,copyObjv);
3175 code = (traceSubCmds[2])(interp,TRACE_REMOVE,objc+1,copyObjv);
3177 Tcl_DecrRefCount(opsList);
3180 case TRACE_OLD_VINFO: {
3181 ClientData clientData;
3183 Tcl_Obj *resultListPtr, *pairObjPtr, *elemObjPtr;
3186 Tcl_WrongNumArgs(interp, 2, objv, "name");
3189 resultListPtr = Tcl_GetObjResult(interp);
3191 name = Tcl_GetString(objv[2]);
3192 while ((clientData = Tcl_VarTraceInfo(interp, name, 0,
3193 TraceVarProc, clientData)) != 0) {
3195 TraceVarInfo *tvarPtr = (TraceVarInfo *) clientData;
3197 pairObjPtr = Tcl_NewListObj(0, (Tcl_Obj **) NULL);
3199 if (tvarPtr->flags & TCL_TRACE_READS) {
3203 if (tvarPtr->flags & TCL_TRACE_WRITES) {
3207 if (tvarPtr->flags & TCL_TRACE_UNSETS) {
3211 if (tvarPtr->flags & TCL_TRACE_ARRAY) {
3218 * Build a pair (2-item list) with the ops string as
3219 * the first obj element and the tvarPtr->command string
3220 * as the second obj element. Append the pair (as an
3221 * element) to the end of the result object list.
3224 elemObjPtr = Tcl_NewStringObj(ops, -1);
3225 Tcl_ListObjAppendElement(NULL, pairObjPtr, elemObjPtr);
3226 elemObjPtr = Tcl_NewStringObj(tvarPtr->command, -1);
3227 Tcl_ListObjAppendElement(NULL, pairObjPtr, elemObjPtr);
3228 Tcl_ListObjAppendElement(interp, resultListPtr, pairObjPtr);
3230 Tcl_SetObjResult(interp, resultListPtr);
3233 #endif /* TCL_REMOVE_OBSOLETE_TRACES */
3238 Tcl_AppendResult(interp, "bad operations \"", flagOps,
3239 "\": should be one or more of rwua", (char *) NULL);
3245 *----------------------------------------------------------------------
3247 * TclTraceExecutionObjCmd --
3249 * Helper function for Tcl_TraceObjCmd; implements the
3250 * [trace {add|remove|info} execution ...] subcommands.
3251 * See the user documentation for details on what these do.
3254 * Standard Tcl result.
3257 * Depends on the operation (add, remove, or info) being performed;
3258 * may add or remove command traces on a command.
3260 *----------------------------------------------------------------------
3264 TclTraceExecutionObjCmd(interp, optionIndex, objc, objv)
3265 Tcl_Interp *interp; /* Current interpreter. */
3266 int optionIndex; /* Add, info or remove */
3267 int objc; /* Number of arguments. */
3268 Tcl_Obj *CONST objv[]; /* Argument objects. */
3270 int commandLength, index;
3271 char *name, *command;
3273 enum traceOptions { TRACE_ADD, TRACE_INFO, TRACE_REMOVE };
3274 static CONST char *opStrings[] = { "enter", "leave",
3275 "enterstep", "leavestep", (char *) NULL };
3276 enum operations { TRACE_EXEC_ENTER, TRACE_EXEC_LEAVE,
3277 TRACE_EXEC_ENTER_STEP, TRACE_EXEC_LEAVE_STEP };
3279 switch ((enum traceOptions) optionIndex) {
3281 case TRACE_REMOVE: {
3283 int i, listLen, result;
3286 Tcl_WrongNumArgs(interp, 3, objv, "name opList command");
3290 * Make sure the ops argument is a list object; get its length and
3291 * a pointer to its array of element pointers.
3294 result = Tcl_ListObjGetElements(interp, objv[4], &listLen,
3296 if (result != TCL_OK) {
3300 Tcl_SetResult(interp, "bad operation list \"\": must be "
3301 "one or more of enter, leave, enterstep, or leavestep",
3305 for (i = 0; i < listLen; i++) {
3306 if (Tcl_GetIndexFromObj(interp, elemPtrs[i], opStrings,
3307 "operation", TCL_EXACT, &index) != TCL_OK) {
3310 switch ((enum operations) index) {
3311 case TRACE_EXEC_ENTER:
3312 flags |= TCL_TRACE_ENTER_EXEC;
3314 case TRACE_EXEC_LEAVE:
3315 flags |= TCL_TRACE_LEAVE_EXEC;
3317 case TRACE_EXEC_ENTER_STEP:
3318 flags |= TCL_TRACE_ENTER_DURING_EXEC;
3320 case TRACE_EXEC_LEAVE_STEP:
3321 flags |= TCL_TRACE_LEAVE_DURING_EXEC;
3325 command = Tcl_GetStringFromObj(objv[5], &commandLength);
3326 length = (size_t) commandLength;
3327 if ((enum traceOptions) optionIndex == TRACE_ADD) {
3328 TraceCommandInfo *tcmdPtr;
3329 tcmdPtr = (TraceCommandInfo *) ckalloc((unsigned)
3330 (sizeof(TraceCommandInfo) - sizeof(tcmdPtr->command)
3332 tcmdPtr->flags = flags;
3333 tcmdPtr->stepTrace = NULL;
3334 tcmdPtr->startLevel = 0;
3335 tcmdPtr->startCmd = NULL;
3336 tcmdPtr->length = length;
3337 tcmdPtr->refCount = 1;
3338 flags |= TCL_TRACE_DELETE;
3339 if (flags & (TCL_TRACE_ENTER_DURING_EXEC |
3340 TCL_TRACE_LEAVE_DURING_EXEC)) {
3341 flags |= (TCL_TRACE_ENTER_EXEC |
3342 TCL_TRACE_LEAVE_EXEC);
3344 strcpy(tcmdPtr->command, command);
3345 name = Tcl_GetString(objv[3]);
3346 if (Tcl_TraceCommand(interp, name, flags, TraceCommandProc,
3347 (ClientData) tcmdPtr) != TCL_OK) {
3348 ckfree((char *) tcmdPtr);
3353 * Search through all of our traces on this command to
3354 * see if there's one with the given command. If so, then
3355 * delete the first one that matches.
3358 TraceCommandInfo *tcmdPtr;
3359 ClientData clientData = NULL;
3360 name = Tcl_GetString(objv[3]);
3362 /* First ensure the name given is valid */
3363 if (Tcl_FindCommand(interp, name, NULL,
3364 TCL_LEAVE_ERR_MSG) == NULL) {
3368 while ((clientData = Tcl_CommandTraceInfo(interp, name, 0,
3369 TraceCommandProc, clientData)) != NULL) {
3370 tcmdPtr = (TraceCommandInfo *) clientData;
3372 * In checking the 'flags' field we must remove any
3373 * extraneous flags which may have been temporarily
3374 * added by various pieces of the trace mechanism.
3376 if ((tcmdPtr->length == length)
3377 && ((tcmdPtr->flags & (TCL_TRACE_ANY_EXEC |
3379 TCL_TRACE_DELETE)) == flags)
3380 && (strncmp(command, tcmdPtr->command,
3381 (size_t) length) == 0)) {
3382 flags |= TCL_TRACE_DELETE;
3383 if (flags & (TCL_TRACE_ENTER_DURING_EXEC |
3384 TCL_TRACE_LEAVE_DURING_EXEC)) {
3385 flags |= (TCL_TRACE_ENTER_EXEC |
3386 TCL_TRACE_LEAVE_EXEC);
3388 Tcl_UntraceCommand(interp, name,
3389 flags, TraceCommandProc, clientData);
3390 if (tcmdPtr->stepTrace != NULL) {
3392 * We need to remove the interpreter-wide trace
3393 * which we created to allow 'step' traces.
3395 Tcl_DeleteTrace(interp, tcmdPtr->stepTrace);
3396 tcmdPtr->stepTrace = NULL;
3397 if (tcmdPtr->startCmd != NULL) {
3398 ckfree((char *)tcmdPtr->startCmd);
3401 if (tcmdPtr->flags & TCL_TRACE_EXEC_IN_PROGRESS) {
3402 /* Postpone deletion */
3405 tcmdPtr->refCount--;
3406 if (tcmdPtr->refCount < 0) {
3407 Tcl_Panic("TclTraceExecutionObjCmd: negative TraceCommandInfo refCount");
3409 if (tcmdPtr->refCount == 0) {
3410 ckfree((char*)tcmdPtr);
3419 ClientData clientData;
3420 Tcl_Obj *resultListPtr, *eachTraceObjPtr, *elemObjPtr;
3422 Tcl_WrongNumArgs(interp, 3, objv, "name");
3427 name = Tcl_GetString(objv[3]);
3429 /* First ensure the name given is valid */
3430 if (Tcl_FindCommand(interp, name, NULL,
3431 TCL_LEAVE_ERR_MSG) == NULL) {
3435 resultListPtr = Tcl_NewListObj(0, (Tcl_Obj **) NULL);
3436 while ((clientData = Tcl_CommandTraceInfo(interp, name, 0,
3437 TraceCommandProc, clientData)) != NULL) {
3440 TraceCommandInfo *tcmdPtr = (TraceCommandInfo *) clientData;
3443 * Build a list with the ops list as the first obj
3444 * element and the tcmdPtr->command string as the
3445 * second obj element. Append this list (as an
3446 * element) to the end of the result object list.
3449 elemObjPtr = Tcl_NewListObj(0, (Tcl_Obj **) NULL);
3450 Tcl_IncrRefCount(elemObjPtr);
3451 if (tcmdPtr->flags & TCL_TRACE_ENTER_EXEC) {
3452 Tcl_ListObjAppendElement(NULL, elemObjPtr,
3453 Tcl_NewStringObj("enter",5));
3455 if (tcmdPtr->flags & TCL_TRACE_LEAVE_EXEC) {
3456 Tcl_ListObjAppendElement(NULL, elemObjPtr,
3457 Tcl_NewStringObj("leave",5));
3459 if (tcmdPtr->flags & TCL_TRACE_ENTER_DURING_EXEC) {
3460 Tcl_ListObjAppendElement(NULL, elemObjPtr,
3461 Tcl_NewStringObj("enterstep",9));
3463 if (tcmdPtr->flags & TCL_TRACE_LEAVE_DURING_EXEC) {
3464 Tcl_ListObjAppendElement(NULL, elemObjPtr,
3465 Tcl_NewStringObj("leavestep",9));
3467 Tcl_ListObjLength(NULL, elemObjPtr, &numOps);
3469 Tcl_DecrRefCount(elemObjPtr);
3472 eachTraceObjPtr = Tcl_NewListObj(0, (Tcl_Obj **) NULL);
3473 Tcl_ListObjAppendElement(NULL, eachTraceObjPtr, elemObjPtr);
3474 Tcl_DecrRefCount(elemObjPtr);
3477 Tcl_ListObjAppendElement(NULL, eachTraceObjPtr,
3478 Tcl_NewStringObj(tcmdPtr->command, -1));
3479 Tcl_ListObjAppendElement(interp, resultListPtr,
3482 Tcl_SetObjResult(interp, resultListPtr);
3491 *----------------------------------------------------------------------
3493 * TclTraceCommandObjCmd --
3495 * Helper function for Tcl_TraceObjCmd; implements the
3496 * [trace {add|info|remove} command ...] subcommands.
3497 * See the user documentation for details on what these do.
3500 * Standard Tcl result.
3503 * Depends on the operation (add, remove, or info) being performed;
3504 * may add or remove command traces on a command.
3506 *----------------------------------------------------------------------
3510 TclTraceCommandObjCmd(interp, optionIndex, objc, objv)
3511 Tcl_Interp *interp; /* Current interpreter. */
3512 int optionIndex; /* Add, info or remove */
3513 int objc; /* Number of arguments. */
3514 Tcl_Obj *CONST objv[]; /* Argument objects. */
3516 int commandLength, index;
3517 char *name, *command;
3519 enum traceOptions { TRACE_ADD, TRACE_INFO, TRACE_REMOVE };
3520 static CONST char *opStrings[] = { "delete", "rename", (char *) NULL };
3521 enum operations { TRACE_CMD_DELETE, TRACE_CMD_RENAME };
3523 switch ((enum traceOptions) optionIndex) {
3525 case TRACE_REMOVE: {
3527 int i, listLen, result;
3530 Tcl_WrongNumArgs(interp, 3, objv, "name opList command");
3534 * Make sure the ops argument is a list object; get its length and
3535 * a pointer to its array of element pointers.
3538 result = Tcl_ListObjGetElements(interp, objv[4], &listLen,
3540 if (result != TCL_OK) {
3544 Tcl_SetResult(interp, "bad operation list \"\": must be "
3545 "one or more of delete or rename", TCL_STATIC);
3548 for (i = 0; i < listLen; i++) {
3549 if (Tcl_GetIndexFromObj(interp, elemPtrs[i], opStrings,
3550 "operation", TCL_EXACT, &index) != TCL_OK) {
3553 switch ((enum operations) index) {
3554 case TRACE_CMD_RENAME:
3555 flags |= TCL_TRACE_RENAME;
3557 case TRACE_CMD_DELETE:
3558 flags |= TCL_TRACE_DELETE;
3562 command = Tcl_GetStringFromObj(objv[5], &commandLength);
3563 length = (size_t) commandLength;
3564 if ((enum traceOptions) optionIndex == TRACE_ADD) {
3565 TraceCommandInfo *tcmdPtr;
3566 tcmdPtr = (TraceCommandInfo *) ckalloc((unsigned)
3567 (sizeof(TraceCommandInfo) - sizeof(tcmdPtr->command)
3569 tcmdPtr->flags = flags;
3570 tcmdPtr->stepTrace = NULL;
3571 tcmdPtr->startLevel = 0;
3572 tcmdPtr->startCmd = NULL;
3573 tcmdPtr->length = length;
3574 tcmdPtr->refCount = 1;
3575 flags |= TCL_TRACE_DELETE;
3576 strcpy(tcmdPtr->command, command);
3577 name = Tcl_GetString(objv[3]);
3578 if (Tcl_TraceCommand(interp, name, flags, TraceCommandProc,
3579 (ClientData) tcmdPtr) != TCL_OK) {
3580 ckfree((char *) tcmdPtr);
3585 * Search through all of our traces on this command to
3586 * see if there's one with the given command. If so, then
3587 * delete the first one that matches.
3590 TraceCommandInfo *tcmdPtr;
3591 ClientData clientData = NULL;
3592 name = Tcl_GetString(objv[3]);
3594 /* First ensure the name given is valid */
3595 if (Tcl_FindCommand(interp, name, NULL,
3596 TCL_LEAVE_ERR_MSG) == NULL) {
3600 while ((clientData = Tcl_CommandTraceInfo(interp, name, 0,
3601 TraceCommandProc, clientData)) != NULL) {
3602 tcmdPtr = (TraceCommandInfo *) clientData;
3603 if ((tcmdPtr->length == length)
3604 && (tcmdPtr->flags == flags)
3605 && (strncmp(command, tcmdPtr->command,
3606 (size_t) length) == 0)) {
3607 Tcl_UntraceCommand(interp, name,
3608 flags | TCL_TRACE_DELETE,
3609 TraceCommandProc, clientData);
3610 tcmdPtr->flags |= TCL_TRACE_DESTROYED;
3611 tcmdPtr->refCount--;
3612 if (tcmdPtr->refCount < 0) {
3613 Tcl_Panic("TclTraceCommandObjCmd: negative TraceCommandInfo refCount");
3615 if (tcmdPtr->refCount == 0) {
3616 ckfree((char *) tcmdPtr);
3625 ClientData clientData;
3626 Tcl_Obj *resultListPtr, *eachTraceObjPtr, *elemObjPtr;
3628 Tcl_WrongNumArgs(interp, 3, objv, "name");
3633 name = Tcl_GetString(objv[3]);
3635 /* First ensure the name given is valid */
3636 if (Tcl_FindCommand(interp, name, NULL,
3637 TCL_LEAVE_ERR_MSG) == NULL) {
3641 resultListPtr = Tcl_NewListObj(0, (Tcl_Obj **) NULL);
3642 while ((clientData = Tcl_CommandTraceInfo(interp, name, 0,
3643 TraceCommandProc, clientData)) != NULL) {
3646 TraceCommandInfo *tcmdPtr = (TraceCommandInfo *) clientData;
3649 * Build a list with the ops list as
3650 * the first obj element and the tcmdPtr->command string
3651 * as the second obj element. Append this list (as an
3652 * element) to the end of the result object list.
3655 elemObjPtr = Tcl_NewListObj(0, (Tcl_Obj **) NULL);
3656 Tcl_IncrRefCount(elemObjPtr);
3657 if (tcmdPtr->flags & TCL_TRACE_RENAME) {
3658 Tcl_ListObjAppendElement(NULL, elemObjPtr,
3659 Tcl_NewStringObj("rename",6));
3661 if (tcmdPtr->flags & TCL_TRACE_DELETE) {
3662 Tcl_ListObjAppendElement(NULL, elemObjPtr,
3663 Tcl_NewStringObj("delete",6));
3665 Tcl_ListObjLength(NULL, elemObjPtr, &numOps);
3667 Tcl_DecrRefCount(elemObjPtr);
3670 eachTraceObjPtr = Tcl_NewListObj(0, (Tcl_Obj **) NULL);
3671 Tcl_ListObjAppendElement(NULL, eachTraceObjPtr, elemObjPtr);
3672 Tcl_DecrRefCount(elemObjPtr);
3674 elemObjPtr = Tcl_NewStringObj(tcmdPtr->command, -1);
3675 Tcl_ListObjAppendElement(NULL, eachTraceObjPtr, elemObjPtr);
3676 Tcl_ListObjAppendElement(interp, resultListPtr,
3679 Tcl_SetObjResult(interp, resultListPtr);
3688 *----------------------------------------------------------------------
3690 * TclTraceVariableObjCmd --
3692 * Helper function for Tcl_TraceObjCmd; implements the
3693 * [trace {add|info|remove} variable ...] subcommands.
3694 * See the user documentation for details on what these do.
3697 * Standard Tcl result.
3700 * Depends on the operation (add, remove, or info) being performed;
3701 * may add or remove variable traces on a variable.
3703 *----------------------------------------------------------------------
3707 TclTraceVariableObjCmd(interp, optionIndex, objc, objv)
3708 Tcl_Interp *interp; /* Current interpreter. */
3709 int optionIndex; /* Add, info or remove */
3710 int objc; /* Number of arguments. */
3711 Tcl_Obj *CONST objv[]; /* Argument objects. */
3713 int commandLength, index;
3714 char *name, *command;
3716 enum traceOptions { TRACE_ADD, TRACE_INFO, TRACE_REMOVE };
3717 static CONST char *opStrings[] = { "array", "read", "unset", "write",
3719 enum operations { TRACE_VAR_ARRAY, TRACE_VAR_READ, TRACE_VAR_UNSET,
3722 switch ((enum traceOptions) optionIndex) {
3724 case TRACE_REMOVE: {
3726 int i, listLen, result;
3729 Tcl_WrongNumArgs(interp, 3, objv, "name opList command");
3733 * Make sure the ops argument is a list object; get its length and
3734 * a pointer to its array of element pointers.
3737 result = Tcl_ListObjGetElements(interp, objv[4], &listLen,
3739 if (result != TCL_OK) {
3743 Tcl_SetResult(interp, "bad operation list \"\": must be "
3744 "one or more of array, read, unset, or write",
3748 for (i = 0; i < listLen ; i++) {
3749 if (Tcl_GetIndexFromObj(interp, elemPtrs[i], opStrings,
3750 "operation", TCL_EXACT, &index) != TCL_OK) {
3753 switch ((enum operations) index) {
3754 case TRACE_VAR_ARRAY:
3755 flags |= TCL_TRACE_ARRAY;
3757 case TRACE_VAR_READ:
3758 flags |= TCL_TRACE_READS;
3760 case TRACE_VAR_UNSET:
3761 flags |= TCL_TRACE_UNSETS;
3763 case TRACE_VAR_WRITE:
3764 flags |= TCL_TRACE_WRITES;
3768 command = Tcl_GetStringFromObj(objv[5], &commandLength);
3769 length = (size_t) commandLength;
3770 if ((enum traceOptions) optionIndex == TRACE_ADD) {
3772 * This code essentially mallocs together the VarTrace and the
3773 * TraceVarInfo, then inlines the Tcl_TraceVar(). This is
3774 * necessary in order to have the TraceVarInfo to be freed
3775 * automatically when the VarTrace is freed [Bug 1348775]
3778 CompoundVarTrace *compTracePtr;
3779 TraceVarInfo *tvarPtr;
3780 Var *varPtr, *arrayPtr;
3784 compTracePtr = (CompoundVarTrace *) ckalloc((unsigned)
3785 (sizeof(CompoundVarTrace) - sizeof(tvarPtr->command)
3787 tracePtr = &(compTracePtr->trace);
3788 tvarPtr = &(compTracePtr->tvar);
3789 tvarPtr->flags = flags;
3790 if (objv[0] == NULL) {
3791 tvarPtr->flags |= TCL_TRACE_OLD_STYLE;
3793 tvarPtr->length = length;
3794 flags |= TCL_TRACE_UNSETS | TCL_TRACE_RESULT_OBJECT;
3795 strcpy(tvarPtr->command, command);
3796 name = Tcl_GetString(objv[3]);
3797 flagMask = TCL_GLOBAL_ONLY | TCL_NAMESPACE_ONLY;
3798 varPtr = TclLookupVar(interp, name, NULL,
3799 (flags & flagMask) | TCL_LEAVE_ERR_MSG, "trace",
3800 /*createPart1*/ 1, /*createPart2*/ 1, &arrayPtr);
3801 if (varPtr == NULL) {
3802 ckfree((char *) tracePtr);
3805 flagMask = TCL_TRACE_READS | TCL_TRACE_WRITES
3806 | TCL_TRACE_UNSETS | TCL_TRACE_ARRAY
3807 | TCL_TRACE_RESULT_DYNAMIC | TCL_TRACE_RESULT_OBJECT;
3808 #ifndef TCL_REMOVE_OBSOLETE_TRACES
3809 flagMask |= TCL_TRACE_OLD_STYLE;
3811 tracePtr->traceProc = TraceVarProc;
3812 tracePtr->clientData = (ClientData) tvarPtr;
3813 tracePtr->flags = flags & flagMask;
3814 tracePtr->nextPtr = varPtr->tracePtr;
3815 varPtr->tracePtr = tracePtr;
3818 * Search through all of our traces on this variable to
3819 * see if there's one with the given command. If so, then
3820 * delete the first one that matches.
3823 TraceVarInfo *tvarPtr;
3824 ClientData clientData = 0;
3825 name = Tcl_GetString(objv[3]);
3826 while ((clientData = Tcl_VarTraceInfo(interp, name, 0,
3827 TraceVarProc, clientData)) != 0) {
3828 tvarPtr = (TraceVarInfo *) clientData;
3829 if ((tvarPtr->length == length)
3830 && ((tvarPtr->flags & ~TCL_TRACE_OLD_STYLE)==flags)
3831 && (strncmp(command, tvarPtr->command,
3832 (size_t) length) == 0)) {
3833 Tcl_UntraceVar2(interp, name, NULL,
3834 flags | TCL_TRACE_UNSETS | TCL_TRACE_RESULT_OBJECT,
3835 TraceVarProc, clientData);
3843 ClientData clientData;
3844 Tcl_Obj *resultListPtr, *eachTraceObjPtr, *elemObjPtr;
3846 Tcl_WrongNumArgs(interp, 3, objv, "name");
3850 resultListPtr = Tcl_GetObjResult(interp);
3852 name = Tcl_GetString(objv[3]);
3853 while ((clientData = Tcl_VarTraceInfo(interp, name, 0,
3854 TraceVarProc, clientData)) != 0) {
3856 TraceVarInfo *tvarPtr = (TraceVarInfo *) clientData;
3859 * Build a list with the ops list as
3860 * the first obj element and the tcmdPtr->command string
3861 * as the second obj element. Append this list (as an
3862 * element) to the end of the result object list.
3865 elemObjPtr = Tcl_NewListObj(0, (Tcl_Obj **) NULL);
3866 if (tvarPtr->flags & TCL_TRACE_ARRAY) {
3867 Tcl_ListObjAppendElement(NULL, elemObjPtr,
3868 Tcl_NewStringObj("array", 5));
3870 if (tvarPtr->flags & TCL_TRACE_READS) {
3871 Tcl_ListObjAppendElement(NULL, elemObjPtr,
3872 Tcl_NewStringObj("read", 4));
3874 if (tvarPtr->flags & TCL_TRACE_WRITES) {
3875 Tcl_ListObjAppendElement(NULL, elemObjPtr,
3876 Tcl_NewStringObj("write", 5));
3878 if (tvarPtr->flags & TCL_TRACE_UNSETS) {
3879 Tcl_ListObjAppendElement(NULL, elemObjPtr,
3880 Tcl_NewStringObj("unset", 5));
3882 eachTraceObjPtr = Tcl_NewListObj(0, (Tcl_Obj **) NULL);
3883 Tcl_ListObjAppendElement(NULL, eachTraceObjPtr, elemObjPtr);
3885 elemObjPtr = Tcl_NewStringObj(tvarPtr->command, -1);
3886 Tcl_ListObjAppendElement(NULL, eachTraceObjPtr, elemObjPtr);
3887 Tcl_ListObjAppendElement(interp, resultListPtr,
3890 Tcl_SetObjResult(interp, resultListPtr);
3899 *----------------------------------------------------------------------
3901 * Tcl_CommandTraceInfo --
3903 * Return the clientData value associated with a trace on a
3904 * command. This procedure can also be used to step through
3905 * all of the traces on a particular command that have the
3906 * same trace procedure.
3909 * The return value is the clientData value associated with
3910 * a trace on the given command. Information will only be
3911 * returned for a trace with proc as trace procedure. If
3912 * the clientData argument is NULL then the first such trace is
3913 * returned; otherwise, the next relevant one after the one
3914 * given by clientData will be returned. If the command
3915 * doesn't exist then an error message is left in the interpreter
3916 * and NULL is returned. Also, if there are no (more) traces for
3917 * the given command, NULL is returned.
3922 *----------------------------------------------------------------------
3926 Tcl_CommandTraceInfo(interp, cmdName, flags, proc, prevClientData)
3927 Tcl_Interp *interp; /* Interpreter containing command. */
3928 CONST char *cmdName; /* Name of command. */
3929 int flags; /* OR-ed combo or TCL_GLOBAL_ONLY,
3930 * TCL_NAMESPACE_ONLY (can be 0). */
3931 Tcl_CommandTraceProc *proc; /* Procedure assocated with trace. */
3932 ClientData prevClientData; /* If non-NULL, gives last value returned
3933 * by this procedure, so this call will
3934 * return the next trace after that one.
3935 * If NULL, this call will return the
3939 register CommandTrace *tracePtr;
3941 cmdPtr = (Command*)Tcl_FindCommand(interp, cmdName,
3942 NULL, TCL_LEAVE_ERR_MSG);
3943 if (cmdPtr == NULL) {
3948 * Find the relevant trace, if any, and return its clientData.
3951 tracePtr = cmdPtr->tracePtr;
3952 if (prevClientData != NULL) {
3953 for ( ; tracePtr != NULL; tracePtr = tracePtr->nextPtr) {
3954 if ((tracePtr->clientData == prevClientData)
3955 && (tracePtr->traceProc == proc)) {
3956 tracePtr = tracePtr->nextPtr;
3961 for ( ; tracePtr != NULL; tracePtr = tracePtr->nextPtr) {
3962 if (tracePtr->traceProc == proc) {
3963 return tracePtr->clientData;
3970 *----------------------------------------------------------------------
3972 * Tcl_TraceCommand --
3974 * Arrange for rename/deletes to a command to cause a
3975 * procedure to be invoked, which can monitor the operations.
3977 * Also optionally arrange for execution of that command
3978 * to cause a procedure to be invoked.
3981 * A standard Tcl return value.
3984 * A trace is set up on the command given by cmdName, such that
3985 * future changes to the command will be intermediated by
3986 * proc. See the manual entry for complete details on the calling
3987 * sequence for proc.
3989 *----------------------------------------------------------------------
3993 Tcl_TraceCommand(interp, cmdName, flags, proc, clientData)
3994 Tcl_Interp *interp; /* Interpreter in which command is
3996 CONST char *cmdName; /* Name of command. */
3997 int flags; /* OR-ed collection of bits, including any
3998 * of TCL_TRACE_RENAME, TCL_TRACE_DELETE,
3999 * and any of the TRACE_*_EXEC flags */
4000 Tcl_CommandTraceProc *proc; /* Procedure to call when specified ops are
4001 * invoked upon varName. */
4002 ClientData clientData; /* Arbitrary argument to pass to proc. */
4005 register CommandTrace *tracePtr;
4007 cmdPtr = (Command*)Tcl_FindCommand(interp, cmdName,
4008 NULL, TCL_LEAVE_ERR_MSG);
4009 if (cmdPtr == NULL) {
4014 * Set up trace information.
4017 tracePtr = (CommandTrace *) ckalloc(sizeof(CommandTrace));
4018 tracePtr->traceProc = proc;
4019 tracePtr->clientData = clientData;
4020 tracePtr->flags = flags & (TCL_TRACE_RENAME | TCL_TRACE_DELETE
4021 | TCL_TRACE_ANY_EXEC);
4022 tracePtr->nextPtr = cmdPtr->tracePtr;
4023 tracePtr->refCount = 1;
4024 cmdPtr->tracePtr = tracePtr;
4025 if (tracePtr->flags & TCL_TRACE_ANY_EXEC) {
4026 cmdPtr->flags |= CMD_HAS_EXEC_TRACES;
4032 *----------------------------------------------------------------------
4034 * Tcl_UntraceCommand --
4036 * Remove a previously-created trace for a command.
4042 * If there exists a trace for the command given by cmdName
4043 * with the given flags, proc, and clientData, then that trace
4046 *----------------------------------------------------------------------
4050 Tcl_UntraceCommand(interp, cmdName, flags, proc, clientData)
4051 Tcl_Interp *interp; /* Interpreter containing command. */
4052 CONST char *cmdName; /* Name of command. */
4053 int flags; /* OR-ed collection of bits, including any
4054 * of TCL_TRACE_RENAME, TCL_TRACE_DELETE,
4055 * and any of the TRACE_*_EXEC flags */
4056 Tcl_CommandTraceProc *proc; /* Procedure assocated with trace. */
4057 ClientData clientData; /* Arbitrary argument to pass to proc. */
4059 register CommandTrace *tracePtr;
4060 CommandTrace *prevPtr;
4062 Interp *iPtr = (Interp *) interp;
4063 ActiveCommandTrace *activePtr;
4064 int hasExecTraces = 0;
4066 cmdPtr = (Command*)Tcl_FindCommand(interp, cmdName,
4067 NULL, TCL_LEAVE_ERR_MSG);
4068 if (cmdPtr == NULL) {
4072 flags &= (TCL_TRACE_RENAME | TCL_TRACE_DELETE | TCL_TRACE_ANY_EXEC);
4074 for (tracePtr = cmdPtr->tracePtr, prevPtr = NULL; ;
4075 prevPtr = tracePtr, tracePtr = tracePtr->nextPtr) {
4076 if (tracePtr == NULL) {
4079 if ((tracePtr->traceProc == proc)
4080 && ((tracePtr->flags & (TCL_TRACE_RENAME | TCL_TRACE_DELETE |
4081 TCL_TRACE_ANY_EXEC)) == flags)
4082 && (tracePtr->clientData == clientData)) {
4083 if (tracePtr->flags & TCL_TRACE_ANY_EXEC) {
4091 * The code below makes it possible to delete traces while traces
4092 * are active: it makes sure that the deleted trace won't be
4093 * processed by CallCommandTraces.
4096 for (activePtr = iPtr->activeCmdTracePtr; activePtr != NULL;
4097 activePtr = activePtr->nextPtr) {
4098 if (activePtr->nextTracePtr == tracePtr) {
4099 if (activePtr->reverseScan) {
4100 activePtr->nextTracePtr = prevPtr;
4102 activePtr->nextTracePtr = tracePtr->nextPtr;
4106 if (prevPtr == NULL) {
4107 cmdPtr->tracePtr = tracePtr->nextPtr;
4109 prevPtr->nextPtr = tracePtr->nextPtr;
4111 tracePtr->flags = 0;
4113 if ((--tracePtr->refCount) <= 0) {
4114 ckfree((char*)tracePtr);
4117 if (hasExecTraces) {
4118 for (tracePtr = cmdPtr->tracePtr, prevPtr = NULL; tracePtr != NULL ;
4119 prevPtr = tracePtr, tracePtr = tracePtr->nextPtr) {
4120 if (tracePtr->flags & TCL_TRACE_ANY_EXEC) {
4125 * None of the remaining traces on this command are execution
4126 * traces. We therefore remove this flag:
4128 cmdPtr->flags &= ~CMD_HAS_EXEC_TRACES;
4133 *----------------------------------------------------------------------
4135 * TraceCommandProc --
4137 * This procedure is called to handle command changes that have
4138 * been traced using the "trace" command, when using the
4139 * 'rename' or 'delete' options.
4145 * Depends on the command associated with the trace.
4147 *----------------------------------------------------------------------
4152 TraceCommandProc(clientData, interp, oldName, newName, flags)
4153 ClientData clientData; /* Information about the command trace. */
4154 Tcl_Interp *interp; /* Interpreter containing command. */
4155 CONST char *oldName; /* Name of command being changed. */
4156 CONST char *newName; /* New name of command. Empty string
4157 * or NULL means command is being deleted
4158 * (renamed to ""). */
4159 int flags; /* OR-ed bits giving operation and other
4162 Interp *iPtr = (Interp *) interp;
4164 Tcl_SavedResult state;
4165 TraceCommandInfo *tcmdPtr = (TraceCommandInfo *) clientData;
4169 tcmdPtr->refCount++;
4171 if ((tcmdPtr->flags & flags) && !Tcl_InterpDeleted(interp)) {
4173 * Generate a command to execute by appending list elements
4174 * for the old and new command name and the operation.
4177 Tcl_DStringInit(&cmd);
4178 Tcl_DStringAppend(&cmd, tcmdPtr->command, (int) tcmdPtr->length);
4179 Tcl_DStringAppendElement(&cmd, oldName);
4180 Tcl_DStringAppendElement(&cmd, (newName ? newName : ""));
4181 if (flags & TCL_TRACE_RENAME) {
4182 Tcl_DStringAppend(&cmd, " rename", 7);
4183 } else if (flags & TCL_TRACE_DELETE) {
4184 Tcl_DStringAppend(&cmd, " delete", 7);
4188 * Execute the command. Save the interp's result used for the
4189 * command, including the value of iPtr->returnCode which may be
4190 * modified when Tcl_Eval is invoked. We discard any object
4191 * result the command returns.
4193 * Add the TCL_TRACE_DESTROYED flag to tcmdPtr to indicate to
4194 * other areas that this will be destroyed by us, otherwise a
4195 * double-free might occur depending on what the eval does.
4198 Tcl_SaveResult(interp, &state);
4199 stateCode = iPtr->returnCode;
4200 if (flags & TCL_TRACE_DESTROYED) {
4201 tcmdPtr->flags |= TCL_TRACE_DESTROYED;
4204 code = Tcl_EvalEx(interp, Tcl_DStringValue(&cmd),
4205 Tcl_DStringLength(&cmd), 0);
4206 if (code != TCL_OK) {
4207 /* We ignore errors in these traced commands */
4210 Tcl_RestoreResult(interp, &state);
4211 iPtr->returnCode = stateCode;
4213 Tcl_DStringFree(&cmd);
4216 * We delete when the trace was destroyed or if this is a delete trace,
4217 * because command deletes are unconditional, so the trace must go away.
4219 if (flags & (TCL_TRACE_DESTROYED | TCL_TRACE_DELETE)) {
4220 int untraceFlags = tcmdPtr->flags;
4222 if (tcmdPtr->stepTrace != NULL) {
4223 Tcl_DeleteTrace(interp, tcmdPtr->stepTrace);
4224 tcmdPtr->stepTrace = NULL;
4225 if (tcmdPtr->startCmd != NULL) {
4226 ckfree((char *)tcmdPtr->startCmd);
4229 if (tcmdPtr->flags & TCL_TRACE_EXEC_IN_PROGRESS) {
4230 /* Postpone deletion, until exec trace returns */
4235 * We need to construct the same flags for Tcl_UntraceCommand
4236 * as were passed to Tcl_TraceCommand. Reproduce the processing
4237 * of [trace add execution/command]. Be careful to keep this
4238 * code in sync with that.
4241 if (untraceFlags & TCL_TRACE_ANY_EXEC) {
4242 untraceFlags |= TCL_TRACE_DELETE;
4243 if (untraceFlags & (TCL_TRACE_ENTER_DURING_EXEC
4244 | TCL_TRACE_LEAVE_DURING_EXEC)) {
4245 untraceFlags |= (TCL_TRACE_ENTER_EXEC | TCL_TRACE_LEAVE_EXEC);
4247 } else if (untraceFlags & TCL_TRACE_RENAME) {
4248 untraceFlags |= TCL_TRACE_DELETE;
4252 * Remove the trace since TCL_TRACE_DESTROYED tells us to, or the
4253 * command we're tracing has just gone away. Then decrement the
4254 * clientData refCount that was set up by trace creation.
4256 * Note that we save the (return) state of the interpreter to prevent
4257 * bizarre error messages.
4260 Tcl_SaveResult(interp, &state);
4261 stateCode = iPtr->returnCode;
4262 Tcl_UntraceCommand(interp, oldName, untraceFlags,
4263 TraceCommandProc, clientData);
4264 Tcl_RestoreResult(interp, &state);
4265 iPtr->returnCode = stateCode;
4267 tcmdPtr->refCount--;
4269 tcmdPtr->refCount--;
4270 if (tcmdPtr->refCount < 0) {
4271 Tcl_Panic("TraceCommandProc: negative TraceCommandInfo refCount");
4273 if (tcmdPtr->refCount == 0) {
4274 ckfree((char*)tcmdPtr);
4280 *----------------------------------------------------------------------
4282 * TclCheckExecutionTraces --
4284 * Checks on all current command execution traces, and invokes
4285 * procedures which have been registered. This procedure can be
4286 * used by other code which performs execution to unify the
4287 * tracing system, so that execution traces will function for that
4290 * For instance extensions like [incr Tcl] which use their
4291 * own execution technique can make use of Tcl's tracing.
4293 * This procedure is called by 'TclEvalObjvInternal'
4296 * The return value is a standard Tcl completion code such as
4297 * TCL_OK or TCL_ERROR, etc.
4300 * Those side effects made by any trace procedures called.
4302 *----------------------------------------------------------------------
4305 TclCheckExecutionTraces(interp, command, numChars, cmdPtr, code,
4306 traceFlags, objc, objv)
4307 Tcl_Interp *interp; /* The current interpreter. */
4308 CONST char *command; /* Pointer to beginning of the current
4309 * command string. */
4310 int numChars; /* The number of characters in 'command'
4311 * which are part of the command string. */
4312 Command *cmdPtr; /* Points to command's Command struct. */
4313 int code; /* The current result code. */
4314 int traceFlags; /* Current tracing situation. */
4315 int objc; /* Number of arguments for the command. */
4316 Tcl_Obj *CONST objv[]; /* Pointers to Tcl_Obj of each argument. */
4318 Interp *iPtr = (Interp *) interp;
4319 CommandTrace *tracePtr, *lastTracePtr;
4320 ActiveCommandTrace active;
4322 int traceCode = TCL_OK;
4323 TraceCommandInfo* tcmdPtr;
4325 if (command == NULL || cmdPtr->tracePtr == NULL) {
4329 curLevel = ((iPtr->varFramePtr == NULL) ? 0 : iPtr->varFramePtr->level);
4331 active.nextPtr = iPtr->activeCmdTracePtr;
4332 iPtr->activeCmdTracePtr = &active;
4334 active.cmdPtr = cmdPtr;
4335 lastTracePtr = NULL;
4336 for (tracePtr = cmdPtr->tracePtr;
4337 (traceCode == TCL_OK) && (tracePtr != NULL);
4338 tracePtr = active.nextTracePtr) {
4339 if (traceFlags & TCL_TRACE_LEAVE_EXEC) {
4340 /* execute the trace command in order of creation for "leave" */
4341 active.reverseScan = 1;
4342 active.nextTracePtr = NULL;
4343 tracePtr = cmdPtr->tracePtr;
4344 while (tracePtr->nextPtr != lastTracePtr) {
4345 active.nextTracePtr = tracePtr;
4346 tracePtr = tracePtr->nextPtr;
4349 active.reverseScan = 0;
4350 active.nextTracePtr = tracePtr->nextPtr;
4352 if (tracePtr->traceProc == TraceCommandProc) {
4353 tcmdPtr = (TraceCommandInfo*)tracePtr->clientData;
4354 if (tcmdPtr->flags != 0) {
4355 tcmdPtr->curFlags = traceFlags | TCL_TRACE_EXEC_DIRECT;
4356 tcmdPtr->curCode = code;
4357 tcmdPtr->refCount++;
4358 traceCode = TraceExecutionProc((ClientData)tcmdPtr, interp,
4359 curLevel, command, (Tcl_Command)cmdPtr, objc, objv);
4360 tcmdPtr->refCount--;
4361 if (tcmdPtr->refCount < 0) {
4362 Tcl_Panic("TclCheckExecutionTraces: negative TraceCommandInfo refCount");
4364 if (tcmdPtr->refCount == 0) {
4365 ckfree((char*)tcmdPtr);
4369 if (active.nextTracePtr) {
4370 lastTracePtr = active.nextTracePtr->nextPtr;
4373 iPtr->activeCmdTracePtr = active.nextPtr;
4378 *----------------------------------------------------------------------
4380 * TclCheckInterpTraces --
4382 * Checks on all current traces, and invokes procedures which
4383 * have been registered. This procedure can be used by other
4384 * code which performs execution to unify the tracing system.
4385 * For instance extensions like [incr Tcl] which use their
4386 * own execution technique can make use of Tcl's tracing.
4388 * This procedure is called by 'TclEvalObjvInternal'
4391 * The return value is a standard Tcl completion code such as
4392 * TCL_OK or TCL_ERROR, etc.
4395 * Those side effects made by any trace procedures called.
4397 *----------------------------------------------------------------------
4400 TclCheckInterpTraces(interp, command, numChars, cmdPtr, code,
4401 traceFlags, objc, objv)
4402 Tcl_Interp *interp; /* The current interpreter. */
4403 CONST char *command; /* Pointer to beginning of the current
4404 * command string. */
4405 int numChars; /* The number of characters in 'command'
4406 * which are part of the command string. */
4407 Command *cmdPtr; /* Points to command's Command struct. */
4408 int code; /* The current result code. */
4409 int traceFlags; /* Current tracing situation. */
4410 int objc; /* Number of arguments for the command. */
4411 Tcl_Obj *CONST objv[]; /* Pointers to Tcl_Obj of each argument. */
4413 Interp *iPtr = (Interp *) interp;
4414 Trace *tracePtr, *lastTracePtr;
4415 ActiveInterpTrace active;
4417 int traceCode = TCL_OK;
4419 if (command == NULL || iPtr->tracePtr == NULL ||
4420 (iPtr->flags & INTERP_TRACE_IN_PROGRESS)) {
4424 curLevel = iPtr->numLevels;
4426 active.nextPtr = iPtr->activeInterpTracePtr;
4427 iPtr->activeInterpTracePtr = &active;
4429 lastTracePtr = NULL;
4430 for ( tracePtr = iPtr->tracePtr;
4431 (traceCode == TCL_OK) && (tracePtr != NULL);
4432 tracePtr = active.nextTracePtr) {
4433 if (traceFlags & TCL_TRACE_ENTER_EXEC) {
4435 * Execute the trace command in reverse order of creation
4436 * for "enterstep" operation. The order is changed for
4437 * "enterstep" instead of for "leavestep" as was done in
4438 * TclCheckExecutionTraces because for step traces,
4439 * Tcl_CreateObjTrace creates one more linked list of traces
4440 * which results in one more reversal of trace invocation.
4442 active.reverseScan = 1;
4443 active.nextTracePtr = NULL;
4444 tracePtr = iPtr->tracePtr;
4445 while (tracePtr->nextPtr != lastTracePtr) {
4446 active.nextTracePtr = tracePtr;
4447 tracePtr = tracePtr->nextPtr;
4450 active.reverseScan = 0;
4451 active.nextTracePtr = tracePtr->nextPtr;
4453 if (tracePtr->level > 0 && curLevel > tracePtr->level) {
4456 if (!(tracePtr->flags & TCL_TRACE_EXEC_IN_PROGRESS)) {
4458 * The proc invoked might delete the traced command which
4459 * which might try to free tracePtr. We want to use tracePtr
4460 * until the end of this if section, so we use
4461 * Tcl_Preserve() and Tcl_Release() to be sure it is not
4462 * freed while we still need it.
4464 Tcl_Preserve((ClientData) tracePtr);
4465 tracePtr->flags |= TCL_TRACE_EXEC_IN_PROGRESS;
4467 if (tracePtr->flags & (TCL_TRACE_ENTER_EXEC | TCL_TRACE_LEAVE_EXEC)) {
4468 /* New style trace */
4469 if (tracePtr->flags & traceFlags) {
4470 if (tracePtr->proc == TraceExecutionProc) {
4471 TraceCommandInfo *tcmdPtr =
4472 (TraceCommandInfo *) tracePtr->clientData;
4473 tcmdPtr->curFlags = traceFlags;
4474 tcmdPtr->curCode = code;
4476 traceCode = (tracePtr->proc)(tracePtr->clientData,
4477 interp, curLevel, command, (Tcl_Command)cmdPtr,
4481 /* Old-style trace */
4483 if (traceFlags & TCL_TRACE_ENTER_EXEC) {
4485 * Old-style interpreter-wide traces only trigger
4486 * before the command is executed.
4488 traceCode = CallTraceProcedure(interp, tracePtr, cmdPtr,
4489 command, numChars, objc, objv);
4492 tracePtr->flags &= ~TCL_TRACE_EXEC_IN_PROGRESS;
4493 Tcl_Release((ClientData) tracePtr);
4495 if (active.nextTracePtr) {
4496 lastTracePtr = active.nextTracePtr->nextPtr;
4499 iPtr->activeInterpTracePtr = active.nextPtr;
4504 *----------------------------------------------------------------------
4506 * CallTraceProcedure --
4508 * Invokes a trace procedure registered with an interpreter. These
4509 * procedures trace command execution. Currently this trace procedure
4510 * is called with the address of the string-based Tcl_CmdProc for the
4511 * command, not the Tcl_ObjCmdProc.
4517 * Those side effects made by the trace procedure.
4519 *----------------------------------------------------------------------
4523 CallTraceProcedure(interp, tracePtr, cmdPtr, command, numChars, objc, objv)
4524 Tcl_Interp *interp; /* The current interpreter. */
4525 register Trace *tracePtr; /* Describes the trace procedure to call. */
4526 Command *cmdPtr; /* Points to command's Command struct. */
4527 CONST char *command; /* Points to the first character of the
4528 * command's source before substitutions. */
4529 int numChars; /* The number of characters in the
4530 * command's source. */
4531 register int objc; /* Number of arguments for the command. */
4532 Tcl_Obj *CONST objv[]; /* Pointers to Tcl_Obj of each argument. */
4534 Interp *iPtr = (Interp *) interp;
4539 * Copy the command characters into a new string.
4542 commandCopy = (char *) ckalloc((unsigned) (numChars + 1));
4543 memcpy((VOID *) commandCopy, (VOID *) command, (size_t) numChars);
4544 commandCopy[numChars] = '\0';
4547 * Call the trace procedure then free allocated storage.
4550 traceCode = (tracePtr->proc)( tracePtr->clientData, (Tcl_Interp*) iPtr,
4551 iPtr->numLevels, commandCopy,
4552 (Tcl_Command) cmdPtr, objc, objv );
4554 ckfree((char *) commandCopy);
4559 *----------------------------------------------------------------------
4561 * CommandObjTraceDeleted --
4563 * Ensure the trace is correctly deleted by decrementing its
4564 * refCount and only deleting if no other references exist.
4570 * May release memory.
4572 *----------------------------------------------------------------------
4575 CommandObjTraceDeleted(ClientData clientData) {
4576 TraceCommandInfo* tcmdPtr = (TraceCommandInfo*)clientData;
4577 tcmdPtr->refCount--;
4578 if (tcmdPtr->refCount < 0) {
4579 Tcl_Panic("CommandObjTraceDeleted: negative TraceCommandInfo refCount");
4581 if (tcmdPtr->refCount == 0) {
4582 ckfree((char*)tcmdPtr);
4587 *----------------------------------------------------------------------
4589 * TraceExecutionProc --
4591 * This procedure is invoked whenever code relevant to a
4592 * 'trace execution' command is executed. It is called in one
4593 * of two ways in Tcl's core:
4595 * (i) by the TclCheckExecutionTraces, when an execution trace
4596 * has been triggered.
4597 * (ii) by TclCheckInterpTraces, when a prior execution trace has
4598 * created a trace of the internals of a procedure, passing in
4599 * this procedure as the one to be called.
4602 * The return value is a standard Tcl completion code such as
4603 * TCL_OK or TCL_ERROR, etc.
4606 * May invoke an arbitrary Tcl procedure, and may create or
4607 * delete an interpreter-wide trace.
4609 *----------------------------------------------------------------------
4612 TraceExecutionProc(ClientData clientData, Tcl_Interp *interp,
4613 int level, CONST char* command, Tcl_Command cmdInfo,
4614 int objc, struct Tcl_Obj *CONST objv[]) {
4616 Interp *iPtr = (Interp *) interp;
4617 TraceCommandInfo* tcmdPtr = (TraceCommandInfo*)clientData;
4618 int flags = tcmdPtr->curFlags;
4619 int code = tcmdPtr->curCode;
4620 int traceCode = TCL_OK;
4622 if (tcmdPtr->flags & TCL_TRACE_EXEC_IN_PROGRESS) {
4624 * Inside any kind of execution trace callback, we do
4625 * not allow any further execution trace callbacks to
4626 * be called for the same trace.
4631 if (!Tcl_InterpDeleted(interp)) {
4633 * Check whether the current call is going to eval arbitrary
4634 * Tcl code with a generated trace, or whether we are only
4635 * going to setup interpreter-wide traces to implement the
4636 * 'step' traces. This latter situation can happen if
4637 * we create a command trace without either before or after
4638 * operations, but with either of the step operations.
4640 if (flags & TCL_TRACE_EXEC_DIRECT) {
4641 call = flags & tcmdPtr->flags
4642 & (TCL_TRACE_ENTER_EXEC | TCL_TRACE_LEAVE_EXEC);
4647 * First, if we have returned back to the level at which we
4648 * created an interpreter trace for enterstep and/or leavestep
4649 * execution traces, we remove it here.
4651 if (flags & TCL_TRACE_LEAVE_EXEC) {
4652 if ((tcmdPtr->stepTrace != NULL) && (level == tcmdPtr->startLevel)
4653 && (strcmp(command, tcmdPtr->startCmd) == 0)) {
4654 Tcl_DeleteTrace(interp, tcmdPtr->stepTrace);
4655 tcmdPtr->stepTrace = NULL;
4656 if (tcmdPtr->startCmd != NULL) {
4657 ckfree((char *)tcmdPtr->startCmd);
4663 * Second, create the tcl callback, if required.
4666 Tcl_SavedResult state;
4667 int stateCode, i, saveInterpFlags;
4671 Tcl_DStringInit(&cmd);
4672 Tcl_DStringAppend(&cmd, tcmdPtr->command, (int)tcmdPtr->length);
4673 /* Append command with arguments */
4674 Tcl_DStringInit(&sub);
4675 for (i = 0; i < objc; i++) {
4678 str = Tcl_GetStringFromObj(objv[i],&len);
4679 Tcl_DStringAppendElement(&sub, str);
4681 Tcl_DStringAppendElement(&cmd, Tcl_DStringValue(&sub));
4682 Tcl_DStringFree(&sub);
4684 if (flags & TCL_TRACE_ENTER_EXEC) {
4685 /* Append trace operation */
4686 if (flags & TCL_TRACE_EXEC_DIRECT) {
4687 Tcl_DStringAppendElement(&cmd, "enter");
4689 Tcl_DStringAppendElement(&cmd, "enterstep");
4691 } else if (flags & TCL_TRACE_LEAVE_EXEC) {
4692 Tcl_Obj* resultCode;
4693 char* resultCodeStr;
4695 /* Append result code */
4696 resultCode = Tcl_NewIntObj(code);
4697 resultCodeStr = Tcl_GetString(resultCode);
4698 Tcl_DStringAppendElement(&cmd, resultCodeStr);
4699 Tcl_DecrRefCount(resultCode);
4701 /* Append result string */
4702 Tcl_DStringAppendElement(&cmd, Tcl_GetStringResult(interp));
4703 /* Append trace operation */
4704 if (flags & TCL_TRACE_EXEC_DIRECT) {
4705 Tcl_DStringAppendElement(&cmd, "leave");
4707 Tcl_DStringAppendElement(&cmd, "leavestep");
4710 panic("TraceExecutionProc: bad flag combination");
4714 * Execute the command. Save the interp's result used for
4715 * the command, including the value of iPtr->returnCode which
4716 * may be modified when Tcl_Eval is invoked. We discard any
4717 * object result the command returns.
4720 Tcl_SaveResult(interp, &state);
4721 stateCode = iPtr->returnCode;
4723 saveInterpFlags = iPtr->flags;
4724 iPtr->flags |= INTERP_TRACE_IN_PROGRESS;
4725 tcmdPtr->flags |= TCL_TRACE_EXEC_IN_PROGRESS;
4726 tcmdPtr->refCount++;
4728 * This line can have quite arbitrary side-effects,
4729 * including deleting the trace, the command being
4730 * traced, or even the interpreter.
4732 traceCode = Tcl_Eval(interp, Tcl_DStringValue(&cmd));
4733 tcmdPtr->flags &= ~TCL_TRACE_EXEC_IN_PROGRESS;
4736 * Restore the interp tracing flag to prevent cmd traces
4737 * from affecting interp traces
4739 iPtr->flags = saveInterpFlags;;
4740 if (tcmdPtr->flags == 0) {
4741 flags |= TCL_TRACE_DESTROYED;
4744 if (traceCode == TCL_OK) {
4745 /* Restore result if trace execution was successful */
4746 Tcl_RestoreResult(interp, &state);
4747 iPtr->returnCode = stateCode;
4749 Tcl_DiscardResult(&state);
4752 Tcl_DStringFree(&cmd);
4756 * Third, if there are any step execution traces for this proc,
4757 * we register an interpreter trace to invoke enterstep and/or
4759 * We also need to save the current stack level and the proc
4760 * string in startLevel and startCmd so that we can delete this
4761 * interpreter trace when it reaches the end of this proc.
4763 if ((flags & TCL_TRACE_ENTER_EXEC) && (tcmdPtr->stepTrace == NULL)
4764 && (tcmdPtr->flags & (TCL_TRACE_ENTER_DURING_EXEC |
4765 TCL_TRACE_LEAVE_DURING_EXEC))) {
4766 tcmdPtr->startLevel = level;
4768 (char *) ckalloc((unsigned) (strlen(command) + 1));
4769 strcpy(tcmdPtr->startCmd, command);
4770 tcmdPtr->refCount++;
4771 tcmdPtr->stepTrace = Tcl_CreateObjTrace(interp, 0,
4772 (tcmdPtr->flags & TCL_TRACE_ANY_EXEC) >> 2,
4773 TraceExecutionProc, (ClientData)tcmdPtr,
4774 CommandObjTraceDeleted);
4777 if (flags & TCL_TRACE_DESTROYED) {
4778 if (tcmdPtr->stepTrace != NULL) {
4779 Tcl_DeleteTrace(interp, tcmdPtr->stepTrace);
4780 tcmdPtr->stepTrace = NULL;
4781 if (tcmdPtr->startCmd != NULL) {
4782 ckfree((char *)tcmdPtr->startCmd);
4787 tcmdPtr->refCount--;
4788 if (tcmdPtr->refCount < 0) {
4789 Tcl_Panic("TraceExecutionProc: negative TraceCommandInfo refCount");
4791 if (tcmdPtr->refCount == 0) {
4792 ckfree((char*)tcmdPtr);
4799 *----------------------------------------------------------------------
4803 * This procedure is called to handle variable accesses that have
4804 * been traced using the "trace" command.
4807 * Normally returns NULL. If the trace command returns an error,
4808 * then this procedure returns an error string.
4811 * Depends on the command associated with the trace.
4813 *----------------------------------------------------------------------
4818 TraceVarProc(clientData, interp, name1, name2, flags)
4819 ClientData clientData; /* Information about the variable trace. */
4820 Tcl_Interp *interp; /* Interpreter containing variable. */
4821 CONST char *name1; /* Name of variable or array. */
4822 CONST char *name2; /* Name of element within array; NULL means
4823 * scalar variable is being referenced. */
4824 int flags; /* OR-ed bits giving operation and other
4827 Tcl_SavedResult state;
4828 TraceVarInfo *tvarPtr = (TraceVarInfo *) clientData;
4830 int code, destroy = 0;
4834 * We might call Tcl_Eval() below, and that might evaluate [trace
4835 * vdelete] which might try to free tvarPtr. However we do not
4836 * need to protect anything here; it's done by our caller because
4837 * the TraceVarInfo is really part of a CompoundVarTrace. [Bug 1348775]
4841 if ((tvarPtr->flags & flags) && !Tcl_InterpDeleted(interp)) {
4842 if (tvarPtr->length != (size_t) 0) {
4844 * Generate a command to execute by appending list elements
4845 * for the two variable names and the operation.
4848 Tcl_DStringInit(&cmd);
4849 Tcl_DStringAppend(&cmd, tvarPtr->command, (int) tvarPtr->length);
4850 Tcl_DStringAppendElement(&cmd, name1);
4851 Tcl_DStringAppendElement(&cmd, (name2 ? name2 : ""));
4852 #ifndef TCL_REMOVE_OBSOLETE_TRACES
4853 if (tvarPtr->flags & TCL_TRACE_OLD_STYLE) {
4854 if (flags & TCL_TRACE_ARRAY) {
4855 Tcl_DStringAppend(&cmd, " a", 2);
4856 } else if (flags & TCL_TRACE_READS) {
4857 Tcl_DStringAppend(&cmd, " r", 2);
4858 } else if (flags & TCL_TRACE_WRITES) {
4859 Tcl_DStringAppend(&cmd, " w", 2);
4860 } else if (flags & TCL_TRACE_UNSETS) {
4861 Tcl_DStringAppend(&cmd, " u", 2);
4865 if (flags & TCL_TRACE_ARRAY) {
4866 Tcl_DStringAppend(&cmd, " array", 6);
4867 } else if (flags & TCL_TRACE_READS) {
4868 Tcl_DStringAppend(&cmd, " read", 5);
4869 } else if (flags & TCL_TRACE_WRITES) {
4870 Tcl_DStringAppend(&cmd, " write", 6);
4871 } else if (flags & TCL_TRACE_UNSETS) {
4872 Tcl_DStringAppend(&cmd, " unset", 6);
4874 #ifndef TCL_REMOVE_OBSOLETE_TRACES
4879 * Execute the command. Save the interp's result used for
4880 * the command. We discard any object result the command returns.
4882 * Add the TCL_TRACE_DESTROYED flag to tvarPtr to indicate to
4883 * other areas that this will be destroyed by us, otherwise a
4884 * double-free might occur depending on what the eval does.
4887 Tcl_SaveResult(interp, &state);
4888 if ((flags & TCL_TRACE_DESTROYED)
4889 && !(tvarPtr->flags & TCL_TRACE_DESTROYED)) {
4891 tvarPtr->flags |= TCL_TRACE_DESTROYED;
4894 code = Tcl_EvalEx(interp, Tcl_DStringValue(&cmd),
4895 Tcl_DStringLength(&cmd), 0);
4896 if (code != TCL_OK) { /* copy error msg to result */
4897 register Tcl_Obj *errMsgObj = Tcl_GetObjResult(interp);
4898 Tcl_IncrRefCount(errMsgObj);
4899 result = (char *) errMsgObj;
4902 Tcl_RestoreResult(interp, &state);
4904 Tcl_DStringFree(&cmd);
4908 if (result != NULL) {
4909 register Tcl_Obj *errMsgObj = (Tcl_Obj *) result;
4911 Tcl_DecrRefCount(errMsgObj);
4919 *----------------------------------------------------------------------
4921 * Tcl_WhileObjCmd --
4923 * This procedure is invoked to process the "while" Tcl command.
4924 * See the user documentation for details on what it does.
4926 * With the bytecode compiler, this procedure is only called when
4927 * a command name is computed at runtime, and is "while" or the name
4928 * to which "while" was renamed: e.g., "set z while; $z {$i<100} {}"
4931 * A standard Tcl result.
4934 * See the user documentation.
4936 *----------------------------------------------------------------------
4941 Tcl_WhileObjCmd(dummy, interp, objc, objv)
4942 ClientData dummy; /* Not used. */
4943 Tcl_Interp *interp; /* Current interpreter. */
4944 int objc; /* Number of arguments. */
4945 Tcl_Obj *CONST objv[]; /* Argument objects. */
4949 Interp* iPtr = (Interp*) interp;
4953 Tcl_WrongNumArgs(interp, 1, objv, "test command");
4958 result = Tcl_ExprBooleanObj(interp, objv[1], &value);
4959 if (result != TCL_OK) {
4966 result = Tcl_EvalObjEx(interp, objv[2], 0);
4969 result = TclEvalObjEx(interp, objv[2], 0, iPtr->cmdFramePtr,2);
4971 if ((result != TCL_OK) && (result != TCL_CONTINUE)) {
4972 if (result == TCL_ERROR) {
4973 char msg[32 + TCL_INTEGER_SPACE];
4975 sprintf(msg, "\n (\"while\" body line %d)",
4977 Tcl_AddErrorInfo(interp, msg);
4982 if (result == TCL_BREAK) {
4985 if (result == TCL_OK) {
4986 Tcl_ResetResult(interp);
4993 ListLines(listStr, line, n, lines)
4994 CONST char* listStr; /* Pointer to string with list structure.
4995 * Assumed to be valid. Assumed to contain
4998 int line; /* line the list as a whole starts on */
4999 int n; /* #elements in lines */
5000 int* lines; /* Array of line numbers, to fill */
5003 int length = strlen( listStr);
5004 CONST char *element = NULL;
5005 CONST char* next = NULL;
5007 for (i = 0; i < n; i++) {
5008 TclFindElement(NULL, listStr, length, &element, &next, NULL, NULL);
5010 TclAdvanceLines (&line, listStr, element); /* Leading whitespace */
5012 length -= (next - listStr);
5013 TclAdvanceLines (&line, element, next); /* Element */
5016 if (*element == 0) {