os/persistentdata/persistentstorage/sqlite3api/TEST/TCL/tcldistribution/generic/tclPkg.c
author sl@SLION-WIN7.fritz.box
Fri, 15 Jun 2012 03:10:57 +0200
changeset 0 bde4ae8d615e
permissions -rw-r--r--
First public contribution.
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