context: //
  // Authors:
  //   Miguel de Icaza (miguel@novell.com)
  //
  // (C) 2007 Novell, Inc
  //
  
  //
  // Permission is hereby granted, free of charge, to any person obtaining
  // a copy of this software and associated documentation files (the
  // "Software"), to deal in the Software without restriction, including
  // without limitation the rights to use, copy, modify, merge, publish,
  // distribute, sublicense, and/or sell copies of the Software, and to
  // permit persons to whom the Software is furnished to do so, subject to
  // the following conditions:
  // 
  // The above copyright notice and this permission notice shall be
  // included in all copies or substantial portions of the Software.
  // 
  // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
  // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
  // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
  // NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
  // LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
  // OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
  // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
  //
  
  using System;
  using System.IO;
  using System.Collections;
  using System.ComponentModel;
  using System.Reflection;
  using System.Configuration.Install;
  
  public class InstallUtil {
  
  	static bool showcallstack = false;
  	static bool logtoconsole = false;
  	static string assembly = null;
  	
  	static void ShowHelpForAssembly (string assembly)
  	{
  		Console.WriteLine ("Help for assembly not implemented");
  	}
  	
  	static void ShowHelp ()
  	{
  		Console.WriteLine ("installutil -- Installs Assemblies that use System.Configuration.Install");
  		Console.WriteLine ("Usage is: installutil commands\n");
  		Console.WriteLine ("\n" + 
  				   "   /help            Shows help\n" +
  				   "   /help ASSEM      Shows help for the given assembly\n" +
  				   "   /logfile[=out]   Specifies a log file\n" +
  				   "   /uninstall ASSEM Uninstall the given assembly\n");
  	}
  
  	static void Call (Installer instance, string method, object arg)
  	{
  		Console.WriteLine ("M: " + method);
  		MethodInfo mi = typeof (Installer).GetMethod (method, BindingFlags.Public|BindingFlags.Instance|BindingFlags.NonPublic|BindingFlags.Static);
  		mi.Invoke (instance, new object [] { arg });
  	}
  
  	static void Error (string st)
  	{
  		Console.Error.WriteLine (st);
  	}
  	
  	static void Perform (bool install, string executable)
  	{
  		ArrayList order = new ArrayList ();
  		Hashtable states = new Hashtable ();
  		
  		try {
  			Assembly a;
  
  			if (assembly != null)
  				a = Assembly.Load (assembly);
  			else
  				a = Assembly.LoadFrom (executable);
  			
  			Type [] types = a.GetTypes ();
  
  			// todo: pass arguments, they are kind of useless though.
  			InstallContext ctx = new InstallContext ();
  			
  			foreach (Type t in types){
  				if (!t.IsSubclassOf (typeof (Installer)))
  					continue;
  
  				object [] attrs = t.GetCustomAttributes (typeof (RunInstallerAttribute), false);
  				if (attrs == null || attrs.Length == 0)
  					continue;
  
  				RunInstallerAttribute ria = attrs [0] as RunInstallerAttribute;
  				if (ria == null || !ria.RunInstaller)
  					continue;
  
  				try {
  					Installer installer = (Installer) Activator.CreateInstance (t);
  					Hashtable state = new Hashtable ();
  					
  					order.Add (installer);
  					states [installer] = state;
  					
  					if (install)
  						Call (installer, "OnBeforeInstall", state);
  					else
  						Call (installer, "OnBeforeUninstall", state);
  					
  					installer.Install (state);
  
  					if (install)
  						Call (installer, "OnAfterInstall", state);
  					else
  						Call (installer, "OnAfterUninstall", state);
  					
  				} catch (Exception e) {
  					Error (String.Format ("Can not create installer of type {0}", t));
  					
  					//
  					// According to the docs uninstall should not do rollback
  					//
  					if (install){
  						foreach (Installer installer in order){
  							Hashtable state = (Hashtable) states [installer];
  								
  							Call (installer, "OnBeforeRollback", state);
  							installer.Rollback (state);
  							Call (installer, "OnAfterRollback", state);
  						}
  					}
  				}
  			}
  			//
  			// Got it, now commit them
  			//
  			if (install){
  				foreach (Installer inst in order){
  					Hashtable state = (Hashtable) states [inst];
  					
  					Call (inst, "OnCommitting", state);
  					inst.Commit (state);
  					Call (inst, "OnCommitted", state);
  				}
  			}
  		} catch {
  			Error (String.Format ("Unable to load assembly {0}", assembly));
  		}		
  	}
  
  	static void Install (string assembly)
  	{
  		Perform (true, assembly);
  	}
  	
  	static void Uninstall (string assembly)
  	{
  		Perform (false, assembly);
  	}
  	
  	static int Main (string [] args)
  	{
  		bool did_something = false;
  		string logfile = null;
  		
  		for (int i = 0; i < args.Length; i++){
  			string arg = args [i];
  			char c = arg [0];
  
  			if (c == '/' || c == '-'){
  				switch (arg.ToLower ()){
  				case "/help": case "/h": case "/?": 
  				case "-help": case "-h": case "-?": 
  					if (i + 1 < args.Length){
  						i++;
  						ShowHelpForAssembly (args [i]);
  					} else {
  						ShowHelp ();
  						return 1;
  					}
  					break;
  
  				case "-showcallstack":
  				case "/showcallstack":
  					showcallstack = true;
  					break;
  
  				case "-logtoconsole":
  				case "/logtoconsole":
  					logtoconsole = true;
  					break;
  
  				case "-u": case "-uninstall":
  				case "/u": case "/uninstall":
  					if (i + 1 < args.Length){
  						i++;
  						Uninstall (args [i]);
  						did_something = true;
  					} else {
  						ShowHelp ();
  						return 1;
  					}
  					break;
  
  				case "-assemblyname":
  				case "/assemblyname":
  					if (i + 1 < args.Length){
  						i++;
  
gt: 						assembly = args [i];