@need 3200

@node Parsing Command-line Options
@chapter Parsing Command-line Options

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 @emph{consistent} command line interfaces, rather than 
@emph{arbitrary} command line interfaces.  Consequently, these functions
aren't especially useful for implementing some of the standard Posix 
command line syntaxes.  

@menu
* Option Tables::
* Parsing Options::
* Printing Help Messages::
* Shifting Arguments::
* An Example of Option Parsing ::
@end menu

@need 3200

@node Option Tables
@section Option Tables
@sp 1
@need 1750
@noindent
@b{* Type} @code{opt_desc}@tyindex opt_desc
@*
@example
@group
struct opt_desc
@{
  int opt_value;
  t_uchar * char_name;
  t_uchar * long_name;
  int requires_arg;
  t_uchar * desc;
@};
@end group
@group
@end group
@end example
@sp 1
@need 1750
@noindent
@b{* Type} @code{opt_parsed}@tyindex opt_parsed
@*
@example
@group
struct opt_parsed
@{
  int opt_value;
  t_uchar * opt_string;
  t_uchar * arg_string;
  struct opt_desc * desc;
@};
@end group
@group
@end group
@end example

An array of @code{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 @code{OPTS}
of two arguments: @code{OP}, which is used to define a program option
and @code{OP2}, which is used to add additional lines of documentation
to an option.


Both @code{OP} and @code{OP2} are used as macros which accept @code{5} arguments:

@example
@group
     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
@end group
@group
@end group
@end example

Once you have defined @code{OPTS} it is easy to create a table of
@code{struct opt_desc}, as in the following example:

@example
@group

@end group
@group
#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 */ \
@end group
@group
      "Display a help message and exit.") /*  help message */ \
  \
  OP (opt_version, "V", "version", 0, \
           "Display a release identifier string and exit.") \
  \
@end group
@group
  /* The next option illustrates how to handle a multi-line */ \
  /* help message: */ \
  \
@end group
@group
  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 (...) ... 
@end group
@group

@end group
@group
/* 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.
 */
@end group
@group

@end group
@group
enum options
@{
  OPTS (OPT_ENUM, OPT_IGN)  
@};
@end group
@group

@end group
@group
struct opt_desc opts[] = 
@{
  OPTS (OPT_DESC, OPT_DESC)
    @{-1, 0, 0, 0, 0@}
@};
@end group
@group
@end group
@end example



@need 3200

@node Parsing Options
@section Parsing Options
@sp 1
@need 1750
@noindent
@b{* Function} @code{opt_low}@fuindex opt_low
@*
@example
@group
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);
@end group
@group
@end group
@end example

Return the next command line option and modify argc/argv to 
remove that option.


@code{opts} should point to an array of @code{struct opt_desc} whose final
element contains a value less than @code{0} in the field @code{opt_value}.


Ordinarily @code{opt} returns the @code{opt_value} field from the element of
@code{opts} that matches the next argument.  If the field @code{requires_arg}
is not @code{0}, then the option argument is also returned.  The option
and its argument are removed from argc/argv.  @code{opt_string} returns
a string naming the option (@geindex -x
@dfn{-x}, @geindex --long-opt
@dfn{--long-opt} or
@geindex --long-opt=value
@dfn{--long-opt=value}).  The string returned in @code{opt_string} may be
overwritten by later calls to @code{opt}.  @code{opt_arg} returns
the argument to the option, if the argument requires an argument
and one was provided.  A string returned in @code{opt_arg} points to
a string or substring from @code{argv}.  @code{desc} returns the option
description for the option returned.


Any of @code{opt_string}, @code{opt_arg}, and @code{desc} may be @code{0}.


@code{opt_string_space} should point to an array of three characters.
If the next option is a short option, the value stored in @code{*opt_string}
will point to @code{opt_string_space}.  @code{opt_string_space} may be @code{0}
if @code{opt_string} is @code{0}.


