bug-gnulib
[Top][All Lists]
Advanced

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

Re: glibc getopt bugs


From: Eric Blake
Subject: Re: glibc getopt bugs
Date: Wed, 2 Dec 2009 22:53:01 +0000 (UTC)
User-agent: Loom/3.14 (http://gmane.org/)

Eric Blake <ebb9 <at> byu.net> writes:

> 
> The carnage continues.
> 
> http://sources.redhat.com/bugzilla/show_bug.cgi?id=11043

Here's a real-life example of glibc's getopt getting in the way of sane 
behavior.  Anyone opposed to me change gnulib to blindly replacing the broken 
glibc getopt?

$ env -ua -:
env: invalid option -- :
Try `env --help' for more information.
$ env -:
env: invalid option -- :
Try `env --help' for more information.
$ env -+
env: invalid option -- +
Try `env --help' for more information.
$ env -ua -+
Try `env --help' for more information.

Ouch.  If -+ is not the first option, the error message is omitted.

By the way, here's the tests I've been working on to expose all of these glibc 
bugs; still a work in progress (I'd have to tweak m4/getopt.m4 and lib/getopt.c 
in order to remove some of the commented out portions in the test 
improvements).  It also exposes some more corner case bugs in BSD; for example, 
in glibc, getopt_long_only intentionally handles ambiguous options differently 
than getopt_long, due to a usage pattern demanded by ld; but various BSD 
implementations have not picked up on that subtle difference.

>From aabb0345c0e9f7e76ea74bd519046118ec8ff18c Mon Sep 17 00:00:00 2001
From: Eric Blake <address@hidden>
Date: Tue, 1 Dec 2009 17:21:34 -0700
Subject: [PATCH 1/2] test-getopt: enhance test

* tests/test-getopt.c (OPTIND_MIN): Move...
* tests/test-getopt.h (OPTIND_MIN): ...here.
* tests/test-getopt_long.h (test_getopt_long): Add more coverage.
Require that optind=0 works, since modern BSD supports it in
addition to optreset.
(test_getopt_long_only): New test.
* doc/glibc-functions/getopt_long.texi (getopt_long): Document
glibc shortcomings with 'W;'.
* doc/glibc-functions/getopt_long_only.texi (getopt_long_only):
Likewise.
---
 ChangeLog                                 |   14 +
 doc/glibc-functions/getopt_long.texi      |    4 +
 doc/glibc-functions/getopt_long_only.texi |    4 +
 tests/test-getopt.c                       |   13 +-
 tests/test-getopt.h                       |   11 +
 tests/test-getopt_long.h                  | 1119 ++++++++++++++++++++++++++++-
 6 files changed, 1129 insertions(+), 36 deletions(-)

diff --git a/ChangeLog b/ChangeLog
index 6eec830..a1d3f50 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,3 +1,17 @@
+2009-12-02  Eric Blake  <address@hidden>
+
+       test-getopt: enhance test
+       * tests/test-getopt.c (OPTIND_MIN): Move...
+       * tests/test-getopt.h (OPTIND_MIN): ...here.
+       * tests/test-getopt_long.h (test_getopt_long): Add more coverage.
+       Require that optind=0 works, since modern BSD supports it in
+       addition to optreset.
+       (test_getopt_long_only): New test.
+       * doc/glibc-functions/getopt_long.texi (getopt_long): Document
+       glibc shortcomings with 'W;'.
+       * doc/glibc-functions/getopt_long_only.texi (getopt_long_only):
+       Likewise.
+
 2009-12-01  Jim Meyering  <address@hidden>

        fts: fts_open: do not let an empty string cause immediate failure
diff --git a/doc/glibc-functions/getopt_long.texi b/doc/glibc-
functions/getopt_long.texi
index c9843ea..275722b 100644
--- a/doc/glibc-functions/getopt_long.texi
+++ b/doc/glibc-functions/getopt_long.texi
@@ -31,4 +31,8 @@ getopt_long
 @code{optind} to 0.  Other implementations provide @code{optreset},
 causing a reset by setting it non-zero, although it does not
 necessarily re-read @env{POSIXLY_CORRECT}.
address@hidden
+The glibc extension of using an option string containing @samp{W;} to
+allow @code{-W foo} to behave synonymously with @code{--foo} is not
+very reliable.
 @end itemize
diff --git a/doc/glibc-functions/getopt_long_only.texi b/doc/glibc-
functions/getopt_long_only.texi
index ed6cd7b..5ade129 100644
--- a/doc/glibc-functions/getopt_long_only.texi
+++ b/doc/glibc-functions/getopt_long_only.texi
@@ -32,4 +32,8 @@ getopt_long_only
 @code{optind} to 0.  Other implementations provide @code{optreset},
 causing a reset by setting it non-zero, although it does not
 necessarily re-read @env{POSIXLY_CORRECT}.
address@hidden
+The glibc extension of using an option string containing @samp{W;} to
+allow @code{-W foo} to behave synonymously with @code{--foo} is not
+very reliable.
 @end itemize
diff --git a/tests/test-getopt.c b/tests/test-getopt.c
index cc87f32..5ef6974 100644
--- a/tests/test-getopt.c
+++ b/tests/test-getopt.c
@@ -39,14 +39,6 @@
     }                                                                       \
   while (0)

-/* The glibc/gnulib implementation of getopt supports setting optind = 0,
-   but other implementations don't.  */
-#if defined __GETOPT_PREFIX || (__GLIBC__ >= 2)
-# define OPTIND_MIN 0
-#else
-# define OPTIND_MIN 1
-#endif
-
 #include "test-getopt.h"
 #if GNULIB_GETOPT_GNU
 # include "test-getopt_long.h"
