Tcl Source Code

Artifact [b423108fd5]
Login

Artifact b423108fd507156f35dedf1d5a10489b807cc432:

Attachment "tclCmdIL.c#1.114-patch" to ticket [1366484fff] added by afredd 2007-04-26 23:07:36.
--- tclCmdIL.c-orig	2007-04-25 10:03:55.342816900 +0100
+++ tclCmdIL.c	2007-04-26 09:10:54.115302100 +0100
@@ -23,25 +23,72 @@
 #include "tclRegexp.h"
 
 /*
- * 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.
+ * A union containing the possible types stored in a SortElement's cache.
+ * NB. we do not store a double in the cache as this increases
+ * sizeof(SortElement) by 4 bytes and sizeof(SortElementUnique) by 8 bytes
+ * on LP32 machines, and the resultant increase in memory usage will slow
+ * down non [lsort -real]'s (the common usage), with only a small benefit
+ * to [lsort -real]'s.
+ */
+
+typedef union SortElementCache {
+    long integer;			/* Used if lsort is -integer */
+    char *string;			/* Used if lsort is -ascii, -nocase,
+					 * or -dictionary */
+    Tcl_Obj *tclObj;			/* Used if lsort is -command, or -real
+					 */
+
+} SortElementCache;
+
+/*
+ * 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. Note that the merge sort algorithm used does not
+ * make use of the size of the structure (a fact we make use of!) but
+ * simply follows each SortElements nextPtr to traverse the list.
  */
 
 typedef struct SortElement {
-    Tcl_Obj *objPtr;		/* Object being sorted. */
-    int count;			/* Number of same elements in list. */
-    struct SortElement *nextPtr;/* Next element in the list, or NULL for end
-				 * of list. */
+    struct SortElement *nextPtr;	/* Next element in the list, or
+					 * NULL for end of list. */
+    Tcl_Obj *objPtr;			/* Object being sorted. */
+    SortElementCache cache;		/* A cache storing the item
+					 * that will be compared */
 } SortElement;
 
 /*
- * These function pointer types are used with the "lsearch" and "lsort"
- * commands to facilitate the "-nocase" option.
+ * In the case of an "lsort -unique", we need to store the fact that
+ * an individual element is or is not unique. We use this larger
+ * form of a SortElement structure to do this. The member named 'unique'
+ * is not included in the general SortElement structure because the larger
+ * the SortElement structure, the more likely we are to generate cache
+ * misses during the merge sort process and thus run slower.
+ * Note the leading members of this struct must match those of a
+ * SortElement to take advantage of C struct equivalence so that
+ * we can safely cast a (SortElementUnique*) to a (SortElement*).
+ */
+
+typedef struct SortElementUnique {
+    struct SortElement *nextPtr;	/* Next element in the list, or
+					 * NULL for end of list. */
+    Tcl_Obj *objPtr;			/* Object being sorted. */
+    SortElementCache cache;		/* A cache storing the item
+					 * that will be compared */
+    int unique;				/* Nonzero if this element does
+					 * not compare the same as another.
+					 */
+} SortElementUnique;
+
+/*
+ * These function pointer types are used by the "lsearch" and "lsort"
+ * commands to compare list elements.
  */
 
+struct SortInfo; /* forward declaration */
+
+typedef int (*SortCompareFn_t) (SortElement *, SortElement *,
+	struct SortInfo *);
 typedef int (*SortStrCmpFn_t) (const char *, const char *);
-typedef int (*SortMemCmpFn_t) (const void *, const void *, size_t);
 
 /*
  * The "lsort" command needs to pass certain information down to the function
@@ -52,13 +99,19 @@
 
 typedef struct SortInfo {
     int isIncreasing;		/* Nonzero means sort in increasing order. */
-    int sortMode;		/* The sort mode. One of SORTMODE_* values
-				 * defined below. */
-    SortStrCmpFn_t strCmpFn;	/* Basic string compare command (used with
-				 * ASCII mode). */
+    int isUnique;		/* Nonzero means sort is -unique. */
+    int nonUniqueCount;		/* The number of elements that compare the
+				 * same during a "lsort -unique". */
+    int compareCmdObjc;         /* The number of arguments used by the
+				 * lsort -command procedure call. */
+    SortCompareFn_t sortCompareFn;
+				/* The function used to compare elements
+				 * within the list during a sort. */
     Tcl_Obj *compareCmdPtr;	/* The Tcl comparison command when sortMode is
 				 * SORTMODE_COMMAND. Pre-initialized to hold
 				 * base of command. */
