/* Test of command line argument processing.
   Copyright (C) 2009-2022 Free Software Foundation, Inc.

   This program is free software: you can redistribute it and/or modify
   it under the terms of the GNU General Public License as published by
   the Free Software Foundation, either version 3 of the License, or
   (at your option) any later version.

   This program is distributed in the hope that it will be useful,
   but WITHOUT ANY WARRANTY; without even the implied warranty of
   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
   GNU General Public License for more details.

   You should have received a copy of the GNU General Public License
   along with this program.  If not, see <https://www.gnu.org/licenses/>.  */

/* Written by Bruno Haible <bruno@clisp.org>, 2009.  */

static int a_seen;
static int b_seen;
static int q_seen;

static const struct option long_options_required[] =
  {
    { "alpha",    no_argument,       NULL, 'a' },
    { "beta",     no_argument,       &b_seen, 1 },
    { "prune",    required_argument, NULL, 'p' },
    { "quetsche", required_argument, &q_seen, 1 },
    { "xtremely-",no_argument,       NULL, 1003 },
    { "xtra",     no_argument,       NULL, 1001 },
    { "xtreme",   no_argument,       NULL, 1002 },
    { "xtremely", no_argument,       NULL, 1003 },
    { NULL,       0,                 NULL, 0 }
  };

static const struct option long_options_optional[] =
  {
    { "alpha",    no_argument,       NULL, 'a' },
    { "beta",     no_argument,       &b_seen, 1 },
    { "prune",    optional_argument, NULL, 'p' },
    { "quetsche", optional_argument, &q_seen, 1 },
    { NULL,       0,                 NULL, 0 }
  };

static void
getopt_long_loop (int argc, const char **argv,
                  const char *options, const struct option *long_options,
                  const char **p_value, const char **q_value,
                  int *non_options_count, const char **non_options,
                  int *unrecognized)
{
  int option_index = -1;
  int c;

  opterr = 0;
  q_seen = 0;
  while ((c = getopt_long (argc, (char **) argv, options, long_options,
                           &option_index))
         != -1)
    {
      switch (c)
        {
        case 0:
          /* An option with a non-NULL flag pointer was processed.  */
          if (q_seen)
            *q_value = optarg;
          break;
        case 'a':
          a_seen++;
          break;
        case 'b':
          b_seen = 1;
          break;
        case 'p':
          *p_value = optarg;
          break;
        case 'q':
          *q_value = optarg;
          break;
        case '\1':
          /* Must only happen with option '-' at the beginning.  */
          ASSERT (options[0] == '-');
          non_options[(*non_options_count)++] = optarg;
          break;
        case ':':
          /* Must only happen with option ':' at the beginning.  */
          ASSERT (options[0] == ':'
                  || ((options[0] == '-' || options[0] == '+')
                      && options[1] == ':'));
          FALLTHROUGH;
        case '?':
          *unrecognized = optopt;
          break;
        default:
          *unrecognized = c;
          break;
        }
    }
}

/* Reduce casting, so we can use string literals elsewhere.
   getopt_long takes an array of char*, but luckily does not modify
   those elements, so we can pass const char*.  */
static int
do_getopt_long (int argc, const char **argv, const char *shortopts,
                const struct option *longopts, int *longind)
{
  return getopt_long (argc, (char **) argv, shortopts, longopts, longind);
}