@@ -55,6 +47,10 @@
 int
 main (void)
 {
+  /* These default values are required by POSIX.  */
+  ASSERT (optind == 1);
+  ASSERT (opterr != 0);
+
   setenv ("POSIXLY_CORRECT", "1", 1);
   test_getopt ();

@@ -67,6 +63,7 @@ main (void)

 #if GNULIB_GETOPT_GNU
   test_getopt_long ();
+  test_getopt_long_only ();
 #endif

   return 0;
diff --git a/tests/test-getopt.h b/tests/test-getopt.h
index be47b53..3a3ee63 100644
--- a/tests/test-getopt.h
+++ b/tests/test-getopt.h
@@ -18,6 +18,17 @@

 #include <stdbool.h>

+/* The glibc/gnulib implementation of getopt supports setting optind =
+   0, but not all other implementations do.  This matters for getopt.
+   But for getopt_long, we require GNU compatibility.  */
+#if defined __GETOPT_PREFIX || (__GLIBC__ >= 2)
+# define OPTIND_MIN 0
+#elif HAVE_DECL_OPTRESET
+# define OPTIND_MIN (optreset = 1)
+#else
+# define OPTIND_MIN 1
+#endif
+
 static void
 getopt_loop (int argc, const char **argv,
             const char *options,
diff --git a/tests/test-getopt_long.h b/tests/test-getopt_long.h
index 63cc5c7..7f629bc 100644
--- a/tests/test-getopt_long.h
+++ b/tests/test-getopt_long.h
@@ -18,13 +18,15 @@

 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, NULL, 'q' },
+    { "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 },
@@ -36,7 +38,7 @@ 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, NULL, 'q' },
+    { "quetsche", optional_argument, &q_seen, 1 },
     { NULL,       0,                 NULL, 0 }
   };

@@ -47,10 +49,11 @@ getopt_long_loop (int argc, const char **argv,
                  int *non_options_count, const char **non_options,
                  int *unrecognized)
 {
-  int option_index;
+  int option_index = -1;
   int c;

   opterr = 0;
+  q_seen = 0;
   while ((c = getopt_long (argc, (char **) argv, options, long_options,
                           &option_index))
         != -1)
@@ -59,6 +62,8 @@ getopt_long_loop (int argc, const char **argv,
        {
        case 0:
          /* An option with a non-NULL flag pointer was processed.  */
+         if (q_seen)
+           *q_value = optarg;
          break;
        case 'a':
          a_seen++;
@@ -77,6 +82,12 @@ getopt_long_loop (int argc, const char **argv,
          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] == ':'));
+         /* fall through */
        case '?':
          *unrecognized = optopt;
          break;
@@ -235,8 +246,76 @@ test_getopt_long (void)
     ASSERT (c == 1003);
   }

-  /* Test processing of boolean options.  */
-  for (start = OPTIND_MIN; start <= 1; start++)
+  /* 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 processing of boolean short options.  */
+  for (start = 0; start <= 1; start++)
     {
       const char *p_value = NULL;
       const char *q_value = NULL;
@@ -265,7 +344,7 @@ test_getopt_long (void)
       ASSERT (unrecognized == 0);
       ASSERT (optind == 2);
     }
-  for (start = OPTIND_MIN; start <= 1; start++)
+  for (start = 0; start <= 1; start++)
     {
       const char *p_value = NULL;
       const char *q_value = NULL;
@@ -295,7 +374,7 @@ test_getopt_long (void)
       ASSERT (unrecognized == 0);
       ASSERT (optind == 3);
     }
-  for (start = OPTIND_MIN; start <= 1; start++)
+  for (start = 0; start <= 1; start++)
     {
       const char *p_value = NULL;
       const char *q_value = NULL;
@@ -324,7 +403,7 @@ test_getopt_long (void)
       ASSERT (unrecognized == 0);
       ASSERT (optind == 2);
     }
-  for (start = OPTIND_MIN; start <= 1; start++)
+  for (start = 0; start <= 1; start++)
     {
       const char *p_value = NULL;
       const char *q_value = NULL;
@@ -355,8 +434,196 @@ test_getopt_long (void)
       ASSERT (optind == 3);
     }

-  /* Test processing of options with arguments.  */
-  for (start = OPTIND_MIN; start <= 1; start++)
+  /* 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;
@@ -384,7 +651,7 @@ test_getopt_long (void)
       ASSERT (unrecognized == 0);
       ASSERT (optind == 2);
     }
-  for (start = OPTIND_MIN; start <= 1; start++)
+  for (start = 0; start <= 1; start++)
     {
       const char *p_value = NULL;
       const char *q_value = NULL;
@@ -413,7 +680,7 @@ test_getopt_long (void)
       ASSERT (unrecognized == 0);
       ASSERT (optind == 3);
     }
-  for (start = OPTIND_MIN; start <= 1; start++)
+  for (start = 0; start <= 1; start++)
     {
       const char *p_value = NULL;
       const char *q_value = NULL;
@@ -445,8 +712,190 @@ test_getopt_long (void)
       ASSERT (optind == 5);
     }

-  /* Test processing of options with optional arguments.  */
-  for (start = OPTIND_MIN; start <= 1; start++)
+  /* 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;
@@ -474,7 +923,7 @@ test_getopt_long (void)
       ASSERT (unrecognized == 0);
       ASSERT (optind == 2);
     }
-  for (start = OPTIND_MIN; start <= 1; start++)
+  for (start = 0; start <= 1; start++)
     {
       const char *p_value = NULL;
       const char *q_value = NULL;
@@ -503,7 +952,7 @@ test_getopt_long (void)
       ASSERT (unrecognized == 0);
       ASSERT (optind == 2);
     }
-  for (start = OPTIND_MIN; start <= 1; start++)
+  for (start = 0; start <= 1; start++)
     {
       const char *p_value = NULL;
       const char *q_value = NULL;
@@ -533,8 +982,274 @@ test_getopt_long (void)
       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);
+      /* glibc bug http://sources.redhat.com/bugzilla/show_bug.cgi?id=11041 */
+      /* 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 = OPTIND_MIN; start <= 1; start++)
+  for (start = 0; start <= 1; start++)
     {
       const char *p_value = NULL;
       const char *q_value = NULL;
@@ -565,9 +1280,104 @@ test_getopt_long (void)
       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 = OPTIND_MIN; start <= 1; start++)
+  for (start = 0; start <= 1; start++)
     {
       const char *p_value = NULL;
       const char *q_value = NULL;
@@ -609,7 +1419,7 @@ test_getopt_long (void)
     }

   /* Check that '--' ends the argument processing.  */
-  for (start = OPTIND_MIN; start <= 1; start++)
+  for (start = 0; start <= 1; start++)
     {
       const char *p_value = NULL;
       const char *q_value = NULL;
@@ -661,7 +1471,7 @@ test_getopt_long (void)
     }

   /* Check that the '-' flag causes non-options to be returned in order.  */
-  for (start = OPTIND_MIN; start <= 1; start++)
+  for (start = 0; start <= 1; start++)
     {
       const char *p_value = NULL;
       const char *q_value = NULL;
@@ -706,7 +1516,7 @@ test_getopt_long (void)
     }

   /* Check that '--' ends the argument processing.  */
