diff --git a/.github/workflows/builds.yml b/.github/workflows/builds.yml index 8ea8364..62b9c34 100644 --- a/.github/workflows/builds.yml +++ b/.github/workflows/builds.yml @@ -186,7 +186,7 @@ jobs: - name: Install Cygwin uses: cygwin/cygwin-install-action@v6 with: - packages: cmake gcc-core ninja + packages: cmake gcc-core gcc-g++ ninja - name: Configure CMake run: > diff --git a/CMakeLists.txt b/CMakeLists.txt index 55eac93..d867ed1 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -21,6 +21,7 @@ option(argp_standalone_ENABLE_WARNINGS "Build argp-standalone with warnings enab if(argp_standalone_BUILD_TESTING) enable_testing() + enable_language(CXX) endif() @@ -39,10 +40,6 @@ cmake_push_check_state() set(CMAKE_REQUIRED_DEFINITIONS -D_GNU_SOURCE) # Check for headers -check_include_file(mempcpy.h HAVE_MEMPCPY_H) -check_include_file(strcase.h HAVE_STRCASE_H) -check_include_file(strchrnul.h HAVE_STRCHRNUL_H) -check_include_file(strndup.h HAVE_STRNDUP_H) check_include_file(sysexits.h HAVE_SYSEXITS_H) check_include_file(unistd.h HAVE_UNISTD_H) @@ -67,7 +64,7 @@ check_symbol_exists(strerror_s string.h HAVE_DECL_STRERROR_S) cmake_pop_check_state() # Check for miscellaneous functions -check_symbol_exists(asprintf stdio.h HAVE_ASPRINTF) +check_function_exists(xasprintf HAVE_XASPRINTF) check_symbol_exists(mempcpy string.h HAVE_MEMPCPY) check_symbol_exists(random stdlib.h HAVE_RANDOM) check_symbol_exists(sleep unistd.h HAVE_SLEEP) diff --git a/CMakePresets.json b/CMakePresets.json index 36dd127..e022ba1 100644 --- a/CMakePresets.json +++ b/CMakePresets.json @@ -23,14 +23,16 @@ "name": "dev-linux-clang", "inherits": "dev-linux", "cacheVariables": { - "CMAKE_C_COMPILER": "clang" + "CMAKE_C_COMPILER": "clang", + "CMAKE_CXX_COMPILER": "clang++" } }, { "name": "dev-linux-gcc", "inherits": "dev-linux", "cacheVariables": { - "CMAKE_C_COMPILER": "gcc" + "CMAKE_C_COMPILER": "gcc", + "CMAKE_CXX_COMPILER": "g++" } }, { @@ -38,6 +40,7 @@ "inherits": "dev-base", "cacheVariables": { "CMAKE_C_COMPILER": "i686-w64-mingw32-gcc", + "CMAKE_CXX_COMPILER": "i686-w64-mingw32-g++", "CMAKE_SYSTEM_NAME": "Windows", "CMAKE_CROSSCOMPILING_EMULATOR": "wine" } @@ -47,6 +50,7 @@ "inherits": "dev-base", "cacheVariables": { "CMAKE_C_COMPILER": "x86_64-w64-mingw32-gcc", + "CMAKE_CXX_COMPILER": "x86_64-w64-mingw32-g++", "CMAKE_SYSTEM_NAME": "Windows", "CMAKE_CROSSCOMPILING_EMULATOR": "wine" } diff --git a/config.h.in b/config.h.in index a6d16ee..616ea4a 100644 --- a/config.h.in +++ b/config.h.in @@ -3,10 +3,6 @@ argp-standalone - standalone version of glibc's argp functions. */ /* Headers */ -#cmakedefine01 HAVE_MEMPCPY_H -#cmakedefine01 HAVE_STRCASE_H -#cmakedefine01 HAVE_STRCHRNUL_H -#cmakedefine01 HAVE_STRNDUP_H #cmakedefine01 HAVE_SYSEXITS_H #cmakedefine01 HAVE_UNISTD_H @@ -22,7 +18,7 @@ #cmakedefine01 HAVE_DECL_STRERROR_S /* Miscellaneous functions */ -#cmakedefine01 HAVE_ASPRINTF +#cmakedefine01 HAVE_XASPRINTF #cmakedefine01 HAVE_MEMPCPY #cmakedefine01 HAVE_RANDOM #cmakedefine01 HAVE_SLEEP diff --git a/include/argp-standalone/argp.h b/include/argp-standalone/argp.h index 5191290..7f7f2fe 100644 --- a/include/argp-standalone/argp.h +++ b/include/argp-standalone/argp.h @@ -1,6 +1,6 @@ /* SPDX-License-Identifier: LGPL-2.1-or-later */ -/* Hierarchial argument parsing, layered over getopt. - Copyright (C) 1995-2016 Free Software Foundation, Inc. +/* Hierarchical argument parsing, layered over getopt. + Copyright (C) 1995-2026 Free Software Foundation, Inc. This file is part of the GNU C Library. Written by Miles Bader . @@ -16,25 +16,39 @@ You should have received a copy of the GNU Lesser General Public License along with the GNU C Library; if not, see - . */ + . */ #ifndef _ARGP_H #define _ARGP_H #include #include +/* argp-standalone: do not include getopt.h in public header, + since it may not be available. */ +/*#include */ #include - -#define __need_error_t #include +/* argp-standalone: define __THROW. Taken from older version of */ #ifndef __THROW -# define __THROW +# ifndef __GNUC_PREREQ +# define __GNUC_PREREQ(maj, min) (0) +# endif +# if defined __cplusplus && __GNUC_PREREQ (2,8) +# define __THROW throw () +# else +# define __THROW +# endif #endif + +/* argp-standalone: define __NTH. Taken from older version of */ #ifndef __NTH # define __NTH(fct) fct __THROW #endif +/* argp-standalone: more recent versions of glibc do not define __attribute__ + here anymore but in some non-standard header that we might not have. + Add an own definition of __attribute__ from an older version of argp. */ #ifndef __attribute__ /* This feature is available in gcc versions 2.5 and later. */ # if __GNUC__ < 2 || (__GNUC__ == 2 && __GNUC_MINOR__ < 5) || \ @@ -50,6 +64,9 @@ # endif #endif +/* argp-standalone: more recent versions of glibc do not define __restrict + here anymore but in some non-standard header that we might not have. + Add an own definition of __restrict from an older version of argp. */ /* GCC 2.95 and later have "__restrict"; C99 compilers have "restrict", and "configure" may have defined "restrict". */ #ifndef __restrict @@ -62,15 +79,28 @@ # endif #endif -#ifndef __error_t_defined -typedef int error_t; -# define __error_t_defined +/* argp-standalone: implement -Wformat warnings correctly when using MinGW-w64. + See https://stackoverflow.com/a/79974346/17365470. */ +#ifdef __MINGW32__ +# define argp_attribute_format(argp_format, argp_args) \ + __attribute__ ((__format__ (__MINGW_PRINTF_FORMAT, argp_format, argp_args))) +#else +# define argp_attribute_format(argp_format, argp_args) \ + __attribute__ ((__format__ (__printf__, argp_format, argp_args))) #endif - + +/* argp-standalone: we do not have __BEGIN_DECLS/__END_DECLS. */ #ifdef __cplusplus extern "C" { #endif +/* error_t may or may not be available from errno.h, depending on the + operating system. */ +#ifndef __error_t_defined +# define __error_t_defined 1 +typedef int error_t; +#endif + /* A description of a particular option. A pointer to an array of these is passed in the OPTIONS field of an argp structure. Each option entry can correspond to one long option and/or one short option; more @@ -269,7 +299,7 @@ struct argp }; /* Possible KEY arguments to a help filter function. */ -#define ARGP_KEY_HELP_PRE_DOC 0x2000001 /* Help text preceeding options. */ +#define ARGP_KEY_HELP_PRE_DOC 0x2000001 /* Help text preceding options. */ #define ARGP_KEY_HELP_POST_DOC 0x2000002 /* Help text following options. */ #define ARGP_KEY_HELP_HEADER 0x2000003 /* Option header string. */ #define ARGP_KEY_HELP_EXTRA 0x2000004 /* After all other documentation; @@ -404,10 +434,12 @@ struct argp_state returned. This function may also call exit unless the ARGP_NO_HELP flag is set. INPUT is a pointer to a value to be passed in to the parser. */ extern error_t argp_parse (const struct argp *__restrict __argp, + /* argp-standalone: __argc and __argv are macros in MSVC. */ int argc, char **__restrict argv, unsigned __flags, int *__restrict __arg_index, void *__restrict __input); extern error_t __argp_parse (const struct argp *__restrict __argp, + /* argp-standalone: __argc and __argv are macros in MSVC. */ int argc, char **__restrict argv, unsigned __flags, int *__restrict __arg_index, void *__restrict __input); @@ -483,7 +515,7 @@ extern void __argp_help (const struct argp *__restrict __argp, parsing routine (thus taking an argp_state structure as the first argument). They may or may not print an error message and exit, depending on the flags in STATE -- in any case, the caller should be prepared for - them *not* to exit, and should return an appropiate error after calling + them *not* to exit, and should return an appropriate error after calling them. [argp_usage & argp_error should probably be called argp_state_..., but they're used often enough that they should be short] */ @@ -505,10 +537,10 @@ extern void __argp_usage (const struct argp_state *__state); message, then exit (1). */ extern void argp_error (const struct argp_state *__restrict __state, const char *__restrict __fmt, ...) - __attribute__ ((__format__ (__printf__, 2, 3))); + argp_attribute_format (2, 3); /* argp-standalone: use own attribute. */ extern void __argp_error (const struct argp_state *__restrict __state, const char *__restrict __fmt, ...) - __attribute__ ((__format__ (__printf__, 2, 3))); + argp_attribute_format (2, 3); /* argp-standalone: use own attribute. */ /* Similar to the standard gnu error-reporting function error(), but will respect the ARGP_NO_EXIT and ARGP_NO_ERRS flags in STATE, and will print @@ -521,11 +553,11 @@ extern void __argp_error (const struct argp_state *__restrict __state, extern void argp_failure (const struct argp_state *__restrict __state, int __status, int __errnum, const char *__restrict __fmt, ...) - __attribute__ ((__format__ (__printf__, 4, 5))); + argp_attribute_format (4, 5); /* argp-standalone: use own attribute. */ extern void __argp_failure (const struct argp_state *__restrict __state, int __status, int __errnum, const char *__restrict __fmt, ...) - __attribute__ ((__format__ (__printf__, 4, 5))); + argp_attribute_format (4, 5); /* argp-standalone: use own attribute. */ /* Returns true if the option OPT is a valid short option. */ extern int _option_is_short (const struct argp_option *__opt) __THROW; @@ -547,13 +579,14 @@ extern void *__argp_input (const struct argp *__restrict __argp, #ifdef __USE_EXTERN_INLINES -# if !_LIBC +# if !(defined _LIBC && _LIBC) # define __argp_usage argp_usage # define __argp_state_help argp_state_help # define __option_is_short _option_is_short # define __option_is_end _option_is_end # endif +/* argp-standalone: needed for old argp-xinl.c from glibc 2.42 we're using. */ # ifndef ARGP_EI # define ARGP_EI __extern_inline # endif @@ -582,7 +615,7 @@ __NTH (__option_is_end (const struct argp_option *__opt)) return !__opt->key && !__opt->name && !__opt->doc && !__opt->group; } -# if !_LIBC +# if !(defined _LIBC && _LIBC) # undef __argp_usage # undef __argp_state_help # undef __option_is_short @@ -590,6 +623,12 @@ __NTH (__option_is_end (const struct argp_option *__opt)) # endif #endif /* Use extern inlines. */ +/* argp-standalone: do not implement glibc's long double redirection. */ +/*#include +#if defined __LDBL_COMPAT || __LDOUBLE_REDIRECTS_TO_FLOAT128_ABI == 1 +# include +#endif*/ + #ifdef __cplusplus } #endif diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 857a4c5..5ee1d42 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -2,8 +2,8 @@ # SPDX-License-Identifier: LGPL-2.1-or-later # argp-standalone - standalone version of glibc's argp functions. -set( - SOURCES +add_library( + argp-standalone argp-ba.c argp-compat.c argp-eexst.c @@ -17,8 +17,6 @@ set( getopt.c getopt1.c) -add_library(argp-standalone ${SOURCES}) - target_compile_definitions(argp-standalone PRIVATE HAVE_CONFIG_H) argp_standalone_target_enable_warnings(argp-standalone) @@ -26,6 +24,7 @@ target_include_directories( argp-standalone PRIVATE "${PROJECT_BINARY_DIR}" + "${CMAKE_CURRENT_SOURCE_DIR}" PUBLIC ../include/argp-standalone) diff --git a/src/argp-ba.c b/src/argp-ba.c index 9247d8f..9b3caad 100644 --- a/src/argp-ba.c +++ b/src/argp-ba.c @@ -1,6 +1,6 @@ /* SPDX-License-Identifier: LGPL-2.1-or-later */ /* Default definition for ARGP_PROGRAM_BUG_ADDRESS. - Copyright (C) 1996-2016 Free Software Foundation, Inc. + Copyright (C) 1996-2026 Free Software Foundation, Inc. This file is part of the GNU C Library. Written by Miles Bader . @@ -16,7 +16,7 @@ You should have received a copy of the GNU Lesser General Public License along with the GNU C Library; if not, see - . */ + . */ /* If set by the user program, it should point to string that is the bug-reporting address for the program. It will be printed by argp_help if diff --git a/src/argp-compat.c b/src/argp-compat.c index 1f05d14..6b2bff9 100644 --- a/src/argp-compat.c +++ b/src/argp-compat.c @@ -17,8 +17,8 @@ #if defined(HAVE_MEMPCPY) && !HAVE_MEMPCPY void* argp_compat_mempcpy(void* out, const void* in, size_t n) { - memcpy(out, in, n); - return (char*)out + n; + memcpy(out, in, n); + return (char*)out + n; } #endif diff --git a/src/argp-eexst.c b/src/argp-eexst.c index a8426c5..b0e57b5 100644 --- a/src/argp-eexst.c +++ b/src/argp-eexst.c @@ -1,6 +1,6 @@ /* SPDX-License-Identifier: LGPL-2.1-or-later */ /* Default definition for ARGP_ERR_EXIT_STATUS - Copyright (C) 1997-2016 Free Software Foundation, Inc. + Copyright (C) 1997-2026 Free Software Foundation, Inc. This file is part of the GNU C Library. Written by Miles Bader . @@ -16,12 +16,13 @@ You should have received a copy of the GNU Lesser General Public License along with the GNU C Library; if not, see - . */ + . */ #ifdef HAVE_CONFIG_H # include #endif +/* argp-standalone: use EXIT_FAILURE if is not available. */ #if defined(HAVE_SYSEXITS_H) && HAVE_SYSEXITS_H # include #else @@ -33,6 +34,7 @@ /* The exit status that argp will use when exiting due to a parsing error. If not defined or set by the user program, this defaults to EX_USAGE from . */ +/* argp-standalone: use EXIT_FAILURE if is not available. */ error_t argp_err_exit_status = #if defined(HAVE_SYSEXITS_H) && HAVE_SYSEXITS_H EX_USAGE; diff --git a/src/argp-fmtstream.c b/src/argp-fmtstream.c index fb2eaa8..ea60830 100644 --- a/src/argp-fmtstream.c +++ b/src/argp-fmtstream.c @@ -1,6 +1,6 @@ /* SPDX-License-Identifier: LGPL-2.1-or-later */ /* Word-wrapping and line-truncating streams - Copyright (C) 1997-2016 Free Software Foundation, Inc. + Copyright (C) 1997-2026 Free Software Foundation, Inc. This file is part of the GNU C Library. Written by Miles Bader . @@ -16,7 +16,7 @@ You should have received a copy of the GNU Lesser General Public License along with the GNU C Library; if not, see - . */ + . */ /* This package emulates glibc `line_wrap_stream' semantics for systems that don't have that. */ @@ -31,7 +31,7 @@ #include #include -#include "argp-fmtstream.h" +#include #include "argp-namefrob.h" #ifndef ARGP_FMTSTREAM_USE_LINEWRAP @@ -43,7 +43,6 @@ #ifdef _LIBC # include # include -# define __vsnprintf(s, l, f, a) _IO_vsnprintf (s, l, f, a) #endif #define INIT_BUF_SIZE 200 @@ -76,7 +75,7 @@ __argp_make_fmtstream (FILE *stream, if (! fs->buf) { free (fs); - fs = 0; + fs = NULL; } else { @@ -150,7 +149,7 @@ __argp_fmtstream_update (argp_fmtstream_t fs) size_t i; for (i = 0; i < pad; i++) { -#ifdef USE_IN_LIBIO +#ifdef _LIBC if (_IO_fwide (fs->stream, 0) > 0) putwc_unlocked (L' ', fs->stream); else @@ -315,7 +314,7 @@ __argp_fmtstream_update (argp_fmtstream_t fs) *nl++ = ' '; else for (i = 0; i < fs->wmargin; ++i) -#ifdef USE_IN_LIBIO +#ifdef _LIBC if (_IO_fwide (fs->stream, 0) > 0) putwc_unlocked (L' ', fs->stream); else @@ -414,7 +413,8 @@ __argp_fmtstream_printf (struct argp_fmtstream *fs, const char *fmt, ...) va_start (args, fmt); avail = fs->end - fs->p; - out = __vsnprintf (fs->p, avail, fmt, args); + /* argp-standalone: use vsnprintf, not __vsnprintf_internal. */ + out = vsnprintf (fs->p, avail, fmt, args); va_end (args); if ((size_t) out >= avail) size_guess = out + 1; diff --git a/src/argp-fmtstream.h b/src/argp-fmtstream.h index 0530e44..526746a 100644 --- a/src/argp-fmtstream.h +++ b/src/argp-fmtstream.h @@ -1,6 +1,6 @@ /* SPDX-License-Identifier: LGPL-2.1-or-later */ /* Word-wrapping and line-truncating streams. - Copyright (C) 1997-2016 Free Software Foundation, Inc. + Copyright (C) 1997-2026 Free Software Foundation, Inc. This file is part of the GNU C Library. Written by Miles Bader . @@ -16,7 +16,7 @@ You should have received a copy of the GNU Lesser General Public License along with the GNU C Library; if not, see - . */ + . */ /* This package emulates glibc `line_wrap_stream' semantics for systems that don't have that. If the system does have it, it is just a wrapper for @@ -26,17 +26,22 @@ #ifndef _ARGP_FMTSTREAM_H #define _ARGP_FMTSTREAM_H +/* argp-standalone: include config.h */ #ifdef HAVE_CONFIG_H # include #endif #include #include +/* argp-standalone: only include if it's available. */ #if defined(HAVE_UNISTD_H) && HAVE_UNISTD_H # include #endif -#include "argp-compat.h" +#include "argp-compat.h" /* argp-standalone: include compatibility header */ +/* argp-standalone: more recent versions of glibc do not define __attribute__ + here anymore but in some non-standard header that we might not have. + Add an own definition of __attribute__ from an older version of argp. */ #ifndef __attribute__ /* This feature is available in gcc versions 2.5 and later. */ # if __GNUC__ < 2 || (__GNUC__ == 2 && __GNUC_MINOR__ < 5) || \ @@ -52,6 +57,21 @@ # endif #endif +/* argp-standalone: define attribute_hidden as no-op if it does not exist. */ +#ifndef attribute_hidden +#define attribute_hidden +#endif + +/* argp-standalone: implement -Wformat warnings correctly when using MinGW-w64. + See https://stackoverflow.com/a/79974346/17365470. */ +#ifdef __MINGW32__ +# define argp_attribute_format(argp_format, argp_args) \ +__attribute__ ((__format__ (__MINGW_PRINTF_FORMAT, argp_format, argp_args))) +#else +# define argp_attribute_format(argp_format, argp_args) \ +__attribute__ ((__format__ (__printf__, argp_format, argp_args))) +#endif + #if defined (__GNU_LIBRARY__) && defined (HAVE_LINEWRAP_H) /* line_wrap_stream is available, so use that. */ #define ARGP_FMTSTREAM_USE_LINEWRAP @@ -119,6 +139,11 @@ struct argp_fmtstream typedef struct argp_fmtstream *argp_fmtstream_t; +/* argp-standalone: we do not have __BEGIN_DECLS/__END_DECLS. */ +#ifdef __cplusplus +extern "C" { +#endif + /* Return an argp_fmtstream that outputs to STREAM, and which prefixes lines written on it with LMARGIN spaces and limits them to RMARGIN columns total. If WMARGIN >= 0, words that extend past RMARGIN are wrapped by @@ -128,22 +153,25 @@ typedef struct argp_fmtstream *argp_fmtstream_t; extern argp_fmtstream_t __argp_make_fmtstream (FILE *__stream, size_t __lmargin, size_t __rmargin, - ssize_t __wmargin); + ssize_t __wmargin) + attribute_hidden; extern argp_fmtstream_t argp_make_fmtstream (FILE *__stream, size_t __lmargin, size_t __rmargin, ssize_t __wmargin); /* Flush __FS to its stream, and free it (but don't close the stream). */ -extern void __argp_fmtstream_free (argp_fmtstream_t __fs); +extern void __argp_fmtstream_free (argp_fmtstream_t __fs) + attribute_hidden; extern void argp_fmtstream_free (argp_fmtstream_t __fs); extern ssize_t __argp_fmtstream_printf (argp_fmtstream_t __fs, const char *__fmt, ...) - __attribute__ ((__format__ (printf, 2, 3))); + argp_attribute_format (2, 3) /* argp-standalone: use argp_attribute_format */ + attribute_hidden; extern ssize_t argp_fmtstream_printf (argp_fmtstream_t __fs, const char *__fmt, ...) - __attribute__ ((__format__ (printf, 2, 3))); + argp_attribute_format (2, 3); /* argp-standalone: use argp_attribute_format */ extern int __argp_fmtstream_putc (argp_fmtstream_t __fs, int __ch); extern int argp_fmtstream_putc (argp_fmtstream_t __fs, int __ch); @@ -152,7 +180,8 @@ extern int __argp_fmtstream_puts (argp_fmtstream_t __fs, const char *__str); extern int argp_fmtstream_puts (argp_fmtstream_t __fs, const char *__str); extern size_t __argp_fmtstream_write (argp_fmtstream_t __fs, - const char *__str, size_t __len); + const char *__str, size_t __len) + attribute_hidden; extern size_t argp_fmtstream_write (argp_fmtstream_t __fs, const char *__str, size_t __len); @@ -188,9 +217,11 @@ extern size_t __argp_fmtstream_point (argp_fmtstream_t __fs); /* Internal routines. */ extern void _argp_fmtstream_update (argp_fmtstream_t __fs); -extern void __argp_fmtstream_update (argp_fmtstream_t __fs); +extern void __argp_fmtstream_update (argp_fmtstream_t __fs) + attribute_hidden; extern int _argp_fmtstream_ensure (argp_fmtstream_t __fs, size_t __amount); -extern int __argp_fmtstream_ensure (argp_fmtstream_t __fs, size_t __amount); +extern int __argp_fmtstream_ensure (argp_fmtstream_t __fs, size_t __amount) + attribute_hidden; #ifdef __OPTIMIZE__ /* Inline versions of above routines. */ @@ -305,6 +336,10 @@ __argp_fmtstream_point (argp_fmtstream_t __fs) #endif /* __OPTIMIZE__ */ +#ifdef __cplusplus +} +#endif + #endif /* ARGP_FMTSTREAM_USE_LINEWRAP */ #endif /* argp-fmtstream.h */ diff --git a/src/argp-fs-xinl.c b/src/argp-fs-xinl.c index 6a5a7c1..4ebc778 100644 --- a/src/argp-fs-xinl.c +++ b/src/argp-fs-xinl.c @@ -1,6 +1,6 @@ /* SPDX-License-Identifier: LGPL-2.1-or-later */ /* Real definitions for extern inline functions in argp-fmtstream.h - Copyright (C) 1997-2016 Free Software Foundation, Inc. + Copyright (C) 1997-2026 Free Software Foundation, Inc. This file is part of the GNU C Library. Written by Miles Bader . @@ -16,7 +16,7 @@ You should have received a copy of the GNU Lesser General Public License along with the GNU C Library; if not, see - . */ + . */ #ifdef HAVE_CONFIG_H # include @@ -25,7 +25,7 @@ #define ARGP_FS_EI #undef __OPTIMIZE__ #define __OPTIMIZE__ 1 -#include "argp-fmtstream.h" +#include #if 0 /* Not exported. */ diff --git a/src/argp-getopt.h b/src/argp-getopt.h index b864743..6569b52 100644 --- a/src/argp-getopt.h +++ b/src/argp-getopt.h @@ -1,7 +1,9 @@ /* SPDX-License-Identifier: LGPL-2.1-or-later */ /* Declarations for getopt. - Copyright (C) 1989-2016 Free Software Foundation, Inc. + Copyright (C) 1989-2026 Free Software Foundation, Inc. This file is part of the GNU C Library. + Unlike the bulk of the getopt implementation, this file is NOT part + of gnulib; gnulib also has a getopt.h but it is different. The GNU C Library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public @@ -15,14 +17,13 @@ You should have received a copy of the GNU Lesser General Public License along with the GNU C Library; if not, see - . */ + . */ #ifndef _GETOPT_H +#define _GETOPT_H 1 -#ifndef __need_getopt -# define _GETOPT_H 1 -#endif - +/* argp-standalone: include to get glibc's if it exists. + This will get us the definition of __THROW. */ /* If __GNU_LIBRARY__ is not already defined, either we are being used standalone, or this is the first header included in the source file. If we are being used with glibc, we need to include , but @@ -34,6 +35,7 @@ # include #endif +/* argp-standalone: define __THROW. Taken from older version of */ #ifndef __THROW # ifndef __GNUC_PREREQ # define __GNUC_PREREQ(maj, min) (0) @@ -45,165 +47,17 @@ # endif #endif -#ifdef __cplusplus -extern "C" { -#endif - -/* - * The following declarations may conflict with declarations from unistd.h, - * e.g. on Cygwin where the declarations from unistd.h are dllimports. - * Since getopt itself compiles without these declarations and argp-standalone - * only provides argp but not getopt to its clients we simply remove these - * declarations. - */ -#if 0 -/* For communication from `getopt' to the caller. - When `getopt' finds an option that takes an argument, - the argument value is returned here. - Also, when `ordering' is RETURN_IN_ORDER, - each non-option ARGV-element is returned here. */ - -extern char *optarg; - -/* Index in ARGV of the next element to be scanned. - This is used for communication to and from the caller - and for communication between successive calls to `getopt'. - - On entry to `getopt', zero means this is the first call; initialize. - - When `getopt' returns -1, this is the index of the first of the - non-option elements that the caller should itself scan. - - Otherwise, `optind' communicates from one call to the next - how much of ARGV has been scanned so far. */ - -extern int optind; - -/* Callers store zero here to inhibit the error message `getopt' prints - for unrecognized options. */ - -extern int opterr; - -/* Set to an option character which was unrecognized. */ - -extern int optopt; -#endif - -#ifndef __need_getopt -/* Describe the long-named options requested by the application. - The LONG_OPTIONS argument to getopt_long or getopt_long_only is a vector - of `struct option' terminated by an element containing a name which is - zero. - - The field `has_arg' is: - no_argument (or 0) if the option does not take an argument, - required_argument (or 1) if the option requires an argument, - optional_argument (or 2) if the option takes an optional argument. - - If the field `flag' is not NULL, it points to a variable that is set - to the value given in the field `val' when the option is found, but - left unchanged if the option is not found. - - To have a long-named option do something other than set an `int' to - a compiled-in constant, such as set a value from `optarg', set the - option's `flag' field to zero and its `val' field to a nonzero - value (the equivalent single-letter option character, if there is - one). For long options that have a zero `flag' field, `getopt' - returns the contents of the `val' field. */ - -struct option -{ - const char *name; - /* has_arg can't be an enum because some compilers complain about - type mismatches in all the code that assumes it is an int. */ - int has_arg; - int *flag; - int val; -}; - -/* Names for the values of the `has_arg' field of `struct option'. */ - -# define no_argument 0 -# define required_argument 1 -# define optional_argument 2 -#endif /* need getopt */ - - -/* Get definitions and prototypes for functions to process the - arguments in ARGV (ARGC of them, minus the program name) for - options given in OPTS. - - Return the option character from OPTS just read. Return -1 when - there are no more options. For unrecognized options, or options - missing arguments, `optopt' is set to the option letter, and '?' is - returned. - - The OPTS string is a list of characters which are recognized option - letters, optionally followed by colons, specifying that that letter - takes an argument, to be placed in `optarg'. - - If a letter in OPTS is followed by two colons, its argument is - optional. This behavior is specific to the GNU `getopt'. - - The argument `--' causes premature termination of argument - scanning, explicitly telling `getopt' that there are no more - options. - - If OPTS begins with `--', then non-option arguments are treated as - arguments to the option '\0'. This behavior is specific to the GNU - `getopt'. */ - -#ifdef __GNU_LIBRARY__ -/* Many other libraries have conflicting prototypes for getopt, with - differences in the consts, in stdlib.h. To avoid compilation - errors, only prototype getopt for the GNU C library. */ -extern int getopt (int ___argc, char *const *___argv, const char *__shortopts) - __THROW; - -# if defined __need_getopt && defined __USE_POSIX2 \ - && !defined __USE_POSIX_IMPLICITLY && !defined __USE_GNU -/* The GNU getopt has more functionality than the standard version. The - additional functionality can be disable at runtime. This redirection - helps to also do this at runtime. */ -# ifdef __REDIRECT - extern int __REDIRECT_NTH (getopt, (int ___argc, char *const *___argv, - const char *__shortopts), - __posix_getopt); -# else -extern int __posix_getopt (int ___argc, char *const *___argv, - const char *__shortopts) __THROW; -# define getopt __posix_getopt -# endif -# endif -#else /* not __GNU_LIBRARY__ */ -/* - * The following declaration may conflict with the declaration from unistd.h. - * Since getopt itself compiles without this declaration and argp-standalone - * only provides argp but not getopt to its clients we simply remove this - * declaration. - */ -# if 0 -extern int getopt (); -# endif -#endif /* __GNU_LIBRARY__ */ - -#ifndef __need_getopt -extern int getopt_long (int ___argc, char *const *___argv, - const char *__shortopts, - const struct option *__longopts, int *__longind) - __THROW; -extern int getopt_long_only (int ___argc, char *const *___argv, - const char *__shortopts, - const struct option *__longopts, int *__longind) - __THROW; - -#endif - -#ifdef __cplusplus -} +/* The type of the 'argv' argument to getopt_long and getopt_long_only + is properly 'char **', since both functions may write to the array + (in order to move all the options to the beginning). However, for + compatibility with old versions of LSB, glibc has to use 'char *const *' + instead. */ +#ifndef __getopt_argv_const +# define __getopt_argv_const const #endif -/* Make sure we later can get all the definitions and declarations. */ -#undef __need_getopt +/* argp-standalone: include our own renamed and patched headers from glibc. */ +#include "argp-getopt_core.h" +#include "argp-getopt_ext.h" #endif /* getopt.h */ diff --git a/src/argp-getopt_core.h b/src/argp-getopt_core.h new file mode 100644 index 0000000..fe0a3aa --- /dev/null +++ b/src/argp-getopt_core.h @@ -0,0 +1,109 @@ +/* SPDX-License-Identifier: LGPL-2.1-or-later */ +/* Declarations for getopt (basic, portable features only). + Copyright (C) 1989-2026 Free Software Foundation, Inc. + This file is part of the GNU C Library and is also part of gnulib. + Patches to this file should be submitted to both projects. + + The GNU C Library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + The GNU C Library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with the GNU C Library; if not, see + . */ + +#ifndef _GETOPT_CORE_H +#define _GETOPT_CORE_H 1 + +/* This header should not be used directly; include getopt.h or + unistd.h instead. Unlike most bits headers, it does not have + a protective #error, because the guard macro for getopt.h in + gnulib is not fixed. */ + +/* argp-standalone: we do not have __BEGIN_DECLS/__END_DECLS. */ +#ifdef __cplusplus +extern "C" { +#endif + +/* argp-standalone: The following declarations may conflict with declarations + from unistd.h, e.g. on Cygwin where the declarations from unistd.h have + dllimport storage class. Since getopt itself compiles without these + declarations and argp-standalone only provides argp but not getopt to its + clients we simply remove these declarations. */ +#if 0 +/* For communication from 'getopt' to the caller. + When 'getopt' finds an option that takes an argument, + the argument value is returned here. + Also, when 'ordering' is RETURN_IN_ORDER, + each non-option ARGV-element is returned here. */ + +extern char *optarg; + +/* Index in ARGV of the next element to be scanned. + This is used for communication to and from the caller + and for communication between successive calls to 'getopt'. + + On entry to 'getopt', zero means this is the first call; initialize. + + When 'getopt' returns -1, this is the index of the first of the + non-option elements that the caller should itself scan. + + Otherwise, 'optind' communicates from one call to the next + how much of ARGV has been scanned so far. */ + +extern int optind; + +/* Callers store zero here to inhibit the error message 'getopt' prints + for unrecognized options. */ + +extern int opterr; + +/* Set to an option character which was unrecognized. */ + +extern int optopt; +#endif + +/* Get definitions and prototypes for functions to process the + arguments in ARGV (ARGC of them, minus the program name) for + options given in OPTS. + + Return the option character from OPTS just read. Return -1 when + there are no more options. For unrecognized options, or options + missing arguments, 'optopt' is set to the option letter, and '?' is + returned. + + The OPTS string is a list of characters which are recognized option + letters, optionally followed by colons, specifying that that letter + takes an argument, to be placed in 'optarg'. + + If a letter in OPTS is followed by two colons, its argument is + optional. This behavior is specific to the GNU 'getopt'. + + The argument '--' causes premature termination of argument + scanning, explicitly telling 'getopt' that there are no more + options. + + If OPTS begins with '-', then non-option arguments are treated as + arguments to the option '\1'. This behavior is specific to the GNU + 'getopt'. If OPTS begins with '+', or POSIXLY_CORRECT is set in + the environment, then do not permute arguments. + + For standards compliance, the 'argv' argument has the type + char *const *, but this is inaccurate; if argument permutation is + enabled, the argv array (not the strings it points to) must be + writable. */ + +extern int getopt (int ___argc, char *const *___argv, const char *__shortopts) + __THROW /*__nonnull ((2, 3))*/; /* argp-standalone: removed __nonnull */ + +#ifdef __cplusplus +} +#endif + +#endif /* getopt_core.h */ diff --git a/src/argp-getopt_ext.h b/src/argp-getopt_ext.h new file mode 100644 index 0000000..3b5a9a4 --- /dev/null +++ b/src/argp-getopt_ext.h @@ -0,0 +1,83 @@ +/* SPDX-License-Identifier: LGPL-2.1-or-later */ +/* Declarations for getopt (GNU extensions). + Copyright (C) 1989-2026 Free Software Foundation, Inc. + This file is part of the GNU C Library and is also part of gnulib. + Patches to this file should be submitted to both projects. + + The GNU C Library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + The GNU C Library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with the GNU C Library; if not, see + . */ + +#ifndef _GETOPT_EXT_H +#define _GETOPT_EXT_H 1 + +/* This header should not be used directly; include getopt.h instead. + Unlike most bits headers, it does not have a protective #error, + because the guard macro for getopt.h in gnulib is not fixed. */ + +/* argp-standalone: we do not have __BEGIN_DECLS/__END_DECLS. */ +#ifdef __cplusplus +extern "C" { +#endif + +/* Describe the long-named options requested by the application. + The LONG_OPTIONS argument to getopt_long or getopt_long_only is a vector + of 'struct option' terminated by an element containing a name which is + zero. + + The field 'has_arg' is: + no_argument (or 0) if the option does not take an argument, + required_argument (or 1) if the option requires an argument, + optional_argument (or 2) if the option takes an optional argument. + + If the field 'flag' is not NULL, it points to a variable that is set + to the value given in the field 'val' when the option is found, but + left unchanged if the option is not found. + + To have a long-named option do something other than set an 'int' to + a compiled-in constant, such as set a value from 'optarg', set the + option's 'flag' field to zero and its 'val' field to a nonzero + value (the equivalent single-letter option character, if there is + one). For long options that have a zero 'flag' field, 'getopt' + returns the contents of the 'val' field. */ + +struct option +{ + const char *name; + /* has_arg can't be an enum because some compilers complain about + type mismatches in all the code that assumes it is an int. */ + int has_arg; + int *flag; + int val; +}; + +/* Names for the values of the 'has_arg' field of 'struct option'. */ + +#define no_argument 0 +#define required_argument 1 +#define optional_argument 2 + +extern int getopt_long (int ___argc, char *__getopt_argv_const *___argv, + const char *__shortopts, + const struct option *__longopts, int *__longind) + __THROW /*__nonnull ((2, 3))*/; /* argp-standalone: removed __nonnull */ +extern int getopt_long_only (int ___argc, char *__getopt_argv_const *___argv, + const char *__shortopts, + const struct option *__longopts, int *__longind) + __THROW /*__nonnull ((2, 3))*/; /* argp-standalone: removed __nonnull */ + +#ifdef __cplusplus +} +#endif + +#endif /* getopt_ext.h */ diff --git a/src/argp-getopt_int.h b/src/argp-getopt_int.h index 558df48..b678d59 100644 --- a/src/argp-getopt_int.h +++ b/src/argp-getopt_int.h @@ -1,7 +1,8 @@ /* SPDX-License-Identifier: LGPL-2.1-or-later */ /* Internal declarations for getopt. - Copyright (C) 1989-2016 Free Software Foundation, Inc. - This file is part of the GNU C Library. + Copyright (C) 1989-2026 Free Software Foundation, Inc. + This file is part of the GNU C Library and is also part of gnulib. + Patches to this file should be submitted to both projects. The GNU C Library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public @@ -15,20 +16,49 @@ You should have received a copy of the GNU Lesser General Public License along with the GNU C Library; if not, see - . */ + . */ #ifndef _GETOPT_INT_H #define _GETOPT_INT_H 1 -extern int _getopt_internal (int ___argc, char *const *___argv, +/* argp-standalone: include our own renamed and patched headers from glibc. */ +#include "argp-getopt.h" + +extern int _getopt_internal (int ___argc, char **___argv, const char *__shortopts, - const struct option *__longopts, int *__longind, - int __long_only, int posixly_correct); + const struct option *__longopts, int *__longind, + int __long_only, int __posixly_correct); /* Reentrant versions which can handle parsing multiple argument vectors at the same time. */ +/* Describe how to deal with options that follow non-option ARGV-elements. + + REQUIRE_ORDER means don't recognize them as options; stop option + processing when the first non-option is seen. This is what POSIX + specifies should happen. + + PERMUTE means permute the contents of ARGV as we scan, so that + eventually all the non-options are at the end. This allows options + to be given in any order, even with programs that were not written + to expect this. + + RETURN_IN_ORDER is an option available to programs that were + written to expect options and other ARGV-elements in any order + and that care about the ordering of the two. We describe each + non-option ARGV-element as if it were the argument of an option + with character code 1. + + The special argument '--' forces an end of option-scanning regardless + of the value of 'ordering'. In the case of RETURN_IN_ORDER, only + '--' can cause 'getopt' to return -1 with 'optind' != ARGC. */ + +enum __ord + { + REQUIRE_ORDER, PERMUTE, RETURN_IN_ORDER + }; + /* Data type for reentrant functions. */ struct _getopt_data { @@ -53,75 +83,35 @@ struct _getopt_data by advancing to the next ARGV-element. */ char *__nextchar; - /* Describe how to deal with options that follow non-option ARGV-elements. - - If the caller did not specify anything, - the default is REQUIRE_ORDER if the environment variable - POSIXLY_CORRECT is defined, PERMUTE otherwise. - - REQUIRE_ORDER means don't recognize them as options; - stop option processing when the first non-option is seen. - This is what Unix does. - This mode of operation is selected by either setting the environment - variable POSIXLY_CORRECT, or using `+' as the first character - of the list of option characters. - - PERMUTE is the default. We permute the contents of ARGV as we - scan, so that eventually all the non-options are at the end. - This allows options to be given in any order, even with programs - that were not written to expect this. - - RETURN_IN_ORDER is an option available to programs that were - written to expect options and other ARGV-elements in any order - and that care about the ordering of the two. We describe each - non-option ARGV-element as if it were the argument of an option - with character code 1. Using `-' as the first character of the - list of option characters selects this mode of operation. - - The special argument `--' forces an end of option-scanning regardless - of the value of `ordering'. In the case of RETURN_IN_ORDER, only - `--' can cause `getopt' to return -1 with `optind' != ARGC. */ - - enum - { - REQUIRE_ORDER, PERMUTE, RETURN_IN_ORDER - } __ordering; - - /* If the POSIXLY_CORRECT environment variable is set. */ - int __posixly_correct; - + /* See __ord above. */ + enum __ord __ordering; /* Handle permutation of arguments. */ /* Describe the part of ARGV that contains non-options that have - been skipped. `first_nonopt' is the index in ARGV of the first - of them; `last_nonopt' is the index after the last of them. */ + been skipped. 'first_nonopt' is the index in ARGV of the first + of them; 'last_nonopt' is the index after the last of them. */ int __first_nonopt; int __last_nonopt; - -#if defined _LIBC && defined USE_NONOPTION_FLAGS - int __nonoption_flags_max_len; - int __nonoption_flags_len; -# endif }; /* The initializer is necessary to set OPTIND and OPTERR to their default values and to clear the initialization flag. */ #define _GETOPT_DATA_INITIALIZER { 1, 1 } -extern int _getopt_internal_r (int ___argc, char *const *___argv, +extern int _getopt_internal_r (int ___argc, char **___argv, const char *__shortopts, const struct option *__longopts, int *__longind, int __long_only, struct _getopt_data *__data, - int posixly_correct); + int __posixly_correct); -extern int _getopt_long_r (int ___argc, char *const *___argv, +extern int _getopt_long_r (int ___argc, char **___argv, const char *__shortopts, const struct option *__longopts, int *__longind, struct _getopt_data *__data); -extern int _getopt_long_only_r (int ___argc, char *const *___argv, +extern int _getopt_long_only_r (int ___argc, char **___argv, const char *__shortopts, const struct option *__longopts, int *__longind, diff --git a/src/argp-help.c b/src/argp-help.c index 7c92747..57b5367 100644 --- a/src/argp-help.c +++ b/src/argp-help.c @@ -1,6 +1,6 @@ /* SPDX-License-Identifier: LGPL-2.1-or-later */ /* Hierarchial argument parsing help output - Copyright (C) 1995-2016 Free Software Foundation, Inc. + Copyright (C) 1995-2026 Free Software Foundation, Inc. This file is part of the GNU C Library. Written by Miles Bader . @@ -16,7 +16,7 @@ You should have received a copy of the GNU Lesser General Public License along with the GNU C Library; if not, see - . */ + . */ #ifndef _GNU_SOURCE # define _GNU_SOURCE 1 @@ -33,7 +33,7 @@ # else # ifdef _AIX #pragma alloca -# elif defined(_WIN32) +# elif defined(_WIN32) /* argp-standalone: alloca is _alloca on Windows */ # define alloca _alloca # else # ifndef alloca /* predefined by HP cc +Olibcalls */ @@ -45,7 +45,7 @@ char *alloca (); #include #include -#include +#include /* argp-standalone: get declaration of uint8_t */ #include #include #include @@ -83,14 +83,16 @@ char *strerror (int errnum); # endif #endif -#include "argp.h" -#include "argp-fmtstream.h" +#include +#include #include "argp-namefrob.h" #ifndef SIZE_MAX # define SIZE_MAX ((size_t) -1) #endif +/* ========================================================================== */ + /* User-selectable (using an environment variable) formatting parameters. These may be specified in an environment variable called `ARGP_HELP_FMT', @@ -170,7 +172,7 @@ fill_in_uparams (const struct argp_state *state) { const char *var = getenv ("ARGP_HELP_FMT"); -#define SKIPWS(p) do { while (isspace (*p)) p++; } while (0); +#define SKIPWS(p) do { while (isspace ((unsigned char) *p)) p++; } while (0); if (var) /* Parse var. */ @@ -178,14 +180,14 @@ fill_in_uparams (const struct argp_state *state) { SKIPWS (var); - if (isalpha (*var)) + if (isalpha ((unsigned char) *var)) { size_t var_len; const struct uparam_name *un; int unspec = 0, val = 0; const char *arg = var; - while (isalnum (*arg) || *arg == '-' || *arg == '_') + while (isalnum ((unsigned char) *arg) || *arg == '-' || *arg == '_') arg++; var_len = arg - var; @@ -210,11 +212,11 @@ fill_in_uparams (const struct argp_state *state) else val = 1; } - else if (isdigit (*arg)) + else if (isdigit ((unsigned char) *arg)) { - val = atoi (arg); - while (isdigit (*arg)) - arg++; + char *ep; + val = strtol (arg, &ep, 10); + arg = ep; SKIPWS (arg); } @@ -257,6 +259,8 @@ fill_in_uparams (const struct argp_state *state) } } +/* ========================================================================== */ + /* Returns true if OPT hasn't been marked invisible. Visibility only affects whether OPT is displayed or used in sorting, not option shadowing. */ #define ovisible(opt) (! ((opt)->flags & OPTION_HIDDEN)) @@ -349,6 +353,9 @@ find_char (char ch, char *beg, char *end) return 0; } +/* -------------------------------------------------------------------------- */ +/* Data structure: HOL = Help Option List */ + struct hol_cluster; /* fwd decl */ struct hol_entry @@ -367,11 +374,11 @@ struct hol_entry char *short_options; /* Entries are sorted by their group first, in the order: - 1, 2, ..., n, 0, -m, ..., -2, -1 + 0, 1, 2, ..., n, -m, ..., -2, -1 and then alphabetically within each group. The default is 0. */ int group; - /* The cluster of options this entry belongs to, or 0 if none. */ + /* The cluster of options this entry belongs to, or NULL if none. */ struct hol_cluster *cluster; /* The argp from which this option came. */ @@ -393,7 +400,7 @@ struct hol_cluster same depth (clusters always follow options in the same group). */ int group; - /* The cluster to which this cluster belongs, or 0 if it's at the base + /* The cluster to which this cluster belongs, or NULL if it's at the base level. */ struct hol_cluster *parent; @@ -426,7 +433,7 @@ struct hol }; /* Create a struct hol from the options in ARGP. CLUSTER is the - hol_cluster in which these entries occur, or 0, if at the root. */ + hol_cluster in which these entries occur, or NULL if at the root. */ static struct hol * make_hol (const struct argp *argp, struct hol_cluster *cluster) { @@ -440,7 +447,7 @@ make_hol (const struct argp *argp, struct hol_cluster *cluster) assert (hol); hol->num_entries = 0; - hol->clusters = 0; + hol->clusters = NULL; if (opts) { @@ -544,6 +551,9 @@ hol_free (struct hol *hol) free (hol); } +/* Iterate across the short_options of the given ENTRY. Call FUNC for each. + Stop when such a call returns a non-zero value, and return this value. + If all FUNC invocations returned 0, return 0. */ static int hol_entry_short_iterate (const struct hol_entry *entry, int (*func)(const struct argp_option *opt, @@ -569,8 +579,11 @@ hol_entry_short_iterate (const struct hol_entry *entry, return val; } +/* Iterate across the long options of the given ENTRY. Call FUNC for each. + Stop when such a call returns a non-zero value, and return this value. + If all FUNC invocations returned 0, return 0. */ static inline int -__attribute__ ((always_inline)) +/*__attribute__ ((always_inline))*/ /* argp-standalone: comment out */ hol_entry_long_iterate (const struct hol_entry *entry, int (*func)(const struct argp_option *opt, const struct argp_option *real, @@ -593,7 +606,7 @@ hol_entry_long_iterate (const struct hol_entry *entry, return val; } -/* Iterator that returns true for the first short option. */ +/* A filter that returns true for the first short option of a given ENTRY. */ static inline int until_short (const struct argp_option *opt, const struct argp_option *real, const char *domain, void *cookie) @@ -606,10 +619,10 @@ static char hol_entry_first_short (const struct hol_entry *entry) { return hol_entry_short_iterate (entry, until_short, - entry->argp->argp_domain, 0); + entry->argp->argp_domain, NULL); } -/* Returns the first valid long option in ENTRY, or 0 if there is none. */ +/* Returns the first valid long option in ENTRY, or NULL if there is none. */ static const char * hol_entry_first_long (const struct hol_entry *entry) { @@ -618,10 +631,10 @@ hol_entry_first_long (const struct hol_entry *entry) for (opt = entry->opt, num = entry->num; num > 0; opt++, num--) if (opt->name && ovisible (opt)) return opt->name; - return 0; + return NULL; } -/* Returns the entry in HOL with the long option name NAME, or 0 if there is +/* Returns the entry in HOL with the long option name NAME, or NULL if there is none. */ static struct hol_entry * hol_find_entry (struct hol *hol, const char *name) @@ -643,7 +656,7 @@ hol_find_entry (struct hol *hol, const char *name) entry++; } - return 0; + return NULL; } /* If an entry with the long option NAME occurs in HOL, set it's special @@ -656,37 +669,93 @@ hol_set_group (struct hol *hol, const char *name, int group) entry->group = group; } -/* Order by group: 0, 1, 2, ..., n, -m, ..., -2, -1. - EQ is what to return if GROUP1 and GROUP2 are the same. */ +/* -------------------------------------------------------------------------- */ +/* Sorting the entries in a HOL. */ + +/* Order by group: 0, 1, 2, ..., n, -m, ..., -2, -1. */ static int -group_cmp (int group1, int group2, int eq) +group_cmp (int group1, int group2) { - if (group1 == group2) - return eq; - else if ((group1 < 0 && group2 < 0) || (group1 >= 0 && group2 >= 0)) + if ((group1 < 0 && group2 < 0) || (group1 >= 0 && group2 >= 0)) return group1 - group2; else + /* Return > 0 if group1 < 0 <= group2. + Return < 0 if group2 < 0 <= group1. */ return group2 - group1; } -/* Compare clusters CL1 & CL2 by the order that they should appear in +/* Compare clusters CL1 and CL2 by the order that they should appear in + output. Assume CL1 and CL2 have the same parent. */ +static int +hol_sibling_cluster_cmp (const struct hol_cluster *cl1, + const struct hol_cluster *cl2) +{ + /* Compare by group first. */ + int cmp = group_cmp (cl1->group, cl2->group); + if (cmp != 0) + return cmp; + + /* Within a group, compare by index within the group. */ + return cl2->index - cl1->index; +} + +/* Compare clusters CL1 and CL2 by the order that they should appear in + output. Assume CL1 and CL2 are at the same depth. */ +static int +hol_cousin_cluster_cmp (const struct hol_cluster *cl1, + const struct hol_cluster *cl2) +{ + if (cl1->parent == cl2->parent) + return hol_sibling_cluster_cmp (cl1, cl2); + else + { + /* Compare the parent clusters first. */ + int cmp = hol_cousin_cluster_cmp (cl1->parent, cl2->parent); + if (cmp != 0) + return cmp; + + /* Next, compare by group. */ + cmp = group_cmp (cl1->group, cl2->group); + if (cmp != 0) + return cmp; + + /* Next, within a group, compare by index within the group. */ + return cl2->index - cl1->index; + } +} + +/* Compare clusters CL1 and CL2 by the order that they should appear in output. */ static int hol_cluster_cmp (const struct hol_cluster *cl1, const struct hol_cluster *cl2) { /* If one cluster is deeper than the other, use its ancestor at the same - level, so that finding the common ancestor is straightforward. */ - while (cl1->depth > cl2->depth) - cl1 = cl1->parent; - while (cl2->depth > cl1->depth) - cl2 = cl2->parent; + level. Then, go by the rule that entries that are not in a sub-cluster + come before entries in a sub-cluster. */ + if (cl1->depth > cl2->depth) + { + do + cl1 = cl1->parent; + while (cl1->depth > cl2->depth); + int cmp = hol_cousin_cluster_cmp (cl1, cl2); + if (cmp != 0) + return cmp; - /* Now reduce both clusters to their ancestors at the point where both have - a common parent; these can be directly compared. */ - while (cl1->parent != cl2->parent) - cl1 = cl1->parent, cl2 = cl2->parent; + return 1; + } + else if (cl1->depth < cl2->depth) + { + do + cl2 = cl2->parent; + while (cl1->depth < cl2->depth); + int cmp = hol_cousin_cluster_cmp (cl1, cl2); + if (cmp != 0) + return cmp; - return group_cmp (cl1->group, cl2->group, cl2->index - cl1->index); + return -1; + } + else + return hol_cousin_cluster_cmp (cl1, cl2); } /* Return the ancestor of CL that's just below the root (i.e., has a parent @@ -699,17 +768,7 @@ hol_cluster_base (struct hol_cluster *cl) return cl; } -/* Return true if CL1 is a child of CL2. */ -static int -hol_cluster_is_child (const struct hol_cluster *cl1, - const struct hol_cluster *cl2) -{ - while (cl1 && cl1 != cl2) - cl1 = cl1->parent; - return cl1 == cl2; -} - -/* Given the name of a OPTION_DOC option, modifies NAME to start at the tail +/* Given the name of an OPTION_DOC option, modifies *NAME to start at the tail that should be used for comparisons, and returns true iff it should be treated as a non-option. */ static int @@ -717,92 +776,129 @@ canon_doc_option (const char **name) { int non_opt; /* Skip initial whitespace. */ - while (isspace (**name)) + while (isspace ((unsigned char) **name)) (*name)++; - /* Decide whether this looks like an option (leading `-') or not. */ + /* Decide whether this looks like an option (leading '-') or not. */ non_opt = (**name != '-'); /* Skip until part of name used for sorting. */ - while (**name && !isalnum (**name)) + while (**name && !isalnum ((unsigned char) **name)) (*name)++; return non_opt; } -/* Order ENTRY1 & ENTRY2 by the order which they should appear in a help - listing. */ +/* Order ENTRY1 and ENTRY2 by the order which they should appear in a help + listing. + This function implements a total order, that is: + - if cmp (entry1, entry2) < 0 and cmp (entry2, entry3) < 0, + then cmp (entry1, entry3) < 0. + - if cmp (entry1, entry2) < 0 and cmp (entry2, entry3) == 0, + then cmp (entry1, entry3) < 0. + - if cmp (entry1, entry2) == 0 and cmp (entry2, entry3) < 0, + then cmp (entry1, entry3) < 0. + - if cmp (entry1, entry2) == 0 and cmp (entry2, entry3) == 0, + then cmp (entry1, entry3) == 0. */ static int hol_entry_cmp (const struct hol_entry *entry1, const struct hol_entry *entry2) { - /* The group numbers by which the entries should be ordered; if either is - in a cluster, then this is just the group within the cluster. */ - int group1 = entry1->group, group2 = entry2->group; - - if (entry1->cluster != entry2->cluster) + /* First, compare the group numbers. For entries within a cluster, what + matters is the group number of the base cluster in which the entry + resides. */ + int group1 = (entry1->cluster + ? hol_cluster_base (entry1->cluster)->group + : entry1->group); + int group2 = (entry2->cluster + ? hol_cluster_base (entry2->cluster)->group + : entry2->group); + int cmp = group_cmp (group1, group2); + if (cmp != 0) + return cmp; + + /* The group numbers are the same. */ + + /* Entries that are not in a cluster come before entries in a cluster. */ + cmp = (entry1->cluster != NULL) - (entry2->cluster != NULL); + if (cmp != 0) + return cmp; + + /* Compare the clusters. */ + if (entry1->cluster != NULL) { - /* The entries are not within the same cluster, so we can't compare them - directly, we have to use the appropriate clustering level too. */ - if (! entry1->cluster) - /* ENTRY1 is at the `base level', not in a cluster, so we have to - compare it's group number with that of the base cluster in which - ENTRY2 resides. Note that if they're in the same group, the - clustered option always comes last. */ - return group_cmp (group1, hol_cluster_base (entry2->cluster)->group, -1); - else if (! entry2->cluster) - /* Likewise, but ENTRY2's not in a cluster. */ - return group_cmp (hol_cluster_base (entry1->cluster)->group, group2, 1); - else - /* Both entries are in clusters, we can just compare the clusters. */ - return hol_cluster_cmp (entry1->cluster, entry2->cluster); + cmp = hol_cluster_cmp (entry1->cluster, entry2->cluster); + if (cmp != 0) + return cmp; } - else if (group1 == group2) - /* The entries are both in the same cluster and group, so compare them - alphabetically. */ + + /* For entries in the same cluster, compare also the group numbers + within the cluster. */ + cmp = group_cmp (entry1->group, entry2->group); + if (cmp != 0) + return cmp; + + /* The entries are both in the same group and the same cluster. */ + + /* 'documentation' options always follow normal options (or documentation + options that *look* like normal options). */ + const char *long1 = hol_entry_first_long (entry1); + const char *long2 = hol_entry_first_long (entry2); + int doc1 = + (odoc (entry1->opt) ? long1 != NULL && canon_doc_option (&long1) : 0); + int doc2 = + (odoc (entry2->opt) ? long2 != NULL && canon_doc_option (&long2) : 0); + cmp = doc1 - doc2; + if (cmp != 0) + return cmp; + + /* Compare the entries alphabetically. */ + + /* First, compare the first character of the options. + Put entries without *any* valid options (such as options with + OPTION_HIDDEN set) first. But as they're not displayed, it doesn't + matter where they are. */ + int short1 = hol_entry_first_short (entry1); + int short2 = hol_entry_first_short (entry2); + unsigned char first1 = short1 ? short1 : long1 != NULL ? *long1 : 0; + unsigned char first2 = short2 ? short2 : long2 != NULL ? *long2 : 0; + /* Compare ignoring case. */ + /* Use tolower, not _tolower, since the latter has undefined behaviour + for characters that are not uppercase letters. */ + cmp = tolower (first1) - tolower (first2); + if (cmp != 0) + return cmp; + /* When the options start with the same letter (ignoring case), lower-case + comes first. */ + cmp = first2 - first1; + if (cmp != 0) + return cmp; + + /* The first character of the options agree. */ + + /* Put entries with a short option before entries without a short option. */ + cmp = (short1 != 0) - (short2 != 0); + if (cmp != 0) + return cmp; + + /* Compare entries without a short option by comparing the long option. */ + if (short1 == 0) { - int short1 = hol_entry_first_short (entry1); - int short2 = hol_entry_first_short (entry2); - int doc1 = odoc (entry1->opt); - int doc2 = odoc (entry2->opt); - const char *long1 = hol_entry_first_long (entry1); - const char *long2 = hol_entry_first_long (entry2); - - if (doc1) - doc1 = long1 != NULL && canon_doc_option (&long1); - if (doc2) - doc2 = long2 != NULL && canon_doc_option (&long2); - - if (doc1 != doc2) - /* `documentation' options always follow normal options (or - documentation options that *look* like normal options). */ - return doc1 - doc2; - else if (!short1 && !short2 && long1 && long2) - /* Only long options. */ - return __strcasecmp (long1, long2); - else - /* Compare short/short, long/short, short/long, using the first - character of long options. Entries without *any* valid - options (such as options with OPTION_HIDDEN set) will be put - first, but as they're not displayed, it doesn't matter where - they are. */ + cmp = (long1 != NULL) - (long2 != NULL); + if (cmp != 0) + return cmp; + + if (long1 != NULL) { - char first1 = short1 ? short1 : long1 ? *long1 : 0; - char first2 = short2 ? short2 : long2 ? *long2 : 0; -#ifdef _tolower - int lower_cmp = _tolower (first1) - _tolower (first2); -#else - int lower_cmp = tolower (first1) - tolower (first2); -#endif - /* Compare ignoring case, except when the options are both the - same letter, in which case lower-case always comes first. */ - return lower_cmp ? lower_cmp : first2 - first1; - } + cmp = __strcasecmp (long1, long2); + if (cmp != 0) + return cmp; + } } - else - /* Within the same cluster, but not the same group, so just compare - groups. */ - return group_cmp (group1, group2, 0); + + /* We're out of comparison criteria. At this point, if ENTRY1 != ENTRY2, + the order of these entries will be unpredictable. */ + return 0; } -/* Version of hol_entry_cmp with correct signature for qsort. */ +/* Variant of hol_entry_cmp with correct signature for qsort. */ static int hol_entry_qcmp (const void *entry1_v, const void *entry2_v) { @@ -820,6 +916,9 @@ hol_sort (struct hol *hol) hol_entry_qcmp); } +/* -------------------------------------------------------------------------- */ +/* Constructing the HOL. */ + /* Append MORE to HOL, destroying MORE in the process. Options in HOL shadow any in MORE with the same name. */ static void @@ -831,7 +930,7 @@ hol_append (struct hol *hol, struct hol *more) while (*cl_end) cl_end = &(*cl_end)->next; *cl_end = more->clusters; - more->clusters = 0; + more->clusters = NULL; /* Merge entries. */ if (more->num_entries > 0) @@ -871,7 +970,8 @@ hol_append (struct hol *hol, struct hol *more) /* Fix up the short options pointers from HOL. */ for (e = entries, left = hol->num_entries; left > 0; e++, left--) - e->short_options += (short_options - hol->short_options); + e->short_options + = short_options + (e->short_options - hol->short_options); /* Now add the short options from MORE, fixing up its entries too. */ @@ -914,6 +1014,32 @@ hol_append (struct hol *hol, struct hol *more) hol_free (more); } +/* Make a HOL containing all levels of options in ARGP. CLUSTER is the + cluster in which ARGP's entries should be clustered, or 0. */ +static struct hol * +argp_hol (const struct argp *argp, struct hol_cluster *cluster) +{ + const struct argp_child *child = argp->children; + struct hol *hol = make_hol (argp, cluster); + if (child) + while (child->argp) + { + struct hol_cluster *child_cluster = + ((child->group || child->header) + /* Put CHILD->argp within its own cluster. */ + ? hol_add_cluster (hol, child->group, child->header, + child - argp->children, cluster, argp) + /* Just merge it into the parent's cluster. */ + : cluster); + hol_append (hol, argp_hol (child->argp, child_cluster)) ; + child++; + } + return hol; +} + +/* -------------------------------------------------------------------------- */ +/* Printing the HOL. */ + /* Inserts enough spaces to make sure STREAM is at column COL. */ static void indent_to (argp_fmtstream_t stream, unsigned col) @@ -958,7 +1084,7 @@ arg (const struct argp_option *real, const char *req_fmt, const char *opt_fmt, /* State used during the execution of hol_help. */ struct hol_help_state { - /* PREV_ENTRY should contain the previous entry printed, or 0. */ + /* PREV_ENTRY should contain the previous entry printed, or NULL. */ struct hol_entry *prev_entry; /* If an entry is in a different group from the previous one, and SEP_GROUPS @@ -1036,6 +1162,16 @@ print_header (const char *str, const struct argp *argp, free ((char *) fstr); } +/* Return true if CL1 is a child of CL2. */ +static int +hol_cluster_is_child (const struct hol_cluster *cl1, + const struct hol_cluster *cl2) +{ + while (cl1 && cl1 != cl2) + cl1 = cl1->parent; + return cl1 == cl2; +} + /* Inserts a comma if this isn't the first item on the line, and then makes sure we're at least to column COL. If this *is* the first item on a line, prints any pending whitespace/headers that should precede this line. Also @@ -1167,7 +1303,7 @@ hol_entry_help (struct hol_entry *entry, const struct argp_state *state, { const char *tstr = real->doc ? dgettext (state == NULL ? NULL : state->root_argp->argp_domain, - real->doc) : 0; + real->doc) : NULL; const char *fstr = filter_doc (tstr, real->key, entry->argp, state); if (fstr && *fstr) { @@ -1207,7 +1343,7 @@ hol_help (struct hol *hol, const struct argp_state *state, { unsigned num; struct hol_entry *entry; - struct hol_help_state hhstate = { 0, 0, 0 }; + struct hol_help_state hhstate = { NULL, 0, 0 }; for (entry = hol->entries, num = hol->num_entries; num > 0; entry++, num--) hol_entry_help (entry, state, stream, &hhstate); @@ -1219,7 +1355,7 @@ hol_help (struct hol *hol, const struct argp_state *state, Mandatory or optional arguments to long options are also mandatory or \ optional for any corresponding short options."); const char *fstr = filter_doc (tstr, ARGP_KEY_HELP_DUP_ARGS_NOTE, - state ? state->root_argp : 0, state); + state ? state->root_argp : NULL, state); if (fstr && *fstr) { __argp_fmtstream_putc (stream, '\n'); @@ -1349,29 +1485,6 @@ hol_usage (struct hol *hol, argp_fmtstream_t stream) } } -/* Make a HOL containing all levels of options in ARGP. CLUSTER is the - cluster in which ARGP's entries should be clustered, or 0. */ -static struct hol * -argp_hol (const struct argp *argp, struct hol_cluster *cluster) -{ - const struct argp_child *child = argp->children; - struct hol *hol = make_hol (argp, cluster); - if (child) - while (child->argp) - { - struct hol_cluster *child_cluster = - ((child->group || child->header) - /* Put CHILD->argp within its own cluster. */ - ? hol_add_cluster (hol, child->group, child->header, - child - argp->children, cluster, argp) - /* Just merge it into the parent's cluster. */ - : cluster); - hol_append (hol, argp_hol (child->argp, child_cluster)) ; - child++; - } - return hol; -} - /* Calculate how many different levels with alternative args strings exist in ARGP. */ static size_t @@ -1402,7 +1515,7 @@ argp_args_usage (const struct argp *argp, const struct argp_state *state, char *our_level = *levels; int multiple = 0; const struct argp_child *child = argp->children; - const char *tdoc = dgettext (argp->argp_domain, argp->args_doc), *nl = 0; + const char *tdoc = dgettext (argp->argp_domain, argp->args_doc), *nl = NULL; const char *fdoc = filter_doc (tdoc, ARGP_KEY_HELP_ARGS_DOC, argp, state); if (fdoc) @@ -1451,7 +1564,7 @@ argp_args_usage (const struct argp *argp, const struct argp_state *state, } /* Print the documentation for ARGP to STREAM; if POST is false, then - everything preceeding a `\v' character in the documentation strings (or + everything preceding a `\v' character in the documentation strings (or the whole string, for those with none) is printed, otherwise, everything following the `\v' character (nothing for strings without). Each separate bit of documentation is separated a blank line, and if PRE_BLANK is true, @@ -1464,7 +1577,7 @@ argp_doc (const struct argp *argp, const struct argp_state *state, { const char *text; const char *inp_text; - void *input = 0; + void *input = NULL; int anything = 0; size_t inp_text_limit = 0; const char *doc = dgettext (argp->argp_domain, argp->doc); @@ -1473,11 +1586,11 @@ argp_doc (const struct argp *argp, const struct argp_state *state, if (doc) { char *vt = strchr (doc, '\v'); - inp_text = post ? (vt ? vt + 1 : 0) : doc; + inp_text = post ? (vt ? vt + 1 : NULL) : doc; inp_text_limit = (!post && vt) ? (vt - doc) : 0; } else - inp_text = 0; + inp_text = NULL; if (argp->help_filter) /* We have to filter the doc strings. */ @@ -1519,7 +1632,7 @@ argp_doc (const struct argp *argp, const struct argp_state *state, if (post && argp->help_filter) /* Now see if we have to output a ARGP_KEY_HELP_EXTRA text. */ { - text = (*argp->help_filter) (ARGP_KEY_HELP_EXTRA, 0, input); + text = (*argp->help_filter) (ARGP_KEY_HELP_EXTRA, NULL, input); if (text) { if (anything || pre_blank) @@ -1544,7 +1657,7 @@ argp_doc (const struct argp *argp, const struct argp_state *state, } /* Output a usage message for ARGP to STREAM. If called from - argp_state_help, STATE is the relevent parsing state. FLAGS are from the + argp_state_help, STATE is the relevant parsing state. FLAGS are from the set ARGP_HELP_*. NAME is what to use wherever a `program name' is needed. */ static void @@ -1552,7 +1665,7 @@ _help (const struct argp *argp, const struct argp_state *state, FILE *stream, unsigned flags, char *name) { int anything = 0; /* Whether we've output anything. */ - struct hol *hol = 0; + struct hol *hol = NULL; argp_fmtstream_t fs; if (! stream) @@ -1575,7 +1688,7 @@ _help (const struct argp *argp, const struct argp_state *state, FILE *stream, if (flags & (ARGP_HELP_USAGE | ARGP_HELP_SHORT_USAGE | ARGP_HELP_LONG)) { - hol = argp_hol (argp, 0); + hol = argp_hol (argp, NULL); /* If present, these options always come last. */ hol_set_group (hol, "help", -1); @@ -1692,7 +1805,7 @@ Try `%s --help' or `%s --usage' for more information.\n"), void __argp_help (const struct argp *argp, FILE *stream, unsigned flags, char *name) { - _help (argp, 0, stream, flags, name); + _help (argp, NULL, stream, flags, name); } #ifdef weak_alias weak_alias (__argp_help, argp_help) @@ -1701,10 +1814,14 @@ weak_alias (__argp_help, argp_help) #ifndef _LIBC char *__argp_basename (char *name) { - char *short_name = strrchr (name, ARGP_PATH_SEPARATOR); + char *short_name = strrchr (name, '/'); return short_name ? short_name + 1 : name; } +/* argp-standalone: there is no header that declares __progname on BSDs. + Supply our own declaration. Probably we should not do this and use + getprogname from instead, but getprogname returns const char*, + whereas argp wants char*. */ #if defined(HAVE___PROGNAME) && HAVE___PROGNAME extern char* __progname; #endif @@ -1712,7 +1829,7 @@ extern char* __progname; char * __argp_short_program_name (void) { - /* Order matters here: attempt most preferred mechanism first. */ + /* argp-standalone: attempt most preferred mechanism first. */ # if defined(HAVE_DECL_PROGRAM_INVOCATION_SHORT_NAME) && HAVE_DECL_PROGRAM_INVOCATION_SHORT_NAME return program_invocation_short_name; # elif defined(HAVE_DECL_PROGRAM_INVOCATION_NAME) && HAVE_DECL_PROGRAM_INVOCATION_NAME @@ -1743,7 +1860,7 @@ __argp_state_help (const struct argp_state *state, FILE *stream, unsigned flags) if (state && (state->flags & ARGP_LONG_ONLY)) flags |= ARGP_HELP_LONG_ONLY; - _help (state ? state->root_argp : 0, state, stream, flags, + _help (state ? state->root_argp : NULL, state, stream, flags, state ? state->name : __argp_short_program_name ()); if (!state || ! (state->flags & ARGP_NO_EXIT)) @@ -1763,7 +1880,8 @@ weak_alias (__argp_state_help, argp_state_help) by the program name and `:', to stderr, and followed by a `Try ... --help' message, then exit (1). */ void -__argp_error (const struct argp_state *state, const char *fmt, ...) +__argp_error_internal (const struct argp_state *state, const char *fmt, + va_list ap, unsigned int mode_flags) { if (!state || !(state->flags & ARGP_NO_ERRS)) { @@ -1771,18 +1889,14 @@ __argp_error (const struct argp_state *state, const char *fmt, ...) if (stream) { - va_list ap; - #if _LIBC || (HAVE_FLOCKFILE && HAVE_FUNLOCKFILE) __flockfile (stream); #endif - va_start (ap, fmt); - #ifdef _LIBC char *buf; - if (_IO_vasprintf (&buf, fmt, ap) < 0) + if (__vasprintf_internal (&buf, fmt, ap, mode_flags) < 0) buf = NULL; __fxprintf (stream, "%s: %s\n", @@ -1802,14 +1916,20 @@ __argp_error (const struct argp_state *state, const char *fmt, ...) __argp_state_help (state, stream, ARGP_HELP_STD_ERR); - va_end (ap); - #if _LIBC || (HAVE_FLOCKFILE && HAVE_FUNLOCKFILE) __funlockfile (stream); #endif } } } +void +__argp_error (const struct argp_state *state, const char *fmt, ...) +{ + va_list ap; + va_start (ap, fmt); + __argp_error_internal (state, fmt, ap, 0); + va_end (ap); +} #ifdef weak_alias weak_alias (__argp_error, argp_error) #endif @@ -1823,8 +1943,9 @@ weak_alias (__argp_error, argp_error) *parsing errors*, and the former is for other problems that occur during parsing but don't reflect a (syntactic) problem with the input. */ void -__argp_failure (const struct argp_state *state, int status, int errnum, - const char *fmt, ...) +__argp_failure_internal (const struct argp_state *state, int status, + int errnum, const char *fmt, va_list ap, + unsigned int mode_flags) { if (!state || !(state->flags & ARGP_NO_ERRS)) { @@ -1846,13 +1967,10 @@ __argp_failure (const struct argp_state *state, int status, int errnum, if (fmt) { - va_list ap; - - va_start (ap, fmt); #ifdef _LIBC char *buf; - if (_IO_vasprintf (&buf, fmt, ap) < 0) + if (__vasprintf_internal (&buf, fmt, ap, mode_flags) < 0) buf = NULL; __fxprintf (stream, ": %s", buf); @@ -1864,15 +1982,11 @@ __argp_failure (const struct argp_state *state, int status, int errnum, vfprintf (stream, fmt, ap); #endif - - va_end (ap); } if (errnum) { -/* #if defined(_LIBC) || (defined(HAVE_STRERROR_R) && HAVE_STRERROR_R) */ char buf[200]; -/* #endif */ #ifdef _LIBC __fxprintf (stream, ": %s", @@ -1880,18 +1994,17 @@ __argp_failure (const struct argp_state *state, int status, int errnum, #else putc_unlocked (':', stream); putc_unlocked (' ', stream); + /* argp-standalone: use our replacement no matter what. */ fputs (argp_compat_strerror (errnum, buf, sizeof (buf)), stream); -#if 0 -# if defined(HAVE_STRERROR_R) && HAVE_STRERROR_R +/*# ifdef HAVE_STRERROR_R fputs (__strerror_r (errnum, buf, sizeof (buf)), stream); # else fputs (strerror (errnum), stream); -# endif -#endif +# endif*/ #endif } -#ifdef USE_IN_LIBIO +#ifdef _LIBC if (_IO_fwide (stream, 0) > 0) putwc_unlocked (L'\n', stream); else @@ -1907,6 +2020,15 @@ __argp_failure (const struct argp_state *state, int status, int errnum, } } } +void +__argp_failure (const struct argp_state *state, int status, int errnum, + const char *fmt, ...) +{ + va_list ap; + va_start (ap, fmt); + __argp_failure_internal (state, status, errnum, fmt, ap, 0); + va_end (ap); +} #ifdef weak_alias weak_alias (__argp_failure, argp_failure) #endif diff --git a/src/argp-namefrob.h b/src/argp-namefrob.h index 911dcbc..f8c8b84 100644 --- a/src/argp-namefrob.h +++ b/src/argp-namefrob.h @@ -1,6 +1,6 @@ /* SPDX-License-Identifier: LGPL-2.1-or-later */ /* Name frobnication for compiling argp outside of glibc - Copyright (C) 1997-2016 Free Software Foundation, Inc. + Copyright (C) 1997-2026 Free Software Foundation, Inc. This file is part of the GNU C Library. Written by Miles Bader . @@ -16,11 +16,7 @@ You should have received a copy of the GNU Lesser General Public License along with the GNU C Library; if not, see - . */ - -#ifdef HAVE_CONFIG_H -# include -#endif + . */ #if !_LIBC /* This code is written for inclusion in gnu-libc, and uses names in the @@ -81,19 +77,6 @@ #undef __argp_fmtstream_wmargin #define __argp_fmtstream_wmargin argp_fmtstream_wmargin -#if defined(HAVE_MEMPCPY_H) && HAVE_MEMPCPY_H -# include "mempcpy.h" -#endif -#if defined(HAVE_STRCASE_H) && HAVE_STRCASE_H -# include "strcase.h" -#endif -#if defined(HAVE_STRCHRNUL_H) && HAVE_STRCHRNUL_H -# include "strchrnul.h" -#endif -#if defined(HAVE_STRNDUP_H) && HAVE_STRNDUP_H -# include "strndup.h" -#endif - /* normal libc functions we call */ #undef __flockfile #define __flockfile flockfile @@ -111,8 +94,6 @@ #define __strerror_r strerror_r #undef __strndup #define __strndup strndup -#undef __vsnprintf -#define __vsnprintf vsnprintf #if defined(HAVE_DECL_CLEARERR_UNLOCKED) && !HAVE_DECL_CLEARERR_UNLOCKED # define clearerr_unlocked(x) clearerr (x) @@ -162,4 +143,12 @@ extern char *__argp_basename (char *name); #define __set_errno(e) (errno = (e)) #endif +/* argp-standalone: this is a micro optimization that turns calls to + _argp_short_program_name() into reads of program_invocation_short_name + if it is available. But this only complicates matters more by adding + even more conditional compilation, so we comment it out and move on. */ +/*#if defined _LIBC || HAVE_DECL_PROGRAM_INVOCATION_SHORT_NAME +# define __argp_short_program_name() (program_invocation_short_name) +#else*/ extern char *__argp_short_program_name (void); +/*#endif*/ diff --git a/src/argp-parse.c b/src/argp-parse.c index db170b8..ed36a71 100644 --- a/src/argp-parse.c +++ b/src/argp-parse.c @@ -1,6 +1,6 @@ /* SPDX-License-Identifier: LGPL-2.1-or-later */ /* Hierarchial argument parsing, layered over getopt - Copyright (C) 1995-2016 Free Software Foundation, Inc. + Copyright (C) 1995-2026 Free Software Foundation, Inc. This file is part of the GNU C Library. Written by Miles Bader . @@ -16,12 +16,14 @@ You should have received a copy of the GNU Lesser General Public License along with the GNU C Library; if not, see - . */ + . */ #ifdef HAVE_CONFIG_H #include #endif +/* argp-standalone: when compiling with glibc we need to define _GNU_SOURCE + to get program_invocation_short_name and program_invocation_name. */ #if (defined(HAVE_DECL_PROGRAM_INVOCATION_SHORT_NAME) && HAVE_DECL_PROGRAM_INVOCATION_SHORT_NAME) || \ (defined(HAVE_DECL_PROGRAM_INVOCATION_NAME) && HAVE_DECL_PROGRAM_INVOCATION_NAME) #ifndef _GNU_SOURCE @@ -29,29 +31,14 @@ #endif #endif -/* AIX requires this to be the first thing in the file. */ -#ifndef __GNUC__ -# if HAVE_ALLOCA_H || defined _LIBC -# include -# else -# ifdef _AIX -#pragma alloca -# elif defined(_WIN32) -# define alloca _alloca -# else -# ifndef alloca /* predefined by HP cc +Olibcalls */ -char *alloca (); -# endif -# endif -# endif -#endif - #include #include +/* argp-standalone: only include if it's available. */ #if defined(HAVE_UNISTD_H) && HAVE_UNISTD_H # include #endif #include +/* argp-standalone: include our own renamed and patched headers from glibc. */ #include "argp-getopt.h" #include "argp-getopt_int.h" @@ -74,9 +61,9 @@ char *alloca (); # define N_(msgid) (msgid) #endif -#include "argp.h" +#include #include "argp-namefrob.h" -#include "argp-compat.h" +#include "argp-compat.h" /* argp-standalone: include compatibility header */ /* Getopt return values. */ #define KEY_END (-1) /* The end of the options. */ @@ -111,13 +98,13 @@ static volatile int _argp_hang; static const struct argp_option argp_default_options[] = { - {"help", '?', 0, 0, N_("Give this help list"), -1}, - {"usage", OPT_USAGE, 0, 0, N_("Give a short usage message")}, + {"help", '?', NULL, 0, N_("Give this help list"), -1}, + {"usage", OPT_USAGE, NULL, 0, N_("Give a short usage message")}, {"program-name",OPT_PROGNAME, N_("NAME"), OPTION_HIDDEN, N_("Set the program name")}, {"HANG", OPT_HANG, N_("SECS"), OPTION_ARG_OPTIONAL | OPTION_HIDDEN, N_("Hang for SECS seconds (default 3600)")}, - {0, 0} + {NULL, 0} }; static error_t @@ -134,7 +121,7 @@ argp_default_parser (int key, char *arg, struct argp_state *state) break; case OPT_PROGNAME: /* Set the program name. */ -#if defined _LIBC || (defined(HAVE_DECL_PROGRAM_INVOCATION_NAME) && HAVE_DECL_PROGRAM_INVOCATION_NAME) +#if defined _LIBC || HAVE_DECL_PROGRAM_INVOCATION_NAME program_invocation_name = arg; #endif /* [Note that some systems only have PROGRAM_INVOCATION_SHORT_NAME (aka @@ -142,13 +129,13 @@ argp_default_parser (int key, char *arg, struct argp_state *state) to be that, so we have to be a bit careful here.] */ /* Update what we use for messages. */ - state->name = strrchr (arg, ARGP_PATH_SEPARATOR); + state->name = strrchr (arg, '/'); if (state->name) state->name++; else state->name = arg; -#if defined _LIBC || (defined(HAVE_DECL_PROGRAM_INVOCATION_SHORT_NAME) && HAVE_DECL_PROGRAM_INVOCATION_SHORT_NAME) +#if defined _LIBC || HAVE_DECL_PROGRAM_INVOCATION_SHORT_NAME program_invocation_short_name = state->name; #endif @@ -160,7 +147,7 @@ argp_default_parser (int key, char *arg, struct argp_state *state) break; case OPT_HANG: - _argp_hang = atoi (arg ? arg : "3600"); + _argp_hang = arg ? strtol (arg, NULL, 10) : 3600; while (_argp_hang-- > 0) __sleep (1); break; @@ -177,8 +164,8 @@ static const struct argp argp_default_argp = static const struct argp_option argp_version_options[] = { - {"version", 'V', 0, 0, N_("Print program version"), -1}, - {0, 0} + {"version", 'V', NULL, 0, N_("Print program version"), -1}, + {NULL, 0} }; static error_t @@ -241,7 +228,7 @@ struct group particular short options is from. */ char *short_end; - /* The number of non-option args sucessfully handled by this parser. */ + /* The number of non-option args successfully handled by this parser. */ unsigned args_processed; /* This group's parser's parent's group. */ @@ -369,7 +356,7 @@ convert_options (const struct argp *argp, ? optional_argument : required_argument) : no_argument); - cvt->long_end->flag = 0; + cvt->long_end->flag = NULL; /* we add a disambiguating code to all the user's values (which is removed before we actually call the function to parse the value); this means that @@ -392,9 +379,9 @@ convert_options (const struct argp *argp, group->args_processed = 0; group->parent = parent; group->parent_index = parent_index; - group->input = 0; - group->hook = 0; - group->child_inputs = 0; + group->input = NULL; + group->hook = NULL; + group->child_inputs = NULL; if (children) /* Assign GROUP's CHILD_INPUTS field some space from @@ -410,7 +397,7 @@ convert_options (const struct argp *argp, parent = group++; } else - parent = 0; + parent = NULL; if (children) { @@ -445,7 +432,7 @@ parser_convert (struct parser *parser, const struct argp *argp, int flags) parser->argp = argp; if (argp) - parser->egroup = convert_options (argp, 0, 0, parser->groups, &cvt); + parser->egroup = convert_options (argp, NULL, 0, parser->groups, &cvt); else parser->egroup = parser->groups; /* No parsers at all! */ } @@ -519,19 +506,11 @@ parser_init (struct parser *parser, const struct argp *argp, return ENOMEM; parser->groups = parser->storage; -#if defined(_MSC_VER) - /* Fix for compilers that don't support void pointer arithmetics. */ - { - char* p = parser->storage; - parser->child_inputs = (void*)(p + GLEN); - parser->long_opts = (void*)(p + GLEN + CLEN); - parser->short_opts = (void*)(p + GLEN + CLEN + LLEN); - } -#else - parser->child_inputs = parser->storage + GLEN; - parser->long_opts = parser->storage + GLEN + CLEN; - parser->short_opts = parser->storage + GLEN + CLEN + LLEN; -#endif + /* argp-standalone: no void pointer arithmetic */ + {char* p = parser->storage; + parser->child_inputs = (void*)(p + GLEN); + parser->long_opts = (void*)(p + GLEN + CLEN); + parser->short_opts = (void*)(p + GLEN + CLEN + LLEN);} parser->opt_data = opt_data; memset (parser->child_inputs, 0, szs.num_child_inputs * sizeof (void *)); @@ -568,7 +547,7 @@ parser_init (struct parser *parser, const struct argp *argp, makes very simple wrapper argps more convenient). */ group->child_inputs[0] = group->input; - err = group_parse (group, &parser->state, ARGP_KEY_INIT, 0); + err = group_parse (group, &parser->state, ARGP_KEY_INIT, NULL); } if (err == EBADKEY) err = 0; /* Some parser didn't understand. */ @@ -590,7 +569,7 @@ parser_init (struct parser *parser, const struct argp *argp, if (parser->state.argv == argv && argv[0]) /* There's an argv[0]; use it for messages. */ { - char *short_name = strrchr (argv[0], ARGP_PATH_SEPARATOR); + char *short_name = strrchr (argv[0], '/'); parser->state.name = short_name ? short_name + 1 : argv[0]; } else @@ -620,11 +599,11 @@ parser_finalize (struct parser *parser, group < parser->egroup && (!err || err==EBADKEY); group++) if (group->args_processed == 0) - err = group_parse (group, &parser->state, ARGP_KEY_NO_ARGS, 0); + err = group_parse (group, &parser->state, ARGP_KEY_NO_ARGS, NULL); for (group = parser->egroup - 1; group >= parser->groups && (!err || err==EBADKEY); group--) - err = group_parse (group, &parser->state, ARGP_KEY_END, 0); + err = group_parse (group, &parser->state, ARGP_KEY_END, NULL); if (err == EBADKEY) err = 0; /* Some parser didn't understand. */ @@ -663,7 +642,7 @@ parser_finalize (struct parser *parser, /* Since we didn't exit, give each parser an error indication. */ for (group = parser->groups; group < parser->egroup; group++) - group_parse (group, &parser->state, ARGP_KEY_ERROR, 0); + group_parse (group, &parser->state, ARGP_KEY_ERROR, NULL); } else /* Notify parsers of success, and propagate back values from parsers. */ @@ -674,14 +653,14 @@ parser_finalize (struct parser *parser, for (group = parser->egroup - 1 ; group >= parser->groups && (!err || err == EBADKEY) ; group--) - err = group_parse (group, &parser->state, ARGP_KEY_SUCCESS, 0); + err = group_parse (group, &parser->state, ARGP_KEY_SUCCESS, NULL); if (err == EBADKEY) err = 0; /* Some parser didn't understand. */ } /* Call parsers once more, to do any final cleanup. Errors are ignored. */ for (group = parser->egroup - 1; group >= parser->groups; group--) - group_parse (group, &parser->state, ARGP_KEY_FINI, 0); + group_parse (group, &parser->state, ARGP_KEY_FINI, NULL); if (err == EBADKEY) err = EINVAL; @@ -720,7 +699,7 @@ parser_parse_arg (struct parser *parser, char *val) { parser->state.next--; /* For ARGP_KEY_ARGS, put back the arg. */ key = ARGP_KEY_ARGS; - err = group_parse (group, &parser->state, key, 0); + err = group_parse (group, &parser->state, key, NULL); } } @@ -773,12 +752,15 @@ parser_parse_opt (struct parser *parser, int opt, char *val) } } else - /* A long option. We use shifts instead of masking for extracting - the user value in order to preserve the sign. */ - err = - group_parse (&parser->groups[group_key - 1], &parser->state, - (opt << GROUP_BITS) >> GROUP_BITS, - parser->opt_data.optarg); + /* A long option. Preserve the sign in the user key, without + invoking undefined behavior. Assume two's complement. */ + { + int user_key = + ((opt & (1 << (USER_BITS - 1))) ? ~USER_MASK : 0) | (opt & USER_MASK); + err = + group_parse (&parser->groups[group_key - 1], &parser->state, + user_key, parser->opt_data.optarg); + } if (err == EBADKEY) /* At least currently, an option not recognized is an error in the @@ -830,11 +812,11 @@ parser_parse_next (struct parser *parser, int *arg_ebadkey) parser->opt_data.optopt = KEY_END; if (parser->state.flags & ARGP_LONG_ONLY) opt = _getopt_long_only_r (parser->state.argc, parser->state.argv, - parser->short_opts, parser->long_opts, 0, + parser->short_opts, parser->long_opts, NULL, &parser->opt_data); else opt = _getopt_long_r (parser->state.argc, parser->state.argv, - parser->short_opts, parser->long_opts, 0, + parser->short_opts, parser->long_opts, NULL, &parser->opt_data); /* And see what getopt did. */ parser->state.next = parser->opt_data.optind; @@ -907,6 +889,9 @@ __argp_parse (const struct argp *argp, int argc, char **argv, unsigned flags, error_t err; struct parser parser; + struct argp_child child[4]; + struct argp top_argp; + /* If true, then err == EBADKEY is a result of a non-option argument failing to be parsed (which in some cases isn't actually an error). */ int arg_ebadkey = 0; @@ -914,24 +899,23 @@ __argp_parse (const struct argp *argp, int argc, char **argv, unsigned flags, if (! (flags & ARGP_NO_HELP)) /* Add our own options. */ { - struct argp_child *child = alloca (4 * sizeof (struct argp_child)); - struct argp *top_argp = alloca (sizeof (struct argp)); + int child_index = 0; /* TOP_ARGP has no options, it just serves to group the user & default argps. */ - memset (top_argp, 0, sizeof (*top_argp)); - top_argp->children = child; + memset (&top_argp, 0, sizeof (struct argp)); + top_argp.children = child; memset (child, 0, 4 * sizeof (struct argp_child)); if (argp) - (child++)->argp = argp; - (child++)->argp = &argp_default_argp; + child[child_index++].argp = argp; + child[child_index++].argp = &argp_default_argp; if (argp_program_version || argp_program_version_hook) - (child++)->argp = &argp_version_argp; - child->argp = 0; + child[child_index++].argp = &argp_version_argp; + child[child_index].argp = NULL; - argp = top_argp; + argp = &top_argp; } /* Construct a parser for these arguments. */ @@ -966,7 +950,7 @@ __argp_input (const struct argp *argp, const struct argp_state *state) return group->input; } - return 0; + return NULL; } #ifdef weak_alias weak_alias (__argp_input, _argp_input) diff --git a/src/argp-pv.c b/src/argp-pv.c index 83b447a..b52944e 100644 --- a/src/argp-pv.c +++ b/src/argp-pv.c @@ -1,6 +1,6 @@ /* SPDX-License-Identifier: LGPL-2.1-or-later */ /* Default definition for ARGP_PROGRAM_VERSION. - Copyright (C) 1996-2016 Free Software Foundation, Inc. + Copyright (C) 1996-2026 Free Software Foundation, Inc. This file is part of the GNU C Library. Written by Miles Bader . @@ -16,7 +16,7 @@ You should have received a copy of the GNU Lesser General Public License along with the GNU C Library; if not, see - . */ + . */ /* If set by the user program to a non-zero value, then a default option --version is added (unless the ARGP_NO_HELP flag is used), which will diff --git a/src/argp-pvh.c b/src/argp-pvh.c index cbfff4a..f7d9dbb 100644 --- a/src/argp-pvh.c +++ b/src/argp-pvh.c @@ -1,6 +1,6 @@ /* SPDX-License-Identifier: LGPL-2.1-or-later */ /* Default definition for ARGP_PROGRAM_VERSION_HOOK. - Copyright (C) 1996-2016 Free Software Foundation, Inc. + Copyright (C) 1996-2026 Free Software Foundation, Inc. This file is part of the GNU C Library. Written by Miles Bader . @@ -16,7 +16,7 @@ You should have received a copy of the GNU Lesser General Public License along with the GNU C Library; if not, see - . */ + . */ #ifdef HAVE_CONFIG_H #include diff --git a/src/argp-xinl.c b/src/argp-xinl.c index 0be1171..4a70c57 100644 --- a/src/argp-xinl.c +++ b/src/argp-xinl.c @@ -1,6 +1,6 @@ /* SPDX-License-Identifier: LGPL-2.1-or-later */ /* Real definitions for extern inline functions in argp.h - Copyright (C) 1997-2016 Free Software Foundation, Inc. + Copyright (C) 1997-2025 Free Software Foundation, Inc. This file is part of the GNU C Library. Written by Miles Bader . @@ -16,7 +16,9 @@ You should have received a copy of the GNU Lesser General Public License along with the GNU C Library; if not, see - . */ + . */ + +/* argp-standalone: old argp-xinl.c from glibc 2.42 works better for us. */ #ifdef HAVE_CONFIG_H # include @@ -32,7 +34,7 @@ #define ARGP_EI #undef __OPTIMIZE__ #define __OPTIMIZE__ 1 -#include "argp.h" +#include /* Add weak aliases. */ #if _LIBC - 0 && defined (weak_alias) diff --git a/src/getopt.c b/src/getopt.c index 3c2d9ee..a9f6f27 100644 --- a/src/getopt.c +++ b/src/getopt.c @@ -1,10 +1,8 @@ /* SPDX-License-Identifier: LGPL-2.1-or-later */ /* Getopt for GNU. - NOTE: getopt is part of the C library, so if you don't know what - "Keep this file name-space clean" means, talk to drepper@gnu.org - before changing it! - Copyright (C) 1987-2016 Free Software Foundation, Inc. - This file is part of the GNU C Library. + Copyright (C) 1987-2026 Free Software Foundation, Inc. + This file is part of the GNU C Library and is also part of gnulib. + Patches to this file should be submitted to both projects. The GNU C Library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public @@ -18,123 +16,96 @@ You should have received a copy of the GNU Lesser General Public License along with the GNU C Library; if not, see - . */ + . */ -/* This tells Alpha OSF/1 not to define a getopt prototype in . - Ditto for AIX 3.2 and . */ -#ifndef _NO_PROTO -# define _NO_PROTO -#endif - +/* argp-standalone: changed condition, use HAVE_CONFIG_H, not _LIBC. */ #ifdef HAVE_CONFIG_H # include #endif -/* AIX requires this to be the first thing in the file. */ -#ifndef __GNUC__ -# if HAVE_ALLOCA_H || defined _LIBC -# include -# else -# ifdef _AIX -#pragma alloca -# elif defined(_WIN32) -# define alloca _alloca -# else -# ifndef alloca /* predefined by HP cc +Olibcalls */ -char* alloca(); -# endif -# endif -# endif -#endif +/* argp-standalone: include our own renamed and patched headers from glibc. */ +#include "argp-getopt.h" -#include #include - -/* Comment out all this code if we are using the GNU C Library, and are not - actually compiling the library itself. This code is part of the GNU C - Library, but also included in many other GNU distributions. Compiling - and linking in this code is a waste when using the GNU C library - (especially if it is a shared library). Rather than having every GNU - program understand `configure --with-gnu-libc' and omit the object files, - it is simpler to just do this in the source for each such file. */ - -#define GETOPT_INTERFACE_VERSION 2 -#if !defined _LIBC && defined __GLIBC__ && __GLIBC__ >= 2 -# include -# if _GNU_GETOPT_INTERFACE_VERSION == GETOPT_INTERFACE_VERSION -# define ELIDE_CODE -# endif -#endif - -/*#ifndef ELIDE_CODE*/ - - -/* This needs to come after some library #include - to get __GNU_LIBRARY__ defined. */ -#ifdef __GNU_LIBRARY__ -/* Don't include stdlib.h for non-GNU C libraries because some of them - contain conflicting prototypes for getopt. */ -# include -# include -#endif /* GNU C library. */ - +#include #include - -#ifdef VMS -# include -#endif +/* argp-standalone: do not include unistd.h. It may provide declarations that + conflict with definitions provided by us, e.g. optarg etc. on Cygwin. */ +/*#include */ #ifdef _LIBC +/* When used as part of glibc, error printing must be done differently + for standards compliance. getopt is not a cancellation point, so + it must not call functions that are, and it is specified by an + older standard than stdio locking, so it must not refer to + functions in the "user namespace" related to stdio locking. + Finally, it must use glibc's internal message translation so that + the messages are looked up in the proper text domain. */ # include +# define fprintf __fxprintf_nocancel +# define flockfile(fp) _IO_flockfile (fp) +# define funlockfile(fp) _IO_funlockfile (fp) #else +/* argp-standalone: we currently do not support gettext. */ /*# include "gettext.h" # define _(msgid) gettext (msgid)*/ # define _(msgid) msgid +/* When used standalone, flockfile and funlockfile might not be + available. */ +# if (!defined _POSIX_THREAD_SAFE_FUNCTIONS \ + || (defined _WIN32 && ! defined __CYGWIN__)) +# define flockfile(fp) /* nop */ +# define funlockfile(fp) /* nop */ +# endif +/* When used standalone, do not attempt to use alloca. */ +# define __libc_use_alloca(size) 0 +# undef alloca +# define alloca(size) (abort (), (void *)0) #endif -#if defined _LIBC -# include -#endif - -#ifndef attribute_hidden -# define attribute_hidden -#endif - -/* This version of `getopt' appears to the caller like standard Unix `getopt' - but it behaves differently for the user, since it allows the user - to intersperse the options with the other arguments. - - As `getopt' works, it permutes the elements of ARGV so that, - when it is done, all the options precede everything else. Thus - all application programs are extended to handle flexible argument order. - - Setting the environment variable POSIXLY_CORRECT disables permutation. - Then the behavior is completely standard. - - GNU application programs can use a third alternative mode in which - they can distinguish the relative order of options and other arguments. */ - -#include "argp-getopt.h" +/* This implementation of 'getopt' has three modes for handling + options interspersed with non-option arguments. It can stop + scanning for options at the first non-option argument encountered, + as POSIX specifies. It can continue scanning for options after the + first non-option argument, but permute 'argv' as it goes so that, + after 'getopt' is done, all the options precede all the non-option + arguments and 'optind' points to the first non-option argument. + Or, it can report non-option arguments as if they were arguments to + the option character '\x01'. + + The default behavior of 'getopt_long' is to permute the argument list. + When this implementation is used standalone, the default behavior of + 'getopt' is to stop at the first non-option argument, but when it is + used as part of GNU libc it also permutes the argument list. In both + cases, setting the environment variable POSIXLY_CORRECT to any value + disables permutation. + + If the first character of the OPTSTRING argument to 'getopt' or + 'getopt_long' is '+', both functions will stop at the first + non-option argument. If it is '-', both functions will report + non-option arguments as arguments to the option character '\x01'. */ + +/* argp-standalone: include our own renamed and patched headers from glibc. */ #include "argp-getopt_int.h" -/* For communication from `getopt' to the caller. - When `getopt' finds an option that takes an argument, +/* For communication from 'getopt' to the caller. + When 'getopt' finds an option that takes an argument, the argument value is returned here. - Also, when `ordering' is RETURN_IN_ORDER, + Also, when 'ordering' is RETURN_IN_ORDER, each non-option ARGV-element is returned here. */ char *optarg; /* Index in ARGV of the next element to be scanned. This is used for communication to and from the caller - and for communication between successive calls to `getopt'. + and for communication between successive calls to 'getopt'. - On entry to `getopt', zero means this is the first call; initialize. + On entry to 'getopt', zero means this is the first call; initialize. - When `getopt' returns -1, this is the index of the first of the + When 'getopt' returns -1, this is the index of the first of the non-option elements that the caller should itself scan. - Otherwise, `optind' communicates from one call to the next + Otherwise, 'optind' communicates from one call to the next how much of ARGV has been scanned so far. */ /* 1003.2 says this must be 1 before any call. */ @@ -154,56 +125,14 @@ int optopt = '?'; /* Keep a global copy of all internal members of getopt_data. */ static struct _getopt_data getopt_data; - - -#ifndef __GNU_LIBRARY__ - -/* Avoid depending on library functions or files - whose names are inconsistent. */ - -/*#ifndef getenv -extern char *getenv (); -#endif*/ - -#endif /* not __GNU_LIBRARY__ */ -#ifdef _LIBC -/* Stored original parameters. - XXX This is no good solution. We should rather copy the args so - that we can compare them later. But we must not use malloc(3). */ -extern int __libc_argc; -extern char **__libc_argv; - -/* Bash 2.0 gives us an environment variable containing flags - indicating ARGV elements that should not be considered arguments. */ - -# ifdef USE_NONOPTION_FLAGS -/* Defined in getopt_init.c */ -extern char *__getopt_nonoption_flags; -# endif - -# ifdef USE_NONOPTION_FLAGS -# define SWAP_FLAGS(ch1, ch2) \ - if (d->__nonoption_flags_len > 0) \ - { \ - char __tmp = __getopt_nonoption_flags[ch1]; \ - __getopt_nonoption_flags[ch1] = __getopt_nonoption_flags[ch2]; \ - __getopt_nonoption_flags[ch2] = __tmp; \ - } -# else -# define SWAP_FLAGS(ch1, ch2) -# endif -#else /* !_LIBC */ -# define SWAP_FLAGS(ch1, ch2) -#endif /* _LIBC */ - /* Exchange two adjacent subsequences of ARGV. One subsequence is elements [first_nonopt,last_nonopt) which contains all the non-options that have been skipped so far. The other is elements [last_nonopt,optind), which contains all the options processed since those non-options were skipped. - `first_nonopt' and `last_nonopt' are relocated so that they describe + 'first_nonopt' and 'last_nonopt' are relocated so that they describe the new indices of the non-options in ARGV after they are moved. */ static void @@ -219,28 +148,6 @@ exchange (char **argv, struct _getopt_data *d) It leaves the longer segment in the right place overall, but it consists of two parts that need to be swapped next. */ -#if defined _LIBC && defined USE_NONOPTION_FLAGS - /* First make sure the handling of the `__getopt_nonoption_flags' - string can work normally. Our top argument must be in the range - of the string. */ - if (d->__nonoption_flags_len > 0 && top >= d->__nonoption_flags_max_len) - { - /* We must extend the array. The user plays games with us and - presents new arguments. */ - char *new_str = malloc (top + 1); - if (new_str == NULL) - d->__nonoption_flags_len = d->__nonoption_flags_max_len = 0; - else - { - memset (__mempcpy (new_str, __getopt_nonoption_flags, - d->__nonoption_flags_max_len), - '\0', top + 1 - d->__nonoption_flags_max_len); - d->__nonoption_flags_max_len = top + 1; - __getopt_nonoption_flags = new_str; - } - } -#endif - while (top > middle && middle > bottom) { if (top - middle > middle - bottom) @@ -255,7 +162,6 @@ exchange (char **argv, struct _getopt_data *d) tem = argv[bottom + i]; argv[bottom + i] = argv[top - (middle - bottom) + i]; argv[top - (middle - bottom) + i] = tem; - SWAP_FLAGS (bottom + i, top - (middle - bottom) + i); } /* Exclude the moved bottom segment from further swapping. */ top -= len; @@ -272,7 +178,6 @@ exchange (char **argv, struct _getopt_data *d) tem = argv[bottom + i]; argv[bottom + i] = argv[middle + i]; argv[middle + i] = tem; - SWAP_FLAGS (bottom + i, middle + i); } /* Exclude the moved top segment from further swapping. */ bottom += len; @@ -285,24 +190,217 @@ exchange (char **argv, struct _getopt_data *d) d->__last_nonopt = d->optind; } -/* Initialize the internal data when the first call is made. */ +/* Process the argument starting with d->__nextchar as a long option. + d->optind should *not* have been advanced over this argument. + + If the value returned is -1, it was not actually a long option, the + state is unchanged, and the argument should be processed as a set + of short options (this can only happen when long_only is true). + Otherwise, the option (and its argument, if any) have been consumed + and the return value is the value to return from _getopt_internal_r. */ +static int +process_long_option (int argc, char **argv, const char *optstring, + const struct option *longopts, int *longind, + int long_only, struct _getopt_data *d, + int print_errors, const char *prefix) +{ + char *nameend; + size_t namelen; + const struct option *p; + const struct option *pfound = NULL; + int n_options; + int option_index; + + for (nameend = d->__nextchar; *nameend && *nameend != '='; nameend++) + /* Do nothing. */ ; + namelen = nameend - d->__nextchar; + + /* First look for an exact match, counting the options as a side + effect. */ + for (p = longopts, n_options = 0; p->name; p++, n_options++) + if (!strncmp (p->name, d->__nextchar, namelen) + && namelen == strlen (p->name)) + { + /* Exact match found. */ + pfound = p; + option_index = n_options; + break; + } + + if (pfound == NULL) + { + /* Didn't find an exact match, so look for abbreviations. */ + unsigned char *ambig_set = NULL; + int ambig_malloced = 0; + int ambig_fallback = 0; + int indfound = -1; + + for (p = longopts, option_index = 0; p->name; p++, option_index++) + if (!strncmp (p->name, d->__nextchar, namelen)) + { + if (pfound == NULL) + { + /* First nonexact match found. */ + pfound = p; + indfound = option_index; + } + else if (long_only + || pfound->has_arg != p->has_arg + || pfound->flag != p->flag + || pfound->val != p->val) + { + /* Second or later nonexact match found. */ + if (!ambig_fallback) + { + if (!print_errors) + /* Don't waste effort tracking the ambig set if + we're not going to print it anyway. */ + ambig_fallback = 1; + else if (!ambig_set) + { + if (__libc_use_alloca (n_options)) + ambig_set = alloca (n_options); + else if ((ambig_set = malloc (n_options)) == NULL) + /* Fall back to simpler error message. */ + ambig_fallback = 1; + else + ambig_malloced = 1; + + if (ambig_set) + { + memset (ambig_set, 0, n_options); + ambig_set[indfound] = 1; + } + } + if (ambig_set) + ambig_set[option_index] = 1; + } + } + } + + if (ambig_set || ambig_fallback) + { + if (print_errors) + { + if (ambig_fallback) + fprintf (stderr, _("%s: option '%s%s' is ambiguous\n"), + argv[0], prefix, d->__nextchar); + else + { + flockfile (stderr); + fprintf (stderr, + _("%s: option '%s%s' is ambiguous; possibilities:"), + argv[0], prefix, d->__nextchar); + + for (option_index = 0; option_index < n_options; option_index++) + if (ambig_set[option_index]) + fprintf (stderr, " '%s%s'", + prefix, longopts[option_index].name); + + /* This must use 'fprintf' even though it's only + printing a single character, so that it goes through + __fxprintf_nocancel when compiled as part of glibc. */ + fprintf (stderr, "\n"); + funlockfile (stderr); + } + } + if (ambig_malloced) + free (ambig_set); + d->__nextchar += strlen (d->__nextchar); + d->optind++; + d->optopt = 0; + return '?'; + } + + option_index = indfound; + } + + if (pfound == NULL) + { + /* Can't find it as a long option. If this is not getopt_long_only, + or the option starts with '--' or is not a valid short option, + then it's an error. */ + if (!long_only || argv[d->optind][1] == '-' + || strchr (optstring, *d->__nextchar) == NULL) + { + if (print_errors) + fprintf (stderr, _("%s: unrecognized option '%s%s'\n"), + argv[0], prefix, d->__nextchar); + + d->__nextchar = NULL; + d->optind++; + d->optopt = 0; + return '?'; + } + + /* Otherwise interpret it as a short option. */ + return -1; + } + + /* We have found a matching long option. Consume it. */ + d->optind++; + d->__nextchar = NULL; + if (*nameend) + { + /* Don't test has_arg with >, because some C compilers don't + allow it to be used on enums. */ + if (pfound->has_arg) + d->optarg = nameend + 1; + else + { + if (print_errors) + fprintf (stderr, + _("%s: option '%s%s' doesn't allow an argument\n"), + argv[0], prefix, pfound->name); + + d->optopt = pfound->val; + return '?'; + } + } + else if (pfound->has_arg == 1) + { + if (d->optind < argc) + d->optarg = argv[d->optind++]; + else + { + if (print_errors) + fprintf (stderr, + _("%s: option '%s%s' requires an argument\n"), + argv[0], prefix, pfound->name); + + d->optopt = pfound->val; + return optstring[0] == ':' ? ':' : '?'; + } + } + + if (longind != NULL) + *longind = option_index; + if (pfound->flag) + { + *(pfound->flag) = pfound->val; + return 0; + } + return pfound->val; +} + +/* Initialize internal data upon the first call to getopt. */ static const char * -_getopt_initialize (int argc, char *const *argv, const char *optstring, +/* argp-standalone: do not bother to supply _GL_UNUSED. */ +_getopt_initialize (/*_GL_UNUSED*/ int argc, + /*_GL_UNUSED*/ char **argv, const char *optstring, struct _getopt_data *d, int posixly_correct) { /* Start processing options with ARGV-element 1 (since ARGV-element 0 is the program name); the sequence of previously skipped non-option ARGV-elements is empty. */ + if (d->optind == 0) + d->optind = 1; d->__first_nonopt = d->__last_nonopt = d->optind; - d->__nextchar = NULL; - d->__posixly_correct = posixly_correct | !!getenv ("POSIXLY_CORRECT"); - /* Determine how to handle the ordering of options and nonoptions. */ - if (optstring[0] == '-') { d->__ordering = RETURN_IN_ORDER; @@ -313,41 +411,12 @@ _getopt_initialize (int argc, char *const *argv, const char *optstring, d->__ordering = REQUIRE_ORDER; ++optstring; } - else if (d->__posixly_correct) + else if (posixly_correct || !!getenv ("POSIXLY_CORRECT")) d->__ordering = REQUIRE_ORDER; else d->__ordering = PERMUTE; -#if defined _LIBC && defined USE_NONOPTION_FLAGS - if (!d->__posixly_correct - && argc == __libc_argc && argv == __libc_argv) - { - if (d->__nonoption_flags_max_len == 0) - { - if (__getopt_nonoption_flags == NULL - || __getopt_nonoption_flags[0] == '\0') - d->__nonoption_flags_max_len = -1; - else - { - const char *orig_str = __getopt_nonoption_flags; - int len = d->__nonoption_flags_max_len = strlen (orig_str); - if (d->__nonoption_flags_max_len < argc) - d->__nonoption_flags_max_len = argc; - __getopt_nonoption_flags = - (char *) malloc (d->__nonoption_flags_max_len); - if (__getopt_nonoption_flags == NULL) - d->__nonoption_flags_max_len = -1; - else - memset (__mempcpy (__getopt_nonoption_flags, orig_str, len), - '\0', d->__nonoption_flags_max_len - len); - } - } - d->__nonoption_flags_len = d->__nonoption_flags_max_len; - } - else - d->__nonoption_flags_len = 0; -#endif - + d->__initialized = 1; return optstring; } @@ -356,48 +425,48 @@ _getopt_initialize (int argc, char *const *argv, const char *optstring, If an element of ARGV starts with '-', and is not exactly "-" or "--", then it is an option element. The characters of this element - (aside from the initial '-') are option characters. If `getopt' + (aside from the initial '-') are option characters. If 'getopt' is called repeatedly, it returns successively each of the option characters from each of the option elements. - If `getopt' finds another option character, it returns that character, - updating `optind' and `nextchar' so that the next call to `getopt' can + If 'getopt' finds another option character, it returns that character, + updating 'optind' and 'nextchar' so that the next call to 'getopt' can resume the scan with the following option character or ARGV-element. - If there are no more option characters, `getopt' returns -1. - Then `optind' is the index in ARGV of the first ARGV-element + If there are no more option characters, 'getopt' returns -1. + Then 'optind' is the index in ARGV of the first ARGV-element that is not an option. (The ARGV-elements have been permuted so that those that are not options now come last.) OPTSTRING is a string containing the legitimate option characters. If an option character is seen that is not listed in OPTSTRING, - return '?' after printing an error message. If you set `opterr' to + return '?' after printing an error message. If you set 'opterr' to zero, the error message is suppressed but we still return '?'. If a char in OPTSTRING is followed by a colon, that means it wants an arg, so the following text in the same ARGV-element, or the text of the following - ARGV-element, is returned in `optarg'. Two colons mean an option that + ARGV-element, is returned in 'optarg'. Two colons mean an option that wants an optional arg; if there is text in the current ARGV-element, - it is returned in `optarg', otherwise `optarg' is set to zero. + it is returned in 'optarg', otherwise 'optarg' is set to zero. - If OPTSTRING starts with `-' or `+', it requests different methods of + If OPTSTRING starts with '-' or '+', it requests different methods of handling the non-option ARGV-elements. See the comments about RETURN_IN_ORDER and REQUIRE_ORDER, above. - Long-named options begin with `--' instead of `-'. + Long-named options begin with '--' instead of '-'. Their names may be abbreviated as long as the abbreviation is unique or is an exact match for some defined option. If they have an argument, it follows the option name in the same ARGV-element, separated - from the option name by a `=', or else the in next ARGV-element. - When `getopt' finds a long-named option, it returns 0 if that option's - `flag' field is nonzero, the value of the option's `val' field - if the `flag' field is zero. + from the option name by a '=', or else the in next ARGV-element. + When 'getopt' finds a long-named option, it returns 0 if that option's + 'flag' field is nonzero, the value of the option's 'val' field + if the 'flag' field is zero. The elements of ARGV aren't really const, because we permute them. But we pretend they're const in the prototype to be compatible with other systems. - LONGOPTS is a vector of `struct option' terminated by an + LONGOPTS is a vector of 'struct option' terminated by an element containing a name which is zero. LONGIND returns the index in LONGOPT of the long-named option found. @@ -408,7 +477,7 @@ _getopt_initialize (int argc, char *const *argv, const char *optstring, long-named options. */ int -_getopt_internal_r (int argc, char *const *argv, const char *optstring, +_getopt_internal_r (int argc, char **argv, const char *optstring, const struct option *longopts, int *longind, int long_only, struct _getopt_data *d, int posixly_correct) { @@ -420,29 +489,15 @@ _getopt_internal_r (int argc, char *const *argv, const char *optstring, d->optarg = NULL; if (d->optind == 0 || !d->__initialized) - { - if (d->optind == 0) - d->optind = 1; /* Don't scan ARGV[0], the program name. */ - optstring = _getopt_initialize (argc, argv, optstring, d, - posixly_correct); - d->__initialized = 1; - } + optstring = _getopt_initialize (argc, argv, optstring, d, posixly_correct); else if (optstring[0] == '-' || optstring[0] == '+') optstring++; + if (optstring[0] == ':') print_errors = 0; - /* Test whether ARGV[optind] points to a non-option argument. - Either it does not have option syntax, or there is an environment flag - from the shell indicating it is not an option. The later information - is only used when the used in the GNU libc. */ -#if defined _LIBC && defined USE_NONOPTION_FLAGS -# define NONOPTION_P (argv[d->optind][0] != '-' || argv[d->optind][1] == '\0' \ - || (d->optind < d->__nonoption_flags_len \ - && __getopt_nonoption_flags[d->optind] == '1')) -#else -# define NONOPTION_P (argv[d->optind][0] != '-' || argv[d->optind][1] == '\0') -#endif + /* Test whether ARGV[optind] points to a non-option argument. */ +#define NONOPTION_P (argv[d->optind][0] != '-' || argv[d->optind][1] == '\0') if (d->__nextchar == NULL || *d->__nextchar == '\0') { @@ -462,7 +517,7 @@ _getopt_internal_r (int argc, char *const *argv, const char *optstring, if (d->__first_nonopt != d->__last_nonopt && d->__last_nonopt != d->optind) - exchange ((char **) argv, d); + exchange (argv, d); else if (d->__last_nonopt != d->optind) d->__first_nonopt = d->optind; @@ -474,7 +529,7 @@ _getopt_internal_r (int argc, char *const *argv, const char *optstring, d->__last_nonopt = d->optind; } - /* The special ARGV-element `--' means premature end of options. + /* The special ARGV-element '--' means premature end of options. Skip it like a null option, then exchange with previous non-options as if it were an option, then skip everything else like a non-option. */ @@ -485,7 +540,7 @@ _getopt_internal_r (int argc, char *const *argv, const char *optstring, if (d->__first_nonopt != d->__last_nonopt && d->__last_nonopt != d->optind) - exchange ((char **) argv, d); + exchange (argv, d); else if (d->__first_nonopt == d->__last_nonopt) d->__first_nonopt = d->optind; d->__last_nonopt = argc; @@ -517,426 +572,79 @@ _getopt_internal_r (int argc, char *const *argv, const char *optstring, } /* We have found another option-ARGV-element. - Skip the initial punctuation. */ - - d->__nextchar = (argv[d->optind] + 1 - + (longopts != NULL && argv[d->optind][1] == '-')); - } - - /* Decode the current option-ARGV-element. */ - - /* Check whether the ARGV-element is a long option. - - If long_only and the ARGV-element has the form "-f", where f is - a valid short option, don't consider it an abbreviated form of - a long option that starts with f. Otherwise there would be no - way to give the -f short option. - - On the other hand, if there's a long option "fubar" and - the ARGV-element is "-fu", do consider that an abbreviation of - the long option, just like "--fu", and not "-f" with arg "u". - - This distinction seems to be the most useful approach. */ - - if (longopts != NULL - && (argv[d->optind][1] == '-' - || (long_only && (argv[d->optind][2] - || !strchr (optstring, argv[d->optind][1]))))) - { - char *nameend; - unsigned int namelen; - const struct option *p; - const struct option *pfound = NULL; - struct option_list - { - const struct option *p; - struct option_list *next; - } *ambig_list = NULL; - int exact = 0; - int indfound = -1; - int option_index; - - for (nameend = d->__nextchar; *nameend && *nameend != '='; nameend++) - /* Do nothing. */ ; - namelen = nameend - d->__nextchar; - - /* Test all long options for either exact match - or abbreviated matches. */ - for (p = longopts, option_index = 0; p->name; p++, option_index++) - if (!strncmp (p->name, d->__nextchar, namelen)) - { - if (namelen == (unsigned int) strlen (p->name)) - { - /* Exact match found. */ - pfound = p; - indfound = option_index; - exact = 1; - break; - } - else if (pfound == NULL) - { - /* First nonexact match found. */ - pfound = p; - indfound = option_index; - } - else if (long_only - || pfound->has_arg != p->has_arg - || pfound->flag != p->flag - || pfound->val != p->val) - { - /* Second or later nonexact match found. */ - struct option_list *newp = alloca (sizeof (*newp)); - newp->p = p; - newp->next = ambig_list; - ambig_list = newp; - } - } - - if (ambig_list != NULL && !exact) - { - if (print_errors) - { - struct option_list first; - first.p = pfound; - first.next = ambig_list; - ambig_list = &first; - -#if defined _LIBC - char *buf = NULL; - size_t buflen = 0; - - FILE *fp = __open_memstream (&buf, &buflen); - if (fp != NULL) - { - fprintf (fp, - _("%s: option '%s' is ambiguous; possibilities:"), - argv[0], argv[d->optind]); - - do - { - fprintf (fp, " '--%s'", ambig_list->p->name); - ambig_list = ambig_list->next; - } - while (ambig_list != NULL); - - fputc_unlocked ('\n', fp); - - if (__glibc_likely (fclose (fp) != EOF)) - { - _IO_flockfile (stderr); - - int old_flags2 = ((_IO_FILE *) stderr)->_flags2; - ((_IO_FILE *) stderr)->_flags2 |= _IO_FLAGS2_NOTCANCEL; - - __fxprintf (NULL, "%s", buf); - - ((_IO_FILE *) stderr)->_flags2 = old_flags2; - _IO_funlockfile (stderr); - - free (buf); - } - } -#else - fprintf (stderr, - _("%s: option '%s' is ambiguous; possibilities:"), - argv[0], argv[d->optind]); - do - { - fprintf (stderr, " '--%s'", ambig_list->p->name); - ambig_list = ambig_list->next; - } - while (ambig_list != NULL); - - fputc ('\n', stderr); -#endif - } - d->__nextchar += strlen (d->__nextchar); - d->optind++; - d->optopt = 0; - return '?'; - } - - if (pfound != NULL) + Check whether it might be a long option. */ + if (longopts) { - option_index = indfound; - d->optind++; - if (*nameend) + if (argv[d->optind][1] == '-') { - /* Don't test has_arg with >, because some C compilers don't - allow it to be used on enums. */ - if (pfound->has_arg) - d->optarg = nameend + 1; - else - { - if (print_errors) - { -#if defined _LIBC - char *buf; - int n; -#endif - - if (argv[d->optind - 1][1] == '-') - { - /* --option */ -#if defined _LIBC - n = __asprintf (&buf, _("\ -%s: option '--%s' doesn't allow an argument\n"), - argv[0], pfound->name); -#else - fprintf (stderr, _("\ -%s: option '--%s' doesn't allow an argument\n"), - argv[0], pfound->name); -#endif - } - else - { - /* +option or -option */ -#if defined _LIBC - n = __asprintf (&buf, _("\ -%s: option '%c%s' doesn't allow an argument\n"), - argv[0], argv[d->optind - 1][0], - pfound->name); -#else - fprintf (stderr, _("\ -%s: option '%c%s' doesn't allow an argument\n"), - argv[0], argv[d->optind - 1][0], - pfound->name); -#endif - } - -#if defined _LIBC - if (n >= 0) - { - _IO_flockfile (stderr); - - int old_flags2 = ((_IO_FILE *) stderr)->_flags2; - ((_IO_FILE *) stderr)->_flags2 - |= _IO_FLAGS2_NOTCANCEL; - - __fxprintf (NULL, "%s", buf); - - ((_IO_FILE *) stderr)->_flags2 = old_flags2; - _IO_funlockfile (stderr); - - free (buf); - } -#endif - } - - d->__nextchar += strlen (d->__nextchar); - - d->optopt = pfound->val; - return '?'; - } + /* "--foo" is always a long option. The special option + "--" was handled above. */ + d->__nextchar = argv[d->optind] + 2; + return process_long_option (argc, argv, optstring, longopts, + longind, long_only, d, + print_errors, "--"); } - else if (pfound->has_arg == 1) - { - if (d->optind < argc) - d->optarg = argv[d->optind++]; - else - { - if (print_errors) - { -#if defined _LIBC - char *buf; - - if (__asprintf (&buf, _("\ -%s: option '--%s' requires an argument\n"), - argv[0], pfound->name) >= 0) - { - _IO_flockfile (stderr); - - int old_flags2 = ((_IO_FILE *) stderr)->_flags2; - ((_IO_FILE *) stderr)->_flags2 - |= _IO_FLAGS2_NOTCANCEL; - __fxprintf (NULL, "%s", buf); + /* If long_only and the ARGV-element has the form "-f", + where f is a valid short option, don't consider it an + abbreviated form of a long option that starts with f. + Otherwise there would be no way to give the -f short + option. - ((_IO_FILE *) stderr)->_flags2 = old_flags2; - _IO_funlockfile (stderr); + On the other hand, if there's a long option "fubar" and + the ARGV-element is "-fu", do consider that an + abbreviation of the long option, just like "--fu", and + not "-f" with arg "u". - free (buf); - } -#else - fprintf (stderr, - _("%s: option '--%s' requires an argument\n"), - argv[0], pfound->name); -#endif - } - d->__nextchar += strlen (d->__nextchar); - d->optopt = pfound->val; - return optstring[0] == ':' ? ':' : '?'; - } - } - d->__nextchar += strlen (d->__nextchar); - if (longind != NULL) - *longind = option_index; - if (pfound->flag) + This distinction seems to be the most useful approach. */ + if (long_only && (argv[d->optind][2] + || !strchr (optstring, argv[d->optind][1]))) { - *(pfound->flag) = pfound->val; - return 0; + int code; + d->__nextchar = argv[d->optind] + 1; + code = process_long_option (argc, argv, optstring, longopts, + longind, long_only, d, + print_errors, "-"); + if (code != -1) + return code; } - return pfound->val; } - /* Can't find it as a long option. If this is not getopt_long_only, - or the option starts with '--' or is not a valid short - option, then it's an error. - Otherwise interpret it as a short option. */ - if (!long_only || argv[d->optind][1] == '-' - || strchr (optstring, *d->__nextchar) == NULL) - { - if (print_errors) - { -#if defined _LIBC - char *buf; - int n; -#endif - - if (argv[d->optind][1] == '-') - { - /* --option */ -#if defined _LIBC - n = __asprintf (&buf, _("%s: unrecognized option '--%s'\n"), - argv[0], d->__nextchar); -#else - fprintf (stderr, _("%s: unrecognized option '--%s'\n"), - argv[0], d->__nextchar); -#endif - } - else - { - /* +option or -option */ -#if defined _LIBC - n = __asprintf (&buf, _("%s: unrecognized option '%c%s'\n"), - argv[0], argv[d->optind][0], d->__nextchar); -#else - fprintf (stderr, _("%s: unrecognized option '%c%s'\n"), - argv[0], argv[d->optind][0], d->__nextchar); -#endif - } - -#if defined _LIBC - if (n >= 0) - { - _IO_flockfile (stderr); - - int old_flags2 = ((_IO_FILE *) stderr)->_flags2; - ((_IO_FILE *) stderr)->_flags2 |= _IO_FLAGS2_NOTCANCEL; - - __fxprintf (NULL, "%s", buf); - - ((_IO_FILE *) stderr)->_flags2 = old_flags2; - _IO_funlockfile (stderr); - - free (buf); - } -#endif - } - d->__nextchar = (char *) ""; - d->optind++; - d->optopt = 0; - return '?'; - } + /* It is not a long option. Skip the initial punctuation. */ + d->__nextchar = argv[d->optind] + 1; } /* Look at and handle the next short option-character. */ { char c = *d->__nextchar++; - char *temp = strchr (optstring, c); + const char *temp = strchr (optstring, c); - /* Increment `optind' when we start to process its last character. */ + /* Increment 'optind' when we start to process its last character. */ if (*d->__nextchar == '\0') ++d->optind; if (temp == NULL || c == ':' || c == ';') { if (print_errors) - { -#if defined _LIBC - char *buf; - int n; -#endif - -#if defined _LIBC - n = __asprintf (&buf, _("%s: invalid option -- '%c'\n"), - argv[0], c); -#else - fprintf (stderr, _("%s: invalid option -- '%c'\n"), argv[0], c); -#endif - -#if defined _LIBC - if (n >= 0) - { - _IO_flockfile (stderr); - - int old_flags2 = ((_IO_FILE *) stderr)->_flags2; - ((_IO_FILE *) stderr)->_flags2 |= _IO_FLAGS2_NOTCANCEL; - - __fxprintf (NULL, "%s", buf); - - ((_IO_FILE *) stderr)->_flags2 = old_flags2; - _IO_funlockfile (stderr); - - free (buf); - } -#endif - } + fprintf (stderr, _("%s: invalid option -- '%c'\n"), argv[0], c); d->optopt = c; return '?'; } + /* Convenience. Treat POSIX -W foo same as long option --foo */ - if (temp[0] == 'W' && temp[1] == ';') + if (temp[0] == 'W' && temp[1] == ';' && longopts != NULL) { - if (longopts == NULL) - goto no_longs; - - char *nameend; - const struct option *p; - const struct option *pfound = NULL; - int exact = 0; - int ambig = 0; - int indfound = 0; - int option_index; - /* This is an option that requires an argument. */ if (*d->__nextchar != '\0') - { - d->optarg = d->__nextchar; - /* If we end this ARGV-element by taking the rest as an arg, - we must advance to the next element now. */ - d->optind++; - } + d->optarg = d->__nextchar; else if (d->optind == argc) { if (print_errors) - { -#if defined _LIBC - char *buf; - - if (__asprintf (&buf, - _("%s: option requires an argument -- '%c'\n"), - argv[0], c) >= 0) - { - _IO_flockfile (stderr); - - int old_flags2 = ((_IO_FILE *) stderr)->_flags2; - ((_IO_FILE *) stderr)->_flags2 |= _IO_FLAGS2_NOTCANCEL; - - __fxprintf (NULL, "%s", buf); - - ((_IO_FILE *) stderr)->_flags2 = old_flags2; - _IO_funlockfile (stderr); + fprintf (stderr, + _("%s: option requires an argument -- '%c'\n"), + argv[0], c); - free (buf); - } -#else - fprintf (stderr, - _("%s: option requires an argument -- '%c'\n"), - argv[0], c); -#endif - } d->optopt = c; if (optstring[0] == ':') c = ':'; @@ -945,172 +653,12 @@ _getopt_internal_r (int argc, char *const *argv, const char *optstring, return c; } else - /* We already incremented `d->optind' once; - increment it again when taking next ARGV-elt as argument. */ - d->optarg = argv[d->optind++]; - - /* optarg is now the argument, see if it's in the - table of longopts. */ - - for (d->__nextchar = nameend = d->optarg; *nameend && *nameend != '='; - nameend++) - /* Do nothing. */ ; - - /* Test all long options for either exact match - or abbreviated matches. */ - for (p = longopts, option_index = 0; p->name; p++, option_index++) - if (!strncmp (p->name, d->__nextchar, nameend - d->__nextchar)) - { - if ((unsigned int) (nameend - d->__nextchar) == strlen (p->name)) - { - /* Exact match found. */ - pfound = p; - indfound = option_index; - exact = 1; - break; - } - else if (pfound == NULL) - { - /* First nonexact match found. */ - pfound = p; - indfound = option_index; - } - else if (long_only - || pfound->has_arg != p->has_arg - || pfound->flag != p->flag - || pfound->val != p->val) - /* Second or later nonexact match found. */ - ambig = 1; - } - if (ambig && !exact) - { - if (print_errors) - { -#if defined _LIBC - char *buf; - - if (__asprintf (&buf, _("%s: option '-W %s' is ambiguous\n"), - argv[0], d->optarg) >= 0) - { - _IO_flockfile (stderr); - - int old_flags2 = ((_IO_FILE *) stderr)->_flags2; - ((_IO_FILE *) stderr)->_flags2 |= _IO_FLAGS2_NOTCANCEL; - - __fxprintf (NULL, "%s", buf); - - ((_IO_FILE *) stderr)->_flags2 = old_flags2; - _IO_funlockfile (stderr); - - free (buf); - } -#else - fprintf (stderr, _("%s: option '-W %s' is ambiguous\n"), - argv[0], d->optarg); -#endif - } - d->__nextchar += strlen (d->__nextchar); - d->optind++; - return '?'; - } - if (pfound != NULL) - { - option_index = indfound; - if (*nameend) - { - /* Don't test has_arg with >, because some C compilers don't - allow it to be used on enums. */ - if (pfound->has_arg) - d->optarg = nameend + 1; - else - { - if (print_errors) - { -#if defined _LIBC - char *buf; - - if (__asprintf (&buf, _("\ -%s: option '-W %s' doesn't allow an argument\n"), - argv[0], pfound->name) >= 0) - { - _IO_flockfile (stderr); - - int old_flags2 = ((_IO_FILE *) stderr)->_flags2; - ((_IO_FILE *) stderr)->_flags2 - |= _IO_FLAGS2_NOTCANCEL; - - __fxprintf (NULL, "%s", buf); - - ((_IO_FILE *) stderr)->_flags2 = old_flags2; - _IO_funlockfile (stderr); + d->optarg = argv[d->optind]; - free (buf); - } -#else - fprintf (stderr, _("\ -%s: option '-W %s' doesn't allow an argument\n"), - argv[0], pfound->name); -#endif - } - - d->__nextchar += strlen (d->__nextchar); - return '?'; - } - } - else if (pfound->has_arg == 1) - { - if (d->optind < argc) - d->optarg = argv[d->optind++]; - else - { - if (print_errors) - { -#if defined _LIBC - char *buf; - - if (__asprintf (&buf, _("\ -%s: option '-W %s' requires an argument\n"), - argv[0], pfound->name) >= 0) - { - _IO_flockfile (stderr); - - int old_flags2 = ((_IO_FILE *) stderr)->_flags2; - ((_IO_FILE *) stderr)->_flags2 - |= _IO_FLAGS2_NOTCANCEL; - - __fxprintf (NULL, "%s", buf); - - ((_IO_FILE *) stderr)->_flags2 = old_flags2; - _IO_funlockfile (stderr); - - free (buf); - } -#else - fprintf (stderr, _("\ -%s: option '-W %s' requires an argument\n"), - argv[0], pfound->name); -#endif - } - d->__nextchar += strlen (d->__nextchar); - return optstring[0] == ':' ? ':' : '?'; - } - } - else - d->optarg = NULL; - d->__nextchar += strlen (d->__nextchar); - if (longind != NULL) - *longind = option_index; - if (pfound->flag) - { - *(pfound->flag) = pfound->val; - return 0; - } - return pfound->val; - } - - no_longs: - d->__nextchar = NULL; - return 'W'; /* Let the application handle it. */ + d->__nextchar = d->optarg; + d->optarg = NULL; + return process_long_option (argc, argv, optstring, longopts, longind, + 0 /* long_only */, d, print_errors, "-W "); } if (temp[1] == ':') { @@ -1139,32 +687,10 @@ _getopt_internal_r (int argc, char *const *argv, const char *optstring, else if (d->optind == argc) { if (print_errors) - { -#if defined _LIBC - char *buf; + fprintf (stderr, + _("%s: option requires an argument -- '%c'\n"), + argv[0], c); - if (__asprintf (&buf, _("\ -%s: option requires an argument -- '%c'\n"), - argv[0], c) >= 0) - { - _IO_flockfile (stderr); - - int old_flags2 = ((_IO_FILE *) stderr)->_flags2; - ((_IO_FILE *) stderr)->_flags2 |= _IO_FLAGS2_NOTCANCEL; - - __fxprintf (NULL, "%s", buf); - - ((_IO_FILE *) stderr)->_flags2 = old_flags2; - _IO_funlockfile (stderr); - - free (buf); - } -#else - fprintf (stderr, - _("%s: option requires an argument -- '%c'\n"), - argv[0], c); -#endif - } d->optopt = c; if (optstring[0] == ':') c = ':'; @@ -1172,7 +698,7 @@ _getopt_internal_r (int argc, char *const *argv, const char *optstring, c = '?'; } else - /* We already incremented `optind' once; + /* We already incremented 'optind' once; increment it again when taking next ARGV-elt as argument. */ d->optarg = argv[d->optind++]; d->__nextchar = NULL; @@ -1183,7 +709,7 @@ _getopt_internal_r (int argc, char *const *argv, const char *optstring, } int -_getopt_internal (int argc, char *const *argv, const char *optstring, +_getopt_internal (int argc, char **argv, const char *optstring, const struct option *longopts, int *longind, int long_only, int posixly_correct) { @@ -1203,32 +729,30 @@ _getopt_internal (int argc, char *const *argv, const char *optstring, return result; } -int -getopt (int argc, char *const *argv, const char *optstring) -{ - return _getopt_internal (argc, argv, optstring, - (const struct option *) 0, - (int *) 0, - 0, 0); -} +/* glibc gets a LSB-compliant getopt and a POSIX-complaint __posix_getopt. + Standalone applications just get a POSIX-compliant getopt. + POSIX and LSB both require these functions to take 'char *const *argv' + even though this is incorrect (because of the permutation). */ +#define GETOPT_ENTRY(NAME, POSIXLY_CORRECT) \ + int \ + NAME (int argc, char *const *argv, const char *optstring) \ + { \ + return _getopt_internal (argc, (char **)argv, optstring, \ + NULL, NULL, 0, POSIXLY_CORRECT); \ + } #ifdef _LIBC -int -__posix_getopt (int argc, char *const *argv, const char *optstring) -{ - return _getopt_internal (argc, argv, optstring, - (const struct option *) 0, - (int *) 0, - 0, 1); -} +GETOPT_ENTRY(getopt, 0) +GETOPT_ENTRY(__posix_getopt, 1) +#else +GETOPT_ENTRY(getopt, 1) #endif -/*#endif*/ /* Not ELIDE_CODE. */ #ifdef TEST /* Compile with -DTEST to make an executable for use in testing - the above definition of `getopt'. */ + the above definition of 'getopt'. */ int main (int argc, char **argv) diff --git a/src/getopt1.c b/src/getopt1.c index 2123fad..dbb58ea 100644 --- a/src/getopt1.c +++ b/src/getopt1.c @@ -1,7 +1,8 @@ /* SPDX-License-Identifier: LGPL-2.1-or-later */ /* getopt_long and getopt_long_only entry points for GNU getopt. - Copyright (C) 1987-2016 Free Software Foundation, Inc. - This file is part of the GNU C Library. + Copyright (C) 1987-2026 Free Software Foundation, Inc. + This file is part of the GNU C Library and is also part of gnulib. + Patches to this file should be submitted to both projects. The GNU C Library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public @@ -15,59 +16,26 @@ You should have received a copy of the GNU Lesser General Public License along with the GNU C Library; if not, see - . */ + . */ -#ifdef HAVE_CONFIG_H -#include +#ifndef _LIBC +# include #endif -#ifdef _LIBC -# include -#else -# include "argp-getopt.h" -#endif +/* argp-standalone: include our own renamed and patched headers from glibc. */ +#include "argp-getopt.h" #include "argp-getopt_int.h" -#include - -/* Comment out all this code if we are using the GNU C Library, and are not - actually compiling the library itself. This code is part of the GNU C - Library, but also included in many other GNU distributions. Compiling - and linking in this code is a waste when using the GNU C library - (especially if it is a shared library). Rather than having every GNU - program understand `configure --with-gnu-libc' and omit the object files, - it is simpler to just do this in the source for each such file. */ - -#define GETOPT_INTERFACE_VERSION 2 -#if !defined _LIBC && defined __GLIBC__ && __GLIBC__ >= 2 -#include -#if _GNU_GETOPT_INTERFACE_VERSION == GETOPT_INTERFACE_VERSION -#define ELIDE_CODE -#endif -#endif - -/*#ifndef ELIDE_CODE*/ - - -/* This needs to come after some library #include - to get __GNU_LIBRARY__ defined. */ -#ifdef __GNU_LIBRARY__ -#include -#endif - -#ifndef NULL -#define NULL 0 -#endif - int -getopt_long (int argc, char *const *argv, const char *options, +getopt_long (int argc, char *__getopt_argv_const *argv, const char *options, const struct option *long_options, int *opt_index) { - return _getopt_internal (argc, argv, options, long_options, opt_index, 0, 0); + return _getopt_internal (argc, (char **) argv, options, long_options, + opt_index, 0, 0); } int -_getopt_long_r (int argc, char *const *argv, const char *options, +_getopt_long_r (int argc, char **argv, const char *options, const struct option *long_options, int *opt_index, struct _getopt_data *d) { @@ -81,14 +49,16 @@ _getopt_long_r (int argc, char *const *argv, const char *options, instead. */ int -getopt_long_only (int argc, char *const *argv, const char *options, +getopt_long_only (int argc, char *__getopt_argv_const *argv, + const char *options, const struct option *long_options, int *opt_index) { - return _getopt_internal (argc, argv, options, long_options, opt_index, 1, 0); + return _getopt_internal (argc, (char **) argv, options, long_options, + opt_index, 1, 0); } int -_getopt_long_only_r (int argc, char *const *argv, const char *options, +_getopt_long_only_r (int argc, char **argv, const char *options, const struct option *long_options, int *opt_index, struct _getopt_data *d) { @@ -96,11 +66,11 @@ _getopt_long_only_r (int argc, char *const *argv, const char *options, 1, d, 0); } -/*#endif*/ /* Not ELIDE_CODE. */ #ifdef TEST #include +#include int main (int argc, char **argv) @@ -112,7 +82,7 @@ main (int argc, char **argv) { int this_option_optind = optind ? optind : 1; int option_index = 0; - static struct option long_options[] = + static const struct option long_options[] = { {"add", 1, 0, 0}, {"append", 0, 0, 0}, @@ -162,11 +132,11 @@ main (int argc, char **argv) break; case 'c': - printf ("option c with value `%s'\n", optarg); + printf ("option c with value '%s'\n", optarg); break; case 'd': - printf ("option d with value `%s'\n", optarg); + printf ("option d with value '%s'\n", optarg); break; case '?': diff --git a/test/CMakeLists.txt b/test/CMakeLists.txt index bc6ae61..e2c578e 100644 --- a/test/CMakeLists.txt +++ b/test/CMakeLists.txt @@ -8,9 +8,6 @@ if(WIN32) target_compile_definitions(argp-test PRIVATE _CRT_NONSTDC_NO_DEPRECATE) target_compile_definitions(argp-test PRIVATE _CRT_SECURE_NO_WARNINGS) endif() -if(CMAKE_C_COMPILER_ID MATCHES "GNU") - target_compile_options(argp-test PRIVATE -Wno-unused-result) -endif() argp_standalone_target_enable_warnings(argp-test) target_include_directories(argp-test PRIVATE "${PROJECT_BINARY_DIR}") target_link_libraries(argp-test PRIVATE argp-standalone) @@ -23,10 +20,19 @@ target_link_libraries(argp-compat-test PRIVATE argp-standalone) add_test(NAME argp-compat-test COMMAND argp-compat-test) add_executable(argp-failure-test argp-failure-test.c) +argp_standalone_target_enable_warnings(argp-failure-test) target_link_libraries(argp-failure-test PRIVATE argp-standalone) add_test(NAME argp-failure-test COMMAND argp-failure-test) set_tests_properties(argp-failure-test PROPERTIES PASS_REGULAR_EXPRESSION "Error message format string: Permission denied") +add_executable(argp-cpp-test argp-cpp-test.cpp) +argp_standalone_target_enable_warnings(argp-cpp-test) +target_compile_definitions(argp-cpp-test PRIVATE HAVE_CONFIG_H) +target_include_directories(argp-cpp-test PRIVATE "${PROJECT_BINARY_DIR}" ../src) +target_link_libraries(argp-cpp-test PRIVATE argp-standalone) +add_test(NAME argp-cpp-test COMMAND argp-cpp-test) +set_tests_properties(argp-cpp-test PROPERTIES PASS_REGULAR_EXPRESSION "failure message") + add_executable(tst-argp1 tst-argp1.c) argp_standalone_target_enable_warnings(tst-argp1) target_link_libraries(tst-argp1 PRIVATE argp-standalone) @@ -36,3 +42,11 @@ add_executable(tst-argp2 tst-argp2.c) argp_standalone_target_enable_warnings(tst-argp2) target_link_libraries(tst-argp2 PRIVATE argp-standalone) add_test(NAME tst-argp2 COMMAND tst-argp2) + +add_executable(tst-ldbl-argp tst-ldbl-argp.c) +argp_standalone_target_enable_warnings(tst-ldbl-argp) +target_link_libraries(tst-ldbl-argp PRIVATE argp-standalone) +add_test(NAME tst-ldbl-argp-error COMMAND tst-ldbl-argp --error) +set_tests_properties(tst-ldbl-argp-error PROPERTIES PASS_REGULAR_EXPRESSION "test-argp: -1.000000-2.000000-3.000000-4.000000\nTry `test-argp --help' or `test-argp --usage' for more information.\n") +add_test(NAME tst-ldbl-argp-failure COMMAND tst-ldbl-argp --failure) +set_tests_properties(tst-ldbl-argp-failure PROPERTIES PASS_REGULAR_EXPRESSION "test-argp: -1.000000-2.000000-3.000000-4.000000\n") diff --git a/test/argp-cpp-test.cpp b/test/argp-cpp-test.cpp new file mode 100644 index 0000000..eb0607e --- /dev/null +++ b/test/argp-cpp-test.cpp @@ -0,0 +1,19 @@ +/* SPDX-FileCopyrightText: 2026 Thomas Mathys + SPDX-License-Identifier: LGPL-2.1-or-later + argp-standalone - standalone version of glibc's argp functions. */ + +// C++ compilation and linking test. +// The code doesn't do anything actually useful. + +#include +#include +#include +#include + +int main(int argc, char** argv) +{ + argp_failure(nullptr, 0, 0, "failure message"); + argp_fmtstream_free(argp_make_fmtstream(stdout, 0, 0, 0)); + getopt(0, argv, ""); + getopt_long(0, argv, "", nullptr, nullptr); +} diff --git a/test/argp-test.c b/test/argp-test.c index fb66355..f92077c 100644 --- a/test/argp-test.c +++ b/test/argp-test.c @@ -1,8 +1,7 @@ /* SPDX-License-Identifier: LGPL-2.1-or-later */ /* Test program for argp argument parser - Copyright (C) 1997-2016 Free Software Foundation, Inc. + Copyright (C) 1997-2026 Free Software Foundation, Inc. This file is part of the GNU C Library. - Written by Miles Bader . The GNU C Library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public @@ -16,11 +15,7 @@ You should have received a copy of the GNU Lesser General Public License along with the GNU C Library; if not, see - . */ - -#ifndef _GNU_SOURCE -#define _GNU_SOURCE 1 -#endif + . */ #ifdef HAVE_CONFIG_H #include @@ -31,39 +26,8 @@ #include #include -/* - * Implementation of random() for systems that don't have it. - * Suitable for test purposes only, not for production code. - */ -#if defined(HAVE_RANDOM) && !HAVE_RANDOM -int random(void) -{ - return rand(); -} -#endif - -/* - * Implementation of asprintf() for systems that don't have it. - * Suitable for test purposes only, not for production code. - */ -#if defined(HAVE_ASPRINTF) && ! HAVE_ASPRINTF -#include -void asprintf(char** strp, const char* fmt, ...) -{ - const size_t bufsize = 1024; - va_list ap; - va_start(ap, fmt); - - /* Since this is test code we don't bother checking whether malloc returns 0. */ - *strp = malloc(bufsize); - - /* Format text and ensure it is terminated in any case. */ - vsnprintf(*strp, bufsize, fmt, ap); - (*strp)[bufsize - 1] = 0; - - va_end(ap); -} -#endif +/* argp-standalone: include our own test support file. */ +#include "test_support.h" const char *argp_program_version = "argp-test 1.0"; @@ -218,12 +182,12 @@ help_filter (int key, const char *text, void *input) if (key == ARGP_KEY_HELP_POST_DOC && text) { time_t now = time (0); - asprintf (&new_text, text, ctime (&now)); + new_text = xasprintf (text, ctime (&now)); } else if (key == 'f') /* Show the default for the --foonly option. */ - asprintf (&new_text, "%s (ZOT defaults to %x)", - text, params->foonly_default); + new_text = xasprintf ("%s (ZOT defaults to %x)", + text, params->foonly_default); else new_text = (char *)text; diff --git a/test/test_support.h b/test/test_support.h new file mode 100644 index 0000000..57c436d --- /dev/null +++ b/test/test_support.h @@ -0,0 +1,80 @@ +/* SPDX-FileCopyrightText: 2020 Thomas Mathys + SPDX-License-Identifier: LGPL-2.1-or-later + argp-standalone - standalone version of glibc's argp functions. */ + +#ifndef ARGP_TEST_SUPPORT_H_INCLUDED +#define ARGP_TEST_SUPPORT_H_INCLUDED + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include +#include +#include + +/* Implementation of random() for systems that don't have it. + * Suitable for test purposes only, not for production code. */ +#if defined(HAVE_RANDOM) && !HAVE_RANDOM +static int random(void) +{ + return rand(); +} +#endif + +/* Implementation of xasprintf() for systems that don't have it. + * Suitable for test purposes only, not for production code. */ +#if defined(HAVE_XASPRINTF) && !HAVE_XASPRINTF +static int xvsnprintf(char* buf, size_t siz, const char* fmt, va_list ap) +{ + const int slen = vsnprintf(buf, siz, fmt, ap); + if (slen < 0) + { + fprintf(stderr, "xvsnprintf: vsnprintf returned a negative value (%d)\n", slen); + exit(EXIT_FAILURE); + } + + return slen; +} + +static void* xmalloc(size_t siz) +{ + char* p = malloc(siz); + if (!p) + { + fprintf(stderr, "xmalloc: malloc returned NULL\n"); + exit(EXIT_FAILURE); + } + + return p; +} + +static char* xasprintf(const char* fmt, ...) +{ + /* Measure length of resulting string. */ + va_list ap; + va_start(ap, fmt); + const int slen = xvsnprintf(NULL, 0, fmt, ap); + va_end(ap); + + /* Calculate buffer size: add one for the terminating 0. + The +1 cannot overflow since slen is int. */ + const size_t bufsiz = (size_t)slen + 1; + + /* Allocate memory, abort if insufficient memory. */ + char* buf = xmalloc(bufsiz); + + /* Print to buffer. */ + va_start(ap, fmt); + xvsnprintf(buf, bufsiz, fmt, ap); + va_end(ap); + + /* Ensure string is terminated. Not necessary with a conforming snprintf, + but remember the broken _snprintf that Microsoft has. */ + buf[bufsiz - 1] = '\0'; + + return buf; +} +#endif + +#endif diff --git a/test/tst-argp1.c b/test/tst-argp1.c index 51ce075..74b4d03 100644 --- a/test/tst-argp1.c +++ b/test/tst-argp1.c @@ -1,7 +1,6 @@ /* SPDX-License-Identifier: LGPL-2.1-or-later */ -/* Copyright (C) 2002-2016 Free Software Foundation, Inc. +/* Copyright (C) 2002-2026 Free Software Foundation, Inc. This file is part of the GNU C Library. - Contributed by Ulrich Drepper , 2002. The GNU C Library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public @@ -15,7 +14,7 @@ You should have received a copy of the GNU Lesser General Public License along with the GNU C Library; if not, see - . */ + . */ #include @@ -32,7 +31,7 @@ static const struct argp_option test_options[] = { { NULL, 0, NULL, 0, "\ -This is a test for threads so we allow ther user to selection the number of \ +This is a test for threads so we allow the user to select the number of \ threads which are used at any one time. Independently the total number of \ rounds can be selected. This is the total number of threads which will have \ run when the process terminates:" }, @@ -93,8 +92,8 @@ static struct argp argp = }; -int -main (void) +static int +do_test (void) { int argc = 2; char *argv[3] = { (char *) "tst-argp1", (char *) "--help", NULL }; @@ -113,3 +112,11 @@ parse_opt (int key, char *arg, struct argp_state *state) { return ARGP_ERR_UNKNOWN; } + +/* argp-standalone: we do not have glibc's test framework. */ +int +main (int argc, char** argv) +{ + do_test (); + return 0; +} diff --git a/test/tst-argp2.c b/test/tst-argp2.c index 30b51ac..2d2cc08 100644 --- a/test/tst-argp2.c +++ b/test/tst-argp2.c @@ -1,7 +1,6 @@ /* SPDX-License-Identifier: LGPL-2.1-or-later */ -/* Copyright (C) 2007-2016 Free Software Foundation, Inc. +/* Copyright (C) 2007-2026 Free Software Foundation, Inc. This file is part of the GNU C Library. - Contributed by Jakub Jelinek , 2007. The GNU C Library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public @@ -15,7 +14,7 @@ You should have received a copy of the GNU Lesser General Public License along with the GNU C Library; if not, see - . */ + . */ #include @@ -89,9 +88,18 @@ static struct argp argp1 = }; -int -main (void) +static int +do_test (void) { argp_help (&argp1, stdout, ARGP_HELP_LONG, (char *) "tst-argp2"); return 0; } + + +/* argp-standalone: we do not have glibc's test framework. */ +int +main(int argc, char** argv) +{ + do_test(); + return 0; +} diff --git a/test/tst-ldbl-argp.c b/test/tst-ldbl-argp.c new file mode 100644 index 0000000..4e30269 --- /dev/null +++ b/test/tst-ldbl-argp.c @@ -0,0 +1,70 @@ +/* SPDX-License-Identifier: LGPL-2.1-or-later */ +/* Testing of long double conversions in argp.h functions. + Copyright (C) 2018-2026 Free Software Foundation, Inc. + This file is part of the GNU C Library. + + The GNU C Library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + The GNU C Library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with the GNU C Library; if not, see + . */ + +#include +#include + +/* argp-standalone: we do not have glibc's test framework. */ +/*#include */ +/*#include */ + +static const struct argp_option +options[] = +{ + { "error", 'e', "format", OPTION_ARG_OPTIONAL, + "Run argp_error function with a format string", 0 }, + { "failure", 'f', "format", OPTION_ARG_OPTIONAL, + "Run argp_failure function with a format string", 0 }, + { NULL, 0, NULL, 0, NULL } +}; + +static error_t +parser (int key, char *arg, struct argp_state *state) +{ + switch (key) + { + case 'e': + argp_error (state, "%Lf%f%Lf%f", (long double) -1, (double) -2, + (long double) -3, (double) -4); + break; + case 'f': + argp_failure (state, 0, 0, "%Lf%f%Lf%f", (long double) -1, + (double) -2, (long double) -3, (double) -4); + break; + default: + return ARGP_ERR_UNKNOWN; + } + return 0; +} + +static struct argp +argp = +{ + options, parser +}; + +/* argp-standalone: test run and verification is done with CTest. */ +int +main(int argc, char** argv) +{ + int remaining; + argv[0] = "test-argp"; /* Hardcoded argv[0] simplifies verification. */ + argp_parse (&argp, argc, argv, 0, &remaining, NULL); + return 0; +}