There is quite some confusion in the getopt module about which functionality requires which replacement. For example, while mingw fails to provide a working POSIX compliant getopt() function, systems like MacOS X, Cygwin, HP-UX, Solaris provide this but fail in the GNU compatibility section. So a package which only needs a POSIX compliant one compiles the GNU one on most Unix platforms without need.
The situation is made even more confusing through POSIX incompatibilities in the glibc implementation of getopt(). Additionally, the POSIX compliance bugs on mingw come from a wrongly implemented attempt at imitating glibc's incompatibilities... To make all this clearer, I'm introducing 'getopt-posix' and 'getopt-gnu' modules, in the same way as we did for 'fnmatch'. Ultimately 'getopt-posix' should be renamed to 'getopt', but this cannot be done after a sufficiently long time of deprecation of the 'getopt' module. 2009-08-12 Bruno Haible <br...@clisp.org> New modules 'getopt-posix', 'getopt-gnu'. * modules/getopt-gnu: New file, renamed from modules/getopt. * modules/getopt-posix: New file. * modules/getopt: Turn into an obsolete alias for getopt-gnu. * m4/getopt.m4 (gl_FUNC_GETOPT_POSIX, gl_FUNC_GETOPT_GNU): New macros. (gl_GETOPT): Remove macro. (gl_GETOPT_CHECK_HEADERS): Do some checks only for gl_FUNC_GETOPT_GNU. Disable the test against BSD systems that declare optreset. Test against mingw bug. Test against lack of support of optional arguments on many platforms. * doc/glibc-headers/getopt.texi: Update module name and list of relevant platforms. * doc/posix-functions/getopt.texi: Mention modules 'getopt-posix' and 'getopt-gnu' and more portability problems. * NEWS: Mention the changes. *** NEWS.orig 2009-08-13 01:13:48.000000000 +0200 --- NEWS 2009-08-13 01:10:36.000000000 +0200 *************** *** 6,11 **** --- 6,16 ---- Date Modules Changes + 2009-08-12 getopt This module is deprecated. Please choose among + getopt-posix and getopt-gnu. getopt-gnu provides + "long options" and "options with optional + arguments", getopt-posix doesn't. + 2009-06-25 fpurge The include file is changed from "fpurge.h" to <stdio.h>. *** /dev/null 2008-06-06 22:36:48.000000000 +0200 --- modules/getopt-posix 2009-08-12 21:13:05.000000000 +0200 *************** *** 0 **** --- 1,38 ---- + Description: + getopt() function: process command line arguments. + + Files: + lib/getopt.in.h + lib/getopt.c + lib/getopt1.c + lib/getopt_int.h + m4/getopt.m4 + + Depends-on: + gettext-h + unistd + extensions + + configure.ac: + gl_FUNC_GETOPT_POSIX + + Makefile.am: + BUILT_SOURCES += $(GETOPT_H) + + # We need the following in order to create <getopt.h> when the system + # doesn't have one that works with the given compiler. + getopt.h: getopt.in.h + { echo '/* DO NOT EDIT! GENERATED AUTOMATICALLY! */'; \ + cat $(srcdir)/getopt.in.h; \ + } > $...@-t + mv -f $...@-t $@ + MOSTLYCLEANFILES += getopt.h getopt.h-t + + Include: + <unistd.h> + + License: + LGPL + + Maintainer: + all, glibc *** /dev/null 2008-06-06 22:36:48.000000000 +0200 --- modules/getopt-gnu 2009-08-12 18:52:15.000000000 +0200 *************** *** 0 **** --- 1,23 ---- + Description: + GNU-like getopt() function, getopt_long() function, getopt_long_only() function: + process command line arguments. + + Files: + + Depends-on: + getopt-posix + + configure.ac: + gl_FUNC_GETOPT_GNU + gl_MODULE_INDICATOR([getopt-gnu]) + + Makefile.am: + + Include: + <getopt.h> + + License: + LGPL + + Maintainer: + all, glibc *** modules/getopt.orig 2009-08-13 01:13:48.000000000 +0200 --- modules/getopt 2009-08-13 01:11:24.000000000 +0200 *************** *** 1,32 **** Description: Process command line arguments. Files: - lib/getopt.in.h - lib/getopt.c - lib/getopt1.c - lib/getopt_int.h - m4/getopt.m4 Depends-on: ! gettext-h ! unistd ! extensions configure.ac: - gl_GETOPT Makefile.am: - BUILT_SOURCES += $(GETOPT_H) - - # We need the following in order to create <getopt.h> when the system - # doesn't have one that works with the given compiler. - getopt.h: getopt.in.h - { echo '/* DO NOT EDIT! GENERATED AUTOMATICALLY! */'; \ - cat $(srcdir)/getopt.in.h; \ - } > $...@-t - mv -f $...@-t $@ - MOSTLYCLEANFILES += getopt.h getopt.h-t Include: <getopt.h> --- 1,20 ---- Description: Process command line arguments. + Status: + obsolete + + Notice: + This module is obsolete. Use the module 'getopt-posix' or 'getopt-gnu' instead. + Files: Depends-on: ! getopt-gnu configure.ac: Makefile.am: Include: <getopt.h> *** m4/getopt.m4.orig 2009-08-13 01:13:48.000000000 +0200 --- m4/getopt.m4 2009-08-13 01:11:15.000000000 +0200 *************** *** 1,20 **** ! # getopt.m4 serial 20 dnl Copyright (C) 2002-2006, 2008-2009 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 with or without modifications, as long as this notice is preserved. ! # The getopt module assume you want GNU getopt, with getopt_long etc, ! # rather than vanilla POSIX getopt. This means your code should ! # always include <getopt.h> for the getopt prototypes. ! ! # This is gnulib's entry-point. ! AC_DEFUN([gl_GETOPT], [ AC_REQUIRE([gl_UNISTD_H_DEFAULTS]) gl_GETOPT_IFELSE([ gl_REPLACE_GETOPT ! ]) ]) # Request the gnulib implementation of the getopt functions unconditionally. --- 1,28 ---- ! # getopt.m4 serial 21 dnl Copyright (C) 2002-2006, 2008-2009 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 with or without modifications, as long as this notice is preserved. ! # Request a POSIX compliant getopt function. ! AC_DEFUN([gl_FUNC_GETOPT_POSIX], [ + m4_divert_text([DEFAULTS], [gl_getopt_required=POSIX]) AC_REQUIRE([gl_UNISTD_H_DEFAULTS]) gl_GETOPT_IFELSE([ gl_REPLACE_GETOPT ! ], ! []) ! ]) ! ! # Request a POSIX compliant getopt function with GNU extensions (such as ! # options with optional arguments) and the functions getopt_long, ! # getopt_long_only. ! AC_DEFUN([gl_FUNC_GETOPT_GNU], ! [ ! m4_divert_text([INIT_PREPARE], [gl_getopt_required=GNU]) ! ! AC_REQUIRE([gl_FUNC_GETOPT_POSIX]) ]) # Request the gnulib implementation of the getopt functions unconditionally. *************** *** 41,87 **** # Determine whether to replace the entire getopt facility. AC_DEFUN([gl_GETOPT_CHECK_HEADERS], [ dnl Persuade Solaris <unistd.h> to declare optarg, optind, opterr, optopt. AC_REQUIRE([AC_USE_SYSTEM_EXTENSIONS]) gl_replace_getopt= ! if test -z "$gl_replace_getopt"; then AC_CHECK_HEADERS([getopt.h], [], [gl_replace_getopt=yes]) fi ! if test -z "$gl_replace_getopt"; then AC_CHECK_FUNCS([getopt_long_only], [], [gl_replace_getopt=yes]) fi dnl BSD getopt_long uses an incompatible method to reset option processing, ! dnl and (as of 2004-10-15) mishandles optional option-arguments. ! if test -z "$gl_replace_getopt"; then AC_CHECK_DECL([optreset], [gl_replace_getopt=yes], [], [#include <getopt.h>]) fi ! dnl Solaris 10 getopt doesn't handle `+' as a leading character in an ! dnl option string (as of 2005-05-05). if test -z "$gl_replace_getopt"; then ! AC_CACHE_CHECK([for working GNU getopt function], [gl_cv_func_gnu_getopt], [AC_RUN_IFELSE( ! [AC_LANG_PROGRAM([[#include <getopt.h>]], [[ ! char *myargv[3]; ! myargv[0] = "conftest"; ! myargv[1] = "-+"; ! myargv[2] = 0; ! return getopt (2, myargv, "+a") != '?'; ]])], ! [gl_cv_func_gnu_getopt=yes], ! [gl_cv_func_gnu_getopt=no], ! [dnl cross compiling - pessimistically guess based on decls ! dnl Solaris 10 getopt doesn't handle `+' as a leading character in an ! dnl option string (as of 2005-05-05). ! AC_CHECK_DECL([getopt_clip], ! [gl_cv_func_gnu_getopt=no], [gl_cv_func_gnu_getopt=yes], ! [#include <getopt.h>])])]) ! if test "$gl_cv_func_gnu_getopt" = "no"; then gl_replace_getopt=yes fi fi --- 49,223 ---- # Determine whether to replace the entire getopt facility. AC_DEFUN([gl_GETOPT_CHECK_HEADERS], [ + AC_REQUIRE([AC_CANONICAL_HOST]) dnl for cross-compiles + dnl Persuade Solaris <unistd.h> to declare optarg, optind, opterr, optopt. AC_REQUIRE([AC_USE_SYSTEM_EXTENSIONS]) gl_replace_getopt= ! ! dnl Test whether <getopt.h> is available. ! if test -z "$gl_replace_getopt" && test $gl_getopt_required = GNU; then AC_CHECK_HEADERS([getopt.h], [], [gl_replace_getopt=yes]) fi ! dnl Test whether the function getopt_long is available. ! if test -z "$gl_replace_getopt" && test $gl_getopt_required = GNU; then AC_CHECK_FUNCS([getopt_long_only], [], [gl_replace_getopt=yes]) fi dnl BSD getopt_long uses an incompatible method to reset option processing, ! dnl but the testsuite does not show a need to use this 'optreset' variable. ! if false && test -z "$gl_replace_getopt" && test $gl_getopt_required = GNU; then AC_CHECK_DECL([optreset], [gl_replace_getopt=yes], [], [#include <getopt.h>]) fi ! dnl mingw's getopt (in libmingwex.a) does weird things when the options ! 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. if test -z "$gl_replace_getopt"; then ! AC_CACHE_CHECK([whether getopt is POSIX compatible], ! [gl_cv_func_getopt_posix], ! [ ! dnl This test fails on mingw and succeeds on all other platforms. ! AC_TRY_RUN([ ! #include <unistd.h> ! #include <stdlib.h> ! #include <string.h> ! ! /* The glibc implementation of getopt supports setting optind = 0 as a means ! of clearing the internal state, but other implementations don't. */ ! #if (__GLIBC__ >= 2) ! # define OPTIND_MIN 0 ! #else ! # define OPTIND_MIN 1 ! #endif ! ! int ! main () ! { ! { ! int argc = 0; ! char *argv[10]; ! int c; ! ! argv[argc++] = "program"; ! argv[argc++] = "-a"; ! argv[argc++] = "foo"; ! argv[argc++] = "bar"; ! optind = OPTIND_MIN; ! opterr = 0; ! ! c = getopt (argc, argv, "ab"); ! if (!(c == 'a')) ! return 1; ! c = getopt (argc, argv, "ab"); ! if (!(c == -1)) ! return 2; ! if (!(optind == 2)) ! return 3; ! } ! /* Some internal state exists at this point. */ ! { ! int argc = 0; ! char *argv[10]; ! int c; ! ! argv[argc++] = "program"; ! argv[argc++] = "donald"; ! argv[argc++] = "-p"; ! argv[argc++] = "billy"; ! argv[argc++] = "duck"; ! argv[argc++] = "-a"; ! argv[argc++] = "bar"; ! optind = OPTIND_MIN; ! opterr = 0; ! ! c = getopt (argc, argv, "+abp:q:"); ! if (!(c == -1)) ! return 4; ! if (!(strcmp (argv[0], "program") == 0)) ! return 5; ! if (!(strcmp (argv[1], "donald") == 0)) ! return 6; ! if (!(strcmp (argv[2], "-p") == 0)) ! return 7; ! if (!(strcmp (argv[3], "billy") == 0)) ! return 8; ! if (!(strcmp (argv[4], "duck") == 0)) ! return 9; ! if (!(strcmp (argv[5], "-a") == 0)) ! return 10; ! if (!(strcmp (argv[6], "bar") == 0)) ! return 11; ! if (!(optind == 1)) ! return 12; ! } ! ! return 0; ! } ! ], ! [gl_cv_func_getopt_posix=yes], [gl_cv_func_getopt_posix=no], ! [case "$host_os" in ! mingw*) gl_cv_func_getopt_posix="guessing no";; ! *) gl_cv_func_getopt_posix="guessing yes";; ! esac ! ]) ! ]) ! case "$gl_cv_func_getopt_posix" in ! *no) gl_replace_getopt=yes ;; ! esac ! fi ! ! if test -z "$gl_replace_getopt" && test $gl_getopt_required = GNU; then ! AC_CACHE_CHECK([for working GNU getopt function], [gl_cv_func_getopt_gnu], [AC_RUN_IFELSE( ! [AC_LANG_PROGRAM([[#include <getopt.h> ! #include <stddef.h> ! #include <string.h>]], [[ ! /* This code succeeds on glibc 2.8, OpenBSD 4.0, Cygwin, mingw, ! and fails on MacOS X 10.5, AIX 5.2, HP-UX 11, IRIX 6.5, ! OSF/1 5.1, Solaris 10. */ ! { ! char *myargv[3]; ! myargv[0] = "conftest"; ! myargv[1] = "-+"; ! myargv[2] = 0; ! opterr = 0; ! if (getopt (2, myargv, "+a") != '?') ! return 1; ! } ! /* This code succeeds on glibc 2.8, mingw, ! and fails on MacOS X 10.5, OpenBSD 4.0, AIX 5.2, HP-UX 11, ! IRIX 6.5, OSF/1 5.1, Solaris 10, Cygwin. */ ! { ! char *argv[] = { "program", "-p", "foo", "bar" }; ! ! optind = 1; ! if (getopt (4, argv, "p::") != 'p') ! return 2; ! if (optarg != NULL) ! return 3; ! if (getopt (4, argv, "p::") != -1) ! return 4; ! if (optind != 2) ! return 5; ! } ! return 0; ]])], ! [gl_cv_func_getopt_gnu=yes], ! [gl_cv_func_getopt_gnu=no], ! [dnl Cross compiling. Guess based on host and declarations. ! case "$host_os" in ! *-gnu* | mingw*) gl_cv_func_getopt_gnu=no;; ! *) gl_cv_func_getopt_gnu=yes;; ! esac ! ]) ! ]) ! if test "$gl_cv_func_getopt_gnu" = "no"; then gl_replace_getopt=yes fi fi *** doc/glibc-headers/getopt.texi.orig 2009-08-13 01:13:48.000000000 +0200 --- doc/glibc-headers/getopt.texi 2009-08-13 01:10:36.000000000 +0200 *************** *** 18,24 **** @uref{http://www.kernel.org/doc/man-pages/online/pages/man3/getopt.3.html,,man getopt}. @end itemize ! Gnulib module: getopt Portability problems fixed by Gnulib: @itemize --- 18,24 ---- @uref{http://www.kernel.org/doc/man-pages/online/pages/man3/getopt.3.html,,man getopt}. @end itemize ! Gnulib module: getopt-gnu Portability problems fixed by Gnulib: @itemize *************** *** 26,33 **** This header file is missing on some platforms: AIX 5.1, HP-UX 11, Interix 3.5. @item The function @code{getopt_long_only} is missing on some platforms: ! MacOS X 10.3, FreeBSD 5.2.1, NetBSD 3.0, IRIX 6.5, OSF/1 5.1, mingw. @item The method to reset options is incompatible on some platforms: FreeBSD 6.0, NetBSD 3.0(?), OpenBSD 3.8, Cygwin(?), mingw. --- 26,36 ---- This header file is missing on some platforms: AIX 5.1, HP-UX 11, Interix 3.5. @item + The function @code{getopt_long} is missing on some platforms: + IRIX 6.5, OSF/1 5.1, Solaris 9. + @item The function @code{getopt_long_only} is missing on some platforms: ! MacOS X 10.3, FreeBSD 5.2.1, NetBSD 3.0, IRIX 6.5, OSF/1 5.1, Solaris 9, mingw. @item The method to reset options is incompatible on some platforms: FreeBSD 6.0, NetBSD 3.0(?), OpenBSD 3.8, Cygwin(?), mingw. *** doc/posix-functions/getopt.texi.orig 2009-08-13 01:13:48.000000000 +0200 --- doc/posix-functions/getopt.texi 2009-08-13 01:10:36.000000000 +0200 *************** *** 4,13 **** POSIX specification: @url{http://www.opengroup.org/onlinepubs/9699919799/functions/getopt.html} ! Gnulib module: --- ! Portability problems fixed by Gnulib: @itemize @end itemize Portability problems not fixed by Gnulib: --- 4,36 ---- POSIX specification: @url{http://www.opengroup.org/onlinepubs/9699919799/functions/getopt.html} ! Gnulib module: getopt-posix or getopt-gnu ! The module @code{getopt-gnu} has support for ``long options'' and for ! ``options that take optional arguments''. Compared to the API defined by POSIX, ! it adds a header file @code{<getopt.h>} and a function @code{getopt_long}. ! ! Portability problems fixed by either Gnulib module @code{getopt-posix} or @code{getopt-gnu}: ! @itemize ! @item ! The @code{getopt} function keeps some internal state that cannot be explicitly ! reset on some platforms: ! mingw. ! @end itemize ! ! 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. + @item + The function @code{getopt} does not support options with optional arguments + on some platforms: + MacOS X 10.5, OpenBSD 4.0, AIX 5.2, HP-UX 11, IRIX 6.5, OSF/1 5.1, Solaris 10, Cygwin. + @item + The function @code{getopt_long} is missing on some platforms: + AIX 5.1, HP-UX 11, IRIX 6.5, OSF/1 5.1, Solaris 9, Interix 3.5. @end itemize Portability problems not fixed by Gnulib: *************** *** 18,25 **** Other implementations, such as the one in Cygwin, enforce strict POSIX compliance: they require that the option arguments precede the non-option arguments. This is something to watch out in your program's testsuite. @end itemize - - Gnulib provides a module @code{getopt} that has support for ``long options''. - Compared to POSIX, it adds a header file @code{<getopt.h>} and functions - @code{getopt_long} and @code{getopt_long_only}. --- 41,47 ---- Other implementations, such as the one in Cygwin, enforce strict POSIX compliance: they require that the option arguments precede the non-option arguments. This is something to watch out in your program's testsuite. + @item + The function @code{getopt_long_only} is missing on some platforms: + MacOS X 10.3, FreeBSD 5.2.1, NetBSD 3.0, AIX 5.1, HP-UX 11, IRIX 6.5, OSF/1 5.1, Solaris 9, mingw, Interix 3.5. @end itemize