[Mono-dev] ANNOUNCE: NDesk.Options 0.2.0
Jonathan Pryor
jonpryor at vt.edu
Thu Feb 14 16:58:15 EST 2008
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[0] documentation for
examples.
[0] http://www.ndesk.org/doc/ndesk-options/NDesk.Options/OptionSet.html
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:
1. cvfname must not match a registered Option. If it does
match a registered option, then that is the Option that
will (eventually) be invoked.
2. c must be a registered option. If it isn't, then
-cvfname is returned from
OptionSet.Parse(IEnumerable<string>).
3. Each character is looked up; if it's a boolean Option,
then the associated action is invoked with a non-null
value.
4. 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.
5. 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 individual 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.
As always, I'm interested in API and design feedback, and any way that
the library could be improved.
Thanks,
- Jon
More information about the Mono-devel-list
mailing list