4 // Author: Rafael Teixeira (rafaelteixeirabr@hotmail.com)
6 // (C) 2002 Rafael Teixeira
10 using System.Collections;
12 using System.Reflection;
14 namespace Mono.GetOptions
18 public enum OptionsParsingMode
28 public class OptionList
31 private Options optionBundle = null;
32 private OptionsParsingMode parsingMode;
33 private bool endOptionProcessingWithDoubleDash;
35 private string appExeName;
36 private string appVersion;
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;
45 private ArrayList list = new ArrayList();
46 private ArrayList arguments = new ArrayList();
47 private ArrayList argumentsTail = new ArrayList();
48 private MethodInfo argumentProcessor = null;
54 return "Usage: " + appExeName + " [options] " + appUsageComplement;
58 public string AboutDetails
62 return appAboutDetails;
66 #region Assembly Attributes
70 private object[] GetAssemblyAttributes(Type type)
72 return entry.GetCustomAttributes(type, false);
75 private string[] GetAssemblyAttributeStrings(Type type)
77 object[] result = GetAssemblyAttributes(type);
79 if ((result == null) || (result.Length == 0))
83 string[] var = new string[result.Length];
85 foreach(object o in result)
86 var[i++] = o.ToString();
91 private void GetAssemblyAttributeValue(Type type, string propertyName, ref string var)
93 object[] result = GetAssemblyAttributes(type);
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 [] {}); ;
99 private void GetAssemblyAttributeValue(Type type, ref string var)
101 object[] result = GetAssemblyAttributes(type);
103 if ((result != null) && (result.Length > 0))
104 var = result[0].ToString();
111 private void AddArgumentProcessor(MemberInfo memberInfo)
113 if (argumentProcessor != null)
114 throw new NotSupportedException("More than one argument processor method found");
116 if ((memberInfo.MemberType == MemberTypes.Method && memberInfo is MethodInfo))
118 if (((MethodInfo)memberInfo).ReturnType.FullName != typeof(void).FullName)
119 throw new NotSupportedException("Argument processor method must return 'void'");
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");
125 argumentProcessor = (MethodInfo)memberInfo;
128 throw new NotSupportedException("Argument processor marked member isn't a method");
131 private void Initialize(Options optionBundle)
133 if (optionBundle == null)
134 throw new ArgumentNullException("optionBundle");
136 entry = Assembly.GetEntryAssembly();
137 appExeName = entry.GetName().Name;
138 appVersion = entry.GetName().Version.ToString();
140 this.optionBundle = optionBundle;
141 this.parsingMode = optionBundle.ParsingMode ;
142 this.endOptionProcessingWithDoubleDash = optionBundle.EndOptionProcessingWithDoubleDash;
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)
152 appAuthors = new String[1];
153 appAuthors[0] = "Add one or more [assembly: Mono.GetOptions.Author(\"Here goes the author name\")] to your assembly";
156 foreach(MemberInfo mi in optionBundle.GetType().GetMembers())
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));
163 attribs = mi.GetCustomAttributes(typeof(ArgumentProcessorAttribute), true);
164 if (attribs != null && attribs.Length > 0)
165 AddArgumentProcessor(mi);
170 public OptionList(Options optionBundle)
172 Initialize(optionBundle);
177 #region Prebuilt Options
179 private void ShowTitleLines()
181 Console.WriteLine(appTitle + " " + appVersion + " - " + appCopyright);
182 Console.WriteLine(appDescription);
186 private void ShowAbout()
189 Console.WriteLine(appAboutDetails);
191 Console.WriteLine("Authors:");
192 foreach(string s in appAuthors)
193 Console.WriteLine ("\t" + s);
196 private void ShowHelp()
199 Console.WriteLine(Usage);
200 Console.WriteLine("Options:");
201 foreach (OptionDetails option in list)
202 Console.WriteLine(option);
205 private void ShowUsage()
207 Console.WriteLine(Usage);
208 Console.Write("Short Options: ");
209 foreach (OptionDetails option in list)
210 Console.Write((option.ShortForm != ' ') ? option.ShortForm.ToString() : "");
215 private void ShowUsage(string errorMessage)
217 Console.WriteLine("ERROR: " + errorMessage.TrimEnd());
221 internal WhatToDoNext DoUsage()
224 return WhatToDoNext.AbandonProgram;
227 internal WhatToDoNext DoAbout()
230 return WhatToDoNext.GoAhead;
233 internal WhatToDoNext DoHelp()
236 return WhatToDoNext.AbandonProgram;
241 #region Arguments Processing
243 public string[] ExpandResponseFiles(string[] args)
245 ArrayList result = new ArrayList();
247 foreach(string arg in args)
249 if (arg.StartsWith("@"))
253 StreamReader tr = new StreamReader(arg.Substring(1));
255 while ((line = tr.ReadLine()) != null)
257 result.AddRange(line.Split());
261 catch (FileNotFoundException exception)
263 Console.WriteLine("Could not find response file: " + arg.Substring(1));
266 catch (Exception exception)
268 Console.WriteLine("Error trying to read response file: " + arg.Substring(1));
269 Console.WriteLine(exception.Message);
277 return (string[])result.ToArray(typeof(string));
280 public string[] NormalizeArgs(string[] args)
282 bool ParsingOptions = true;
283 ArrayList result = new ArrayList();
285 foreach(string arg in ExpandResponseFiles(args))
291 if (endOptionProcessingWithDoubleDash && (arg == "--"))
293 ParsingOptions = false;
297 if ((parsingMode & OptionsParsingMode.Windows) > 0)
300 result.AddRange(arg.Split(':'));
303 if ((parsingMode & OptionsParsingMode.Linux) > 0)
305 if ((arg[0] == '-') && (arg[1] != '-'))
307 foreach(char c in arg.Substring(1)) // many single-letter options
308 result.Add("-" + c); // expand into individualized options
312 if (arg.StartsWith("--"))
314 result.AddRange(arg.Split('=')); // put in the same form of one-letter options with a parameter
321 argumentsTail.Add(arg);
325 // if nothing else matches then it get here
330 return (string[])result.ToArray(typeof(string));
333 public string[] ProcessArgs(string[] args)
337 bool OptionWasProcessed;
341 args = NormalizeArgs(args);
345 int argc = args.Length;
346 for(int i = 0; i < argc; i++)
352 if (nextArg.StartsWith("-"))
358 OptionWasProcessed = false;
360 if (arg.StartsWith("-") || arg.StartsWith("/"))
362 foreach(OptionDetails option in list)
364 if (option.ProcessArgument(arg, nextArg))
366 OptionWasProcessed = true;
374 if (!OptionWasProcessed)
378 foreach(OptionDetails option in list)
379 option.TransferValues();
381 foreach(string argument in argumentsTail)
382 arguments.Add(argument);
384 if (argumentProcessor == null)
385 return (string[])arguments.ToArray(typeof(string));
387 foreach(string argument in arguments)
388 argumentProcessor.Invoke(optionBundle, new object[] { argument });
392 ShowUsage(ex.Message);
393 System.Environment.Exit(1);