4 // Author: Rafael Teixeira (rafaelteixeirabr@hotmail.com)
6 // (C) 2002 Rafael Teixeira
10 // Permission is hereby granted, free of charge, to any person obtaining
11 // a copy of this software and associated documentation files (the
12 // "Software"), to deal in the Software without restriction, including
13 // without limitation the rights to use, copy, modify, merge, publish,
14 // distribute, sublicense, and/or sell copies of the Software, and to
15 // permit persons to whom the Software is furnished to do so, subject to
16 // the following conditions:
18 // The above copyright notice and this permission notice shall be
19 // included in all copies or substantial portions of the Software.
21 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
22 // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
23 // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
24 // NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
25 // LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
26 // OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
27 // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
31 using System
.Collections
;
33 using System
.Reflection
;
36 namespace Mono
.GetOptions
42 public class OptionList
45 private Options optionBundle
= null;
46 private OptionsParsingMode parsingMode
;
47 private bool breakSingleDashManyLettersIntoManyOptions
;
48 private bool endOptionProcessingWithDoubleDash
;
50 private string appExeName
;
51 private string appVersion
;
53 private string appTitle
= "Add a [assembly: AssemblyTitle(\"Here goes the application name\")] to your assembly";
54 private string appCopyright
= "Add a [assembly: AssemblyCopyright(\"(c)200n Here goes the copyright holder name\")] to your assembly";
55 private string appDescription
= "Add a [assembly: AssemblyDescription(\"Here goes the short description\")] to your assembly";
56 private string appAboutDetails
= "Add a [assembly: Mono.About(\"Here goes the short about details\")] to your assembly";
57 private string appUsageComplement
= "Add a [assembly: Mono.UsageComplement(\"Here goes the usage clause complement\")] to your assembly";
58 private string[] appAuthors
;
60 private ArrayList list
= new ArrayList();
61 private ArrayList arguments
= new ArrayList();
62 private ArrayList argumentsTail
= new ArrayList();
63 private MethodInfo argumentProcessor
= null;
65 internal bool MaybeAnOption(string arg
)
67 return ((parsingMode
& OptionsParsingMode
.Windows
) > 0 && arg
[0] == '/') ||
68 ((parsingMode
& OptionsParsingMode
.Linux
) > 0 && arg
[0] == '-');
75 return "Usage: " + appExeName
+ " [options] " + appUsageComplement
;
79 public string AboutDetails
83 return appAboutDetails
;
87 #region Assembly Attributes
91 private object[] GetAssemblyAttributes(Type type
)
93 return entry
.GetCustomAttributes(type
, false);
96 private string[] GetAssemblyAttributeStrings(Type type
)
98 object[] result
= GetAssemblyAttributes(type
);
100 if ((result
== null) || (result
.Length
== 0))
101 return new string[0];
104 string[] var = new string[result
.Length
];
106 foreach(object o
in result
)
107 var[i
++] = o
.ToString();
112 private void GetAssemblyAttributeValue(Type type
, string propertyName
, ref string var)
114 object[] result
= GetAssemblyAttributes(type
);
116 if ((result
!= null) && (result
.Length
> 0))
117 var = (string)type
.InvokeMember(propertyName
, BindingFlags
.Public
| BindingFlags
.GetField
| BindingFlags
.GetProperty
| BindingFlags
.Instance
, null, result
[0], new object [] {}); ;
120 private void GetAssemblyAttributeValue(Type type
, ref string var)
122 object[] result
= GetAssemblyAttributes(type
);
124 if ((result
!= null) && (result
.Length
> 0))
125 var = result
[0].ToString();
132 private void AddArgumentProcessor(MemberInfo memberInfo
)
134 if (argumentProcessor
!= null)
135 throw new NotSupportedException("More than one argument processor method found");
137 if ((memberInfo
.MemberType
== MemberTypes
.Method
&& memberInfo
is MethodInfo
))
139 if (((MethodInfo
)memberInfo
).ReturnType
.FullName
!= typeof(void).FullName
)
140 throw new NotSupportedException("Argument processor method must return 'void'");
142 ParameterInfo
[] parameters
= ((MethodInfo
)memberInfo
).GetParameters();
143 if ((parameters
== null) || (parameters
.Length
!= 1) || (parameters
[0].ParameterType
.FullName
!= typeof(string).FullName
))
144 throw new NotSupportedException("Argument processor method must have a string parameter");
146 argumentProcessor
= (MethodInfo
)memberInfo
;
149 throw new NotSupportedException("Argument processor marked member isn't a method");
152 private void Initialize(Options optionBundle
)
154 if (optionBundle
== null)
155 throw new ArgumentNullException("optionBundle");
157 entry
= Assembly
.GetEntryAssembly();
158 appExeName
= entry
.GetName().Name
;
159 appVersion
= entry
.GetName().Version
.ToString();
161 this.optionBundle
= optionBundle
;
162 this.parsingMode
= optionBundle
.ParsingMode
;
163 this.breakSingleDashManyLettersIntoManyOptions
= optionBundle
.BreakSingleDashManyLettersIntoManyOptions
;
164 this.endOptionProcessingWithDoubleDash
= optionBundle
.EndOptionProcessingWithDoubleDash
;
166 GetAssemblyAttributeValue(typeof(AssemblyTitleAttribute
), "Title", ref appTitle
);
167 GetAssemblyAttributeValue(typeof(AssemblyCopyrightAttribute
), "Copyright", ref appCopyright
);
168 GetAssemblyAttributeValue(typeof(AssemblyDescriptionAttribute
), "Description", ref appDescription
);
169 GetAssemblyAttributeValue(typeof(Mono
.AboutAttribute
), ref appAboutDetails
);
170 GetAssemblyAttributeValue(typeof(Mono
.UsageComplementAttribute
), ref appUsageComplement
);
171 appAuthors
= GetAssemblyAttributeStrings(typeof(AuthorAttribute
));
172 if (appAuthors
.Length
== 0)
174 appAuthors
= new String
[1];
175 appAuthors
[0] = "Add one or more [assembly: Mono.GetOptions.Author(\"Here goes the author name\")] to your assembly";
178 foreach(MemberInfo mi
in optionBundle
.GetType().GetMembers())
180 object[] attribs
= mi
.GetCustomAttributes(typeof(OptionAttribute
), true);
181 if (attribs
!= null && attribs
.Length
> 0)
182 list
.Add(new OptionDetails(mi
, (OptionAttribute
)attribs
[0], optionBundle
));
185 attribs
= mi
.GetCustomAttributes(typeof(ArgumentProcessorAttribute
), true);
186 if (attribs
!= null && attribs
.Length
> 0)
187 AddArgumentProcessor(mi
);
192 public OptionList(Options optionBundle
)
194 Initialize(optionBundle
);
199 #region Prebuilt Options
201 private void ShowTitleLines()
203 Console
.WriteLine(appTitle
+ " " + appVersion
+ " - " + appCopyright
);
204 Console
.WriteLine(appDescription
);
208 private void ShowAbout()
211 Console
.WriteLine(appAboutDetails
);
212 StringBuilder sb
= new StringBuilder("Authors: ");
214 foreach(string s
in appAuthors
)
222 Console
.WriteLine(sb
.ToString());
225 private void ShowHelp()
228 Console
.WriteLine(Usage
);
229 Console
.WriteLine("Options:");
230 foreach (OptionDetails option
in list
)
231 Console
.WriteLine(option
);
234 private void ShowUsage()
236 Console
.WriteLine(Usage
);
237 Console
.Write("Short Options: ");
238 foreach (OptionDetails option
in list
)
239 Console
.Write(option
.ShortForm
.Trim());
244 private void ShowUsage(string errorMessage
)
246 Console
.WriteLine("ERROR: " + errorMessage
.TrimEnd());
250 internal WhatToDoNext
DoUsage()
253 return WhatToDoNext
.AbandonProgram
;
256 internal WhatToDoNext
DoAbout()
259 return WhatToDoNext
.AbandonProgram
;
262 internal WhatToDoNext
DoHelp()
265 return WhatToDoNext
.AbandonProgram
;
270 #region Arguments Processing
272 public string[] ExpandResponseFiles(string[] args
)
274 ArrayList result
= new ArrayList();
276 foreach(string arg
in args
)
278 if (arg
.StartsWith("@"))
282 StreamReader tr
= new StreamReader(arg
.Substring(1));
284 while ((line
= tr
.ReadLine()) != null)
286 result
.AddRange(line
.Split());
290 catch (FileNotFoundException exception
)
292 Console
.WriteLine("Could not find response file: " + arg
.Substring(1));
295 catch (Exception exception
)
297 Console
.WriteLine("Error trying to read response file: " + arg
.Substring(1));
298 Console
.WriteLine(exception
.Message
);
306 return (string[])result
.ToArray(typeof(string));
309 public string[] NormalizeArgs(string[] args
)
311 bool ParsingOptions
= true;
312 ArrayList result
= new ArrayList();
314 foreach(string arg
in ExpandResponseFiles(args
))
320 if (endOptionProcessingWithDoubleDash
&& (arg
== "--"))
322 ParsingOptions
= false;
326 if ((parsingMode
& OptionsParsingMode
.Linux
) > 0 &&
327 arg
[0] == '-' && arg
[1] != '-' &&
328 breakSingleDashManyLettersIntoManyOptions
)
330 foreach(char c
in arg
.Substring(1)) // many single-letter options
331 result
.Add("-" + c
); // expand into individualized options
335 if (MaybeAnOption(arg
))
337 int pos
= arg
.IndexOfAny(":=".ToCharArray());
342 result
.Add(arg
.Substring(0, pos
));
343 result
.Add(arg
.Substring(pos
+1));
350 argumentsTail
.Add(arg
);
354 // if nothing else matches then it get here
359 return (string[])result
.ToArray(typeof(string));
362 public string[] ProcessArgs(string[] args
)
366 bool OptionWasProcessed
;
370 args
= NormalizeArgs(args
);
374 int argc
= args
.Length
;
375 for (int i
= 0; i
< argc
; i
++)
383 OptionWasProcessed
= false;
385 if (arg
.StartsWith("-") || arg
.StartsWith("/"))
387 foreach(OptionDetails option
in list
)
389 OptionProcessingResult result
= option
.ProcessArgument(arg
, nextArg
);
390 if (result
!= OptionProcessingResult
.NotThisOption
)
392 OptionWasProcessed
= true;
393 if (result
== OptionProcessingResult
.OptionConsumedParameter
)
400 if (!OptionWasProcessed
)
402 if (OptionDetails
.Verbose
)
403 Console
.WriteLine("argument [" + arg
+ "]");
409 foreach(OptionDetails option
in list
)
410 option
.TransferValues();
412 foreach(string argument
in argumentsTail
)
413 arguments
.Add(argument
);
415 if (argumentProcessor
== null)
416 return (string[])arguments
.ToArray(typeof(string));
418 foreach(string argument
in arguments
)
419 argumentProcessor
.Invoke(optionBundle
, new object[] { argument }
);
423 System
.Console
.WriteLine(ex
.ToString());
424 System
.Environment
.Exit(1);