-  for (start = OPTIND_MIN; start <= 1; start++)
+  for (start = 0; start <= 1; start++)
     {
       const char *p_value = NULL;
       const char *q_value = NULL;
@@ -778,7 +1588,7 @@ test_getopt_long (void)
     }

   /* Check that the '-' flag has to come first.  */
-  for (start = OPTIND_MIN; start <= 1; start++)
+  for (start = 0; start <= 1; start++)
     {
       const char *p_value = NULL;
       const char *q_value = NULL;
@@ -821,7 +1631,7 @@ test_getopt_long (void)

   /* Check that the '+' flag causes the first non-option to terminate the
      loop.  */
-  for (start = OPTIND_MIN; start <= 1; start++)
+  for (start = 0; start <= 1; start++)
     {
       const char *p_value = NULL;
       const char *q_value = NULL;
@@ -861,7 +1671,7 @@ test_getopt_long (void)
       ASSERT (unrecognized == 0);
       ASSERT (optind == 1);
     }
-  for (start = OPTIND_MIN; start <= 1; start++)
+  for (start = 0; start <= 1; start++)
     {
       const char *p_value = NULL;
       const char *q_value = NULL;
@@ -890,7 +1700,7 @@ test_getopt_long (void)
     }

   /* Check that '--' ends the argument processing.  */
-  for (start = OPTIND_MIN; start <= 1; start++)
+  for (start = 0; start <= 1; start++)
     {
       const char *p_value = NULL;
       const char *q_value = NULL;
@@ -942,7 +1752,7 @@ test_getopt_long (void)
     }

   /* Check that the '+' flag has to come first.  */
-  for (start = OPTIND_MIN; start <= 1; start++)
+  for (start = 0; start <= 1; start++)
     {
       const char *p_value = NULL;
       const char *q_value = NULL;
@@ -993,8 +1803,50 @@ 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 = OPTIND_MIN; start <= 1; start++)
+  for (start = 0; start <= 1; start++)
     {
       const char *p_value = NULL;
       const char *q_value = NULL;
@@ -1024,7 +1876,7 @@ test_getopt_long_posix (void)
     }

   /* Check that leading - still sees options after non-options.  */
-  for (start = OPTIND_MIN; start <= 1; start++)
+  for (start = 0; start <= 1; start++)
     {
       const char *p_value = NULL;
       const char *q_value = NULL;
@@ -1055,3 +1907,214 @@ test_getopt_long_posix (void)
       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 bug http://sources.redhat.com/bugzilla/show_bug.cgi?id=11041 */
+    /* ASSERT (c == 1003); */
+    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 bug http://sources.redhat.com/bugzilla/show_bug.cgi?id=11041 */
+    /* ASSERT (c == 1003); */
+    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);
+  }
+}
-- 
1.6.4.2


>From e7dc3f8ed92d330a367f7e5d02ed84d700ad23cb Mon Sep 17 00:00:00 2001
From: Eric Blake <address@hidden>
Date: Wed, 2 Dec 2009 15:48:08 -0700
Subject: [PATCH 2/2] test-getopt: test stderr behavior

Portions of this commit are commented out because they tickle
glibc bugs.  For a real-life example of the bug:

$ env -ua -:
env: invalid option -- :
Try `env --help' for more information.
$ env -:
env: invalid option -- :
Try `env --help' for more information.
$ env -+
env: invalid option -- +
Try `env --help' for more information.
$ env -ua -+
Try `env --help' for more information.

Notice that when -+ is not given as the first argument, the
error message is mistakenly suppressed.

* modules/getopt-posix-tests (Depends-on): Add dup2.
* tests/test-getopt.c (ASSERT): Avoid stderr.
(main): Move stderr to a temporary file.
* tests/test-getopt.h (getopt_loop): No longer manipulate opterr.
Instead, add parameter to inform caller if output occurred.
(test_getopt): Adjust all tests to expect silence, and add new
tests of leading ":".

Signed-off-by: Eric Blake <address@hidden>
---
 ChangeLog                  |    9 +
 modules/getopt-posix-tests |    1 +
 tests/test-getopt.c        |   28 +++-
 tests/test-getopt.h        |  387 +++++++++++++++++++++++++++++++++++++++++---
 4 files changed, 400 insertions(+), 25 deletions(-)

diff --git a/ChangeLog b/ChangeLog
index a1d3f50..c2a2242 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,5 +1,14 @@
 2009-12-02  Eric Blake  <address@hidden>

+       test-getopt: test stderr behavior
+       * modules/getopt-posix-tests (Depends-on): Add dup2.
+       * tests/test-getopt.c (ASSERT): Avoid stderr.
+       (main): Move stderr to a temporary file.
+       * tests/test-getopt.h (getopt_loop): No longer manipulate opterr.
+       Instead, add parameter to inform caller if output occurred.
+       (test_getopt): Adjust all tests to expect silence, and add new
+       tests of leading ":".
+
        test-getopt: enhance test
        * tests/test-getopt.c (OPTIND_MIN): Move...
        * tests/test-getopt.h (OPTIND_MIN): ...here.
diff --git a/modules/getopt-posix-tests b/modules/getopt-posix-tests
index be828cf..b2e3727 100644
--- a/modules/getopt-posix-tests
+++ b/modules/getopt-posix-tests
@@ -4,6 +4,7 @@ tests/test-getopt.h
 tests/test-getopt_long.h

 Depends-on:
+dup2
 setenv
 stdbool
 unistd
diff --git a/tests/test-getopt.c b/tests/test-getopt.c
index 5ef6974..cac54f5 100644
--- a/tests/test-getopt.c
+++ b/tests/test-getopt.c
@@ -27,18 +27,31 @@
 #include <stdlib.h>
 #include <string.h>

+/* This test intentionally remaps stderr.  So, we arrange to have fd 10
+   (outside the range of interesting fd's during the test) set up to
+   duplicate the original stderr.  */
+
+#define BACKUP_STDERR_FILENO 10
+static FILE *myerr;
+
 #define ASSERT(expr) \
   do                                                                        \
     {                                                                       \
       if (!(expr))                                                          \
         {                                                                   \
-          fprintf (stderr, "%s:%d: assertion failed\n", __FILE__, __LINE__); \
-          fflush (stderr);                                                  \
+          fprintf (myerr, "%s:%d: assertion failed\n", __FILE__, __LINE__);  \
+          fflush (myerr);                                                   \
           abort ();                                                         \
         }                                                                   \
     }                                                                       \
   while (0)

