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