see the changelog
[mono.git] / mcs / class / Mono.GetOptions / OptionList.cs
1 //
2 // OptionList.cs
3 //
4 // Author: Rafael Teixeira (rafaelteixeirabr@hotmail.com) (original)
5 // Author: Dean Scarff (D_Scarff@AIMINGFORSPAMFREEmsn.com) (modifications)
6 //
7 // (C) 2002 Rafael Teixeira
8 // Modifications (C) 2002 Dean Scarff, distributed under the Gnu General Public License Version 2 or later
9 //
10
11 using System;
12 using System.Collections;
13 using System.IO;
14 using System.Reflection;
15
16 namespace Mono.GetOptions
17 {
18
19         [Flags]
20         public enum OptionsParsingMode 
21         { 
22                 Linux   = 1, 
23                 Windows = 2,
24                 Both    = 3
25         }
26
27         /// <summary>
28         /// Option Parsing
29         /// </summary>
30         public class OptionList
31         {
32         
33                 private Options optionBundle = null;
34                 private OptionsParsingMode parsingMode;
35                 private bool endOptionProcessingWithDoubleDash;
36                 
37                 private string appExeName;
38                 private string appVersion;
39
40                 private string appTitle = "Add a [assembly: AssemblyTitle(\"Here goes the application name\")] to your assembly";
41                 private string appCopyright = "Add a [assembly: AssemblyCopyright(\"(c)200n Here goes the copyright holder name\")] to your assembly";
42                 private string appDescription = "Add a [assembly: AssemblyDescription(\"Here goes the short description\")] to your assembly";
43                 private string appAboutDetails = "Add a [assembly: Mono.About(\"Here goes the short about details\")] to your assembly";
44                 private string appUsageComplement = "Add a [assembly: Mono.UsageComplement(\"Here goes the usage clause complement\")] to your assembly";
45                 private string[] appAuthors;
46  
47                 private ArrayList list = new ArrayList();
48                 private ArrayList arguments = new ArrayList();
49                 private ArrayList argumentsTail = new ArrayList();
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 Initialize(Options optionBundle)
113                 {
114                         if (optionBundle == null)
115                                 throw new ArgumentNullException("optionBundle");
116
117                         entry = Assembly.GetEntryAssembly();
118                         appExeName = entry.GetName().Name;
119                         appVersion = entry.GetName().Version.ToString();
120
121                         this.optionBundle = optionBundle; 
122                         this.parsingMode = optionBundle.ParsingMode ;
123                         this.endOptionProcessingWithDoubleDash = optionBundle.EndOptionProcessingWithDoubleDash;
124
125                         GetAssemblyAttributeValue(typeof(AssemblyTitleAttribute), "Title", ref appTitle);
126                         GetAssemblyAttributeValue(typeof(AssemblyCopyrightAttribute), "Copyright", ref appCopyright);
127                         GetAssemblyAttributeValue(typeof(AssemblyDescriptionAttribute), "Description", ref appDescription);
128                         GetAssemblyAttributeValue(typeof(Mono.AboutAttribute), ref appAboutDetails);
129                         GetAssemblyAttributeValue(typeof(Mono.UsageComplementAttribute), ref appUsageComplement);
130                         appAuthors = GetAssemblyAttributeStrings(typeof(AuthorAttribute));
131                         if (appAuthors.Length == 0)
132                         {
133                                 appAuthors = new String[1];
134                                 appAuthors[0] = "Add one or more [assembly: Mono.GetOptions.Author(\"Here goes the author name\")] to your assembly";
135                         }
136
137                         foreach(MemberInfo mi in optionBundle.GetType().GetMembers())
138                         {
139                                 object[] attribs = mi.GetCustomAttributes(typeof(OptionAttribute), true);
140                                 if (attribs != null && attribs.Length > 0)
141                                         list.Add(new OptionDetails(mi, (OptionAttribute)attribs[0], optionBundle));
142                         }
143                 }
144
145                 public OptionList(Options optionBundle)
146                 {
147                         Initialize(optionBundle);
148                 }
149
150                 #endregion
151
152                 #region Prebuilt Options
153
154                 private void ShowTitleLines()
155                 {
156                         Console.WriteLine(appTitle + "  " + appVersion + " - " + appCopyright); 
157                         Console.WriteLine(appDescription); 
158                         Console.WriteLine();
159                 }
160
161                 private void ShowAbout()
162                 {
163                         ShowTitleLines();
164                         Console.WriteLine(appAboutDetails); 
165                         Console.WriteLine();
166                         Console.WriteLine("Authors:");
167                         foreach(string s in appAuthors)
168                                 Console.WriteLine ("\t" + s);
169                 }
170
171                 private void ShowHelp()
172                 {
173                         ShowTitleLines();
174                         Console.WriteLine(Usage);
175                         Console.WriteLine("Options:");
176                         foreach (OptionDetails option in list)
177                                 Console.WriteLine(option);
178                 }
179
180                 private void ShowUsage()
181                 {
182                         Console.WriteLine(Usage);
183                         Console.Write("Short Options: ");
184                         foreach (OptionDetails option in list)
185                                 Console.Write((option.ShortForm != ' ') ? option.ShortForm.ToString() : "");
186                         Console.WriteLine();
187                         
188                 }
189
190                 private void ShowUsage(string errorMessage)
191                 {
192                         Console.WriteLine("ERROR: " + errorMessage.TrimEnd());
193                         ShowUsage();
194                 }
195
196                 internal WhatToDoNext DoUsage()
197                 {
198                         ShowUsage();
199                         return WhatToDoNext.AbandonProgram;
200                 }
201
202                 internal WhatToDoNext DoAbout()
203                 {
204                         ShowAbout();
205                         return WhatToDoNext.GoAhead;
206                 }
207
208                 internal WhatToDoNext DoHelp()
209                 {
210                         ShowHelp();
211                         return WhatToDoNext.AbandonProgram;
212                 }
213
214                 #endregion
215
216                 #region Arguments Processing
217
218                 public string[] NormalizeArgs(string[] args)
219                 {
220                         bool ParsingOptions = true;
221                         ArrayList result = new ArrayList();
222
223                         foreach(string arg in args)
224                         {
225                                 if (ParsingOptions)
226                                 {
227                                         if (endOptionProcessingWithDoubleDash && (arg == "--"))
228                                         {
229                                                 ParsingOptions = false;
230                                                 continue;
231                                         }
232
233                                         if ((parsingMode & OptionsParsingMode.Windows) > 0)
234                                         {
235                                                 if ((arg.Length == 2) && (arg[0] == '/')) // Windows options only come in this fashion
236                                                 {
237                                                         result.Add("-" + arg[1]); // translate to Linux style
238                                                         continue;
239                                                 }
240                                         }
241
242                                         if ((parsingMode & OptionsParsingMode.Linux) > 0)
243                                         {
244                                                 if ((arg[0] == '-') && (arg[1] != '-'))
245                                                 {
246                                                         foreach(char c in arg.Substring(1)) // many single-letter options
247                                                                 result.Add("-" + c); // expand into individualized options
248                                                         continue;
249                                                 }
250
251                                                 if (arg.StartsWith("--"))
252                                                 {
253                                                         result.AddRange(arg.Split('='));  // put in the same form of one-letter options with a parameter
254                                                         continue;
255                                                 }
256                                         }
257                                 }
258                                 else
259                                 {
260                                         argumentsTail.Add(arg);
261                                         continue;
262                                 }
263
264                                 // if nothing else matches then it get here
265                                 result.Add(arg);
266                         }
267
268                         return (string[])result.ToArray(typeof(string));
269                 }
270
271                 public string[] ProcessArgs(string[] args)
272                 {
273                         string arg;
274                         string nextArg;
275                         bool OptionWasProcessed;
276
277                         list.Sort();
278
279                         args = NormalizeArgs(args);
280
281                         try
282                         {
283                                 int argc = args.Length;
284                                 for(int i = 0; i < argc; i++)
285                                 {
286                                         arg =  args[i];
287                                         if (i+1 < argc)
288                                         {
289                                                 nextArg = args[i+1];
290                                                 if (nextArg.StartsWith("-"))
291                                                         nextArg = null;
292                                         }
293                                         else
294                                                 nextArg = null;
295
296                                         OptionWasProcessed = false;
297
298                                         if (arg.StartsWith("-"))
299                                         {
300                                                 foreach(OptionDetails option in list)
301                                                 {
302                                                         if (option.ProcessArgument(arg, nextArg))
303                                                         {
304                                                                 OptionWasProcessed = true;
305                                                                 if (nextArg != null)
306                                                                         i++;
307                                                                 break;
308                                                         }
309                                                 }
310                                         }
311
312                                         if (!OptionWasProcessed)
313                                                 arguments.Add(arg); 
314                                 }
315
316                                 foreach(OptionDetails option in list)
317                                         option.TransferValues(); 
318
319                                 foreach(string argument in argumentsTail)
320                                         arguments.Add(argument);
321                         }
322                         catch (Exception ex)
323                         {
324                                 ShowUsage(ex.Message);
325                                 System.Environment.Exit(1);
326                         }
327
328                         return (string[])arguments.ToArray(typeof(string));
329                 }
330                 
331                 #endregion
332
333         }
334 }