tclPkg.c
上传用户:rrhhcc
上传日期:2015-12-11
资源大小:54129k
文件大小:58k
源码类别:

通讯编程

开发平台:

Visual C++

  1. /* 
  2.  * tclPkg.c --
  3.  *
  4.  * This file implements package and version control for Tcl via
  5.  * the "package" command and a few C APIs.
  6.  *
  7.  * Copyright (c) 1996 Sun Microsystems, Inc.
  8.  * Copyright (c) 2006 Andreas Kupries <andreas_kupries@users.sourceforge.net>
  9.  *
  10.  * See the file "license.terms" for information on usage and redistribution
  11.  * of this file, and for a DISCLAIMER OF ALL WARRANTIES.
  12.  *
  13.  * RCS: @(#) $Id: tclPkg.c,v 1.9.2.10 2007/09/19 09:23:59 dgp Exp $
  14.  *
  15.  * TIP #268.
  16.  * Heavily rewritten to handle the extend version numbers, and extended
  17.  * package requirements.
  18.  */
  19. #include "tclInt.h"
  20. /*
  21.  * Each invocation of the "package ifneeded" command creates a structure
  22.  * of the following type, which is used to load the package into the
  23.  * interpreter if it is requested with a "package require" command.
  24.  */
  25. typedef struct PkgAvail {
  26.     char *version; /* Version string; malloc'ed. */
  27.     char *script; /* Script to invoke to provide this version
  28.  * of the package.  Malloc'ed and protected
  29.  * by Tcl_Preserve and Tcl_Release. */
  30.     struct PkgAvail *nextPtr; /* Next in list of available versions of
  31.  * the same package. */
  32. } PkgAvail;
  33. /*
  34.  * For each package that is known in any way to an interpreter, there
  35.  * is one record of the following type.  These records are stored in
  36.  * the "packageTable" hash table in the interpreter, keyed by
  37.  * package name such as "Tk" (no version number).
  38.  */
  39. typedef struct Package {
  40.     char *version; /* Version that has been supplied in this
  41.  * interpreter via "package provide"
  42.  * (malloc'ed).  NULL means the package doesn't
  43.  * exist in this interpreter yet. */
  44.     PkgAvail *availPtr; /* First in list of all available versions
  45.  * of this package. */
  46.     ClientData clientData; /* Client data. */
  47. } Package;
  48. /*
  49.  * Prototypes for procedures defined in this file:
  50.  */
  51. #ifndef TCL_TIP268
  52. static int CheckVersion _ANSI_ARGS_((Tcl_Interp *interp,
  53.     CONST char *string));
  54. static int ComparePkgVersions _ANSI_ARGS_((CONST char *v1, 
  55.                             CONST char *v2,
  56.     int *satPtr));
  57. static Package * FindPackage _ANSI_ARGS_((Tcl_Interp *interp,
  58.     CONST char *name));
  59. #else
  60. static int CheckVersionAndConvert(Tcl_Interp *interp,
  61.     CONST char *string, char** internal, int* stable);
  62. static int CompareVersions(CONST char *v1i, CONST char *v2i,
  63.     int *isMajorPtr);
  64. static int CheckRequirement(Tcl_Interp *interp,
  65.     CONST char *string);
  66. static int CheckAllRequirements(Tcl_Interp* interp, int reqc,
  67.     Tcl_Obj *CONST reqv[]);
  68. static int RequirementSatisfied(char *havei, CONST char *req);
  69. static int SomeRequirementSatisfied(char *havei, int reqc,
  70.     Tcl_Obj *CONST reqv[]);
  71. static void AddRequirementsToResult(Tcl_Interp* interp, int reqc,
  72.     Tcl_Obj *CONST reqv[]);
  73. static void AddRequirementsToDString(Tcl_DString* dstring,
  74.     int reqc, Tcl_Obj *CONST reqv[]);
  75. static Package * FindPackage(Tcl_Interp *interp, CONST char *name);
  76. static const char * PkgRequireCore(Tcl_Interp *interp, CONST char *name,
  77.     int reqx, Tcl_Obj *CONST reqv[],
  78.     ClientData *clientDataPtr);
  79. #endif
  80. /*
  81.  * Helper macros.
  82.  */
  83. #define DupBlock(v,s,len) 
  84.     ((v) = ckalloc(len), memcpy((v),(s),(len)))
  85. #define DupString(v,s) 
  86.     do { 
  87. unsigned local__len = (unsigned) (strlen(s) + 1); 
  88. DupBlock((v),(s),local__len); 
  89.     } while (0)
  90. /*
  91.  *----------------------------------------------------------------------
  92.  *
  93.  * Tcl_PkgProvide / Tcl_PkgProvideEx --
  94.  *
  95.  * This procedure is invoked to declare that a particular version
  96.  * of a particular package is now present in an interpreter.  There
  97.  * must not be any other version of this package already
  98.  * provided in the interpreter.
  99.  *
  100.  * Results:
  101.  * Normally returns TCL_OK;  if there is already another version
  102.  * of the package loaded then TCL_ERROR is returned and an error
  103.  * message is left in the interp's result.
  104.  *
  105.  * Side effects:
  106.  * The interpreter remembers that this package is available,
  107.  * so that no other version of the package may be provided for
  108.  * the interpreter.
  109.  *
  110.  *----------------------------------------------------------------------
  111.  */
  112. int
  113. Tcl_PkgProvide(interp, name, version)
  114.      Tcl_Interp *interp; /* Interpreter in which package is now
  115.  * available. */
  116.      CONST char *name; /* Name of package. */
  117.      CONST char *version; /* Version string for package. */
  118. {
  119.     return Tcl_PkgProvideEx(interp, name, version, (ClientData) NULL);
  120. }
  121. int
  122. Tcl_PkgProvideEx(interp, name, version, clientData)
  123.      Tcl_Interp *interp; /* Interpreter in which package is now
  124.  * available. */
  125.      CONST char *name; /* Name of package. */
  126.      CONST char *version; /* Version string for package. */
  127.      ClientData clientData;     /* clientdata for this package (normally
  128.  * used for C callback function table) */
  129. {
  130.     Package *pkgPtr;
  131. #ifdef TCL_TIP268
  132.     char* pvi;
  133.     char* vi;
  134.     int res;
  135. #endif
  136.     pkgPtr = FindPackage(interp, name);
  137.     if (pkgPtr->version == NULL) {
  138. DupString(pkgPtr->version, version);
  139. pkgPtr->clientData = clientData;
  140. return TCL_OK;
  141.     }
  142. #ifndef TCL_TIP268
  143.     if (ComparePkgVersions(pkgPtr->version, version, (int *) NULL) == 0) {
  144. #else
  145.     if (CheckVersionAndConvert (interp, pkgPtr->version, &pvi, NULL) != TCL_OK) {
  146. return TCL_ERROR;
  147.     } else if (CheckVersionAndConvert (interp, version, &vi, NULL) != TCL_OK) {
  148. ckfree(pvi);
  149. return TCL_ERROR;
  150.     }
  151.     res = CompareVersions(pvi, vi, NULL);
  152.     ckfree(pvi);
  153.     ckfree(vi);
  154.     if (res == 0) {
  155. #endif
  156. if (clientData != NULL) {
  157.     pkgPtr->clientData = clientData;
  158. }
  159. return TCL_OK;
  160.     }
  161.     Tcl_AppendResult(interp, "conflicting versions provided for package "",
  162.      name, "": ", pkgPtr->version, ", then ", version, (char *) NULL);
  163.     return TCL_ERROR;
  164. }
  165. /*
  166.  *----------------------------------------------------------------------
  167.  *
  168.  * Tcl_PkgRequire / Tcl_PkgRequireEx / Tcl_PkgRequireProc --
  169.  *
  170.  * This procedure is called by code that depends on a particular
  171.  * version of a particular package.  If the package is not already
  172.  * provided in the interpreter, this procedure invokes a Tcl script
  173.  * to provide it.  If the package is already provided, this
  174.  * procedure makes sure that the caller's needs don't conflict with
  175.  * the version that is present.
  176.  *
  177.  * Results:
  178.  * If successful, returns the version string for the currently
  179.  * provided version of the package, which may be different from
  180.  * the "version" argument.  If the caller's requirements
  181.  * cannot be met (e.g. the version requested conflicts with
  182.  * a currently provided version, or the required version cannot
  183.  * be found, or the script to provide the required version
  184.  * generates an error), NULL is returned and an error
  185.  * message is left in the interp's result.
  186.  *
  187.  * Side effects:
  188.  * The script from some previous "package ifneeded" command may
  189.  * be invoked to provide the package.
  190.  *
  191.  *----------------------------------------------------------------------
  192.  */
  193. #ifndef TCL_TIP268
  194. /*
  195.  * Empty definition for Stubs when TIP 268 is not activated.
  196.  */
  197. int
  198. Tcl_PkgRequireProc(interp,name,reqc,reqv,clientDataPtr)
  199.      Tcl_Interp *interp; /* Interpreter in which package is now
  200.  * available. */
  201.      CONST char *name; /* Name of desired package. */
  202.      int reqc;                  /* Requirements constraining the desired version. */
  203.      Tcl_Obj *CONST reqv[];     /* 0 means to use the latest version available. */
  204.      ClientData *clientDataPtr;
  205. {
  206.     return TCL_ERROR;
  207. }
  208. #endif
  209. CONST char *
  210. Tcl_PkgRequire(interp, name, version, exact)
  211.     Tcl_Interp *interp;         /* Interpreter in which package is now
  212.  * available. */
  213.      CONST char *name; /* Name of desired package. */
  214.      CONST char *version; /* Version string for desired version; NULL
  215.  * means use the latest version available. */
  216.      int exact; /* Non-zero means that only the particular
  217.  * version given is acceptable. Zero means use
  218.  * the latest compatible version. */
  219. {
  220.     return Tcl_PkgRequireEx(interp, name, version, exact, (ClientData *) NULL);
  221. }
  222. CONST char *
  223. Tcl_PkgRequireEx(interp, name, version, exact, clientDataPtr)
  224.      Tcl_Interp *interp; /* Interpreter in which package is now
  225.  * available. */
  226.      CONST char *name; /* Name of desired package. */
  227.      CONST char *version; /* Version string for desired version;
  228.  * NULL means use the latest version
  229.  * available. */
  230.      int exact; /* Non-zero means that only the particular
  231.  * version given is acceptable. Zero means
  232.  * use the latest compatible version. */
  233.      ClientData *clientDataPtr; /* Used to return the client data for this
  234.  * package. If it is NULL then the client
  235.  * data is not returned. This is unchanged
  236.  * if this call fails for any reason. */
  237. {
  238. #ifndef TCL_TIP268
  239.     Package *pkgPtr;
  240.     PkgAvail *availPtr, *bestPtr;
  241.     char *script;
  242.     int code, satisfies, result, pass;
  243.     Tcl_DString command;
  244. #else
  245.     Tcl_Obj *ov;
  246.     const char *result = NULL;
  247. #endif
  248.     /*
  249.      * If an attempt is being made to load this into a standalone executable
  250.      * on a platform where backlinking is not supported then this must be
  251.      * a shared version of Tcl (Otherwise the load would have failed).
  252.      * Detect this situation by checking that this library has been correctly
  253.      * initialised. If it has not been then return immediately as nothing will
  254.      * work.
  255.      */
  256.     
  257.     if (tclEmptyStringRep == NULL) {
  258. /*
  259.  * OK, so what's going on here?
  260.  *
  261.  * First, what are we doing?  We are performing a check on behalf of
  262.  * one particular caller, Tcl_InitStubs().  When a package is
  263.  * stub-enabled, it is statically linked to libtclstub.a, which
  264.  * contains a copy of Tcl_InitStubs().  When a stub-enabled package
  265.  * is loaded, its *_Init() function is supposed to call
  266.  * Tcl_InitStubs() before calling any other functions in the Tcl
  267.  * library.  The first Tcl function called by Tcl_InitStubs() through
  268.  * the stub table is Tcl_PkgRequireEx(), so this code right here is
  269.  * the first code that is part of the original Tcl library in the
  270.  * executable that gets executed on behalf of a newly loaded
  271.  * stub-enabled package.
  272.  *
  273.  * One easy error for the developer/builder of a stub-enabled package
  274.  * to make is to forget to define USE_TCL_STUBS when compiling the
  275.  * package.  When that happens, the package will contain symbols
  276.  * that are references to the Tcl library, rather than function
  277.  * pointers referencing the stub table.  On platforms that lack
  278.  * backlinking, those unresolved references may cause the loading
  279.  * of the package to also load a second copy of the Tcl library,
  280.  * leading to all kinds of trouble.  We would like to catch that
  281.  * error and report a useful message back to the user.  That's
  282.  * what we're doing.
  283.  *
  284.  * Second, how does this work?  If we reach this point, then the
  285.  * global variable tclEmptyStringRep has the value NULL.  Compare
  286.  * that with the definition of tclEmptyStringRep near the top of
  287.  * the file generic/tclObj.c.  It clearly should not have the value
  288.  * NULL; it should point to the char tclEmptyString.  If we see it
  289.  * having the value NULL, then somehow we are seeing a Tcl library
  290.  * that isn't completely initialized, and that's an indicator for the
  291.  * error condition described above.  (Further explanation is welcome.)
  292.  *
  293.  * Third, so what do we do about it?  This situation indicates
  294.  * the package we just loaded wasn't properly compiled to be
  295.  * stub-enabled, yet it thinks it is stub-enabled (it called
  296.  * Tcl_InitStubs()).  We want to report that the package just
  297.  * loaded is broken, so we want to place an error message in
  298.  * the interpreter result and return NULL to indicate failure
  299.  * to Tcl_InitStubs() so that it will also fail.  (Further
  300.  * explanation why we don't want to Tcl_Panic() is welcome.
  301.  * After all, two Tcl libraries can't be a good thing!)
  302.  *
  303.  * Trouble is that's going to be tricky.  We're now using a Tcl
  304.  * library that's not fully initialized.  In particular, it 
  305.  * doesn't have a proper value for tclEmptyStringRep.  The
  306.  * Tcl_Obj system heavily depends on the value of tclEmptyStringRep
  307.  * and all of Tcl depends (increasingly) on the Tcl_Obj system, we
  308.  * need to correct that flaw before making the calls to set the 
  309.  * interpreter result to the error message.  That's the only flaw
  310.  * corrected; other problems with initialization of the Tcl library
  311.  * are not remedied, so be very careful about adding any other calls
  312.  * here without checking how they behave when initialization is
  313.  * incomplete.
  314.  */
  315. tclEmptyStringRep = &tclEmptyString;
  316.         Tcl_AppendResult(interp, "Cannot load package "", name, 
  317.  "" in standalone executable: This package is not ",
  318.  "compiled with stub support", NULL);
  319.         return NULL;
  320.     }
  321. #ifdef TCL_TIP268
  322.     /* Translate between old and new API, and defer to the new function. */
  323.     if (version == NULL) {
  324. result = PkgRequireCore(interp, name, 0, NULL, clientDataPtr);
  325.     } else {
  326. if (exact && TCL_OK
  327. != CheckVersionAndConvert(interp, version, NULL, NULL)) {
  328.     return NULL;
  329. }
  330. ov = Tcl_NewStringObj(version, -1);
  331. if (exact) {
  332.     Tcl_AppendStringsToObj(ov, "-", version, NULL);
  333. }
  334. Tcl_IncrRefCount (ov);
  335. result = PkgRequireCore(interp, name, 1, &ov, clientDataPtr);
  336. Tcl_DecrRefCount (ov);
  337.     }
  338.     return result;
  339. }
  340. int
  341. Tcl_PkgRequireProc(
  342.      Tcl_Interp *interp, /* Interpreter in which package is now
  343.  * available. */
  344.      CONST char *name, /* Name of desired package. */
  345.      int reqc,                  /* Requirements constraining the desired
  346.  * version. */
  347.      Tcl_Obj *CONST reqv[],     /* 0 means to use the latest version
  348.  * available. */
  349.      ClientData *clientDataPtr)
  350. {
  351.     const char *result =
  352.     PkgRequireCore(interp, name, reqc, reqv, clientDataPtr);
  353.     if (result == NULL) {
  354. return TCL_ERROR;
  355.     }
  356.     Tcl_SetObjResult(interp, Tcl_NewStringObj(result, -1));
  357.     return TCL_OK;
  358. }
  359. static const char *
  360. PkgRequireCore(
  361.      Tcl_Interp *interp, /* Interpreter in which package is now
  362.  * available. */
  363.      CONST char *name, /* Name of desired package. */
  364.      int reqc,                  /* Requirements constraining the desired
  365.  * version. */
  366.      Tcl_Obj *CONST reqv[],     /* 0 means to use the latest version
  367.  * available. */
  368.      ClientData *clientDataPtr)
  369. {
  370.     Interp *iPtr = (Interp *) interp;
  371.     Package *pkgPtr;
  372.     PkgAvail *availPtr, *bestPtr, *bestStablePtr;
  373.     char *availVersion, *bestVersion; /* Internal rep. of versions */
  374.     int availStable;
  375.     char *script;
  376.     int code, satisfies, pass;
  377.     Tcl_DString command;
  378.     char* pkgVersionI;
  379. #endif
  380.     /*
  381.      * It can take up to three passes to find the package: one pass to run the
  382.      * "package unknown" script, one to run the "package ifneeded" script for
  383.      * a specific version, and a final pass to lookup the package loaded by
  384.      * the "package ifneeded" script.
  385.      */
  386.     for (pass = 1; ; pass++) {
  387. pkgPtr = FindPackage(interp, name);
  388. if (pkgPtr->version != NULL) {
  389.     break;
  390. }
  391. /* 
  392.  * Check whether we're already attempting to load some version
  393.  * of this package (circular dependency detection).
  394.  */
  395. if (pkgPtr->clientData != NULL) {
  396.     Tcl_AppendResult(interp, "circular package dependency: ",
  397.     "attempt to provide ", name, " ",
  398.     (char *)(pkgPtr->clientData), " requires ", name, NULL);
  399. #ifndef TCL_TIP268
  400.     if (version != NULL) {
  401. Tcl_AppendResult(interp, " ", version, NULL);
  402.     }
  403. #else
  404.     AddRequirementsToResult (interp, reqc, reqv);
  405. #endif
  406.     return NULL;
  407. }
  408. /*
  409.  * The package isn't yet present. Search the list of available
  410.  * versions and invoke the script for the best available version.
  411.  *
  412.  * For TIP 268 we are actually locating the best, and the best stable
  413.  * version.  One of them is then chosen based on the selection mode.
  414.  */
  415. #ifndef TCL_TIP268    
  416. bestPtr = NULL;
  417. for (availPtr = pkgPtr->availPtr; availPtr != NULL;
  418. availPtr = availPtr->nextPtr) {
  419.     if ((bestPtr != NULL) && (ComparePkgVersions(availPtr->version,
  420.     bestPtr->version, (int *) NULL) <= 0)) {
  421. #else
  422. bestPtr = NULL;
  423. bestStablePtr = NULL;
  424. bestVersion = NULL;
  425. for (availPtr = pkgPtr->availPtr; availPtr != NULL;
  426. availPtr = availPtr->nextPtr) {
  427.     if (CheckVersionAndConvert(interp, availPtr->version,
  428.     &availVersion, &availStable) != TCL_OK) {
  429. /* The provided version number has invalid syntax. This
  430.  * should not happen. This should have been caught by the
  431.  * 'package ifneeded' registering the package.
  432.  */
  433. #endif
  434. continue;
  435.     }
  436. #ifndef TCL_TIP268
  437.     if (version != NULL) {
  438. result = ComparePkgVersions(availPtr->version, version,
  439. &satisfies);
  440. if ((result != 0) && exact) {
  441. #else
  442.     if (bestPtr != NULL) {
  443. int res = CompareVersions (availVersion, bestVersion, NULL);
  444. /* Note: Use internal reps! */
  445. if (res <= 0) {
  446.     /*
  447.      * The version of the package sought is not as good as the
  448.      * currently selected version. Ignore it.
  449.      */
  450.     ckfree(availVersion);
  451.     availVersion = NULL;
  452. #endif
  453.     continue;
  454. }
  455. #ifdef TCL_TIP268
  456.     }
  457.     /* We have found a version which is better than our max. */
  458.     if (reqc > 0) {
  459. /* Check satisfaction of requirements. */
  460. satisfies = SomeRequirementSatisfied(availVersion, reqc, reqv);
  461. #endif
  462. if (!satisfies) {
  463. #ifdef TCL_TIP268
  464.     ckfree(availVersion);
  465.     availVersion = NULL;
  466. #endif
  467.     continue;
  468. }
  469.     }
  470.     bestPtr = availPtr;
  471. #ifdef TCL_TIP268
  472.     if (bestVersion != NULL) {
  473. ckfree(bestVersion);
  474.     }
  475.     bestVersion = availVersion;
  476.     availVersion = NULL;
  477.     /*
  478.      * If this new best version is stable then it also has to be
  479.      * better than the max stable version found so far.
  480.      */
  481.     if (availStable) {
  482. bestStablePtr = availPtr;
  483.     }
  484. }
  485. if (bestVersion != NULL) {
  486.    ckfree(bestVersion);
  487. }
  488. /* Now choose a version among the two best. For 'latest' we simply
  489.  * take (actually keep) the best. For 'stable' we take the best
  490.  * stable, if there is any, or the best if there is nothing stable.
  491.  */
  492. if ((iPtr->packagePrefer == PKG_PREFER_STABLE)
  493. && (bestStablePtr != NULL)) {
  494.     bestPtr = bestStablePtr;
  495. #endif
  496. }
  497. if (bestPtr != NULL) {
  498.     /*
  499.      * We found an ifneeded script for the package. Be careful while
  500.      * executing it: this could cause reentrancy, so (a) protect the
  501.      * script itself from deletion and (b) don't assume that bestPtr
  502.      * will still exist when the script completes.
  503.      */
  504.     CONST char *versionToProvide = bestPtr->version;
  505.     script = bestPtr->script;
  506.     pkgPtr->clientData = (ClientData) versionToProvide;
  507.     Tcl_Preserve((ClientData) script);
  508.     Tcl_Preserve((ClientData) versionToProvide);
  509.     code = Tcl_EvalEx(interp, script, -1, TCL_EVAL_GLOBAL);
  510.     Tcl_Release((ClientData) script);
  511.     pkgPtr = FindPackage(interp, name);
  512.     if (code == TCL_OK) {
  513. #ifdef TCL_TIP268
  514. Tcl_ResetResult(interp);
  515. #endif
  516. if (pkgPtr->version == NULL) {
  517. #ifndef TCL_TIP268
  518.     Tcl_ResetResult(interp);
  519. #endif
  520.     code = TCL_ERROR;
  521.     Tcl_AppendResult(interp, "attempt to provide package ",
  522.     name, " ", versionToProvide,
  523.     " failed: no version of package ", name,
  524.     " provided", NULL);
  525. #ifndef TCL_TIP268
  526. } else if (0 != ComparePkgVersions(
  527. pkgPtr->version, versionToProvide, NULL)) {
  528.     /* At this point, it is clear that a prior
  529.      * [package ifneeded] command lied to us.  It said
  530.      * that to get a particular version of a particular
  531.      * package, we needed to evaluate a particular script.
  532.      * However, we evaluated that script and got a different
  533.      * version than we were told.  This is an error, and we
  534.      * ought to report it.
  535.      *
  536.      * However, we've been letting this type of error slide
  537.      * for a long time, and as a result, a lot of packages
  538.      * suffer from them.
  539.      *
  540.      * It's a bit too harsh to make a large number of
  541.      * existing packages start failing by releasing a
  542.      * new patch release, so we forgive this type of error
  543.      * for the rest of the Tcl 8.4 series.
  544.      *
  545.      * We considered reporting a warning, but in practice
  546.      * even that appears too harsh a change for a patch release.
  547.      *
  548.      * We limit the error reporting to only
  549.      * the situation where a broken ifneeded script leads
  550.      * to a failure to satisfy the requirement.
  551.      */
  552.     if (version) {
  553. result = ComparePkgVersions(
  554. pkgPtr->version, version, &satisfies);
  555. if (result && (exact || !satisfies)) {
  556.     Tcl_ResetResult(interp);
  557.     code = TCL_ERROR;
  558.     Tcl_AppendResult(interp,
  559.     "attempt to provide package ", name, " ",
  560.     versionToProvide, " failed: package ",
  561.     name, " ", pkgPtr->version,
  562.     " provided instead", NULL);
  563. #else
  564. } else {
  565.     char *pvi, *vi;
  566.     int res;
  567.     if (CheckVersionAndConvert(interp, pkgPtr->version, &pvi,
  568.     NULL) != TCL_OK) {
  569. code = TCL_ERROR;
  570.     } else if (CheckVersionAndConvert(interp,
  571.     versionToProvide, &vi, NULL) != TCL_OK) {
  572. ckfree(pvi);
  573. code = TCL_ERROR;
  574.     } else {
  575. res = CompareVersions(pvi, vi, NULL);
  576. ckfree(vi);
  577. if (res != 0) {
  578.     /* At this point, it is clear that a prior
  579.      * [package ifneeded] command lied to us.  It said
  580.      * that to get a particular version of a particular
  581.      * package, we needed to evaluate a particular
  582.      * script.  However, we evaluated that script and
  583.      * got a different version than we were told.
  584.      * This is an error, and we ought to report it.
  585.      *
  586.      * However, we've been letting this type of error
  587.      * slide for a long time, and as a result, a lot
  588.      * of packages suffer from them.
  589.      *
  590.      * It's a bit too harsh to make a large number of
  591.      * existing packages start failing by releasing a
  592.      * new patch release, so we forgive this type of
  593.      * error for the rest of the Tcl 8.4 series.
  594.      *
  595.      * We considered reporting a warning, but in
  596.      * practice even that appears too harsh a change
  597.      * for a patch release.
  598.      *
  599.      * We limit the error reporting to only the
  600.      * situation where a broken ifneeded script leads
  601.      * to a failure to satisfy the requirement.
  602.      */
  603.     if (reqc > 0) {
  604.         satisfies = SomeRequirementSatisfied(pvi,
  605. reqc, reqv);
  606. if (!satisfies) {
  607.     code = TCL_ERROR;
  608.     Tcl_AppendResult(interp,
  609.     "attempt to provide package ",
  610.     name, " ", versionToProvide,
  611.     " failed: package ", name, " ",
  612.     pkgPtr->version,
  613.     " provided instead", NULL);
  614. }
  615.     }
  616. #endif
  617. }
  618. #ifdef TCL_TIP268
  619. ckfree(pvi);
  620. #endif
  621.     }
  622. }
  623.     } else if (code != TCL_ERROR) {
  624. Tcl_Obj *codePtr = Tcl_NewIntObj(code);
  625. Tcl_ResetResult(interp);
  626. Tcl_AppendResult(interp, "attempt to provide package ",
  627. name, " ", versionToProvide, " failed: ",
  628. "bad return code: ", Tcl_GetString(codePtr), NULL);
  629. Tcl_DecrRefCount(codePtr);
  630. code = TCL_ERROR;
  631.     }
  632.     Tcl_Release((ClientData) versionToProvide);
  633.     if (code != TCL_OK) {
  634. /*
  635.  * Take a non-TCL_OK code from the script as an indication the
  636.  * package wasn't loaded properly, so the package system
  637.  * should not remember an improper load.
  638.  *
  639.  * This is consistent with our returning NULL.  If we're not
  640.  * willing to tell our caller we got a particular version, we
  641.  * shouldn't store that version for telling future callers
  642.  * either.
  643.  */
  644. Tcl_AddErrorInfo(interp, "n    ("package ifneeded" script)");
  645. if (pkgPtr->version != NULL) {
  646.     ckfree(pkgPtr->version);
  647.     pkgPtr->version = NULL;
  648. }
  649. pkgPtr->clientData = NULL;
  650. return NULL;
  651.     }
  652.     break;
  653. }
  654. /*
  655.  * The package is not in the database. If there is a "package unknown"
  656.  * command, invoke it (but only on the first pass; after that, we
  657.  * should not get here in the first place).
  658.  */
  659. if (pass > 1) {
  660.     break;
  661. }
  662. script = ((Interp *) interp)->packageUnknown;
  663. if (script != NULL) {
  664.     Tcl_DStringInit(&command);
  665.     Tcl_DStringAppend(&command, script, -1);
  666.     Tcl_DStringAppendElement(&command, name);
  667. #ifndef TCL_TIP268
  668.     Tcl_DStringAppend(&command, " ", 1);
  669.     Tcl_DStringAppend(&command, (version != NULL) ? version : "{}",
  670.     -1);
  671.     if (exact) {
  672. Tcl_DStringAppend(&command, " -exact", 7);
  673.     }
  674. #else
  675.     AddRequirementsToDString(&command, reqc, reqv);
  676. #endif
  677.     code = Tcl_EvalEx(interp, Tcl_DStringValue(&command),
  678.       Tcl_DStringLength(&command), TCL_EVAL_GLOBAL);
  679.     Tcl_DStringFree(&command);
  680.     if ((code != TCL_OK) && (code != TCL_ERROR)) {
  681. Tcl_Obj *codePtr = Tcl_NewIntObj(code);
  682. Tcl_ResetResult(interp);
  683. Tcl_AppendResult(interp, "bad return code: ",
  684. Tcl_GetString(codePtr), NULL);
  685. Tcl_DecrRefCount(codePtr);
  686. code = TCL_ERROR;
  687.     }
  688.     if (code == TCL_ERROR) {
  689. Tcl_AddErrorInfo(interp, "n    ("package unknown" script)");
  690. return NULL;
  691.     }
  692.     Tcl_ResetResult(interp);
  693. }
  694.     }
  695.     if (pkgPtr->version == NULL) {
  696. Tcl_AppendResult(interp, "can't find package ", name, (char *) NULL);
  697. #ifndef TCL_TIP268
  698. if (version != NULL) {
  699.     Tcl_AppendResult(interp, " ", version, (char *) NULL);
  700. }
  701. #else
  702. AddRequirementsToResult(interp, reqc, reqv);
  703. #endif
  704. return NULL;
  705.     }
  706.     /*
  707.      * At this point we know that the package is present. Make sure that the
  708.      * provided version meets the current requirements.
  709.      */
  710. #ifndef TCL_TIP268
  711.     if (version == NULL) {
  712.         if (clientDataPtr) {
  713.     *clientDataPtr = pkgPtr->clientData;
  714. }
  715. return pkgPtr->version;
  716. #else
  717.     if (reqc == 0) {
  718. satisfies = 1;
  719.     } else {
  720. CheckVersionAndConvert(interp, pkgPtr->version, &pkgVersionI, NULL);
  721. satisfies = SomeRequirementSatisfied(pkgVersionI, reqc, reqv);
  722. ckfree(pkgVersionI);
  723. #endif
  724.     }
  725. #ifndef TCL_TIP268
  726.     result = ComparePkgVersions(pkgPtr->version, version, &satisfies);
  727.     if ((satisfies && !exact) || (result == 0)) {
  728. #else
  729.     if (satisfies) {
  730. #endif
  731. if (clientDataPtr) {
  732.     *clientDataPtr = pkgPtr->clientData;
  733. }
  734. return pkgPtr->version;
  735.     }
  736.     Tcl_AppendResult(interp, "version conflict for package "", name,
  737.     "": have ", pkgPtr->version,
  738. #ifndef TCL_TIP268
  739.     ", need ", version, (char *) NULL);
  740. #else
  741.     ", need", (char*) NULL);
  742.     AddRequirementsToResult (interp, reqc, reqv);
  743. #endif
  744.     return NULL;
  745. }
  746. /*
  747.  *----------------------------------------------------------------------
  748.  *
  749.  * Tcl_PkgPresent / Tcl_PkgPresentEx --
  750.  *
  751.  * Checks to see whether the specified package is present. If it
  752.  * is not then no additional action is taken.
  753.  *
  754.  * Results:
  755.  * If successful, returns the version string for the currently
  756.  * provided version of the package, which may be different from
  757.  * the "version" argument.  If the caller's requirements
  758.  * cannot be met (e.g. the version requested conflicts with
  759.  * a currently provided version), NULL is returned and an error
  760.  * message is left in interp->result.
  761.  *
  762.  * Side effects:
  763.  * None.
  764.  *
  765.  *----------------------------------------------------------------------
  766.  */
  767. CONST char *
  768. Tcl_PkgPresent(interp, name, version, exact)
  769.      Tcl_Interp *interp; /* Interpreter in which package is now
  770.  * available. */
  771.      CONST char *name; /* Name of desired package. */
  772.      CONST char *version; /* Version string for desired version;
  773.  * NULL means use the latest version
  774.  * available. */
  775.      int exact; /* Non-zero means that only the particular
  776.  * version given is acceptable. Zero means
  777.  * use the latest compatible version. */
  778. {
  779.     return Tcl_PkgPresentEx(interp, name, version, exact, (ClientData *) NULL);
  780. }
  781. CONST char *
  782. Tcl_PkgPresentEx(interp, name, version, exact, clientDataPtr)
  783.      Tcl_Interp *interp; /* Interpreter in which package is now
  784.  * available. */
  785.      CONST char *name; /* Name of desired package. */
  786.      CONST char *version; /* Version string for desired version;
  787.  * NULL means use the latest version
  788.  * available. */
  789.      int exact; /* Non-zero means that only the particular
  790.  * version given is acceptable. Zero means
  791.  * use the latest compatible version. */
  792.      ClientData *clientDataPtr; /* Used to return the client data for this
  793.  * package. If it is NULL then the client
  794.  * data is not returned. This is unchanged
  795.  * if this call fails for any reason. */
  796. {
  797.     Interp *iPtr = (Interp *) interp;
  798.     Tcl_HashEntry *hPtr;
  799.     Package *pkgPtr;
  800.     hPtr = Tcl_FindHashEntry(&iPtr->packageTable, name);
  801.     if (hPtr) {
  802. pkgPtr = (Package *) Tcl_GetHashValue(hPtr);
  803. if (pkgPtr->version != NULL) {
  804.     
  805.     /*
  806.      * At this point we know that the package is present.  Make sure
  807.      * that the provided version meets the current requirement by
  808.      * calling Tcl_PkgRequireEx() to check for us.
  809.      */
  810.     return Tcl_PkgRequireEx(interp, name, version, exact,
  811.     clientDataPtr);
  812. }
  813.     }
  814.     if (version != NULL) {
  815. Tcl_AppendResult(interp, "package ", name, " ", version,
  816. " is not present", NULL);
  817.     } else {
  818. Tcl_AppendResult(interp, "package ", name, " is not present", NULL);
  819.     }
  820.     return NULL;
  821. }
  822. /*
  823.  *----------------------------------------------------------------------
  824.  *
  825.  * Tcl_PackageObjCmd --
  826.  *
  827.  * This procedure is invoked to process the "package" Tcl command.
  828.  * See the user documentation for details on what it does.
  829.  *
  830.  * Results:
  831.  * A standard Tcl result.
  832.  *
  833.  * Side effects:
  834.  * See the user documentation.
  835.  *
  836.  *----------------------------------------------------------------------
  837.  */
  838. /* ARGSUSED */
  839. int
  840. Tcl_PackageObjCmd(dummy, interp, objc, objv)
  841.      ClientData dummy; /* Not used. */
  842.      Tcl_Interp *interp; /* Current interpreter. */
  843.      int objc; /* Number of arguments. */
  844.      Tcl_Obj *CONST objv[]; /* Argument objects. */
  845. {
  846.     static CONST char *pkgOptions[] = {
  847. "forget", "ifneeded", "names",
  848. #ifdef TCL_TIP268
  849. "prefer",
  850. #endif
  851. "present", "provide", "require", "unknown", "vcompare",
  852. "versions", "vsatisfies", (char *) NULL
  853.     };
  854.     enum pkgOptions {
  855. PKG_FORGET, PKG_IFNEEDED, PKG_NAMES,
  856. #ifdef TCL_TIP268
  857. PKG_PREFER,
  858. #endif
  859. PKG_PRESENT, PKG_PROVIDE, PKG_REQUIRE, PKG_UNKNOWN, PKG_VCOMPARE,
  860. PKG_VERSIONS, PKG_VSATISFIES
  861.     };
  862.     Interp *iPtr = (Interp *) interp;
  863.     int optionIndex, exact, i, satisfies;
  864.     PkgAvail *availPtr, *prevPtr;
  865.     Package *pkgPtr;
  866.     Tcl_HashEntry *hPtr;
  867.     Tcl_HashSearch search;
  868.     Tcl_HashTable *tablePtr;
  869.     CONST char *version;
  870.     char *argv2, *argv3, *argv4;
  871. #ifdef TCL_TIP268
  872.     char* iva = NULL;
  873.     char* ivb = NULL;
  874. #endif
  875.     if (objc < 2) {
  876.         Tcl_WrongNumArgs(interp, 1, objv, "option ?arg arg ...?");
  877. return TCL_ERROR;
  878.     }
  879.     if (Tcl_GetIndexFromObj(interp, objv[1], pkgOptions, "option", 0,
  880.     &optionIndex) != TCL_OK) {
  881. return TCL_ERROR;
  882.     }
  883.     switch ((enum pkgOptions) optionIndex) {
  884.     case PKG_FORGET: {
  885. char *keyString;
  886. for (i = 2; i < objc; i++) {
  887.     keyString = Tcl_GetString(objv[i]);
  888.     hPtr = Tcl_FindHashEntry(&iPtr->packageTable, keyString);
  889.     if (hPtr == NULL) {
  890. continue;
  891.     }
  892.     pkgPtr = (Package *) Tcl_GetHashValue(hPtr);
  893.     Tcl_DeleteHashEntry(hPtr);
  894.     if (pkgPtr->version != NULL) {
  895. ckfree(pkgPtr->version);
  896.     }
  897.     while (pkgPtr->availPtr != NULL) {
  898. availPtr = pkgPtr->availPtr;
  899. pkgPtr->availPtr = availPtr->nextPtr;
  900. Tcl_EventuallyFree((ClientData)availPtr->version, TCL_DYNAMIC);
  901. Tcl_EventuallyFree((ClientData)availPtr->script, TCL_DYNAMIC);
  902. ckfree((char *) availPtr);
  903.     }
  904.     ckfree((char *) pkgPtr);
  905. }
  906. break;
  907.     }
  908.     case PKG_IFNEEDED: {
  909. int length;
  910. #ifdef TCL_TIP268
  911. int res;
  912. char *argv3i, *avi;
  913. #endif
  914. if ((objc != 4) && (objc != 5)) {
  915.     Tcl_WrongNumArgs(interp, 2, objv, "package version ?script?");
  916.     return TCL_ERROR;
  917. }
  918. argv3 = Tcl_GetString(objv[3]);
  919. #ifdef TCL_TIP268
  920. if (CheckVersionAndConvert(interp, argv3, &argv3i, NULL) != TCL_OK) {
  921. #else
  922. if (CheckVersion(interp, argv3) != TCL_OK) {
  923. #endif
  924.     return TCL_ERROR;
  925. }
  926. argv2 = Tcl_GetString(objv[2]);
  927. if (objc == 4) {
  928.     hPtr = Tcl_FindHashEntry(&iPtr->packageTable, argv2);
  929.     if (hPtr == NULL) {
  930. #ifdef TCL_TIP268
  931. ckfree(argv3i);
  932. #endif
  933. return TCL_OK;
  934.     }
  935.     pkgPtr = (Package *) Tcl_GetHashValue(hPtr);
  936. } else {
  937.     pkgPtr = FindPackage(interp, argv2);
  938. }
  939. argv3 = Tcl_GetStringFromObj(objv[3], &length);
  940. for (availPtr = pkgPtr->availPtr, prevPtr = NULL; availPtr != NULL;
  941.      prevPtr = availPtr, availPtr = availPtr->nextPtr) {
  942. #ifdef TCL_TIP268
  943.     if (CheckVersionAndConvert(interp, availPtr->version, &avi,
  944.     NULL) != TCL_OK) {
  945. ckfree(argv3i);
  946. return TCL_ERROR;
  947.     }
  948.     res = CompareVersions(avi, argv3i, NULL);
  949.     ckfree(avi);
  950.     if (res == 0){
  951. #else
  952.     if (ComparePkgVersions(availPtr->version, argv3, (int *) NULL)
  953.     == 0) {
  954. #endif
  955. if (objc == 4) {
  956. #ifdef TCL_TIP268
  957.     ckfree(argv3i);
  958. #endif
  959.     Tcl_SetResult(interp, availPtr->script, TCL_VOLATILE);
  960.     return TCL_OK;
  961. }
  962. Tcl_EventuallyFree((ClientData)availPtr->script, TCL_DYNAMIC);
  963. break;
  964.     }
  965. }
  966. #ifdef TCL_TIP268
  967. ckfree(argv3i);
  968. #endif
  969. if (objc == 4) {
  970.     return TCL_OK;
  971. }
  972. if (availPtr == NULL) {
  973.     availPtr = (PkgAvail *) ckalloc(sizeof(PkgAvail));
  974.     DupBlock(availPtr->version, argv3, (unsigned) length + 1);
  975.     if (prevPtr == NULL) {
  976. availPtr->nextPtr = pkgPtr->availPtr;
  977. pkgPtr->availPtr = availPtr;
  978.     } else {
  979. availPtr->nextPtr = prevPtr->nextPtr;
  980. prevPtr->nextPtr = availPtr;
  981.     }
  982. }
  983. argv4 = Tcl_GetStringFromObj(objv[4], &length);
  984. DupBlock(availPtr->script, argv4, (unsigned) length + 1);
  985. break;
  986.     }
  987.     case PKG_NAMES: {
  988. if (objc != 2) {
  989.     Tcl_WrongNumArgs(interp, 2, objv, NULL);
  990.     return TCL_ERROR;
  991. }
  992. tablePtr = &iPtr->packageTable;
  993. for (hPtr = Tcl_FirstHashEntry(tablePtr, &search); hPtr != NULL;
  994.      hPtr = Tcl_NextHashEntry(&search)) {
  995.     pkgPtr = (Package *) Tcl_GetHashValue(hPtr);
  996.     if ((pkgPtr->version != NULL) || (pkgPtr->availPtr != NULL)) {
  997. Tcl_AppendElement(interp, Tcl_GetHashKey(tablePtr, hPtr));
  998.     }
  999. }
  1000. break;
  1001.     }
  1002.     case PKG_PRESENT: {
  1003. const char *name;
  1004. if (objc < 3) {
  1005.     goto require;
  1006. }
  1007. argv2 = Tcl_GetString(objv[2]);
  1008. if ((argv2[0] == '-') && (strcmp(argv2, "-exact") == 0)) {
  1009.     if (objc != 5) {
  1010. goto requireSyntax;
  1011.     }
  1012.     exact = 1;
  1013.     name = TclGetString(objv[3]);
  1014. } else {
  1015.     exact = 0;
  1016.     name = argv2;
  1017. }
  1018. hPtr = Tcl_FindHashEntry(&iPtr->packageTable, name);
  1019. if (hPtr != NULL) {
  1020.     pkgPtr = Tcl_GetHashValue(hPtr);
  1021.     if (pkgPtr->version != NULL) {
  1022. goto require;
  1023.     }
  1024. }
  1025. #ifndef TCL_TIP268
  1026. version = NULL;
  1027. if (objc == (4 + exact)) {
  1028.     version =  Tcl_GetString(objv[3 + exact]);
  1029.     if (CheckVersion(interp, version) != TCL_OK) {
  1030. return TCL_ERROR;
  1031.     }
  1032. } else if ((objc != 3) || exact) {
  1033.     goto requireSyntax;
  1034. }
  1035. #else
  1036. version = NULL;
  1037. if (exact) {
  1038.     version = Tcl_GetString(objv[4]);
  1039.     if (CheckVersionAndConvert(interp, version, NULL, NULL) != TCL_OK) {
  1040. return TCL_ERROR;
  1041.     }
  1042. } else {
  1043.     if (CheckAllRequirements(interp, objc-3, objv+3) != TCL_OK) {
  1044. return TCL_ERROR;
  1045.     } 
  1046.     if ((objc > 3) && (CheckVersionAndConvert(interp,
  1047.     TclGetString(objv[3]), NULL, NULL) == TCL_OK)) {
  1048. version = TclGetString(objv[3]);
  1049.     }
  1050. }
  1051. #endif
  1052. Tcl_PkgPresent(interp, name, version, exact);
  1053. return TCL_ERROR;
  1054. break;
  1055.     }
  1056.     case PKG_PROVIDE: {
  1057. if ((objc != 3) && (objc != 4)) {
  1058.     Tcl_WrongNumArgs(interp, 2, objv, "package ?version?");
  1059.     return TCL_ERROR;
  1060. }
  1061. argv2 = Tcl_GetString(objv[2]);
  1062. if (objc == 3) {
  1063.     hPtr = Tcl_FindHashEntry(&iPtr->packageTable, argv2);
  1064.     if (hPtr != NULL) {
  1065. pkgPtr = (Package *) Tcl_GetHashValue(hPtr);
  1066. if (pkgPtr->version != NULL) {
  1067.     Tcl_SetResult(interp, pkgPtr->version, TCL_VOLATILE);
  1068. }
  1069.     }
  1070.     return TCL_OK;
  1071. }
  1072. argv3 = Tcl_GetString(objv[3]);
  1073. #ifndef TCL_TIP268
  1074. if (CheckVersion(interp, argv3) != TCL_OK) {
  1075. #else
  1076. if (CheckVersionAndConvert(interp, argv3, NULL, NULL) != TCL_OK) {
  1077. #endif
  1078.     return TCL_ERROR;
  1079. }
  1080. return Tcl_PkgProvide(interp, argv2, argv3);
  1081.     }
  1082.     case PKG_REQUIRE: {
  1083.     require:
  1084. if (objc < 3) {
  1085. requireSyntax:
  1086. #ifndef TCL_TIP268
  1087.     Tcl_WrongNumArgs(interp, 2, objv, "?-exact? package ?version?");
  1088. #else
  1089.     Tcl_WrongNumArgs(interp, 2, objv,
  1090.     "?-exact? package ?requirement...?");
  1091. #endif
  1092.     return TCL_ERROR;
  1093. }
  1094. #ifndef TCL_TIP268
  1095. argv2 = Tcl_GetString(objv[2]);
  1096. if ((argv2[0] == '-') && (strcmp(argv2, "-exact") == 0)) {
  1097.     exact = 1;
  1098. } else {
  1099.     exact = 0;
  1100. }
  1101. version = NULL;
  1102. if (objc == (4 + exact)) {
  1103.     version =  Tcl_GetString(objv[3 + exact]);
  1104.     if (CheckVersion(interp, version) != TCL_OK) {
  1105. return TCL_ERROR;
  1106.     }
  1107. } else if ((objc != 3) || exact) {
  1108.     goto requireSyntax;
  1109. }
  1110. if (exact) {
  1111.     argv3 =  Tcl_GetString(objv[3]);
  1112.     version = Tcl_PkgRequire(interp, argv3, version, exact);
  1113. } else {
  1114.     version = Tcl_PkgRequire(interp, argv2, version, exact);
  1115. }
  1116. if (version == NULL) {
  1117.     return TCL_ERROR;
  1118. }
  1119. Tcl_SetObjResult( interp, Tcl_NewStringObj( version, -1 ) );
  1120. #else
  1121. version = NULL;
  1122. argv2 = Tcl_GetString(objv[2]);
  1123. if ((argv2[0] == '-') && (strcmp(argv2, "-exact") == 0)) {
  1124.     Tcl_Obj* ov;
  1125.     int res;
  1126.     if (objc != 5) {
  1127. goto requireSyntax;
  1128.     }
  1129.     version = Tcl_GetString(objv[4]);
  1130.     if (CheckVersionAndConvert(interp, version, NULL, NULL) != TCL_OK) {
  1131. return TCL_ERROR;
  1132.     }
  1133.     /* Create a new-style requirement for the exact version. */
  1134.     ov = Tcl_NewStringObj(version, -1);
  1135.     Tcl_AppendStringsToObj(ov, "-", version, NULL);
  1136.     version = NULL;
  1137.     argv3   = Tcl_GetString(objv[3]);
  1138.     Tcl_IncrRefCount (ov);
  1139.     res = Tcl_PkgRequireProc(interp, argv3, 1, &ov, NULL);
  1140.     Tcl_DecrRefCount (ov);
  1141.     return res;
  1142. } else {
  1143.     if (CheckAllRequirements (interp, objc-3, objv+3) != TCL_OK) {
  1144. return TCL_ERROR;
  1145.     }
  1146.     return Tcl_PkgRequireProc(interp, argv2, objc-3, objv+3, NULL);
  1147. }
  1148. #endif
  1149. break;
  1150.     }
  1151.     case PKG_UNKNOWN: {
  1152. int length;
  1153. if (objc == 2) {
  1154.     if (iPtr->packageUnknown != NULL) {
  1155. Tcl_SetResult(interp, iPtr->packageUnknown, TCL_VOLATILE);
  1156.     }
  1157. } else if (objc == 3) {
  1158.     if (iPtr->packageUnknown != NULL) {
  1159. ckfree(iPtr->packageUnknown);
  1160.     }
  1161.     argv2 = Tcl_GetStringFromObj(objv[2], &length);
  1162.     if (argv2[0] == 0) {
  1163. iPtr->packageUnknown = NULL;
  1164.     } else {
  1165. DupBlock(iPtr->packageUnknown, argv2, (unsigned) length + 1);
  1166.     }
  1167. } else {
  1168.     Tcl_WrongNumArgs(interp, 2, objv, "?command?");
  1169.     return TCL_ERROR;
  1170. }
  1171. break;
  1172.     }
  1173. #ifdef TCL_TIP268
  1174.     case PKG_PREFER: {
  1175. /* See tclInt.h for the enum, just before Interp */
  1176. static CONST char *pkgPreferOptions[] = {
  1177.     "latest", "stable", NULL
  1178. };
  1179. if (objc > 3) {
  1180.     Tcl_WrongNumArgs(interp, 2, objv, "?latest|stable?");
  1181.     return TCL_ERROR;
  1182. } else if (objc == 3) {
  1183.     /* Set value. */
  1184.     int new;
  1185.     if (Tcl_GetIndexFromObj(interp, objv[2], pkgPreferOptions,
  1186.     "preference", 0, &new) != TCL_OK) {
  1187. return TCL_ERROR;
  1188.     }
  1189.     if (new < iPtr->packagePrefer) {
  1190. iPtr->packagePrefer = new;
  1191.     }
  1192. }
  1193. /* Always return current value. */
  1194. Tcl_SetObjResult(interp,
  1195. Tcl_NewStringObj(pkgPreferOptions[iPtr->packagePrefer], -1));
  1196. break;
  1197.     }
  1198. #endif
  1199.     case PKG_VCOMPARE: {
  1200. if (objc != 4) {
  1201.     Tcl_WrongNumArgs(interp, 2, objv, "version1 version2");
  1202.     return TCL_ERROR;
  1203. }
  1204. argv3 = Tcl_GetString(objv[3]);
  1205. argv2 = Tcl_GetString(objv[2]);
  1206. #ifndef TCL_TIP268
  1207. if ((CheckVersion(interp, argv2) != TCL_OK)
  1208. || (CheckVersion(interp, argv3) != TCL_OK)) {
  1209.     return TCL_ERROR;
  1210. }
  1211. Tcl_SetObjResult(interp, Tcl_NewIntObj(
  1212. ComparePkgVersions(argv2, argv3, (int *) NULL)));
  1213. #else
  1214. if ((CheckVersionAndConvert (interp, argv2, &iva, NULL) != TCL_OK)
  1215. || (CheckVersionAndConvert (interp, argv3, &ivb, NULL)
  1216. != TCL_OK)) {
  1217.     if (iva != NULL) {
  1218. ckfree(iva);
  1219.     }
  1220.     /* ivb cannot be set in this branch */
  1221.     return TCL_ERROR;
  1222. }
  1223. /* Comparison is done on the internal representation */
  1224. Tcl_SetObjResult(interp,
  1225. Tcl_NewIntObj(CompareVersions(iva, ivb, NULL)));
  1226. ckfree(iva);
  1227. ckfree(ivb);
  1228. #endif
  1229. break;
  1230.     }
  1231.     case PKG_VERSIONS: {
  1232. if (objc != 3) {
  1233.     Tcl_WrongNumArgs(interp, 2, objv, "package");
  1234.     return TCL_ERROR;
  1235. }
  1236. argv2 = Tcl_GetString(objv[2]);
  1237. hPtr = Tcl_FindHashEntry(&iPtr->packageTable, argv2);
  1238. if (hPtr != NULL) {
  1239.     pkgPtr = (Package *) Tcl_GetHashValue(hPtr);
  1240.     for (availPtr = pkgPtr->availPtr; availPtr != NULL;
  1241.  availPtr = availPtr->nextPtr) {
  1242. Tcl_AppendElement(interp, availPtr->version);
  1243.     }
  1244. }
  1245. break;
  1246.     }
  1247.     case PKG_VSATISFIES: {
  1248. #ifdef TCL_TIP268
  1249. char* argv2i = NULL;
  1250. if (objc < 4) {
  1251.     Tcl_WrongNumArgs(interp, 2, objv,
  1252.     "version requirement requirement...");
  1253.     return TCL_ERROR;
  1254. }
  1255. argv2 = Tcl_GetString(objv[2]);
  1256. if ((CheckVersionAndConvert(interp, argv2, &argv2i, NULL) != TCL_OK)) {
  1257.     return TCL_ERROR;
  1258. } else if (CheckAllRequirements(interp, objc-3, objv+3) != TCL_OK) {
  1259.     ckfree(argv2i);
  1260.     return TCL_ERROR;
  1261. }
  1262. satisfies = SomeRequirementSatisfied(argv2i, objc-3, objv+3);
  1263. ckfree (argv2i);
  1264. #else
  1265. if (objc != 4) {
  1266.     Tcl_WrongNumArgs(interp, 2, objv, "version1 version2");
  1267.     return TCL_ERROR;
  1268. }
  1269. argv3 = Tcl_GetString(objv[3]);
  1270. argv2 = Tcl_GetString(objv[2]);
  1271. if ((CheckVersion(interp, argv2) != TCL_OK)
  1272. || (CheckVersion(interp, argv3) != TCL_OK)) {
  1273.     return TCL_ERROR;
  1274. }
  1275. ComparePkgVersions(argv2, argv3, &satisfies);
  1276. #endif
  1277. Tcl_SetObjResult(interp, Tcl_NewIntObj(satisfies));
  1278. break;
  1279.     }
  1280.     default: {
  1281. panic("Tcl_PackageObjCmd: bad option index to pkgOptions");
  1282.     }
  1283.     }
  1284.     return TCL_OK;
  1285. }
  1286. /*
  1287.  *----------------------------------------------------------------------
  1288.  *
  1289.  * FindPackage --
  1290.  *
  1291.  * This procedure finds the Package record for a particular package
  1292.  * in a particular interpreter, creating a record if one doesn't
  1293.  * already exist.
  1294.  *
  1295.  * Results:
  1296.  * The return value is a pointer to the Package record for the
  1297.  * package.
  1298.  *
  1299.  * Side effects:
  1300.  * A new Package record may be created.
  1301.  *
  1302.  *----------------------------------------------------------------------
  1303.  */
  1304. static Package *
  1305. FindPackage(interp, name)
  1306.      Tcl_Interp *interp; /* Interpreter to use for package lookup. */
  1307.      CONST char *name; /* Name of package to fine. */
  1308. {
  1309.     Interp *iPtr = (Interp *) interp;
  1310.     Tcl_HashEntry *hPtr;
  1311.     int new;
  1312.     Package *pkgPtr;
  1313.     hPtr = Tcl_CreateHashEntry(&iPtr->packageTable, name, &new);
  1314.     if (new) {
  1315. pkgPtr = (Package *) ckalloc(sizeof(Package));
  1316. pkgPtr->version = NULL;
  1317. pkgPtr->availPtr = NULL;
  1318. pkgPtr->clientData = NULL;
  1319. Tcl_SetHashValue(hPtr, pkgPtr);
  1320.     } else {
  1321. pkgPtr = (Package *) Tcl_GetHashValue(hPtr);
  1322.     }
  1323.     return pkgPtr;
  1324. }
  1325. /*
  1326.  *----------------------------------------------------------------------
  1327.  *
  1328.  * TclFreePackageInfo --
  1329.  *
  1330.  * This procedure is called during interpreter deletion to
  1331.  * free all of the package-related information for the
  1332.  * interpreter.
  1333.  *
  1334.  * Results:
  1335.  * None.
  1336.  *
  1337.  * Side effects:
  1338.  * Memory is freed.
  1339.  *
  1340.  *----------------------------------------------------------------------
  1341.  */
  1342. void
  1343. TclFreePackageInfo(iPtr)
  1344.      Interp *iPtr; /* Interpreter that is being deleted. */
  1345. {
  1346.     Package *pkgPtr;
  1347.     Tcl_HashSearch search;
  1348.     Tcl_HashEntry *hPtr;
  1349.     PkgAvail *availPtr;
  1350.     for (hPtr = Tcl_FirstHashEntry(&iPtr->packageTable, &search);
  1351.  hPtr != NULL;  hPtr = Tcl_NextHashEntry(&search)) {
  1352. pkgPtr = (Package *) Tcl_GetHashValue(hPtr);
  1353. if (pkgPtr->version != NULL) {
  1354.     ckfree(pkgPtr->version);
  1355. }
  1356. while (pkgPtr->availPtr != NULL) {
  1357.     availPtr = pkgPtr->availPtr;
  1358.     pkgPtr->availPtr = availPtr->nextPtr;
  1359.     Tcl_EventuallyFree((ClientData)availPtr->version, TCL_DYNAMIC);
  1360.     Tcl_EventuallyFree((ClientData)availPtr->script, TCL_DYNAMIC);
  1361.     ckfree((char *) availPtr);
  1362. }
  1363. ckfree((char *) pkgPtr);
  1364.     }
  1365.     Tcl_DeleteHashTable(&iPtr->packageTable);
  1366.     if (iPtr->packageUnknown != NULL) {
  1367. ckfree(iPtr->packageUnknown);
  1368.     }
  1369. }
  1370. /*
  1371.  *----------------------------------------------------------------------
  1372.  *
  1373.  * CheckVersion / CheckVersionAndConvert --
  1374.  *
  1375.  * This procedure checks to see whether a version number has
  1376.  * valid syntax.
  1377.  *
  1378.  * Results:
  1379.  * If string is a properly formed version number the TCL_OK
  1380.  * is returned.  Otherwise TCL_ERROR is returned and an error
  1381.  * message is left in the interp's result.
  1382.  *
  1383.  * Side effects:
  1384.  * None.
  1385.  *
  1386.  *----------------------------------------------------------------------
  1387.  */
  1388. #ifndef TCL_TIP268
  1389. static int
  1390. CheckVersion(interp, string)
  1391.     Tcl_Interp *interp; /* Used for error reporting. */
  1392.     CONST char *string; /* Supposedly a version number, which is
  1393.  * groups of decimal digits separated
  1394.  * by dots. */
  1395. {
  1396.     CONST char *p = string;
  1397.     char prevChar;
  1398.     if (!isdigit(UCHAR(*p))) { /* INTL: digit */
  1399. goto error;
  1400.     }
  1401.     for (prevChar = *p, p++; *p != 0; p++) {
  1402. if (!isdigit(UCHAR(*p)) &&
  1403. ((*p != '.') || (prevChar == '.'))) { /* INTL: digit */
  1404.     goto error;
  1405. }
  1406. prevChar = *p;
  1407.     }
  1408.     if (prevChar != '.') {
  1409. return TCL_OK;
  1410.     }
  1411.  error:
  1412.     Tcl_AppendResult(interp, "expected version number but got "",
  1413.     string, """, (char *) NULL);
  1414.     return TCL_ERROR;
  1415. }
  1416. #else
  1417. static int
  1418. CheckVersionAndConvert(interp, string, internal, stable)
  1419.      Tcl_Interp *interp; /* Used for error reporting. */
  1420.      CONST char *string; /* Supposedly a version number, which is
  1421.  * groups of decimal digits separated by
  1422.  * dots. */
  1423.      char** internal;    /* Internal normalized representation */
  1424.      int* stable;      /* Flag: Version is (un)stable. */
  1425. {
  1426.     CONST char *p = string;
  1427.     char prevChar;
  1428.     int hasunstable = 0;
  1429.     /*
  1430.      * 4* assuming that each char is a separator (a,b become ' -x ').
  1431.      * 4+ to have spce for an additional -2 at the end
  1432.      */
  1433.     char* ibuf = ckalloc(4+4*strlen(string));
  1434.     char* ip = ibuf;
  1435.     /* Basic rules
  1436.      * (1) First character has to be a digit.
  1437.      * (2) All other characters have to be a digit or '.'
  1438.      * (3) Two '.'s may not follow each other.
  1439.      * TIP 268, Modified rules
  1440.      * (1) s.a.
  1441.      * (2) All other characters have to be a digit, 'a', 'b', or '.'
  1442.      * (3) s.a.
  1443.      * (4) Only one of 'a' or 'b' may occur.
  1444.      * (5) Neither 'a', nor 'b' may occur before or after a '.'
  1445.      */
  1446.     if (!isdigit(UCHAR(*p))) { /* INTL: digit */
  1447. goto error;
  1448.     }
  1449.     *ip++ = *p;
  1450.     for (prevChar = *p, p++; *p != 0; p++) {
  1451. if ((!isdigit(UCHAR(*p))) && (((*p != '.') && (*p != 'a')
  1452. && (*p != 'b')) || ((hasunstable && ((*p == 'a')
  1453. || (*p == 'b'))) || (((prevChar == 'a') || (prevChar == 'b')
  1454. || (prevChar == '.')) && (*p == '.')) || (((*p == 'a')
  1455. || (*p == 'b') || (*p == '.')) && (prevChar == '.'))))) {
  1456.     /* INTL: digit */
  1457.     goto error;
  1458. }
  1459. if ((*p == 'a') || (*p == 'b')) {
  1460.     hasunstable = 1;
  1461. }
  1462. /*
  1463.  * Translation to the internal rep. Regular version chars are copied
  1464.  * as is. The separators are translated to numerics. The new separator
  1465.  * for all parts is space.
  1466.  */
  1467. if (*p == '.') {
  1468.     *ip++ = ' ';
  1469.     *ip++ = '0';
  1470.     *ip++ = ' ';
  1471. } else if (*p == 'a') {
  1472.     *ip++ = ' ';
  1473.     *ip++ = '-';
  1474.     *ip++ = '2';
  1475.     *ip++ = ' ';
  1476. } else if (*p == 'b') {
  1477.     *ip++ = ' ';
  1478.     *ip++ = '-';
  1479.     *ip++ = '1';
  1480.     *ip++ = ' ';
  1481. } else {
  1482.     *ip++ = *p;
  1483. }
  1484. prevChar = *p;
  1485.     }
  1486.     if ((prevChar != '.') && (prevChar != 'a') && (prevChar != 'b')) {
  1487. *ip = '';
  1488. if (internal != NULL) {
  1489.     *internal = ibuf;
  1490. } else {
  1491.     ckfree(ibuf);
  1492. }
  1493. if (stable != NULL) {
  1494.     *stable = !hasunstable;
  1495. }
  1496. return TCL_OK;
  1497.     }
  1498.  error:
  1499.     ckfree(ibuf);
  1500.     Tcl_AppendResult(interp, "expected version number but got "",
  1501.     string, """, (char *) NULL);
  1502.     return TCL_ERROR;
  1503. }
  1504. #endif
  1505. /*
  1506.  *----------------------------------------------------------------------
  1507.  *
  1508.  * ComparePkgVersions / CompareVersions --
  1509.  *
  1510.  * This procedure compares two version numbers. (268: in internal rep).
  1511.  *
  1512.  * Results:
  1513.  * The return value is -1 if v1 is less than v2, 0 if the two
  1514.  * version numbers are the same, and 1 if v1 is greater than v2.
  1515.  * If *satPtr is non-NULL, the word it points to is filled in
  1516.  * with 1 if v2 >= v1 and both numbers have the same major number
  1517.  * or 0 otherwise.
  1518.  *
  1519.  * Side effects:
  1520.  * None.
  1521.  *
  1522.  *----------------------------------------------------------------------
  1523.  */
  1524. #ifndef TCL_TIP268
  1525. static int
  1526. ComparePkgVersions(v1, v2, satPtr)
  1527.     CONST char *v1;
  1528.     CONST char *v2; /* Versions strings, of form 2.1.3 (any
  1529.  * number of version numbers). */
  1530.     int *satPtr; /* If non-null, the word pointed to is
  1531.  * filled in with a 0/1 value.  1 means
  1532.  * v1 "satisfies" v2:  v1 is greater than
  1533.  * or equal to v2 and both version numbers
  1534.  * have the same major number. */
  1535. {
  1536.     int thisIsMajor, n1, n2;
  1537.     /*
  1538.      * Each iteration of the following loop processes one number from each
  1539.      * string, terminated by a " " (space). If those numbers don't match then
  1540.      * the comparison is over; otherwise, we loop back for the next number.
  1541.      */
  1542.     thisIsMajor = 1;
  1543.     while (1) {
  1544. /* Parse one decimal number from the front of each string. */
  1545. n1 = n2 = 0;
  1546. while ((*v1 != 0) && (*v1 != '.')) {
  1547.     n1 = 10*n1 + (*v1 - '0');
  1548.     v1++;
  1549. }
  1550. while ((*v2 != 0) && (*v2 != '.')) {
  1551.     n2 = 10*n2 + (*v2 - '0');
  1552.     v2++;
  1553. }
  1554. /*
  1555.  * Compare and go on to the next version number if the current numbers
  1556.  * match.
  1557.  */
  1558. if (n1 != n2) {
  1559.     break;
  1560. }
  1561. if (*v1 != 0) {
  1562.     v1++;
  1563. } else if (*v2 == 0) {
  1564.     break;
  1565. }
  1566. if (*v2 != 0) {
  1567.     v2++;
  1568. }
  1569. thisIsMajor = 0;
  1570.     }
  1571.     if (satPtr != NULL) {
  1572. *satPtr = (n1 == n2) || ((n1 > n2) && !thisIsMajor);
  1573.     }
  1574.     if (n1 > n2) {
  1575. return 1;
  1576.     } else if (n1 == n2) {
  1577. return 0;
  1578.     } else {
  1579. return -1;
  1580.     }
  1581. }
  1582. #else
  1583. static int
  1584. CompareVersions(v1, v2, isMajorPtr)
  1585.      CONST char *v1; /* Versions strings, of form 2.1.3 (any number */
  1586.      CONST char *v2; /* of version numbers). */
  1587.      int *isMajorPtr;   /* If non-null, the word pointed to is filled
  1588.  * in with a 0/1 value. 1 means that the difference
  1589.  * occured in the first element. */
  1590. {
  1591.     int thisIsMajor, n1, n2;
  1592.     int res, flip;
  1593.     /*
  1594.      * Each iteration of the following loop processes one number from each
  1595.      * string, terminated by a " " (space). If those numbers don't match then
  1596.      * the comparison is over; otherwise, we loop back for the next number.
  1597.      *
  1598.      * TIP 268.
  1599.      * This is identical the function 'ComparePkgVersion', but using the new
  1600.      * space separator as used by the internal rep of version numbers. The
  1601.      * special separators 'a' and 'b' have already been dealt with in
  1602.      * 'CheckVersionAndConvert', they were translated into numbers as
  1603.      * well. This keeps the comparison sane. Otherwise we would have to
  1604.      * compare numerics, the separators, and also deal with the special case
  1605.      * of end-of-string compared to separators. The semi-list rep we get here
  1606.      * is much easier to handle, as it is still regular.
  1607.      */
  1608.     thisIsMajor = 1;
  1609.     while (1) {
  1610. /* Parse one decimal number from the front of each string. */
  1611. n1 = n2 = 0;
  1612. flip = 0;
  1613. while ((*v1 != 0) && (*v1 != ' ')) {
  1614.     if (*v1 == '-') {flip = 1 ; v1++ ; continue;}
  1615.     n1 = 10*n1 + (*v1 - '0');
  1616.     v1++;
  1617. }
  1618. if (flip) n1 = -n1;
  1619. flip = 0;
  1620. while ((*v2 != 0) && (*v2 != ' ')) {
  1621.     if (*v2 == '-') {flip = 1; v2++ ; continue;}
  1622.     n2 = 10*n2 + (*v2 - '0');
  1623.     v2++;
  1624. }
  1625. if (flip) n2 = -n2;
  1626. /*
  1627.  * Compare and go on to the next version number if the current numbers
  1628.  * match.
  1629.  */
  1630. if (n1 != n2) {
  1631.     break;
  1632. }
  1633. if (*v1 != 0) {
  1634.     v1++;
  1635. } else if (*v2 == 0) {
  1636.     break;
  1637. }
  1638. if (*v2 != 0) {
  1639.     v2++;
  1640. }
  1641. thisIsMajor = 0;
  1642.     }
  1643.     if (n1 > n2) {
  1644. res = 1;
  1645.     } else if (n1 == n2) {
  1646. res = 0;
  1647.     } else {
  1648. res = -1;
  1649.     }
  1650.     if (isMajorPtr != NULL) {
  1651. *isMajorPtr = thisIsMajor;
  1652.     }
  1653.     return res;
  1654. }
  1655. /*
  1656.  *----------------------------------------------------------------------
  1657.  *
  1658.  * CheckAllRequirements --
  1659.  *
  1660.  * This function checks to see whether all requirements in a set
  1661.  * have valid syntax.
  1662.  *
  1663.  * Results:
  1664.  * TCL_OK is returned if all requirements are valid.
  1665.  * Otherwise TCL_ERROR is returned and an error message
  1666.  * is left in the interp's result.
  1667.  *
  1668.  * Side effects:
  1669.  * May modify the interpreter result.
  1670.  *
  1671.  *----------------------------------------------------------------------
  1672.  */
  1673. static int
  1674. CheckAllRequirements(interp, reqc, reqv)
  1675.      Tcl_Interp* interp;
  1676.      int reqc;                   /* Requirements to check. */
  1677.      Tcl_Obj *CONST reqv[];
  1678. {
  1679.     int i;
  1680.     for (i = 0; i < reqc; i++) {
  1681. if ((CheckRequirement(interp, Tcl_GetString(reqv[i])) != TCL_OK)) {
  1682.     return TCL_ERROR;
  1683. }
  1684.     }
  1685.     return TCL_OK;
  1686. }
  1687. /*
  1688.  *----------------------------------------------------------------------
  1689.  *
  1690.  * CheckRequirement --
  1691.  *
  1692.  * This function checks to see whether a requirement has valid syntax.
  1693.  *
  1694.  * Results:
  1695.  * If string is a properly formed requirement then TCL_OK is returned.
  1696.  * Otherwise TCL_ERROR is returned and an error message is left in the
  1697.  * interp's result.
  1698.  *
  1699.  * Side effects:
  1700.  * None.
  1701.  *
  1702.  *----------------------------------------------------------------------
  1703.  */
  1704. static int
  1705. CheckRequirement(interp, string)
  1706.      Tcl_Interp *interp; /* Used for error reporting. */
  1707.      CONST char *string; /* Supposedly a requirement. */
  1708. {
  1709.     /* Syntax of requirement = version
  1710.      *                       = version-version
  1711.      *                       = version-
  1712.      */
  1713.     char* dash = NULL;
  1714.     char* buf;
  1715.     dash = strchr (string, '-');
  1716.     if (dash == NULL) {
  1717. /* no dash found, has to be a simple version */
  1718. return CheckVersionAndConvert (interp, string, NULL, NULL);
  1719.     }
  1720.     if (strchr (dash+1, '-') != NULL) {
  1721. /* More dashes found after the first. This is wrong. */
  1722. Tcl_AppendResult(interp, "expected versionMin-versionMax but got "",
  1723. string, """, NULL);
  1724. return TCL_ERROR;
  1725.     }
  1726.     /* Exactly one dash is present. Copy the string, split at the location of
  1727.      * dash and check that both parts are versions. Note that the max part can
  1728.      * be empty.
  1729.      */
  1730.     DupString(buf, string);
  1731.     dash = buf + (dash - string);  
  1732.     *dash = '';     /* buf  now <=> min part */
  1733.     dash ++;          /* dash now <=> max part */
  1734.     if ((CheckVersionAndConvert(interp, buf, NULL, NULL) != TCL_OK)
  1735.     || ((*dash != '')
  1736.     && (CheckVersionAndConvert(interp, dash, NULL, NULL) != TCL_OK))) {
  1737. ckfree(buf);
  1738. return TCL_ERROR;
  1739.     }
  1740.     ckfree(buf);
  1741.     return TCL_OK;
  1742. }
  1743. /*
  1744.  *----------------------------------------------------------------------
  1745.  *
  1746.  * AddRequirementsToResult --
  1747.  *
  1748.  * This function accumulates requirements in the interpreter result.
  1749.  *
  1750.  * Results:
  1751.  * None.
  1752.  *
  1753.  * Side effects:
  1754.  * The interpreter result is extended.
  1755.  *
  1756.  *----------------------------------------------------------------------
  1757.  */
  1758. static void
  1759. AddRequirementsToResult(interp, reqc, reqv)
  1760.      Tcl_Interp* interp;
  1761.      int reqc; /* Requirements constraining the desired version. */
  1762.      Tcl_Obj *CONST reqv[]; /* 0 means use the latest version available. */
  1763. {
  1764.     if (reqc > 0) {
  1765. int i;
  1766. for (i = 0; i < reqc; i++) {
  1767.     int length;
  1768.     char *v = Tcl_GetStringFromObj(reqv[i], &length);
  1769.     if ((length & 0x1) && (v[length/2] == '-')
  1770.     && (strncmp(v, v+((length+1)/2), length/2) == 0)) {
  1771. Tcl_AppendResult(interp, " ", v+((length+1)/2), NULL);
  1772.     } else {
  1773. Tcl_AppendResult(interp, " ", v, NULL);
  1774.     }
  1775. }
  1776.     }
  1777. }
  1778. /*
  1779.  *----------------------------------------------------------------------
  1780.  *
  1781.  * AddRequirementsToDString --
  1782.  *
  1783.  * This function accumulates requirements in a DString.
  1784.  *
  1785.  * Results:
  1786.  * None.
  1787.  *
  1788.  * Side effects:
  1789.  * The DString argument is extended.
  1790.  *
  1791.  *----------------------------------------------------------------------
  1792.  */
  1793. static void
  1794. AddRequirementsToDString(dstring, reqc, reqv)
  1795.      Tcl_DString* dstring;
  1796.      int reqc; /* Requirements constraining the desired version. */
  1797.      Tcl_Obj *CONST reqv[]; /* 0 means use the latest version available. */
  1798. {
  1799.     if (reqc > 0) {
  1800. int i;
  1801. for (i = 0; i < reqc; i++) {
  1802.     Tcl_DStringAppend(dstring, " ", 1);
  1803.     Tcl_DStringAppend(dstring, TclGetString(reqv[i]), -1);
  1804. }
  1805.     } else {
  1806. Tcl_DStringAppend(dstring, " 0-", -1);
  1807.     }
  1808. }
  1809. /*
  1810.  *----------------------------------------------------------------------
  1811.  *
  1812.  * SomeRequirementSatisfied --
  1813.  *
  1814.  * This function checks to see whether a version satisfies at
  1815.  * least one of a set of requirements.
  1816.  *
  1817.  * Results:
  1818.  * If the requirements are satisfied 1 is returned.
  1819.  * Otherwise 0 is returned. The function assumes
  1820.  * that all pieces have valid syntax. And is allowed
  1821.  * to make that assumption.
  1822.  *
  1823.  * Side effects:
  1824.  * None.
  1825.  *
  1826.  *----------------------------------------------------------------------
  1827.  */
  1828. static int
  1829. SomeRequirementSatisfied(availVersionI, reqc, reqv)
  1830.      char *availVersionI; /* Candidate version to check against the
  1831.  * requirements. */
  1832.      int reqc; /* Requirements constraining the desired
  1833.  * version. */
  1834.      Tcl_Obj *CONST reqv[]; /* 0 means use the latest version available. */
  1835. {
  1836.     int i;
  1837.     for (i = 0; i < reqc; i++) {
  1838. if (RequirementSatisfied(availVersionI, Tcl_GetString(reqv[i]))) {
  1839.     return 1;
  1840. }
  1841.     }
  1842.     return 0;
  1843. }
  1844. /*
  1845.  *----------------------------------------------------------------------
  1846.  *
  1847.  * RequirementSatisfied --
  1848.  *
  1849.  * This function checks to see whether a version satisfies a requirement.
  1850.  *
  1851.  * Results:
  1852.  * If the requirement is satisfied 1 is returned.
  1853.  * Otherwise 0 is returned. The function assumes
  1854.  * that all pieces have valid syntax. And is allowed
  1855.  * to make that assumption.
  1856.  *
  1857.  * Side effects:
  1858.  * None.
  1859.  *
  1860.  *----------------------------------------------------------------------
  1861.  */
  1862. static int
  1863. RequirementSatisfied(havei, req)
  1864.      char *havei; /* Version string, of candidate package we have */
  1865.      CONST char *req;   /* Requirement string the candidate has to satisfy */
  1866. {
  1867.     /* The have candidate is already in internal rep. */
  1868.     int satisfied, res;
  1869.     char* dash = NULL;
  1870.     char* buf, *min, *max;
  1871.     dash = strchr (req, '-');
  1872.     if (dash == NULL) {
  1873. /* No dash found, is a simple version, fallback to regular check.
  1874.  * The 'CheckVersionAndConvert' cannot fail. We pad the requirement with
  1875.  * 'a0', i.e '-2' before doing the comparison to properly accept
  1876.  * unstables as well.
  1877.  */
  1878. char* reqi = NULL;
  1879. int thisIsMajor;
  1880. CheckVersionAndConvert (NULL, req, &reqi, NULL);
  1881. strcat (reqi, " -2");
  1882. res = CompareVersions(havei, reqi, &thisIsMajor);
  1883. satisfied = (res == 0) || ((res == 1) && !thisIsMajor);
  1884. ckfree(reqi);
  1885. return satisfied;
  1886.     }
  1887.     /* Exactly one dash is present (Assumption of valid syntax). Copy the req,
  1888.      * split at the location of dash and check that both parts are
  1889.      * versions. Note that the max part can be empty.
  1890.      */
  1891.     DupString(buf, req);
  1892.     dash  = buf + (dash - req);  
  1893.     *dash = '';     /* buf  now <=> min part */
  1894.     dash ++;          /* dash now <=> max part */
  1895.     if (*dash == '') {
  1896. /* We have a min, but no max. For the comparison we generate the
  1897.  * internal rep, padded with 'a0' i.e. '-2'.
  1898.  */
  1899. /* No max part, unbound */
  1900. CheckVersionAndConvert (NULL, buf, &min, NULL);
  1901. strcat (min, " -2");
  1902. satisfied = (CompareVersions(havei, min, NULL) >= 0);
  1903. ckfree(min);
  1904. ckfree(buf);
  1905. return satisfied;
  1906.     }
  1907.     /* We have both min and max, and generate their internal reps.
  1908.      * When identical we compare as is, otherwise we pad with 'a0'
  1909.      * to ove the range a bit.
  1910.      */
  1911.     CheckVersionAndConvert (NULL, buf,  &min, NULL);
  1912.     CheckVersionAndConvert (NULL, dash, &max, NULL);
  1913.     if (CompareVersions(min, max, NULL) == 0) {
  1914. satisfied = (CompareVersions(min, havei, NULL) == 0);
  1915.     } else {
  1916. strcat (min, " -2");
  1917. strcat (max, " -2");
  1918. satisfied = ((CompareVersions(min, havei, NULL) <= 0) &&
  1919.      (CompareVersions(havei, max, NULL) < 0));
  1920.     }
  1921.     ckfree(min);
  1922.     ckfree(max);
  1923.     ckfree(buf);
  1924.     return satisfied;
  1925. }
  1926. /*
  1927.  * Local Variables:
  1928.  * mode: c
  1929.  * c-basic-offset: 4
  1930.  * fill-column: 78
  1931.  * End:
  1932.  */
  1933. #endif