// // Options.cs // // Authors: // Jonathan Pryor // // Copyright (C) 2008 Novell (http://www.novell.com) // // Permission is hereby granted, free of charge, to any person obtaining // a copy of this software and associated documentation files (the // "Software"), to deal in the Software without restriction, including // without limitation the rights to use, copy, modify, merge, publish, // distribute, sublicense, and/or sell copies of the Software, and to // permit persons to whom the Software is furnished to do so, subject to // the following conditions: // // The above copyright notice and this permission notice shall be // included in all copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND // NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE // LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION // OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. // // Compile With: // gmcs -debug+ -d:TEST -langversion:linq -r:System.Core Options.cs // // A Getopt::Long-inspired option parsing library for C#. // // Mono.Documentation.Options is built upon a key/value table, where the // key is a option format string and the value is an Action // delegate that is invoked when the format string is matched. // // Option format strings: // BNF Grammar: ( name [=:]? ) ( '|' name [=:]? )+ // // Each '|'-delimited name is an alias for the associated action. If the // format string ends in a '=', it has a required value. If the format // string ends in a ':', it has an optional value. If neither '=' or ':' // is present, no value is supported. // // Options are extracted either from the current option by looking for // the option name followed by an '=' or ':', or is taken from the // following option IFF: // - The current option does not contain a '=' or a ':' // - The following option is not a registered named option // // The `name' used in the option format string does NOT include any leading // option indicator, such as '-', '--', or '/'. All three of these are // permitted/required on any named option. // // Option bundling is permitted so long as: // - '-' is used to start the option group // - all of the bundled options do not require values // - all of the bundled options are a single character // // This allows specifying '-a -b -c' as '-abc'. // // Option processing is disabled by specifying "--". All options after "--" // are returned by Options.Parse() unchanged and unprocessed. // // Unprocessed options are returned from Options.Parse(). // // Examples: // int verbose = 0; // Options p = new Options () // .Add ("v", (v) => ++verbose) // .Add ("name=|value=", (v) => Console.WriteLine (v)); // p.Parse (new string[]{"-v", "--v", "/v", "-name=A", "/name", "B", "extra"}) // .ToArray (); // // The above would parse the argument string array, and would invoke the // lambda expression three times, setting `verbose' to 3 when complete. // It would also print out "A" and "B" to standard output. // The returned arrray would contain the string "extra". // // C# 3.0 collection initializers are supported: // var p = new Options () { // { "h|?|help", (v) => ShowHelp () }, // }; // // System.ComponentModel.TypeConverter is also supported, allowing the use of // custom data types in the callback type; TypeConverter.ConvertFromString() // is used to convert the value option to an instance of the specified // type: // // var p = new Options () { // { "foo=", (Foo f) => Console.WriteLine (f.ToString ()) }, // }; // // Random other tidbits: // - Boolean options (those w/o '=' or ':' in the option format string) // are explicitly enabled if they are followed with '+', and explicitly // disabled if they are followed with '-': // string a = null; // var p = new Options () { // { "a", (s) => a = s }, // }; // p.Parse (new string[]{"-a"}); // sets v != null // p.Parse (new string[]{"-a+"}); // sets v != null // p.Parse (new string[]{"-a-"}); // sets v == null // using System; using System.Collections; using System.Collections.Generic; using System.Collections.ObjectModel; using System.ComponentModel; using System.Globalization; using System.IO; using System.Text.RegularExpressions; #if TEST using Mono.Documentation; #endif namespace Mono.Documentation { enum OptionValue { None, Optional, Required } public class Option { string prototype, description; Action action; string[] prototypes; OptionValue type; public Option (string prototype, string description, Action action) { this.prototype = prototype; this.prototypes = prototype.Split ('|'); this.description = description; this.action = action; this.type = GetOptionValue (); } public string Prototype { get { return prototype; } } public string Description { get { return description; } } public Action Action { get { return action; } } internal string[] Prototypes { get { return prototypes; } } internal OptionValue OptionValue { get { return type; } } OptionValue GetOptionValue () { foreach (string n in Prototypes) { if (n.IndexOf ('=') >= 0) return OptionValue.Required; if (n.IndexOf (':') >= 0) return OptionValue.Optional; } return OptionValue.None; } public override string ToString () { return Prototype; } } public class Options : Collection