B.1 Option Parser [ToC] [Index]     [Skip Fwd]     [Prev] [Up] [Next]

The BST test program contains an option parser for handling command-line options. See User Interaction, for an introduction to its public interface. This section describes the option parser's implementation.

The option parsing state is kept in struct option_state:

588. <Option parser 588> =
/* Option parsing state. */
struct option_state 
  { const struct option *options; /* List of options. */ char **arg_next; /* Remaining arguments. */ char *short_next; /* When non-null, unparsed short options. */ };

See also 589 and 590.

This code is included in 98.

The initialization function just creates and returns one of these structures:

589. <Option parser 588> +=
/* Creates and returns a command-line options parser.  
   args is a null-terminated array of command-line arguments, not
   including program name. */
static struct option_state *
option_init (const struct option *options, char **args)
{ struct option_state *state; assert (options != NULL && args != NULL); state = xmalloc (sizeof *state); state->options = options; state->arg_next = args; state->short_next = NULL; return state; }

The option retrieval function uses a couple of helper functions. The code is lengthy, but not complicated:

590. <Option parser 588> +=
/* Parses a short option whose single-character name is pointed to by
   state->short_next.  Advances past the option so that the next one
   will be parsed in the next call to option_get().  Sets *argp to
   the option's argument, if any.  Returns the option's short name. */
static int 
handle_short_option (struct option_state *state, char **argp)
{ const struct option *o; assert (state != NULL
          && state->short_next != NULL && *state->short_next != '\0' && state->options != NULL); /* Find option in o. */ for (o = state->options; ; o++) if (o->long_name == NULL) fail ("unknown option `%c'; use -help for help", *state->short_next); else if (o->short_name == *state->short_next) break; state->short_next++; /* Handle argument. */ if (o->has_arg)
    { if (*state->arg_next == NULL || **state->arg_next == ) fail ("`%c' requires an argument; use -help for help"); *argp = *state->arg_next++; } return o->short_name; } /* Parses a long option whose command-line argument is pointed to by *state->arg_next. Advances past the option so that the next one will be parsed in the next call to option_get(). Sets *argp to the option's argument, if any. Returns the option's identifier. */ static int
handle_long_option (struct option_state *state, char **argp)
{ const struct option *o; /* Iterator on options. */ char name[16]; /* Option name. */ const char *arg; /* Option argument. */ assert (state != NULL
          && state->arg_next != NULL && *state->arg_next != NULL && state->options != NULL
          && argp != NULL); /* Copy the option name into name and put a pointer to its argument, or NULL if none, into arg. */ { const char *p = *state->arg_next + 2; const char *q = p + strcspn (p, "="); size_t name_len = q - p; if (name_len > (sizeof name) - 1) name_len = (sizeof name) - 1; memcpy (name, p, name_len); name[name_len] = '\0'; arg = (*q == '=') ? q + 1 : NULL; } /* Find option in o. */ for (o = state->options; ; o++) if (o->long_name == NULL) fail ("unknown option -%s; use -help for help", name); else if (!strcmp (name, o->long_name)) break; /* Make sure option has an argument if it should. */ if ((arg != NULL) != (o->has_arg != 0))
    { if (arg != NULL) fail ("-%s can't take an argument; use -help for help", name); else
        fail ("-%s requires an argument; use -help for help", name); } /* Advance and return. */ state->arg_next++; *argp = (char *) arg; return o->short_name; } /* Retrieves the next option in the state vector state. Returns the option's identifier, or -1 if out of options. Stores the option's argument, or NULL if none, into *argp. */ static int
option_get (struct option_state *state, char **argp)
{ assert (state != NULL && argp != NULL); /* No argument by default. */ *argp = NULL; /* Deal with left-over short options. */ if (state->short_next != NULL)
    { if (*state->short_next != '\0') return handle_short_option (state, argp); else
        state->short_next = NULL; } /* Out of options? */ if (*state->arg_next == NULL)
    { free (state); return -1; } /* Non-option arguments not supported. */ if ((*state->arg_next)[0] != ) fail ("nonoption arguments encountered; use -help for help"); if ((*state->arg_next)[1] == '\0') fail ("unknown option `'; use -help for help"); /* Handle the option. */ if ((*state->arg_next)[1] == ) return handle_long_option (state, argp); else
    { state->short_next = *state->arg_next + 1; state->arg_next++; return handle_short_option (state, argp); } }