OpenCores
URL https://opencores.org/ocsvn/or1k/or1k/trunk

Subversion Repositories or1k

[/] [or1k/] [trunk/] [insight/] [tcl/] [generic/] [tclPkg.c] - Blame information for rev 1765

Details | Compare with Previous | View Log

Line No. Rev Author Line
1 578 markom
/*
2
 * tclPkg.c --
3
 *
4
 *      This file implements package and version control for Tcl via
5
 *      the "package" command and a few C APIs.
6
 *
7
 * Copyright (c) 1996 Sun Microsystems, Inc.
8
 *
9
 * See the file "license.terms" for information on usage and redistribution
10
 * of this file, and for a DISCLAIMER OF ALL WARRANTIES.
11
 *
12
 * RCS: @(#) $Id: tclPkg.c,v 1.1.1.1 2002-01-16 10:25:28 markom Exp $
13
 */
14
 
15
#include "tclInt.h"
16
 
17
/*
18
 * Each invocation of the "package ifneeded" command creates a structure
19
 * of the following type, which is used to load the package into the
20
 * interpreter if it is requested with a "package require" command.
21
 */
22
 
23
typedef struct PkgAvail {
24
    char *version;              /* Version string; malloc'ed. */
25
    char *script;               /* Script to invoke to provide this version
26
                                 * of the package.  Malloc'ed and protected
27
                                 * by Tcl_Preserve and Tcl_Release. */
28
    struct PkgAvail *nextPtr;   /* Next in list of available versions of
29
                                 * the same package. */
30
} PkgAvail;
31
 
32
/*
33
 * For each package that is known in any way to an interpreter, there
34
 * is one record of the following type.  These records are stored in
35
 * the "packageTable" hash table in the interpreter, keyed by
36
 * package name such as "Tk" (no version number).
37
 */
38
 
39
typedef struct Package {
40
    char *version;              /* Version that has been supplied in this
41
                                 * interpreter via "package provide"
42
                                 * (malloc'ed).  NULL means the package doesn't
43
                                 * exist in this interpreter yet. */
44
    PkgAvail *availPtr;         /* First in list of all available versions
45
                                 * of this package. */
46
} Package;
47
 
48
/*
49
 * Prototypes for procedures defined in this file:
50
 */
51
 
52
static int              CheckVersion _ANSI_ARGS_((Tcl_Interp *interp,
53
                            char *string));
54
static int              ComparePkgVersions _ANSI_ARGS_((char *v1, char *v2,
55
                            int *satPtr));
56
static Package *        FindPackage _ANSI_ARGS_((Tcl_Interp *interp,
57
                            char *name));
58
 
59
/*
60
 *----------------------------------------------------------------------
61
 *
62
 * Tcl_PkgProvide --
63
 *
64
 *      This procedure is invoked to declare that a particular version
65
 *      of a particular package is now present in an interpreter.  There
66
 *      must not be any other version of this package already
67
 *      provided in the interpreter.
68
 *
69
 * Results:
70
 *      Normally returns TCL_OK;  if there is already another version
71
 *      of the package loaded then TCL_ERROR is returned and an error
72
 *      message is left in interp->result.
73
 *
74
 * Side effects:
75
 *      The interpreter remembers that this package is available,
76
 *      so that no other version of the package may be provided for
77
 *      the interpreter.
78
 *
79
 *----------------------------------------------------------------------
80
 */
81
 
82
int
83
Tcl_PkgProvide(interp, name, version)
84
    Tcl_Interp *interp;         /* Interpreter in which package is now
85
                                 * available. */
86
    char *name;                 /* Name of package. */
87
    char *version;              /* Version string for package. */
88
{
89
    Package *pkgPtr;
90
 
91
    pkgPtr = FindPackage(interp, name);
92
    if (pkgPtr->version == NULL) {
93
        pkgPtr->version = ckalloc((unsigned) (strlen(version) + 1));
94
        strcpy(pkgPtr->version, version);
95
        return TCL_OK;
96
    }
97
    if (ComparePkgVersions(pkgPtr->version, version, (int *) NULL) == 0) {
98
        return TCL_OK;
99
    }
100
    Tcl_AppendResult(interp, "conflicting versions provided for package \"",
101
            name, "\": ", pkgPtr->version, ", then ", version, (char *) NULL);
102
    return TCL_ERROR;
103
}
104
 
