Attachment "tip-339-tcl86.patch" to
ticket [2316115fff]
added by
andreas_kupries
2008-11-27 05:58:19.
--- tcl86.orig/generic/tclBasic.c 2008-11-18 09:10:04.000000000 -0800
+++ tcl86/generic/tclBasic.c 2008-11-24 11:10:43.000000000 -0800
@@ -535,6 +535,7 @@
iPtr->appendUsed = 0;
Tcl_InitHashTable(&iPtr->packageTable, TCL_STRING_KEYS);
+ Tcl_InitHashTable(&iPtr->packageTableLC, TCL_STRING_KEYS);
iPtr->packageUnknown = NULL;
/* TIP #268 */
--- tcl86.orig/generic/tcl.decls 2008-11-18 09:15:02.000000000 -0800
+++ tcl86/generic/tcl.decls 2008-11-24 11:11:04.000000000 -0800
@@ -2204,6 +2204,15 @@
int *objcPtr, Tcl_Obj *const *objv, Tcl_Obj ***remObjv)
}
+# TIP#339: Case-insensitive package names
+# The slot is present even if TCL_TIP339 is not activated.
+
+declare 605 generic {
+ int Tcl_PkgRequireProcEx(Tcl_Interp *interp, CONST char *name,
+ int objc, Tcl_Obj *CONST objv[], ClientData *clientDataPtr,
+ int flags)
+}
+
##############################################################################
# Define the platform specific public Tcl interface. These functions are
--- tcl86.orig/generic/tclDecls.h 2008-11-18 09:15:02.000000000 -0800
+++ tcl86/generic/tclDecls.h 2008-11-24 12:14:51.000000000 -0800
@@ -3657,6 +3657,14 @@
const Tcl_ArgvInfo * argTable, int * objcPtr,
Tcl_Obj *const * objv, Tcl_Obj *** remObjv);
#endif
+#ifndef Tcl_PkgRequireProcEx_TCL_DECLARED
+#define Tcl_PkgRequireProcEx_TCL_DECLARED
+/* 605 */
+EXTERN int Tcl_PkgRequireProcEx (Tcl_Interp * interp,
+ CONST char * name, int objc,
+ Tcl_Obj *CONST objv[],
+ ClientData * clientDataPtr, int flags);
+#endif
typedef struct TclStubHooks {
const struct TclPlatStubs *tclPlatStubs;
@@ -4321,6 +4329,7 @@
int (*tcl_SetEnsembleParameterList) (Tcl_Interp * interp, Tcl_Command token, Tcl_Obj * paramList); /* 602 */
int (*tcl_GetEnsembleParameterList) (Tcl_Interp * interp, Tcl_Command token, Tcl_Obj ** paramListPtr); /* 603 */
int (*tcl_ParseArgsObjv) (Tcl_Interp * interp, const Tcl_ArgvInfo * argTable, int * objcPtr, Tcl_Obj *const * objv, Tcl_Obj *** remObjv); /* 604 */
+ int (*tcl_PkgRequireProcEx) (Tcl_Interp * interp, CONST char * name, int objc, Tcl_Obj *CONST objv[], ClientData * clientDataPtr, int flags); /* 605 */
} TclStubs;
#if defined(USE_TCL_STUBS) && !defined(USE_TCL_STUB_PROCS)
@@ -6813,6 +6822,10 @@
#define Tcl_ParseArgsObjv \
(tclStubsPtr->tcl_ParseArgsObjv) /* 604 */
#endif
+#ifndef Tcl_PkgRequireProcEx
+#define Tcl_PkgRequireProcEx \
+ (tclStubsPtr->tcl_PkgRequireProcEx) /* 605 */
+#endif
#endif /* defined(USE_TCL_STUBS) && !defined(USE_TCL_STUB_PROCS) */
--- tcl86.orig/generic/tcl.h 2008-11-18 09:15:02.000000000 -0800
+++ tcl86/generic/tcl.h 2008-11-24 11:12:24.000000000 -0800
@@ -2152,6 +2152,13 @@
#endif
/*
+ * TIP #339.
+ * Flags for Tcl_Pkg{Present,Require} instead of the exact boolean.
+ */
+#define TCL_PKG_EXACT (1) /* Exact version required */
+#define TCL_PKG_STRICT (2) /* Use strict (case-sensitive) package name comparison */
+
+/*
* TIP #59: The following structure is used in calls 'Tcl_RegisterConfig' to
* provide the system with the embedded configuration data.
*/
--- tcl86.orig/generic/tclInt.h 2008-11-18 09:15:02.000000000 -0800
+++ tcl86/generic/tclInt.h 2008-11-24 11:13:06.000000000 -0800
@@ -1756,6 +1756,14 @@
* commands for packages that aren't described
* in packageTable. Ckalloc'ed, may be
* NULL. */
+ Tcl_HashTable packageTableLC; /* Describes all of the packages
+ * loaded in or available to this
+ * interpreter. Keys are package
+ * names in lower case for
+ * non-strict searching, values are
+ * (Package *) pointers. Contains
+ * only the first name in the eq.class
+ */
/*
* Miscellaneous information:
*/
--- tcl86.orig/generic/tclPkg.c 2008-11-18 09:10:04.000000000 -0800
+++ tcl86/generic/tclPkg.c 2008-11-24 12:17:43.000000000 -0800
@@ -49,6 +49,17 @@
PkgAvail *availPtr; /* First in list of all available versions of
* this package. */
ClientData clientData; /* Client data. */
+
+ /* TIP #339 */
+ Tcl_HashEntry *myHEPtr; /* Owning hash entry in packageTable */
+ Tcl_HashEntry *myHEPtrLC; /* Owning hash entry in packageTableLC, if any */
+ struct Package *firstEqPtr; /* First, last, and next element of the list of */
+ struct Package *lastEqPtr; /* equivalent packages, when comparing names */
+ struct Package *nextEqPtr; /* case-insensitively. First and next are
+ * always set. Next however only when the list
+ * contains more than one element. Last is set
+ * only for the first element, and only if the
+ * list contains more than one element. */
} Package;
/*
@@ -70,10 +81,13 @@
Tcl_Obj *const reqv[]);
static void AddRequirementsToDString(Tcl_DString *dstring,
int reqc, Tcl_Obj *const reqv[]);
-static Package * FindPackage(Tcl_Interp *interp, const char *name);
+static Package * FindPackage(Tcl_Interp *interp, const char *name, int strict);
static const char * PkgRequireCore(Tcl_Interp *interp, const char *name,
int reqc, Tcl_Obj *const reqv[],
- ClientData *clientDataPtr);
+ ClientData *clientDataPtr, int strict);
+
+/* TIP #339 */
+static Tcl_HashEntry * LookupPackage(Interp *iPtr, const char *name, int strict);
/*
* Helper macros.
@@ -131,13 +145,21 @@
char *pvi, *vi;
int res;
- pkgPtr = FindPackage(interp, name);
+ pkgPtr = FindPackage(interp, name, TCL_PKG_STRICT);
if (pkgPtr->version == NULL) {
+ /* This package was not loaded yet and now is, remember the loaded
+ * version.
+ */
+
DupString(pkgPtr->version, version);
pkgPtr->clientData = clientData;
return TCL_OK;
}
+ /* A version of the package has been loaded already. Check that the now
+ * requested version matches what we have, throw an error otherwise.
+ */
+
if (CheckVersionAndConvert(interp, pkgPtr->version, &pvi,
NULL) != TCL_OK) {
return TCL_ERROR;
@@ -209,9 +231,14 @@
const char *name, /* Name of desired package. */
const char *version, /* Version string for desired version; NULL
* means use the latest version available. */
- int exact, /* Non-zero means that only the particular
- * version given is acceptable. Zero means use
- * the latest compatible version. */
+ int flags, /* Bit field. Flags are TCL_PKG_EXACT,
+ * TCL_PKG_STRICT. TCL_PKG_EXACT means that
+ * only the particular version given is
+ * acceptable. Otherwise use the latest
+ * compatible version. TCL_PKG_STRICT means
+ * that string comparison has to be
+ * case-sensitive. Otherwise ignore case in
+ * package names. */
ClientData *clientDataPtr) /* Used to return the client data for this
* package. If it is NULL then the client data
* is not returned. This is unchanged if this
@@ -296,18 +323,20 @@
*/
if (version == NULL) {
- result = PkgRequireCore(interp, name, 0, NULL, clientDataPtr);
+ result = PkgRequireCore(interp, name, 0, NULL, clientDataPtr,
+ flags & TCL_PKG_STRICT);
} else {
- if (exact && TCL_OK
+ if ((flags & TCL_PKG_EXACT) && TCL_OK
!= CheckVersionAndConvert(interp, version, NULL, NULL)) {
return NULL;
}
ov = Tcl_NewStringObj(version, -1);
- if (exact) {
+ if (flags & TCL_PKG_EXACT) {
Tcl_AppendStringsToObj(ov, "-", version, NULL);
}
Tcl_IncrRefCount(ov);
- result = PkgRequireCore(interp, name, 1, &ov, clientDataPtr);
+ result = PkgRequireCore(interp, name, 1, &ov, clientDataPtr,
+ flags & TCL_PKG_STRICT);
TclDecrRefCount(ov);
}
@@ -325,8 +354,23 @@
* available. */
ClientData *clientDataPtr)
{
+ return Tcl_PkgRequireProcEx (interp, name, reqc, reqv, clientDataPtr, 0 /*!strict*/);
+}
+
+int
+Tcl_PkgRequireProcEx(
+ Tcl_Interp *interp, /* Interpreter in which package is now
+ * available. */
+ CONST char *name, /* Name of desired package. */
+ int reqc, /* Requirements constraining the desired
+ * version. */
+ Tcl_Obj *CONST reqv[], /* 0 means to use the latest version
+ * available. */
+ ClientData *clientDataPtr,
+ int flags)
+{
const char *result =
- PkgRequireCore(interp, name, reqc, reqv, clientDataPtr);
+ PkgRequireCore(interp, name, reqc, reqv, clientDataPtr, flags & TCL_PKG_STRICT);
if (result == NULL) {
return TCL_ERROR;
@@ -344,7 +388,8 @@
* version. */
Tcl_Obj *const reqv[], /* 0 means to use the latest version
* available. */
- ClientData *clientDataPtr)
+ ClientData *clientDataPtr,
+ int flags) /* TCL_PKG_STRICT package search or not ? */
{
Interp *iPtr = (Interp *) interp;
Package *pkgPtr;
@@ -363,7 +408,7 @@
*/
for (pass=1 ;; pass++) {
- pkgPtr = FindPackage(interp, name);
+ pkgPtr = FindPackage(interp, name, flags);
if (pkgPtr->version != NULL) {
break;
}
@@ -486,7 +531,7 @@
code = Tcl_EvalEx(interp, script, -1, TCL_EVAL_GLOBAL);
Tcl_Release((ClientData) script);
- pkgPtr = FindPackage(interp, name);
+ pkgPtr = FindPackage(interp, name, flags);
if (code == TCL_OK) {
Tcl_ResetResult(interp);
if (pkgPtr->version == NULL) {
@@ -660,11 +705,16 @@
const char *name, /* Name of desired package. */
const char *version, /* Version string for desired version; NULL
* means use the latest version available. */
- int exact) /* Non-zero means that only the particular
- * version given is acceptable. Zero means use
- * the latest compatible version. */
+ int flags) /* Bit field. Flags are TCL_PKG_EXACT,
+ * TCL_PKG_STRICT. TCL_PKG_EXACT means that
+ * only the particular version given is
+ * acceptable. Otherwise use the latest
+ * compatible version. TCL_PKG_STRICT means
+ * that string comparison has to be
+ * case-sensitive. Otherwise ignore case in
+ * package names. */
{
- return Tcl_PkgPresentEx(interp, name, version, exact, NULL);
+ return Tcl_PkgPresentEx(interp, name, version, flags, NULL);
}
const char *
@@ -674,9 +724,14 @@
const char *name, /* Name of desired package. */
const char *version, /* Version string for desired version; NULL
* means use the latest version available. */
- int exact, /* Non-zero means that only the particular
- * version given is acceptable. Zero means use
- * the latest compatible version. */
+ int flags, /* Bit field. Flags are TCL_PKG_EXACT,
+ * TCL_PKG_STRICT. TCL_PKG_EXACT means that
+ * only the particular version given is
+ * acceptable. Otherwise use the latest
+ * compatible version. TCL_PKG_STRICT means
+ * that string comparison has to be
+ * case-sensitive. Otherwise ignore case in
+ * package names. */
ClientData *clientDataPtr) /* Used to return the client data for this
* package. If it is NULL then the client data
* is not returned. This is unchanged if this
@@ -686,7 +741,7 @@
Tcl_HashEntry *hPtr;
Package *pkgPtr;
- hPtr = Tcl_FindHashEntry(&iPtr->packageTable, name);
+ hPtr = LookupPackage (iPtr, name, flags & TCL_PKG_STRICT);
if (hPtr) {
pkgPtr = Tcl_GetHashValue(hPtr);
if (pkgPtr->version != NULL) {
@@ -697,7 +752,7 @@
*/
const char *foundVersion = Tcl_PkgRequireEx(interp, name, version,
- exact, clientDataPtr);
+ flags, clientDataPtr);
if (foundVersion == NULL) {
Tcl_SetErrorCode(interp, "TCL", "LOOKUP", "PACKAGE", name,
@@ -734,6 +789,34 @@
*----------------------------------------------------------------------
*/
+static void
+Forget (Package *pkgPtr)
+{
+ PkgAvail* availPtr;
+
+ if (pkgPtr->version != NULL) {
+ ckfree(pkgPtr->version);
+ }
+ while (pkgPtr->availPtr != NULL) {
+ availPtr = pkgPtr->availPtr;
+ pkgPtr->availPtr = availPtr->nextPtr;
+ Tcl_EventuallyFree((ClientData)availPtr->version, TCL_DYNAMIC);
+ Tcl_EventuallyFree((ClientData)availPtr->script, TCL_DYNAMIC);
+ ckfree((char *) availPtr);
+ }
+ ckfree((char *) pkgPtr);
+}
+
+
+static Package*
+GetPrevious (Package *pkgPtr, Package* findPtr)
+{
+ while (pkgPtr->nextEqPtr != findPtr) {
+ pkgPtr = pkgPtr->nextEqPtr;
+ }
+ return pkgPtr;
+}
+
/* ARGSUSED */
int
Tcl_PackageObjCmd(
@@ -775,53 +858,153 @@
case PKG_FORGET: {
char *keyString;
- for (i = 2; i < objc; i++) {
- keyString = TclGetString(objv[i]);
- hPtr = Tcl_FindHashEntry(&iPtr->packageTable, keyString);
+ int start = 2;
+ int strict = 0;
+
+ if ((argv2[0] == '-') && (strcmp(argv2, "-strict") == 0)) {
+ start ++;
+ strict = 1;
+ }
+
+ for (i = start; i < objc; i++) {
+ keyString = Tcl_GetString(objv[i]);
+
+ hPtr = LookupPackage (iPtr, keyString, strict);
+
if (hPtr == NULL) {
continue;
}
- pkgPtr = Tcl_GetHashValue(hPtr);
+
+ pkgPtr = (Package *) Tcl_GetHashValue(hPtr);
+
+ if (strict) {
+ /*
+ * Delete just this package. Remove it from its equivalence
+ * class as well. Depending on where it is (first, or middle)
+ * we may have to update the LC table.
+ */
+
+ if (pkgPtr->firstEqPtr == pkgPtr) {
+ /*
+ * The removed package is the first in its equivalence
+ * class.
+ */
+
+ if (pkgPtr->nextEqPtr) {
+ /*
+ * The class will not become empty however, therefore
+ * correct the first/last links, and the LC entry for
+ * the class, the first links and the latter to point
+ * to the new first of the class.
+ */
+
+ Package* runPtr = pkgPtr->nextEqPtr;
+
+ runPtr->lastEqPtr = pkgPtr->lastEqPtr;
+ Tcl_SetHashValue(pkgPtr->myHEPtrLC, runPtr);
+
+ while (runPtr) {
+ runPtr->firstEqPtr = pkgPtr->nextEqPtr;
+ runPtr = runPtr->nextEqPtr;
+ }
+ } else {
+ /*
+ * The class contained only this element, and is thus
+ * now gone, therefore we have to delete its LC entry.
+ */
+ Tcl_DeleteHashEntry(pkgPtr->myHEPtrLC);
+ }
+ } else {
+ /*
+ * The removed package is in the middle of its class. We
+ * have to cut its structure out of the class list, and
+ * correct the next/last pointers.
+ */
+ Package* firstPtr = pkgPtr->firstEqPtr;
+ Package* prevPtr = GetPrevious (firstPtr, pkgPtr);
+ prevPtr->nextEqPtr = pkgPtr->nextEqPtr;
+ if (firstPtr->lastEqPtr == pkgPtr) {
+ firstPtr->lastEqPtr = prevPtr->nextEqPtr;
+ }
+ }
+
Tcl_DeleteHashEntry(hPtr);
- if (pkgPtr->version != NULL) {
- ckfree(pkgPtr->version);
+ Forget (pkgPtr);
+
+ } else {
+ /*
+ * We did a non-strict search, this means we have to delete
+ * the whole equivalence class. That is much simpler than
+ * manipulating just parts of the class list.
+ */
+
+ Package* firstPtr = pkgPtr->firstEqPtr;
+ Package* nextPtr;
+
+ Tcl_DeleteHashEntry(firstPtr->myHEPtrLC);
+ while (firstPtr) {
+ Tcl_DeleteHashEntry(firstPtr->myHEPtr);
+ nextPtr = firstPtr->nextEqPtr;
+ Forget (firstPtr);
+ firstPtr = nextPtr;
}
- while (pkgPtr->availPtr != NULL) {
- availPtr = pkgPtr->availPtr;
- pkgPtr->availPtr = availPtr->nextPtr;
- Tcl_EventuallyFree((ClientData)availPtr->version, TCL_DYNAMIC);
- Tcl_EventuallyFree((ClientData)availPtr->script, TCL_DYNAMIC);
- ckfree((char *) availPtr);
}
- ckfree((char *) pkgPtr);
}
break;
}
case PKG_IFNEEDED: {
int length, res;
char *argv3i, *avi;
+ /* New syntax for #339:
+ * package ifneeded -strict P V (= 5 words, fits into the check below)
+ * Old syntax:
+ * package ifneeded P V | 4
+ * package ineeded P V S | 5
+ */
+ int strict = 0;
if ((objc != 4) && (objc != 5)) {
- Tcl_WrongNumArgs(interp, 2, objv, "package version ?script?");
+ Tcl_WrongNumArgs(interp, 2, objv, "?-strict? package version ?script?");
return TCL_ERROR;
}
+ argv2 = TclGetString(objv[2]);
argv3 = TclGetString(objv[3]);
+ if ((objc == 5)) {
+ strict = 1;
+ if ((argv2[0] == '-') && (strcmp(argv2, "-strict") == 0)) {
+ objc--;
+ objv++;
+ argv2 = Tcl_GetString(objv[2]);
+ }
+ }
if (CheckVersionAndConvert(interp, argv3, &argv3i, NULL) != TCL_OK) {
return TCL_ERROR;
}
- argv2 = TclGetString(objv[2]);
if (objc == 4) {
- hPtr = Tcl_FindHashEntry(&iPtr->packageTable, argv2);
+ /*
+ * This is the form of ifneeded which queries the database for
+ * where a specific package will be loaded from. This can be
+ * non-strict.
+ */
+
+ hPtr = LookupPackage(iPtr, argv2, strict);
if (hPtr == NULL) {
ckfree(argv3i);
return TCL_OK;
}
pkgPtr = Tcl_GetHashValue(hPtr);
} else {
- pkgPtr = FindPackage(interp, argv2);
+ /* TIP#339 NOTE: ifneeded always searches strict, to ensure that
+ * the name matches the name used by 'provide'.
+ */
+ pkgPtr = FindPackage(interp, argv2, TCL_PKG_STRICT);
}
argv3 = Tcl_GetStringFromObj(objv[3], &length);
+ /* When non-strict we search in the equivalence class from first to
+ * last, until we find the version, or have exhausted the class.
+ */
+ do {
for (availPtr = pkgPtr->availPtr, prevPtr = NULL; availPtr != NULL;
prevPtr = availPtr, availPtr = availPtr->nextPtr) {
if (CheckVersionAndConvert(interp, availPtr->version, &avi,
@@ -843,6 +1026,12 @@
break;
}
}
+ if (!strict) {
+ pkgPtr = pkgPtr->nextEqPtr;
+ } else {
+ break;
+ }
+ } while (pkgPtr);
ckfree(argv3i);
if (objc == 4) {
@@ -869,6 +1058,7 @@
Tcl_WrongNumArgs(interp, 2, objv, NULL);
return TCL_ERROR;
}
+ /* TIP#339 NOTE: package names is unchanged */
tablePtr = &iPtr->packageTable;
for (hPtr = Tcl_FirstHashEntry(tablePtr, &search); hPtr != NULL;
hPtr = Tcl_NextHashEntry(&search)) {
@@ -884,18 +1074,27 @@
goto require;
}
argv2 = TclGetString(objv[2]);
+ int strict = 0;
+ exact = 0;
+
+ if ((argv2[0] == '-') && (strcmp(argv2, "-strict") == 0)) {
+ strict = TCL_PKG_STRICT;
+ objv++;
+ objc--;
+ argv2 = Tcl_GetString(objv[2]);
+ }
if ((argv2[0] == '-') && (strcmp(argv2, "-exact") == 0)) {
if (objc != 5) {
goto requireSyntax;
}
- exact = 1;
+ exact = TCL_PKG_EXACT;
name = TclGetString(objv[3]);
} else {
exact = 0;
name = argv2;
}
- hPtr = Tcl_FindHashEntry(&iPtr->packageTable, name);
+ hPtr = LookupPackage (iPtr, name, strict);
if (hPtr != NULL) {
pkgPtr = Tcl_GetHashValue(hPtr);
if (pkgPtr->version != NULL) {
@@ -919,18 +1118,35 @@
version = TclGetString(objv[3]);
}
}
- Tcl_PkgPresent(interp, name, version, exact);
+ /*
+ * Munge the search flags together for propagation to the core require
+ * resolution code.
+ */
+ Tcl_PkgPresent(interp, name, version, exact|strict);
return TCL_ERROR;
break;
}
- case PKG_PROVIDE:
+ case PKG_PROVIDE: {
+ /* New syntax for #339:
+ * package provide -strict P (= 4 words, fits into the check below)
+ * Old syntax:
+ * package provide P V | 4
+ * package provide P | 3
+ */
+ int strict = 0;
if ((objc != 3) && (objc != 4)) {
- Tcl_WrongNumArgs(interp, 2, objv, "package ?version?");
+ Tcl_WrongNumArgs(interp, 2, objv, "?-strict? package ?version?");
return TCL_ERROR;
}
argv2 = TclGetString(objv[2]);
+ if ((objc == 4) && (argv2[0] == '-') && (strcmp(argv2, "-strict") == 0)) {
+ strict = 1;
+ objc--;
+ objv++;
+ argv2 = Tcl_GetString(objv[2]);
+ }
if (objc == 3) {
- hPtr = Tcl_FindHashEntry(&iPtr->packageTable, argv2);
+ hPtr = LookupPackage (iPtr, argv2, strict);
if (hPtr != NULL) {
pkgPtr = Tcl_GetHashValue(hPtr);
if (pkgPtr->version != NULL) {
@@ -943,19 +1159,28 @@
if (CheckVersionAndConvert(interp, argv3, NULL, NULL) != TCL_OK) {
return TCL_ERROR;
}
+ /* TIP#339 NOTE: nothing to be done here, always strict, and internal */
return Tcl_PkgProvide(interp, argv2, argv3);
- case PKG_REQUIRE:
+ }
+ case PKG_REQUIRE: {
+ int strict = 0;
require:
if (objc < 3) {
requireSyntax:
Tcl_WrongNumArgs(interp, 2, objv,
- "?-exact? package ?requirement ...?");
+ "?-strict? ?-exact? package ?requirement ...?");
return TCL_ERROR;
}
version = NULL;
argv2 = TclGetString(objv[2]);
+ if ((argv2[0] == '-') && (strcmp(argv2, "-strict") == 0)) {
+ strict = TCL_PKG_STRICT;
+ objv ++;
+ objc --;
+ argv2 = Tcl_GetString(objv[2]);
+ }
if ((argv2[0] == '-') && (strcmp(argv2, "-exact") == 0)) {
Tcl_Obj *ov;
int res;
@@ -980,7 +1205,7 @@
argv3 = TclGetString(objv[3]);
Tcl_IncrRefCount(ov);
- res = Tcl_PkgRequireProc(interp, argv3, 1, &ov, NULL);
+ res = Tcl_PkgRequireProcEx(interp, argv3, 1, &ov, NULL, strict);
TclDecrRefCount(ov);
return res;
} else {
@@ -988,9 +1213,10 @@
return TCL_ERROR;
}
- return Tcl_PkgRequireProc(interp, argv2, objc-3, objv+3, NULL);
+ return Tcl_PkgRequireProcEx(interp, argv2, objc-3, objv+3, NULL, strict);
}
break;
+ }
case PKG_UNKNOWN: {
int length;
@@ -1080,13 +1306,23 @@
ckfree(iva);
ckfree(ivb);
break;
- case PKG_VERSIONS:
- if (objc != 3) {
- Tcl_WrongNumArgs(interp, 2, objv, "package");
+ case PKG_VERSIONS: {
+ int strict = 0;
+ if ((objc != 3) && (objc != 4)) {
+ versions_syntax:
+ Tcl_WrongNumArgs(interp, 2, objv, "?-strict? package");
return TCL_ERROR;
}
- argv2 = TclGetString(objv[2]);
- hPtr = Tcl_FindHashEntry(&iPtr->packageTable, argv2);
+ argv2 = Tcl_GetString(objv[2]);
+ if (objc == 4) {
+ if ((argv2[0] == '-') && (strcmp(argv2, "-strict") == 0)) {
+ strict = 1;
+ } else {
+ goto versions_syntax;
+ }
+ argv2 = Tcl_GetString(objv[3]);
+ }
+ hPtr = LookupPackage (iPtr, argv2, strict);
if (hPtr != NULL) {
pkgPtr = Tcl_GetHashValue(hPtr);
for (availPtr = pkgPtr->availPtr; availPtr != NULL;
@@ -1095,6 +1331,7 @@
}
}
break;
+ }
case PKG_VSATISFIES: {
char *argv2i = NULL;
@@ -1127,6 +1364,45 @@
/*
*----------------------------------------------------------------------
*
+ * LookupPackage --
+ *
+ * This procedure finds the Package record (represented by a HashEntry)
+ * for a particular package in a particular interpreter. None will be
+ * created if it doesn't exist.
+ *
+ * Results:
+ * The return value is a pointer to the Package record for the
+ * package, or NULL.
+ *
+ * Side effects:
+ * None.
+ *
+ *----------------------------------------------------------------------
+ */
+static Tcl_HashEntry *
+LookupPackage(
+ Interp *iPtr,
+ CONST char *name,
+ int strict)
+{
+ Tcl_HashEntry* hPtr;
+
+ if (strict) {
+ hPtr = Tcl_FindHashEntry(&iPtr->packageTable, name);
+ } else {
+ char* nameLower;
+ DupString (nameLower, name);
+ Tcl_UtfToLower (nameLower);
+
+ hPtr = Tcl_FindHashEntry(&iPtr->packageTableLC, nameLower);
+ ckfree (nameLower);
+ }
+ return hPtr;
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
* FindPackage --
*
* This function finds the Package record for a particular package in a
@@ -1145,12 +1421,28 @@
static Package *
FindPackage(
Tcl_Interp *interp, /* Interpreter to use for package lookup. */
- const char *name) /* Name of package to fine. */
+ const char *name, /* Name of package to find. */
+ int strict) /* Boolean flag. Set if case-sensitive matching is required */
{
Interp *iPtr = (Interp *) interp;
Tcl_HashEntry *hPtr;
int isNew;
Package *pkgPtr;
+ char* nameLower;
+ int isNewLC;
+
+ /* Find and create ... That is a bit of a problem ...
+ *
+ * ... strict ... look and create in regular table ...
+ * ... ...... ... if new => create in lc table ...
+ * ... ...... ... ... if !new - extend eq. list ...
+ * .............. return regular pkg ptr.
+ *
+ * !strict ... look and create in regular table ... -> new
+ * ........... look and create in lc table ... -> new'
+ * ........... new' => return created ...
+ * ........... !new' && new => extend eq list, and return first.
+ */
hPtr = Tcl_CreateHashEntry(&iPtr->packageTable, name, &isNew);
if (isNew) {
@@ -1159,9 +1451,53 @@
pkgPtr->availPtr = NULL;
pkgPtr->clientData = NULL;
Tcl_SetHashValue(hPtr, pkgPtr);
+
+ pkgPtr->myHEPtr = hPtr;
+ pkgPtr->myHEPtrLC = NULL;
+ pkgPtr->firstEqPtr = pkgPtr;
+ pkgPtr->lastEqPtr = NULL;
+ pkgPtr->nextEqPtr = NULL;
} else {
pkgPtr = Tcl_GetHashValue(hPtr);
}
+
+ /* TIP #339 */
+ DupString (nameLower, name);
+ Tcl_UtfToLower (nameLower);
+
+ hPtr = Tcl_CreateHashEntry(&iPtr->packageTableLC, nameLower, &isNewLC);
+ ckfree ((char*) nameLower);
+
+ if (!isNewLC) {
+ Package *pkgLCPtr = (Package *) Tcl_GetHashValue(hPtr);
+
+ if (isNew) {
+ /*
+ * The class list has to be extended if and only if the
+ * package name is strictly new.
+ */
+
+ if (!pkgLCPtr->nextEqPtr) {
+ pkgLCPtr->nextEqPtr = pkgPtr;
+ } else {
+ pkgLCPtr->lastEqPtr->nextEqPtr = pkgPtr;
+ }
+ pkgLCPtr->lastEqPtr = pkgPtr;
+ pkgPtr->firstEqPtr = pkgLCPtr;
+ }
+
+ if (!strict) {
+ /* Non strict match. Return first in LC table instead of the
+ * exact entry we had.
+ */
+
+ pkgPtr = pkgLCPtr;
+ }
+ } else {
+ pkgPtr->myHEPtrLC = hPtr;
+ Tcl_SetHashValue(hPtr, pkgPtr);
+ }
+
return pkgPtr;
}
@@ -1207,6 +1543,13 @@
ckfree((char *) pkgPtr);
}
Tcl_DeleteHashTable(&iPtr->packageTable);
+ /*
+ * TIP #339.
+ * Note: The contents (struct Package) have already been removed as part
+ * of deleting the regular table, see above. This table shared the
+ * pointers and contained only a subset.
+ */
+ Tcl_DeleteHashTable(&iPtr->packageTableLC);
if (iPtr->packageUnknown != NULL) {
ckfree(iPtr->packageUnknown);
}
--- tcl86.orig/generic/tclStubInit.c 2008-11-18 09:10:04.000000000 -0800
+++ tcl86/generic/tclStubInit.c 2008-11-24 12:14:52.000000000 -0800
@@ -1130,6 +1130,7 @@
Tcl_SetEnsembleParameterList, /* 602 */
Tcl_GetEnsembleParameterList, /* 603 */
Tcl_ParseArgsObjv, /* 604 */
+ Tcl_PkgRequireProcEx, /* 605 */
};
/* !END!: Do not edit above this line. */
--- tcl86.orig/generic/tclTest.c 2008-11-25 09:26:10.000000000 -0800
+++ tcl86/generic/tclTest.c 2008-11-25 09:26:00.000000000 -0800
@@ -534,7 +534,7 @@
/* TIP #268: Full patchlevel instead of just major.minor */
- if (Tcl_PkgProvide(interp, "Tcltest", TCL_PATCH_LEVEL) == TCL_ERROR) {
+ if (Tcl_PkgProvide(interp, "TcltestApp", TCL_PATCH_LEVEL) == TCL_ERROR) {
return TCL_ERROR;
}
--- tcl86.orig/library/tm.tcl 2008-11-18 09:10:03.000000000 -0800
+++ tcl86/library/tm.tcl 2008-11-26 14:54:06.000000000 -0800
@@ -217,8 +217,7 @@
if {![interp issafe] && ![file exists $path]} {
continue
}
- set currentsearchpath [file join $path $pkgroot]
- if {![interp issafe] && ![file exists $currentsearchpath]} {
+ if {![interp issafe] && ![FileExists $path $pkgroot currentsearchpath]} {
continue
}
set strip [llength [file split $path]]
@@ -232,7 +231,15 @@
catch {
# We always look for _all_ possible modules in the current
- # path, to get the max result out of the glob.
+ # path, to get the best result out of a single glob.
+
+ # NOTE regarding TIP 339: The currentsearchpath was
+ # identified via case-insensitive search (see
+ # implementation of FileExists below, as used above)
+ # and can now be used case-sensitive. The pattern for
+ # the contents has no alphanumeric, so no case-issues
+ # there either. No changes are needed for the inner
+ # loop.
foreach file [glob -nocomplain -directory $currentsearchpath *.tm] {
set pkgfilename [join [lrange [file split $file] $strip end] ::]
@@ -374,6 +381,68 @@
return
}
+# TIP 339 support.
+if {$tcl_platform(platform) eq "windows"} {
+ # On windows we can use the regular operations, they are
+ # case-insensitive.
+ proc ::tcl::tm::FileExists {base path rv} {
+ upvar $rv result
+ set result [file join $base $path]
+ return [file exists $result]
+ }
+} else {
+ # On unix we have to search ourselves to get the case-insensitve
+ # matching we want.
+
+ proc ::tcl::tm::FileExists {base path rv} {
+ upvar 1 $rv result
+ set path [file split $path]
+
+ # Unix. We glob down the path forcing case insensitive
+ # matching by constructing a proper pattern. We claim
+ # non-existence not only when we find nothing, but also when
+ # we find multiple possibilities. The latter as it indicates a
+ # violation of the TIP, namely that module names may no
+ # conflict case-insensitively.
+
+ while {[llength $path]} {
+ set rc [glob -nocomplain -directory $base [Pattern [lindex $path 0]]]
+
+ if {![llength $rc] || ([llength $rc] > 1)} {
+ # No match, or too many => consider both as
+ # non-existing. TIP 189 said that multiple modules
+ # using same name, but different case in their names
+ # are not allowed.
+ return 0
+ }
+ # Push the one and only possibility to the base and go to
+ # the next segment for the next round.
+ set base [file join [lindex $rc 0]]
+ set path [lrange $path 1 end]
+ }
+ # The final base is what we need later.
+ set result $base
+ return 1
+ }
+
+ proc ::tcl::tm::Pattern {path} {
+ set res ""
+ foreach c [split $path {}] {
+ set cu [string toupper $c]
+ set cl [string tolower $c]
+ if {$cu eq $cl} {
+ # A character which has no different cases remains as
+ # is.
+ append res $c
+ } else {
+ # A character with multiple cases, we look for both,
+ # use glob character-class
+ append res "\[$cu$cl\]"
+ }
+ }
+ }
+}
+
# Initialization. Set up the default paths, then insert the new
# handler into the chain.
--- tcl86.orig/tests/pkg.test 2008-11-18 09:10:01.000000000 -0800
+++ tcl86/tests/pkg.test 2008-11-24 12:11:22.000000000 -0800
@@ -572,10 +572,10 @@
} {}
test pkg-3.6 {Tcl_PackageCmd procedure, "ifneeded" option} {
list [catch {package ifneeded a} msg] $msg
-} {1 {wrong # args: should be "package ifneeded package version ?script?"}}
+} {1 {wrong # args: should be "package ifneeded ?-strict? package version ?script?"}}
test pkg-3.7 {Tcl_PackageCmd procedure, "ifneeded" option} {
list [catch {package ifneeded a b c d} msg] $msg
-} {1 {wrong # args: should be "package ifneeded package version ?script?"}}
+} {1 {wrong # args: should be "package ifneeded ?-strict? package version ?script?"}}
test pkg-3.8 {Tcl_PackageCmd procedure, "ifneeded" option} {
list [catch {package ifneeded t xyz} msg] $msg
} {1 {expected version number but got "xyz"}}
@@ -629,10 +629,10 @@
} {x y}
test pkg-3.17 {Tcl_PackageCmd procedure, "provide" option} {
list [catch {package provide} msg] $msg
-} {1 {wrong # args: should be "package provide package ?version?"}}
+} {1 {wrong # args: should be "package provide ?-strict? package ?version?"}}
test pkg-3.18 {Tcl_PackageCmd procedure, "provide" option} {
list [catch {package provide a b c} msg] $msg
-} {1 {wrong # args: should be "package provide package ?version?"}}
+} {1 {wrong # args: should be "package provide ?-strict? package ?version?"}}
test pkg-3.19 {Tcl_PackageCmd procedure, "provide" option} {
package forget t
package provide t
@@ -648,13 +648,13 @@
} {1 {expected version number but got "a.b"}}
test pkg-3.22 {Tcl_PackageCmd procedure, "require" option} {
list [catch {package require} msg] $msg
-} {1 {wrong # args: should be "package require ?-exact? package ?requirement ...?"}}
+} {1 {wrong # args: should be "package require ?-strict? ?-exact? package ?requirement ...?"}}
test pkg-3.24 {Tcl_PackageCmd procedure, "require" option} {
list [catch {package require -exact a b c} msg] $msg
# Exact syntax: -exact name version
# name ?requirement ...?
-} {1 {wrong # args: should be "package require ?-exact? package ?requirement ...?"}}
+} {1 {wrong # args: should be "package require ?-strict? ?-exact? package ?requirement ...?"}}
test pkg-3.26 {Tcl_PackageCmd procedure, "require" option} {
list [catch {package require x a.b} msg] $msg
@@ -664,10 +664,10 @@
} {1 {expected version number but got "a.b"}}
test pkg-3.28 {Tcl_PackageCmd procedure, "require" option} {
list [catch {package require -exact x} msg] $msg
-} {1 {wrong # args: should be "package require ?-exact? package ?requirement ...?"}}
+} {1 {wrong # args: should be "package require ?-strict? ?-exact? package ?requirement ...?"}}
test pkg-3.29 {Tcl_PackageCmd procedure, "require" option} {
list [catch {package require -exact} msg] $msg
-} {1 {wrong # args: should be "package require ?-exact? package ?requirement ...?"}}
+} {1 {wrong # args: should be "package require ?-strict? ?-exact? package ?requirement ...?"}}
test pkg-3.30 {Tcl_PackageCmd procedure, "require" option} {
package forget t
package provide t 2.3
@@ -714,10 +714,10 @@
} {0}
test pkg-3.42 {Tcl_PackageCmd procedure, "versions" option} {
list [catch {package versions} msg] $msg
-} {1 {wrong # args: should be "package versions package"}}
+} {1 {wrong # args: should be "package versions ?-strict? package"}}
test pkg-3.43 {Tcl_PackageCmd procedure, "versions" option} {
list [catch {package versions a b} msg] $msg
-} {1 {wrong # args: should be "package versions package"}}
+} {1 {wrong # args: should be "package versions ?-strict? package"}}
test pkg-3.44 {Tcl_PackageCmd procedure, "versions" option} {
package forget t
package versions t
@@ -888,13 +888,13 @@
} {1 {package t 2.4 is not present}}
test pkg-7.11 {Tcl_PackageCmd procedure, "present" option} {
list [catch {package present} msg] $msg
-} {1 {wrong # args: should be "package present ?-exact? package ?requirement ...?"}}
+} {1 {wrong # args: should be "package present ?-strict? ?-exact? package ?requirement ...?"}}
test pkg-7.12 {Tcl_PackageCmd procedure, "present" option} {
list [catch {package present a b c} msg] $msg
} {1 {expected version number but got "b"}}
test pkg-7.13 {Tcl_PackageCmd procedure, "present" option} {
list [catch {package present -exact a b c} msg] $msg
-} {1 {wrong # args: should be "package present ?-exact? package ?requirement ...?"}}
+} {1 {wrong # args: should be "package present ?-strict? ?-exact? package ?requirement ...?"}}
test pkg-7.14 {Tcl_PackageCmd procedure, "present" option} {
list [catch {package present -bs a b} msg] $msg
} {1 {expected version number but got "a"}}
@@ -906,10 +906,10 @@
} {1 {expected version number but got "a.b"}}
test pkg-7.17 {Tcl_PackageCmd procedure, "present" option} {
list [catch {package present -exact x} msg] $msg
-} {1 {wrong # args: should be "package present ?-exact? package ?requirement ...?"}}
+} {1 {wrong # args: should be "package present ?-strict? ?-exact? package ?requirement ...?"}}
test pkg-7.18 {Tcl_PackageCmd procedure, "present" option} {
list [catch {package present -exact} msg] $msg
-} {1 {wrong # args: should be "package present ?-exact? package ?requirement ...?"}}
+} {1 {wrong # args: should be "package present ?-strict? ?-exact? package ?requirement ...?"}}