Ben Pfaff wrote: > I think the API is fine, at first glance. Thanks for the review.
I'm adding this as a Gnulib module now. It's functionally equivalent to 'getopt_long', except that it doesn't support the "W;" feature that probably no application uses [1]. Compared to yesterday's draft, I - changed the allocation of the arrays from 'static' to stack-allocated, - added a test suite, - fixed a bug. [1] https://sourceware.org/git/?p=glibc.git;a=commit;h=7e161bef0bc9d5ea5e6f3dd490ecd5da6f642671 2025-06-27 Bruno Haible <br...@clisp.org> options: Add tests. * tests/test-options.c: New file, based on tests/test-getopt_long.h. * tests/test-options-prog.c: New file. * modules/options-tests: New file. options: New module. * lib/options.h: New file. * lib/options.c: New file. * modules/options: New file. * doc/glibc-functions/getopt_long.texi: Mention the new module.
>From 0d1077abf990afe45e0bc8d16997f3997e0dac51 Mon Sep 17 00:00:00 2001 From: Bruno Haible <br...@clisp.org> Date: Fri, 27 Jun 2025 15:27:24 +0200 Subject: [PATCH 1/2] options: New module. * lib/options.h: New file. * lib/options.c: New file. * modules/options: New file. * doc/glibc-functions/getopt_long.texi: Mention the new module. --- ChangeLog | 8 + doc/glibc-functions/getopt_long.texi | 103 ++++++++++++ lib/options.c | 145 ++++++++++++++++ lib/options.h | 238 +++++++++++++++++++++++++++ modules/options | 24 +++ 5 files changed, 518 insertions(+) create mode 100644 lib/options.c create mode 100644 lib/options.h create mode 100644 modules/options diff --git a/ChangeLog b/ChangeLog index b83e1a74a8..8e4355a079 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,11 @@ +2025-06-27 Bruno Haible <br...@clisp.org> + + options: New module. + * lib/options.h: New file. + * lib/options.c: New file. + * modules/options: New file. + * doc/glibc-functions/getopt_long.texi: Mention the new module. + 2025-06-26 Bruno Haible <br...@clisp.org> kwset: Improve header file. diff --git a/doc/glibc-functions/getopt_long.texi b/doc/glibc-functions/getopt_long.texi index b0220d1c92..32b5fb9352 100644 --- a/doc/glibc-functions/getopt_long.texi +++ b/doc/glibc-functions/getopt_long.texi @@ -62,3 +62,106 @@ Portability problems not fixed by Gnulib: @itemize @end itemize + +@mindex options +Gnulib provides also a module @code{options}, that +fixes the following shortcomings of the @code{getopt_long} API. + +These shortcomings are best illustrated with an example: + +@example +static struct option const long_options[] = +@{ + @{ "width", required_argument, NULL, 'w' @}, + @{ "help", no_argument, &show_help, 1 @}, + @{ "version", no_argument, &show_version, 1 @}, + @{ NULL, 0, NULL, 0 @} +@}; + +while ((optchar = getopt_long (argc, argv, "w:xhV", long_options, NULL)) + != -1) + switch (optchar) + @{ + case '\0': /* Long option with flag != NULL. */ + break; + case 'w': + set_width (optarg); + break; + case 'x': + do_x = true; + break; + case 'h': + show_help = 1; /* Action code duplication! */ + break; + case 'V': + show_version = 1; /* Action code duplication! */ + break; + default: + usage (EXIT_FAILURE); + @} +@end example + +@itemize +@item +The information whether an option takes a required vs.@: optional argument +needs to be specified twice: +in the @code{option[]} array for the long option +and in the string argument for the short option. +It is too easy to forget to +add the @code{":"} or @code{"::"} part in the string argument +and thus get inconsistent behaviour +between the long option and the short option. +@item +This information needs to be specified twice, but in different ways: +@multitable @columnfractions .3 .3 +@headitem In the array @tab In the string +@item @code{no_argument} @tab @code{""} +@item @code{required_argument} @tab @code{":"} +@item @code{optional_argument} @tab @code{"::"} +@end multitable +@item +For an action that merely sets an @code{int}-typed variable to a value, +you can specify this action in the @code{options[]} array, +and thus omit the handling in the @code{switch} statement. +But this works only for options that are +long options without a corresponding short option. +As soon as the option has a corresponding short option, +you do need to handle it in the @code{switch} statement. +Here again, there is the opportunity for +inconsistent behaviour between the long option and the short option. +@item +The @code{val} field in a @code{struct option} has different meanings, +depending on another field: +If the @code{flag} field is non-NULL, +@code{val} is a value to be stored in a variable. +If the @code{flag} field is NULL, +@code{val} is a key to be returned from @code{getopt_long} +and subject to the @code{switch} statement. +@item +The handling of non-option arguments is specified by +prepending a certain character (@samp{+} or @samp{-}) to the string argument. +This is not one of the usual ways to specify things in an API. +The conventional way in an API is +an argument of @code{enum} type, or a flags word. +@item +The handling of errors consists of two independent flags, +and each of the flags has to be specified in a different way: +one flag is specified by +prepending a certain character (@samp{:}) to the string argument; +the other flag is specified through the global variable @code{opterr}. +@item +The @code{struct option} is a misnomer: +It cannot encode short options. +Therefore, it would have better been called @code{struct long_option}. +@item +The @code{getopt_long} function is expected to +receive the same arguments in each call, in the @code{while} loop. +The effects are undefined if you don't follow this (unwritten!) constraint. +@item +The fifth argument to @code{getopt_long}, @var{indexptr}, is redundant, because +when the @code{flag} is non-NULL, +the @code{switch} statement does not need to handle the option, +and when the @code{flag} is NULL, +@code{getopt_long} returns the value of @code{val}, +as a way to identify which option was seen. +@end itemize diff --git a/lib/options.c b/lib/options.c new file mode 100644 index 0000000000..f981613bf8 --- /dev/null +++ b/lib/options.c @@ -0,0 +1,145 @@ +/* Parsing program options. + Copyright (C) 2025 Free Software Foundation, Inc. + + This file is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published + by the Free Software Foundation, either version 3 of the License, + or (at your option) any later version. + + This file 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 General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see <https://www.gnu.org/licenses/>. */ + +/* Written by Bruno Haible <br...@clisp.org>. */ + +#include <config.h> + +/* Specification. */ +#include "options.h" + +#include <limits.h> +#include <stdio.h> +#include <stdlib.h> + +/* State for communicating between _gl_start_options and get_next_option. */ +static struct + { + int argc; + char **argv; + const struct program_option *options; + size_t n_options; + struct option *long_options; + char *short_options; + } +state; + +void +_gl_start_options (int argc, char **argv, + const struct program_option *options, size_t n_options, + struct option *long_options, char *short_options, + enum non_option_handling nonopt_handling, + unsigned int error_handling) +{ + /* Construct the long_options array. */ + { + struct option *p = long_options; + + for (size_t i = 0; i < n_options; i++) + if (options[i].name != NULL) + { + if (options[i].key == 0 && options[i].variable == NULL) + fprintf (stderr, + "start_options: warning: Option '--%s' has no action. Use the 'key' or the 'variable' field to specify an action.\n", + options[i].name); + + p->name = options[i].name; + p->has_arg = options[i].has_arg; + if (options[i].key == 0 && options[i].variable != NULL) + { + p->flag = options[i].variable; + p->val = options[i].value; + } + else + { + p->flag = NULL; + p->val = options[i].key; + } + p++; + } + + p->name = NULL; + p->has_arg = 0; + p->flag = NULL; + p->val = 0; + p++; + /* Verify that we haven't exceeded its allocated size. */ + if (!(p - long_options <= _GL_LONG_OPTIONS_SIZE (n_options))) + abort (); + } + + /* Construct the short_options string. */ + { + char *p = short_options; + + if (nonopt_handling == NON_OPTION_TERMINATES_OPTIONS) + *p++ = '+'; + else if (nonopt_handling == PROCESS_NON_OPTIONS) + *p++ = '-'; + + if (error_handling & OPTIONS_MISSING_IS_COLON) + *p++ = ':'; + + for (size_t i = 0; i < n_options; i++) + if (options[i].key != 0 && options[i].key <= CHAR_MAX) + { + *p++ = options[i].key; + if (options[i].has_arg != no_argument) + { + *p++ = ':'; + if (options[i].has_arg == optional_argument) + *p++ = ':'; + } + } + + *p++ = '\0'; + /* Verify that we haven't exceeded its allocated size. */ + if (!(p - short_options <= _GL_SHORT_OPTIONS_SIZE (n_options))) + abort (); + } + + state.argc = argc; + state.argv = argv; + state.options = options; + state.n_options = n_options; + state.long_options = long_options; + state.short_options = short_options; + opterr = (error_handling & OPTIONS_ERRORS_SILENT) == 0; +} + +int +get_next_option (void) +{ + if (state.argv == NULL) + { + fprintf (stderr, "fatal: start_options has not been invoked\n"); + abort (); + } + int ret = getopt_long (state.argc, state.argv, + state.short_options, state.long_options, NULL); + if (ret > 1) + { + const struct program_option *options = state.options; + size_t n_options = state.n_options; + for (size_t i = 0; i < n_options; i++) + if (ret == options[i].key) + { + if (options[i].variable != NULL) + *(options[i].variable) = options[i].value; + } + } + return ret; +} diff --git a/lib/options.h b/lib/options.h new file mode 100644 index 0000000000..18e590804f --- /dev/null +++ b/lib/options.h @@ -0,0 +1,238 @@ +/* Parsing program options. + Copyright (C) 2025 Free Software Foundation, Inc. + + This file is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published + by the Free Software Foundation, either version 3 of the License, + or (at your option) any later version. + + This file 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 General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see <https://www.gnu.org/licenses/>. */ + +/* Written by Bruno Haible <br...@clisp.org>. */ + +#ifndef _OPTIONS_H +#define _OPTIONS_H + +/* This file provides a more convenient API to parsing program options, + based on GNU getopt_long() and thus compatible with the option parsing + conventions for GNU programs + <https://www.gnu.org/prep/standards/html_node/Command_002dLine-Interfaces.html>. + + Instead of writing + + static struct option const long_options[] = + { + { "width", required_argument, NULL, 'w' }, + { "help", no_argument, &show_help, 1 }, + { "version", no_argument, &show_version, 1 }, + { NULL, 0, NULL, 0 } + }; + + while ((optchar = getopt_long (argc, argv, "w:xhV", long_options, NULL)) + != -1) + switch (optchar) + { + case '\0': // Long option with flag != NULL. + break; + case 'w': + set_width (optarg); + break; + case 'x': + do_x = true; + break; + case 'h': + show_help = 1; + break; + case 'V': + show_version = 1; + break; + default: + usage (EXIT_FAILURE); + } + + you write + + static struct program_option const options[] = + { + { "width", 'w', required_argument }, + { NULL, 'x', no_argument }, + { "help", 'h', no_argument, &show_help, 1 }, + { "version", 'V', no_argument, &show_version, 1 }, + }; + + start_options (argc, argv, options, MOVE_OPTIONS_FIRST, 0); + while ((optchar = get_next_option ()) != -1) + switch (optchar) + { + case '\0': // Long option with key == 0. + break; + case 'w': + set_width (optarg); + break; + case 'x': + do_x = true; + break; + case 'h': + case 'V': + break; + default: + usage (EXIT_FAILURE); + } + + This API fixes the following shortcomings of the getopt_long() API: + + * The information whether an option takes a required vs. optional argument + needs to be specified twice: in the option[] array for the long option + and in the string argument for the short option. + It is too easy to forget to add the ":" or "::" part in the string + argument and thus get inconsistent behaviour between the long option and + the short option. + + * This information needs to be specified twice, but in different ways: + In the array In the string + ------------ ------------- + no_argument "" + required_argument ":" + optional_argument "::" + + * For an action that merely sets an 'int'-typed variable to a value, you + can specify this action in the options[] array, and thus omit the + handling in the 'switch' statement. But this works only for options + that are long options without a corresponding short option. As soon + as the option has a corresponding short option, you *do* need to handle + it in the 'switch' statement. Here again, there is the opportunity + for inconsistent behaviour between the long option and the short option. + + * The 'val' field in a 'struct option' has different meanings, depending + on another field: If the 'flag' field is non-NULL, 'val' is a value to + be stored in a variable. If the 'flag' field is NULL, 'val' is a key + to be returned from getopt_long() and subject to the 'switch' statement. + + * The handling of non-option arguments is specified by prepending a + certain character ('+' or '-') to the string argument. This is not + one of the usual ways to specify things in an API. The conventional + way in an API is an argument of 'enum' type, or a flags word. + + * The handling of errors consists of two independent flags, and each of + the flags has to be specified in a different way: one flag is specified + by prepending a certain character (':') to the string argument; the + other flag is specified through the global variable 'opterr'. + + * The 'struct option' is a misnomer: It cannot encode short options. + Therefore, it would have better been called 'struct long_option'. + + * The getopt_long() function is expected to receive the same arguments in + each call, in the 'while' loop. The effects are undefined if you + don't follow this (unwritten!) constraint. + + * The fifth argument to getopt_long(), indexptr, is redundant, because + when the 'flag' is non-NULL, the switch statement does not need to + handle the option, and when the 'flag' is NULL, getopt_long returns + the value of 'val', as a way to identify which option was seen. + + It keeps the following properties the getopt_long() API: + + * The programmer writes in actions directly in the main() function. + That is, the actions don't go into separate callback functions + (like with argp). Such callback functions are a fine thing in languages + with nested function (and implicit closures), like Lisp and C++, but not + in C. + + * The option processing does not require dynamic memory allocation. That + is, you don't need to worry about out-of-memory situations here. + */ + +/* Get no_argument, required_argument, optional_argument. */ +#include <getopt.h> +/* Get size_t. */ +#include <stddef.h> +/* Get countof. */ +#include <stdcountof.h> + +#ifdef __cplusplus +extern "C" { +#endif + +/* Description of an option (long option or short option or both). */ +struct program_option +{ + /* The name of the long option, or NULL for a short-option-only. */ + const char *name; + /* A key that uniquely identifies the option. + If the option has a short option, use the character of that short option. + Otherwise, use an arbitrary unique value > CHAR_MAX. + Don't use '\0' or '\1' here, because they have a special meaning. */ + int key; + + /* One of: no_argument, required_argument, optional_argument. */ + int has_arg; + + /* For an action that consists in assigned a value to an 'int'-typed variable, + put the variable and the value here. + Otherwise, use NULL and 0, or omit these fields. */ + int *variable; + int value; +}; + +/* Handling of non-option arguments. */ +enum non_option_handling { + /* Move options before non-option arguments. + This is suitable for most programs. */ + MOVE_OPTIONS_FIRST, + /* Option processing stops when the first non-option argument is encountered. + This is suitable for programs that pass an entire argument list to another + program (such as 'env'), or for programs that accept normal arguments that + start with '-' (such as 'expr' and 'printf'). */ + NON_OPTION_TERMINATES_OPTIONS, + /* Process non-option arguments as if they were options, associated with the + key '\1'. */ + PROCESS_NON_OPTIONS +}; + +/* Handling of errors: A bit mask. */ +#define OPTIONS_ERRORS_SILENT 0x1 +#define OPTIONS_MISSING_IS_COLON 0x2 + +/* Starts the processing of options. */ +#define start_options(argc, argv, options, nonopt_handling, error_handling) \ + /* Allocate room for the long options and short options. */ \ + struct option _gl_long_options[_GL_LONG_OPTIONS_SIZE (countof (options))]; \ + char _gl_short_options[_GL_SHORT_OPTIONS_SIZE (countof (options))]; \ + _gl_start_options (argc, argv, \ + options, countof (options), \ + _gl_long_options, _gl_short_options, \ + nonopt_handling, error_handling) +#define _GL_LONG_OPTIONS_SIZE(count) ((count) + 1) +#define _GL_SHORT_OPTIONS_SIZE(count) (3 * (count) + 3) + +extern void _gl_start_options (int argc, /*const*/ char **argv, + const struct program_option *options, + size_t n_options, + struct option *long_options, char *short_options, + enum non_option_handling nonopt_handling, + unsigned int error_handling); + +/* Processes the next option (or, if PROCESS_NON_OPTIONS was specified, + non-option). + Requires a prior 'start_options' invocation in the same scope or an outer + scope. + If the option has a 'variable' field, that variable is assigned the 'value' + field. + Returns the key of the option, or '\1' when returning a non-option argument. + If the option lacks an argument, it returns '?'. + If the option is unknown, it returns '?' or (if OPTIONS_MISSING_IS_COLON was + specified) ':'. + If the processing is terminated, it returns -1. */ +extern int get_next_option (void); + +#ifdef __cplusplus +} +#endif + +#endif /* _OPTIONS_H */ diff --git a/modules/options b/modules/options new file mode 100644 index 0000000000..2e73ce886e --- /dev/null +++ b/modules/options @@ -0,0 +1,24 @@ +Description: +Parsing program options. + +Files: +lib/options.h +lib/options.c + +Depends-on: +getopt-gnu +stdcountof-h + +configure.ac: + +Makefile.am: +lib_SOURCES += options.c + +Include: +"options.h" + +License: +GPL + +Maintainer: +all -- 2.43.0
>From c6a73ed9b50751f41276c30475e20fa40f87b2be Mon Sep 17 00:00:00 2001 From: Bruno Haible <br...@clisp.org> Date: Fri, 27 Jun 2025 15:30:23 +0200 Subject: [PATCH 2/2] options: Add tests. * tests/test-options.c: New file, based on tests/test-getopt_long.h. * tests/test-options-prog.c: New file. * modules/options-tests: New file. --- ChangeLog | 5 + modules/options-tests | 16 + tests/test-options-prog.c | 140 ++++ tests/test-options.c | 1574 +++++++++++++++++++++++++++++++++++++ 4 files changed, 1735 insertions(+) create mode 100644 modules/options-tests create mode 100644 tests/test-options-prog.c create mode 100644 tests/test-options.c diff --git a/ChangeLog b/ChangeLog index 8e4355a079..db31670380 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,5 +1,10 @@ 2025-06-27 Bruno Haible <br...@clisp.org> + options: Add tests. + * tests/test-options.c: New file, based on tests/test-getopt_long.h. + * tests/test-options-prog.c: New file. + * modules/options-tests: New file. + options: New module. * lib/options.h: New file. * lib/options.c: New file. diff --git a/modules/options-tests b/modules/options-tests new file mode 100644 index 0000000000..23f618d3ee --- /dev/null +++ b/modules/options-tests @@ -0,0 +1,16 @@ +Files: +tests/test-options.c +tests/test-options-prog.c +tests/macros.h + +Depends-on: +bool +setenv +unsetenv + +configure.ac: + +Makefile.am: +TESTS += test-options +check_PROGRAMS += test-options +noinst_PROGRAMS += test-options-prog diff --git a/tests/test-options-prog.c b/tests/test-options-prog.c new file mode 100644 index 0000000000..1e2de5db48 --- /dev/null +++ b/tests/test-options-prog.c @@ -0,0 +1,140 @@ +/* Test program for program options. + Copyright (C) 2025 Free Software Foundation, Inc. + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program 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 General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see <https://www.gnu.org/licenses/>. */ + +/* Written by Bruno Haible <br...@clisp.org>, 2025. */ + +#include <config.h> + +/* Specification. */ +#include "options.h" + +#include <stdbool.h> +#include <stdio.h> +#include <stdlib.h> + +/* Display usage information and exit. */ +static void +usage (int status) +{ + if (status != EXIT_SUCCESS) + fprintf (stderr, "Try 'foo --help' for more information.\n"); + else + { + printf ("\ +Usage: foo [OPTION] STRING\n\ +"); + printf ("\n"); + printf ("\ +Does something with the STRING.\n"); + printf ("\n"); + printf ("\ +Options and arguments:\n"); + printf ("\ + -w, --width=WIDTH specify line width\n"); + printf ("\ + STRING a string\n"); + printf ("\ +Informative output:\n"); + printf ("\ + -h, --help display this help and exit\n"); + printf ("\ + -V, --version display version information and exit\n"); + } + + exit (status); +} + +/* Default values for command line options. */ +static int show_help = 0; +static int show_version = 0; +static int width = 80; +static bool do_x = false; + +static void +set_width (const char *arg) +{ + width = atoi (arg); +} + +int +main (int argc, char *argv[]) +{ + /* Parse command line options. */ + { + static struct program_option const options[] = + { + { "width", 'w', required_argument }, + { NULL, 'x', no_argument }, + { "help", 'h', no_argument, &show_help, 1 }, + { "version", 'V', no_argument, &show_version, 1 }, + }; + + start_options (argc, argv, options, MOVE_OPTIONS_FIRST, 0); + int optchar; + while ((optchar = get_next_option ()) != -1) + switch (optchar) + { + case 'w': + set_width (optarg); + break; + case 'x': + do_x = true; + break; + case 'h': + case 'V': + break; + default: + usage (EXIT_FAILURE); + } + } + + /* Version information is requested. */ + if (show_version) + { + printf ("foo 0.0\n"); + printf ("Copyright (C) %s Free Software Foundation, Inc.\n\ +License GPLv3+: GNU GPL version 3 or later <%s>\n\ +This is free software: you are free to change and redistribute it.\n\ +There is NO WARRANTY, to the extent permitted by law.\n\ +", + "2025", "https://gnu.org/licenses/gpl.html"); + exit (EXIT_SUCCESS); + } + + /* Help is requested. */ + if (show_help) + usage (EXIT_SUCCESS); + + /* The STRING argument is the first non-option argument. */ + if (!(argc - optind >= 1)) + { + fprintf (stderr, "missing argument\n"); + usage (EXIT_FAILURE); + } + const char *string = argv[optind++]; + if (!(argc == optind)) + { + fprintf (stderr, "too many arguments\n"); + usage (EXIT_FAILURE); + } + + printf ("Width: %d\n", width); + printf ("x: %s\n", do_x ? "true" : "false"); + printf ("String: %s\n", string); + + exit (EXIT_SUCCESS); +} + diff --git a/tests/test-options.c b/tests/test-options.c new file mode 100644 index 0000000000..d3e7ac3613 --- /dev/null +++ b/tests/test-options.c @@ -0,0 +1,1574 @@ +/* Test of command line argument processing. + Copyright (C) 2009-2025 Free Software Foundation, Inc. + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program 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 General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see <https://www.gnu.org/licenses/>. */ + +/* Written by Bruno Haible <br...@clisp.org>, 2009. */ + +#include <config.h> + +/* Specification. */ +#include "options.h" + +#include <string.h> + +#include "macros.h" + +static int a_seen; +static int b_seen; +static int q_seen; + +/* Reduce casting, so we can use string literals elsewhere. + getopt_long takes an array of char*, but luckily does not modify + those elements, so we can pass const char*. */ + +static void +options_loop (enum non_option_handling nonopt_handling, + unsigned int error_handling, + const char **p_value, const char **q_value, + int *non_options_count, const char **non_options, + int *unrecognized) +{ + int c; + + q_seen = 0; + while ((c = get_next_option ()) != -1) + { + switch (c) + { + case 0: + /* An option with key == 0 was processed. */ + if (q_seen) + *q_value = optarg; + break; + case 'a': + a_seen++; + break; + case 'b': + b_seen = 1; + break; + case 'p': + *p_value = optarg; + break; + case 'q': + *q_value = optarg; + break; + case '\1': + ASSERT (nonopt_handling == PROCESS_NON_OPTIONS); + non_options[(*non_options_count)++] = optarg; + break; + case ':': + ASSERT (error_handling & OPTIONS_MISSING_IS_COLON); + FALLTHROUGH; + case '?': + *unrecognized = optopt; + break; + default: + *unrecognized = c; + break; + } + } +} + +static void +test_getopt_long (void) +{ + int start; + + /* Test disambiguation of options. */ + { + static const struct program_option options[] = + { + { "alpha", 'a', no_argument }, + { "beta", 0, no_argument, &b_seen, 1 }, + { "prune", 1000, required_argument }, + { "quetsche", 0, required_argument, &q_seen, 1 }, + { "xtremely-", 1003, no_argument }, + { "xtra", 1001, no_argument }, + { "xtreme", 1002, no_argument }, + { "xtremely", 1003, no_argument }, + { NULL, 'b', no_argument }, + }; + + { + int argc = 0; + const char *argv[10]; + int c; + + argv[argc++] = "program"; + argv[argc++] = "--x"; + argv[argc] = NULL; + optind = 1; + start_options (argc, (char **) argv, options, MOVE_OPTIONS_FIRST, OPTIONS_ERRORS_SILENT); + c = get_next_option (); + ASSERT (c == '?'); + ASSERT (optopt == 0); + } + { + int argc = 0; + const char *argv[10]; + int c; + + argv[argc++] = "program"; + argv[argc++] = "--xt"; + argv[argc] = NULL; + optind = 1; + start_options (argc, (char **) argv, options, MOVE_OPTIONS_FIRST, OPTIONS_ERRORS_SILENT); + c = get_next_option (); + ASSERT (c == '?'); + ASSERT (optopt == 0); + } + { + int argc = 0; + const char *argv[10]; + int c; + + argv[argc++] = "program"; + argv[argc++] = "--xtr"; + argv[argc] = NULL; + optind = 1; + start_options (argc, (char **) argv, options, MOVE_OPTIONS_FIRST, OPTIONS_ERRORS_SILENT); + c = get_next_option (); + ASSERT (c == '?'); + ASSERT (optopt == 0); + } + { + int argc = 0; + const char *argv[10]; + int c; + + argv[argc++] = "program"; + argv[argc++] = "--xtra"; + argv[argc] = NULL; + optind = 1; + start_options (argc, (char **) argv, options, MOVE_OPTIONS_FIRST, OPTIONS_ERRORS_SILENT); + c = get_next_option (); + ASSERT (c == 1001); + } + { + int argc = 0; + const char *argv[10]; + int c; + + argv[argc++] = "program"; + argv[argc++] = "--xtre"; + argv[argc] = NULL; + optind = 1; + start_options (argc, (char **) argv, options, MOVE_OPTIONS_FIRST, OPTIONS_ERRORS_SILENT); + c = get_next_option (); + ASSERT (c == '?'); + ASSERT (optopt == 0); + } + { + int argc = 0; + const char *argv[10]; + int c; + + argv[argc++] = "program"; + argv[argc++] = "--xtrem"; + argv[argc] = NULL; + optind = 1; + start_options (argc, (char **) argv, options, MOVE_OPTIONS_FIRST, OPTIONS_ERRORS_SILENT); + c = get_next_option (); + ASSERT (c == '?'); + ASSERT (optopt == 0); + } + { + int argc = 0; + const char *argv[10]; + int c; + + argv[argc++] = "program"; + argv[argc++] = "--xtreme"; + argv[argc] = NULL; + optind = 1; + start_options (argc, (char **) argv, options, MOVE_OPTIONS_FIRST, OPTIONS_ERRORS_SILENT); + c = get_next_option (); + ASSERT (c == 1002); + } + { + int argc = 0; + const char *argv[10]; + int c; + + argv[argc++] = "program"; + argv[argc++] = "--xtremel"; + argv[argc] = NULL; + optind = 1; + start_options (argc, (char **) argv, options, MOVE_OPTIONS_FIRST, OPTIONS_ERRORS_SILENT); + c = get_next_option (); + ASSERT (c == 1003); + } + { + int argc = 0; + const char *argv[10]; + int c; + + argv[argc++] = "program"; + argv[argc++] = "--xtremely"; + argv[argc] = NULL; + optind = 1; + start_options (argc, (char **) argv, options, MOVE_OPTIONS_FIRST, OPTIONS_ERRORS_SILENT); + c = get_next_option (); + ASSERT (c == 1003); + } + } + + { + static const struct program_option options[] = + { + { "alpha", 'a', no_argument }, + { "beta", 0, no_argument, &b_seen, 1 }, + { "prune", 1000, required_argument }, + { "quetsche", 0, required_argument, &q_seen, 1 }, + { "xtremely-", 1003, no_argument }, + { "xtra", 1001, no_argument }, + { "xtreme", 1002, no_argument }, + { "xtremely", 1003, no_argument }, + { NULL, 'b', no_argument }, + }; + + /* Test processing of boolean short options. */ + for (start = 0; start <= 1; start++) + { + const char *p_value = NULL; + const char *q_value = NULL; + int non_options_count = 0; + const char *non_options[10]; + int unrecognized = 0; + int argc = 0; + const char *argv[10]; + a_seen = 0; + b_seen = 0; + + argv[argc++] = "program"; + argv[argc++] = "-a"; + argv[argc++] = "foo"; + argv[argc++] = "bar"; + argv[argc] = NULL; + optind = start; + start_options (argc, (char **) argv, options, MOVE_OPTIONS_FIRST, OPTIONS_ERRORS_SILENT); + options_loop (MOVE_OPTIONS_FIRST, OPTIONS_ERRORS_SILENT, + &p_value, &q_value, + &non_options_count, non_options, &unrecognized); + ASSERT (a_seen == 1); + ASSERT (b_seen == 0); + ASSERT (p_value == NULL); + ASSERT (q_value == NULL); + ASSERT (non_options_count == 0); + ASSERT (unrecognized == 0); + ASSERT (optind == 2); + } + for (start = 0; start <= 1; start++) + { + const char *p_value = NULL; + const char *q_value = NULL; + int non_options_count = 0; + const char *non_options[10]; + int unrecognized = 0; + int argc = 0; + const char *argv[10]; + a_seen = 0; + b_seen = 0; + + argv[argc++] = "program"; + argv[argc++] = "-b"; + argv[argc++] = "-a"; + argv[argc++] = "foo"; + argv[argc++] = "bar"; + argv[argc] = NULL; + optind = start; + start_options (argc, (char **) argv, options, MOVE_OPTIONS_FIRST, OPTIONS_ERRORS_SILENT); + options_loop (MOVE_OPTIONS_FIRST, OPTIONS_ERRORS_SILENT, + &p_value, &q_value, + &non_options_count, non_options, &unrecognized); + ASSERT (a_seen == 1); + ASSERT (b_seen == 1); + ASSERT (p_value == NULL); + ASSERT (q_value == NULL); + ASSERT (non_options_count == 0); + ASSERT (unrecognized == 0); + ASSERT (optind == 3); + } + for (start = 0; start <= 1; start++) + { + const char *p_value = NULL; + const char *q_value = NULL; + int non_options_count = 0; + const char *non_options[10]; + int unrecognized = 0; + int argc = 0; + const char *argv[10]; + a_seen = 0; + b_seen = 0; + + argv[argc++] = "program"; + argv[argc++] = "-ba"; + argv[argc++] = "foo"; + argv[argc++] = "bar"; + argv[argc] = NULL; + optind = start; + start_options (argc, (char **) argv, options, MOVE_OPTIONS_FIRST, OPTIONS_ERRORS_SILENT); + options_loop (MOVE_OPTIONS_FIRST, OPTIONS_ERRORS_SILENT, + &p_value, &q_value, + &non_options_count, non_options, &unrecognized); + ASSERT (a_seen == 1); + ASSERT (b_seen == 1); + ASSERT (p_value == NULL); + ASSERT (q_value == NULL); + ASSERT (non_options_count == 0); + ASSERT (unrecognized == 0); + ASSERT (optind == 2); + } + for (start = 0; start <= 1; start++) + { + const char *p_value = NULL; + const char *q_value = NULL; + int non_options_count = 0; + const char *non_options[10]; + int unrecognized = 0; + int argc = 0; + const char *argv[10]; + a_seen = 0; + b_seen = 0; + + argv[argc++] = "program"; + argv[argc++] = "-ab"; + argv[argc++] = "-a"; + argv[argc++] = "foo"; + argv[argc++] = "bar"; + argv[argc] = NULL; + optind = start; + start_options (argc, (char **) argv, options, MOVE_OPTIONS_FIRST, OPTIONS_ERRORS_SILENT); + options_loop (MOVE_OPTIONS_FIRST, OPTIONS_ERRORS_SILENT, + &p_value, &q_value, + &non_options_count, non_options, &unrecognized); + ASSERT (a_seen == 2); + ASSERT (b_seen == 1); + ASSERT (p_value == NULL); + ASSERT (q_value == NULL); + ASSERT (non_options_count == 0); + ASSERT (unrecognized == 0); + ASSERT (optind == 3); + } + + /* Test processing of boolean long options. */ + for (start = 0; start <= 1; start++) + { + const char *p_value = NULL; + const char *q_value = NULL; + int non_options_count = 0; + const char *non_options[10]; + int unrecognized = 0; + int argc = 0; + const char *argv[10]; + a_seen = 0; + b_seen = 0; + + argv[argc++] = "program"; + argv[argc++] = "--alpha"; + argv[argc++] = "foo"; + argv[argc++] = "bar"; + argv[argc] = NULL; + optind = start; + start_options (argc, (char **) argv, options, MOVE_OPTIONS_FIRST, OPTIONS_ERRORS_SILENT); + options_loop (MOVE_OPTIONS_FIRST, OPTIONS_ERRORS_SILENT, + &p_value, &q_value, + &non_options_count, non_options, &unrecognized); + ASSERT (a_seen == 1); + ASSERT (b_seen == 0); + ASSERT (p_value == NULL); + ASSERT (q_value == NULL); + ASSERT (non_options_count == 0); + ASSERT (unrecognized == 0); + ASSERT (optind == 2); + } + for (start = 0; start <= 1; start++) + { + const char *p_value = NULL; + const char *q_value = NULL; + int non_options_count = 0; + const char *non_options[10]; + int unrecognized = 0; + int argc = 0; + const char *argv[10]; + a_seen = 0; + b_seen = 0; + + argv[argc++] = "program"; + argv[argc++] = "--beta"; + argv[argc++] = "--alpha"; + argv[argc++] = "foo"; + argv[argc++] = "bar"; + argv[argc] = NULL; + optind = start; + start_options (argc, (char **) argv, options, MOVE_OPTIONS_FIRST, OPTIONS_ERRORS_SILENT); + options_loop (MOVE_OPTIONS_FIRST, OPTIONS_ERRORS_SILENT, + &p_value, &q_value, + &non_options_count, non_options, &unrecognized); + ASSERT (a_seen == 1); + ASSERT (b_seen == 1); + ASSERT (p_value == NULL); + ASSERT (q_value == NULL); + ASSERT (non_options_count == 0); + ASSERT (unrecognized == 0); + ASSERT (optind == 3); + } + for (start = 0; start <= 1; start++) + { + const char *p_value = NULL; + const char *q_value = NULL; + int non_options_count = 0; + const char *non_options[10]; + int unrecognized = 0; + int argc = 0; + const char *argv[10]; + a_seen = 0; + b_seen = 0; + + argv[argc++] = "program"; + argv[argc++] = "--alpha"; + argv[argc++] = "--beta"; + argv[argc++] = "--alpha"; + argv[argc++] = "--beta"; + argv[argc++] = "foo"; + argv[argc++] = "bar"; + argv[argc] = NULL; + optind = start; + start_options (argc, (char **) argv, options, MOVE_OPTIONS_FIRST, OPTIONS_ERRORS_SILENT); + options_loop (MOVE_OPTIONS_FIRST, OPTIONS_ERRORS_SILENT, + &p_value, &q_value, + &non_options_count, non_options, &unrecognized); + ASSERT (a_seen == 2); + ASSERT (b_seen == 1); + ASSERT (p_value == NULL); + ASSERT (q_value == NULL); + ASSERT (non_options_count == 0); + ASSERT (unrecognized == 0); + ASSERT (optind == 5); + } + } + + { + static const struct program_option options[] = + { + { "alpha", 0, no_argument }, + { "beta", 0, no_argument, &b_seen, 1 }, + { "prune", 'p', required_argument }, + { "quetsche", 0, required_argument, &q_seen, 1 }, + { "xtremely-", 1003, no_argument }, + { "xtra", 1001, no_argument }, + { "xtreme", 1002, no_argument }, + { "xtremely", 1003, no_argument }, + { NULL, 'q', required_argument }, + }; + static const struct program_option options_with_ab[] = + { + { "alpha", 'a', no_argument }, + { "beta", 0, no_argument, &b_seen, 1 }, + { "prune", 'p', required_argument }, + { "quetsche", 0, required_argument, &q_seen, 1 }, + { "xtremely-", 1003, no_argument }, + { "xtra", 1001, no_argument }, + { "xtreme", 1002, no_argument }, + { "xtremely", 1003, no_argument }, + { NULL, 'q', required_argument }, + { NULL, 'b', no_argument }, + }; + + /* Test processing of short options with arguments. */ + for (start = 0; start <= 1; start++) + { + const char *p_value = NULL; + const char *q_value = NULL; + int non_options_count = 0; + const char *non_options[10]; + int unrecognized = 0; + int argc = 0; + const char *argv[10]; + a_seen = 0; + b_seen = 0; + + argv[argc++] = "program"; + argv[argc++] = "-pfoo"; + argv[argc++] = "bar"; + argv[argc] = NULL; + optind = start; + start_options (argc, (char **) argv, options, MOVE_OPTIONS_FIRST, OPTIONS_ERRORS_SILENT); + options_loop (MOVE_OPTIONS_FIRST, OPTIONS_ERRORS_SILENT, + &p_value, &q_value, + &non_options_count, non_options, &unrecognized); + ASSERT (a_seen == 0); + ASSERT (b_seen == 0); + ASSERT (p_value != NULL && strcmp (p_value, "foo") == 0); + ASSERT (q_value == NULL); + ASSERT (non_options_count == 0); + ASSERT (unrecognized == 0); + ASSERT (optind == 2); + } + for (start = 0; start <= 1; start++) + { + const char *p_value = NULL; + const char *q_value = NULL; + int non_options_count = 0; + const char *non_options[10]; + int unrecognized = 0; + int argc = 0; + const char *argv[10]; + a_seen = 0; + b_seen = 0; + + argv[argc++] = "program"; + argv[argc++] = "-p"; + argv[argc++] = "foo"; + argv[argc++] = "bar"; + argv[argc] = NULL; + optind = start; + start_options (argc, (char **) argv, options, MOVE_OPTIONS_FIRST, OPTIONS_ERRORS_SILENT); + options_loop (MOVE_OPTIONS_FIRST, OPTIONS_ERRORS_SILENT, + &p_value, &q_value, + &non_options_count, non_options, &unrecognized); + ASSERT (a_seen == 0); + ASSERT (b_seen == 0); + ASSERT (p_value != NULL && strcmp (p_value, "foo") == 0); + ASSERT (q_value == NULL); + ASSERT (non_options_count == 0); + ASSERT (unrecognized == 0); + ASSERT (optind == 3); + } + for (start = 0; start <= 1; start++) + { + const char *p_value = NULL; + const char *q_value = NULL; + int non_options_count = 0; + const char *non_options[10]; + int unrecognized = 0; + int argc = 0; + const char *argv[10]; + a_seen = 0; + b_seen = 0; + + argv[argc++] = "program"; + argv[argc++] = "-ab"; + argv[argc++] = "-q"; + argv[argc++] = "baz"; + argv[argc++] = "-pfoo"; + argv[argc++] = "bar"; + argv[argc] = NULL; + optind = start; + start_options (argc, (char **) argv, options_with_ab, MOVE_OPTIONS_FIRST, OPTIONS_ERRORS_SILENT); + options_loop (MOVE_OPTIONS_FIRST, OPTIONS_ERRORS_SILENT, + &p_value, &q_value, + &non_options_count, non_options, &unrecognized); + ASSERT (a_seen == 1); + ASSERT (b_seen == 1); + ASSERT (p_value != NULL && strcmp (p_value, "foo") == 0); + ASSERT (q_value != NULL && strcmp (q_value, "baz") == 0); + ASSERT (non_options_count == 0); + ASSERT (unrecognized == 0); + ASSERT (optind == 5); + } + + /* Test processing of long options with arguments. */ + for (start = 0; start <= 1; start++) + { + const char *p_value = NULL; + const char *q_value = NULL; + int non_options_count = 0; + const char *non_options[10]; + int unrecognized = 0; + int argc = 0; + const char *argv[10]; + a_seen = 0; + b_seen = 0; + + argv[argc++] = "program"; + argv[argc++] = "--p=foo"; + argv[argc++] = "bar"; + argv[argc] = NULL; + optind = start; + start_options (argc, (char **) argv, options, MOVE_OPTIONS_FIRST, OPTIONS_ERRORS_SILENT); + options_loop (MOVE_OPTIONS_FIRST, OPTIONS_ERRORS_SILENT, + &p_value, &q_value, + &non_options_count, non_options, &unrecognized); + ASSERT (a_seen == 0); + ASSERT (b_seen == 0); + ASSERT (p_value != NULL && strcmp (p_value, "foo") == 0); + ASSERT (q_value == NULL); + ASSERT (non_options_count == 0); + ASSERT (unrecognized == 0); + ASSERT (optind == 2); + } + for (start = 0; start <= 1; start++) + { + const char *p_value = NULL; + const char *q_value = NULL; + int non_options_count = 0; + const char *non_options[10]; + int unrecognized = 0; + int argc = 0; + const char *argv[10]; + a_seen = 0; + b_seen = 0; + + argv[argc++] = "program"; + argv[argc++] = "--p"; + argv[argc++] = "foo"; + argv[argc++] = "bar"; + argv[argc] = NULL; + optind = start; + start_options (argc, (char **) argv, options, MOVE_OPTIONS_FIRST, OPTIONS_ERRORS_SILENT); + options_loop (MOVE_OPTIONS_FIRST, OPTIONS_ERRORS_SILENT, + &p_value, &q_value, + &non_options_count, non_options, &unrecognized); + ASSERT (a_seen == 0); + ASSERT (b_seen == 0); + ASSERT (p_value != NULL && strcmp (p_value, "foo") == 0); + ASSERT (q_value == NULL); + ASSERT (non_options_count == 0); + ASSERT (unrecognized == 0); + ASSERT (optind == 3); + } + for (start = 0; start <= 1; start++) + { + const char *p_value = NULL; + const char *q_value = NULL; + int non_options_count = 0; + const char *non_options[10]; + int unrecognized = 0; + int argc = 0; + const char *argv[10]; + a_seen = 0; + b_seen = 0; + + argv[argc++] = "program"; + argv[argc++] = "-ab"; + argv[argc++] = "--q"; + argv[argc++] = "baz"; + argv[argc++] = "--p=foo"; + argv[argc++] = "bar"; + argv[argc] = NULL; + optind = start; + start_options (argc, (char **) argv, options_with_ab, MOVE_OPTIONS_FIRST, OPTIONS_ERRORS_SILENT); + options_loop (MOVE_OPTIONS_FIRST, OPTIONS_ERRORS_SILENT, + &p_value, &q_value, + &non_options_count, non_options, &unrecognized); + ASSERT (a_seen == 1); + ASSERT (b_seen == 1); + ASSERT (p_value != NULL && strcmp (p_value, "foo") == 0); + ASSERT (q_value != NULL && strcmp (q_value, "baz") == 0); + ASSERT (non_options_count == 0); + ASSERT (unrecognized == 0); + ASSERT (optind == 5); + } + } + + { + static const struct program_option options[] = + { + { "alpha", 0, no_argument }, + { "beta", 0, no_argument, &b_seen, 1 }, + { "prune", 'p', optional_argument }, + { "quetsche", 0, optional_argument, &q_seen, 1 }, + { NULL, 'q', optional_argument }, + }; + static const struct program_option options_with_ab[] = + { + { "alpha", 'a', no_argument }, + { "beta", 0, no_argument, &b_seen, 1 }, + { "prune", 'p', optional_argument }, + { "quetsche", 0, optional_argument, &q_seen, 1 }, + { NULL, 'q', optional_argument }, + { NULL, 'b', no_argument }, + }; + + /* Test processing of short options with optional arguments. */ + for (start = 0; start <= 1; start++) + { + const char *p_value = NULL; + const char *q_value = NULL; + int non_options_count = 0; + const char *non_options[10]; + int unrecognized = 0; + int argc = 0; + const char *argv[10]; + a_seen = 0; + b_seen = 0; + + argv[argc++] = "program"; + argv[argc++] = "-pfoo"; + argv[argc++] = "bar"; + argv[argc] = NULL; + optind = start; + start_options (argc, (char **) argv, options, MOVE_OPTIONS_FIRST, OPTIONS_ERRORS_SILENT); + options_loop (MOVE_OPTIONS_FIRST, OPTIONS_ERRORS_SILENT, + &p_value, &q_value, + &non_options_count, non_options, &unrecognized); + ASSERT (a_seen == 0); + ASSERT (b_seen == 0); + ASSERT (p_value != NULL && strcmp (p_value, "foo") == 0); + ASSERT (q_value == NULL); + ASSERT (non_options_count == 0); + ASSERT (unrecognized == 0); + ASSERT (optind == 2); + } + for (start = 0; start <= 1; start++) + { + const char *p_value = NULL; + const char *q_value = NULL; + int non_options_count = 0; + const char *non_options[10]; + int unrecognized = 0; + int argc = 0; + const char *argv[10]; + a_seen = 0; + b_seen = 0; + + argv[argc++] = "program"; + argv[argc++] = "-p"; + argv[argc++] = "foo"; + argv[argc++] = "bar"; + argv[argc] = NULL; + optind = start; + start_options (argc, (char **) argv, options, MOVE_OPTIONS_FIRST, OPTIONS_ERRORS_SILENT); + options_loop (MOVE_OPTIONS_FIRST, OPTIONS_ERRORS_SILENT, + &p_value, &q_value, + &non_options_count, non_options, &unrecognized); + ASSERT (a_seen == 0); + ASSERT (b_seen == 0); + ASSERT (p_value == NULL); + ASSERT (q_value == NULL); + ASSERT (non_options_count == 0); + ASSERT (unrecognized == 0); + ASSERT (optind == 2); + } + for (start = 0; start <= 1; start++) + { + const char *p_value = NULL; + const char *q_value = NULL; + int non_options_count = 0; + const char *non_options[10]; + int unrecognized = 0; + int argc = 0; + const char *argv[10]; + a_seen = 0; + b_seen = 0; + + argv[argc++] = "program"; + argv[argc++] = "-p"; + argv[argc++] = "-a"; + argv[argc++] = "bar"; + argv[argc] = NULL; + optind = start; + start_options (argc, (char **) argv, options_with_ab, MOVE_OPTIONS_FIRST, OPTIONS_ERRORS_SILENT); + options_loop (MOVE_OPTIONS_FIRST, OPTIONS_ERRORS_SILENT, + &p_value, &q_value, + &non_options_count, non_options, &unrecognized); + ASSERT (a_seen == 1); + ASSERT (b_seen == 0); + ASSERT (p_value == NULL); + ASSERT (q_value == NULL); + ASSERT (non_options_count == 0); + ASSERT (unrecognized == 0); + ASSERT (optind == 3); + } + + /* Test processing of long options with optional arguments. */ + for (start = 0; start <= 1; start++) + { + const char *p_value = NULL; + const char *q_value = NULL; + int non_options_count = 0; + const char *non_options[10]; + int unrecognized = 0; + int argc = 0; + const char *argv[10]; + a_seen = 0; + b_seen = 0; + + argv[argc++] = "program"; + argv[argc++] = "--p=foo"; + argv[argc++] = "bar"; + argv[argc] = NULL; + optind = start; + start_options (argc, (char **) argv, options, MOVE_OPTIONS_FIRST, OPTIONS_ERRORS_SILENT); + options_loop (MOVE_OPTIONS_FIRST, OPTIONS_ERRORS_SILENT, + &p_value, &q_value, + &non_options_count, non_options, &unrecognized); + ASSERT (a_seen == 0); + ASSERT (b_seen == 0); + ASSERT (p_value != NULL && strcmp (p_value, "foo") == 0); + ASSERT (q_value == NULL); + ASSERT (non_options_count == 0); + ASSERT (unrecognized == 0); + ASSERT (optind == 2); + } + for (start = 0; start <= 1; start++) + { + const char *p_value = NULL; + const char *q_value = NULL; + int non_options_count = 0; + const char *non_options[10]; + int unrecognized = 0; + int argc = 0; + const char *argv[10]; + a_seen = 0; + b_seen = 0; + + argv[argc++] = "program"; + argv[argc++] = "--p"; + argv[argc++] = "foo"; + argv[argc++] = "bar"; + argv[argc] = NULL; + optind = start; + start_options (argc, (char **) argv, options, MOVE_OPTIONS_FIRST, OPTIONS_ERRORS_SILENT); + options_loop (MOVE_OPTIONS_FIRST, OPTIONS_ERRORS_SILENT, + &p_value, &q_value, + &non_options_count, non_options, &unrecognized); + ASSERT (a_seen == 0); + ASSERT (b_seen == 0); + ASSERT (p_value == NULL); + ASSERT (q_value == NULL); + ASSERT (non_options_count == 0); + ASSERT (unrecognized == 0); + ASSERT (optind == 2); + } + for (start = 0; start <= 1; start++) + { + const char *p_value = NULL; + const char *q_value = NULL; + int non_options_count = 0; + const char *non_options[10]; + int unrecognized = 0; + int argc = 0; + const char *argv[10]; + a_seen = 0; + b_seen = 0; + + argv[argc++] = "program"; + argv[argc++] = "--p="; + argv[argc++] = "foo"; + argv[argc++] = "bar"; + argv[argc] = NULL; + optind = start; + start_options (argc, (char **) argv, options, MOVE_OPTIONS_FIRST, OPTIONS_ERRORS_SILENT); + options_loop (MOVE_OPTIONS_FIRST, OPTIONS_ERRORS_SILENT, + &p_value, &q_value, + &non_options_count, non_options, &unrecognized); + ASSERT (a_seen == 0); + ASSERT (b_seen == 0); + ASSERT (p_value != NULL && *p_value == '\0'); + ASSERT (q_value == NULL); + ASSERT (non_options_count == 0); + ASSERT (unrecognized == 0); + ASSERT (optind == 2); + } + for (start = 0; start <= 1; start++) + { + const char *p_value = NULL; + const char *q_value = NULL; + int non_options_count = 0; + const char *non_options[10]; + int unrecognized = 0; + int argc = 0; + const char *argv[10]; + a_seen = 0; + b_seen = 0; + + argv[argc++] = "program"; + argv[argc++] = "--p"; + argv[argc++] = "-a"; + argv[argc++] = "bar"; + argv[argc] = NULL; + optind = start; + start_options (argc, (char **) argv, options_with_ab, MOVE_OPTIONS_FIRST, OPTIONS_ERRORS_SILENT); + options_loop (MOVE_OPTIONS_FIRST, OPTIONS_ERRORS_SILENT, + &p_value, &q_value, + &non_options_count, non_options, &unrecognized); + ASSERT (a_seen == 1); + ASSERT (b_seen == 0); + ASSERT (p_value == NULL); + ASSERT (q_value == NULL); + ASSERT (non_options_count == 0); + ASSERT (unrecognized == 0); + ASSERT (optind == 3); + } + } + + { + static const struct program_option options[] = + { + { "alpha", 'a', no_argument }, + { "beta", 0, no_argument, &b_seen, 1 }, + { "prune", 'p', required_argument }, + { "quetsche", 0, required_argument, &q_seen, 1 }, + { "xtremely-", 1003, no_argument }, + { "xtra", 1001, no_argument }, + { "xtreme", 1002, no_argument }, + { "xtremely", 1003, no_argument }, + { NULL, 'q', required_argument }, + { NULL, 'b', no_argument }, + }; + + /* Check that invalid options are recognized. */ + for (start = 0; start <= 1; start++) + { + const char *p_value = NULL; + const char *q_value = NULL; + int non_options_count = 0; + const char *non_options[10]; + int unrecognized = 0; + int argc = 0; + const char *argv[10]; + a_seen = 0; + b_seen = 0; + + argv[argc++] = "program"; + argv[argc++] = "-p"; + argv[argc++] = "foo"; + argv[argc++] = "-x"; + argv[argc++] = "-a"; + argv[argc++] = "bar"; + argv[argc] = NULL; + optind = start; + start_options (argc, (char **) argv, options, MOVE_OPTIONS_FIRST, OPTIONS_ERRORS_SILENT); + options_loop (MOVE_OPTIONS_FIRST, OPTIONS_ERRORS_SILENT, + &p_value, &q_value, + &non_options_count, non_options, &unrecognized); + ASSERT (a_seen == 1); + ASSERT (b_seen == 0); + ASSERT (p_value != NULL && strcmp (p_value, "foo") == 0); + ASSERT (q_value == NULL); + ASSERT (non_options_count == 0); + ASSERT (unrecognized == 'x'); + ASSERT (optind == 5); + } + for (start = 0; start <= 1; start++) + { + const char *p_value = NULL; + const char *q_value = NULL; + int non_options_count = 0; + const char *non_options[10]; + int unrecognized = 0; + int argc = 0; + const char *argv[10]; + a_seen = 0; + b_seen = 0; + + argv[argc++] = "program"; + argv[argc++] = "-p"; + argv[argc++] = "foo"; + argv[argc++] = "-:"; + argv[argc++] = "-a"; + argv[argc++] = "bar"; + argv[argc] = NULL; + optind = start; + start_options (argc, (char **) argv, options, MOVE_OPTIONS_FIRST, OPTIONS_ERRORS_SILENT); + options_loop (MOVE_OPTIONS_FIRST, OPTIONS_ERRORS_SILENT, + &p_value, &q_value, + &non_options_count, non_options, &unrecognized); + ASSERT (a_seen == 1); + ASSERT (b_seen == 0); + ASSERT (p_value != NULL && strcmp (p_value, "foo") == 0); + ASSERT (q_value == NULL); + ASSERT (non_options_count == 0); + ASSERT (unrecognized == ':'); + ASSERT (optind == 5); + } + + /* Check that unexpected arguments are recognized. */ + for (start = 0; start <= 1; start++) + { + const char *p_value = NULL; + const char *q_value = NULL; + int non_options_count = 0; + const char *non_options[10]; + int unrecognized = 0; + int argc = 0; + const char *argv[10]; + a_seen = 0; + b_seen = 0; + + argv[argc++] = "program"; + argv[argc++] = "-p"; + argv[argc++] = "foo"; + argv[argc++] = "--a="; + argv[argc++] = "bar"; + argv[argc] = NULL; + optind = start; + start_options (argc, (char **) argv, options, MOVE_OPTIONS_FIRST, OPTIONS_ERRORS_SILENT); + options_loop (MOVE_OPTIONS_FIRST, OPTIONS_ERRORS_SILENT, + &p_value, &q_value, + &non_options_count, non_options, &unrecognized); + ASSERT (a_seen == 0); + ASSERT (b_seen == 0); + ASSERT (p_value != NULL && strcmp (p_value, "foo") == 0); + ASSERT (q_value == NULL); + ASSERT (non_options_count == 0); + ASSERT (unrecognized == 'a'); + ASSERT (optind == 4); + } + for (start = 0; start <= 1; start++) + { + const char *p_value = NULL; + const char *q_value = NULL; + int non_options_count = 0; + const char *non_options[10]; + int unrecognized = 0; + int argc = 0; + const char *argv[10]; + a_seen = 0; + b_seen = 0; + + argv[argc++] = "program"; + argv[argc++] = "-p"; + argv[argc++] = "foo"; + argv[argc++] = "--b="; + argv[argc++] = "bar"; + argv[argc] = NULL; + optind = start; + start_options (argc, (char **) argv, options, MOVE_OPTIONS_FIRST, OPTIONS_ERRORS_SILENT); + options_loop (MOVE_OPTIONS_FIRST, OPTIONS_ERRORS_SILENT, + &p_value, &q_value, + &non_options_count, non_options, &unrecognized); + ASSERT (a_seen == 0); + ASSERT (b_seen == 0); + ASSERT (p_value != NULL && strcmp (p_value, "foo") == 0); + ASSERT (q_value == NULL); + ASSERT (non_options_count == 0); + /* When flag is non-zero, glibc sets optopt anyway, but BSD + leaves optopt unchanged. */ + ASSERT (unrecognized == 1 || unrecognized == 0); + ASSERT (optind == 4); + } + + /* Check that by default, non-options arguments are moved to the end. */ + for (start = 0; start <= 1; start++) + { + const char *p_value = NULL; + const char *q_value = NULL; + int non_options_count = 0; + const char *non_options[10]; + int unrecognized = 0; + int argc = 0; + const char *argv[10]; + a_seen = 0; + b_seen = 0; + + argv[argc++] = "program"; + argv[argc++] = "donald"; + argv[argc++] = "-p"; + argv[argc++] = "billy"; + argv[argc++] = "duck"; + argv[argc++] = "-a"; + argv[argc++] = "bar"; + argv[argc] = NULL; + optind = start; + start_options (argc, (char **) argv, options, MOVE_OPTIONS_FIRST, OPTIONS_ERRORS_SILENT); + options_loop (MOVE_OPTIONS_FIRST, OPTIONS_ERRORS_SILENT, + &p_value, &q_value, + &non_options_count, non_options, &unrecognized); + ASSERT (strcmp (argv[0], "program") == 0); + ASSERT (strcmp (argv[1], "-p") == 0); + ASSERT (strcmp (argv[2], "billy") == 0); + ASSERT (strcmp (argv[3], "-a") == 0); + ASSERT (strcmp (argv[4], "donald") == 0); + ASSERT (strcmp (argv[5], "duck") == 0); + ASSERT (strcmp (argv[6], "bar") == 0); + ASSERT (argv[7] == NULL); + ASSERT (a_seen == 1); + ASSERT (b_seen == 0); + ASSERT (p_value != NULL && strcmp (p_value, "billy") == 0); + ASSERT (q_value == NULL); + ASSERT (non_options_count == 0); + ASSERT (unrecognized == 0); + ASSERT (optind == 4); + } + + /* Check that '--' ends the argument processing. */ + for (start = 0; start <= 1; start++) + { + const char *p_value = NULL; + const char *q_value = NULL; + int non_options_count = 0; + const char *non_options[10]; + int unrecognized = 0; + int argc = 0; + const char *argv[20]; + a_seen = 0; + b_seen = 0; + + argv[argc++] = "program"; + argv[argc++] = "donald"; + argv[argc++] = "-p"; + argv[argc++] = "billy"; + argv[argc++] = "duck"; + argv[argc++] = "-a"; + argv[argc++] = "--"; + argv[argc++] = "-b"; + argv[argc++] = "foo"; + argv[argc++] = "-q"; + argv[argc++] = "johnny"; + argv[argc++] = "bar"; + argv[argc] = NULL; + optind = start; + start_options (argc, (char **) argv, options, MOVE_OPTIONS_FIRST, OPTIONS_ERRORS_SILENT); + options_loop (MOVE_OPTIONS_FIRST, OPTIONS_ERRORS_SILENT, + &p_value, &q_value, + &non_options_count, non_options, &unrecognized); + ASSERT (strcmp (argv[0], "program") == 0); + ASSERT (strcmp (argv[1], "-p") == 0); + ASSERT (strcmp (argv[2], "billy") == 0); + ASSERT (strcmp (argv[3], "-a") == 0); + ASSERT (strcmp (argv[4], "--") == 0); + ASSERT (strcmp (argv[5], "donald") == 0); + ASSERT (strcmp (argv[6], "duck") == 0); + ASSERT (strcmp (argv[7], "-b") == 0); + ASSERT (strcmp (argv[8], "foo") == 0); + ASSERT (strcmp (argv[9], "-q") == 0); + ASSERT (strcmp (argv[10], "johnny") == 0); + ASSERT (strcmp (argv[11], "bar") == 0); + ASSERT (argv[12] == NULL); + ASSERT (a_seen == 1); + ASSERT (b_seen == 0); + ASSERT (p_value != NULL && strcmp (p_value, "billy") == 0); + ASSERT (q_value == NULL); + ASSERT (non_options_count == 0); + ASSERT (unrecognized == 0); + ASSERT (optind == 5); + } + + /* Check that the PROCESS_NON_OPTIONS flag causes non-options to be + returned in order. */ + for (start = 0; start <= 1; start++) + { + const char *p_value = NULL; + const char *q_value = NULL; + int non_options_count = 0; + const char *non_options[10]; + int unrecognized = 0; + int argc = 0; + const char *argv[10]; + a_seen = 0; + b_seen = 0; + + argv[argc++] = "program"; + argv[argc++] = "donald"; + argv[argc++] = "-p"; + argv[argc++] = "billy"; + argv[argc++] = "duck"; + argv[argc++] = "-a"; + argv[argc++] = "bar"; + argv[argc] = NULL; + optind = start; + start_options (argc, (char **) argv, options, PROCESS_NON_OPTIONS, OPTIONS_ERRORS_SILENT); + options_loop (PROCESS_NON_OPTIONS, OPTIONS_ERRORS_SILENT, + &p_value, &q_value, + &non_options_count, non_options, &unrecognized); + ASSERT (strcmp (argv[0], "program") == 0); + ASSERT (strcmp (argv[1], "donald") == 0); + ASSERT (strcmp (argv[2], "-p") == 0); + ASSERT (strcmp (argv[3], "billy") == 0); + ASSERT (strcmp (argv[4], "duck") == 0); + ASSERT (strcmp (argv[5], "-a") == 0); + ASSERT (strcmp (argv[6], "bar") == 0); + ASSERT (argv[7] == NULL); + ASSERT (a_seen == 1); + ASSERT (b_seen == 0); + ASSERT (p_value != NULL && strcmp (p_value, "billy") == 0); + ASSERT (q_value == NULL); + ASSERT (non_options_count == 3); + ASSERT (strcmp (non_options[0], "donald") == 0); + ASSERT (strcmp (non_options[1], "duck") == 0); + ASSERT (strcmp (non_options[2], "bar") == 0); + ASSERT (unrecognized == 0); + ASSERT (optind == 7); + } + + /* Check that '--' ends the argument processing. */ + for (start = 0; start <= 1; start++) + { + const char *p_value = NULL; + const char *q_value = NULL; + int non_options_count = 0; + const char *non_options[10]; + int unrecognized = 0; + int argc = 0; + const char *argv[20]; + a_seen = 0; + b_seen = 0; + + argv[argc++] = "program"; + argv[argc++] = "donald"; + argv[argc++] = "-p"; + argv[argc++] = "billy"; + argv[argc++] = "duck"; + argv[argc++] = "-a"; + argv[argc++] = "--"; + argv[argc++] = "-b"; + argv[argc++] = "foo"; + argv[argc++] = "-q"; + argv[argc++] = "johnny"; + argv[argc++] = "bar"; + argv[argc] = NULL; + optind = start; + start_options (argc, (char **) argv, options, PROCESS_NON_OPTIONS, OPTIONS_ERRORS_SILENT); + options_loop (PROCESS_NON_OPTIONS, OPTIONS_ERRORS_SILENT, + &p_value, &q_value, + &non_options_count, non_options, &unrecognized); + ASSERT (strcmp (argv[0], "program") == 0); + ASSERT (strcmp (argv[1], "donald") == 0); + ASSERT (strcmp (argv[2], "-p") == 0); + ASSERT (strcmp (argv[3], "billy") == 0); + ASSERT (strcmp (argv[4], "duck") == 0); + ASSERT (strcmp (argv[5], "-a") == 0); + ASSERT (strcmp (argv[6], "--") == 0); + ASSERT (strcmp (argv[7], "-b") == 0); + ASSERT (strcmp (argv[8], "foo") == 0); + ASSERT (strcmp (argv[9], "-q") == 0); + ASSERT (strcmp (argv[10], "johnny") == 0); + ASSERT (strcmp (argv[11], "bar") == 0); + ASSERT (argv[12] == NULL); + ASSERT (a_seen == 1); + ASSERT (b_seen == 0); + ASSERT (p_value != NULL && strcmp (p_value, "billy") == 0); + ASSERT (q_value == NULL); + if (non_options_count == 2) + { + /* glibc behaviour. */ + ASSERT (non_options_count == 2); + ASSERT (strcmp (non_options[0], "donald") == 0); + ASSERT (strcmp (non_options[1], "duck") == 0); + ASSERT (unrecognized == 0); + ASSERT (optind == 7); + } + else + { + /* Another valid behaviour. */ + ASSERT (non_options_count == 7); + ASSERT (strcmp (non_options[0], "donald") == 0); + ASSERT (strcmp (non_options[1], "duck") == 0); + ASSERT (strcmp (non_options[2], "-b") == 0); + ASSERT (strcmp (non_options[3], "foo") == 0); + ASSERT (strcmp (non_options[4], "-q") == 0); + ASSERT (strcmp (non_options[5], "johnny") == 0); + ASSERT (strcmp (non_options[6], "bar") == 0); + ASSERT (unrecognized == 0); + ASSERT (optind == 12); + } + } + + /* Check that the NON_OPTION_TERMINATES_OPTIONS flag causes the first + non-option to terminate the loop. */ + for (start = 0; start <= 1; start++) + { + const char *p_value = NULL; + const char *q_value = NULL; + int non_options_count = 0; + const char *non_options[10]; + int unrecognized = 0; + int argc = 0; + const char *argv[10]; + a_seen = 0; + b_seen = 0; + + argv[argc++] = "program"; + argv[argc++] = "donald"; + argv[argc++] = "-p"; + argv[argc++] = "billy"; + argv[argc++] = "duck"; + argv[argc++] = "-a"; + argv[argc++] = "bar"; + argv[argc] = NULL; + optind = start; + start_options (argc, (char **) argv, options, NON_OPTION_TERMINATES_OPTIONS, OPTIONS_ERRORS_SILENT); + options_loop (NON_OPTION_TERMINATES_OPTIONS, OPTIONS_ERRORS_SILENT, + &p_value, &q_value, + &non_options_count, non_options, &unrecognized); + ASSERT (strcmp (argv[0], "program") == 0); + ASSERT (strcmp (argv[1], "donald") == 0); + ASSERT (strcmp (argv[2], "-p") == 0); + ASSERT (strcmp (argv[3], "billy") == 0); + ASSERT (strcmp (argv[4], "duck") == 0); + ASSERT (strcmp (argv[5], "-a") == 0); + ASSERT (strcmp (argv[6], "bar") == 0); + ASSERT (argv[7] == NULL); + ASSERT (a_seen == 0); + ASSERT (b_seen == 0); + ASSERT (p_value == NULL); + ASSERT (q_value == NULL); + ASSERT (non_options_count == 0); + ASSERT (unrecognized == 0); + ASSERT (optind == 1); + } + for (start = 0; start <= 1; start++) + { + const char *p_value = NULL; + const char *q_value = NULL; + int non_options_count = 0; + const char *non_options[10]; + int unrecognized = 0; + int argc = 0; + const char *argv[10]; + a_seen = 0; + b_seen = 0; + + argv[argc++] = "program"; + argv[argc++] = "-+"; + argv[argc] = NULL; + optind = start; + start_options (argc, (char **) argv, options, NON_OPTION_TERMINATES_OPTIONS, OPTIONS_ERRORS_SILENT); + options_loop (NON_OPTION_TERMINATES_OPTIONS, OPTIONS_ERRORS_SILENT, + &p_value, &q_value, + &non_options_count, non_options, &unrecognized); + ASSERT (a_seen == 0); + ASSERT (b_seen == 0); + ASSERT (p_value == NULL); + ASSERT (q_value == NULL); + ASSERT (non_options_count == 0); + ASSERT (unrecognized == '+'); + ASSERT (optind == 2); + } + + /* Check that '--' ends the argument processing. */ + for (start = 0; start <= 1; start++) + { + const char *p_value = NULL; + const char *q_value = NULL; + int non_options_count = 0; + const char *non_options[10]; + int unrecognized = 0; + int argc = 0; + const char *argv[20]; + a_seen = 0; + b_seen = 0; + + argv[argc++] = "program"; + argv[argc++] = "donald"; + argv[argc++] = "-p"; + argv[argc++] = "billy"; + argv[argc++] = "duck"; + argv[argc++] = "-a"; + argv[argc++] = "--"; + argv[argc++] = "-b"; + argv[argc++] = "foo"; + argv[argc++] = "-q"; + argv[argc++] = "johnny"; + argv[argc++] = "bar"; + argv[argc] = NULL; + optind = start; + start_options (argc, (char **) argv, options, NON_OPTION_TERMINATES_OPTIONS, OPTIONS_ERRORS_SILENT); + options_loop (NON_OPTION_TERMINATES_OPTIONS, OPTIONS_ERRORS_SILENT, + &p_value, &q_value, + &non_options_count, non_options, &unrecognized); + ASSERT (strcmp (argv[0], "program") == 0); + ASSERT (strcmp (argv[1], "donald") == 0); + ASSERT (strcmp (argv[2], "-p") == 0); + ASSERT (strcmp (argv[3], "billy") == 0); + ASSERT (strcmp (argv[4], "duck") == 0); + ASSERT (strcmp (argv[5], "-a") == 0); + ASSERT (strcmp (argv[6], "--") == 0); + ASSERT (strcmp (argv[7], "-b") == 0); + ASSERT (strcmp (argv[8], "foo") == 0); + ASSERT (strcmp (argv[9], "-q") == 0); + ASSERT (strcmp (argv[10], "johnny") == 0); + ASSERT (strcmp (argv[11], "bar") == 0); + ASSERT (argv[12] == NULL); + ASSERT (a_seen == 0); + ASSERT (b_seen == 0); + ASSERT (p_value == NULL); + ASSERT (q_value == NULL); + ASSERT (non_options_count == 0); + ASSERT (unrecognized == 0); + ASSERT (optind == 1); + } + } +} + +/* Test behavior of getopt_long when POSIXLY_CORRECT is set in the + environment. Options with optional arguments should not change + behavior just because of an environment variable. + https://lists.gnu.org/r/bug-m4/2006-09/msg00028.html */ +static void +test_getopt_long_posix (void) +{ + int start; + + { + static const struct program_option options[] = + { + { "alpha", 'a', no_argument }, + { "beta", 0, no_argument, &b_seen, 1 }, + { "prune", 'p', required_argument }, + { "quetsche", 0, required_argument, &q_seen, 1 }, + { "xtremely-", 1003, no_argument }, + { "xtra", 1001, no_argument }, + { "xtreme", 1002, no_argument }, + { "xtremely", 1003, no_argument }, + { NULL, 'q', required_argument }, + { NULL, 'b', no_argument }, + }; + + /* Check that POSIXLY_CORRECT stops parsing the same as leading '+'. */ + for (start = 0; start <= 1; start++) + { + const char *p_value = NULL; + const char *q_value = NULL; + int non_options_count = 0; + const char *non_options[10]; + int unrecognized = 0; + int argc = 0; + const char *argv[10]; + a_seen = 0; + b_seen = 0; + + argv[argc++] = "program"; + argv[argc++] = "donald"; + argv[argc++] = "-p"; + argv[argc++] = "billy"; + argv[argc++] = "duck"; + argv[argc++] = "-a"; + argv[argc++] = "bar"; + argv[argc] = NULL; + optind = start; + start_options (argc, (char **) argv, options, MOVE_OPTIONS_FIRST, OPTIONS_ERRORS_SILENT); + options_loop (MOVE_OPTIONS_FIRST, OPTIONS_ERRORS_SILENT, + &p_value, &q_value, + &non_options_count, non_options, &unrecognized); + ASSERT (strcmp (argv[0], "program") == 0); + ASSERT (strcmp (argv[1], "donald") == 0); + ASSERT (strcmp (argv[2], "-p") == 0); + ASSERT (strcmp (argv[3], "billy") == 0); + ASSERT (strcmp (argv[4], "duck") == 0); + ASSERT (strcmp (argv[5], "-a") == 0); + ASSERT (strcmp (argv[6], "bar") == 0); + ASSERT (argv[7] == NULL); + ASSERT (a_seen == 0); + ASSERT (b_seen == 0); + ASSERT (p_value == NULL); + ASSERT (q_value == NULL); + ASSERT (non_options_count == 0); + ASSERT (unrecognized == 0); + ASSERT (optind == 1); + } + } + + { + static const struct program_option options[] = + { + { "alpha", 0, no_argument }, + { "beta", 0, no_argument, &b_seen, 1 }, + { "prune", 0, required_argument }, + { "quetsche", 0, required_argument, &q_seen, 1 }, + { "xtremely-", 1003, no_argument }, + { "xtra", 1001, no_argument }, + { "xtreme", 1002, no_argument }, + { "xtremely", 1003, no_argument }, + { NULL, 'p', optional_argument }, + }; + + /* Check that POSIXLY_CORRECT doesn't change optional arguments. */ + for (start = 0; start <= 1; start++) + { + const char *p_value = NULL; + const char *q_value = NULL; + int non_options_count = 0; + const char *non_options[10]; + int unrecognized = 0; + int argc = 0; + const char *argv[10]; + a_seen = 0; + b_seen = 0; + + argv[argc++] = "program"; + argv[argc++] = "-p"; + argv[argc++] = "billy"; + argv[argc] = NULL; + optind = start; + start_options (argc, (char **) argv, options, MOVE_OPTIONS_FIRST, OPTIONS_ERRORS_SILENT); + options_loop (MOVE_OPTIONS_FIRST, OPTIONS_ERRORS_SILENT, + &p_value, &q_value, + &non_options_count, non_options, &unrecognized); + ASSERT (a_seen == 0); + ASSERT (b_seen == 0); + ASSERT (p_value == NULL); + ASSERT (q_value == NULL); + ASSERT (non_options_count == 0); + ASSERT (unrecognized == 0); + ASSERT (optind == 2); + } + } + + { + static const struct program_option options[] = + { + { "alpha", 'a', no_argument }, + { "beta", 0, no_argument, &b_seen, 1 }, + { "prune", 0, required_argument }, + { "quetsche", 0, required_argument, &q_seen, 1 }, + { "xtremely-", 1003, no_argument }, + { "xtra", 1001, no_argument }, + { "xtreme", 1002, no_argument }, + { "xtremely", 1003, no_argument }, + { NULL, 'b', no_argument }, + }; + + /* Check that leading - still sees options after non-options. */ + for (start = 0; start <= 1; start++) + { + const char *p_value = NULL; + const char *q_value = NULL; + int non_options_count = 0; + const char *non_options[10]; + int unrecognized = 0; + int argc = 0; + const char *argv[10]; + a_seen = 0; + b_seen = 0; + + argv[argc++] = "program"; + argv[argc++] = "-a"; + argv[argc++] = "billy"; + argv[argc++] = "-b"; + argv[argc] = NULL; + optind = start; + start_options (argc, (char **) argv, options, PROCESS_NON_OPTIONS, OPTIONS_ERRORS_SILENT); + options_loop (PROCESS_NON_OPTIONS, OPTIONS_ERRORS_SILENT, + &p_value, &q_value, + &non_options_count, non_options, &unrecognized); + ASSERT (a_seen == 1); + ASSERT (b_seen == 1); + ASSERT (p_value == NULL); + ASSERT (q_value == NULL); + ASSERT (non_options_count == 1); + ASSERT (strcmp (non_options[0], "billy") == 0); + ASSERT (unrecognized == 0); + ASSERT (optind == 4); + } + } +} + +int +main () +{ + setenv ("POSIXLY_CORRECT", "1", 1); + + test_getopt_long_posix (); + + unsetenv ("POSIXLY_CORRECT"); + + test_getopt_long (); + + return test_exit_status; +} -- 2.43.0