static void
test_getopt_long (void)
{
  int start;

  /* Test disambiguation of options.  */
  {
    int argc = 0;
    const char *argv[10];
    int option_index;
    int c;

    argv[argc++] = "program";
    argv[argc++] = "--x";
    argv[argc] = NULL;
    optind = 1;
    opterr = 0;
    c = do_getopt_long (argc, argv, "ab", long_options_required, &option_index);
    ASSERT (c == '?');
    ASSERT (optopt == 0);
  }
  {
    int argc = 0;
    const char *argv[10];
    int option_index;
    int c;

    argv[argc++] = "program";
    argv[argc++] = "--xt";
    argv[argc] = NULL;
    optind = 1;
    opterr = 0;
    c = do_getopt_long (argc, argv, "ab", long_options_required, &option_index);
    ASSERT (c == '?');
    ASSERT (optopt == 0);
  }
  {
    int argc = 0;
    const char *argv[10];
    int option_index;
    int c;

    argv[argc++] = "program";
    argv[argc++] = "--xtr";
    argv[argc] = NULL;
    optind = 1;
    opterr = 0;
    c = do_getopt_long (argc, argv, "ab", long_options_required, &option_index);
    ASSERT (c == '?');
    ASSERT (optopt == 0);
  }
  {
    int argc = 0;
    const char *argv[10];
    int option_index;
    int c;

    argv[argc++] = "program";
    argv[argc++] = "--xtra";
    argv[argc] = NULL;
    optind = 1;
    opterr = 0;
    c = do_getopt_long (argc, argv, "ab", long_options_required, &option_index);
    ASSERT (c == 1001);
  }
  {
    int argc = 0;
    const char *argv[10];
    int option_index;
    int c;

    argv[argc++] = "program";
    argv[argc++] = "--xtre";
    argv[argc] = NULL;
    optind = 1;
    opterr = 0;
    c = do_getopt_long (argc, argv, "ab", long_options_required, &option_index);
    ASSERT (c == '?');
    ASSERT (optopt == 0);
  }
  {
    int argc = 0;
    const char *argv[10];
    int option_index;
    int c;

    argv[argc++] = "program";
    argv[argc++] = "--xtrem";
    argv[argc] = NULL;
    optind = 1;
    opterr = 0;
    c = do_getopt_long (argc, argv, "ab", long_options_required, &option_index);
    ASSERT (c == '?');
    ASSERT (optopt == 0);
  }
  {
    int argc = 0;
    const char *argv[10];
    int option_index;
    int c;

    argv[argc++] = "program";
    argv[argc++] = "--xtreme";
    argv[argc] = NULL;
    optind = 1;
    opterr = 0;
    c = do_getopt_long (argc, argv, "ab", long_options_required, &option_index);
    ASSERT (c == 1002);
  }
  {
    int argc = 0;
    const char *argv[10];
    int option_index;
    int c;

    argv[argc++] = "program";
    argv[argc++] = "--xtremel";
    argv[argc] = NULL;
    optind = 1;
    opterr = 0;
    c = do_getopt_long (argc, argv, "ab", long_options_required, &option_index);
    ASSERT (c == 1003);
  }
  {
    int argc = 0;
    const char *argv[10];
    int option_index;
    int c;

    argv[argc++] = "program";
    argv[argc++] = "--xtremely";
    argv[argc] = NULL;
    optind = 1;
    opterr = 0;
    c = do_getopt_long (argc, argv, "ab", long_options_required, &option_index);
    ASSERT (c == 1003);
  }

  /* Check that -W handles unknown options.  */
  {
    int argc = 0;
    const char *argv[10];
    int option_index;
    int c;

    argv[argc++] = "program";
    argv[argc++] = "-W";
    argv[argc] = NULL;
    optind = 1;
    opterr = 0;
    c = do_getopt_long (argc, argv, "W;", long_options_required, &option_index);
    ASSERT (c == '?');
    ASSERT (optopt == 'W');
  }
  {
    int argc = 0;
    const char *argv[10];
    int option_index;
    int c;

    argv[argc++] = "program";
    argv[argc++] = "-Wunknown";
    argv[argc] = NULL;
    optind = 1;
    opterr = 0;
    c = do_getopt_long (argc, argv, "W;", long_options_required, &option_index);
    /* glibc and BSD behave differently here, but for now, we allow
       both behaviors since W support is not frequently used.  */
    if (c == '?')
      {
        ASSERT (optopt == 0);
        ASSERT (optarg == NULL);
      }
    else
      {
        ASSERT (c == 'W');
        ASSERT (strcmp (optarg, "unknown") == 0);
      }
  }
  {
    int argc = 0;
    const char *argv[10];
    int option_index;
    int c;

    argv[argc++] = "program";
    argv[argc++] = "-W";
    argv[argc++] = "unknown";
    argv[argc] = NULL;
    optind = 1;
    opterr = 0;
    c = do_getopt_long (argc, argv, "W;", long_options_required, &option_index);
    /* glibc and BSD behave differently here, but for now, we allow
       both behaviors since W support is not frequently used.  */
    if (c == '?')
      {
        ASSERT (optopt == 0);
        ASSERT (optarg == NULL);
      }
    else
      {
        ASSERT (c == 'W');
        ASSERT (strcmp (optarg, "unknown") == 0);
      }
  }

  /* Test that 'W' does not dump core:
     https://sourceware.org/bugzilla/show_bug.cgi?id=12922  */
  {
    int argc = 0;
    const char *argv[10];
    int option_index;
    int c;

    argv[argc++] = "program";
    argv[argc++] = "-W";
    argv[argc++] = "dummy";
    argv[argc] = NULL;
    optind = 1;
    opterr = 0;
    c = do_getopt_long (argc, argv, "W;", NULL, &option_index);
    ASSERT (c == 'W');
    ASSERT (optind == 2);
  }

  /* Test processing of boolean short options.  */
  for (start = 0; start <= 1; start++)
    {
      const char *p_value = NULL;
      const char *q_value = NULL;
      int non_options_count = 0;
      const char *non_options[10];
      int unrecognized = 0;
      int argc = 0;
      const char *argv[10];
      a_seen = 0;
      b_seen = 0;

      argv[argc++] = "program";
      argv[argc++] = "-a";
      argv[argc++] = "foo";
      argv[argc++] = "bar";
      argv[argc] = NULL;
      optind = start;
      getopt_long_loop (argc, argv, "ab", long_options_required,
                        &p_value, &q_value,
                        &non_options_count, non_options, &unrecognized);
      ASSERT (a_seen == 1);
      ASSERT (b_seen == 0);
      ASSERT (p_value == NULL);
      ASSERT (q_value == NULL);
      ASSERT (non_options_count == 0);
      ASSERT (unrecognized == 0);
      ASSERT (optind == 2);
    }
  for (start = 0; start <= 1; start++)
    {
      const char *p_value = NULL;
      const char *q_value = NULL;
      int non_options_count = 0;
      const char *non_options[10];
      int unrecognized = 0;
      int argc = 0;
      const char *argv[10];
      a_seen = 0;
      b_seen = 0;

      argv[argc++] = "program";
      argv[argc++] = "-b";
      argv[argc++] = "-a";
      argv[argc++] = "foo";
      argv[argc++] = "bar";
      argv[argc] = NULL;
      optind = start;
      getopt_long_loop (argc, argv, "ab", long_options_required,
                        &p_value, &q_value,
                        &non_options_count, non_options, &unrecognized);
      ASSERT (a_seen == 1);
      ASSERT (b_seen == 1);
      ASSERT (p_value == NULL);
      ASSERT (q_value == NULL);
      ASSERT (non_options_count == 0);
      ASSERT (unrecognized == 0);
      ASSERT (optind == 3);
    }
  for (start = 0; start <= 1; start++)
    {
      const char *p_value = NULL;
      const char *q_value = NULL;
      int non_options_count = 0;
      const char *non_options[10];
      int unrecognized = 0;
      int argc = 0;
      const char *argv[10];
      a_seen = 0;
      b_seen = 0;

      argv[argc++] = "program";
      argv[argc++] = "-ba";
      argv[argc++] = "foo";
      argv[argc++] = "bar";
      argv[argc] = NULL;
      optind = start;
      getopt_long_loop (argc, argv, "ab", long_options_required,
                        &p_value, &q_value,
                        &non_options_count, non_options, &unrecognized);
      ASSERT (a_seen == 1);
      ASSERT (b_seen == 1);
      ASSERT (p_value == NULL);
      ASSERT (q_value == NULL);
      ASSERT (non_options_count == 0);
      ASSERT (unrecognized == 0);
      ASSERT (optind == 2);
    }
  for (start = 0; start <= 1; start++)
    {
      const char *p_value = NULL;
      const char *q_value = NULL;
      int non_options_count = 0;
      const char *non_options[10];
      int unrecognized = 0;
      int argc = 0;
      const char *argv[10];
      a_seen = 0;
      b_seen = 0;

      argv[argc++] = "program";
      argv[argc++] = "-ab";
      argv[argc++] = "-a";
      argv[argc++] = "foo";
      argv[argc++] = "bar";
      argv[argc] = NULL;
      optind = start;
      getopt_long_loop (argc, argv, "ab", long_options_required,
                        &p_value, &q_value,
                        &non_options_count, non_options, &unrecognized);
      ASSERT (a_seen == 2);
      ASSERT (b_seen == 1);
      ASSERT (p_value == NULL);
      ASSERT (q_value == NULL);
      ASSERT (non_options_count == 0);
      ASSERT (unrecognized == 0);
      ASSERT (optind == 3);
    }

  /* Test processing of boolean long options.  */
  for (start = 0; start <= 1; start++)
    {
      const char *p_value = NULL;
      const char *q_value = NULL;
      int non_options_count = 0;
      const char *non_options[10];
      int unrecognized = 0;
      int argc = 0;
      const char *argv[10];
      a_seen = 0;
      b_seen = 0;

      argv[argc++] = "program";
      argv[argc++] = "--alpha";
      argv[argc++] = "foo";
      argv[argc++] = "bar";
      argv[argc] = NULL;
      optind = start;
      getopt_long_loop (argc, argv, "ab", long_options_required,
                        &p_value, &q_value,
                        &non_options_count, non_options, &unrecognized);
      ASSERT (a_seen == 1);
      ASSERT (b_seen == 0);
      ASSERT (p_value == NULL);
      ASSERT (q_value == NULL);
      ASSERT (non_options_count == 0);
      ASSERT (unrecognized == 0);
      ASSERT (optind == 2);
    }
  for (start = 0; start <= 1; start++)
    {
      const char *p_value = NULL;
      const char *q_value = NULL;
      int non_options_count = 0;
      const char *non_options[10];
      int unrecognized = 0;
      int argc = 0;
      const char *argv[10];
      a_seen = 0;
      b_seen = 0;

      argv[argc++] = "program";
      argv[argc++] = "--beta";
      argv[argc++] = "--alpha";
      argv[argc++] = "foo";
      argv[argc++] = "bar";
      argv[argc] = NULL;
      optind = start;
      getopt_long_loop (argc, argv, "ab", long_options_required,
                        &p_value, &q_value,
                        &non_options_count, non_options, &unrecognized);
      ASSERT (a_seen == 1);
      ASSERT (b_seen == 1);
      ASSERT (p_value == NULL);
      ASSERT (q_value == NULL);
      ASSERT (non_options_count == 0);
      ASSERT (unrecognized == 0);
      ASSERT (optind == 3);
    }
  for (start = 0; start <= 1; start++)
    {
      const char *p_value = NULL;
      const char *q_value = NULL;
      int non_options_count = 0;
      const char *non_options[10];
      int unrecognized = 0;
      int argc = 0;
      const char *argv[10];
      a_seen = 0;
      b_seen = 0;

      argv[argc++] = "program";
      argv[argc++] = "--alpha";
      argv[argc++] = "--beta";
      argv[argc++] = "--alpha";
      argv[argc++] = "--beta";
      argv[argc++] = "foo";
      argv[argc++] = "bar";
      argv[argc] = NULL;
      optind = start;
      getopt_long_loop (argc, argv, "ab", long_options_required,
                        &p_value, &q_value,
                        &non_options_count, non_options, &unrecognized);
      ASSERT (a_seen == 2);
      ASSERT (b_seen == 1);
      ASSERT (p_value == NULL);
      ASSERT (q_value == NULL);
      ASSERT (non_options_count == 0);
      ASSERT (unrecognized == 0);
      ASSERT (optind == 5);
    }

  /* Test processing of boolean long options via -W.  */
  for (start = 0; start <= 1; start++)
    {
      const char *p_value = NULL;
      const char *q_value = NULL;
      int non_options_count = 0;
      const char *non_options[10];
      int unrecognized = 0;
      int argc = 0;
      const char *argv[10];
      a_seen = 0;
      b_seen = 0;

      argv[argc++] = "program";
      argv[argc++] = "-Walpha";
      argv[argc++] = "foo";
      argv[argc++] = "bar";
      argv[argc] = NULL;
      optind = start;
      getopt_long_loop (argc, argv, "abW;", long_options_required,
                        &p_value, &q_value,
                        &non_options_count, non_options, &unrecognized);
      ASSERT (a_seen == 1);
      ASSERT (b_seen == 0);
      ASSERT (p_value == NULL);
      ASSERT (q_value == NULL);
      ASSERT (non_options_count == 0);
      ASSERT (unrecognized == 0);
      ASSERT (optind == 2);
    }
  for (start = 0; start <= 1; start++)
    {
      const char *p_value = NULL;
      const char *q_value = NULL;
      int non_options_count = 0;
      const char *non_options[10];
      int unrecognized = 0;
      int argc = 0;
      const char *argv[10];
      a_seen = 0;
      b_seen = 0;

      argv[argc++] = "program";
      argv[argc++] = "-W";
      argv[argc++] = "beta";
      argv[argc++] = "-W";
      argv[argc++] = "alpha";
      argv[argc++] = "foo";
      argv[argc++] = "bar";
      argv[argc] = NULL;
      optind = start;
      getopt_long_loop (argc, argv, "aW;b", long_options_required,
                        &p_value, &q_value,
                        &non_options_count, non_options, &unrecognized);
      ASSERT (a_seen == 1);
      ASSERT (b_seen == 1);
      ASSERT (p_value == NULL);
      ASSERT (q_value == NULL);
      ASSERT (non_options_count == 0);
      ASSERT (unrecognized == 0);
      ASSERT (optind == 5);
    }
  for (start = 0; start <= 1; start++)
    {
      const char *p_value = NULL;
      const char *q_value = NULL;
      int non_options_count = 0;
      const char *non_options[10];
      int unrecognized = 0;
      int argc = 0;
      const char *argv[10];
      a_seen = 0;
      b_seen = 0;

      argv[argc++] = "program";
      argv[argc++] = "-Walpha";
      argv[argc++] = "-Wbeta";
      argv[argc++] = "-Walpha";
      argv[argc++] = "-Wbeta";
      argv[argc++] = "foo";
      argv[argc++] = "bar";
      argv[argc] = NULL;
      optind = start;
      getopt_long_loop (argc, argv, "W;ab", long_options_required,
                        &p_value, &q_value,
                        &non_options_count, non_options, &unrecognized);
      ASSERT (a_seen == 2);
      ASSERT (b_seen == 1);
      ASSERT (p_value == NULL);
      ASSERT (q_value == NULL);
      ASSERT (non_options_count == 0);
      ASSERT (unrecognized == 0);
      ASSERT (optind == 5);
    }

  /* Test processing of short options with arguments.  */
  for (start = 0; start <= 1; start++)
    {
      const char *p_value = NULL;
      const char *q_value = NULL;
      int non_options_count = 0;
      const char *non_options[10];
      int unrecognized = 0;
      int argc = 0;
      const char *argv[10];
      a_seen = 0;
      b_seen = 0;

      argv[argc++] = "program";
      argv[argc++] = "-pfoo";
      argv[argc++] = "bar";
      argv[argc] = NULL;
      optind = start;
      getopt_long_loop (argc, argv, "p:q:", long_options_required,
                        &p_value, &q_value,
                        &non_options_count, non_options, &unrecognized);
      ASSERT (a_seen == 0);
      ASSERT (b_seen == 0);
      ASSERT (p_value != NULL && strcmp (p_value, "foo") == 0);
      ASSERT (q_value == NULL);
      ASSERT (non_options_count == 0);
      ASSERT (unrecognized == 0);
      ASSERT (optind == 2);
    }
  for (start = 0; start <= 1; start++)
    {
      const char *p_value = NULL;
      const char *q_value = NULL;
      int non_options_count = 0;
      const char *non_options[10];
      int unrecognized = 0;
      int argc = 0;
      const char *argv[10];
      a_seen = 0;
      b_seen = 0;

      argv[argc++] = "program";
      argv[argc++] = "-p";
      argv[argc++] = "foo";
      argv[argc++] = "bar";
      argv[argc] = NULL;
      optind = start;
      getopt_long_loop (argc, argv, "p:q:", long_options_required,
                        &p_value, &q_value,
                        &non_options_count, non_options, &unrecognized);
      ASSERT (a_seen == 0);
      ASSERT (b_seen == 0);
      ASSERT (p_value != NULL && strcmp (p_value, "foo") == 0);
      ASSERT (q_value == NULL);
      ASSERT (non_options_count == 0);
      ASSERT (unrecognized == 0);
      ASSERT (optind == 3);
    }
  for (start = 0; start <= 1; start++)
    {
      const char *p_value = NULL;
      const char *q_value = NULL;
      int non_options_count = 0;
      const char *non_options[10];
      int unrecognized = 0;
      int argc = 0;
      const char *argv[10];
      a_seen = 0;
      b_seen = 0;

      argv[argc++] = "program";
      argv[argc++] = "-ab";
      argv[argc++] = "-q";
      argv[argc++] = "baz";
      argv[argc++] = "-pfoo";
      argv[argc++] = "bar";
      argv[argc] = NULL;
      optind = start;
      getopt_long_loop (argc, argv, "abp:q:", long_options_required,
                        &p_value, &q_value,
                        &non_options_count, non_options, &unrecognized);
      ASSERT (a_seen == 1);
      ASSERT (b_seen == 1);
      ASSERT (p_value != NULL && strcmp (p_value, "foo") == 0);
      ASSERT (q_value != NULL && strcmp (q_value, "baz") == 0);
      ASSERT (non_options_count == 0);
      ASSERT (unrecognized == 0);
      ASSERT (optind == 5);
    }

  /* Test processing of long options with arguments.  */
  for (start = 0; start <= 1; start++)
    {
      const char *p_value = NULL;
      const char *q_value = NULL;
      int non_options_count = 0;
      const char *non_options[10];
      int unrecognized = 0;
      int argc = 0;
      const char *argv[10];
      a_seen = 0;
      b_seen = 0;

      argv[argc++] = "program";
      argv[argc++] = "--p=foo";
      argv[argc++] = "bar";
      argv[argc] = NULL;
      optind = start;
      getopt_long_loop (argc, argv, "p:q:", long_options_required,
                        &p_value, &q_value,
                        &non_options_count, non_options, &unrecognized);
      ASSERT (a_seen == 0);
      ASSERT (b_seen == 0);
      ASSERT (p_value != NULL && strcmp (p_value, "foo") == 0);
      ASSERT (q_value == NULL);
      ASSERT (non_options_count == 0);
      ASSERT (unrecognized == 0);
      ASSERT (optind == 2);
    }
  for (start = 0; start <= 1; start++)
    {
      const char *p_value = NULL;
      const char *q_value = NULL;
      int non_options_count = 0;
      const char *non_options[10];
      int unrecognized = 0;
      int argc = 0;
      const char *argv[10];
      a_seen = 0;
      b_seen = 0;

      argv[argc++] = "program";
      argv[argc++] = "--p";
      argv[argc++] = "foo";
      argv[argc++] = "bar";
      argv[argc] = NULL;
      optind = start;
      getopt_long_loop (argc, argv, "p:q:", long_options_required,
                        &p_value, &q_value,
                        &non_options_count, non_options, &unrecognized);
      ASSERT (a_seen == 0);
      ASSERT (b_seen == 0);
      ASSERT (p_value != NULL && strcmp (p_value, "foo") == 0);
      ASSERT (q_value == NULL);
      ASSERT (non_options_count == 0);
      ASSERT (unrecognized == 0);
      ASSERT (optind == 3);
    }
  for (start = 0; start <= 1; start++)
    {
      const char *p_value = NULL;
      const char *q_value = NULL;
      int non_options_count = 0;
      const char *non_options[10];
      int unrecognized = 0;
      int argc = 0;
      const char *argv[10];
      a_seen = 0;
      b_seen = 0;

      argv[argc++] = "program";
      argv[argc++] = "-ab";
      argv[argc++] = "--q";
      argv[argc++] = "baz";
      argv[argc++] = "--p=foo";
      argv[argc++] = "bar";
      argv[argc] = NULL;
      optind = start;
      getopt_long_loop (argc, argv, "abp:q:", long_options_required,
                        &p_value, &q_value,
                        &non_options_count, non_options, &unrecognized);
      ASSERT (a_seen == 1);
      ASSERT (b_seen == 1);
      ASSERT (p_value != NULL && strcmp (p_value, "foo") == 0);
      ASSERT (q_value != NULL && strcmp (q_value, "baz") == 0);
      ASSERT (non_options_count == 0);
      ASSERT (unrecognized == 0);
      ASSERT (optind == 5);
    }

  /* Test processing of long options with arguments via -W.  */
  for (start = 0; start <= 1; start++)
    {
      const char *p_value = NULL;
      const char *q_value = NULL;
      int non_options_count = 0;
      const char *non_options[10];
      int unrecognized = 0;
      int argc = 0;
      const char *argv[10];
      a_seen = 0;
      b_seen = 0;

      argv[argc++] = "program";
      argv[argc++] = "-Wp=foo";
      argv[argc++] = "bar";
      argv[argc] = NULL;
      optind = start;
      getopt_long_loop (argc, argv, "p:q:W;", long_options_required,
                        &p_value, &q_value,
                        &non_options_count, non_options, &unrecognized);
      ASSERT (a_seen == 0);
      ASSERT (b_seen == 0);
      ASSERT (p_value != NULL && strcmp (p_value, "foo") == 0);
      ASSERT (q_value == NULL);
      ASSERT (non_options_count == 0);
      ASSERT (unrecognized == 0);
      ASSERT (optind == 2);
    }
  for (start = 0; start <= 1; start++)
    {
      const char *p_value = NULL;
      const char *q_value = NULL;
      int non_options_count = 0;
      const char *non_options[10];
      int unrecognized = 0;
      int argc = 0;
      const char *argv[10];
      a_seen = 0;
      b_seen = 0;

      argv[argc++] = "program";
      argv[argc++] = "-W";
      argv[argc++] = "p";
      argv[argc++] = "foo";
      argv[argc++] = "bar";
      argv[argc] = NULL;
      optind = start;
      getopt_long_loop (argc, argv, "p:W;q:", long_options_required,
                        &p_value, &q_value,
                        &non_options_count, non_options, &unrecognized);
      ASSERT (a_seen == 0);
      ASSERT (b_seen == 0);
      ASSERT (p_value != NULL && strcmp (p_value, "foo") == 0);
      ASSERT (q_value == NULL);
      ASSERT (non_options_count == 0);
      ASSERT (unrecognized == 0);
      ASSERT (optind == 4);
    }
  for (start = 0; start <= 1; start++)
    {
      const char *p_value = NULL;
      const char *q_value = NULL;
      int non_options_count = 0;
      const char *non_options[10];
      int unrecognized = 0;
      int argc = 0;
      const char *argv[10];
      a_seen = 0;
      b_seen = 0;

      argv[argc++] = "program";
      argv[argc++] = "-ab";
      argv[argc++] = "-Wq";
      argv[argc++] = "baz";
      argv[argc++] = "-W";
      argv[argc++] = "p=foo";
      argv[argc++] = "bar";
      argv[argc] = NULL;
      optind = start;
      getopt_long_loop (argc, argv, "W;abp:q:", long_options_required,
                        &p_value, &q_value,
                        &non_options_count, non_options, &unrecognized);
      ASSERT (a_seen == 1);
      ASSERT (b_seen == 1);
      ASSERT (p_value != NULL && strcmp (p_value, "foo") == 0);
      ASSERT (q_value != NULL && strcmp (q_value, "baz") == 0);
      ASSERT (non_options_count == 0);
      ASSERT (unrecognized == 0);
      ASSERT (optind == 6);
    }

  /* Test processing of short options with optional arguments.  */
  for (start = 0; start <= 1; start++)
    {
      const char *p_value = NULL;
      const char *q_value = NULL;
      int non_options_count = 0;
      const char *non_options[10];
      int unrecognized = 0;
      int argc = 0;
      const char *argv[10];
      a_seen = 0;
      b_seen = 0;

      argv[argc++] = "program";
      argv[argc++] = "-pfoo";
      argv[argc++] = "bar";
      argv[argc] = NULL;
      optind = start;
      getopt_long_loop (argc, argv, "p::q::", long_options_optional,
                        &p_value, &q_value,
                        &non_options_count, non_options, &unrecognized);
      ASSERT (a_seen == 0);
      ASSERT (b_seen == 0);
      ASSERT (p_value != NULL && strcmp (p_value, "foo") == 0);
      ASSERT (q_value == NULL);
      ASSERT (non_options_count == 0);
      ASSERT (unrecognized == 0);
      ASSERT (optind == 2);
    }
  for (start = 0; start <= 1; start++)
    {
      const char *p_value = NULL;
      const char *q_value = NULL;
      int non_options_count = 0;
      const char *non_options[10];
      int unrecognized = 0;
      int argc = 0;
      const char *argv[10];
      a_seen = 0;
      b_seen = 0;

      argv[argc++] = "program";
      argv[argc++] = "-p";
      argv[argc++] = "foo";
      argv[argc++] = "bar";
      argv[argc] = NULL;
      optind = start;
      getopt_long_loop (argc, argv, "p::q::", long_options_optional,
                        &p_value, &q_value,
                        &non_options_count, non_options, &unrecognized);
      ASSERT (a_seen == 0);
      ASSERT (b_seen == 0);
      ASSERT (p_value == NULL);
      ASSERT (q_value == NULL);
      ASSERT (non_options_count == 0);
      ASSERT (unrecognized == 0);
      ASSERT (optind == 2);
    }
  for (start = 0; start <= 1; start++)
    {
      const char *p_value = NULL;
      const char *q_value = NULL;
      int non_options_count = 0;
      const char *non_options[10];
      int unrecognized = 0;
      int argc = 0;
      const char *argv[10];
      a_seen = 0;
      b_seen = 0;

      argv[argc++] = "program";
      argv[argc++] = "-p";
      argv[argc++] = "-a";
      argv[argc++] = "bar";
      argv[argc] = NULL;
      optind = start;
      getopt_long_loop (argc, argv, "abp::q::", long_options_optional,
                        &p_value, &q_value,
                        &non_options_count, non_options, &unrecognized);
      ASSERT (a_seen == 1);
      ASSERT (b_seen == 0);
      ASSERT (p_value == NULL);
      ASSERT (q_value == NULL);
      ASSERT (non_options_count == 0);
      ASSERT (unrecognized == 0);
      ASSERT (optind == 3);
    }

  /* Test processing of long options with optional arguments.  */
  for (start = 0; start <= 1; start++)
    {
      const char *p_value = NULL;
      const char *q_value = NULL;
      int non_options_count = 0;
      const char *non_options[10];
      int unrecognized = 0;
      int argc = 0;
      const char *argv[10];
      a_seen = 0;
      b_seen = 0;

      argv[argc++] = "program";
      argv[argc++] = "--p=foo";
      argv[argc++] = "bar";
      argv[argc] = NULL;
      optind = start;
      getopt_long_loop (argc, argv, "p::q::", long_options_optional,
                        &p_value, &q_value,
                        &non_options_count, non_options, &unrecognized);
      ASSERT (a_seen == 0);
      ASSERT (b_seen == 0);
      ASSERT (p_value != NULL && strcmp (p_value, "foo") == 0);
      ASSERT (q_value == NULL);
      ASSERT (non_options_count == 0);
      ASSERT (unrecognized == 0);
      ASSERT (optind == 2);
    }
  for (start = 0; start <= 1; start++)
    {
      const char *p_value = NULL;
      const char *q_value = NULL;
      int non_options_count = 0;
      const char *non_options[10];
      int unrecognized = 0;
      int argc = 0;
      const char *argv[10];
      a_seen = 0;
      b_seen = 0;

      argv[argc++] = "program";
      argv[argc++] = "--p";
      argv[argc++] = "foo";
      argv[argc++] = "bar";
      argv[argc] = NULL;
      optind = start;
      getopt_long_loop (argc, argv, "p::q::", long_options_optional,
                        &p_value, &q_value,
                        &non_options_count, non_options, &unrecognized);
      ASSERT (a_seen == 0);
      ASSERT (b_seen == 0);
      ASSERT (p_value == NULL);
      ASSERT (q_value == NULL);
      ASSERT (non_options_count == 0);
      ASSERT (unrecognized == 0);
      ASSERT (optind == 2);
    }
  for (start = 0; start <= 1; start++)
    {
      const char *p_value = NULL;
      const char *q_value = NULL;
      int non_options_count = 0;
      const char *non_options[10];
      int unrecognized = 0;
      int argc = 0;
      const char *argv[10];
      a_seen = 0;
      b_seen = 0;

      argv[argc++] = "program";
      argv[argc++] = "--p=";
      argv[argc++] = "foo";
      argv[argc++] = "bar";
      argv[argc] = NULL;
      optind = start;
      getopt_long_loop (argc, argv, "p::q::", long_options_optional,
                        &p_value, &q_value,
                        &non_options_count, non_options, &unrecognized);
      ASSERT (a_seen == 0);
      ASSERT (b_seen == 0);
      ASSERT (p_value != NULL && *p_value == '\0');
      ASSERT (q_value == NULL);
      ASSERT (non_options_count == 0);
      ASSERT (unrecognized == 0);
      ASSERT (optind == 2);
    }
  for (start = 0; start <= 1; start++)
    {
      const char *p_value = NULL;
      const char *q_value = NULL;
      int non_options_count = 0;
      const char *non_options[10];
      int unrecognized = 0;
      int argc = 0;
      const char *argv[10];
      a_seen = 0;
      b_seen = 0;

      argv[argc++] = "program";
      argv[argc++] = "--p";
      argv[argc++] = "-a";
      argv[argc++] = "bar";
      argv[argc] = NULL;
      optind = start;
      getopt_long_loop (argc, argv, "abp::q::", long_options_optional,
                        &p_value, &q_value,
                        &non_options_count, non_options, &unrecognized);
      ASSERT (a_seen == 1);
      ASSERT (b_seen == 0);
      ASSERT (p_value == NULL);
      ASSERT (q_value == NULL);
      ASSERT (non_options_count == 0);
      ASSERT (unrecognized == 0);
      ASSERT (optind == 3);
    }

  /* Test processing of long options with optional arguments via -W.  */
  for (start = 0; start <= 1; start++)
    {
      const char *p_value = NULL;
      const char *q_value = NULL;
      int non_options_count = 0;
      const char *non_options[10];
      int unrecognized = 0;
      int argc = 0;
      const char *argv[10];
      a_seen = 0;
      b_seen = 0;

      argv[argc++] = "program";
      argv[argc++] = "-Wp=foo";
      argv[argc++] = "bar";
      argv[argc] = NULL;
      optind = start;
      getopt_long_loop (argc, argv, "p::q::W;", long_options_optional,
                        &p_value, &q_value,
                        &non_options_count, non_options, &unrecognized);
      ASSERT (a_seen == 0);
      ASSERT (b_seen == 0);
      ASSERT (p_value != NULL && strcmp (p_value, "foo") == 0);
      ASSERT (q_value == NULL);
      ASSERT (non_options_count == 0);
      ASSERT (unrecognized == 0);
      ASSERT (optind == 2);
    }
  for (start = 0; start <= 1; start++)
    {
      const char *p_value = NULL;
      const char *q_value = NULL;
      int non_options_count = 0;
      const char *non_options[10];
      int unrecognized = 0;
      int argc = 0;
      const char *argv[10];
      a_seen = 0;
      b_seen = 0;

      argv[argc++] = "program";
      argv[argc++] = "-Wp";
      argv[argc++] = "foo";
      argv[argc++] = "bar";
      argv[argc] = NULL;
      optind = start;
      getopt_long_loop (argc, argv, "p::q::W;", long_options_optional,
                        &p_value, &q_value,
                        &non_options_count, non_options, &unrecognized);
      ASSERT (a_seen == 0);
      ASSERT (b_seen == 0);
      ASSERT (p_value == NULL);
      ASSERT (q_value == NULL);
      ASSERT (non_options_count == 0);
      ASSERT (unrecognized == 0);
      ASSERT (optind == 2);
    }
  for (start = 0; start <= 1; start++)
    {
      const char *p_value = NULL;
      const char *q_value = NULL;
      int non_options_count = 0;
      const char *non_options[10];
      int unrecognized = 0;
      int argc = 0;
      const char *argv[10];
      a_seen = 0;
      b_seen = 0;

      argv[argc++] = "program";
      argv[argc++] = "-Wp=";
      argv[argc++] = "foo";
      argv[argc++] = "bar";
      argv[argc] = NULL;
      optind = start;
      getopt_long_loop (argc, argv, "W;p::q::", long_options_optional,
                        &p_value, &q_value,
                        &non_options_count, non_options, &unrecognized);
      ASSERT (a_seen == 0);
      ASSERT (b_seen == 0);
      ASSERT (p_value != NULL && *p_value == '\0');
      ASSERT (q_value == NULL);
      ASSERT (non_options_count == 0);
      ASSERT (unrecognized == 0);
      ASSERT (optind == 2);
    }
  for (start = 0; start <= 1; start++)
    {
      const char *p_value = NULL;
      const char *q_value = NULL;
      int non_options_count = 0;
      const char *non_options[10];
      int unrecognized = 0;
      int argc = 0;
      const char *argv[10];
      a_seen = 0;
      b_seen = 0;

      argv[argc++] = "program";
      argv[argc++] = "-W";
      argv[argc++] = "p=";
      argv[argc++] = "foo";
      argv[argc++] = "bar";
      argv[argc] = NULL;
      optind = start;
      getopt_long_loop (argc, argv, "W;p::q::", long_options_optional,
                        &p_value, &q_value,
                        &non_options_count, non_options, &unrecognized);
      ASSERT (a_seen == 0);
      ASSERT (b_seen == 0);
      ASSERT (p_value != NULL && *p_value == '\0');
      ASSERT (q_value == NULL);
      ASSERT (non_options_count == 0);
      ASSERT (unrecognized == 0);
      ASSERT (optind == 3);
    }
  for (start = 0; start <= 1; start++)
    {
      const char *p_value = NULL;
      const char *q_value = NULL;
      int non_options_count = 0;
      const char *non_options[10];
      int unrecognized = 0;
      int argc = 0;
      const char *argv[10];
      a_seen = 0;
      b_seen = 0;

      argv[argc++] = "program";
      argv[argc++] = "-W";
      argv[argc++] = "p";
      argv[argc++] = "-a";
      argv[argc++] = "bar";
      argv[argc] = NULL;
      optind = start;
      getopt_long_loop (argc, argv, "W;abp::q::", long_options_optional,
                        &p_value, &q_value,
                        &non_options_count, non_options, &unrecognized);
      ASSERT (a_seen == 1);
      ASSERT (b_seen == 0);
      /* ASSERT (p_value == NULL); */
      ASSERT (q_value == NULL);
      ASSERT (non_options_count == 0);
      ASSERT (unrecognized == 0);
      ASSERT (optind == 4);
    }

  /* Check that invalid options are recognized.  */
  for (start = 0; start <= 1; start++)
    {
      const char *p_value = NULL;
      const char *q_value = NULL;
      int non_options_count = 0;
      const char *non_options[10];
      int unrecognized = 0;
      int argc = 0;
      const char *argv[10];
      a_seen = 0;
      b_seen = 0;

      argv[argc++] = "program";
      argv[argc++] = "-p";
      argv[argc++] = "foo";
      argv[argc++] = "-x";
      argv[argc++] = "-a";
      argv[argc++] = "bar";
      argv[argc] = NULL;
      optind = start;
      getopt_long_loop (argc, argv, "abp:q:", long_options_required,
                        &p_value, &q_value,
                        &non_options_count, non_options, &unrecognized);
      ASSERT (a_seen == 1);
      ASSERT (b_seen == 0);
      ASSERT (p_value != NULL && strcmp (p_value, "foo") == 0);
      ASSERT (q_value == NULL);
      ASSERT (non_options_count == 0);
      ASSERT (unrecognized == 'x');
      ASSERT (optind == 5);
    }
  for (start = 0; start <= 1; start++)
    {
      const char *p_value = NULL;
      const char *q_value = NULL;
      int non_options_count = 0;
      const char *non_options[10];
      int unrecognized = 0;
      int argc = 0;
      const char *argv[10];
      a_seen = 0;
      b_seen = 0;

      argv[argc++] = "program";
      argv[argc++] = "-p";
      argv[argc++] = "foo";
      argv[argc++] = "-:";
      argv[argc++] = "-a";
      argv[argc++] = "bar";
      argv[argc] = NULL;
      optind = start;
      getopt_long_loop (argc, argv, "abp:q:", long_options_required,
                        &p_value, &q_value,
                        &non_options_count, non_options, &unrecognized);
      ASSERT (a_seen == 1);
      ASSERT (b_seen == 0);
      ASSERT (p_value != NULL && strcmp (p_value, "foo") == 0);
      ASSERT (q_value == NULL);
      ASSERT (non_options_count == 0);
      ASSERT (unrecognized == ':');
      ASSERT (optind == 5);
    }

  /* Check that unexpected arguments are recognized.  */
  for (start = 0; start <= 1; start++)
    {
      const char *p_value = NULL;
      const char *q_value = NULL;
      int non_options_count = 0;
      const char *non_options[10];
      int unrecognized = 0;
      int argc = 0;
      const char *argv[10];
      a_seen = 0;
      b_seen = 0;

      argv[argc++] = "program";
      argv[argc++] = "-p";
      argv[argc++] = "foo";
      argv[argc++] = "--a=";
      argv[argc++] = "bar";
      argv[argc] = NULL;
      optind = start;
      getopt_long_loop (argc, argv, "abp:q:", long_options_required,
                        &p_value, &q_value,
                        &non_options_count, non_options, &unrecognized);
      ASSERT (a_seen == 0);
      ASSERT (b_seen == 0);
      ASSERT (p_value != NULL && strcmp (p_value, "foo") == 0);
      ASSERT (q_value == NULL);
      ASSERT (non_options_count == 0);
      ASSERT (unrecognized == 'a');
      ASSERT (optind == 4);
    }
  for (start = 0; start <= 1; start++)
    {
      const char *p_value = NULL;
      const char *q_value = NULL;
      int non_options_count = 0;
      const char *non_options[10];
      int unrecognized = 0;
      int argc = 0;
      const char *argv[10];
      a_seen = 0;
      b_seen = 0;

      argv[argc++] = "program";
      argv[argc++] = "-p";
      argv[argc++] = "foo";
      argv[argc++] = "--b=";
      argv[argc++] = "bar";
      argv[argc] = NULL;
      optind = start;
      getopt_long_loop (argc, argv, "abp:q:", long_options_required,
                        &p_value, &q_value,
                        &non_options_count, non_options, &unrecognized);
      ASSERT (a_seen == 0);
      ASSERT (b_seen == 0);
      ASSERT (p_value != NULL && strcmp (p_value, "foo") == 0);
      ASSERT (q_value == NULL);
      ASSERT (non_options_count == 0);
      /* When flag is non-zero, glibc sets optopt anyway, but BSD
         leaves optopt unchanged.  */
      ASSERT (unrecognized == 1 || unrecognized == 0);
      ASSERT (optind == 4);
    }

  /* Check that by default, non-options arguments are moved to the end.  */
  for (start = 0; start <= 1; start++)
    {
      const char *p_value = NULL;
      const char *q_value = NULL;
      int non_options_count = 0;
      const char *non_options[10];
      int unrecognized = 0;
      int argc = 0;
      const char *argv[10];
      a_seen = 0;
      b_seen = 0;

      argv[argc++] = "program";
      argv[argc++] = "donald";
      argv[argc++] = "-p";
      argv[argc++] = "billy";
      argv[argc++] = "duck";
      argv[argc++] = "-a";
      argv[argc++] = "bar";
      argv[argc] = NULL;
      optind = start;
      getopt_long_loop (argc, argv, "abp:q:", long_options_required,
                        &p_value, &q_value,
                        &non_options_count, non_options, &unrecognized);
      ASSERT (strcmp (argv[0], "program") == 0);
      ASSERT (strcmp (argv[1], "-p") == 0);
      ASSERT (strcmp (argv[2], "billy") == 0);
      ASSERT (strcmp (argv[3], "-a") == 0);
      ASSERT (strcmp (argv[4], "donald") == 0);
      ASSERT (strcmp (argv[5], "duck") == 0);
      ASSERT (strcmp (argv[6], "bar") == 0);
      ASSERT (argv[7] == NULL);
      ASSERT (a_seen == 1);
      ASSERT (b_seen == 0);
      ASSERT (p_value != NULL && strcmp (p_value, "billy") == 0);
      ASSERT (q_value == NULL);
      ASSERT (non_options_count == 0);
      ASSERT (unrecognized == 0);
      ASSERT (optind == 4);
    }

  /* Check that '--' ends the argument processing.  */
  for (start = 0; start <= 1; start++)
    {
      const char *p_value = NULL;
      const char *q_value = NULL;
      int non_options_count = 0;
      const char *non_options[10];
      int unrecognized = 0;
      int argc = 0;
      const char *argv[20];
      a_seen = 0;
      b_seen = 0;

      argv[argc++] = "program";
      argv[argc++] = "donald";
      argv[argc++] = "-p";
      argv[argc++] = "billy";
      argv[argc++] = "duck";
      argv[argc++] = "-a";
      argv[argc++] = "--";
      argv[argc++] = "-b";
      argv[argc++] = "foo";
      argv[argc++] = "-q";
      argv[argc++] = "johnny";
      argv[argc++] = "bar";
      argv[argc] = NULL;
      optind = start;
      getopt_long_loop (argc, argv, "abp:q:", long_options_required,
                        &p_value, &q_value,
                        &non_options_count, non_options, &unrecognized);
      ASSERT (strcmp (argv[0], "program") == 0);
      ASSERT (strcmp (argv[1], "-p") == 0);
      ASSERT (strcmp (argv[2], "billy") == 0);
      ASSERT (strcmp (argv[3], "-a") == 0);
      ASSERT (strcmp (argv[4], "--") == 0);
      ASSERT (strcmp (argv[5], "donald") == 0);
      ASSERT (strcmp (argv[6], "duck") == 0);
      ASSERT (strcmp (argv[7], "-b") == 0);
      ASSERT (strcmp (argv[8], "foo") == 0);
      ASSERT (strcmp (argv[9], "-q") == 0);
      ASSERT (strcmp (argv[10], "johnny") == 0);
      ASSERT (strcmp (argv[11], "bar") == 0);
      ASSERT (argv[12] == NULL);
      ASSERT (a_seen == 1);
      ASSERT (b_seen == 0);
      ASSERT (p_value != NULL && strcmp (p_value, "billy") == 0);
      ASSERT (q_value == NULL);
      ASSERT (non_options_count == 0);
      ASSERT (unrecognized == 0);
      ASSERT (optind == 5);
    }

  /* Check that the '-' flag causes non-options to be returned in order.  */
  for (start = 0; start <= 1; start++)
    {
      const char *p_value = NULL;
      const char *q_value = NULL;
      int non_options_count = 0;
      const char *non_options[10];
      int unrecognized = 0;
      int argc = 0;
      const char *argv[10];
      a_seen = 0;
      b_seen = 0;

      argv[argc++] = "program";
      argv[argc++] = "donald";
      argv[argc++] = "-p";
      argv[argc++] = "billy";
      argv[argc++] = "duck";
      argv[argc++] = "-a";
      argv[argc++] = "bar";
      argv[argc] = NULL;
      optind = start;
      getopt_long_loop (argc, argv, "-abp:q:", long_options_required,
                        &p_value, &q_value,
                        &non_options_count, non_options, &unrecognized);
      ASSERT (strcmp (argv[0], "program") == 0);
      ASSERT (strcmp (argv[1], "donald") == 0);
      ASSERT (strcmp (argv[2], "-p") == 0);
      ASSERT (strcmp (argv[3], "billy") == 0);
      ASSERT (strcmp (argv[4], "duck") == 0);
      ASSERT (strcmp (argv[5], "-a") == 0);
      ASSERT (strcmp (argv[6], "bar") == 0);
      ASSERT (argv[7] == NULL);
      ASSERT (a_seen == 1);
      ASSERT (b_seen == 0);
      ASSERT (p_value != NULL && strcmp (p_value, "billy") == 0);
      ASSERT (q_value == NULL);
      ASSERT (non_options_count == 3);
      ASSERT (strcmp (non_options[0], "donald") == 0);
      ASSERT (strcmp (non_options[1], "duck") == 0);
      ASSERT (strcmp (non_options[2], "bar") == 0);
      ASSERT (unrecognized == 0);
      ASSERT (optind == 7);
    }

  /* Check that '--' ends the argument processing.  */
  for (start = 0; start <= 1; start++)
    {
      const char *p_value = NULL;
      const char *q_value = NULL;
      int non_options_count = 0;
      const char *non_options[10];
      int unrecognized = 0;
      int argc = 0;
      const char *argv[20];
      a_seen = 0;
      b_seen = 0;

      argv[argc++] = "program";
      argv[argc++] = "donald";
      argv[argc++] = "-p";
      argv[argc++] = "billy";
      argv[argc++] = "duck";
      argv[argc++] = "-a";
      argv[argc++] = "--";
      argv[argc++] = "-b";
      argv[argc++] = "foo";
      argv[argc++] = "-q";
      argv[argc++] = "johnny";
      argv[argc++] = "bar";
      argv[argc] = NULL;
      optind = start;
      getopt_long_loop (argc, argv, "-abp:q:", long_options_required,
                        &p_value, &q_value,
                        &non_options_count, non_options, &unrecognized);
      ASSERT (strcmp (argv[0], "program") == 0);
      ASSERT (strcmp (argv[1], "donald") == 0);
      ASSERT (strcmp (argv[2], "-p") == 0);
      ASSERT (strcmp (argv[3], "billy") == 0);
      ASSERT (strcmp (argv[4], "duck") == 0);
      ASSERT (strcmp (argv[5], "-a") == 0);
      ASSERT (strcmp (argv[6], "--") == 0);
      ASSERT (strcmp (argv[7], "-b") == 0);
      ASSERT (strcmp (argv[8], "foo") == 0);
      ASSERT (strcmp (argv[9], "-q") == 0);
      ASSERT (strcmp (argv[10], "johnny") == 0);
      ASSERT (strcmp (argv[11], "bar") == 0);
      ASSERT (argv[12] == NULL);
      ASSERT (a_seen == 1);
      ASSERT (b_seen == 0);
      ASSERT (p_value != NULL && strcmp (p_value, "billy") == 0);
      ASSERT (q_value == NULL);
      if (non_options_count == 2)
      {
        /* glibc behaviour.  */
        ASSERT (non_options_count == 2);
        ASSERT (strcmp (non_options[0], "donald") == 0);
        ASSERT (strcmp (non_options[1], "duck") == 0);
        ASSERT (unrecognized == 0);
        ASSERT (optind == 7);
      }
      else
      {
        /* Another valid behaviour.  */
        ASSERT (non_options_count == 7);
        ASSERT (strcmp (non_options[0], "donald") == 0);
        ASSERT (strcmp (non_options[1], "duck") == 0);
        ASSERT (strcmp (non_options[2], "-b") == 0);
        ASSERT (strcmp (non_options[3], "foo") == 0);
        ASSERT (strcmp (non_options[4], "-q") == 0);
        ASSERT (strcmp (non_options[5], "johnny") == 0);
        ASSERT (strcmp (non_options[6], "bar") == 0);
        ASSERT (unrecognized == 0);
        ASSERT (optind == 12);
      }
    }

  /* Check that the '-' flag has to come first.  */
  for (start = 0; start <= 1; start++)
    {
      const char *p_value = NULL;
      const char *q_value = NULL;
      int non_options_count = 0;
      const char *non_options[10];
      int unrecognized = 0;
      int argc = 0;
      const char *argv[10];
      a_seen = 0;
      b_seen = 0;

      argv[argc++] = "program";
      argv[argc++] = "donald";
      argv[argc++] = "-p";
      argv[argc++] = "billy";
      argv[argc++] = "duck";
      argv[argc++] = "-a";
      argv[argc++] = "bar";
      argv[argc] = NULL;
      optind = start;
      getopt_long_loop (argc, argv, "abp:q:-", long_options_required,
                        &p_value, &q_value,
                        &non_options_count, non_options, &unrecognized);
      ASSERT (strcmp (argv[0], "program") == 0);
      ASSERT (strcmp (argv[1], "-p") == 0);
      ASSERT (strcmp (argv[2], "billy") == 0);
      ASSERT (strcmp (argv[3], "-a") == 0);
      ASSERT (strcmp (argv[4], "donald") == 0);
      ASSERT (strcmp (argv[5], "duck") == 0);
      ASSERT (strcmp (argv[6], "bar") == 0);
      ASSERT (argv[7] == NULL);
      ASSERT (a_seen == 1);
      ASSERT (b_seen == 0);
      ASSERT (p_value != NULL && strcmp (p_value, "billy") == 0);
      ASSERT (q_value == NULL);
      ASSERT (non_options_count == 0);
      ASSERT (unrecognized == 0);
      ASSERT (optind == 4);
    }

  /* Check that the '+' flag causes the first non-option to terminate the
     loop.  */
  for (start = 0; start <= 1; start++)
    {
      const char *p_value = NULL;
      const char *q_value = NULL;
      int non_options_count = 0;
      const char *non_options[10];
      int unrecognized = 0;
      int argc = 0;
      const char *argv[10];
      a_seen = 0;
      b_seen = 0;

      argv[argc++] = "program";
      argv[argc++] = "donald";
      argv[argc++] = "-p";
      argv[argc++] = "billy";
      argv[argc++] = "duck";
      argv[argc++] = "-a";
      argv[argc++] = "bar";
      argv[argc] = NULL;
      optind = start;
      getopt_long_loop (argc, argv, "+abp:q:", long_options_required,
                        &p_value, &q_value,
                        &non_options_count, non_options, &unrecognized);
      ASSERT (strcmp (argv[0], "program") == 0);
      ASSERT (strcmp (argv[1], "donald") == 0);
      ASSERT (strcmp (argv[2], "-p") == 0);
      ASSERT (strcmp (argv[3], "billy") == 0);
      ASSERT (strcmp (argv[4], "duck") == 0);
      ASSERT (strcmp (argv[5], "-a") == 0);
      ASSERT (strcmp (argv[6], "bar") == 0);
      ASSERT (argv[7] == NULL);
      ASSERT (a_seen == 0);
      ASSERT (b_seen == 0);
      ASSERT (p_value == NULL);
      ASSERT (q_value == NULL);
      ASSERT (non_options_count == 0);
      ASSERT (unrecognized == 0);
      ASSERT (optind == 1);
    }
  for (start = 0; start <= 1; start++)
    {
      const char *p_value = NULL;
      const char *q_value = NULL;
      int non_options_count = 0;
      const char *non_options[10];
      int unrecognized = 0;
      int argc = 0;
      const char *argv[10];
      a_seen = 0;
      b_seen = 0;

      argv[argc++] = "program";
      argv[argc++] = "-+";
      argv[argc] = NULL;
      optind = start;
      getopt_long_loop (argc, argv, "+abp:q:", long_options_required,
                        &p_value, &q_value,
                        &non_options_count, non_options, &unrecognized);
      ASSERT (a_seen == 0);
      ASSERT (b_seen == 0);
      ASSERT (p_value == NULL);
      ASSERT (q_value == NULL);
      ASSERT (non_options_count == 0);
      ASSERT (unrecognized == '+');
      ASSERT (optind == 2);
    }

  /* Check that '--' ends the argument processing.  */
  for (start = 0; start <= 1; start++)
    {
      const char *p_value = NULL;
      const char *q_value = NULL;
      int non_options_count = 0;
      const char *non_options[10];
      int unrecognized = 0;
      int argc = 0;
      const char *argv[20];
      a_seen = 0;
      b_seen = 0;

      argv[argc++] = "program";
      argv[argc++] = "donald";
      argv[argc++] = "-p";
      argv[argc++] = "billy";
      argv[argc++] = "duck";
      argv[argc++] = "-a";
      argv[argc++] = "--";
      argv[argc++] = "-b";
      argv[argc++] = "foo";
      argv[argc++] = "-q";
      argv[argc++] = "johnny";
      argv[argc++] = "bar";
      argv[argc] = NULL;
      optind = start;
      getopt_long_loop (argc, argv, "+abp:q:", long_options_required,
                        &p_value, &q_value,
                        &non_options_count, non_options, &unrecognized);
      ASSERT (strcmp (argv[0], "program") == 0);
      ASSERT (strcmp (argv[1], "donald") == 0);
      ASSERT (strcmp (argv[2], "-p") == 0);
      ASSERT (strcmp (argv[3], "billy") == 0);
      ASSERT (strcmp (argv[4], "duck") == 0);
      ASSERT (strcmp (argv[5], "-a") == 0);
      ASSERT (strcmp (argv[6], "--") == 0);
      ASSERT (strcmp (argv[7], "-b") == 0);
      ASSERT (strcmp (argv[8], "foo") == 0);
      ASSERT (strcmp (argv[9], "-q") == 0);
      ASSERT (strcmp (argv[10], "johnny") == 0);
      ASSERT (strcmp (argv[11], "bar") == 0);
      ASSERT (argv[12] == NULL);
      ASSERT (a_seen == 0);
      ASSERT (b_seen == 0);
      ASSERT (p_value == NULL);
      ASSERT (q_value == NULL);
      ASSERT (non_options_count == 0);
      ASSERT (unrecognized == 0);
      ASSERT (optind == 1);
    }

  /* Check that the '+' flag has to come first.  */
  for (start = 0; start <= 1; start++)
    {
      const char *p_value = NULL;
      const char *q_value = NULL;
      int non_options_count = 0;
      const char *non_options[10];
      int unrecognized = 0;
      int argc = 0;
      const char *argv[10];
      a_seen = 0;
      b_seen = 0;

      argv[argc++] = "program";
      argv[argc++] = "donald";
      argv[argc++] = "-p";
      argv[argc++] = "billy";
      argv[argc++] = "duck";
      argv[argc++] = "-a";
      argv[argc++] = "bar";
      argv[argc] = NULL;
      optind = start;
      getopt_long_loop (argc, argv, "abp:q:+", long_options_required,
                        &p_value, &q_value,
                        &non_options_count, non_options, &unrecognized);
      ASSERT (strcmp (argv[0], "program") == 0);
      ASSERT (strcmp (argv[1], "-p") == 0);
      ASSERT (strcmp (argv[2], "billy") == 0);
      ASSERT (strcmp (argv[3], "-a") == 0);
      ASSERT (strcmp (argv[4], "donald") == 0);
      ASSERT (strcmp (argv[5], "duck") == 0);
      ASSERT (strcmp (argv[6], "bar") == 0);
      ASSERT (argv[7] == NULL);
      ASSERT (a_seen == 1);
      ASSERT (b_seen == 0);
      ASSERT (p_value != NULL && strcmp (p_value, "billy") == 0);
      ASSERT (q_value == NULL);
      ASSERT (non_options_count == 0);
      ASSERT (unrecognized == 0);
      ASSERT (optind == 4);
    }
}