105
/*
106
 *----------------------------------------------------------------------
107
 *
108
 * Tcl_PkgRequire --
109
 *
110
 *      This procedure is called by code that depends on a particular
111
 *      version of a particular package.  If the package is not already
112
 *      provided in the interpreter, this procedure invokes a Tcl script
113
 *      to provide it.  If the package is already provided, this
114
 *      procedure makes sure that the caller's needs don't conflict with
115
 *      the version that is present.
116
 *
117
 * Results:
118
 *      If successful, returns the version string for the currently
119
 *      provided version of the package, which may be different from
120
 *      the "version" argument.  If the caller's requirements
121
 *      cannot be met (e.g. the version requested conflicts with
122
 *      a currently provided version, or the required version cannot
123
 *      be found, or the script to provide the required version
124
 *      generates an error), NULL is returned and an error
125
 *      message is left in interp->result.
126
 *
127
 * Side effects:
128
 *      The script from some previous "package ifneeded" command may
129
 *      be invoked to provide the package.
130
 *
131
 *----------------------------------------------------------------------
132
 */
133
 
134
char *
135
Tcl_PkgRequire(interp, name, version, exact)
136
    Tcl_Interp *interp;         /* Interpreter in which package is now
137
                                 * available. */
138
    char *name;                 /* Name of desired package. */
139
    char *version;              /* Version string for desired version;
140
                                 * NULL means use the latest version
141
                                 * available. */
142
    int exact;                  /* Non-zero means that only the particular
143
                                 * version given is acceptable. Zero means
144
                                 * use the latest compatible version. */
145
{
146
    Package *pkgPtr;
147
    PkgAvail *availPtr, *bestPtr;
148
    char *script;
149
    int code, satisfies, result, pass;
150
    Tcl_DString command;
151
 
152
    /*
153
     * It can take up to three passes to find the package:  one pass to
154
     * run the "package unknown" script, one to run the "package ifneeded"
155
     * script for a specific version, and a final pass to lookup the
156
     * package loaded by the "package ifneeded" script.
157
     */
158
 
159
    for (pass = 1; ; pass++) {
160
        pkgPtr = FindPackage(interp, name);
161
        if (pkgPtr->version != NULL) {
162
            break;
163
        }
164
 
165
        /*
166
         * The package isn't yet present.  Search the list of available
167
         * versions and invoke the script for the best available version.
168
         */
169
 
170
        bestPtr = NULL;
171
        for (availPtr = pkgPtr->availPtr; availPtr != NULL;
172
                availPtr = availPtr->nextPtr) {
173
            if ((bestPtr != NULL) && (ComparePkgVersions(availPtr->version,
174
                    bestPtr->version, (int *) NULL) <= 0)) {
175
                continue;
176
            }
177
            if (version != NULL) {
178
                result = ComparePkgVersions(availPtr->version, version,
179
                        &satisfies);
180
                if ((result != 0) && exact) {
181
                    continue;
182
                }
183
                if (!satisfies) {
184
                    continue;
185
                }
186
            }
187
            bestPtr = availPtr;
188
        }
189
        if (bestPtr != NULL) {
190
            /*
191
             * We found an ifneeded script for the package.  Be careful while
192
             * executing it:  this could cause reentrancy, so (a) protect the
193
             * script itself from deletion and (b) don't assume that bestPtr
194
             * will still exist when the script completes.
195
             */
196
 
197
            script = bestPtr->script;
198
            Tcl_Preserve((ClientData) script);
199
            code = Tcl_GlobalEval(interp, script);
200
            Tcl_Release((ClientData) script);
201
            if (code != TCL_OK) {
202
                if (code == TCL_ERROR) {
203
                    Tcl_AddErrorInfo(interp,
204
                            "\n    (\"package ifneeded\" script)");
205
                }
206
                return NULL;
207
            }
208
            Tcl_ResetResult(interp);
209
            pkgPtr = FindPackage(interp, name);
210
            break;
211
        }
212
 
213
        /*
214
         * Package not in the database.  If there is a "package unknown"
215
         * command, invoke it (but only on the first pass;  after that,
216
         * we should not get here in the first place).
217
         */
218
 
219
        if (pass > 1) {
220
            break;
221
        }
222
        script = ((Interp *) interp)->packageUnknown;
223
        if (script != NULL) {
224
            Tcl_DStringInit(&command);
225
            Tcl_DStringAppend(&command, script, -1);
226
            Tcl_DStringAppendElement(&command, name);
227
            Tcl_DStringAppend(&command, " ", 1);
228
            Tcl_DStringAppend(&command, (version != NULL) ? version : "{}",
229
                    -1);
230
            if (exact) {
231
                Tcl_DStringAppend(&command, " -exact", 7);
232
            }
233
            code = Tcl_GlobalEval(interp, Tcl_DStringValue(&command));
234
            Tcl_DStringFree(&command);
235
            if (code != TCL_OK) {
236
                if (code == TCL_ERROR) {
237
                    Tcl_AddErrorInfo(interp,
238
                            "\n    (\"package unknown\" script)");
239
                }
240
                return NULL;
241
            }
242
            Tcl_ResetResult(interp);
243
        }
244
    }
245
 
246
    if (pkgPtr->version == NULL) {
247
        Tcl_AppendResult(interp, "can't find package ", name,
248
                (char *) NULL);
249
        if (version != NULL) {
250
            Tcl_AppendResult(interp, " ", version, (char *) NULL);
251
        }
252
        return NULL;
253
    }
254
 
255
    /*
256
     * At this point we now that the package is present.  Make sure that the
257
     * provided version meets the current requirement.
258
     */
259
 
260
    if (version == NULL) {
261
        return pkgPtr->version;
262
    }
263
    result = ComparePkgVersions(pkgPtr->version, version, &satisfies);
264
    if ((satisfies && !exact) || (result == 0)) {
265
        return pkgPtr->version;
266
    }
267
    Tcl_AppendResult(interp, "version conflict for package \"",
268
            name, "\": have ", pkgPtr->version, ", need ", version,
269
            (char *) NULL);
270
    return NULL;
271
}
272
 
