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