tclPkg.cGo to the documentation of this file.00001 /* 00002 * tclPkg.c -- 00003 * 00004 * This file implements package and version control for Tcl via the 00005 * "package" command and a few C APIs. 00006 * 00007 * Copyright (c) 1996 Sun Microsystems, Inc. 00008 * Copyright (c) 2006 Andreas Kupries <andreas_kupries@users.sourceforge.net> 00009 * 00010 * See the file "license.terms" for information on usage and redistribution of 00011 * this file, and for a DISCLAIMER OF ALL WARRANTIES. 00012 * 00013 * RCS: @(#) $Id: tclPkg.c,v 1.34 2007/12/13 15:23:20 dgp Exp $ 00014 * 00015 * TIP #268. 00016 * Heavily rewritten to handle the extend version numbers, and extended 00017 * package requirements. 00018 */ 00019 00020 #include "tclInt.h" 00021 00022 /* 00023 * Each invocation of the "package ifneeded" command creates a structure of 00024 * the following type, which is used to load the package into the interpreter 00025 * if it is requested with a "package require" command. 00026 */ 00027 00028 typedef struct PkgAvail { 00029 char *version; /* Version string; malloc'ed. */ 00030 char *script; /* Script to invoke to provide this version of 00031 * the package. Malloc'ed and protected by 00032 * Tcl_Preserve and Tcl_Release. */ 00033 struct PkgAvail *nextPtr; /* Next in list of available versions of the 00034 * same package. */ 00035 } PkgAvail; 00036 00037 /* 00038 * For each package that is known in any way to an interpreter, there is one 00039 * record of the following type. These records are stored in the 00040 * "packageTable" hash table in the interpreter, keyed by package name such as 00041 * "Tk" (no version number). 00042 */ 00043 00044 typedef struct Package { 00045 char *version; /* Version that has been supplied in this 00046 * interpreter via "package provide" 00047 * (malloc'ed). NULL means the package doesn't 00048 * exist in this interpreter yet. */ 00049 PkgAvail *availPtr; /* First in list of all available versions of 00050 * this package. */ 00051 ClientData clientData; /* Client data. */ 00052 } Package; 00053 00054 /* 00055 * Prototypes for functions defined in this file: 00056 */ 00057 00058 static int CheckVersionAndConvert(Tcl_Interp *interp, 00059 const char *string, char **internal, int *stable); 00060 static int CompareVersions(char *v1i, char *v2i, 00061 int *isMajorPtr); 00062 static int CheckRequirement(Tcl_Interp *interp, 00063 const char *string); 00064 static int CheckAllRequirements(Tcl_Interp *interp, int reqc, 00065 Tcl_Obj *const reqv[]); 00066 static int RequirementSatisfied(char *havei, const char *req); 00067 static int SomeRequirementSatisfied(char *havei, int reqc, 00068 Tcl_Obj *const reqv[]); 00069 static void AddRequirementsToResult(Tcl_Interp *interp, int reqc, 00070 Tcl_Obj *const reqv[]); 00071 static void AddRequirementsToDString(Tcl_DString *dstring, 00072 int reqc, Tcl_Obj *const reqv[]); 00073 static Package * FindPackage(Tcl_Interp *interp, const char *name); 00074 static const char * PkgRequireCore(Tcl_Interp *interp, const char *name, 00075 int reqc, Tcl_Obj *const reqv[], 00076 ClientData *clientDataPtr); 00077 00078 /* 00079 * Helper macros. 00080 */ 00081 00082 #define DupBlock(v,s,len) \ 00083 ((v) = ckalloc(len), memcpy((v),(s),(len))) 00084 #define DupString(v,s) \ 00085 do { \ 00086 unsigned local__len = (unsigned) (strlen(s) + 1); \ 00087 DupBlock((v),(s),local__len); \ 00088 } while (0) 00089 00090 /* 00091 *---------------------------------------------------------------------- 00092 * 00093 * Tcl_PkgProvide / Tcl_PkgProvideEx -- 00094 * 00095 * This function is invoked to declare that a particular version of a 00096 * particular package is now present in an interpreter. There must not be 00097 * any other version of this package already provided in the interpreter. 00098 * 00099 * Results: 00100 * Normally returns TCL_OK; if there is already another version of the 00101 * package loaded then TCL_ERROR is returned and an error message is left 00102 * in the interp's result. 00103 * 00104 * Side effects: 00105 * The interpreter remembers that this package is available, so that no 00106 * other version of the package may be provided for the interpreter. 00107 * 00108 *---------------------------------------------------------------------- 00109 */ 00110 00111 int 00112 Tcl_PkgProvide( 00113 Tcl_Interp *interp, /* Interpreter in which package is now 00114 * available. */ 00115 const char *name, /* Name of package. */ 00116 const char *version) /* Version string for package. */ 00117 { 00118 return Tcl_PkgProvideEx(interp, name, version, NULL); 00119 } 00120 00121 int 00122 Tcl_PkgProvideEx( 00123 Tcl_Interp *interp, /* Interpreter in which package is now 00124 * available. */ 00125 const char *name, /* Name of package. */ 00126 const char *version, /* Version string for package. */ 00127 ClientData clientData) /* clientdata for this package (normally used 00128 * for C callback function table) */ 00129 { 00130 Package *pkgPtr; 00131 char *pvi, *vi; 00132 int res; 00133 00134 pkgPtr = FindPackage(interp, name); 00135 if (pkgPtr->version == NULL) { 00136 DupString(pkgPtr->version, version); 00137 pkgPtr->clientData = clientData; 00138 return TCL_OK; 00139 } 00140 00141 if (CheckVersionAndConvert(interp, pkgPtr->version, &pvi, 00142 NULL) != TCL_OK) { 00143 return TCL_ERROR; 00144 } else if (CheckVersionAndConvert(interp, version, &vi, NULL) != TCL_OK) { 00145 ckfree(pvi); 00146 return TCL_ERROR; 00147 } 00148 00149 res = CompareVersions(pvi, vi, NULL); 00150 ckfree(pvi); 00151 ckfree(vi); 00152 00153 if (res == 0) { 00154 if (clientData != NULL) { 00155 pkgPtr->clientData = clientData; 00156 } 00157 return TCL_OK; 00158 } 00159 Tcl_AppendResult(interp, "conflicting versions provided for package \"", 00160 name, "\": ", pkgPtr->version, ", then ", version, NULL); 00161 return TCL_ERROR; 00162 } 00163 00164 /* 00165 *---------------------------------------------------------------------- 00166 * 00167 * Tcl_PkgRequire / Tcl_PkgRequireEx / Tcl_PkgRequireProc -- 00168 * 00169 * This function is called by code that depends on a particular version 00170 * of a particular package. If the package is not already provided in the 00171 * interpreter, this function invokes a Tcl script to provide it. If the 00172 * package is already provided, this function makes sure that the 00173 * caller's needs don't conflict with the version that is present. 00174 * 00175 * Results: 00176 * If successful, returns the version string for the currently provided 00177 * version of the package, which may be different from the "version" 00178 * argument. If the caller's requirements cannot be met (e.g. the version 00179 * requested conflicts with a currently provided version, or the required 00180 * version cannot be found, or the script to provide the required version 00181 * generates an error), NULL is returned and an error message is left in 00182 * the interp's result. 00183 * 00184 * Side effects: 00185 * The script from some previous "package ifneeded" command may be 00186 * invoked to provide the package. 00187 * 00188 *---------------------------------------------------------------------- 00189 */ 00190 00191 const char * 00192 Tcl_PkgRequire( 00193 Tcl_Interp *interp, /* Interpreter in which package is now 00194 * available. */ 00195 const char *name, /* Name of desired package. */ 00196 const char *version, /* Version string for desired version; NULL 00197 * means use the latest version available. */ 00198 int exact) /* Non-zero means that only the particular 00199 * version given is acceptable. Zero means use 00200 * the latest compatible version. */ 00201 { 00202 return Tcl_PkgRequireEx(interp, name, version, exact, NULL); 00203 } 00204 00205 const char * 00206 Tcl_PkgRequireEx( 00207 Tcl_Interp *interp, /* Interpreter in which package is now 00208 * available. */ 00209 const char *name, /* Name of desired package. */ 00210 const char *version, /* Version string for desired version; NULL 00211 * means use the latest version available. */ 00212 int exact, /* Non-zero means that only the particular 00213 * version given is acceptable. Zero means use 00214 * the latest compatible version. */ 00215 ClientData *clientDataPtr) /* Used to return the client data for this 00216 * package. If it is NULL then the client data 00217 * is not returned. This is unchanged if this 00218 * call fails for any reason. */ 00219 { 00220 Tcl_Obj *ov; 00221 const char *result = NULL; 00222 00223 /* 00224 * If an attempt is being made to load this into a standalone executable 00225 * on a platform where backlinking is not supported then this must be a 00226 * shared version of Tcl (Otherwise the load would have failed). Detect 00227 * this situation by checking that this library has been correctly 00228 * initialised. If it has not been then return immediately as nothing will 00229 * work. 00230 */ 00231 00232 if (tclEmptyStringRep == NULL) { 00233 /* 00234 * OK, so what's going on here? 00235 * 00236 * First, what are we doing? We are performing a check on behalf of 00237 * one particular caller, Tcl_InitStubs(). When a package is stub- 00238 * enabled, it is statically linked to libtclstub.a, which contains a 00239 * copy of Tcl_InitStubs(). When a stub-enabled package is loaded, its 00240 * *_Init() function is supposed to call Tcl_InitStubs() before 00241 * calling any other functions in the Tcl library. The first Tcl 00242 * function called by Tcl_InitStubs() through the stub table is 00243 * Tcl_PkgRequireEx(), so this code right here is the first code that 00244 * is part of the original Tcl library in the executable that gets 00245 * executed on behalf of a newly loaded stub-enabled package. 00246 * 00247 * One easy error for the developer/builder of a stub-enabled package 00248 * to make is to forget to define USE_TCL_STUBS when compiling the 00249 * package. When that happens, the package will contain symbols that 00250 * are references to the Tcl library, rather than function pointers 00251 * referencing the stub table. On platforms that lack backlinking, 00252 * those unresolved references may cause the loading of the package to 00253 * also load a second copy of the Tcl library, leading to all kinds of 00254 * trouble. We would like to catch that error and report a useful 00255 * message back to the user. That's what we're doing. 00256 * 00257 * Second, how does this work? If we reach this point, then the global 00258 * variable tclEmptyStringRep has the value NULL. Compare that with 00259 * the definition of tclEmptyStringRep near the top of the file 00260 * generic/tclObj.c. It clearly should not have the value NULL; it 00261 * should point to the char tclEmptyString. If we see it having the 00262 * value NULL, then somehow we are seeing a Tcl library that isn't 00263 * completely initialized, and that's an indicator for the error 00264 * condition described above. (Further explanation is welcome.) 00265 * 00266 * Third, so what do we do about it? This situation indicates the 00267 * package we just loaded wasn't properly compiled to be stub-enabled, 00268 * yet it thinks it is stub-enabled (it called Tcl_InitStubs()). We 00269 * want to report that the package just loaded is broken, so we want 00270 * to place an error message in the interpreter result and return NULL 00271 * to indicate failure to Tcl_InitStubs() so that it will also fail. 00272 * (Further explanation why we don't want to Tcl_Panic() is welcome. 00273 * After all, two Tcl libraries can't be a good thing!) 00274 * 00275 * Trouble is that's going to be tricky. We're now using a Tcl library 00276 * that's not fully initialized. In particular, it doesn't have a 00277 * proper value for tclEmptyStringRep. The Tcl_Obj system heavily 00278 * depends on the value of tclEmptyStringRep and all of Tcl depends 00279 * (increasingly) on the Tcl_Obj system, we need to correct that flaw 00280 * before making the calls to set the interpreter result to the error 00281 * message. That's the only flaw corrected; other problems with 00282 * initialization of the Tcl library are not remedied, so be very 00283 * careful about adding any other calls here without checking how they 00284 * behave when initialization is incomplete. 00285 */ 00286 00287 tclEmptyStringRep = &tclEmptyString; 00288 Tcl_AppendResult(interp, "Cannot load package \"", name, 00289 "\" in standalone executable: This package is not " 00290 "compiled with stub support", NULL); 00291 return NULL; 00292 } 00293 00294 /* 00295 * Translate between old and new API, and defer to the new function. 00296 */ 00297 00298 if (version == NULL) { 00299 result = PkgRequireCore(interp, name, 0, NULL, clientDataPtr); 00300 } else { 00301 if (exact && TCL_OK 00302 != CheckVersionAndConvert(interp, version, NULL, NULL)) { 00303 return NULL; 00304 } 00305 ov = Tcl_NewStringObj(version, -1); 00306 if (exact) { 00307 Tcl_AppendStringsToObj(ov, "-", version, NULL); 00308 } 00309 Tcl_IncrRefCount(ov); 00310 result = PkgRequireCore(interp, name, 1, &ov, clientDataPtr); 00311 TclDecrRefCount(ov); 00312 } 00313 00314 return result; 00315 } 00316 00317 int 00318 Tcl_PkgRequireProc( 00319 Tcl_Interp *interp, /* Interpreter in which package is now 00320 * available. */ 00321 const char *name, /* Name of desired package. */ 00322 int reqc, /* Requirements constraining the desired 00323 * version. */ 00324 Tcl_Obj *const reqv[], /* 0 means to use the latest version 00325 * available. */ 00326 ClientData *clientDataPtr) 00327 { 00328 const char *result = 00329 PkgRequireCore(interp, name, reqc, reqv, clientDataPtr); 00330 00331 if (result == NULL) { 00332 return TCL_ERROR; 00333 } 00334 Tcl_SetObjResult(interp, Tcl_NewStringObj(result, -1)); 00335 return TCL_OK; 00336 } 00337 00338 static const char * 00339 PkgRequireCore( 00340 Tcl_Interp *interp, /* Interpreter in which package is now 00341 * available. */ 00342 const char *name, /* Name of desired package. */ 00343 int reqc, /* Requirements constraining the desired 00344 * version. */ 00345 Tcl_Obj *const reqv[], /* 0 means to use the latest version 00346 * available. */ 00347 ClientData *clientDataPtr) 00348 { 00349 Interp *iPtr = (Interp *) interp; 00350 Package *pkgPtr; 00351 PkgAvail *availPtr, *bestPtr, *bestStablePtr; 00352 char *availVersion, *bestVersion; 00353 /* Internal rep. of versions */ 00354 int availStable, code, satisfies, pass; 00355 char *script, *pkgVersionI; 00356 Tcl_DString command; 00357 00358 /* 00359 * It can take up to three passes to find the package: one pass to run the 00360 * "package unknown" script, one to run the "package ifneeded" script for 00361 * a specific version, and a final pass to lookup the package loaded by 00362 * the "package ifneeded" script. 00363 */ 00364 00365 for (pass=1 ;; pass++) { 00366 pkgPtr = FindPackage(interp, name); 00367 if (pkgPtr->version != NULL) { 00368 break; 00369 } 00370 00371 /* 00372 * Check whether we're already attempting to load some version of this 00373 * package (circular dependency detection). 00374 */ 00375 00376 if (pkgPtr->clientData != NULL) { 00377 Tcl_AppendResult(interp, "circular package dependency: " 00378 "attempt to provide ", name, " ", 00379 (char *) pkgPtr->clientData, " requires ", name, NULL); 00380 AddRequirementsToResult(interp, reqc, reqv); 00381 return NULL; 00382 } 00383 00384 /* 00385 * The package isn't yet present. Search the list of available 00386 * versions and invoke the script for the best available version. We 00387 * are actually locating the best, and the best stable version. One of 00388 * them is then chosen based on the selection mode. 00389 */ 00390 00391 bestPtr = NULL; 00392 bestStablePtr = NULL; 00393 bestVersion = NULL; 00394 00395 for (availPtr = pkgPtr->availPtr; availPtr != NULL; 00396 availPtr = availPtr->nextPtr) { 00397 if (CheckVersionAndConvert(interp, availPtr->version, 00398 &availVersion, &availStable) != TCL_OK) { 00399 /* 00400 * The provided version number has invalid syntax. This 00401 * should not happen. This should have been caught by the 00402 * 'package ifneeded' registering the package. 00403 */ 00404 00405 continue; 00406 } 00407 00408 if (bestPtr != NULL) { 00409 int res = CompareVersions(availVersion, bestVersion, NULL); 00410 00411 /* 00412 * Note: Use internal reps! 00413 */ 00414 00415 if (res <= 0) { 00416 /* 00417 * The version of the package sought is not as good as the 00418 * currently selected version. Ignore it. 00419 */ 00420 00421 ckfree(availVersion); 00422 availVersion = NULL; 00423 continue; 00424 } 00425 } 00426 00427 /* We have found a version which is better than our max. */ 00428 00429 if (reqc > 0) { 00430 /* Check satisfaction of requirements. */ 00431 00432 satisfies = SomeRequirementSatisfied(availVersion, reqc, reqv); 00433 if (!satisfies) { 00434 ckfree(availVersion); 00435 availVersion = NULL; 00436 continue; 00437 } 00438 } 00439 00440 bestPtr = availPtr; 00441 00442 if (bestVersion != NULL) { 00443 ckfree(bestVersion); 00444 } 00445 bestVersion = availVersion; 00446 00447 /* 00448 * If this new best version is stable then it also has to be 00449 * better than the max stable version found so far. 00450 */ 00451 00452 if (availStable) { 00453 bestStablePtr = availPtr; 00454 } 00455 } 00456 00457 if (bestVersion != NULL) { 00458 ckfree(bestVersion); 00459 } 00460 00461 /* 00462 * Now choose a version among the two best. For 'latest' we simply 00463 * take (actually keep) the best. For 'stable' we take the best 00464 * stable, if there is any, or the best if there is nothing stable. 00465 */ 00466 00467 if ((iPtr->packagePrefer == PKG_PREFER_STABLE) 00468 && (bestStablePtr != NULL)) { 00469 bestPtr = bestStablePtr; 00470 } 00471 00472 if (bestPtr != NULL) { 00473 /* 00474 * We found an ifneeded script for the package. Be careful while 00475 * executing it: this could cause reentrancy, so (a) protect the 00476 * script itself from deletion and (b) don't assume that bestPtr 00477 * will still exist when the script completes. 00478 */ 00479 00480 const char *versionToProvide = bestPtr->version; 00481 script = bestPtr->script; 00482 00483 pkgPtr->clientData = (ClientData) versionToProvide; 00484 Tcl_Preserve((ClientData) script); 00485 Tcl_Preserve((ClientData) versionToProvide); 00486 code = Tcl_EvalEx(interp, script, -1, TCL_EVAL_GLOBAL); 00487 Tcl_Release((ClientData) script); 00488 00489 pkgPtr = FindPackage(interp, name); 00490 if (code == TCL_OK) { 00491 Tcl_ResetResult(interp); 00492 if (pkgPtr->version == NULL) { 00493 code = TCL_ERROR; 00494 Tcl_AppendResult(interp, "attempt to provide package ", 00495 name, " ", versionToProvide, 00496 " failed: no version of package ", name, 00497 " provided", NULL); 00498 } else { 00499 char *pvi, *vi; 00500 00501 if (CheckVersionAndConvert(interp, pkgPtr->version, &pvi, 00502 NULL) != TCL_OK) { 00503 code = TCL_ERROR; 00504 } else if (CheckVersionAndConvert(interp, 00505 versionToProvide, &vi, NULL) != TCL_OK) { 00506 ckfree(pvi); 00507 code = TCL_ERROR; 00508 } else { 00509 int res = CompareVersions(pvi, vi, NULL); 00510 00511 ckfree(pvi); 00512 ckfree(vi); 00513 if (res != 0) { 00514 code = TCL_ERROR; 00515 Tcl_AppendResult(interp, 00516 "attempt to provide package ", name, " ", 00517 versionToProvide, " failed: package ", 00518 name, " ", pkgPtr->version, 00519 " provided instead", NULL); 00520 } 00521 } 00522 } 00523 } else if (code != TCL_ERROR) { 00524 Tcl_Obj *codePtr = Tcl_NewIntObj(code); 00525 00526 Tcl_ResetResult(interp); 00527 Tcl_AppendResult(interp, "attempt to provide package ", name, 00528 " ", versionToProvide, " failed: bad return code: ", 00529 TclGetString(codePtr), NULL); 00530 TclDecrRefCount(codePtr); 00531 code = TCL_ERROR; 00532 } 00533 00534 if (code == TCL_ERROR) { 00535 Tcl_AppendObjToErrorInfo(interp, Tcl_ObjPrintf( 00536 "\n (\"package ifneeded %s %s\" script)", 00537 name, versionToProvide)); 00538 } 00539 Tcl_Release((ClientData) versionToProvide); 00540 00541 if (code != TCL_OK) { 00542 /* 00543 * Take a non-TCL_OK code from the script as an indication the 00544 * package wasn't loaded properly, so the package system 00545 * should not remember an improper load. 00546 * 00547 * This is consistent with our returning NULL. If we're not 00548 * willing to tell our caller we got a particular version, we 00549 * shouldn't store that version for telling future callers 00550 * either. 00551 */ 00552 00553 if (pkgPtr->version != NULL) { 00554 ckfree(pkgPtr->version); 00555 pkgPtr->version = NULL; 00556 } 00557 pkgPtr->clientData = NULL; 00558 return NULL; 00559 } 00560 00561 break; 00562 } 00563 00564 /* 00565 * The package is not in the database. If there is a "package unknown" 00566 * command, invoke it (but only on the first pass; after that, we 00567 * should not get here in the first place). 00568 */ 00569 00570 if (pass > 1) { 00571 break; 00572 } 00573 00574 script = ((Interp *) interp)->packageUnknown; 00575 if (script != NULL) { 00576 Tcl_DStringInit(&command); 00577 Tcl_DStringAppend(&command, script, -1); 00578 Tcl_DStringAppendElement(&command, name); 00579 AddRequirementsToDString(&command, reqc, reqv); 00580 00581 code = Tcl_EvalEx(interp, Tcl_DStringValue(&command), 00582 Tcl_DStringLength(&command), TCL_EVAL_GLOBAL); 00583 Tcl_DStringFree(&command); 00584 00585 if ((code != TCL_OK) && (code != TCL_ERROR)) { 00586 Tcl_Obj *codePtr = Tcl_NewIntObj(code); 00587 Tcl_ResetResult(interp); 00588 Tcl_AppendResult(interp, "bad return code: ", 00589 TclGetString(codePtr), NULL); 00590 Tcl_DecrRefCount(codePtr); 00591 code = TCL_ERROR; 00592 } 00593 if (code == TCL_ERROR) { 00594 Tcl_AddErrorInfo(interp, 00595 "\n (\"package unknown\" script)"); 00596 return NULL; 00597 } 00598 Tcl_ResetResult(interp); 00599 } 00600 } 00601 00602 if (pkgPtr->version == NULL) { 00603 Tcl_AppendResult(interp, "can't find package ", name, NULL); 00604 AddRequirementsToResult(interp, reqc, reqv); 00605 return NULL; 00606 } 00607 00608 /* 00609 * At this point we know that the package is present. Make sure that the 00610 * provided version meets the current requirements. 00611 */ 00612 00613 if (reqc == 0) { 00614 satisfies = 1; 00615 } else { 00616 CheckVersionAndConvert(interp, pkgPtr->version, &pkgVersionI, NULL); 00617 satisfies = SomeRequirementSatisfied(pkgVersionI, reqc, reqv); 00618 00619 ckfree(pkgVersionI); 00620 } 00621 00622 if (satisfies) { 00623 if (clientDataPtr) { 00624 *clientDataPtr = pkgPtr->clientData; 00625 } 00626 return pkgPtr->version; 00627 } 00628 00629 Tcl_AppendResult(interp, "version conflict for package \"", name, 00630 "\": have ", pkgPtr->version, ", need", NULL); 00631 AddRequirementsToResult(interp, reqc, reqv); 00632 return NULL; 00633 } 00634 00635 /* 00636 *---------------------------------------------------------------------- 00637 * 00638 * Tcl_PkgPresent / Tcl_PkgPresentEx -- 00639 * 00640 * Checks to see whether the specified package is present. If it is not 00641 * then no additional action is taken. 00642 * 00643 * Results: 00644 * If successful, returns the version string for the currently provided 00645 * version of the package, which may be different from the "version" 00646 * argument. If the caller's requirements cannot be met (e.g. the version 00647 * requested conflicts with a currently provided version), NULL is 00648 * returned and an error message is left in interp->result. 00649 * 00650 * Side effects: 00651 * None. 00652 * 00653 *---------------------------------------------------------------------- 00654 */ 00655 00656 const char * 00657 Tcl_PkgPresent( 00658 Tcl_Interp *interp, /* Interpreter in which package is now 00659 * available. */ 00660 const char *name, /* Name of desired package. */ 00661 const char *version, /* Version string for desired version; NULL 00662 * means use the latest version available. */ 00663 int exact) /* Non-zero means that only the particular 00664 * version given is acceptable. Zero means use 00665 * the latest compatible version. */ 00666 { 00667 return Tcl_PkgPresentEx(interp, name, version, exact, NULL); 00668 } 00669 00670 const char * 00671 Tcl_PkgPresentEx( 00672 Tcl_Interp *interp, /* Interpreter in which package is now 00673 * available. */ 00674 const char *name, /* Name of desired package. */ 00675 const char *version, /* Version string for desired version; NULL 00676 * means use the latest version available. */ 00677 int exact, /* Non-zero means that only the particular 00678 * version given is acceptable. Zero means use 00679 * the latest compatible version. */ 00680 ClientData *clientDataPtr) /* Used to return the client data for this 00681 * package. If it is NULL then the client data 00682 * is not returned. This is unchanged if this 00683 * call fails for any reason. */ 00684 { 00685 Interp *iPtr = (Interp *) interp; 00686 Tcl_HashEntry *hPtr; 00687 Package *pkgPtr; 00688 00689 hPtr = Tcl_FindHashEntry(&iPtr->packageTable, name); 00690 if (hPtr) { 00691 pkgPtr = Tcl_GetHashValue(hPtr); 00692 if (pkgPtr->version != NULL) { 00693 /* 00694 * At this point we know that the package is present. Make sure 00695 * that the provided version meets the current requirement by 00696 * calling Tcl_PkgRequireEx() to check for us. 00697 */ 00698 00699 const char *foundVersion = Tcl_PkgRequireEx(interp, name, version, 00700 exact, clientDataPtr); 00701 00702 if (foundVersion == NULL) { 00703 Tcl_SetErrorCode(interp, "TCL", "LOOKUP", "PACKAGE", name, 00704 NULL); 00705 } 00706 return foundVersion; 00707 } 00708 } 00709 00710 if (version != NULL) { 00711 Tcl_AppendResult(interp, "package ", name, " ", version, 00712 " is not present", NULL); 00713 } else { 00714 Tcl_AppendResult(interp, "package ", name, " is not present", NULL); 00715 } 00716 Tcl_SetErrorCode(interp, "TCL", "LOOKUP", "PACKAGE", name, NULL); 00717 return NULL; 00718 } 00719 00720 /* 00721 *---------------------------------------------------------------------- 00722 * 00723 * Tcl_PackageObjCmd -- 00724 * 00725 * This function is invoked to process the "package" Tcl command. See the 00726 * user documentation for details on what it does. 00727 * 00728 * Results: 00729 * A standard Tcl result. 00730 * 00731 * Side effects: 00732 * See the user documentation. 00733 * 00734 *---------------------------------------------------------------------- 00735 */ 00736 00737 /* ARGSUSED */ 00738 int 00739 Tcl_PackageObjCmd( 00740 ClientData dummy, /* Not used. */ 00741 Tcl_Interp *interp, /* Current interpreter. */ 00742 int objc, /* Number of arguments. */ 00743 Tcl_Obj *const objv[]) /* Argument objects. */ 00744 { 00745 static const char *pkgOptions[] = { 00746 "forget", "ifneeded", "names", "prefer", "present", 00747 "provide", "require", "unknown", "vcompare", "versions", 00748 "vsatisfies", NULL 00749 }; 00750 enum pkgOptions { 00751 PKG_FORGET, PKG_IFNEEDED, PKG_NAMES, PKG_PREFER, PKG_PRESENT, 00752 PKG_PROVIDE, PKG_REQUIRE, PKG_UNKNOWN, PKG_VCOMPARE, PKG_VERSIONS, 00753 PKG_VSATISFIES 00754 }; 00755 Interp *iPtr = (Interp *) interp; 00756 int optionIndex, exact, i, satisfies; 00757 PkgAvail *availPtr, *prevPtr; 00758 Package *pkgPtr; 00759 Tcl_HashEntry *hPtr; 00760 Tcl_HashSearch search; 00761 Tcl_HashTable *tablePtr; 00762 const char *version; 00763 char *argv2, *argv3, *argv4, *iva = NULL, *ivb = NULL; 00764 00765 if (objc < 2) { 00766 Tcl_WrongNumArgs(interp, 1, objv, "option ?arg arg ...?"); 00767 return TCL_ERROR; 00768 } 00769 00770 if (Tcl_GetIndexFromObj(interp, objv[1], pkgOptions, "option", 0, 00771 &optionIndex) != TCL_OK) { 00772 return TCL_ERROR; 00773 } 00774 switch ((enum pkgOptions) optionIndex) { 00775 case PKG_FORGET: { 00776 char *keyString; 00777 00778 for (i = 2; i < objc; i++) { 00779 keyString = TclGetString(objv[i]); 00780 hPtr = Tcl_FindHashEntry(&iPtr->packageTable, keyString); 00781 if (hPtr == NULL) { 00782 continue; 00783 } 00784 pkgPtr = Tcl_GetHashValue(hPtr); 00785 Tcl_DeleteHashEntry(hPtr); 00786 if (pkgPtr->version != NULL) { 00787 ckfree(pkgPtr->version); 00788 } 00789 while (pkgPtr->availPtr != NULL) { 00790 availPtr = pkgPtr->availPtr; 00791 pkgPtr->availPtr = availPtr->nextPtr; 00792 Tcl_EventuallyFree((ClientData)availPtr->version, TCL_DYNAMIC); 00793 Tcl_EventuallyFree((ClientData)availPtr->script, TCL_DYNAMIC); 00794 ckfree((char *) availPtr); 00795 } 00796 ckfree((char *) pkgPtr); 00797 } 00798 break; 00799 } 00800 case PKG_IFNEEDED: { 00801 int length, res; 00802 char *argv3i, *avi; 00803 00804 if ((objc != 4) && (objc != 5)) { 00805 Tcl_WrongNumArgs(interp, 2, objv, "package version ?script?"); 00806 return TCL_ERROR; 00807 } 00808 argv3 = TclGetString(objv[3]); 00809 if (CheckVersionAndConvert(interp, argv3, &argv3i, NULL) != TCL_OK) { 00810 return TCL_ERROR; 00811 } 00812 argv2 = TclGetString(objv[2]); 00813 if (objc == 4) { 00814 hPtr = Tcl_FindHashEntry(&iPtr->packageTable, argv2); 00815 if (hPtr == NULL) { 00816 ckfree(argv3i); 00817 return TCL_OK; 00818 } 00819 pkgPtr = Tcl_GetHashValue(hPtr); 00820 } else { 00821 pkgPtr = FindPackage(interp, argv2); 00822 } 00823 argv3 = Tcl_GetStringFromObj(objv[3], &length); 00824 00825 for (availPtr = pkgPtr->availPtr, prevPtr = NULL; availPtr != NULL; 00826 prevPtr = availPtr, availPtr = availPtr->nextPtr) { 00827 if (CheckVersionAndConvert(interp, availPtr->version, &avi, 00828 NULL) != TCL_OK) { 00829 ckfree(argv3i); 00830 return TCL_ERROR; 00831 } 00832 00833 res = CompareVersions(avi, argv3i, NULL); 00834 ckfree(avi); 00835 00836 if (res == 0){ 00837 if (objc == 4) { 00838 ckfree(argv3i); 00839 Tcl_SetResult(interp, availPtr->script, TCL_VOLATILE); 00840 return TCL_OK; 00841 } 00842 Tcl_EventuallyFree((ClientData)availPtr->script, TCL_DYNAMIC); 00843 break; 00844 } 00845 } 00846 ckfree(argv3i); 00847 00848 if (objc == 4) { 00849 return TCL_OK; 00850 } 00851 if (availPtr == NULL) { 00852 availPtr = (PkgAvail *) ckalloc(sizeof(PkgAvail)); 00853 DupBlock(availPtr->version, argv3, (unsigned) length + 1); 00854 00855 if (prevPtr == NULL) { 00856 availPtr->nextPtr = pkgPtr->availPtr; 00857 pkgPtr->availPtr = availPtr; 00858 } else { 00859 availPtr->nextPtr = prevPtr->nextPtr; 00860 prevPtr->nextPtr = availPtr; 00861 } 00862 } 00863 argv4 = Tcl_GetStringFromObj(objv[4], &length); 00864 DupBlock(availPtr->script, argv4, (unsigned) length + 1); 00865 break; 00866 } 00867 case PKG_NAMES: 00868 if (objc != 2) { 00869 Tcl_WrongNumArgs(interp, 2, objv, NULL); 00870 return TCL_ERROR; 00871 } 00872 tablePtr = &iPtr->packageTable; 00873 for (hPtr = Tcl_FirstHashEntry(tablePtr, &search); hPtr != NULL; 00874 hPtr = Tcl_NextHashEntry(&search)) { 00875 pkgPtr = Tcl_GetHashValue(hPtr); 00876 if ((pkgPtr->version != NULL) || (pkgPtr->availPtr != NULL)) { 00877 Tcl_AppendElement(interp, Tcl_GetHashKey(tablePtr, hPtr)); 00878 } 00879 } 00880 break; 00881 case PKG_PRESENT: { 00882 const char *name; 00883 if (objc < 3) { 00884 goto require; 00885 } 00886 argv2 = TclGetString(objv[2]); 00887 if ((argv2[0] == '-') && (strcmp(argv2, "-exact") == 0)) { 00888 if (objc != 5) { 00889 goto requireSyntax; 00890 } 00891 exact = 1; 00892 name = TclGetString(objv[3]); 00893 } else { 00894 exact = 0; 00895 name = argv2; 00896 } 00897 00898 hPtr = Tcl_FindHashEntry(&iPtr->packageTable, name); 00899 if (hPtr != NULL) { 00900 pkgPtr = Tcl_GetHashValue(hPtr); 00901 if (pkgPtr->version != NULL) { 00902 goto require; 00903 } 00904 } 00905 00906 version = NULL; 00907 if (exact) { 00908 version = TclGetString(objv[4]); 00909 if (CheckVersionAndConvert(interp, version, NULL, 00910 NULL) != TCL_OK) { 00911 return TCL_ERROR; 00912 } 00913 } else { 00914 if (CheckAllRequirements(interp, objc-3, objv+3) != TCL_OK) { 00915 return TCL_ERROR; 00916 } 00917 if ((objc > 3) && (CheckVersionAndConvert(interp, 00918 TclGetString(objv[3]), NULL, NULL) == TCL_OK)) { 00919 version = TclGetString(objv[3]); 00920 } 00921 } 00922 Tcl_PkgPresent(interp, name, version, exact); 00923 return TCL_ERROR; 00924 break; 00925 } 00926 case PKG_PROVIDE: 00927 if ((objc != 3) && (objc != 4)) { 00928 Tcl_WrongNumArgs(interp, 2, objv, "package ?version?"); 00929 return TCL_ERROR; 00930 } 00931 argv2 = TclGetString(objv[2]); 00932 if (objc == 3) { 00933 hPtr = Tcl_FindHashEntry(&iPtr->packageTable, argv2); 00934 if (hPtr != NULL) { 00935 pkgPtr = Tcl_GetHashValue(hPtr); 00936 if (pkgPtr->version != NULL) { 00937 Tcl_SetResult(interp, pkgPtr->version, TCL_VOLATILE); 00938 } 00939 } 00940 return TCL_OK; 00941 } 00942 argv3 = TclGetString(objv[3]); 00943 if (CheckVersionAndConvert(interp, argv3, NULL, NULL) != TCL_OK) { 00944 return TCL_ERROR; 00945 } 00946 return Tcl_PkgProvide(interp, argv2, argv3); 00947 case PKG_REQUIRE: 00948 require: 00949 if (objc < 3) { 00950 requireSyntax: 00951 Tcl_WrongNumArgs(interp, 2, objv, 00952 "?-exact? package ?requirement...?"); 00953 return TCL_ERROR; 00954 } 00955 00956 version = NULL; 00957 00958 argv2 = TclGetString(objv[2]); 00959 if ((argv2[0] == '-') && (strcmp(argv2, "-exact") == 0)) { 00960 Tcl_Obj *ov; 00961 int res; 00962 00963 if (objc != 5) { 00964 goto requireSyntax; 00965 } 00966 00967 version = TclGetString(objv[4]); 00968 if (CheckVersionAndConvert(interp, version, NULL, 00969 NULL) != TCL_OK) { 00970 return TCL_ERROR; 00971 } 00972 00973 /* 00974 * Create a new-style requirement for the exact version. 00975 */ 00976 00977 ov = Tcl_NewStringObj(version, -1); 00978 Tcl_AppendStringsToObj(ov, "-", version, NULL); 00979 version = NULL; 00980 argv3 = TclGetString(objv[3]); 00981 00982 Tcl_IncrRefCount(ov); 00983 res = Tcl_PkgRequireProc(interp, argv3, 1, &ov, NULL); 00984 TclDecrRefCount(ov); 00985 return res; 00986 } else { 00987 if (CheckAllRequirements(interp, objc-3, objv+3) != TCL_OK) { 00988 return TCL_ERROR; 00989 } 00990 00991 return Tcl_PkgRequireProc(interp, argv2, objc-3, objv+3, NULL); 00992 } 00993 break; 00994 case PKG_UNKNOWN: { 00995 int length; 00996 00997 if (objc == 2) { 00998 if (iPtr->packageUnknown != NULL) { 00999 Tcl_SetResult(interp, iPtr->packageUnknown, TCL_VOLATILE); 01000 } 01001 } else if (objc == 3) { 01002 if (iPtr->packageUnknown != NULL) { 01003 ckfree(iPtr->packageUnknown); 01004 } 01005 argv2 = Tcl_GetStringFromObj(objv[2], &length); 01006 if (argv2[0] == 0) { 01007 iPtr->packageUnknown = NULL; 01008 } else { 01009 DupBlock(iPtr->packageUnknown, argv2, (unsigned) length+1); 01010 } 01011 } else { 01012 Tcl_WrongNumArgs(interp, 2, objv, "?command?"); 01013 return TCL_ERROR; 01014 } 01015 break; 01016 } 01017 case PKG_PREFER: { 01018 static const char *pkgPreferOptions[] = { 01019 "latest", "stable", NULL 01020 }; 01021 01022 /* 01023 * See tclInt.h for the enum, just before Interp. 01024 */ 01025 01026 if (objc > 3) { 01027 Tcl_WrongNumArgs(interp, 2, objv, "?latest|stable?"); 01028 return TCL_ERROR; 01029 } else if (objc == 3) { 01030 /* 01031 * Seting the value. 01032 */ 01033 01034 int newPref; 01035 01036 if (Tcl_GetIndexFromObj(interp, objv[2], pkgPreferOptions, 01037 "preference", 0, &newPref) != TCL_OK) { 01038 return TCL_ERROR; 01039 } 01040 01041 if (newPref < iPtr->packagePrefer) { 01042 iPtr->packagePrefer = newPref; 01043 } 01044 } 01045 01046 /* 01047 * Always return current value. 01048 */ 01049 01050 Tcl_SetObjResult(interp, 01051 Tcl_NewStringObj(pkgPreferOptions[iPtr->packagePrefer], -1)); 01052 break; 01053 } 01054 case PKG_VCOMPARE: 01055 if (objc != 4) { 01056 Tcl_WrongNumArgs(interp, 2, objv, "version1 version2"); 01057 return TCL_ERROR; 01058 } 01059 argv3 = TclGetString(objv[3]); 01060 argv2 = TclGetString(objv[2]); 01061 if (CheckVersionAndConvert(interp, argv2, &iva, NULL) != TCL_OK || 01062 CheckVersionAndConvert(interp, argv3, &ivb, NULL) != TCL_OK) { 01063 if (iva != NULL) { 01064 ckfree(iva); 01065 } 01066 01067 /* 01068 * ivb cannot be set in this branch. 01069 */ 01070 01071 return TCL_ERROR; 01072 } 01073 01074 /* 01075 * Comparison is done on the internal representation. 01076 */ 01077 01078 Tcl_SetObjResult(interp, 01079 Tcl_NewIntObj(CompareVersions(iva, ivb, NULL))); 01080 ckfree(iva); 01081 ckfree(ivb); 01082 break; 01083 case PKG_VERSIONS: 01084 if (objc != 3) { 01085 Tcl_WrongNumArgs(interp, 2, objv, "package"); 01086 return TCL_ERROR; 01087 } 01088 argv2 = TclGetString(objv[2]); 01089 hPtr = Tcl_FindHashEntry(&iPtr->packageTable, argv2); 01090 if (hPtr != NULL) { 01091 pkgPtr = Tcl_GetHashValue(hPtr); 01092 for (availPtr = pkgPtr->availPtr; availPtr != NULL; 01093 availPtr = availPtr->nextPtr) { 01094 Tcl_AppendElement(interp, availPtr->version); 01095 } 01096 } 01097 break; 01098 case PKG_VSATISFIES: { 01099 char *argv2i = NULL; 01100 01101 if (objc < 4) { 01102 Tcl_WrongNumArgs(interp, 2, objv, 01103 "version requirement requirement..."); 01104 return TCL_ERROR; 01105 } 01106 01107 argv2 = TclGetString(objv[2]); 01108 if (CheckVersionAndConvert(interp, argv2, &argv2i, NULL) != TCL_OK) { 01109 return TCL_ERROR; 01110 } else if (CheckAllRequirements(interp, objc-3, objv+3) != TCL_OK) { 01111 ckfree(argv2i); 01112 return TCL_ERROR; 01113 } 01114 01115 satisfies = SomeRequirementSatisfied(argv2i, objc-3, objv+3); 01116 ckfree(argv2i); 01117 01118 Tcl_SetObjResult(interp, Tcl_NewBooleanObj(satisfies)); 01119 break; 01120 } 01121 default: 01122 Tcl_Panic("Tcl_PackageObjCmd: bad option index to pkgOptions"); 01123 } 01124 return TCL_OK; 01125 } 01126 01127 /* 01128 *---------------------------------------------------------------------- 01129 * 01130 * FindPackage -- 01131 * 01132 * This function finds the Package record for a particular package in a 01133 * particular interpreter, creating a record if one doesn't already 01134 * exist. 01135 * 01136 * Results: 01137 * The return value is a pointer to the Package record for the package. 01138 * 01139 * Side effects: 01140 * A new Package record may be created. 01141 * 01142 *---------------------------------------------------------------------- 01143 */ 01144 01145 static Package * 01146 FindPackage( 01147 Tcl_Interp *interp, /* Interpreter to use for package lookup. */ 01148 const char *name) /* Name of package to fine. */ 01149 { 01150 Interp *iPtr = (Interp *) interp; 01151 Tcl_HashEntry *hPtr; 01152 int isNew; 01153 Package *pkgPtr; 01154 01155 hPtr = Tcl_CreateHashEntry(&iPtr->packageTable, name, &isNew); 01156 if (isNew) { 01157 pkgPtr = (Package *) ckalloc(sizeof(Package)); 01158 pkgPtr->version = NULL; 01159 pkgPtr->availPtr = NULL; 01160 pkgPtr->clientData = NULL; 01161 Tcl_SetHashValue(hPtr, pkgPtr); 01162 } else { 01163 pkgPtr = Tcl_GetHashValue(hPtr); 01164 } 01165 return pkgPtr; 01166 } 01167 01168 /* 01169 *---------------------------------------------------------------------- 01170 * 01171 * TclFreePackageInfo -- 01172 * 01173 * This function is called during interpreter deletion to free all of the 01174 * package-related information for the interpreter. 01175 * 01176 * Results: 01177 * None. 01178 * 01179 * Side effects: 01180 * Memory is freed. 01181 * 01182 *---------------------------------------------------------------------- 01183 */ 01184 01185 void 01186 TclFreePackageInfo( 01187 Interp *iPtr) /* Interpereter that is being deleted. */ 01188 { 01189 Package *pkgPtr; 01190 Tcl_HashSearch search; 01191 Tcl_HashEntry *hPtr; 01192 PkgAvail *availPtr; 01193 01194 for (hPtr = Tcl_FirstHashEntry(&iPtr->packageTable, &search); 01195 hPtr != NULL; hPtr = Tcl_NextHashEntry(&search)) { 01196 pkgPtr = Tcl_GetHashValue(hPtr); 01197 if (pkgPtr->version != NULL) { 01198 ckfree(pkgPtr->version); 01199 } 01200 while (pkgPtr->availPtr != NULL) { 01201 availPtr = pkgPtr->availPtr; 01202 pkgPtr->availPtr = availPtr->nextPtr; 01203 Tcl_EventuallyFree((ClientData)availPtr->version, TCL_DYNAMIC); 01204 Tcl_EventuallyFree((ClientData)availPtr->script, TCL_DYNAMIC); 01205 ckfree((char *) availPtr); 01206 } 01207 ckfree((char *) pkgPtr); 01208 } 01209 Tcl_DeleteHashTable(&iPtr->packageTable); 01210 if (iPtr->packageUnknown != NULL) { 01211 ckfree(iPtr->packageUnknown); 01212 } 01213 } 01214 01215 /* 01216 *---------------------------------------------------------------------- 01217 * 01218 * CheckVersionAndConvert -- 01219 * 01220 * This function checks to see whether a version number has valid syntax. 01221 * It also generates a semi-internal representation (string rep of a list 01222 * of numbers). 01223 * 01224 * Results: 01225 * If string is a properly formed version number the TCL_OK is returned. 01226 * Otherwise TCL_ERROR is returned and an error message is left in the 01227 * interp's result. 01228 * 01229 * Side effects: 01230 * None. 01231 * 01232 *---------------------------------------------------------------------- 01233 */ 01234 01235 static int 01236 CheckVersionAndConvert( 01237 Tcl_Interp *interp, /* Used for error reporting. */ 01238 const char *string, /* Supposedly a version number, which is 01239 * groups of decimal digits separated by 01240 * dots. */ 01241 char **internal, /* Internal normalized representation */ 01242 int *stable) /* Flag: Version is (un)stable. */ 01243 { 01244 const char *p = string; 01245 char prevChar; 01246 int hasunstable = 0; 01247 /* 01248 * 4* assuming that each char is a separator (a,b become ' -x '). 01249 * 4+ to have spce for an additional -2 at the end 01250 */ 01251 char *ibuf = ckalloc(4 + 4*strlen(string)); 01252 char *ip = ibuf; 01253 01254 /* 01255 * Basic rules 01256 * (1) First character has to be a digit. 01257 * (2) All other characters have to be a digit or '.' 01258 * (3) Two '.'s may not follow each other. 01259 * 01260 * TIP 268, Modified rules 01261 * (1) s.a. 01262 * (2) All other characters have to be a digit, 'a', 'b', or '.' 01263 * (3) s.a. 01264 * (4) Only one of 'a' or 'b' may occur. 01265 * (5) Neither 'a', nor 'b' may occur before or after a '.' 01266 */ 01267 01268 if (!isdigit(UCHAR(*p))) { /* INTL: digit */ 01269 goto error; 01270 } 01271 01272 *ip++ = *p; 01273 01274 for (prevChar = *p, p++; *p != 0; p++) { 01275 if (!isdigit(UCHAR(*p)) && /* INTL: digit */ 01276 ((*p!='.' && *p!='a' && *p!='b') || 01277 ((hasunstable && (*p=='a' || *p=='b')) || 01278 ((prevChar=='a' || prevChar=='b' || prevChar=='.') 01279 && (*p=='.')) || 01280 ((*p=='a' || *p=='b' || *p=='.') && prevChar=='.')))) { 01281 goto error; 01282 } 01283 01284 if (*p == 'a' || *p == 'b') { 01285 hasunstable = 1; 01286 } 01287 01288 /* 01289 * Translation to the internal rep. Regular version chars are copied 01290 * as is. The separators are translated to numerics. The new separator 01291 * for all parts is space. 01292 */ 01293 01294 if (*p == '.') { 01295 *ip++ = ' '; 01296 *ip++ = '0'; 01297 *ip++ = ' '; 01298 } else if (*p == 'a') { 01299 *ip++ = ' '; 01300 *ip++ = '-'; 01301 *ip++ = '2'; 01302 *ip++ = ' '; 01303 } else if (*p == 'b') { 01304 *ip++ = ' '; 01305 *ip++ = '-'; 01306 *ip++ = '1'; 01307 *ip++ = ' '; 01308 } else { 01309 *ip++ = *p; 01310 } 01311 01312 prevChar = *p; 01313 } 01314 if (prevChar!='.' && prevChar!='a' && prevChar!='b') { 01315 *ip = '\0'; 01316 if (internal != NULL) { 01317 *internal = ibuf; 01318 } else { 01319 ckfree(ibuf); 01320 } 01321 if (stable != NULL) { 01322 *stable = !hasunstable; 01323 } 01324 return TCL_OK; 01325 } 01326 01327 error: 01328 ckfree(ibuf); 01329 Tcl_AppendResult(interp, "expected version number but got \"", string, 01330 "\"", NULL); 01331 return TCL_ERROR; 01332 } 01333 01334 /* 01335 *---------------------------------------------------------------------- 01336 * 01337 * CompareVersions -- 01338 * 01339 * This function compares two version numbers (in internal rep). 01340 * 01341 * Results: 01342 * The return value is -1 if v1 is less than v2, 0 if the two version 01343 * numbers are the same, and 1 if v1 is greater than v2. If *satPtr is 01344 * non-NULL, the word it points to is filled in with 1 if v2 >= v1 and 01345 * both numbers have the same major number or 0 otherwise. 01346 * 01347 * Side effects: 01348 * None. 01349 * 01350 *---------------------------------------------------------------------- 01351 */ 01352 01353 static int 01354 CompareVersions( 01355 char *v1, char *v2, /* Versions strings, of form 2.1.3 (any number 01356 * of version numbers). */ 01357 int *isMajorPtr) /* If non-null, the word pointed to is filled 01358 * in with a 0/1 value. 1 means that the 01359 * difference occured in the first element. */ 01360 { 01361 int thisIsMajor, res, flip; 01362 char *s1, *e1, *s2, *e2, o1, o2; 01363 01364 /* 01365 * Each iteration of the following loop processes one number from each 01366 * string, terminated by a " " (space). If those numbers don't match then 01367 * the comparison is over; otherwise, we loop back for the next number. 01368 * 01369 * TIP 268. 01370 * This is identical the function 'ComparePkgVersion', but using the new 01371 * space separator as used by the internal rep of version numbers. The 01372 * special separators 'a' and 'b' have already been dealt with in 01373 * 'CheckVersionAndConvert', they were translated into numbers as well. 01374 * This keeps the comparison sane. Otherwise we would have to compare 01375 * numerics, the separators, and also deal with the special case of 01376 * end-of-string compared to separators. The semi-list rep we get here is 01377 * much easier to handle, as it is still regular. 01378 * 01379 * Rewritten to not compute a numeric value for the extracted version 01380 * number, but do string comparison. Skip any leading zeros for that to 01381 * work. This change breaks through the 32bit-limit on version numbers. 01382 */ 01383 01384 thisIsMajor = 1; 01385 s1 = v1; 01386 s2 = v2; 01387 01388 while (1) { 01389 /* 01390 * Parse one decimal number from the front of each string. Skip 01391 * leading zeros. Terminate found number for upcoming string-wise 01392 * comparison, if needed. 01393 */ 01394 01395 while ((*s1 != 0) && (*s1 == '0')) { 01396 s1++; 01397 } 01398 while ((*s2 != 0) && (*s2 == '0')) { 01399 s2++; 01400 } 01401 01402 /* 01403 * s1, s2 now point to the beginnings of the numbers to compare. Test 01404 * for their signs first, as shortcut to the result (different signs), 01405 * or determines if result has to be flipped (both negative). If there 01406 * is no shortcut we have to insert terminators later to limit the 01407 * strcmp. 01408 */ 01409 01410 if ((*s1 == '-') && (*s2 != '-')) { 01411 /* s1 < 0, s2 >= 0 => s1 < s2 */ 01412 res = -1; 01413 break; 01414 } 01415 if ((*s1 != '-') && (*s2 == '-')) { 01416 /* s1 >= 0, s2 < 0 => s1 > s2 */ 01417 res = 1; 01418 break; 01419 } 01420 01421 if ((*s1 == '-') && (*s2 == '-')) { 01422 /* a < b => -a > -b, etc. */ 01423 s1++; 01424 s2++; 01425 flip = 1; 01426 } else { 01427 flip = 0; 01428 } 01429 01430 /* 01431 * The string comparison is needed, so now we determine where the 01432 * numbers end. 01433 */ 01434 01435 e1 = s1; 01436 while ((*e1 != 0) && (*e1 != ' ')) { 01437 e1++; 01438 } 01439 e2 = s2; 01440 while ((*e2 != 0) && (*e2 != ' ')) { 01441 e2++; 01442 } 01443 01444 /* 01445 * s1 .. e1 and s2 .. e2 now bracket the numbers to compare. Insert 01446 * terminators, compare, and restore actual contents. First however 01447 * another shortcut. Compare lengths. Shorter string is smaller 01448 * number! Thus we strcmp only strings of identical length. 01449 */ 01450 01451 if ((e1-s1) < (e2-s2)) { 01452 res = -1; 01453 } else if ((e2-s2) < (e1-s1)) { 01454 res = 1; 01455 } else { 01456 o1 = *e1; 01457 *e1 = '\0'; 01458 o2 = *e2; 01459 *e2 = '\0'; 01460 01461 res = strcmp(s1, s2); 01462 res = (res < 0) ? -1 : (res ? 1 : 0); 01463 01464 *e1 = o1; 01465 *e2 = o2; 01466 } 01467 01468 /* 01469 * Stop comparing segments when a difference has been found. Here we 01470 * may have to flip the result to account for signs. 01471 */ 01472 01473 if (res != 0) { 01474 if (flip) { 01475 res = -res; 01476 } 01477 break; 01478 } 01479 01480 /* 01481 * Go on to the next version number if the current numbers match. 01482 * However stop processing if the end of both numbers has been 01483 * reached. 01484 */ 01485 01486 s1 = e1; 01487 s2 = e2; 01488 01489 if (*s1 != 0) { 01490 s1++; 01491 } else if (*s2 == 0) { 01492 /* 01493 * s1, s2 both at the end => identical 01494 */ 01495 01496 res = 0; 01497 break; 01498 } 01499 if (*s2 != 0) { 01500 s2++; 01501 } 01502 thisIsMajor = 0; 01503 } 01504 01505 if (isMajorPtr != NULL) { 01506 *isMajorPtr = thisIsMajor; 01507 } 01508 01509 return res; 01510 } 01511 01512 /* 01513 *---------------------------------------------------------------------- 01514 * 01515 * CheckAllRequirements -- 01516 * 01517 * This function checks to see whether all requirements in a set have 01518 * valid syntax. 01519 * 01520 * Results: 01521 * TCL_OK is returned if all requirements are valid. Otherwise TCL_ERROR 01522 * is returned and an error message is left in the interp's result. 01523 * 01524 * Side effects: 01525 * May modify the interpreter result. 01526 * 01527 *---------------------------------------------------------------------- 01528 */ 01529 01530 static int 01531 CheckAllRequirements( 01532 Tcl_Interp *interp, 01533 int reqc, /* Requirements to check. */ 01534 Tcl_Obj *const reqv[]) 01535 { 01536 int i; 01537 01538 for (i = 0; i < reqc; i++) { 01539 if ((CheckRequirement(interp, TclGetString(reqv[i])) != TCL_OK)) { 01540 return TCL_ERROR; 01541 } 01542 } 01543 return TCL_OK; 01544 } 01545 01546 /* 01547 *---------------------------------------------------------------------- 01548 * 01549 * CheckRequirement -- 01550 * 01551 * This function checks to see whether a requirement has valid syntax. 01552 * 01553 * Results: 01554 * If string is a properly formed requirement then TCL_OK is returned. 01555 * Otherwise TCL_ERROR is returned and an error message is left in the 01556 * interp's result. 01557 * 01558 * Side effects: 01559 * None. 01560 * 01561 *---------------------------------------------------------------------- 01562 */ 01563 01564 static int 01565 CheckRequirement( 01566 Tcl_Interp *interp, /* Used for error reporting. */ 01567 const char *string) /* Supposedly a requirement. */ 01568 { 01569 /* 01570 * Syntax of requirement = version 01571 * = version-version 01572 * = version- 01573 */ 01574 01575 char *dash = NULL, *buf; 01576 01577 dash = strchr(string, '-'); 01578 if (dash == NULL) { 01579 /* 01580 * No dash found, has to be a simple version. 01581 */ 01582 01583 return CheckVersionAndConvert(interp, string, NULL, NULL); 01584 } 01585 01586 if (strchr(dash+1, '-') != NULL) { 01587 /* 01588 * More dashes found after the first. This is wrong. 01589 */ 01590 01591 Tcl_AppendResult(interp, "expected versionMin-versionMax but got \"", 01592 string, "\"", NULL); 01593 return TCL_ERROR; 01594 } 01595 01596 /* 01597 * Exactly one dash is present. Copy the string, split at the location of 01598 * dash and check that both parts are versions. Note that the max part can 01599 * be empty. Also note that the string allocated with strdup() must be 01600 * freed with free() and not ckfree(). 01601 */ 01602 01603 DupString(buf, string); 01604 dash = buf + (dash - string); 01605 *dash = '\0'; /* buf now <=> min part */ 01606 dash++; /* dash now <=> max part */ 01607 01608 if ((CheckVersionAndConvert(interp, buf, NULL, NULL) != TCL_OK) || 01609 ((*dash != '\0') && 01610 (CheckVersionAndConvert(interp, dash, NULL, NULL) != TCL_OK))) { 01611 ckfree(buf); 01612 return TCL_ERROR; 01613 } 01614 01615 ckfree(buf); 01616 return TCL_OK; 01617 } 01618 01619 /* 01620 *---------------------------------------------------------------------- 01621 * 01622 * AddRequirementsToResult -- 01623 * 01624 * This function accumulates requirements in the interpreter result. 01625 * 01626 * Results: 01627 * None. 01628 * 01629 * Side effects: 01630 * The interpreter result is extended. 01631 * 01632 *---------------------------------------------------------------------- 01633 */ 01634 01635 static void 01636 AddRequirementsToResult( 01637 Tcl_Interp *interp, 01638 int reqc, /* Requirements constraining the desired 01639 * version. */ 01640 Tcl_Obj *const reqv[]) /* 0 means to use the latest version 01641 * available. */ 01642 { 01643 if (reqc > 0) { 01644 int i; 01645 01646 for (i = 0; i < reqc; i++) { 01647 int length; 01648 char *v = Tcl_GetStringFromObj(reqv[i], &length); 01649 01650 if ((length & 0x1) && (v[length/2] == '-') 01651 && (strncmp(v, v+((length+1)/2), length/2) == 0)) { 01652 Tcl_AppendResult(interp, " exactly ", v+((length+1)/2), NULL); 01653 } else { 01654 Tcl_AppendResult(interp, " ", v, NULL); 01655 } 01656 } 01657 } 01658 } 01659 01660 /* 01661 *---------------------------------------------------------------------- 01662 * 01663 * AddRequirementsToDString -- 01664 * 01665 * This function accumulates requirements in a DString. 01666 * 01667 * Results: 01668 * None. 01669 * 01670 * Side effects: 01671 * The DString argument is extended. 01672 * 01673 *---------------------------------------------------------------------- 01674 */ 01675 01676 static void 01677 AddRequirementsToDString( 01678 Tcl_DString *dsPtr, 01679 int reqc, /* Requirements constraining the desired 01680 * version. */ 01681 Tcl_Obj *const reqv[]) /* 0 means to use the latest version 01682 * available. */ 01683 { 01684 if (reqc > 0) { 01685 int i; 01686 01687 for (i = 0; i < reqc; i++) { 01688 Tcl_DStringAppend(dsPtr, " ", 1); 01689 Tcl_DStringAppend(dsPtr, TclGetString(reqv[i]), -1); 01690 } 01691 } else { 01692 Tcl_DStringAppend(dsPtr, " 0-", -1); 01693 } 01694 } 01695 01696 /* 01697 *---------------------------------------------------------------------- 01698 * 01699 * SomeRequirementSatisfied -- 01700 * 01701 * This function checks to see whether a version satisfies at least one 01702 * of a set of requirements. 01703 * 01704 * Results: 01705 * If the requirements are satisfied 1 is returned. Otherwise 0 is 01706 * returned. The function assumes that all pieces have valid syntax. And 01707 * is allowed to make that assumption. 01708 * 01709 * Side effects: 01710 * None. 01711 * 01712 *---------------------------------------------------------------------- 01713 */ 01714 01715 static int 01716 SomeRequirementSatisfied( 01717 char *availVersionI, /* Candidate version to check against the 01718 * requirements. */ 01719 int reqc, /* Requirements constraining the desired 01720 * version. */ 01721 Tcl_Obj *const reqv[]) /* 0 means to use the latest version 01722 * available. */ 01723 { 01724 int i; 01725 01726 for (i = 0; i < reqc; i++) { 01727 if (RequirementSatisfied(availVersionI, TclGetString(reqv[i]))) { 01728 return 1; 01729 } 01730 } 01731 return 0; 01732 } 01733 01734 /* 01735 *---------------------------------------------------------------------- 01736 * 01737 * RequirementSatisfied -- 01738 * 01739 * This function checks to see whether a version satisfies a requirement. 01740 * 01741 * Results: 01742 * If the requirement is satisfied 1 is returned. Otherwise 0 is 01743 * returned. The function assumes that all pieces have valid syntax, and 01744 * is allowed to make that assumption. 01745 * 01746 * Side effects: 01747 * None. 01748 * 01749 *---------------------------------------------------------------------- 01750 */ 01751 01752 static int 01753 RequirementSatisfied( 01754 char *havei, /* Version string, of candidate package we 01755 * have. */ 01756 const char *req) /* Requirement string the candidate has to 01757 * satisfy. */ 01758 { 01759 /* 01760 * The have candidate is already in internal rep. 01761 */ 01762 01763 int satisfied, res; 01764 char *dash = NULL, *buf, *min, *max; 01765 01766 dash = strchr(req, '-'); 01767 if (dash == NULL) { 01768 /* 01769 * No dash found, is a simple version, fallback to regular check. The 01770 * 'CheckVersionAndConvert' cannot fail. We pad the requirement with 01771 * 'a0', i.e '-2' before doing the comparison to properly accept 01772 * unstables as well. 01773 */ 01774 01775 char *reqi = NULL; 01776 int thisIsMajor; 01777 01778 CheckVersionAndConvert(NULL, req, &reqi, NULL); 01779 strcat(reqi, " -2"); 01780 res = CompareVersions(havei, reqi, &thisIsMajor); 01781 satisfied = (res == 0) || ((res == 1) && !thisIsMajor); 01782 ckfree(reqi); 01783 return satisfied; 01784 } 01785 01786 /* 01787 * Exactly one dash is present (Assumption of valid syntax). Copy the req, 01788 * split at the location of dash and check that both parts are versions. 01789 * Note that the max part can be empty. 01790 */ 01791 01792 DupString(buf, req); 01793 dash = buf + (dash - req); 01794 *dash = '\0'; /* buf now <=> min part */ 01795 dash++; /* dash now <=> max part */ 01796 01797 if (*dash == '\0') { 01798 /* 01799 * We have a min, but no max. For the comparison we generate the 01800 * internal rep, padded with 'a0' i.e. '-2'. 01801 */ 01802 01803 CheckVersionAndConvert(NULL, buf, &min, NULL); 01804 strcat(min, " -2"); 01805 satisfied = (CompareVersions(havei, min, NULL) >= 0); 01806 ckfree(min); 01807 ckfree(buf); 01808 return satisfied; 01809 } 01810 01811 /* 01812 * We have both min and max, and generate their internal reps. When 01813 * identical we compare as is, otherwise we pad with 'a0' to ove the range 01814 * a bit. 01815 */ 01816 01817 CheckVersionAndConvert(NULL, buf, &min, NULL); 01818 CheckVersionAndConvert(NULL, dash, &max, NULL); 01819 01820 if (CompareVersions(min, max, NULL) == 0) { 01821 satisfied = (CompareVersions(min, havei, NULL) == 0); 01822 } else { 01823 strcat(min, " -2"); 01824 strcat(max, " -2"); 01825 satisfied = ((CompareVersions(min, havei, NULL) <= 0) && 01826 (CompareVersions(havei, max, NULL) < 0)); 01827 } 01828 01829 ckfree(min); 01830 ckfree(max); 01831 ckfree(buf); 01832 return satisfied; 01833 } 01834 01835 /* 01836 *---------------------------------------------------------------------- 01837 * 01838 * Tcl_PkgInitStubsCheck -- 01839 * 01840 * This is a replacement routine for Tcl_InitStubs() that is called 01841 * from code where -DUSE_TCL_STUBS has not been enabled. 01842 * 01843 * Results: 01844 * Returns the version of a conforming stubs table, or NULL, if 01845 * the table version doesn't satisfy the requested requirements, 01846 * according to historical practice. 01847 * 01848 * Side effects: 01849 * None. 01850 * 01851 *---------------------------------------------------------------------- 01852 */ 01853 01854 const char * 01855 Tcl_PkgInitStubsCheck( 01856 Tcl_Interp *interp, 01857 const char * version, 01858 int exact) 01859 { 01860 const char *actualVersion = Tcl_PkgPresent(interp, "Tcl", version, 0); 01861 01862 if (exact && actualVersion) { 01863 const char *p = version; 01864 int count = 0; 01865 01866 while (*p) { 01867 count += !isdigit(*p++); 01868 } 01869 if (count == 1) { 01870 if (0 != strncmp(version, actualVersion, strlen(version))) { 01871 return NULL; 01872 } 01873 } else { 01874 return Tcl_PkgPresent(interp, "Tcl", version, 1); 01875 } 01876 } 01877 return actualVersion; 01878 } 01879 /* 01880 * Local Variables: 01881 * mode: c 01882 * c-basic-offset: 4 01883 * fill-column: 78 01884 * End: 01885 */
Generated on Wed Mar 12 12:18:20 2008 by 1.5.1 |