273
/*
274
 *----------------------------------------------------------------------
275
 *
276
 * Tcl_PackageCmd --
277
 *
278
 *      This procedure is invoked to process the "package" Tcl command.
279
 *      See the user documentation for details on what it does.
280
 *
281
 * Results:
282
 *      A standard Tcl result.
283
 *
284
 * Side effects:
285
 *      See the user documentation.
286
 *
287
 *----------------------------------------------------------------------
288
 */
289
 
290
        /* ARGSUSED */
291
int
292
Tcl_PackageCmd(dummy, interp, argc, argv)
293
    ClientData dummy;                   /* Not used. */
294
    Tcl_Interp *interp;                 /* Current interpreter. */
295
    int argc;                           /* Number of arguments. */
296
    char **argv;                        /* Argument strings. */
297
{
298
    Interp *iPtr = (Interp *) interp;
299
    size_t length;
300
    int c, exact, i, satisfies;
301
    PkgAvail *availPtr, *prevPtr;
302
    Package *pkgPtr;
303
    Tcl_HashEntry *hPtr;
304
    Tcl_HashSearch search;
305
    Tcl_HashTable *tablePtr;
306
    char *version;
307
    char buf[30];
308
 
309
    if (argc < 2) {
310
        Tcl_AppendResult(interp, "wrong # args: should be \"", argv[0],
311
                " option ?arg arg ...?\"", (char *) NULL);
312
        return TCL_ERROR;
313
    }
314
    c = argv[1][0];
315
    length = strlen(argv[1]);
316
    if ((c == 'f') && (strncmp(argv[1], "forget", length) == 0)) {
317
        for (i = 2; i < argc; i++) {
318
            hPtr = Tcl_FindHashEntry(&iPtr->packageTable, argv[i]);
319
            if (hPtr == NULL) {
320
                return TCL_OK;
321
            }
322
            pkgPtr = (Package *) Tcl_GetHashValue(hPtr);
323
            Tcl_DeleteHashEntry(hPtr);
324
            if (pkgPtr->version != NULL) {
325
                ckfree(pkgPtr->version);
326
            }
327
            while (pkgPtr->availPtr != NULL) {
328
                availPtr = pkgPtr->availPtr;
329
                pkgPtr->availPtr = availPtr->nextPtr;
330
                ckfree(availPtr->version);
331
                Tcl_EventuallyFree((ClientData)availPtr->script, TCL_DYNAMIC);
332
                ckfree((char *) availPtr);
333
            }
334
            ckfree((char *) pkgPtr);
335
        }
336
    } else if ((c == 'i') && (strncmp(argv[1], "ifneeded", length) == 0)) {
337
        if ((argc != 4) && (argc != 5)) {
338
            Tcl_AppendResult(interp, "wrong # args: should be \"", argv[0],
339
                    " ifneeded package version ?script?\"", (char *) NULL);
340
            return TCL_ERROR;
341
        }
342
        if (CheckVersion(interp, argv[3]) != TCL_OK) {
343
            return TCL_ERROR;
344
        }
345
        if (argc == 4) {
346
            hPtr = Tcl_FindHashEntry(&iPtr->packageTable, argv[2]);
347
            if (hPtr == NULL) {
348
                return TCL_OK;
349
            }
350
            pkgPtr = (Package *) Tcl_GetHashValue(hPtr);
351
        } else {
352
            pkgPtr = FindPackage(interp, argv[2]);
353
        }
354
        for (availPtr = pkgPtr->availPtr, prevPtr = NULL; availPtr != NULL;
355
                prevPtr = availPtr, availPtr = availPtr->nextPtr) {
356
            if (ComparePkgVersions(availPtr->version, argv[3], (int *) NULL)
357
                    == 0) {
358
                if (argc == 4) {
359
                    Tcl_SetResult(interp, availPtr->script, TCL_VOLATILE);
360
                    return TCL_OK;
361
                }
362
                Tcl_EventuallyFree((ClientData)availPtr->script, TCL_DYNAMIC);
363
                break;
364
            }
365
        }
366
        if (argc == 4) {
367
            return TCL_OK;
368
        }
369
        if (availPtr == NULL) {
370
            availPtr = (PkgAvail *) ckalloc(sizeof(PkgAvail));
371
            availPtr->version = ckalloc((unsigned) (strlen(argv[3]) + 1));
372
            strcpy(availPtr->version, argv[3]);
373
            if (prevPtr == NULL) {
374
                availPtr->nextPtr = pkgPtr->availPtr;
375
                pkgPtr->availPtr = availPtr;
376
            } else {
377
                availPtr->nextPtr = prevPtr->nextPtr;
378
                prevPtr->nextPtr = availPtr;
379
            }
380
        }
381
        availPtr->script = ckalloc((unsigned) (strlen(argv[4]) + 1));
382
        strcpy(availPtr->script, argv[4]);
383
    } else if ((c == 'n') && (strncmp(argv[1], "names", length) == 0)) {
384
        if (argc != 2) {
385
            Tcl_AppendResult(interp, "wrong # args: should be \"", argv[0],
386
                    " names\"", (char *) NULL);
387
            return TCL_ERROR;
388
        }
389
        tablePtr = &iPtr->packageTable;
390
        for (hPtr = Tcl_FirstHashEntry(tablePtr, &search); hPtr != NULL;
391
                hPtr = Tcl_NextHashEntry(&search)) {
392
            pkgPtr = (Package *) Tcl_GetHashValue(hPtr);
393
            if ((pkgPtr->version != NULL) || (pkgPtr->availPtr != NULL)) {
394
                Tcl_AppendElement(interp, Tcl_GetHashKey(tablePtr, hPtr));
395
            }
396
        }
397
    } else if ((c == 'p') && (strncmp(argv[1], "provide", length) == 0)) {
398
        if ((argc != 3) && (argc != 4)) {
399
            Tcl_AppendResult(interp, "wrong # args: should be \"", argv[0],
400
                    " provide package ?version?\"", (char *) NULL);
401
            return TCL_ERROR;
402
        }
403
        if (argc == 3) {
404
            hPtr = Tcl_FindHashEntry(&iPtr->packageTable, argv[2]);
405
            if (hPtr != NULL) {
406
                pkgPtr = (Package *) Tcl_GetHashValue(hPtr);
407
                if (pkgPtr->version != NULL) {
408
                    Tcl_SetResult(interp, pkgPtr->version, TCL_VOLATILE);
409
                }
410
            }
411
            return TCL_OK;
412
        }
413
        if (CheckVersion(interp, argv[3]) != TCL_OK) {
414
            return TCL_ERROR;
415
        }
416
        return Tcl_PkgProvide(interp, argv[2], argv[3]);
417
    } else if ((c == 'r') && (strncmp(argv[1], "require", length) == 0)) {
418
        if (argc < 3) {
419
            requireSyntax:
420
            Tcl_AppendResult(interp, "wrong # args: should be \"", argv[0],
421
                    " require ?-exact? package ?version?\"", (char *) NULL);
422
            return TCL_ERROR;
423
        }
424
        if ((argv[2][0] == '-') && (strcmp(argv[2], "-exact") == 0)) {
425
            exact = 1;
426
        } else {
427
            exact = 0;
428
        }
429
        version = NULL;
430
        if (argc == (4+exact)) {
431
            version = argv[3+exact];
432
            if (CheckVersion(interp, version) != TCL_OK) {
433
                return TCL_ERROR;
434
            }
435
        } else if ((argc != 3) || exact) {
436
            goto requireSyntax;
437
        }
438
        version = Tcl_PkgRequire(interp, argv[2+exact], version, exact);
439
        if (version == NULL) {
440
            return TCL_ERROR;
441
        }
442
        Tcl_SetResult(interp, version, TCL_VOLATILE);
443
    } else if ((c == 'u') && (strncmp(argv[1], "unknown", length) == 0)) {
444
        if (argc == 2) {
445
            if (iPtr->packageUnknown != NULL) {
446
                Tcl_SetResult(interp, iPtr->packageUnknown, TCL_VOLATILE);
447
            }
448
        } else if (argc == 3) {
449
            if (iPtr->packageUnknown != NULL) {
450
                ckfree(iPtr->packageUnknown);
451
            }
452
            if (argv[2][0] == 0) {
453
                iPtr->packageUnknown = NULL;
454
            } else {
455
                iPtr->packageUnknown = (char *) ckalloc((unsigned)
456
                        (strlen(argv[2]) + 1));
457
                strcpy(iPtr->packageUnknown, argv[2]);
458
            }
459
        } else {
460
            Tcl_AppendResult(interp, "wrong # args: should be \"", argv[0],
461
                    " unknown ?command?\"", (char *) NULL);
462
            return TCL_ERROR;
463
        }
464
    } else if ((c == 'v') && (strncmp(argv[1], "vcompare", length) == 0)
465
            && (length >= 2)) {
466
        if (argc != 4) {
467
            Tcl_AppendResult(interp, "wrong # args: should be \"", argv[0],
468
                    " vcompare version1 version2\"", (char *) NULL);
469
            return TCL_ERROR;
470
        }
471
        if ((CheckVersion(interp, argv[2]) != TCL_OK)
472
                || (CheckVersion(interp, argv[3]) != TCL_OK)) {
473
            return TCL_ERROR;
474
        }
475
        TclFormatInt(buf, ComparePkgVersions(argv[2], argv[3], (int *) NULL));
476
        Tcl_SetResult(interp, buf, TCL_VOLATILE);
477
    } else if ((c == 'v') && (strncmp(argv[1], "versions", length) == 0)
478
            && (length >= 2)) {
479
        if (argc != 3) {
480
            Tcl_AppendResult(interp, "wrong # args: should be \"", argv[0],
481
                    " versions package\"", (char *) NULL);
482
            return TCL_ERROR;
483
        }
484
        hPtr = Tcl_FindHashEntry(&iPtr->packageTable, argv[2]);
485
        if (hPtr != NULL) {
486
            pkgPtr = (Package *) Tcl_GetHashValue(hPtr);
487
            for (availPtr = pkgPtr->availPtr; availPtr != NULL;
488
                    availPtr = availPtr->nextPtr) {
489
                Tcl_AppendElement(interp, availPtr->version);
490
            }
491
        }
492
    } else if ((c == 'v') && (strncmp(argv[1], "vsatisfies", length) == 0)
493
            && (length >= 2)) {
494
        if (argc != 4) {
495
            Tcl_AppendResult(interp, "wrong # args: should be \"", argv[0],
496
                    " vsatisfies version1 version2\"", (char *) NULL);
497
            return TCL_ERROR;
498
        }
499
        if ((CheckVersion(interp, argv[2]) != TCL_OK)
500
                || (CheckVersion(interp, argv[3]) != TCL_OK)) {
501
            return TCL_ERROR;
502
        }
503
        ComparePkgVersions(argv[2], argv[3], &satisfies);
504
        TclFormatInt(buf, satisfies);
505
        Tcl_SetResult(interp, buf, TCL_VOLATILE);
506
    } else {
507
        Tcl_AppendResult(interp, "bad option \"", argv[1],
508
                "\": should be forget, ifneeded, names, ",
509
                "provide, require, unknown, vcompare, ",
510
                "versions, or vsatisfies", (char *) NULL);
511
        return TCL_ERROR;
512
    }
513
    return TCL_OK;
514
}
515
 
