diff options
Diffstat (limited to 'generic/tclPkg.c')
| -rw-r--r-- | generic/tclPkg.c | 703 |
1 files changed, 322 insertions, 381 deletions
diff --git a/generic/tclPkg.c b/generic/tclPkg.c index cecc634..df90cea 100644 --- a/generic/tclPkg.c +++ b/generic/tclPkg.c @@ -10,8 +10,6 @@ * See the file "license.terms" for information on usage and redistribution of * this file, and for a DISCLAIMER OF ALL WARRANTIES. * - * RCS: @(#) $Id: tclPkg.c,v 1.25 2006/11/15 20:08:45 dgp Exp $ - * * TIP #268. * Heavily rewritten to handle the extend version numbers, and extended * package requirements. @@ -44,11 +42,11 @@ typedef struct PkgAvail { typedef struct Package { char *version; /* Version that has been supplied in this * interpreter via "package provide" - * (malloc'ed). NULL means the package - * doesn't exist in this interpreter yet. */ + * (malloc'ed). NULL means the package doesn't + * exist in this interpreter yet. */ PkgAvail *availPtr; /* First in list of all available versions of * this package. */ - ClientData clientData; /* Client data. */ + const void *clientData; /* Client data. */ } Package; /* @@ -56,24 +54,24 @@ typedef struct Package { */ static int CheckVersionAndConvert(Tcl_Interp *interp, - CONST char *string, char **internal, int *stable); + const char *string, char **internal, int *stable); static int CompareVersions(char *v1i, char *v2i, int *isMajorPtr); static int CheckRequirement(Tcl_Interp *interp, - CONST char *string); + const char *string); static int CheckAllRequirements(Tcl_Interp *interp, int reqc, - Tcl_Obj *CONST reqv[]); -static int RequirementSatisfied(char *havei, CONST char *req); -static int AllRequirementsSatisfied(char *havei, int reqc, - Tcl_Obj *CONST reqv[]); + Tcl_Obj *const reqv[]); +static int RequirementSatisfied(char *havei, const char *req); +static int SomeRequirementSatisfied(char *havei, int reqc, + Tcl_Obj *const reqv[]); static void AddRequirementsToResult(Tcl_Interp *interp, int reqc, - Tcl_Obj *CONST reqv[]); + 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 Tcl_Obj * ExactRequirement(CONST char *version); -static void VersionCleanupProc(ClientData clientData, - Tcl_Interp *interp); + int reqc, Tcl_Obj *const reqv[]); +static Package * FindPackage(Tcl_Interp *interp, const char *name); +static const char * PkgRequireCore(Tcl_Interp *interp, const char *name, + int reqc, Tcl_Obj *const reqv[], + void *clientDataPtr); /* * Helper macros. @@ -108,12 +106,13 @@ static void VersionCleanupProc(ClientData clientData, *---------------------------------------------------------------------- */ +#undef Tcl_PkgProvide int Tcl_PkgProvide( Tcl_Interp *interp, /* Interpreter in which package is now * available. */ - CONST char *name, /* Name of package. */ - CONST char *version) /* Version string for package. */ + const char *name, /* Name of package. */ + const char *version) /* Version string for package. */ { return Tcl_PkgProvideEx(interp, name, version, NULL); } @@ -122,9 +121,9 @@ int Tcl_PkgProvideEx( Tcl_Interp *interp, /* Interpreter in which package is now * available. */ - CONST char *name, /* Name of package. */ - CONST char *version, /* Version string for package. */ - ClientData clientData) /* clientdata for this package (normally used + const char *name, /* Name of package. */ + const char *version, /* Version string for package. */ + const void *clientData) /* clientdata for this package (normally used * for C callback function table) */ { Package *pkgPtr; @@ -156,8 +155,10 @@ Tcl_PkgProvideEx( } return TCL_OK; } - Tcl_AppendResult(interp, "conflicting versions provided for package \"", - name, "\": ", pkgPtr->version, ", then ", version, NULL); + Tcl_SetObjResult(interp, Tcl_ObjPrintf( + "conflicting versions provided for package \"%s\": %s, then %s", + name, pkgPtr->version, version)); + Tcl_SetErrorCode(interp, "TCL", "PACKAGE", "VERSIONCONFLICT", NULL); return TCL_ERROR; } @@ -188,12 +189,13 @@ Tcl_PkgProvideEx( *---------------------------------------------------------------------- */ -CONST char * +#undef Tcl_PkgRequire +const char * Tcl_PkgRequire( Tcl_Interp *interp, /* Interpreter in which package is now * available. */ - CONST char *name, /* Name of desired package. */ - CONST char *version, /* Version string for desired version; NULL + 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 @@ -202,23 +204,23 @@ Tcl_PkgRequire( return Tcl_PkgRequireEx(interp, name, version, exact, NULL); } -CONST char * +const char * Tcl_PkgRequireEx( Tcl_Interp *interp, /* Interpreter in which package is now * available. */ - CONST char *name, /* Name of desired package. */ - CONST char *version, /* Version string for desired version; NULL + 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. */ - ClientData *clientDataPtr) /* Used to return the client data for this + void *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 * call fails for any reason. */ { Tcl_Obj *ov; - int res; + const char *result = NULL; /* * If an attempt is being made to load this into a standalone executable @@ -285,81 +287,80 @@ Tcl_PkgRequireEx( */ tclEmptyStringRep = &tclEmptyString; - Tcl_AppendResult(interp, "Cannot load package \"", name, - "\" in standalone executable: This package is not ", - "compiled with stub support", NULL); + Tcl_SetObjResult(interp, Tcl_ObjPrintf( + "Cannot load package \"%s\" in standalone executable:" + " This package is not compiled with stub support", name)); + Tcl_SetErrorCode(interp, "TCL", "PACKAGE", "UNSTUBBED", NULL); return NULL; } - /* Translate between old and new API, and defer to the new function. */ + /* + * Translate between old and new API, and defer to the new function. + */ if (version == NULL) { - res = Tcl_PkgRequireProc(interp, name, 0, NULL, clientDataPtr); + result = PkgRequireCore(interp, name, 0, NULL, clientDataPtr); } else { + if (exact && TCL_OK + != CheckVersionAndConvert(interp, version, NULL, NULL)) { + return NULL; + } + ov = Tcl_NewStringObj(version, -1); if (exact) { - ov = ExactRequirement(version); - } else { - ov = Tcl_NewStringObj(version, -1); + Tcl_AppendStringsToObj(ov, "-", version, NULL); } - Tcl_IncrRefCount(ov); - res = Tcl_PkgRequireProc(interp, name, 1, &ov, clientDataPtr); + result = PkgRequireCore(interp, name, 1, &ov, clientDataPtr); TclDecrRefCount(ov); } - if (res != TCL_OK) { - return NULL; - } + return result; +} - /* - * This function returns the version string explictly, and leaves the - * interpreter result empty. However "Tcl_PkgRequireProc" above returned - * the version through the interpreter result. Simply resetting the result - * now potentially deletes the string (obj), and the pointer to its string - * rep we have, as our result, may be dangling due to this. Our solution - * is to remember the object in interp associated data, with a proper - * reference count, and then reset the result. Now pointers will not - * dangle. It will be a leak however if nothing is done. So the next time - * we come through here we delete the object remembered by this call, as - * we can then be sure that there is no pointer to its string around - * anymore. Beyond that we have a deletion function which cleans up the - * last remembered object which was not cleaned up directly, here. - */ +int +Tcl_PkgRequireProc( + 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. */ + void *clientDataPtr) +{ + const char *result = + PkgRequireCore(interp, name, reqc, reqv, clientDataPtr); - ov = (Tcl_Obj *) Tcl_GetAssocData(interp, "tcl/Tcl_PkgRequireEx", NULL); - if (ov != NULL) { - TclDecrRefCount(ov); + if (result == NULL) { + return TCL_ERROR; } - - ov = Tcl_GetObjResult(interp); - Tcl_IncrRefCount(ov); - Tcl_SetAssocData(interp, "tcl/Tcl_PkgRequireEx", VersionCleanupProc, ov); - Tcl_ResetResult(interp); - - return TclGetString(ov); + Tcl_SetObjResult(interp, Tcl_NewStringObj(result, -1)); + return TCL_OK; } -int -Tcl_PkgRequireProc( +static const char * +PkgRequireCore( Tcl_Interp *interp, /* Interpreter in which package is now * available. */ - CONST char *name, /* Name of desired package. */ + 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 + Tcl_Obj *const reqv[], /* 0 means to use the latest version * available. */ - ClientData *clientDataPtr) + void *clientDataPtr) { Interp *iPtr = (Interp *) interp; Package *pkgPtr; PkgAvail *availPtr, *bestPtr, *bestStablePtr; char *availVersion, *bestVersion; /* Internal rep. of versions */ - int availStable; - char *script; - int code, satisfies, pass; + int availStable, code, satisfies, pass; + char *script, *pkgVersionI; Tcl_DString command; - char *pkgVersionI; + + if (TCL_OK != CheckAllRequirements(interp, reqc, reqv)) { + return NULL; + } /* * It can take up to three passes to find the package: one pass to run the @@ -374,17 +375,19 @@ Tcl_PkgRequireProc( break; } - /* - * Check whether we're already attempting to load some version - * of this package (circular dependency detection). + /* + * Check whether we're already attempting to load some version of this + * package (circular dependency detection). */ if (pkgPtr->clientData != NULL) { - Tcl_AppendResult(interp, "circular package dependency: ", - "attempt to provide ", name, " ", - (char *) pkgPtr->clientData, " requires ", name, NULL); + Tcl_SetObjResult(interp, Tcl_ObjPrintf( + "circular package dependency:" + " attempt to provide %s %s requires %s", + name, (char *) pkgPtr->clientData, name)); AddRequirementsToResult(interp, reqc, reqv); - return TCL_ERROR; + Tcl_SetErrorCode(interp, "TCL", "PACKAGE", "CIRCULARITY", NULL); + return NULL; } /* @@ -403,7 +406,7 @@ Tcl_PkgRequireProc( if (CheckVersionAndConvert(interp, availPtr->version, &availVersion, &availStable) != TCL_OK) { /* - * The provided version number is has invalid syntax. This + * The provided version number has invalid syntax. This * should not happen. This should have been caught by the * 'package ifneeded' registering the package. */ @@ -414,7 +417,10 @@ Tcl_PkgRequireProc( if (bestPtr != NULL) { int res = CompareVersions(availVersion, bestVersion, NULL); - /* Note: Use internal reps! */ + /* + * Note: Use internal reps! + */ + if (res <= 0) { /* * The version of the package sought is not as good as the @@ -432,11 +438,9 @@ Tcl_PkgRequireProc( */ if (reqc > 0) { - /* - * Check satisfaction of requirements. - */ + /* Check satisfaction of requirements. */ - satisfies = AllRequirementsSatisfied(availVersion,reqc,reqv); + satisfies = SomeRequirementSatisfied(availVersion, reqc, reqv); if (!satisfies) { ckfree(availVersion); availVersion = NULL; @@ -484,27 +488,28 @@ Tcl_PkgRequireProc( * will still exist when the script completes. */ - CONST char *versionToProvide = bestPtr->version; + char *versionToProvide = bestPtr->version; script = bestPtr->script; - pkgPtr->clientData = (ClientData) versionToProvide; - Tcl_Preserve((ClientData) script); - Tcl_Preserve((ClientData) versionToProvide); + pkgPtr->clientData = versionToProvide; + Tcl_Preserve(script); + Tcl_Preserve(versionToProvide); code = Tcl_EvalEx(interp, script, -1, TCL_EVAL_GLOBAL); - Tcl_Release((ClientData) script); + Tcl_Release(script); pkgPtr = FindPackage(interp, name); if (code == TCL_OK) { Tcl_ResetResult(interp); if (pkgPtr->version == NULL) { code = TCL_ERROR; - Tcl_AppendResult(interp, "attempt to provide package ", - name, " ", versionToProvide, - " failed: no version of package ", name, - " provided", NULL); + Tcl_SetObjResult(interp, Tcl_ObjPrintf( + "attempt to provide package %s %s failed:" + " no version of package %s provided", + name, versionToProvide, name)); + Tcl_SetErrorCode(interp, "TCL", "PACKAGE", "UNPROVIDED", + NULL); } else { char *pvi, *vi; - int res; if (CheckVersionAndConvert(interp, pkgPtr->version, &pvi, NULL) != TCL_OK) { @@ -514,26 +519,30 @@ Tcl_PkgRequireProc( ckfree(pvi); code = TCL_ERROR; } else { - res = CompareVersions(pvi, vi, NULL); + int res = CompareVersions(pvi, vi, NULL); + ckfree(pvi); ckfree(vi); - if (res != 0) { code = TCL_ERROR; - Tcl_AppendResult(interp, - "attempt to provide package ", name, " ", - versionToProvide, " failed: package ", - name, " ", pkgPtr->version, - " provided instead", NULL); + Tcl_SetObjResult(interp, Tcl_ObjPrintf( + "attempt to provide package %s %s failed:" + " package %s %s provided instead", + name, versionToProvide, + name, pkgPtr->version)); + Tcl_SetErrorCode(interp, "TCL", "PACKAGE", + "WRONGPROVIDE", NULL); } } } } else if (code != TCL_ERROR) { Tcl_Obj *codePtr = Tcl_NewIntObj(code); - Tcl_ResetResult(interp); - Tcl_AppendResult(interp, "attempt to provide package ", - name, " ", versionToProvide, " failed: ", - "bad return code: ", TclGetString(codePtr), NULL); + + Tcl_SetObjResult(interp, Tcl_ObjPrintf( + "attempt to provide package %s %s failed:" + " bad return code: %s", + name, versionToProvide, TclGetString(codePtr))); + Tcl_SetErrorCode(interp, "TCL", "PACKAGE", "BADRESULT", NULL); TclDecrRefCount(codePtr); code = TCL_ERROR; } @@ -543,7 +552,7 @@ Tcl_PkgRequireProc( "\n (\"package ifneeded %s %s\" script)", name, versionToProvide)); } - Tcl_Release((ClientData) versionToProvide); + Tcl_Release(versionToProvide); if (code != TCL_OK) { /* @@ -562,7 +571,7 @@ Tcl_PkgRequireProc( pkgPtr->version = NULL; } pkgPtr->clientData = NULL; - return TCL_ERROR; + return NULL; } break; @@ -590,26 +599,26 @@ Tcl_PkgRequireProc( Tcl_DStringFree(&command); if ((code != TCL_OK) && (code != TCL_ERROR)) { - Tcl_Obj *codePtr = Tcl_NewIntObj(code); - Tcl_ResetResult(interp); - Tcl_AppendResult(interp, "bad return code: ", - TclGetString(codePtr), NULL); - Tcl_DecrRefCount(codePtr); + Tcl_SetObjResult(interp, Tcl_ObjPrintf( + "bad return code: %d", code)); + Tcl_SetErrorCode(interp, "TCL", "PACKAGE", "BADRESULT", NULL); code = TCL_ERROR; } if (code == TCL_ERROR) { Tcl_AddErrorInfo(interp, "\n (\"package unknown\" script)"); - return TCL_ERROR; + return NULL; } Tcl_ResetResult(interp); } } if (pkgPtr->version == NULL) { - Tcl_AppendResult(interp, "can't find package ", name, NULL); + Tcl_SetObjResult(interp, Tcl_ObjPrintf( + "can't find package %s", name)); + Tcl_SetErrorCode(interp, "TCL", "PACKAGE", "UNFOUND", NULL); AddRequirementsToResult(interp, reqc, reqv); - return TCL_ERROR; + return NULL; } /* @@ -617,27 +626,29 @@ Tcl_PkgRequireProc( * provided version meets the current requirements. */ - if (reqc == 0) { - satisfies = 1; - } else { + if (reqc != 0) { CheckVersionAndConvert(interp, pkgPtr->version, &pkgVersionI, NULL); - satisfies = AllRequirementsSatisfied(pkgVersionI, reqc, reqv); + satisfies = SomeRequirementSatisfied(pkgVersionI, reqc, reqv); ckfree(pkgVersionI); - } - if (satisfies) { - if (clientDataPtr) { - *clientDataPtr = pkgPtr->clientData; + if (!satisfies) { + Tcl_SetObjResult(interp, Tcl_ObjPrintf( + "version conflict for package \"%s\": have %s, need", + name, pkgPtr->version)); + Tcl_SetErrorCode(interp, "TCL", "PACKAGE", "VERSIONCONFLICT", + NULL); + AddRequirementsToResult(interp, reqc, reqv); + return NULL; } - Tcl_SetObjResult(interp, Tcl_NewStringObj(pkgPtr->version, -1)); - return TCL_OK; } - Tcl_AppendResult(interp, "version conflict for package \"", name, - "\": have ", pkgPtr->version, ", need", NULL); - AddRequirementsToResult(interp, reqc, reqv); - return TCL_ERROR; + if (clientDataPtr) { + const void **ptr = (const void **) clientDataPtr; + + *ptr = pkgPtr->clientData; + } + return pkgPtr->version; } /* @@ -661,12 +672,13 @@ Tcl_PkgRequireProc( *---------------------------------------------------------------------- */ -CONST char * +#undef Tcl_PkgPresent +const char * Tcl_PkgPresent( Tcl_Interp *interp, /* Interpreter in which package is now * available. */ - CONST char *name, /* Name of desired package. */ - CONST char *version, /* Version string for desired version; NULL + 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 @@ -675,17 +687,17 @@ Tcl_PkgPresent( return Tcl_PkgPresentEx(interp, name, version, exact, NULL); } -CONST char * +const char * Tcl_PkgPresentEx( Tcl_Interp *interp, /* Interpreter in which package is now * available. */ - CONST char *name, /* Name of desired package. */ - CONST char *version, /* Version string for desired version; NULL + 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. */ - ClientData *clientDataPtr) /* Used to return the client data for this + void *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 * call fails for any reason. */ @@ -693,62 +705,36 @@ Tcl_PkgPresentEx( Interp *iPtr = (Interp *) interp; Tcl_HashEntry *hPtr; Package *pkgPtr; - int satisfies, result; hPtr = Tcl_FindHashEntry(&iPtr->packageTable, name); if (hPtr) { pkgPtr = Tcl_GetHashValue(hPtr); if (pkgPtr->version != NULL) { - char *pvi, *vi; - int thisIsMajor; - /* * At this point we know that the package is present. Make sure - * that the provided version meets the current requirement. + * that the provided version meets the current requirement by + * calling Tcl_PkgRequireEx() to check for us. */ - if (version == NULL) { - if (clientDataPtr) { - *clientDataPtr = pkgPtr->clientData; - } - - return pkgPtr->version; - } - - if (CheckVersionAndConvert(interp, pkgPtr->version, &pvi, - NULL) != TCL_OK) { - return NULL; - } else if (CheckVersionAndConvert(interp, version, &vi, - NULL) != TCL_OK) { - ckfree(pvi); - return NULL; - } - - result = CompareVersions(pvi, vi, &thisIsMajor); - ckfree(pvi); - ckfree(vi); - - satisfies = (result == 0) || ((result == 1) && !thisIsMajor); + const char *foundVersion = Tcl_PkgRequireEx(interp, name, version, + exact, clientDataPtr); - if ((satisfies && !exact) || (result == 0)) { - if (clientDataPtr) { - *clientDataPtr = pkgPtr->clientData; - } - - return pkgPtr->version; + if (foundVersion == NULL) { + Tcl_SetErrorCode(interp, "TCL", "LOOKUP", "PACKAGE", name, + NULL); } - Tcl_AppendResult(interp, "version conflict for package \"", name, - "\": have ", pkgPtr->version, ", need ", version, NULL); - return NULL; + return foundVersion; } } if (version != NULL) { - Tcl_AppendResult(interp, "package ", name, " ", version, - " is not present", NULL); + Tcl_SetObjResult(interp, Tcl_ObjPrintf( + "package %s %s is not present", name, version)); } else { - Tcl_AppendResult(interp, "package ", name, " is not present", NULL); + Tcl_SetObjResult(interp, Tcl_ObjPrintf( + "package %s is not present", name)); } + Tcl_SetErrorCode(interp, "TCL", "LOOKUP", "PACKAGE", name, NULL); return NULL; } @@ -775,9 +761,9 @@ Tcl_PackageObjCmd( ClientData dummy, /* Not used. */ Tcl_Interp *interp, /* Current interpreter. */ int objc, /* Number of arguments. */ - Tcl_Obj *CONST objv[]) /* Argument objects. */ + Tcl_Obj *const objv[]) /* Argument objects. */ { - static CONST char *pkgOptions[] = { + static const char *const pkgOptions[] = { "forget", "ifneeded", "names", "prefer", "present", "provide", "require", "unknown", "vcompare", "versions", "vsatisfies", NULL @@ -794,11 +780,12 @@ Tcl_PackageObjCmd( Tcl_HashEntry *hPtr; Tcl_HashSearch search; Tcl_HashTable *tablePtr; - CONST char *version; - char *argv2, *argv3, *argv4, *iva = NULL, *ivb = NULL; + const char *version; + const char *argv2, *argv3, *argv4; + char *iva = NULL, *ivb = NULL; if (objc < 2) { - Tcl_WrongNumArgs(interp, 1, objv, "option ?arg arg ...?"); + Tcl_WrongNumArgs(interp, 1, objv, "option ?arg ...?"); return TCL_ERROR; } @@ -808,7 +795,7 @@ Tcl_PackageObjCmd( } switch ((enum pkgOptions) optionIndex) { case PKG_FORGET: { - char *keyString; + const char *keyString; for (i = 2; i < objc; i++) { keyString = TclGetString(objv[i]); @@ -824,11 +811,11 @@ Tcl_PackageObjCmd( 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); + Tcl_EventuallyFree(availPtr->version, TCL_DYNAMIC); + Tcl_EventuallyFree(availPtr->script, TCL_DYNAMIC); + ckfree(availPtr); } - ckfree((char *) pkgPtr); + ckfree(pkgPtr); } break; } @@ -859,7 +846,6 @@ Tcl_PackageObjCmd( for (availPtr = pkgPtr->availPtr, prevPtr = NULL; availPtr != NULL; prevPtr = availPtr, availPtr = availPtr->nextPtr) { - if (CheckVersionAndConvert(interp, availPtr->version, &avi, NULL) != TCL_OK) { ckfree(argv3i); @@ -872,10 +858,11 @@ Tcl_PackageObjCmd( if (res == 0){ if (objc == 4) { ckfree(argv3i); - Tcl_SetResult(interp, availPtr->script, TCL_VOLATILE); + Tcl_SetObjResult(interp, + Tcl_NewStringObj(availPtr->script, -1)); return TCL_OK; } - Tcl_EventuallyFree((ClientData)availPtr->script, TCL_DYNAMIC); + Tcl_EventuallyFree(availPtr->script, TCL_DYNAMIC); break; } } @@ -885,7 +872,7 @@ Tcl_PackageObjCmd( return TCL_OK; } if (availPtr == NULL) { - availPtr = (PkgAvail *) ckalloc(sizeof(PkgAvail)); + availPtr = ckalloc(sizeof(PkgAvail)); DupBlock(availPtr->version, argv3, (unsigned) length + 1); if (prevPtr == NULL) { @@ -904,49 +891,68 @@ Tcl_PackageObjCmd( if (objc != 2) { Tcl_WrongNumArgs(interp, 2, objv, NULL); return TCL_ERROR; - } - tablePtr = &iPtr->packageTable; - for (hPtr = Tcl_FirstHashEntry(tablePtr, &search); hPtr != NULL; - hPtr = Tcl_NextHashEntry(&search)) { - pkgPtr = Tcl_GetHashValue(hPtr); - if ((pkgPtr->version != NULL) || (pkgPtr->availPtr != NULL)) { - Tcl_AppendElement(interp, Tcl_GetHashKey(tablePtr, hPtr)); + } else { + Tcl_Obj *resultObj; + + resultObj = Tcl_NewObj(); + tablePtr = &iPtr->packageTable; + for (hPtr = Tcl_FirstHashEntry(tablePtr, &search); hPtr != NULL; + hPtr = Tcl_NextHashEntry(&search)) { + pkgPtr = Tcl_GetHashValue(hPtr); + if ((pkgPtr->version != NULL) || (pkgPtr->availPtr != NULL)) { + Tcl_ListObjAppendElement(NULL,resultObj, Tcl_NewStringObj( + Tcl_GetHashKey(tablePtr, hPtr), -1)); + } } + Tcl_SetObjResult(interp, resultObj); } break; - case PKG_PRESENT: + case PKG_PRESENT: { + const char *name; + if (objc < 3) { - presentSyntax: - Tcl_WrongNumArgs(interp, 2, objv, "?-exact? package ?version?"); - return TCL_ERROR; + goto require; } argv2 = TclGetString(objv[2]); if ((argv2[0] == '-') && (strcmp(argv2, "-exact") == 0)) { + if (objc != 5) { + goto requireSyntax; + } exact = 1; + name = TclGetString(objv[3]); } else { exact = 0; + name = argv2; + } + + hPtr = Tcl_FindHashEntry(&iPtr->packageTable, name); + if (hPtr != NULL) { + pkgPtr = Tcl_GetHashValue(hPtr); + if (pkgPtr->version != NULL) { + goto require; + } } + version = NULL; - if (objc == (4 + exact)) { - version = TclGetString(objv[3 + exact]); + if (exact) { + version = TclGetString(objv[4]); if (CheckVersionAndConvert(interp, version, NULL, NULL) != TCL_OK) { return TCL_ERROR; } - } else if ((objc != 3) || exact) { - goto presentSyntax; - } - if (exact) { - argv3 = TclGetString(objv[3]); - version = Tcl_PkgPresent(interp, argv3, version, exact); } else { - version = Tcl_PkgPresent(interp, argv2, version, exact); - } - if (version == NULL) { - return TCL_ERROR; + if (CheckAllRequirements(interp, objc-3, objv+3) != TCL_OK) { + return TCL_ERROR; + } + if ((objc > 3) && (CheckVersionAndConvert(interp, + TclGetString(objv[3]), NULL, NULL) == TCL_OK)) { + version = TclGetString(objv[3]); + } } - Tcl_SetObjResult(interp, Tcl_NewStringObj(version, -1)); + Tcl_PkgPresentEx(interp, name, version, exact, NULL); + return TCL_ERROR; break; + } case PKG_PROVIDE: if ((objc != 3) && (objc != 4)) { Tcl_WrongNumArgs(interp, 2, objv, "package ?version?"); @@ -958,7 +964,8 @@ Tcl_PackageObjCmd( if (hPtr != NULL) { pkgPtr = Tcl_GetHashValue(hPtr); if (pkgPtr->version != NULL) { - Tcl_SetResult(interp, pkgPtr->version, TCL_VOLATILE); + Tcl_SetObjResult(interp, + Tcl_NewStringObj(pkgPtr->version, -1)); } } return TCL_OK; @@ -967,12 +974,13 @@ Tcl_PackageObjCmd( if (CheckVersionAndConvert(interp, argv3, NULL, NULL) != TCL_OK) { return TCL_ERROR; } - return Tcl_PkgProvide(interp, argv2, argv3); + return Tcl_PkgProvideEx(interp, argv2, argv3, NULL); case PKG_REQUIRE: + require: if (objc < 3) { requireSyntax: Tcl_WrongNumArgs(interp, 2, objv, - "?-exact? package ?requirement...?"); + "?-exact? package ?requirement ...?"); return TCL_ERROR; } @@ -997,7 +1005,8 @@ Tcl_PackageObjCmd( * Create a new-style requirement for the exact version. */ - ov = ExactRequirement(version); + ov = Tcl_NewStringObj(version, -1); + Tcl_AppendStringsToObj(ov, "-", version, NULL); version = NULL; argv3 = TclGetString(objv[3]); @@ -1018,7 +1027,8 @@ Tcl_PackageObjCmd( if (objc == 2) { if (iPtr->packageUnknown != NULL) { - Tcl_SetResult(interp, iPtr->packageUnknown, TCL_VOLATILE); + Tcl_SetObjResult(interp, + Tcl_NewStringObj(iPtr->packageUnknown, -1)); } } else if (objc == 3) { if (iPtr->packageUnknown != NULL) { @@ -1037,7 +1047,7 @@ Tcl_PackageObjCmd( break; } case PKG_PREFER: { - static CONST char *pkgPreferOptions[] = { + static const char *const pkgPreferOptions[] = { "latest", "stable", NULL }; @@ -1106,23 +1116,27 @@ Tcl_PackageObjCmd( if (objc != 3) { Tcl_WrongNumArgs(interp, 2, objv, "package"); return TCL_ERROR; - } - argv2 = TclGetString(objv[2]); - hPtr = Tcl_FindHashEntry(&iPtr->packageTable, argv2); - if (hPtr != NULL) { - pkgPtr = Tcl_GetHashValue(hPtr); - for (availPtr = pkgPtr->availPtr; availPtr != NULL; - availPtr = availPtr->nextPtr) { - Tcl_AppendElement(interp, availPtr->version); + } else { + Tcl_Obj *resultObj = Tcl_NewObj(); + + argv2 = TclGetString(objv[2]); + hPtr = Tcl_FindHashEntry(&iPtr->packageTable, argv2); + if (hPtr != NULL) { + pkgPtr = Tcl_GetHashValue(hPtr); + for (availPtr = pkgPtr->availPtr; availPtr != NULL; + availPtr = availPtr->nextPtr) { + Tcl_ListObjAppendElement(NULL, resultObj, + Tcl_NewStringObj(availPtr->version, -1)); + } } + Tcl_SetObjResult(interp, resultObj); } break; case PKG_VSATISFIES: { char *argv2i = NULL; if (objc < 4) { - Tcl_WrongNumArgs(interp, 2, objv, - "version requirement requirement..."); + Tcl_WrongNumArgs(interp, 2, objv, "version ?requirement ...?"); return TCL_ERROR; } @@ -1134,7 +1148,7 @@ Tcl_PackageObjCmd( return TCL_ERROR; } - satisfies = AllRequirementsSatisfied(argv2i, objc-3, objv+3); + satisfies = SomeRequirementSatisfied(argv2i, objc-3, objv+3); ckfree(argv2i); Tcl_SetObjResult(interp, Tcl_NewBooleanObj(satisfies)); @@ -1167,7 +1181,7 @@ Tcl_PackageObjCmd( 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 fine. */ { Interp *iPtr = (Interp *) interp; Tcl_HashEntry *hPtr; @@ -1176,7 +1190,7 @@ FindPackage( hPtr = Tcl_CreateHashEntry(&iPtr->packageTable, name, &isNew); if (isNew) { - pkgPtr = (Package *) ckalloc(sizeof(Package)); + pkgPtr = ckalloc(sizeof(Package)); pkgPtr->version = NULL; pkgPtr->availPtr = NULL; pkgPtr->clientData = NULL; @@ -1222,11 +1236,11 @@ TclFreePackageInfo( 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); + Tcl_EventuallyFree(availPtr->version, TCL_DYNAMIC); + Tcl_EventuallyFree(availPtr->script, TCL_DYNAMIC); + ckfree(availPtr); } - ckfree((char *) pkgPtr); + ckfree(pkgPtr); } Tcl_DeleteHashTable(&iPtr->packageTable); if (iPtr->packageUnknown != NULL) { @@ -1257,13 +1271,13 @@ TclFreePackageInfo( static int CheckVersionAndConvert( Tcl_Interp *interp, /* Used for error reporting. */ - CONST char *string, /* Supposedly a version number, which is + const char *string, /* Supposedly a version number, which is * groups of decimal digits separated by * dots. */ char **internal, /* Internal normalized representation */ int *stable) /* Flag: Version is (un)stable. */ { - CONST char *p = string; + const char *p = string; char prevChar; int hasunstable = 0; /* @@ -1348,8 +1362,9 @@ CheckVersionAndConvert( error: ckfree(ibuf); - Tcl_AppendResult(interp, "expected version number but got \"", string, - "\"", NULL); + Tcl_SetObjResult(interp, Tcl_ObjPrintf( + "expected version number but got \"%s\"", string)); + Tcl_SetErrorCode(interp, "TCL", "VALUE", "VERSION", NULL); return TCL_ERROR; } @@ -1511,7 +1526,10 @@ CompareVersions( if (*s1 != 0) { s1++; } else if (*s2 == 0) { - /* s1, s2 both at the end => identical */ + /* + * s1, s2 both at the end => identical + */ + res = 0; break; } @@ -1550,7 +1568,7 @@ static int CheckAllRequirements( Tcl_Interp *interp, int reqc, /* Requirements to check. */ - Tcl_Obj *CONST reqv[]) + Tcl_Obj *const reqv[]) { int i; @@ -1583,7 +1601,7 @@ CheckAllRequirements( static int CheckRequirement( Tcl_Interp *interp, /* Used for error reporting. */ - CONST char *string) /* Supposedly a requirement. */ + const char *string) /* Supposedly a requirement. */ { /* * Syntax of requirement = version @@ -1596,7 +1614,7 @@ CheckRequirement( dash = strchr(string, '-'); if (dash == NULL) { /* - * no dash found, has to be a simple version. + * No dash found, has to be a simple version. */ return CheckVersionAndConvert(interp, string, NULL, NULL); @@ -1607,15 +1625,17 @@ CheckRequirement( * More dashes found after the first. This is wrong. */ - Tcl_AppendResult(interp, "expected versionMin-versionMax but got \"", - string, "\"", NULL); + Tcl_SetObjResult(interp, Tcl_ObjPrintf( + "expected versionMin-versionMax but got \"%s\"", string)); + Tcl_SetErrorCode(interp, "TCL", "VALUE", "VERSIONRANGE", NULL); return TCL_ERROR; } /* * Exactly one dash is present. Copy the string, split at the location of * dash and check that both parts are versions. Note that the max part can - * be empty. + * be empty. Also note that the string allocated with strdup() must be + * freed with free() and not ckfree(). */ DupString(buf, string); @@ -1655,14 +1675,20 @@ AddRequirementsToResult( Tcl_Interp *interp, int reqc, /* Requirements constraining the desired * version. */ - Tcl_Obj *CONST reqv[]) /* 0 means to use the latest version + Tcl_Obj *const reqv[]) /* 0 means to use the latest version * available. */ { - if (reqc > 0) { - int i; + Tcl_Obj *result = Tcl_GetObjResult(interp); + int i, length; - for (i = 0; i < reqc; i++) { - Tcl_AppendResult(interp, " ", TclGetString(reqv[i]), NULL); + for (i = 0; i < reqc; i++) { + const char *v = Tcl_GetStringFromObj(reqv[i], &length); + + if ((length & 0x1) && (v[length/2] == '-') + && (strncmp(v, v+((length+1)/2), length/2) == 0)) { + Tcl_AppendPrintfToObj(result, " exactly %s", v+((length+1)/2)); + } else { + Tcl_AppendPrintfToObj(result, " %s", v); } } } @@ -1688,23 +1714,25 @@ AddRequirementsToDString( Tcl_DString *dsPtr, int reqc, /* Requirements constraining the desired * version. */ - Tcl_Obj *CONST reqv[]) /* 0 means to use the latest version + Tcl_Obj *const reqv[]) /* 0 means to use the latest version * available. */ { - if (reqc > 0) { - int i; + int i; + if (reqc > 0) { for (i = 0; i < reqc; i++) { - Tcl_DStringAppend(dsPtr, " ", 1); - Tcl_DStringAppend(dsPtr, TclGetString(reqv[i]), -1); + TclDStringAppendLiteral(dsPtr, " "); + TclDStringAppendObj(dsPtr, reqv[i]); } + } else { + TclDStringAppendLiteral(dsPtr, " 0-"); } } /* *---------------------------------------------------------------------- * - * AllRequirementSatisfied -- + * SomeRequirementSatisfied -- * * This function checks to see whether a version satisfies at least one * of a set of requirements. @@ -1721,12 +1749,12 @@ AddRequirementsToDString( */ static int -AllRequirementsSatisfied( +SomeRequirementSatisfied( char *availVersionI, /* Candidate version to check against the * requirements. */ int reqc, /* Requirements constraining the desired * version. */ - Tcl_Obj *CONST reqv[]) /* 0 means to use the latest version + Tcl_Obj *const reqv[]) /* 0 means to use the latest version * available. */ { int i; @@ -1761,7 +1789,7 @@ static int RequirementSatisfied( char *havei, /* Version string, of candidate package we * have. */ - CONST char *req) /* Requirement string the candidate has to + const char *req) /* Requirement string the candidate has to * satisfy. */ { /* @@ -1843,14 +1871,15 @@ RequirementSatisfied( /* *---------------------------------------------------------------------- * - * ExactRequirement -- + * Tcl_PkgInitStubsCheck -- * - * This function is the core for the translation of -exact requests. It - * translates the request of the version into a range of versions. The - * translation was chosen for backwards compatibility. + * This is a replacement routine for Tcl_InitStubs() that is called + * from code where -DUSE_TCL_STUBS has not been enabled. * * Results: - * A Tcl_Obj containing the version range as string. + * Returns the version of a conforming stubs table, or NULL, if + * the table version doesn't satisfy the requested requirements, + * according to historical practice. * * Side effects: * None. @@ -1858,121 +1887,33 @@ RequirementSatisfied( *---------------------------------------------------------------------- */ -static Tcl_Obj * -ExactRequirement( - CONST char *version) +const char * +Tcl_PkgInitStubsCheck( + Tcl_Interp *interp, + const char * version, + int exact) { - /* - * A -exact request for a version X.y is translated into the range - * X.y-X.(y+1). For example -exact 8.4 means the range "8.4-8.5". - * - * This translation was chosen to prevent packages which currently use a - * 'package require -exact tclversion' from being affected by the core now - * registering itself as 8.4.x (patchlevel) instead of 8.4 (version). - * Examples are tbcload, compiler, and ITcl. - * - * Translating -exact 8.4 to the range "8.4-8.4" instead would require us - * and everyone else to rebuild these packages to require -exact 8.4.14, - * or whatever the exact current patchlevel is. A backward compatibility - * issue with effects similar to the bugfix made in 8.5 now requiring - * ifneeded and provided versions to match. Instead we have chosen to - * interpret exactness to not be exactly equal, but to be exact only - * within the specified level, and allowing variation in the deeper level. - * More examples: - * - * -exact 8 => "8-9" - * -exact 8.4 => "8.4-8.5" - * -exact 8.4.14 => "8.4.14-8.4.15" - * -exact 8.0a2 => "8.0a2-8.0a3" - */ - - char *iv, buf[30]; - int lc, i; - CONST char **lv; - Tcl_Obj *objPtr = Tcl_NewStringObj(version, -1); + const char *actualVersion = Tcl_PkgPresent(interp, "Tcl", version, 0); - Tcl_AppendStringsToObj(objPtr, "-", NULL); + if (exact && actualVersion) { + const char *p = version; + int count = 0; - /* - * Assuming valid syntax here. - */ - - CheckVersionAndConvert(NULL, version, &iv, NULL); - - /* - * Split the list into components. - */ - - Tcl_SplitList(NULL, iv, &lc, &lv); - - /* - * Iterate over the components and make them parts of the result. Except - * for the last, which is handled separately, to allow the incrementation. - */ - - for (i=0; i < (lc-1); i++) { - /* - * Regular component. - */ - - Tcl_AppendStringsToObj(objPtr, lv[i], NULL); - - /* - * Separator component. - */ - - i++; - if (0 == strcmp("-1", lv[i])) { - Tcl_AppendStringsToObj(objPtr, "b", NULL); - } else if (0 == strcmp("-2", lv[i])) { - Tcl_AppendStringsToObj(objPtr, "a", NULL); + while (*p) { + count += !isdigit(UCHAR(*p++)); + } + if (count == 1) { + if (0 != strncmp(version, actualVersion, strlen(version))) { + /* Construct error message */ + Tcl_PkgPresent(interp, "Tcl", version, 1); + return NULL; + } } else { - Tcl_AppendStringsToObj(objPtr, ".", NULL); + return Tcl_PkgPresent(interp, "Tcl", version, 1); } } - - /* - * Regular component, last. - */ - - sprintf(buf, "%d", atoi(lv[lc-1]) + 1); - Tcl_AppendStringsToObj(objPtr, buf, NULL); - - ckfree((char *) iv); - ckfree((char *) lv); - return objPtr; -} - -/* - *---------------------------------------------------------------------- - * - * VersionCleanupProc -- - * - * This function is called to delete the last remember package version - * string for an interpreter when the interpreter is deleted. It gets - * invoked via the Tcl AssocData mechanism. - * - * Results: - * None. - * - * Side effects: - * Storage for the version object for interp get deleted. - * - *---------------------------------------------------------------------- - */ - -static void -VersionCleanupProc( - ClientData clientData, /* Pointer to remembered version string object - * for interp. */ - Tcl_Interp *interp) /* Interpreter that is being deleted. */ -{ - Tcl_Obj *ov = clientData; - if (ov != NULL) { - TclDecrRefCount(ov); - } + return actualVersion; } - /* * Local Variables: * mode: c |
