build fix
[mono.git] / mcs / class / Mono.GetOptions / OptionList.cs
1 //
2 // OptionList.cs
3 //
4 // Author: Rafael Teixeira (rafaelteixeirabr@hotmail.com)
5 //
6 // (C) 2002 Rafael Teixeira
7 //
8
9 using System;
10 using System.Collections;
11 using System.IO;
12 using System.Reflection;
13
14 namespace Mono.GetOptions
15 {
16
17         [Flags]
18         public enum OptionsParsingMode 
19         { 
20                 Linux   = 1, 
21                 Windows = 2,
22                 Both    = 3
23         }
24
25         /// <summary>
26         /// Option Parsing
27         /// </summary>
28         public class OptionList
29         {
30         
31                 private Options optionBundle = null;
32                 private OptionsParsingMode parsingMode;
33                 private bool breakSingleDashManyLettersIntoManyOptions;
34                 private bool endOptionProcessingWithDoubleDash;
35                 
36                 private string appExeName;
37                 private string appVersion;
38
39                 private string appTitle = "Add a [assembly: AssemblyTitle(\"Here goes the application name\")] to your assembly";
40                 private string appCopyright = "Add a [assembly: AssemblyCopyright(\"(c)200n Here goes the copyright holder name\")] to your assembly";
41                 private string appDescription = "Add a [assembly: AssemblyDescription(\"Here goes the short description\")] to your assembly";
42                 private string appAboutDetails = "Add a [assembly: Mono.About(\"Here goes the short about details\")] to your assembly";
43                 private string appUsageComplement = "Add a [assembly: Mono.UsageComplement(\"Here goes the usage clause complement\")] to your assembly";
44                 private string[] appAuthors;
45  
46                 private ArrayList list = new ArrayList();
47                 private ArrayList arguments = new ArrayList();
48                 private ArrayList argumentsTail = new ArrayList();
49                 private MethodInfo argumentProcessor = null;
50
51                 public string Usage
52                 {
53                         get
54                         {
55                                 return "Usage: " + appExeName + " [options] " + appUsageComplement;
56                         }
57                 }
58
59                 public string AboutDetails
60                 {
61                         get
62                         {
63                                 return appAboutDetails;
64                         }
65                 }
66
67                 #region Assembly Attributes
68
69                 Assembly entry;
70                 
71                 private object[] GetAssemblyAttributes(Type type)
72                 {
73                         return entry.GetCustomAttributes(type, false);
74                 }
75                         
76                 private string[] GetAssemblyAttributeStrings(Type type)
77                 {
78                         object[] result = GetAssemblyAttributes(type);
79                         
80                         if ((result == null) || (result.Length == 0))
81                                 return new string[0];
82
83                         int i = 0;
84                         string[] var = new string[result.Length];
85
86                         foreach(object o in result)
87                                 var[i++] = o.ToString(); 
88
89                         return var;
90                 }
91
92                 private void GetAssemblyAttributeValue(Type type, string propertyName, ref string var)
93                 {
94                         object[] result = GetAssemblyAttributes(type);
95                         
96                         if ((result != null) && (result.Length > 0))
97                                 var = (string)type.InvokeMember(propertyName, BindingFlags.Public | BindingFlags.GetField | BindingFlags.GetProperty | BindingFlags.Instance, null, result[0], new object [] {}); ;
98                 }
99
100                 private void GetAssemblyAttributeValue(Type type, ref string var)
101                 {
102                         object[] result = GetAssemblyAttributes(type);
103                         
104                         if ((result != null) && (result.Length > 0))
105                                 var = result[0].ToString();
106                 }
107
108                 #endregion
109
110                 #region Constructors
111
112                 private void AddArgumentProcessor(MemberInfo memberInfo)
113                 {
114                         if (argumentProcessor != null)
115                                 throw new NotSupportedException("More than one argument processor method found");
116
117                         if ((memberInfo.MemberType == MemberTypes.Method && memberInfo is MethodInfo))
118                         {
119                                 if (((MethodInfo)memberInfo).ReturnType.FullName != typeof(void).FullName)
120                                         throw new NotSupportedException("Argument processor method must return 'void'");
121
122                                 ParameterInfo[] parameters = ((MethodInfo)memberInfo).GetParameters();
123                                 if ((parameters == null) || (parameters.Length != 1) || (parameters[0].ParameterType.FullName != typeof(string).FullName))
124                                         throw new NotSupportedException("Argument processor method must have a string parameter");
125                                 
126                                 argumentProcessor = (MethodInfo)memberInfo; 
127                         }
128                         else
129                                 throw new NotSupportedException("Argument processor marked member isn't a method");
130                 }
131
132                 private void Initialize(Options optionBundle)
133                 {
134                         if (optionBundle == null)
135                                 throw new ArgumentNullException("optionBundle");
136
137                         entry = Assembly.GetEntryAssembly();
138                         appExeName = entry.GetName().Name;
139                         appVersion = entry.GetName().Version.ToString();
140
141                         this.optionBundle = optionBundle; 
142                         this.parsingMode = optionBundle.ParsingMode ;
143                         this.breakSingleDashManyLettersIntoManyOptions = optionBundle.BreakSingleDashManyLettersIntoManyOptions;
144                         this.endOptionProcessingWithDoubleDash = optionBundle.EndOptionProcessingWithDoubleDash;
145
146                         GetAssemblyAttributeValue(typeof(AssemblyTitleAttribute), "Title", ref appTitle);
147                         GetAssemblyAttributeValue(typeof(AssemblyCopyrightAttribute), "Copyright", ref appCopyright);
148                         GetAssemblyAttributeValue(typeof(AssemblyDescriptionAttribute), "Description", ref appDescription);
149                         GetAssemblyAttributeValue(typeof(Mono.AboutAttribute), ref appAboutDetails);
150                         GetAssemblyAttributeValue(typeof(Mono.UsageComplementAttribute), ref appUsageComplement);
151                         appAuthors = GetAssemblyAttributeStrings(typeof(AuthorAttribute));
152                         if (appAuthors.Length == 0)
153                         {
154                                 appAuthors = new String[1];
155                                 appAuthors[0] = "Add one or more [assembly: Mono.GetOptions.Author(\"Here goes the author name\")] to your assembly";
156                         }
157
158                         foreach(MemberInfo mi in optionBundle.GetType().GetMembers())
159                         {
160                                 object[] attribs = mi.GetCustomAttributes(typeof(OptionAttribute), true);
161                                 if (attribs != null && attribs.Length > 0)
162                                         list.Add(new OptionDetails(mi, (OptionAttribute)attribs[0], optionBundle));
163                                 else
164                                 {
165                                         attribs = mi.GetCustomAttributes(typeof(ArgumentProcessorAttribute), true);
166                                         if (attribs != null && attribs.Length > 0)
167                                                 AddArgumentProcessor(mi);
168                                 }
169                         }
170                 }
171
172                 public OptionList(Options optionBundle)
173                 {
174                         Initialize(optionBundle);
175                 }
176
177                 #endregion
178
179                 #region Prebuilt Options
180
181                 private void ShowTitleLines()
182                 {
183                         Console.WriteLine(appTitle + "  " + appVersion + " - " + appCopyright); 
184                         Console.WriteLine(appDescription); 
185                         Console.WriteLine();
186                 }
187
188                 private void ShowAbout()
189                 {
190                         ShowTitleLines();
191                         Console.WriteLine(appAboutDetails); 
192                         Console.WriteLine();
193                         Console.WriteLine("Authors:");
194                         foreach(string s in appAuthors)
195                                 Console.WriteLine ("\t" + s);
196                 }
197
198                 private void ShowHelp()
199                 {
200                         ShowTitleLines();
201                         Console.WriteLine(Usage);
202                         Console.WriteLine("Options:");
203                         foreach (OptionDetails option in list)
204                                 Console.WriteLine(option);
205                 }
206
207                 private void ShowUsage()
208                 {
209                         Console.WriteLine(Usage);
210                         Console.Write("Short Options: ");
211                         foreach (OptionDetails option in list)
212                                 Console.Write((option.ShortForm != ' ') ? option.ShortForm.ToString() : "");
213                         Console.WriteLine();
214                         
215                 }
216
217                 private void ShowUsage(string errorMessage)
218                 {
219                         Console.WriteLine("ERROR: " + errorMessage.TrimEnd());
220                         ShowUsage();
221                 }
222
223                 internal WhatToDoNext DoUsage()
224                 {
225                         ShowUsage();
226                         return WhatToDoNext.AbandonProgram;
227                 }
228
229                 internal WhatToDoNext DoAbout()
230                 {
231                         ShowAbout();
232                         return WhatToDoNext.GoAhead;
233                 }
234
235                 internal WhatToDoNext DoHelp()
236                 {
237                         ShowHelp();
238                         return WhatToDoNext.AbandonProgram;
239                 }
240
241                 #endregion
242
243                 #region Arguments Processing
244
245                 public string[] ExpandResponseFiles(string[] args)
246                 {
247                         ArrayList result = new ArrayList();
248
249                         foreach(string arg in args)
250                         {
251                                 if (arg.StartsWith("@"))
252                                 {
253                                         try 
254                                         {
255                                                 StreamReader tr = new StreamReader(arg.Substring(1));
256                                                 string line;
257                                                 while ((line = tr.ReadLine()) != null)
258                                                 {
259                                                         result.AddRange(line.Split());
260                                                 }
261                                                 tr.Close(); 
262                                         }
263                                         catch (FileNotFoundException exception)
264                                         {
265                                                 Console.WriteLine("Could not find response file: " + arg.Substring(1));
266                                                 continue;
267                                         }
268                                         catch (Exception exception)
269                                         {
270                                                 Console.WriteLine("Error trying to read response file: " + arg.Substring(1));
271                                                 Console.WriteLine(exception.Message);
272                                                 continue;
273                                         }
274                                 }
275                                 else
276                                         result.Add(arg);
277                         }
278
279                         return (string[])result.ToArray(typeof(string));
280                 }
281
282                 public string[] NormalizeArgs(string[] args)
283                 {
284                         bool ParsingOptions = true;
285                         ArrayList result = new ArrayList();
286
287                         foreach(string arg in ExpandResponseFiles(args))
288                         {
289                                 if (arg.Length > 0)
290                                 {
291                                         if (ParsingOptions)
292                                         {
293                                                 if (endOptionProcessingWithDoubleDash && (arg == "--"))
294                                                 {
295                                                         ParsingOptions = false;
296                                                         continue;
297                                                 }
298
299                                                 if (arg[0] == '/')
300                                                 {
301                                                         if ((parsingMode & OptionsParsingMode.Windows) > 0)
302                                                         {
303                                                                 string newArg = '-' + arg.TrimStart('/');
304                                                                 result.AddRange(newArg.Split(':'));
305                                                                 continue;
306                                                         }
307                                                 }
308
309                                                 if ((parsingMode & OptionsParsingMode.Linux) > 0)
310                                                 {
311                                                         if ((arg[0] == '-') && (arg[1] != '-'))
312                                                         {
313                                                                 if (breakSingleDashManyLettersIntoManyOptions)
314                                                                 {
315                                                                         foreach(char c in arg.Substring(1)) // many single-letter options
316                                                                                 result.Add("-" + c); // expand into individualized options
317                                                                 }
318                                                                 else
319                                                                         result.Add(arg);
320                                                                 continue;
321                                                         }
322
323                                                         if (arg.StartsWith("--"))
324                                                         {
325                                                                 result.AddRange(arg.Split('='));  // put in the same form of one-letter options with a parameter
326                                                                 continue;
327                                                         }
328                                                 }
329                                         }
330                                         else
331                                         {
332                                                 argumentsTail.Add(arg);
333                                                 continue;
334                                         }
335
336                                         // if nothing else matches then it get here
337                                         result.Add(arg);
338                                 }
339                         }
340
341                         return (string[])result.ToArray(typeof(string));
342                 }
343
344                 public string[] ProcessArgs(string[] args)
345                 {
346                         string arg;
347                         string nextArg;
348                         bool OptionWasProcessed;
349
350                         list.Sort();
351
352                         args = NormalizeArgs(args);
353
354                         try
355                         {
356                                 int argc = args.Length;
357                                 for (int i = 0; i < argc; i++)
358                                 {
359                                         arg =  args[i];
360                                         if (i+1 < argc)
361                                                 nextArg = args[i+1];
362                                         else
363                                                 nextArg = null;
364
365                                         OptionWasProcessed = false;
366
367                                         if (arg.StartsWith("-"))
368                                         {
369                                                 foreach(OptionDetails option in list)
370                                                 {
371                                                         OptionProcessingResult result = option.ProcessArgument(arg, nextArg);
372                                                         if (result != OptionProcessingResult.NotThisOption)
373                                                         {
374                                                                 OptionWasProcessed = true;
375                                                                 if (result == OptionProcessingResult.OptionConsumedParameter)
376                                                                         i++;
377                                                                 break;
378                                                         }
379                                                 }
380                                         }
381
382                                         if (!OptionWasProcessed)
383                                         {
384                                                 if (OptionDetails.Verbose)
385                                                         Console.WriteLine("argument [" + arg + "]");
386
387                                                 arguments.Add(arg);
388                                         }
389                                 }
390
391                                 foreach(OptionDetails option in list)
392                                         option.TransferValues(); 
393
394                                 foreach(string argument in argumentsTail)
395                                         arguments.Add(argument);
396
397                                 if (argumentProcessor == null)
398                                         return (string[])arguments.ToArray(typeof(string));
399                         
400                                 foreach(string argument in arguments)
401                                         argumentProcessor.Invoke(optionBundle, new object[] { argument });  
402                         }
403                         catch (Exception ex)
404                         {
405                                 System.Console.WriteLine(ex.ToString());
406                                 System.Environment.Exit(1);
407                         }
408
409                         return null;
410                 }
411                 
412                 #endregion
413
414         }
415 }