os/persistentdata/persistentstorage/sqlite3api/TEST/TCL/tcldistribution/generic/tclPkg.c
Update contrib.
4 * This file implements package and version control for Tcl via
5 * the "package" command and a few C APIs.
7 * Copyright (c) 1996 Sun Microsystems, Inc.
8 * Copyright (c) 2006 Andreas Kupries <andreas_kupries@users.sourceforge.net>
9 * Portions Copyright (c) 2007-2008 Nokia Corporation and/or its subsidiaries. All rights reserved.
11 * See the file "license.terms" for information on usage and redistribution
12 * of this file, and for a DISCLAIMER OF ALL WARRANTIES.
14 * RCS: @(#) $Id: tclPkg.c,v 1.9.2.9 2007/03/19 17:06:26 dgp Exp $
17 * Heavily rewritten to handle the extend version numbers, and extended
18 * package requirements.
24 * Each invocation of the "package ifneeded" command creates a structure
25 * of the following type, which is used to load the package into the
26 * interpreter if it is requested with a "package require" command.
29 typedef struct PkgAvail {
30 char *version; /* Version string; malloc'ed. */
31 char *script; /* Script to invoke to provide this version
32 * of the package. Malloc'ed and protected
33 * by Tcl_Preserve and Tcl_Release. */
34 struct PkgAvail *nextPtr; /* Next in list of available versions of
35 * the same package. */
39 * For each package that is known in any way to an interpreter, there
40 * is one record of the following type. These records are stored in
41 * the "packageTable" hash table in the interpreter, keyed by
42 * package name such as "Tk" (no version number).
45 typedef struct Package {
46 char *version; /* Version that has been supplied in this
47 * interpreter via "package provide"
48 * (malloc'ed). NULL means the package doesn't
49 * exist in this interpreter yet. */
50 PkgAvail *availPtr; /* First in list of all available versions
52 ClientData clientData; /* Client data. */
56 * Prototypes for procedures defined in this file:
60 static int CheckVersion _ANSI_ARGS_((Tcl_Interp *interp,
62 static int ComparePkgVersions _ANSI_ARGS_((CONST char *v1,
65 static Package * FindPackage _ANSI_ARGS_((Tcl_Interp *interp,
68 static int CheckVersionAndConvert(Tcl_Interp *interp, CONST char *string,
69 char** internal, int* stable);
70 static int CompareVersions(CONST char *v1i, CONST char *v2i,
72 static int CheckRequirement(Tcl_Interp *interp, CONST char *string);
73 static int CheckAllRequirements(Tcl_Interp* interp,
74 int reqc, Tcl_Obj *CONST reqv[]);
75 static int RequirementSatisfied(CONST char *havei, CONST char *req);
76 static int AllRequirementsSatisfied(CONST char *havei,
77 int reqc, Tcl_Obj *CONST reqv[]);
78 static void AddRequirementsToResult(Tcl_Interp* interp,
79 int reqc, Tcl_Obj *CONST reqv[]);
80 static void AddRequirementsToDString(Tcl_DString* dstring,
81 int reqc, Tcl_Obj *CONST reqv[]);
82 static Package * FindPackage(Tcl_Interp *interp, CONST char *name);
83 static Tcl_Obj* ExactRequirement(CONST char* version);
84 static void VersionCleanupProc(ClientData clientData,
89 *----------------------------------------------------------------------
91 * Tcl_PkgProvide / Tcl_PkgProvideEx --
93 * This procedure is invoked to declare that a particular version
94 * of a particular package is now present in an interpreter. There
95 * must not be any other version of this package already
96 * provided in the interpreter.
99 * Normally returns TCL_OK; if there is already another version
100 * of the package loaded then TCL_ERROR is returned and an error
101 * message is left in the interp's result.
104 * The interpreter remembers that this package is available,
105 * so that no other version of the package may be provided for
108 *----------------------------------------------------------------------
112 Tcl_PkgProvide(interp, name, version)
113 Tcl_Interp *interp; /* Interpreter in which package is now
115 CONST char *name; /* Name of package. */
116 CONST char *version; /* Version string for package. */
118 return Tcl_PkgProvideEx(interp, name, version, (ClientData) NULL);
122 Tcl_PkgProvideEx(interp, name, version, clientData)
123 Tcl_Interp *interp; /* Interpreter in which package is now
125 CONST char *name; /* Name of package. */
126 CONST char *version; /* Version string for package. */
127 ClientData clientData; /* clientdata for this package (normally
128 * used for C callback function table) */
137 pkgPtr = FindPackage(interp, name);
138 if (pkgPtr->version == NULL) {
139 pkgPtr->version = ckalloc((unsigned) (strlen(version) + 1));
140 strcpy(pkgPtr->version, version);
141 pkgPtr->clientData = clientData;
145 if (ComparePkgVersions(pkgPtr->version, version, (int *) NULL) == 0) {
147 if (CheckVersionAndConvert (interp, pkgPtr->version, &pvi, NULL) != TCL_OK) {
149 } else if (CheckVersionAndConvert (interp, version, &vi, NULL) != TCL_OK) {
154 res = CompareVersions(pvi, vi, NULL);
160 if (clientData != NULL) {
161 pkgPtr->clientData = clientData;
165 Tcl_AppendResult(interp, "conflicting versions provided for package \"",
166 name, "\": ", pkgPtr->version, ", then ", version, (char *) NULL);
171 *----------------------------------------------------------------------
173 * Tcl_PkgRequire / Tcl_PkgRequireEx / Tcl_PkgRequireProc --
175 * This procedure is called by code that depends on a particular
176 * version of a particular package. If the package is not already
177 * provided in the interpreter, this procedure invokes a Tcl script
178 * to provide it. If the package is already provided, this
179 * procedure makes sure that the caller's needs don't conflict with
180 * the version that is present.
183 * If successful, returns the version string for the currently
184 * provided version of the package, which may be different from
185 * the "version" argument. If the caller's requirements
186 * cannot be met (e.g. the version requested conflicts with
187 * a currently provided version, or the required version cannot
188 * be found, or the script to provide the required version
189 * generates an error), NULL is returned and an error
190 * message is left in the interp's result.
193 * The script from some previous "package ifneeded" command may
194 * be invoked to provide the package.
196 *----------------------------------------------------------------------
201 * Empty definition for Stubs when TIP 268 is not activated.
204 Tcl_PkgRequireProc(interp,name,reqc,reqv,clientDataPtr)
205 Tcl_Interp *interp; /* Interpreter in which package is now
207 CONST char *name; /* Name of desired package. */
208 int reqc; /* Requirements constraining the desired version. */
209 Tcl_Obj *CONST reqv[]; /* 0 means to use the latest version available. */
210 ClientData *clientDataPtr;
216 EXPORT_C CONST char *
217 Tcl_PkgRequire(interp, name, version, exact)
218 Tcl_Interp *interp; /* Interpreter in which package is now
220 CONST char *name; /* Name of desired package. */
221 CONST char *version; /* Version string for desired version; NULL
222 * means use the latest version available. */
223 int exact; /* Non-zero means that only the particular
224 * version given is acceptable. Zero means use
225 * the latest compatible version. */
227 return Tcl_PkgRequireEx(interp, name, version, exact, (ClientData *) NULL);
230 EXPORT_C CONST char *
231 Tcl_PkgRequireEx(interp, name, version, exact, clientDataPtr)
232 Tcl_Interp *interp; /* Interpreter in which package is now
234 CONST char *name; /* Name of desired package. */
235 CONST char *version; /* Version string for desired version;
236 * NULL means use the latest version
238 int exact; /* Non-zero means that only the particular
239 * version given is acceptable. Zero means
240 * use the latest compatible version. */
241 ClientData *clientDataPtr; /* Used to return the client data for this
242 * package. If it is NULL then the client
243 * data is not returned. This is unchanged
244 * if this call fails for any reason. */
248 PkgAvail *availPtr, *bestPtr;
250 int code, satisfies, result, pass;
258 * If an attempt is being made to load this into a standalone executable
259 * on a platform where backlinking is not supported then this must be
260 * a shared version of Tcl (Otherwise the load would have failed).
261 * Detect this situation by checking that this library has been correctly
262 * initialised. If it has not been then return immediately as nothing will
266 if (tclEmptyStringRep == NULL) {
269 * OK, so what's going on here?
271 * First, what are we doing? We are performing a check on behalf of
272 * one particular caller, Tcl_InitStubs(). When a package is
273 * stub-enabled, it is statically linked to libtclstub.a, which
274 * contains a copy of Tcl_InitStubs(). When a stub-enabled package
275 * is loaded, its *_Init() function is supposed to call
276 * Tcl_InitStubs() before calling any other functions in the Tcl
277 * library. The first Tcl function called by Tcl_InitStubs() through
278 * the stub table is Tcl_PkgRequireEx(), so this code right here is
279 * the first code that is part of the original Tcl library in the
280 * executable that gets executed on behalf of a newly loaded
281 * stub-enabled package.
283 * One easy error for the developer/builder of a stub-enabled package
284 * to make is to forget to define USE_TCL_STUBS when compiling the
285 * package. When that happens, the package will contain symbols
286 * that are references to the Tcl library, rather than function
287 * pointers referencing the stub table. On platforms that lack
288 * backlinking, those unresolved references may cause the loading
289 * of the package to also load a second copy of the Tcl library,
290 * leading to all kinds of trouble. We would like to catch that
291 * error and report a useful message back to the user. That's
294 * Second, how does this work? If we reach this point, then the
295 * global variable tclEmptyStringRep has the value NULL. Compare
296 * that with the definition of tclEmptyStringRep near the top of
297 * the file generic/tclObj.c. It clearly should not have the value
298 * NULL; it should point to the char tclEmptyString. If we see it
299 * having the value NULL, then somehow we are seeing a Tcl library
300 * that isn't completely initialized, and that's an indicator for the
301 * error condition described above. (Further explanation is welcome.)
303 * Third, so what do we do about it? This situation indicates
304 * the package we just loaded wasn't properly compiled to be
305 * stub-enabled, yet it thinks it is stub-enabled (it called
306 * Tcl_InitStubs()). We want to report that the package just
307 * loaded is broken, so we want to place an error message in
308 * the interpreter result and return NULL to indicate failure
309 * to Tcl_InitStubs() so that it will also fail. (Further
310 * explanation why we don't want to Tcl_Panic() is welcome.
311 * After all, two Tcl libraries can't be a good thing!)
313 * Trouble is that's going to be tricky. We're now using a Tcl
314 * library that's not fully initialized. In particular, it
315 * doesn't have a proper value for tclEmptyStringRep. The
316 * Tcl_Obj system heavily depends on the value of tclEmptyStringRep
317 * and all of Tcl depends (increasingly) on the Tcl_Obj system, we
318 * need to correct that flaw before making the calls to set the
319 * interpreter result to the error message. That's the only flaw
320 * corrected; other problems with initialization of the Tcl library
321 * are not remedied, so be very careful about adding any other calls
322 * here without checking how they behave when initialization is
326 tclEmptyStringRep = &tclEmptyString;
327 Tcl_AppendResult(interp, "Cannot load package \"", name,
328 "\" in standalone executable: This package is not ",
329 "compiled with stub support", NULL);
334 /* Translate between old and new API, and defer to the new function. */
336 if (version == NULL) {
337 res = Tcl_PkgRequireProc(interp, name, 0, NULL, clientDataPtr);
340 ov = ExactRequirement (version);
342 ov = Tcl_NewStringObj (version,-1);
345 Tcl_IncrRefCount (ov);
346 res = Tcl_PkgRequireProc(interp, name, 1, &ov, clientDataPtr);
347 Tcl_DecrRefCount (ov);
354 /* This function returns the version string explictly, and leaves the
355 * interpreter result empty. However "Tcl_PkgRequireProc" above returned
356 * the version through the interpreter result. Simply resetting the result
357 * now potentially deletes the string (obj), and the pointer to its string
358 * rep we have, as our result, may be dangling due to this. Our solution
359 * is to remember the object in interp associated data, with a proper
360 * reference count, and then reset the result. Now pointers will not
361 * dangle. It will be a leak however if nothing is done. So the next time
362 * we come through here we delete the object remembered by this call, as
363 * we can then be sure that there is no pointer to its string around
364 * anymore. Beyond that we have a deletion function which cleans up the last
365 * remembered object which was not cleaned up directly, here.
368 ov = (Tcl_Obj*) Tcl_GetAssocData (interp, "tcl/Tcl_PkgRequireEx", NULL);
370 Tcl_DecrRefCount (ov);
373 ov = Tcl_GetObjResult (interp);
374 Tcl_IncrRefCount (ov);
375 Tcl_SetAssocData(interp, "tcl/Tcl_PkgRequireEx", VersionCleanupProc,
377 Tcl_ResetResult (interp);
379 return Tcl_GetString (ov);
383 Tcl_PkgRequireProc(interp,name,reqc,reqv,clientDataPtr)
384 Tcl_Interp *interp; /* Interpreter in which package is now
386 CONST char *name; /* Name of desired package. */
387 int reqc; /* Requirements constraining the desired version. */
388 Tcl_Obj *CONST reqv[]; /* 0 means to use the latest version available. */
389 ClientData *clientDataPtr;
391 Interp *iPtr = (Interp *) interp;
393 PkgAvail *availPtr, *bestPtr, *bestStablePtr;
394 char *availVersion, *bestVersion; /* Internal rep. of versions */
397 int code, satisfies, pass;
403 * It can take up to three passes to find the package: one pass to run the
404 * "package unknown" script, one to run the "package ifneeded" script for
405 * a specific version, and a final pass to lookup the package loaded by
406 * the "package ifneeded" script.
409 for (pass = 1; ; pass++) {
410 pkgPtr = FindPackage(interp, name);
411 if (pkgPtr->version != NULL) {
416 * Check whether we're already attempting to load some version
417 * of this package (circular dependency detection).
420 if (pkgPtr->clientData != NULL) {
421 Tcl_AppendResult(interp, "circular package dependency: ",
422 "attempt to provide ", name, " ",
423 (char *)(pkgPtr->clientData), " requires ", name, NULL);
425 if (version != NULL) {
426 Tcl_AppendResult(interp, " ", version, NULL);
430 AddRequirementsToResult (interp, reqc, reqv);
436 * The package isn't yet present. Search the list of available
437 * versions and invoke the script for the best available version.
439 * For TIP 268 we are actually locating the best, and the best stable
440 * version. One of them is then chosen based on the selection mode.
444 for (availPtr = pkgPtr->availPtr; availPtr != NULL;
445 availPtr = availPtr->nextPtr) {
446 if ((bestPtr != NULL) && (ComparePkgVersions(availPtr->version,
447 bestPtr->version, (int *) NULL) <= 0)) {
450 bestStablePtr = NULL;
453 for (availPtr = pkgPtr->availPtr;
455 availPtr = availPtr->nextPtr) {
456 if (CheckVersionAndConvert (interp, availPtr->version,
457 &availVersion, &availStable) != TCL_OK) {
458 /* The provided version number is has invalid syntax. This
459 * should not happen. This should have been caught by the
460 * 'package ifneeded' registering the package.
466 if (version != NULL) {
467 result = ComparePkgVersions(availPtr->version, version,
469 if ((result != 0) && exact) {
471 if (bestPtr != NULL) {
472 int res = CompareVersions (availVersion, bestVersion, NULL);
473 /* Note: Use internal reps! */
475 /* The version of the package sought is not as good as the
476 * currently selected version. Ignore it. */
477 Tcl_Free (availVersion);
485 /* We have found a version which is better than our max. */
488 /* Check satisfaction of requirements */
489 satisfies = AllRequirementsSatisfied (availVersion, reqc, reqv);
493 Tcl_Free (availVersion);
501 if (bestVersion != NULL) Tcl_Free (bestVersion);
502 bestVersion = availVersion;
505 /* If this new best version is stable then it also has to be
506 * better than the max stable version found so far.
510 bestStablePtr = availPtr;
514 if (bestVersion != NULL) {
515 Tcl_Free (bestVersion);
518 /* Now choose a version among the two best. For 'latest' we simply
519 * take (actually keep) the best. For 'stable' we take the best
520 * stable, if there is any, or the best if there is nothing stable.
523 if ((iPtr->packagePrefer == PKG_PREFER_STABLE) && (bestStablePtr != NULL)) {
524 bestPtr = bestStablePtr;
527 if (bestPtr != NULL) {
529 * We found an ifneeded script for the package. Be careful while
530 * executing it: this could cause reentrancy, so (a) protect the
531 * script itself from deletion and (b) don't assume that bestPtr
532 * will still exist when the script completes.
535 CONST char *versionToProvide = bestPtr->version;
536 script = bestPtr->script;
537 pkgPtr->clientData = (ClientData) versionToProvide;
538 Tcl_Preserve((ClientData) script);
539 Tcl_Preserve((ClientData) versionToProvide);
540 code = Tcl_EvalEx(interp, script, -1, TCL_EVAL_GLOBAL);
541 Tcl_Release((ClientData) script);
542 pkgPtr = FindPackage(interp, name);
543 if (code == TCL_OK) {
545 Tcl_ResetResult(interp);
547 if (pkgPtr->version == NULL) {
549 Tcl_ResetResult(interp);
552 Tcl_AppendResult(interp, "attempt to provide package ",
553 name, " ", versionToProvide,
554 " failed: no version of package ", name,
557 } else if (0 != ComparePkgVersions(
558 pkgPtr->version, versionToProvide, NULL)) {
559 /* At this point, it is clear that a prior
560 * [package ifneeded] command lied to us. It said
561 * that to get a particular version of a particular
562 * package, we needed to evaluate a particular script.
563 * However, we evaluated that script and got a different
564 * version than we were told. This is an error, and we
565 * ought to report it.
567 * However, we've been letting this type of error slide
568 * for a long time, and as a result, a lot of packages
571 * It's a bit too harsh to make a large number of
572 * existing packages start failing by releasing a
573 * new patch release, so we forgive this type of error
574 * for the rest of the Tcl 8.4 series.
576 * We considered reporting a warning, but in practice
577 * even that appears too harsh a change for a patch release.
579 * We limit the error reporting to only
580 * the situation where a broken ifneeded script leads
581 * to a failure to satisfy the requirement.
584 result = ComparePkgVersions(
585 pkgPtr->version, version, &satisfies);
586 if (result && (exact || !satisfies)) {
587 Tcl_ResetResult(interp);
589 Tcl_AppendResult(interp,
590 "attempt to provide package ", name, " ",
591 versionToProvide, " failed: package ",
592 name, " ", pkgPtr->version,
593 " provided instead", NULL);
600 if (CheckVersionAndConvert (interp, pkgPtr->version, &pvi, NULL) != TCL_OK) {
602 } else if (CheckVersionAndConvert (interp, versionToProvide, &vi, NULL) != TCL_OK) {
606 res = CompareVersions(pvi, vi, NULL);
610 /* At this point, it is clear that a prior
611 * [package ifneeded] command lied to us. It said
612 * that to get a particular version of a particular
613 * package, we needed to evaluate a particular script.
614 * However, we evaluated that script and got a different
615 * version than we were told. This is an error, and we
616 * ought to report it.
618 * However, we've been letting this type of error slide
619 * for a long time, and as a result, a lot of packages
622 * It's a bit too harsh to make a large number of
623 * existing packages start failing by releasing a
624 * new patch release, so we forgive this type of error
625 * for the rest of the Tcl 8.4 series.
627 * We considered reporting a warning, but in practice
628 * even that appears too harsh a change for a patch release.
630 * We limit the error reporting to only
631 * the situation where a broken ifneeded script leads
632 * to a failure to satisfy the requirement.
636 satisfies = AllRequirementsSatisfied (pvi, reqc, reqv);
638 Tcl_ResetResult(interp);
640 Tcl_AppendResult(interp,
641 "attempt to provide package ", name, " ",
642 versionToProvide, " failed: package ",
643 name, " ", pkgPtr->version,
644 " provided instead", NULL);
648 * Warning generation now disabled
649 if (code == TCL_OK) {
650 Tcl_Obj *msg = Tcl_NewStringObj(
651 "attempt to provide package ", -1);
652 Tcl_Obj *cmdPtr = Tcl_NewListObj(0, NULL);
653 Tcl_ListObjAppendElement(NULL, cmdPtr,
654 Tcl_NewStringObj("tclLog", -1));
655 Tcl_AppendStringsToObj(msg, name, " ", versionToProvide,
656 " failed: package ", name, " ",
657 pkgPtr->version, " provided instead", NULL);
658 Tcl_ListObjAppendElement(NULL, cmdPtr, msg);
659 Tcl_IncrRefCount(cmdPtr);
660 Tcl_EvalObjEx(interp, cmdPtr, TCL_EVAL_GLOBAL);
661 Tcl_DecrRefCount(cmdPtr);
662 Tcl_ResetResult(interp);
673 * Warning generation now disabled
674 if (code == TCL_OK) {
675 Tcl_Obj *msg = Tcl_NewStringObj(
676 "attempt to provide package ", -1);
677 Tcl_Obj *cmdPtr = Tcl_NewListObj(0, NULL);
678 Tcl_ListObjAppendElement(NULL, cmdPtr,
679 Tcl_NewStringObj("tclLog", -1));
680 Tcl_AppendStringsToObj(msg, name, " ", versionToProvide,
681 " failed: package ", name, " ",
682 pkgPtr->version, " provided instead", NULL);
683 Tcl_ListObjAppendElement(NULL, cmdPtr, msg);
684 Tcl_IncrRefCount(cmdPtr);
685 Tcl_EvalObjEx(interp, cmdPtr, TCL_EVAL_GLOBAL);
686 Tcl_DecrRefCount(cmdPtr);
687 Tcl_ResetResult(interp);
692 } else if (code != TCL_ERROR) {
693 Tcl_Obj *codePtr = Tcl_NewIntObj(code);
694 Tcl_ResetResult(interp);
695 Tcl_AppendResult(interp, "attempt to provide package ",
696 name, " ", versionToProvide, " failed: ",
697 "bad return code: ", Tcl_GetString(codePtr), NULL);
698 Tcl_DecrRefCount(codePtr);
701 Tcl_Release((ClientData) versionToProvide);
703 if (code != TCL_OK) {
705 * Take a non-TCL_OK code from the script as an
706 * indication the package wasn't loaded properly,
707 * so the package system should not remember an
710 * This is consistent with our returning NULL.
711 * If we're not willing to tell our caller we
712 * got a particular version, we shouldn't store
713 * that version for telling future callers either.
715 Tcl_AddErrorInfo(interp, "\n (\"package ifneeded\" script)");
716 if (pkgPtr->version != NULL) {
717 ckfree(pkgPtr->version);
718 pkgPtr->version = NULL;
720 pkgPtr->clientData = NULL;
731 * The package is not in the database. If there is a "package unknown"
732 * command, invoke it (but only on the first pass; after that, we
733 * should not get here in the first place).
739 script = ((Interp *) interp)->packageUnknown;
740 if (script != NULL) {
741 Tcl_DStringInit(&command);
742 Tcl_DStringAppend(&command, script, -1);
743 Tcl_DStringAppendElement(&command, name);
745 Tcl_DStringAppend(&command, " ", 1);
746 Tcl_DStringAppend(&command, (version != NULL) ? version : "{}",
749 Tcl_DStringAppend(&command, " -exact", 7);
752 AddRequirementsToDString(&command, reqc, reqv);
754 code = Tcl_EvalEx(interp, Tcl_DStringValue(&command),
755 Tcl_DStringLength(&command), TCL_EVAL_GLOBAL);
756 Tcl_DStringFree(&command);
757 if ((code != TCL_OK) && (code != TCL_ERROR)) {
758 Tcl_Obj *codePtr = Tcl_NewIntObj(code);
759 Tcl_ResetResult(interp);
760 Tcl_AppendResult(interp, "bad return code: ",
761 Tcl_GetString(codePtr), NULL);
762 Tcl_DecrRefCount(codePtr);
765 if (code == TCL_ERROR) {
766 Tcl_AddErrorInfo(interp, "\n (\"package unknown\" script)");
773 Tcl_ResetResult(interp);
777 if (pkgPtr->version == NULL) {
778 Tcl_AppendResult(interp, "can't find package ", name, (char *) NULL);
780 if (version != NULL) {
781 Tcl_AppendResult(interp, " ", version, (char *) NULL);
785 AddRequirementsToResult(interp, reqc, reqv);
791 * At this point we know that the package is present. Make sure that the
792 * provided version meets the current requirements.
796 if (version == NULL) {
798 *clientDataPtr = pkgPtr->clientData;
800 return pkgPtr->version;
805 CheckVersionAndConvert (interp, pkgPtr->version, &pkgVersionI, NULL);
806 satisfies = AllRequirementsSatisfied (pkgVersionI, reqc, reqv);
808 Tcl_Free (pkgVersionI);
812 result = ComparePkgVersions(pkgPtr->version, version, &satisfies);
813 if ((satisfies && !exact) || (result == 0)) {
818 *clientDataPtr = pkgPtr->clientData;
821 return pkgPtr->version;
823 Tcl_SetObjResult (interp, Tcl_NewStringObj (pkgPtr->version, -1));
827 Tcl_AppendResult(interp, "version conflict for package \"",
828 name, "\": have ", pkgPtr->version,
830 ", need ", version, (char *) NULL);
833 ", need", (char*) NULL);
834 AddRequirementsToResult (interp, reqc, reqv);
840 *----------------------------------------------------------------------
842 * Tcl_PkgPresent / Tcl_PkgPresentEx --
844 * Checks to see whether the specified package is present. If it
845 * is not then no additional action is taken.
848 * If successful, returns the version string for the currently
849 * provided version of the package, which may be different from
850 * the "version" argument. If the caller's requirements
851 * cannot be met (e.g. the version requested conflicts with
852 * a currently provided version), NULL is returned and an error
853 * message is left in interp->result.
858 *----------------------------------------------------------------------
861 EXPORT_C CONST char *
862 Tcl_PkgPresent(interp, name, version, exact)
863 Tcl_Interp *interp; /* Interpreter in which package is now
865 CONST char *name; /* Name of desired package. */
866 CONST char *version; /* Version string for desired version;
867 * NULL means use the latest version
869 int exact; /* Non-zero means that only the particular
870 * version given is acceptable. Zero means
871 * use the latest compatible version. */
873 return Tcl_PkgPresentEx(interp, name, version, exact, (ClientData *) NULL);
876 EXPORT_C CONST char *
877 Tcl_PkgPresentEx(interp, name, version, exact, clientDataPtr)
878 Tcl_Interp *interp; /* Interpreter in which package is now
880 CONST char *name; /* Name of desired package. */
881 CONST char *version; /* Version string for desired version;
882 * NULL means use the latest version
884 int exact; /* Non-zero means that only the particular
885 * version given is acceptable. Zero means
886 * use the latest compatible version. */
887 ClientData *clientDataPtr; /* Used to return the client data for this
888 * package. If it is NULL then the client
889 * data is not returned. This is unchanged
890 * if this call fails for any reason. */
892 Interp *iPtr = (Interp *) interp;
895 int satisfies, result;
897 hPtr = Tcl_FindHashEntry(&iPtr->packageTable, name);
899 pkgPtr = (Package *) Tcl_GetHashValue(hPtr);
900 if (pkgPtr->version != NULL) {
908 * At this point we know that the package is present. Make sure
909 * that the provided version meets the current requirement.
912 if (version == NULL) {
914 *clientDataPtr = pkgPtr->clientData;
917 return pkgPtr->version;
920 result = ComparePkgVersions(pkgPtr->version, version, &satisfies);
922 if (CheckVersionAndConvert (interp, pkgPtr->version, &pvi, NULL) != TCL_OK) {
924 } else if (CheckVersionAndConvert (interp, version, &vi, NULL) != TCL_OK) {
928 result = CompareVersions(pvi, vi, &thisIsMajor);
931 satisfies = (result == 0) || ((result == 1) && !thisIsMajor);
933 if ((satisfies && !exact) || (result == 0)) {
935 *clientDataPtr = pkgPtr->clientData;
938 return pkgPtr->version;
940 Tcl_AppendResult(interp, "version conflict for package \"",
941 name, "\": have ", pkgPtr->version,
942 ", need ", version, (char *) NULL);
947 if (version != NULL) {
948 Tcl_AppendResult(interp, "package ", name, " ", version,
949 " is not present", (char *) NULL);
951 Tcl_AppendResult(interp, "package ", name, " is not present",
958 *----------------------------------------------------------------------
960 * Tcl_PackageObjCmd --
962 * This procedure is invoked to process the "package" Tcl command.
963 * See the user documentation for details on what it does.
966 * A standard Tcl result.
969 * See the user documentation.
971 *----------------------------------------------------------------------
976 Tcl_PackageObjCmd(dummy, interp, objc, objv)
977 ClientData dummy; /* Not used. */
978 Tcl_Interp *interp; /* Current interpreter. */
979 int objc; /* Number of arguments. */
980 Tcl_Obj *CONST objv[]; /* Argument objects. */
982 static CONST char *pkgOptions[] = {
983 "forget", "ifneeded", "names",
987 "present", "provide", "require", "unknown", "vcompare",
988 "versions", "vsatisfies", (char *) NULL
991 PKG_FORGET, PKG_IFNEEDED, PKG_NAMES,
995 PKG_PRESENT, PKG_PROVIDE, PKG_REQUIRE, PKG_UNKNOWN, PKG_VCOMPARE,
996 PKG_VERSIONS, PKG_VSATISFIES
998 Interp *iPtr = (Interp *) interp;
999 int optionIndex, exact, i, satisfies;
1000 PkgAvail *availPtr, *prevPtr;
1002 Tcl_HashEntry *hPtr;
1003 Tcl_HashSearch search;
1004 Tcl_HashTable *tablePtr;
1005 CONST char *version;
1006 char *argv2, *argv3, *argv4;
1013 Tcl_WrongNumArgs(interp, 1, objv, "option ?arg arg ...?");
1017 if (Tcl_GetIndexFromObj(interp, objv[1], pkgOptions, "option", 0,
1018 &optionIndex) != TCL_OK) {
1021 switch ((enum pkgOptions) optionIndex) {
1025 for (i = 2; i < objc; i++) {
1026 keyString = Tcl_GetString(objv[i]);
1027 hPtr = Tcl_FindHashEntry(&iPtr->packageTable, keyString);
1031 pkgPtr = (Package *) Tcl_GetHashValue(hPtr);
1032 Tcl_DeleteHashEntry(hPtr);
1033 if (pkgPtr->version != NULL) {
1034 ckfree(pkgPtr->version);
1036 while (pkgPtr->availPtr != NULL) {
1037 availPtr = pkgPtr->availPtr;
1038 pkgPtr->availPtr = availPtr->nextPtr;
1039 Tcl_EventuallyFree((ClientData)availPtr->version, TCL_DYNAMIC);
1040 Tcl_EventuallyFree((ClientData)availPtr->script, TCL_DYNAMIC);
1041 ckfree((char *) availPtr);
1043 ckfree((char *) pkgPtr);
1049 for (i = 2; i < objc; i++) {
1050 keyString = Tcl_GetString(objv[i]);
1051 hPtr = Tcl_FindHashEntry(&iPtr->packageTable, keyString);
1055 pkgPtr = (Package *) Tcl_GetHashValue(hPtr);
1056 Tcl_DeleteHashEntry(hPtr);
1057 if (pkgPtr->version != NULL) {
1058 ckfree(pkgPtr->version);
1060 while (pkgPtr->availPtr != NULL) {
1061 availPtr = pkgPtr->availPtr;
1062 pkgPtr->availPtr = availPtr->nextPtr;
1063 Tcl_EventuallyFree((ClientData)availPtr->version, TCL_DYNAMIC);
1064 Tcl_EventuallyFree((ClientData)availPtr->script, TCL_DYNAMIC);
1065 ckfree((char *) availPtr);
1067 ckfree((char *) pkgPtr);
1071 case PKG_IFNEEDED: {
1077 if ((objc != 4) && (objc != 5)) {
1078 Tcl_WrongNumArgs(interp, 2, objv, "package version ?script?");
1081 argv3 = Tcl_GetString(objv[3]);
1082 if (CheckVersionAndConvert(interp, argv3, &argv3i, NULL) != TCL_OK) {
1087 case PKG_IFNEEDED: {
1089 if ((objc != 4) && (objc != 5)) {
1090 Tcl_WrongNumArgs(interp, 2, objv, "package version ?script?");
1093 argv2 = Tcl_GetString(objv[2]);
1095 hPtr = Tcl_FindHashEntry(&iPtr->packageTable, argv2);
1102 argv3 = Tcl_GetString(objv[3]);
1103 if (CheckVersion(interp, argv3) != TCL_OK) {
1105 pkgPtr = (Package *) Tcl_GetHashValue(hPtr);
1107 pkgPtr = FindPackage(interp, argv2);
1109 argv3 = Tcl_GetStringFromObj(objv[3], &length);
1111 for (availPtr = pkgPtr->availPtr, prevPtr = NULL;
1113 prevPtr = availPtr, availPtr = availPtr->nextPtr) {
1115 if (CheckVersionAndConvert (interp, availPtr->version, &avi, NULL) != TCL_OK) {
1121 argv2 = Tcl_GetString(objv[2]);
1123 hPtr = Tcl_FindHashEntry(&iPtr->packageTable, argv2);
1127 res = CompareVersions(avi, argv3i, NULL);
1133 Tcl_SetResult(interp, availPtr->script, TCL_VOLATILE);
1138 pkgPtr = (Package *) Tcl_GetHashValue(hPtr);
1140 pkgPtr = FindPackage(interp, argv2);
1142 argv3 = Tcl_GetStringFromObj(objv[3], &length);
1143 for (availPtr = pkgPtr->availPtr, prevPtr = NULL; availPtr != NULL;
1144 prevPtr = availPtr, availPtr = availPtr->nextPtr) {
1145 if (ComparePkgVersions(availPtr->version, argv3, (int *) NULL)
1148 Tcl_SetResult(interp, availPtr->script, TCL_VOLATILE);
1151 Tcl_EventuallyFree((ClientData)availPtr->script, TCL_DYNAMIC);
1158 Tcl_EventuallyFree((ClientData)availPtr->script, TCL_DYNAMIC);
1163 if (availPtr == NULL) {
1164 availPtr = (PkgAvail *) ckalloc(sizeof(PkgAvail));
1165 availPtr->version = ckalloc((unsigned) (length + 1));
1166 strcpy(availPtr->version, argv3);
1167 if (prevPtr == NULL) {
1168 availPtr->nextPtr = pkgPtr->availPtr;
1169 pkgPtr->availPtr = availPtr;
1171 availPtr->nextPtr = prevPtr->nextPtr;
1172 prevPtr->nextPtr = availPtr;
1180 if (availPtr == NULL) {
1181 availPtr = (PkgAvail *) ckalloc(sizeof(PkgAvail));
1182 availPtr->version = ckalloc((unsigned) (length + 1));
1183 strcpy(availPtr->version, argv3);
1184 if (prevPtr == NULL) {
1185 availPtr->nextPtr = pkgPtr->availPtr;
1186 pkgPtr->availPtr = availPtr;
1188 availPtr->nextPtr = prevPtr->nextPtr;
1189 prevPtr->nextPtr = availPtr;
1193 argv4 = Tcl_GetStringFromObj(objv[4], &length);
1194 availPtr->script = ckalloc((unsigned) (length + 1));
1195 strcpy(availPtr->script, argv4);
1202 Tcl_WrongNumArgs(interp, 2, objv, NULL);
1204 argv4 = Tcl_GetStringFromObj(objv[4], &length);
1205 availPtr->script = ckalloc((unsigned) (length + 1));
1206 strcpy(availPtr->script, argv4);
1211 Tcl_WrongNumArgs(interp, 2, objv, NULL);
1214 tablePtr = &iPtr->packageTable;
1215 for (hPtr = Tcl_FirstHashEntry(tablePtr, &search); hPtr != NULL;
1216 hPtr = Tcl_NextHashEntry(&search)) {
1217 pkgPtr = (Package *) Tcl_GetHashValue(hPtr);
1218 if ((pkgPtr->version != NULL) || (pkgPtr->availPtr != NULL)) {
1219 Tcl_AppendElement(interp, Tcl_GetHashKey(tablePtr, hPtr));
1227 Tcl_WrongNumArgs(interp, 2, objv, "?-exact? package ?version?");
1230 argv2 = Tcl_GetString(objv[2]);
1231 if ((argv2[0] == '-') && (strcmp(argv2, "-exact") == 0)) {
1237 if (objc == (4 + exact)) {
1238 version = Tcl_GetString(objv[3 + exact]);
1239 if (CheckVersionAndConvert(interp, version, NULL, NULL) != TCL_OK) {
1244 tablePtr = &iPtr->packageTable;
1245 for (hPtr = Tcl_FirstHashEntry(tablePtr, &search); hPtr != NULL;
1246 hPtr = Tcl_NextHashEntry(&search)) {
1248 } else if ((objc != 3) || exact) {
1252 argv3 = Tcl_GetString(objv[3]);
1253 version = Tcl_PkgPresent(interp, argv3, version, exact);
1255 version = Tcl_PkgPresent(interp, argv2, version, exact);
1257 if (version == NULL) {
1260 Tcl_SetObjResult( interp, Tcl_NewStringObj( version, -1 ) );
1264 if ((objc != 3) && (objc != 4)) {
1265 Tcl_WrongNumArgs(interp, 2, objv, "package ?version?");
1268 argv2 = Tcl_GetString(objv[2]);
1270 hPtr = Tcl_FindHashEntry(&iPtr->packageTable, argv2);
1273 pkgPtr = (Package *) Tcl_GetHashValue(hPtr);
1275 if ((pkgPtr->version != NULL) || (pkgPtr->availPtr != NULL)) {
1276 Tcl_AppendElement(interp, Tcl_GetHashKey(tablePtr, hPtr));
1278 if (pkgPtr->version != NULL) {
1279 Tcl_SetResult(interp, pkgPtr->version, TCL_VOLATILE);
1293 Tcl_WrongNumArgs(interp, 2, objv, "?-exact? package ?version?");
1296 argv3 = Tcl_GetString(objv[3]);
1297 if (CheckVersionAndConvert(interp, argv3, NULL, NULL) != TCL_OK) {
1300 return Tcl_PkgProvide(interp, argv2, argv3);
1305 Tcl_WrongNumArgs(interp, 2, objv, "?-exact? package ?requirement...?");
1309 argv2 = Tcl_GetString(objv[2]);
1310 if ((argv2[0] == '-') && (strcmp(argv2, "-exact") == 0)) {
1319 argv2 = Tcl_GetString(objv[2]);
1320 if ((argv2[0] == '-') && (strcmp(argv2, "-exact") == 0)) {
1325 version = Tcl_GetString(objv[4]);
1326 if (CheckVersionAndConvert(interp, version, NULL, NULL) != TCL_OK) {
1331 /* Create a new-style requirement for the exact version. */
1333 ov = ExactRequirement (version);
1337 if (objc == (4 + exact)) {
1338 version = Tcl_GetString(objv[3 + exact]);
1339 if (CheckVersion(interp, version) != TCL_OK) {
1342 } else if ((objc != 3) || exact) {
1346 argv3 = Tcl_GetString(objv[3]);
1347 version = Tcl_PkgPresent(interp, argv3, version, exact);
1349 version = Tcl_PkgPresent(interp, argv2, version, exact);
1351 if (version == NULL) {
1353 argv3 = Tcl_GetString(objv[3]);
1355 Tcl_IncrRefCount (ov);
1356 res = Tcl_PkgRequireProc(interp, argv3, 1, &ov, NULL);
1357 Tcl_DecrRefCount (ov);
1360 if (CheckAllRequirements (interp, objc-3, objv+3) != TCL_OK) {
1365 Tcl_SetObjResult( interp, Tcl_NewStringObj( version, -1 ) );
1368 return Tcl_PkgRequireProc(interp, argv2, objc-3, objv+3, NULL);
1373 if ((objc != 3) && (objc != 4)) {
1374 Tcl_WrongNumArgs(interp, 2, objv, "package ?version?");
1381 if (iPtr->packageUnknown != NULL) {
1382 Tcl_SetResult(interp, iPtr->packageUnknown, TCL_VOLATILE);
1384 } else if (objc == 3) {
1385 if (iPtr->packageUnknown != NULL) {
1386 ckfree(iPtr->packageUnknown);
1388 argv2 = Tcl_GetStringFromObj(objv[2], &length);
1389 if (argv2[0] == 0) {
1390 iPtr->packageUnknown = NULL;
1392 iPtr->packageUnknown = (char *) ckalloc((unsigned)
1394 strcpy(iPtr->packageUnknown, argv2);
1397 Tcl_WrongNumArgs(interp, 2, objv, "?command?");
1403 /* See tclInt.h for the enum, just before Interp */
1404 static CONST char *pkgPreferOptions[] = {
1405 "latest", "stable", NULL
1409 Tcl_WrongNumArgs(interp, 2, objv, "?latest|stable?");
1411 } else if (objc == 3) {
1414 if (Tcl_GetIndexFromObj(interp, objv[2], pkgPreferOptions, "preference", 0,
1420 argv2 = Tcl_GetString(objv[2]);
1422 hPtr = Tcl_FindHashEntry(&iPtr->packageTable, argv2);
1424 pkgPtr = (Package *) Tcl_GetHashValue(hPtr);
1425 if (pkgPtr->version != NULL) {
1426 Tcl_SetResult(interp, pkgPtr->version, TCL_VOLATILE);
1431 if (new < iPtr->packagePrefer) {
1432 iPtr->packagePrefer = new;
1436 argv3 = Tcl_GetString(objv[3]);
1437 if (CheckVersion(interp, argv3) != TCL_OK) {
1440 return Tcl_PkgProvide(interp, argv2, argv3);
1447 Tcl_WrongNumArgs(interp, 2, objv, "?-exact? package ?version?");
1450 argv2 = Tcl_GetString(objv[2]);
1451 if ((argv2[0] == '-') && (strcmp(argv2, "-exact") == 0)) {
1457 if (objc == (4 + exact)) {
1458 version = Tcl_GetString(objv[3 + exact]);
1459 if (CheckVersion(interp, version) != TCL_OK) {
1462 } else if ((objc != 3) || exact) {
1466 argv3 = Tcl_GetString(objv[3]);
1467 version = Tcl_PkgRequire(interp, argv3, version, exact);
1469 version = Tcl_PkgRequire(interp, argv2, version, exact);
1471 if (version == NULL) {
1474 Tcl_SetObjResult( interp, Tcl_NewStringObj( version, -1 ) );
1477 /* Always return current value. */
1478 Tcl_SetObjResult(interp, Tcl_NewStringObj (pkgPreferOptions [iPtr->packagePrefer], -1));
1481 case PKG_VCOMPARE: {
1483 Tcl_WrongNumArgs(interp, 2, objv, "version1 version2");
1491 if (iPtr->packageUnknown != NULL) {
1492 Tcl_SetResult(interp, iPtr->packageUnknown, TCL_VOLATILE);
1494 } else if (objc == 3) {
1495 if (iPtr->packageUnknown != NULL) {
1496 ckfree(iPtr->packageUnknown);
1498 argv2 = Tcl_GetStringFromObj(objv[2], &length);
1499 if (argv2[0] == 0) {
1500 iPtr->packageUnknown = NULL;
1502 iPtr->packageUnknown = (char *) ckalloc((unsigned)
1504 strcpy(iPtr->packageUnknown, argv2);
1507 Tcl_WrongNumArgs(interp, 2, objv, "?command?");
1512 argv3 = Tcl_GetString(objv[3]);
1513 argv2 = Tcl_GetString(objv[2]);
1514 if ((CheckVersionAndConvert (interp, argv2, &iva, NULL) != TCL_OK) ||
1515 (CheckVersionAndConvert (interp, argv3, &ivb, NULL) != TCL_OK)) {
1516 if (iva != NULL) { Tcl_Free (iva); }
1517 /* ivb cannot be set in this branch */
1522 case PKG_VCOMPARE: {
1524 Tcl_WrongNumArgs(interp, 2, objv, "version1 version2");
1527 argv3 = Tcl_GetString(objv[3]);
1528 argv2 = Tcl_GetString(objv[2]);
1529 if ((CheckVersion(interp, argv2) != TCL_OK)
1530 || (CheckVersion(interp, argv3) != TCL_OK)) {
1533 Tcl_SetIntObj(Tcl_GetObjResult(interp),
1534 ComparePkgVersions(argv2, argv3, (int *) NULL));
1538 /* Comparison is done on the internal representation */
1539 Tcl_SetObjResult(interp,Tcl_NewIntObj(CompareVersions(iva, ivb, NULL)));
1544 case PKG_VERSIONS: {
1546 Tcl_WrongNumArgs(interp, 2, objv, "package");
1551 case PKG_VERSIONS: {
1553 Tcl_WrongNumArgs(interp, 2, objv, "package");
1556 argv2 = Tcl_GetString(objv[2]);
1557 hPtr = Tcl_FindHashEntry(&iPtr->packageTable, argv2);
1559 pkgPtr = (Package *) Tcl_GetHashValue(hPtr);
1560 for (availPtr = pkgPtr->availPtr; availPtr != NULL;
1561 availPtr = availPtr->nextPtr) {
1562 Tcl_AppendElement(interp, availPtr->version);
1566 argv2 = Tcl_GetString(objv[2]);
1567 hPtr = Tcl_FindHashEntry(&iPtr->packageTable, argv2);
1569 pkgPtr = (Package *) Tcl_GetHashValue(hPtr);
1570 for (availPtr = pkgPtr->availPtr; availPtr != NULL;
1571 availPtr = availPtr->nextPtr) {
1572 Tcl_AppendElement(interp, availPtr->version);
1579 case PKG_VSATISFIES: {
1581 Tcl_WrongNumArgs(interp, 2, objv, "version1 version2");
1584 argv3 = Tcl_GetString(objv[3]);
1585 argv2 = Tcl_GetString(objv[2]);
1586 if ((CheckVersion(interp, argv2) != TCL_OK)
1587 || (CheckVersion(interp, argv3) != TCL_OK)) {
1590 ComparePkgVersions(argv2, argv3, &satisfies);
1591 Tcl_SetIntObj(Tcl_GetObjResult(interp), satisfies);
1596 case PKG_VSATISFIES: {
1597 char* argv2i = NULL;
1600 Tcl_WrongNumArgs(interp, 2, objv, "version requirement requirement...");
1606 panic("Tcl_PackageObjCmd: bad option index to pkgOptions");
1609 argv2 = Tcl_GetString(objv[2]);
1610 if ((CheckVersionAndConvert(interp, argv2, &argv2i, NULL) != TCL_OK)) {
1612 } else if (CheckAllRequirements (interp, objc-3, objv+3) != TCL_OK) {
1619 satisfies = AllRequirementsSatisfied (argv2i, objc-3, objv+3);
1622 Tcl_SetIntObj(Tcl_GetObjResult(interp), satisfies);
1626 panic("Tcl_PackageObjCmd: bad option index to pkgOptions");
1634 *----------------------------------------------------------------------
1638 * This procedure finds the Package record for a particular package
1639 * in a particular interpreter, creating a record if one doesn't
1643 * The return value is a pointer to the Package record for the
1647 * A new Package record may be created.
1649 *----------------------------------------------------------------------
1653 FindPackage(interp, name)
1654 Tcl_Interp *interp; /* Interpreter to use for package lookup. */
1655 CONST char *name; /* Name of package to fine. */
1657 Interp *iPtr = (Interp *) interp;
1658 Tcl_HashEntry *hPtr;
1662 hPtr = Tcl_CreateHashEntry(&iPtr->packageTable, name, &new);
1664 pkgPtr = (Package *) ckalloc(sizeof(Package));
1665 pkgPtr->version = NULL;
1666 pkgPtr->availPtr = NULL;
1667 pkgPtr->clientData = NULL;
1668 Tcl_SetHashValue(hPtr, pkgPtr);
1670 pkgPtr = (Package *) Tcl_GetHashValue(hPtr);
1676 *----------------------------------------------------------------------
1678 * TclFreePackageInfo --
1680 * This procedure is called during interpreter deletion to
1681 * free all of the package-related information for the
1690 *----------------------------------------------------------------------
1694 TclFreePackageInfo(iPtr)
1695 Interp *iPtr; /* Interpreter that is being deleted. */
1698 Tcl_HashSearch search;
1699 Tcl_HashEntry *hPtr;
1702 for (hPtr = Tcl_FirstHashEntry(&iPtr->packageTable, &search);
1703 hPtr != NULL; hPtr = Tcl_NextHashEntry(&search)) {
1704 pkgPtr = (Package *) Tcl_GetHashValue(hPtr);
1705 if (pkgPtr->version != NULL) {
1706 ckfree(pkgPtr->version);
1708 while (pkgPtr->availPtr != NULL) {
1709 availPtr = pkgPtr->availPtr;
1710 pkgPtr->availPtr = availPtr->nextPtr;
1711 Tcl_EventuallyFree((ClientData)availPtr->version, TCL_DYNAMIC);
1712 Tcl_EventuallyFree((ClientData)availPtr->script, TCL_DYNAMIC);
1713 ckfree((char *) availPtr);
1715 ckfree((char *) pkgPtr);
1717 Tcl_DeleteHashTable(&iPtr->packageTable);
1718 if (iPtr->packageUnknown != NULL) {
1719 ckfree(iPtr->packageUnknown);
1724 *----------------------------------------------------------------------
1726 * CheckVersion / CheckVersionAndConvert --
1728 * This procedure checks to see whether a version number has
1732 * If string is a properly formed version number the TCL_OK
1733 * is returned. Otherwise TCL_ERROR is returned and an error
1734 * message is left in the interp's result.
1739 *----------------------------------------------------------------------
1744 CheckVersion(interp, string)
1745 Tcl_Interp *interp; /* Used for error reporting. */
1746 CONST char *string; /* Supposedly a version number, which is
1747 * groups of decimal digits separated
1750 CheckVersionAndConvert(interp, string, internal, stable)
1751 Tcl_Interp *interp; /* Used for error reporting. */
1752 CONST char *string; /* Supposedly a version number, which is
1753 * groups of decimal digits separated by
1755 char** internal; /* Internal normalized representation */
1756 int* stable; /* Flag: Version is (un)stable. */
1759 CONST char *p = string;
1762 int hasunstable = 0;
1763 /* 4* assuming that each char is a separator (a,b become ' -x ').
1764 * 4+ to have spce for an additional -2 at the end
1766 char* ibuf = ckalloc (4+4*strlen(string));
1770 * (1) First character has to be a digit.
1771 * (2) All other characters have to be a digit or '.'
1772 * (3) Two '.'s may not follow each other.
1774 * TIP 268, Modified rules
1776 * (2) All other characters have to be a digit, 'a', 'b', or '.'
1778 * (4) Only one of 'a' or 'b' may occur.
1779 * (5) Neither 'a', nor 'b' may occur before or after a '.'
1783 if (!isdigit(UCHAR(*p))) { /* INTL: digit */
1789 for (prevChar = *p, p++; *p != 0; p++) {
1791 if (!isdigit(UCHAR(*p)) &&
1792 ((*p != '.') || (prevChar == '.'))) { /* INTL: digit */
1795 (!isdigit(UCHAR(*p))) &&
1796 (((*p != '.') && (*p != 'a') && (*p != 'b')) ||
1797 ((hasunstable && ((*p == 'a') || (*p == 'b'))) ||
1798 (((prevChar == 'a') || (prevChar == 'b') || (prevChar == '.')) && (*p == '.')) ||
1799 (((*p == 'a') || (*p == 'b') || (*p == '.')) && (prevChar == '.'))))
1806 if ((*p == 'a') || (*p == 'b')) { hasunstable = 1 ; }
1808 /* Translation to the internal rep. Regular version chars are copied
1809 * as is. The separators are translated to numerics. The new separator
1810 * for all parts is space. */
1812 if (*p == '.') { *ip++ = ' '; *ip++ = '0'; *ip++ = ' '; }
1813 else if (*p == 'a') { *ip++ = ' '; *ip++ = '-'; *ip++ = '2'; *ip++ = ' '; }
1814 else if (*p == 'b') { *ip++ = ' '; *ip++ = '-'; *ip++ = '1'; *ip++ = ' '; }
1815 else { *ip++ = *p; }
1820 if (prevChar != '.') {
1822 if ((prevChar != '.') && (prevChar != 'a') && (prevChar != 'b')) {
1824 if (internal != NULL) {
1829 if (stable != NULL) {
1830 *stable = !hasunstable;
1840 Tcl_AppendResult(interp, "expected version number but got \"",
1841 string, "\"", (char *) NULL);
1846 *----------------------------------------------------------------------
1848 * ComparePkgVersions / CompareVersions --
1850 * This procedure compares two version numbers. (268: in internal rep).
1853 * The return value is -1 if v1 is less than v2, 0 if the two
1854 * version numbers are the same, and 1 if v1 is greater than v2.
1855 * If *satPtr is non-NULL, the word it points to is filled in
1856 * with 1 if v2 >= v1 and both numbers have the same major number
1862 *----------------------------------------------------------------------
1867 ComparePkgVersions(v1, v2, satPtr)
1869 CONST char *v2; /* Versions strings, of form 2.1.3 (any
1870 * number of version numbers). */
1871 int *satPtr; /* If non-null, the word pointed to is
1872 * filled in with a 0/1 value. 1 means
1873 * v1 "satisfies" v2: v1 is greater than
1874 * or equal to v2 and both version numbers
1875 * have the same major number. */
1877 CompareVersions(v1, v2, isMajorPtr)
1878 CONST char *v1; /* Versions strings, of form 2.1.3 (any number */
1879 CONST char *v2; /* of version numbers). */
1880 int *isMajorPtr; /* If non-null, the word pointed to is filled
1881 * in with a 0/1 value. 1 means that the difference
1882 * occured in the first element. */
1885 int thisIsMajor, n1, n2;
1891 * Each iteration of the following loop processes one number from each
1892 * string, terminated by a " " (space). If those numbers don't match then the
1893 * comparison is over; otherwise, we loop back for the next number.
1896 * This is identical the function 'ComparePkgVersion', but using the new
1897 * space separator as used by the internal rep of version numbers. The
1898 * special separators 'a' and 'b' have already been dealt with in
1899 * 'CheckVersionAndConvert', they were translated into numbers as
1900 * well. This keeps the comparison sane. Otherwise we would have to
1901 * compare numerics, the separators, and also deal with the special case
1902 * of end-of-string compared to separators. The semi-list rep we get here
1903 * is much easier to handle, as it is still regular.
1909 * Parse one decimal number from the front of each string.
1914 while ((*v1 != 0) && (*v1 != '.')) {
1917 while ((*v1 != 0) && (*v1 != ' ')) {
1918 if (*v1 == '-') {flip = 1 ; v1++ ; continue;}
1920 n1 = 10*n1 + (*v1 - '0');
1924 while ((*v2 != 0) && (*v2 != '.')) {
1928 while ((*v2 != 0) && (*v2 != ' ')) {
1929 if (*v2 == '-') {flip = 1; v2++ ; continue;}
1931 n2 = 10*n2 + (*v2 - '0');
1939 * Compare and go on to the next version number if the current numbers
1948 } else if (*v2 == 0) {
1957 if (satPtr != NULL) {
1958 *satPtr = (n1 == n2) || ((n1 > n2) && !thisIsMajor);
1967 } else if (n1 == n2) {
1980 if (isMajorPtr != NULL) {
1981 *isMajorPtr = thisIsMajor;
1988 *----------------------------------------------------------------------
1990 * CheckAllRequirements --
1992 * This function checks to see whether all requirements in a set
1993 * have valid syntax.
1996 * TCL_OK is returned if all requirements are valid.
1997 * Otherwise TCL_ERROR is returned and an error message
1998 * is left in the interp's result.
2001 * May modify the interpreter result.
2003 *----------------------------------------------------------------------
2007 CheckAllRequirements(interp, reqc, reqv)
2009 int reqc; /* Requirements to check. */
2010 Tcl_Obj *CONST reqv[];
2013 for (i = 0; i < reqc; i++) {
2014 if ((CheckRequirement(interp, Tcl_GetString(reqv[i])) != TCL_OK)) {
2022 *----------------------------------------------------------------------
2024 * CheckRequirement --
2026 * This function checks to see whether a requirement has valid syntax.
2029 * If string is a properly formed requirement then TCL_OK is returned.
2030 * Otherwise TCL_ERROR is returned and an error message is left in the
2036 *----------------------------------------------------------------------
2040 CheckRequirement(interp, string)
2041 Tcl_Interp *interp; /* Used for error reporting. */
2042 CONST char *string; /* Supposedly a requirement. */
2044 /* Syntax of requirement = version
2052 dash = strchr (string, '-');
2054 /* no dash found, has to be a simple version */
2055 return CheckVersionAndConvert (interp, string, NULL, NULL);
2057 if (strchr (dash+1, '-') != NULL) {
2058 /* More dashes found after the first. This is wrong. */
2059 Tcl_AppendResult(interp, "expected versionMin-versionMax but got \"", string,
2066 /* Exactly one dash is present. Copy the string, split at the location of
2067 * dash and check that both parts are versions. Note that the max part can
2071 buf = strdup (string);
2072 dash = buf + (dash - string);
2073 *dash = '\0'; /* buf now <=> min part */
2074 dash ++; /* dash now <=> max part */
2076 if ((CheckVersionAndConvert(interp, buf, NULL, NULL) != TCL_OK) ||
2078 (CheckVersionAndConvert(interp, dash, NULL, NULL) != TCL_OK))) {
2090 *----------------------------------------------------------------------
2092 * AddRequirementsToResult --
2094 * This function accumulates requirements in the interpreter result.
2100 * The interpreter result is extended.
2102 *----------------------------------------------------------------------
2106 AddRequirementsToResult(interp, reqc, reqv)
2108 int reqc; /* Requirements constraining the desired version. */
2109 Tcl_Obj *CONST reqv[]; /* 0 means to use the latest version available. */
2113 for (i = 0; i < reqc; i++) {
2114 Tcl_AppendResult(interp, " ", TclGetString(reqv[i]), NULL);
2120 *----------------------------------------------------------------------
2122 * AddRequirementsToDString --
2124 * This function accumulates requirements in a DString.
2130 * The DString argument is extended.
2132 *----------------------------------------------------------------------
2136 AddRequirementsToDString(dstring, reqc, reqv)
2137 Tcl_DString* dstring;
2138 int reqc; /* Requirements constraining the desired version. */
2139 Tcl_Obj *CONST reqv[]; /* 0 means to use the latest version available. */
2143 for (i = 0; i < reqc; i++) {
2144 Tcl_DStringAppend(dstring, " ", 1);
2145 Tcl_DStringAppend(dstring, TclGetString(reqv[i]), -1);
2148 Tcl_DStringAppend(dstring, " 0-", -1);
2153 *----------------------------------------------------------------------
2155 * AllRequirementSatisfied --
2157 * This function checks to see whether a version satisfies at
2158 * least one of a set of requirements.
2161 * If the requirements are satisfied 1 is returned.
2162 * Otherwise 0 is returned. The function assumes
2163 * that all pieces have valid syntax. And is allowed
2164 * to make that assumption.
2169 *----------------------------------------------------------------------
2173 AllRequirementsSatisfied(availVersionI, reqc, reqv)
2174 CONST char* availVersionI; /* Candidate version to check against the requirements */
2175 int reqc; /* Requirements constraining the desired version. */
2176 Tcl_Obj *CONST reqv[]; /* 0 means to use the latest version available. */
2180 for (satisfies = i = 0; i < reqc; i++) {
2181 satisfies = RequirementSatisfied(availVersionI, Tcl_GetString(reqv[i]));
2182 if (satisfies) break;
2188 *----------------------------------------------------------------------
2190 * RequirementSatisfied --
2192 * This function checks to see whether a version satisfies a requirement.
2195 * If the requirement is satisfied 1 is returned.
2196 * Otherwise 0 is returned. The function assumes
2197 * that all pieces have valid syntax. And is allowed
2198 * to make that assumption.
2203 *----------------------------------------------------------------------
2207 RequirementSatisfied(havei, req)
2208 CONST char *havei; /* Version string, of candidate package we have */
2209 CONST char *req; /* Requirement string the candidate has to satisfy */
2211 /* The have candidate is already in internal rep. */
2215 char* buf, *min, *max;
2217 dash = strchr (req, '-');
2219 /* No dash found, is a simple version, fallback to regular check.
2220 * The 'CheckVersionAndConvert' cannot fail. We pad the requirement with
2221 * 'a0', i.e '-2' before doing the comparison to properly accept
2222 * unstables as well.
2228 CheckVersionAndConvert (NULL, req, &reqi, NULL);
2229 strcat (reqi, " -2");
2230 res = CompareVersions(havei, reqi, &thisIsMajor);
2231 satisfied = (res == 0) || ((res == 1) && !thisIsMajor);
2236 /* Exactly one dash is present (Assumption of valid syntax). Copy the req,
2237 * split at the location of dash and check that both parts are
2238 * versions. Note that the max part can be empty.
2242 dash = buf + (dash - req);
2243 *dash = '\0'; /* buf now <=> min part */
2244 dash ++; /* dash now <=> max part */
2246 if (*dash == '\0') {
2247 /* We have a min, but no max. For the comparison we generate the
2248 * internal rep, padded with 'a0' i.e. '-2'.
2251 /* No max part, unbound */
2253 CheckVersionAndConvert (NULL, buf, &min, NULL);
2254 strcat (min, " -2");
2255 satisfied = (CompareVersions(havei, min, NULL) >= 0);
2261 /* We have both min and max, and generate their internal reps.
2262 * When identical we compare as is, otherwise we pad with 'a0'
2263 * to ove the range a bit.
2266 CheckVersionAndConvert (NULL, buf, &min, NULL);
2267 CheckVersionAndConvert (NULL, dash, &max, NULL);
2269 if (CompareVersions(min, max, NULL) == 0) {
2270 satisfied = (CompareVersions(min, havei, NULL) == 0);
2272 strcat (min, " -2");
2273 strcat (max, " -2");
2274 satisfied = ((CompareVersions(min, havei, NULL) <= 0) &&
2275 (CompareVersions(havei, max, NULL) < 0));
2285 *----------------------------------------------------------------------
2287 * ExactRequirement --
2289 * This function is the core for the translation of -exact requests.
2290 * It translates the request of the version into a range of versions.
2291 * The translation was chosen for backwards compatibility.
2294 * A Tcl_Obj containing the version range as string.
2299 *----------------------------------------------------------------------
2303 ExactRequirement(version)
2304 CONST char* version;
2306 /* A -exact request for a version X.y is translated into the range
2307 * X.y-X.(y+1). For example -exact 8.4 means the range "8.4-8.5".
2309 * This translation was chosen to prevent packages which currently use a
2310 * 'package require -exact tclversion' from being affected by the core now
2311 * registering itself as 8.4.x (patchlevel) instead of 8.4
2312 * (version). Examples are tbcload, compiler, and ITcl.
2314 * Translating -exact 8.4 to the range "8.4-8.4" instead would require us
2315 * and everyone else to rebuild these packages to require -exact 8.4.14,
2316 * or whatever the exact current patchlevel is. A backward compatibility
2317 * issue with effects similar to the bugfix made in 8.5 now requiring
2318 * ifneeded and provided versions to match. Instead we have chosen to
2319 * interpret exactness to not be exactly equal, but to be exact only
2320 * within the specified level, and allowing variation in the deeper
2321 * level. More examples:
2324 * -exact 8.4 => "8.4-8.5"
2325 * -exact 8.4.14 => "8.4.14-8.4.15"
2326 * -exact 8.0a2 => "8.0a2-8.0a3"
2333 Tcl_Obj* o = Tcl_NewStringObj (version,-1);
2334 Tcl_AppendStringsToObj (o, "-", NULL);
2336 /* Assuming valid syntax here */
2337 CheckVersionAndConvert (NULL, version, &iv, NULL);
2339 /* Split the list into components */
2340 Tcl_SplitList (NULL, iv, &lc, &lv);
2342 /* Iterate over the components and make them parts of the result. Except
2343 * for the last, which is handled separately, to allow the
2347 for (i=0; i < (lc-1); i++) {
2348 /* Regular component */
2349 Tcl_AppendStringsToObj (o, lv[i], NULL);
2350 /* Separator component */
2352 if (0 == strcmp ("-1", lv[i])) {
2353 Tcl_AppendStringsToObj (o, "b", NULL);
2354 } else if (0 == strcmp ("-2", lv[i])) {
2355 Tcl_AppendStringsToObj (o, "a", NULL);
2357 Tcl_AppendStringsToObj (o, ".", NULL);
2360 /* Regular component, last */
2361 sprintf (buf, "%d", atoi (lv [lc-1]) + 1);
2362 Tcl_AppendStringsToObj (o, buf, NULL);
2364 ckfree ((char*) lv);
2369 *----------------------------------------------------------------------
2371 * VersionCleanupProc --
2373 * This function is called to delete the last remember package version
2374 * string for an interpreter when the interpreter is deleted. It gets
2375 * invoked via the Tcl AssocData mechanism.
2381 * Storage for the version object for interp get deleted.
2383 *----------------------------------------------------------------------
2387 VersionCleanupProc (
2388 ClientData clientData, /* Pointer to remembered version string object
2390 Tcl_Interp *interp) /* Interpreter that is being deleted. */
2392 Tcl_Obj* ov = (Tcl_Obj*) clientData;
2394 Tcl_DecrRefCount (ov);