001: /*
002: * PackageCmd.java --
003: *
004: * This class implements the built-in "package" command in Tcl.
005: *
006: * Copyright (c) 1997 by Sun Microsystems, Inc.
007: *
008: * See the file "license.terms" for information on usage and redistribution
009: * of this file, and for a DISCLAIMER OF ALL WARRANTIES.
010: *
011: * RCS: @(#) $Id: PackageCmd.java,v 1.7 2006/01/26 19:49:18 mdejong Exp $
012: */
013:
014: package tcl.lang;
015:
016: import java.util.*;
017:
018: class PackageCmd implements Command {
019:
020: private static final String[] validCmds = { "forget", "ifneeded",
021: "names", "present", "provide", "require", "unknown",
022: "vcompare", "versions", "vsatisfies" };
023:
024: static final private int OPT_FORGET = 0;
025: static final private int OPT_IFNEEDED = 1;
026: static final private int OPT_NAMES = 2;
027: static final private int OPT_PRESENT = 3;
028: static final private int OPT_PROVIDE = 4;
029: static final private int OPT_REQUIRE = 5;
030: static final private int OPT_UNKNOWN = 6;
031: static final private int OPT_VCOMPARE = 7;
032: static final private int OPT_VERSIONS = 8;
033: static final private int OPT_VSATISFIES = 9;
034:
035: /*
036: *----------------------------------------------------------------------
037: *
038: * pkgProvide --
039: *
040: * This procedure is invoked to declare that a particular version
041: * of a particular package is now present in an interpreter. There
042: * must not be any other version of this package already
043: * provided in the interpreter.
044: *
045: * Results:
046: * Normally does nothing; if there is already another version
047: * of the package loaded then an error is raised.
048: *
049: * Side effects:
050: * The interpreter remembers that this package is available,
051: * so that no other version of the package may be provided for
052: * the interpreter.
053: *
054: *----------------------------------------------------------------------
055: */
056:
057: static void pkgProvide(Interp interp, // Interpreter in which package is now
058: // available.
059: String pkgName, // Name of package.
060: String version) // Version string for package.
061: throws TclException {
062: Package pkg;
063:
064: // Validate the version string that was passed in.
065:
066: checkVersion(interp, version);
067: pkg = findPackage(interp, pkgName);
068: if (pkg.version == null) {
069: pkg.version = version;
070: return;
071: }
072: if (compareVersions(pkg.version, version, null) != 0) {
073: throw new TclException(interp,
074: "conflicting versions provided for package \""
075: + pkgName + "\": " + pkg.version
076: + ", then " + version);
077: }
078: }
079:
080: /*
081: *----------------------------------------------------------------------
082: *
083: * pkgRequire --
084: *
085: * This procedure is called by code that depends on a particular
086: * version of a particular package. If the package is not already
087: * provided in the interpreter, this procedure invokes a Tcl script
088: * to provide it. If the package is already provided, this
089: * procedure makes sure that the caller's needs don't conflict with
090: * the version that is present.
091: *
092: * Results:
093: * If successful, returns the version string for the currently
094: * provided version of the package, which may be different from
095: * the "version" argument. If the caller's requirements
096: * cannot be met (e.g. the version requested conflicts with
097: * a currently provided version, or the required version cannot
098: * be found, or the script to provide the required version
099: * generates an error), a TclException is raised.
100: *
101: * Side effects:
102: * The script from some previous "package ifneeded" command may
103: * be invoked to provide the package.
104: *
105: *----------------------------------------------------------------------
106: */
107:
108: static String pkgRequire(Interp interp, // Interpreter in which package is now
109: // available.
110: String pkgName, // Name of desired package.
111: String version, // Version string for desired version;
112: // null means use the latest version
113: // available.
114: boolean exact) // true means that only the particular
115: // version given is acceptable. false means
116: // use the latest compatible version.
117: throws TclException {
118: VersionSatisfiesResult vsres;
119: Package pkg;
120: PkgAvail avail, best;
121: String script;
122: StringBuffer sbuf;
123: int pass, result;
124:
125: // Do extra check to make sure that version is not
126: // null when the exact flag is set to true.
127:
128: if (version == null && exact) {
129: throw new TclException(interp,
130: "conflicting arguments : version == null and exact == true");
131: }
132:
133: // Before we can compare versions the version string
134: // must be verified but if it is null we are just looking
135: // for the latest version so skip the check in this case.
136:
137: if (version != null) {
138: checkVersion(interp, version);
139: }
140:
141: // It can take up to three passes to find the package: one pass to
142: // run the "package unknown" script, one to run the "package ifneeded"
143: // script for a specific version, and a final pass to lookup the
144: // package loaded by the "package ifneeded" script.
145:
146: vsres = new VersionSatisfiesResult();
147: for (pass = 1;; pass++) {
148: pkg = findPackage(interp, pkgName);
149: if (pkg.version != null) {
150: break;
151: }
152:
153: // The package isn't yet present. Search the list of available
154: // versions and invoke the script for the best available version.
155:
156: best = null;
157: for (avail = pkg.avail; avail != null; avail = avail.next) {
158: if ((best != null)
159: && (compareVersions(avail.version,
160: best.version, null) <= 0)) {
161: continue;
162: }
163: if (version != null) {
164: result = compareVersions(avail.version, version,
165: vsres);
166: if ((result != 0) && exact) {
167: continue;
168: }
169: if (!vsres.satisfies) {
170: continue;
171: }
172: }
173: best = avail;
174: }
175: if (best != null) {
176: // We found an ifneeded script for the package. Be careful while
177: // executing it: this could cause reentrancy, so (a) protect the
178: // script itself from deletion and (b) don't assume that best
179: // will still exist when the script completes.
180:
181: script = best.script;
182: try {
183: interp.eval(script, TCL.EVAL_GLOBAL);
184: } catch (TclException e) {
185: interp
186: .addErrorInfo("\n (\"package ifneeded\" script)");
187:
188: // Throw the error with new info added to errorInfo.
189:
190: throw e;
191: }
192: interp.resetResult();
193: pkg = findPackage(interp, pkgName);
194: break;
195: }
196:
197: // Package not in the database. If there is a "package unknown"
198: // command, invoke it (but only on the first pass; after that,
199: // we should not get here in the first place).
200:
201: if (pass > 1) {
202: break;
203: }
204: script = interp.packageUnknown;
205: if (script != null) {
206: sbuf = new StringBuffer();
207: try {
208: Util.appendElement(interp, sbuf, script);
209: Util.appendElement(interp, sbuf, pkgName);
210: if (version == null) {
211: Util.appendElement(interp, sbuf, "");
212: } else {
213: Util.appendElement(interp, sbuf, version);
214: }
215: if (exact) {
216: Util.appendElement(interp, sbuf, "-exact");
217: }
218: } catch (TclException e) {
219: throw new TclRuntimeError(
220: "unexpected TclException: " + e);
221: }
222: try {
223: interp.eval(sbuf.toString(), TCL.EVAL_GLOBAL);
224: } catch (TclException e) {
225: interp
226: .addErrorInfo("\n (\"package unknown\" script)");
227:
228: // Throw the first exception.
229:
230: throw e;
231: }
232: interp.resetResult();
233: }
234: }
235: if (pkg.version == null) {
236: sbuf = new StringBuffer();
237: sbuf.append("can't find package " + pkgName);
238: if (version != null) {
239: sbuf.append(" " + version);
240: }
241: throw new TclException(interp, sbuf.toString());
242: }
243:
244: // At this point we know that the package is present. Make sure that the
245: // provided version meets the current requirement.
246:
247: if (version == null) {
248: return pkg.version;
249: }
250:
251: result = compareVersions(pkg.version, version, vsres);
252: if ((vsres.satisfies && !exact) || (result == 0)) {
253: return pkg.version;
254: }
255:
256: // If we have a version conflict we throw a TclException.
257:
258: throw new TclException(interp,
259: "version conflict for package \"" + pkgName
260: + "\": have " + pkg.version + ", need "
261: + version);
262: }
263:
264: /*
265: *----------------------------------------------------------------------
266: *
267: * Tcl_PkgPresent -> pkgPresent
268: *
269: * Checks to see whether the specified package is present. If it
270: * is not then no additional action is taken.
271: *
272: * Results:
273: * If successful, returns the version string for the currently
274: * provided version of the package, which may be different from
275: * the "version" argument. If the caller's requirements
276: * cannot be met (e.g. the version requested conflicts with
277: * a currently provided version), a TclException is raised.
278: *
279: * Side effects:
280: * None.
281: *
282: *----------------------------------------------------------------------
283: */
284: static String pkgPresent(Interp interp, // Interpreter in which package is now
285: // available.
286: String pkgName, // Name of desired package.
287: String version, // Version string for desired version;
288: // null means use the latest version
289: // available.
290: boolean exact) // true means that only the particular
291: // version given is acceptable. false means
292: // use the latest compatible version.
293: throws TclException {
294: Package pkg;
295: VersionSatisfiesResult vsres = new VersionSatisfiesResult();
296: int result;
297:
298: pkg = (Package) interp.packageTable.get(pkgName);
299: if (pkg != null) {
300: if (pkg.version != null) {
301:
302: // At this point we know that the package is present. Make sure
303: // that the provided version meets the current requirement.
304:
305: if (version == null) {
306: return pkg.version;
307: }
308: result = compareVersions(pkg.version, version, vsres);
309: if ((vsres.satisfies && !exact) || (result == 0)) {
310: return pkg.version;
311: }
312: throw new TclException(interp,
313: "version conflict for package \"" + pkgName
314: + "\": have " + pkg.version + ", need "
315: + version);
316: }
317: }
318:
319: if (version != null) {
320: throw new TclException(interp, "package " + pkgName + " "
321: + version + " is not present");
322: } else {
323: throw new TclException(interp, "package " + pkgName
324: + " is not present");
325: }
326: }
327:
328: /*
329: *----------------------------------------------------------------------
330: *
331: * cmdProc --
332: *
333: * This procedure is invoked to process the "package" Tcl command.
334: * See the user documentation for details on what it does.
335: *
336: * Side effects:
337: * |>None.<|
338: *
339: *----------------------------------------------------------------------
340: */
341:
342: public void cmdProc(Interp interp, // The current interpreter.
343: TclObject[] objv) // Command arguments.
344: throws TclException // Thrown if an error occurs.
345: {
346: VersionSatisfiesResult vsres;
347: Package pkg;
348: PkgAvail avail;
349: PkgAvail prev;
350: String version;
351: String pkgName;
352: String key;
353: String cmd;
354: String ver1, ver2;
355: StringBuffer sbuf;
356: Enumeration e;
357: int i, opt, exact;
358: boolean once;
359:
360: if (objv.length < 2) {
361: throw new TclNumArgsException(interp, 1, objv,
362: "option ?arg arg ...?");
363: }
364: opt = TclIndex.get(interp, objv[1], validCmds, "option", 0);
365: switch (opt) {
366: case OPT_FORGET: {
367: // Forget takes 0 or more arguments.
368:
369: for (i = 2; i < objv.length; i++) {
370: // We do not need to check to make sure
371: // package name is "" because it would not
372: // be in the hash table so name will be ignored.
373:
374: pkgName = objv[i].toString();
375: pkg = (Package) interp.packageTable.get(pkgName);
376:
377: // If this package does not exist, go to next one.
378:
379: if (pkg == null) {
380: continue;
381: }
382: interp.packageTable.remove(pkgName);
383: while (pkg.avail != null) {
384: avail = pkg.avail;
385: pkg.avail = avail.next;
386: avail = null;
387: }
388: pkg = null;
389: }
390: return;
391: }
392: case OPT_IFNEEDED: {
393: if ((objv.length < 4) || (objv.length > 5)) {
394: throw new TclNumArgsException(interp, 1, objv,
395: "ifneeded package version ?script?");
396: }
397: pkgName = objv[2].toString();
398: version = objv[3].toString();
399:
400: // Verify that this version string is valid.
401:
402: checkVersion(interp, version);
403: if (objv.length == 4) {
404: pkg = (Package) interp.packageTable.get(pkgName);
405: if (pkg == null)
406: return;
407:
408: } else {
409: pkg = findPackage(interp, pkgName);
410: }
411: for (avail = pkg.avail, prev = null; avail != null; prev = avail, avail = avail.next) {
412: if (compareVersions(avail.version, version, null) == 0) {
413: if (objv.length == 4) {
414: // If doing a query return current script.
415:
416: interp.setResult(avail.script);
417: return;
418: }
419:
420: // We matched so we must be setting the script.
421:
422: break;
423: }
424: }
425:
426: // When we do not match on a query return nothing.
427:
428: if (objv.length == 4) {
429: return;
430: }
431: if (avail == null) {
432: avail = new PkgAvail();
433: avail.version = version;
434: if (prev == null) {
435: avail.next = pkg.avail;
436: pkg.avail = avail;
437: } else {
438: avail.next = prev.next;
439: prev.next = avail;
440: }
441: }
442: avail.script = objv[4].toString();
443: return;
444: }
445: case OPT_NAMES: {
446: if (objv.length != 2) {
447: throw new TclNumArgsException(interp, 1, objv, "names");
448: }
449:
450: try {
451: sbuf = new StringBuffer();
452: once = false;
453: for (Iterator iter = interp.packageTable.entrySet()
454: .iterator(); iter.hasNext();) {
455: Map.Entry entry = (Map.Entry) iter.next();
456: key = (String) entry.getKey();
457: pkg = (Package) entry.getValue();
458: once = true;
459: if ((pkg.version != null) || (pkg.avail != null)) {
460: Util.appendElement(interp, sbuf, key);
461: }
462: }
463: if (once) {
464: interp.setResult(sbuf.toString());
465: }
466: } catch (TclException ex) {
467: throw new TclRuntimeError("unexpected TclException: "
468: + ex);
469: }
470: return;
471: }
472: case OPT_PRESENT: {
473: if (objv.length < 3) {
474: throw new TclNumArgsException(interp, 2, objv,
475: "?-exact? package ?version?");
476: }
477: if (objv[2].toString().equals("-exact")) {
478: exact = 1;
479: } else {
480: exact = 0;
481: }
482:
483: version = null;
484: if (objv.length == (4 + exact)) {
485: version = objv[3 + exact].toString();
486: checkVersion(interp, version);
487: } else if ((objv.length != 3) || (exact == 1)) {
488: throw new TclNumArgsException(interp, 2, objv,
489: "?-exact? package ?version?");
490: }
491: if (exact == 1) {
492: version = pkgPresent(interp, objv[3].toString(),
493: version, true);
494: } else {
495: version = pkgPresent(interp, objv[2].toString(),
496: version, false);
497: }
498: interp.setResult(version);
499: break;
500: }
501: case OPT_PROVIDE: {
502: if ((objv.length < 3) || (objv.length > 4)) {
503: throw new TclNumArgsException(interp, 1, objv,
504: "provide package ?version?");
505: }
506: if (objv.length == 3) {
507: pkg = (Package) interp.packageTable.get(objv[2]
508: .toString());
509: if (pkg != null) {
510: if (pkg.version != null) {
511: interp.setResult(pkg.version);
512: }
513: }
514: return;
515: }
516: pkgProvide(interp, objv[2].toString(), objv[3].toString());
517: return;
518: }
519: case OPT_REQUIRE: {
520: if ((objv.length < 3) || (objv.length > 5)) {
521: throw new TclNumArgsException(interp, 1, objv,
522: "require ?-exact? package ?version?");
523: }
524: if (objv[2].toString().equals("-exact")) {
525: exact = 1;
526: } else {
527: exact = 0;
528: }
529: version = null;
530: if (objv.length == (4 + exact)) {
531: version = objv[3 + exact].toString();
532: checkVersion(interp, version);
533: } else if ((objv.length != 3) || (exact == 1)) {
534: throw new TclNumArgsException(interp, 1, objv,
535: "require ?-exact? package ?version?");
536: }
537: if (exact == 1) {
538: version = pkgRequire(interp, objv[3].toString(),
539: version, true);
540: } else {
541: version = pkgRequire(interp, objv[2].toString(),
542: version, false);
543: }
544: interp.setResult(version);
545: return;
546: }
547: case OPT_UNKNOWN: {
548: if (objv.length > 3) {
549: throw new TclNumArgsException(interp, 1, objv,
550: "unknown ?command?");
551: }
552: if (objv.length == 2) {
553: if (interp.packageUnknown != null) {
554: interp.setResult(interp.packageUnknown);
555: }
556: } else if (objv.length == 3) {
557: interp.packageUnknown = null;
558: cmd = objv[2].toString();
559: if (cmd.length() > 0) {
560: interp.packageUnknown = cmd;
561: }
562: }
563: return;
564: }
565: case OPT_VCOMPARE: {
566: if (objv.length != 4) {
567: throw new TclNumArgsException(interp, 1, objv,
568: "vcompare version1 version2");
569: }
570: ver1 = objv[2].toString();
571: ver2 = objv[3].toString();
572: checkVersion(interp, ver1);
573: checkVersion(interp, ver2);
574: interp.setResult(compareVersions(ver1, ver2, null));
575: return;
576: }
577: case OPT_VERSIONS: {
578: if (objv.length != 3) {
579: throw new TclNumArgsException(interp, 1, objv,
580: "versions package");
581: }
582: pkg = (Package) interp.packageTable.get(objv[2].toString());
583: if (pkg != null) {
584: try {
585: sbuf = new StringBuffer();
586: once = false;
587: for (avail = pkg.avail; avail != null; avail = avail.next) {
588: once = true;
589: Util.appendElement(interp, sbuf, avail.version);
590: }
591: if (once) {
592: interp.setResult(sbuf.toString());
593: }
594: } catch (TclException ex) {
595: throw new TclRuntimeError(
596: "unexpected TclException: " + ex);
597: }
598: }
599: return;
600: }
601: case OPT_VSATISFIES: {
602: if (objv.length != 4) {
603: throw new TclNumArgsException(interp, 1, objv,
604: "vsatisfies version1 version2");
605: }
606:
607: ver1 = objv[2].toString();
608: ver2 = objv[3].toString();
609: checkVersion(interp, ver1);
610: checkVersion(interp, ver2);
611: vsres = new VersionSatisfiesResult();
612: compareVersions(ver1, ver2, vsres);
613: interp.setResult(vsres.satisfies);
614: return;
615: }
616: default: {
617: throw new TclRuntimeError("TclIndex.get() error");
618: }
619: } // end switch(opt)
620: }
621:
622: /*
623: *----------------------------------------------------------------------
624: *
625: * findPackage --
626: *
627: * This procedure finds the Package record for a particular package
628: * in a particular interpreter, creating a record if one doesn't
629: * already exist.
630: *
631: * Results:
632: * The return value is a ref to the Package record for the
633: * package.
634: *
635: * Side effects:
636: * A new Package record may be created.
637: *
638: *----------------------------------------------------------------------
639: */
640:
641: private static Package findPackage(Interp interp, // Interpreter to use for package lookup.
642: String pkgName) // Name of package to find.
643: throws TclException {
644: Package pkg;
645:
646: // check package name to make sure it is not null or "".
647:
648: if (pkgName == null || pkgName.length() == 0) {
649: throw new TclException(interp,
650: "expected package name but got \"\"");
651: }
652:
653: pkg = (Package) interp.packageTable.get(pkgName);
654: if (pkg == null) {
655: // We should add a package with this name.
656:
657: pkg = new Package();
658: interp.packageTable.put(pkgName, pkg);
659: }
660: return pkg;
661: }
662:
663: /*
664: *----------------------------------------------------------------------
665: *
666: * checkVersion --
667: *
668: * This procedure checks to see whether a version number has
669: * valid syntax.
670: *
671: * Results:
672: * If string is not properly formed version number then a TclException
673: * is raised.
674: *
675: * Side effects:
676: * None.
677: *
678: *----------------------------------------------------------------------
679: */
680:
681: private static void checkVersion(Interp interp, // Used for error reporting.
682: String version) // Supposedly a version number, which is
683: // groups of decimal digits separated
684: // by dots.
685: throws TclException {
686: int i, len;
687: char c, prevChar;
688: boolean error = true;
689:
690: try {
691: if ((version == null) || (version.length() == 0)) {
692: version = "";
693: return;
694: }
695: if (!Character.isDigit(version.charAt(0))) {
696: return;
697: }
698: len = version.length();
699: for (prevChar = version.charAt(0), i = 1; i < len; i++) {
700: c = version.charAt(i);
701: if (!Character.isDigit(c)
702: && ((c != '.') || (prevChar == '.'))) {
703: return;
704: }
705: prevChar = c;
706: }
707: if (prevChar != '.') {
708: error = false;
709: return;
710: }
711: } finally {
712: if (error) {
713: throw new TclException(interp,
714: "expected version number but got \"" + version
715: + "\"");
716: }
717: }
718: }
719:
720: /*
721: *----------------------------------------------------------------------
722: *
723: * compareVersions --
724: *
725: * This procedure compares two version numbers.
726: *
727: * Results:
728: * This function will return a -1 if v1 is less than v2, 0
729: * if the two version numbers are the same, and 1 if v1 is
730: * greater than v2. If the sat argument is not null then
731: * then its VersionSatisfiesResult.satisifes field will be
732: * true if v2 >= v1 and both numbers have the same major number
733: * or false otherwise.
734: *
735: * Side effects:
736: * None.
737: *
738: *----------------------------------------------------------------------
739: */
740:
741: private static int compareVersions(String v1, // Versions strings. (e.g. 2.1.3)
742: String v2, VersionSatisfiesResult vsres)
743:
744: {
745: int i;
746: int max;
747: int n1 = 0;
748: int n2 = 0;
749: boolean this IsMajor = true;
750: String[] v1ns;
751: String[] v2ns;
752:
753: // Each iteration of the following loop processes one number from
754: // each string, terminated by a ".". If those numbers don't match
755: // then the comparison is over; otherwise, we loop back for the
756: // next number.
757:
758: // This should never happen because null strings would not
759: // have gotten past the version verify.
760:
761: if ((v1 == null) || (v2 == null)) {
762: throw new TclRuntimeError(
763: "null version in package version compare");
764: }
765: v1ns = split(v1, '.');
766: v2ns = split(v2, '.');
767:
768: // We are sure there is at least one string in each array so
769: // this should never happen.
770:
771: if (v1ns.length == 0 || v2ns.length == 0) {
772: throw new TclRuntimeError("version length is 0");
773: }
774: if (v1ns.length > v2ns.length) {
775: max = v1ns.length;
776: } else {
777: max = v2ns.length;
778: }
779:
780: for (i = 0; i < max; i++) {
781: n1 = n2 = 0;
782:
783: // Grab number from each version ident if version spec
784: // ends the use a 0 as value.
785:
786: try {
787: if (i < v1ns.length) {
788: n1 = Integer.parseInt(v1ns[i]);
789: }
790: if (i < v2ns.length) {
791: n2 = Integer.parseInt(v2ns[i]);
792: }
793: } catch (NumberFormatException ex) {
794: throw new TclRuntimeError(
795: "NumberFormatException for package versions \""
796: + v1 + "\" or \"" + v2 + "\"");
797: }
798:
799: // Compare and go on to the next version number if the
800: // current numbers match.
801:
802: if (n1 != n2) {
803: break;
804: }
805: this IsMajor = false;
806: }
807: if (vsres != null) {
808: vsres.satisfies = ((n1 == n2) || ((n1 > n2) && !this IsMajor));
809: }
810: if (n1 > n2) {
811: return 1;
812: } else if (n1 == n2) {
813: return 0;
814: } else {
815: return -1;
816: }
817: }
818:
819: /*
820: *----------------------------------------------------------------------
821: *
822: * split --
823: *
824: * Util function used in version compare to split a string on a
825: * single char it is only used in the version compare function.
826: *
827: * Results:
828: * |>None.<|
829: *
830: * Side effects:
831: * |>None.<|
832: *
833: *----------------------------------------------------------------------
834: */
835:
836: static String[] split(String in, char splitchar) {
837: ArrayList words;
838: int i;
839: int len;
840: char[] str;
841: int wordstart = 0;
842:
843: // Create an array that is as big as the input
844: // str plus one for an extra split char.
845:
846: len = in.length();
847: str = new char[len + 1];
848: in.getChars(0, len, str, 0);
849: str[len++] = splitchar;
850: words = new ArrayList(5);
851:
852: for (i = 0; i < len; i++) {
853:
854: // Compare this char to the split char
855: // if they are the same the we need to
856: // add the last word to the array.
857:
858: if (str[i] == splitchar) {
859: if (wordstart <= (i - 1)) {
860: words
861: .add(new String(str, wordstart, i
862: - wordstart));
863: }
864: wordstart = (i + 1);
865: }
866: }
867:
868: // Create an array that is as big as the number
869: // of elements in the vector, copy over and return.
870:
871: String[] ret = { (String) null };
872: ret = (String[]) words.toArray(ret);
873: return ret;
874: }
875:
876: // If compare versions is called with a third argument then one of
877: // these structures needs to be created and passed in
878:
879: static class VersionSatisfiesResult {
880: boolean satisfies = false;
881: }
882:
883: // Each invocation of the "package ifneeded" command creates a class
884: // of the following type, which is used to load the package into the
885: // interpreter if it is requested with a "package require" command.
886:
887: static class PkgAvail {
888: String version = null; // Version string.
889: String script = null; // Script to invoke to provide this package version
890: PkgAvail next = null; // Next in list of available package versions
891: }
892:
893: // For each package that is known in any way to an interpreter, there
894: // is one record of the following type. These records are stored in
895: // the "packageTable" hash table in the interpreter, keyed by
896: // package name such as "Tk" (no version number).
897:
898: static class Package {
899: String version = null; // Version that has been supplied in this
900: // interpreter via "package provide"
901: // null means the package doesn't
902: // exist in this interpreter yet.
903:
904: PkgAvail avail = null; // First in list of all available package versions
905: }
906:
907: } //end of class PackageCmd
|