regexps.com
The functions and macros in this chapter provide a convenient and standard way to parse command line arguments and provide on-line help for command-line invocation. An example at the end of the chapter illustrates their use.
It is important to understand that these functions are designed to support consistent command line interfaces, rather than arbitrary command line interfaces. Consequently, these functions aren't especially useful for implementing some of the standard Posix command line syntaxes.
struct opt_desc
{
  int opt_value;
  t_uchar * char_name;
  t_uchar * long_name;
  int requires_arg;
  t_uchar * desc;
};
struct opt_parsed
{
  int opt_value;
  t_uchar * opt_string;
  t_uchar * arg_string;
  struct opt_desc * desc;
};
An array of struct opt_desc
 is used to describe the options
accepted by a program.
The easiest way to create this array is by defining a macro OPTS
of two arguments: OP
, which is used to define a program option
and OP2
, which is used to add additional lines of documentation
to an option.
Both OP
 and OP2
 are used as macros which accept 5
 arguments:
     name            an enum name for the option
     char_name       0 or a string beginning with a one-character 
                     name for the option.  
     long_name       0 or a string beginning with a long name for 
                     the option
     arg             1 if the option requires an argument, 0 
                     otherwise
     desc            A documentation string for the option
Once you have defined OPTS
 it is easy to create a table of
struct opt_desc
, as in the following example:
#define OPTS(OP, OP2) \
  OP (opt_help_msg,     /* An enum name for the option */ \
      "h",              /* A short name for the option */ \
      "help",           /* A long name for the option */ \
      0,                /* The option takes no arguments */ \
      "Display a help message and exit.") /*  help message */ \
  \
  OP (opt_version, "V", "version", 0, \
           "Display a release identifier string and exit.") \
  \
  /* The next option illustrates how to handle a multi-line */ \
  /* help message: */ \
  \
  OP (opt_output_file, "o file", "output-file=file", 1, \
           "Write output to FILE.") \
  OP2 (opt_output_file, 0, 0, 1, \
            "The file must not already exist.") \
  OP (...) ... 
/* Note that the short and long names for an option are optional
 * (may be 0) but if both are omitted, then there is no way to
 * specify the option on a command line.
 */
enum options
{
  OPTS (OPT_ENUM, OPT_IGN)  
};
struct opt_desc opts[] = 
{
  OPTS (OPT_DESC, OPT_DESC)
    {-1, 0, 0, 0, 0}
};
int opt_low (t_uchar * opt_string,
             t_uchar * opt_arg,
             struct opt_desc ** desc,
             struct opt_desc * opts,
             int * argcp,
             char ** argv,
             t_uchar * opt_string_space);
Return the next command line option and modify argc/argv to remove that option.
opts
 should point to an array of struct opt_desc
 whose final
element contains a value less than 0
 in the field opt_value
.
Ordinarily opt
 returns the opt_value
 field from the element of
opts
 that matches the next argument.  If the field requires_arg
is not 0
, then the option argument is also returned.  The option
and its argument are removed from argc/argv.  opt_string
 returns
a string naming the option (
-x
, 
--long-opt
 or
--long-opt=value
).  The string returned in opt_string
 may be
overwritten by later calls to opt
.  opt_arg
 returns
the argument to the option, if the argument requires an argument
and one was provided.  A string returned in opt_arg
 points to
a string or substring from argv
.  desc
 returns the option
description for the option returned.
Any of opt_string
, opt_arg
, and desc
 may be 0
.
opt_string_space
 should point to an array of three characters.
If the next option is a short option, the value stored in *opt_string
will point to opt_string_space
.  opt_string_space
 may be 0
if opt_string
 is 0
.
If the option requires an argument but none is provided, *opt_arg
is set to 0
, and the opt_value
 field of the option is returned;
argc/argv is not modified.  To see that an option with required
argument has been correctly provided, it is necessary to check both
the return value of opt
, and the value of *opt_arg
.  This
behavior is to facilitate providing a specific error message to the
user -- to quote POSIX: 
option arguments may not be optional
.
If the next argument is not an ordinary option, a value less than 0
is returned.  These special values are given symbolic names in
opt.h
 (enum opt_return_values
):
             opt_dash        -- the next argument is simply "-".
             opt_double_dash -- the next argument is simply "--".
In either case, the argument ( - or -- ) is removed from argc/argv.
             opt_unknown     -- the next argument does not match 
                                any known option. 
             opt_bogus_argument -- a recognized long option was 
                                   provided with an option argument, 
                                   but the option does not accept 
                                   an argument.
             opt_none        -- the next argument does not begin
                                with "-".
In any of those cases, argc/argv is not modified.
int opt_standard (alloc_limits limits,
                  struct opt_parsed ** parsed_p,
                  struct opt_desc * opts,
                  int * argcp,
                  char * argvp[],
                  t_uchar * program_name,
                  t_uchar * usage,
                  t_uchar * version_string,
                  t_uchar * long_help,
                  int help_option,
                  int long_help_option,
                  int version_option);
This function calls opt
, but handles some commonly
implemented options internally, in a standard way.
The parameters limits
, parsed_p
, opts
, argcp
,
and argvp
 are as to opt_low
