diff options
Diffstat (limited to 'generic/tclCmdMZ.c')
| -rw-r--r-- | generic/tclCmdMZ.c | 3163 |
1 files changed, 919 insertions, 2244 deletions
diff --git a/generic/tclCmdMZ.c b/generic/tclCmdMZ.c index 1df9dd1..30586b1 100644 --- a/generic/tclCmdMZ.c +++ b/generic/tclCmdMZ.c @@ -6,65 +6,21 @@ * contains only commands in the generic core (i.e. those that don't * depend much upon UNIX facilities). * - * Copyright © 1987-1993 The Regents of the University of California. - * Copyright © 1994-1997 Sun Microsystems, Inc. - * Copyright © 1998-2000 Scriptics Corporation. - * Copyright © 2002 ActiveState Corporation. - * Copyright © 2003-2009 Donal K. Fellows. + * Copyright (c) 1987-1993 The Regents of the University of California. + * Copyright (c) 1994-1997 Sun Microsystems, Inc. + * Copyright (c) 1998-2000 Scriptics Corporation. + * Copyright (c) 2002 ActiveState Corporation. + * Copyright (c) 2003 Donal K. Fellows. * * See the file "license.terms" for information on usage and redistribution of * this file, and for a DISCLAIMER OF ALL WARRANTIES. */ #include "tclInt.h" -#include "tclCompile.h" #include "tclRegexp.h" -#include "tclStringTrim.h" - -static inline Tcl_Obj * During(Tcl_Interp *interp, int resultCode, - Tcl_Obj *oldOptions, Tcl_Obj *errorInfo); -static Tcl_NRPostProc SwitchPostProc; -static Tcl_NRPostProc TryPostBody; -static Tcl_NRPostProc TryPostFinal; -static Tcl_NRPostProc TryPostHandler; + static int UniCharIsAscii(int character); static int UniCharIsHexDigit(int character); -static int StringCmpOpts(Tcl_Interp *interp, int objc, - Tcl_Obj *const objv[], int *nocase, - Tcl_Size *reqlength); - -/* - * Default set of characters to trim in [string trim] and friends. This is a - * UTF-8 literal string containing all Unicode space characters [TIP #413] - */ - -const char tclDefaultTrimSet[] = - "\x09\x0A\x0B\x0C\x0D " /* ASCII */ - "\xC0\x80" /* nul (U+0000) */ - "\xC2\x85" /* next line (U+0085) */ - "\xC2\xA0" /* non-breaking space (U+00a0) */ - "\xE1\x9A\x80" /* ogham space mark (U+1680) */ - "\xE1\xA0\x8E" /* mongolian vowel separator (U+180e) */ - "\xE2\x80\x80" /* en quad (U+2000) */ - "\xE2\x80\x81" /* em quad (U+2001) */ - "\xE2\x80\x82" /* en space (U+2002) */ - "\xE2\x80\x83" /* em space (U+2003) */ - "\xE2\x80\x84" /* three-per-em space (U+2004) */ - "\xE2\x80\x85" /* four-per-em space (U+2005) */ - "\xE2\x80\x86" /* six-per-em space (U+2006) */ - "\xE2\x80\x87" /* figure space (U+2007) */ - "\xE2\x80\x88" /* punctuation space (U+2008) */ - "\xE2\x80\x89" /* thin space (U+2009) */ - "\xE2\x80\x8A" /* hair space (U+200a) */ - "\xE2\x80\x8B" /* zero width space (U+200b) */ - "\xE2\x80\xA8" /* line separator (U+2028) */ - "\xE2\x80\xA9" /* paragraph separator (U+2029) */ - "\xE2\x80\xAF" /* narrow no-break space (U+202f) */ - "\xE2\x81\x9F" /* medium mathematical space (U+205f) */ - "\xE2\x81\xA0" /* word joiner (U+2060) */ - "\xE3\x80\x80" /* ideographic space (U+3000) */ - "\xEF\xBB\xBF" /* zero width no-break space (U+feff) */ -; /* *---------------------------------------------------------------------- @@ -85,7 +41,7 @@ const char tclDefaultTrimSet[] = int Tcl_PwdObjCmd( - TCL_UNUSED(void *), + ClientData dummy, /* Not used. */ Tcl_Interp *interp, /* Current interpreter. */ int objc, /* Number of arguments. */ Tcl_Obj *const objv[]) /* Argument objects. */ @@ -125,22 +81,22 @@ Tcl_PwdObjCmd( int Tcl_RegexpObjCmd( - TCL_UNUSED(void *), + ClientData dummy, /* Not used. */ Tcl_Interp *interp, /* Current interpreter. */ int objc, /* Number of arguments. */ Tcl_Obj *const objv[]) /* Argument objects. */ { - Tcl_Size offset, stringLength, matchLength, cflags, eflags; - int i, indices, match, about, all, doinline, numMatchesSaved; + int i, indices, match, about, offset, all, doinline, numMatchesSaved; + int cflags, eflags, stringLength, matchLength; Tcl_RegExp regExpr; Tcl_Obj *objPtr, *startIndex = NULL, *resultPtr = NULL; Tcl_RegExpInfo info; - static const char *const options[] = { + static const char *options[] = { "-all", "-about", "-indices", "-inline", "-expanded", "-line", "-linestop", "-lineanchor", "-nocase", "-start", "--", NULL }; - enum regexpoptions { + enum options { REGEXP_ALL, REGEXP_ABOUT, REGEXP_INDICES, REGEXP_INLINE, REGEXP_EXPANDED,REGEXP_LINE, REGEXP_LINESTOP,REGEXP_LINEANCHOR, REGEXP_NOCASE, REGEXP_START, REGEXP_LAST @@ -149,23 +105,24 @@ Tcl_RegexpObjCmd( indices = 0; about = 0; cflags = TCL_REG_ADVANCED; + eflags = 0; offset = 0; all = 0; doinline = 0; for (i = 1; i < objc; i++) { - const char *name; + char *name; int index; name = TclGetString(objv[i]); if (name[0] != '-') { break; } - if (Tcl_GetIndexFromObj(interp, objv[i], options, "option", TCL_EXACT, + if (Tcl_GetIndexFromObj(interp, objv[i], options, "switch", TCL_EXACT, &index) != TCL_OK) { goto optionError; } - switch ((enum regexpoptions) index) { + switch ((enum options) index) { case REGEXP_ALL: all = 1; break; @@ -194,11 +151,11 @@ Tcl_RegexpObjCmd( cflags |= TCL_REG_NLANCH; break; case REGEXP_START: { - Tcl_Size temp; + int temp; if (++i >= objc) { goto endOfForLoop; } - if (TclGetIntForIndexM(interp, objv[i], TCL_SIZE_MAX - 1, &temp) != TCL_OK) { + if (TclGetIntForIndexM(interp, objv[i], 0, &temp) != TCL_OK) { goto optionError; } if (startIndex) { @@ -217,7 +174,7 @@ Tcl_RegexpObjCmd( endOfForLoop: if ((objc - i) < (2 - about)) { Tcl_WrongNumArgs(interp, 1, objv, - "?-option ...? exp string ?matchVar? ?subMatchVar ...?"); + "?switches? exp string ?matchVar? ?subMatchVar subMatchVar ...?"); goto optionError; } objc -= i; @@ -229,10 +186,8 @@ Tcl_RegexpObjCmd( */ if (doinline && ((objc - 2) != 0)) { - Tcl_SetObjResult(interp, Tcl_NewStringObj( - "regexp match variables not allowed when using -inline", -1)); - Tcl_SetErrorCode(interp, "TCL", "OPERATION", "REGEXP", - "MIX_VAR_INLINE", (void *)NULL); + Tcl_AppendResult(interp, "regexp match variables not allowed" + " when using -inline", NULL); goto optionError; } @@ -259,10 +214,10 @@ Tcl_RegexpObjCmd( */ objPtr = objv[1]; - stringLength = TclGetCharLength(objPtr); + stringLength = Tcl_GetCharLength(objPtr); if (startIndex) { - TclGetIntForIndexM(interp, startIndex, stringLength, &offset); + TclGetIntForIndexM(NULL, startIndex, stringLength, &offset); Tcl_DecrRefCount(startIndex); if (offset < 0) { offset = 0; @@ -313,7 +268,7 @@ Tcl_RegexpObjCmd( eflags = 0; } else if (offset > stringLength) { eflags = TCL_REG_NOTBOL; - } else if (TclGetUniChar(objPtr, offset-1) == '\n') { + } else if (Tcl_GetUniChar(objPtr, offset-1) == (Tcl_UniChar)'\n') { eflags = 0; } else { eflags = TCL_REG_NOTBOL; @@ -327,7 +282,7 @@ Tcl_RegexpObjCmd( if (match == 0) { /* - * We want to set the value of the interpreter result only when + * We want to set the value of the intepreter result only when * this is the first time through the loop. */ @@ -339,7 +294,7 @@ Tcl_RegexpObjCmd( */ if (!doinline) { - Tcl_SetObjResult(interp, Tcl_NewWideIntObj(0)); + Tcl_SetObjResult(interp, Tcl_NewIntObj(0)); } return TCL_OK; } @@ -360,14 +315,14 @@ Tcl_RegexpObjCmd( objc = info.nsubs + 1; if (all <= 1) { - TclNewObj(resultPtr); + resultPtr = Tcl_NewObj(); } } for (i = 0; i < objc; i++) { Tcl_Obj *newPtr; if (indices) { - Tcl_Size start, end; + int start, end; Tcl_Obj *objs[2]; /* @@ -388,21 +343,21 @@ Tcl_RegexpObjCmd( end--; } } else { - start = TCL_INDEX_NONE; - end = TCL_INDEX_NONE; + start = -1; + end = -1; } - TclNewIndexObj(objs[0], start); - TclNewIndexObj(objs[1], end); + objs[0] = Tcl_NewLongObj(start); + objs[1] = Tcl_NewLongObj(end); newPtr = Tcl_NewListObj(2, objs); } else { - if ((i <= info.nsubs) && (info.matches[i].end > 0)) { - newPtr = TclGetRange(objPtr, + if (i <= info.nsubs) { + newPtr = Tcl_GetRange(objPtr, offset + info.matches[i].start, offset + info.matches[i].end - 1); } else { - TclNewObj(newPtr); + newPtr = Tcl_NewObj(); } } if (doinline) { @@ -413,8 +368,11 @@ Tcl_RegexpObjCmd( return TCL_ERROR; } } else { - if (Tcl_ObjSetVar2(interp, objv[i], NULL, newPtr, - TCL_LEAVE_ERR_MSG) == NULL) { + Tcl_Obj *valuePtr; + valuePtr = Tcl_ObjSetVar2(interp, objv[i], NULL, newPtr, 0); + if (valuePtr == NULL) { + Tcl_AppendResult(interp, "couldn't set variable \"", + TclGetString(objv[i]), "\"", NULL); return TCL_ERROR; } } @@ -430,12 +388,11 @@ Tcl_RegexpObjCmd( * match. We always increment the offset by at least one to prevent * endless looping (as in the case: regexp -all {a*} a). Otherwise, * when we match the NULL string at the end of the input string, we - * will loop indefinitely (because the length of the match is 0, so + * will loop indefinately (because the length of the match is 0, so * offset never changes). */ - matchLength = (info.matches[0].end - info.matches[0].start); - + matchLength = info.matches[0].end - info.matches[0].start; offset += info.matches[0].end; /* @@ -461,7 +418,7 @@ Tcl_RegexpObjCmd( if (doinline) { Tcl_SetObjResult(interp, resultPtr); } else { - Tcl_SetObjResult(interp, Tcl_NewWideIntObj(all ? all-1 : 1)); + Tcl_SetObjResult(interp, Tcl_NewIntObj(all ? all-1 : 1)); } return TCL_OK; } @@ -485,58 +442,53 @@ Tcl_RegexpObjCmd( int Tcl_RegsubObjCmd( - TCL_UNUSED(void *), + ClientData dummy, /* Not used. */ Tcl_Interp *interp, /* Current interpreter. */ int objc, /* Number of arguments. */ Tcl_Obj *const objv[]) /* Argument objects. */ { - int result, cflags, all, match, command; - Tcl_Size idx, wlen, wsublen, offset, numMatches, numParts; - Tcl_Size start, end, subStart, subEnd; + int idx, result, cflags, all, wlen, wsublen, numMatches, offset; + int start, end, subStart, subEnd, match; Tcl_RegExp regExpr; Tcl_RegExpInfo info; Tcl_Obj *resultPtr, *subPtr, *objPtr, *startIndex = NULL; - Tcl_UniChar ch, *wsrc, *wfirstChar, *wstring, *wsubspec = 0, *wend; + Tcl_UniChar ch, *wsrc, *wfirstChar, *wstring, *wsubspec, *wend; - static const char *const options[] = { - "-all", "-command", "-expanded", "-line", - "-linestop", "-lineanchor", "-nocase", "-start", + static const char *options[] = { + "-all", "-nocase", "-expanded", + "-line", "-linestop", "-lineanchor", "-start", "--", NULL }; - enum regsubobjoptions { - REGSUB_ALL, REGSUB_COMMAND, REGSUB_EXPANDED, REGSUB_LINE, - REGSUB_LINESTOP, REGSUB_LINEANCHOR, REGSUB_NOCASE, REGSUB_START, + enum options { + REGSUB_ALL, REGSUB_NOCASE, REGSUB_EXPANDED, + REGSUB_LINE, REGSUB_LINESTOP, REGSUB_LINEANCHOR, REGSUB_START, REGSUB_LAST }; cflags = TCL_REG_ADVANCED; all = 0; offset = 0; - command = 0; resultPtr = NULL; for (idx = 1; idx < objc; idx++) { - const char *name; + char *name; int index; name = TclGetString(objv[idx]); if (name[0] != '-') { break; } - if (Tcl_GetIndexFromObj(interp, objv[idx], options, "option", + if (Tcl_GetIndexFromObj(interp, objv[idx], options, "switch", TCL_EXACT, &index) != TCL_OK) { goto optionError; } - switch ((enum regsubobjoptions) index) { + switch ((enum options) index) { case REGSUB_ALL: all = 1; break; case REGSUB_NOCASE: cflags |= TCL_REG_NOCASE; break; - case REGSUB_COMMAND: - command = 1; - break; case REGSUB_EXPANDED: cflags |= TCL_REG_EXPANDED; break; @@ -550,11 +502,11 @@ Tcl_RegsubObjCmd( cflags |= TCL_REG_NLANCH; break; case REGSUB_START: { - Tcl_Size temp; + int temp; if (++idx >= objc) { goto endOfForLoop; } - if (TclGetIntForIndexM(interp, objv[idx], TCL_SIZE_MAX - 1, &temp) != TCL_OK) { + if (TclGetIntForIndexM(interp, objv[idx], 0, &temp) != TCL_OK) { goto optionError; } if (startIndex) { @@ -571,9 +523,9 @@ Tcl_RegsubObjCmd( } endOfForLoop: - if (objc < idx + 3 || objc > idx + 4) { + if (objc-idx < 3 || objc-idx > 4) { Tcl_WrongNumArgs(interp, 1, objv, - "?-option ...? exp string subSpec ?varName?"); + "?switches? exp string subSpec ?varName?"); optionError: if (startIndex) { Tcl_DecrRefCount(startIndex); @@ -585,16 +537,16 @@ Tcl_RegsubObjCmd( objv += idx; if (startIndex) { - Tcl_Size stringLength = TclGetCharLength(objv[1]); + int stringLength = Tcl_GetCharLength(objv[1]); - TclGetIntForIndexM(interp, startIndex, stringLength, &offset); + TclGetIntForIndexM(NULL, startIndex, stringLength, &offset); Tcl_DecrRefCount(startIndex); if (offset < 0) { offset = 0; } } - if (all && (offset == 0) && (command == 0) + if (all && (offset == 0) && (strpbrk(TclGetString(objv[2]), "&\\") == NULL) && (strpbrk(TclGetString(objv[0]), "*+?{}()[].\\|^$") == NULL)) { /* @@ -602,18 +554,17 @@ Tcl_RegsubObjCmd( * slightly modified version of the one pair STR_MAP code. */ - Tcl_Size slen; - int nocase, wsrclc; - int (*strCmpFn)(const Tcl_UniChar*,const Tcl_UniChar*,size_t); - Tcl_UniChar *p; + int slen, nocase; + int (*strCmpFn)(const Tcl_UniChar*,const Tcl_UniChar*,unsigned long); + Tcl_UniChar *p, wsrclc; numMatches = 0; nocase = (cflags & TCL_REG_NOCASE); - strCmpFn = nocase ? TclUniCharNcasecmp : TclUniCharNcmp; + strCmpFn = nocase ? Tcl_UniCharNcasecmp : Tcl_UniCharNcmp; - wsrc = TclGetUnicodeFromObj(objv[0], &slen); - wstring = TclGetUnicodeFromObj(objv[1], &wlen); - wsubspec = TclGetUnicodeFromObj(objv[2], &wsublen); + wsrc = Tcl_GetUnicodeFromObj(objv[0], &slen); + wstring = Tcl_GetUnicodeFromObj(objv[1], &wlen); + wsubspec = Tcl_GetUnicodeFromObj(objv[2], &wsublen); wend = wstring + wlen - (slen ? slen - 1 : 0); result = TCL_OK; @@ -624,11 +575,11 @@ Tcl_RegsubObjCmd( */ if (wstring < wend) { - resultPtr = TclNewUnicodeObj(wstring, 0); + resultPtr = Tcl_NewUnicodeObj(wstring, 0); Tcl_IncrRefCount(resultPtr); for (; wstring < wend; wstring++) { - TclAppendUnicodeToObj(resultPtr, wsubspec, wsublen); - TclAppendUnicodeToObj(resultPtr, wstring, 1); + Tcl_AppendUnicodeToObj(resultPtr, wsubspec, wsublen); + Tcl_AppendUnicodeToObj(resultPtr, wstring, 1); numMatches++; } wlen = 0; @@ -641,18 +592,18 @@ Tcl_RegsubObjCmd( (slen==1 || (strCmpFn(wstring, wsrc, (unsigned long) slen) == 0))) { if (numMatches == 0) { - resultPtr = TclNewUnicodeObj(wstring, 0); + resultPtr = Tcl_NewUnicodeObj(wstring, 0); Tcl_IncrRefCount(resultPtr); } if (p != wstring) { - TclAppendUnicodeToObj(resultPtr, p, wstring - p); + Tcl_AppendUnicodeToObj(resultPtr, p, wstring - p); p = wstring + slen; } else { p += slen; } wstring = p - 1; - TclAppendUnicodeToObj(resultPtr, wsubspec, wsublen); + Tcl_AppendUnicodeToObj(resultPtr, wsubspec, wsublen); numMatches++; } } @@ -671,28 +622,6 @@ Tcl_RegsubObjCmd( return TCL_ERROR; } - if (command) { - /* - * In command-prefix mode, we require that the third non-option - * argument be a list, so we enforce that here. Afterwards, we fetch - * the RE compilation again in case objv[0] and objv[2] are the same - * object. (If they aren't, that's cheap to do.) - */ - - if (TclListObjLength(interp, objv[2], &numParts) != TCL_OK) { - return TCL_ERROR; - } - if (numParts < 1) { - Tcl_SetObjResult(interp, Tcl_NewStringObj( - "command prefix must be a list of at least one element", - -1)); - Tcl_SetErrorCode(interp, "TCL", "OPERATION", "REGSUB", - "CMDEMPTY", (void *)NULL); - return TCL_ERROR; - } - regExpr = Tcl_GetRegExpFromObj(interp, objv[0], cflags); - } - /* * Make sure to avoid problems where the objects are shared. This can * cause RegExpObj <> UnicodeObj shimmering that causes data corruption. @@ -704,15 +633,13 @@ Tcl_RegsubObjCmd( } else { objPtr = objv[1]; } - wstring = TclGetUnicodeFromObj(objPtr, &wlen); + wstring = Tcl_GetUnicodeFromObj(objPtr, &wlen); if (objv[2] == objv[0]) { subPtr = Tcl_DuplicateObj(objv[2]); } else { subPtr = objv[2]; } - if (!command) { - wsubspec = TclGetUnicodeFromObj(subPtr, &wsublen); - } + wsubspec = Tcl_GetUnicodeFromObj(subPtr, &wsublen); result = TCL_OK; @@ -747,7 +674,7 @@ Tcl_RegsubObjCmd( break; } if (numMatches == 0) { - resultPtr = TclNewUnicodeObj(wstring, 0); + resultPtr = Tcl_NewUnicodeObj(wstring, 0); Tcl_IncrRefCount(resultPtr); if (offset > 0) { /* @@ -755,7 +682,7 @@ Tcl_RegsubObjCmd( * specified. */ - TclAppendUnicodeToObj(resultPtr, wstring, offset); + Tcl_AppendUnicodeToObj(resultPtr, wstring, offset); } } numMatches++; @@ -768,91 +695,7 @@ Tcl_RegsubObjCmd( Tcl_RegExpGetInfo(regExpr, &info); start = info.matches[0].start; end = info.matches[0].end; - TclAppendUnicodeToObj(resultPtr, wstring + offset, start); - - /* - * In command-prefix mode, the substitutions are added as quoted - * arguments to the subSpec to form a command, that is then executed - * and the result used as the string to substitute in. Actually, - * everything is passed through Tcl_EvalObjv, as that's much faster. - */ - - if (command) { - Tcl_Obj **args = NULL, **parts; - Tcl_Size numArgs; - - TclListObjGetElements(interp, subPtr, &numParts, &parts); - numArgs = numParts + info.nsubs + 1; - args = (Tcl_Obj **)ckalloc(sizeof(Tcl_Obj*) * numArgs); - memcpy(args, parts, sizeof(Tcl_Obj*) * numParts); - - for (idx = 0 ; idx <= info.nsubs ; idx++) { - subStart = info.matches[idx].start; - subEnd = info.matches[idx].end; - if ((subStart >= 0) && (subEnd >= 0)) { - args[idx + numParts] = TclNewUnicodeObj( - wstring + offset + subStart, subEnd - subStart); - } else { - TclNewObj(args[idx + numParts]); - } - Tcl_IncrRefCount(args[idx + numParts]); - } - - /* - * At this point, we're locally holding the references to the - * argument words we added for this time round the loop, and the - * subPtr is holding the references to the words that the user - * supplied directly. None are zero-refcount, which is important - * because Tcl_EvalObjv is "hairy monster" in terms of refcount - * handling, being able to optionally add references to any of its - * argument words. We'll drop the local refs immediately - * afterwards; subPtr is handled in the main exit stanza. - */ - - result = Tcl_EvalObjv(interp, numArgs, args, 0); - for (idx = 0 ; idx <= info.nsubs ; idx++) { - TclDecrRefCount(args[idx + numParts]); - } - ckfree(args); - if (result != TCL_OK) { - if (result == TCL_ERROR) { - Tcl_AppendObjToErrorInfo(interp, Tcl_ObjPrintf( - "\n (%s substitution computation script)", - options[REGSUB_COMMAND])); - } - goto done; - } - - Tcl_AppendObjToObj(resultPtr, Tcl_GetObjResult(interp)); - Tcl_ResetResult(interp); - - /* - * Refetch the unicode, in case the representation was smashed by - * the user code. - */ - - wstring = TclGetUnicodeFromObj(objPtr, &wlen); - - offset += end; - if (end == 0 || start == end) { - /* - * Always consume at least one character of the input string - * in order to prevent infinite loops, even when we - * technically matched the empty string; we must not match - * again at the same spot. - */ - - if (offset < wlen) { - TclAppendUnicodeToObj(resultPtr, wstring + offset, 1); - } - offset++; - } - if (all) { - continue; - } else { - break; - } - } + Tcl_AppendUnicodeToObj(resultPtr, wstring + offset, start); /* * Append the subSpec argument to the variable, making appropriate @@ -872,7 +715,7 @@ Tcl_RegsubObjCmd( idx = ch - '0'; } else if ((ch == '\\') || (ch == '&')) { *wsrc = ch; - TclAppendUnicodeToObj(resultPtr, wfirstChar, + Tcl_AppendUnicodeToObj(resultPtr, wfirstChar, wsrc - wfirstChar + 1); *wsrc = '\\'; wfirstChar = wsrc + 2; @@ -886,7 +729,7 @@ Tcl_RegsubObjCmd( } if (wfirstChar != wsrc) { - TclAppendUnicodeToObj(resultPtr, wfirstChar, + Tcl_AppendUnicodeToObj(resultPtr, wfirstChar, wsrc - wfirstChar); } @@ -894,7 +737,7 @@ Tcl_RegsubObjCmd( subStart = info.matches[idx].start; subEnd = info.matches[idx].end; if ((subStart >= 0) && (subEnd >= 0)) { - TclAppendUnicodeToObj(resultPtr, + Tcl_AppendUnicodeToObj(resultPtr, wstring + offset + subStart, subEnd - subStart); } } @@ -906,7 +749,7 @@ Tcl_RegsubObjCmd( } if (wfirstChar != wsrc) { - TclAppendUnicodeToObj(resultPtr, wfirstChar, wsrc - wfirstChar); + Tcl_AppendUnicodeToObj(resultPtr, wfirstChar, wsrc - wfirstChar); } if (end == 0) { @@ -916,7 +759,7 @@ Tcl_RegsubObjCmd( */ if (offset < wlen) { - TclAppendUnicodeToObj(resultPtr, wstring + offset, 1); + Tcl_AppendUnicodeToObj(resultPtr, wstring + offset, 1); } offset++; } else { @@ -928,7 +771,7 @@ Tcl_RegsubObjCmd( */ if (offset < wlen) { - TclAppendUnicodeToObj(resultPtr, wstring + offset, 1); + Tcl_AppendUnicodeToObj(resultPtr, wstring + offset, 1); } offset++; } @@ -953,11 +796,12 @@ Tcl_RegsubObjCmd( resultPtr = objv[1]; Tcl_IncrRefCount(resultPtr); } else if (offset < wlen) { - TclAppendUnicodeToObj(resultPtr, wstring + offset, wlen - offset); + Tcl_AppendUnicodeToObj(resultPtr, wstring + offset, wlen - offset); } if (objc == 4) { - if (Tcl_ObjSetVar2(interp, objv[3], NULL, resultPtr, - TCL_LEAVE_ERR_MSG) == NULL) { + if (Tcl_ObjSetVar2(interp, objv[3], NULL, resultPtr, 0) == NULL) { + Tcl_AppendResult(interp, "couldn't set variable \"", + TclGetString(objv[3]), "\"", NULL); result = TCL_ERROR; } else { /* @@ -965,7 +809,7 @@ Tcl_RegsubObjCmd( * holding the number of matches. */ - Tcl_SetObjResult(interp, Tcl_NewWideIntObj(numMatches)); + Tcl_SetObjResult(interp, Tcl_NewIntObj(numMatches)); } } else { /* @@ -1007,12 +851,12 @@ Tcl_RegsubObjCmd( int Tcl_RenameObjCmd( - TCL_UNUSED(void *), + ClientData dummy, /* Arbitrary value passed to the command. */ Tcl_Interp *interp, /* Current interpreter. */ int objc, /* Number of arguments. */ Tcl_Obj *const objv[]) /* Argument objects. */ { - const char *oldName, *newName; + char *oldName, *newName; if (objc != 3) { Tcl_WrongNumArgs(interp, 1, objv, "oldName newName"); @@ -1043,7 +887,7 @@ Tcl_RenameObjCmd( int Tcl_ReturnObjCmd( - TCL_UNUSED(void *), + ClientData dummy, /* Not used. */ Tcl_Interp *interp, /* Current interpreter. */ int objc, /* Number of arguments. */ Tcl_Obj *const objv[]) /* Argument objects. */ @@ -1057,7 +901,7 @@ Tcl_ReturnObjCmd( */ int explicitResult = (0 == (objc % 2)); - Tcl_Size numOptionWords = objc - 1 - explicitResult; + int numOptionWords = objc - 1 - explicitResult; if (TCL_ERROR == TclMergeReturnOptions(interp, numOptionWords, objv+1, &returnOpts, &code, &level)) { @@ -1090,28 +934,15 @@ Tcl_ReturnObjCmd( int Tcl_SourceObjCmd( - void *clientData, - Tcl_Interp *interp, /* Current interpreter. */ - int objc, /* Number of arguments. */ - Tcl_Obj *const objv[]) /* Argument objects. */ -{ - return Tcl_NRCallObjProc(interp, TclNRSourceObjCmd, clientData, objc, objv); -} - -int -TclNRSourceObjCmd( - TCL_UNUSED(void *), + ClientData dummy, /* Not used. */ Tcl_Interp *interp, /* Current interpreter. */ int objc, /* Number of arguments. */ Tcl_Obj *const objv[]) /* Argument objects. */ { const char *encodingName = NULL; Tcl_Obj *fileName; - int result; - void **pkgFiles = NULL; - void *names = NULL; - if (objc < 2 || objc > 4) { + if (objc != 2 && objc !=4) { Tcl_WrongNumArgs(interp, 1, objv, "?-encoding name? fileName"); return TCL_ERROR; } @@ -1119,7 +950,7 @@ TclNRSourceObjCmd( fileName = objv[objc-1]; if (objc == 4) { - static const char *const options[] = { + static const char *options[] = { "-encoding", NULL }; int index; @@ -1129,30 +960,9 @@ TclNRSourceObjCmd( return TCL_ERROR; } encodingName = TclGetString(objv[2]); - } else if (objc == 3) { - /* Handle undocumented -nopkg option. This should only be - * used by the internal ::tcl::Pkg::source utility function. */ - static const char *const nopkgoptions[] = { - "-nopkg", NULL - }; - int index; - - if (TCL_ERROR == Tcl_GetIndexFromObj(interp, objv[1], nopkgoptions, - "option", TCL_EXACT, &index)) { - return TCL_ERROR; - } - pkgFiles = (void **)Tcl_GetAssocData(interp, "tclPkgFiles", NULL); - /* Make sure that during the following TclNREvalFile no filenames - * are recorded for inclusion in the "package files" command */ - names = *pkgFiles; - *pkgFiles = NULL; - } - result = TclNREvalFile(interp, fileName, encodingName); - if (pkgFiles) { - /* restore "tclPkgFiles" assocdata to how it was. */ - *pkgFiles = names; } - return result; + + return Tcl_FSEvalFileEx(interp, fileName, encodingName); } /* @@ -1174,17 +984,16 @@ TclNRSourceObjCmd( int Tcl_SplitObjCmd( - TCL_UNUSED(void *), + ClientData dummy, /* Not used. */ Tcl_Interp *interp, /* Current interpreter. */ int objc, /* Number of arguments. */ Tcl_Obj *const objv[]) /* Argument objects. */ { - int ch = 0; + Tcl_UniChar ch; int len; const char *splitChars; - const char *stringPtr; - const char *end; - Tcl_Size splitCharLen, stringLen; + char *stringPtr, *end; + int splitCharLen, stringLen; Tcl_Obj *listPtr, *objPtr; if (objc == 2) { @@ -1199,7 +1008,7 @@ Tcl_SplitObjCmd( stringPtr = TclGetStringFromObj(objv[1], &stringLen); end = stringPtr + stringLen; - TclNewObj(listPtr); + listPtr = Tcl_NewObj(); if (stringLen == 0) { /* @@ -1223,7 +1032,12 @@ Tcl_SplitObjCmd( for ( ; stringPtr < end; stringPtr += len) { len = TclUtfToUniChar(stringPtr, &ch); - hPtr = Tcl_CreateHashEntry(&charReuseTable, INT2PTR(ch), &isNew); + + /* + * Assume Tcl_UniChar is an integral type... + */ + + hPtr = Tcl_CreateHashEntry(&charReuseTable, (char*)0+ch, &isNew); if (isNew) { TclNewStringObj(objPtr, stringPtr, len); @@ -1231,24 +1045,24 @@ Tcl_SplitObjCmd( * Don't need to fiddle with refcount... */ - Tcl_SetHashValue(hPtr, objPtr); + Tcl_SetHashValue(hPtr, (ClientData) objPtr); } else { - objPtr = (Tcl_Obj *)Tcl_GetHashValue(hPtr); + objPtr = (Tcl_Obj *) Tcl_GetHashValue(hPtr); } Tcl_ListObjAppendElement(NULL, listPtr, objPtr); } Tcl_DeleteHashTable(&charReuseTable); } else if (splitCharLen == 1) { - const char *p; + char *p; /* * Handle the special case of splitting on a single character. This is - * only true for the one-char ASCII case, as one Unicode char is > 1 + * only true for the one-char ASCII case, as one unicode char is > 1 * byte in length. */ - while (*stringPtr && (p=strchr(stringPtr,*splitChars)) != NULL) { + while (*stringPtr && (p=strchr(stringPtr,(int)*splitChars)) != NULL) { objPtr = Tcl_NewStringObj(stringPtr, p - stringPtr); Tcl_ListObjAppendElement(NULL, listPtr, objPtr); stringPtr = p + 1; @@ -1256,9 +1070,10 @@ Tcl_SplitObjCmd( TclNewStringObj(objPtr, stringPtr, end - stringPtr); Tcl_ListObjAppendElement(NULL, listPtr, objPtr); } else { - const char *element, *p, *splitEnd; - Tcl_Size splitLen; - int splitChar; + char *element; + const char *p, *splitEnd; + int splitLen; + Tcl_UniChar splitChar; /* * Normal case: split on any of a given set of characters. Discard @@ -1293,8 +1108,7 @@ Tcl_SplitObjCmd( * StringFirstCmd -- * * This procedure is invoked to process the "string first" Tcl command. - * See the user documentation for details on what it does. Note that this - * command only functions correctly on properly formed Tcl UTF strings. + * See the user documentation for details on what it does. * * Results: * A standard Tcl result. @@ -1307,12 +1121,13 @@ Tcl_SplitObjCmd( static int StringFirstCmd( - TCL_UNUSED(void *), + ClientData dummy, /* Not used. */ Tcl_Interp *interp, /* Current interpreter. */ int objc, /* Number of arguments. */ Tcl_Obj *const objv[]) /* Argument objects. */ { - Tcl_Size start = 0; + Tcl_UniChar *ustring1, *ustring2; + int match, start, length1, length2; if (objc < 3 || objc > 4) { Tcl_WrongNumArgs(interp, 1, objv, @@ -1320,14 +1135,81 @@ StringFirstCmd( return TCL_ERROR; } + /* + * We are searching string2 for the sequence string1. + */ + + match = -1; + start = 0; + length2 = -1; + + ustring1 = Tcl_GetUnicodeFromObj(objv[1], &length1); + ustring2 = Tcl_GetUnicodeFromObj(objv[2], &length2); + if (objc == 4) { - Tcl_Size end = TclGetCharLength(objv[2]) - 1; + /* + * If a startIndex is specified, we will need to fast forward to that + * point in the string before we think about a match. + */ - if (TCL_OK != TclGetIntForIndexM(interp, objv[3], end, &start)) { + if (TclGetIntForIndexM(interp, objv[3], length2-1, &start) != TCL_OK){ return TCL_ERROR; } + + /* + * Reread to prevent shimmering problems. + */ + + ustring1 = Tcl_GetUnicodeFromObj(objv[1], &length1); + ustring2 = Tcl_GetUnicodeFromObj(objv[2], &length2); + + if (start >= length2) { + goto str_first_done; + } else if (start > 0) { + ustring2 += start; + length2 -= start; + } else if (start < 0) { + /* + * Invalid start index mapped to string start; Bug #423581 + */ + + start = 0; + } + } + + /* + * If the length of the needle is more than the length of the haystack, it + * cannot be contained in there so we can avoid searching. [Bug 2960021] + */ + + if (length1 > 0 && length1 <= length2) { + register Tcl_UniChar *p, *end; + + end = ustring2 + length2 - length1 + 1; + for (p = ustring2; p < end; p++) { + /* + * Scan forward to find the first character. + */ + + if ((*p == *ustring1) && (TclUniCharNcmp(ustring1, p, + (unsigned long) length1) == 0)) { + match = p - ustring2; + break; + } + } + } + + /* + * Compute the character index of the matching string by counting the + * number of characters before the match. + */ + + if ((match != -1) && (objc == 4)) { + match += start; } - Tcl_SetObjResult(interp, TclStringFirst(objv[1], objv[2], start)); + + str_first_done: + Tcl_SetObjResult(interp, Tcl_NewIntObj(match)); return TCL_OK; } @@ -1337,8 +1219,7 @@ StringFirstCmd( * StringLastCmd -- * * This procedure is invoked to process the "string last" Tcl command. - * See the user documentation for details on what it does. Note that this - * command only functions correctly on properly formed Tcl UTF strings. + * See the user documentation for details on what it does. * * Results: * A standard Tcl result. @@ -1351,27 +1232,80 @@ StringFirstCmd( static int StringLastCmd( - TCL_UNUSED(void *), + ClientData dummy, /* Not used. */ Tcl_Interp *interp, /* Current interpreter. */ int objc, /* Number of arguments. */ Tcl_Obj *const objv[]) /* Argument objects. */ { - Tcl_Size last = TCL_SIZE_MAX; + Tcl_UniChar *ustring1, *ustring2, *p; + int match, start, length1, length2; if (objc < 3 || objc > 4) { Tcl_WrongNumArgs(interp, 1, objv, - "needleString haystackString ?lastIndex?"); + "needleString haystackString ?startIndex?"); return TCL_ERROR; } + /* + * We are searching string2 for the sequence string1. + */ + + match = -1; + start = 0; + length2 = -1; + + ustring1 = Tcl_GetUnicodeFromObj(objv[1], &length1); + ustring2 = Tcl_GetUnicodeFromObj(objv[2], &length2); + if (objc == 4) { - Tcl_Size end = TclGetCharLength(objv[2]) - 1; + /* + * If a startIndex is specified, we will need to restrict the string + * range to that char index in the string + */ - if (TCL_OK != TclGetIntForIndexM(interp, objv[3], end, &last)) { + if (TclGetIntForIndexM(interp, objv[3], length2-1, &start) != TCL_OK){ return TCL_ERROR; } + + /* + * Reread to prevent shimmering problems. + */ + + ustring1 = Tcl_GetUnicodeFromObj(objv[1], &length1); + ustring2 = Tcl_GetUnicodeFromObj(objv[2], &length2); + + if (start < 0) { + goto str_last_done; + } else if (start < length2) { + p = ustring2 + start + 1 - length1; + } else { + p = ustring2 + length2 - length1; + } + } else { + p = ustring2 + length2 - length1; } - Tcl_SetObjResult(interp, TclStringLast(objv[1], objv[2], last)); + + /* + * If the length of the needle is more than the length of the haystack, it + * cannot be contained in there so we can avoid searching. [Bug 2960021] + */ + + if (length1 > 0 && length1 <= length2) { + for (; p >= ustring2; p--) { + /* + * Scan backwards to find the first character. + */ + + if ((*p == *ustring1) && !memcmp(ustring1, p, + sizeof(Tcl_UniChar) * (size_t)length1)) { + match = p - ustring2; + break; + } + } + } + + str_last_done: + Tcl_SetObjResult(interp, Tcl_NewIntObj(match)); return TCL_OK; } @@ -1395,12 +1329,12 @@ StringLastCmd( static int StringIndexCmd( - TCL_UNUSED(void *), + ClientData dummy, /* Not used. */ Tcl_Interp *interp, /* Current interpreter. */ int objc, /* Number of arguments. */ Tcl_Obj *const objv[]) /* Argument objects. */ { - Tcl_Size index, end; + int length, index; if (objc != 3) { Tcl_WrongNumArgs(interp, 1, objv, "string charIndex"); @@ -1408,38 +1342,39 @@ StringIndexCmd( } /* - * Get the char length to calculate what 'end' means. + * If we have a ByteArray object, avoid indexing in the Utf string since + * the byte array contains one byte per character. Otherwise, use the + * Unicode string rep to get the index'th char. */ - end = TclGetCharLength(objv[1]) - 1; - if (TclGetIntForIndexM(interp, objv[2], end, &index) != TCL_OK) { - return TCL_ERROR; - } - - if ((index >= 0) && (index <= end)) { - int ch = TclGetUniChar(objv[1], index); + if (TclIsPureByteArray(objv[1])) { + const unsigned char *string = + Tcl_GetByteArrayFromObj(objv[1], &length); - if (ch == -1) { - return TCL_OK; + if (TclGetIntForIndexM(interp, objv[2], length-1, &index) != TCL_OK){ + return TCL_ERROR; } - + string = Tcl_GetByteArrayFromObj(objv[1], &length); + if ((index >= 0) && (index < length)) { + Tcl_SetObjResult(interp, Tcl_NewByteArrayObj(string + index, 1)); + } + } else { /* - * If we have a ByteArray object, we're careful to generate a new - * bytearray for a result. + * Get Unicode char length to calulate what 'end' means. */ - if (TclIsPureByteArray(objv[1])) { - unsigned char uch = UCHAR(ch); + length = Tcl_GetCharLength(objv[1]); - Tcl_SetObjResult(interp, Tcl_NewByteArrayObj(&uch, 1)); - } else { - char buf[4] = ""; + if (TclGetIntForIndexM(interp, objv[2], length-1, &index) != TCL_OK){ + return TCL_ERROR; + } + if ((index >= 0) && (index < length)) { + char buf[TCL_UTF_MAX]; + Tcl_UniChar ch; - end = Tcl_UniCharToUtf(ch, buf); - if ((ch >= 0xD800) && (end < 3)) { - end += Tcl_UniCharToUtf(-1, buf + end); - } - Tcl_SetObjResult(interp, Tcl_NewStringObj(buf, end)); + ch = Tcl_GetUniChar(objv[1], index); + length = Tcl_UniCharToUtf(ch, buf); + Tcl_SetObjResult(interp, Tcl_NewStringObj(buf, length)); } } return TCL_OK; @@ -1448,63 +1383,6 @@ StringIndexCmd( /* *---------------------------------------------------------------------- * - * StringInsertCmd -- - * - * This procedure is invoked to process the "string insert" Tcl command. - * See the user documentation for details on what it does. Note that this - * command only functions correctly on properly formed Tcl UTF strings. - * - * Results: - * A standard Tcl result. - * - * Side effects: - * See the user documentation. - * - *---------------------------------------------------------------------- - */ - -static int -StringInsertCmd( - TCL_UNUSED(void *), - Tcl_Interp *interp, /* Current interpreter */ - int objc, /* Number of arguments */ - Tcl_Obj *const objv[]) /* Argument objects */ -{ - Tcl_Size length; /* String length */ - Tcl_Size index; /* Insert index */ - Tcl_Obj *outObj; /* Output object */ - - if (objc != 4) { - Tcl_WrongNumArgs(interp, 1, objv, "string index insertString"); - return TCL_ERROR; - } - - length = TclGetCharLength(objv[1]); - if (TclGetIntForIndexM(interp, objv[2], length, &index) != TCL_OK) { - return TCL_ERROR; - } - - if (index < 0) { - index = 0; - } - if (index > length) { - index = length; - } - - outObj = TclStringReplace(interp, objv[1], index, 0, objv[3], - TCL_STRING_IN_PLACE); - - if (outObj != NULL) { - Tcl_SetObjResult(interp, outObj); - return TCL_OK; - } - - return TCL_ERROR; -} - -/* - *---------------------------------------------------------------------- - * * StringIsCmd -- * * This procedure is invoked to process the "string is" Tcl command. See @@ -1522,37 +1400,37 @@ StringInsertCmd( static int StringIsCmd( - TCL_UNUSED(void *), + ClientData dummy, /* Not used. */ Tcl_Interp *interp, /* Current interpreter. */ int objc, /* Number of arguments. */ Tcl_Obj *const objv[]) /* Argument objects. */ { const char *string1, *end, *stop; + Tcl_UniChar ch; int (*chcomp)(int) = NULL; /* The UniChar comparison function. */ int i, failat = 0, result = 1, strict = 0, index, length1, length2; Tcl_Obj *objPtr, *failVarObj = NULL; Tcl_WideInt w; - static const char *const isClasses[] = { + static const char *isClasses[] = { "alnum", "alpha", "ascii", "control", - "boolean", "dict", "digit", "double", - "entier", "false", "graph", "integer", - "list", "lower", "print", "punct", - "space", "true", "upper", - "wideinteger", "wordchar", "xdigit", NULL + "boolean", "digit", "double", "false", + "graph", "integer", "list", "lower", + "print", "punct", "space", "true", + "upper", "wideinteger", "wordchar", "xdigit", + NULL }; - enum isClassesEnum { - STR_IS_ALNUM, STR_IS_ALPHA, STR_IS_ASCII, STR_IS_CONTROL, - STR_IS_BOOL, STR_IS_DICT, STR_IS_DIGIT, STR_IS_DOUBLE, - STR_IS_ENTIER, STR_IS_FALSE, STR_IS_GRAPH, STR_IS_INT, - STR_IS_LIST, STR_IS_LOWER, STR_IS_PRINT, STR_IS_PUNCT, - STR_IS_SPACE, STR_IS_TRUE, STR_IS_UPPER, - STR_IS_WIDE, STR_IS_WORD, STR_IS_XDIGIT + enum isClasses { + STR_IS_ALNUM, STR_IS_ALPHA, STR_IS_ASCII, STR_IS_CONTROL, + STR_IS_BOOL, STR_IS_DIGIT, STR_IS_DOUBLE, STR_IS_FALSE, + STR_IS_GRAPH, STR_IS_INT, STR_IS_LIST, STR_IS_LOWER, + STR_IS_PRINT, STR_IS_PUNCT, STR_IS_SPACE, STR_IS_TRUE, + STR_IS_UPPER, STR_IS_WIDE, STR_IS_WORD, STR_IS_XDIGIT }; - static const char *const isOptions[] = { + static const char *isOptions[] = { "-strict", "-failindex", NULL }; - enum isOptionsEnum { + enum isOptions { OPT_STRICT, OPT_FAILIDX }; @@ -1574,7 +1452,7 @@ StringIsCmd( &idx2) != TCL_OK) { return TCL_ERROR; } - switch ((enum isOptionsEnum) idx2) { + switch ((enum isOptions) idx2) { case OPT_STRICT: strict = 1; break; @@ -1593,7 +1471,7 @@ StringIsCmd( /* * We get the objPtr so that we can short-cut for some classes by checking * the object type (int and double), but we need the string otherwise, - * because we don't want any conversion of type occurring (as, for example, + * because we don't want any conversion of type occuring (as, for example, * Tcl_Get*FromObj would do). */ @@ -1603,7 +1481,7 @@ StringIsCmd( * When entering here, result == 1 and failat == 0. */ - switch ((enum isClassesEnum) index) { + switch ((enum isClasses) index) { case STR_IS_ALNUM: chcomp = Tcl_UniCharIsAlnum; break; @@ -1616,80 +1494,34 @@ StringIsCmd( case STR_IS_BOOL: case STR_IS_TRUE: case STR_IS_FALSE: - if (!TclHasInternalRep(objPtr, &tclBooleanType) - && (TCL_OK != TclSetBooleanFromAny(NULL, objPtr))) { + if (TCL_OK != Tcl_ConvertToType(NULL, objPtr, &tclBooleanType)) { if (strict) { result = 0; } else { string1 = TclGetStringFromObj(objPtr, &length1); result = length1 == 0; } - } else if (index != STR_IS_BOOL) { - TclGetBooleanFromObj(NULL, objPtr, &i); - if ((index == STR_IS_TRUE) ^ i) { - result = 0; - } + } else if (((index == STR_IS_TRUE) && + objPtr->internalRep.longValue == 0) + || ((index == STR_IS_FALSE) && + objPtr->internalRep.longValue != 0)) { + result = 0; } break; case STR_IS_CONTROL: chcomp = Tcl_UniCharIsControl; break; - case STR_IS_DICT: { - int dresult; - Tcl_Size dsize; - - dresult = Tcl_DictObjSize(interp, objPtr, &dsize); - Tcl_ResetResult(interp); - result = (dresult == TCL_OK) ? 1 : 0; - if (dresult != TCL_OK && failVarObj != NULL) { - /* - * Need to figure out where the list parsing failed, which is - * fairly expensive. This is adapted from the core of - * SetDictFromAny(). - */ - - const char *elemStart, *nextElem; - Tcl_Size lenRemain, elemSize; - const char *p; - - string1 = TclGetStringFromObj(objPtr, &length1); - end = string1 + length1; - failat = -1; - for (p=string1, lenRemain=length1; lenRemain > 0; - p=nextElem, lenRemain=end-nextElem) { - if (TCL_ERROR == TclFindElement(NULL, p, lenRemain, - &elemStart, &nextElem, &elemSize, NULL)) { - Tcl_Obj *tmpStr; - - /* - * This is the simplest way of getting the number of - * characters parsed. Note that this is not the same as - * the number of bytes when parsing strings with non-ASCII - * characters in them. - * - * Skip leading spaces first. This is only really an issue - * if it is the first "element" that has the failure. - */ - - while (TclIsSpaceProc(*p)) { - p++; - } - TclNewStringObj(tmpStr, string1, p-string1); - failat = TclGetCharLength(tmpStr); - TclDecrRefCount(tmpStr); - break; - } - } - } - break; - } case STR_IS_DIGIT: chcomp = Tcl_UniCharIsDigit; break; case STR_IS_DOUBLE: { - if (TclHasInternalRep(objPtr, &tclDoubleType) || - TclHasInternalRep(objPtr, &tclIntType) || - TclHasInternalRep(objPtr, &tclBignumType)) { + /* TODO */ + if ((objPtr->typePtr == &tclDoubleType) || + (objPtr->typePtr == &tclIntType) || +#ifndef NO_WIDE_TYPE + (objPtr->typePtr == &tclWideIntType) || +#endif + (objPtr->typePtr == &tclBignumType)) { break; } string1 = TclGetStringFromObj(objPtr, &length1); @@ -1700,7 +1532,7 @@ StringIsCmd( goto str_is_done; } end = string1 + length1; - if (TclParseNumber(NULL, objPtr, NULL, NULL, TCL_INDEX_NONE, + if (TclParseNumber(NULL, objPtr, NULL, NULL, -1, (const char **) &stop, 0) != TCL_OK) { result = 0; failat = 0; @@ -1708,7 +1540,8 @@ StringIsCmd( failat = stop - string1; if (stop < end) { result = 0; - TclFreeInternalRep(objPtr); + TclFreeIntRep(objPtr); + objPtr->typePtr = NULL; } } break; @@ -1717,53 +1550,16 @@ StringIsCmd( chcomp = Tcl_UniCharIsGraph; break; case STR_IS_INT: - case STR_IS_ENTIER: - if (TclHasInternalRep(objPtr, &tclIntType) || - TclHasInternalRep(objPtr, &tclBignumType)) { + if (TCL_OK == TclGetIntFromObj(NULL, objPtr, &i)) { break; } - string1 = TclGetStringFromObj(objPtr, &length1); - if (length1 == 0) { - if (strict) { - result = 0; - } - goto str_is_done; - } - end = string1 + length1; - if (TclParseNumber(NULL, objPtr, NULL, NULL, TCL_INDEX_NONE, - (const char **) &stop, TCL_PARSE_INTEGER_ONLY) == TCL_OK) { - if (stop == end) { - /* - * Entire string parses as an integer. - */ - - break; - } else { - /* - * Some prefix parsed as an integer, but not the whole string, - * so return failure index as the point where parsing stopped. - * Clear out the internal rep, since keeping it would leave - * *objPtr in an inconsistent state. - */ - - result = 0; - failat = stop - string1; - TclFreeInternalRep(objPtr); - } - } else { - /* - * No prefix is a valid integer. Fail at beginning. - */ - - result = 0; - failat = 0; - } - break; + goto failedIntParse; case STR_IS_WIDE: - if (TCL_OK == TclGetWideIntFromObj(NULL, objPtr, &w)) { + if (TCL_OK == Tcl_GetWideIntFromObj(NULL, objPtr, &w)) { break; } + failedIntParse: string1 = TclGetStringFromObj(objPtr, &length1); if (length1 == 0) { if (strict) { @@ -1781,7 +1577,7 @@ StringIsCmd( break; } end = string1 + length1; - if (TclParseNumber(NULL, objPtr, NULL, NULL, TCL_INDEX_NONE, + if (TclParseNumber(NULL, objPtr, NULL, NULL, -1, (const char **) &stop, TCL_PARSE_INTEGER_ONLY) == TCL_OK) { if (stop == end) { /* @@ -1801,7 +1597,8 @@ StringIsCmd( */ failat = stop - string1; - TclFreeInternalRep(objPtr); + TclFreeIntRep(objPtr); + objPtr->typePtr = NULL; } } else { /* @@ -1829,8 +1626,8 @@ StringIsCmd( */ const char *elemStart, *nextElem; - Tcl_Size lenRemain, elemSize; - const char *p; + int lenRemain, elemSize; + register const char *p; string1 = TclGetStringFromObj(objPtr, &length1); end = string1 + length1; @@ -1851,11 +1648,11 @@ StringIsCmd( * if it is the first "element" that has the failure. */ - while (TclIsSpaceProcM(*p)) { + while (TclIsSpaceProc(*p)) { p++; } TclNewStringObj(tmpStr, string1, p-string1); - failat = TclGetCharLength(tmpStr); + failat = Tcl_GetCharLength(tmpStr); TclDecrRefCount(tmpStr); break; } @@ -1896,10 +1693,8 @@ StringIsCmd( } end = string1 + length1; for (; string1 < end; string1 += length2, failat++) { - int ucs4; - - length2 = TclUtfToUniChar(string1, &ucs4); - if (!chcomp(ucs4)) { + length2 = TclUtfToUniChar(string1, &ch); + if (!chcomp(ch)) { result = 0; break; } @@ -1912,11 +1707,10 @@ StringIsCmd( */ str_is_done: - if ((result == 0) && (failVarObj != NULL)) { - TclNewIndexObj(objPtr, failat); - if (Tcl_ObjSetVar2(interp, failVarObj, NULL, objPtr, TCL_LEAVE_ERR_MSG) == NULL) { - return TCL_ERROR; - } + if ((result == 0) && (failVarObj != NULL) && + Tcl_ObjSetVar2(interp, failVarObj, NULL, Tcl_NewIntObj(failat), + TCL_LEAVE_ERR_MSG) == NULL) { + return TCL_ERROR; } Tcl_SetObjResult(interp, Tcl_NewBooleanObj(result)); return TCL_OK; @@ -1933,7 +1727,7 @@ static int UniCharIsHexDigit( int character) { - return (character >= 0) && (character < 0x80) && isxdigit(UCHAR(character)); + return (character >= 0) && (character < 0x80) && isxdigit(character); } /* @@ -1956,16 +1750,16 @@ UniCharIsHexDigit( static int StringMapCmd( - TCL_UNUSED(void *), + ClientData dummy, /* Not used. */ Tcl_Interp *interp, /* Current interpreter. */ int objc, /* Number of arguments. */ Tcl_Obj *const objv[]) /* Argument objects. */ { - Tcl_Size length1, length2, mapElemc, index; + int length1, length2, mapElemc, index; int nocase = 0, mapWithDict = 0, copySource = 0; Tcl_Obj **mapElemv, *sourceObj, *resultPtr; Tcl_UniChar *ustring1, *ustring2, *p, *end; - int (*strCmpFn)(const Tcl_UniChar*, const Tcl_UniChar*, size_t); + int (*strCmpFn)(const Tcl_UniChar*, const Tcl_UniChar*, unsigned long); if (objc < 3 || objc > 4) { Tcl_WrongNumArgs(interp, 1, objv, "?-nocase? charMap string"); @@ -1976,24 +1770,21 @@ StringMapCmd( const char *string = TclGetStringFromObj(objv[1], &length2); if ((length2 > 1) && - strncmp(string, "-nocase", length2) == 0) { + strncmp(string, "-nocase", (size_t) length2) == 0) { nocase = 1; } else { - Tcl_SetObjResult(interp, Tcl_ObjPrintf( - "bad option \"%s\": must be -nocase", string)); - Tcl_SetErrorCode(interp, "TCL", "LOOKUP", "INDEX", "option", - string, (void *)NULL); + Tcl_AppendResult(interp, "bad option \"", string, + "\": must be -nocase", NULL); return TCL_ERROR; } } /* * This test is tricky, but has to be that way or you get other strange - * inconsistencies (see test string-10.20.1 for illustration why!) + * inconsistencies (see test string-10.20 for illustration why!) */ - if (!TclHasStringRep(objv[objc-2]) - && TclHasInternalRep(objv[objc-2], &tclDictType)) { + if (objv[objc-2]->typePtr == &tclDictType && objv[objc-2]->bytes == NULL){ int i, done; Tcl_DictSearch search; @@ -2020,7 +1811,8 @@ StringMapCmd( * adapt this code... */ - mapElemv = (Tcl_Obj **)TclStackAlloc(interp, sizeof(Tcl_Obj *) * mapElemc); + mapElemv = (Tcl_Obj **) + TclStackAlloc(interp, sizeof(Tcl_Obj *) * mapElemc); Tcl_DictObjFirst(interp, objv[objc-2], &search, mapElemv+0, mapElemv+1, &done); for (i=2 ; i<mapElemc ; i+=2) { @@ -2046,8 +1838,6 @@ StringMapCmd( Tcl_SetObjResult(interp, Tcl_NewStringObj("char map list unbalanced", -1)); - Tcl_SetErrorCode(interp, "TCL", "OPERATION", "MAP", - "UNBALANCED", (void *)NULL); return TCL_ERROR; } } @@ -2063,7 +1853,7 @@ StringMapCmd( } else { sourceObj = objv[objc-1]; } - ustring1 = TclGetUnicodeFromObj(sourceObj, &length1); + ustring1 = Tcl_GetUnicodeFromObj(sourceObj, &length1); if (length1 == 0) { /* * Empty input string, just stop now. @@ -2073,13 +1863,13 @@ StringMapCmd( } end = ustring1 + length1; - strCmpFn = nocase ? TclUniCharNcasecmp : TclUniCharNcmp; + strCmpFn = (nocase ? Tcl_UniCharNcasecmp : Tcl_UniCharNcmp); /* * Force result to be Unicode */ - resultPtr = TclNewUnicodeObj(ustring1, 0); + resultPtr = Tcl_NewUnicodeObj(ustring1, 0); if (mapElemc == 2) { /* @@ -2089,11 +1879,10 @@ StringMapCmd( * larger strings. */ - Tcl_Size mapLen; - int u2lc; - Tcl_UniChar *mapString; + int mapLen; + Tcl_UniChar *mapString, u2lc; - ustring2 = TclGetUnicodeFromObj(mapElemv[0], &length2); + ustring2 = Tcl_GetUnicodeFromObj(mapElemv[0], &length2); p = ustring1; if ((length2 > length1) || (length2 == 0)) { /* @@ -2102,7 +1891,7 @@ StringMapCmd( ustring1 = end; } else { - mapString = TclGetUnicodeFromObj(mapElemv[1], &mapLen); + mapString = Tcl_GetUnicodeFromObj(mapElemv[1], &mapLen); u2lc = (nocase ? Tcl_UniCharToLower(*ustring2) : 0); for (; ustring1 < end; ustring1++) { if (((*ustring1 == *ustring2) || @@ -2110,36 +1899,37 @@ StringMapCmd( (length2==1 || strCmpFn(ustring1, ustring2, (unsigned long) length2) == 0)) { if (p != ustring1) { - TclAppendUnicodeToObj(resultPtr, p, ustring1-p); + Tcl_AppendUnicodeToObj(resultPtr, p, ustring1-p); p = ustring1 + length2; } else { p += length2; } ustring1 = p - 1; - TclAppendUnicodeToObj(resultPtr, mapString, mapLen); + Tcl_AppendUnicodeToObj(resultPtr, mapString, mapLen); } } } } else { - Tcl_UniChar **mapStrings; - Tcl_Size *mapLens; - int *u2lc = NULL; + Tcl_UniChar **mapStrings, *u2lc = NULL; + int *mapLens; /* - * Precompute pointers to the Unicode string and length. This saves us + * Precompute pointers to the unicode string and length. This saves us * repeated function calls later, significantly speeding up the * algorithm. We only need the lowercase first char in the nocase * case. */ - mapStrings = (Tcl_UniChar **)TclStackAlloc(interp, mapElemc*sizeof(Tcl_UniChar *)*2); - mapLens = (Tcl_Size *)TclStackAlloc(interp, mapElemc * sizeof(Tcl_Size) * 2); + mapStrings = (Tcl_UniChar **) TclStackAlloc(interp, + mapElemc * 2 * sizeof(Tcl_UniChar *)); + mapLens = (int *) TclStackAlloc(interp, mapElemc * 2 * sizeof(int)); if (nocase) { - u2lc = (int *)TclStackAlloc(interp, mapElemc * sizeof(int)); + u2lc = (Tcl_UniChar *) TclStackAlloc(interp, + mapElemc * sizeof(Tcl_UniChar)); } for (index = 0; index < mapElemc; index++) { - mapStrings[index] = TclGetUnicodeFromObj(mapElemv[index], + mapStrings[index] = Tcl_GetUnicodeFromObj(mapElemv[index], mapLens+index); if (nocase && ((index % 2) == 0)) { u2lc[index/2] = Tcl_UniCharToLower(*mapStrings[index]); @@ -2157,13 +1947,13 @@ StringMapCmd( (Tcl_UniCharToLower(*ustring1) == u2lc[index/2]))) && /* Restrict max compare length. */ (end-ustring1 >= length2) && ((length2 == 1) || - !strCmpFn(ustring2, ustring1, length2))) { + !strCmpFn(ustring2, ustring1, (unsigned) length2))) { if (p != ustring1) { /* * Put the skipped chars onto the result first. */ - TclAppendUnicodeToObj(resultPtr, p, ustring1-p); + Tcl_AppendUnicodeToObj(resultPtr, p, ustring1-p); p = ustring1 + length2; } else { p += length2; @@ -2176,10 +1966,10 @@ StringMapCmd( ustring1 = p - 1; /* - * Append the map value to the Unicode string. + * Append the map value to the unicode string. */ - TclAppendUnicodeToObj(resultPtr, + Tcl_AppendUnicodeToObj(resultPtr, mapStrings[index+1], mapLens[index+1]); break; } @@ -2196,7 +1986,7 @@ StringMapCmd( * Put the rest of the unmapped chars onto result. */ - TclAppendUnicodeToObj(resultPtr, p, ustring1 - p); + Tcl_AppendUnicodeToObj(resultPtr, p, ustring1 - p); } Tcl_SetObjResult(interp, resultPtr); done: @@ -2229,7 +2019,7 @@ StringMapCmd( static int StringMatchCmd( - TCL_UNUSED(void *), + ClientData dummy, /* Not used. */ Tcl_Interp *interp, /* Current interpreter. */ int objc, /* Number of arguments. */ Tcl_Obj *const objv[]) /* Argument objects. */ @@ -2242,17 +2032,15 @@ StringMatchCmd( } if (objc == 4) { - Tcl_Size length; + int length; const char *string = TclGetStringFromObj(objv[1], &length); if ((length > 1) && - strncmp(string, "-nocase", length) == 0) { + strncmp(string, "-nocase", (size_t) length) == 0) { nocase = TCL_MATCH_NOCASE; } else { - Tcl_SetObjResult(interp, Tcl_ObjPrintf( - "bad option \"%s\": must be -nocase", string)); - Tcl_SetErrorCode(interp, "TCL", "LOOKUP", "INDEX", "option", - string, (void *)NULL); + Tcl_AppendResult(interp, "bad option \"", string, + "\": must be -nocase", NULL); return TCL_ERROR; } } @@ -2281,12 +2069,13 @@ StringMatchCmd( static int StringRangeCmd( - TCL_UNUSED(void *), + ClientData dummy, /* Not used. */ Tcl_Interp *interp, /* Current interpreter. */ int objc, /* Number of arguments. */ Tcl_Obj *const objv[]) /* Argument objects. */ { - Tcl_Size first, last, end; + const unsigned char *string; + int length, first, last; if (objc != 4) { Tcl_WrongNumArgs(interp, 1, objv, "string first last"); @@ -2294,19 +2083,46 @@ StringRangeCmd( } /* - * Get the length in actual characters; Then reduce it by one because - * 'end' refers to the last character, not one past it. + * If we have a ByteArray object, avoid indexing in the Utf string since + * the byte array contains one byte per character. Otherwise, use the + * Unicode string rep to get the range. */ - end = TclGetCharLength(objv[1]) - 1; + if (TclIsPureByteArray(objv[1])) { + string = Tcl_GetByteArrayFromObj(objv[1], &length); + length--; + } else { + /* + * Get the length in actual characters. + */ - if (TclGetIntForIndexM(interp, objv[2], end, &first) != TCL_OK || - TclGetIntForIndexM(interp, objv[3], end, &last) != TCL_OK) { + string = NULL; + length = Tcl_GetCharLength(objv[1]) - 1; + } + + if (TclGetIntForIndexM(interp, objv[2], length, &first) != TCL_OK || + TclGetIntForIndexM(interp, objv[3], length, &last) != TCL_OK) { return TCL_ERROR; } - if (last >= 0) { - Tcl_SetObjResult(interp, TclGetRange(objv[1], first, last)); + if (first < 0) { + first = 0; + } + if (last >= length) { + last = length; + } + if (last >= first) { + if (string != NULL) { + /* + * Reread the string to prevent shimmering nasties. + */ + + string = Tcl_GetByteArrayFromObj(objv[1], &length); + Tcl_SetObjResult(interp, + Tcl_NewByteArrayObj(string+first, last - first + 1)); + } else { + Tcl_SetObjResult(interp, Tcl_GetRange(objv[1], first, last)); + } } return TCL_OK; } @@ -2331,12 +2147,14 @@ StringRangeCmd( static int StringReptCmd( - TCL_UNUSED(void *), + ClientData dummy, /* Not used. */ Tcl_Interp *interp, /* Current interpreter. */ int objc, /* Number of arguments. */ Tcl_Obj *const objv[]) /* Argument objects. */ { - int count; + const char *string1; + char *string2; + int count, index, length1, length2; Tcl_Obj *resultPtr; if (objc != 3) { @@ -2354,17 +2172,68 @@ StringReptCmd( if (count == 1) { Tcl_SetObjResult(interp, objv[1]); - return TCL_OK; + goto done; } else if (count < 1) { - return TCL_OK; + goto done; + } + string1 = TclGetStringFromObj(objv[1], &length1); + if (length1 <= 0) { + goto done; } - resultPtr = TclStringRepeat(interp, objv[1], count, TCL_STRING_IN_PLACE); - if (resultPtr) { - Tcl_SetObjResult(interp, resultPtr); - return TCL_OK; + /* + * Only build up a string that has data. Instead of building it up with + * repeated appends, we just allocate the necessary space once and copy + * the string value in. + * + * We have to worry about overflow [Bugs 714106, 2561746]. + * At this point we know 1 <= length1 <= INT_MAX and 2 <= count <= INT_MAX. + * We need to keep 2 <= length2 <= INT_MAX. + */ + + if (count > (INT_MAX / length1)) { + Tcl_SetObjResult(interp, Tcl_ObjPrintf( + "result exceeds max size for a Tcl value (%d bytes)", INT_MAX)); + return TCL_ERROR; + } + length2 = length1 * count; + + /* + * Include space for the NUL. + */ + + string2 = attemptckalloc((unsigned) length2 + 1); + if (string2 == NULL) { + /* + * Alloc failed. Note that in this case we try to do an error message + * since this is a case that's most likely when the alloc is large and + * that's easy to do with this API. Note that if we fail allocating a + * short string, this will likely keel over too (and fatally). + */ + + Tcl_SetObjResult(interp, Tcl_ObjPrintf( + "string size overflow, out of memory allocating %u bytes", + length2 + 1)); + return TCL_ERROR; } - return TCL_ERROR; + for (index = 0; index < count; index++) { + memcpy(string2 + (length1 * index), string1, (size_t) length1); + } + string2[length2] = '\0'; + + /* + * We have to directly assign this instead of using Tcl_SetStringObj (and + * indirectly TclInitStringRep) because that makes another copy of the + * data. + */ + + TclNewObj(resultPtr); + resultPtr->bytes = string2; + resultPtr->length = length2; + Tcl_SetObjResult(interp, resultPtr); + + done: + return TCL_OK; } /* @@ -2387,57 +2256,46 @@ StringReptCmd( static int StringRplcCmd( - TCL_UNUSED(void *), + ClientData dummy, /* Not used. */ Tcl_Interp *interp, /* Current interpreter. */ int objc, /* Number of arguments. */ Tcl_Obj *const objv[]) /* Argument objects. */ { - Tcl_Size first, last, end; + Tcl_UniChar *ustring; + int first, last, length; if (objc < 4 || objc > 5) { Tcl_WrongNumArgs(interp, 1, objv, "string first last ?string?"); return TCL_ERROR; } - end = TclGetCharLength(objv[1]) - 1; + ustring = Tcl_GetUnicodeFromObj(objv[1], &length); + length--; - if (TclGetIntForIndexM(interp, objv[2], end, &first) != TCL_OK || - TclGetIntForIndexM(interp, objv[3], end, &last) != TCL_OK) { + if (TclGetIntForIndexM(interp, objv[2], length, &first) != TCL_OK || + TclGetIntForIndexM(interp, objv[3], length, &last) != TCL_OK){ return TCL_ERROR; } - /* - * The following test screens out most empty substrings as candidates for - * replacement. When they are detected, no replacement is done, and the - * result is the original string. - */ - - if ((last < 0) || /* Range ends before start of string */ - (first > end) || /* Range begins after end of string */ - (last < first)) { /* Range begins after it starts */ - /* - * BUT!!! when (end < 0) -- an empty original string -- we can - * have (first <= end < 0 <= last) and an empty string is permitted - * to be replaced. - */ - + if ((last < first) || (last < 0) || (first > length)) { Tcl_SetObjResult(interp, objv[1]); } else { Tcl_Obj *resultPtr; + ustring = Tcl_GetUnicodeFromObj(objv[1], &length); + length--; + if (first < 0) { first = 0; } - if (last > end) { - last = end; - } - - resultPtr = TclStringReplace(interp, objv[1], first, - last + 1 - first, (objc == 5) ? objv[4] : NULL, - TCL_STRING_IN_PLACE); - if (resultPtr == NULL) { - return TCL_ERROR; + resultPtr = Tcl_NewUnicodeObj(ustring, first); + if (objc == 5) { + Tcl_AppendObjToObj(resultPtr, objv[4]); + } + if (last < length) { + Tcl_AppendUnicodeToObj(resultPtr, ustring + last + 1, + length - last); } Tcl_SetObjResult(interp, resultPtr); } @@ -2464,7 +2322,7 @@ StringRplcCmd( static int StringRevCmd( - TCL_UNUSED(void *), + ClientData dummy, /* Not used. */ Tcl_Interp *interp, /* Current interpreter. */ int objc, /* Number of arguments. */ Tcl_Obj *const objv[]) /* Argument objects. */ @@ -2474,7 +2332,7 @@ StringRevCmd( return TCL_ERROR; } - Tcl_SetObjResult(interp, TclStringReverse(objv[1], TCL_STRING_IN_PLACE)); + Tcl_SetObjResult(interp, TclStringObjReverse(objv[1])); return TCL_OK; } @@ -2484,7 +2342,9 @@ StringRevCmd( * StringStartCmd -- * * This procedure is invoked to process the "string wordstart" Tcl - * command. See the user documentation for details on what it does. + * command. See the user documentation for details on what it does. Note + * that this command only functions correctly on properly formed Tcl UTF + * strings. * * Results: * A standard Tcl result. @@ -2497,55 +2357,44 @@ StringRevCmd( static int StringStartCmd( - TCL_UNUSED(void *), + ClientData dummy, /* Not used. */ Tcl_Interp *interp, /* Current interpreter. */ int objc, /* Number of arguments. */ Tcl_Obj *const objv[]) /* Argument objects. */ { - int ch; - const Tcl_UniChar *p, *string; - Tcl_Size cur, index, length; - Tcl_Obj *obj; + Tcl_UniChar ch; + const char *p, *string; + int cur, index, length, numChars; if (objc != 3) { Tcl_WrongNumArgs(interp, 1, objv, "string index"); return TCL_ERROR; } - string = TclGetUnicodeFromObj(objv[1], &length); - if (TclGetIntForIndexM(interp, objv[2], length-1, &index) != TCL_OK) { + string = TclGetStringFromObj(objv[1], &length); + numChars = Tcl_NumUtfChars(string, length); + if (TclGetIntForIndexM(interp, objv[2], numChars-1, &index) != TCL_OK) { return TCL_ERROR; } - if (index >= length) { - index = length - 1; + string = TclGetStringFromObj(objv[1], &length); + if (index >= numChars) { + index = numChars - 1; } cur = 0; if (index > 0) { - p = &string[index]; - - ch = *p; - for (cur = index; cur != TCL_INDEX_NONE; cur--) { - int delta = 0; - const Tcl_UniChar *next; - + p = Tcl_UtfAtIndex(string, index); + for (cur = index; cur >= 0; cur--) { + TclUtfToUniChar(p, &ch); if (!Tcl_UniCharIsWordChar(ch)) { break; } - - next = (p > string) ? p - 1 : p; - do { - next += delta; - ch = *next; - delta = 1; - } while (next + delta < p); - p = next; + p = Tcl_UtfPrev(p, string); } if (cur != index) { cur += 1; } } - TclNewIndexObj(obj, cur); - Tcl_SetObjResult(interp, obj); + Tcl_SetObjResult(interp, Tcl_NewIntObj(cur)); return TCL_OK; } @@ -2555,7 +2404,8 @@ StringStartCmd( * StringEndCmd -- * * This procedure is invoked to process the "string wordend" Tcl command. - * See the user documentation for details on what it does. + * See the user documentation for details on what it does. Note that this + * command only functions correctly on properly formed Tcl UTF strings. * * Results: * A standard Tcl result. @@ -2568,33 +2418,34 @@ StringStartCmd( static int StringEndCmd( - TCL_UNUSED(void *), + ClientData dummy, /* Not used. */ Tcl_Interp *interp, /* Current interpreter. */ int objc, /* Number of arguments. */ Tcl_Obj *const objv[]) /* Argument objects. */ { - int ch; - const Tcl_UniChar *p, *end, *string; - Tcl_Size cur, index, length; - Tcl_Obj *obj; + Tcl_UniChar ch; + const char *p, *end, *string; + int cur, index, length, numChars; if (objc != 3) { Tcl_WrongNumArgs(interp, 1, objv, "string index"); return TCL_ERROR; } - string = TclGetUnicodeFromObj(objv[1], &length); - if (TclGetIntForIndexM(interp, objv[2], length-1, &index) != TCL_OK) { + string = TclGetStringFromObj(objv[1], &length); + numChars = Tcl_NumUtfChars(string, length); + if (TclGetIntForIndexM(interp, objv[2], numChars-1, &index) != TCL_OK) { return TCL_ERROR; } + string = TclGetStringFromObj(objv[1], &length); if (index < 0) { index = 0; } - if (index < length) { - p = &string[index]; + if (index < numChars) { + p = Tcl_UtfAtIndex(string, index); end = string+length; for (cur = index; p < end; cur++) { - ch = *p++; + p += TclUtfToUniChar(p, &ch); if (!Tcl_UniCharIsWordChar(ch)) { break; } @@ -2603,10 +2454,9 @@ StringEndCmd( cur++; } } else { - cur = length; + cur = numChars; } - TclNewIndexObj(obj, cur); - Tcl_SetObjResult(interp, obj); + Tcl_SetObjResult(interp, Tcl_NewIntObj(cur)); return TCL_OK; } @@ -2630,7 +2480,7 @@ StringEndCmd( static int StringEqualCmd( - TCL_UNUSED(void *), + ClientData dummy, /* Not used. */ Tcl_Interp *interp, /* Current interpreter. */ int objc, /* Number of arguments. */ Tcl_Obj *const objv[]) /* Argument objects. */ @@ -2641,10 +2491,10 @@ StringEqualCmd( * the expr string comparison in INST_EQ/INST_NEQ/INST_LT/...). */ - const char *string2; - int i, match, nocase = 0; - Tcl_Size length; - Tcl_WideInt reqlength = -1; + char *string1, *string2; + int length1, length2, i, match, length, nocase = 0, reqlength = -1; + typedef int (*strCmpFn_t)(const char *, const char *, unsigned int); + strCmpFn_t strCmpFn; if (objc < 3 || objc > 6) { str_cmp_args: @@ -2654,27 +2504,21 @@ StringEqualCmd( } for (i = 1; i < objc-2; i++) { - string2 = TclGetStringFromObj(objv[i], &length); - if ((length > 1) && !strncmp(string2, "-nocase", length)) { + string2 = TclGetStringFromObj(objv[i], &length2); + if ((length2 > 1) && !strncmp(string2, "-nocase", (size_t)length2)) { nocase = 1; - } else if ((length > 1) - && !strncmp(string2, "-length", length)) { + } else if ((length2 > 1) + && !strncmp(string2, "-length", (size_t)length2)) { if (i+1 >= objc-2) { goto str_cmp_args; } - i++; - if (Tcl_GetWideIntFromObj(interp, objv[i], &reqlength) != TCL_OK) { + ++i; + if (TclGetIntFromObj(interp, objv[i], &reqlength) != TCL_OK) { return TCL_ERROR; } - if ((Tcl_WideUInt)reqlength > TCL_SIZE_MAX) { - reqlength = -1; - } } else { - Tcl_SetObjResult(interp, Tcl_ObjPrintf( - "bad option \"%s\": must be -nocase or -length", - string2)); - Tcl_SetErrorCode(interp, "TCL", "LOOKUP", "INDEX", "option", - string2, (void *)NULL); + Tcl_AppendResult(interp, "bad option \"", string2, + "\": must be -nocase or -length", NULL); return TCL_ERROR; } } @@ -2685,7 +2529,80 @@ StringEqualCmd( */ objv += objc-2; - match = TclStringCmp(objv[0], objv[1], 1, nocase, reqlength); + + if ((reqlength == 0) || (objv[0] == objv[1])) { + /* + * Always match at 0 chars of if it is the same obj. + */ + + Tcl_SetObjResult(interp, Tcl_NewBooleanObj(1)); + return TCL_OK; + } + + if (!nocase && TclIsPureByteArray(objv[0]) && + TclIsPureByteArray(objv[1])) { + /* + * Use binary versions of comparisons since that won't cause undue + * type conversions and it is much faster. Only do this if we're + * case-sensitive (which is all that really makes sense with byte + * arrays anyway, and we have no memcasecmp() for some reason... :^) + */ + + string1 = (char *) Tcl_GetByteArrayFromObj(objv[0], &length1); + string2 = (char *) Tcl_GetByteArrayFromObj(objv[1], &length2); + strCmpFn = (strCmpFn_t) memcmp; + } else if ((objv[0]->typePtr == &tclStringType) + && (objv[1]->typePtr == &tclStringType)) { + /* + * Do a unicode-specific comparison if both of the args are of String + * type. In benchmark testing this proved the most efficient check + * between the unicode and string comparison operations. + */ + + string1 = (char *) Tcl_GetUnicodeFromObj(objv[0], &length1); + string2 = (char *) Tcl_GetUnicodeFromObj(objv[1], &length2); + strCmpFn = (strCmpFn_t) + (nocase ? Tcl_UniCharNcasecmp : Tcl_UniCharNcmp); + } else { + /* + * As a catch-all we will work with UTF-8. We cannot use memcmp() as + * that is unsafe with any string containing NUL (\xC0\x80 in Tcl's + * utf rep). We can use the more efficient TclpUtfNcmp2 if we are + * case-sensitive and no specific length was requested. + */ + + string1 = (char *) TclGetStringFromObj(objv[0], &length1); + string2 = (char *) TclGetStringFromObj(objv[1], &length2); + if ((reqlength < 0) && !nocase) { + strCmpFn = (strCmpFn_t) TclpUtfNcmp2; + } else { + length1 = Tcl_NumUtfChars(string1, length1); + length2 = Tcl_NumUtfChars(string2, length2); + strCmpFn = (strCmpFn_t) (nocase ? Tcl_UtfNcasecmp : Tcl_UtfNcmp); + } + } + + if ((reqlength < 0) && (length1 != length2)) { + match = 1; /* This will be reversed below. */ + } else { + length = (length1 < length2) ? length1 : length2; + if (reqlength > 0 && reqlength < length) { + length = reqlength; + } else if (reqlength < 0) { + /* + * The requested length is negative, so we ignore it by setting it + * to length + 1 so we correct the match var. + */ + + reqlength = length + 1; + } + + match = strCmpFn(string1, string2, (unsigned) length); + if ((match == 0) && (reqlength > length)) { + match = length1 - length2; + } + } + Tcl_SetObjResult(interp, Tcl_NewBooleanObj(match ? 0 : 1)); return TCL_OK; } @@ -2710,7 +2627,7 @@ StringEqualCmd( static int StringCmpCmd( - TCL_UNUSED(void *), + ClientData dummy, /* Not used. */ Tcl_Interp *interp, /* Current interpreter. */ int objc, /* Number of arguments. */ Tcl_Obj *const objv[]) /* Argument objects. */ @@ -2721,34 +2638,11 @@ StringCmpCmd( * the expr string comparison in INST_EQ/INST_NEQ/INST_LT/...). */ - int match, nocase, status; - Tcl_Size reqlength = -1; - - status = StringCmpOpts(interp, objc, objv, &nocase, &reqlength); - if (status != TCL_OK) { - return status; - } + char *string1, *string2; + int length1, length2, i, match, length, nocase = 0, reqlength = -1; + typedef int (*strCmpFn_t)(const char *, const char *, unsigned int); + strCmpFn_t strCmpFn; - objv += objc-2; - match = TclStringCmp(objv[0], objv[1], 0, nocase, reqlength); - Tcl_SetObjResult(interp, Tcl_NewWideIntObj(match)); - return TCL_OK; -} - -int -StringCmpOpts( - Tcl_Interp *interp, /* Current interpreter. */ - int objc, /* Number of arguments. */ - Tcl_Obj *const objv[], /* Argument objects. */ - int *nocase, - Tcl_Size *reqlength) -{ - int i; - Tcl_Size length; - const char *string; - Tcl_WideInt wreqlength = -1; - - *nocase = 0; if (objc < 3 || objc > 6) { str_cmp_args: Tcl_WrongNumArgs(interp, 1, objv, @@ -2757,77 +2651,104 @@ StringCmpOpts( } for (i = 1; i < objc-2; i++) { - string = TclGetStringFromObj(objv[i], &length); - if ((length > 1) && !strncmp(string, "-nocase", length)) { - *nocase = 1; - } else if ((length > 1) - && !strncmp(string, "-length", length)) { + string2 = TclGetStringFromObj(objv[i], &length2); + if ((length2 > 1) && !strncmp(string2, "-nocase", (size_t)length2)) { + nocase = 1; + } else if ((length2 > 1) + && !strncmp(string2, "-length", (size_t)length2)) { if (i+1 >= objc-2) { goto str_cmp_args; } - i++; - if (Tcl_GetWideIntFromObj(interp, objv[i], &wreqlength) != TCL_OK) { + ++i; + if (TclGetIntFromObj(interp, objv[i], &reqlength) != TCL_OK) { return TCL_ERROR; } - if ((Tcl_WideUInt)wreqlength > TCL_SIZE_MAX) { - *reqlength = -1; - } else { - *reqlength = wreqlength; - } } else { - Tcl_SetObjResult(interp, Tcl_ObjPrintf( - "bad option \"%s\": must be -nocase or -length", - string)); - Tcl_SetErrorCode(interp, "TCL", "LOOKUP", "INDEX", "option", - string, (void *)NULL); + Tcl_AppendResult(interp, "bad option \"", string2, + "\": must be -nocase or -length", NULL); return TCL_ERROR; } } - return TCL_OK; -} - -/* - *---------------------------------------------------------------------- - * - * StringCatCmd -- - * - * This procedure is invoked to process the "string cat" Tcl command. - * See the user documentation for details on what it does. - * - * Results: - * A standard Tcl result. - * - * Side effects: - * See the user documentation. - * - *---------------------------------------------------------------------- - */ -static int -StringCatCmd( - TCL_UNUSED(void *), - Tcl_Interp *interp, /* Current interpreter. */ - int objc, /* Number of arguments. */ - Tcl_Obj *const objv[]) /* Argument objects. */ -{ - Tcl_Obj *objResultPtr; + /* + * From now on, we only access the two objects at the end of the argument + * array. + */ + + objv += objc-2; - if (objc < 2) { + if ((reqlength == 0) || (objv[0] == objv[1])) { /* - * If there are no args, the result is an empty object. - * Just leave the preset empty interp result. + * Always match at 0 chars of if it is the same obj. */ + + Tcl_SetObjResult(interp, Tcl_NewBooleanObj(0)); return TCL_OK; } - objResultPtr = TclStringCat(interp, objc-1, objv+1, TCL_STRING_IN_PLACE); + if (!nocase && TclIsPureByteArray(objv[0]) && + TclIsPureByteArray(objv[1])) { + /* + * Use binary versions of comparisons since that won't cause undue + * type conversions and it is much faster. Only do this if we're + * case-sensitive (which is all that really makes sense with byte + * arrays anyway, and we have no memcasecmp() for some reason... :^) + */ - if (objResultPtr) { - Tcl_SetObjResult(interp, objResultPtr); - return TCL_OK; + string1 = (char *) Tcl_GetByteArrayFromObj(objv[0], &length1); + string2 = (char *) Tcl_GetByteArrayFromObj(objv[1], &length2); + strCmpFn = (strCmpFn_t) memcmp; + } else if ((objv[0]->typePtr == &tclStringType) + && (objv[1]->typePtr == &tclStringType)) { + /* + * Do a unicode-specific comparison if both of the args are of String + * type. In benchmark testing this proved the most efficient check + * between the unicode and string comparison operations. + */ + + string1 = (char *) Tcl_GetUnicodeFromObj(objv[0], &length1); + string2 = (char *) Tcl_GetUnicodeFromObj(objv[1], &length2); + strCmpFn = (strCmpFn_t) + (nocase ? Tcl_UniCharNcasecmp : Tcl_UniCharNcmp); + } else { + /* + * As a catch-all we will work with UTF-8. We cannot use memcmp() as + * that is unsafe with any string containing NUL (\xC0\x80 in Tcl's + * utf rep). We can use the more efficient TclpUtfNcmp2 if we are + * case-sensitive and no specific length was requested. + */ + + string1 = (char *) TclGetStringFromObj(objv[0], &length1); + string2 = (char *) TclGetStringFromObj(objv[1], &length2); + if ((reqlength < 0) && !nocase) { + strCmpFn = (strCmpFn_t) TclpUtfNcmp2; + } else { + length1 = Tcl_NumUtfChars(string1, length1); + length2 = Tcl_NumUtfChars(string2, length2); + strCmpFn = (strCmpFn_t) (nocase ? Tcl_UtfNcasecmp : Tcl_UtfNcmp); + } } - return TCL_ERROR; + length = (length1 < length2) ? length1 : length2; + if (reqlength > 0 && reqlength < length) { + length = reqlength; + } else if (reqlength < 0) { + /* + * The requested length is negative, so we ignore it by setting it to + * length + 1 so we correct the match var. + */ + + reqlength = length + 1; + } + + match = strCmpFn(string1, string2, (unsigned) length); + if ((match == 0) && (reqlength > length)) { + match = length1 - length2; + } + + Tcl_SetObjResult(interp, + Tcl_NewIntObj((match > 0) ? 1 : (match < 0) ? -1 : 0)); + return TCL_OK; } /* @@ -2848,10 +2769,10 @@ StringCatCmd( * *---------------------------------------------------------------------- */ -#if TCL_MAJOR_VERSION < 9 && !defined(TCL_NO_DEPRECATED) + static int StringBytesCmd( - TCL_UNUSED(ClientData), + ClientData dummy, /* Not used. */ Tcl_Interp *interp, /* Current interpreter. */ int objc, /* Number of arguments. */ Tcl_Obj *const objv[]) /* Argument objects. */ @@ -2864,10 +2785,9 @@ StringBytesCmd( } (void) TclGetStringFromObj(objv[1], &length); - Tcl_SetObjResult(interp, Tcl_NewWideIntObj(length)); + Tcl_SetObjResult(interp, Tcl_NewIntObj(length)); return TCL_OK; } -#endif /* *---------------------------------------------------------------------- @@ -2889,17 +2809,30 @@ StringBytesCmd( static int StringLenCmd( - TCL_UNUSED(void *), + ClientData dummy, /* Not used. */ Tcl_Interp *interp, /* Current interpreter. */ int objc, /* Number of arguments. */ Tcl_Obj *const objv[]) /* Argument objects. */ { + int length; + if (objc != 2) { Tcl_WrongNumArgs(interp, 1, objv, "string"); return TCL_ERROR; } - Tcl_SetObjResult(interp, Tcl_NewWideIntObj(TclGetCharLength(objv[1]))); + /* + * If we have a ByteArray object, avoid recomputing the string since the + * byte array contains one byte per character. Otherwise, use the Unicode + * string rep to calculate the length. + */ + + if (objv[1]->typePtr == &tclByteArrayType) { + (void) Tcl_GetByteArrayFromObj(objv[1], &length); + } else { + length = Tcl_GetCharLength(objv[1]); + } + Tcl_SetObjResult(interp, Tcl_NewIntObj(length)); return TCL_OK; } @@ -2923,14 +2856,13 @@ StringLenCmd( static int StringLowerCmd( - TCL_UNUSED(void *), + ClientData dummy, /* Not used. */ Tcl_Interp *interp, /* Current interpreter. */ int objc, /* Number of arguments. */ Tcl_Obj *const objv[]) /* Argument objects. */ { - Tcl_Size length1, length2; - const char *string1; - char *string2; + int length1, length2; + char *string1, *string2; if (objc < 2 || objc > 4) { Tcl_WrongNumArgs(interp, 1, objv, "string ?first? ?last?"); @@ -2946,11 +2878,11 @@ StringLowerCmd( Tcl_SetObjLength(resultPtr, length1); Tcl_SetObjResult(interp, resultPtr); } else { - Tcl_Size first, last; + int first, last; const char *start, *end; Tcl_Obj *resultPtr; - length1 = TclNumUtfChars(string1, length1) - 1; + length1 = Tcl_NumUtfChars(string1, length1) - 1; if (TclGetIntForIndexM(interp,objv[2],length1, &first) != TCL_OK) { return TCL_ERROR; } @@ -2973,8 +2905,8 @@ StringLowerCmd( } string1 = TclGetStringFromObj(objv[1], &length1); - start = TclUtfAtIndex(string1, first); - end = TclUtfAtIndex(start, last - first + 1); + start = Tcl_UtfAtIndex(string1, first); + end = Tcl_UtfAtIndex(start, last - first + 1); resultPtr = Tcl_NewStringObj(string1, end - string1); string2 = TclGetString(resultPtr) + (start - string1); @@ -3008,14 +2940,13 @@ StringLowerCmd( static int StringUpperCmd( - TCL_UNUSED(void *), + ClientData dummy, /* Not used. */ Tcl_Interp *interp, /* Current interpreter. */ int objc, /* Number of arguments. */ Tcl_Obj *const objv[]) /* Argument objects. */ { - Tcl_Size length1, length2; - const char *string1; - char *string2; + int length1, length2; + char *string1, *string2; if (objc < 2 || objc > 4) { Tcl_WrongNumArgs(interp, 1, objv, "string ?first? ?last?"); @@ -3031,11 +2962,11 @@ StringUpperCmd( Tcl_SetObjLength(resultPtr, length1); Tcl_SetObjResult(interp, resultPtr); } else { - Tcl_Size first, last; + int first, last; const char *start, *end; Tcl_Obj *resultPtr; - length1 = TclNumUtfChars(string1, length1) - 1; + length1 = Tcl_NumUtfChars(string1, length1) - 1; if (TclGetIntForIndexM(interp,objv[2],length1, &first) != TCL_OK) { return TCL_ERROR; } @@ -3058,8 +2989,8 @@ StringUpperCmd( } string1 = TclGetStringFromObj(objv[1], &length1); - start = TclUtfAtIndex(string1, first); - end = TclUtfAtIndex(start, last - first + 1); + start = Tcl_UtfAtIndex(string1, first); + end = Tcl_UtfAtIndex(start, last - first + 1); resultPtr = Tcl_NewStringObj(string1, end - string1); string2 = TclGetString(resultPtr) + (start - string1); @@ -3093,14 +3024,13 @@ StringUpperCmd( static int StringTitleCmd( - TCL_UNUSED(void *), + ClientData dummy, /* Not used. */ Tcl_Interp *interp, /* Current interpreter. */ int objc, /* Number of arguments. */ Tcl_Obj *const objv[]) /* Argument objects. */ { - Tcl_Size length1, length2; - const char *string1; - char *string2; + int length1, length2; + char *string1, *string2; if (objc < 2 || objc > 4) { Tcl_WrongNumArgs(interp, 1, objv, "string ?first? ?last?"); @@ -3116,11 +3046,11 @@ StringTitleCmd( Tcl_SetObjLength(resultPtr, length1); Tcl_SetObjResult(interp, resultPtr); } else { - Tcl_Size first, last; + int first, last; const char *start, *end; Tcl_Obj *resultPtr; - length1 = TclNumUtfChars(string1, length1) - 1; + length1 = Tcl_NumUtfChars(string1, length1) - 1; if (TclGetIntForIndexM(interp,objv[2],length1, &first) != TCL_OK) { return TCL_ERROR; } @@ -3143,8 +3073,8 @@ StringTitleCmd( } string1 = TclGetStringFromObj(objv[1], &length1); - start = TclUtfAtIndex(string1, first); - end = TclUtfAtIndex(start, last - first + 1); + start = Tcl_UtfAtIndex(string1, first); + end = Tcl_UtfAtIndex(start, last - first + 1); resultPtr = Tcl_NewStringObj(string1, end - string1); string2 = TclGetString(resultPtr) + (start - string1); @@ -3178,7 +3108,7 @@ StringTitleCmd( static int StringTrimCmd( - TCL_UNUSED(void *), + ClientData dummy, /* Not used. */ Tcl_Interp *interp, /* Current interpreter. */ int objc, /* Number of arguments. */ Tcl_Obj *const objv[]) /* Argument objects. */ @@ -3189,15 +3119,16 @@ StringTrimCmd( if (objc == 3) { string2 = TclGetStringFromObj(objv[2], &length2); } else if (objc == 2) { - string2 = tclDefaultTrimSet; - length2 = strlen(tclDefaultTrimSet); + string2 = " \t\n\r"; + length2 = strlen(string2); } else { Tcl_WrongNumArgs(interp, 1, objv, "string ?chars?"); return TCL_ERROR; } string1 = TclGetStringFromObj(objv[1], &length1); - triml = TclTrim(string1, length1, string2, length2, &trimr); + triml = TclTrimLeft(string1, length1, string2, length2); + trimr = TclTrimRight(string1 + triml, length1 - triml, string2, length2); Tcl_SetObjResult(interp, Tcl_NewStringObj(string1 + triml, length1 - triml - trimr)); @@ -3225,20 +3156,19 @@ StringTrimCmd( static int StringTrimLCmd( - TCL_UNUSED(void *), + ClientData dummy, /* Not used. */ Tcl_Interp *interp, /* Current interpreter. */ int objc, /* Number of arguments. */ Tcl_Obj *const objv[]) /* Argument objects. */ { const char *string1, *string2; - int trim; - Tcl_Size length1, length2; + int trim, length1, length2; if (objc == 3) { string2 = TclGetStringFromObj(objv[2], &length2); } else if (objc == 2) { - string2 = tclDefaultTrimSet; - length2 = strlen(tclDefaultTrimSet); + string2 = " \t\n\r"; + length2 = strlen(string2); } else { Tcl_WrongNumArgs(interp, 1, objv, "string ?chars?"); return TCL_ERROR; @@ -3272,20 +3202,19 @@ StringTrimLCmd( static int StringTrimRCmd( - TCL_UNUSED(void *), + ClientData dummy, /* Not used. */ Tcl_Interp *interp, /* Current interpreter. */ int objc, /* Number of arguments. */ Tcl_Obj *const objv[]) /* Argument objects. */ { const char *string1, *string2; - int trim; - Tcl_Size length1, length2; + int trim, length1, length2; if (objc == 3) { string2 = TclGetStringFromObj(objv[2], &length2); } else if (objc == 2) { - string2 = tclDefaultTrimSet; - length2 = strlen(tclDefaultTrimSet); + string2 = " \t\n\r"; + length2 = strlen(string2); } else { Tcl_WrongNumArgs(interp, 1, objv, "string ?chars?"); return TCL_ERROR; @@ -3326,33 +3255,29 @@ TclInitStringCmd( Tcl_Interp *interp) /* Current interpreter. */ { static const EnsembleImplMap stringImplMap[] = { -#if TCL_MAJOR_VERSION < 9 && !defined(TCL_NO_DEPRECATED) - {"bytelength", StringBytesCmd, TclCompileBasic1ArgCmd, NULL, NULL, 0}, -#endif - {"cat", StringCatCmd, TclCompileStringCatCmd, NULL, NULL, 0}, - {"compare", StringCmpCmd, TclCompileStringCmpCmd, NULL, NULL, 0}, - {"equal", StringEqualCmd, TclCompileStringEqualCmd, NULL, NULL, 0}, - {"first", StringFirstCmd, TclCompileStringFirstCmd, NULL, NULL, 0}, - {"index", StringIndexCmd, TclCompileStringIndexCmd, NULL, NULL, 0}, - {"insert", StringInsertCmd, TclCompileStringInsertCmd, NULL, NULL, 0}, - {"is", StringIsCmd, TclCompileStringIsCmd, NULL, NULL, 0}, - {"last", StringLastCmd, TclCompileStringLastCmd, NULL, NULL, 0}, - {"length", StringLenCmd, TclCompileStringLenCmd, NULL, NULL, 0}, - {"map", StringMapCmd, TclCompileStringMapCmd, NULL, NULL, 0}, - {"match", StringMatchCmd, TclCompileStringMatchCmd, NULL, NULL, 0}, - {"range", StringRangeCmd, TclCompileStringRangeCmd, NULL, NULL, 0}, - {"repeat", StringReptCmd, TclCompileBasic2ArgCmd, NULL, NULL, 0}, - {"replace", StringRplcCmd, TclCompileStringReplaceCmd, NULL, NULL, 0}, - {"reverse", StringRevCmd, TclCompileBasic1ArgCmd, NULL, NULL, 0}, - {"tolower", StringLowerCmd, TclCompileStringToLowerCmd, NULL, NULL, 0}, - {"toupper", StringUpperCmd, TclCompileStringToUpperCmd, NULL, NULL, 0}, - {"totitle", StringTitleCmd, TclCompileStringToTitleCmd, NULL, NULL, 0}, - {"trim", StringTrimCmd, TclCompileStringTrimCmd, NULL, NULL, 0}, - {"trimleft", StringTrimLCmd, TclCompileStringTrimLCmd, NULL, NULL, 0}, - {"trimright", StringTrimRCmd, TclCompileStringTrimRCmd, NULL, NULL, 0}, - {"wordend", StringEndCmd, TclCompileBasic2ArgCmd, NULL, NULL, 0}, - {"wordstart", StringStartCmd, TclCompileBasic2ArgCmd, NULL, NULL, 0}, - {NULL, NULL, NULL, NULL, NULL, 0} + {"bytelength", StringBytesCmd, NULL}, + {"compare", StringCmpCmd, TclCompileStringCmpCmd}, + {"equal", StringEqualCmd, TclCompileStringEqualCmd}, + {"first", StringFirstCmd, NULL}, + {"index", StringIndexCmd, TclCompileStringIndexCmd}, + {"is", StringIsCmd, NULL}, + {"last", StringLastCmd, NULL}, + {"length", StringLenCmd, TclCompileStringLenCmd}, + {"map", StringMapCmd, NULL}, + {"match", StringMatchCmd, TclCompileStringMatchCmd}, + {"range", StringRangeCmd, NULL}, + {"repeat", StringReptCmd, NULL}, + {"replace", StringRplcCmd, NULL}, + {"reverse", StringRevCmd, NULL}, + {"tolower", StringLowerCmd, NULL}, + {"toupper", StringUpperCmd, NULL}, + {"totitle", StringTitleCmd, NULL}, + {"trim", StringTrimCmd, NULL}, + {"trimleft", StringTrimLCmd, NULL}, + {"trimright", StringTrimRCmd, NULL}, + {"wordend", StringEndCmd, NULL}, + {"wordstart", StringStartCmd, NULL}, + {NULL, NULL, NULL} }; return TclMakeEnsemble(interp, "string", stringImplMap); @@ -3377,24 +3302,30 @@ TclInitStringCmd( */ int -TclSubstOptions( - Tcl_Interp *interp, - Tcl_Size numOpts, - Tcl_Obj *const opts[], - int *flagPtr) +Tcl_SubstObjCmd( + ClientData dummy, /* Not used. */ + Tcl_Interp *interp, /* Current interpreter. */ + int objc, /* Number of arguments. */ + Tcl_Obj *const objv[]) /* Argument objects. */ { - static const char *const substOptions[] = { + static const char *substOptions[] = { "-nobackslashes", "-nocommands", "-novariables", NULL }; - enum { + enum substOptions { SUBST_NOBACKSLASHES, SUBST_NOCOMMANDS, SUBST_NOVARS }; - int i, flags = TCL_SUBST_ALL; + Tcl_Obj *resultPtr; + int flags, i; - for (i = 0; i < numOpts; i++) { + /* + * Parse command-line options. + */ + + flags = TCL_SUBST_ALL; + for (i = 1; i < (objc-1); i++) { int optionIndex; - if (Tcl_GetIndexFromObj(interp, opts[i], substOptions, "option", 0, + if (Tcl_GetIndexFromObj(interp, objv[i], substOptions, "switch", 0, &optionIndex) != TCL_OK) { return TCL_ERROR; } @@ -3412,39 +3343,23 @@ TclSubstOptions( Tcl_Panic("Tcl_SubstObjCmd: bad option index to SubstOptions"); } } - *flagPtr = flags; - return TCL_OK; -} - -int -Tcl_SubstObjCmd( - void *clientData, - Tcl_Interp *interp, /* Current interpreter. */ - int objc, /* Number of arguments. */ - Tcl_Obj *const objv[]) /* Argument objects. */ -{ - return Tcl_NRCallObjProc(interp, TclNRSubstObjCmd, clientData, objc, objv); -} - -int -TclNRSubstObjCmd( - TCL_UNUSED(void *), - Tcl_Interp *interp, /* Current interpreter. */ - int objc, /* Number of arguments. */ - Tcl_Obj *const objv[]) /* Argument objects. */ -{ - int flags; - - if (objc < 2) { + if (i != objc-1) { Tcl_WrongNumArgs(interp, 1, objv, "?-nobackslashes? ?-nocommands? ?-novariables? string"); return TCL_ERROR; } - if (TclSubstOptions(interp, objc-2, objv+1, &flags) != TCL_OK) { + /* + * Perform the substitution. + */ + + resultPtr = Tcl_SubstObj(interp, objv[i], flags); + + if (resultPtr == NULL) { return TCL_ERROR; } - return Tcl_NRSubstObj(interp, objv[objc-1], flags); + Tcl_SetObjResult(interp, resultPtr); + return TCL_OK; } /* @@ -3466,24 +3381,14 @@ TclNRSubstObjCmd( int Tcl_SwitchObjCmd( - void *clientData, + ClientData dummy, /* Not used. */ Tcl_Interp *interp, /* Current interpreter. */ int objc, /* Number of arguments. */ Tcl_Obj *const objv[]) /* Argument objects. */ { - return Tcl_NRCallObjProc(interp, TclNRSwitchObjCmd, clientData, objc, objv); -} -int -TclNRSwitchObjCmd( - TCL_UNUSED(void *), - Tcl_Interp *interp, /* Current interpreter. */ - int objc, /* Number of arguments. */ - Tcl_Obj *const objv[]) /* Argument objects. */ -{ - int i, index, mode, foundmode, splitObjs, numMatchesSaved; - int noCase; - Tcl_Size patternLength, j; - const char *pattern; + int i,j, index, mode, foundmode, result, splitObjs, numMatchesSaved; + int noCase, patternLength; + char *pattern; Tcl_Obj *stringObj, *indexVarObj, *matchVarObj; Tcl_Obj *const *savedObjv = objv; Tcl_RegExp regExpr = NULL; @@ -3499,16 +3404,16 @@ TclNRSwitchObjCmd( * -glob, you *must* fix TclCompileSwitchCmd's option parser as well. */ - static const char *const options[] = { + static const char *options[] = { "-exact", "-glob", "-indexvar", "-matchvar", "-nocase", "-regexp", "--", NULL }; - enum switchOptionsEnum { + enum options { OPT_EXACT, OPT_GLOB, OPT_INDEXV, OPT_MATCHV, OPT_NOCASE, OPT_REGEXP, OPT_LAST }; typedef int (*strCmpFn_t)(const char *, const char *); - strCmpFn_t strCmpFn = TclUtfCmp; + strCmpFn_t strCmpFn = strcmp; mode = OPT_EXACT; foundmode = 0; @@ -3524,7 +3429,7 @@ TclNRSwitchObjCmd( &index) != TCL_OK) { return TCL_ERROR; } - switch ((enum switchOptionsEnum) index) { + switch ((enum options) index) { /* * General options. */ @@ -3547,16 +3452,15 @@ TclNRSwitchObjCmd( * Mode already set via -exact, -glob, or -regexp. */ - Tcl_SetObjResult(interp, Tcl_ObjPrintf( - "bad option \"%s\": %s option already found", - TclGetString(objv[i]), options[mode])); - Tcl_SetErrorCode(interp, "TCL", "OPERATION", "SWITCH", - "DOUBLEOPT", (void *)NULL); + Tcl_AppendResult(interp, "bad option \"", + TclGetString(objv[i]), "\": ", options[mode], + " option already found", NULL); return TCL_ERROR; + } else { + foundmode = 1; + mode = index; + break; } - foundmode = 1; - mode = index; - break; /* * Check for TIP#75 options specifying the variables to write @@ -3566,11 +3470,8 @@ TclNRSwitchObjCmd( case OPT_INDEXV: i++; if (i >= objc-2) { - Tcl_SetObjResult(interp, Tcl_ObjPrintf( - "missing variable name argument to %s option", - "-indexvar")); - Tcl_SetErrorCode(interp, "TCL", "OPERATION", "SWITCH", - "NOVAR", (void *)NULL); + Tcl_AppendResult(interp, "missing variable name argument to ", + "-indexvar", " option", NULL); return TCL_ERROR; } indexVarObj = objv[i]; @@ -3579,11 +3480,8 @@ TclNRSwitchObjCmd( case OPT_MATCHV: i++; if (i >= objc-2) { - Tcl_SetObjResult(interp, Tcl_ObjPrintf( - "missing variable name argument to %s option", - "-matchvar")); - Tcl_SetErrorCode(interp, "TCL", "OPERATION", "SWITCH", - "NOVAR", (void *)NULL); + Tcl_AppendResult(interp, "missing variable name argument to ", + "-matchvar", " option", NULL); return TCL_ERROR; } matchVarObj = objv[i]; @@ -3595,21 +3493,17 @@ TclNRSwitchObjCmd( finishedOptions: if (objc - i < 2) { Tcl_WrongNumArgs(interp, 1, objv, - "?-option ...? string ?pattern body ...? ?default body?"); + "?switches? string pattern body ... ?default body?"); return TCL_ERROR; } if (indexVarObj != NULL && mode != OPT_REGEXP) { - Tcl_SetObjResult(interp, Tcl_ObjPrintf( - "%s option requires -regexp option", "-indexvar")); - Tcl_SetErrorCode(interp, "TCL", "OPERATION", "SWITCH", - "MODERESTRICTION", (void *)NULL); + Tcl_AppendResult(interp, + "-indexvar option requires -regexp option", NULL); return TCL_ERROR; } if (matchVarObj != NULL && mode != OPT_REGEXP) { - Tcl_SetObjResult(interp, Tcl_ObjPrintf( - "%s option requires -regexp option", "-matchvar")); - Tcl_SetErrorCode(interp, "TCL", "OPERATION", "SWITCH", - "MODERESTRICTION", (void *)NULL); + Tcl_AppendResult(interp, + "-matchvar option requires -regexp option", NULL); return TCL_ERROR; } @@ -3630,9 +3524,9 @@ TclNRSwitchObjCmd( splitObjs = 0; if (objc == 1) { Tcl_Obj **listv; - blist = objv[0]; - if (TclListObjLength(interp, objv[0], &objc) != TCL_OK) { + + if (TclListObjGetElements(interp, objv[0], &objc, &listv) != TCL_OK){ return TCL_ERROR; } @@ -3642,10 +3536,7 @@ TclNRSwitchObjCmd( if (objc < 1) { Tcl_WrongNumArgs(interp, 1, savedObjv, - "?-option ...? string {?pattern body ...? ?default body?}"); - return TCL_ERROR; - } - if (TclListObjGetElements(interp, objv[0], &objc, &listv) != TCL_OK) { + "?switches? string {pattern body ... ?default body?}"); return TCL_ERROR; } objv = listv; @@ -3659,10 +3550,7 @@ TclNRSwitchObjCmd( if (objc % 2) { Tcl_ResetResult(interp); - Tcl_SetObjResult(interp, Tcl_NewStringObj( - "extra switch pattern with no body", -1)); - Tcl_SetErrorCode(interp, "TCL", "OPERATION", "SWITCH", "BADARM", - (void *)NULL); + Tcl_AppendResult(interp, "extra switch pattern with no body", NULL); /* * Check if this can be due to a badly placed comment in the switch @@ -3675,12 +3563,10 @@ TclNRSwitchObjCmd( if (splitObjs) { for (i=0 ; i<objc ; i+=2) { if (TclGetString(objv[i])[0] == '#') { - Tcl_AppendToObj(Tcl_GetObjResult(interp), - ", this may be due to a comment incorrectly" - " placed outside of a switch body - see the" - " \"switch\" documentation", -1); - Tcl_SetErrorCode(interp, "TCL", "OPERATION", "SWITCH", - "BADARM", "COMMENT?", (void *)NULL); + Tcl_AppendResult(interp, ", this may be due to a " + "comment incorrectly placed outside of a " + "switch body - see the \"switch\" " + "documentation", NULL); break; } } @@ -3695,11 +3581,9 @@ TclNRSwitchObjCmd( */ if (strcmp(TclGetString(objv[objc-1]), "-") == 0) { - Tcl_SetObjResult(interp, Tcl_ObjPrintf( - "no body specified for pattern \"%s\"", - TclGetString(objv[objc-2]))); - Tcl_SetErrorCode(interp, "TCL", "OPERATION", "SWITCH", "BADARM", - "FALLTHROUGH", (void *)NULL); + Tcl_ResetResult(interp); + Tcl_AppendResult(interp, "no body specified for pattern \"", + TclGetString(objv[objc-2]), "\"", NULL); return TCL_ERROR; } @@ -3738,35 +3622,36 @@ TclNRSwitchObjCmd( } } goto matchFound; - } - - switch (mode) { - case OPT_EXACT: - if (strCmpFn(TclGetString(stringObj), pattern) == 0) { - goto matchFound; - } - break; - case OPT_GLOB: - if (Tcl_StringCaseMatch(TclGetString(stringObj),pattern,noCase)) { - goto matchFound; - } - break; - case OPT_REGEXP: - regExpr = Tcl_GetRegExpFromObj(interp, objv[i], - TCL_REG_ADVANCED | (noCase ? TCL_REG_NOCASE : 0)); - if (regExpr == NULL) { - return TCL_ERROR; - } else { - int matched = Tcl_RegExpExecObj(interp, regExpr, stringObj, 0, - numMatchesSaved, 0); - - if (matched < 0) { + } else { + switch (mode) { + case OPT_EXACT: + if (strCmpFn(TclGetString(stringObj), pattern) == 0) { + goto matchFound; + } + break; + case OPT_GLOB: + if (Tcl_StringCaseMatch(TclGetString(stringObj), pattern, + noCase)) { + goto matchFound; + } + break; + case OPT_REGEXP: + regExpr = Tcl_GetRegExpFromObj(interp, objv[i], + TCL_REG_ADVANCED | (noCase ? TCL_REG_NOCASE : 0)); + if (regExpr == NULL) { return TCL_ERROR; - } else if (matched) { - goto matchFoundRegexp; + } else { + int matched = Tcl_RegExpExecObj(interp, regExpr, + stringObj, 0, numMatchesSaved, 0); + + if (matched < 0) { + return TCL_ERROR; + } else if (matched) { + goto matchFoundRegexp; + } } + break; } - break; } } return TCL_OK; @@ -3797,11 +3682,10 @@ TclNRSwitchObjCmd( Tcl_Obj *rangeObjAry[2]; if (info.matches[j].end > 0) { - TclNewIndexObj(rangeObjAry[0], info.matches[j].start); - TclNewIndexObj(rangeObjAry[1], info.matches[j].end-1); + rangeObjAry[0] = Tcl_NewLongObj(info.matches[j].start); + rangeObjAry[1] = Tcl_NewLongObj(info.matches[j].end-1); } else { - TclNewIntObj(rangeObjAry[1], -1); - rangeObjAry[0] = rangeObjAry[1]; + rangeObjAry[0] = rangeObjAry[1] = Tcl_NewIntObj(-1); } /* @@ -3815,12 +3699,8 @@ TclNRSwitchObjCmd( if (matchVarObj != NULL) { Tcl_Obj *substringObj; - if (info.matches[j].end > 0) { - substringObj = TclGetRange(stringObj, - info.matches[j].start, info.matches[j].end-1); - } else { - TclNewObj(substringObj); - } + substringObj = Tcl_GetRange(stringObj, + info.matches[j].start, info.matches[j].end-1); /* * Never fails; the object is always clean at this point. @@ -3867,7 +3747,7 @@ TclNRSwitchObjCmd( */ matchFound: - ctxPtr = (CmdFrame *)TclStackAlloc(interp, sizeof(CmdFrame)); + ctxPtr = (CmdFrame *) TclStackAlloc(interp, sizeof(CmdFrame)); *ctxPtr = *iPtr->cmdFramePtr; if (splitObjs) { @@ -3882,7 +3762,7 @@ TclNRSwitchObjCmd( if (ctxPtr->type == TCL_LOCATION_BC) { /* * Type BC => ctxPtr->data.eval.path is not used. - * ctxPtr->data.tebc.codePtr is used instead. + * ctxPtr->data.tebc.codePtr is used instead. */ TclGetSrcInfoForPc(ctxPtr); @@ -3897,7 +3777,7 @@ TclNRSwitchObjCmd( if (ctxPtr->type == TCL_LOCATION_SOURCE && ctxPtr->line[bidx] >= 0) { int bline = ctxPtr->line[bidx]; - ctxPtr->line = (Tcl_Size *)ckalloc(objc * sizeof(Tcl_Size)); + ctxPtr->line = (int *) ckalloc(objc * sizeof(int)); ctxPtr->nline = objc; TclListLines(blist, bline, objc, ctxPtr->line, objv); } else { @@ -3911,7 +3791,7 @@ TclNRSwitchObjCmd( int k; - ctxPtr->line = (Tcl_Size *)ckalloc(objc * sizeof(Tcl_Size)); + ctxPtr->line = (int *) ckalloc(objc * sizeof(int)); ctxPtr->nline = objc; for (k=0; k < objc; k++) { ctxPtr->line[k] = -1; @@ -3937,31 +3817,9 @@ TclNRSwitchObjCmd( * TIP #280: Make invoking context available to switch branch. */ - Tcl_NRAddCallback(interp, SwitchPostProc, INT2PTR(splitObjs), ctxPtr, - INT2PTR(pc), (void *)pattern); - return TclNREvalObjEx(interp, objv[j], 0, ctxPtr, splitObjs ? j : bidx+j); -} - -static int -SwitchPostProc( - void *data[], /* Data passed from Tcl_NRAddCallback above */ - Tcl_Interp *interp, /* Tcl interpreter */ - int result) /* Result to return*/ -{ - /* Unpack the preserved data */ - - int splitObjs = PTR2INT(data[0]); - CmdFrame *ctxPtr = (CmdFrame *)data[1]; - int pc = PTR2INT(data[2]); - const char *pattern = (const char *)data[3]; - Tcl_Size patternLength = strlen(pattern); - - /* - * Clean up TIP 280 context information - */ - + result = TclEvalObjEx(interp, objv[j], 0, ctxPtr, splitObjs ? j : bidx+j); if (splitObjs) { - ckfree(ctxPtr->line); + ckfree((char *) ctxPtr->line); if (pc && (ctxPtr->type == TCL_LOCATION_SOURCE)) { /* * Death of SrcInfo reference. @@ -3982,7 +3840,7 @@ SwitchPostProc( Tcl_AppendObjToErrorInfo(interp, Tcl_ObjPrintf( "\n (\"%.*s%s\" arm line %d)", (overflow ? limit : patternLength), pattern, - (overflow ? "..." : ""), Tcl_GetErrorLine(interp))); + (overflow ? "..." : ""), interp->errorLine)); } TclStackFree(interp, ctxPtr); return result; @@ -3991,68 +3849,6 @@ SwitchPostProc( /* *---------------------------------------------------------------------- * - * Tcl_ThrowObjCmd -- - * - * This procedure is invoked to process the "throw" Tcl command. See the - * user documentation for details on what it does. - * - * Results: - * A standard Tcl result. - * - * Side effects: - * See the user documentation. - * - *---------------------------------------------------------------------- - */ - -int -Tcl_ThrowObjCmd( - TCL_UNUSED(void *), - Tcl_Interp *interp, /* Current interpreter. */ - int objc, /* Number of arguments. */ - Tcl_Obj *const objv[]) /* Argument objects. */ -{ - Tcl_Obj *options; - Tcl_Size len; - - if (objc != 3) { - Tcl_WrongNumArgs(interp, 1, objv, "type message"); - return TCL_ERROR; - } - - /* - * The type must be a list of at least length 1. - */ - - if (TclListObjLength(interp, objv[1], &len) != TCL_OK) { - return TCL_ERROR; - } else if (len < 1) { - Tcl_SetObjResult(interp, Tcl_NewStringObj( - "type must be non-empty list", -1)); - Tcl_SetErrorCode(interp, "TCL", "OPERATION", "THROW", "BADEXCEPTION", - (void *)NULL); - return TCL_ERROR; - } - - /* - * Now prepare the result options dictionary. We use the list API as it is - * slightly more convenient. - */ - - TclNewLiteralStringObj(options, "-code error -level 0 -errorcode"); - Tcl_ListObjAppendElement(NULL, options, objv[1]); - - /* - * We're ready to go. Fire things into the low-level result machinery. - */ - - Tcl_SetObjResult(interp, objv[2]); - return Tcl_SetReturnOptions(interp, options); -} - -/* - *---------------------------------------------------------------------- - * * Tcl_TimeObjCmd -- * * This object-based procedure is invoked to process the "time" Tcl @@ -4069,14 +3865,14 @@ Tcl_ThrowObjCmd( int Tcl_TimeObjCmd( - TCL_UNUSED(void *), + ClientData dummy, /* Not used. */ Tcl_Interp *interp, /* Current interpreter. */ int objc, /* Number of arguments. */ Tcl_Obj *const objv[]) /* Argument objects. */ { - Tcl_Obj *objPtr; + register Tcl_Obj *objPtr; Tcl_Obj *objs[4]; - int i, result; + register int i, result; int count; double totalMicroSec; #ifndef TCL_WIDE_CLICKS @@ -4105,7 +3901,7 @@ Tcl_TimeObjCmd( start = TclpGetWideClicks(); #endif while (i-- > 0) { - result = TclEvalObjEx(interp, objPtr, 0, NULL, 0); + result = Tcl_EvalObjEx(interp, objPtr, 0); if (result != TCL_OK) { return result; } @@ -4124,9 +3920,9 @@ Tcl_TimeObjCmd( * Use int obj since we know time is not fractional. [Bug 1202178] */ - TclNewIntObj(objs[0], (count <= 0) ? 0 : (Tcl_WideInt)totalMicroSec); + objs[0] = Tcl_NewIntObj((count <= 0) ? 0 : (int) totalMicroSec); } else { - TclNewDoubleObj(objs[0], totalMicroSec/count); + objs[0] = Tcl_NewDoubleObj(totalMicroSec/count); } /* @@ -4145,19 +3941,17 @@ Tcl_TimeObjCmd( /* *---------------------------------------------------------------------- * - * Tcl_TimeRateObjCmd -- - * - * This object-based procedure is invoked to process the "timerate" Tcl - * command. + * Tcl_WhileObjCmd -- * - * This is similar to command "time", except the execution limited by - * given time (in milliseconds) instead of repetition count. + * This procedure is invoked to process the "while" Tcl command. See the + * user documentation for details on what it does. * - * Example: - * timerate {after 5} 1000; # equivalent to: time {after 5} [expr 1000/5] + * With the bytecode compiler, this procedure is only called when a + * command name is computed at runtime, and is "while" or the name to + * which "while" was renamed: e.g., "set z while; $z {$i<100} {}" * * Results: - * A standard Tcl object result. + * A standard Tcl result. * * Side effects: * See the user documentation. @@ -4166,1104 +3960,44 @@ Tcl_TimeObjCmd( */ int -Tcl_TimeRateObjCmd( - TCL_UNUSED(void *), +Tcl_WhileObjCmd( + ClientData dummy, /* Not used. */ Tcl_Interp *interp, /* Current interpreter. */ int objc, /* Number of arguments. */ Tcl_Obj *const objv[]) /* Argument objects. */ { - static double measureOverhead = 0; - /* global measure-overhead */ - double overhead = -1; /* given measure-overhead */ - Tcl_Obj *objPtr; - int result, i; - Tcl_Obj *calibrate = NULL, *direct = NULL; - Tcl_WideUInt count = 0; /* Holds repetition count */ - Tcl_WideInt maxms = WIDE_MIN; - /* Maximal running time (in milliseconds) */ - Tcl_WideUInt maxcnt = WIDE_MAX; - /* Maximal count of iterations. */ - Tcl_WideUInt threshold = 1; /* Current threshold for check time (faster - * repeat count without time check) */ - Tcl_WideUInt maxIterTm = 1; /* Max time of some iteration as max - * threshold, additionally avoiding divide to - * zero (i.e., never < 1) */ - unsigned short factor = 50; /* Factor (4..50) limiting threshold to avoid - * growth of execution time. */ - Tcl_WideInt start, middle, stop; -#ifndef TCL_WIDE_CLICKS - Tcl_Time now; -#endif /* !TCL_WIDE_CLICKS */ - static const char *const options[] = { - "-direct", "-overhead", "-calibrate", "--", NULL - }; - enum timeRateOptionsEnum { - TMRT_EV_DIRECT, TMRT_OVERHEAD, TMRT_CALIBRATE, TMRT_LAST - }; - NRE_callback *rootPtr; - ByteCode *codePtr = NULL; - - for (i = 1; i < objc - 1; i++) { - int index; - - if (Tcl_GetIndexFromObj(NULL, objv[i], options, "option", TCL_EXACT, - &index) != TCL_OK) { - break; - } - if (index == TMRT_LAST) { - i++; - break; - } - switch ((enum timeRateOptionsEnum)index) { - case TMRT_EV_DIRECT: - direct = objv[i]; - break; - case TMRT_OVERHEAD: - if (++i >= objc - 1) { - goto usage; - } - if (Tcl_GetDoubleFromObj(interp, objv[i], &overhead) != TCL_OK) { - return TCL_ERROR; - } - break; - case TMRT_CALIBRATE: - calibrate = objv[i]; - break; - case TMRT_LAST: - break; - } - } + int result, value; + Interp *iPtr = (Interp *) interp; - if (i >= objc || i < objc - 3) { - usage: - Tcl_WrongNumArgs(interp, 1, objv, - "?-direct? ?-calibrate? ?-overhead double? " - "command ?time ?max-count??"); + if (objc != 3) { + Tcl_WrongNumArgs(interp, 1, objv, "test command"); return TCL_ERROR; } - objPtr = objv[i++]; - if (i < objc) { /* max-time */ - result = Tcl_GetWideIntFromObj(interp, objv[i++], &maxms); - if (result != TCL_OK) { - return result; - } - if (i < objc) { /* max-count*/ - Tcl_WideInt v; - - result = Tcl_GetWideIntFromObj(interp, objv[i], &v); - if (result != TCL_OK) { - return result; - } - maxcnt = (v > 0) ? v : 0; - } - } - - /* - * If we are doing calibration. - */ - - if (calibrate) { - /* - * If no time specified for the calibration. - */ - - if (maxms == WIDE_MIN) { - Tcl_Obj *clobjv[6]; - Tcl_WideInt maxCalTime = 5000; - double lastMeasureOverhead = measureOverhead; - - clobjv[0] = objv[0]; - i = 1; - if (direct) { - clobjv[i++] = direct; - } - clobjv[i++] = objPtr; - - /* - * Reset last measurement overhead. - */ - - measureOverhead = (double) 0; - - /* - * Self-call with 100 milliseconds to warm-up, before entering the - * calibration cycle. - */ - - TclNewIntObj(clobjv[i], 100); - Tcl_IncrRefCount(clobjv[i]); - result = Tcl_TimeRateObjCmd(NULL, interp, i + 1, clobjv); - Tcl_DecrRefCount(clobjv[i]); - if (result != TCL_OK) { - return result; - } - - i--; - clobjv[i++] = calibrate; - clobjv[i++] = objPtr; - - /* - * Set last measurement overhead to max. - */ - - measureOverhead = (double) UWIDE_MAX; - - /* - * Run the calibration cycle until it is more precise. - */ - - maxms = -1000; - do { - lastMeasureOverhead = measureOverhead; - TclNewIntObj(clobjv[i], (int) maxms); - Tcl_IncrRefCount(clobjv[i]); - result = Tcl_TimeRateObjCmd(NULL, interp, i + 1, clobjv); - Tcl_DecrRefCount(clobjv[i]); - if (result != TCL_OK) { - return result; - } - maxCalTime += maxms; - - /* - * Increase maxms for more precise calibration. - */ - - maxms -= -maxms / 4; - - /* - * As long as new value more as 0.05% better - */ - } while ((measureOverhead >= lastMeasureOverhead - || measureOverhead / lastMeasureOverhead <= 0.9995) - && maxCalTime > 0); + while (1) { + result = Tcl_ExprBooleanObj(interp, objv[1], &value); + if (result != TCL_OK) { return result; } - if (maxms == 0) { - /* - * Reset last measurement overhead - */ - - measureOverhead = 0; - Tcl_SetObjResult(interp, Tcl_NewWideIntObj(0)); - return TCL_OK; - } - - /* - * If time is negative, make current overhead more precise. - */ - - if (maxms > 0) { - /* - * Set last measurement overhead to max. - */ - - measureOverhead = (double) UWIDE_MAX; - } else { - maxms = -maxms; - } - } - - if (maxms == WIDE_MIN) { - maxms = 1000; - } - if (overhead == -1) { - overhead = measureOverhead; - } - - /* - * Ensure that resetting of result will not smudge the further - * measurement. - */ - - Tcl_ResetResult(interp); - - /* - * Compile object if needed. - */ - - if (!direct) { - if (TclInterpReady(interp) != TCL_OK) { - return TCL_ERROR; - } - codePtr = TclCompileObj(interp, objPtr, NULL, 0); - TclPreserveByteCode(codePtr); - } - - /* - * Get start and stop time. - */ - -#ifdef TCL_WIDE_CLICKS - start = middle = TclpGetWideClicks(); - - /* - * Time to stop execution (in wide clicks). - */ - - stop = start + (maxms * 1000 / TclpWideClickInMicrosec()); -#else - Tcl_GetTime(&now); - start = now.sec; - start *= 1000000; - start += now.usec; - middle = start; - - /* - * Time to stop execution (in microsecs). - */ - - stop = start + maxms * 1000; -#endif /* TCL_WIDE_CLICKS */ - - /* - * Start measurement. - */ - - if (maxcnt > 0) { - while (1) { - /* - * Evaluate a single iteration. - */ - - count++; - if (!direct) { /* precompiled */ - rootPtr = TOP_CB(interp); - /* - * Use loop optimized TEBC call (TCL_EVAL_DISCARD_RESULT): it's a part of - * iteration, this way evaluation will be more similar to a cycle (also - * avoids extra overhead to set result to interp, etc.) - */ - ((Interp *)interp)->evalFlags |= TCL_EVAL_DISCARD_RESULT; - result = TclNRExecuteByteCode(interp, codePtr); - result = TclNRRunCallbacks(interp, result, rootPtr); - } else { /* eval */ - result = TclEvalObjEx(interp, objPtr, 0, NULL, 0); - } - /* - * Allow break and continue from measurement cycle (used for - * conditional stop and flow control of iterations). - */ - - switch (result) { - case TCL_OK: - break; - case TCL_BREAK: - /* - * Force stop immediately. - */ - threshold = 1; - maxcnt = 0; - /* FALLTHRU */ - case TCL_CONTINUE: - result = TCL_OK; - break; - default: - goto done; - } - - /* - * Don't check time up to threshold. - */ - - if (--threshold > 0) { - continue; - } - - /* - * Check stop time reached, estimate new threshold. - */ - -#ifdef TCL_WIDE_CLICKS - middle = TclpGetWideClicks(); -#else - Tcl_GetTime(&now); - middle = now.sec; - middle *= 1000000; - middle += now.usec; -#endif /* TCL_WIDE_CLICKS */ - - if (middle >= stop || count >= maxcnt) { - break; - } - - /* - * Don't calculate threshold by few iterations, because sometimes - * first iteration(s) can be too fast or slow (cached, delayed - * clean up, etc). - */ - - if (count < 10) { - threshold = 1; - continue; - } - - /* - * Average iteration time in microsecs. - */ - - threshold = (middle - start) / count; - if (threshold > maxIterTm) { - maxIterTm = threshold; - - /* - * Iterations seem to be longer. - */ - - if (threshold > maxIterTm * 2) { - factor *= 2; - if (factor > 50) { - factor = 50; - } - } else { - if (factor < 50) { - factor++; - } - } - } else if (factor > 4) { - /* - * Iterations seem to be shorter. - */ - - if (threshold < (maxIterTm / 2)) { - factor /= 2; - if (factor < 4) { - factor = 4; - } - } else { - factor--; - } - } - - /* - * As relation between remaining time and time since last check, - * maximal some % of time (by factor), so avoid growing of the - * execution time if iterations are not consistent, e.g. was - * continuously on time). - */ - - threshold = ((stop - middle) / maxIterTm) / factor + 1; - if (threshold > 100000) { /* fix for too large threshold */ - threshold = 100000; - } - - /* - * Consider max-count - */ - - if (threshold > maxcnt - count) { - threshold = maxcnt - count; - } - } - } - - { - Tcl_Obj *objarr[8], **objs = objarr; - Tcl_WideUInt usec, val; - int digits; - - /* - * Absolute execution time in microseconds or in wide clicks. - */ - usec = (Tcl_WideUInt)(middle - start); - -#ifdef TCL_WIDE_CLICKS - /* - * convert execution time (in wide clicks) to microsecs. - */ - - usec *= TclpWideClickInMicrosec(); -#endif /* TCL_WIDE_CLICKS */ - - if (!count) { /* no iterations - avoid divide by zero */ - TclNewIntObj(objs[4], 0); - objs[0] = objs[2] = objs[4]; - goto retRes; - } - - /* - * If not calibrating... - */ - - if (!calibrate) { - /* - * Minimize influence of measurement overhead. - */ - - if (overhead > 0) { - /* - * Estimate the time of overhead (microsecs). - */ - - Tcl_WideUInt curOverhead = overhead * count; - - if (usec > curOverhead) { - usec -= curOverhead; - } else { - usec = 0; - } - } - } else { - /* - * Calibration: obtaining new measurement overhead. - */ - - if (measureOverhead > ((double) usec) / count) { - measureOverhead = ((double) usec) / count; - } - TclNewDoubleObj(objs[0], measureOverhead); - TclNewLiteralStringObj(objs[1], "\xC2\xB5s/#-overhead"); /* mics */ - objs += 2; - } - - val = usec / count; /* microsecs per iteration */ - if (val >= 1000000) { - TclNewIntObj(objs[0], val); - } else { - if (val < 10) { - digits = 6; - } else if (val < 100) { - digits = 4; - } else if (val < 1000) { - digits = 3; - } else if (val < 10000) { - digits = 2; - } else { - digits = 1; - } - objs[0] = Tcl_ObjPrintf("%.*f", digits, ((double) usec)/count); - } - - TclNewIntObj(objs[2], count); /* iterations */ - - /* - * Calculate speed as rate (count) per sec - */ - - if (!usec) { - usec++; /* Avoid divide by zero. */ - } - if (count < (WIDE_MAX / 1000000)) { - val = (count * 1000000) / usec; - if (val < 100000) { - if (val < 100) { - digits = 3; - } else if (val < 1000) { - digits = 2; - } else { - digits = 1; - } - objs[4] = Tcl_ObjPrintf("%.*f", - digits, ((double) (count * 1000000)) / usec); - } else { - TclNewIntObj(objs[4], val); - } - } else { - objs[4] = Tcl_NewWideIntObj((count / usec) * 1000000); - } - - retRes: - /* - * Estimated net execution time (in millisecs). - */ - - if (!calibrate) { - if (usec >= 1) { - objs[6] = Tcl_ObjPrintf("%.3f", (double)usec / 1000); - } else { - TclNewIntObj(objs[6], 0); - } - TclNewLiteralStringObj(objs[7], "net-ms"); - } - - /* - * Construct the result as a list because many programs have always - * parsed as such (extracting the first element, typically). - */ - - TclNewLiteralStringObj(objs[1], "\xC2\xB5s/#"); /* mics/# */ - TclNewLiteralStringObj(objs[3], "#"); - TclNewLiteralStringObj(objs[5], "#/sec"); - Tcl_SetObjResult(interp, Tcl_NewListObj(8, objarr)); - } - - done: - if (codePtr != NULL) { - TclReleaseByteCode(codePtr); - } - return result; -} - -/* - *---------------------------------------------------------------------- - * - * Tcl_TryObjCmd, TclNRTryObjCmd -- - * - * This procedure is invoked to process the "try" Tcl command. See the - * user documentation (or TIP #329) for details on what it does. - * - * Results: - * A standard Tcl object result. - * - * Side effects: - * See the user documentation. - * - *---------------------------------------------------------------------- - */ - -int -Tcl_TryObjCmd( - void *clientData, - Tcl_Interp *interp, /* Current interpreter. */ - int objc, /* Number of arguments. */ - Tcl_Obj *const objv[]) /* Argument objects. */ -{ - return Tcl_NRCallObjProc(interp, TclNRTryObjCmd, clientData, objc, objv); -} - -int -TclNRTryObjCmd( - TCL_UNUSED(void *), - Tcl_Interp *interp, /* Current interpreter. */ - int objc, /* Number of arguments. */ - Tcl_Obj *const objv[]) /* Argument objects. */ -{ - Tcl_Obj *bodyObj, *handlersObj, *finallyObj = NULL; - int i, bodyShared, haveHandlers, code; - Tcl_Size dummy; - static const char *const handlerNames[] = { - "finally", "on", "trap", NULL - }; - enum Handlers { - TryFinally, TryOn, TryTrap - }; - - /* - * Parse the arguments. The handlers are passed to subsequent callbacks as - * a Tcl_Obj list of the 5-tuples like (type, returnCode, errorCodePrefix, - * bindVariables, script), and the finally script is just passed as it is. - */ - - if (objc < 2) { - Tcl_WrongNumArgs(interp, 1, objv, - "body ?handler ...? ?finally script?"); - return TCL_ERROR; - } - bodyObj = objv[1]; - TclNewObj(handlersObj); - bodyShared = 0; - haveHandlers = 0; - for (i=2 ; i<objc ; i++) { - int type; - Tcl_Obj *info[5]; - - if (Tcl_GetIndexFromObj(interp, objv[i], handlerNames, "handler type", - 0, &type) != TCL_OK) { - Tcl_DecrRefCount(handlersObj); - return TCL_ERROR; - } - switch ((enum Handlers) type) { - case TryFinally: /* finally script */ - if (i < objc-2) { - Tcl_SetObjResult(interp, Tcl_NewStringObj( - "finally clause must be last", -1)); - Tcl_DecrRefCount(handlersObj); - Tcl_SetErrorCode(interp, "TCL", "OPERATION", "TRY", "FINALLY", - "NONTERMINAL", (void *)NULL); - return TCL_ERROR; - } else if (i == objc-1) { - Tcl_SetObjResult(interp, Tcl_NewStringObj( - "wrong # args to finally clause: must be" - " \"... finally script\"", -1)); - Tcl_DecrRefCount(handlersObj); - Tcl_SetErrorCode(interp, "TCL", "OPERATION", "TRY", "FINALLY", - "ARGUMENT", (void *)NULL); - return TCL_ERROR; - } - finallyObj = objv[++i]; - break; - - case TryOn: /* on code variableList script */ - if (i > objc-4) { - Tcl_SetObjResult(interp, Tcl_NewStringObj( - "wrong # args to on clause: must be \"... on code" - " variableList script\"", -1)); - Tcl_DecrRefCount(handlersObj); - Tcl_SetErrorCode(interp, "TCL", "OPERATION", "TRY", "ON", - "ARGUMENT", (void *)NULL); - return TCL_ERROR; - } - if (TclGetCompletionCodeFromObj(interp, objv[i+1], - &code) != TCL_OK) { - Tcl_DecrRefCount(handlersObj); - return TCL_ERROR; - } - info[2] = NULL; - goto commonHandler; - - case TryTrap: /* trap pattern variableList script */ - if (i > objc-4) { - Tcl_SetObjResult(interp, Tcl_NewStringObj( - "wrong # args to trap clause: " - "must be \"... trap pattern variableList script\"", - -1)); - Tcl_DecrRefCount(handlersObj); - Tcl_SetErrorCode(interp, "TCL", "OPERATION", "TRY", "TRAP", - "ARGUMENT", (void *)NULL); - return TCL_ERROR; - } - code = 1; - if (TclListObjLength(NULL, objv[i+1], &dummy) != TCL_OK) { - Tcl_SetObjResult(interp, Tcl_ObjPrintf( - "bad prefix '%s': must be a list", - TclGetString(objv[i+1]))); - Tcl_DecrRefCount(handlersObj); - Tcl_SetErrorCode(interp, "TCL", "OPERATION", "TRY", "TRAP", - "EXNFORMAT", (void *)NULL); - return TCL_ERROR; - } - info[2] = objv[i+1]; - - commonHandler: - if (TclListObjLength(interp, objv[i+2], &dummy) != TCL_OK) { - Tcl_DecrRefCount(handlersObj); - return TCL_ERROR; - } - - info[0] = objv[i]; /* type */ - TclNewIntObj(info[1], code); /* returnCode */ - if (info[2] == NULL) { /* errorCodePrefix */ - TclNewObj(info[2]); - } - info[3] = objv[i+2]; /* bindVariables */ - info[4] = objv[i+3]; /* script */ - - bodyShared = !strcmp(TclGetString(objv[i+3]), "-"); - Tcl_ListObjAppendElement(NULL, handlersObj, - Tcl_NewListObj(5, info)); - haveHandlers = 1; - i += 3; + if (!value) { break; } - } - if (bodyShared) { - Tcl_SetObjResult(interp, Tcl_NewStringObj( - "last non-finally clause must not have a body of \"-\"", -1)); - Tcl_DecrRefCount(handlersObj); - Tcl_SetErrorCode(interp, "TCL", "OPERATION", "TRY", "BADFALLTHROUGH", - (void *)NULL); - return TCL_ERROR; - } - if (!haveHandlers) { - Tcl_DecrRefCount(handlersObj); - handlersObj = NULL; - } - - /* - * Execute the body. - */ - - Tcl_NRAddCallback(interp, TryPostBody, handlersObj, finallyObj, - (void *)objv, INT2PTR(objc)); - return TclNREvalObjEx(interp, bodyObj, 0, - ((Interp *) interp)->cmdFramePtr, 1); -} - -/* - *---------------------------------------------------------------------- - * - * During -- - * - * This helper function patches together the updates to the interpreter's - * return options that are needed when things fail during the processing - * of a handler or finally script for the [try] command. - * - * Returns: - * The new option dictionary. - * - *---------------------------------------------------------------------- - */ - -static inline Tcl_Obj * -During( - Tcl_Interp *interp, - int resultCode, /* The result code from the just-evaluated - * script. */ - Tcl_Obj *oldOptions, /* The old option dictionary. */ - Tcl_Obj *errorInfo) /* An object to append to the errorinfo and - * release, or NULL if nothing is to be added. - * Designed to be used with Tcl_ObjPrintf. */ -{ - Tcl_Obj *during, *options; - - if (errorInfo != NULL) { - Tcl_AppendObjToErrorInfo(interp, errorInfo); - } - options = Tcl_GetReturnOptions(interp, resultCode); - TclNewLiteralStringObj(during, "-during"); - Tcl_IncrRefCount(during); - Tcl_DictObjPut(interp, options, during, oldOptions); - Tcl_DecrRefCount(during); - Tcl_IncrRefCount(options); - Tcl_DecrRefCount(oldOptions); - return options; -} - -/* - *---------------------------------------------------------------------- - * - * TryPostBody -- - * - * Callback to handle the outcome of the execution of the body of a 'try' - * command. - * - *---------------------------------------------------------------------- - */ - -static int -TryPostBody( - void *data[], - Tcl_Interp *interp, - int result) -{ - Tcl_Obj *resultObj, *options, *handlersObj, *finallyObj, *cmdObj, **objv; - int code, objc; - Tcl_Size i, numHandlers = 0; - - handlersObj = (Tcl_Obj *)data[0]; - finallyObj = (Tcl_Obj *)data[1]; - objv = (Tcl_Obj **)data[2]; - objc = PTR2INT(data[3]); - - cmdObj = objv[0]; - - /* - * Check for limits/rewinding, which override normal trapping behaviour. - */ - - if (((Interp*) interp)->execEnvPtr->rewind || Tcl_LimitExceeded(interp)) { - Tcl_AppendObjToErrorInfo(interp, Tcl_ObjPrintf( - "\n (\"%s\" body line %d)", TclGetString(cmdObj), - Tcl_GetErrorLine(interp))); - if (handlersObj != NULL) { - Tcl_DecrRefCount(handlersObj); - } - return TCL_ERROR; - } - - /* - * Basic processing of the outcome of the script, including adding of - * errorinfo trace. - */ - - if (result == TCL_ERROR) { - Tcl_AppendObjToErrorInfo(interp, Tcl_ObjPrintf( - "\n (\"%s\" body line %d)", TclGetString(cmdObj), - Tcl_GetErrorLine(interp))); - } - resultObj = Tcl_GetObjResult(interp); - Tcl_IncrRefCount(resultObj); - options = Tcl_GetReturnOptions(interp, result); - Tcl_IncrRefCount(options); - Tcl_ResetResult(interp); - - /* - * Handle the results. - */ - - if (handlersObj != NULL) { - int found = 0; - Tcl_Obj **handlers, **info; - - TclListObjGetElements(NULL, handlersObj, &numHandlers, &handlers); - for (i=0 ; i<numHandlers ; i++) { - Tcl_Obj *handlerBodyObj; - Tcl_Size numElems = 0; - - TclListObjGetElements(NULL, handlers[i], &numElems, &info); - if (!found) { - Tcl_GetIntFromObj(NULL, info[1], &code); - if (code != result) { - continue; - } - - /* - * When processing an error, we must also perform list-prefix - * matching of the errorcode list. However, if this was an - * 'on' handler, the list that we are matching against will be - * empty. - */ - - if (code == TCL_ERROR) { - Tcl_Obj *errorCodeName, *errcode, **bits1, **bits2; - Tcl_Size len1, len2, j; - - TclNewLiteralStringObj(errorCodeName, "-errorcode"); - Tcl_DictObjGet(NULL, options, errorCodeName, &errcode); - Tcl_DecrRefCount(errorCodeName); - TclListObjGetElements(NULL, info[2], &len1, &bits1); - if (TclListObjGetElements(NULL, errcode, &len2, - &bits2) != TCL_OK) { - continue; - } - if (len2 < len1) { - continue; - } - for (j=0 ; j<len1 ; j++) { - if (strcmp(TclGetString(bits1[j]), - TclGetString(bits2[j])) != 0) { - /* - * Really want 'continue outerloop;', but C does - * not give us that. - */ - - goto didNotMatch; - } - } - } - - found = 1; - } - - /* - * Now we need to scan forward over "-" bodies. Note that we've - * already checked that the last body is not a "-", so this search - * will terminate successfully. - */ - - if (!strcmp(TclGetString(info[4]), "-")) { - continue; - } - - /* - * Bind the variables. We already know this is a list of variable - * names, but it might be empty. - */ - - Tcl_ResetResult(interp); - result = TCL_ERROR; - TclListObjLength(NULL, info[3], &numElems); - if (numElems> 0) { - Tcl_Obj *varName; - Tcl_ListObjIndex(NULL, info[3], 0, &varName); - if (Tcl_ObjSetVar2(interp, varName, NULL, resultObj, - TCL_LEAVE_ERR_MSG) == NULL) { - Tcl_DecrRefCount(resultObj); - goto handlerFailed; - } - Tcl_DecrRefCount(resultObj); - if (numElems> 1) { - Tcl_ListObjIndex(NULL, info[3], 1, &varName); - if (Tcl_ObjSetVar2(interp, varName, NULL, options, - TCL_LEAVE_ERR_MSG) == NULL) { - goto handlerFailed; - } - } - } else { - /* - * Dispose of the result to prevent a memleak. [Bug 2910044] - */ - - Tcl_DecrRefCount(resultObj); + /* TIP #280. */ + result = TclEvalObjEx(interp, objv[2], 0, iPtr->cmdFramePtr, 2); + if ((result != TCL_OK) && (result != TCL_CONTINUE)) { + if (result == TCL_ERROR) { + Tcl_AppendObjToErrorInfo(interp, Tcl_ObjPrintf( + "\n (\"while\" body line %d)", interp->errorLine)); } - - /* - * Evaluate the handler body and process the outcome. Note that we - * need to keep the kind of handler for debugging purposes, and in - * any case anything we want from info[] must be extracted right - * now because the info[] array is about to become invalid. There - * is very little refcount handling here however, since we know - * that the objects that we still want to refer to now were input - * arguments to [try] and so are still on the Tcl value stack. - */ - - handlerBodyObj = info[4]; - Tcl_NRAddCallback(interp, TryPostHandler, objv, options, info[0], - INT2PTR((finallyObj == NULL) ? 0 : objc - 1)); - Tcl_DecrRefCount(handlersObj); - return TclNREvalObjEx(interp, handlerBodyObj, 0, - ((Interp *) interp)->cmdFramePtr, 4*i + 5); - - handlerFailed: - resultObj = Tcl_GetObjResult(interp); - Tcl_IncrRefCount(resultObj); - options = During(interp, result, options, NULL); break; - - didNotMatch: - continue; } - - /* - * No handler matched; get rid of the list of handlers. - */ - - Tcl_DecrRefCount(handlersObj); - } - - /* - * Process the finally clause. - */ - - if (finallyObj != NULL) { - Tcl_NRAddCallback(interp, TryPostFinal, resultObj, options, cmdObj, - NULL); - return TclNREvalObjEx(interp, finallyObj, 0, - ((Interp *) interp)->cmdFramePtr, objc - 1); - } - - /* - * Install the correct result/options into the interpreter and clean up - * any temporary storage. - */ - - result = Tcl_SetReturnOptions(interp, options); - Tcl_DecrRefCount(options); - Tcl_SetObjResult(interp, resultObj); - Tcl_DecrRefCount(resultObj); - return result; -} - -/* - *---------------------------------------------------------------------- - * - * TryPostHandler -- - * - * Callback to handle the outcome of the execution of a handler of a - * 'try' command. - * - *---------------------------------------------------------------------- - */ - -static int -TryPostHandler( - void *data[], - Tcl_Interp *interp, - int result) -{ - Tcl_Obj *resultObj, *cmdObj, *options, *handlerKindObj, **objv; - Tcl_Obj *finallyObj; - int finallyIndex; - - objv = (Tcl_Obj **)data[0]; - options = (Tcl_Obj *)data[1]; - handlerKindObj = (Tcl_Obj *)data[2]; - finallyIndex = PTR2INT(data[3]); - - cmdObj = objv[0]; - finallyObj = finallyIndex ? objv[finallyIndex] : 0; - - /* - * Check for limits/rewinding, which override normal trapping behaviour. - */ - - if (((Interp*) interp)->execEnvPtr->rewind || Tcl_LimitExceeded(interp)) { - options = During(interp, result, options, Tcl_ObjPrintf( - "\n (\"%s ... %s\" handler line %d)", - TclGetString(cmdObj), TclGetString(handlerKindObj), - Tcl_GetErrorLine(interp))); - Tcl_DecrRefCount(options); - return TCL_ERROR; } - - /* - * The handler result completely substitutes for the result of the body. - */ - - resultObj = Tcl_GetObjResult(interp); - Tcl_IncrRefCount(resultObj); - if (result == TCL_ERROR) { - options = During(interp, result, options, Tcl_ObjPrintf( - "\n (\"%s ... %s\" handler line %d)", - TclGetString(cmdObj), TclGetString(handlerKindObj), - Tcl_GetErrorLine(interp))); - } else { - Tcl_DecrRefCount(options); - options = Tcl_GetReturnOptions(interp, result); - Tcl_IncrRefCount(options); - } - - /* - * Process the finally clause if it is present. - */ - - if (finallyObj != NULL) { - Interp *iPtr = (Interp *) interp; - - Tcl_NRAddCallback(interp, TryPostFinal, resultObj, options, cmdObj, - NULL); - - /* The 'finally' script is always the last argument word. */ - return TclNREvalObjEx(interp, finallyObj, 0, iPtr->cmdFramePtr, - finallyIndex); - } - - /* - * Install the correct result/options into the interpreter and clean up - * any temporary storage. - */ - - result = Tcl_SetReturnOptions(interp, options); - Tcl_DecrRefCount(options); - Tcl_SetObjResult(interp, resultObj); - Tcl_DecrRefCount(resultObj); - return result; -} - -/* - *---------------------------------------------------------------------- - * - * TryPostFinal -- - * - * Callback to handle the outcome of the execution of the finally script - * of a 'try' command. - * - *---------------------------------------------------------------------- - */ - -static int -TryPostFinal( - void *data[], - Tcl_Interp *interp, - int result) -{ - Tcl_Obj *resultObj, *options, *cmdObj; - - resultObj = (Tcl_Obj *)data[0]; - options = (Tcl_Obj *)data[1]; - cmdObj = (Tcl_Obj *)data[2]; - - /* - * If the result wasn't OK, we need to adjust the result options. - */ - - if (result != TCL_OK) { - Tcl_DecrRefCount(resultObj); - resultObj = NULL; - if (result == TCL_ERROR) { - options = During(interp, result, options, Tcl_ObjPrintf( - "\n (\"%s ... finally\" body line %d)", - TclGetString(cmdObj), Tcl_GetErrorLine(interp))); - } else { - Tcl_Obj *origOptions = options; - - options = Tcl_GetReturnOptions(interp, result); - Tcl_IncrRefCount(options); - Tcl_DecrRefCount(origOptions); - } + if (result == TCL_BREAK) { + result = TCL_OK; } - - /* - * Install the correct result/options into the interpreter and clean up - * any temporary storage. - */ - - result = Tcl_SetReturnOptions(interp, options); - Tcl_DecrRefCount(options); - if (resultObj != NULL) { - Tcl_SetObjResult(interp, resultObj); - Tcl_DecrRefCount(resultObj); + if (result == TCL_OK) { + Tcl_ResetResult(interp); } return result; } @@ -5271,67 +4005,6 @@ TryPostFinal( /* *---------------------------------------------------------------------- * - * Tcl_WhileObjCmd -- - * - * This procedure is invoked to process the "while" Tcl command. See the - * user documentation for details on what it does. - * - * With the bytecode compiler, this procedure is only called when a - * command name is computed at runtime, and is "while" or the name to - * which "while" was renamed: e.g., "set z while; $z {$i<100} {}" - * - * Results: - * A standard Tcl result. - * - * Side effects: - * See the user documentation. - * - *---------------------------------------------------------------------- - */ - -int -Tcl_WhileObjCmd( - void *clientData, - Tcl_Interp *interp, /* Current interpreter. */ - int objc, /* Number of arguments. */ - Tcl_Obj *const objv[]) /* Argument objects. */ -{ - return Tcl_NRCallObjProc(interp, TclNRWhileObjCmd, clientData, objc, objv); -} - -int -TclNRWhileObjCmd( - TCL_UNUSED(void *), - Tcl_Interp *interp, /* Current interpreter. */ - int objc, /* Number of arguments. */ - Tcl_Obj *const objv[]) /* Argument objects. */ -{ - ForIterData *iterPtr; - - if (objc != 3) { - Tcl_WrongNumArgs(interp, 1, objv, "test command"); - return TCL_ERROR; - } - - /* - * We reuse [for]'s callback, passing a NULL for the 'next' script. - */ - - TclSmallAllocEx(interp, sizeof(ForIterData), iterPtr); - iterPtr->cond = objv[1]; - iterPtr->body = objv[2]; - iterPtr->next = NULL; - iterPtr->msg = "\n (\"while\" body line %d)"; - iterPtr->word = 2; - - TclNRAddCallback(interp, TclNRForIterCallback, iterPtr, NULL, - NULL, NULL); - return TCL_OK; -} - -/* - *---------------------------------------------------------------------- - * * TclListLines -- * * ??? @@ -5347,30 +4020,32 @@ TclNRWhileObjCmd( void TclListLines( - Tcl_Obj *listObj, /* Pointer to obj holding a string with list - * structure. Assumed to be valid. Assumed to - * contain n elements. */ - Tcl_Size line, /* Line the list as a whole starts on. */ - Tcl_Size n, /* #elements in lines */ - Tcl_Size *lines, /* Array of line numbers, to fill. */ - Tcl_Obj *const *elems) /* The list elems as Tcl_Obj*, in need of + Tcl_Obj* listObj, /* Pointer to obj holding a string with list + * structure. Assumed to be valid. Assumed to + * contain n elements. + */ + int line, /* Line the list as a whole starts on. */ + int n, /* #elements in lines */ + int *lines, /* Array of line numbers, to fill. */ + Tcl_Obj* const* elems) /* The list elems as Tcl_Obj*, in need of * derived continuation data */ { - const char *listStr = TclGetString(listObj); - const char *listHead = listStr; - Tcl_Size i, length = strlen(listStr); + const char* listStr = Tcl_GetString (listObj); + const char* listHead = listStr; + int i, length = strlen(listStr); const char *element = NULL, *next = NULL; - ContLineLoc *clLocPtr = TclContinuationsGet(listObj); - Tcl_Size *clNext = (clLocPtr ? &clLocPtr->loc[0] : NULL); + ContLineLoc* clLocPtr = TclContinuationsGet(listObj); + int* clNext = (clLocPtr ? &clLocPtr->loc[0] : NULL); for (i = 0; i < n; i++) { TclFindElement(NULL, listStr, length, &element, &next, NULL, NULL); TclAdvanceLines(&line, listStr, element); /* Leading whitespace */ - TclAdvanceContinuations(&line, &clNext, element - listHead); + TclAdvanceContinuations (&line, &clNext, element - listHead); if (elems && clNext) { - TclContinuationsEnterDerived(elems[i], element-listHead, clNext); + TclContinuationsEnterDerived (elems[i], element - listHead, + clNext); } lines[i] = line; length -= (next - listStr); |