/* Test behavior of getopt_long when POSIXLY_CORRECT is set in the
   environment.  Options with optional arguments should not change
   behavior just because of an environment variable.
   https://lists.gnu.org/r/bug-m4/2006-09/msg00028.html  */
static void
test_getopt_long_posix (void)
{
  int start;

  /* Check that POSIXLY_CORRECT stops parsing the same as leading '+'.  */
  for (start = 0; start <= 1; start++)
    {
      const char *p_value = NULL;
      const char *q_value = NULL;
      int non_options_count = 0;
      const char *non_options[10];
      int unrecognized = 0;
      int argc = 0;
      const char *argv[10];
      a_seen = 0;
      b_seen = 0;

      argv[argc++] = "program";
      argv[argc++] = "donald";
      argv[argc++] = "-p";
      argv[argc++] = "billy";
      argv[argc++] = "duck";
      argv[argc++] = "-a";
      argv[argc++] = "bar";
      argv[argc] = NULL;
      optind = start;
      getopt_long_loop (argc, argv, "abp:q:", long_options_required,
                        &p_value, &q_value,
                        &non_options_count, non_options, &unrecognized);
      ASSERT (strcmp (argv[0], "program") == 0);
      ASSERT (strcmp (argv[1], "donald") == 0);
      ASSERT (strcmp (argv[2], "-p") == 0);
      ASSERT (strcmp (argv[3], "billy") == 0);
      ASSERT (strcmp (argv[4], "duck") == 0);
      ASSERT (strcmp (argv[5], "-a") == 0);
      ASSERT (strcmp (argv[6], "bar") == 0);
      ASSERT (argv[7] == NULL);
      ASSERT (a_seen == 0);
      ASSERT (b_seen == 0);
      ASSERT (p_value == NULL);
      ASSERT (q_value == NULL);
      ASSERT (non_options_count == 0);
      ASSERT (unrecognized == 0);
      ASSERT (optind == 1);
    }

  /* Check that POSIXLY_CORRECT doesn't change optional arguments.  */
  for (start = 0; start <= 1; start++)
    {
      const char *p_value = NULL;
      const char *q_value = NULL;
      int non_options_count = 0;
      const char *non_options[10];
      int unrecognized = 0;
      int argc = 0;
      const char *argv[10];
      a_seen = 0;
      b_seen = 0;

      argv[argc++] = "program";
      argv[argc++] = "-p";
      argv[argc++] = "billy";
      argv[argc] = NULL;
      optind = start;
      getopt_long_loop (argc, argv, "p::", long_options_required,
                        &p_value, &q_value,
                        &non_options_count, non_options, &unrecognized);
      ASSERT (a_seen == 0);
      ASSERT (b_seen == 0);
      ASSERT (p_value == NULL);
      ASSERT (q_value == NULL);
      ASSERT (non_options_count == 0);
      ASSERT (unrecognized == 0);
      ASSERT (optind == 2);
    }

  /* Check that leading - still sees options after non-options.  */
  for (start = 0; start <= 1; start++)
    {
      const char *p_value = NULL;
      const char *q_value = NULL;
      int non_options_count = 0;
      const char *non_options[10];
      int unrecognized = 0;
      int argc = 0;
      const char *argv[10];
      a_seen = 0;
      b_seen = 0;

      argv[argc++] = "program";
      argv[argc++] = "-a";
      argv[argc++] = "billy";
      argv[argc++] = "-b";
      argv[argc] = NULL;
      optind = start;
      getopt_long_loop (argc, argv, "-ab", long_options_required,
                        &p_value, &q_value,
                        &non_options_count, non_options, &unrecognized);
      ASSERT (a_seen == 1);
      ASSERT (b_seen == 1);
      ASSERT (p_value == NULL);
      ASSERT (q_value == NULL);
      ASSERT (non_options_count == 1);
      ASSERT (strcmp (non_options[0], "billy") == 0);
      ASSERT (unrecognized == 0);
      ASSERT (optind == 4);
    }
}