+    Tcl_Interp *interp;		/* The interpreter in which the sort is being
+				 * done. */
     int *indexv;		/* If the -index option was specified, this
 				 * holds the indexes contained in the list
 				 * supplied as an argument to that option.
@@ -66,12 +119,12 @@
 				 * singleIndex field when only one
 				 * supplied. */
     int indexc;			/* Number of indexes in indexv array. */
-    int singleIndex;		/* Static space for common index case. */
-    Tcl_Interp *interp;		/* The interpreter in which the sort is 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. */
+    int singleIndex;		/* Static space for common index case. */
+    int sortMode;		/* The sort mode. One of SORTMODE_* values
+				 * defined below */
 } SortInfo;
 
 /*
@@ -149,10 +202,21 @@
 static SortElement *    MergeSort(SortElement *headPt, SortInfo *infoPtr);
 static SortElement *    MergeLists(SortElement *leftPtr, SortElement *rightPtr,
 			    SortInfo *infoPtr);
-static int		SortCompare(Tcl_Obj *firstPtr, Tcl_Obj *second,
-			    SortInfo *infoPtr);
 static Tcl_Obj *	SelectObjFromSublist(Tcl_Obj *firstPtr,
 			    SortInfo *infoPtr);
+static int		SortCompareInteger(SortElement *sortElemPtr1,
+			    SortElement *sortElemPtr2, SortInfo *infoPtr);
+static int		SortCompareReal(SortElement *sortElemPtr1,
+			    SortElement *sortElemPtr2, SortInfo *infoPtr);
+static int		SortCompareCommand(SortElement *sortElemPtr1,
+			    SortElement *sortElemPtr2, SortInfo *infoPtr);
+static int		SortCompareAscii(SortElement *sortElemPtr1,
+			    SortElement *sortElemPtr2, SortInfo *infoPtr);
+static int		SortCompareNoCase(SortElement *sortElemPtr1,
+			    SortElement *sortElemPtr2, SortInfo *infoPtr);
+static int		SortCompareDictionary(SortElement *sortElemPtr1,
+			    SortElement *sortElemPtr2, SortInfo *infoPtr);
+
 
 /*
  *----------------------------------------------------------------------
@@ -2988,7 +3052,7 @@
     }
 
     if (first < 0) {
-    	first = 0;
+	first = 0;
     }
 
     /*
@@ -3004,7 +3068,7 @@
 	return TCL_ERROR;
     }
     if (last >= listLen) {
-    	last = (listLen - 1);
+	last = (listLen - 1);
     }
     if (first <= last) {
 	numToDelete = (last - first + 1);
@@ -3145,7 +3209,7 @@
 {
     char *bytes, *patternBytes;
     int i, match, mode, index, result, listc, length, elemLen;
-    int dataType, isIncreasing, lower, upper, patInt, objInt, offset;
+    int dataType, lower, upper, patInt, objInt, offset;
     int allMatches, inlineReturn, negatedMatch, returnSubindices, noCase;
     double patDouble, objDouble;
     SortInfo sortInfo;
@@ -3175,7 +3239,6 @@
 
     mode = GLOB;
     dataType = ASCII;
-    isIncreasing = 1;
     allMatches = 0;
     inlineReturn = 0;
     returnSubindices = 0;
@@ -3185,8 +3248,8 @@
     offset = 0;
     noCase = 0;
     sortInfo.compareCmdPtr = NULL;
-    sortInfo.isIncreasing = 0;
-    sortInfo.sortMode = 0;
+    sortInfo.isIncreasing = 1;
+    sortInfo.sortMode = SORTMODE_ASCII;
     sortInfo.interp = interp;
     sortInfo.resultCode = TCL_OK;
     sortInfo.indexv = NULL;
@@ -3216,7 +3279,7 @@
 	    dataType = ASCII;
 	    break;
 	case LSEARCH_DECREASING:	/* -decreasing */
-	    isIncreasing = 0;
+	    sortInfo.isIncreasing = 0;
 	    break;
 	case LSEARCH_DICTIONARY:	/* -dictionary */
 	    dataType = DICTIONARY;
@@ -3228,7 +3291,7 @@
 	    mode = GLOB;
 	    break;
 	case LSEARCH_INCREASING:	/* -increasing */
-	    isIncreasing = 1;
+	    sortInfo.isIncreasing = 1;
 	    break;
 	case LSEARCH_INLINE:		/* -inline */
 	    inlineReturn = 1;
