It is often useful to have an option which takes an argument which is a
string that is restricted to a set of values. For example, GNU
ls
(and many other programs) take a --color option,
which has an argument that can be ‘always’, ‘auto’, or
‘never’ (in addition to various synonyms). Mu supports similar
argument parsing, called enumerated arguments.
Names and values for enumerated types are specified in the
enum_values
field of the MU_OPT
structure (see Option Structure). enum_values
is an array of MU_ENUM_VALUE
structures as defined below. enum_values
is terminated by an
element with a name
field of NULL
.
If the enum_case_match
field of the MU_OPT
structure is
nonzero (see Option Structure), matching is case
sensitive. Otherwise, matching is case insensitive.
Like long options and suboptions, abbreviation is allowed when passing enumerated arguments, as long as it is not ambiguous.
Unlike MU_OPT
(see Option Structure), this structure is
simple and it’s organization is guaranteed. Therefore, you may use
positional initializers to initialize this structure. Of course, you can
still use designated initializers if you prefer.
const char *name
This field specifies the name to match against when parsing the
argument. Like long options, suboptions, and environment variables,
name
may have aliases separated by ‘|’ (see Aliases for Options and Environment Variables). Alternatively, aliases can be specified by using separate
entries with the same value
(see below).
Duplicates in this field, either duplicate aliases or duplicates between
entries, are not allowed. Note that if enum_case_match
is zero,
case is not considered. So, if enum_case_match
is zero, you
cannot have two entries, ‘foo’ and ‘FOO’, nor can you have two
aliases specified as ‘foo|FOO’.
int value
This is the value of the enumeration. It is the value passed as the
arg parameter of callback_enum
(see Option Callbacks),
and, if the arg
field of the MU_OPT
structure is not
NULL
, it is the value stored in *arg
.
The following example illustrates how to use both case insensitive enumerated argument parsing, and case sensitive enumerated argument parsing:
#include <stdio.h> #include <mu/options.h> #include <mu/safe.h> /* For mu_opt_context_x{new,free} */ enum selection { FOO, BAR, BAZ }; int main(int argc, char **argv) { enum selection sel; int found_sel; int ret; const MU_ENUM_VALUE enum_table[] = { /* Aliases can be specified for enumerated arguments. */ { "foo|alias-foo", FOO }, { "bar", BAR }, /* Aliases can alternatively be specified like this. */ { "alias-bar", BAR }, { "baz", BAZ }, /* This terminates the enumeration specification. */ { 0 } }; const MU_OPT options[] = { { .short_opt = "s", .long_opt = "selection", .has_arg = MU_OPT_REQUIRED, .arg_type = MU_OPT_ENUM, /* This indicates that matching should be case insensitive. */ .enum_case_match = 0, .enum_values = enum_table, .found_arg = &found_sel, .arg = &sel }, { .short_opt = "c", .long_opt = "case-selection", .has_arg = MU_OPT_REQUIRED, .arg_type = MU_OPT_ENUM, /* This indicates that matching should be case sensitive. */ .enum_case_match = 1, .enum_values = enum_table, .found_arg = &found_sel, .arg = &sel }, { 0 } }; MU_OPT_CONTEXT *context; /* Parse the options. */ context = mu_opt_context_xnew(argc, argv, options, MU_OPT_PERMUTE); ret = mu_parse_opts(context); mu_opt_context_xfree(context); if (MU_OPT_ERR(ret)) return 1; if (found_sel) { /* Print the selection. */ fputs("You selected: ", stdout); switch (sel) { case FOO: puts("FOO"); break; case BAR: puts("BAR"); break; case BAZ: puts("BAZ"); break; default: /* This is guaranteed not to happen. */ puts("an unknown value!"); } } else puts("You didn't select anything."); return 0; }
Here is the output of the above example, to show exactly how enumerated arguments are parsed:
$ ./option-enum --selection=foo -| You selected: FOO $ ./option-enum --selection=alias-foo -| You selected: FOO $ ./option-enum --selection=alias-bar -| You selected: BAR $ ./option-enum --selection=bAr -| You selected: BAR $ ./option-enum --selection=Ba error→ ./option-enum: 'Ba': argument for '--selection' is ambiguous; possibilities: error→ bar error→ baz $ ./option-enum --selection=qux error→ ./option-enum: 'qux': invalid argument for '--selection'; must be one of 'foo', 'bar', 'alias-bar', or 'baz' $ ./option-enum --case-selection=FoO error→ ./option-enum: 'FoO': invalid argument for '--case-selection'; must be one of 'foo', 'bar', 'alias-bar', or 'baz' $ ./option-enum --case-selection=foo -| You selected: FOO