The Hackerlab at regexps.com

Parsing Command-line Options

up: libhackerlab
prev: File-name Manipulation Functions

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.


Option Tables

up: Parsing Command-line Options
next: Parsing Options

Type opt_desc

struct opt_desc
{
  int opt_value;
  t_uchar * char_name;
  t_uchar * long_name;
  int requires_arg;
  t_uchar * desc;
};



Type opt_parsed

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}
};


Parsing Options

up: Parsing Command-line Options
next: Printing Help Messages
prev: Option Tables

Function opt_low

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.



Function opt_standard

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.




Printing Help Messages

up: Parsing Command-line Options
next: Shifting Arguments
prev: Parsing Options

Function opt_usage

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).



Function opt_help

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.




Shifting Arguments

up: Parsing Command-line Options
next: An Example of Option Parsing
prev: Printing Help Messages

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}



Function opt_shift

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}




An Example of Option Parsing

up: Parsing Command-line Options
prev: Shifting Arguments

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
The Hackerlab at regexps.com