From: Bruno Haible Date: Fri, 27 Jun 2025 13:30:23 +0000 (+0200) Subject: options: Add tests. X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=2140e5c54e055bc454f3281397c02a834c334e76;p=thirdparty%2Fgnulib.git 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. --- diff --git a/ChangeLog b/ChangeLog index 8e4355a079..db31670380 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,5 +1,10 @@ 2025-06-27 Bruno Haible + 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..249b5cee64 --- /dev/null +++ b/tests/test-options-prog.c @@ -0,0 +1,139 @@ +/* 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 . */ + +/* Written by Bruno Haible , 2025. */ + +#include + +/* Specification. */ +#include "options.h" + +#include +#include +#include + +/* 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 . */ + +/* Written by Bruno Haible , 2009. */ + +#include + +/* Specification. */ +#include "options.h" + +#include + +#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; +}