+/* None of the output to stderr by this test is large, so disable the
+   ftell link warning if we are not using the gnulib ftell module.  */
+#if !GNULIB_FTELL
+# undef ftell
+#endif
+
 #include "test-getopt.h"
 #if GNULIB_GETOPT_GNU
 # include "test-getopt_long.h"
@@ -47,6 +60,14 @@
 int
 main (void)
 {
+   /* This test validates that stderr is used correctly, so move the
+      original into fd 10.  */
+  if (dup2 (STDERR_FILENO, BACKUP_STDERR_FILENO) != BACKUP_STDERR_FILENO
+      || (myerr = fdopen (BACKUP_STDERR_FILENO, "w")) == NULL)
+    return 2;
+
+  ASSERT (freopen ("test-getopt.tmp", "w", stderr) == stderr);
+
   /* These default values are required by POSIX.  */
   ASSERT (optind == 1);
   ASSERT (opterr != 0);
@@ -66,5 +87,8 @@ main (void)
   test_getopt_long_only ();
 #endif

+  ASSERT (fclose (stderr) == 0);
+  ASSERT (remove ("test-getopt.tmp") == 0);
+
   return 0;
 }
diff --git a/tests/test-getopt.h b/tests/test-getopt.h
index 3a3ee63..245a6f8 100644
--- a/tests/test-getopt.h
+++ b/tests/test-getopt.h
@@ -35,11 +35,11 @@ getopt_loop (int argc, const char **argv,
             int *a_seen, int *b_seen,
             const char **p_value, const char **q_value,
             int *non_options_count, const char **non_options,
-            int *unrecognized)
+            int *unrecognized, bool *message_issued)
 {
   int c;
+  int pos = ftell (stderr);

-  opterr = 0;
   while ((c = getopt (argc, (char **) argv, options)) != -1)
     {
       switch (c)
@@ -61,6 +61,12 @@ getopt_loop (int argc, const char **argv,
          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] == ':'));
+         /* fall through */
        case '?':
          *unrecognized = optopt;
          break;
@@ -69,6 +75,8 @@ getopt_loop (int argc, const char **argv,
          break;
        }
     }
+
+  *message_issued = pos < ftell (stderr);
 }

 static void
@@ -94,6 +102,7 @@ test_getopt (void)
       int non_options_count = 0;
       const char *non_options[10];
       int unrecognized = 0;
+      bool output;
       int argc = 0;
       const char *argv[10];

@@ -103,9 +112,10 @@ test_getopt (void)
       argv[argc++] = "bar";
       argv[argc] = NULL;
       optind = start;
+      opterr = 1;
       getopt_loop (argc, argv, "ab",
                   &a_seen, &b_seen, &p_value, &q_value,
-                  &non_options_count, non_options, &unrecognized);
+                  &non_options_count, non_options, &unrecognized, &output);
       ASSERT (a_seen == 1);
       ASSERT (b_seen == 0);
       ASSERT (p_value == NULL);
@@ -113,6 +123,7 @@ test_getopt (void)
       ASSERT (non_options_count == 0);
       ASSERT (unrecognized == 0);
       ASSERT (optind == 2);
+      ASSERT (!output);
     }
   for (start = OPTIND_MIN; start <= 1; start++)
     {
@@ -123,6 +134,7 @@ test_getopt (void)
       int non_options_count = 0;
       const char *non_options[10];
       int unrecognized = 0;
+      bool output;
       int argc = 0;
       const char *argv[10];

@@ -133,9 +145,10 @@ test_getopt (void)
       argv[argc++] = "bar";
       argv[argc] = NULL;
       optind = start;
+      opterr = 1;
       getopt_loop (argc, argv, "ab",
                   &a_seen, &b_seen, &p_value, &q_value,
-                  &non_options_count, non_options, &unrecognized);
+                  &non_options_count, non_options, &unrecognized, &output);
       ASSERT (a_seen == 1);
       ASSERT (b_seen == 1);
       ASSERT (p_value == NULL);
@@ -143,6 +156,7 @@ test_getopt (void)
       ASSERT (non_options_count == 0);
       ASSERT (unrecognized == 0);
       ASSERT (optind == 3);
+      ASSERT (!output);
     }
   for (start = OPTIND_MIN; start <= 1; start++)
     {
@@ -153,6 +167,7 @@ test_getopt (void)
       int non_options_count = 0;
       const char *non_options[10];
       int unrecognized = 0;
+      bool output;
       int argc = 0;
       const char *argv[10];

@@ -162,9 +177,10 @@ test_getopt (void)
       argv[argc++] = "bar";
       argv[argc] = NULL;
       optind = start;
+      opterr = 1;
       getopt_loop (argc, argv, "ab",
                   &a_seen, &b_seen, &p_value, &q_value,
-                  &non_options_count, non_options, &unrecognized);
+                  &non_options_count, non_options, &unrecognized, &output);
       ASSERT (a_seen == 1);
       ASSERT (b_seen == 1);
       ASSERT (p_value == NULL);
@@ -172,6 +188,7 @@ test_getopt (void)
       ASSERT (non_options_count == 0);
       ASSERT (unrecognized == 0);
       ASSERT (optind == 2);
+      ASSERT (!output);
     }
   for (start = OPTIND_MIN; start <= 1; start++)
     {
@@ -182,6 +199,7 @@ test_getopt (void)
       int non_options_count = 0;
       const char *non_options[10];
       int unrecognized = 0;
+      bool output;
       int argc = 0;
       const char *argv[10];

@@ -192,9 +210,10 @@ test_getopt (void)
       argv[argc++] = "bar";
       argv[argc] = NULL;
       optind = start;
+      opterr = 1;
       getopt_loop (argc, argv, "ab",
                   &a_seen, &b_seen, &p_value, &q_value,
-                  &non_options_count, non_options, &unrecognized);
+                  &non_options_count, non_options, &unrecognized, &output);
       ASSERT (a_seen == 2);
       ASSERT (b_seen == 1);
       ASSERT (p_value == NULL);
@@ -202,6 +221,7 @@ test_getopt (void)
       ASSERT (non_options_count == 0);
       ASSERT (unrecognized == 0);
       ASSERT (optind == 3);
+      ASSERT (!output);
     }

   /* Test processing of options with arguments.  */
@@ -214,6 +234,7 @@ test_getopt (void)
       int non_options_count = 0;
       const char *non_options[10];
       int unrecognized = 0;
