Announcing NDesk.Options 0.2.0 - Jonathan Pryor's web log
« Unix Signal Handling In C# | Main | HackWeek Summary »
Announcing NDesk.Options 0.2.0
I am pleased to announce the release of NDesk.Options 0.2.0. NDesk.Options is a C# program option parser library, inspired by Perl's Getopt::Long option parser.
To download, visit the NDesk.Options web page:
http://www.ndesk.org/Options
Usage
See http://www.ndesk.org/Options and the OptionSet documentation for examples.
What's New?
There have been numerous changes since the previous 0.1.0 release:
- Mono 1.9 is now required to build. (An svn release was previously required anyway, so this isn't a surprising requirement.)
-
Simplify the API by removing all OptionSet.Add() methods which provided an OptionContext to the callback function; this includes:
- OptionSet.Add(string, Action<string, OptionContext>)
- OptionSet.Add(string, string, Action<string, OptionContext>)
- OptionSet.Add<T>(string, Action<T, OptionContext>)
- OptionSet.Add<T>(string, string, Action<T, OptionContext>)
If you really need access to an OptionContext, you can Add your own Option and override Option.OnParseComplete(OptionContext).
By Miguel's request, change the semantics for Options with optional values (arguments that have a type value of `:'). Previously, Options accepting an optional value were virtually identical to Options accepting a required value; the only place they would differ is at the end of the command line, where if a value was missing for a Option with an optional value no error would occur, while an Option with a required value would generate an error.
Now, we introduce the notion of greediness: required values are greedy, and will eat any number of following arguments in order to fulfill their requirements. Optional values are not greedy, and will only extract a value from the current argument.
By way of example:
string color = null; var p = new OptionSet () { { "-color:", v => color = v }, }; p.Parse (new string[]{"--color=auto"}); // 1 p.Parse (new string[]{"--color", "auto"}); // 2
In NDesk.Options 0.1.0, (1) and (2) would be identical and color would be given the value auto. In 0.2.0, they are not identical: (1) would assign the value auto to color, while (2) would assign null to color. This permits consistency with GNU ls(1)'s ls --color behavior.
If a required option were to be specified (by using = instead of :), then (1) and (2) would again have identical results.
- NDesk.Options 0.1.0 restricted option bundling to boolean
Options. This restriction has been relaxed so that
(1) Options accepting both optional and required values may be
bundled with boolean Options, and (2) the optional or required
value may be bundled as well. As before, only single character
Options may be bundled.
The logic is as follows: given an argument such as -cvfname:
- cvfname must not match a registered Option. If it does match a registered option, then that is the Option that will (eventually) be invoked.
- c must be a registered option. If it isn't, then -cvfname is returned from OptionSet.Parse(IEnumerable<string>).
- Each character is looked up; if it's a boolean Option, then the associated action is invoked with a non-null value.
- If instead the character is an Option accepting one or more optional or required values, then the rest of the argument (not including the Option character) is used as the value. This also follows the greediness of optional vs. required values: optional values will only use the current argument, while required values may use the following argument(s) if e.g. the Option's character is the last character in the sequence.
- If a non-Option character is encountered that is not (a) the first character in the sequence, or (b) used as the value for a previous Option, then an OptionException is thrown.
This does The Right Thing for tar(1)-like option handling, with tar -cvfname ... creating (with verbose output) the file with the name name.
- Options may now accept (or require) more than one value. The
Option (string, string, int) constructor allows specifying how many
values are accepted/required (depending on whether the Option has
optional or required values).
The Option values are available through the OptionContext.OptionValues collection.
- Direct support for Options accepting/required two values within
OptionSet:
- OptionSet.Add(string, OptionAction<string, string>)
- OptionSet.Add(string, string, OptionAction<string, string>)
- OptionSet.Add<TKey, TValue> (string, OptionAction<TKey, TValue>)
- OptionSet.Add<TKey, TValue> (string, string, OptionAction<TKey, TValue>)
This now permits reasonable handling of cc(1)-style parameters:
var macros = new Dictionary<string, string> (); var p = new OptionSet () { { "D:", (k, v) => { if (k != null) macros.Add (k, v); } }, }; p.Parse (new string[]{"-DNAME1", "-DNAME2=VALUE2"}); // Adds the keys "NAME1" (with null value) // and "NAME2" (with value "VALUE2") to `macros'.
Note that an optional value is used; if D= were specified, two values would be required, so -DNAME1 -DNAME2=VALUE2 would insert one macro -- NAME1 -- with the value -DNAME2=VALUE2.
- When an Option permits more than one value, it may provide a
list of value separator strings, strings that may be used to
separate the multiple values. If no separators are listed, = and
: are used as the default (thus permitting the previous
-DNAME2=VALUE2 example to work; -DNAME2:VALUE2 would
have had the same result).
The value separator strings follow the : or = in the Option prototype. They consist of:
- The string within { and }.
- Any other invidividual character.
Thus, the prototype M:+-*/ would use +, -, *, or / to split values, so -M5+2, -M5-2, -M5*2, and -M5/2 all provide two values (5 and 2) to the M option.
The prototype N={-->}{=>} would parse both -NA-->B and -NA=>B so that A and B are provided as the two values to the N option.
As a special construct, the separator {} requires that each value be a separate argument. (This makes no sense for Options with optional values.)
- Naming consistency improvements: an argument is an unparsed string, an option is a parsed argument that corresponds to a registered Option, and a prototype is a description of an Option, describing all aliases, the value type, and value separators. This has resulted in method argument name changes.
- Removal of .NET 3.5 support. Instead of using System.Action`2 from System.Core.dll (or providing an internal equivalent), I've just defined a OptionAction<TKey, TValue> type. This simplifies assembly versioning.