516
/*
517
 *----------------------------------------------------------------------
518
 *
519
 * FindPackage --
520
 *
521
 *      This procedure finds the Package record for a particular package
522
 *      in a particular interpreter, creating a record if one doesn't
523
 *      already exist.
524
 *
525
 * Results:
526
 *      The return value is a pointer to the Package record for the
527
 *      package.
528
 *
529
 * Side effects:
530
 *      A new Package record may be created.
531
 *
532
 *----------------------------------------------------------------------
533
 */
534
 
535
static Package *
536
FindPackage(interp, name)
537
    Tcl_Interp *interp;         /* Interpreter to use for package lookup. */
538
    char *name;                 /* Name of package to fine. */
539
{
540
    Interp *iPtr = (Interp *) interp;
541
    Tcl_HashEntry *hPtr;
542
    int new;
543
    Package *pkgPtr;
544
 
545
    hPtr = Tcl_CreateHashEntry(&iPtr->packageTable, name, &new);
546
    if (new) {
547
        pkgPtr = (Package *) ckalloc(sizeof(Package));
548
        pkgPtr->version = NULL;
549
        pkgPtr->availPtr = NULL;
550
        Tcl_SetHashValue(hPtr, pkgPtr);
551
    } else {
552
        pkgPtr = (Package *) Tcl_GetHashValue(hPtr);
553
    }
554
    return pkgPtr;
555
}
556
 
