summaryrefslogtreecommitdiff
path: root/contrib/tcl/generic/tclCmdIL.c
diff options
context:
space:
mode:
Diffstat (limited to 'contrib/tcl/generic/tclCmdIL.c')
-rw-r--r--contrib/tcl/generic/tclCmdIL.c3138
1 files changed, 2275 insertions, 863 deletions
diff --git a/contrib/tcl/generic/tclCmdIL.c b/contrib/tcl/generic/tclCmdIL.c
index 0a3b25a98c816..18342f37ab485 100644
--- a/contrib/tcl/generic/tclCmdIL.c
+++ b/contrib/tcl/generic/tclCmdIL.c
@@ -7,12 +7,13 @@
* (i.e. those that don't depend much upon UNIX facilities).
*
* Copyright (c) 1987-1993 The Regents of the University of California.
- * Copyright (c) 1994-1995 Sun Microsystems, Inc.
+ * Copyright (c) 1993-1997 Lucent Technologies.
+ * Copyright (c) 1994-1997 Sun Microsystems, Inc.
*
* See the file "license.terms" for information on usage and redistribution
* of this file, and for a DISCLAIMER OF ALL WARRANTIES.
*
- * SCCS: @(#) tclCmdIL.c 1.120 96/07/10 17:16:03
+ * SCCS: @(#) tclCmdIL.c 1.163 97/06/13 18:16:52
*/
#include "tclInt.h"
@@ -28,37 +29,126 @@
char *tclExecutableName = NULL;
/*
- * The variables below are used to implement the "lsort" command.
- * Unfortunately, this use of static variables prevents "lsort"
- * from being thread-safe, but there's no alternative given the
- * current implementation of qsort. In a threaded environment
- * these variables should be made thread-local if possible, or else
- * "lsort" needs internal mutual exclusion.
+ * During execution of the "lsort" command, structures of the following
+ * type are used to arrange the objects being sorted into a collection
+ * of linked lists.
*/
-static Tcl_Interp *sortInterp = NULL; /* Interpreter for "lsort" command.
- * NULL means no lsort is active. */
-static enum {ASCII, INTEGER, REAL, COMMAND} sortMode;
- /* Mode for sorting: compare as strings,
- * compare as numbers, or call
- * user-defined command for
- * comparison. */
-static Tcl_DString sortCmd; /* Holds command if mode is COMMAND.
- * pre-initialized to hold base of
- * command. */
-static int sortIncreasing; /* 0 means sort in decreasing order,
- * 1 means increasing order. */
-static int sortCode; /* Anything other than TCL_OK means a
- * problem occurred while sorting; this
- * executing a comparison command, so
- * the sort was aborted. */
+typedef struct SortElement {
+ Tcl_Obj *objPtr; /* Object being sorted. */
+ struct SortElement *nextPtr; /* Next element in the list, or
+ * NULL for end of list. */
+} SortElement;
+
+/*
+ * The "lsort" command needs to pass certain information down to the
+ * function that compares two list elements, and the comparison function
+ * needs to pass success or failure information back up to the top-level
+ * "lsort" command. The following structure is used to pass this
+ * information.
+ */
+
+typedef struct SortInfo {
+ int isIncreasing; /* Nonzero means sort in increasing order. */
+ int sortMode; /* The sort mode. One of SORTMODE_*
+ * values defined below */
+ Tcl_DString compareCmd; /* The Tcl comparison command when sortMode
+ * is SORTMODE_COMMAND. Pre-initialized to
+ * hold base of command.*/
+ long index; /* If the -index option was specified, this
+ * holds the index of the list element
+ * to extract for comparison. If -index
+ * wasn't specified, this is -1. */
+ Tcl_Interp *interp; /* The interpreter in which the sortis
+ * being done. */
+ int resultCode; /* Completion code for the lsort command.
+ * If an error occurs during the sort this
+ * is changed from TCL_OK to TCL_ERROR. */
+} SortInfo;
+
+/*
+ * The "sortMode" field of the SortInfo structure can take on any of the
+ * following values.
+ */
+
+#define SORTMODE_ASCII 0
+#define SORTMODE_INTEGER 1
+#define SORTMODE_REAL 2
+#define SORTMODE_COMMAND 3
+#define SORTMODE_DICTIONARY 4
/*
* Forward declarations for procedures defined in this file:
*/
-static int SortCompareProc _ANSI_ARGS_((CONST VOID *first,
- CONST VOID *second));
+static int DictionaryCompare _ANSI_ARGS_((char *left,
+ char *right));
+static int InfoArgsCmd _ANSI_ARGS_((ClientData dummy,
+ Tcl_Interp *interp, int objc,
+ Tcl_Obj *CONST objv[]));
+static int InfoBodyCmd _ANSI_ARGS_((ClientData dummy,
+ Tcl_Interp *interp, int objc,
+ Tcl_Obj *CONST objv[]));
+static int InfoCmdCountCmd _ANSI_ARGS_((ClientData dummy,
+ Tcl_Interp *interp, int objc,
+ Tcl_Obj *CONST objv[]));
+static int InfoCommandsCmd _ANSI_ARGS_((ClientData dummy,
+ Tcl_Interp *interp, int objc,
+ Tcl_Obj *CONST objv[]));
+static int InfoCompleteCmd _ANSI_ARGS_((ClientData dummy,
+ Tcl_Interp *interp, int objc,
+ Tcl_Obj *CONST objv[]));
+static int InfoDefaultCmd _ANSI_ARGS_((ClientData dummy,
+ Tcl_Interp *interp, int objc,
+ Tcl_Obj *CONST objv[]));
+static int InfoExistsCmd _ANSI_ARGS_((ClientData dummy,
+ Tcl_Interp *interp, int objc,
+ Tcl_Obj *CONST objv[]));
+static int InfoGlobalsCmd _ANSI_ARGS_((ClientData dummy,
+ Tcl_Interp *interp, int objc,
+ Tcl_Obj *CONST objv[]));
+static int InfoHostnameCmd _ANSI_ARGS_((ClientData dummy,
+ Tcl_Interp *interp, int objc,
+ Tcl_Obj *CONST objv[]));
+static int InfoLevelCmd _ANSI_ARGS_((ClientData dummy,
+ Tcl_Interp *interp, int objc,
+ Tcl_Obj *CONST objv[]));
+static int InfoLibraryCmd _ANSI_ARGS_((ClientData dummy,
+ Tcl_Interp *interp, int objc,
+ Tcl_Obj *CONST objv[]));
+static int InfoLoadedCmd _ANSI_ARGS_((ClientData dummy,
+ Tcl_Interp *interp, int objc,
+ Tcl_Obj *CONST objv[]));
+static int InfoLocalsCmd _ANSI_ARGS_((ClientData dummy,
+ Tcl_Interp *interp, int objc,
+ Tcl_Obj *CONST objv[]));
+static int InfoNameOfExecutableCmd _ANSI_ARGS_((
+ ClientData dummy, Tcl_Interp *interp, int objc,
+ Tcl_Obj *CONST objv[]));
+static int InfoPatchLevelCmd _ANSI_ARGS_((ClientData dummy,
+ Tcl_Interp *interp, int objc,
+ Tcl_Obj *CONST objv[]));
+static int InfoProcsCmd _ANSI_ARGS_((ClientData dummy,
+ Tcl_Interp *interp, int objc,
+ Tcl_Obj *CONST objv[]));
+static int InfoScriptCmd _ANSI_ARGS_((ClientData dummy,
+ Tcl_Interp *interp, int objc,
+ Tcl_Obj *CONST objv[]));
+static int InfoSharedlibCmd _ANSI_ARGS_((ClientData dummy,
+ Tcl_Interp *interp, int objc,
+ Tcl_Obj *CONST objv[]));
+static int InfoTclVersionCmd _ANSI_ARGS_((ClientData dummy,
+ Tcl_Interp *interp, int objc,
+ Tcl_Obj *CONST objv[]));
+static int InfoVarsCmd _ANSI_ARGS_((ClientData dummy,
+ Tcl_Interp *interp, int objc,
+ Tcl_Obj *CONST objv[]));
+static SortElement * MergeSort _ANSI_ARGS_((SortElement *headPt,
+ SortInfo *infoPtr));
+static SortElement * MergeLists _ANSI_ARGS_((SortElement *leftPtr,
+ SortElement *rightPtr, SortInfo *infoPtr));
+static int SortCompare _ANSI_ARGS_((Tcl_Obj *firstPtr,
+ Tcl_Obj *second, SortInfo *infoPtr));
/*
*----------------------------------------------------------------------
@@ -68,6 +158,10 @@ static int SortCompareProc _ANSI_ARGS_((CONST VOID *first,
* This procedure is invoked to process the "if" 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 "if" or the name
+ * to which "if" was renamed: e.g., "set z if; $z 1 {puts foo}"
+ *
* Results:
* A standard Tcl result.
*
@@ -118,7 +212,7 @@ Tcl_IfCmd(dummy, interp, argc, argv)
if (value) {
return Tcl_Eval(interp, argv[i]);
}
-
+
/*
* The expression evaluated to false. Skip the command, then
* see if there is an "else" or "elseif" clause.
@@ -161,6 +255,10 @@ Tcl_IfCmd(dummy, interp, argc, argv)
* This procedure is invoked to process the "incr" 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 "incr" or the name
+ * to which "incr" was renamed: e.g., "set z incr; $z i -1"
+ *
* Results:
* A standard Tcl result.
*
@@ -209,19 +307,24 @@ Tcl_IncrCmd(dummy, interp, argc, argv)
}
value += increment;
}
- sprintf(newString, "%d", value);
+ TclFormatInt(newString, value);
result = Tcl_SetVar(interp, argv[1], newString, TCL_LEAVE_ERR_MSG);
if (result == NULL) {
return TCL_ERROR;
}
- interp->result = result;
+
+ /*
+ * Copy the result since the variable's value might change.
+ */
+
+ Tcl_SetResult(interp, result, TCL_VOLATILE);
return TCL_OK;
}
/*
*----------------------------------------------------------------------
*
- * Tcl_InfoCmd --
+ * Tcl_InfoObjCmd --
*
* This procedure is invoked to process the "info" Tcl command.
* See the user documentation for details on what it does.
@@ -237,434 +340,1394 @@ Tcl_IncrCmd(dummy, interp, argc, argv)
/* ARGSUSED */
int
-Tcl_InfoCmd(dummy, interp, argc, argv)
- ClientData dummy; /* Not used. */
- Tcl_Interp *interp; /* Current interpreter. */
- int argc; /* Number of arguments. */
- char **argv; /* Argument strings. */
+Tcl_InfoObjCmd(clientData, interp, objc, objv)
+ ClientData clientData; /* Arbitrary value passed to the command. */
+ Tcl_Interp *interp; /* Current interpreter. */
+ int objc; /* Number of arguments. */
+ Tcl_Obj *CONST objv[]; /* Argument objects. */
+{
+ static char *subCmds[] = {
+ "args", "body", "cmdcount", "commands",
+ "complete", "default", "exists", "globals",
+ "hostname", "level", "library", "loaded",
+ "locals", "nameofexecutable", "patchlevel", "procs",
+ "script", "sharedlibextension", "tclversion", "vars",
+ (char *) NULL};
+ enum ISubCmdIdx {
+ IArgsIdx, IBodyIdx, ICmdCountIdx, ICommandsIdx,
+ ICompleteIdx, IDefaultIdx, IExistsIdx, IGlobalsIdx,
+ IHostnameIdx, ILevelIdx, ILibraryIdx, ILoadedIdx,
+ ILocalsIdx, INameOfExecutableIdx, IPatchLevelIdx, IProcsIdx,
+ IScriptIdx, ISharedLibExtensionIdx, ITclVersionIdx, IVarsIdx
+ } index;
+ int result;
+
+ if (objc < 2) {
+ Tcl_WrongNumArgs(interp, 1, objv, "option ?arg arg ...?");
+ return TCL_ERROR;
+ }
+
+ result = Tcl_GetIndexFromObj(interp, objv[1], subCmds, "option", 0,
+ (int *) &index);
+ if (result != TCL_OK) {
+ return result;
+ }
+
+ switch (index) {
+ case IArgsIdx:
+ result = InfoArgsCmd(clientData, interp, objc, objv);
+ break;
+ case IBodyIdx:
+ result = InfoBodyCmd(clientData, interp, objc, objv);
+ break;
+ case ICmdCountIdx:
+ result = InfoCmdCountCmd(clientData, interp, objc, objv);
+ break;
+ case ICommandsIdx:
+ result = InfoCommandsCmd(clientData, interp, objc, objv);
+ break;
+ case ICompleteIdx:
+ result = InfoCompleteCmd(clientData, interp, objc, objv);
+ break;
+ case IDefaultIdx:
+ result = InfoDefaultCmd(clientData, interp, objc, objv);
+ break;
+ case IExistsIdx:
+ result = InfoExistsCmd(clientData, interp, objc, objv);
+ break;
+ case IGlobalsIdx:
+ result = InfoGlobalsCmd(clientData, interp, objc, objv);
+ break;
+ case IHostnameIdx:
+ result = InfoHostnameCmd(clientData, interp, objc, objv);
+ break;
+ case ILevelIdx:
+ result = InfoLevelCmd(clientData, interp, objc, objv);
+ break;
+ case ILibraryIdx:
+ result = InfoLibraryCmd(clientData, interp, objc, objv);
+ break;
+ case ILoadedIdx:
+ result = InfoLoadedCmd(clientData, interp, objc, objv);
+ break;
+ case ILocalsIdx:
+ result = InfoLocalsCmd(clientData, interp, objc, objv);
+ break;
+ case INameOfExecutableIdx:
+ result = InfoNameOfExecutableCmd(clientData, interp, objc, objv);
+ break;
+ case IPatchLevelIdx:
+ result = InfoPatchLevelCmd(clientData, interp, objc, objv);
+ break;
+ case IProcsIdx:
+ result = InfoProcsCmd(clientData, interp, objc, objv);
+ break;
+ case IScriptIdx:
+ result = InfoScriptCmd(clientData, interp, objc, objv);
+ break;
+ case ISharedLibExtensionIdx:
+ result = InfoSharedlibCmd(clientData, interp, objc, objv);
+ break;
+ case ITclVersionIdx:
+ result = InfoTclVersionCmd(clientData, interp, objc, objv);
+ break;
+ case IVarsIdx:
+ result = InfoVarsCmd(clientData, interp, objc, objv);
+ break;
+ }
+ return result;
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * InfoArgsCmd --
+ *
+ * Called to implement the "info args" command that returns the
+ * argument list for a procedure. Handles the following syntax:
+ *
+ * info args procName
+ *
+ * Results:
+ * Returns TCL_OK is successful and TCL_ERROR is 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.
+ *
+ *----------------------------------------------------------------------
+ */
+
+static int
+InfoArgsCmd(dummy, interp, objc, objv)
+ ClientData dummy; /* Not used. */
+ Tcl_Interp *interp; /* Current interpreter. */
+ int objc; /* Number of arguments. */
+ Tcl_Obj *CONST objv[]; /* Argument objects. */
{
register Interp *iPtr = (Interp *) interp;
- size_t length;
- int c;
- Arg *argPtr;
+ char *name;
Proc *procPtr;
- Var *varPtr;
- Command *cmdPtr;
- Tcl_HashEntry *hPtr;
- Tcl_HashSearch search;
+ CompiledLocal *localPtr;
+ Tcl_Obj *listObjPtr;
- if (argc < 2) {
- Tcl_AppendResult(interp, "wrong # args: should be \"", argv[0],
- " option ?arg arg ...?\"", (char *) NULL);
- return TCL_ERROR;
+ if (objc != 3) {
+ Tcl_WrongNumArgs(interp, 1, objv, "args procname");
+ return TCL_ERROR;
}
- c = argv[1][0];
- length = strlen(argv[1]);
- if ((c == 'a') && (strncmp(argv[1], "args", length)) == 0) {
- if (argc != 3) {
- Tcl_AppendResult(interp, "wrong # args: should be \"",
- argv[0], " args procname\"", (char *) NULL);
- return TCL_ERROR;
- }
- procPtr = TclFindProc(iPtr, argv[2]);
- if (procPtr == NULL) {
- infoNoSuchProc:
- Tcl_AppendResult(interp, "\"", argv[2],
- "\" isn't a procedure", (char *) NULL);
- return TCL_ERROR;
- }
- for (argPtr = procPtr->argPtr; argPtr != NULL;
- argPtr = argPtr->nextPtr) {
- Tcl_AppendElement(interp, argPtr->name);
- }
- return TCL_OK;
- } else if ((c == 'b') && (strncmp(argv[1], "body", length)) == 0) {
- if (argc != 3) {
- Tcl_AppendResult(interp, "wrong # args: should be \"", argv[0],
- " body procname\"", (char *) NULL);
- return TCL_ERROR;
- }
- procPtr = TclFindProc(iPtr, argv[2]);
- if (procPtr == NULL) {
- goto infoNoSuchProc;
- }
- iPtr->result = procPtr->command;
- return TCL_OK;
- } else if ((c == 'c') && (strncmp(argv[1], "cmdcount", length) == 0)
- && (length >= 2)) {
- if (argc != 2) {
- Tcl_AppendResult(interp, "wrong # args: should be \"", argv[0],
- " cmdcount\"", (char *) NULL);
- return TCL_ERROR;
- }
- sprintf(iPtr->result, "%d", iPtr->cmdCount);
- return TCL_OK;
- } else if ((c == 'c') && (strncmp(argv[1], "commands", length) == 0)
- && (length >= 4)) {
- if (argc > 3) {
- Tcl_AppendResult(interp, "wrong # args: should be \"", argv[0],
- " commands ?pattern?\"", (char *) NULL);
- return TCL_ERROR;
- }
- for (hPtr = Tcl_FirstHashEntry(&iPtr->commandTable, &search);
- hPtr != NULL; hPtr = Tcl_NextHashEntry(&search)) {
- char *name = Tcl_GetHashKey(&iPtr->commandTable, hPtr);
- if ((argc == 3) && !Tcl_StringMatch(name, argv[2])) {
- continue;
- }
- Tcl_AppendElement(interp, name);
- }
- return TCL_OK;
- } else if ((c == 'c') && (strncmp(argv[1], "complete", length) == 0)
- && (length >= 4)) {
- if (argc != 3) {
- Tcl_AppendResult(interp, "wrong # args: should be \"", argv[0],
- " complete command\"", (char *) NULL);
- return TCL_ERROR;
- }
- if (Tcl_CommandComplete(argv[2])) {
- interp->result = "1";
- } else {
- interp->result = "0";
- }
- return TCL_OK;
- } else if ((c == 'd') && (strncmp(argv[1], "default", length)) == 0) {
- if (argc != 5) {
- Tcl_AppendResult(interp, "wrong # args: should be \"",
- argv[0], " default procname arg varname\"",
- (char *) NULL);
+
+ name = Tcl_GetStringFromObj(objv[2], (int *) NULL);
+ procPtr = TclFindProc(iPtr, name);
+ if (procPtr == NULL) {
+ Tcl_AppendStringsToObj(Tcl_GetObjResult(interp),
+ "\"", name, "\" isn't a procedure", (char *) NULL);
+ return TCL_ERROR;
+ }
+
+ /*
+ * Build a return list containing the arguments.
+ */
+
+ listObjPtr = Tcl_NewListObj(0, (Tcl_Obj **) NULL);
+ for (localPtr = procPtr->firstLocalPtr; localPtr != NULL;
+ localPtr = localPtr->nextPtr) {
+ if (localPtr->isArg) {
+ Tcl_ListObjAppendElement(interp, listObjPtr,
+ Tcl_NewStringObj(localPtr->name, -1));
+ }
+ }
+ Tcl_SetObjResult(interp, listObjPtr);
+ return TCL_OK;
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * InfoBodyCmd --
+ *
+ * Called to implement the "info body" command that returns the body
+ * for a procedure. Handles the following syntax:
+ *
+ * info body procName
+ *
+ * Results:
+ * Returns TCL_OK is successful and TCL_ERROR is 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.
+ *
+ *----------------------------------------------------------------------
+ */
+
+static int
+InfoBodyCmd(dummy, interp, objc, objv)
+ ClientData dummy; /* Not used. */
+ Tcl_Interp *interp; /* Current interpreter. */
+ int objc; /* Number of arguments. */
+ Tcl_Obj *CONST objv[]; /* Argument objects. */
+{
+ register Interp *iPtr = (Interp *) interp;
+ char *name;
+ Proc *procPtr;
+
+ if (objc != 3) {
+ Tcl_WrongNumArgs(interp, 1, objv, "body procname");
+ return TCL_ERROR;
+ }
+
+ name = Tcl_GetStringFromObj(objv[2], (int *) NULL);
+ procPtr = TclFindProc(iPtr, name);
+ if (procPtr == NULL) {
+ Tcl_AppendStringsToObj(Tcl_GetObjResult(interp),
+ "\"", name, "\" isn't a procedure", (char *) NULL);
+ return TCL_ERROR;
+ }
+ Tcl_SetObjResult(interp, procPtr->bodyPtr);
+ return TCL_OK;
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * InfoCmdCountCmd --
+ *
+ * Called to implement the "info cmdcount" command that returns the
+ * number of commands that have been executed. Handles the following
+ * syntax:
+ *
+ * info cmdcount
+ *
+ * Results:
+ * Returns TCL_OK is successful and TCL_ERROR is 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.
+ *
+ *----------------------------------------------------------------------
+ */
+
+static int
+InfoCmdCountCmd(dummy, interp, objc, objv)
+ ClientData dummy; /* Not used. */
+ Tcl_Interp *interp; /* Current interpreter. */
+ int objc; /* Number of arguments. */
+ Tcl_Obj *CONST objv[]; /* Argument objects. */
+{
+ Interp *iPtr = (Interp *) interp;
+
+ if (objc != 2) {
+ Tcl_WrongNumArgs(interp, 1, objv, "cmdcount");
+ return TCL_ERROR;
+ }
+
+ Tcl_SetIntObj(Tcl_GetObjResult(interp), iPtr->cmdCount);
+ return TCL_OK;
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * InfoCommandsCmd --
+ *
+ * Called to implement the "info commands" command that returns the
+ * list of commands in the interpreter that match an optional pattern.
+ * The pattern, if any, consists of an optional sequence of namespace
+ * names separated by "::" qualifiers, which is followed by a
+ * glob-style pattern that restricts which commands are returned.
+ * Handles the following syntax:
+ *
+ * info commands ?pattern?
+ *
+ * Results:
+ * Returns TCL_OK is successful and TCL_ERROR is 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.
+ *
+ *----------------------------------------------------------------------
+ */
+
+static int
+InfoCommandsCmd(dummy, interp, objc, objv)
+ ClientData dummy; /* Not used. */
+ Tcl_Interp *interp; /* Current interpreter. */
+ int objc; /* Number of arguments. */
+ Tcl_Obj *CONST objv[]; /* Argument objects. */
+{
+ char *cmdName, *pattern, *simplePattern;
+ register Tcl_HashEntry *entryPtr;
+ Tcl_HashSearch search;
+ Namespace *nsPtr;
+ Namespace *globalNsPtr = (Namespace *) Tcl_GetGlobalNamespace(interp);
+ Namespace *currNsPtr = (Namespace *) Tcl_GetCurrentNamespace(interp);
+ Tcl_Obj *listPtr, *elemObjPtr;
+ int specificNsInPattern = 0; /* Init. to avoid compiler warning. */
+ Tcl_Command cmd;
+ int result;
+
+ /*
+ * Get the pattern and find the "effective namespace" in which to
+ * list commands.
+ */
+
+ if (objc == 2) {
+ simplePattern = NULL;
+ nsPtr = currNsPtr;
+ specificNsInPattern = 0;
+ } else if (objc == 3) {
+ /*
+ * From the pattern, get the effective namespace and the simple
+ * pattern (no namespace qualifiers or ::'s) at the end. If an
+ * error was found while parsing the pattern, return it. Otherwise,
+ * if the namespace wasn't found, just leave nsPtr NULL: we will
+ * return an empty list since no commands there can be found.
+ */
+
+ Namespace *dummy1NsPtr, *dummy2NsPtr;
+
+ pattern = Tcl_GetStringFromObj(objv[2], (int *) NULL);
+ result = TclGetNamespaceForQualName(interp, pattern,
+ (Namespace *) NULL, /*flags*/ TCL_LEAVE_ERR_MSG,
+ &nsPtr, &dummy1NsPtr, &dummy2NsPtr, &simplePattern);
+ if (result != TCL_OK) {
return TCL_ERROR;
}
- procPtr = TclFindProc(iPtr, argv[2]);
- if (procPtr == NULL) {
- goto infoNoSuchProc;
+ if (nsPtr != NULL) { /* we successfully found the pattern's ns */
+ specificNsInPattern = (strcmp(simplePattern, pattern) != 0);
}
- for (argPtr = procPtr->argPtr; ; argPtr = argPtr->nextPtr) {
- if (argPtr == NULL) {
- Tcl_AppendResult(interp, "procedure \"", argv[2],
- "\" doesn't have an argument \"", argv[3],
- "\"", (char *) NULL);
- return TCL_ERROR;
- }
- if (strcmp(argv[3], argPtr->name) == 0) {
- if (argPtr->defValue != NULL) {
- if (Tcl_SetVar((Tcl_Interp *) iPtr, argv[4],
- argPtr->defValue, 0) == NULL) {
- defStoreError:
- Tcl_AppendResult(interp,
- "couldn't store default value in variable \"",
- argv[4], "\"", (char *) NULL);
- return TCL_ERROR;
- }
- iPtr->result = "1";
+ } else {
+ Tcl_WrongNumArgs(interp, 1, objv, "commands ?pattern?");
+ return TCL_ERROR;
+ }
+
+ /*
+ * Scan through the effective namespace's command table and create a
+ * list with all commands that match the pattern. If a specific
+ * namespace was requested in the pattern, qualify the command names
+ * with the namespace name.
+ */
+
+ listPtr = Tcl_NewListObj(0, (Tcl_Obj **) NULL);
+
+ if (nsPtr != NULL) {
+ entryPtr = Tcl_FirstHashEntry(&nsPtr->cmdTable, &search);
+ while (entryPtr != NULL) {
+ cmdName = Tcl_GetHashKey(&nsPtr->cmdTable, entryPtr);
+ if ((simplePattern == NULL)
+ || Tcl_StringMatch(cmdName, simplePattern)) {
+ if (specificNsInPattern) {
+ cmd = (Tcl_Command) Tcl_GetHashValue(entryPtr);
+ elemObjPtr = Tcl_NewObj();
+ Tcl_GetCommandFullName(interp, cmd, elemObjPtr);
} else {
- if (Tcl_SetVar((Tcl_Interp *) iPtr, argv[4], "", 0)
- == NULL) {
- goto defStoreError;
- }
- iPtr->result = "0";
+ elemObjPtr = Tcl_NewStringObj(cmdName, -1);
}
- return TCL_OK;
+ Tcl_ListObjAppendElement(interp, listPtr, elemObjPtr);
}
+ entryPtr = Tcl_NextHashEntry(&search);
}
- } else if ((c == 'e') && (strncmp(argv[1], "exists", length) == 0)) {
- char *p;
- if (argc != 3) {
- Tcl_AppendResult(interp, "wrong # args: should be \"", argv[0],
- " exists varName\"", (char *) NULL);
- return TCL_ERROR;
- }
- p = Tcl_GetVar((Tcl_Interp *) iPtr, argv[2], 0);
/*
- * The code below handles the special case where the name is for
- * an array: Tcl_GetVar will reject this since you can't read
- * an array variable without an index.
+ * If the effective namespace isn't the global :: namespace, and a
+ * specific namespace wasn't requested in the pattern, then add in
+ * all global :: commands that match the simple pattern. Of course,
+ * we add in only those commands that aren't hidden by a command in
+ * the effective namespace.
*/
-
- if (p == NULL) {
- Tcl_HashEntry *hPtr;
- Var *varPtr;
-
- if (strchr(argv[2], '(') != NULL) {
- noVar:
- iPtr->result = "0";
- return TCL_OK;
- }
- if (iPtr->varFramePtr == NULL) {
- hPtr = Tcl_FindHashEntry(&iPtr->globalTable, argv[2]);
- } else {
- hPtr = Tcl_FindHashEntry(&iPtr->varFramePtr->varTable, argv[2]);
- }
- if (hPtr == NULL) {
- goto noVar;
- }
- varPtr = (Var *) Tcl_GetHashValue(hPtr);
- if (varPtr->flags & VAR_UPVAR) {
- varPtr = varPtr->value.upvarPtr;
- }
- if (!(varPtr->flags & VAR_ARRAY)) {
- goto noVar;
+
+ if ((nsPtr != globalNsPtr) && !specificNsInPattern) {
+ entryPtr = Tcl_FirstHashEntry(&globalNsPtr->cmdTable, &search);
+ while (entryPtr != NULL) {
+ cmdName = Tcl_GetHashKey(&globalNsPtr->cmdTable, entryPtr);
+ if ((simplePattern == NULL)
+ || Tcl_StringMatch(cmdName, simplePattern)) {
+ if (Tcl_FindHashEntry(&nsPtr->cmdTable, cmdName) == NULL) {
+ Tcl_ListObjAppendElement(interp, listPtr,
+ Tcl_NewStringObj(cmdName, -1));
+ }
+ }
+ entryPtr = Tcl_NextHashEntry(&search);
}
}
- iPtr->result = "1";
- return TCL_OK;
- } else if ((c == 'g') && (strncmp(argv[1], "globals", length) == 0)) {
- char *name;
+ }
+
+ Tcl_SetObjResult(interp, listPtr);
+ return TCL_OK;
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * InfoCompleteCmd --
+ *
+ * Called to implement the "info complete" command that determines
+ * whether a string is a complete Tcl command. Handles the following
+ * syntax:
+ *
+ * info complete command
+ *
+ * Results:
+ * Returns TCL_OK is successful and TCL_ERROR is 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.
+ *
+ *----------------------------------------------------------------------
+ */
- if (argc > 3) {
- Tcl_AppendResult(interp, "wrong # args: should be \"", argv[0],
- " globals ?pattern?\"", (char *) NULL);
- return TCL_ERROR;
- }
- for (hPtr = Tcl_FirstHashEntry(&iPtr->globalTable, &search);
- hPtr != NULL; hPtr = Tcl_NextHashEntry(&search)) {
- varPtr = (Var *) Tcl_GetHashValue(hPtr);
- if (varPtr->flags & VAR_UNDEFINED) {
- continue;
- }
- name = Tcl_GetHashKey(&iPtr->globalTable, hPtr);
- if ((argc == 3) && !Tcl_StringMatch(name, argv[2])) {
- continue;
- }
- Tcl_AppendElement(interp, name);
- }
- return TCL_OK;
- } else if ((c == 'h') && (strncmp(argv[1], "hostname", length) == 0)) {
- if (argc > 2) {
- Tcl_AppendResult(interp, "wrong # args: should be \"", argv[0],
- " hostname\"", (char *) NULL);
- return TCL_ERROR;
- }
- Tcl_AppendResult(interp, Tcl_GetHostName(), NULL);
+static int
+InfoCompleteCmd(dummy, interp, objc, objv)
+ ClientData dummy; /* Not used. */
+ Tcl_Interp *interp; /* Current interpreter. */
+ int objc; /* Number of arguments. */
+ Tcl_Obj *CONST objv[]; /* Argument objects. */
+{
+ char *command;
+
+ if (objc != 3) {
+ Tcl_WrongNumArgs(interp, 1, objv, "complete command");
+ return TCL_ERROR;
+ }
+
+ command = Tcl_GetStringFromObj(objv[2], (int *) NULL);
+ if (Tcl_CommandComplete(command)) {
+ Tcl_SetIntObj(Tcl_GetObjResult(interp), 1);
+ } else {
+ Tcl_SetIntObj(Tcl_GetObjResult(interp), 0);
+ }
+ return TCL_OK;
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * InfoDefaultCmd --
+ *
+ * Called to implement the "info default" command that returns the
+ * default value for a procedure argument. Handles the following
+ * syntax:
+ *
+ * info default procName arg varName
+ *
+ * Results:
+ * Returns TCL_OK is successful and TCL_ERROR is 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.
+ *
+ *----------------------------------------------------------------------
+ */
+
+static int
+InfoDefaultCmd(dummy, interp, objc, objv)
+ ClientData dummy; /* Not used. */
+ Tcl_Interp *interp; /* Current interpreter. */
+ int objc; /* Number of arguments. */
+ Tcl_Obj *CONST objv[]; /* Argument objects. */
+{
+ Interp *iPtr = (Interp *) interp;
+ char *procName, *argName, *varName;
+ Proc *procPtr;
+ CompiledLocal *localPtr;
+ Tcl_Obj *valueObjPtr;
+
+ if (objc != 5) {
+ Tcl_WrongNumArgs(interp, 1, objv, "default procname arg varname");
+ return TCL_ERROR;
+ }
+
+ procName = Tcl_GetStringFromObj(objv[2], (int *) NULL);
+ argName = Tcl_GetStringFromObj(objv[3], (int *) NULL);
+
+ procPtr = TclFindProc(iPtr, procName);
+ if (procPtr == NULL) {
+ Tcl_AppendStringsToObj(Tcl_GetObjResult(interp),
+ "\"", procName, "\" isn't a procedure", (char *) NULL);
+ return TCL_ERROR;
+ }
+
+ for (localPtr = procPtr->firstLocalPtr; localPtr != NULL;
+ localPtr = localPtr->nextPtr) {
+ if ((localPtr->isArg) && (strcmp(argName, localPtr->name) == 0)) {
+ if (localPtr->defValuePtr != NULL) {
+ valueObjPtr = Tcl_ObjSetVar2(interp, objv[4], NULL,
+ localPtr->defValuePtr, 0);
+ if (valueObjPtr == NULL) {
+ defStoreError:
+ varName = Tcl_GetStringFromObj(objv[4], (int *) NULL);
+ Tcl_AppendStringsToObj(Tcl_GetObjResult(interp),
+ "couldn't store default value in variable \"",
+ varName, "\"", (char *) NULL);
+ return TCL_ERROR;
+ }
+ Tcl_SetIntObj(Tcl_GetObjResult(interp), 1);
+ } else {
+ Tcl_Obj *nullObjPtr = Tcl_NewObj();
+ valueObjPtr = Tcl_ObjSetVar2(interp, objv[4], NULL,
+ nullObjPtr, 0);
+ if (valueObjPtr == NULL) {
+ Tcl_DecrRefCount(nullObjPtr); /* free unneeded obj */
+ goto defStoreError;
+ }
+ Tcl_SetIntObj(Tcl_GetObjResult(interp), 0);
+ }
+ return TCL_OK;
+ }
+ }
+
+ Tcl_AppendStringsToObj(Tcl_GetObjResult(interp),
+ "procedure \"", procName, "\" doesn't have an argument \"",
+ argName, "\"", (char *) NULL);
+ return TCL_ERROR;
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * InfoExistsCmd --
+ *
+ * Called to implement the "info exists" command that determines
+ * whether a variable exists. Handles the following syntax:
+ *
+ * info exists varName
+ *
+ * Results:
+ * Returns TCL_OK is successful and TCL_ERROR is 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.
+ *
+ *----------------------------------------------------------------------
+ */
+
+static int
+InfoExistsCmd(dummy, interp, objc, objv)
+ ClientData dummy; /* Not used. */
+ Tcl_Interp *interp; /* Current interpreter. */
+ int objc; /* Number of arguments. */
+ Tcl_Obj *CONST objv[]; /* Argument objects. */
+{
+ char *varName;
+ Var *varPtr, *arrayPtr;
+
+ if (objc != 3) {
+ Tcl_WrongNumArgs(interp, 1, objv, "exists varName");
+ return TCL_ERROR;
+ }
+
+ varName = Tcl_GetStringFromObj(objv[2], (int *) NULL);
+ varPtr = TclLookupVar(interp, varName, (char *) NULL,
+ TCL_PARSE_PART1, "access",
+ /*createPart1*/ 0, /*createPart2*/ 0, &arrayPtr);
+ if ((varPtr != NULL) && !TclIsVarUndefined(varPtr)) {
+ Tcl_SetIntObj(Tcl_GetObjResult(interp), 1);
+ } else {
+ Tcl_SetIntObj(Tcl_GetObjResult(interp), 0);
+ }
+ return TCL_OK;
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * InfoGlobalsCmd --
+ *
+ * Called to implement the "info globals" command that returns the list
+ * of global variables matching an optional pattern. Handles the
+ * following syntax:
+ *
+ * info globals ?pattern?
+ *
+ * Results:
+ * Returns TCL_OK is successful and TCL_ERROR is 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.
+ *
+ *----------------------------------------------------------------------
+ */
+
+static int
+InfoGlobalsCmd(dummy, interp, objc, objv)
+ ClientData dummy; /* Not used. */
+ Tcl_Interp *interp; /* Current interpreter. */
+ int objc; /* Number of arguments. */
+ Tcl_Obj *CONST objv[]; /* Argument objects. */
+{
+ char *varName, *pattern;
+ Namespace *globalNsPtr = (Namespace *) Tcl_GetGlobalNamespace(interp);
+ register Tcl_HashEntry *entryPtr;
+ Tcl_HashSearch search;
+ Var *varPtr;
+ Tcl_Obj *listPtr;
+
+ if (objc == 2) {
+ pattern = NULL;
+ } else if (objc == 3) {
+ pattern = Tcl_GetStringFromObj(objv[2], (int *) NULL);
+ } else {
+ Tcl_WrongNumArgs(interp, 1, objv, "globals ?pattern?");
+ return TCL_ERROR;
+ }
+
+ /*
+ * Scan through the global :: namespace's variable table and create a
+ * list of all global variables that match the pattern.
+ */
+
+ listPtr = Tcl_NewListObj(0, (Tcl_Obj **) NULL);
+ for (entryPtr = Tcl_FirstHashEntry(&globalNsPtr->varTable, &search);
+ entryPtr != NULL;
+ entryPtr = Tcl_NextHashEntry(&search)) {
+ varPtr = (Var *) Tcl_GetHashValue(entryPtr);
+ if (TclIsVarUndefined(varPtr)) {
+ continue;
+ }
+ varName = Tcl_GetHashKey(&globalNsPtr->varTable, entryPtr);
+ if ((pattern == NULL) || Tcl_StringMatch(varName, pattern)) {
+ Tcl_ListObjAppendElement(interp, listPtr,
+ Tcl_NewStringObj(varName, -1));
+ }
+ }
+ Tcl_SetObjResult(interp, listPtr);
+ return TCL_OK;
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * InfoHostnameCmd --
+ *
+ * Called to implement the "info hostname" command that returns the
+ * host name. Handles the following syntax:
+ *
+ * info hostname
+ *
+ * Results:
+ * Returns TCL_OK is successful and TCL_ERROR is 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.
+ *
+ *----------------------------------------------------------------------
+ */
+
+static int
+InfoHostnameCmd(dummy, interp, objc, objv)
+ ClientData dummy; /* Not used. */
+ Tcl_Interp *interp; /* Current interpreter. */
+ int objc; /* Number of arguments. */
+ Tcl_Obj *CONST objv[]; /* Argument objects. */
+{
+ if (objc != 2) {
+ Tcl_WrongNumArgs(interp, 1, objv, "hostname");
+ return TCL_ERROR;
+ }
+
+ Tcl_SetStringObj(Tcl_GetObjResult(interp), Tcl_GetHostName(), -1);
+ return TCL_OK;
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * InfoLevelCmd --
+ *
+ * Called to implement the "info level" command that returns
+ * information about the call stack. Handles the following syntax:
+ *
+ * info level ?number?
+ *
+ * Results:
+ * Returns TCL_OK is successful and TCL_ERROR is 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.
+ *
+ *----------------------------------------------------------------------
+ */
+
+static int
+InfoLevelCmd(dummy, interp, objc, objv)
+ ClientData dummy; /* Not used. */
+ Tcl_Interp *interp; /* Current interpreter. */
+ int objc; /* Number of arguments. */
+ Tcl_Obj *CONST objv[]; /* Argument objects. */
+{
+ Interp *iPtr = (Interp *) interp;
+ int level;
+ CallFrame *framePtr;
+ Tcl_Obj *listPtr;
+
+ if (objc == 2) { /* just "info level" */
+ if (iPtr->varFramePtr == NULL) {
+ Tcl_SetIntObj(Tcl_GetObjResult(interp), 0);
+ } else {
+ Tcl_SetIntObj(Tcl_GetObjResult(interp), iPtr->varFramePtr->level);
+ }
return TCL_OK;
- } else if ((c == 'l') && (strncmp(argv[1], "level", length) == 0)
- && (length >= 2)) {
- if (argc == 2) {
- if (iPtr->varFramePtr == NULL) {
- iPtr->result = "0";
- } else {
- sprintf(iPtr->result, "%d", iPtr->varFramePtr->level);
- }
- return TCL_OK;
- } else if (argc == 3) {
- int level;
- CallFrame *framePtr;
+ } else if (objc == 3) {
+ if (Tcl_GetIntFromObj(interp, objv[2], &level) != TCL_OK) {
+ return TCL_ERROR;
+ }
+ if (level <= 0) {
+ if (iPtr->varFramePtr == NULL) {
+ levelError:
+ Tcl_AppendStringsToObj(Tcl_GetObjResult(interp),
+ "bad level \"",
+ Tcl_GetStringFromObj(objv[2], (int *) NULL),
+ "\"", (char *) NULL);
+ return TCL_ERROR;
+ }
+ level += iPtr->varFramePtr->level;
+ }
+ for (framePtr = iPtr->varFramePtr; framePtr != NULL;
+ framePtr = framePtr->callerVarPtr) {
+ if (framePtr->level == level) {
+ break;
+ }
+ }
+ if (framePtr == NULL) {
+ goto levelError;
+ }
+
+ listPtr = Tcl_NewListObj(framePtr->objc, framePtr->objv);
+ Tcl_SetObjResult(interp, listPtr);
+ return TCL_OK;
+ }
- if (Tcl_GetInt(interp, argv[2], &level) != TCL_OK) {
- return TCL_ERROR;
- }
- if (level <= 0) {
- if (iPtr->varFramePtr == NULL) {
- levelError:
- Tcl_AppendResult(interp, "bad level \"", argv[2],
- "\"", (char *) NULL);
- return TCL_ERROR;
- }
- level += iPtr->varFramePtr->level;
+ Tcl_WrongNumArgs(interp, 1, objv, "level ?number?");
+ return TCL_ERROR;
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * InfoLibraryCmd --
+ *
+ * Called to implement the "info library" command that returns the
+ * library directory for the Tcl installation. Handles the following
+ * syntax:
+ *
+ * info library
+ *
+ * Results:
+ * Returns TCL_OK is successful and TCL_ERROR is 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.
+ *
+ *----------------------------------------------------------------------
+ */
+
+static int
+InfoLibraryCmd(dummy, interp, objc, objv)
+ ClientData dummy; /* Not used. */
+ Tcl_Interp *interp; /* Current interpreter. */
+ int objc; /* Number of arguments. */
+ Tcl_Obj *CONST objv[]; /* Argument objects. */
+{
+ char *libDirName;
+
+ if (objc != 2) {
+ Tcl_WrongNumArgs(interp, 1, objv, "library");
+ return TCL_ERROR;
+ }
+
+ libDirName = Tcl_GetVar(interp, "tcl_library", TCL_GLOBAL_ONLY);
+ if (libDirName != NULL) {
+ Tcl_SetStringObj(Tcl_GetObjResult(interp), libDirName, -1);
+ return TCL_OK;
+ }
+ Tcl_SetStringObj(Tcl_GetObjResult(interp),
+ "no library has been specified for Tcl", -1);
+ return TCL_ERROR;
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * InfoLoadedCmd --
+ *
+ * Called to implement the "info loaded" command that returns the
+ * packages that have been loaded into an interpreter. Handles the
+ * following syntax:
+ *
+ * info loaded ?interp?
+ *
+ * Results:
+ * Returns TCL_OK is successful and TCL_ERROR is 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.
+ *
+ *----------------------------------------------------------------------
+ */
+
+static int
+InfoLoadedCmd(dummy, interp, objc, objv)
+ ClientData dummy; /* Not used. */
+ Tcl_Interp *interp; /* Current interpreter. */
+ int objc; /* Number of arguments. */
+ Tcl_Obj *CONST objv[]; /* Argument objects. */
+{
+ char *interpName;
+ int result;
+
+ if ((objc != 2) && (objc != 3)) {
+ Tcl_WrongNumArgs(interp, 1, objv, "loaded ?interp?");
+ return TCL_ERROR;
+ }
+
+ if (objc == 2) { /* get loaded pkgs in all interpreters */
+ interpName = NULL;
+ } else { /* get pkgs just in specified interp */
+ interpName = Tcl_GetStringFromObj(objv[2], (int *) NULL);
+ }
+ result = TclGetLoadedPackages(interp, interpName);
+ return result;
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * InfoLocalsCmd --
+ *
+ * Called to implement the "info locals" command to return a list of
+ * local variables that match an optional pattern. Handles the
+ * following syntax:
+ *
+ * info locals ?pattern?
+ *
+ * Results:
+ * Returns TCL_OK is successful and TCL_ERROR is 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.
+ *
+ *----------------------------------------------------------------------
+ */
+
+static int
+InfoLocalsCmd(dummy, interp, objc, objv)
+ ClientData dummy; /* Not used. */
+ Tcl_Interp *interp; /* Current interpreter. */
+ int objc; /* Number of arguments. */
+ Tcl_Obj *CONST objv[]; /* Argument objects. */
+{
+ Interp *iPtr = (Interp *) interp;
+ Var *varPtr;
+ char *varName, *pattern;
+ int i, localVarCt;
+ Tcl_HashTable *localVarTablePtr;
+ register Tcl_HashEntry *entryPtr;
+ Tcl_HashSearch search;
+ Tcl_Obj *listPtr;
+
+ if (objc == 2) {
+ pattern = NULL;
+ } else if (objc == 3) {
+ pattern = Tcl_GetStringFromObj(objv[2], (int *) NULL);
+ } else {
+ Tcl_WrongNumArgs(interp, 1, objv, "locals ?pattern?");
+ return TCL_ERROR;
+ }
+
+ if (iPtr->varFramePtr == NULL) {
+ return TCL_OK;
+ }
+ localVarTablePtr = iPtr->varFramePtr->varTablePtr;
+
+ /*
+ * Return a list containing names of first the compiled locals (i.e. the
+ * ones stored in the call frame), then the variables in the local hash
+ * table (if one exists).
+ */
+
+ listPtr = Tcl_NewListObj(0, (Tcl_Obj **) NULL);
+
+ localVarCt = iPtr->varFramePtr->numCompiledLocals;
+ for (i = 0, varPtr = iPtr->varFramePtr->compiledLocals;
+ i < localVarCt;
+ i++, varPtr++) {
+ if (!TclIsVarUndefined(varPtr)) {
+ varName = varPtr->name;
+ if ((pattern == NULL) || Tcl_StringMatch(varName, pattern)) {
+ Tcl_ListObjAppendElement(interp, listPtr,
+ Tcl_NewStringObj(varName, -1));
}
- for (framePtr = iPtr->varFramePtr; framePtr != NULL;
- framePtr = framePtr->callerVarPtr) {
- if (framePtr->level == level) {
- break;
+ }
+ }
+
+ if (localVarTablePtr != NULL) {
+ for (entryPtr = Tcl_FirstHashEntry(localVarTablePtr, &search);
+ entryPtr != NULL;
+ entryPtr = Tcl_NextHashEntry(&search)) {
+ varPtr = (Var *) Tcl_GetHashValue(entryPtr);
+ if (!TclIsVarUndefined(varPtr) && !TclIsVarLink(varPtr)) {
+ varName = Tcl_GetHashKey(localVarTablePtr, entryPtr);
+ if ((pattern == NULL)
+ || Tcl_StringMatch(varName, pattern)) {
+ Tcl_ListObjAppendElement(interp, listPtr,
+ Tcl_NewStringObj(varName, -1));
}
}
- if (framePtr == NULL) {
- goto levelError;
- }
- iPtr->result = Tcl_Merge(framePtr->argc, framePtr->argv);
- iPtr->freeProc = TCL_DYNAMIC;
- return TCL_OK;
}
- Tcl_AppendResult(interp, "wrong # args: should be \"", argv[0],
- " level [number]\"", (char *) NULL);
- return TCL_ERROR;
- } else if ((c == 'l') && (strncmp(argv[1], "library", length) == 0)
- && (length >= 2)) {
- if (argc != 2) {
- Tcl_AppendResult(interp, "wrong # args: should be \"", argv[0],
- " library\"", (char *) NULL);
+ }
+
+ Tcl_SetObjResult(interp, listPtr);
+ return TCL_OK;
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * InfoNameOfExecutableCmd --
+ *
+ * Called to implement the "info nameofexecutable" command that returns
+ * the name of the binary file running this application. Handles the
+ * following syntax:
+ *
+ * info nameofexecutable
+ *
+ * Results:
+ * Returns TCL_OK is successful and TCL_ERROR is 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.
+ *
+ *----------------------------------------------------------------------
+ */
+
+static int
+InfoNameOfExecutableCmd(dummy, interp, objc, objv)
+ ClientData dummy; /* Not used. */
+ Tcl_Interp *interp; /* Current interpreter. */
+ int objc; /* Number of arguments. */
+ Tcl_Obj *CONST objv[]; /* Argument objects. */
+{
+ if (objc != 2) {
+ Tcl_WrongNumArgs(interp, 1, objv, "nameofexecutable");
+ return TCL_ERROR;
+ }
+
+ if (tclExecutableName != NULL) {
+ Tcl_SetStringObj(Tcl_GetObjResult(interp), tclExecutableName, -1);
+ }
+ return TCL_OK;
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * InfoPatchLevelCmd --
+ *
+ * Called to implement the "info patchlevel" command that returns the
+ * default value for an argument to a procedure. Handles the following
+ * syntax:
+ *
+ * info patchlevel
+ *
+ * Results:
+ * Returns TCL_OK is successful and TCL_ERROR is 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.
+ *
+ *----------------------------------------------------------------------
+ */
+
+static int
+InfoPatchLevelCmd(dummy, interp, objc, objv)
+ ClientData dummy; /* Not used. */
+ Tcl_Interp *interp; /* Current interpreter. */
+ int objc; /* Number of arguments. */
+ Tcl_Obj *CONST objv[]; /* Argument objects. */
+{
+ char *patchlevel;
+
+ if (objc != 2) {
+ Tcl_WrongNumArgs(interp, 1, objv, "patchlevel");
+ return TCL_ERROR;
+ }
+
+ patchlevel = Tcl_GetVar(interp, "tcl_patchLevel",
+ (TCL_GLOBAL_ONLY | TCL_LEAVE_ERR_MSG));
+ if (patchlevel != NULL) {
+ Tcl_SetStringObj(Tcl_GetObjResult(interp), patchlevel, -1);
+ return TCL_OK;
+ }
+ return TCL_ERROR;
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * InfoProcsCmd --
+ *
+ * Called to implement the "info procs" command that returns the
+ * procedures in the current namespace that match an optional pattern.
+ * Handles the following syntax:
+ *
+ * info procs ?pattern?
+ *
+ * Results:
+ * Returns TCL_OK is successful and TCL_ERROR is 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.
+ *
+ *----------------------------------------------------------------------
+ */
+
+static int
+InfoProcsCmd(dummy, interp, objc, objv)
+ ClientData dummy; /* Not used. */
+ Tcl_Interp *interp; /* Current interpreter. */
+ int objc; /* Number of arguments. */
+ Tcl_Obj *CONST objv[]; /* Argument objects. */
+{
+ char *cmdName, *pattern;
+ Namespace *currNsPtr = (Namespace *) Tcl_GetCurrentNamespace(interp);
+ register Tcl_HashEntry *entryPtr;
+ Tcl_HashSearch search;
+ Command *cmdPtr;
+ Tcl_Obj *listPtr;
+
+ if (objc == 2) {
+ pattern = NULL;
+ } else if (objc == 3) {
+ pattern = Tcl_GetStringFromObj(objv[2], (int *) NULL);
+ } else {
+ Tcl_WrongNumArgs(interp, 1, objv, "procs ?pattern?");
+ return TCL_ERROR;
+ }
+
+ /*
+ * Scan through the current namespace's command table and return a list
+ * of all procs that match the pattern.
+ */
+
+ listPtr = Tcl_NewListObj(0, (Tcl_Obj **) NULL);
+ for (entryPtr = Tcl_FirstHashEntry(&currNsPtr->cmdTable, &search);
+ entryPtr != NULL;
+ entryPtr = Tcl_NextHashEntry(&search)) {
+ cmdName = Tcl_GetHashKey(&currNsPtr->cmdTable, entryPtr);
+ cmdPtr = (Command *) Tcl_GetHashValue(entryPtr);
+ if (TclIsProc(cmdPtr)) {
+ if ((pattern == NULL) || Tcl_StringMatch(cmdName, pattern)) {
+ Tcl_ListObjAppendElement(interp, listPtr,
+ Tcl_NewStringObj(cmdName, -1));
+ }
+ }
+ }
+ Tcl_SetObjResult(interp, listPtr);
+ return TCL_OK;
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * InfoScriptCmd --
+ *
+ * Called to implement the "info script" command that returns the
+ * script file that is currently being evaluated. Handles the
+ * following syntax:
+ *
+ * info script
+ *
+ * Results:
+ * Returns TCL_OK is successful and TCL_ERROR is 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.
+ *
+ *----------------------------------------------------------------------
+ */
+
+static int
+InfoScriptCmd(dummy, interp, objc, objv)
+ ClientData dummy; /* Not used. */
+ Tcl_Interp *interp; /* Current interpreter. */
+ int objc; /* Number of arguments. */
+ Tcl_Obj *CONST objv[]; /* Argument objects. */
+{
+ Interp *iPtr = (Interp *) interp;
+ if (objc != 2) {
+ Tcl_WrongNumArgs(interp, 1, objv, "script");
+ return TCL_ERROR;
+ }
+
+ if (iPtr->scriptFile != NULL) {
+ Tcl_SetStringObj(Tcl_GetObjResult(interp), iPtr->scriptFile, -1);
+ }
+ return TCL_OK;
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * InfoSharedlibCmd --
+ *
+ * Called to implement the "info sharedlibextension" command that
+ * returns the file extension used for shared libraries. Handles the
+ * following syntax:
+ *
+ * info sharedlibextension
+ *
+ * Results:
+ * Returns TCL_OK is successful and TCL_ERROR is 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.
+ *
+ *----------------------------------------------------------------------
+ */
+
+static int
+InfoSharedlibCmd(dummy, interp, objc, objv)
+ ClientData dummy; /* Not used. */
+ Tcl_Interp *interp; /* Current interpreter. */
+ int objc; /* Number of arguments. */
+ Tcl_Obj *CONST objv[]; /* Argument objects. */
+{
+ if (objc != 2) {
+ Tcl_WrongNumArgs(interp, 1, objv, "sharedlibextension");
+ return TCL_ERROR;
+ }
+
+#ifdef TCL_SHLIB_EXT
+ Tcl_SetStringObj(Tcl_GetObjResult(interp), TCL_SHLIB_EXT, -1);
+#endif
+ return TCL_OK;
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * InfoTclVersionCmd --
+ *
+ * Called to implement the "info tclversion" command that returns the
+ * version number for this Tcl library. Handles the following syntax:
+ *
+ * info tclversion
+ *
+ * Results:
+ * Returns TCL_OK is successful and TCL_ERROR is 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.
+ *
+ *----------------------------------------------------------------------
+ */
+
+static int
+InfoTclVersionCmd(dummy, interp, objc, objv)
+ ClientData dummy; /* Not used. */
+ Tcl_Interp *interp; /* Current interpreter. */
+ int objc; /* Number of arguments. */
+ Tcl_Obj *CONST objv[]; /* Argument objects. */
+{
+ char *version;
+
+ if (objc != 2) {
+ Tcl_WrongNumArgs(interp, 1, objv, "tclversion");
+ return TCL_ERROR;
+ }
+
+ version = Tcl_GetVar(interp, "tcl_version",
+ (TCL_GLOBAL_ONLY | TCL_LEAVE_ERR_MSG));
+ if (version != NULL) {
+ Tcl_SetStringObj(Tcl_GetObjResult(interp), version, -1);
+ return TCL_OK;
+ }
+ return TCL_ERROR;
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * InfoVarsCmd --
+ *
+ * Called to implement the "info vars" command that returns the
+ * list of variables in the interpreter that match an optional pattern.
+ * The pattern, if any, consists of an optional sequence of namespace
+ * names separated by "::" qualifiers, which is followed by a
+ * glob-style pattern that restricts which variables are returned.
+ * Handles the following syntax:
+ *
+ * info vars ?pattern?
+ *
+ * Results:
+ * Returns TCL_OK is successful and TCL_ERROR is 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.
+ *
+ *----------------------------------------------------------------------
+ */
+
+static int
+InfoVarsCmd(dummy, interp, objc, objv)
+ ClientData dummy; /* Not used. */
+ Tcl_Interp *interp; /* Current interpreter. */
+ int objc; /* Number of arguments. */
+ Tcl_Obj *CONST objv[]; /* Argument objects. */
+{
+ Interp *iPtr = (Interp *) interp;
+ char *varName, *pattern, *simplePattern;
+ register Tcl_HashEntry *entryPtr;
+ Tcl_HashSearch search;
+ Var *varPtr, *localVarPtr;
+ Namespace *nsPtr;
+ Namespace *globalNsPtr = (Namespace *) Tcl_GetGlobalNamespace(interp);
+ Namespace *currNsPtr = (Namespace *) Tcl_GetCurrentNamespace(interp);
+ Tcl_Obj *listPtr, *elemObjPtr;
+ int specificNsInPattern = 0; /* Init. to avoid compiler warning. */
+ int i, result;
+
+ /*
+ * Get the pattern and find the "effective namespace" in which to
+ * list variables. We only use this effective namespace if there's
+ * no active Tcl procedure frame.
+ */
+
+ if (objc == 2) {
+ simplePattern = NULL;
+ nsPtr = currNsPtr;
+ specificNsInPattern = 0;
+ } else if (objc == 3) {
+ /*
+ * From the pattern, get the effective namespace and the simple
+ * pattern (no namespace qualifiers or ::'s) at the end. If an
+ * error was found while parsing the pattern, return it. Otherwise,
+ * if the namespace wasn't found, just leave nsPtr NULL: we will
+ * return an empty list since no variables there can be found.
+ */
+
+ Namespace *dummy1NsPtr, *dummy2NsPtr;
+
+ pattern = Tcl_GetStringFromObj(objv[2], (int *) NULL);
+ result = TclGetNamespaceForQualName(interp, pattern,
+ (Namespace *) NULL, /*flags*/ TCL_LEAVE_ERR_MSG,
+ &nsPtr, &dummy1NsPtr, &dummy2NsPtr, &simplePattern);
+ if (result != TCL_OK) {
return TCL_ERROR;
}
- interp->result = Tcl_GetVar(interp, "tcl_library", TCL_GLOBAL_ONLY);
- if (interp->result == NULL) {
- interp->result = "no library has been specified for Tcl";
- return TCL_ERROR;
+ if (nsPtr != NULL) { /* we successfully found the pattern's ns */
+ specificNsInPattern = (strcmp(simplePattern, pattern) != 0);
}
+ } else {
+ Tcl_WrongNumArgs(interp, 1, objv, "vars ?pattern?");
+ return TCL_ERROR;
+ }
+
+ /*
+ * If the namespace specified in the pattern wasn't found, just return.
+ */
+
+ if (nsPtr == NULL) {
return TCL_OK;
- } else if ((c == 'l') && (strncmp(argv[1], "loaded", length) == 0)
- && (length >= 3)) {
- if ((argc != 2) && (argc != 3)) {
- Tcl_AppendResult(interp, "wrong # args: should be \"", argv[0],
- " loaded ?interp?\"", (char *) NULL);
- return TCL_ERROR;
- }
- return TclGetLoadedPackages(interp, argv[2]);
- } else if ((c == 'l') && (strncmp(argv[1], "locals", length) == 0)
- && (length >= 3)) {
- char *name;
-
- if (argc > 3) {
- Tcl_AppendResult(interp, "wrong # args: should be \"", argv[0],
- " locals ?pattern?\"", (char *) NULL);
- return TCL_ERROR;
- }
- if (iPtr->varFramePtr == NULL) {
- return TCL_OK;
- }
- for (hPtr = Tcl_FirstHashEntry(&iPtr->varFramePtr->varTable, &search);
- hPtr != NULL; hPtr = Tcl_NextHashEntry(&search)) {
- varPtr = (Var *) Tcl_GetHashValue(hPtr);
- if (varPtr->flags & (VAR_UNDEFINED|VAR_UPVAR)) {
- continue;
- }
- name = Tcl_GetHashKey(&iPtr->varFramePtr->varTable, hPtr);
- if ((argc == 3) && !Tcl_StringMatch(name, argv[2])) {
- continue;
+ }
+
+ listPtr = Tcl_NewListObj(0, (Tcl_Obj **) NULL);
+
+ if ((iPtr->varFramePtr == NULL)
+ || !iPtr->varFramePtr->isProcCallFrame
+ || specificNsInPattern) {
+ /*
+ * There is no frame pointer, the frame pointer was pushed only
+ * to activate a namespace, or we are in a procedure call frame
+ * but a specific namespace was specified. Create a list containing
+ * only the variables in the effective namespace's variable table.
+ */
+
+ entryPtr = Tcl_FirstHashEntry(&nsPtr->varTable, &search);
+ while (entryPtr != NULL) {
+ varPtr = (Var *) Tcl_GetHashValue(entryPtr);
+ if (!TclIsVarUndefined(varPtr)) {
+ varName = Tcl_GetHashKey(&nsPtr->varTable, entryPtr);
+ if ((simplePattern == NULL)
+ || Tcl_StringMatch(varName, simplePattern)) {
+ if (specificNsInPattern) {
+ elemObjPtr = Tcl_NewObj();
+ Tcl_GetVariableFullName(interp, (Tcl_Var) varPtr,
+ elemObjPtr);
+ } else {
+ elemObjPtr = Tcl_NewStringObj(varName, -1);
+ }
+ Tcl_ListObjAppendElement(interp, listPtr, elemObjPtr);
+ }
}
- Tcl_AppendElement(interp, name);
+ entryPtr = Tcl_NextHashEntry(&search);
}
- return TCL_OK;
- } else if ((c == 'n') && (strncmp(argv[1], "nameofexecutable",
- length) == 0)) {
- if (argc != 2) {
- Tcl_AppendResult(interp, "wrong # args: should be \"", argv[0],
- " nameofexecutable\"", (char *) NULL);
- return TCL_ERROR;
- }
- if (tclExecutableName != NULL) {
- interp->result = tclExecutableName;
- }
- return TCL_OK;
- } else if ((c == 'p') && (strncmp(argv[1], "patchlevel", length) == 0)
- && (length >= 2)) {
- char *value;
- if (argc != 2) {
- Tcl_AppendResult(interp, "wrong # args: should be \"", argv[0],
- " patchlevel\"", (char *) NULL);
- return TCL_ERROR;
- }
- value = Tcl_GetVar(interp, "tcl_patchLevel",
- TCL_GLOBAL_ONLY|TCL_LEAVE_ERR_MSG);
- if (value == NULL) {
- return TCL_ERROR;
- }
- interp->result = value;
- return TCL_OK;
- } else if ((c == 'p') && (strncmp(argv[1], "procs", length) == 0)
- && (length >= 2)) {
- if (argc > 3) {
- Tcl_AppendResult(interp, "wrong # args: should be \"", argv[0],
- " procs ?pattern?\"", (char *) NULL);
- return TCL_ERROR;
- }
- for (hPtr = Tcl_FirstHashEntry(&iPtr->commandTable, &search);
- hPtr != NULL; hPtr = Tcl_NextHashEntry(&search)) {
- char *name = Tcl_GetHashKey(&iPtr->commandTable, hPtr);
+ /*
+ * If the effective namespace isn't the global :: namespace, and a
+ * specific namespace wasn't requested in the pattern (i.e., the
+ * pattern only specifies variable names), then add in all global ::
+ * variables that match the simple pattern. Of course, add in only
+ * those variables that aren't hidden by a variable in the effective
+ * namespace.
+ */
- cmdPtr = (Command *) Tcl_GetHashValue(hPtr);
- if (!TclIsProc(cmdPtr)) {
- continue;
- }
- if ((argc == 3) && !Tcl_StringMatch(name, argv[2])) {
- continue;
+ if ((nsPtr != globalNsPtr) && !specificNsInPattern) {
+ entryPtr = Tcl_FirstHashEntry(&globalNsPtr->varTable, &search);
+ while (entryPtr != NULL) {
+ varPtr = (Var *) Tcl_GetHashValue(entryPtr);
+ if (!TclIsVarUndefined(varPtr)) {
+ varName = Tcl_GetHashKey(&globalNsPtr->varTable,
+ entryPtr);
+ if ((simplePattern == NULL)
+ || Tcl_StringMatch(varName, simplePattern)) {
+ if (Tcl_FindHashEntry(&nsPtr->varTable, varName) == NULL) {
+ Tcl_ListObjAppendElement(interp, listPtr,
+ Tcl_NewStringObj(varName, -1));
+ }
+ }
+ }
+ entryPtr = Tcl_NextHashEntry(&search);
}
- Tcl_AppendElement(interp, name);
- }
- return TCL_OK;
- } else if ((c == 's') && (strncmp(argv[1], "script", length) == 0)
- && (length >= 2)) {
- if (argc != 2) {
- Tcl_AppendResult(interp, "wrong # args: should be \"",
- argv[0], " script\"", (char *) NULL);
- return TCL_ERROR;
}
- if (iPtr->scriptFile != NULL) {
- /*
- * Can't depend on iPtr->scriptFile to be non-volatile:
- * if this command is returned as the result of the script,
- * then iPtr->scriptFile will go away.
- */
+ } else {
+ /*
+ * We're in a local call frame and no specific namespace was
+ * specific. Create a list that starts with the compiled locals
+ * (i.e. the ones stored in the call frame).
+ */
- Tcl_SetResult(interp, iPtr->scriptFile, TCL_VOLATILE);
- }
- return TCL_OK;
- } else if ((c == 's') && (strncmp(argv[1], "sharedlibextension",
- length) == 0) && (length >= 2)) {
- if (argc != 2) {
- Tcl_AppendResult(interp, "wrong # args: should be \"",
- argv[0], " sharedlibextension\"", (char *) NULL);
- return TCL_ERROR;
- }
-#ifdef TCL_SHLIB_EXT
- interp->result = TCL_SHLIB_EXT;
-#endif
- return TCL_OK;
- } else if ((c == 't') && (strncmp(argv[1], "tclversion", length) == 0)) {
- char *value;
+ CallFrame *varFramePtr = iPtr->varFramePtr;
+ int localVarCt = varFramePtr->numCompiledLocals;
+ Tcl_HashTable *varTablePtr = varFramePtr->varTablePtr;
+
+ for (i = 0, localVarPtr = iPtr->varFramePtr->compiledLocals;
+ i < localVarCt;
+ i++, localVarPtr++) {
+ if (!TclIsVarUndefined(localVarPtr)) {
+ varName = localVarPtr->name;
+ if ((simplePattern == NULL)
+ || Tcl_StringMatch(varName, simplePattern)) {
+ Tcl_ListObjAppendElement(interp, listPtr,
+ Tcl_NewStringObj(varName, -1));
+ }
+ }
+ }
- if (argc != 2) {
- Tcl_AppendResult(interp, "wrong # args: should be \"",
- argv[0], " tclversion\"", (char *) NULL);
- return TCL_ERROR;
- }
- value = Tcl_GetVar(interp, "tcl_version",
- TCL_GLOBAL_ONLY|TCL_LEAVE_ERR_MSG);
- if (value == NULL) {
- return TCL_ERROR;
- }
- interp->result = value;
- return TCL_OK;
- } else if ((c == 'v') && (strncmp(argv[1], "vars", length)) == 0) {
- Tcl_HashTable *tablePtr;
- char *name;
+ /*
+ * Now add in the variables in the call frame's variable hash
+ * table (if one exists).
+ */
- if (argc > 3) {
- Tcl_AppendResult(interp, "wrong # args: should be \"",
- argv[0], " vars ?pattern?\"", (char *) NULL);
- return TCL_ERROR;
- }
- if (iPtr->varFramePtr == NULL) {
- tablePtr = &iPtr->globalTable;
- } else {
- tablePtr = &iPtr->varFramePtr->varTable;
- }
- for (hPtr = Tcl_FirstHashEntry(tablePtr, &search);
- hPtr != NULL; hPtr = Tcl_NextHashEntry(&search)) {
- varPtr = (Var *) Tcl_GetHashValue(hPtr);
- if (varPtr->flags & VAR_UNDEFINED) {
- continue;
- }
- name = Tcl_GetHashKey(tablePtr, hPtr);
- if ((argc == 3) && !Tcl_StringMatch(name, argv[2])) {
- continue;
+ if (varTablePtr != NULL) {
+ for (entryPtr = Tcl_FirstHashEntry(varTablePtr, &search);
+ entryPtr != NULL;
+ entryPtr = Tcl_NextHashEntry(&search)) {
+ varPtr = (Var *) Tcl_GetHashValue(entryPtr);
+ if (!TclIsVarUndefined(varPtr)) {
+ varName = Tcl_GetHashKey(varTablePtr, entryPtr);
+ if ((simplePattern == NULL)
+ || Tcl_StringMatch(varName, simplePattern)) {
+ Tcl_ListObjAppendElement(interp, listPtr,
+ Tcl_NewStringObj(varName, -1));
+ }
+ }
}
- Tcl_AppendElement(interp, name);
}
- return TCL_OK;
- } else {
- Tcl_AppendResult(interp, "bad option \"", argv[1],
- "\": should be args, body, cmdcount, commands, ",
- "complete, default, ",
- "exists, globals, hostname, level, library, loaded, locals, ",
- "nameofexecutable, patchlevel, procs, script, ",
- "sharedlibextension, tclversion, or vars",
- (char *) NULL);
- return TCL_ERROR;
}
+
+ Tcl_SetObjResult(interp, listPtr);
+ return TCL_OK;
}
/*
*----------------------------------------------------------------------
*
- * Tcl_JoinCmd --
+ * Tcl_JoinObjCmd --
*
* This procedure is invoked to process the "join" Tcl command.
* See the user documentation for details on what it does.
*
* Results:
- * A standard Tcl result.
+ * A standard Tcl object result.
*
* Side effects:
* See the user documentation.
@@ -674,50 +1737,63 @@ Tcl_InfoCmd(dummy, interp, argc, argv)
/* ARGSUSED */
int
-Tcl_JoinCmd(dummy, interp, argc, argv)
- ClientData dummy; /* Not used. */
- Tcl_Interp *interp; /* Current interpreter. */
- int argc; /* Number of arguments. */
- char **argv; /* Argument strings. */
+Tcl_JoinObjCmd(dummy, interp, objc, objv)
+ ClientData dummy; /* Not used. */
+ Tcl_Interp *interp; /* Current interpreter. */
+ int objc; /* Number of arguments. */
+ Tcl_Obj *CONST objv[]; /* The argument objects. */
{
- char *joinString;
- char **listArgv;
- int listArgc, i;
+ char *joinString, *bytes;
+ int joinLength, listLen, length, i, result;
+ Tcl_Obj **elemPtrs;
- if (argc == 2) {
+ if (objc == 2) {
joinString = " ";
- } else if (argc == 3) {
- joinString = argv[2];
+ joinLength = 1;
+ } else if (objc == 3) {
+ joinString = Tcl_GetStringFromObj(objv[2], &joinLength);
} else {
- Tcl_AppendResult(interp, "wrong # args: should be \"", argv[0],
- " list ?joinString?\"", (char *) NULL);
+ Tcl_WrongNumArgs(interp, 1, objv, "list ?joinString?");
return TCL_ERROR;
}
- if (Tcl_SplitList(interp, argv[1], &listArgc, &listArgv) != TCL_OK) {
- 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.
+ */
+
+ result = Tcl_ListObjGetElements(interp, objv[1], &listLen, &elemPtrs);
+ if (result != TCL_OK) {
+ return result;
}
- for (i = 0; i < listArgc; i++) {
- if (i == 0) {
- Tcl_AppendResult(interp, listArgv[0], (char *) NULL);
- } else {
- Tcl_AppendResult(interp, joinString, listArgv[i], (char *) NULL);
+
+ /*
+ * Now concatenate strings to form the "joined" result. We append
+ * directly into the interpreter's result object.
+ */
+
+ for (i = 0; i < listLen; i++) {
+ bytes = Tcl_GetStringFromObj(elemPtrs[i], &length);
+ if (i > 0) {
+ Tcl_AppendStringsToObj(Tcl_GetObjResult(interp), joinString,
+ bytes, (char *) NULL);
+ } else {
+ Tcl_AppendToObj(Tcl_GetObjResult(interp), bytes, length);
}
}
- ckfree((char *) listArgv);
return TCL_OK;
}
/*
*----------------------------------------------------------------------
*
- * Tcl_LindexCmd --
+ * Tcl_LindexObjCmd --
*
- * This procedure is invoked to process the "lindex" Tcl command.
- * See the user documentation for details on what it does.
+ * This object-based procedure is invoked to process the "lindex" Tcl
+ * command. See the user documentation for details on what it does.
*
* Results:
- * A standard Tcl result.
+ * A standard Tcl object result.
*
* Side effects:
* See the user documentation.
@@ -727,69 +1803,80 @@ Tcl_JoinCmd(dummy, interp, argc, argv)
/* ARGSUSED */
int
-Tcl_LindexCmd(dummy, interp, argc, argv)
- ClientData dummy; /* Not used. */
- Tcl_Interp *interp; /* Current interpreter. */
- int argc; /* Number of arguments. */
- char **argv; /* Argument strings. */
+Tcl_LindexObjCmd(dummy, interp, objc, objv)
+ ClientData dummy; /* Not used. */
+ Tcl_Interp *interp; /* Current interpreter. */
+ int objc; /* Number of arguments. */
+ Tcl_Obj *CONST objv[]; /* Argument objects. */
{
- char *p, *element, *next;
- int index, size, parenthesized, result, returnLast;
+ Tcl_Obj *listPtr;
+ Tcl_Obj **elemPtrs;
+ int listLen, index, result;
- if (argc != 3) {
- Tcl_AppendResult(interp, "wrong # args: should be \"", argv[0],
- " list index\"", (char *) NULL);
+ if (objc != 3) {
+ Tcl_WrongNumArgs(interp, 1, objv, "list index");
return TCL_ERROR;
}
- if ((*argv[2] == 'e') && (strncmp(argv[2], "end", strlen(argv[2])) == 0)) {
- returnLast = 1;
- index = INT_MAX;
- } else {
- returnLast = 0;
- if (Tcl_GetInt(interp, argv[2], &index) != TCL_OK) {
- return TCL_ERROR;
- }
+
+ /*
+ * Convert the first argument to a list if necessary.
+ */
+
+ listPtr = objv[1];
+ result = Tcl_ListObjGetElements(interp, listPtr, &listLen, &elemPtrs);
+ if (result != TCL_OK) {
+ return result;
+ }
+
+ /*
+ * Get the index from objv[2].
+ */
+
+ result = TclGetIntForIndex(interp, objv[2], /*endValue*/ (listLen - 1),
+ &index);
+ if (result != TCL_OK) {
+ return result;
}
- if (index < 0) {
+ if ((index < 0) || (index >= listLen)) {
+ /*
+ * The index is out of range: the result is an empty string object.
+ */
+
return TCL_OK;
}
- for (p = argv[1] ; index >= 0; index--) {
- result = TclFindElement(interp, p, &element, &next, &size,
- &parenthesized);
+
+ /*
+ * Make sure listPtr still refers to a list object. It might have been
+ * converted to an int above if the argument objects were shared.
+ */
+
+ if (listPtr->typePtr != &tclListType) {
+ result = Tcl_ListObjGetElements(interp, listPtr, &listLen,
+ &elemPtrs);
if (result != TCL_OK) {
return result;
}
- if ((*next == 0) && returnLast) {
- break;
- }
- p = next;
- }
- if (size == 0) {
- return TCL_OK;
- }
- if (size >= TCL_RESULT_SIZE) {
- interp->result = (char *) ckalloc((unsigned) size+1);
- interp->freeProc = TCL_DYNAMIC;
- }
- if (parenthesized) {
- memcpy((VOID *) interp->result, (VOID *) element, (size_t) size);
- interp->result[size] = 0;
- } else {
- TclCopyAndCollapse(size, element, interp->result);
}
+
+ /*
+ * Set the interpreter's object result to the index-th list element.
+ */
+
+ Tcl_SetObjResult(interp, elemPtrs[index]);
return TCL_OK;
}
/*
*----------------------------------------------------------------------
*
- * Tcl_LinsertCmd --
+ * Tcl_LinsertObjCmd --
*
- * This procedure is invoked to process the "linsert" Tcl command.
- * See the user documentation for details on what it does.
+ * This object-based procedure is invoked to process the "linsert" Tcl
+ * command. See the user documentation for details on what it does.
*
* Results:
- * A standard Tcl result.
+ * A new Tcl list object formed by inserting zero or more elements
+ * into a list.
*
* Side effects:
* See the user documentation.
@@ -799,70 +1886,75 @@ Tcl_LindexCmd(dummy, interp, argc, argv)
/* ARGSUSED */
int
-Tcl_LinsertCmd(dummy, interp, argc, argv)
- ClientData dummy; /* Not used. */
- Tcl_Interp *interp; /* Current interpreter. */
- int argc; /* Number of arguments. */
- char **argv; /* Argument strings. */
+Tcl_LinsertObjCmd(dummy, interp, objc, objv)
+ ClientData dummy; /* Not used. */
+ Tcl_Interp *interp; /* Current interpreter. */
+ register int objc; /* Number of arguments. */
+ Tcl_Obj *CONST objv[]; /* Argument objects. */
{
- char *p, *element, savedChar;
- int i, index, count, result, size;
+ Tcl_Obj *listPtr, *resultPtr;
+ int index, isDuplicate;
+ int result;
- if (argc < 4) {
- Tcl_AppendResult(interp, "wrong # args: should be \"", argv[0],
- " list index element ?element ...?\"", (char *) NULL);
- return TCL_ERROR;
- }
- if ((*argv[2] == 'e') && (strncmp(argv[2], "end", strlen(argv[2])) == 0)) {
- index = INT_MAX;
- } else if (Tcl_GetInt(interp, argv[2], &index) != TCL_OK) {
+ if (objc < 4) {
+ Tcl_WrongNumArgs(interp, 1, objv, "list index element ?element ...?");
return TCL_ERROR;
}
/*
- * Skip over the first "index" elements of the list, then add
- * all of those elements to the result.
+ * Get the index first since, if a conversion to int is needed, it
+ * will invalidate the list's internal representation.
*/
- size = 0;
- element = argv[1];
- for (count = 0, p = argv[1]; (count < index) && (*p != 0); count++) {
- result = TclFindElement(interp, p, &element, &p, &size, (int *) NULL);
- if (result != TCL_OK) {
- return result;
- }
- }
- if (*p == 0) {
- Tcl_AppendResult(interp, argv[1], (char *) NULL);
- } else {
- char *end;
-
- end = element+size;
- if (element != argv[1]) {
- while ((*end != 0) && !isspace(UCHAR(*end))) {
- end++;
- }
- }
- savedChar = *end;
- *end = 0;
- Tcl_AppendResult(interp, argv[1], (char *) NULL);
- *end = savedChar;
+ result = TclGetIntForIndex(interp, objv[2], /*endValue*/ INT_MAX,
+ &index);
+ if (result != TCL_OK) {
+ return result;
}
/*
- * Add the new list elements.
+ * If the list object is unshared we can modify it directly. Otherwise
+ * we create a copy to modify: this is "copy on write". We create the
+ * duplicate directly in the interpreter's object result.
*/
-
- for (i = 3; i < argc; i++) {
- Tcl_AppendElement(interp, argv[i]);
+
+ listPtr = objv[1];
+ isDuplicate = 0;
+ if (Tcl_IsShared(listPtr)) {
+ Tcl_ResetResult(interp);
+ resultPtr = Tcl_GetObjResult(interp);
+ if (listPtr->typePtr != NULL) {
+ Tcl_InvalidateStringRep(resultPtr);
+ listPtr->typePtr->dupIntRepProc(listPtr, resultPtr);
+ } else if (listPtr->bytes != NULL) {
+ int len = listPtr->length;
+
+ TclInitStringRep(resultPtr, listPtr->bytes, len);
+ }
+ listPtr = resultPtr;
+ isDuplicate = 1;
}
+
+ if ((objc == 4) && (index == INT_MAX)) {
+ /*
+ * Special case: insert one element at the end of the list.
+ */
+ result = Tcl_ListObjAppendElement(interp, listPtr, objv[3]);
+ } else if (objc > 3) {
+ result = Tcl_ListObjReplace(interp, listPtr, index, 0,
+ (objc-3), &(objv[3]));
+ }
+ if (result != TCL_OK) {
+ return result;
+ }
+
/*
- * Append the remainder of the original list.
+ * Set the interpreter's object result.
*/
- if (*p != 0) {
- Tcl_AppendResult(interp, " ", p, (char *) NULL);
+ if (!isDuplicate) {
+ Tcl_SetObjResult(interp, listPtr);
}
return TCL_OK;
}
@@ -870,13 +1962,13 @@ Tcl_LinsertCmd(dummy, interp, argc, argv)
/*
*----------------------------------------------------------------------
*
- * Tcl_ListCmd --
+ * Tcl_ListObjCmd --
*
* This procedure is invoked to process the "list" Tcl command.
* See the user documentation for details on what it does.
*
* Results:
- * A standard Tcl result.
+ * A standard Tcl object result.
*
* Side effects:
* See the user documentation.
@@ -886,15 +1978,19 @@ Tcl_LinsertCmd(dummy, interp, argc, argv)
/* ARGSUSED */
int
-Tcl_ListCmd(dummy, interp, argc, argv)
+Tcl_ListObjCmd(dummy, interp, objc, objv)
ClientData dummy; /* Not used. */
Tcl_Interp *interp; /* Current interpreter. */
- int argc; /* Number of arguments. */
- char **argv; /* Argument strings. */
+ register int objc; /* Number of arguments. */
+ register Tcl_Obj *CONST objv[]; /* The argument objects. */
{
- if (argc >= 2) {
- interp->result = Tcl_Merge(argc-1, argv+1);
- interp->freeProc = TCL_DYNAMIC;
+ /*
+ * If there are no list elements, the result is an empty object.
+ * Otherwise modify the interpreter's result object to be a list object.
+ */
+
+ if (objc > 1) {
+ Tcl_SetListObj(Tcl_GetObjResult(interp), (objc-1), &(objv[1]));
}
return TCL_OK;
}
@@ -902,13 +1998,13 @@ Tcl_ListCmd(dummy, interp, argc, argv)
/*
*----------------------------------------------------------------------
*
- * Tcl_LlengthCmd --
+ * Tcl_LlengthObjCmd --
*
- * This procedure is invoked to process the "llength" Tcl command.
- * See the user documentation for details on what it does.
+ * This object-based procedure is invoked to process the "llength" Tcl
+ * command. See the user documentation for details on what it does.
*
* Results:
- * A standard Tcl result.
+ * A standard Tcl object result.
*
* Side effects:
* See the user documentation.
@@ -918,44 +2014,43 @@ Tcl_ListCmd(dummy, interp, argc, argv)
/* ARGSUSED */
int
-Tcl_LlengthCmd(dummy, interp, argc, argv)
+Tcl_LlengthObjCmd(dummy, interp, objc, objv)
ClientData dummy; /* Not used. */
Tcl_Interp *interp; /* Current interpreter. */
- int argc; /* Number of arguments. */
- char **argv; /* Argument strings. */
+ int objc; /* Number of arguments. */
+ register Tcl_Obj *CONST objv[]; /* Argument objects. */
{
- int count, result;
- char *element, *p;
+ int listLen, result;
- if (argc != 2) {
- Tcl_AppendResult(interp, "wrong # args: should be \"", argv[0],
- " list\"", (char *) NULL);
+ if (objc != 2) {
+ Tcl_WrongNumArgs(interp, 1, objv, "list");
return TCL_ERROR;
}
- for (count = 0, p = argv[1]; *p != 0 ; count++) {
- result = TclFindElement(interp, p, &element, &p, (int *) NULL,
- (int *) NULL);
- if (result != TCL_OK) {
- return result;
- }
- if (*element == 0) {
- break;
- }
+
+ result = Tcl_ListObjLength(interp, objv[1], &listLen);
+ if (result != TCL_OK) {
+ return result;
}
- sprintf(interp->result, "%d", count);
+
+ /*
+ * Set the interpreter's object result to an integer object holding the
+ * length.
+ */
+
+ Tcl_SetIntObj(Tcl_GetObjResult(interp), listLen);
return TCL_OK;
}
/*
*----------------------------------------------------------------------
*
- * Tcl_LrangeCmd --
+ * Tcl_LrangeObjCmd --
*
* This procedure is invoked to process the "lrange" Tcl command.
* See the user documentation for details on what it does.
*
* Results:
- * A standard Tcl result.
+ * A standard Tcl object result.
*
* Side effects:
* See the user documentation.
@@ -965,103 +2060,92 @@ Tcl_LlengthCmd(dummy, interp, argc, argv)
/* ARGSUSED */
int
-Tcl_LrangeCmd(notUsed, interp, argc, argv)
+Tcl_LrangeObjCmd(notUsed, interp, objc, objv)
ClientData notUsed; /* Not used. */
Tcl_Interp *interp; /* Current interpreter. */
- int argc; /* Number of arguments. */
- char **argv; /* Argument strings. */
+ int objc; /* Number of arguments. */
+ register Tcl_Obj *CONST objv[]; /* Argument objects. */
{
- int first, last, result;
- char *begin, *end, c, *dummy, *next;
- int count, firstIsEnd;
+ Tcl_Obj *listPtr;
+ Tcl_Obj **elemPtrs;
+ int listLen, first, last, numElems, result;
- if (argc != 4) {
- Tcl_AppendResult(interp, "wrong # args: should be \"", argv[0],
- " list first last\"", (char *) NULL);
+ if (objc != 4) {
+ Tcl_WrongNumArgs(interp, 1, objv, "list first last");
return TCL_ERROR;
}
- if ((*argv[2] == 'e') && (strncmp(argv[2], "end", strlen(argv[2])) == 0)) {
- firstIsEnd = 1;
- first = INT_MAX;
- } else {
- firstIsEnd = 0;
- if (Tcl_GetInt(interp, argv[2], &first) != TCL_OK) {
- 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 = objv[1];
+ result = Tcl_ListObjGetElements(interp, listPtr, &listLen, &elemPtrs);
+ if (result != TCL_OK) {
+ return result;
+ }
+
+ /*
+ * Get the first and last indexes.
+ */
+
+ result = TclGetIntForIndex(interp, objv[2], /*endValue*/ (listLen - 1),
+ &first);
+ if (result != TCL_OK) {
+ return result;
}
if (first < 0) {
first = 0;
}
- if ((*argv[3] == 'e') && (strncmp(argv[3], "end", strlen(argv[3])) == 0)) {
- last = INT_MAX;
- } else {
- if (Tcl_GetInt(interp, argv[3], &last) != TCL_OK) {
- Tcl_ResetResult(interp);
- Tcl_AppendResult(interp, "expected integer or \"end\" but got \"",
- argv[3], "\"", (char *) NULL);
- return TCL_ERROR;
- }
+
+ result = TclGetIntForIndex(interp, objv[3], /*endValue*/ (listLen - 1),
+ &last);
+ if (result != TCL_OK) {
+ return result;
}
- if ((first > last) && !firstIsEnd) {
- return TCL_OK;
+ if (last >= listLen) {
+ last = (listLen - 1);
+ }
+
+ if (first > last) {
+ return TCL_OK; /* the result is an empty object */
}
/*
- * Extract a range of fields.
- */
-
- for (count = 0, begin = argv[1]; count < first; begin = next, count++) {
- result = TclFindElement(interp, begin, &dummy, &next, (int *) NULL,
- (int *) NULL);
- if (result != TCL_OK) {
- return result;
- }
- if (*next == 0) {
- if (firstIsEnd) {
- first = count;
- } else {
- begin = next;
- }
- break;
- }
- }
- for (count = first, end = begin; (count <= last) && (*end != 0);
- count++) {
- result = TclFindElement(interp, end, &dummy, &end, (int *) NULL,
- (int *) NULL);
- if (result != TCL_OK) {
- return result;
- }
- }
- if (end == begin) {
- return TCL_OK;
+ * Make sure listPtr still refers to a list object. It might have been
+ * converted to an int above if the argument objects were shared.
+ */
+
+ if (listPtr->typePtr != &tclListType) {
+ result = Tcl_ListObjGetElements(interp, listPtr, &listLen,
+ &elemPtrs);
+ if (result != TCL_OK) {
+ return result;
+ }
}
/*
- * Chop off trailing spaces.
+ * Extract a range of fields. We modify the interpreter's result object
+ * to be a list object containing the specified elements.
*/
- while ((end != begin) && (isspace(UCHAR(end[-1])))
- && (((end-1) == begin) || (end[-2] != '\\'))) {
- end--;
- }
- c = *end;
- *end = 0;
- Tcl_SetResult(interp, begin, TCL_VOLATILE);
- *end = c;
+ numElems = (last - first + 1);
+ Tcl_SetListObj(Tcl_GetObjResult(interp), numElems, &(elemPtrs[first]));
return TCL_OK;
}
/*
*----------------------------------------------------------------------
*
- * Tcl_LreplaceCmd --
+ * Tcl_LreplaceObjCmd --
*
- * This procedure is invoked to process the "lreplace" Tcl command.
- * See the user documentation for details on what it does.
+ * This object-based procedure is invoked to process the "lreplace"
+ * Tcl command. See the user documentation for details on what it does.
*
* Results:
- * A standard Tcl result.
+ * A new Tcl list object formed by replacing zero or more elements of
+ * a list.
*
* Side effects:
* See the user documentation.
@@ -1071,123 +2155,99 @@ Tcl_LrangeCmd(notUsed, interp, argc, argv)
/* ARGSUSED */
int
-Tcl_LreplaceCmd(notUsed, interp, argc, argv)
- ClientData notUsed; /* Not used. */
- Tcl_Interp *interp; /* Current interpreter. */
- int argc; /* Number of arguments. */
- char **argv; /* Argument strings. */
+Tcl_LreplaceObjCmd(dummy, interp, objc, objv)
+ ClientData dummy; /* Not used. */
+ Tcl_Interp *interp; /* Current interpreter. */
+ int objc; /* Number of arguments. */
+ Tcl_Obj *CONST objv[]; /* Argument objects. */
{
- char *p1, *p2, *element, savedChar, *dummy, *next;
- int i, first, last, count, result, size, firstIsEnd;
+ register Tcl_Obj *listPtr;
+ int createdNewObj, first, last, listLen, numToDelete, result;
- if (argc < 4) {
- Tcl_AppendResult(interp, "wrong # args: should be \"", argv[0],
- " list first last ?element element ...?\"", (char *) NULL);
+ if (objc < 4) {
+ Tcl_WrongNumArgs(interp, 1, objv,
+ "list first last ?element element ...?");
return TCL_ERROR;
}
- if ((*argv[2] == 'e') && (strncmp(argv[2], "end", strlen(argv[2])) == 0)) {
- firstIsEnd = 1;
- first = INT_MAX;
- } else {
- firstIsEnd = 0;
- if (Tcl_GetInt(interp, argv[2], &first) != TCL_OK) {
- Tcl_ResetResult(interp);
- Tcl_AppendResult(interp, "bad index \"", argv[2],
- "\": must be integer or \"end\"", (char *) NULL);
- return TCL_ERROR;
- }
- }
- if ((*argv[3] == 'e') && (strncmp(argv[3], "end", strlen(argv[3])) == 0)) {
- last = INT_MAX;
- } else {
- if (Tcl_GetInt(interp, argv[3], &last) != TCL_OK) {
- Tcl_ResetResult(interp);
- Tcl_AppendResult(interp, "bad index \"", argv[3],
- "\": must be integer or \"end\"", (char *) NULL);
- return TCL_ERROR;
- }
- }
- if (first < 0) {
- first = 0;
- }
/*
- * Skip over the elements of the list before "first".
+ * If the list object is unshared we can modify it directly, otherwise
+ * we create a copy to modify: this is "copy on write".
*/
-
- size = 0;
- element = argv[1];
- for (count = 0, p1 = argv[1]; (count < first) && (*p1 != 0); count++) {
- result = TclFindElement(interp, p1, &element, &next, &size,
- (int *) NULL);
- if (result != TCL_OK) {
- return result;
- }
- if ((*next == 0) && firstIsEnd) {
- break;
- }
- p1 = next;
+
+ listPtr = objv[1];
+ createdNewObj = 0;
+ if (Tcl_IsShared(listPtr)) {
+ listPtr = Tcl_DuplicateObj(listPtr);
+ createdNewObj = 1;
}
- if (*p1 == 0) {
- Tcl_AppendResult(interp, "list doesn't contain element ",
- argv[2], (char *) NULL);
- return TCL_ERROR;
+ result = Tcl_ListObjLength(interp, listPtr, &listLen);
+ if (result != TCL_OK) {
+ errorReturn:
+ if (createdNewObj) {
+ Tcl_DecrRefCount(listPtr); /* free unneeded obj */
+ }
+ return result;
}
/*
- * Skip over the elements of the list up through "last".
+ * Get the first and last indexes.
*/
- for (p2 = p1 ; (count <= last) && (*p2 != 0); count++) {
- result = TclFindElement(interp, p2, &dummy, &p2, (int *) NULL,
- (int *) NULL);
- if (result != TCL_OK) {
- return result;
- }
+ result = TclGetIntForIndex(interp, objv[2], /*endValue*/ (listLen - 1),
+ &first);
+ if (result != TCL_OK) {
+ goto errorReturn;
}
- /*
- * Add the elements before "first" to the result. Remove any
- * trailing white space, to make the result look as clean as
- * possible (this matters primarily if the replacement string is
- * empty).
- */
-
- while ((p1 != argv[1]) && (isspace(UCHAR(p1[-1])))
- && (((p1-1) == argv[1]) || (p1[-2] != '\\'))) {
- p1--;
+ result = TclGetIntForIndex(interp, objv[3], /*endValue*/ (listLen - 1),
+ &last);
+ if (result != TCL_OK) {
+ goto errorReturn;
}
- savedChar = *p1;
- *p1 = 0;
- Tcl_AppendResult(interp, argv[1], (char *) NULL);
- *p1 = savedChar;
- /*
- * Add the new list elements.
- */
+ if (first < 0) {
+ first = 0;
+ }
+ if (first >= listLen) {
+ Tcl_AppendStringsToObj(Tcl_GetObjResult(interp),
+ "list doesn't contain element ",
+ Tcl_GetStringFromObj(objv[2], (int *) NULL), (int *) NULL);
+ result = TCL_ERROR;
+ goto errorReturn;
+ }
+ if (last >= listLen) {
+ last = (listLen - 1);
+ }
+ if (first <= last) {
+ numToDelete = (last - first + 1);
+ } else {
+ numToDelete = 0;
+ }
- for (i = 4; i < argc; i++) {
- Tcl_AppendElement(interp, argv[i]);
+ if (objc > 4) {
+ result = Tcl_ListObjReplace(interp, listPtr, first, numToDelete,
+ (objc-4), &(objv[4]));
+ } else {
+ result = Tcl_ListObjReplace(interp, listPtr, first, numToDelete,
+ 0, NULL);
+ }
+ if (result != TCL_OK) {
+ goto errorReturn;
}
/*
- * Append the remainder of the original list.
+ * Set the interpreter's object result.
*/
- if (*p2 != 0) {
- if (*interp->result == 0) {
- Tcl_SetResult(interp, p2, TCL_VOLATILE);
- } else {
- Tcl_AppendResult(interp, " ", p2, (char *) NULL);
- }
- }
+ Tcl_SetObjResult(interp, listPtr);
return TCL_OK;
}
/*
*----------------------------------------------------------------------
*
- * Tcl_LsearchCmd --
+ * Tcl_LsearchObjCmd --
*
* This procedure is invoked to process the "lsearch" Tcl command.
* See the user documentation for details on what it does.
@@ -1201,56 +2261,68 @@ Tcl_LreplaceCmd(notUsed, interp, argc, argv)
*----------------------------------------------------------------------
*/
- /* ARGSUSED */
int
-Tcl_LsearchCmd(notUsed, interp, argc, argv)
- ClientData notUsed; /* Not used. */
- Tcl_Interp *interp; /* Current interpreter. */
- int argc; /* Number of arguments. */
- char **argv; /* Argument strings. */
+Tcl_LsearchObjCmd(clientData, interp, objc, objv)
+ ClientData clientData; /* Not used. */
+ Tcl_Interp *interp; /* Current interpreter. */
+ int objc; /* Number of arguments. */
+ Tcl_Obj *CONST objv[]; /* Argument values. */
{
#define EXACT 0
#define GLOB 1
#define REGEXP 2
- int listArgc;
- char **listArgv;
- int i, match, mode, index;
+ char *bytes, *patternBytes;
+ int i, match, mode, index, result, listLen, length, elemLen;
+ Tcl_Obj **elemPtrs;
+ static char *switches[] =
+ {"-exact", "-glob", "-regexp", (char *) NULL};
mode = GLOB;
- if (argc == 4) {
- if (strcmp(argv[1], "-exact") == 0) {
- mode = EXACT;
- } else if (strcmp(argv[1], "-glob") == 0) {
- mode = GLOB;
- } else if (strcmp(argv[1], "-regexp") == 0) {
- mode = REGEXP;
- } else {
- Tcl_AppendResult(interp, "bad search mode \"", argv[1],
- "\": must be -exact, -glob, or -regexp", (char *) NULL);
+ if (objc == 4) {
+ if (Tcl_GetIndexFromObj(interp, objv[1], switches,
+ "search mode", 0, &mode) != TCL_OK) {
return TCL_ERROR;
}
- } else if (argc != 3) {
- Tcl_AppendResult(interp, "wrong # args: should be \"", argv[0],
- " ?mode? list pattern\"", (char *) NULL);
+ } else if (objc != 3) {
+ Tcl_WrongNumArgs(interp, 1, objv, "?mode? list pattern");
return TCL_ERROR;
}
- if (Tcl_SplitList(interp, argv[argc-2], &listArgc, &listArgv) != TCL_OK) {
- 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.
+ */
+
+ result = Tcl_ListObjGetElements(interp, objv[objc-2], &listLen, &elemPtrs);
+ if (result != TCL_OK) {
+ return result;
}
+
+ patternBytes = Tcl_GetStringFromObj(objv[objc-1], &length);
+
index = -1;
- for (i = 0; i < listArgc; i++) {
+ for (i = 0; i < listLen; i++) {
match = 0;
+ bytes = Tcl_GetStringFromObj(elemPtrs[i], &elemLen);
switch (mode) {
case EXACT:
- match = (strcmp(listArgv[i], argv[argc-1]) == 0);
+ if (length == elemLen) {
+ match = (memcmp(bytes, patternBytes,
+ (size_t) length) == 0);
+ }
break;
case GLOB:
- match = Tcl_StringMatch(listArgv[i], argv[argc-1]);
+ /*
+ * WARNING: will not work with data containing NULLs.
+ */
+ match = Tcl_StringMatch(bytes, patternBytes);
break;
case REGEXP:
- match = Tcl_RegExpMatch(interp, listArgv[i], argv[argc-1]);
+ /*
+ * WARNING: will not work with data containing NULLs.
+ */
+ match = Tcl_RegExpMatch(interp, bytes, patternBytes);
if (match < 0) {
- ckfree((char *) listArgv);
return TCL_ERROR;
}
break;
@@ -1260,15 +2332,15 @@ Tcl_LsearchCmd(notUsed, interp, argc, argv)
break;
}
}
- sprintf(interp->result, "%d", index);
- ckfree((char *) listArgv);
+
+ Tcl_SetIntObj(Tcl_GetObjResult(interp), index);
return TCL_OK;
}
/*
*----------------------------------------------------------------------
*
- * Tcl_LsortCmd --
+ * Tcl_LsortObjCmd --
*
* This procedure is invoked to process the "lsort" Tcl command.
* See the user documentation for details on what it does.
@@ -1282,29 +2354,29 @@ Tcl_LsearchCmd(notUsed, interp, argc, argv)
*----------------------------------------------------------------------
*/
- /* ARGSUSED */
int
-Tcl_LsortCmd(notUsed, interp, argc, argv)
- ClientData notUsed; /* Not used. */
- Tcl_Interp *interp; /* Current interpreter. */
- int argc; /* Number of arguments. */
- char **argv; /* Argument strings. */
+Tcl_LsortObjCmd(clientData, interp, objc, objv)
+ ClientData clientData; /* Not used. */
+ Tcl_Interp *interp; /* Current interpreter. */
+ int objc; /* Number of arguments. */
+ Tcl_Obj *CONST objv[]; /* Argument values. */
{
- int listArgc, i, c;
- size_t length;
- char **listArgv;
- char *command = NULL; /* Initialization needed only to
- * prevent compiler warning. */
-
- if (argc < 2) {
- Tcl_AppendResult(interp, "wrong # args: should be \"", argv[0],
- " ?-ascii? ?-integer? ?-real? ?-increasing? ?-decreasing?",
- " ?-command string? list\"", (char *) NULL);
- return TCL_ERROR;
- }
-
- if (sortInterp != NULL) {
- interp->result = "can't invoke \"lsort\" recursively";
+ int i, index, dummy;
+ Tcl_Obj *resultPtr;
+ int length;
+ Tcl_Obj *cmdPtr, **listObjPtrs;
+ SortElement *elementArray;
+ SortElement *elementPtr;
+ SortInfo sortInfo; /* Information about this sort that
+ * needs to be passed to the
+ * comparison function */
+ static char *switches[] =
+ {"-ascii", "-command", "-decreasing", "-dictionary",
+ "-increasing", "-index", "-integer", "-real", (char *) NULL};
+
+ resultPtr = Tcl_GetObjResult(interp);
+ if (objc < 2) {
+ Tcl_WrongNumArgs(interp, 1, objv, "?options? list");
return TCL_ERROR;
}
@@ -1312,87 +2384,244 @@ Tcl_LsortCmd(notUsed, interp, argc, argv)
* Parse arguments to set up the mode for the sort.
*/
- sortInterp = interp;
- sortMode = ASCII;
- sortIncreasing = 1;
- sortCode = TCL_OK;
- for (i = 1; i < argc-1; i++) {
- length = strlen(argv[i]);
- if (length < 2) {
- badSwitch:
- Tcl_AppendResult(interp, "bad switch \"", argv[i],
- "\": must be -ascii, -integer, -real, -increasing",
- " -decreasing, or -command", (char *) NULL);
- sortCode = TCL_ERROR;
- goto done;
+ sortInfo.isIncreasing = 1;
+ sortInfo.sortMode = SORTMODE_ASCII;
+ sortInfo.index = -1;
+ sortInfo.interp = interp;
+ sortInfo.resultCode = TCL_OK;
+ cmdPtr = NULL;
+ for (i = 1; i < objc-1; i++) {
+ if (Tcl_GetIndexFromObj(interp, objv[i], switches, "option", 0, &index)
+ != TCL_OK) {
+ return TCL_ERROR;
}
- c = argv[i][1];
- if ((c == 'a') && (strncmp(argv[i], "-ascii", length) == 0)) {
- sortMode = ASCII;
- } else if ((c == 'c') && (strncmp(argv[i], "-command", length) == 0)) {
- if (i == argc-2) {
- Tcl_AppendResult(interp, "\"-command\" must be",
- " followed by comparison command", (char *) NULL);
- sortCode = TCL_ERROR;
- goto done;
- }
- sortMode = COMMAND;
- command = argv[i+1];
- i++;
- } else if ((c == 'd')
- && (strncmp(argv[i], "-decreasing", length) == 0)) {
- sortIncreasing = 0;
- } else if ((c == 'i') && (length >= 4)
- && (strncmp(argv[i], "-increasing", length) == 0)) {
- sortIncreasing = 1;
- } else if ((c == 'i') && (length >= 4)
- && (strncmp(argv[i], "-integer", length) == 0)) {
- sortMode = INTEGER;
- } else if ((c == 'r')
- && (strncmp(argv[i], "-real", length) == 0)) {
- sortMode = REAL;
- } else {
- goto badSwitch;
+ switch (index) {
+ case 0: /* -ascii */
+ sortInfo.sortMode = SORTMODE_ASCII;
+ break;
+ case 1: /* -command */
+ if (i == (objc-2)) {
+ Tcl_AppendToObj(resultPtr,
+ "\"-command\" option must be followed by comparison command",
+ -1);
+ return TCL_ERROR;
+ }
+ sortInfo.sortMode = SORTMODE_COMMAND;
+ cmdPtr = objv[i+1];
+ i++;
+ break;
+ case 2: /* -decreasing */
+ sortInfo.isIncreasing = 0;
+ break;
+ case 3: /* -dictionary */
+ sortInfo.sortMode = SORTMODE_DICTIONARY;
+ break;
+ case 4: /* -increasing */
+ sortInfo.isIncreasing = 1;
+ break;
+ case 5: /* -index */
+ if (i == (objc-2)) {
+ Tcl_AppendToObj(resultPtr,
+ "\"-index\" option must be followed by list index",
+ -1);
+ return TCL_ERROR;
+ }
+ if (Tcl_GetLongFromObj(interp, objv[i+1], &sortInfo.index)
+ != TCL_OK) {
+ if (strcmp("end", Tcl_GetStringFromObj(objv[i+1], &dummy))
+ == 0) {
+ sortInfo.index = -2;
+ } else {
+ return TCL_ERROR;
+ }
+ }
+ cmdPtr = objv[i+1];
+ i++;
+ break;
+ case 6: /* -integer */
+ sortInfo.sortMode = SORTMODE_INTEGER;
+ break;
+ case 7: /* -real */
+ sortInfo.sortMode = SORTMODE_REAL;
+ break;
}
}
- if (sortMode == COMMAND) {
- Tcl_DStringInit(&sortCmd);
- Tcl_DStringAppend(&sortCmd, command, -1);
+ if (sortInfo.sortMode == SORTMODE_COMMAND) {
+ Tcl_DStringInit(&sortInfo.compareCmd);
+ Tcl_DStringAppend(&sortInfo.compareCmd,
+ Tcl_GetStringFromObj(cmdPtr, &dummy), -1);
}
- if (Tcl_SplitList(interp, argv[argc-1], &listArgc, &listArgv) != TCL_OK) {
- sortCode = TCL_ERROR;
+ sortInfo.resultCode = Tcl_ListObjGetElements(interp, objv[objc-1],
+ &length, &listObjPtrs);
+ if (sortInfo.resultCode != TCL_OK) {
goto done;
}
- qsort((VOID *) listArgv, (size_t) listArgc, sizeof (char *),
- SortCompareProc);
- if (sortCode == TCL_OK) {
- Tcl_ResetResult(interp);
- interp->result = Tcl_Merge(listArgc, listArgv);
- interp->freeProc = TCL_DYNAMIC;
+ if (length <= 0) {
+ return TCL_OK;
+ }
+ elementArray = (SortElement *) ckalloc(length * sizeof(SortElement));
+ for (i=0; i < length; i++){
+ elementArray[i].objPtr = listObjPtrs[i];
+ elementArray[i].nextPtr = &elementArray[i+1];
}
- if (sortMode == COMMAND) {
- Tcl_DStringFree(&sortCmd);
+ elementArray[length-1].nextPtr = NULL;
+ elementPtr = MergeSort(elementArray, &sortInfo);
+ if (sortInfo.resultCode == TCL_OK) {
+ /*
+ * Note: must clear the interpreter's result object: it could
+ * have been set by the -command script.
+ */
+
+ Tcl_ResetResult(interp);
+ resultPtr = Tcl_GetObjResult(interp);
+ for (; elementPtr != NULL; elementPtr = elementPtr->nextPtr){
+ Tcl_ListObjAppendElement(interp, resultPtr, elementPtr->objPtr);
+ }
}
- ckfree((char *) listArgv);
+ ckfree((char*) elementArray);
done:
- sortInterp = NULL;
- return sortCode;
+ if (sortInfo.sortMode == SORTMODE_COMMAND) {
+ Tcl_DStringFree(&sortInfo.compareCmd);
+ }
+ return sortInfo.resultCode;
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * MergeSort -
+ *
+ * This procedure sorts a linked list of SortElement structures
+ * use the merge-sort algorithm.
+ *
+ * Results:
+ * A pointer to the head of the list after sorting is returned.
+ *
+ * Side effects:
+ * None, unless a user-defined comparison command does something
+ * weird.
+ *
+ *----------------------------------------------------------------------
+ */
+
+static SortElement *
+MergeSort(headPtr, infoPtr)
+ SortElement *headPtr; /* First element on the list */
+ SortInfo *infoPtr; /* Information needed by the
+ * comparison operator */
+{
+ /*
+ * 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];
+ SortElement *elementPtr;
+ int i;
+
+ for(i = 0; i < NUM_LISTS; i++){
+ subList[i] = NULL;
+ }
+ while (headPtr != NULL) {
+ elementPtr = headPtr;
+ headPtr = headPtr->nextPtr;
+ elementPtr->nextPtr = 0;
+ for (i = 0; (i < NUM_LISTS) && (subList[i] != NULL); i++){
+ elementPtr = MergeLists(subList[i], elementPtr, infoPtr);
+ subList[i] = NULL;
+ }
+ if (i >= NUM_LISTS) {
+ i = NUM_LISTS-1;
+ }
+ subList[i] = elementPtr;
+ }
+ elementPtr = NULL;
+ for (i = 0; i < NUM_LISTS; i++){
+ elementPtr = MergeLists(subList[i], elementPtr, infoPtr);
+ }
+ return elementPtr;
}
/*
*----------------------------------------------------------------------
*
- * SortCompareProc --
+ * MergeLists -
+ *
+ * This procedure combines two sorted lists of SortElement structures
+ * into a single sorted list.
+ *
+ * Results:
+ * The unified list of SortElement structures.
+ *
+ * Side effects:
+ * None, unless a user-defined comparison command does something
+ * weird.
*
- * This procedure is invoked by qsort to determine the proper
+ *----------------------------------------------------------------------
+ */
+
+static SortElement *
+MergeLists(leftPtr, rightPtr, infoPtr)
+ SortElement *leftPtr; /* First list to be merged; may be
+ * NULL. */
+ SortElement *rightPtr; /* Second list to be merged; may be
+ * NULL. */
+ SortInfo *infoPtr; /* Information needed by the
+ * comparison operator. */
+{
+ SortElement *headPtr;
+ SortElement *tailPtr;
+
+ if (leftPtr == NULL) {
+ return rightPtr;
+ }
+ if (rightPtr == NULL) {
+ return leftPtr;
+ }
+ if (SortCompare(leftPtr->objPtr, rightPtr->objPtr, infoPtr) > 0) {
+ tailPtr = rightPtr;
+ rightPtr = rightPtr->nextPtr;
+ } else {
+ tailPtr = leftPtr;
+ leftPtr = leftPtr->nextPtr;
+ }
+ headPtr = tailPtr;
+ while ((leftPtr != NULL) && (rightPtr != NULL)) {
+ if (SortCompare(leftPtr->objPtr, rightPtr->objPtr, infoPtr) > 0) {
+ tailPtr->nextPtr = rightPtr;
+ tailPtr = rightPtr;
+ rightPtr = rightPtr->nextPtr;
+ } else {
+ tailPtr->nextPtr = leftPtr;
+ tailPtr = leftPtr;
+ leftPtr = leftPtr->nextPtr;
+ }
+ }
+ if (leftPtr != NULL) {
+ tailPtr->nextPtr = leftPtr;
+ } else {
+ tailPtr->nextPtr = rightPtr;
+ }
+ return headPtr;
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * SortCompare --
+ *
+ * This procedure is invoked by MergeLists to determine the proper
* ordering between two elements.
*
* Results:
- * < 0 means first is "smaller" than "second", > 0 means "first"
- * is larger than "second", and 0 means they should be treated
- * as equal.
+ * A negative results means the the first element comes before the
+ * second, and a positive results means that the second element
+ * should come first. A result of zero means the two elements
+ * are equal and it doesn't matter which comes first.
*
* Side effects:
* None, unless a user-defined comparison command does something
@@ -1402,15 +2631,17 @@ Tcl_LsortCmd(notUsed, interp, argc, argv)
*/
static int
-SortCompareProc(first, second)
- CONST VOID *first, *second; /* Elements to be compared. */
+SortCompare(objPtr1, objPtr2, infoPtr)
+ Tcl_Obj *objPtr1, *objPtr2; /* Values to be compared. */
+ SortInfo *infoPtr; /* Information passed from the
+ * top-level "lsort" command */
{
- int order;
- char *firstString = *((char **) first);
- char *secondString = *((char **) second);
+ int order, dummy, listLen, index;
+ Tcl_Obj *objPtr;
+ char buffer[30];
order = 0;
- if (sortCode != TCL_OK) {
+ if (infoPtr->resultCode != TCL_OK) {
/*
* Once an error has occurred, skip any future comparisons
* so as to preserve the error message in sortInterp->result.
@@ -1418,16 +2649,77 @@ SortCompareProc(first, second)
return order;
}
- if (sortMode == ASCII) {
- order = strcmp(firstString, secondString);
- } else if (sortMode == INTEGER) {
+ if (infoPtr->index != -1) {
+ /*
+ * The "-index" option was specified. Treat each object as a
+ * list, extract the requested element from each list, and
+ * compare the elements, not the lists. The special index "end"
+ * is signaled here with a large negative index.
+ */
+
+ if (Tcl_ListObjLength(infoPtr->interp, objPtr1, &listLen) != TCL_OK) {
+ infoPtr->resultCode = TCL_ERROR;
+ return order;
+ }
+ if (infoPtr->index < -1) {
+ index = listLen - 1;
+ } else {
+ index = infoPtr->index;
+ }
+
+ if (Tcl_ListObjIndex(infoPtr->interp, objPtr1, index, &objPtr)
+ != TCL_OK) {
+ infoPtr->resultCode = TCL_ERROR;
+ return order;
+ }
+ if (objPtr == NULL) {
+ objPtr = objPtr1;
+ missingElement:
+ sprintf(buffer, "%ld", infoPtr->index);
+ Tcl_AppendStringsToObj(Tcl_GetObjResult(infoPtr->interp),
+ "element ", buffer, " missing from sublist \"",
+ Tcl_GetStringFromObj(objPtr, (int *) NULL),
+ "\"", (char *) NULL);
+ infoPtr->resultCode = TCL_ERROR;
+ return order;
+ }
+ objPtr1 = objPtr;
+
+ if (Tcl_ListObjLength(infoPtr->interp, objPtr2, &listLen) != TCL_OK) {
+ infoPtr->resultCode = TCL_ERROR;
+ return order;
+ }
+ if (infoPtr->index < -1) {
+ index = listLen - 1;
+ } else {
+ index = infoPtr->index;
+ }
+
+ if (Tcl_ListObjIndex(infoPtr->interp, objPtr2, index, &objPtr)
+ != TCL_OK) {
+ infoPtr->resultCode = TCL_ERROR;
+ return order;
+ }
+ if (objPtr == NULL) {
+ objPtr = objPtr2;
+ goto missingElement;
+ }
+ objPtr2 = objPtr;
+ }
+ if (infoPtr->sortMode == SORTMODE_ASCII) {
+ order = strcmp(Tcl_GetStringFromObj(objPtr1, &dummy),
+ Tcl_GetStringFromObj(objPtr2, &dummy));
+ } else if (infoPtr->sortMode == SORTMODE_DICTIONARY) {
+ order = DictionaryCompare(
+ Tcl_GetStringFromObj(objPtr1, &dummy),
+ Tcl_GetStringFromObj(objPtr2, &dummy));
+ } else if (infoPtr->sortMode == SORTMODE_INTEGER) {
int a, b;
- if ((Tcl_GetInt(sortInterp, firstString, &a) != TCL_OK)
- || (Tcl_GetInt(sortInterp, secondString, &b) != TCL_OK)) {
- Tcl_AddErrorInfo(sortInterp,
- "\n (converting list element from string to integer)");
- sortCode = TCL_ERROR;
+ if ((Tcl_GetIntFromObj(infoPtr->interp, objPtr1, &a) != TCL_OK)
+ || (Tcl_GetIntFromObj(infoPtr->interp, objPtr2, &b)
+ != TCL_OK)) {
+ infoPtr->resultCode = TCL_ERROR;
return order;
}
if (a > b) {
@@ -1435,14 +2727,13 @@ SortCompareProc(first, second)
} else if (b > a) {
order = -1;
}
- } else if (sortMode == REAL) {
+ } else if (infoPtr->sortMode == SORTMODE_REAL) {
double a, b;
- if ((Tcl_GetDouble(sortInterp, firstString, &a) != TCL_OK)
- || (Tcl_GetDouble(sortInterp, secondString, &b) != TCL_OK)) {
- Tcl_AddErrorInfo(sortInterp,
- "\n (converting list element from string to real)");
- sortCode = TCL_ERROR;
+ if ((Tcl_GetDoubleFromObj(infoPtr->interp, objPtr1, &a) != TCL_OK)
+ || (Tcl_GetDoubleFromObj(infoPtr->interp, objPtr2, &b)
+ != TCL_OK)) {
+ infoPtr->resultCode = TCL_ERROR;
return order;
}
if (a > b) {
@@ -1452,21 +2743,23 @@ SortCompareProc(first, second)
}
} else {
int oldLength;
- char *end;
/*
* Generate and evaluate a command to determine which string comes
* first.
*/
- oldLength = Tcl_DStringLength(&sortCmd);
- Tcl_DStringAppendElement(&sortCmd, firstString);
- Tcl_DStringAppendElement(&sortCmd, secondString);
- sortCode = Tcl_Eval(sortInterp, Tcl_DStringValue(&sortCmd));
- Tcl_DStringTrunc(&sortCmd, oldLength);
- if (sortCode != TCL_OK) {
- Tcl_AddErrorInfo(sortInterp,
- "\n (user-defined comparison command)");
+ oldLength = Tcl_DStringLength(&infoPtr->compareCmd);
+ Tcl_DStringAppendElement(&infoPtr->compareCmd,
+ Tcl_GetStringFromObj(objPtr1, &dummy));
+ Tcl_DStringAppendElement(&infoPtr->compareCmd,
+ Tcl_GetStringFromObj(objPtr2, &dummy));
+ infoPtr->resultCode = Tcl_Eval(infoPtr->interp,
+ Tcl_DStringValue(&infoPtr->compareCmd));
+ Tcl_DStringTrunc(&infoPtr->compareCmd, oldLength);
+ if (infoPtr->resultCode != TCL_OK) {
+ Tcl_AddErrorInfo(infoPtr->interp,
+ "\n (-compare command)");
return order;
}
@@ -1474,18 +2767,137 @@ SortCompareProc(first, second)
* Parse the result of the command.
*/
- order = strtol(sortInterp->result, &end, 0);
- if ((end == sortInterp->result) || (*end != 0)) {
- Tcl_ResetResult(sortInterp);
- Tcl_AppendResult(sortInterp,
- "comparison command returned non-numeric result",
- (char *) NULL);
- sortCode = TCL_ERROR;
+ if (Tcl_GetIntFromObj(infoPtr->interp,
+ Tcl_GetObjResult(infoPtr->interp), &order) != TCL_OK) {
+ Tcl_ResetResult(infoPtr->interp);
+ Tcl_AppendToObj(Tcl_GetObjResult(infoPtr->interp),
+ "-compare command returned non-numeric result", -1);
+ infoPtr->resultCode = TCL_ERROR;
return order;
}
}
- if (!sortIncreasing) {
+ if (!infoPtr->isIncreasing) {
order = -order;
}
return order;
}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * DictionaryCompare
+ *
+ * This function compares two strings as if they were being used in
+ * an index or card catalog. The case of alphabetic characters is
+ * ignored, except to break ties. Thus "B" comes before "b" but
+ * after "a". Also, integers embedded in the strings compare in
+ * numerical order. In other words, "x10y" comes after "x9y", not
+ * before it as it would when using strcmp().
+ *
+ * Results:
+ * A negative result means that the first element comes before the
+ * second, and a positive result means that the second element
+ * should come first. A result of zero means the two elements
+ * are equal and it doesn't matter which comes first.
+ *
+ * Side effects:
+ * None.
+ *
+ *----------------------------------------------------------------------
+ */
+
+static int
+DictionaryCompare(left, right)
+ char *left, *right; /* The strings to compare */
+{
+ int diff, zeros;
+ int secondaryDiff = 0;
+
+ while (1) {
+ if (isdigit(UCHAR(*right)) && isdigit(UCHAR(*left))) {
+ /*
+ * There are decimal numbers embedded in the two
+ * strings. Compare them as numbers, rather than
+ * strings. If one number has more leading zeros than
+ * the other, the number with more leading zeros sorts
+ * later, but only as a secondary choice.
+ */
+
+ zeros = 0;
+ while (*right == '0') {
+ right++;
+ zeros--;
+ }
+ while (*left == '0') {
+ left++;
+ zeros++;
+ }
+ if (secondaryDiff == 0) {
+ secondaryDiff = zeros;
+ }
+
+ /*
+ * The code below compares the numbers in the two
+ * strings without ever converting them to integers. It
+ * does this by first comparing the lengths of the
+ * numbers and then comparing the digit values.
+ */
+
+ diff = 0;
+ while (1) {
+ if (diff == 0) {
+ diff = *left - *right;
+ }
+ right++;
+ left++;
+ if (!isdigit(UCHAR(*right))) {
+ if (isdigit(UCHAR(*left))) {
+ return 1;
+ } else {
+ /*
+ * The two numbers have the same length. See
+ * if their values are different.
+ */
+
+ if (diff != 0) {
+ return diff;
+ }
+ break;
+ }
+ } else if (!isdigit(UCHAR(*left))) {
+ return -1;
+ }
+ }
+ continue;
+ }
+ diff = *left - *right;
+ if (diff) {
+ if (isupper(UCHAR(*left)) && islower(UCHAR(*right))) {
+ diff = tolower(*left) - *right;
+ if (diff) {
+ return diff;
+ } else if (secondaryDiff == 0) {
+ secondaryDiff = -1;
+ }
+ } else if (isupper(UCHAR(*right)) && islower(UCHAR(*left))) {
+ diff = *left - tolower(UCHAR(*right));
+ if (diff) {
+ return diff;
+ } else if (secondaryDiff == 0) {
+ secondaryDiff = 1;
+ }
+ } else {
+ return diff;
+ }
+ }
+ if (*left == 0) {
+ break;
+ }
+ left++;
+ right++;
+ }
+ if (diff == 0) {
+ diff = secondaryDiff;
+ }
+ return diff;
+}