tclPkg.c

Go 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  doxygen 1.5.1