557
/*
558
 *----------------------------------------------------------------------
559
 *
560
 * TclFreePackageInfo --
561
 *
562
 *      This procedure is called during interpreter deletion to
563
 *      free all of the package-related information for the
564
 *      interpreter.
565
 *
566
 * Results:
567
 *      None.
568
 *
569
 * Side effects:
570
 *      Memory is freed.
571
 *
572
 *----------------------------------------------------------------------
573
 */
574
 
575
void
576
TclFreePackageInfo(iPtr)
577
    Interp *iPtr;               /* Interpereter that is being deleted. */
578
{
579
    Package *pkgPtr;
580
    Tcl_HashSearch search;
581
    Tcl_HashEntry *hPtr;
582
    PkgAvail *availPtr;
583
 
584
    for (hPtr = Tcl_FirstHashEntry(&iPtr->packageTable, &search);
585
            hPtr != NULL;  hPtr = Tcl_NextHashEntry(&search)) {
586
        pkgPtr = (Package *) Tcl_GetHashValue(hPtr);
587
        if (pkgPtr->version != NULL) {
588
            ckfree(pkgPtr->version);
589
        }
590
        while (pkgPtr->availPtr != NULL) {
591
            availPtr = pkgPtr->availPtr;
592
            pkgPtr->availPtr = availPtr->nextPtr;
593
            ckfree(availPtr->version);
594
            Tcl_EventuallyFree((ClientData)availPtr->script, TCL_DYNAMIC);
595
            ckfree((char *) availPtr);
596
        }
597
        ckfree((char *) pkgPtr);
598
    }
599
    Tcl_DeleteHashTable(&iPtr->packageTable);
600
    if (iPtr->packageUnknown != NULL) {
601
        ckfree(iPtr->packageUnknown);
602
    }
603
}
604
 
