2010-04-13 Eric Blake <eblake@redhat.com>
+ getopt-gnu: match recent glibc fixes and posix ruling
+ * tests/test-getopt.h (test_getopt): Strengthen tests of leading
+ '+' handling, when requesting extensions.
+ * tests/test-getopt_long.h (test_getopt_long): Strengthen test of
+ 'W;' handling.
+ * m4/getopt.m4 (gl_GETOPT_CHECK_HEADERS): Detect glibc 2.11 bug.
+ * doc/posix-functions/getopt.texi (getopt): Document this.
+ * doc/glibc-functions/getopt_long.texi (getopt_long): Likewise.
+ * doc/glibc-functions/getopt_long_only.texi (getopt_long_only):
+ Likewise.
+
getopt: merge bug fixes from glibc
* lib/getopt.c (_getopt_internal_r): Use correct message for 'W;'
diagnostics. Honor '+:' correctly. Reject ';'.
Portability problems fixed by Gnulib:
@itemize
@item
+The function @code{getopt_long} does not obey the combination of
+@samp{+} and @samp{:} flags in the options string on some platforms:
+glibc 2.11.
+@item
+The use of @samp{W;} in the optstring argument to does not always
+allow @code{-W foo} to behave synonymously with @code{--foo}:
+glibc 2.11.
+@item
The function @code{getopt_long} does not support the @samp{+} flag in
the options string on some platforms:
MacOS X 10.5, AIX 5.2, OSF/1 5.1, Solaris 10.
Portability problems not fixed by Gnulib:
@itemize
-@item
-The glibc extension of using @samp{W;} in the optstring argument to
-allow @code{-W foo} to behave synonymously with @code{--foo} is not
-very reliable, even in glibc.
-@item
-Mixing a leading @samp{-} or @samp{+} with a leading @samp{:} in the
-optstring argument has inconsistent effects across platforms.
@end itemize
Portability problems fixed by Gnulib:
@itemize
@item
+The function @code{getopt_long_only} does not obey the combination of
+@samp{+} and @samp{:} flags in the options string on some platforms:
+glibc 2.11.
+@item
+The use of @samp{W;} in the optstring argument to does not always
+allow @code{-W foo} to behave synonymously with @code{--foo}:
+glibc 2.11.
+@item
The function @code{getopt_long_only} does not support the @samp{+}
flag in the options string on some platforms:
MacOS X 10.5, AIX 5.2, OSF/1 5.1, Solaris 10.
Portability problems not fixed by Gnulib:
@itemize
@item
-The glibc extension of using @samp{W;} in the optstring argument to
-allow @code{-W foo} to behave synonymously with @code{--foo} is not
-very reliable.
-@item
Some implementations return success instead of reporting an ambiguity
-if user's option is a prefix of two long options with the same flag:
+if user's option is a prefix of two long options with the same outcome:
FreeBSD.
@item
-Mixing a leading @samp{-} or @samp{+} with a leading @samp{:} in the
-optstring argument has inconsistent effects across platforms.
+The GNU Coding Standards discourage the use of @code{getopt_long_only}
+in new programs.
@end itemize
string on some platforms:
MacOS X 10.5, AIX 5.2, HP-UX 11, IRIX 6.5, OSF/1 5.1, Solaris 10.
@item
+The function @code{getopt} does not obey the combination of @samp{+}
+and @samp{:} flags in the options string on some platforms:
+glibc 2.11.
+@item
The function @code{getopt} does not obey the @samp{-} flag in the options
string when @env{POSIXLY_CORRECT} is set on some platforms:
Cygwin 1.7.0.
@item
The glibc implementation allows a complete reset of the environment,
including re-checking for @env{POSIXLY_CORRECT}, by setting
-@code{optind} to 0. Other implementations provide @code{optreset},
+@code{optind} to 0. Several BSD implementations provide @code{optreset},
causing a reset by setting it non-zero, although it does not
-necessarily re-read @env{POSIXLY_CORRECT}.
-@item
-Mixing a leading @samp{-} or @samp{+} with a leading @samp{:} in the
-optstring argument has inconsistent effects across platforms.
+necessarily re-read @env{POSIXLY_CORRECT}. Solaris @code{getopt} does
+not support either reset method, but does not maintain state that
+needs the extra level of reset.
@end itemize
-# getopt.m4 serial 27
+# getopt.m4 serial 28
dnl Copyright (C) 2002-2006, 2008-2010 Free Software Foundation, Inc.
dnl This file is free software; the Free Software Foundation
dnl gives unlimited permission to copy and/or distribute it,
dnl is left over from earlier calls, and neither setting optind = 0 nor
dnl setting optreset = 1 get rid of this internal state.
dnl POSIX is silent on optind vs. optreset, so we allow either behavior.
+ dnl POSIX 2008 does not specify leading '+' behavior, but see
+ dnl http://austingroupbugs.net/view.php?id=191 for a recommendation on
+ dnl the next version of POSIX. For now, we only guarantee leading '+'
+ dnl behavior with getopt-gnu.
if test -z "$gl_replace_getopt"; then
AC_CACHE_CHECK([whether getopt is POSIX compatible],
[gl_cv_func_getopt_posix],
if (getopt (3, argv, "-p") != 'p')
return 7;
}
+ /* This code fails on glibc 2.11. */
+ {
+ char *argv[] = { "program", "-b", "-a", NULL };
+ optind = opterr = 0;
+ if (getopt (3, argv, "+:a:b") != 'b')
+ return 8;
+ if (getopt (3, argv, "+:a:b") != ':')
+ return 9;
+ }
return 0;
]])],
[gl_cv_func_getopt_gnu=yes],
ASSERT (optind == 3);
ASSERT (!output);
}
-#endif
+#endif /* GNULIB_TEST_GETOPT_GNU */
/* Check that invalid options are recognized; and that both opterr
and leading ':' can silence output. */
ASSERT (optind == 12);
}
}
-#endif
/* Check that the '-' flag has to come first. */
for (start = OPTIND_MIN; start <= 1; start++)
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, &output);
ASSERT (non_options_count == 0);
ASSERT (unrecognized == '+');
ASSERT (optind == 2);
- ASSERT (!output);
+ ASSERT (output);
}
/* Check that '--' ends the argument processing. */
ASSERT (optind = 1);
ASSERT (!output);
}
+#endif /* GNULIB_TEST_GETOPT_GNU */
/* Check that the '+' flag has to come first. */
for (start = OPTIND_MIN; start <= 1; start++)
}
}
- /* No tests of "-:..." or "+:...", due to glibc bug:
- http://sources.redhat.com/bugzilla/show_bug.cgi?id=11039 */
+#if GNULIB_TEST_GETOPT_GNU
+ /* If GNU extensions are supported, require compliance with POSIX
+ interpretation on leading '+' behavior.
+ http://austingroupbugs.net/view.php?id=191 */
+ 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++] = "donald";
+ argv[argc++] = "-p";
+ argv[argc++] = "billy";
+ argv[argc++] = "duck";
+ 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 (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);
+ 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] = NULL;
+ optind = start;
+ getopt_loop (argc, argv, "+:abp:q:",
+ &a_seen, &b_seen, &p_value, &q_value,
+ &non_options_count, non_options, &unrecognized, &output);
+ ASSERT (a_seen == 0);
+ 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++] = "-b";
+ argv[argc++] = "-p";
+ argv[argc] = NULL;
+ optind = start;
+ getopt_loop (argc, argv, "+:abp:q:",
+ &a_seen, &b_seen, &p_value, &q_value,
+ &non_options_count, non_options, &unrecognized, &output);
+ ASSERT (a_seen == 0);
+ ASSERT (b_seen == 1);
+ ASSERT (p_value == NULL);
+ ASSERT (q_value == NULL);
+ ASSERT (non_options_count == 0);
+ ASSERT (unrecognized == 'p');
+ ASSERT (optind == 3);
+ ASSERT (!output);
+ }
+#endif /* GNULIB_TEST_GETOPT_GNU */
}
&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 (p_value == NULL);
ASSERT (q_value == NULL);
ASSERT (non_options_count == 0);
ASSERT (unrecognized == 0);
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); */
+ /* 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);
}
{
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); */
+ /* 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);
}