/* Reduce casting, so we can use string literals elsewhere.
   getopt_long_only takes an array of char*, but luckily does not
   modify those elements, so we can pass const char*.  */
static int
do_getopt_long_only (int argc, const char **argv, const char *shortopts,
                     const struct option *longopts, int *longind)
{
  return getopt_long_only (argc, (char **) argv, shortopts, longopts, longind);
}

static void
test_getopt_long_only (void)
{
  /* Test disambiguation of options.  */
  {
    int argc = 0;
    const char *argv[10];
    int option_index;
    int c;

    argv[argc++] = "program";
    argv[argc++] = "-x";
    argv[argc] = NULL;
    optind = 1;
    opterr = 0;
    c = do_getopt_long_only (argc, argv, "ab", long_options_required,
                             &option_index);
    ASSERT (c == '?');
    ASSERT (optopt == 0);
  }
  {
    int argc = 0;
    const char *argv[10];
    int option_index;
    int c;

    argv[argc++] = "program";
    argv[argc++] = "-x";
    argv[argc] = NULL;
    optind = 1;
    opterr = 0;
    c = do_getopt_long_only (argc, argv, "abx", long_options_required,
                             &option_index);
    ASSERT (c == 'x');
    ASSERT (optopt == 0);
  }
  {
    int argc = 0;
    const char *argv[10];
    int option_index;
    int c;

    argv[argc++] = "program";
    argv[argc++] = "--x";
    argv[argc] = NULL;
    optind = 1;
    opterr = 0;
    c = do_getopt_long_only (argc, argv, "abx", long_options_required,
                             &option_index);
    ASSERT (c == '?');
    ASSERT (optopt == 0);
  }
  {
    int argc = 0;
    const char *argv[10];
    int option_index;
    int c;

    argv[argc++] = "program";
    argv[argc++] = "-b";
    argv[argc] = NULL;
    optind = 1;
    opterr = 0;
    b_seen = 0;
    c = do_getopt_long_only (argc, argv, "abx", long_options_required,
                             &option_index);
    ASSERT (c == 'b');
    ASSERT (b_seen == 0);
  }
  {
    int argc = 0;
    const char *argv[10];
    int option_index;
    int c;

    argv[argc++] = "program";
    argv[argc++] = "--b";
    argv[argc] = NULL;
    optind = 1;
    opterr = 0;
    b_seen = 0;
    c = do_getopt_long_only (argc, argv, "abx", long_options_required,
                             &option_index);
    ASSERT (c == 0);
    ASSERT (b_seen == 1);
  }
  {
    int argc = 0;
    const char *argv[10];
    int option_index;
    int c;

    argv[argc++] = "program";
    argv[argc++] = "-xt";
    argv[argc] = NULL;
    optind = 1;
    opterr = 0;
    c = do_getopt_long_only (argc, argv, "ab", long_options_required,
                             &option_index);
    ASSERT (c == '?');
    ASSERT (optopt == 0);
  }
  {
    int argc = 0;
    const char *argv[10];
    int option_index;
    int c;

    argv[argc++] = "program";
    argv[argc++] = "-xt";
    argv[argc] = NULL;
    optind = 1;
    opterr = 0;
    c = do_getopt_long_only (argc, argv, "abx", long_options_required,
                             &option_index);
    ASSERT (c == '?');
    ASSERT (optopt == 0);
  }
  {
    int argc = 0;
    const char *argv[10];
    int option_index;
    int c;

    argv[argc++] = "program";
    argv[argc++] = "-xtra";
    argv[argc] = NULL;
    optind = 1;
    opterr = 0;
    c = do_getopt_long_only (argc, argv, "ab", long_options_required,
                             &option_index);
    ASSERT (c == 1001);
  }
  {
    int argc = 0;
    const char *argv[10];
    int option_index;
    int c;

    argv[argc++] = "program";
    argv[argc++] = "-xtreme";
    argv[argc] = NULL;
    optind = 1;
    opterr = 0;
    c = do_getopt_long_only (argc, argv, "abx:", long_options_required,
                             &option_index);
    ASSERT (c == 1002);
  }
  {
    int argc = 0;
    const char *argv[10];
    int option_index;
    int c;

    argv[argc++] = "program";
    argv[argc++] = "-xtremel";
    argv[argc] = NULL;
    optind = 1;
    opterr = 0;
    c = do_getopt_long_only (argc, argv, "ab", long_options_required,
                             &option_index);
    /* glibc getopt_long_only is intentionally different from
       getopt_long when handling a prefix that is common to two
       spellings, when both spellings have the same option directives.
       BSD getopt_long_only treats both cases the same.  */
    ASSERT (c == 1003 || c == '?');
    ASSERT (optind == 2);
  }
  {
    int argc = 0;
    const char *argv[10];
    int option_index;
    int c;

    argv[argc++] = "program";
    argv[argc++] = "-xtremel";
    argv[argc] = NULL;
    optind = 1;
    opterr = 0;
    c = do_getopt_long_only (argc, argv, "abx::", long_options_required,
                             &option_index);
    /* glibc getopt_long_only is intentionally different from
       getopt_long when handling a prefix that is common to two
       spellings, when both spellings have the same option directives.
       BSD getopt_long_only treats both cases the same.  */
    ASSERT (c == 1003 || c == '?');
    ASSERT (optind == 2);
    ASSERT (optarg == NULL);
  }
  {
    int argc = 0;
    const char *argv[10];
    int option_index;
    int c;

    argv[argc++] = "program";
    argv[argc++] = "-xtras";
    argv[argc] = NULL;
    optind = 1;
    opterr = 0;
    c = do_getopt_long_only (argc, argv, "abx::", long_options_required,
                             &option_index);
    ASSERT (c == 'x');
    ASSERT (strcmp (optarg, "tras") == 0);
  }
}