+      bool output;
       int argc = 0;
       const char *argv[10];

@@ -222,9 +243,10 @@ test_getopt (void)
       argv[argc++] = "bar";
       argv[argc] = NULL;
       optind = start;
+      opterr = 1;
       getopt_loop (argc, argv, "p:q:",
                   &a_seen, &b_seen, &p_value, &q_value,
-                  &non_options_count, non_options, &unrecognized);
+                  &non_options_count, non_options, &unrecognized, &output);
       ASSERT (a_seen == 0);
       ASSERT (b_seen == 0);
       ASSERT (p_value != NULL && strcmp (p_value, "foo") == 0);
@@ -232,6 +254,7 @@ test_getopt (void)
       ASSERT (non_options_count == 0);
       ASSERT (unrecognized == 0);
       ASSERT (optind == 2);
+      ASSERT (!output);
     }
   for (start = OPTIND_MIN; start <= 1; start++)
     {
@@ -242,6 +265,7 @@ test_getopt (void)
       int non_options_count = 0;
       const char *non_options[10];
       int unrecognized = 0;
+      bool output;
       int argc = 0;
       const char *argv[10];

@@ -251,9 +275,10 @@ test_getopt (void)
       argv[argc++] = "bar";
       argv[argc] = NULL;
       optind = start;
+      opterr = 1;
       getopt_loop (argc, argv, "p:q:",
                   &a_seen, &b_seen, &p_value, &q_value,
-                  &non_options_count, non_options, &unrecognized);
+                  &non_options_count, non_options, &unrecognized, &output);
       ASSERT (a_seen == 0);
       ASSERT (b_seen == 0);
       ASSERT (p_value != NULL && strcmp (p_value, "foo") == 0);
@@ -261,6 +286,7 @@ test_getopt (void)
       ASSERT (non_options_count == 0);
       ASSERT (unrecognized == 0);
       ASSERT (optind == 3);
+      ASSERT (!output);
     }
   for (start = OPTIND_MIN; start <= 1; start++)
     {
@@ -271,6 +297,7 @@ test_getopt (void)
       int non_options_count = 0;
       const char *non_options[10];
       int unrecognized = 0;
+      bool output;
       int argc = 0;
       const char *argv[10];

@@ -282,9 +309,10 @@ test_getopt (void)
       argv[argc++] = "bar";
       argv[argc] = NULL;
       optind = start;
+      opterr = 1;
       getopt_loop (argc, argv, "abp:q:",
                   &a_seen, &b_seen, &p_value, &q_value,
-                  &non_options_count, non_options, &unrecognized);
+                  &non_options_count, non_options, &unrecognized, &output);
       ASSERT (a_seen == 1);
       ASSERT (b_seen == 1);
       ASSERT (p_value != NULL && strcmp (p_value, "foo") == 0);
@@ -292,6 +320,7 @@ test_getopt (void)
       ASSERT (non_options_count == 0);
       ASSERT (unrecognized == 0);
       ASSERT (optind == 5);
+      ASSERT (!output);
     }

 #if GNULIB_GETOPT_GNU
@@ -305,6 +334,7 @@ test_getopt (void)
       int non_options_count = 0;
       const char *non_options[10];
       int unrecognized = 0;
+      bool output;
       int argc = 0;
       const char *argv[10];

@@ -313,9 +343,10 @@ test_getopt (void)
       argv[argc++] = "bar";
       argv[argc] = NULL;
       optind = start;
+      opterr = 1;
       getopt_loop (argc, argv, "p::q::",
                   &a_seen, &b_seen, &p_value, &q_value,
-                  &non_options_count, non_options, &unrecognized);
+                  &non_options_count, non_options, &unrecognized, &output);
       ASSERT (a_seen == 0);
       ASSERT (b_seen == 0);
       ASSERT (p_value != NULL && strcmp (p_value, "foo") == 0);
@@ -323,6 +354,7 @@ test_getopt (void)
       ASSERT (non_options_count == 0);
       ASSERT (unrecognized == 0);
       ASSERT (optind == 2);
+      ASSERT (!output);
     }
   for (start = OPTIND_MIN; start <= 1; start++)
     {
@@ -333,6 +365,7 @@ test_getopt (void)
       int non_options_count = 0;
       const char *non_options[10];
       int unrecognized = 0;
+      bool output;
       int argc = 0;
       const char *argv[10];

@@ -342,9 +375,10 @@ test_getopt (void)
       argv[argc++] = "bar";
       argv[argc] = NULL;
       optind = start;
+      opterr = 1;
       getopt_loop (argc, argv, "p::q::",
                   &a_seen, &b_seen, &p_value, &q_value,
-                  &non_options_count, non_options, &unrecognized);
+                  &non_options_count, non_options, &unrecognized, &output);
       ASSERT (a_seen == 0);
       ASSERT (b_seen == 0);
       ASSERT (p_value == NULL);
@@ -352,6 +386,7 @@ test_getopt (void)
       ASSERT (non_options_count == 0);
       ASSERT (unrecognized == 0);
       ASSERT (optind == 2);
+      ASSERT (!output);
     }
   for (start = OPTIND_MIN; start <= 1; start++)
     {
@@ -362,6 +397,7 @@ test_getopt (void)
       int non_options_count = 0;
       const char *non_options[10];
       int unrecognized = 0;
+      bool output;
       int argc = 0;
       const char *argv[10];

@@ -371,9 +407,10 @@ test_getopt (void)
       argv[argc++] = "bar";
       argv[argc] = NULL;
       optind = start;
+      opterr = 1;
       getopt_loop (argc, argv, "abp::q::",
                   &a_seen, &b_seen, &p_value, &q_value,
-                  &non_options_count, non_options, &unrecognized);
+                  &non_options_count, non_options, &unrecognized, &output);
       ASSERT (a_seen == 1);
       ASSERT (b_seen == 0);
       ASSERT (p_value == NULL);
@@ -381,10 +418,46 @@ test_getopt (void)
       ASSERT (non_options_count == 0);
       ASSERT (unrecognized == 0);
       ASSERT (optind == 3);
+      ASSERT (!output);
     }
 #endif

