diff options
Diffstat (limited to 'generic/tclCmdIL.c')
-rw-r--r-- | generic/tclCmdIL.c | 1217 |
1 files changed, 742 insertions, 475 deletions
diff --git a/generic/tclCmdIL.c b/generic/tclCmdIL.c index 25fd078..b6e9f21 100644 --- a/generic/tclCmdIL.c +++ b/generic/tclCmdIL.c @@ -3,7 +3,7 @@ * * This file contains the top-level command routines for most of the Tcl * built-in commands whose names begin with the letters I through L. It - * contains only commands in the generic core (i.e. those that don't + * contains only commands in the generic core (i.e., those that don't * depend much upon UNIX facilities). * * Copyright (c) 1987-1993 The Regents of the University of California. @@ -27,13 +27,16 @@ */ typedef struct SortElement { - union { - char *strValuePtr; - long intValue; + union { /* The value that we sorting by. */ + const char *strValuePtr; + long intValue; double doubleValue; Tcl_Obj *objValuePtr; - } index; - Tcl_Obj *objPtr; /* Object being sorted, or its index. */ + } collationKey; + union { /* Object being sorted, or its index. */ + Tcl_Obj *objPtr; + int index; + } payload; struct SortElement *nextPtr;/* Next element in the list, or NULL for end * of list. */ } SortElement; @@ -101,46 +104,51 @@ typedef struct SortInfo { * Forward declarations for procedures defined in this file: */ -static int DictionaryCompare(char *left, char *right); +static int DictionaryCompare(const char *left, const char *right); +static int IfConditionCallback(ClientData data[], + Tcl_Interp *interp, int result); static int InfoArgsCmd(ClientData dummy, Tcl_Interp *interp, - int objc, Tcl_Obj *CONST objv[]); + int objc, Tcl_Obj *const objv[]); static int InfoBodyCmd(ClientData dummy, Tcl_Interp *interp, - int objc, Tcl_Obj *CONST objv[]); + int objc, Tcl_Obj *const objv[]); static int InfoCmdCountCmd(ClientData dummy, Tcl_Interp *interp, - int objc, Tcl_Obj *CONST objv[]); + int objc, Tcl_Obj *const objv[]); static int InfoCommandsCmd(ClientData dummy, Tcl_Interp *interp, - int objc, Tcl_Obj *CONST objv[]); + int objc, Tcl_Obj *const objv[]); static int InfoCompleteCmd(ClientData dummy, Tcl_Interp *interp, - int objc, Tcl_Obj *CONST objv[]); + int objc, Tcl_Obj *const objv[]); static int InfoDefaultCmd(ClientData dummy, Tcl_Interp *interp, - int objc, Tcl_Obj *CONST objv[]); + int objc, Tcl_Obj *const objv[]); +/* TIP #348 - New 'info' subcommand 'errorstack' */ +static int InfoErrorStackCmd(ClientData dummy, Tcl_Interp *interp, + int objc, Tcl_Obj *const objv[]); /* TIP #280 - New 'info' subcommand 'frame' */ static int InfoFrameCmd(ClientData dummy, Tcl_Interp *interp, - int objc, Tcl_Obj *CONST objv[]); + int objc, Tcl_Obj *const objv[]); static int InfoFunctionsCmd(ClientData dummy, Tcl_Interp *interp, - int objc, Tcl_Obj *CONST objv[]); + int objc, Tcl_Obj *const objv[]); static int InfoHostnameCmd(ClientData dummy, Tcl_Interp *interp, - int objc, Tcl_Obj *CONST objv[]); + int objc, Tcl_Obj *const objv[]); static int InfoLevelCmd(ClientData dummy, Tcl_Interp *interp, - int objc, Tcl_Obj *CONST objv[]); + int objc, Tcl_Obj *const objv[]); static int InfoLibraryCmd(ClientData dummy, Tcl_Interp *interp, - int objc, Tcl_Obj *CONST objv[]); + int objc, Tcl_Obj *const objv[]); static int InfoLoadedCmd(ClientData dummy, Tcl_Interp *interp, - int objc, Tcl_Obj *CONST objv[]); + int objc, Tcl_Obj *const objv[]); static int InfoNameOfExecutableCmd(ClientData dummy, Tcl_Interp *interp, int objc, - Tcl_Obj *CONST objv[]); + Tcl_Obj *const objv[]); static int InfoPatchLevelCmd(ClientData dummy, Tcl_Interp *interp, - int objc, Tcl_Obj *CONST objv[]); + int objc, Tcl_Obj *const objv[]); static int InfoProcsCmd(ClientData dummy, Tcl_Interp *interp, - int objc, Tcl_Obj *CONST objv[]); + int objc, Tcl_Obj *const objv[]); static int InfoScriptCmd(ClientData dummy, Tcl_Interp *interp, - int objc, Tcl_Obj *CONST objv[]); + int objc, Tcl_Obj *const objv[]); static int InfoSharedlibCmd(ClientData dummy, Tcl_Interp *interp, - int objc, Tcl_Obj *CONST objv[]); + int objc, Tcl_Obj *const objv[]); static int InfoTclVersionCmd(ClientData dummy, Tcl_Interp *interp, - int objc, Tcl_Obj *CONST objv[]); -static SortElement * MergeLists(SortElement *leftPtr, SortElement *rightPtr, + int objc, Tcl_Obj *const objv[]); +static SortElement * MergeLists(SortElement *leftPtr, SortElement *rightPtr, SortInfo *infoPtr); static int SortCompare(SortElement *firstPtr, SortElement *second, SortInfo *infoPtr); @@ -153,29 +161,31 @@ static Tcl_Obj * SelectObjFromSublist(Tcl_Obj *firstPtr, */ static const EnsembleImplMap defaultInfoMap[] = { - {"args", InfoArgsCmd, NULL}, - {"body", InfoBodyCmd, NULL}, - {"cmdcount", InfoCmdCountCmd, NULL}, - {"commands", InfoCommandsCmd, NULL}, - {"complete", InfoCompleteCmd, NULL}, - {"default", InfoDefaultCmd, NULL}, - {"exists", TclInfoExistsCmd, TclCompileInfoExistsCmd}, - {"frame", InfoFrameCmd, NULL}, - {"functions", InfoFunctionsCmd, NULL}, - {"globals", TclInfoGlobalsCmd, NULL}, - {"hostname", InfoHostnameCmd, NULL}, - {"level", InfoLevelCmd, NULL}, - {"library", InfoLibraryCmd, NULL}, - {"loaded", InfoLoadedCmd, NULL}, - {"locals", TclInfoLocalsCmd, NULL}, - {"nameofexecutable", InfoNameOfExecutableCmd, NULL}, - {"patchlevel", InfoPatchLevelCmd, NULL}, - {"procs", InfoProcsCmd, NULL}, - {"script", InfoScriptCmd, NULL}, - {"sharedlibextension", InfoSharedlibCmd, NULL}, - {"tclversion", InfoTclVersionCmd, NULL}, - {"vars", TclInfoVarsCmd, NULL}, - {NULL, NULL, NULL} + {"args", InfoArgsCmd, NULL, NULL, NULL, 0}, + {"body", InfoBodyCmd, NULL, NULL, NULL, 0}, + {"cmdcount", InfoCmdCountCmd, NULL, NULL, NULL, 0}, + {"commands", InfoCommandsCmd, NULL, NULL, NULL, 0}, + {"complete", InfoCompleteCmd, NULL, NULL, NULL, 0}, + {"coroutine", TclInfoCoroutineCmd, NULL, NULL, NULL, 0}, + {"default", InfoDefaultCmd, NULL, NULL, NULL, 0}, + {"errorstack", InfoErrorStackCmd, NULL, NULL, NULL, 0}, + {"exists", TclInfoExistsCmd, TclCompileInfoExistsCmd, NULL, NULL, 0}, + {"frame", InfoFrameCmd, NULL, NULL, NULL, 0}, + {"functions", InfoFunctionsCmd, NULL, NULL, NULL, 0}, + {"globals", TclInfoGlobalsCmd, NULL, NULL, NULL, 0}, + {"hostname", InfoHostnameCmd, NULL, NULL, NULL, 0}, + {"level", InfoLevelCmd, NULL, NULL, NULL, 0}, + {"library", InfoLibraryCmd, NULL, NULL, NULL, 0}, + {"loaded", InfoLoadedCmd, NULL, NULL, NULL, 0}, + {"locals", TclInfoLocalsCmd, NULL, NULL, NULL, 0}, + {"nameofexecutable", InfoNameOfExecutableCmd, NULL, NULL, NULL, 0}, + {"patchlevel", InfoPatchLevelCmd, NULL, NULL, NULL, 0}, + {"procs", InfoProcsCmd, NULL, NULL, NULL, 0}, + {"script", InfoScriptCmd, NULL, NULL, NULL, 0}, + {"sharedlibextension", InfoSharedlibCmd, NULL, NULL, NULL, 0}, + {"tclversion", InfoTclVersionCmd, NULL, NULL, NULL, 0}, + {"vars", TclInfoVarsCmd, NULL, NULL, NULL, 0}, + {NULL, NULL, NULL, NULL, NULL, 0} }; /* @@ -204,42 +214,67 @@ Tcl_IfObjCmd( ClientData dummy, /* Not used. */ Tcl_Interp *interp, /* Current interpreter. */ int objc, /* Number of arguments. */ - Tcl_Obj *CONST objv[]) /* Argument objects. */ + Tcl_Obj *const objv[]) /* Argument objects. */ +{ + return Tcl_NRCallObjProc(interp, TclNRIfObjCmd, dummy, objc, objv); +} + +int +TclNRIfObjCmd( + ClientData dummy, /* Not used. */ + Tcl_Interp *interp, /* Current interpreter. */ + int objc, /* Number of arguments. */ + Tcl_Obj *const objv[]) /* Argument objects. */ +{ + Tcl_Obj *boolObj; + + if (objc <= 1) { + Tcl_AppendResult(interp, "wrong # args: no expression after \"", + TclGetString(objv[0]), "\" argument", NULL); + Tcl_SetErrorCode(interp, "TCL", "WRONGARGS", NULL); + return TCL_ERROR; + } + + /* + * At this point, objv[1] refers to the main expression to test. The + * arguments after the expression must be "then" (optional) and a script + * to execute if the expression is true. + */ + + TclNewObj(boolObj); + Tcl_NRAddCallback(interp, IfConditionCallback, INT2PTR(objc), + (ClientData) objv, INT2PTR(1), boolObj); + return Tcl_NRExprObj(interp, objv[1], boolObj); +} + +static int +IfConditionCallback( + ClientData data[], + Tcl_Interp *interp, + int result) { - int thenScriptIndex = 0; /* "then" script to be evaled after syntax - * check. */ Interp *iPtr = (Interp *) interp; - int i, result, value; - char *clause; + int objc = PTR2INT(data[0]); + Tcl_Obj *const *objv = data[1]; + int i = PTR2INT(data[2]); + Tcl_Obj *boolObj = data[3]; + int value, thenScriptIndex = 0; + const char *clause; - i = 1; - while (1) { - /* - * At this point in the loop, objv and objc refer to an expression to - * test, either for the main expression or an expression following an - * "elseif". The arguments after the expression must be "then" - * (optional) and a script to execute if the expression is true. - */ + if (result != TCL_OK) { + TclDecrRefCount(boolObj); + return result; + } + if (Tcl_GetBooleanFromObj(interp, boolObj, &value) != TCL_OK) { + TclDecrRefCount(boolObj); + return TCL_ERROR; + } + TclDecrRefCount(boolObj); - if (i >= objc) { - clause = TclGetString(objv[i-1]); - Tcl_AppendResult(interp, "wrong # args: ", - "no expression after \"", clause, "\" argument", NULL); - return TCL_ERROR; - } - if (!thenScriptIndex) { - result = Tcl_ExprBooleanObj(interp, objv[i], &value); - if (result != TCL_OK) { - return result; - } - } + while (1) { i++; if (i >= objc) { - missingScript: - clause = TclGetString(objv[i-1]); - Tcl_AppendResult(interp, "wrong # args: ", - "no script following \"", clause, "\" argument", NULL); - return TCL_ERROR; + goto missingScript; } clause = TclGetString(objv[i]); if ((i < objc) && (strcmp(clause, "then") == 0)) { @@ -265,17 +300,36 @@ Tcl_IfObjCmd( * TIP #280. Make invoking context available to branch. */ - return TclEvalObjEx(interp, objv[thenScriptIndex], 0, + return TclNREvalObjEx(interp, objv[thenScriptIndex], 0, iPtr->cmdFramePtr, thenScriptIndex); } return TCL_OK; } clause = TclGetString(objv[i]); - if ((clause[0] == 'e') && (strcmp(clause, "elseif") == 0)) { - i++; - continue; + if ((clause[0] != 'e') || (strcmp(clause, "elseif") != 0)) { + break; + } + i++; + + /* + * At this point in the loop, objv and objc refer to an expression to + * test, either for the main expression or an expression following an + * "elseif". The arguments after the expression must be "then" + * (optional) and a script to execute if the expression is true. + */ + + if (i >= objc) { + Tcl_AppendResult(interp, "wrong # args: ", + "no expression after \"", clause, "\" argument", NULL); + Tcl_SetErrorCode(interp, "TCL", "WRONGARGS", NULL); + return TCL_ERROR; + } + if (!thenScriptIndex) { + TclNewObj(boolObj); + Tcl_NRAddCallback(interp, IfConditionCallback, data[0], data[1], + INT2PTR(i), boolObj); + return Tcl_NRExprObj(interp, objv[i], boolObj); } - break; } /* @@ -287,14 +341,13 @@ Tcl_IfObjCmd( if (strcmp(clause, "else") == 0) { i++; if (i >= objc) { - Tcl_AppendResult(interp, "wrong # args: ", - "no script following \"else\" argument", NULL); - return TCL_ERROR; + goto missingScript; } } if (i < objc - 1) { Tcl_AppendResult(interp, "wrong # args: ", "extra words after \"else\" clause in \"if\" command", NULL); + Tcl_SetErrorCode(interp, "TCL", "WRONGARGS", NULL); return TCL_ERROR; } if (thenScriptIndex) { @@ -302,10 +355,17 @@ Tcl_IfObjCmd( * TIP #280. Make invoking context available to branch/else. */ - return TclEvalObjEx(interp, objv[thenScriptIndex], 0, + return TclNREvalObjEx(interp, objv[thenScriptIndex], 0, iPtr->cmdFramePtr, thenScriptIndex); } - return TclEvalObjEx(interp, objv[i], 0, iPtr->cmdFramePtr, i); + return TclNREvalObjEx(interp, objv[i], 0, iPtr->cmdFramePtr, i); + + missingScript: + clause = TclGetString(objv[i-1]); + Tcl_AppendResult(interp, "wrong # args: no script following \"", clause, + "\" argument", NULL); + Tcl_SetErrorCode(interp, "TCL", "WRONGARGS", NULL); + return TCL_ERROR; } /* @@ -334,7 +394,7 @@ Tcl_IncrObjCmd( ClientData dummy, /* Not used. */ Tcl_Interp *interp, /* Current interpreter. */ int objc, /* Number of arguments. */ - Tcl_Obj *CONST objv[]) /* Argument objects. */ + Tcl_Obj *const objv[]) /* Argument objects. */ { Tcl_Obj *newValuePtr, *incrPtr; @@ -375,7 +435,7 @@ Tcl_IncrObjCmd( * documentation for details on what it does. * * Results: - * FIXME + * Handle for the info command, or NULL on failure. * * Side effects: * none @@ -415,10 +475,10 @@ InfoArgsCmd( ClientData dummy, /* Not used. */ Tcl_Interp *interp, /* Current interpreter. */ int objc, /* Number of arguments. */ - Tcl_Obj *CONST objv[]) /* Argument objects. */ + Tcl_Obj *const objv[]) /* Argument objects. */ { register Interp *iPtr = (Interp *) interp; - char *name; + const char *name; Proc *procPtr; CompiledLocal *localPtr; Tcl_Obj *listObjPtr; @@ -432,6 +492,7 @@ InfoArgsCmd( procPtr = TclFindProc(iPtr, name); if (procPtr == NULL) { Tcl_AppendResult(interp, "\"", name, "\" isn't a procedure", NULL); + Tcl_SetErrorCode(interp, "TCL", "LOOKUP", "PROCEDURE", name, NULL); return TCL_ERROR; } @@ -476,10 +537,10 @@ InfoBodyCmd( ClientData dummy, /* Not used. */ Tcl_Interp *interp, /* Current interpreter. */ int objc, /* Number of arguments. */ - Tcl_Obj *CONST objv[]) /* Argument objects. */ + Tcl_Obj *const objv[]) /* Argument objects. */ { register Interp *iPtr = (Interp *) interp; - char *name; + const char *name; Proc *procPtr; Tcl_Obj *bodyPtr, *resultPtr; @@ -492,6 +553,7 @@ InfoBodyCmd( procPtr = TclFindProc(iPtr, name); if (procPtr == NULL) { Tcl_AppendResult(interp, "\"", name, "\" isn't a procedure", NULL); + Tcl_SetErrorCode(interp, "TCL", "LOOKUP", "PROCEDURE", name, NULL); return TCL_ERROR; } @@ -511,7 +573,7 @@ InfoBodyCmd( * run before. [Bug #545644] */ - (void) TclGetString(bodyPtr); + TclGetString(bodyPtr); } resultPtr = Tcl_NewStringObj(bodyPtr->bytes, bodyPtr->length); @@ -545,7 +607,7 @@ InfoCmdCountCmd( ClientData dummy, /* Not used. */ Tcl_Interp *interp, /* Current interpreter. */ int objc, /* Number of arguments. */ - Tcl_Obj *CONST objv[]) /* Argument objects. */ + Tcl_Obj *const objv[]) /* Argument objects. */ { Interp *iPtr = (Interp *) interp; @@ -587,10 +649,10 @@ InfoCommandsCmd( ClientData dummy, /* Not used. */ Tcl_Interp *interp, /* Current interpreter. */ int objc, /* Number of arguments. */ - Tcl_Obj *CONST objv[]) /* Argument objects. */ + Tcl_Obj *const objv[]) /* Argument objects. */ { - char *cmdName, *pattern; - CONST char *simplePattern; + const char *cmdName, *pattern; + const char *simplePattern; register Tcl_HashEntry *entryPtr; Tcl_HashSearch search; Namespace *nsPtr; @@ -622,8 +684,8 @@ InfoCommandsCmd( Namespace *dummy1NsPtr, *dummy2NsPtr; pattern = TclGetString(objv[1]); - TclGetNamespaceForQualName(interp, pattern, (Namespace *) NULL, 0, - &nsPtr, &dummy1NsPtr, &dummy2NsPtr, &simplePattern); + TclGetNamespaceForQualName(interp, pattern, NULL, 0, &nsPtr, + &dummy1NsPtr, &dummy2NsPtr, &simplePattern); if (nsPtr != NULL) { /* We successfully found the pattern's ns. */ specificNsInPattern = (strcmp(simplePattern, pattern) != 0); @@ -659,7 +721,7 @@ InfoCommandsCmd( entryPtr = Tcl_FindHashEntry(&nsPtr->cmdTable, simplePattern); if (entryPtr != NULL) { if (specificNsInPattern) { - cmd = (Tcl_Command) Tcl_GetHashValue(entryPtr); + cmd = Tcl_GetHashValue(entryPtr); elemObjPtr = Tcl_NewObj(); Tcl_GetCommandFullName(interp, cmd, elemObjPtr); } else { @@ -710,7 +772,7 @@ InfoCommandsCmd( if ((simplePattern == NULL) || Tcl_StringMatch(cmdName, simplePattern)) { if (specificNsInPattern) { - cmd = (Tcl_Command) Tcl_GetHashValue(entryPtr); + cmd = Tcl_GetHashValue(entryPtr); elemObjPtr = Tcl_NewObj(); Tcl_GetCommandFullName(interp, cmd, elemObjPtr); } else { @@ -769,7 +831,7 @@ InfoCommandsCmd( elemObjPtr = Tcl_NewStringObj(cmdName, -1); Tcl_ListObjAppendElement(interp, listPtr, elemObjPtr); (void) Tcl_CreateHashEntry(&addedCommandsTable, - (char *)elemObjPtr, &isNew); + elemObjPtr, &isNew); } entryPtr = Tcl_NextHashEntry(&search); } @@ -794,7 +856,7 @@ InfoCommandsCmd( || Tcl_StringMatch(cmdName, simplePattern)) { elemObjPtr = Tcl_NewStringObj(cmdName, -1); (void) Tcl_CreateHashEntry(&addedCommandsTable, - (char *) elemObjPtr, &isNew); + elemObjPtr, &isNew); if (isNew) { Tcl_ListObjAppendElement(interp, listPtr, elemObjPtr); } else { @@ -864,7 +926,7 @@ InfoCompleteCmd( ClientData dummy, /* Not used. */ Tcl_Interp *interp, /* Current interpreter. */ int objc, /* Number of arguments. */ - Tcl_Obj *CONST objv[]) /* Argument objects. */ + Tcl_Obj *const objv[]) /* Argument objects. */ { if (objc != 2) { Tcl_WrongNumArgs(interp, 1, objv, "command"); @@ -901,10 +963,10 @@ InfoDefaultCmd( ClientData dummy, /* Not used. */ Tcl_Interp *interp, /* Current interpreter. */ int objc, /* Number of arguments. */ - Tcl_Obj *CONST objv[]) /* Argument objects. */ + Tcl_Obj *const objv[]) /* Argument objects. */ { Interp *iPtr = (Interp *) interp; - char *procName, *argName, *varName; + const char *procName, *argName; Proc *procPtr; CompiledLocal *localPtr; Tcl_Obj *valueObjPtr; @@ -920,6 +982,8 @@ InfoDefaultCmd( procPtr = TclFindProc(iPtr, procName); if (procPtr == NULL) { Tcl_AppendResult(interp, "\"", procName, "\" isn't a procedure",NULL); + Tcl_SetErrorCode(interp, "TCL", "LOOKUP", "PROCEDURE", procName, + NULL); return TCL_ERROR; } @@ -929,17 +993,18 @@ InfoDefaultCmd( && (strcmp(argName, localPtr->name) == 0)) { if (localPtr->defValuePtr != NULL) { valueObjPtr = Tcl_ObjSetVar2(interp, objv[3], NULL, - localPtr->defValuePtr, 0); + localPtr->defValuePtr, TCL_LEAVE_ERR_MSG); if (valueObjPtr == NULL) { - goto defStoreError; + return TCL_ERROR; } Tcl_SetObjResult(interp, Tcl_NewIntObj(1)); } else { Tcl_Obj *nullObjPtr = Tcl_NewObj(); + valueObjPtr = Tcl_ObjSetVar2(interp, objv[3], NULL, - nullObjPtr, 0); + nullObjPtr, TCL_LEAVE_ERR_MSG); if (valueObjPtr == NULL) { - goto defStoreError; + return TCL_ERROR; } Tcl_SetObjResult(interp, Tcl_NewIntObj(0)); } @@ -949,13 +1014,57 @@ InfoDefaultCmd( Tcl_AppendResult(interp, "procedure \"", procName, "\" doesn't have an argument \"", argName, "\"", NULL); + Tcl_SetErrorCode(interp, "TCL", "LOOKUP", "ARGUMENT", argName, NULL); return TCL_ERROR; +} + +/* + *---------------------------------------------------------------------- + * + * InfoErrorStackCmd -- + * + * Called to implement the "info errorstack" command that returns information + * about the last error's call stack. Handles the following syntax: + * + * info errorstack ?interp? + * + * Results: + * Returns TCL_OK if successful and TCL_ERROR if there is an error. + * + * Side effects: + * Returns a result in the interpreter's result object. If there is an + * error, the result is an error message. + * + *---------------------------------------------------------------------- + */ - defStoreError: - varName = TclGetString(objv[3]); - Tcl_AppendResult(interp, "couldn't store default value in variable \"", - varName, "\"", NULL); - return TCL_ERROR; +static int +InfoErrorStackCmd( + ClientData dummy, /* Not used. */ + Tcl_Interp *interp, /* Current interpreter. */ + int objc, /* Number of arguments. */ + Tcl_Obj *const objv[]) /* Argument objects. */ +{ + Tcl_Interp *target; + Interp *iPtr; + + if ((objc != 1) && (objc != 2)) { + Tcl_WrongNumArgs(interp, 1, objv, "?interp?"); + return TCL_ERROR; + } + + target = interp; + if (objc == 2) { + target = Tcl_GetSlave(interp, Tcl_GetString(objv[1])); + if (target == NULL) { + return TCL_ERROR; + } + } + + iPtr = (Interp *) target; + Tcl_SetObjResult(interp, iPtr->errorStack); + + return TCL_OK; } /* @@ -983,9 +1092,9 @@ TclInfoExistsCmd( ClientData dummy, /* Not used. */ Tcl_Interp *interp, /* Current interpreter. */ int objc, /* Number of arguments. */ - Tcl_Obj *CONST objv[]) /* Argument objects. */ + Tcl_Obj *const objv[]) /* Argument objects. */ { - char *varName; + const char *varName; Var *varPtr; if (objc != 2) { @@ -1028,21 +1137,43 @@ InfoFrameCmd( ClientData dummy, /* Not used. */ Tcl_Interp *interp, /* Current interpreter. */ int objc, /* Number of arguments. */ - Tcl_Obj *CONST objv[]) /* Argument objects. */ + Tcl_Obj *const objv[]) /* Argument objects. */ { Interp *iPtr = (Interp *) interp; - int level; + int level, topLevel; CmdFrame *framePtr; + topLevel = ((iPtr->cmdFramePtr == NULL) + ? 0 + : iPtr->cmdFramePtr->level); + + + if (iPtr->execEnvPtr->corPtr) { + /* + * A coroutine: must fix the level computations AND the cmdFrame chain, + * which is interrupted at the base. + */ + + CoroutineData *corPtr = iPtr->execEnvPtr->corPtr; + CmdFrame *runPtr = iPtr->cmdFramePtr; + CmdFrame *lastPtr = NULL; + + topLevel += corPtr->caller.cmdFramePtr->level; + while (runPtr && (runPtr != corPtr->caller.cmdFramePtr)) { + lastPtr = runPtr; + runPtr = runPtr->nextPtr; + } + if (lastPtr && !runPtr) { + lastPtr->nextPtr = corPtr->caller.cmdFramePtr; + } + } + if (objc == 1) { /* * Just "info frame". */ - int levels = - (iPtr->cmdFramePtr == NULL ? 0 : iPtr->cmdFramePtr->level); - - Tcl_SetObjResult(interp, Tcl_NewIntObj (levels)); + Tcl_SetObjResult(interp, Tcl_NewIntObj(topLevel)); return TCL_OK; } else if (objc != 2) { Tcl_WrongNumArgs(interp, 1, objv, "?number?"); @@ -1056,35 +1187,31 @@ InfoFrameCmd( if (TclGetIntFromObj(interp, objv[1], &level) != TCL_OK) { return TCL_ERROR; } - if (level <= 0) { - /* - * Negative levels are adressing relative to the current frame's - * depth. - */ - if (iPtr->cmdFramePtr == NULL) { - levelError: - Tcl_AppendStringsToObj(Tcl_GetObjResult(interp), "bad level \"", - TclGetString(objv[1]), "\"", NULL); - return TCL_ERROR; - } + if ((level > topLevel) || (level <= - topLevel)) { + levelError: + Tcl_AppendResult(interp, "bad level \"", TclGetString(objv[1]), "\"", + NULL); + Tcl_SetErrorCode(interp, "TCL", "LOOKUP", "STACK_FRAME", + TclGetString(objv[1]), NULL); + return TCL_ERROR; + } - /* - * Convert to absolute. - */ + /* + * Let us convert to relative so that we know how many levels to go back + */ - level += iPtr->cmdFramePtr->level; + if (level > 0) { + level -= topLevel; } - for (framePtr = iPtr->cmdFramePtr; framePtr != NULL; - framePtr = framePtr->nextPtr) { - if (framePtr->level == level) { - break; + framePtr = iPtr->cmdFramePtr; + while (++level <= 0) { + framePtr = framePtr->nextPtr; + if (!framePtr) { + goto levelError; } } - if (framePtr == NULL) { - goto levelError; - } Tcl_SetObjResult(interp, TclInfoFrame(interp, framePtr)); return TCL_OK; @@ -1112,6 +1239,7 @@ TclInfoFrame( CmdFrame *framePtr) /* Frame to get info for. */ { Interp *iPtr = (Interp *) interp; + Tcl_Obj *tmpObj; Tcl_Obj *lv[20]; /* Keep uptodate when more keys are added to * the dict. */ int lc = 0; @@ -1119,14 +1247,12 @@ TclInfoFrame( * This array is indexed by the TCL_LOCATION_... values, except * for _LAST. */ - static CONST char *typeString[TCL_LOCATION_LAST] = { + static const char *const typeString[TCL_LOCATION_LAST] = { "eval", "eval", "eval", "precompiled", "source", "proc" }; - Tcl_Obj *tmpObj; - Proc *procPtr = - framePtr->framePtr ? framePtr->framePtr->procPtr : NULL; + Proc *procPtr = framePtr->framePtr ? framePtr->framePtr->procPtr : NULL; - /* + /* * Pull the information and construct the dictionary to return, as list. * Regarding use of the CmdFrame fields see tclInt.h, and its definition. */ @@ -1181,9 +1307,8 @@ TclInfoFrame( * Execution of bytecode. Talk to the BC engine to fill out the frame. */ - CmdFrame *fPtr; + CmdFrame *fPtr = TclStackAlloc(interp, sizeof(CmdFrame)); - fPtr = (CmdFrame *) TclStackAlloc(interp, sizeof(CmdFrame)); *fPtr = *framePtr; /* @@ -1252,19 +1377,16 @@ TclInfoFrame( Tcl_HashEntry *namePtr = procPtr->cmdPtr->hPtr; if (namePtr) { + Tcl_Obj *procNameObj; + /* * This is a regular command. */ - char *procName = Tcl_GetHashKey(namePtr->tablePtr, namePtr); - char *nsName = procPtr->cmdPtr->nsPtr->fullName; - - ADD_PAIR("proc", Tcl_NewStringObj(nsName, -1)); - - if (strcmp(nsName, "::") != 0) { - Tcl_AppendToObj(lv[lc-1], "::", -1); - } - Tcl_AppendToObj(lv[lc-1], procName, -1); + TclNewObj(procNameObj); + Tcl_GetCommandFullName(interp, (Tcl_Command) procPtr->cmdPtr, + procNameObj); + ADD_PAIR("proc", procNameObj); } else if (procPtr->cmdPtr->clientData) { ExtraFrameInfo *efiPtr = procPtr->cmdPtr->clientData; int i; @@ -1336,9 +1458,9 @@ InfoFunctionsCmd( ClientData dummy, /* Not used. */ Tcl_Interp *interp, /* Current interpreter. */ int objc, /* Number of arguments. */ - Tcl_Obj *CONST objv[]) /* Argument objects. */ + Tcl_Obj *const objv[]) /* Argument objects. */ { - char *pattern; + const char *pattern; if (objc == 1) { pattern = NULL; @@ -1378,9 +1500,9 @@ InfoHostnameCmd( ClientData dummy, /* Not used. */ Tcl_Interp *interp, /* Current interpreter. */ int objc, /* Number of arguments. */ - Tcl_Obj *CONST objv[]) /* Argument objects. */ + Tcl_Obj *const objv[]) /* Argument objects. */ { - CONST char *name; + const char *name; if (objc != 1) { Tcl_WrongNumArgs(interp, 1, objv, NULL); @@ -1393,6 +1515,7 @@ InfoHostnameCmd( return TCL_OK; } Tcl_SetResult(interp, "unable to determine name of host", TCL_STATIC); + Tcl_SetErrorCode(interp, "TCL", "OPERATION", "HOSTNAME", "UNKNOWN", NULL); return TCL_ERROR; } @@ -1421,7 +1544,7 @@ InfoLevelCmd( ClientData dummy, /* Not used. */ Tcl_Interp *interp, /* Current interpreter. */ int objc, /* Number of arguments. */ - Tcl_Obj *CONST objv[]) /* Argument objects. */ + Tcl_Obj *const objv[]) /* Argument objects. */ { Interp *iPtr = (Interp *) interp; @@ -1464,6 +1587,8 @@ InfoLevelCmd( levelError: Tcl_AppendResult(interp, "bad level \"", TclGetString(objv[1]), "\"", NULL); + Tcl_SetErrorCode(interp, "TCL", "LOOKUP", "STACK_LEVEL", + TclGetString(objv[1]), NULL); return TCL_ERROR; } @@ -1493,9 +1618,9 @@ InfoLibraryCmd( ClientData dummy, /* Not used. */ Tcl_Interp *interp, /* Current interpreter. */ int objc, /* Number of arguments. */ - Tcl_Obj *CONST objv[]) /* Argument objects. */ + Tcl_Obj *const objv[]) /* Argument objects. */ { - CONST char *libDirName; + const char *libDirName; if (objc != 1) { Tcl_WrongNumArgs(interp, 1, objv, NULL); @@ -1508,6 +1633,7 @@ InfoLibraryCmd( return TCL_OK; } Tcl_SetResult(interp, "no library has been specified for Tcl",TCL_STATIC); + Tcl_SetErrorCode(interp, "TCL", "LOOKUP", "VARIABLE", "tcl_library",NULL); return TCL_ERROR; } @@ -1537,9 +1663,9 @@ InfoLoadedCmd( ClientData dummy, /* Not used. */ Tcl_Interp *interp, /* Current interpreter. */ int objc, /* Number of arguments. */ - Tcl_Obj *CONST objv[]) /* Argument objects. */ + Tcl_Obj *const objv[]) /* Argument objects. */ { - char *interpName; + const char *interpName; if ((objc != 1) && (objc != 2)) { Tcl_WrongNumArgs(interp, 1, objv, "?interp?"); @@ -1580,7 +1706,7 @@ InfoNameOfExecutableCmd( ClientData dummy, /* Not used. */ Tcl_Interp *interp, /* Current interpreter. */ int objc, /* Number of arguments. */ - Tcl_Obj *CONST objv[]) /* Argument objects. */ + Tcl_Obj *const objv[]) /* Argument objects. */ { if (objc != 1) { Tcl_WrongNumArgs(interp, 1, objv, NULL); @@ -1616,9 +1742,9 @@ InfoPatchLevelCmd( ClientData dummy, /* Not used. */ Tcl_Interp *interp, /* Current interpreter. */ int objc, /* Number of arguments. */ - Tcl_Obj *CONST objv[]) /* Argument objects. */ + Tcl_Obj *const objv[]) /* Argument objects. */ { - CONST char *patchlevel; + const char *patchlevel; if (objc != 1) { Tcl_WrongNumArgs(interp, 1, objv, NULL); @@ -1663,10 +1789,10 @@ InfoProcsCmd( ClientData dummy, /* Not used. */ Tcl_Interp *interp, /* Current interpreter. */ int objc, /* Number of arguments. */ - Tcl_Obj *CONST objv[]) /* Argument objects. */ + Tcl_Obj *const objv[]) /* Argument objects. */ { - char *cmdName, *pattern; - CONST char *simplePattern; + const char *cmdName, *pattern; + const char *simplePattern; Namespace *nsPtr; #ifdef INFO_PROCS_SEARCH_GLOBAL_NS Namespace *globalNsPtr = (Namespace *) Tcl_GetGlobalNamespace(interp); @@ -1699,9 +1825,8 @@ InfoProcsCmd( Namespace *dummy1NsPtr, *dummy2NsPtr; pattern = TclGetString(objv[1]); - TclGetNamespaceForQualName(interp, pattern, (Namespace *) NULL, - /*flags*/ 0, &nsPtr, &dummy1NsPtr, &dummy2NsPtr, - &simplePattern); + TclGetNamespaceForQualName(interp, pattern, NULL, /*flags*/ 0, &nsPtr, + &dummy1NsPtr, &dummy2NsPtr, &simplePattern); if (nsPtr != NULL) { /* We successfully found the pattern's ns. */ specificNsInPattern = (strcmp(simplePattern, pattern) != 0); @@ -1727,7 +1852,7 @@ InfoProcsCmd( if (simplePattern != NULL && TclMatchIsTrivial(simplePattern)) { entryPtr = Tcl_FindHashEntry(&nsPtr->cmdTable, simplePattern); if (entryPtr != NULL) { - cmdPtr = (Command *) Tcl_GetHashValue(entryPtr); + cmdPtr = Tcl_GetHashValue(entryPtr); if (!TclIsProc(cmdPtr)) { realCmdPtr = (Command *) @@ -1755,7 +1880,7 @@ InfoProcsCmd( cmdName = Tcl_GetHashKey(&nsPtr->cmdTable, entryPtr); if ((simplePattern == NULL) || Tcl_StringMatch(cmdName, simplePattern)) { - cmdPtr = (Command *) Tcl_GetHashValue(entryPtr); + cmdPtr = Tcl_GetHashValue(entryPtr); if (!TclIsProc(cmdPtr)) { realCmdPtr = (Command *) @@ -1802,7 +1927,7 @@ InfoProcsCmd( if ((simplePattern == NULL) || Tcl_StringMatch(cmdName, simplePattern)) { if (Tcl_FindHashEntry(&nsPtr->cmdTable,cmdName) == NULL) { - cmdPtr = (Command *) Tcl_GetHashValue(entryPtr); + cmdPtr = Tcl_GetHashValue(entryPtr); realCmdPtr = (Command *) TclGetOriginalCommand( (Tcl_Command) cmdPtr); @@ -1851,7 +1976,7 @@ InfoScriptCmd( ClientData dummy, /* Not used. */ Tcl_Interp *interp, /* Current interpreter. */ int objc, /* Number of arguments. */ - Tcl_Obj *CONST objv[]) /* Argument objects. */ + Tcl_Obj *const objv[]) /* Argument objects. */ { Interp *iPtr = (Interp *) interp; if ((objc != 1) && (objc != 2)) { @@ -1898,7 +2023,7 @@ InfoSharedlibCmd( ClientData dummy, /* Not used. */ Tcl_Interp *interp, /* Current interpreter. */ int objc, /* Number of arguments. */ - Tcl_Obj *CONST objv[]) /* Argument objects. */ + Tcl_Obj *const objv[]) /* Argument objects. */ { if (objc != 1) { Tcl_WrongNumArgs(interp, 1, objv, NULL); @@ -1936,7 +2061,7 @@ InfoTclVersionCmd( ClientData dummy, /* Not used. */ Tcl_Interp *interp, /* Current interpreter. */ int objc, /* Number of arguments. */ - Tcl_Obj *CONST objv[]) /* Argument objects. */ + Tcl_Obj *const objv[]) /* Argument objects. */ { Tcl_Obj *version; @@ -1976,7 +2101,7 @@ Tcl_JoinObjCmd( ClientData dummy, /* Not used. */ Tcl_Interp *interp, /* Current interpreter. */ int objc, /* Number of arguments. */ - Tcl_Obj *CONST objv[]) /* The argument objects. */ + Tcl_Obj *const objv[]) /* The argument objects. */ { int listLen, i; Tcl_Obj *resObjPtr, *joinObjPtr, **elemPtrs; @@ -2033,15 +2158,15 @@ Tcl_LassignObjCmd( ClientData dummy, /* Not used. */ Tcl_Interp *interp, /* Current interpreter. */ int objc, /* Number of arguments. */ - Tcl_Obj *CONST objv[]) /* Argument objects. */ + Tcl_Obj *const objv[]) /* Argument objects. */ { Tcl_Obj *listCopyPtr; Tcl_Obj **listObjv; /* The contents of the list. */ int listObjc; /* The length of the list. */ int code = TCL_OK; - if (objc < 3) { - Tcl_WrongNumArgs(interp, 1, objv, "list varName ?varName ...?"); + if (objc < 2) { + Tcl_WrongNumArgs(interp, 1, objv, "list ?varName ...?"); return TCL_ERROR; } @@ -2055,20 +2180,22 @@ Tcl_LassignObjCmd( objc -= 2; objv += 2; while (code == TCL_OK && objc > 0 && listObjc > 0) { - if (NULL == Tcl_ObjSetVar2(interp, *objv++, NULL, - *listObjv++, TCL_LEAVE_ERR_MSG)) { + if (Tcl_ObjSetVar2(interp, *objv++, NULL, *listObjv++, + TCL_LEAVE_ERR_MSG) == NULL) { code = TCL_ERROR; } - objc--; listObjc--; + objc--; + listObjc--; } if (code == TCL_OK && objc > 0) { Tcl_Obj *emptyObj; + TclNewObj(emptyObj); Tcl_IncrRefCount(emptyObj); while (code == TCL_OK && objc-- > 0) { - if (NULL == Tcl_ObjSetVar2(interp, *objv++, NULL, - emptyObj, TCL_LEAVE_ERR_MSG)) { + if (Tcl_ObjSetVar2(interp, *objv++, NULL, emptyObj, + TCL_LEAVE_ERR_MSG) == NULL) { code = TCL_ERROR; } } @@ -2105,13 +2232,13 @@ Tcl_LindexObjCmd( ClientData dummy, /* Not used. */ Tcl_Interp *interp, /* Current interpreter. */ int objc, /* Number of arguments. */ - Tcl_Obj *CONST objv[]) /* Argument objects. */ + Tcl_Obj *const objv[]) /* Argument objects. */ { Tcl_Obj *elemPtr; /* Pointer to the element being extracted. */ if (objc < 2) { - Tcl_WrongNumArgs(interp, 1, objv, "list ?index...?"); + Tcl_WrongNumArgs(interp, 1, objv, "list ?index ...?"); return TCL_ERROR; } @@ -2134,11 +2261,11 @@ Tcl_LindexObjCmd( if (elemPtr == NULL) { return TCL_ERROR; - } else { - Tcl_SetObjResult(interp, elemPtr); - Tcl_DecrRefCount(elemPtr); - return TCL_OK; } + + Tcl_SetObjResult(interp, elemPtr); + Tcl_DecrRefCount(elemPtr); + return TCL_OK; } /* @@ -2164,13 +2291,13 @@ Tcl_LinsertObjCmd( ClientData dummy, /* Not used. */ Tcl_Interp *interp, /* Current interpreter. */ register int objc, /* Number of arguments. */ - Tcl_Obj *CONST objv[]) /* Argument objects. */ + Tcl_Obj *const objv[]) /* Argument objects. */ { Tcl_Obj *listPtr; int index, len, result; - if (objc < 4) { - Tcl_WrongNumArgs(interp, 1, objv, "list index element ?element ...?"); + if (objc < 3) { + Tcl_WrongNumArgs(interp, 1, objv, "list index ?element ...?"); return TCL_ERROR; } @@ -2243,7 +2370,7 @@ Tcl_ListObjCmd( ClientData dummy, /* Not used. */ Tcl_Interp *interp, /* Current interpreter. */ register int objc, /* Number of arguments. */ - register Tcl_Obj *CONST objv[]) + register Tcl_Obj *const objv[]) /* The argument objects. */ { /* @@ -2252,7 +2379,7 @@ Tcl_ListObjCmd( */ if (objc > 1) { - Tcl_SetObjResult(interp, Tcl_NewListObj((objc-1), &(objv[1]))); + Tcl_SetObjResult(interp, Tcl_NewListObj(objc-1, &objv[1])); } return TCL_OK; } @@ -2279,7 +2406,7 @@ Tcl_LlengthObjCmd( ClientData dummy, /* Not used. */ Tcl_Interp *interp, /* Current interpreter. */ int objc, /* Number of arguments. */ - register Tcl_Obj *CONST objv[]) + register Tcl_Obj *const objv[]) /* Argument objects. */ { int listLen, result; @@ -2325,55 +2452,77 @@ Tcl_LrangeObjCmd( ClientData notUsed, /* Not used. */ Tcl_Interp *interp, /* Current interpreter. */ int objc, /* Number of arguments. */ - register Tcl_Obj *CONST objv[]) + register Tcl_Obj *const objv[]) /* Argument objects. */ { - Tcl_Obj *listPtr, **elemPtrs; - int listLen, first, result; + Tcl_Obj **elemPtrs; + int listLen, first, last, result; if (objc != 4) { Tcl_WrongNumArgs(interp, 1, objv, "list first last"); return TCL_ERROR; } - /* - * Make sure the list argument is a list object and get its length and a - * pointer to its array of element pointers. - */ - - listPtr = TclListObjCopy(interp, objv[1]); - if (listPtr == NULL) { - return TCL_ERROR; + result = TclListObjLength(interp, objv[1], &listLen); + if (result != TCL_OK) { + return result; } - TclListObjGetElements(NULL, listPtr, &listLen, &elemPtrs); result = TclGetIntForIndexM(interp, objv[2], /*endValue*/ listLen - 1, &first); - if (result == TCL_OK) { - int last; + if (result != TCL_OK) { + return result; + } + if (first < 0) { + first = 0; + } - if (first < 0) { - first = 0; - } + result = TclGetIntForIndexM(interp, objv[3], /*endValue*/ listLen - 1, + &last); + if (result != TCL_OK) { + return result; + } + if (last >= listLen) { + last = listLen - 1; + } - result = TclGetIntForIndexM(interp, objv[3], /*endValue*/ listLen - 1, - &last); - if (result == TCL_OK) { - if (last >= listLen) { - last = (listLen - 1); - } + if (first > last) { + /* + * Returning an empty list is easy. + */ - if (first <= last) { - int numElems = (last - first + 1); + return TCL_OK; + } - Tcl_SetObjResult(interp, - Tcl_NewListObj(numElems, &(elemPtrs[first]))); - } + result = TclListObjGetElements(interp, objv[1], &listLen, &elemPtrs); + if (result != TCL_OK) { + return result; + } + + if (Tcl_IsShared(objv[1]) || + ((ListRepPtr(objv[1])->refCount > 1))) { + Tcl_SetObjResult(interp, Tcl_NewListObj(last - first + 1, + &elemPtrs[first])); + } else { + /* + * In-place is possible. + */ + + if (last < (listLen - 1)) { + Tcl_ListObjReplace(interp, objv[1], last + 1, listLen - 1 - last, + 0, NULL); } + + /* + * This one is not conditioned on (first > 0) in order to preserve the + * string-canonizing effect of [lrange 0 end]. + */ + + Tcl_ListObjReplace(interp, objv[1], 0, first, 0, NULL); + Tcl_SetObjResult(interp, objv[1]); } - Tcl_DecrRefCount(listPtr); - return result; + return TCL_OK; } /* @@ -2398,27 +2547,28 @@ Tcl_LrepeatObjCmd( ClientData dummy, /* Not used. */ Tcl_Interp *interp, /* Current interpreter. */ register int objc, /* Number of arguments. */ - register Tcl_Obj *CONST objv[]) + register Tcl_Obj *const objv[]) /* The argument objects. */ { int elementCount, i, totalElems; - Tcl_Obj *listPtr, **dataArray; - List *listRepPtr; + Tcl_Obj *listPtr, **dataArray = NULL; /* * Check arguments for legality: - * lrepeat posInt value ?value ...? + * lrepeat count ?value ...? */ - if (objc < 3) { - Tcl_WrongNumArgs(interp, 1, objv, "positiveCount value ?value ...?"); + if (objc < 2) { + Tcl_WrongNumArgs(interp, 1, objv, "count ?value ...?"); return TCL_ERROR; } - if (TCL_ERROR == TclGetIntFromObj(interp, objv[1], &elementCount)) { + if (TCL_OK != TclGetIntFromObj(interp, objv[1], &elementCount)) { return TCL_ERROR; } - if (elementCount < 1) { - Tcl_AppendResult(interp, "must have a count of at least 1", NULL); + if (elementCount < 0) { + Tcl_SetObjResult(interp, Tcl_Format(NULL, + "bad count \"%d\": must be integer >= 0", 1, objv+1)); + Tcl_SetErrorCode(interp, "TCL","OPERATION","LREPEAT","NEGARG", NULL); return TCL_ERROR; } @@ -2436,12 +2586,15 @@ Tcl_LrepeatObjCmd( */ totalElems = objc * elementCount; - if (totalElems/objc != elementCount || totalElems/elementCount != objc) { + if (totalElems != 0 && (totalElems/objc != elementCount + || totalElems/elementCount != objc)) { Tcl_AppendResult(interp, "too many elements in result list", NULL); + Tcl_SetErrorCode(interp, "TCL", "MEMORY", NULL); return TCL_ERROR; } if (totalElems >= 0x20000000) { Tcl_AppendResult(interp, "too many elements in result list", NULL); + Tcl_SetErrorCode(interp, "TCL", "MEMORY", NULL); return TCL_ERROR; } @@ -2451,9 +2604,12 @@ Tcl_LrepeatObjCmd( */ listPtr = Tcl_NewListObj(totalElems, NULL); - listRepPtr = ListRepPtr(listPtr); - listRepPtr->elemCount = elementCount*objc; - dataArray = &listRepPtr->elements; + if (totalElems) { + List *listRepPtr = ListRepPtr(listPtr); + + listRepPtr->elemCount = elementCount*objc; + dataArray = &listRepPtr->elements; + } /* * Set the elements. Note that we handle the common degenerate case of a @@ -2462,6 +2618,7 @@ Tcl_LrepeatObjCmd( * number of times. */ + CLANG_ASSERT(dataArray); if (objc == 1) { register Tcl_Obj *tmpPtr = objv[0]; @@ -2507,14 +2664,14 @@ Tcl_LreplaceObjCmd( ClientData dummy, /* Not used. */ Tcl_Interp *interp, /* Current interpreter. */ int objc, /* Number of arguments. */ - Tcl_Obj *CONST objv[]) /* Argument objects. */ + Tcl_Obj *const objv[]) /* Argument objects. */ { register Tcl_Obj *listPtr; int first, last, listLen, numToDelete, result; if (objc < 4) { Tcl_WrongNumArgs(interp, 1, objv, - "list first last ?element element ...?"); + "list first last ?element ...?"); return TCL_ERROR; } @@ -2540,7 +2697,7 @@ Tcl_LreplaceObjCmd( } if (first < 0) { - first = 0; + first = 0; } /* @@ -2553,13 +2710,14 @@ Tcl_LreplaceObjCmd( if ((first >= listLen) && (listLen > 0)) { Tcl_AppendResult(interp, "list doesn't contain element ", TclGetString(objv[2]), NULL); + Tcl_SetErrorCode(interp, "TCL","OPERATION","LREPLACE","BADIDX", NULL); return TCL_ERROR; } if (last >= listLen) { - last = (listLen - 1); + last = listLen - 1; } if (first <= last) { - numToDelete = (last - first + 1); + numToDelete = last - first + 1; } else { numToDelete = 0; } @@ -2582,7 +2740,7 @@ Tcl_LreplaceObjCmd( * optimize this case away. */ - Tcl_ListObjReplace(NULL, listPtr, first, numToDelete, objc-4, &(objv[4])); + Tcl_ListObjReplace(NULL, listPtr, first, numToDelete, objc-4, objv+4); /* * Set the interpreter's object result. @@ -2614,7 +2772,7 @@ Tcl_LreverseObjCmd( ClientData clientData, /* Not used. */ Tcl_Interp *interp, /* Current interpreter. */ int objc, /* Number of arguments. */ - Tcl_Obj *CONST objv[]) /* Argument values. */ + Tcl_Obj *const objv[]) /* Argument values. */ { Tcl_Obj **elemv; int elemc, i, j; @@ -2628,7 +2786,7 @@ Tcl_LreverseObjCmd( } /* - * If the list is empty, just return it [Bug 1876793] + * If the list is empty, just return it. [Bug 1876793] */ if (!elemc) { @@ -2693,10 +2851,10 @@ Tcl_LsearchObjCmd( ClientData clientData, /* Not used. */ Tcl_Interp *interp, /* Current interpreter. */ int objc, /* Number of arguments. */ - Tcl_Obj *CONST objv[]) /* Argument values. */ + Tcl_Obj *const objv[]) /* Argument values. */ { - char *bytes, *patternBytes; - int i, match, mode, index, result, listc, length, elemLen; + const char *bytes, *patternBytes; + int i, match, index, result, listc, length, elemLen, bisect; int dataType, isIncreasing, lower, upper, patInt, objInt, offset; int allMatches, inlineReturn, negatedMatch, returnSubindices, noCase; double patDouble, objDouble; @@ -2704,19 +2862,19 @@ Tcl_LsearchObjCmd( Tcl_Obj *patObj, **listv, *listPtr, *startPtr, *itemPtr; SortStrCmpFn_t strCmpFn = strcmp; Tcl_RegExp regexp = NULL; - static CONST char *options[] = { - "-all", "-ascii", "-decreasing", "-dictionary", + static const char *const options[] = { + "-all", "-ascii", "-bisect", "-decreasing", "-dictionary", "-exact", "-glob", "-increasing", "-index", "-inline", "-integer", "-nocase", "-not", "-real", "-regexp", "-sorted", "-start", "-subindices", NULL }; enum options { - LSEARCH_ALL, LSEARCH_ASCII, LSEARCH_DECREASING, LSEARCH_DICTIONARY, - LSEARCH_EXACT, LSEARCH_GLOB, LSEARCH_INCREASING, LSEARCH_INDEX, - LSEARCH_INLINE, LSEARCH_INTEGER, LSEARCH_NOCASE, LSEARCH_NOT, - LSEARCH_REAL, LSEARCH_REGEXP, LSEARCH_SORTED, LSEARCH_START, - LSEARCH_SUBINDICES + LSEARCH_ALL, LSEARCH_ASCII, LSEARCH_BISECT, LSEARCH_DECREASING, + LSEARCH_DICTIONARY, LSEARCH_EXACT, LSEARCH_GLOB, LSEARCH_INCREASING, + LSEARCH_INDEX, LSEARCH_INLINE, LSEARCH_INTEGER, LSEARCH_NOCASE, + LSEARCH_NOT, LSEARCH_REAL, LSEARCH_REGEXP, LSEARCH_SORTED, + LSEARCH_START, LSEARCH_SUBINDICES }; enum datatypes { ASCII, DICTIONARY, INTEGER, REAL @@ -2724,6 +2882,7 @@ Tcl_LsearchObjCmd( enum modes { EXACT, GLOB, REGEXP, SORTED }; + enum modes mode; mode = GLOB; dataType = ASCII; @@ -2732,6 +2891,7 @@ Tcl_LsearchObjCmd( inlineReturn = 0; returnSubindices = 0; negatedMatch = 0; + bisect = 0; listPtr = NULL; startPtr = NULL; offset = 0; @@ -2745,7 +2905,7 @@ Tcl_LsearchObjCmd( sortInfo.indexc = 0; if (objc < 3) { - Tcl_WrongNumArgs(interp, 1, objv, "?options? list pattern"); + Tcl_WrongNumArgs(interp, 1, objv, "?-option value ...? list pattern"); return TCL_ERROR; } @@ -2755,10 +2915,8 @@ Tcl_LsearchObjCmd( if (startPtr != NULL) { Tcl_DecrRefCount(startPtr); } - if (sortInfo.indexc > 1) { - ckfree((char *) sortInfo.indexv); - } - return TCL_ERROR; + result = TCL_ERROR; + goto done; } switch ((enum options) index) { case LSEARCH_ALL: /* -all */ @@ -2767,6 +2925,10 @@ Tcl_LsearchObjCmd( case LSEARCH_ASCII: /* -ascii */ dataType = ASCII; break; + case LSEARCH_BISECT: /* -bisect */ + mode = SORTED; + bisect = 1; + break; case LSEARCH_DECREASING: /* -decreasing */ isIncreasing = 0; sortInfo.isIncreasing = 0; @@ -2819,11 +2981,10 @@ Tcl_LsearchObjCmd( Tcl_DecrRefCount(startPtr); } if (i > objc-4) { - if (sortInfo.indexc > 1) { - ckfree((char *) sortInfo.indexv); - } Tcl_AppendResult(interp, "missing starting index", NULL); - return TCL_ERROR; + Tcl_SetErrorCode(interp, "TCL", "ARGUMENT", "MISSING", NULL); + result = TCL_ERROR; + goto done; } i++; if (objv[i] == objv[objc - 2]) { @@ -2845,7 +3006,7 @@ Tcl_LsearchObjCmd( int j; if (sortInfo.indexc > 1) { - ckfree((char *) sortInfo.indexv); + TclStackFree(interp, sortInfo.indexv); } if (i > objc-4) { if (startPtr != NULL) { @@ -2854,6 +3015,7 @@ Tcl_LsearchObjCmd( Tcl_AppendResult(interp, "\"-index\" option must be followed by list index", NULL); + Tcl_SetErrorCode(interp, "TCL", "ARGUMENT", "MISSING", NULL); return TCL_ERROR; } @@ -2879,8 +3041,8 @@ Tcl_LsearchObjCmd( sortInfo.indexv = &sortInfo.singleIndex; break; default: - sortInfo.indexv = (int *) - ckalloc(sizeof(int) * sortInfo.indexc); + sortInfo.indexv = + TclStackAlloc(interp, sizeof(int) * sortInfo.indexc); } /* @@ -2892,12 +3054,10 @@ Tcl_LsearchObjCmd( for (j=0 ; j<sortInfo.indexc ; j++) { if (TclGetIntForIndexM(interp, indices[j], SORTIDX_END, &sortInfo.indexv[j]) != TCL_OK) { - if (sortInfo.indexc > 1) { - ckfree((char *) sortInfo.indexv); - } Tcl_AppendObjToErrorInfo(interp, Tcl_ObjPrintf( "\n (-index option item number %d)", j)); - return TCL_ERROR; + result = TCL_ERROR; + goto done; } } break; @@ -2915,10 +3075,20 @@ Tcl_LsearchObjCmd( } Tcl_AppendResult(interp, "-subindices cannot be used without -index option", NULL); + Tcl_SetErrorCode(interp, "TCL", "OPERATION", "LSEARCH", + "BAD_OPTION_MIX", NULL); return TCL_ERROR; } - if ((enum modes) mode == REGEXP) { + if (bisect && (allMatches || negatedMatch)) { + Tcl_AppendResult(interp, + "-bisect is not compatible with -all or -not", NULL); + Tcl_SetErrorCode(interp, "TCL", "OPERATION", "LSEARCH", + "BAD_OPTION_MIX", NULL); + return TCL_ERROR; + } + + if (mode == REGEXP) { /* * We can shimmer regexp/list if listv[i] == pattern, so get the * regexp rep before the list rep. First time round, omit the interp @@ -2944,10 +3114,8 @@ Tcl_LsearchObjCmd( if (startPtr != NULL) { Tcl_DecrRefCount(startPtr); } - if (sortInfo.indexc > 1) { - ckfree((char *) sortInfo.indexv); - } - return TCL_ERROR; + result = TCL_ERROR; + goto done; } } @@ -2961,10 +3129,7 @@ Tcl_LsearchObjCmd( if (startPtr != NULL) { Tcl_DecrRefCount(startPtr); } - if (sortInfo.indexc > 1) { - ckfree((char *) sortInfo.indexv); - } - return result; + goto done; } /* @@ -2975,10 +3140,7 @@ Tcl_LsearchObjCmd( result = TclGetIntForIndexM(interp, startPtr, listc-1, &offset); Tcl_DecrRefCount(startPtr); if (result != TCL_OK) { - if (sortInfo.indexc > 1) { - ckfree((char *) sortInfo.indexv); - } - return result; + goto done; } if (offset < 0) { offset = 0; @@ -2991,7 +3153,7 @@ Tcl_LsearchObjCmd( if (offset > listc-1) { if (sortInfo.indexc > 1) { - ckfree((char *) sortInfo.indexv); + TclStackFree(interp, sortInfo.indexv); } if (allMatches || inlineReturn) { Tcl_ResetResult(interp); @@ -3004,7 +3166,7 @@ Tcl_LsearchObjCmd( patObj = objv[objc - 1]; patternBytes = NULL; - if ((enum modes) mode == EXACT || (enum modes) mode == SORTED) { + if (mode == EXACT || mode == SORTED) { switch ((enum datatypes) dataType) { case ASCII: case DICTIONARY: @@ -3013,10 +3175,7 @@ Tcl_LsearchObjCmd( case INTEGER: result = TclGetIntFromObj(interp, patObj, &patInt); if (result != TCL_OK) { - if (sortInfo.indexc > 1) { - ckfree((char *) sortInfo.indexv); - } - return result; + goto done; } /* @@ -3029,10 +3188,7 @@ Tcl_LsearchObjCmd( case REAL: result = Tcl_GetDoubleFromObj(interp, patObj, &patDouble); if (result != TCL_OK) { - if (sortInfo.indexc > 1) { - ckfree((char *) sortInfo.indexv); - } - return result; + goto done; } /* @@ -3055,7 +3211,7 @@ Tcl_LsearchObjCmd( index = -1; match = 0; - if ((enum modes) mode == SORTED && !allMatches && !negatedMatch) { + if (mode == SORTED && !allMatches && !negatedMatch) { /* * If the data is sorted, we can do a more intelligent search. Note * that there is no point in being smart when -all was specified; in @@ -3070,10 +3226,8 @@ Tcl_LsearchObjCmd( if (sortInfo.indexc != 0) { itemPtr = SelectObjFromSublist(listv[i], &sortInfo); if (sortInfo.resultCode != TCL_OK) { - if (sortInfo.indexc > 1) { - ckfree((char *) sortInfo.indexv); - } - return sortInfo.resultCode; + result = sortInfo.resultCode; + goto done; } } else { itemPtr = listv[i]; @@ -3090,10 +3244,7 @@ Tcl_LsearchObjCmd( case INTEGER: result = TclGetIntFromObj(interp, itemPtr, &objInt); if (result != TCL_OK) { - if (sortInfo.indexc > 1) { - ckfree((char *) sortInfo.indexv); - } - return result; + goto done; } if (patInt == objInt) { match = 0; @@ -3106,10 +3257,7 @@ Tcl_LsearchObjCmd( case REAL: result = Tcl_GetDoubleFromObj(interp, itemPtr, &objDouble); if (result != TCL_OK) { - if (sortInfo.indexc > 1) { - ckfree((char *) sortInfo.indexv); - } - return result; + goto done; } if (patDouble == objDouble) { match = 0; @@ -3133,10 +3281,16 @@ Tcl_LsearchObjCmd( * variation means that a search always makes log n * comparisons (normal binary search might "get lucky" with an * early comparison). + * + * In bisect mode though, we want the last of equals. */ index = i; - upper = i; + if (bisect) { + lower = i; + } else { + upper = i; + } } else if (match > 0) { if (isIncreasing) { lower = i; @@ -3151,7 +3305,9 @@ Tcl_LsearchObjCmd( } } } - + if (bisect && index < 0) { + index = lower; + } } else { /* * We need to do a linear search, because (at least one) of: @@ -3165,22 +3321,20 @@ Tcl_LsearchObjCmd( } for (i = offset; i < listc; i++) { match = 0; - if (sortInfo.indexc != 0) { + if (sortInfo.indexc != 0) { itemPtr = SelectObjFromSublist(listv[i], &sortInfo); if (sortInfo.resultCode != TCL_OK) { if (listPtr != NULL) { Tcl_DecrRefCount(listPtr); } - if (sortInfo.indexc > 1) { - ckfree((char *) sortInfo.indexv); - } - return sortInfo.resultCode; + result = sortInfo.resultCode; + goto done; } } else { itemPtr = listv[i]; } - - switch ((enum modes) mode) { + + switch (mode) { case SORTED: case EXACT: switch ((enum datatypes) dataType) { @@ -3212,10 +3366,7 @@ Tcl_LsearchObjCmd( if (listPtr != NULL) { Tcl_DecrRefCount(listPtr); } - if (sortInfo.indexc > 1) { - ckfree((char *) sortInfo.indexv); - } - return result; + goto done; } match = (objInt == patInt); break; @@ -3226,10 +3377,7 @@ Tcl_LsearchObjCmd( if (listPtr) { Tcl_DecrRefCount(listPtr); } - if (sortInfo.indexc > 1) { - ckfree((char *) sortInfo.indexv); - } - return result; + goto done; } match = (objDouble == patDouble); break; @@ -3248,10 +3396,8 @@ Tcl_LsearchObjCmd( if (listPtr != NULL) { Tcl_DecrRefCount(listPtr); } - if (sortInfo.indexc > 1) { - ckfree((char *) sortInfo.indexv); - } - return TCL_ERROR; + result = TCL_ERROR; + goto done; } break; } @@ -3324,15 +3470,17 @@ Tcl_LsearchObjCmd( } else { Tcl_SetObjResult(interp, listv[index]); } + result = TCL_OK; /* * Cleanup the index list array. */ + done: if (sortInfo.indexc > 1) { - ckfree((char *) sortInfo.indexv); + TclStackFree(interp, sortInfo.indexv); } - return TCL_OK; + return result; } /* @@ -3357,7 +3505,7 @@ Tcl_LsetObjCmd( ClientData clientData, /* Not used. */ Tcl_Interp *interp, /* Current interpreter. */ int objc, /* Number of arguments. */ - Tcl_Obj *CONST objv[]) /* Argument values. */ + Tcl_Obj *const objv[]) /* Argument values. */ { Tcl_Obj *listPtr; /* Pointer to the list being altered. */ Tcl_Obj *finalValuePtr; /* Value finally assigned to the variable. */ @@ -3367,7 +3515,8 @@ Tcl_LsetObjCmd( */ if (objc < 3) { - Tcl_WrongNumArgs(interp, 1, objv, "listVar ?index? ?index...? value"); + Tcl_WrongNumArgs(interp, 1, objv, + "listVar ?index? ?index ...? value"); return TCL_ERROR; } @@ -3375,8 +3524,7 @@ Tcl_LsetObjCmd( * Look up the list variable's value. */ - listPtr = Tcl_ObjGetVar2(interp, objv[1], (Tcl_Obj *) NULL, - TCL_LEAVE_ERR_MSG); + listPtr = Tcl_ObjGetVar2(interp, objv[1], NULL, TCL_LEAVE_ERR_MSG); if (listPtr == NULL) { return TCL_ERROR; } @@ -3442,32 +3590,33 @@ Tcl_LsortObjCmd( ClientData clientData, /* Not used. */ Tcl_Interp *interp, /* Current interpreter. */ int objc, /* Number of arguments. */ - Tcl_Obj *CONST objv[]) /* Argument values. */ + Tcl_Obj *const objv[]) /* Argument values. */ { int i, j, index, indices, length, nocase = 0, sortMode, indexc; + int group, groupSize, groupOffset, idx, allocatedIndexVector = 0; Tcl_Obj *resultPtr, *cmdPtr, **listObjPtrs, *listObj, *indexPtr; SortElement *elementArray, *elementPtr; SortInfo sortInfo; /* Information about this sort that needs to * be passed to the comparison function. */ - static CONST char *switches[] = { +# define NUM_LISTS 30 + SortElement *subList[NUM_LISTS+1]; + /* This array holds pointers to temporary + * lists built during the merge sort. Element + * i of the array holds a list of length + * 2**i. */ + static const char *const switches[] = { "-ascii", "-command", "-decreasing", "-dictionary", "-increasing", - "-index", "-indices", "-integer", "-nocase", "-real", "-unique", NULL + "-index", "-indices", "-integer", "-nocase", "-real", "-stride", + "-unique", NULL }; enum Lsort_Switches { LSORT_ASCII, LSORT_COMMAND, LSORT_DECREASING, LSORT_DICTIONARY, LSORT_INCREASING, LSORT_INDEX, LSORT_INDICES, LSORT_INTEGER, - LSORT_NOCASE, LSORT_REAL, LSORT_UNIQUE + LSORT_NOCASE, LSORT_REAL, LSORT_STRIDE, LSORT_UNIQUE }; - /* - * The subList array below holds pointers to temporary lists built during - * the merge sort. Element i of the array holds a list of length 2**i. - */ -# define NUM_LISTS 30 - SortElement *subList[NUM_LISTS+1]; - if (objc < 2) { - Tcl_WrongNumArgs(interp, 1, objv, "?options? list"); + Tcl_WrongNumArgs(interp, 1, objv, "?-option value ...? list"); return TCL_ERROR; } @@ -3481,30 +3630,31 @@ Tcl_LsortObjCmd( sortInfo.indexc = 0; sortInfo.unique = 0; sortInfo.interp = interp; - sortInfo.resultCode = TCL_OK; + sortInfo.resultCode = TCL_OK; cmdPtr = NULL; indices = 0; + group = 0; + groupSize = 1; + groupOffset = 0; + indexPtr = NULL; for (i = 1; i < objc-1; i++) { if (Tcl_GetIndexFromObj(interp, objv[i], switches, "option", 0, &index) != TCL_OK) { - if (sortInfo.indexc > 1) { - ckfree((char *) sortInfo.indexv); - } - return TCL_ERROR; + sortInfo.resultCode = TCL_ERROR; + goto done2; } switch ((enum Lsort_Switches) index) { case LSORT_ASCII: sortInfo.sortMode = SORTMODE_ASCII; break; case LSORT_COMMAND: - if (i == (objc-2)) { - if (sortInfo.indexc > 1) { - ckfree((char *) sortInfo.indexv); - } + if (i == objc-2) { Tcl_AppendResult(interp, "\"-command\" option must be followed " "by comparison command", NULL); - return TCL_ERROR; + Tcl_SetErrorCode(interp, "TCL", "ARGUMENT", "MISSING", NULL); + sortInfo.resultCode = TCL_ERROR; + goto done2; } sortInfo.sortMode = SORTMODE_COMMAND; cmdPtr = objv[i+1]; @@ -3520,55 +3670,41 @@ Tcl_LsortObjCmd( sortInfo.isIncreasing = 1; break; case LSORT_INDEX: { - Tcl_Obj **indices; + int indexc, dummy; + Tcl_Obj **indexv; - if (sortInfo.indexc > 1) { - ckfree((char *) sortInfo.indexv); - } - if (i == (objc-2)) { + if (i == objc-2) { Tcl_AppendResult(interp, "\"-index\" option must be " "followed by list index", NULL); - return TCL_ERROR; + Tcl_SetErrorCode(interp, "TCL", "ARGUMENT", "MISSING", NULL); + sortInfo.resultCode = TCL_ERROR; + goto done2; } - - /* - * Take copy to prevent shimmering problems. - */ - - if (TclListObjGetElements(interp, objv[i+1], &sortInfo.indexc, - &indices) != TCL_OK) { - return TCL_ERROR; + if (TclListObjGetElements(interp, objv[i+1], &indexc, + &indexv) != TCL_OK) { + sortInfo.resultCode = TCL_ERROR; + goto done2; } - switch (sortInfo.indexc) { - case 0: - sortInfo.indexv = NULL; - break; - case 1: - sortInfo.indexv = &sortInfo.singleIndex; - break; - default: - sortInfo.indexv = (int *) - ckalloc(sizeof(int) * sortInfo.indexc); - } - - /* - * Fill the array by parsing each index. We don't know whether - * their scale is sensible yet, but we at least perform the - * syntactic check here. - */ - for (j=0 ; j<sortInfo.indexc ; j++) { - if (TclGetIntForIndexM(interp, indices[j], SORTIDX_END, - &sortInfo.indexv[j]) != TCL_OK) { - if (sortInfo.indexc > 1) { - ckfree((char *) sortInfo.indexv); - } + /* + * Check each of the indices for syntactic correctness. Note that + * we do not store the converted values here because we do not + * know if this is the only -index option yet and so we can't + * allocate any space; that happens after the scan through all the + * options is done. + */ + + for (j=0 ; j<indexc ; j++) { + if (TclGetIntForIndexM(interp, indexv[j], SORTIDX_END, + &dummy) != TCL_OK) { Tcl_AppendObjToErrorInfo(interp, Tcl_ObjPrintf( "\n (-index option item number %d)", j)); - return TCL_ERROR; + sortInfo.resultCode = TCL_ERROR; + goto done2; } } - i++; + indexPtr = objv[i+1]; + i++; break; } case LSORT_INTEGER: @@ -3586,12 +3722,64 @@ Tcl_LsortObjCmd( case LSORT_INDICES: indices = 1; break; + case LSORT_STRIDE: + if (i == objc-2) { + Tcl_AppendResult(interp, "\"-stride\" option must be ", + "followed by stride length", NULL); + Tcl_SetErrorCode(interp, "TCL", "ARGUMENT", "MISSING", NULL); + sortInfo.resultCode = TCL_ERROR; + goto done2; + } + if (Tcl_GetIntFromObj(interp, objv[i+1], &groupSize) != TCL_OK) { + sortInfo.resultCode = TCL_ERROR; + goto done2; + } + if (groupSize < 2) { + Tcl_AppendResult(interp, "stride length must be at least 2", + NULL); + Tcl_SetErrorCode(interp, "TCL", "OPERATION", "LSORT", + "BADSTRIDE", NULL); + sortInfo.resultCode = TCL_ERROR; + goto done2; + } + group = 1; + i++; + break; } } if (nocase && (sortInfo.sortMode == SORTMODE_ASCII)) { sortInfo.sortMode = SORTMODE_ASCII_NC; } + /* + * Now extract the -index list for real, if present. No failures are + * expected here; the values are all of the right type or convertible to + * it. + */ + + if (indexPtr) { + Tcl_Obj **indexv; + + TclListObjGetElements(interp, indexPtr, &sortInfo.indexc, &indexv); + switch (sortInfo.indexc) { + case 0: + sortInfo.indexv = NULL; + break; + case 1: + sortInfo.indexv = &sortInfo.singleIndex; + break; + default: + sortInfo.indexv = + TclStackAlloc(interp, sizeof(int) * sortInfo.indexc); + allocatedIndexVector = 1; /* Cannot use indexc field, as it + * might be decreased by 1 later. */ + } + for (j=0 ; j<sortInfo.indexc ; j++) { + TclGetIntForIndexM(interp, indexv[j], SORTIDX_END, + &sortInfo.indexv[j]); + } + } + listObj = objv[objc-1]; if (sortInfo.sortMode == SORTMODE_COMMAND) { @@ -3606,10 +3794,8 @@ Tcl_LsortObjCmd( listObj = TclListObjCopy(interp, listObj); if (listObj == NULL) { - if (sortInfo.indexc > 1) { - ckfree((char *) sortInfo.indexv); - } - return TCL_ERROR; + sortInfo.resultCode = TCL_ERROR; + goto done2; } /* @@ -3626,10 +3812,8 @@ Tcl_LsortObjCmd( TclDecrRefCount(listObj); Tcl_IncrRefCount(newObjPtr); TclDecrRefCount(newObjPtr); - if (sortInfo.indexc > 1) { - ckfree((char *) sortInfo.indexv); - } - return TCL_ERROR; + sortInfo.resultCode = TCL_ERROR; + goto done2; } Tcl_ListObjAppendElement(interp, newCommandPtr, Tcl_NewObj()); sortInfo.compareCmdPtr = newCommandPtr; @@ -3640,8 +3824,62 @@ Tcl_LsortObjCmd( if (sortInfo.resultCode != TCL_OK || length <= 0) { goto done; } + + /* + * Check for sanity when grouping elements of the overall list together + * because of the -stride option. [TIP #326] + */ + + if (group) { + if (length % groupSize) { + Tcl_AppendResult(interp, + "list size must be a multiple of the stride length", + NULL); + Tcl_SetErrorCode(interp, "TCL", "OPERATION", "LSORT", "BADSTRIDE", + NULL); + sortInfo.resultCode = TCL_ERROR; + goto done; + } + length = length / groupSize; + if (sortInfo.indexc > 0) { + /* + * Use the first value in the list supplied to -index as the + * offset of the element within each group by which to sort. + */ + + groupOffset = sortInfo.indexv[0]; + if (groupOffset <= SORTIDX_END) { + groupOffset = (groupOffset - SORTIDX_END) + groupSize - 1; + } + if (groupOffset < 0 || groupOffset >= groupSize) { + Tcl_AppendResult(interp, "when used with \"-stride\", the " + "leading \"-index\" value must be within the group", + NULL); + Tcl_SetErrorCode(interp, "TCL", "OPERATION", "LSORT", + "BADINDEX", NULL); + sortInfo.resultCode = TCL_ERROR; + goto done; + } + if (sortInfo.indexc == 1) { + sortInfo.indexc = 0; + sortInfo.indexv = NULL; + } else { + sortInfo.indexc--; + + /* + * Do not shrink the actual memory block used; that doesn't + * work with TclStackAlloc-allocated memory. [Bug 2918962] + */ + + for (i = 0; i < sortInfo.indexc; i++) { + sortInfo.indexv[i] = sortInfo.indexv[i+1]; + } + } + } + } + sortInfo.numElements = length; - + indexc = sortInfo.indexc; sortMode = sortInfo.sortMode; if ((sortMode == SORTMODE_ASCII_NC) @@ -3649,7 +3887,7 @@ Tcl_LsortObjCmd( /* * For this function's purpose all string-based modes are equivalent */ - + sortMode = SORTMODE_ASCII; } @@ -3658,7 +3896,7 @@ Tcl_LsortObjCmd( * contain a sorted sublist of length 2**i. Use one extra subList at the * end, always at NULL, to indicate the end of the lists. */ - + for (j=0 ; j<=NUM_LISTS ; j++) { subList[j] = NULL; } @@ -3668,57 +3906,65 @@ Tcl_LsortObjCmd( * begins sorting it into the sublists as it appears. */ - elementArray = (SortElement *) ckalloc( length * sizeof(SortElement)); + elementArray = TclStackAlloc(interp, length * sizeof(SortElement)); for (i=0; i < length; i++){ + idx = groupSize * i + groupOffset; if (indexc) { /* * If this is an indexed sort, retrieve the corresponding element */ - indexPtr = SelectObjFromSublist(listObjPtrs[i], &sortInfo); + indexPtr = SelectObjFromSublist(listObjPtrs[idx], &sortInfo); if (sortInfo.resultCode != TCL_OK) { goto done1; } } else { - indexPtr = listObjPtrs[i]; + indexPtr = listObjPtrs[idx]; } /* * Determine the "value" of this object for sorting purposes */ - + if (sortMode == SORTMODE_ASCII) { - elementArray[i].index.strValuePtr = TclGetString(indexPtr); + elementArray[i].collationKey.strValuePtr = TclGetString(indexPtr); } else if (sortMode == SORTMODE_INTEGER) { long a; + if (TclGetLongFromObj(sortInfo.interp, indexPtr, &a) != TCL_OK) { sortInfo.resultCode = TCL_ERROR; goto done1; } - elementArray[i].index.intValue = a; + elementArray[i].collationKey.intValue = a; } else if (sortInfo.sortMode == SORTMODE_REAL) { double a; - if (Tcl_GetDoubleFromObj(sortInfo.interp, indexPtr, &a) != TCL_OK) { + + if (Tcl_GetDoubleFromObj(sortInfo.interp, indexPtr, + &a) != TCL_OK) { sortInfo.resultCode = TCL_ERROR; goto done1; } - elementArray[i].index.doubleValue = a; + elementArray[i].collationKey.doubleValue = a; } else { - elementArray[i].index.objValuePtr = indexPtr; + elementArray[i].collationKey.objValuePtr = indexPtr; } /* * Determine the representation of this element in the result: either * the objPtr itself, or its index in the original list. */ - - elementArray[i].objPtr = (indices ? INT2PTR(i) : listObjPtrs[i]); + + if (indices || group) { + elementArray[i].payload.index = idx; + } else { + elementArray[i].payload.objPtr = listObjPtrs[idx]; + } /* * Merge this element in the pre-existing sublists (and merge together * sublists when we have two of the same size). */ - + elementArray[i].nextPtr = NULL; elementPtr = &elementArray[i]; for (j=0 ; subList[j] ; j++) { @@ -3734,34 +3980,47 @@ Tcl_LsortObjCmd( /* * Merge all sublists */ - + elementPtr = subList[0]; for (j=1 ; j<NUM_LISTS ; j++) { elementPtr = MergeLists(subList[j], elementPtr, &sortInfo); } - /* * Now store the sorted elements in the result list. */ - + if (sortInfo.resultCode == TCL_OK) { List *listRepPtr; Tcl_Obj **newArray, *objPtr; - int i; - - resultPtr = Tcl_NewListObj(sortInfo.numElements, NULL); + + resultPtr = Tcl_NewListObj(sortInfo.numElements * groupSize, NULL); listRepPtr = ListRepPtr(resultPtr); newArray = &listRepPtr->elements; - if (indices) { - for (i = 0; elementPtr != NULL ; elementPtr = elementPtr->nextPtr){ - objPtr = Tcl_NewIntObj(PTR2INT(elementPtr->objPtr)); + if (group) { + for (i=0; elementPtr!=NULL ; elementPtr=elementPtr->nextPtr) { + idx = elementPtr->payload.index; + for (j = 0; j < groupSize; j++) { + if (indices) { + objPtr = Tcl_NewIntObj(idx + j - groupOffset); + newArray[i++] = objPtr; + Tcl_IncrRefCount(objPtr); + } else { + objPtr = listObjPtrs[idx + j - groupOffset]; + newArray[i++] = objPtr; + Tcl_IncrRefCount(objPtr); + } + } + } + } else if (indices) { + for (i=0; elementPtr != NULL ; elementPtr = elementPtr->nextPtr) { + objPtr = Tcl_NewIntObj(elementPtr->payload.index); newArray[i++] = objPtr; Tcl_IncrRefCount(objPtr); } } else { - for (i = 0; elementPtr != NULL ; elementPtr = elementPtr->nextPtr){ - objPtr = elementPtr->objPtr; + for (i=0; elementPtr != NULL ; elementPtr = elementPtr->nextPtr) { + objPtr = elementPtr->payload.objPtr; newArray[i++] = objPtr; Tcl_IncrRefCount(objPtr); } @@ -3771,7 +4030,7 @@ Tcl_LsortObjCmd( } done1: - ckfree((char *)elementArray); + TclStackFree(interp, elementArray); done: if (sortInfo.sortMode == SORTMODE_COMMAND) { @@ -3779,8 +4038,9 @@ Tcl_LsortObjCmd( TclDecrRefCount(listObj); sortInfo.compareCmdPtr = NULL; } - if (sortInfo.indexc > 1) { - ckfree((char *) sortInfo.indexv); + done2: + if (allocatedIndexVector) { + TclStackFree(interp, sortInfo.indexv); } return sortInfo.resultCode; } @@ -3797,21 +4057,23 @@ Tcl_LsortObjCmd( * The unified list of SortElement structures. * * Side effects: - * If infoPtr->unique is set then infoPtr->numElements may be updated. + * If infoPtr->unique is set then infoPtr->numElements may be updated. * Possibly others, if a user-defined comparison command does something - * weird. + * weird. * * Note: - * If infoPtr->unique is set, the merge assumes that there are no + * If infoPtr->unique is set, the merge assumes that there are no * "repeated" elements in each of the left and right lists. In that case, * if any element of the left list is equivalent to one in the right list * it is omitted from the merged list. - * This simplified mechanism works because of the special way - * our MergeSort creates the sublists to be merged and will fail to - * eliminate all repeats in the general case where they are already - * present in either the left or right list. A general code would need to - * skip adjacent initial repeats in the left and right lists before - * comparing their initial elements, at each step. + * + * This simplified mechanism works because of the special way our + * MergeSort creates the sublists to be merged and will fail to eliminate + * all repeats in the general case where they are already present in + * either the left or right list. A general code would need to skip + * adjacent initial repeats in the left and right lists before comparing + * their initial elements, at each step. + * *---------------------------------------------------------------------- */ @@ -3913,25 +4175,25 @@ SortCompare( int order = 0; if (infoPtr->sortMode == SORTMODE_ASCII) { - order = strcmp(elemPtr1->index.strValuePtr, - elemPtr2->index.strValuePtr); + order = strcmp(elemPtr1->collationKey.strValuePtr, + elemPtr2->collationKey.strValuePtr); } else if (infoPtr->sortMode == SORTMODE_ASCII_NC) { - order = strcasecmp(elemPtr1->index.strValuePtr, - elemPtr2->index.strValuePtr); + order = strcasecmp(elemPtr1->collationKey.strValuePtr, + elemPtr2->collationKey.strValuePtr); } else if (infoPtr->sortMode == SORTMODE_DICTIONARY) { - order = DictionaryCompare(elemPtr1->index.strValuePtr, - elemPtr2->index.strValuePtr); + order = DictionaryCompare(elemPtr1->collationKey.strValuePtr, + elemPtr2->collationKey.strValuePtr); } else if (infoPtr->sortMode == SORTMODE_INTEGER) { long a, b; - a = elemPtr1->index.intValue; - b = elemPtr2->index.intValue; + a = elemPtr1->collationKey.intValue; + b = elemPtr2->collationKey.intValue; order = ((a >= b) - (a <= b)); } else if (infoPtr->sortMode == SORTMODE_REAL) { double a, b; - a = elemPtr1->index.doubleValue; - b = elemPtr2->index.doubleValue; + a = elemPtr1->collationKey.doubleValue; + b = elemPtr2->collationKey.doubleValue; order = ((a >= b) - (a <= b)); } else { Tcl_Obj **objv, *paramObjv[2]; @@ -3943,14 +4205,14 @@ SortCompare( * Once an error has occurred, skip any future comparisons so as * to preserve the error message in sortInterp->result. */ - + return 0; } - objPtr1 = elemPtr1->index.objValuePtr; - objPtr2 = elemPtr2->index.objValuePtr; - + objPtr1 = elemPtr1->collationKey.objValuePtr; + objPtr2 = elemPtr2->collationKey.objValuePtr; + paramObjv[0] = objPtr1; paramObjv[1] = objPtr2; @@ -3968,8 +4230,7 @@ SortCompare( infoPtr->resultCode = Tcl_EvalObjv(infoPtr->interp, objc, objv, 0); if (infoPtr->resultCode != TCL_OK) { - Tcl_AddErrorInfo(infoPtr->interp, - "\n (-compare command)"); + Tcl_AddErrorInfo(infoPtr->interp, "\n (-compare command)"); return 0; } @@ -3982,6 +4243,8 @@ SortCompare( Tcl_ResetResult(infoPtr->interp); Tcl_AppendResult(infoPtr->interp, "-compare command returned non-integer result", NULL); + Tcl_SetErrorCode(infoPtr->interp, "TCL", "OPERATION", "LSORT", + "COMPARISONFAILED", NULL); infoPtr->resultCode = TCL_ERROR; return 0; } @@ -4018,7 +4281,7 @@ SortCompare( static int DictionaryCompare( - char *left, char *right) /* The strings to compare. */ + const char *left, const char *right) /* The strings to compare. */ { Tcl_UniChar uniLeft, uniRight, uniLeftLower, uniRightLower; int diff, zeros; @@ -4035,11 +4298,11 @@ DictionaryCompare( */ zeros = 0; - while ((*right == '0') && (isdigit(UCHAR(right[1])))) { + while ((*right == '0') && isdigit(UCHAR(right[1]))) { right++; zeros--; } - while ((*left == '0') && (isdigit(UCHAR(left[1])))) { + while ((*left == '0') && isdigit(UCHAR(left[1]))) { left++; zeros++; } @@ -4198,6 +4461,8 @@ SelectObjFromSublist( Tcl_AppendResult(infoPtr->interp, "element ", buffer, " missing from sublist \"", TclGetString(objPtr), "\"", NULL); + Tcl_SetErrorCode(infoPtr->interp, "TCL", "OPERATION", "LSORT", + "INDEXFAILED", NULL); infoPtr->resultCode = TCL_ERROR; return NULL; } @@ -4211,5 +4476,7 @@ SelectObjFromSublist( * mode: c * c-basic-offset: 4 * fill-column: 78 + * tab-width: 8 + * indent-tabs-mode: nil * End: */ |