[Profiler] Use the correct function to increment the refcount
[mono.git] / mcs / tools / gacutil / driver.cs
1 //
2 // Mono.Tools.GacUtil
3 //
4 // Author(s):
5 //  Todd Berman <tberman@sevenl.net>
6 //  Jackson Harper <jackson@ximian.com>
7 //
8 // Copyright 2003, 2004 Todd Berman 
9 // Copyright 2004 Novell, Inc (http://www.novell.com)
10 //
11
12
13 using System;
14 using System.IO;
15 using System.Diagnostics;
16 using System.Text;
17 using System.Reflection;
18 using System.Collections;
19 using System.Globalization;
20 using System.Runtime.InteropServices;
21 using System.Security.Cryptography;
22
23 using Mono.Security;
24 using Mono.Security.Cryptography;
25
26 namespace Mono.Tools {
27
28         public class Driver {
29
30                 private enum Command {
31                         Unknown,
32                         Install,
33                         InstallFromList,
34                         Uninstall,
35                         UninstallFromList,
36                         UninstallSpecific,
37                         List,
38                         Help
39                 }
40
41                 private enum VerificationResult
42                 {
43                         StrongNamed,
44                         WeakNamed,
45                         DelaySigned,
46                         Skipped
47                 }
48
49                 private static bool silent;
50                 static bool in_bootstrap;
51
52                 public static int Main (string [] args)
53                 {
54                         if (args.Length == 0)
55                                 Usage ();
56
57                         Command command = Command.Unknown;
58                         string command_str = null;
59
60                         string libdir;
61                         string name, package, gacdir, root;
62                         name = package = root = gacdir = null;
63                         bool check_refs = false;
64
65                         // Check for silent arg first so we can suppress
66                         // warnings during command line parsing
67                         if (Array.IndexOf (args, "/silent") > -1 || Array.IndexOf (args, "-silent") > -1)
68                                 silent = true;
69
70                         for (int i=0; i<args.Length; i++) {
71                                 if (IsSwitch (args [i])) {
72
73                                         // for cmd line compatibility with other gacutils
74                                         // we always force it though
75                                         if (args [i] == "-f" || args [i] == "/f")
76                                                 continue;
77
78                                         // Ignore this option for now, although we might implement it someday
79                                         if (args [i] == "/r") {
80                                                 WriteLine ("WARNING: gacutil does not support traced references." +
81                                                         "This option is being ignored.");
82                                                 i += 3;
83                                                 continue;
84                                         }
85
86                                         // This is already handled we just dont want to choke on it
87                                         if (args [i] == "-silent" || args [i] == "/silent")
88                                                 continue; 
89
90                                         if (args [i] == "-check_refs" || args [i] == "/check_refs") {
91                                                 check_refs = true;
92                                                 continue;
93                                         }
94
95                                         if (args [i] == "-bootstrap" || args [i] == "/bootstrap") {
96                                                 in_bootstrap = true;
97                                                 continue;
98                                         }
99
100                                         if (command == Command.Unknown) {
101                                                 command = GetCommand (args [i]);
102                                                 if (command != Command.Unknown) {
103                                                         command_str = args [i];
104                                                         continue;
105                                                 }
106                                         }
107
108                                         if (i + 1 >= args.Length) {
109                                                 Console.WriteLine ("Option " + args [i] + " takes 1 argument");
110                                                 return 1;
111                                         }
112
113                                         switch (args [i]) {
114                                         case "-package":
115                                         case "/package":
116                                                 package = args [++i];
117                                                 continue;
118                                         case "-root":
119                                         case "/root":
120                                                 root = args [++i];
121                                                 continue;
122                                         case "-gacdir":
123                                         case "/gacdir":
124                                                 gacdir = args [++i];
125                                                 continue;
126                                         case "/nologo":
127                                         case "-nologo":
128                                                 // we currently don't display a
129                                                 // logo banner, so ignore it
130                                                 // for command-line compatibility
131                                                 // with MS gacutil
132                                                 continue;
133                                         }
134                                 }
135                                 if (name == null)
136                                         name = args [i];
137                                 else
138                                         name += args [i];
139                         }
140
141                         if (command == Command.Unknown && IsSwitch (args [0])) {
142                                 Console.WriteLine ("Unknown command: " + args [0]);
143                                 return 1;
144                         } else if (command == Command.Unknown) {
145                                 Usage ();
146                         } else if (command == Command.Help) {
147                                 ShowHelp (true);
148                                 return 1;
149                         }
150
151                         if (gacdir == null) {
152                                 gacdir = GetGacDir ();
153                                 libdir = GetLibDir ();
154                         } else {
155                                 gacdir = EnsureLib (gacdir);
156                                 libdir = Path.Combine (gacdir, "mono");
157                                 gacdir = Path.Combine (libdir, "gac");
158                         }
159
160                         string link_gacdir = gacdir;
161                         string link_libdir = libdir;
162                         if (root != null) {
163                                 libdir = Path.Combine (root, "mono");
164                                 gacdir = Path.Combine (libdir, "gac");
165                         }
166
167                         LoadConfig (silent);
168
169                         switch (command) {
170                         case Command.Install:
171                                 if (name == null) {
172                                         WriteLine ("Option " + command_str + " takes 1 argument");
173                                         return 1;
174                                 }
175                                 if (!Install (check_refs, name, package, gacdir, link_gacdir, libdir, link_libdir))
176                                         return 1;
177                                 break;
178                         case Command.InstallFromList:
179                                 if (name == null) {
180                                         WriteLine ("Option " + command_str + " takes 1 argument");
181                                         return 1;
182                                 }
183                                 if (!InstallFromList (check_refs, name, package, gacdir, link_gacdir, libdir, link_libdir))
184                                         return 1;
185                                 break;
186                         case Command.Uninstall:
187                                 if (name == null) {
188                                         WriteLine ("Option " + command_str + " takes 1 argument");
189                                         return 1;
190                                 }
191                                 int uninstallCount = 0;
192                                 int uninstallFailures = 0;
193                                 Uninstall (name, package, gacdir, libdir, false,
194                                         ref uninstallCount, ref uninstallFailures);
195                                 WriteLine ("Assemblies uninstalled = {0}", uninstallCount);
196                                 WriteLine ("Failures = {0}", uninstallFailures);
197                                 if (uninstallFailures > 0)
198                                         return 1;
199                                 break;
200                         case Command.UninstallFromList:
201                                 if (name == null) {
202                                         WriteLine ("Option " + command_str + " takes 1 argument");
203                                         return 1;
204                                 }
205                                 if (!UninstallFromList (name, package, gacdir, libdir))
206                                         return 1;
207                                 break;
208                         case Command.UninstallSpecific:
209                                 if (name == null) {
210                                         WriteLine ("Option " + command_str + " takes 1 argument");
211                                         return 1;
212                                 }
213                                 if (!UninstallSpecific (name, package, gacdir, libdir))
214                                         return 1;
215                                 break;
216                         case Command.List:
217                                 List (name, gacdir);
218                                 break;
219                         }
220
221                         return 0;
222                 }
223
224                 static void Copy (string source, string target, bool v)
225                 {
226                         try {
227                                 File.Delete (target);
228                         } catch {}
229                         File.Copy (source, target, v);
230                 }
231                 
232                 private static bool Install (bool check_refs, string name, string package,
233                                 string gacdir, string link_gacdir, string libdir, string link_libdir)
234                 {
235                         string failure_msg = "Failure adding assembly {0} to the cache: ";
236                         ArrayList resources;
237
238                         if (!File.Exists (name)) {
239                                 WriteLine (string.Format (failure_msg, name) + "The system cannot find the file specified.");
240                                 return false;
241                         }
242
243                         Assembly assembly = null;
244                         AssemblyName an = null;
245
246                         try {
247                                 assembly = Assembly.LoadFrom (name);
248                         } catch {
249                                 WriteLine (string.Format (failure_msg, name) + "The file specified is not a valid assembly.");
250                                 return false;
251                         }
252
253                         an = assembly.GetName ();
254
255                         switch (VerifyStrongName (an, name)) {
256                         case VerificationResult.StrongNamed:
257                         case VerificationResult.Skipped:
258                                 break;
259                         case VerificationResult.WeakNamed:
260                                 WriteLine (string.Format (failure_msg, name) + "Attempt to install an assembly without a strong name"
261                                         + (in_bootstrap ? "(continuing anyway)" : string.Empty));
262                                 if (!in_bootstrap)
263                                         return false;
264                                 break;
265                         case VerificationResult.DelaySigned:
266                                 WriteLine (string.Format (failure_msg, name) + "Strong name cannot be verified for delay-signed assembly"
267                                         + (in_bootstrap ? "(continuing anyway)" : string.Empty));
268                                 if (!in_bootstrap)
269                                         return false;
270                                 break;
271                         }
272
273                         resources = new ArrayList ();
274                         foreach (string res_name in assembly.GetManifestResourceNames ()) {
275                                 ManifestResourceInfo res_info = assembly.GetManifestResourceInfo (res_name);
276                                 
277                                 if ((res_info.ResourceLocation & ResourceLocation.Embedded) == 0) {
278                                         if (!File.Exists (res_info.FileName)) {
279                                                 WriteLine (string.Format (failure_msg, name) + "The system cannot find resource " + res_info.FileName);
280                                                 return false;
281                                         }
282
283                                         resources.Add (res_info);
284                                 }
285                         }
286
287                         if (check_refs && !CheckReferencedAssemblies (an)) {
288                                 WriteLine (string.Format (failure_msg, name) +
289                                         "Attempt to install an assembly that " +
290                                         "references non strong named assemblies " +
291                                         "with -check_refs enabled.");
292                                 return false;
293                         }
294
295                         string [] siblings = { ".config", ".mdb" };
296                         string version_token = an.Version + "_" +
297                                                an.CultureInfo.Name.ToLower (CultureInfo.InvariantCulture) + "_" +
298                                                GetStringToken (an.GetPublicKeyToken ());
299                         string full_path = Path.Combine (Path.Combine (gacdir, an.Name), version_token);
300                         string asmb_file = Path.GetFileName (name);
301                         string asmb_path = Path.Combine (full_path, asmb_file);
302                         string asmb_name = assembly.GetName ().Name;
303                         
304                         if (Path.GetFileNameWithoutExtension (asmb_file) != asmb_name) {
305                                 WriteLine (string.Format (failure_msg, name) +
306                                     string.Format ("the filename \"{0}\" doesn't match the assembly name \"{1}\"",
307                                         asmb_file, asmb_name));
308                                 return false;
309                         }
310
311                         try {
312                                 if (Directory.Exists (full_path)) {
313                                         // Wipe out the directory. This way we ensure old assemblies    
314                                         // config files, and AOTd files are removed.
315                                         Directory.Delete (full_path, true);
316                                 }
317                                 Directory.CreateDirectory (full_path);
318                         } catch {
319                                 WriteLine (string.Format (failure_msg, name) +
320                                         "gac directories could not be created, " +
321                                         "possibly permission issues.");
322                                 return false;
323                         }
324
325                         Copy (name, asmb_path, true);
326
327                         foreach (string ext in siblings) {
328                                 string sibling = String.Concat (name, ext);
329                                 if (File.Exists (sibling))
330                                         Copy (sibling, String.Concat (asmb_path, ext), true);
331                         }
332
333                         foreach (ManifestResourceInfo resource_info in resources) {
334                                 try {
335                                         Copy (resource_info.FileName, Path.Combine (full_path, Path.GetFileName (resource_info.FileName)), true);
336                                 } catch {
337                                         WriteLine ("ERROR: Could not install resource file " + resource_info.FileName);
338                                         Environment.Exit (1);
339                                 }
340                         }
341
342                         if (package != null) {
343                                 string ref_dir = Path.Combine (libdir, package);
344                                 string ref_path = Path.Combine (ref_dir, asmb_file);
345
346                                 if (File.Exists (ref_path))
347                                         File.Delete (ref_path);
348                                 try {
349                                         Directory.CreateDirectory (ref_dir);
350                                 } catch {
351                                         WriteLine ("ERROR: Could not create package dir file.");
352                                         Environment.Exit (1);
353                                 }
354                                 if (Path.DirectorySeparatorChar == '/') {
355                                         string pkg_path_abs = Path.Combine (gacdir, Path.Combine (an.Name, Path.Combine (version_token, asmb_file)));
356                                         string pkg_path = AbsoluteToRelativePath (ref_dir, pkg_path_abs);
357                                         symlink (pkg_path, ref_path);
358
359                                         foreach (string ext in siblings) {
360                                                 string sibling = String.Concat (pkg_path, ext);
361                                                 string sref = String.Concat (ref_path, ext);
362                                                 if (File.Exists (sibling))
363                                                         symlink (sibling, sref);
364                                                 else {
365                                                         try {
366                                                                 File.Delete (sref);
367                                                         } catch {
368                                                                 // Ignore error, just delete files that should not be there.
369                                                         }
370                                                 }
371                                         }
372                                         WriteLine ("Package exported to: {0} -> {1}", ref_path, pkg_path);
373                                 } else {
374                                         // string link_path = Path.Combine (Path.Combine (link_gacdir, an.Name), version_token);
375                                         //
376                                         // We can't use 'link_path' here, since it need not be a valid path at the time 'gacutil'
377                                         // is run, esp. when invoked in a DESTDIR install.
378                                         Copy (name, ref_path, true);
379                                         WriteLine ("Package exported to: " + ref_path);
380                                 }
381                         }
382
383                         WriteLine ("Installed {0} into the gac ({1})", name,
384                                 gacdir);
385
386                         return true;
387                 }
388
389                 //from MonoDevelop.Core.FileService
390                 unsafe static string AbsoluteToRelativePath (string baseDirectoryPath, string absPath)
391                 {
392                         if (!Path.IsPathRooted (absPath) || string.IsNullOrEmpty (baseDirectoryPath))
393                                 return absPath;
394
395                         absPath = Path.GetFullPath (absPath);
396                         baseDirectoryPath = Path.GetFullPath (baseDirectoryPath).TrimEnd (Path.DirectorySeparatorChar);
397
398                         fixed (char* bPtr = baseDirectoryPath, aPtr = absPath) {
399                                 var bEnd = bPtr + baseDirectoryPath.Length;
400                                 var aEnd = aPtr + absPath.Length;
401                                 char* lastStartA = aEnd;
402                                 char* lastStartB = bEnd;
403
404                                 int indx = 0;
405                                 // search common base path
406                                 var a = aPtr;
407                                 var b = bPtr;
408                                 while (a < aEnd) {
409                                         if (*a != *b)
410                                                 break;
411                                         if (IsSeparator (*a)) {
412                                                 indx++;
413                                                 lastStartA = a + 1;
414                                                 lastStartB = b;
415                                         }
416                                         a++;
417                                         b++;
418                                         if (b >= bEnd) {
419                                                 if (a >= aEnd || IsSeparator (*a)) {
420                                                         indx++;
421                                                         lastStartA = a + 1;
422                                                         lastStartB = b;
423                                                 }
424                                                 break;
425                                         }
426                                 }
427                                 if (indx == 0)
428                                         return absPath;
429
430                                 if (lastStartA >= aEnd)
431                                         return ".";
432
433                                 // handle case a: some/path b: some/path/deeper...
434                                 if (a >= aEnd) {
435                                         if (IsSeparator (*b)) {
436                                                 lastStartA = a + 1;
437                                                 lastStartB = b;
438                                         }
439                                 }
440
441                                 // look how many levels to go up into the base path
442                                 int goUpCount = 0;
443                                 while (lastStartB < bEnd) {
444                                         if (IsSeparator (*lastStartB))
445                                                 goUpCount++;
446                                         lastStartB++;
447                                 }
448                                 var size = goUpCount * 2 + goUpCount + aEnd - lastStartA;
449                                 var result = new char [size];
450                                 fixed (char* rPtr = result) {
451                                         // go paths up
452                                         var r = rPtr;
453                                         for (int i = 0; i < goUpCount; i++) {
454                                                 *(r++) = '.';
455                                                 *(r++) = '.';
456                                                 *(r++) = Path.DirectorySeparatorChar;
457                                         }
458                                         // copy the remaining absulute path
459                                         while (lastStartA < aEnd)
460                                                 *(r++) = *(lastStartA++);
461                                 }
462                                 return new string (result);
463                         }
464                 }
465
466                 static bool IsSeparator (char ch)
467                 {
468                         return ch == Path.DirectorySeparatorChar || ch == Path.AltDirectorySeparatorChar || ch == Path.VolumeSeparatorChar;
469                 }
470
471                 private static void Uninstall (string name, string package, string gacdir, string libdir, bool listMode, ref int uninstalled, ref int failures)
472                 {
473                         string [] assembly_pieces = name.Split (new char[] { ',' });
474                         Hashtable asm_info = new Hashtable ();
475
476                         foreach (string item in assembly_pieces) {
477                                 if (item == String.Empty) continue;
478                                 string[] pieces = item.Trim ().Split (new char[] { '=' }, 2);
479                                 if(pieces.Length == 1)
480                                         asm_info ["assembly"] = pieces [0];
481                                 else
482                                         asm_info [pieces[0].Trim ().ToLower (CultureInfo.InvariantCulture)] = pieces [1];
483                         }
484
485                         string assembly_name = (string) asm_info ["assembly"];
486                         string asmdir = Path.Combine (gacdir, assembly_name);
487                         if (!Directory.Exists (asmdir)) {
488                                 if (listMode) {
489                                         failures++;
490                                         WriteLine ("Assembly: " + name);
491                                 }
492                                 WriteLine ("No assemblies found that match: " + name);
493                                 return;
494                         }
495
496                         string searchString = GetSearchString (asm_info);
497                         string [] directories = Directory.GetDirectories (asmdir, searchString);
498
499                         if (directories.Length == 0) {
500                                 if (listMode) {
501                                         failures++;
502                                         WriteLine ("Assembly: " + name);
503                                         WriteLine ("No assemblies found that match: " + name);
504                                 }
505                                 return;
506                         }
507
508                         for (int i = 0; i < directories.Length; i++) {
509                                 if (listMode && i > 0)
510                                         break;
511
512                                 string dir = directories [i];
513                                 string extension = null;
514
515                                 if (File.Exists (Path.Combine (dir, assembly_name + ".dll"))) {
516                                         extension = ".dll";
517                                 } else if (File.Exists (Path.Combine (dir, assembly_name + ".exe"))) {
518                                         extension = ".exe";
519                                 } else {
520                                         failures++;
521                                         WriteLine("Cannot find the assembly: " + assembly_name);
522                                         continue;
523                                 }
524
525                                 string assembly_filename = assembly_name + extension;
526
527                                 AssemblyName an = AssemblyName.GetAssemblyName (
528                                         Path.Combine (dir, assembly_filename));
529                                 WriteLine ("Assembly: " + an.FullName);
530
531                                 Directory.Delete (dir, true);
532                                 if (package != null) {
533                                         string link_dir = Path.Combine (libdir, package);
534                                         string link = Path.Combine (link_dir, assembly_filename);
535
536                                         try { 
537                                                 File.Delete (link);
538                                         } catch {
539                                                 // The file might not exist, happens with
540                                                 // the debugger on make uninstall
541                                         }
542                                         
543                                         if (Directory.GetFiles (link_dir).Length == 0) {
544                                                 WriteLine ("Cleaning package directory, it is empty.");
545                                                 try {
546                                                         Directory.Delete (link_dir);
547                                                 } catch {
548                                                         // Workaround: GetFiles does not list Symlinks
549                                                 }
550                                         }
551                                 }
552
553                                 uninstalled++;
554                                 WriteLine ("Uninstalled: " + an.FullName);
555                         }
556
557                         if (Directory.GetDirectories (asmdir).Length == 0) {
558                                 WriteLine ("Cleaning assembly dir, it is empty");
559                                 try {
560                                         Directory.Delete (asmdir);
561                                 } catch {
562                                         // Workaround: GetFiles does not list Symlinks
563                                 }
564                         }
565                 }
566
567                 private static bool UninstallSpecific (string name, string package,
568                                 string gacdir, string libdir)
569                 {
570                         string failure_msg = "Failure to remove assembly from the cache: ";
571
572                         if (!File.Exists (name)) {
573                                 WriteLine (failure_msg + "The system cannot find the file specified.");
574                                 return false;
575                         }
576
577                         AssemblyName an = null;
578
579                         try {
580                                 an = AssemblyName.GetAssemblyName (name);
581                         } catch {
582                                 WriteLine (failure_msg + "The file specified is not a valid assembly.");
583                                 return false;
584                         }
585
586                         int uninstallCount = 0;
587                         int uninstallFailures = 0;
588                         Uninstall (an.FullName.Replace (" ", String.Empty),
589                                 package, gacdir, libdir, true, ref uninstallCount,
590                                 ref uninstallFailures);
591                         WriteLine ("Assemblies uninstalled = {0}", uninstallCount);
592                         WriteLine ("Failures = {0}", uninstallFailures);
593                         return (uninstallFailures == 0);
594                 }
595
596                 private static void List (string name, string gacdir)
597                 {
598                         WriteLine ("The following assemblies are installed into the GAC:");
599
600                         if (name != null) {
601                                 FilteredList (name, gacdir);
602                                 return;
603                         }
604
605                         int count = 0;
606                         DirectoryInfo gacinfo = new DirectoryInfo (gacdir);
607                         foreach (DirectoryInfo parent in gacinfo.GetDirectories ()) {
608                                 foreach (DirectoryInfo dir in parent.GetDirectories ()) {
609                                         string asmb = Path.Combine (Path.Combine (parent.FullName, dir.Name), parent.Name) + ".dll";
610                                         if (File.Exists (asmb)) {
611                                                 WriteLine (AsmbNameFromVersionString (parent.Name, dir.Name));
612                                                 count++;
613                                         }
614                                 }
615                         }
616                         WriteLine ("Number of items = " + count);
617                 }
618
619                 private static void FilteredList (string name, string gacdir)
620                 {
621                         string [] assembly_pieces = name.Split (new char[] { ',' });
622                         Hashtable asm_info = new Hashtable ();
623
624                         foreach (string item in assembly_pieces) {
625                                 string[] pieces = item.Trim ().Split (new char[] { '=' }, 2);
626                                 if(pieces.Length == 1)
627                                         asm_info ["assembly"] = pieces [0];
628                                 else
629                                         asm_info [pieces[0].Trim ().ToLower (CultureInfo.InvariantCulture)] = pieces [1];
630                         }
631                         
632                         string asmdir = Path.Combine (gacdir, (string) asm_info ["assembly"]);
633                         if (!Directory.Exists (asmdir)) {
634                                 WriteLine ("Number of items = 0");
635                                 return;
636                         }
637                         string search = GetSearchString (asm_info);
638                         string [] dir_list = Directory.GetDirectories (asmdir, search);
639
640                         int count = 0;
641                         foreach (string dir in dir_list) {
642                                 string asmb = Path.Combine (dir, (string) asm_info ["assembly"]) + ".dll";
643                                 if (File.Exists (asmb)) {
644                                         WriteLine (AsmbNameFromVersionString ((string) asm_info ["assembly"],
645                                                                    new DirectoryInfo (dir).Name));
646                                         count++;
647                                 }
648                         }
649                         WriteLine ("Number of items = " + count);
650                 }
651
652                 private static bool InstallFromList (bool check_refs, string list_file, string package,
653                                 string gacdir, string link_gacdir, string libdir, string link_libdir)
654                 {
655                         StreamReader s = null;
656                         int processed, failed;
657                         string listdir = Path.GetDirectoryName (
658                                 Path.GetFullPath (list_file));
659
660                         processed = failed = 0;
661
662                         try {
663                                 s = new StreamReader (list_file);
664
665                                 string line;
666                                 while ((line = s.ReadLine ()) != null) {
667                                         string file = line.Trim ();
668                                         if (file.Length == 0)
669                                                 continue;
670
671                                         string assemblyPath = Path.Combine (listdir,
672                                                 file);
673
674                                         if (!Install (check_refs, assemblyPath, package, gacdir,
675                                                      link_gacdir, libdir, link_libdir))
676                                                 failed++;
677                                         processed++;
678                                 }
679
680                                 WriteLine ("Assemblies processed = {0}", processed);
681                                 WriteLine ("Assemblies installed = {0}", processed - failed);
682                                 WriteLine ("Failures = {0}", failed);
683
684                                 return (failed == 0);
685                         } catch (IOException) {
686                                 WriteLine ("Failed to open assemblies list file " + list_file + ".");
687                                 return false;
688                         } finally {
689                                 if (s != null)
690                                         s.Close ();
691                         }
692                 }
693
694                 private static bool UninstallFromList (string list_file, string package,
695                                 string gacdir, string libdir)
696                 {
697                         StreamReader s = null;
698                         int failed, uninstalled;
699
700                         failed = uninstalled = 0;
701
702                         try {
703                                 s = new StreamReader (list_file);
704
705                                 string line;
706                                 while ((line = s.ReadLine ()) != null) {
707                                         string name = line.Trim ();
708                                         if (name.Length == 0)
709                                                 continue;
710                                         Uninstall (line, package, gacdir, libdir,
711                                                 true, ref uninstalled, ref failed);
712                                 }
713
714                                 WriteLine ("Assemblies processed = {0}", uninstalled+failed);
715                                 WriteLine ("Assemblies uninstalled = {0}", uninstalled);
716                                 WriteLine ("Failures = {0}", failed);
717
718                                 return (failed == 0);
719                         } catch (IOException) {
720                                 WriteLine ("Failed to open assemblies list file " + list_file + ".");
721                                 return false;
722                         } finally {
723                                 if (s != null)
724                                         s.Close ();
725                         }
726                 }
727
728                 private static bool CheckReferencedAssemblies (AssemblyName an)
729                 {
730                         AppDomain d = null;
731                         try {
732                                 Assembly a = Assembly.LoadFrom (an.CodeBase);
733                                 AssemblyName corlib = typeof (object).Assembly.GetName ();
734
735                                 foreach (AssemblyName ref_an in a.GetReferencedAssemblies ()) {
736                                         if (ref_an.Name == corlib.Name) // Just do a string compare so we can install on diff versions
737                                                 continue;
738                                         byte [] pt = ref_an.GetPublicKeyToken ();
739                                         if (pt == null || pt.Length == 0) {
740                                                 WriteLine ("Assembly " + ref_an.Name + " is not strong named.");
741                                                 return false;
742                                         }
743                                 }
744                         } catch (Exception e) {
745                                 WriteLine (e.ToString ()); // This should be removed pre beta3
746                                 return false;
747                         } finally {
748                                 if (d != null) {
749                                         try {
750                                                 AppDomain.Unload (d);
751                                         } catch { }
752                                 }
753                         }
754
755                         return true;
756                 }
757
758                 private static string GetSearchString (Hashtable asm_info)
759                 {
760                         if (asm_info.Keys.Count == 1)
761                                 return "*";
762                         string version, culture, token;
763
764                         version = asm_info ["version"] as string;
765                         version = (version == null ? "*" : version + "*");
766                         culture = asm_info ["culture"] as string;
767                         culture = (culture == null ? "*" : (culture == "neutral") ? String.Empty : culture.ToLower (CultureInfo.InvariantCulture));
768                         token = asm_info ["publickeytoken"] as string;
769                         token = (token == null ? "*" : token.ToLower (CultureInfo.InvariantCulture));
770                         
771                         return String.Format ("{0}_{1}_{2}", version, culture, token);
772                 }
773
774                 private static string AsmbNameFromVersionString (string name, string str)
775                 {
776                         string [] pieces = str.Split ('_');
777                         return String.Format ("{0}, Version={1}, Culture={2}, PublicKeyToken={3}",
778                                         name, pieces [0], (pieces [1] == String.Empty ? "neutral" : pieces [1]),
779                                         pieces [2]);
780                 }
781
782                 static bool LoadConfig (bool quiet)
783                 {
784                         MethodInfo config = typeof (System.Environment).GetMethod ("GetMachineConfigPath",
785                                 BindingFlags.Static | BindingFlags.NonPublic);
786
787                         if (config != null) {
788                                 string path = (string) config.Invoke (null, null);
789
790                                 bool exist = File.Exists (path);
791                                 if (!quiet && !exist)
792                                         Console.WriteLine ("Couldn't find machine.config");
793
794                                 StrongNameManager.LoadConfig (path);
795                                 return exist;
796                         } else if (!quiet)
797                                 Console.WriteLine ("Couldn't resolve machine.config location (corlib issue)");
798
799                         // default CSP
800                         return false;
801                 }
802
803                 // modified copy from sn
804                 private static VerificationResult VerifyStrongName (AssemblyName an, string assemblyFile)
805                 {
806                         byte [] publicKey = StrongNameManager.GetMappedPublicKey (an.GetPublicKeyToken ());
807                         if ((publicKey == null) || (publicKey.Length < 12)) {
808                                 // no mapping
809                                 publicKey = an.GetPublicKey ();
810                                 if ((publicKey == null) || (publicKey.Length < 12))
811                                         return VerificationResult.WeakNamed;
812                         }
813
814                         // Note: MustVerify is based on the original token (by design). Public key
815                         // remapping won't affect if the assembly is verified or not.
816                         if (StrongNameManager.MustVerify (an)) {
817                                 RSA rsa = CryptoConvert.FromCapiPublicKeyBlob (publicKey, 12);
818                                 StrongName sn = new StrongName (rsa);
819                                 if (sn.Verify (assemblyFile)) {
820                                         return VerificationResult.StrongNamed;
821                                 } else {
822                                         return VerificationResult.DelaySigned;
823                                 }
824                         } else {
825                                 return VerificationResult.Skipped;
826                         }
827                 }
828
829                 private static bool IsSwitch (string arg)
830                 {
831                         return (arg [0] == '-' || (arg [0] == '/' && !arg.EndsWith (".dll") && !arg.EndsWith (".exe") && arg.IndexOf ('/', 1) < 0 ) );
832                 }
833
834                 private static Command GetCommand (string arg)
835                 {
836                         Command c = Command.Unknown;
837
838                         switch (arg) {
839                         case "-i":
840                         case "/i":
841                         case "--install":
842                                 c = Command.Install;
843                                 break;
844                         case "-il":
845                         case "/il":
846                         case "--install-from-list":
847                                 c = Command.InstallFromList;
848                                 break;
849                         case "-u":
850                         case "/u":
851                         case "/uf":
852                         case "--uninstall":
853                                 c = Command.Uninstall;
854                                 break;
855                         case "-ul":
856                         case "/ul":
857                         case "--uninstall-from-list":
858                                 c = Command.UninstallFromList;
859                                 break;
860                         case "-us":
861                         case "/us":
862                         case "--uninstall-specific":
863                                 c = Command.UninstallSpecific;
864                                 break;
865                         case "-l":
866                         case "/l":
867                         case "--list":
868                                 c = Command.List;
869                                 break;
870                         case "-?":
871                         case "/?":
872                         case "--help":
873                                 c = Command.Help;
874                                 break;
875                         }
876                         return c;        
877                 }
878
879                 [DllImport ("libc", SetLastError=true)]
880                 public static extern int symlink (string oldpath, string newpath);
881
882                 private static string GetGacDir () {
883                         PropertyInfo gac = typeof (System.Environment).GetProperty ("GacPath",
884                                         BindingFlags.Static|BindingFlags.NonPublic);
885                         if (gac == null) {
886                                 WriteLine ("ERROR: Mono runtime not detected, please use " +
887                                                 "the mono runtime for gacutil.exe");
888                                 Environment.Exit (1);
889                         }
890                         MethodInfo get_gac = gac.GetGetMethod (true);
891                         return (string) get_gac.Invoke (null, null);
892                 }
893
894                 private static string GetLibDir () {
895                         MethodInfo libdir = typeof (System.Environment).GetMethod ("internalGetGacPath",
896                                         BindingFlags.Static|BindingFlags.NonPublic);
897                         if (libdir == null) {
898                                 WriteLine ("ERROR: Mono runtime not detected, please use " +
899                                                 "the mono runtime for gacutil.exe");
900                                 Environment.Exit (1);
901                         }
902                         return Path.Combine ((string)libdir.Invoke (null, null), "mono");
903                 }
904
905                 private static string GetStringToken (byte[] tok)
906                 {
907                         StringBuilder sb = new StringBuilder ();
908                         for (int i = 0; i < tok.Length ; i++)
909                                 sb.Append (tok[i].ToString ("x2"));
910                         return sb.ToString ();
911                 }
912
913                 private static string EnsureLib (string dir)
914                 {
915                         DirectoryInfo d = new DirectoryInfo (dir);
916                         if (d.Name == "lib")
917                                 return dir;
918                         return Path.Combine (dir, "lib");
919                 }
920
921                 private static void WriteLine ()
922                 {
923                         if (silent)
924                                 return;
925                         Console.WriteLine ();
926                 }
927
928                 private static void WriteLine (string line)
929                 {
930                         if (silent)
931                                 return;
932                         Console.WriteLine (line);
933                 }
934
935                 private static void WriteLine (string line, params object [] p)
936                 {
937                         if (silent)
938                                 return; 
939                         Console.WriteLine (line, p);
940                 }
941
942                 private static void Usage ()
943                 {
944                         ShowHelp (false);
945                         Environment.Exit (1);
946                 }
947
948                 private static void ShowHelp (bool detailed)
949                 {
950                         WriteLine ("Usage: gacutil.exe <commands> [ <options> ]");
951                         WriteLine ("Commands:");
952
953                         WriteLine ("-i <assembly_path> [-check_refs] [-package NAME] [-root ROOTDIR] [-gacdir GACDIR]");
954                         WriteLine ("\tInstalls an assembly into the global assembly cache.");
955                         if (detailed) {
956                                 WriteLine ("\t<assembly_path> is the name of the file that contains the " +
957                                                 "\tassembly manifest\n" +
958                                                 "\tExample: -i myDll.dll");
959                         }
960                         WriteLine ();
961
962                         WriteLine ("-il <assembly_list_file> [-check_refs] [-package NAME] [-root ROOTDIR] [-gacdir GACDIR]");
963                         WriteLine ("\tInstalls one or more assemblies into the global assembly cache.");
964                         if (detailed) {
965                                 WriteLine ("\t<assembly_list_file> is the path to a test file containing a list of\n" +
966                                                 "\tassembly file paths on separate lines.\n" +
967                                                 "\tExample -il assembly_list.txt\n" +
968                                                 "\t\tassembly_list.txt contents:\n" +
969                                                 "\t\tassembly1.dll\n" +
970                                                 "\t\tassembly2.dll");
971                         }
972                         WriteLine ();
973                         
974                         WriteLine ("-u <assembly_display_name> [-package NAME] [-root ROOTDIR] [-gacdir GACDIR]");
975                         WriteLine ("\tUninstalls an assembly from the global assembly cache.");
976                         if (detailed) {
977                                 WriteLine ("\t<assembly_display_name> is the name of the assembly (partial or\n" +
978                                                 "\tfully qualified) to remove from the global assembly cache. If a \n" +
979                                                 "\tpartial name is specified all matching assemblies will be uninstalled.\n" +
980                                                 "\tExample: -u myDll,Version=1.2.1.0");
981                         }
982                         WriteLine ();
983
984                         WriteLine ("-ul <assembly_list_file> [-package NAME] [-root ROOTDIR] [-gacdir GACDIR]");
985                         WriteLine ("\tUninstalls one or more assemblies from the global assembly cache.");
986                         if (detailed) {
987                                 WriteLine ("\t<assembly_list_file> is the path to a test file containing a list of\n" +
988                                                 "\tassembly names on separate lines.\n" +
989                                                 "\tExample -ul assembly_list.txt\n" +
990                                                 "\t\tassembly_list.txt contents:\n" +
991                                                 "\t\tassembly1,Version=1.0.0.0,Culture=en,PublicKeyToken=0123456789abcdef\n" +
992                                                 "\t\tassembly2,Version=2.0.0.0,Culture=en,PublicKeyToken=0123456789abcdef");
993                         }
994                         WriteLine ();
995
996                         WriteLine ("-us <assembly_path> [-package NAME] [-root ROOTDIR] [-gacdir GACDIR]");
997                         WriteLine ("\tUninstalls an assembly using the specifed assemblies full name.");
998                         if (detailed) {
999                                 WriteLine ("\t<assembly path> is the path to an assembly. The full assembly name\n" +
1000                                                 "\tis retrieved from the specified assembly if there is an assembly in\n" +
1001                                                 "\tthe GAC with a matching name, it is removed.\n" +
1002                                                 "\tExample: -us myDll.dll");
1003                         }
1004                         WriteLine ();
1005
1006                         WriteLine ("-l [assembly_name] [-root ROOTDIR] [-gacdir GACDIR]");
1007                         WriteLine ("\tLists the contents of the global assembly cache.");
1008                         if (detailed) {
1009                                 WriteLine ("\tWhen the <assembly_name> parameter is specified only matching\n" +
1010                                                 "\tassemblies are listed.");
1011                         }
1012                         WriteLine ();
1013
1014                         WriteLine ("-?");
1015                         WriteLine ("\tDisplays a detailed help screen");
1016                         WriteLine ();
1017
1018                         if (!detailed)
1019                                 return;
1020
1021                         WriteLine ("Options:");
1022                         WriteLine ("-package <NAME>");
1023                         WriteLine ("\tUsed to create a directory in prefix/lib/mono with the name NAME, and a\n" +
1024                                         "\tsymlink is created from NAME/assembly_name to the assembly on the GAC.\n" +
1025                                         "\tThis is used so developers can reference a set of libraries at once.");
1026                         WriteLine ();
1027
1028                         WriteLine ("-gacdir <GACDIR>");
1029                         WriteLine ("\tUsed to specify the GACs base directory. Once an assembly has been installed\n" +
1030                                         "\tto a non standard gacdir the MONO_GAC_PREFIX environment variable must be used\n" +
1031                                         "\tto access the assembly.");
1032                         WriteLine ();
1033
1034                         WriteLine ("-root <ROOTDIR>");
1035                         WriteLine ("\tUsed by developers integrating this with automake tools or packaging tools\n" +
1036                                         "\tthat require a prefix directory to  be specified. The root represents the\n" +
1037                                         "\t\"libdir\" component of a prefix (typically prefix/lib).");
1038                         WriteLine ();
1039
1040                         WriteLine ("-check_refs");
1041                         WriteLine ("\tUsed to ensure that the assembly being installed into the GAC does not\n" +
1042                                         "\treference any non strong named assemblies. Assemblies being installed to\n" +
1043                                         "\tthe GAC should not reference non strong named assemblies, however the is\n" +
1044                                         "\tan optional check.");
1045
1046                         WriteLine ();
1047                         WriteLine ("Ignored Options:");
1048                         WriteLine ("-f");
1049                         WriteLine ("\tThe Mono gacutil ignores the -f option to maintain commandline compatibility with");
1050                         WriteLine ("\tother gacutils. gacutil will always force the installation of a new assembly.");
1051
1052                         WriteLine ();
1053                         WriteLine ("-r <reference_scheme> <reference_id> <description>");
1054                         WriteLine ("\tThe Mono gacutil has not implemented traced references and will emit a warning");
1055                         WriteLine ("\twhen this option is used.");
1056                 }
1057         }
1058 }
1059