@@ -3566,13 +3629,13 @@
 		index = i;
 		upper = i;
 	    } else if (match > 0) {
-		if (isIncreasing) {
+		if (sortInfo.isIncreasing) {
 		    lower = i;
 		} else {
 		    upper = i;
 		}
 	    } else {
-		if (isIncreasing) {
+		if (sortInfo.isIncreasing) {
 		    upper = i;
 		} else {
 		    lower = i;
@@ -3867,9 +3930,11 @@
     int objc,			/* Number of arguments. */
     Tcl_Obj *CONST objv[])	/* Argument values. */
 {
-    int i, index, unique, indices, length;
+    int i, index, noCase, indices, length;
     Tcl_Obj *resultPtr, *cmdPtr, **listObjPtrs, *listObj;
-    SortElement *elementArray, *elementPtr;
+    char *elementArrayBuf, *elementArrayBufPtr;
+    register SortElement *elementPtr;
+    size_t sizeof_SortElement;
     SortInfo sortInfo;		/* Information about this sort that needs to
 				 * be passed to the comparison function. */
     static CONST char *switches[] = {
@@ -3892,15 +3957,18 @@
      */
 
     sortInfo.isIncreasing = 1;
+    sortInfo.isUnique = 0;
     sortInfo.sortMode = SORTMODE_ASCII;
-    sortInfo.strCmpFn = strcmp;
+    sortInfo.compareCmdObjc = 0;
+    sortInfo.compareCmdPtr = NULL;
     sortInfo.indexv = NULL;
     sortInfo.indexc = 0;
     sortInfo.interp = interp;
     sortInfo.resultCode = TCL_OK;
+    sortInfo.nonUniqueCount = 0;
     cmdPtr = NULL;
-    unique = 0;
     indices = 0;
+    noCase = 0;
     for (i = 1; i < objc-1; i++) {
 	if (Tcl_GetIndexFromObj(interp, objv[i], switches, "option", 0,
 		&index) != TCL_OK) {
@@ -3935,7 +4003,7 @@
 	    break;
 	case LSORT_INDEX: {
 	    int j;
-	    Tcl_Obj **indices;
+	    Tcl_Obj **indicesList;
 
 	    if (sortInfo.indexc > 1) {
 		ckfree((char *) sortInfo.indexv);
@@ -3951,7 +4019,7 @@
 	     */
 
 	    if (Tcl_ListObjGetElements(interp, objv[i+1], &sortInfo.indexc,
-		    &indices) != TCL_OK) {
+		    &indicesList) != TCL_OK) {
 		return TCL_ERROR;
 	    }
 	    switch (sortInfo.indexc) {
@@ -3964,6 +4032,7 @@
 	    default:
 		sortInfo.indexv = (int *)
 			ckalloc(sizeof(int) * sortInfo.indexc);
+		break;
 	    }
 
 	    /*
@@ -3972,8 +4041,8 @@
 	     * syntactic check here.
 	     */
 
-	    for (j=0 ; j<sortInfo.indexc ; j++) {
-		if (TclGetIntForIndex(interp, indices[j], SORTIDX_END,
+	    for (j = 0; j < sortInfo.indexc; ++j) {
+		if (TclGetIntForIndex(interp, indicesList[j], SORTIDX_END,
 			&sortInfo.indexv[j]) != TCL_OK) {
 		    if (sortInfo.indexc > 1) {
 			ckfree((char *) sortInfo.indexv);
@@ -3990,111 +4059,240 @@
 	    sortInfo.sortMode = SORTMODE_INTEGER;
 	    break;
 	case LSORT_NOCASE:
-	    sortInfo.strCmpFn = strcasecmp;
+	    noCase = 1;
 	    break;
 	case LSORT_REAL:
 	    sortInfo.sortMode = SORTMODE_REAL;
 	    break;
 	case LSORT_UNIQUE:
-	    unique = 1;
+	    sortInfo.isUnique = 1;
 	    break;
 	case LSORT_INDICES:
 	    indices = 1;
 	    break;
 	}
     }
+
     listObj = objv[objc-1];
 
-    if (sortInfo.sortMode == SORTMODE_COMMAND) {
-	Tcl_Obj *newCommandPtr, *newObjPtr;
+    switch (sortInfo.sortMode) {
+	case SORTMODE_ASCII:
+	    if (noCase) {
+		sortInfo.sortCompareFn = SortCompareNoCase;
+	    } else {
+		sortInfo.sortCompareFn = SortCompareAscii;
+	    }
+	    break;
+	case SORTMODE_DICTIONARY:
+	    sortInfo.sortCompareFn = SortCompareDictionary;
+	    break;
+	case SORTMODE_INTEGER:
+	    sortInfo.sortCompareFn = SortCompareInteger;
+	    break;
+	case SORTMODE_REAL:
+	    sortInfo.sortCompareFn = SortCompareReal;
+	    break;
+	case SORTMODE_COMMAND: {
+	    Tcl_Obj *newCommandPtr, *newObjPtr;
 
-	/*
-	 * When sorting using a command, we are reentrant and therefore might
-	 * have the representation of the list being sorted shimmered out from
-	 * underneath our feet. Take a copy (cheap) to prevent this. [Bug
-	 * 1675116]
-	 */
+	    /*
+	     * When sorting using a command, we are reentrant and therefore
+	     * might have the representation of the list being sorted
+	     * shimmered out from underneath our feet. Take a copy (cheap) to
+	     * prevent this. [Bug 1675116]
+	     */
 
-	listObj = TclListObjCopy(interp,listObj);
-	if (listObj == NULL) {
-	    if (sortInfo.indexc > 1) {
-		ckfree((char *) sortInfo.indexv);
+	    listObj = TclListObjCopy(interp, listObj);
+	    if (listObj == NULL) {
+		if (sortInfo.indexc > 1) {
+		    ckfree((char *) sortInfo.indexv);
+		}
+		return TCL_ERROR;
 	    }
-	    return TCL_ERROR;
-	}
 
-	/*
-	 * The existing command is a list. We want to flatten it, append two
-	 * dummy arguments on the end, and replace these arguments later.
-	 */
+	    /*
+	     * The existing command is a list. We want to flatten it, append
+	     * two dummy arguments on the end, and replace these arguments
+	     * later.
+	     */
 
-	newCommandPtr = Tcl_DuplicateObj(cmdPtr);
-	TclNewObj(newObjPtr);
-	Tcl_IncrRefCount(newCommandPtr);
-	if (Tcl_ListObjAppendElement(interp, newCommandPtr, newObjPtr)
-		!= TCL_OK) {
-	    Tcl_DecrRefCount(newCommandPtr);
-	    Tcl_IncrRefCount(newObjPtr);
-	    Tcl_DecrRefCount(newObjPtr);
-	    if (sortInfo.indexc > 1) {
-		ckfree((char *) sortInfo.indexv);
+	    newCommandPtr = Tcl_DuplicateObj(cmdPtr);
+	    newObjPtr = Tcl_NewObj();
+
+	    Tcl_IncrRefCount(newCommandPtr);
+	    if (Tcl_ListObjAppendElement(interp, newCommandPtr, newObjPtr)
+		    != TCL_OK) {
+		Tcl_DecrRefCount(newCommandPtr);
+		Tcl_IncrRefCount(newObjPtr);
+		Tcl_DecrRefCount(newObjPtr);
+		if (sortInfo.indexc > 1) {
+		    ckfree((char *) sortInfo.indexv);
+		}
+		return TCL_ERROR;
 	    }
-	    return TCL_ERROR;
+	    Tcl_ListObjAppendElement(interp, newCommandPtr, Tcl_NewObj());
+	    Tcl_ListObjLength(interp, newCommandPtr, &sortInfo.compareCmdObjc);
+	    sortInfo.compareCmdPtr = newCommandPtr;
+	    sortInfo.sortCompareFn = SortCompareCommand;
+	    break;
 	}
-	Tcl_ListObjAppendElement(interp, newCommandPtr, Tcl_NewObj());
-	sortInfo.compareCmdPtr = newCommandPtr;
     }
-
+    
     sortInfo.resultCode = Tcl_ListObjGetElements(interp, listObj,
 	    &length, &listObjPtrs);
     if (sortInfo.resultCode != TCL_OK || length <= 0) {
 	goto done;
     }
-    elementArray = (SortElement *) ckalloc(length * sizeof(SortElement));
-    for (i=0; i < length; i++){
-	elementArray[i].objPtr = listObjPtrs[i];
-	elementArray[i].count = 0;
-	elementArray[i].nextPtr = &elementArray[i+1];
-    }
-    elementArray[length-1].nextPtr = NULL;
-    elementPtr = MergeSort(elementArray, &sortInfo);
-    if (sortInfo.resultCode == TCL_OK) {
-	resultPtr = Tcl_NewObj();
-	if (unique) {
-	    if (indices) {
-		for (; elementPtr != NULL ; elementPtr = elementPtr->nextPtr){
-		    if (elementPtr->count == 0) {
-			Tcl_ListObjAppendElement(NULL, resultPtr,
-				Tcl_NewIntObj(elementPtr - &elementArray[0]));
-		    }
-		}
-	    } else {
-		for (; elementPtr != NULL; elementPtr = elementPtr->nextPtr) {
-		    if (elementPtr->count == 0) {
-			Tcl_ListObjAppendElement(NULL, resultPtr,
-				elementPtr->objPtr);
-		    }
+
+    /*
+     * The documentation for lsort notes that [lsort -integer str] should
+     * _not_ give an "expected integer" error. See [Bug #219202].
+     */
+
+    if (length == 1) {
+	Tcl_SetObjResult(interp, Tcl_NewListObj(1, listObjPtrs));
+	goto done;
+    }
+
+    /*
+     * If the sort is -unique then the sort is performed on a set of
+     * SortElementUnique structures; otherwise we use the smaller SortElement.
+     * Note the common case of sorting without -unique specified has improved
+     * performance due to using a smaller elementArrayBuf: there is less
+     * memory to allocate and there will be fewer cache misses while
+     * traversing it. Note that the MergeSort algorithm does not make use of
+     * the size of a SortElement - it simply uses the nextPtr member to
+     * 'reorder' the sequence of the elements. That is, we can pass a
+     * SortElement pointer about and if sortInfo.isUnique is true then we
+     * know it is really a SortElementUnique and can access its 'unique'
+     * member. We only need to do some simple pointer forging using
+     * sizeof_SortElement to correctly populate the elementArray with the
+     * appropriate form of the sorting element.
+     */
+
+    if (sortInfo.isUnique) {
+	sizeof_SortElement = sizeof(SortElementUnique);
+    } else {
+	sizeof_SortElement = sizeof(SortElement);
+    }
+
+    /*
+     * Allocate space for the elements to be sorted and initialise them.
+     * The string/integer/Tcl_Obj that is used in the sort comparison 
+     * is cached to avoid having to do it repeatedly during the main loop.
+     */
+
+    elementArrayBuf = (char *) ckalloc(length * sizeof_SortElement);
+
+    for (i = 0, elementArrayBufPtr = elementArrayBuf; i < length;
+	    ++i, elementArrayBufPtr += sizeof_SortElement) {
+	Tcl_Obj *objPtr = listObjPtrs[i];
+	elementPtr = (SortElement *) elementArrayBufPtr;
+	elementPtr->nextPtr = (SortElement *)
+		(elementArrayBufPtr + sizeof_SortElement);
+	elementPtr->objPtr = objPtr;
+
+	if (sortInfo.isUnique) {
+	    ((SortElementUnique *) elementPtr)->unique = 1;
+	}
+
+	if (sortInfo.indexc != 0) {
+	    objPtr = SelectObjFromSublist(objPtr, &sortInfo);
+	    if (sortInfo.resultCode != TCL_OK) {
+		goto cleanup;
+	    }
+	}
+
+	switch (sortInfo.sortMode) {
+	    case SORTMODE_ASCII:
+	    case SORTMODE_DICTIONARY:
+		elementPtr->cache.string = TclGetString(objPtr);
+		break;
+	    case SORTMODE_INTEGER: {
+		long l;
+		if (Tcl_GetLongFromObj(interp, objPtr, &l) != TCL_OK) {
+		    sortInfo.resultCode = TCL_ERROR;
+		    goto cleanup;
 		}
+		elementPtr->cache.integer = l;
+		break;
 	    }
-	} else if (indices) {
-	    for (; elementPtr != NULL ; elementPtr = elementPtr->nextPtr) {
-		Tcl_ListObjAppendElement(NULL, resultPtr,
-			Tcl_NewIntObj(elementPtr - &elementArray[0]));
+	    case SORTMODE_REAL:
+	    case SORTMODE_COMMAND:
+		elementPtr->cache.tclObj = objPtr;
+		break;
+	}
+    }
+
+    /*
+     * Set the final SortElement's nextPtr to be NULL.
+     */
+
+    elementPtr->nextPtr = NULL;
+
+    /*
+     * Perform the sort.
+     */
+
+    elementPtr = MergeSort((SortElement *) elementArrayBuf, &sortInfo);
+
+    if (sortInfo.resultCode != TCL_OK) {
+	goto cleanup;
+    }
+
+    /*
+     * The sort was successful, now create the result object.
+     */
+
+    resultPtr = Tcl_NewListObj((length - sortInfo.nonUniqueCount), NULL);
+
+/*
+ * ELEM_INDEX - a helper macro that retrieves the index within the original
+ *  to-be-sorted list of a given pointer into the SortElement list.
+ *  It's prototype is:
+ *
+ * MODULE_SCOPE int ELEM_INDEX(sortElement *elementPtr);
+ */
+
+#define ELEM_INDEX(elementPtr) \
+	(int) ((((char *) elementPtr) - elementArrayBuf) / sizeof_SortElement)
+
+    if (sortInfo.isUnique) {
+	if (indices) {
+	    for (; elementPtr; elementPtr = elementPtr->nextPtr) {
+		if (((SortElementUnique *) elementPtr)->unique) {
+		    Tcl_ListObjAppendElement(NULL, resultPtr,
+			    Tcl_NewIntObj(ELEM_INDEX(elementPtr)));
+		}
 	    }
 	} else {
-	    for (; elementPtr != NULL; elementPtr = elementPtr->nextPtr) {
-		Tcl_ListObjAppendElement(NULL, resultPtr, elementPtr->objPtr);
+	    for (; elementPtr; elementPtr = elementPtr->nextPtr) {
+		if (((SortElementUnique *) elementPtr)->unique) {
+		    Tcl_ListObjAppendElement(NULL, resultPtr,
+			    elementPtr->objPtr);
+		}
 	    }
 	}
-	Tcl_SetObjResult(interp, resultPtr);
+    } else if (indices) {
+	for (; elementPtr; elementPtr = elementPtr->nextPtr) {
+	    Tcl_ListObjAppendElement(NULL, resultPtr,
+		    Tcl_NewIntObj(ELEM_INDEX(elementPtr)));
+	}
+    } else {
+	for (; elementPtr; elementPtr = elementPtr->nextPtr) {
+	    Tcl_ListObjAppendElement(NULL, resultPtr, elementPtr->objPtr);
+	}
     }
-    ckfree((char *) elementArray);
+
+    Tcl_SetObjResult(interp, resultPtr);
+
+  cleanup:
+    ckfree(elementArrayBuf);
 
   done:
     if (sortInfo.sortMode == SORTMODE_COMMAND) {
 	Tcl_DecrRefCount(sortInfo.compareCmdPtr);
 	Tcl_DecrRefCount(listObj);
-	sortInfo.compareCmdPtr = NULL;
     }
     if (sortInfo.indexc > 1) {
 	ckfree((char *) sortInfo.indexv);
@@ -4135,14 +4333,14 @@
     SortElement *elementPtr;
     int i;
 
-    for (i=0 ; i<NUM_LISTS ; 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->nextPtr = NULL;
+	for (i = 0; i < NUM_LISTS && subList[i] != NULL; ++i) {
 	    elementPtr = MergeLists(subList[i], elementPtr, infoPtr);
 	    subList[i] = NULL;
 	}
@@ -4152,7 +4350,7 @@
 	subList[i] = elementPtr;
     }
     elementPtr = NULL;
-    for (i=0 ; i<NUM_LISTS ; i++) {
+    for (i = 0; i < NUM_LISTS; ++i) {
 	elementPtr = MergeLists(subList[i], elementPtr, infoPtr);
     }
     return elementPtr;
@@ -4183,7 +4381,8 @@
 				 * operator. */
 {
     SortElement *headPtr, *tailPtr;
-    int cmp;
+    SortCompareFn_t sortCompare;
+    int cmp, isIncreasing, isUnique;
 
     if (leftPtr == NULL) {
 	return rightPtr;
@@ -4191,27 +4390,43 @@
     if (rightPtr == NULL) {
 	return leftPtr;
     }
-    cmp = SortCompare(leftPtr->objPtr, rightPtr->objPtr, infoPtr);
+    isIncreasing = infoPtr->isIncreasing;
+    isUnique     = infoPtr->isUnique;
+    sortCompare  = infoPtr->sortCompareFn;
+
+    cmp = sortCompare(leftPtr, rightPtr, infoPtr);
+    if (!isIncreasing) {
+	cmp = -cmp;
+    }
     if (cmp > 0) {
 	tailPtr = rightPtr;
 	rightPtr = rightPtr->nextPtr;
     } else {
-	if (cmp == 0) {
-	    leftPtr->count++;
+	if (isUnique && cmp == 0) {
+	    if (((SortElementUnique *) leftPtr)->unique) {
+		((SortElementUnique *) leftPtr)->unique = 0;
+		++infoPtr->nonUniqueCount;
+	    }
 	}
 	tailPtr = leftPtr;
 	leftPtr = leftPtr->nextPtr;
     }
     headPtr = tailPtr;
     while ((leftPtr != NULL) && (rightPtr != NULL)) {
-	cmp = SortCompare(leftPtr->objPtr, rightPtr->objPtr, infoPtr);
+	cmp = sortCompare(leftPtr, rightPtr, infoPtr);
+	if (!isIncreasing) {
+	    cmp = -cmp;
+	}
 	if (cmp > 0) {
 	    tailPtr->nextPtr = rightPtr;
 	    tailPtr = rightPtr;
 	    rightPtr = rightPtr->nextPtr;
 	} else {
-	    if (cmp == 0) {
-		leftPtr->count++;
+	    if (isUnique && cmp == 0) {
+		if (((SortElementUnique *) leftPtr)->unique) {
+		    ((SortElementUnique *) leftPtr)->unique = 0;
+		    ++infoPtr->nonUniqueCount;
+		}
 	    }
 	    tailPtr->nextPtr = leftPtr;
 	    tailPtr = leftPtr;
@@ -4229,129 +4444,181 @@
 /*
  *----------------------------------------------------------------------
  *
- * SortCompare --
+ * The sort comparison functions --
  *
- *	This procedure is invoked by MergeLists to determine the proper
- *	ordering between two elements.
+ *	These functions are invoked by MergeLists to determine the proper
+ *	ordering between two elements. The one used during the sort is
+ *	dependent on the options passed to "lsort". Specifically:
+ *
+ *		-ascii       SortCompareAscii
+ *		-integer     SortCompareInteger
+ *		-real        SortCompareReal
+ *		-nocase      SortCompareNoCase
+ *		-command     SortCompareCommand
+ *		-dictionary  SortCompareDictionary
  *
  * Results:
  *	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.
+ *	come first. A result of zero either means the two elements are equal
+ *	and it doesn't matter which comes first, or that an error has
+ *	occurred and infoPtr->resultCode is set to TCL_ERROR.
  *
  * Side effects:
- *	None, unless a user-defined comparison command does something weird.
+ *	None, except for SortCompareCommand in which a user-defined
+ *	comparison command might do something weird.
  *
  *----------------------------------------------------------------------
  */
 
 static int
-SortCompare(
-    Tcl_Obj *objPtr1, Tcl_Obj *objPtr2,
-				/* Values to be compared. */
-    SortInfo *infoPtr)		/* Information passed from the top-level
-				 * "lsort" command. */
+SortCompareAscii(
+    SortElement *sortElemPtr1,
+    SortElement *sortElemPtr2,
+    SortInfo *infoPtr)
+{
+    char *a, *b;
+
+    a = sortElemPtr1->cache.string;
+    b = sortElemPtr2->cache.string;
+    return strcmp(a, b);
+}
+
+static int
+SortCompareInteger(
+    SortElement *sortElemPtr1,
+    SortElement *sortElemPtr2,
+    SortInfo *infoPtr)
+{
+    long a, b;
+
+    a = sortElemPtr1->cache.integer;
+    b = sortElemPtr2->cache.integer;
+    if (a > b) {
+	return 1;
+    } else if (b > a) {
+	return -1;
+    }
+    return 0;
+}
+
+static int
+SortCompareReal(
+    SortElement *sortElemPtr1,
+    SortElement *sortElemPtr2,
+    SortInfo *infoPtr)
 {
-    int order;
+    Tcl_Obj *aPtr, *bPtr;
+    double a, b;
 
-    order = 0;
     if (infoPtr->resultCode != TCL_OK) {
 	/*
-	 * Once an error has occurred, skip any future comparisons so as to
-	 * preserve the error message in sortInterp->result.
+	 * Once an error has occurred, skip any future comparisons so
+	 * as to preserve the error message in sortInterp->result.
 	 */
-
-	return order;
+	return 0;
     }
+    aPtr = sortElemPtr1->cache.tclObj;
+    bPtr = sortElemPtr2->cache.tclObj;
 
-    objPtr1 = SelectObjFromSublist(objPtr1, infoPtr);
-    if (infoPtr->resultCode != TCL_OK) {
-	return order;
+    if ((Tcl_GetDoubleFromObj(infoPtr->interp, aPtr, &a) != TCL_OK) ||
+	    (Tcl_GetDoubleFromObj(infoPtr->interp, bPtr, &b) != TCL_OK)) {
+	infoPtr->resultCode = TCL_ERROR;
+	return 0;
     }
-    objPtr2 = SelectObjFromSublist(objPtr2, infoPtr);
-    if (infoPtr->resultCode != TCL_OK) {
-	return order;
+    if (a > b) {
+	return 1;
+    } else if (b > a) {
+	return -1;
     }
+    return 0;
+}
+
+static int
+SortCompareNoCase(
+    SortElement *sortElemPtr1,
+    SortElement *sortElemPtr2,
+    SortInfo *infoPtr)
+{
+    char *a, *b;
+
+    a = sortElemPtr1->cache.string;
+    b = sortElemPtr2->cache.string;
+#if 1
+    return strcasecmp(a, b);
+#else
+    /* [Patch #1236896] */
+    return TclUtfCasecmp(a, b);
+#endif
+}
+
+static int
+SortCompareCommand(
+    SortElement *sortElemPtr1,
+    SortElement *sortElemPtr2,
+    SortInfo *infoPtr)
+{
+    int order, objc;
+    Tcl_Obj **objv, *paramObjv[2];
 
-    if (infoPtr->sortMode == SORTMODE_ASCII) {
-	order = infoPtr->strCmpFn(TclGetString(objPtr1),
-		TclGetString(objPtr2));
-    } else if (infoPtr->sortMode == SORTMODE_DICTIONARY) {
-	order = DictionaryCompare(
-		TclGetString(objPtr1), TclGetString(objPtr2));
-    } else if (infoPtr->sortMode == SORTMODE_INTEGER) {
-	long a, b;
-
-	if ((Tcl_GetLongFromObj(infoPtr->interp, objPtr1, &a) != TCL_OK)
-		|| (Tcl_GetLongFromObj(infoPtr->interp, objPtr2, &b)
-		!= TCL_OK)) {
-	    infoPtr->resultCode = TCL_ERROR;
-	    return order;
-	}
-	if (a > b) {
-	    order = 1;
-	} else if (b > a) {
-	    order = -1;
-	}
-    } else if (infoPtr->sortMode == SORTMODE_REAL) {
-	double a, b;
-
-	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) {
-	    order = 1;
-	} else if (b > a) {
-	    order = -1;
-	}
-    } else {
-	Tcl_Obj **objv, *paramObjv[2];
-	int objc;
-
-	paramObjv[0] = objPtr1;
-	paramObjv[1] = objPtr2;
-
+    if (infoPtr->resultCode != TCL_OK) {
 	/*
-	 * We made space in the command list for the two things to compare.
-	 * Replace them and evaluate the result.
+	 * Once an error has occurred, skip any future comparisons so
+	 * as to preserve the error message in sortInterp->result.
 	 */
+	return 0;
+    }
 
-	Tcl_ListObjLength(infoPtr->interp, infoPtr->compareCmdPtr, &objc);
-	Tcl_ListObjReplace(infoPtr->interp, infoPtr->compareCmdPtr, objc - 2,
-		2, 2, paramObjv);
-	Tcl_ListObjGetElements(infoPtr->interp, infoPtr->compareCmdPtr,
-		&objc, &objv);
+    paramObjv[0] = sortElemPtr1->cache.tclObj;
+    paramObjv[1] = sortElemPtr2->cache.tclObj;
 
-	infoPtr->resultCode = Tcl_EvalObjv(infoPtr->interp, objc, objv, 0);
+    /*
+     * We made space in the command list for the two things to compare.
+     * Replace them and evaluate the result.
+     */
 
-	if (infoPtr->resultCode != TCL_OK) {
-	    Tcl_AddErrorInfo(infoPtr->interp,
-		    "\n    (-compare command)");
-	    return order;
-	}
+    objc = infoPtr->compareCmdObjc;
+    Tcl_ListObjReplace(infoPtr->interp, infoPtr->compareCmdPtr, objc - 2,
+	    2, 2, paramObjv);
+    Tcl_ListObjGetElements(infoPtr->interp, infoPtr->compareCmdPtr,
+	    &objc, &objv);
 
-	/*
-	 * Parse the result of the command.
-	 */
+    infoPtr->resultCode = Tcl_EvalObjv(infoPtr->interp, objc, objv, 0);
 
-	if (Tcl_GetIntFromObj(infoPtr->interp,
-		Tcl_GetObjResult(infoPtr->interp), &order) != TCL_OK) {
-	    Tcl_ResetResult(infoPtr->interp);
-	    Tcl_AppendResult(infoPtr->interp,
-		    "-compare command returned non-integer result", NULL);
-	    infoPtr->resultCode = TCL_ERROR;
-	    return order;
-	}
+    if (infoPtr->resultCode != TCL_OK) {
+	Tcl_AddErrorInfo(infoPtr->interp, "\n    (-compare command)");
+	return 0;
     }
-    if (!infoPtr->isIncreasing) {
-	order = -order;
+
+    /*
+     * Parse the result of the command.
+     */
+
+    order = 0;
+    if (Tcl_GetIntFromObj(infoPtr->interp,
+	    Tcl_GetObjResult(infoPtr->interp), &order) != TCL_OK) {
+	Tcl_ResetResult(infoPtr->interp);
+	Tcl_AppendResult(infoPtr->interp,
+		"-compare command returned non-integer result", NULL);
+	infoPtr->resultCode = TCL_ERROR;
+	return 0;
     }
     return order;
 }
 
+static int
+SortCompareDictionary(
+    SortElement *sortElemPtr1,
+    SortElement *sortElemPtr2,
+    SortInfo *infoPtr)
+{
+    char *a, *b;
+
+    a = sortElemPtr1->cache.string;
+    b = sortElemPtr2->cache.string;
+    return DictionaryCompare(a, b);
+}
+
 /*
  *----------------------------------------------------------------------
  *