2.3 Negatable Options

A negatable option is an option which can be specified later on the command line in a different form, to negate the effect of a previous specification. Short options are negated using ‘+’ rather than ‘-’ to specify the option. Long options and suboptions must be prefixed with ‘no-’ or the specified negation prefixes (see Negation Prefixes). For example,

$ prog --foo --no-foo

should act as though --foo were never specified. Only negatable options can be negated. For an option to be negatable, its negatable field must be set to a nonzero value (see Option Structure). Options that take arguments (i.e., options for which the has_arg field is not MU_OPT_NONE) may not be negated. Indeed, setting the negatable field to any value for an option which takes an argument results in undefined behavior.

Environment variables may not be negated. The reason for this is because it is not easy to control the order in which environment variables appear. Thus, if environment variable negation were allowed and FOO were a negatable environment variable,

$ FOO= NO_FOO= prog

may or may not act as though FOO were specified. So if an option for which the negatable field is nonzero also has a non-NULL env_var field, NO_FOO will be ignored. Note, however, that the callback_negatable callback should still be used (but it may be better not to use callbacks at all; see below). Rather than having an env_var field for a negatable option, it is instead better to make a separate environment variable that has a boolean value (see Option Argument Types).

Since environment variables may not be negated, specifying the negatable field for an environment variable which has no equivalent options is useless. Because of this, it is not allowed and will be diagnosed.

When option parsing is finished, the value that the found_opt field points to (if any) will be nonzero if the last instance of the option found on the command line was not negated, or zero if it was negated.

Negatable options should use the callback_negatable field if they are using a callback (see Option Callbacks), although it is usually preferable not to use a callback. Suppose you have a certain negatable option, and you want to, say, open a file when it is found. If you used a callback, you would need to open the file whenever value was nonzero, and then close it again when it is zero. Although in this case this would be fairly easy to implement (although far from ideal), it is still much better to simply wait until option parsing is finished, and then check the value that the found_opt field points to.

Here is an example illustrating how to parse negatable options:

#include <stdio.h>
#include <mu/options.h>
#include <mu/safe.h>            /* For mu_opt_context_x{new,free} */

/* Print a message when we find the negatable option. Usually, we
   shouldn't use a callback for negatable options, but we are just
   using it to print a message. */
static int print_negatable(int value, void *data, char *err) {
  printf("    Found the negatable option, and it was%s negated.\n",
         value ? " not" : "");
  return 0;
}

int main(int argc, char **argv) {
  int found_negatable;
  int ret;
  const MU_OPT options[] = {
    {
     .short_opt          = "n",
     .long_opt           = "negatable",
     .has_arg            = MU_OPT_NONE,
     .negatable          = 1,
     .found_opt          = &found_negatable,
     .callback_negatable = print_negatable
    },
    { 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;

  printf("It appears that the negatable option was%s given.\n",
         found_negatable ? "" : " not");

  return 0;
}

Here is the output of the above program:

$ ./option-negatable
-| It appears that the negatable option was not given.
$ ./option-negatable --negatable -n --no-negatable +n
-|     Found the negatable option, and it was not negated.
-|     Found the negatable option, and it was not negated.
-|     Found the negatable option, and it was negated.
-|     Found the negatable option, and it was negated.
-| It appears that the negatable option was not given.
$ ./option-negatable -n +n --negatable
-|     Found the negatable option, and it was not negated.
-|     Found the negatable option, and it was negated.
-|     Found the negatable option, and it was not negated.
-| It appears that the negatable option was given.