.
The parameters program_name
, usage
 and version_string
 are
used in error and informational messages printed by this function.
The parameters help_option
, long_help_option
 and version_option
 
are the name
 value for some standard options, which are
usually given the command line names:
     -h      --help          help_option
     -H                      long_help_option
     -V      --version       version_option
If this function detects the help_option
, it prints a 
multi-line message summarizing the available options
to standard output and exits the process with a 0
 status.
If this function detects the long_help_option
, it prints a 
multi-line message summarizing the available options
to standard output, adds text from long_help
 and exits the 
process with a 0
 status.  The first line of long_help
 should
summarize the command (conventionally uncapitalized and without
punctuation).  The rest of long_help
 should expand upon the
explanation.  Long help messages are formatted as:
     first line of long_help
     usage: program_name ....
     option list
     rest of long_help
If this function detects the version_option
, it prints a 
single-line message containing version_string
to standard output and exits the process with a 0
 status.
If this function detects an unrecognized option, a missing option argument, or an argument provided for an option that doesn't accept arguments, it prints a usage message to standard error and exits the process with a non-0 status.
void opt_usage (int fd,
                char * argv0,
                t_uchar * program_name,
                t_uchar * usage,
                int suggest_help);
Print a usage message on fd
 using the format:
     "usage: %s %s\n", program_name, usage
If suggest_help
 is not 0
, also print:
     "try %s --help\n", argv0
By convention, argv0
 should be the first command line string
passed to the program (the name by which the program was invoked).
program_name
 should be the default name for the program (not
necessarily the name by which it was invoked).
void opt_help (int fd, struct opt_desc * opts);
Print a help message based on opts
.
A long option name in opts
 can have the form 
--foo=BAR
.
The text 
=BAR
 is included in the help message.
Function 
opt_shift_char_option
void opt_shift_char_option (int * argcp, char ** argv);
Remove a single character argument from argc/argv. For example,
Before:
     *argcp = 3
     argv = {"prog", "-abc", "-def", 0}
After:
     *argcp = 3
     argv = {"prog", "-bc", "-def", 0}
Before:
     *argcp = 3
     argv = {"prog", "-a", "-def", 0}
After:
     *argcp = 2
     argv = {"prog", "-def", 0}
void opt_shift (int * argcp, char ** argv);
Remove a single argument from argc/argv. For example,
Before:
     *argcp = 3
     argv = {"prog", "--abc", "-def", 0}
After:
     *argcp = 2
     argv = {"prog", "-def", 0}
The following program illustrates option parsing with opt
.
These examples illustrates its usage:
     % test --version
     test 1.0.0
     % test --help
     usage: ./,test [options] [input-file]
       -h, --help                   Display a help message and exit.
       -V, --version                Display a release identifier string
                                    and exit.
       -o file, --output-file=file  Write output to FILE.
     % ./,test -o one --output-file two -othree --output-file=four
     output file argument: `one'
     output file argument: `two'
     output file argument: `three'
     output file argument: `four'
     % ./,test -xyzzy
     unhandled option `-xyzzy'
     usage: ./,test [options] [input-file]
     try ./,test --help
Here's the code:
#include "hackerlab/cmd/main.h"
static t_uchar * program_name = "test";
static t_uchar * usage = "[options]";
static t_uchar * version_string = "1.0";
#define OPTS(OP, OP2) \
  OP (opt_help_msg, "h", "help", 0, \
      "Display a help message and exit.") \
  OP (opt_version, "V", "version", 0, \
      "Display a release identifier string") \
  OP2 (opt_version, 0, 0, 0, "and exit.") \
  OP (opt_output_file, "o file", "output-file=file", 1, \
      "Write output to FILE.")
enum options
{
  OPTS (OPT_ENUM, OPT_IGN)  
};
struct opt_desc opts[] = 
{
  OPTS (OPT_DESC, OPT_DESC)
    {-1, 0, 0, 0, 0}
};
int
main (int argc, char * argv[])
{
  int errn;
  int o;
  struct opt_parsed * option;
  option = 0;
  while (1)
    {
      o = opt_standard (lim_use_must_malloc,
                        &option,
                        opts,
                        &argc, argv,
                        program_name,
                        usage,
                        version_string,
                        opt_help_msg,
                        opt_version);
      if (o == opt_none)
        break;
      switch (o)
        {
        default:
          safe_printfmt (2, "unhandled option `%s'\n",
                         option->opt_string);
          panic ("internal error parsing arguments");
        usage_error:
          opt_usage (2, argv[0], program_name, usage, 1);
          panic_exit ();
        bogus_arg:
          safe_printfmt (2, "ill-formed argument for `%s' (`%s')\n",
                         option->opt_string,
                         option->arg_string);
          goto usage_error;
        case opt_output_file:
          if (!opt_arg)
            {
              printfmt (&errn,
                        2,
                        "missing argument for `%s'\n",
                        opt_string);
              goto usage_error;
            }
          printfmt (&errn,
                    2,
                    "output file argument: `%s'\n",
                    opt_arg);
          break;
        }
    }
  return 0;
}
libhackerlab: The Hackerlab C Library
regexps.com