-  /* Check that invalid options are recognized.  */
+  /* Check that invalid options are recognized; and that both opterr
+     and leading ':' can silence output.  */
+  for (start = OPTIND_MIN; start <= 1; start++)
+    {
+      int a_seen = 0;
+      int b_seen = 0;
+      const char *p_value = NULL;
+      const char *q_value = NULL;
+      int non_options_count = 0;
+      const char *non_options[10];
+      int unrecognized = 0;
+      bool output;
+      int argc = 0;
+      const char *argv[10];
+
+      argv[argc++] = "program";
+      argv[argc++] = "-p";
+      argv[argc++] = "foo";
+      argv[argc++] = "-x";
+      argv[argc++] = "-a";
+      argv[argc++] = "bar";
+      argv[argc] = NULL;
+      optind = start;
+      opterr = 42;
+      getopt_loop (argc, argv, "abp:q:",
+                  &a_seen, &b_seen, &p_value, &q_value,
+                  &non_options_count, non_options, &unrecognized, &output);
+      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);
+      ASSERT (output);
+    }
   for (start = OPTIND_MIN; start <= 1; start++)
     {
       int a_seen = 0;
@@ -394,6 +467,7 @@ test_getopt (void)
       int non_options_count = 0;
       const char *non_options[10];
       int unrecognized = 0;
+      bool output;
       int argc = 0;
       const char *argv[10];

@@ -405,9 +479,44 @@ test_getopt (void)
       argv[argc++] = "bar";
       argv[argc] = NULL;
       optind = start;
+      opterr = 0;
       getopt_loop (argc, argv, "abp:q:",
                   &a_seen, &b_seen, &p_value, &q_value,
-                  &non_options_count, non_options, &unrecognized);
+                  &non_options_count, non_options, &unrecognized, &output);
+      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);
+      ASSERT (!output);
+    }
+  for (start = OPTIND_MIN; start <= 1; start++)
+    {
+      int a_seen = 0;
+      int b_seen = 0;
+      const char *p_value = NULL;
+      const char *q_value = NULL;
+      int non_options_count = 0;
+      const char *non_options[10];
+      int unrecognized = 0;
+      bool output;
+      int argc = 0;
+      const char *argv[10];
+
+      argv[argc++] = "program";
+      argv[argc++] = "-p";
+      argv[argc++] = "foo";
+      argv[argc++] = "-x";
+      argv[argc++] = "-a";
+      argv[argc++] = "bar";
+      argv[argc] = NULL;
+      optind = start;
+      opterr = 1;
+      getopt_loop (argc, argv, ":abp:q:",
+                  &a_seen, &b_seen, &p_value, &q_value,
+                  &non_options_count, non_options, &unrecognized, &output);
       ASSERT (a_seen == 1);
       ASSERT (b_seen == 0);
       ASSERT (p_value != NULL && strcmp (p_value, "foo") == 0);
@@ -415,6 +524,201 @@ test_getopt (void)
       ASSERT (non_options_count == 0);
       ASSERT (unrecognized == 'x');
       ASSERT (optind == 5);
+      ASSERT (!output);
+    }
+  for (start = OPTIND_MIN; start <= 1; start++)
+    {
+      int a_seen = 0;
+      int b_seen = 0;
+      const char *p_value = NULL;
+      const char *q_value = NULL;
+      int non_options_count = 0;
+      const char *non_options[10];
+      int unrecognized = 0;
+      bool output;
+      int argc = 0;
+      const char *argv[10];
+
+      argv[argc++] = "program";
+      argv[argc++] = "-p";
+      argv[argc++] = "foo";
+      argv[argc++] = "-:";
+      argv[argc++] = "-a";
+      argv[argc++] = "bar";
+      argv[argc] = NULL;
+      optind = start;
+      opterr = 42;
+      getopt_loop (argc, argv, "abp:q:",
+                  &a_seen, &b_seen, &p_value, &q_value,
+                  &non_options_count, non_options, &unrecognized, &output);
+      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);
+      ASSERT (output);
+    }
+  for (start = OPTIND_MIN; start <= 1; start++)
+    {
+      int a_seen = 0;
+      int b_seen = 0;
+      const char *p_value = NULL;
+      const char *q_value = NULL;
+      int non_options_count = 0;
+      const char *non_options[10];
+      int unrecognized = 0;
+      bool output;
+      int argc = 0;
+      const char *argv[10];
+
+      argv[argc++] = "program";
+      argv[argc++] = "-p";
+      argv[argc++] = "foo";
+      argv[argc++] = "-:";
+      argv[argc++] = "-a";
+      argv[argc++] = "bar";
+      argv[argc] = NULL;
+      optind = start;
+      opterr = 0;
+      getopt_loop (argc, argv, "abp:q:",
+                  &a_seen, &b_seen, &p_value, &q_value,
+                  &non_options_count, non_options, &unrecognized, &output);
+      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);
+      ASSERT (!output);
+    }
+  for (start = OPTIND_MIN; start <= 1; start++)
+    {
+      int a_seen = 0;
+      int b_seen = 0;
+      const char *p_value = NULL;
+      const char *q_value = NULL;
+      int non_options_count = 0;
+      const char *non_options[10];
+      int unrecognized = 0;
+      bool output;
+      int argc = 0;
+      const char *argv[10];
+
+      argv[argc++] = "program";
+      argv[argc++] = "-p";
+      argv[argc++] = "foo";
+      argv[argc++] = "-:";
+      argv[argc++] = "-a";
+      argv[argc++] = "bar";
+      argv[argc] = NULL;
+      optind = start;
+      opterr = 1;
+      getopt_loop (argc, argv, ":abp:q:",
+                  &a_seen, &b_seen, &p_value, &q_value,
+                  &non_options_count, non_options, &unrecognized, &output);
+      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);
+      ASSERT (!output);
+    }
+
+  /* Check for missing argument behavior.  */
+  for (start = OPTIND_MIN; start <= 1; start++)
+    {
+      int a_seen = 0;
+      int b_seen = 0;
+      const char *p_value = NULL;
+      const char *q_value = NULL;
+      int non_options_count = 0;
+      const char *non_options[10];
+      int unrecognized = 0;
+      bool output;
+      int argc = 0;
+      const char *argv[10];
+
+      argv[argc++] = "program";
+      argv[argc++] = "-ap";
+      argv[argc] = NULL;
+      optind = start;
+      opterr = 1;
+      getopt_loop (argc, argv, "abp:q:",
+                  &a_seen, &b_seen, &p_value, &q_value,
+                  &non_options_count, non_options, &unrecognized, &output);
+      ASSERT (a_seen == 1);
+      ASSERT (b_seen == 0);
+      ASSERT (p_value == NULL);
+      ASSERT (q_value == NULL);
+      ASSERT (non_options_count == 0);
+      ASSERT (unrecognized == 'p');
+      ASSERT (optind == 2);
+      ASSERT (output);
+    }
+  for (start = OPTIND_MIN; start <= 1; start++)
+    {
+      int a_seen = 0;
+      int b_seen = 0;
+      const char *p_value = NULL;
+      const char *q_value = NULL;
+      int non_options_count = 0;
+      const char *non_options[10];
+      int unrecognized = 0;
+      bool output;
+      int argc = 0;
+      const char *argv[10];
+
+      argv[argc++] = "program";
+      argv[argc++] = "-ap";
+      argv[argc] = NULL;
+      optind = start;
+      opterr = 0;
+      getopt_loop (argc, argv, "abp:q:",
+                  &a_seen, &b_seen, &p_value, &q_value,
+                  &non_options_count, non_options, &unrecognized, &output);
+      ASSERT (a_seen == 1);
+      ASSERT (b_seen == 0);
+      ASSERT (p_value == NULL);
+      ASSERT (q_value == NULL);
+      ASSERT (non_options_count == 0);
+      ASSERT (unrecognized == 'p');
+      ASSERT (optind == 2);
+      ASSERT (!output);
+    }
+  for (start = OPTIND_MIN; start <= 1; start++)
+    {
+      int a_seen = 0;
+      int b_seen = 0;
+      const char *p_value = NULL;
+      const char *q_value = NULL;
+      int non_options_count = 0;
+      const char *non_options[10];
+      int unrecognized = 0;
+      bool output;
+      int argc = 0;
+      const char *argv[10];
+
+      argv[argc++] = "program";
+      argv[argc++] = "-ap";
+      argv[argc] = NULL;
+      optind = start;
+      opterr = 1;
+      getopt_loop (argc, argv, ":abp:q:",
+                  &a_seen, &b_seen, &p_value, &q_value,
+                  &non_options_count, non_options, &unrecognized, &output);
+      ASSERT (a_seen == 1);
+      ASSERT (b_seen == 0);
+      ASSERT (p_value == NULL);
+      ASSERT (q_value == NULL);
+      ASSERT (non_options_count == 0);
+      ASSERT (unrecognized == 'p');
+      ASSERT (optind == 2);
+      ASSERT (!output);
     }

   /* Check that by default, non-options arguments are moved to the end.  */
