A magic getopt

getopt

switch

case

int ch; while ((ch = getopt(argc, argv, ":af:")) != -1) { switch (ch) { case 'a': aflag = 1; break; case 'f': printf("foo: %s

", optarg); break; case ':': printf("missing argument to -%c

", optopt); /* FALLTHROUGH */ default: usage(); } }

--bar

getopt_long

enum options { OPTION_BAR }; ... static struct option longopts[] = { { "bar", required_argument, NULL, OPTION_BAR } }; ... int ch; while ((ch = getopt_long(argc, argv, ":af:", longopts, NULL)) != -1) { switch (ch) { case 'a': aflag = 1; break; case OPTION_BAR: printf("bar: %s

", optarg); break; case 'f': printf("foo: %s

", optarg); break; case ':': printf("missing argument to -%c

", optopt); /* FALLTHROUGH */ default: usage(); } }

Parsing command lines in C is easy when all of the options are single characters: You pass your command line toalong with a string containing all the valid options; then you have astatement with afor each option you want to handle. It looks something like this:Unfortunately if you want to add support for long options — say, to accept a newoption — you need to switch to usingand your list of options is no longer confined to the options-processing loop:Rather than adding a new option in one place (or two, if you count the list of options at the top of the loop as being a separate place), new long options require changes in three places — one of which (the enum) is often placed in an entirely separate file. So much for keeping code clean and free of duplication. There has got to be a better way, right?

Enter magic getopt. Via a little bit of macro magic, the above options-handling code turns into this:

const char * ch; while ((ch = GETOPT(argc, argv)) != NULL) { GETOPT_SWITCH(ch) { GETOPT_OPT("-a"): aflag = 1; break; GETOPT_OPTARG("--bar"): printf("bar: %s

", optarg); break; GETOPT_OPTARG("-f"): printf("foo: %s

", optarg); break; GETOPT_MISSING_ARG: printf("missing argument to %s

", ch); /* FALLTHROUGH */ GETOPT_DEFAULT: usage(); } }

with each option listed just once, at the point where it is handled.

To use this, add getopt.c and getopt.h to your project, #include "getopt.h" , and then make the following changes:

The option character ( ch in the examples above) turns into an option string .

in the examples above) turns into an option . The function getopt is replaced by the macro GETOPT , which no longer needs the third parameter (the string containing the option characters to accept) and returns NULL upon reaching the end of the options list instead of -1 .

is replaced by the macro , which no longer needs the third parameter (the string containing the option characters to accept) and returns upon reaching the end of the options list instead of . Instead of switch (ch) you now need GETOPT_SWITCH(ch) .

you now need . case 'a' turns into GETOPT_OPT("-a") for options without arguments, or GETOPT_OPTARG("-a") for options with arguments.

turns into for options without arguments, or for options with arguments. case ':' turns into GETOPT_MISSING_ARG .

turns into . default turns into GETOPT_DEFAULT , and this must be present at the end of the switch statement in order for the magic to work.

turns into , and in order for the magic to work. In the unlikely scenario that you had several case labels on the same line: Every GETOPT_* label needs to be on a different source code line.

And that's it. These changes can be made to a program which accepts single-character options almost mechanically and with no increase in the source code complexity; and then new options (short or long) can be added by simply adding the new option-handling code, without needing to make changes in several different places.

I think it's time for me to start adding long options to some of my projects.

Disqus