5 // Jonathan Pryor <jpryor@novell.com>
7 // Copyright (C) 2008 Novell (http://www.novell.com)
9 // Permission is hereby granted, free of charge, to any person obtaining
10 // a copy of this software and associated documentation files (the
11 // "Software"), to deal in the Software without restriction, including
12 // without limitation the rights to use, copy, modify, merge, publish,
13 // distribute, sublicense, and/or sell copies of the Software, and to
14 // permit persons to whom the Software is furnished to do so, subject to
15 // the following conditions:
17 // The above copyright notice and this permission notice shall be
18 // included in all copies or substantial portions of the Software.
20 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
21 // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
22 // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
23 // NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
24 // LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
25 // OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
26 // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
30 // gmcs -debug+ -d:TEST -r:System.Core Options.cs
31 // gmcs -debug+ -d:LINQ -d:TEST -r:System.Core Options.cs
34 // A Getopt::Long-inspired option parsing library for C#.
36 // NDesk.Options.OptionSet is built upon a key/value table, where the
37 // key is a option format string and the value is an Action<string>
38 // delegate that is invoked when the format string is matched.
40 // Option format strings:
41 // BNF Grammar: ( name [=:]? ) ( '|' name [=:]? )+
43 // Each '|'-delimited name is an alias for the associated action. If the
44 // format string ends in a '=', it has a required value. If the format
45 // string ends in a ':', it has an optional value. If neither '=' or ':'
46 // is present, no value is supported.
48 // Options are extracted either from the current option by looking for
49 // the option name followed by an '=' or ':', or is taken from the
50 // following option IFF:
51 // - The current option does not contain a '=' or a ':'
52 // - The following option is not a registered named option
54 // The `name' used in the option format string does NOT include any leading
55 // option indicator, such as '-', '--', or '/'. All three of these are
56 // permitted/required on any named option.
58 // Option bundling is permitted so long as:
59 // - '-' is used to start the option group
60 // - all of the bundled options do not require values
61 // - all of the bundled options are a single character
63 // This allows specifying '-a -b -c' as '-abc'.
65 // Option processing is disabled by specifying "--". All options after "--"
66 // are returned by OptionSet.Parse() unchanged and unprocessed.
68 // Unprocessed options are returned from OptionSet.Parse().
72 // OptionSet p = new OptionSet ()
73 // .Add ("v", v => ++verbose)
74 // .Add ("name=|value=", v => Console.WriteLine (v));
75 // p.Parse (new string[]{"-v", "--v", "/v", "-name=A", "/name", "B", "extra"});
77 // The above would parse the argument string array, and would invoke the
78 // lambda expression three times, setting `verbose' to 3 when complete.
79 // It would also print out "A" and "B" to standard output.
80 // The returned array would contain the string "extra".
82 // C# 3.0 collection initializers are supported:
83 // var p = new OptionSet () {
84 // { "h|?|help", v => ShowHelp () },
87 // System.ComponentModel.TypeConverter is also supported, allowing the use of
88 // custom data types in the callback type; TypeConverter.ConvertFromString()
89 // is used to convert the value option to an instance of the specified
92 // var p = new OptionSet () {
93 // { "foo=", (Foo f) => Console.WriteLine (f.ToString ()) },
96 // Random other tidbits:
97 // - Boolean options (those w/o '=' or ':' in the option format string)
98 // are explicitly enabled if they are followed with '+', and explicitly
99 // disabled if they are followed with '-':
101 // var p = new OptionSet () {
102 // { "a", s => a = s },
104 // p.Parse (new string[]{"-a"}); // sets v != null
105 // p.Parse (new string[]{"-a+"}); // sets v != null
106 // p.Parse (new string[]{"-a-"}); // sets v == null
111 using System
.Collections
;
112 using System
.Collections
.Generic
;
113 using System
.Collections
.ObjectModel
;
114 using System
.ComponentModel
;
115 using System
.Globalization
;
117 using System
.Runtime
.Serialization
;
118 using System
.Text
.RegularExpressions
;
126 using Wv
.NDesk
.Options
;
131 public delegate void Action
<T1
,T2
> (T1 a
, T2 b
);
135 namespace Wv
.NDesk
.Options
{
137 public enum OptionValueType
{
143 public class OptionContext
{
144 public OptionContext ()
148 public Option Option { get; set; }
149 public string OptionName { get; set; }
150 public int OptionIndex { get; set; }
151 public string OptionValue { get; set; }
154 public abstract class Option
{
155 string prototype
, description
;
157 OptionValueType type
;
159 protected Option (string prototype
, string description
)
161 if (prototype
== null)
162 throw new ArgumentNullException ("prototype");
163 if (prototype
.Length
== 0)
164 throw new ArgumentException ("Cannot be the empty string.", "prototype");
166 this.prototype
= prototype
;
167 this.names
= prototype
.Split ('|');
168 this.description
= description
;
169 this.type
= ValidateNames ();
172 public string Prototype { get { return prototype; }
}
173 public string Description { get { return description; }
}
174 public OptionValueType OptionValueType { get { return type; }
}
176 public string[] GetNames ()
178 return (string[]) names
.Clone ();
181 internal string[] Names { get { return names; }
}
183 static readonly char[] NameTerminator
= new char[]{'=', ':'}
;
184 private OptionValueType
ValidateNames ()
187 for (int i
= 0; i
< names
.Length
; ++i
) {
188 string name
= names
[i
];
189 if (name
.Length
== 0)
190 throw new ArgumentException ("Empty option names are not supported.", "prototype");
192 int end
= name
.IndexOfAny (NameTerminator
);
194 names
[i
] = name
.Substring (0, end
);
195 if (type
== '\0' || type
== name
[end
])
198 throw new ArgumentException (
199 string.Format ("Conflicting option types: '{0}' vs. '{1}'.", type
, name
[end
]),
204 return OptionValueType
.None
;
205 return type
== '=' ? OptionValueType
.Required
: OptionValueType
.Optional
;
208 public void Invoke (OptionContext c
)
212 c
.OptionValue
= null;
216 protected abstract void OnParseComplete (OptionContext c
);
218 public override string ToString ()
225 public class OptionException
: Exception
{
226 private string option
;
228 public OptionException (string message
, string optionName
)
231 this.option
= optionName
;
234 public OptionException (string message
, string optionName
, Exception innerException
)
235 : base (message
, innerException
)
237 this.option
= optionName
;
240 protected OptionException (SerializationInfo info
, StreamingContext context
)
241 : base (info
, context
)
243 this.option
= info
.GetString ("OptionName");
246 public string OptionName
{
247 get {return this.option;}
250 public override void GetObjectData (SerializationInfo info
, StreamingContext context
)
252 base.GetObjectData (info
, context
);
253 info
.AddValue ("OptionName", option
);
257 public class OptionSet
: Collection
<Option
>
264 public OptionSet (Converter
<string, string> localizer
)
266 this.localizer
= localizer
;
269 Dictionary
<string, Option
> options
= new Dictionary
<string, Option
> ();
270 Converter
<string, string> localizer
;
272 protected Option
GetOptionForName (string option
)
275 throw new ArgumentNullException ("option");
277 if (options
.TryGetValue (option
, out v
))
282 protected override void ClearItems ()
284 this.options
.Clear ();
287 protected override void InsertItem (int index
, Option item
)
290 base.InsertItem (index
, item
);
293 protected override void RemoveItem (int index
)
295 Option p
= Items
[index
];
296 foreach (string name
in p
.Names
) {
297 this.options
.Remove (name
);
299 base.RemoveItem (index
);
302 protected override void SetItem (int index
, Option item
)
306 base.SetItem (index
, item
);
309 class ActionOption
: Option
{
310 Action
<string, OptionContext
> action
;
312 public ActionOption (string prototype
, string description
, Action
<string, OptionContext
> action
)
313 : base (prototype
, description
)
316 throw new ArgumentNullException ("action");
317 this.action
= action
;
320 protected override void OnParseComplete (OptionContext c
)
322 action (c
.OptionValue
, c
);
326 public new OptionSet
Add (Option option
)
329 throw new ArgumentNullException ("option");
330 List
<string> added
= new List
<string> ();
332 foreach (string name
in option
.Names
) {
333 this.options
.Add (name
, option
);
337 foreach (string name
in added
)
338 this.options
.Remove (name
);
344 public OptionSet
Add (string options
, Action
<string> action
)
346 return Add (options
, null, action
);
349 public OptionSet
Add (string options
, Action
<string, OptionContext
> action
)
351 return Add (options
, null, action
);
354 public OptionSet
Add (string options
, string description
, Action
<string> action
)
357 throw new ArgumentNullException ("action");
358 return Add (options
, description
, (v
,c
) => {action (v);}
);
361 public OptionSet
Add (string options
, string description
, Action
<string, OptionContext
> action
)
363 Option p
= new ActionOption (options
, description
, action
);
368 public OptionSet Add
<T
> (string options
, Action
<T
> action
)
370 return Add (options
, null, action
);
373 public OptionSet Add
<T
> (string options
, Action
<T
, OptionContext
> action
)
375 return Add (options
, null, action
);
378 public OptionSet Add
<T
> (string options
, string description
, Action
<T
> action
)
380 return Add (options
, description
, (T v
, OptionContext c
) => {action (v);}
);
383 public OptionSet Add
<T
> (string options
, string description
, Action
<T
, OptionContext
> action
)
385 TypeConverter conv
= TypeDescriptor
.GetConverter (typeof(T
));
386 Action
<string, OptionContext
> a
= delegate (string s
, OptionContext c
) {
390 t
= (T
) conv
.ConvertFromString (s
);
392 catch (Exception e
) {
393 throw new OptionException (
395 localizer ("Could not convert string `{0}' to type {1} for option `{2}'."),
396 s
, typeof(T
).Name
, c
.OptionName
),
401 return Add (options
, description
, a
);
404 protected virtual OptionContext
CreateOptionContext ()
406 return new OptionContext ();
410 public List
<string> Parse (IEnumerable
<string> options
)
413 OptionContext c
= CreateOptionContext ();
416 from option
in options
417 where
++c
.OptionIndex
>= 0 && process
423 List
<string> r
= unprocessed
.ToList ();
424 if (c
.Option
!= null)
429 public List
<string> Parse (IEnumerable
<string> options
)
431 OptionContext c
= CreateOptionContext ();
434 List
<string> unprocessed
= new List
<string> ();
435 foreach (string option
in options
) {
437 if (option
== "--") {
442 unprocessed
.Add (option
);
445 if (!Parse (option
, c
))
446 unprocessed
.Add (option
);
448 if (c
.Option
!= null)
454 private readonly Regex ValueOption
= new Regex (
455 @"^(?<flag>--|-|/)(?<name>[^:=]+)([:=](?<value>.*))?$");
457 protected bool GetOptionParts (string option
, out string flag
, out string name
, out string value)
459 Match m
= ValueOption
.Match (option
);
461 flag
= name
= value = null;
464 flag
= m
.Groups
["flag"].Value
;
465 name
= m
.Groups
["name"].Value
;
466 value = !m
.Groups
["value"].Success
? null : m
.Groups
["value"].Value
;
470 protected virtual bool Parse (string option
, OptionContext c
)
472 if (c
.Option
!= null) {
473 c
.OptionValue
= option
;
479 if (!GetOptionParts (option
, out f
, out n
, out v
))
483 if (this.options
.TryGetValue (n
, out p
)) {
484 c
.OptionName
= f
+ n
;
486 switch (p
.OptionValueType
) {
487 case OptionValueType
.None
:
491 case OptionValueType
.Optional
:
492 case OptionValueType
.Required
:
501 // no match; is it a bool option?
502 if (ParseBool (option
, n
, c
))
504 // is it a bundled option?
505 if (ParseBundled (f
, n
, c
))
511 private bool ParseBool (string option
, string n
, OptionContext c
)
514 if (n
.Length
>= 1 && (n
[n
.Length
-1] == '+' || n
[n
.Length
-1] == '-') &&
515 this.options
.TryGetValue (n
.Substring (0, n
.Length
-1), out p
)) {
516 string v
= n
[n
.Length
-1] == '+' ? option
: null;
517 c
.OptionName
= option
;
526 private bool ParseBundled (string f
, string n
, OptionContext c
)
529 if (f
== "-" && this.options
.TryGetValue (n
[0].ToString (), out p
)) {
532 string opt
= "-" + n
[i
].ToString ();
533 if (p
.OptionValueType
!= OptionValueType
.None
) {
534 throw new OptionException (string.Format (
535 localizer ("Cannot bundle option '{0}' that requires a value."), opt
),
542 } while (++i
< n
.Length
&& this.options
.TryGetValue (n
[i
].ToString (), out p
));
548 private void NoValue (OptionContext c
)
550 c
.OptionValue
= null;
552 if (p
!= null && p
.OptionValueType
== OptionValueType
.Optional
) {
555 else if (p
!= null && p
.OptionValueType
== OptionValueType
.Required
) {
556 throw new OptionException (string.Format (
557 localizer ("Missing required value for option '{0}'."), c
.OptionName
),
562 private const int OptionWidth
= 29;
564 public void WriteOptionDescriptions (TextWriter o
)
566 foreach (Option p
in this) {
567 List
<string> names
= new List
<string> (p
.Names
);
570 if (names
[0].Length
== 1) {
571 Write (o
, ref written
, " -");
572 Write (o
, ref written
, names
[0]);
575 Write (o
, ref written
, " --");
576 Write (o
, ref written
, names
[0]);
579 for (int i
= 1; i
< names
.Count
; ++i
) {
580 Write (o
, ref written
, ", ");
581 Write (o
, ref written
, names
[i
].Length
== 1 ? "-" : "--");
582 Write (o
, ref written
, names
[i
]);
585 if (p
.OptionValueType
== OptionValueType
.Optional
)
586 Write (o
, ref written
, localizer ("[=VALUE]"));
587 else if (p
.OptionValueType
== OptionValueType
.Required
)
588 Write (o
, ref written
, localizer ("=VALUE"));
590 if (written
< OptionWidth
)
591 o
.Write (new string (' ', OptionWidth
- written
));
594 o
.Write (new string (' ', OptionWidth
));
597 o
.WriteLine (localizer (p
.Description
));
601 static void Write (TextWriter o
, ref int n
, string s
)
610 namespace Tests
.NDesk
.Options
{
614 class FooConverter
: TypeConverter
{
615 public override bool CanConvertFrom (ITypeDescriptorContext context
, Type sourceType
)
617 if (sourceType
== typeof (string))
619 return base.CanConvertFrom (context
, sourceType
);
622 public override object ConvertFrom (ITypeDescriptorContext context
,
623 CultureInfo culture
, object value)
625 string v
= value as string;
628 case "A": return Foo
.A
;
629 case "B": return Foo
.B
;
633 return base.ConvertFrom (context
, culture
, value);
637 [TypeConverter (typeof(FooConverter
))]
639 public static readonly Foo A
= new Foo ("A");
640 public static readonly Foo B
= new Foo ("B");
642 Foo (string s
) { this.s = s; }
643 public override string ToString () {return s;}
647 public static void Main (string[] args
)
649 var tests
= new Dictionary
<string, Action
> () {
650 { "boolean", () => CheckBoolean () }
,
651 { "bundling", () => CheckOptionBundling () }
,
652 { "context", () => CheckOptionContext () }
,
653 { "descriptions", () => CheckWriteOptionDescriptions () }
,
654 { "exceptions", () => CheckExceptions () }
,
655 { "halt", () => CheckHaltProcessing () }
,
656 { "localization", () => CheckLocalization () }
,
657 { "many", () => CheckMany () }
,
658 { "optional", () => CheckOptional () }
,
659 { "required", () => CheckRequired () }
,
660 { "derived-type", () => CheckDerivedType () }
,
664 var p
= new OptionSet () {
666 "Run the specified test. Valid tests:\n" + new string (' ', 32) +
667 string.Join ("\n" + new string (' ', 32), tests
.Keys
.OrderBy (s
=> s
).ToArray ()),
668 v
=> { run = false; Console.WriteLine (v); tests [v] (); }
},
669 { "h|?|help", "Show this message and exit", (v) => help = v != null }
,
673 Console
.WriteLine ("usage: Options.exe [OPTION]+\n");
674 Console
.WriteLine ("Options unit test program.");
675 Console
.WriteLine ("Valid options include:");
676 p
.WriteOptionDescriptions (Console
.Out
);
678 foreach (Action a
in tests
.Values
)
683 static IEnumerable
<string> _ (params string[] a
)
688 static void CheckRequired ()
692 OptionSet p
= new OptionSet () {
693 { "a=", v => a = v }
,
694 { "n=", (int v) => n = v }
,
696 List
<string> extra
= p
.Parse (_("a", "-a", "s", "-n=42", "n"));
697 Assert (extra
.Count
, 2);
698 Assert (extra
[0], "a");
699 Assert (extra
[1], "n");
703 extra
= p
.Parse (_("-a="));
704 Assert (extra
.Count
, 0);
708 static void CheckOptional ()
713 OptionSet p
= new OptionSet () {
714 { "a:", v => a = v }
,
715 { "n:", (int v) => n = v }
,
716 { "f:", (Foo v) => f = v }
,
725 p
.Parse (_("-f", "A"));
730 p
.Parse (_("-n", "42"));
736 static void CheckBoolean ()
739 OptionSet p
= new OptionSet () {
740 { "a", v => a = v != null }
,
750 static void CheckMany ()
753 string av
= null, bv
= null;
757 OptionSet p
= new OptionSet () {
758 { "a=", v => { a = 1; av = v; }
},
759 { "b", "desc", v => {b = 2; bv = v;}
},
760 { "f=", (Foo v) => f = v }
,
761 { "v", v => { ++verbose; }
},
762 { "h|?|help", (v
) => { switch (v
) {
763 case "h": help
|= 0x1; break;
764 case "?": help
|= 0x2; break;
765 case "help": help
|= 0x4; break;
768 List
<string> e
= p
.Parse (new string[]{"foo", "-v", "-a=42", "/b-",
769 "-a", "64", "bar", "--f", "B", "/h", "-?", "--help", "-v"});
772 Assert (e
[0], "foo");
773 Assert (e
[1], "bar");
783 static void Assert
<T
>(T actual
, T expected
)
785 if (!object.Equals (actual
, expected
))
786 throw new InvalidOperationException (
787 string.Format ("Assertion failed: {0} != {1}", actual
, expected
));
790 class DefaultOption
: Option
{
791 public DefaultOption (string prototypes
, string description
)
792 : base (prototypes
, description
)
796 protected override void OnParseComplete (OptionContext c
)
798 throw new NotImplementedException ();
802 static void CheckExceptions ()
805 var p
= new OptionSet () {
806 { "a=", v => a = v }
,
808 { "n=", (int v) => { }
},
809 { "f=", (Foo v) => { }
},
812 AssertException (typeof(OptionException
),
813 "Missing required value for option '-a'.",
814 p
, v
=> { v.Parse (_("-a")); }
);
815 // another named option while expecting one -- follow Getopt::Long
816 AssertException (null, null,
817 p
, v
=> { v.Parse (_("-a", "-a")); }
);
819 // no exception when an unregistered named option follows.
820 AssertException (null, null,
821 p
, v
=> { v.Parse (_("-a", "-b")); }
);
823 AssertException (typeof(ArgumentNullException
),
824 "Argument cannot be null.\nParameter name: option",
825 p
, v
=> { v.Add (null); }
);
828 AssertException (typeof(OptionException
),
829 "Could not convert string `value' to type Int32 for option `-n'.",
830 p
, v
=> { v.Parse (_("-n", "value")); }
);
831 AssertException (typeof(OptionException
),
832 "Could not convert string `invalid' to type Foo for option `--f'.",
833 p
, v
=> { v.Parse (_("--f", "invalid")); }
);
835 // try to bundle with an option requiring a value
836 AssertException (typeof(OptionException
),
837 "Cannot bundle option '-a' that requires a value.",
838 p
, v
=> { v.Parse (_("-ca", "value")); }
);
840 AssertException (typeof(ArgumentNullException
),
841 "Argument cannot be null.\nParameter name: prototype",
842 p
, v
=> { new DefaultOption (null, null); }
);
843 AssertException (typeof(ArgumentException
),
844 "Cannot be the empty string.\nParameter name: prototype",
845 p
, v
=> { new DefaultOption ("", null); }
);
846 AssertException (typeof(ArgumentException
),
847 "Empty option names are not supported.\nParameter name: prototype",
848 p
, v
=> { new DefaultOption ("a|b||c=", null); }
);
849 AssertException (typeof(ArgumentException
),
850 "Conflicting option types: '=' vs. ':'.\nParameter name: prototype",
851 p
, v
=> { new DefaultOption ("a=|b:", null); }
);
852 AssertException (typeof(ArgumentNullException
),
853 "Argument cannot be null.\nParameter name: action",
854 p
, v
=> { v.Add ("foo", (Action<string>) null); }
);
855 AssertException (typeof(ArgumentNullException
),
856 "Argument cannot be null.\nParameter name: action",
857 p
, v
=> { v.Add ("foo", (Action<string, OptionContext>) null); }
);
860 static void AssertException
<T
> (Type exception
, string message
, T a
, Action
<T
> action
)
862 Type actualType
= null;
864 string actualMessage
= null;
868 catch (Exception e
) {
869 actualType
= e
.GetType ();
870 actualMessage
= e
.Message
;
871 if (!object.Equals (actualType
, exception
))
872 stack
= e
.ToString ();
874 if (!object.Equals (actualType
, exception
)) {
875 throw new InvalidOperationException (
876 string.Format ("Assertion failed: Expected Exception Type {0}, got {1}.\n" +
877 "Actual Exception: {2}", exception
, actualType
, stack
));
879 if (!object.Equals (actualMessage
, message
))
880 throw new InvalidOperationException (
881 string.Format ("Assertion failed:\n\tExpected: {0}\n\t Actual: {1}",
882 message
, actualMessage
));
885 static void CheckWriteOptionDescriptions ()
887 var p
= new OptionSet () {
888 { "p|indicator-style=", "append / indicator to directories", v => {}
},
889 { "color:", "controls color info", v => {}
},
890 { "h|?|help", "show help text", v => {}
},
891 { "version", "output version information and exit", v => {}
},
894 StringWriter expected
= new StringWriter ();
895 expected
.WriteLine (" -p, --indicator-style=VALUE");
896 expected
.WriteLine (" append / indicator to directories");
897 expected
.WriteLine (" --color[=VALUE] controls color info");
898 expected
.WriteLine (" -h, -?, --help show help text");
899 expected
.WriteLine (" --version output version information and exit");
901 StringWriter actual
= new StringWriter ();
902 p
.WriteOptionDescriptions (actual
);
904 Assert (actual
.ToString (), expected
.ToString ());
907 static void CheckOptionBundling ()
911 var p
= new OptionSet () {
912 { "a", v => a = "a" }
,
913 { "b", v => b = "b" }
,
914 { "c", v => c = "c" }
,
916 p
.Parse (_ ("-abc"));
922 static void CheckHaltProcessing ()
924 var p
= new OptionSet () {
928 List
<string> e
= p
.Parse (_ ("-a", "-b", "--", "-a", "-b"));
930 Assert (e
[0], "-a");
931 Assert (e
[1], "-b");
934 static void CheckLocalization ()
936 var p
= new OptionSet (f
=> "hello!") {
937 { "n=", (int v) => { }
},
939 AssertException (typeof(OptionException
), "hello!",
940 p
, v
=> { v.Parse (_("-n=value")); }
);
942 StringWriter expected
= new StringWriter ();
943 expected
.WriteLine (" -nhello! hello!");
945 StringWriter actual
= new StringWriter ();
946 p
.WriteOptionDescriptions (actual
);
948 Assert (actual
.ToString (), expected
.ToString ());
951 class CiOptionSet
: OptionSet
{
952 protected override void InsertItem (int index
, Option item
)
954 if (item
.Prototype
.ToLower () != item
.Prototype
)
955 throw new ArgumentException ("prototypes must be null!");
956 base.InsertItem (index
, item
);
959 protected override bool Parse (string option
, OptionContext c
)
961 if (c
.Option
!= null)
962 return base.Parse (option
, c
);
964 if (!GetOptionParts (option
, out f
, out n
, out v
)) {
965 return base.Parse (option
, c
);
967 return base.Parse (f
+ n
.ToLower () + (v
!= null ? "=" + v
: ""), c
);
970 public new Option
GetOptionForName (string n
)
972 return base.GetOptionForName (n
);
976 static void CheckDerivedType ()
979 var p
= new CiOptionSet () {
980 { "h|help", v => help = v != null }
,
985 p
.Parse (_("-HELP"));
988 Assert (p
.GetOptionForName ("h"), p
[0]);
989 Assert (p
.GetOptionForName ("help"), p
[0]);
990 Assert (p
.GetOptionForName ("invalid"), null);
992 AssertException (typeof(ArgumentException
), "prototypes must be null!",
993 p
, v
=> { v.Add ("N|NUM=", (int n) => {}
); });
994 AssertException (typeof(ArgumentNullException
),
995 "Argument cannot be null.\nParameter name: option",
996 p
, v
=> { v.GetOptionForName (null); }
);
999 static void CheckOptionContext ()
1001 var p
= new OptionSet () {
1002 { "a=", "a desc", (v
,c
) => {
1003 Assert (v
, "a-val");
1004 Assert (c
.Option
.Description
, "a desc");
1005 Assert (c
.OptionName
, "/a");
1006 Assert (c
.OptionIndex
, 1);
1007 Assert (c
.OptionValue
, v
);
1009 { "b", "b desc", (v
, c
) => {
1011 Assert (c
.Option
.Description
, "b desc");
1012 Assert (c
.OptionName
, "--b+");
1013 Assert (c
.OptionIndex
, 2);
1014 Assert (c
.OptionValue
, v
);
1016 { "c=", "c desc", (v
, c
) => {
1018 Assert (c
.Option
.Description
, "c desc");
1019 Assert (c
.OptionName
, "--c");
1020 Assert (c
.OptionIndex
, 3);
1021 Assert (c
.OptionValue
, v
);
1023 { "d", "d desc", (v
, c
) => {
1025 Assert (c
.Option
.Description
, "d desc");
1026 Assert (c
.OptionName
, "/d-");
1027 Assert (c
.OptionIndex
, 4);
1028 Assert (c
.OptionValue
, v
);
1031 p
.Parse (_("/a", "a-val", "--b+", "--c=C", "/d-"));