diff --git a/ChangeLog b/ChangeLog index 19c3ad5ffe..f1ccfdc00d 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,5 +1,16 @@ 2010-04-13 Eric Blake + 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 ';'. diff --git a/doc/glibc-functions/getopt_long.texi b/doc/glibc-functions/getopt_long.texi index 3cf17f0252..e014a3cb50 100644 --- a/doc/glibc-functions/getopt_long.texi +++ b/doc/glibc-functions/getopt_long.texi @@ -7,6 +7,14 @@ Gnulib module: getopt-gnu 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. @@ -33,11 +41,4 @@ AIX 5.1, HP-UX 11, IRIX 6.5, OSF/1 5.1, Interix 3.5. 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 diff --git a/doc/glibc-functions/getopt_long_only.texi b/doc/glibc-functions/getopt_long_only.texi index edc832be27..309c5b6b70 100644 --- a/doc/glibc-functions/getopt_long_only.texi +++ b/doc/glibc-functions/getopt_long_only.texi @@ -7,6 +7,14 @@ Gnulib module: getopt-gnu 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. @@ -35,14 +43,10 @@ OSF/1 5.1, mingw, Interix 3.5. 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 diff --git a/doc/posix-functions/getopt.texi b/doc/posix-functions/getopt.texi index d4d03f0c27..50640a8509 100644 --- a/doc/posix-functions/getopt.texi +++ b/doc/posix-functions/getopt.texi @@ -29,6 +29,10 @@ The function @code{getopt} does not support the @samp{+} flag in the options 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. @@ -58,10 +62,9 @@ testsuite. @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 diff --git a/m4/getopt.m4 b/m4/getopt.m4 index 2cd74992cb..5b211e5678 100644 --- a/m4/getopt.m4 +++ b/m4/getopt.m4 @@ -1,4 +1,4 @@ -# 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, @@ -94,6 +94,10 @@ AC_DEFUN([gl_GETOPT_CHECK_HEADERS], 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], @@ -254,6 +258,15 @@ main () 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], diff --git a/tests/test-getopt.h b/tests/test-getopt.h index 96db7a5f8b..8baa39d025 100644 --- a/tests/test-getopt.h +++ b/tests/test-getopt.h @@ -420,7 +420,7 @@ test_getopt (void) 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. */ @@ -993,7 +993,6 @@ test_getopt (void) ASSERT (optind == 12); } } -#endif /* Check that the '-' flag has to come first. */ for (start = OPTIND_MIN; start <= 1; start++) @@ -1124,10 +1123,6 @@ 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, &output); @@ -1138,7 +1133,7 @@ test_getopt (void) ASSERT (non_options_count == 0); ASSERT (unrecognized == '+'); ASSERT (optind == 2); - ASSERT (!output); + ASSERT (output); } /* Check that '--' ends the argument processing. */ @@ -1195,6 +1190,7 @@ test_getopt (void) 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++) @@ -1263,6 +1259,111 @@ test_getopt (void) } } - /* 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 */ } diff --git a/tests/test-getopt_long.h b/tests/test-getopt_long.h index 5f103c8e41..0e58fecbb8 100644 --- a/tests/test-getopt_long.h +++ b/tests/test-getopt_long.h @@ -1151,8 +1151,7 @@ test_getopt_long (void) &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); @@ -2079,8 +2078,11 @@ test_getopt_long_only (void) 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); } { @@ -2096,8 +2098,11 @@ test_getopt_long_only (void) 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); }