@@ -427,6 +731,7 @@ test_getopt (void)
       int non_options_count = 0;
       const char *non_options[10];
       int unrecognized = 0;
+      bool output;
       int argc = 0;
       const char *argv[10];

@@ -439,9 +744,10 @@ test_getopt (void)
       argv[argc++] = "bar";
       argv[argc] = NULL;
       optind = start;
+      opterr = 1;
       getopt_loop (argc, argv, "abp:q:",
                   &a_seen, &b_seen, &p_value, &q_value,
-                  &non_options_count, non_options, &unrecognized);
+                  &non_options_count, non_options, &unrecognized, &output);
       if (posixly)
         {
           ASSERT (strcmp (argv[0], "program") == 0);
@@ -459,6 +765,7 @@ test_getopt (void)
           ASSERT (non_options_count == 0);
           ASSERT (unrecognized == 0);
           ASSERT (optind == 1);
+          ASSERT (!output);
         }
       else
         {
@@ -477,6 +784,7 @@ test_getopt (void)
           ASSERT (non_options_count == 0);
           ASSERT (unrecognized == 0);
           ASSERT (optind == 4);
+          ASSERT (!output);
         }
     }

@@ -490,6 +798,7 @@ test_getopt (void)
       int non_options_count = 0;
       const char *non_options[10];
       int unrecognized = 0;
+      bool output;
       int argc = 0;
       const char *argv[20];

@@ -507,9 +816,10 @@ test_getopt (void)
       argv[argc++] = "bar";
       argv[argc] = NULL;
       optind = start;
+      opterr = 1;
       getopt_loop (argc, argv, "abp:q:",
                   &a_seen, &b_seen, &p_value, &q_value,
-                  &non_options_count, non_options, &unrecognized);
+                  &non_options_count, non_options, &unrecognized, &output);
       if (posixly)
         {
           ASSERT (strcmp (argv[0], "program") == 0);
@@ -532,6 +842,7 @@ test_getopt (void)
           ASSERT (non_options_count == 0);
           ASSERT (unrecognized == 0);
           ASSERT (optind == 1);
+          ASSERT (!output);
         }
       else
         {
@@ -555,6 +866,7 @@ test_getopt (void)
           ASSERT (non_options_count == 0);
           ASSERT (unrecognized == 0);
           ASSERT (optind == 5);
+          ASSERT (!output);
         }
     }

@@ -569,6 +881,7 @@ test_getopt (void)
       int non_options_count = 0;
       const char *non_options[10];
       int unrecognized = 0;
+      bool output;
       int argc = 0;
       const char *argv[10];

@@ -581,9 +894,10 @@ test_getopt (void)
       argv[argc++] = "bar";
       argv[argc] = NULL;
       optind = start;
+      opterr = 1;
       getopt_loop (argc, argv, "-abp:q:",
                   &a_seen, &b_seen, &p_value, &q_value,
-                  &non_options_count, non_options, &unrecognized);
+                  &non_options_count, non_options, &unrecognized, &output);
       ASSERT (strcmp (argv[0], "program") == 0);
       ASSERT (strcmp (argv[1], "donald") == 0);
       ASSERT (strcmp (argv[2], "-p") == 0);
@@ -602,6 +916,7 @@ test_getopt (void)
       ASSERT (strcmp (non_options[2], "bar") == 0);
       ASSERT (unrecognized == 0);
       ASSERT (optind == 7);
+      ASSERT (!output);
     }

   /* Check that '--' ends the argument processing.  */
@@ -614,6 +929,7 @@ test_getopt (void)
       int non_options_count = 0;
       const char *non_options[10];
       int unrecognized = 0;
+      bool output;
       int argc = 0;
       const char *argv[20];

@@ -631,9 +947,10 @@ test_getopt (void)
       argv[argc++] = "bar";
       argv[argc] = NULL;
       optind = start;
+      opterr = 1;
       getopt_loop (argc, argv, "-abp:q:",
                   &a_seen, &b_seen, &p_value, &q_value,
-                  &non_options_count, non_options, &unrecognized);
+                  &non_options_count, non_options, &unrecognized, &output);
       ASSERT (strcmp (argv[0], "program") == 0);
       ASSERT (strcmp (argv[1], "donald") == 0);
       ASSERT (strcmp (argv[2], "-p") == 0);
