The POSIX folks admitted that codifying the behavior of GNU getopt on a leading '+' in optstring is worthwhile, for writing programs such as env(1) even when POSIXLY_CORRECT is not defined. http://austingroupbugs.net/view.php?id=191
Although it technically isn't part of POSIX 2008, there are enough existing non-GNU implementations that already comply that it is worth enforcing. * tests/test-getopt.h (test_getopt): Strengthen tests of leading '+' handling. * 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. --- ChangeLog | 11 +++ doc/glibc-functions/getopt_long.texi | 15 ++-- doc/glibc-functions/getopt_long_only.texi | 15 ++-- doc/posix-functions/getopt.texi | 13 ++-- m4/getopt.m4 | 24 ++++++- tests/test-getopt.h | 112 +++++++++++++++++++++++++++-- tests/test-getopt_long.h | 17 +++-- 7 files changed, 173 insertions(+), 34 deletions(-) diff --git a/ChangeLog b/ChangeLog index fea598f..5c72a7e 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,5 +1,16 @@ 2010-04-10 Eric Blake <[email protected]> + getopt: match recent glibc fixes and posix ruling + * tests/test-getopt.h (test_getopt): Strengthen tests of leading + '+' handling. + * 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 0472514..b06797d 100644 --- a/doc/glibc-functions/getopt_long.texi +++ b/doc/glibc-functions/getopt_long.texi @@ -7,6 +7,14 @@ getopt_long 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. @@ -29,11 +37,4 @@ getopt_long 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 13edfb4..73352a1 100644 --- a/doc/glibc-functions/getopt_long_only.texi +++ b/doc/glibc-functions/getopt_long_only.texi @@ -7,6 +7,14 @@ getopt_long_only 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. @@ -31,14 +39,7 @@ getopt_long_only 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: FreeBSD. -...@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/posix-functions/getopt.texi b/doc/posix-functions/getopt.texi index 822dbb3..b55fd26 100644 --- a/doc/posix-functions/getopt.texi +++ b/doc/posix-functions/getopt.texi @@ -13,6 +13,10 @@ getopt Portability problems fixed by either Gnulib module @code{getopt-posix} or @code{getopt-gnu}: @itemize @item +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 @code{getopt} function keeps some internal state that cannot be explicitly reset on some platforms: mingw. @@ -21,9 +25,9 @@ getopt Portability problems fixed by Gnulib module @code{getopt-gnu}: @itemize @item -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. +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: @@ -57,7 +61,4 @@ getopt @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}. -...@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/m4/getopt.m4 b/m4/getopt.m4 index a19805e..8449034 100644 --- a/m4/getopt.m4 +++ b/m4/getopt.m4 @@ -1,4 +1,4 @@ -# getopt.m4 serial 24 +# getopt.m4 serial 25 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, @@ -88,6 +88,8 @@ AC_DEFUN([gl_GETOPT_CHECK_HEADERS], dnl strings starts with '+' and it's not the first call. Some internal state dnl is left over from earlier calls, and neither setting optind = 0 nor dnl setting optreset = 1 get rid of this internal state. + dnl glibc 2.11 mishandles optstring starting with "+:", in conflict with + dnl the POSIX recommendation http://austingroupbugs.net/view.php?id=191. dnl POSIX is silent on optind vs. optreset, so we allow either behavior. if test -z "$gl_replace_getopt"; then AC_CACHE_CHECK([whether getopt is POSIX compatible], @@ -167,6 +169,26 @@ main () if (!(optind == 1)) return 12; } + /* Detect glibc 2.11 bug. */ + { + int argc = 0; + char *argv[4]; + int c; + + argv[argc++] = "program"; + argv[argc++] = "-b"; + argv[argc++] = "-a"; + argv[argc] = NULL; + optind = OPTIND_MIN; + opterr = 0; + + c = getopt (argc, argv, "+:a:b"); + if (!(c == 'b')) + return 13; + c = getopt (argc, argv, "+:a:b"); + if (!(c == ':')) + return 14; + } return 0; } diff --git a/tests/test-getopt.h b/tests/test-getopt.h index 96db7a5..7bfef0b 100644 --- a/tests/test-getopt.h +++ b/tests/test-getopt.h @@ -1124,10 +1124,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 +1134,7 @@ test_getopt (void) ASSERT (non_options_count == 0); ASSERT (unrecognized == '+'); ASSERT (optind == 2); - ASSERT (!output); + ASSERT (output); } /* Check that '--' ends the argument processing. */ @@ -1263,6 +1259,108 @@ test_getopt (void) } } - /* No tests of "-:..." or "+:...", due to glibc bug: - http://sources.redhat.com/bugzilla/show_bug.cgi?id=11039 */ + /* Require compliance with POSIX interpretation that leading '+' + must be supported. 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); + } } diff --git a/tests/test-getopt_long.h b/tests/test-getopt_long.h index 5f103c8..0e58fec 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); } -- 1.6.6.1