If the option requires an argument but none is provided, @code{*opt_arg}
is set to @code{0}, and the @code{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 @code{opt}, and the value of @code{*opt_arg}.  This
behavior is to facilitate providing a specific error message to the
user -- to quote POSIX: @geindex option arguments may not be optional
@dfn{option arguments may not be optional}.


If the next argument is not an ordinary option, a value less than @code{0}
is returned.  These special values are given symbolic names in
@code{opt.h} (@code{enum opt_return_values}):

@example
@group
             opt_dash        -- the next argument is simply "-".
             opt_double_dash -- the next argument is simply "--".
@end group
@group
@end group
@end example

In either case, the argument (@geindex -
@dfn{-} or @geindex --
@dfn{--}) is removed from
argc/argv.

@example
@group
             opt_unknown     -- the next argument does not match 
                                any known option. 
@end group
@group
@end group
@end example
@example
@group
             opt_bogus_argument -- a recognized long option was 
                                   provided with an option argument, 
                                   but the option does not accept 
                                   an argument.
@end group
@group
@end group
@end example
@example
@group
             opt_none        -- the next argument does not begin
                                with "-".
@end group
@group
@end group
@end example

In any of those cases, argc/argv is not modified.

@sp 1
@need 1750
@noindent
@b{* Function} @code{opt_standard}@fuindex opt_standard
@*
@example
@group
int opt_standard (alloc_limits limits,
                  struct opt_parsed ** parsed_p,
                  struct opt_desc * opts,
                  int * argcp,
                  char * argvp[],
@end group
@group
                  t_uchar * program_name,
                  t_uchar * usage,
@end group
@group
                  t_uchar * version_string,
                  t_uchar * long_help,
                  int help_option,
                  int long_help_option,
                  int version_option);
@end group
@group
@end group
@end example

This function calls @code{opt}, but handles some commonly
implemented options internally, in a standard way.


The parameters @code{limits}, @code{parsed_p}, @code{opts}, @code{argcp},
and @code{argvp} are as to @code{opt_low}.


The parameters @code{program_name}, @code{usage} and @code{version_string} are
used in error and informational messages printed by this function.


The parameters @code{help_option}, @code{long_help_option} and @code{version_option} 
are the @code{name} value for some standard options, which are
usually given the command line names:

@example
@group
     -h      --help          help_option
     -H                      long_help_option
     -V      --version       version_option
@end group
@group
@end group
@end example

If this function detects the @code{help_option}, it prints a 
multi-line message summarizing the available options
to standard output and exits the process with a @code{0} status.


If this function detects the @code{long_help_option}, it prints a 
multi-line message summarizing the available options
to standard output, adds text from @code{long_help} and exits the 
process with a @code{0} status.  The first line of @code{long_help} should
summarize the command (conventionally uncapitalized and without
punctuation).  The rest of @code{long_help} should expand upon the
explanation.  Long help messages are formatted as:

@example
@group
     first line of long_help
     usage: program_name ....
     option list
@end group
@group
@end group
@end example
@example
@group
     rest of long_help
@end group
@group
@end group
@end example

If this function detects the @code{version_option}, it prints a 
single-line message containing @code{version_string}
to standard output and exits the process with a @code{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.




@need 3200

@node Printing Help Messages
@section Printing Help Messages
@sp 1
@need 1750
@noindent
@b{* Function} @code{opt_usage}@fuindex opt_usage
@*
@example
@group
void opt_usage (int fd,
                char * argv0,
                t_uchar * program_name,
                t_uchar * usage,
                int suggest_help);
@end group
@group
@end group
@end example

Print a usage message on @code{fd} using the format:

@example
@group
     "usage: %s %s\n", program_name, usage
@end group
@group
@end group
@end example

If @code{suggest_help} is not @code{0}, also print:

@example
@group
     "try %s --help\n", argv0
@end group
@group
@end group
@end example

By convention, @code{argv0} should be the first command line string
passed to the program (the name by which the program was invoked).


@code{program_name} should be the default name for the program (not
necessarily the name by which it was invoked).

@sp 1
@need 1750
@noindent
@b{* Function} @code{opt_help}@fuindex opt_help
@*
@example
@group
void opt_help (int fd, struct opt_desc * opts);
@end group
@group
@end group
@end example

Print a help message based on @code{opts}.


A long option name in @code{opts} can have the form @geindex --foo=BAR
@dfn{--foo=BAR}.
The text @geindex =BAR
@dfn{=BAR} is included in the help message.




@need 3200

@node Shifting Arguments
@section Shifting Arguments
@sp 1
@need 1750
@noindent
@b{* Function} @code{opt_shift_char_option}@fuindex opt_shift_char_option
@*
@example
@group
void opt_shift_char_option (int * argcp, char ** argv);
@end group
@group
@end group
@end example

Remove a single character argument from argc/argv.  For example,


Before:

@example
@group
     *argcp = 3
     argv = @{"prog", "-abc", "-def", 0@}
@end group
@group
@end group
@end example

After:

@example
@group
     *argcp = 3
     argv = @{"prog", "-bc", "-def", 0@}
@end group
@group
@end group
@end example

Before:

@example
@group
     *argcp = 3
     argv = @{"prog", "-a", "-def", 0@}
@end group
@group
@end group
@end example

After:

@example
@group
     *argcp = 2
     argv = @{"prog", "-def", 0@}
@end group
@group
@end group
@end example
@sp 1
@need 1750
@noindent
@b{* Function} @code{opt_shift}@fuindex opt_shift
@*
@example
@group
void opt_shift (int * argcp, char ** argv);
@end group
@group
@end group
@end example

Remove a single argument from argc/argv.  For example,


Before:

@example
@group
     *argcp = 3
     argv = @{"prog", "--abc", "-def", 0@}
@end group
@group
@end group
@end example

After:

@example
@group
     *argcp = 2
     argv = @{"prog", "-def", 0@}
@end group
@group
@end group
@end example



@need 3200

@node An Example of Option Parsing 
@section An Example of Option Parsing 

The following program illustrates option parsing with @code{opt}.
These examples illustrates its usage:

@example
@group
     % test --version
     test 1.0.0
@end group
@group
@end group
@end example
@example
@group
     % test --help
     usage: ./,test [options] [input-file]
@end group
@group
@end group
@end example
@example
@group
       -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.
@end group
@group
@end group
@end example
@example
@group
     % ./,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'
@end group
@group
@end group
@end example
@example
@group
     % ./,test -xyzzy
     unhandled option `-xyzzy'
     usage: ./,test [options] [input-file]
     try ./,test --help
@end group
@group
@end group
@end example

Here's the code:

@example
@group

@end group
@group
#include "hackerlab/cmd/main.h"
@end group
@group

@end group
@group

@end group
@group


@end group
@group
static t_uchar * program_name = "test";
static t_uchar * usage = "[options]";
static t_uchar * version_string = "1.0";
@end group
@group


@end group
@group
#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.")
@end group
@group

@end group
@group
enum options
@{
  OPTS (OPT_ENUM, OPT_IGN)  
@};
@end group
@group

@end group
@group
struct opt_desc opts[] = 
@{
  OPTS (OPT_DESC, OPT_DESC)
    @{-1, 0, 0, 0, 0@}
@};
@end group
@group

@end group
@group

@end group
@group

@end group
@group
int
main (int argc, char * argv[])
@{
  int errn;
  int o;
  struct opt_parsed * option;
@end group
@group

@end group
@group
  option = 0;
@end group
@group

@end group
@group
  while (1)
    @{
      o = opt_standard (lim_use_must_malloc,
                        &option,
                        opts,
@end group
@group
                        &argc, argv,
                        program_name,
                        usage,
                        version_string,
                        opt_help_msg,
@end group
@group
                        opt_version);
      if (o == opt_none)
        break;
      switch (o)
@end group
@group
        @{
        default:
          safe_printfmt (2, "unhandled option `%s'\n",
                         option->opt_string);
          panic ("internal error parsing arguments");
@end group
@group

@end group
@group
        usage_error:
          opt_usage (2, argv[0], program_name, usage, 1);
          panic_exit ();
@end group
@group

@end group
@group
        bogus_arg:
          safe_printfmt (2, "ill-formed argument for `%s' (`%s')\n",
                         option->opt_string,
                         option->arg_string);
          goto usage_error;
@end group
@group

@end group
@group
        case opt_output_file:
          if (!opt_arg)
            @{
              printfmt (&errn,
                        2,
@end group
@group
                        "missing argument for `%s'\n",
                        opt_string);
              goto usage_error;
            @}
          printfmt (&errn,
@end group
@group
                    2,
                    "output file argument: `%s'\n",
                    opt_arg);
@end group
@group
          break;
        @}
    @}
  return 0;
@}
@end group
@group
@end group
@end example






