private OptionsParsingMode parsingMode;
private bool breakSingleDashManyLettersIntoManyOptions;
private bool endOptionProcessingWithDoubleDash;
+ public ErrorReporter ReportError;
private string appExeName;
private string appVersion;
private string appDescription = "Add a [assembly: AssemblyDescription(\"Here goes the short description\")] to your assembly";
private string appAboutDetails = "Add a [assembly: Mono.About(\"Here goes the short about details\")] to your assembly";
private string appUsageComplement = "Add a [assembly: Mono.UsageComplement(\"Here goes the usage clause complement\")] to your assembly";
+ private string appAdditionalInfo = null;
+ private string appReportBugsTo = null;
private string[] appAuthors;
private ArrayList list = new ArrayList();
private ArrayList arguments = new ArrayList();
private ArrayList argumentsTail = new ArrayList();
private MethodInfo argumentProcessor = null;
+
+ private bool HasSecondLevelHelp = false;
internal bool MaybeAnOption(string arg)
{
public string Usage
{
- get
- {
+ get {
return "Usage: " + appExeName + " [options] " + appUsageComplement;
}
}
public string AboutDetails
{
- get
- {
+ get {
return appAboutDetails;
}
}
var = result[0].ToString();
}
+ private void ExtractEntryAssemblyInfo(Type optionsType)
+ {
+ entry = optionsType.Assembly;
+ if (entry == this.GetType().Assembly) {
+ entry = Assembly.GetEntryAssembly();
+ }
+
+ appExeName = entry.GetName().Name;
+ appVersion = entry.GetName().Version.ToString();
+ GetAssemblyAttributeValue(typeof(AssemblyTitleAttribute), "Title", ref appTitle);
+ GetAssemblyAttributeValue(typeof(AssemblyCopyrightAttribute), "Copyright", ref appCopyright);
+ GetAssemblyAttributeValue(typeof(AssemblyDescriptionAttribute), "Description", ref appDescription);
+ GetAssemblyAttributeValue(typeof(Mono.AboutAttribute), ref appAboutDetails);
+ GetAssemblyAttributeValue(typeof(Mono.UsageComplementAttribute), ref appUsageComplement);
+ GetAssemblyAttributeValue(typeof(Mono.AdditionalInfoAttribute), ref appAdditionalInfo);
+ GetAssemblyAttributeValue(typeof(Mono.ReportBugsToAttribute), ref appReportBugsTo);
+ appAuthors = GetAssemblyAttributeStrings(typeof(AuthorAttribute));
+ if (appAuthors.Length == 0) {
+ appAuthors = new String[1];
+ appAuthors[0] = "Add one or more [assembly: Mono.Author(\"Here goes the author name\")] to your assembly";
+ }
+ }
+
#endregion
#region Constructors
if (argumentProcessor != null)
throw new NotSupportedException("More than one argument processor method found");
- if ((memberInfo.MemberType == MemberTypes.Method && memberInfo is MethodInfo))
- {
+ if ((memberInfo.MemberType == MemberTypes.Method && memberInfo is MethodInfo)) {
if (((MethodInfo)memberInfo).ReturnType.FullName != typeof(void).FullName)
throw new NotSupportedException("Argument processor method must return 'void'");
throw new NotSupportedException("Argument processor marked member isn't a method");
}
- private void Initialize(Options optionBundle)
+ public OptionList(Options optionBundle)
{
if (optionBundle == null)
throw new ArgumentNullException("optionBundle");
- entry = Assembly.GetEntryAssembly();
- appExeName = entry.GetName().Name;
- appVersion = entry.GetName().Version.ToString();
-
+ Type optionsType = optionBundle.GetType();
this.optionBundle = optionBundle;
this.parsingMode = optionBundle.ParsingMode ;
this.breakSingleDashManyLettersIntoManyOptions = optionBundle.BreakSingleDashManyLettersIntoManyOptions;
this.endOptionProcessingWithDoubleDash = optionBundle.EndOptionProcessingWithDoubleDash;
-
- GetAssemblyAttributeValue(typeof(AssemblyTitleAttribute), "Title", ref appTitle);
- GetAssemblyAttributeValue(typeof(AssemblyCopyrightAttribute), "Copyright", ref appCopyright);
- GetAssemblyAttributeValue(typeof(AssemblyDescriptionAttribute), "Description", ref appDescription);
- GetAssemblyAttributeValue(typeof(Mono.AboutAttribute), ref appAboutDetails);
- GetAssemblyAttributeValue(typeof(Mono.UsageComplementAttribute), ref appUsageComplement);
- appAuthors = GetAssemblyAttributeStrings(typeof(AuthorAttribute));
- if (appAuthors.Length == 0)
- {
- appAuthors = new String[1];
- appAuthors[0] = "Add one or more [assembly: Mono.GetOptions.Author(\"Here goes the author name\")] to your assembly";
- }
-
- foreach(MemberInfo mi in optionBundle.GetType().GetMembers())
- {
- object[] attribs = mi.GetCustomAttributes(typeof(OptionAttribute), true);
- if (attribs != null && attribs.Length > 0)
- list.Add(new OptionDetails(mi, (OptionAttribute)attribs[0], optionBundle));
- else
- {
- attribs = mi.GetCustomAttributes(typeof(ArgumentProcessorAttribute), true);
- if (attribs != null && attribs.Length > 0)
- AddArgumentProcessor(mi);
+ this.ReportError = optionBundle.ReportError;
+
+ ExtractEntryAssemblyInfo(optionsType);
+
+ foreach(MemberInfo mi in optionsType.GetMembers()) {
+ object[] attribs = mi.GetCustomAttributes(typeof(KillOptionAttribute), true);
+ if (attribs == null || attribs.Length == 0) {
+ attribs = mi.GetCustomAttributes(typeof(OptionAttribute), true);
+ if (attribs != null && attribs.Length > 0) {
+ OptionDetails option = new OptionDetails(mi, (OptionAttribute)attribs[0], optionBundle);
+ list.Add(option);
+ HasSecondLevelHelp = HasSecondLevelHelp || option.SecondLevelHelp;
+ } else if (mi.DeclaringType == mi.ReflectedType) { // not inherited
+ attribs = mi.GetCustomAttributes(typeof(ArgumentProcessorAttribute), true);
+ if (attribs != null && attribs.Length > 0)
+ AddArgumentProcessor(mi);
+ }
}
}
- }
-
- public OptionList(Options optionBundle)
- {
- Initialize(optionBundle);
+
+ if (argumentProcessor == null) // try to find an inherited one
+ foreach(MemberInfo mi in optionsType.GetMembers())
+ if (mi.DeclaringType != mi.ReflectedType) { // inherited
+ object[] attribs = mi.GetCustomAttributes(typeof(ArgumentProcessorAttribute), true);
+ if (attribs != null && attribs.Length > 0)
+ AddArgumentProcessor(mi);
+ }
}
#endregion
#region Prebuilt Options
+ private bool bannerAlreadyShown = false;
+
+ internal string AdditionalBannerInfo;
+
+ public void ShowBanner()
+ {
+ if (!bannerAlreadyShown) {
+ Console.WriteLine(appTitle + " " + appVersion + " - " + appCopyright);
+ if (AdditionalBannerInfo != null)
+ Console.WriteLine(AdditionalBannerInfo);
+ }
+ bannerAlreadyShown = true;
+ }
+
private void ShowTitleLines()
{
- Console.WriteLine(appTitle + " " + appVersion + " - " + appCopyright);
+ ShowBanner();
Console.WriteLine(appDescription);
Console.WriteLine();
}
{
ShowTitleLines();
Console.WriteLine(appAboutDetails);
- StringBuilder sb = new StringBuilder("Authors: ");
- bool first = true;
- foreach(string s in appAuthors)
- {
- if (first)
- first = false;
- else
- sb.Append(", ");
- sb.Append(s);
- }
- Console.WriteLine(sb.ToString());
+ Console.Write("Authors: ");
+ Console.WriteLine(string.Join(", ", appAuthors));
}
- private void ShowHelp()
+ private void ShowHelp(bool showSecondLevelHelp)
{
ShowTitleLines();
Console.WriteLine(Usage);
Console.WriteLine("Options:");
+ ArrayList lines = new ArrayList(list.Count);
+ int tabSize = 0;
foreach (OptionDetails option in list)
- Console.WriteLine(option);
+ if (option.SecondLevelHelp == showSecondLevelHelp) {
+ string[] optionLines = option.ToString().Split('\n');
+ foreach(string line in optionLines) {
+ int pos = line.IndexOf('\t');
+ if (pos > tabSize)
+ tabSize = pos;
+ lines.Add(line);
+ }
+ }
+ tabSize += 2;
+ foreach (string line in lines) {
+ string[] parts = line.Split('\t');
+ Console.Write(parts[0].PadRight(tabSize));
+ Console.WriteLine(parts[1]);
+ if (parts.Length > 2) {
+ string spacer = new string(' ', tabSize);
+ for(int i = 2; i < parts.Length; i++) {
+ Console.Write(spacer);
+ Console.WriteLine(parts[i]);
+ }
+ }
+ }
+ if (appAdditionalInfo != null)
+ Console.WriteLine("\n{0}", appAdditionalInfo);
+ if (appReportBugsTo != null)
+ Console.WriteLine("\nPlease report bugs {0} <{1}>", (appReportBugsTo.IndexOf('@')>0)?"to":"at" , appReportBugsTo);
+
}
private void ShowUsage()
}
- private void ShowUsage(string errorMessage)
- {
- Console.WriteLine("ERROR: " + errorMessage.TrimEnd());
- ShowUsage();
- }
-
internal WhatToDoNext DoUsage()
{
ShowUsage();
internal WhatToDoNext DoHelp()
{
- ShowHelp();
+ ShowHelp(false);
return WhatToDoNext.AbandonProgram;
}
+ internal WhatToDoNext DoHelp2()
+ {
+ ShowHelp(true);
+ return WhatToDoNext.AbandonProgram;
+ }
+
#endregion
- #region Arguments Processing
-
- public string[] ExpandResponseFiles(string[] args)
+ #region Response File Expansion
+
+ private void processResponseFileLine(string line, ArrayList result, StringBuilder sb)
{
- ArrayList result = new ArrayList();
-
- foreach(string arg in args)
- {
- if (arg.StartsWith("@"))
- {
- try
- {
- StreamReader tr = new StreamReader(arg.Substring(1));
- string line;
- while ((line = tr.ReadLine()) != null)
- {
- result.AddRange(line.Split());
- }
- tr.Close();
+ int t = line.Length;
+ for (int i = 0; i < t; i++) {
+ char c = line [i];
+ if (c == '"' || c == '\'') {
+ char end = c;
+ for (i++; i < t; i++) {
+ c = line [i];
+ if (c == end)
+ break;
+ sb.Append(c);
}
- catch (FileNotFoundException exception)
- {
- Console.WriteLine("Could not find response file: " + arg.Substring(1));
- continue;
- }
- catch (Exception exception)
- {
- Console.WriteLine("Error trying to read response file: " + arg.Substring(1));
- Console.WriteLine(exception.Message);
- continue;
+ } else if (c == ' ') {
+ if (sb.Length > 0) {
+ result.Add(sb.ToString());
+ sb.Length = 0;
}
+ } else {
+ sb.Append(c);
}
- else
- result.Add(arg);
}
+ if (sb.Length > 0) {
+ result.Add(sb.ToString());
+ sb.Length = 0;
+ }
+ }
+
+ private void processResponseFile(string filename, ArrayList result)
+ {
+ StringBuilder sb = new StringBuilder();
+ string line;
+ try {
+ using (StreamReader responseFile = new StreamReader(filename)) {
+ while ((line = responseFile.ReadLine()) != null)
+ processResponseFileLine(line, result, sb);
+ responseFile.Close ();
+ }
+ } catch (FileNotFoundException) {
+ ReportError(2011, "Unable to find response file '" + filename + "'");
+ } catch (Exception exception) {
+ ReportError(2011, "Unable to open response file '" + filename + "'. " + exception.Message);
+ }
+ }
- return (string[])result.ToArray(typeof(string));
+ private ArrayList ExpandResponseFiles(string[] args)
+ {
+ ArrayList result = new ArrayList();
+ foreach(string arg in args)
+ if (arg.StartsWith("@"))
+ processResponseFile(arg.Substring(1), result);
+ else
+ result.Add(arg);
+ return result;
}
+
+ #endregion
- public string[] NormalizeArgs(string[] args)
+ #region Arguments Processing
+
+
+ private static int IndexOfAny(string where, params char[] what)
+ {
+ return where.IndexOfAny(what);
+ }
+
+ private string[] NormalizeArgs(string[] args)
{
bool ParsingOptions = true;
ArrayList result = new ArrayList();
-
- foreach(string arg in ExpandResponseFiles(args))
- {
- if (arg.Length > 0)
- {
- if (ParsingOptions)
- {
- if (endOptionProcessingWithDoubleDash && (arg == "--"))
- {
+
+ foreach(string arg in ExpandResponseFiles(args)) {
+ if (arg.Length > 0) {
+ if (ParsingOptions) {
+ if (endOptionProcessingWithDoubleDash && (arg == "--")) {
ParsingOptions = false;
continue;
}
if ((parsingMode & OptionsParsingMode.Linux) > 0 &&
- arg[0] == '-' && arg[1] != '-' &&
- breakSingleDashManyLettersIntoManyOptions)
- {
+ arg[0] == '-' && arg.Length > 1 && arg[1] != '-' &&
+ breakSingleDashManyLettersIntoManyOptions) {
foreach(char c in arg.Substring(1)) // many single-letter options
result.Add("-" + c); // expand into individualized options
continue;
}
- if (MaybeAnOption(arg))
- {
- int pos = arg.IndexOfAny(":=".ToCharArray());
+ if (MaybeAnOption(arg)) {
+ int pos = IndexOfAny(arg, ':', '=');
if(pos < 0)
result.Add(arg);
}
continue;
}
- }
- else
- {
+ } else {
argumentsTail.Add(arg);
continue;
}
bool OptionWasProcessed;
list.Sort();
+
+ OptionDetails.LinkAlternatesInsideList(list);
args = NormalizeArgs(args);
- try
- {
+ try {
int argc = args.Length;
- for (int i = 0; i < argc; i++)
- {
+ for (int i = 0; i < argc; i++) {
arg = args[i];
if (i+1 < argc)
nextArg = args[i+1];
OptionWasProcessed = false;
- if (arg.StartsWith("-") || arg.StartsWith("/"))
- {
- foreach(OptionDetails option in list)
- {
+ if (arg.Length > 1 && (arg.StartsWith("-") || arg.StartsWith("/"))) {
+ foreach(OptionDetails option in list) {
OptionProcessingResult result = option.ProcessArgument(arg, nextArg);
- if (result != OptionProcessingResult.NotThisOption)
- {
+ if (result != OptionProcessingResult.NotThisOption) {
OptionWasProcessed = true;
if (result == OptionProcessingResult.OptionConsumedParameter)
i++;
}
if (!OptionWasProcessed)
- {
- if (OptionDetails.Verbose)
- Console.WriteLine("argument [" + arg + "]");
-
- arguments.Add(arg);
- }
+ ProcessNonOption(arg);
}
foreach(OptionDetails option in list)
option.TransferValues();
foreach(string argument in argumentsTail)
- arguments.Add(argument);
+ ProcessNonOption(argument);
- if (argumentProcessor == null)
- return (string[])arguments.ToArray(typeof(string));
-
- foreach(string argument in arguments)
- argumentProcessor.Invoke(optionBundle, new object[] { argument });
- }
- catch (Exception ex)
- {
+ return (string[])arguments.ToArray(typeof(string));
+
+ } catch (Exception ex) {
System.Console.WriteLine(ex.ToString());
System.Environment.Exit(1);
}
return null;
}
+ private void ProcessNonOption(string argument)
+ {
+ if (optionBundle.VerboseParsingOfOptions)
+ Console.WriteLine("argument [" + argument + "]");
+ if (argumentProcessor == null)
+ arguments.Add(argument);
+ else
+ argumentProcessor.Invoke(optionBundle, new object[] { argument });
+ }
+
#endregion
}