2007-10-22 Marek Habersack <mhabersack@novell.com>
[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                         "  -C,--list-configs                 List all the default config file names defined in the",
61                         "                                    configuration files.",
62                         "  -F,--list-features                List all the features defined in the configuration files.",
63                         "",
64                         "Commands:",
65                         "  {addfeature,af} <feature_name> [config_file_path]",
66                         "  {defconfig,dc} [config_name] [target_directory]"
67                 };
68                 
69                 public delegate void ListDefaultConfigsHandler ();
70                 public delegate void ListFeaturesHandler ();
71
72                 public event ListDefaultConfigsHandler OnListDefaultConfigs;
73                 public event ListFeaturesHandler OnListFeatures;
74
75                 List <string> plain_arguments;
76                 Dictionary <string, string> unknown_arguments;
77
78                 public string ConfigFile;
79                 public FeatureTarget Target = FeatureTarget.Any;
80                 public bool ListDefaultConfigs;
81                 public bool ListFeatures;
82                 
83                 public Dictionary <string, string> UnknownArguments {
84                         get {
85                                 if (unknown_arguments == null || unknown_arguments.Count == 0)
86                                         return null;
87
88                                 return unknown_arguments;
89                         }
90                 }
91                 
92                 public string[] PlainArguments {
93                         get {
94                                 if (plain_arguments == null || plain_arguments.Count == 0)
95                                         return null;
96                                 
97                                 return plain_arguments.ToArray ();
98                         }
99                 }
100                 
101                 public MConfigOptions ()
102                 {
103                         unknown_arguments = new Dictionary <string, string> ();
104                         plain_arguments = new List <string> ();
105                 }
106
107                 public void Parse (string[] args)
108                 {
109                         if (args == null || args.Length == 0)
110                                 Usage ();
111
112                         int len = args.Length;
113                         string arg;
114                         
115                         for (int i = 0; i < len; i++) {
116                                 arg = args [i];
117
118                                 switch (arg [0]) {
119                                         case '-':
120                                         case '/':
121                                                 i += ProcessArgument (i, arg, args, len);
122                                                 break;
123
124                                         default:
125                                                 plain_arguments.Add (arg);
126                                                 break;
127                                 }
128                         }
129                 }
130
131                 static char[] paramStartChars = {':', '='};
132                 
133                 int ProcessArgument (int idx, string argument, string[] args, int argsLen)
134                 {
135                         int argnameIdx = 1;
136                         bool haveMoreDashes = false, badArg = false;
137                         int argumentLen = argument.Length;
138
139                         if (argumentLen < 2)
140                                 badArg = true;
141                         
142                         haveMoreDashes = !badArg && (argument [1] == '-');
143                         
144                         if (argumentLen == 2 && haveMoreDashes)
145                                 badArg = true;
146                         
147                         if (badArg) {
148                                 Console.Error.WriteLine ("Invalid argument: {0}", argument);
149                                 Environment.Exit (1);
150                         }
151
152                         if (haveMoreDashes)
153                                 argnameIdx++;
154
155                         int paramPos = argument.IndexOfAny (paramStartChars, argnameIdx);
156                         bool haveParam = true;
157                         
158                         if (paramPos == -1) {
159                                 haveParam = false;
160                                 paramPos = argumentLen;
161                         }
162                         
163                         string argName = argument.Substring (argnameIdx, paramPos - argnameIdx);
164                         string argParam = haveParam ? argument.Substring (paramPos + 1) : null;
165
166                         int ret = 0;
167                         
168                         if (!haveParam && haveMoreDashes) {
169                                 idx++;
170                                 if (idx < argsLen) {
171                                         argParam = args [idx];
172                                         ret++;
173                                         haveParam = true;
174                                 }
175                         }
176                         
177                         switch (argName) {
178                                 case "?":
179                                 case "h":
180                                 case "help":
181                                         Usage ();
182                                         break;
183
184                                 case "v":
185                                 case "version":
186                                         ShowVersion ();
187                                         break;
188
189                                 case "t":
190                                 case "target":
191                                         if (!haveParam)
192                                                 RequiredParameterMissing (argName);
193                                         
194                                         try {
195                                                 Target = Helpers.ConvertTarget (argParam);
196                                         } catch (Exception ex) {
197                                                 OptionParameterError (argName, ex.Message);
198                                         }
199                                         break;
200
201                                 case "C":
202                                 case "list-configs":
203                                         ListDefaultConfigs = true;
204                                         break;
205
206                                 case "F":
207                                 case "list-features":
208                                         ListFeatures = true;
209                                         break;
210
211                                 default:
212                                         unknown_arguments.Add (argName, argParam);
213                                         break;
214                         }
215                         
216                         return ret;
217                 }
218
219                 void RequiredParameterMissing (string argName)
220                 {
221                         Console.Error.WriteLine ("Argument '{0}' requires a parameter", argName);
222                         Environment.Exit (1);
223                 }
224
225                 void OptionParameterError (string argName, string message)
226                 {
227                         Console.Error.WriteLine ("Parameter value is invalid for argument '{0}'.",
228                                                  argName);
229                         Console.Error.WriteLine (message);
230                         Environment.Exit (1);
231                 }
232
233                 public void Usage ()
234                 {
235                         foreach (string line in usage)
236                                 Console.WriteLine (line);
237                         Environment.Exit (1);
238                 }
239                 
240                 void ShowVersion ()
241                 {
242                         Assembly asm = Assembly.GetExecutingAssembly () ?? Assembly.GetCallingAssembly ();
243                         object[] attrs = asm != null ? asm.GetCustomAttributes (false) : null;
244                         string product = "mconfig", version = "0.0.0.0", copyright = "", description = "";
245
246                         if (asm != null) {
247                                 Version v = asm.GetName ().Version;
248                                 if (v != null)
249                                         version = v.ToString ();
250                         }
251                         
252                         if (attrs != null) {                            
253                                 foreach (object o in attrs) {
254                                         if (o is AssemblyProductAttribute)
255                                                 product = ((AssemblyProductAttribute)o).Product;
256                                         else if (o is AssemblyCopyrightAttribute)
257                                                 copyright = ((AssemblyCopyrightAttribute)o).Copyright;
258                                         else if (o is AssemblyDescriptionAttribute)
259                                                 description = ((AssemblyDescriptionAttribute)o).Description;
260                                 }
261                         } else
262                                 Console.WriteLine ("Missing version information");
263
264                         Console.WriteLine ("{0} - {1} {2}", product, description, version);
265                         Console.WriteLine (copyright);
266                         
267                         Environment.Exit (1);
268                 }
269         }
270         
271         class MConfig
272         {
273                 static string[] configPaths = {
274                         Constants.GlobalConfigPath,
275                         Path.Combine (ConfigPath, "config.xml"),
276                         Path.Combine (".", "mconfig.xml"),
277                         null
278                 };
279
280                 static CommandHandler[] commands = {
281                         new CommandHandler (new string[] {"addfeature", "af"}, HandleAddFeature),
282                         new CommandHandler (new string[] {"defconfig", "dc"}, HandleDefaultConfig)
283                 };
284                 
285                 static string ConfigPath {
286                         get {
287                                 string configPath = Environment.GetEnvironmentVariable ("XDG_CONFIG_HOME");
288                                 if (String.IsNullOrEmpty (configPath))
289                                         configPath = Path.Combine (Environment.GetEnvironmentVariable ("HOME"), ".config");
290                                 return Path.Combine (configPath, "mconfig");
291                         }
292                 }
293
294                 static HandleCommand FindCommandHandler (string command)
295                 {
296                         foreach (CommandHandler ch in commands) {
297                                 foreach (string name in ch.Names)
298                                         if (name == command)
299                                                 return ch.Handler;
300                         }
301
302                         return null;
303                 }
304
305                 static void DisplayList (string banner, string[] list)
306                 {
307                         Console.WriteLine (banner);
308                         if (list == null || list.Length == 0) {
309                                 Console.WriteLine ("No data found");
310                                 return;
311                         }
312
313                         foreach (string item in list)
314                                 Console.WriteLine (" {0}", item);
315                 }
316                 
317                 static int Main (string[] args)
318                 {
319                         MConfigOptions options = new MConfigOptions ();
320                         options.Parse (args);
321                         
322                         if (!String.IsNullOrEmpty (options.ConfigFile))
323                                 configPaths [3] = options.ConfigFile;
324                         
325                         Configuration config = new Configuration ();
326                         config.Load (configPaths);
327
328                         bool doQuit = false;
329                         if (options.ListDefaultConfigs) {
330                                 DisplayList ("Default config files", config.DefaultConfigFiles);
331                                 doQuit = true;
332                         }
333
334                         if (options.ListFeatures) {
335                                 DisplayList ("Available features", config.Features);
336                                 doQuit = true;
337                         }
338
339                         if (doQuit)
340                                 return 0;
341                         
342                         string[] commandArguments = options.PlainArguments;
343                         if (commandArguments == null || commandArguments.Length == 0) {
344                                 options.Usage ();
345                                 return 1;
346                         }
347                         
348                         HandleCommand commandHandler = FindCommandHandler (commandArguments [0]);
349                         if (commandHandler == null) {
350                                 Console.Error.WriteLine ("Unknown command '{0}'", commandArguments [0]);
351                                 return 1;
352                         }
353
354                         IDefaultConfigFileContainer[] containers = config.GetHandlersForInterface <IDefaultConfigFileContainer> ();
355                         if (containers != null && containers.Length > 0)
356                                 foreach (IDefaultConfigFileContainer c in containers)
357                                         c.OverwriteFile += new OverwriteFileEventHandler (OnOverwriteFile);
358                         
359                         return commandHandler (options, config);
360                 }
361
362                 static void OnOverwriteFile (object sender, OverwriteFileEventArgs e)
363                 {
364                         Console.Write ("Do you want to overwrite existing file '{0}'? [{1}] ",
365                                        e.Name, e.Overwrite ? "Y/n" : "y/N");
366                         ConsoleKeyInfo cki = Console.ReadKey (false);
367                         switch (cki.Key) {
368                                 case ConsoleKey.N:
369                                         e.Overwrite = false;
370                                         break;
371
372                                 case ConsoleKey.Y:
373                                         e.Overwrite = true;
374                                         break;
375                         }
376                         Console.WriteLine ();
377                 }
378
379                 static int HandleAddFeature (MConfigOptions options, Configuration config)
380                 {
381                         string[] commandArguments = options.PlainArguments;
382                         if (commandArguments.Length < 2) {
383                                 Console.Error.WriteLine ("Command requires at least one argument.");
384                                 return 1;
385                         }
386                         
387                         FeatureTarget target = options.Target;
388                         string featureName = commandArguments [1], configPath;
389                         if (commandArguments.Length > 2)
390                                 configPath = commandArguments [2];
391                         else {
392                                 switch (target) {
393                                         case FeatureTarget.Any:
394                                                 Console.Error.WriteLine ("No default config file for target 'Any'");
395                                                 return 1;
396                                                 
397                                         case FeatureTarget.Web:
398                                                 configPath = "Web.config";
399                                                 break;
400                                                 
401                                         case FeatureTarget.Application:
402                                                 configPath = "application.exe.config";
403                                                 break;
404
405                                         default:
406                                                 Console.Error.WriteLine ("Unknown target '{0}'", target);
407                                                 return 1;
408                                 }
409                         }
410                         
411                         try {
412                                 config.AddFeature (configPath, target, featureName);
413                         } catch (Exception ex) {
414                                 Console.Error.WriteLine ("Failed to add feature '{0}' to config file '{1}'.\n{2}",
415                                                          featureName, configPath, ex.Message);
416                                 return 1;
417                         }
418                         
419                         return 0;
420                 }
421
422                 static int HandleDefaultConfig (MConfigOptions options, Configuration config)
423                 {
424                         FeatureTarget target = options.Target;
425                         string[] commandArguments = options.PlainArguments;
426                         string configName, targetPath;
427
428                         if (commandArguments.Length < 2) {
429                                 switch (target) {
430                                         case FeatureTarget.Any:
431                                                 Console.Error.WriteLine ("No default config file for target 'Any'");
432                                                 return 1;
433                                                 
434                                         case FeatureTarget.Web:
435                                                 configName = "Web.config";
436                                                 break;
437                                                 
438                                         case FeatureTarget.Application:
439                                                 configName = "application.exe.config";
440                                                 break;
441
442                                         default:
443                                                 Console.Error.WriteLine ("Unknown target '{0}'", target);
444                                                 return 1;
445                                 }
446                         } else
447                                 configName = commandArguments [1];
448
449                         if (commandArguments.Length < 3)
450                                 targetPath = ".";
451                         else
452                                 targetPath = commandArguments [2];
453
454                         try {
455                                 config.WriteDefaultConfigFile (configName, targetPath, target);
456                         } catch (Exception ex) {
457                                 Console.Error.WriteLine ("Failed to write default config file '{0}':\n{1}",
458                                                          configName, ex.Message);
459                                 return 1;
460                         }                       
461
462                         return 0;
463                 }
464         }
465 }