605
/*
606
 *----------------------------------------------------------------------
607
 *
608
 * CheckVersion --
609
 *
610
 *      This procedure checks to see whether a version number has
611
 *      valid syntax.
612
 *
613
 * Results:
614
 *      If string is a properly formed version number the TCL_OK
615
 *      is returned.  Otherwise TCL_ERROR is returned and an error
616
 *      message is left in interp->result.
617
 *
618
 * Side effects:
619
 *      None.
620
 *
621
 *----------------------------------------------------------------------
622
 */
623
 
624
static int
625
CheckVersion(interp, string)
626
    Tcl_Interp *interp;         /* Used for error reporting. */
627
    char *string;               /* Supposedly a version number, which is
628
                                 * groups of decimal digits separated
629
                                 * by dots. */
630
{
631
    char *p = string;
632
 
633
    if (!isdigit(UCHAR(*p))) {
634
        goto error;
635
    }
636
    for (p++; *p != 0; p++) {
637
        if (!isdigit(UCHAR(*p)) && (*p != '.')) {
638
            goto error;
639
        }
640
    }
641
    if (p[-1] != '.') {
642
        return TCL_OK;
643
    }
644
 
645
    error:
646
    Tcl_AppendResult(interp, "expected version number but got \"",
647
            string, "\"", (char *) NULL);
648
    return TCL_ERROR;
649
}
650
 
