wasCSharpSQLite – Blame information for rev 1

Subversion Repositories:
Rev:
Rev Author Line No. Line
1 office 1 /*
2 * PackageCmd.java --
3 *
4 * This class implements the built-in "package" command in Tcl.
5 *
6 * Copyright (c) 1997 by Sun Microsystems, Inc.
7 *
8 * See the file "license.terms" for information on usage and redistribution
9 * of this file, and for a DISCLAIMER OF ALL WARRANTIES.
10 *
11 * Included in SQLite3 port to C# for use in testharness only; 2008 Noah B Hart
12 *
13 * RCS @(#) $Id: PackageCmd.java,v 1.4 2002/04/12 21:00:26 mdejong Exp $
14 */
15 using System.Collections;
16 using System.Text;
17  
18 namespace tcl.lang
19 {
20  
21 class PackageCmd : Command
22 {
23  
24 private static readonly string[] validCmds = new string[] { "forget", "ifneeded", "names", "present", "provide", "require", "unknown", "vcompare", "versions", "vsatisfies" };
25  
26 private const int OPT_FORGET = 0;
27 private const int OPT_IFNEEDED = 1;
28 private const int OPT_NAMES = 2;
29 private const int OPT_PRESENT = 3;
30 private const int OPT_PROVIDE = 4;
31 private const int OPT_REQUIRE = 5;
32 private const int OPT_UNKNOWN = 6;
33 private const int OPT_VCOMPARE = 7;
34 private const int OPT_VERSIONS = 8;
35 private const int OPT_VSATISFIES = 9;
36 internal static void pkgProvide( Interp interp, string pkgName, string version )
37 {
38 Package pkg;
39  
40 // Validate the version string that was passed in.
41  
42 checkVersion( interp, version );
43 pkg = findPackage( interp, pkgName );
44 if ( (System.Object)pkg.version == null )
45 {
46 pkg.version = version;
47 return;
48 }
49 if ( compareVersions( pkg.version, version, null ) != 0 )
50 {
51 throw new TclException( interp, "conflicting versions provided for package \"" + pkgName + "\": " + pkg.version + ", then " + version );
52 }
53 }
54 internal static string pkgRequire( Interp interp, string pkgName, string version, bool exact )
55 {
56 VersionSatisfiesResult vsres;
57 Package pkg;
58 PkgAvail avail, best;
59 string script;
60 StringBuilder sbuf;
61 int pass, result;
62  
63 // Do extra check to make sure that version is not
64 // null when the exact flag is set to true.
65  
66 if ( (System.Object)version == null && exact )
67 {
68 throw new TclException( interp, "conflicting arguments : version == null and exact == true" );
69 }
70  
71 // Before we can compare versions the version string
72 // must be verified but if it is null we are just looking
73 // for the latest version so skip the check in this case.
74  
75 if ( (System.Object)version != null )
76 {
77 checkVersion( interp, version );
78 }
79  
80 // It can take up to three passes to find the package: one pass to
81 // run the "package unknown" script, one to run the "package ifneeded"
82 // script for a specific version, and a final pass to lookup the
83 // package loaded by the "package ifneeded" script.
84  
85 vsres = new VersionSatisfiesResult();
86 for ( pass = 1; ; pass++ )
87 {
88 pkg = findPackage( interp, pkgName );
89 if ( (System.Object)pkg.version != null )
90 {
91 break;
92 }
93  
94 // The package isn't yet present. Search the list of available
95 // versions and invoke the script for the best available version.
96  
97 best = null;
98 for ( avail = pkg.avail; avail != null; avail = avail.next )
99 {
100 if ( ( best != null ) && ( compareVersions( avail.version, best.version, null ) <= 0 ) )
101 {
102 continue;
103 }
104 if ( (System.Object)version != null )
105 {
106 result = compareVersions( avail.version, version, vsres );
107 if ( ( result != 0 ) && exact )
108 {
109 continue;
110 }
111 if ( !vsres.satisfies )
112 {
113 continue;
114 }
115 }
116 best = avail;
117 }
118 if ( best != null )
119 {
120 // We found an ifneeded script for the package. Be careful while
121 // executing it: this could cause reentrancy, so (a) protect the
122 // script itself from deletion and (b) don't assume that best
123 // will still exist when the script completes.
124  
125 script = best.script;
126 try
127 {
128 interp.eval( script, TCL.EVAL_GLOBAL );
129 }
130 catch ( TclException e )
131 {
132 interp.addErrorInfo( "\n (\"package ifneeded\" script)" );
133  
134 // Throw the error with new info added to errorInfo.
135  
136 throw;
137 }
138 interp.resetResult();
139 pkg = findPackage( interp, pkgName );
140 break;
141 }
142  
143 // Package not in the database. If there is a "package unknown"
144 // command, invoke it (but only on the first pass; after that,
145 // we should not get here in the first place).
146  
147 if ( pass > 1 )
148 {
149 break;
150 }
151 script = interp.packageUnknown;
152 if ( (System.Object)script != null )
153 {
154 sbuf = new StringBuilder();
155 try
156 {
157 Util.appendElement( interp, sbuf, script );
158 Util.appendElement( interp, sbuf, pkgName );
159 if ( (System.Object)version == null )
160 {
161 Util.appendElement( interp, sbuf, "" );
162 }
163 else
164 {
165 Util.appendElement( interp, sbuf, version );
166 }
167 if ( exact )
168 {
169 Util.appendElement( interp, sbuf, "-exact" );
170 }
171 }
172 catch ( TclException e )
173 {
174 throw new TclRuntimeError( "unexpected TclException: " + e.Message );
175 }
176 try
177 {
178 interp.eval( sbuf.ToString(), TCL.EVAL_GLOBAL );
179 }
180 catch ( TclException e )
181 {
182 interp.addErrorInfo( "\n (\"package unknown\" script)" );
183  
184 // Throw the first exception.
185  
186 throw;
187 }
188 interp.resetResult();
189 }
190 }
191 if ( (System.Object)pkg.version == null )
192 {
193 sbuf = new StringBuilder();
194 sbuf.Append( "can't find package " + pkgName );
195 if ( (System.Object)version != null )
196 {
197 sbuf.Append( " " + version );
198 }
199 throw new TclException( interp, sbuf.ToString() );
200 }
201  
202 // At this point we know that the package is present. Make sure that the
203 // provided version meets the current requirement.
204  
205 if ( (System.Object)version == null )
206 {
207 return pkg.version;
208 }
209  
210 result = compareVersions( pkg.version, version, vsres );
211 if ( ( vsres.satisfies && !exact ) || ( result == 0 ) )
212 {
213 return pkg.version;
214 }
215  
216 // If we have a version conflict we throw a TclException.
217  
218 throw new TclException( interp, "version conflict for package \"" + pkgName + "\": have " + pkg.version + ", need " + version );
219 }
220 internal static string pkgPresent( Interp interp, string pkgName, string version, bool exact )
221 {
222 Package pkg;
223 VersionSatisfiesResult vsres = new VersionSatisfiesResult();
224 int result;
225  
226 pkg = (Package)interp.packageTable[pkgName];
227 if ( pkg != null )
228 {
229 if ( (System.Object)pkg.version != null )
230 {
231  
232 // At this point we know that the package is present. Make sure
233 // that the provided version meets the current requirement.
234  
235 if ( (System.Object)version == null )
236 {
237 return pkg.version;
238 }
239 result = compareVersions( pkg.version, version, vsres );
240 if ( ( vsres.satisfies && !exact ) || ( result == 0 ) )
241 {
242 return pkg.version;
243 }
244 throw new TclException( interp, "version conflict for package \"" + pkgName + "\": have " + pkg.version + ", need " + version );
245 }
246 }
247  
248 if ( (System.Object)version != null )
249 {
250 throw new TclException( interp, "package " + pkgName + " " + version + " is not present" );
251 }
252 else
253 {
254 throw new TclException( interp, "package " + pkgName + " is not present" );
255 }
256 }
257 public TCL.CompletionCode cmdProc( Interp interp, TclObject[] objv )
258 {
259 VersionSatisfiesResult vsres;
260 Package pkg;
261 PkgAvail avail;
262 PkgAvail prev;
263 string version;
264 string pkgName;
265 string key;
266 string cmd;
267 string ver1, ver2;
268 StringBuilder sbuf;
269 IDictionaryEnumerator enum_Renamed;
270 int i, opt, exact;
271 bool once;
272  
273 if ( objv.Length < 2 )
274 {
275 throw new TclNumArgsException( interp, 1, objv, "option ?arg arg ...?" );
276 }
277 opt = TclIndex.get( interp, objv[1], validCmds, "option", 0 );
278 switch ( opt )
279 {
280  
281 case OPT_FORGET:
282 {
283 // Forget takes 0 or more arguments.
284  
285 for ( i = 2; i < objv.Length; i++ )
286 {
287 // We do not need to check to make sure
288 // package name is "" because it would not
289 // be in the hash table so name will be ignored.
290  
291  
292 pkgName = objv[i].ToString();
293 pkg = (Package)interp.packageTable[pkgName];
294  
295 // If this package does not exist, go to next one.
296  
297 if ( pkg == null )
298 {
299 continue;
300 }
301 SupportClass.HashtableRemove( interp.packageTable, pkgName );
302 while ( pkg.avail != null )
303 {
304 avail = pkg.avail;
305 pkg.avail = avail.next;
306 avail = null;
307 }
308 pkg = null;
309 }
310 return TCL.CompletionCode.RETURN;
311 }
312  
313 case OPT_IFNEEDED:
314 {
315 if ( ( objv.Length < 4 ) || ( objv.Length > 5 ) )
316 {
317 throw new TclNumArgsException( interp, 1, objv, "ifneeded package version ?script?" );
318 }
319 pkgName = objv[2].ToString();
320 version = objv[3].ToString();
321  
322 // Verify that this version string is valid.
323  
324 checkVersion( interp, version );
325 if ( objv.Length == 4 )
326 {
327 pkg = (Package)interp.packageTable[pkgName];
328 if ( pkg == null )
329 return TCL.CompletionCode.RETURN;
330 }
331 else
332 {
333 pkg = findPackage( interp, pkgName );
334 }
335 for ( avail = pkg.avail, prev = null; avail != null; prev = avail, avail = avail.next )
336 {
337 if ( compareVersions( avail.version, version, null ) == 0 )
338 {
339 if ( objv.Length == 4 )
340 {
341 // If doing a query return current script.
342  
343 interp.setResult( avail.script );
344 return TCL.CompletionCode.RETURN;
345 }
346  
347 // We matched so we must be setting the script.
348  
349 break;
350 }
351 }
352  
353 // When we do not match on a query return nothing.
354  
355 if ( objv.Length == 4 )
356 {
357 return TCL.CompletionCode.RETURN;
358 }
359 if ( avail == null )
360 {
361 avail = new PkgAvail();
362 avail.version = version;
363 if ( prev == null )
364 {
365 avail.next = pkg.avail;
366 pkg.avail = avail;
367 }
368 else
369 {
370 avail.next = prev.next;
371 prev.next = avail;
372 }
373 }
374  
375 avail.script = objv[4].ToString();
376 return TCL.CompletionCode.RETURN;
377 }
378  
379 case OPT_NAMES:
380 {
381 if ( objv.Length != 2 )
382 {
383 throw new TclNumArgsException( interp, 1, objv, "names" );
384 }
385  
386 try
387 {
388 sbuf = new StringBuilder();
389 enum_Renamed = interp.packageTable.GetEnumerator();
390 once = false;
391 while ( enum_Renamed.MoveNext() )
392 {
393 once = true;
394 key = ( (string)enum_Renamed.Current );
395 pkg = (Package)enum_Renamed.Value;
396 if ( ( (System.Object)pkg.version != null ) || ( pkg.avail != null ) )
397 {
398 Util.appendElement( interp, sbuf, key );
399 }
400 }
401 if ( once )
402 {
403 interp.setResult( sbuf.ToString() );
404 }
405 }
406 catch ( TclException e )
407 {
408  
409 throw new TclRuntimeError( "unexpected TclException: " + e );
410 }
411 return TCL.CompletionCode.RETURN;
412 }
413  
414 case OPT_PRESENT:
415 {
416 if ( objv.Length < 3 )
417 {
418 throw new TclNumArgsException( interp, 2, objv, "?-exact? package ?version?" );
419 }
420  
421 if ( objv[2].ToString().Equals( "-exact" ) )
422 {
423 exact = 1;
424 }
425 else
426 {
427 exact = 0;
428 }
429  
430 version = null;
431 if ( objv.Length == ( 4 + exact ) )
432 {
433  
434 version = objv[3 + exact].ToString();
435 checkVersion( interp, version );
436 }
437 else if ( ( objv.Length != 3 ) || ( exact == 1 ) )
438 {
439 throw new TclNumArgsException( interp, 2, objv, "?-exact? package ?version?" );
440 }
441 if ( exact == 1 )
442 {
443  
444 version = pkgPresent( interp, objv[3].ToString(), version, true );
445 }
446 else
447 {
448  
449 version = pkgPresent( interp, objv[2].ToString(), version, false );
450 }
451 interp.setResult( version );
452 break;
453 }
454  
455 case OPT_PROVIDE:
456 {
457 if ( ( objv.Length < 3 ) || ( objv.Length > 4 ) )
458 {
459 throw new TclNumArgsException( interp, 1, objv, "provide package ?version?" );
460 }
461 if ( objv.Length == 3 )
462 {
463  
464 pkg = (Package)interp.packageTable[objv[2].ToString()];
465 if ( pkg != null )
466 {
467 if ( (System.Object)pkg.version != null )
468 {
469 interp.setResult( pkg.version );
470 }
471 }
472 return TCL.CompletionCode.RETURN;
473 }
474  
475 pkgProvide( interp, objv[2].ToString(), objv[3].ToString() );
476 return TCL.CompletionCode.RETURN;
477 }
478  
479 case OPT_REQUIRE:
480 {
481 if ( ( objv.Length < 3 ) || ( objv.Length > 5 ) )
482 {
483 throw new TclNumArgsException( interp, 1, objv, "require ?-exact? package ?version?" );
484 }
485  
486 if ( objv[2].ToString().Equals( "-exact" ) )
487 {
488 exact = 1;
489 }
490 else
491 {
492 exact = 0;
493 }
494 version = null;
495 if ( objv.Length == ( 4 + exact ) )
496 {
497  
498 version = objv[3 + exact].ToString();
499 checkVersion( interp, version );
500 }
501 else if ( ( objv.Length != 3 ) || ( exact == 1 ) )
502 {
503 throw new TclNumArgsException( interp, 1, objv, "require ?-exact? package ?version?" );
504 }
505 if ( exact == 1 )
506 {
507  
508 version = pkgRequire( interp, objv[3].ToString(), version, true );
509 }
510 else
511 {
512  
513 version = pkgRequire( interp, objv[2].ToString(), version, false );
514 }
515 interp.setResult( version );
516 return TCL.CompletionCode.RETURN;
517 }
518  
519 case OPT_UNKNOWN:
520 {
521 if ( objv.Length > 3 )
522 {
523 throw new TclNumArgsException( interp, 1, objv, "unknown ?command?" );
524 }
525 if ( objv.Length == 2 )
526 {
527 if ( (System.Object)interp.packageUnknown != null )
528 {
529 interp.setResult( interp.packageUnknown );
530 }
531 }
532 else if ( objv.Length == 3 )
533 {
534 interp.packageUnknown = null;
535  
536 cmd = objv[2].ToString();
537 if ( cmd.Length > 0 )
538 {
539 interp.packageUnknown = cmd;
540 }
541 }
542 return TCL.CompletionCode.RETURN;
543 }
544  
545 case OPT_VCOMPARE:
546 {
547 if ( objv.Length != 4 )
548 {
549 throw new TclNumArgsException( interp, 1, objv, "vcompare version1 version2" );
550 }
551  
552 ver1 = objv[2].ToString();
553  
554 ver2 = objv[3].ToString();
555 checkVersion( interp, ver1 );
556 checkVersion( interp, ver2 );
557 interp.setResult( compareVersions( ver1, ver2, null ) );
558 return TCL.CompletionCode.RETURN;
559 }
560  
561 case OPT_VERSIONS:
562 {
563 if ( objv.Length != 3 )
564 {
565 throw new TclNumArgsException( interp, 1, objv, "versions package" );
566 }
567  
568 pkg = (Package)interp.packageTable[objv[2].ToString()];
569 if ( pkg != null )
570 {
571 try
572 {
573 sbuf = new StringBuilder();
574 once = false;
575 for ( avail = pkg.avail; avail != null; avail = avail.next )
576 {
577 once = true;
578 Util.appendElement( interp, sbuf, avail.version );
579 }
580 if ( once )
581 {
582 interp.setResult( sbuf.ToString() );
583 }
584 }
585 catch ( TclException e )
586 {
587 throw new TclRuntimeError( "unexpected TclException: " + e.Message, e );
588 }
589 }
590 return TCL.CompletionCode.RETURN;
591 }
592  
593 case OPT_VSATISFIES:
594 {
595 if ( objv.Length != 4 )
596 {
597 throw new TclNumArgsException( interp, 1, objv, "vsatisfies version1 version2" );
598 }
599  
600  
601 ver1 = objv[2].ToString();
602  
603 ver2 = objv[3].ToString();
604 checkVersion( interp, ver1 );
605 checkVersion( interp, ver2 );
606 vsres = new VersionSatisfiesResult();
607 compareVersions( ver1, ver2, vsres );
608 interp.setResult( vsres.satisfies );
609 return TCL.CompletionCode.RETURN;
610 }
611  
612 default:
613 {
614 throw new TclRuntimeError( "TclIndex.get() error" );
615 }
616  
617 } // end switch(opt)
618 return TCL.CompletionCode.RETURN;
619 }
620 private static Package findPackage( Interp interp, string pkgName )
621 {
622 Package pkg;
623  
624 // check package name to make sure it is not null or "".
625  
626 if ( (System.Object)pkgName == null || pkgName.Length == 0 )
627 {
628 throw new TclException( interp, "expected package name but got \"\"" );
629 }
630  
631 pkg = (Package)interp.packageTable[pkgName];
632 if ( pkg == null )
633 {
634 // We should add a package with this name.
635  
636 pkg = new Package();
637 SupportClass.PutElement( interp.packageTable, pkgName, pkg );
638 }
639 return pkg;
640 }
641 private static void checkVersion( Interp interp, string version )
642 {
643 int i, len;
644 char c;
645 bool error = true;
646  
647 try
648 {
649 if ( ( (System.Object)version == null ) || ( version.Length == 0 ) )
650 {
651 version = "";
652 return;
653 }
654 if ( !System.Char.IsDigit( version[0] ) )
655 {
656 return;
657 }
658 len = version.Replace( ".C#", "" ).Replace( "(C#)", "" ).Replace( "C#", "" ).Length;
659 for ( i = 1; i < len; i++ )
660 {
661 c = version[i];
662 if ( !System.Char.IsDigit( c ) && ( c != '.' ) )
663 {
664 return;
665 }
666 }
667 if ( version[len - 1] == '.' )
668 {
669 return;
670 }
671 error = false;
672 }
673 finally
674 {
675 if ( error )
676 {
677 throw new TclException( interp, "expected version number but got \"" + version + "\"" );
678 }
679 }
680 }
681 private static int compareVersions( string v1, string v2, VersionSatisfiesResult vsres )
682 {
683 int i;
684 int max;
685 int n1 = 0;
686 int n2 = 0;
687 bool thisIsMajor = true;
688 string[] v1ns;
689 string[] v2ns;
690  
691 // Each iteration of the following loop processes one number from
692 // each string, terminated by a ".". If those numbers don't match
693 // then the comparison is over; otherwise, we loop back for the
694 // next number.
695  
696  
697 // This should never happen because null strings would not
698 // have gotten past the version verify.
699  
700 if ( ( (System.Object)v1 == null ) || ( (System.Object)v2 == null ) )
701 {
702 throw new TclRuntimeError( "null version in package version compare" );
703 }
704 v1ns = split( v1, '.' );
705 v2ns = split( v2, '.' );
706  
707 // We are sure there is at least one string in each array so
708 // this should never happen.
709  
710 if ( v1ns.Length == 0 || v2ns.Length == 0 )
711 {
712 throw new TclRuntimeError( "version length is 0" );
713 }
714 if ( v1ns.Length > v2ns.Length )
715 {
716 max = v1ns.Length;
717 }
718 else
719 {
720 max = v2ns.Length;
721 }
722  
723 for ( i = 0; i < max; i++ )
724 {
725 n1 = n2 = 0;
726  
727 // Grab number from each version ident if version spec
728 // ends the use a 0 as value.
729  
730 try
731 {
732 if ( i < v1ns.Length )
733 {
734 n1 = System.Int32.Parse( v1ns[i] );
735 }
736 if ( i < v2ns.Length )
737 {
738 n2 = System.Int32.Parse( v2ns[i] );
739 }
740 }
741 catch ( System.FormatException ex )
742 {
743 throw new TclRuntimeError( "NumberFormatException for package versions \"" + v1 + "\" or \"" + v2 + "\"" );
744 }
745  
746 // Compare and go on to the next version number if the
747 // current numbers match.
748  
749 if ( n1 != n2 )
750 {
751 break;
752 }
753 thisIsMajor = false;
754 }
755 if ( vsres != null )
756 {
757 vsres.satisfies = ( ( n1 == n2 ) || ( ( n1 > n2 ) && !thisIsMajor ) );
758 }
759 if ( n1 > n2 )
760 {
761 return 1;
762 }
763 else if ( n1 == n2 )
764 {
765 return 0;
766 }
767 else
768 {
769 return -1;
770 }
771 }
772 internal static string[] split( string in_Renamed, char splitchar )
773 {
774 ArrayList words;
775 string[] ret;
776 int i;
777 int len;
778 char[] str;
779 int wordstart = 0;
780  
781 // Create an array that is as big as the input
782 // str plus one for an extra split char.
783  
784 len = in_Renamed.Length;
785 str = new char[len + 1];
786 SupportClass.GetCharsFromString( in_Renamed, 0, len, ref str, 0 );
787 str[len++] = splitchar;
788 words = new ArrayList( 5 );
789  
790 for ( i = 0; i < len; i++ )
791 {
792  
793 // Compare this char to the split char
794 // if they are the same the we need to
795 // add the last word to the array.
796  
797 if ( str[i] == splitchar )
798 {
799 if ( wordstart <= ( i - 1 ) )
800 {
801 words.Add( new string( str, wordstart, i - wordstart ) );
802 }
803 wordstart = ( i + 1 );
804 }
805 }
806  
807 // Create an array that is as big as the number
808 // of elements in the vector, copy over and return.
809  
810 ret = new string[words.Count];
811 words.CopyTo( ret );
812 return ret;
813 }
814  
815  
816  
817  
818  
819  
820  
821  
822 // If compare versions is called with a third argument then one of
823 // these structures needs to be created and passed in
824  
825  
826 internal class VersionSatisfiesResult
827 {
828 internal bool satisfies = false;
829 }
830  
831 // Each invocation of the "package ifneeded" command creates a class
832 // of the following type, which is used to load the package into the
833 // interpreter if it is requested with a "package require" command.
834  
835 internal class PkgAvail
836 {
837 internal string version = null; // Version string.
838 internal string script = null; // Script to invoke to provide this package version
839 internal PkgAvail next = null; // Next in list of available package versions
840 }
841  
842  
843  
844 // For each package that is known in any way to an interpreter, there
845 // is one record of the following type. These records are stored in
846 // the "packageTable" hash table in the interpreter, keyed by
847 // package name such as "Tk" (no version number).
848  
849 internal class Package
850 {
851 internal string version = null; // Version that has been supplied in this
852 // interpreter via "package provide"
853 // null means the package doesn't
854 // exist in this interpreter yet.
855  
856 internal PkgAvail avail = null; // First in list of all available package versions
857 }
858 } //end of class PackageCmd
859 }