Merge pull request #3066 from alexanderkyte/pedump_sgen
[mono.git] / mcs / tools / mconfig / mconfig.cs
1 //
2 // Authors:
3 //   Marek Habersack (mhabersack@novell.com)
4 //
5 // (C) 2007 Novell, Inc
6 //
7
8 //
9 // Permission is hereby granted, free of charge, to any person obtaining
10 // a copy of this software and associated documentation files (the
11 // "Software"), to deal in the Software without restriction, including
12 // without limitation the rights to use, copy, modify, merge, publish,
13 // distribute, sublicense, and/or sell copies of the Software, and to
14 // permit persons to whom the Software is furnished to do so, subject to
15 // the following conditions:
16 // 
17 // The above copyright notice and this permission notice shall be
18 // included in all copies or substantial portions of the Software.
19 // 
20 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
21 // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
22 // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
23 // NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
24 // LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
25 // OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
26 // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
27 //
28 using System;
29 using System.Collections.Generic;
30 using System.IO;
31 using System.Reflection;
32
33 namespace Mono.MonoConfig 
34 {
35         delegate int HandleCommand (MConfigOptions options, Configuration config);
36         
37         struct CommandHandler {
38                 public readonly HandleCommand Handler;
39                 public readonly string[] Names;
40                         
41                 public CommandHandler (string[] names, HandleCommand handler)
42                 {
43                         this.Names = names;
44                         this.Handler = handler;
45                 }
46         };
47         
48         class MConfigOptions
49         {
50                 string[] usage = {
51                         "Usage: mconfig [options] command [command_parameters]",
52                         "Options:",
53                         "",
54                         "  -?,-h,--help                      Display this usage information",
55                         "  -v,--version                      Display version information",
56                         "  -c,--config=<filepath>            Read the specified config file in addition to",
57                         "                                    the standard ones. Settings in this file override ones",
58                         "                                    in the other files.",
59                         "  -t,--target={any,web,application} Use this target when executing 'command'",
60                         "",
61                         "To see the list of commands, features and default config file templates, run mconfig",
62                         "without any parameters"
63                 };
64
65                 string[] usageCommands = {
66                         "Available commands (see 'man mconfig' for details):",
67                         "  {addfeature,af} <feature_name> [config_file_path]",
68                         "     Add the named feature to the specified config file",
69                         "",
70                         "  {defconfig,dc} [template_name] [target_directory]",
71                         "     Write a config file based on the named template.",
72                         ""
73                 };              
74
75                 List <string> plain_arguments;
76                 Dictionary <string, string> unknown_arguments;
77
78                 public string ConfigFile;
79                 public FeatureTarget Target = FeatureTarget.Any;
80
81                 public Dictionary <string, string> UnknownArguments {
82                         get {
83                                 if (unknown_arguments == null || unknown_arguments.Count == 0)
84                                         return null;
85
86                                 return unknown_arguments;
87                         }
88                 }
89                 
90                 public string[] PlainArguments {
91                         get {
92                                 if (plain_arguments == null || plain_arguments.Count == 0)
93                                         return null;
94                                 
95                                 return plain_arguments.ToArray ();
96                         }
97                 }
98                 
99                 public MConfigOptions ()
100                 {
101                         unknown_arguments = new Dictionary <string, string> ();
102                         plain_arguments = new List <string> ();
103                 }
104
105                 public void Parse (string[] args)
106                 {
107                         if (args == null || args.Length == 0)
108                                 return;
109
110                         int len = args.Length;
111                         string arg;
112                         
113                         for (int i = 0; i < len; i++) {
114                                 arg = args [i];
115
116                                 switch (arg [0]) {
117                                         case '-':
118                                         case '/':
119                                                 i += ProcessArgument (i, arg, args, len);
120                                                 break;
121
122                                         default:
123                                                 plain_arguments.Add (arg);
124                                                 break;
125                                 }
126                         }
127                 }
128
129                 static char[] paramStartChars = {':', '='};
130                 
131                 int ProcessArgument (int idx, string argument, string[] args, int argsLen)
132                 {
133                         int argnameIdx = 1;
134                         bool haveMoreDashes = false, badArg = false;
135                         int argumentLen = argument.Length;
136
137                         if (argumentLen < 2)
138                                 badArg = true;
139                         
140                         haveMoreDashes = !badArg && (argument [1] == '-');
141                         
142                         if (argumentLen == 2 && haveMoreDashes)
143                                 badArg = true;
144                         
145                         if (badArg) {
146                                 Console.Error.WriteLine ("Invalid argument: {0}", argument);
147                                 Environment.Exit (1);
148                         }
149
150                         if (haveMoreDashes)
151                                 argnameIdx++;
152
153                         int paramPos = argument.IndexOfAny (paramStartChars, argnameIdx);
154                         bool haveParam = true;
155                         
156                         if (paramPos == -1) {
157                                 haveParam = false;
158                                 paramPos = argumentLen;
159                         }
160                         
161                         string argName = argument.Substring (argnameIdx, paramPos - argnameIdx);
162                         string argParam = haveParam ? argument.Substring (paramPos + 1) : null;
163
164                         int ret = 0;
165                         
166                         if (!haveParam && haveMoreDashes) {
167                                 idx++;
168                                 if (idx < argsLen) {
169                                         argParam = args [idx];
170                                         ret++;
171                                         haveParam = true;
172                                 }
173                         }
174                         
175                         switch (argName) {
176                                 case "?":
177                                 case "h":
178                                 case "help":
179                                         Usage ();
180                                         break;
181
182                                 case "v":
183                                 case "version":
184                                         ShowVersion ();
185                                         break;
186
187                                 case "t":
188                                 case "target":
189                                         if (!haveParam)
190                                                 RequiredParameterMissing (argName);
191                                         
192                                         try {
193                                                 Target = Helpers.ConvertEnum <FeatureTarget> (argParam, "target");
194                                         } catch (Exception ex) {
195                                                 OptionParameterError (argName, ex.Message);
196                                         }
197                                         break;
198
199                                 default:
200                                         unknown_arguments.Add (argName, argParam);
201                                         break;
202                         }
203                         
204                         return ret;
205                 }
206
207                 void RequiredParameterMissing (string argName)
208                 {
209                         Console.Error.WriteLine ("Argument '{0}' requires a parameter", argName);
210                         Environment.Exit (1);
211                 }
212
213                 void OptionParameterError (string argName, string message)
214                 {
215                         Console.Error.WriteLine ("Parameter value is invalid for argument '{0}'.",
216                                                  argName);
217                         Console.Error.WriteLine (message);
218                         Environment.Exit (1);
219                 }
220
221                 void ShowUsage (string[] msg, bool exit)
222                 {
223                         foreach (string line in msg)
224                                 Console.WriteLine (line);
225                         if (exit)
226                                 Environment.Exit (1);
227                 }
228                 
229                 public void Usage ()
230                 {
231                         ShowUsage (usage, true);
232                 }
233
234                 public void UsageCommands ()
235                 {
236                         ShowUsage (usageCommands, false);
237                 }
238                 
239                 void ShowVersion ()
240                 {
241                         Assembly asm = Assembly.GetExecutingAssembly () ?? Assembly.GetCallingAssembly ();
242                         object[] attrs = asm != null ? asm.GetCustomAttributes (false) : null;
243                         string product = "mconfig", version = "0.0.0.0", copyright = "", description = "";
244
245                         if (asm != null) {
246                                 Version v = asm.GetName ().Version;
247                                 if (v != null)
248                                         version = v.ToString ();
249                         }
250                         
251                         if (attrs != null) {                            
252                                 foreach (object o in attrs) {
253                                         if (o is AssemblyProductAttribute)
254                                                 product = ((AssemblyProductAttribute)o).Product;
255                                         else if (o is AssemblyCopyrightAttribute)
256                                                 copyright = ((AssemblyCopyrightAttribute)o).Copyright;
257                                         else if (o is AssemblyDescriptionAttribute)
258                                                 description = ((AssemblyDescriptionAttribute)o).Description;
259                                 }
260                         } else
261                                 Console.WriteLine ("Missing version information");
262
263                         Console.WriteLine ("{0} - {1} {2}", product, description, version);
264                         Console.WriteLine (copyright);
265                         
266                         Environment.Exit (1);
267                 }
268         }
269         
270         class MConfig
271         {
272                 static string[] configPaths = {
273                         Path.GetFullPath (Path.Combine (Environment.CommandLine, "..", "..", "..","..", "etc", "mono", "mconfig", "config.xml")),
274                         Path.Combine (ConfigPath, "config.xml"),
275                         Path.Combine (".", "mconfig.xml"),
276                         null
277                 };
278
279                 static CommandHandler[] commands = {
280                         new CommandHandler (new string[] {"addfeature", "af"}, HandleAddFeature),
281                         new CommandHandler (new string[] {"defconfig", "dc"}, HandleDefaultConfig)
282                 };
283                 
284                 static string ConfigPath {
285                         get {
286                                 string configPath = Environment.GetEnvironmentVariable ("XDG_CONFIG_HOME");
287                                 if (String.IsNullOrEmpty (configPath))
288                                         configPath = Path.Combine (Environment.GetEnvironmentVariable ("HOME"), ".config");
289                                 return Path.Combine (configPath, "mconfig");
290                         }
291                 }
292
293                 static HandleCommand FindCommandHandler (string command)
294                 {
295                         foreach (CommandHandler ch in commands) {
296                                 foreach (string name in ch.Names)
297                                         if (name == command)
298                                                 return ch.Handler;
299                         }
300
301                         return null;
302                 }
303
304                 static void DisplayList (string banner, string[] list)
305                 {
306                         Console.WriteLine (banner);
307                         if (list == null || list.Length == 0) {
308                                 Console.WriteLine ("No data found");
309                                 return;
310                         }
311
312                         foreach (string item in list)
313                                 Console.WriteLine ("  {0}", item);
314                 }
315
316                 static void PrintException (Exception ex, string format, params object[] parms)
317                 {
318                         if (ex == null)
319                                 return;
320                         Console.Error.WriteLine (format, parms);
321                         Console.Error.WriteLine ("  {0}", ex.Message);
322                         if (ex.InnerException != null)
323                                 Console.Error.WriteLine ("    {0}", ex.InnerException.Message);
324                 }
325                 
326                 static int Main (string[] args)
327                 {
328                         MConfigOptions options = new MConfigOptions ();
329                         options.Parse (args);
330                         
331                         if (!String.IsNullOrEmpty (options.ConfigFile))
332                                 configPaths [3] = options.ConfigFile;
333                         
334                         Configuration config = new Configuration ();
335                         try {
336                                 config.Load (configPaths);
337                         } catch (Exception ex) {
338                                 PrintException (ex, "Failed to load configuration files:");
339                                 return 1;
340                         }
341                         
342                         string[] commandArguments = options.PlainArguments;
343                         if (commandArguments == null || commandArguments.Length == 0) {
344                                 options.UsageCommands ();
345                                 DisplayList ("Default config files:", config.DefaultConfigFiles);
346                                 Console.WriteLine ();
347                                 DisplayList ("Available features:", config.Features);
348                                 return 1;
349                         }
350                         
351                         HandleCommand commandHandler = FindCommandHandler (commandArguments [0]);
352                         if (commandHandler == null) {
353                                 Console.Error.WriteLine ("Unknown command '{0}'", commandArguments [0]);
354                                 return 1;
355                         }
356
357                         IDefaultConfigFileContainer[] containers = config.GetHandlersForInterface <IDefaultConfigFileContainer> ();
358                         if (containers != null && containers.Length > 0)
359                                 foreach (IDefaultConfigFileContainer c in containers)
360                                         c.OverwriteFile += new OverwriteFileEventHandler (OnOverwriteFile);
361                         
362                         return commandHandler (options, config);
363                 }
364
365                 static void OnOverwriteFile (object sender, OverwriteFileEventArgs e)
366                 {
367                         Console.Write ("Do you want to overwrite existing file '{0}'? [{1}] ",
368                                        e.Name, e.Overwrite ? "Y/n" : "y/N");
369                         ConsoleKeyInfo cki = Console.ReadKey (false);
370                         switch (cki.Key) {
371                                 case ConsoleKey.N:
372                                         e.Overwrite = false;
373                                         break;
374
375                                 case ConsoleKey.Y:
376                                         e.Overwrite = true;
377                                         break;
378                         }
379                         Console.WriteLine ();
380                 }
381
382                 static int HandleAddFeature (MConfigOptions options, Configuration config)
383                 {
384                         string[] commandArguments = options.PlainArguments;
385                         if (commandArguments.Length < 2) {
386                                 Console.Error.WriteLine ("Command requires at least one argument.");
387                                 return 1;
388                         }
389                         
390                         FeatureTarget target = options.Target;
391                         string featureName = commandArguments [1], configPath;
392                         if (commandArguments.Length > 2)
393                                 configPath = commandArguments [2];
394                         else {
395                                 switch (target) {
396                                         case FeatureTarget.Any:
397                                                 Console.Error.WriteLine ("No default config file for target 'Any'");
398                                                 return 1;
399                                                 
400                                         case FeatureTarget.Web:
401                                                 configPath = "Web.config";
402                                                 break;
403                                                 
404                                         case FeatureTarget.Application:
405                                                 configPath = "application.exe.config";
406                                                 break;
407
408                                         default:
409                                                 Console.Error.WriteLine ("Unknown target '{0}'", target);
410                                                 return 1;
411                                 }
412                         }
413                         
414                         try {
415                                 config.AddFeature (configPath, target, featureName);
416                         } catch (Exception ex) {
417                                 PrintException (ex, "Failed to add feature '{0}' to config file '{1}'.",
418                                                 featureName, configPath);
419                                 return 1;
420                         }
421                         
422                         return 0;
423                 }
424
425                 static int HandleDefaultConfig (MConfigOptions options, Configuration config)
426                 {
427                         FeatureTarget target = options.Target;
428                         string[] commandArguments = options.PlainArguments;
429                         string configName, targetPath;
430
431                         if (commandArguments.Length < 2) {
432                                 switch (target) {
433                                         case FeatureTarget.Any:
434                                                 Console.Error.WriteLine ("No default config file for target 'Any'");
435                                                 return 1;
436                                                 
437                                         case FeatureTarget.Web:
438                                                 configName = "Web.config";
439                                                 break;
440                                                 
441                                         case FeatureTarget.Application:
442                                                 configName = "application.exe.config";
443                                                 break;
444
445                                         default:
446                                                 Console.Error.WriteLine ("Unknown target '{0}'", target);
447                                                 return 1;
448                                 }
449                         } else
450                                 configName = commandArguments [1];
451
452                         if (commandArguments.Length < 3)
453                                 targetPath = ".";
454                         else
455                                 targetPath = commandArguments [2];
456
457                         try {
458                                 config.WriteDefaultConfigFile (configName, targetPath, target);
459                         } catch (Exception ex) {
460                                 PrintException (ex, "Failed to write default config file '{0}':",
461                                                 configName);
462                                 return 1;
463                         }                       
464
465                         return 0;
466                 }
467         }
468 }