651
/*
652
 *----------------------------------------------------------------------
653
 *
654
 * ComparePkgVersions --
655
 *
656
 *      This procedure compares two version numbers.
657
 *
658
 * Results:
659
 *      The return value is -1 if v1 is less than v2, 0 if the two
660
 *      version numbers are the same, and 1 if v1 is greater than v2.
661
 *      If *satPtr is non-NULL, the word it points to is filled in
662
 *      with 1 if v2 >= v1 and both numbers have the same major number
663
 *      or 0 otherwise.
664
 *
665
 * Side effects:
666
 *      None.
667
 *
668
 *----------------------------------------------------------------------
669
 */
670
 
671
static int
672
ComparePkgVersions(v1, v2, satPtr)
673
    char *v1, *v2;              /* Versions strings, of form 2.1.3 (any
674
                                 * number of version numbers). */
675
    int *satPtr;                /* If non-null, the word pointed to is
676
                                 * filled in with a 0/1 value.  1 means
677
                                 * v1 "satisfies" v2:  v1 is greater than
678
                                 * or equal to v2 and both version numbers
679
                                 * have the same major number. */
680
{
681
    int thisIsMajor, n1, n2;
682
 
683
    /*
684
     * Each iteration of the following loop processes one number from
685
     * each string, terminated by a ".".  If those numbers don't match
686
     * then the comparison is over;  otherwise, we loop back for the
687
     * next number.
688
     */
689
 
690
    thisIsMajor = 1;
691
    while (1) {
692
        /*
693
         * Parse one decimal number from the front of each string.
694
         */
695
 
696
        n1 = n2 = 0;
697
        while ((*v1 != 0) && (*v1 != '.')) {
698
            n1 = 10*n1 + (*v1 - '0');
699
            v1++;
700
        }
701
        while ((*v2 != 0) && (*v2 != '.')) {
702
            n2 = 10*n2 + (*v2 - '0');
703
            v2++;
704
        }
705
 
706
        /*
707
         * Compare and go on to the next version number if the
708
         * current numbers match.
709
         */
710
 
711
        if (n1 != n2) {
712
            break;
713
        }
714
        if (*v1 != 0) {
715
            v1++;
716
        } else if (*v2 == 0) {
717
            break;
718
        }
719
        if (*v2 != 0) {
720
            v2++;
721
        }
722
        thisIsMajor = 0;
723
    }
724
    if (satPtr != NULL) {
725
        *satPtr = (n1 == n2) || ((n1 > n2) && !thisIsMajor);
726
    }
727
    if (n1 > n2) {
728
        return 1;
729
    } else if (n1 == n2) {
730
        return 0;
731
    } else {
732
        return -1;
733
    }
734
}

powered by: WebSVN 2.1.0

© copyright 1999-2024 OpenCores.org, equivalent to Oliscience, all rights reserved. OpenCores®, registered trademark.