GNU libavl 2.0.1
Author: Ben Pfaff

B.1 Option Parser |

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:

    586. <Option parser 586> =
    /* 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 587 and 588.
This code is included in 97.

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

    587. <Option parser 586> +=
    /* 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:

    588. <Option parser 586> +=
    /* 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 ("non-option 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);
        }
    }

Appendix B Supplementary Code | Appendix B Supplementary Code | B.2 Command-Line Parser