@@ -651,6 +968,7 @@ test_getopt (void)
       ASSERT (b_seen == 0);
       ASSERT (p_value != NULL && strcmp (p_value, "billy") == 0);
       ASSERT (q_value == NULL);
+      ASSERT (!output);
       if (non_options_count == 2)
         {
          /* glibc behaviour.  */
@@ -687,6 +1005,7 @@ test_getopt (void)
       int non_options_count = 0;
       const char *non_options[10];
       int unrecognized = 0;
+      bool output;
       int argc = 0;
       const char *argv[10];

@@ -699,9 +1018,10 @@ test_getopt (void)
       argv[argc++] = "bar";
       argv[argc] = NULL;
       optind = start;
+      opterr = 1;
       getopt_loop (argc, argv, "abp:q:-",
                   &a_seen, &b_seen, &p_value, &q_value,
-                  &non_options_count, non_options, &unrecognized);
+                  &non_options_count, non_options, &unrecognized, &output);
       if (posixly)
         {
           ASSERT (strcmp (argv[0], "program") == 0);
@@ -719,6 +1039,7 @@ test_getopt (void)
           ASSERT (non_options_count == 0);
           ASSERT (unrecognized == 0);
           ASSERT (optind == 1);
+          ASSERT (!output);
         }
       else
         {
@@ -737,6 +1058,7 @@ test_getopt (void)
           ASSERT (non_options_count == 0);
           ASSERT (unrecognized == 0);
           ASSERT (optind == 4);
+          ASSERT (!output);
         }
     }

@@ -751,6 +1073,7 @@ test_getopt (void)
       int non_options_count = 0;
       const char *non_options[10];
       int unrecognized = 0;
+      bool output;
       int argc = 0;
       const char *argv[10];

@@ -763,9 +1086,10 @@ test_getopt (void)
       argv[argc++] = "bar";
       argv[argc] = NULL;
       optind = start;
+      opterr = 1;
       getopt_loop (argc, argv, "+abp:q:",
                   &a_seen, &b_seen, &p_value, &q_value,
-                  &non_options_count, non_options, &unrecognized);
+                  &non_options_count, non_options, &unrecognized, &output);
       ASSERT (strcmp (argv[0], "program") == 0);
       ASSERT (strcmp (argv[1], "donald") == 0);
       ASSERT (strcmp (argv[2], "-p") == 0);
@@ -781,6 +1105,7 @@ test_getopt (void)
       ASSERT (non_options_count == 0);
       ASSERT (unrecognized == 0);
       ASSERT (optind == 1);
+      ASSERT (!output);
     }
   for (start = OPTIND_MIN; start <= 1; start++)
     {
@@ -791,6 +1116,7 @@ test_getopt (void)
       int non_options_count = 0;
       const char *non_options[10];
       int unrecognized = 0;
+      bool output;
       int argc = 0;
       const char *argv[10];

@@ -798,9 +1124,13 @@ test_getopt (void)
       argv[argc++] = "-+";
       argv[argc] = NULL;
       optind = start;
+      /* Suppress output, since glibc is inconsistent on whether this
+         prints a message:
+         http://sources.redhat.com/bugzilla/show_bug.cgi?id=11039 */
+      opterr = 0;
       getopt_loop (argc, argv, "+abp:q:",
                   &a_seen, &b_seen, &p_value, &q_value,
-                  &non_options_count, non_options, &unrecognized);
+                  &non_options_count, non_options, &unrecognized, &output);
       ASSERT (a_seen == 0);
       ASSERT (b_seen == 0);
       ASSERT (p_value == NULL);
@@ -808,6 +1138,7 @@ test_getopt (void)
       ASSERT (non_options_count == 0);
       ASSERT (unrecognized == '+');
       ASSERT (optind == 2);
+      ASSERT (!output);
     }

   /* Check that '--' ends the argument processing.  */
@@ -820,6 +1151,7 @@ test_getopt (void)
       int non_options_count = 0;
       const char *non_options[10];
       int unrecognized = 0;
+      bool output;
       int argc = 0;
       const char *argv[20];

@@ -837,9 +1169,10 @@ test_getopt (void)
       argv[argc++] = "bar";
       argv[argc] = NULL;
       optind = start;
+      opterr = 1;
       getopt_loop (argc, argv, "+abp:q:",
                   &a_seen, &b_seen, &p_value, &q_value,
-                  &non_options_count, non_options, &unrecognized);
+                  &non_options_count, non_options, &unrecognized, &output);
       ASSERT (strcmp (argv[0], "program") == 0);
       ASSERT (strcmp (argv[1], "donald") == 0);
       ASSERT (strcmp (argv[2], "-p") == 0);
@@ -860,6 +1193,7 @@ test_getopt (void)
       ASSERT (non_options_count == 0);
       ASSERT (unrecognized == 0);
       ASSERT (optind = 1);
+      ASSERT (!output);
     }

   /* Check that the '+' flag has to come first.  */
@@ -872,6 +1206,7 @@ test_getopt (void)
       int non_options_count = 0;
       const char *non_options[10];
       int unrecognized = 0;
+      bool output;
       int argc = 0;
       const char *argv[10];

@@ -884,9 +1219,10 @@ test_getopt (void)
       argv[argc++] = "bar";
       argv[argc] = NULL;
       optind = start;
+      opterr = 1;
       getopt_loop (argc, argv, "abp:q:+",
                   &a_seen, &b_seen, &p_value, &q_value,
-                  &non_options_count, non_options, &unrecognized);
+                  &non_options_count, non_options, &unrecognized, &output);
       if (posixly)
         {
           ASSERT (strcmp (argv[0], "program") == 0);
@@ -904,6 +1240,7 @@ test_getopt (void)
           ASSERT (non_options_count == 0);
           ASSERT (unrecognized == 0);
           ASSERT (optind == 1);
+          ASSERT (!output);
         }
       else
         {
@@ -922,6 +1259,10 @@ test_getopt (void)
           ASSERT (non_options_count == 0);
           ASSERT (unrecognized == 0);
           ASSERT (optind == 4);
+          ASSERT (!output);
         }
     }
+
+  /* No tests of "-:..." or "+:...", due to glibc bug:
+     http://sources.redhat.com/bugzilla/show_bug.cgi?id=11039 */
 }
-- 
1.6.4.2







reply via email to

[Prev in Thread] Current Thread [Next in Thread]