Remove Mono.GetOptions
[mono.git] / mcs / class / Mono.GetOptions / Mono.GetOptions / OptionList.cs
index fa240a03657d0f2dd647472fb9d58436fa23a8b4..37e64eadaf045b2cb01f5dc0f9c1455d54af322c 100644 (file)
@@ -46,6 +46,7 @@ namespace Mono.GetOptions
                private OptionsParsingMode parsingMode;
                private bool breakSingleDashManyLettersIntoManyOptions;
                private bool endOptionProcessingWithDoubleDash;
+               public ErrorReporter ReportError;
                
                private string appExeName;
                private string appVersion;
@@ -55,12 +56,16 @@ namespace Mono.GetOptions
                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)
                {
@@ -70,16 +75,14 @@ namespace Mono.GetOptions
 
                public string Usage
                {
-                       get
-                       {
+                       get {
                                return "Usage: " + appExeName + " [options] " + appUsageComplement;
                        }
                }
 
                public string AboutDetails
                {
-                       get
-                       {
+                       get {
                                return appAboutDetails;
                        }
                }
@@ -125,6 +128,29 @@ namespace Mono.GetOptions
                                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
@@ -134,8 +160,7 @@ namespace Mono.GetOptions
                        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'");
 
@@ -149,58 +174,66 @@ namespace Mono.GetOptions
                                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();
                }
@@ -209,26 +242,45 @@ namespace Mono.GetOptions
                {
                        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()
@@ -241,12 +293,6 @@ namespace Mono.GetOptions
                        
                }
 
-               private void ShowUsage(string errorMessage)
-               {
-                       Console.WriteLine("ERROR: " + errorMessage.TrimEnd());
-                       ShowUsage();
-               }
-
                internal WhatToDoNext DoUsage()
                {
                        ShowUsage();
@@ -261,80 +307,109 @@ namespace Mono.GetOptions
 
                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);
@@ -344,9 +419,7 @@ namespace Mono.GetOptions
                                                        }
                                                        continue;
                                                }
-                                       }
-                                       else
-                                       {
+                                       } else {
                                                argumentsTail.Add(arg);
                                                continue;
                                        }
@@ -366,14 +439,14 @@ namespace Mono.GetOptions
                        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];
@@ -382,13 +455,10 @@ namespace Mono.GetOptions
 
                                        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++;
@@ -398,28 +468,18 @@ namespace Mono.GetOptions
                                        }
 
                                        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);
                        }
@@ -427,6 +487,16 @@ namespace Mono.GetOptions
                        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
 
        }