diff options
-rw-r--r-- | absl/flags/parse.cc | 233 | ||||
-rw-r--r-- | absl/flags/parse.h | 106 | ||||
-rw-r--r-- | absl/flags/parse_test.cc | 159 |
3 files changed, 342 insertions, 156 deletions
diff --git a/absl/flags/parse.cc b/absl/flags/parse.cc index 768652d8..fcd6a52d 100644 --- a/absl/flags/parse.cc +++ b/absl/flags/parse.cc @@ -19,6 +19,7 @@ #include <algorithm> #include <cstdint> +#include <cstdlib> #include <fstream> #include <iostream> #include <iterator> @@ -278,7 +279,7 @@ std::tuple<absl::string_view, absl::string_view, bool> SplitNameAndValue( return std::make_tuple("", "", false); } - auto equal_sign_pos = arg.find("="); + auto equal_sign_pos = arg.find('='); absl::string_view flag_name = arg.substr(0, equal_sign_pos); @@ -664,40 +665,76 @@ std::vector<std::string> GetMisspellingHints(const absl::string_view flag) { // -------------------------------------------------------------------- std::vector<char*> ParseCommandLineImpl(int argc, char* argv[], - UsageFlagsAction usage_flag_act, - OnUndefinedFlag on_undef_flag) { - ABSL_INTERNAL_CHECK(argc > 0, "Missing argv[0]"); + UsageFlagsAction usage_flag_action, + OnUndefinedFlag undef_flag_action) { + std::vector<char*> positional_args; + std::vector<UnrecognizedFlag> unrecognized_flags; - // Once parsing has started we will not have more flag registrations. - // If we did, they would be missing during parsing, which is a problem on - // itself. - flags_internal::FinalizeRegistry(); + bool parse_successful = absl::ParseAbseilFlagsOnly( + argc, argv, positional_args, unrecognized_flags); - // This routine does not return anything since we abort on failure. - CheckDefaultValuesParsingRoundtrip(); + if (undef_flag_action != OnUndefinedFlag::kIgnoreUndefined) { + ReportUnrecognizedFlags( + unrecognized_flags, + undef_flag_action == + flags_internal::OnUndefinedFlag::kAbortIfUndefined); - std::vector<std::string> flagfile_value; + if (!unrecognized_flags.empty()) { parse_successful = false; } + } + +#if ABSL_FLAGS_STRIP_NAMES + if (!parse_successful) { + ReportUsageError("NOTE: command line flags are disabled in this build", + true); + } +#endif + + if (!parse_successful) { + HandleUsageFlags(std::cout, ProgramUsageMessage()); + std::exit(1); + } + + if (usage_flag_action == UsageFlagsAction::kHandleUsage) { + int exit_code = HandleUsageFlags(std::cout, ProgramUsageMessage()); + + if (exit_code != -1) { + std::exit(exit_code); + } + } + + return positional_args; +} +// -------------------------------------------------------------------- + +} // namespace flags_internal + +bool ParseAbseilFlagsOnly(int argc, char* argv[], + std::vector<char*>& positional_args, + std::vector<UnrecognizedFlag>& unrecognized_flags) { + ABSL_INTERNAL_CHECK(argc > 0, "Missing argv[0]"); + + using flags_internal::ArgsList; + using flags_internal::specified_flags; + + std::vector<std::string> flagfile_value; std::vector<ArgsList> input_args; - input_args.emplace_back(argc, argv); - std::vector<char*> output_args; - std::vector<char*> positional_args; - output_args.reserve(static_cast<size_t>(argc)); + // Once parsing has started we will not allow more flag registrations. + flags_internal::FinalizeRegistry(); - // This is the list of undefined flags. The element of the list is the pair - // consisting of boolean indicating if flag came from command line (vs from - // some flag file we've read) and flag name. - // TODO(rogeeff): Eliminate the first element in the pair after cleanup. - std::vector<std::pair<bool, std::string>> undefined_flag_names; + // This routine does not return anything since we abort on failure. + flags_internal::CheckDefaultValuesParsingRoundtrip(); + + input_args.push_back(ArgsList(argc, argv)); // Set program invocation name if it is not set before. - if (ProgramInvocationName() == "UNKNOWN") { + if (flags_internal::ProgramInvocationName() == "UNKNOWN") { flags_internal::SetProgramInvocationName(argv[0]); } - output_args.push_back(argv[0]); + positional_args.push_back(argv[0]); - absl::MutexLock l(&specified_flags_guard); + absl::MutexLock l(&flags_internal::specified_flags_guard); if (specified_flags == nullptr) { specified_flags = new std::vector<const CommandLineFlag*>; } else { @@ -709,13 +746,15 @@ std::vector<char*> ParseCommandLineImpl(int argc, char* argv[], // recursive parsing of flagfile(s). bool success = true; while (!input_args.empty()) { - // 10. First we process the built-in generator flags. - success &= HandleGeneratorFlags(input_args, flagfile_value); + // First we process the built-in generator flags. + success &= flags_internal::HandleGeneratorFlags(input_args, flagfile_value); - // 30. Select top-most (most recent) arguments list. If it is empty drop it + // Select top-most (most recent) arguments list. If it is empty drop it // and re-try. ArgsList& curr_list = input_args.back(); + // Every ArgsList starts with real or fake program name, so we can always + // start by skipping it. curr_list.PopFront(); if (curr_list.Size() == 0) { @@ -723,13 +762,13 @@ std::vector<char*> ParseCommandLineImpl(int argc, char* argv[], continue; } - // 40. Pick up the front remaining argument in the current list. If current - // stack of argument lists contains only one element - we are processing an - // argument from the original argv. + // Handle the next argument in the current list. If the stack of argument + // lists contains only one element - we are processing an argument from the + // original argv. absl::string_view arg(curr_list.Front()); bool arg_from_argv = input_args.size() == 1; - // 50. If argument does not start with - or is just "-" - this is + // If argument does not start with '-' or is just "-" - this is // positional argument. if (!absl::ConsumePrefix(&arg, "-") || arg.empty()) { ABSL_INTERNAL_CHECK(arg_from_argv, @@ -739,8 +778,8 @@ std::vector<char*> ParseCommandLineImpl(int argc, char* argv[], continue; } - // 60. Split the current argument on '=' to figure out the argument - // name and value. If flag name is empty it means we've got "--". value + // Split the current argument on '=' to deduce the argument flag name and + // value. If flag name is empty it means we've got an "--" argument. Value // can be empty either if there were no '=' in argument string at all or // an argument looked like "--foo=". In a latter case is_empty_value is // true. @@ -748,10 +787,11 @@ std::vector<char*> ParseCommandLineImpl(int argc, char* argv[], absl::string_view value; bool is_empty_value = false; - std::tie(flag_name, value, is_empty_value) = SplitNameAndValue(arg); + std::tie(flag_name, value, is_empty_value) = + flags_internal::SplitNameAndValue(arg); - // 70. "--" alone means what it does for GNU: stop flags parsing. We do - // not support positional arguments in flagfiles, so we just drop them. + // Standalone "--" argument indicates that the rest of the arguments are + // positional. We do not support positional arguments in flagfiles. if (flag_name.empty()) { ABSL_INTERNAL_CHECK(arg_from_argv, "Flagfile cannot contain positional argument"); @@ -760,36 +800,36 @@ std::vector<char*> ParseCommandLineImpl(int argc, char* argv[], break; } - // 80. Locate the flag based on flag name. Handle both --foo and --nofoo + // Locate the flag based on flag name. Handle both --foo and --nofoo. CommandLineFlag* flag = nullptr; bool is_negative = false; - std::tie(flag, is_negative) = LocateFlag(flag_name); + std::tie(flag, is_negative) = flags_internal::LocateFlag(flag_name); if (flag == nullptr) { // Usage flags are not modeled as Abseil flags. Locate them separately. if (flags_internal::DeduceUsageFlags(flag_name, value)) { continue; } - - if (on_undef_flag != OnUndefinedFlag::kIgnoreUndefined) { - undefined_flag_names.emplace_back(arg_from_argv, - std::string(flag_name)); - } + unrecognized_flags.emplace_back(arg_from_argv + ? UnrecognizedFlag::kFromArgv + : UnrecognizedFlag::kFromFlagfile, + flag_name); continue; } - // 90. Deduce flag's value (from this or next argument) + // Deduce flag's value (from this or next argument). bool value_success = true; - std::tie(value_success, value) = - DeduceFlagValue(*flag, value, is_negative, is_empty_value, &curr_list); + std::tie(value_success, value) = flags_internal::DeduceFlagValue( + *flag, value, is_negative, is_empty_value, &curr_list); success &= value_success; - // 100. Set the located flag to a new new value, unless it is retired. - // Setting retired flag fails, but we ignoring it here while also reporting - // access to retired flag. + // Set the located flag to a new value, unless it is retired. Setting + // retired flag fails, but we ignoring it here while also reporting access + // to retired flag. std::string error; if (!flags_internal::PrivateHandleAccessor::ParseFrom( - *flag, value, SET_FLAGS_VALUE, kCommandLine, error)) { + *flag, value, flags_internal::SET_FLAGS_VALUE, + flags_internal::kCommandLine, error)) { if (flag->IsRetired()) continue; flags_internal::ReportUsageError(error, true); @@ -799,71 +839,62 @@ std::vector<char*> ParseCommandLineImpl(int argc, char* argv[], } } - for (const auto& flag_name : undefined_flag_names) { - if (CanIgnoreUndefinedFlag(flag_name.second)) continue; - // Verify if flag_name has the "no" already removed - std::vector<std::string> flags; - if (flag_name.first) flags = GetMisspellingHints(flag_name.second); - if (flags.empty()) { - flags_internal::ReportUsageError( - absl::StrCat("Unknown command line flag '", flag_name.second, "'"), - true); - } else { - flags_internal::ReportUsageError( - absl::StrCat("Unknown command line flag '", flag_name.second, - "'. Did you mean: ", absl::StrJoin(flags, ", "), " ?"), - true); - } - - success = false; - } - -#if ABSL_FLAGS_STRIP_NAMES - if (!success) { - flags_internal::ReportUsageError( - "NOTE: command line flags are disabled in this build", true); - } -#endif - - if (!success) { - flags_internal::HandleUsageFlags(std::cout, - ProgramUsageMessage()); - std::exit(1); - } - - if (usage_flag_act == UsageFlagsAction::kHandleUsage) { - int exit_code = flags_internal::HandleUsageFlags( - std::cout, ProgramUsageMessage()); - - if (exit_code != -1) { - std::exit(exit_code); - } - } - - ResetGeneratorFlags(flagfile_value); - - // Reinstate positional args which were intermixed with flags in the arguments - // list. - for (auto arg : positional_args) { - output_args.push_back(arg); - } + flags_internal::ResetGeneratorFlags(flagfile_value); // All the remaining arguments are positional. if (!input_args.empty()) { for (size_t arg_index = input_args.back().FrontIndex(); arg_index < static_cast<size_t>(argc); ++arg_index) { - output_args.push_back(argv[arg_index]); + positional_args.push_back(argv[arg_index]); } } // Trim and sort the vector. specified_flags->shrink_to_fit(); std::sort(specified_flags->begin(), specified_flags->end(), - SpecifiedFlagsCompare{}); - return output_args; + flags_internal::SpecifiedFlagsCompare{}); + + // Filter out unrecognized flags, which are ok to ignore. + std::vector<UnrecognizedFlag> filtered; + filtered.reserve(unrecognized_flags.size()); + for (const auto& unrecognized : unrecognized_flags) { + if (flags_internal::CanIgnoreUndefinedFlag(unrecognized.flag_name)) + continue; + filtered.push_back(unrecognized); + } + + std::swap(unrecognized_flags, filtered); + + return success; } -} // namespace flags_internal +// -------------------------------------------------------------------- + +void ReportUnrecognizedFlags( + const std::vector<UnrecognizedFlag>& unrecognized_flags, + bool report_fatal_error) { + for (const auto& unrecognized : unrecognized_flags) { + // Verify if flag_name has the "no" already removed + std::vector<std::string> misspelling_hints; + if (unrecognized.source == UnrecognizedFlag::kFromArgv) { + misspelling_hints = + flags_internal::GetMisspellingHints(unrecognized.flag_name); + } + + if (misspelling_hints.empty()) { + flags_internal::ReportUsageError( + absl::StrCat("Unknown command line flag '", unrecognized.flag_name, + "'"), + report_fatal_error); + } else { + flags_internal::ReportUsageError( + absl::StrCat("Unknown command line flag '", unrecognized.flag_name, + "'. Did you mean: ", + absl::StrJoin(misspelling_hints, ", "), " ?"), + report_fatal_error); + } + } +} // -------------------------------------------------------------------- diff --git a/absl/flags/parse.h b/absl/flags/parse.h index 929de2cb..a5629375 100644 --- a/absl/flags/parse.h +++ b/absl/flags/parse.h @@ -23,35 +23,103 @@ #ifndef ABSL_FLAGS_PARSE_H_ #define ABSL_FLAGS_PARSE_H_ +#include <string> #include <vector> #include "absl/base/config.h" #include "absl/flags/internal/parse.h" +#include "absl/strings/string_view.h" namespace absl { ABSL_NAMESPACE_BEGIN +// This type represent information about an unrecognized flag in the command +// line. +struct UnrecognizedFlag { + enum Source { kFromArgv, kFromFlagfile }; + + explicit UnrecognizedFlag(Source s, absl::string_view f) + : source(s), flag_name(f) {} + // This field indicates where we found this flag: on the original command line + // or read in some flag file. + Source source; + // Name of the flag we did not recognize in --flag_name=value or --flag_name. + std::string flag_name; +}; + +inline bool operator==(const UnrecognizedFlag& lhs, + const UnrecognizedFlag& rhs) { + return lhs.source == rhs.source && lhs.flag_name == rhs.flag_name; +} + +// ParseAbseilFlagsOnly() +// +// Parses a list of command-line arguments, passed in the `argc` and `argv[]` +// parameters, into a set of Abseil Flag values, returning any unparsed +// arguments in `positional_args` and `unrecognized_flags` output parameters. +// +// This function classifies all the arguments (including content of the +// flagfiles, if any) into one of the following groups: +// +// * arguments specified as "--flag=value" or "--flag value" that match +// registered or built-in Abseil Flags. These are "Abseil Flag arguments." +// * arguments specified as "--flag" that are unrecognized as Abseil Flags +// * arguments that are not specified as "--flag" are positional arguments +// * arguments that follow the flag-terminating delimiter (`--`) are also +// treated as positional arguments regardless of their syntax. +// +// All of the deduced Abseil Flag arguments are then parsed into their +// corresponding flag values. +// +// All the remaining positional arguments including original program name +// (argv[0]) are are returned in the `positional_args` output parameter. +// +// All unrecognized flags that are not otherwise ignored are returned in the +// `unrecognized_flags` output parameter. Note that the special `undefok` +// flag allows you to specify flags which can be safely ignored; `undefok` +// specifies these flags as a comma-separated list. Any unrecognized flags +// that appear within `undefok` will therefore be ignored and not included in +// the `unrecognized_flag` output parameter. +// +// This function returns true if no syntax errors were found on the command line +// or in the referenced flag files. Unrecognized flags do not cause this routine +// to return false. +bool ParseAbseilFlagsOnly(int argc, char* argv[], + std::vector<char*>& positional_args, + std::vector<UnrecognizedFlag>& unrecognized_flags); + +// ReportUnrecognizedFlags() +// +// Reports an error for all non-ignored unrecognized flags in the provided +// `unrecognized_flags` list. +// +// If `report_fatal_error` is true, the fatal error is reported and program is +// aborted. Otherwise non-fatal error is reported for all flags. +// +// This function returns true if any non-ignored unrecognized flags were +// located in the list and false otherwise. +void ReportUnrecognizedFlags( + const std::vector<UnrecognizedFlag>& unrecognized_flags, + bool report_fatal_error); + // ParseCommandLine() // -// Parses the set of command-line arguments passed in the `argc` (argument -// count) and `argv[]` (argument vector) parameters from `main()`, assigning -// values to any defined Abseil flags. (Any arguments passed after the -// flag-terminating delimiter (`--`) are treated as positional arguments and -// ignored.) -// -// Any command-line flags (and arguments to those flags) are parsed into Abseil -// Flag values, if those flags are defined. Any undefined flags will either -// return an error, or be ignored if that flag is designated using `undefok` to -// indicate "undefined is OK." -// -// Any command-line positional arguments not part of any command-line flag (or -// arguments to a flag) are returned in a vector, with the program invocation -// name at position 0 of that vector. (Note that this includes positional -// arguments after the flag-terminating delimiter `--`.) -// -// After all flags and flag arguments are parsed, this function looks for any -// built-in usage flags (e.g. `--help`), and if any were specified, it reports -// help messages and then exits the program. +// First parses Abseil Flags only from the command line according to the +// description in `ParseAbseilFlagsOnly`. In addition this function handles +// unrecognized and usage flags. +// +// If any unrecognized flags are located they are reported using +// `ReportUnrecognizedFlags`. +// +// If any errors detected during command line parsing, this routine reports a +// usage message and aborts the program. +// +// If any built-in usage flags were specified on the command line (e.g. +// `--help`), this function reports help messages and then gracefully exits the +// program. +// +// This function returns all the remaining positional arguments collected by +// `ParseAbseilFlagsOnly`. std::vector<char*> ParseCommandLine(int argc, char* argv[]); ABSL_NAMESPACE_END diff --git a/absl/flags/parse_test.cc b/absl/flags/parse_test.cc index 18a01377..c46bb46e 100644 --- a/absl/flags/parse_test.cc +++ b/absl/flags/parse_test.cc @@ -20,13 +20,13 @@ #include <cstddef> #include <fstream> #include <string> +#include <utility> #include <vector> #include "gmock/gmock.h" #include "gtest/gtest.h" #include "absl/base/internal/raw_logging.h" #include "absl/base/internal/scoped_set_env.h" -#include "absl/flags/declare.h" #include "absl/flags/flag.h" #include "absl/flags/internal/parse.h" #include "absl/flags/internal/usage.h" @@ -199,7 +199,7 @@ constexpr const char* const ff2_data[] = { // Builds flagfile flag in the flagfile_flag buffer and returns it. This // function also creates a temporary flagfile based on FlagfileData input. // We create a flagfile in a temporary directory with the name specified in -// FlagfileData and populate it with lines specifed in FlagfileData. If $0 is +// FlagfileData and populate it with lines specified in FlagfileData. If $0 is // referenced in any of the lines in FlagfileData they are replaced with // temporary directory location. This way we can test inclusion of one flagfile // from another flagfile. @@ -257,6 +257,17 @@ std::vector<char*> InvokeParse(const char* (&in_argv)[N]) { // -------------------------------------------------------------------- template <int N> +bool InvokeParseAbslOnly(const char* (&in_argv)[N]) { + std::vector<char*> positional_args; + std::vector<absl::UnrecognizedFlag> unrecognized_flags; + + return absl::ParseAbseilFlagsOnly(N, const_cast<char**>(in_argv), + positional_args, unrecognized_flags); +} + +// -------------------------------------------------------------------- + +template <int N> void TestParse(const char* (&in_argv)[N], int int_flag_value, double double_flag_val, absl::string_view string_flag_val, bool bool_flag_val, int exp_position_args = 0) { @@ -854,26 +865,6 @@ TEST_F(ParseTest, TestReadingFlagsFromEnvMoxedWithRegularFlags) { // -------------------------------------------------------------------- -TEST_F(ParseTest, TestIgnoreUndefinedFlags) { - const char* in_args1[] = { - "testbin", - "arg1", - "--undef_flag=aa", - "--int_flag=21", - }; - - auto out_args1 = flags::ParseCommandLineImpl( - 4, const_cast<char**>(in_args1), flags::UsageFlagsAction::kHandleUsage, - flags::OnUndefinedFlag::kIgnoreUndefined); - - EXPECT_THAT(out_args1, ElementsAreArray({absl::string_view("testbin"), - absl::string_view("arg1")})); - - EXPECT_EQ(absl::GetFlag(FLAGS_int_flag), 21); -} - -// -------------------------------------------------------------------- - TEST_F(ParseDeathTest, TestSimpleHelpFlagHandling) { const char* in_args1[] = { "testbin", @@ -888,12 +879,16 @@ TEST_F(ParseDeathTest, TestSimpleHelpFlagHandling) { "--int_flag=3", }; - auto out_args2 = flags::ParseCommandLineImpl( - 3, const_cast<char**>(in_args2), flags::UsageFlagsAction::kIgnoreUsage, - flags::OnUndefinedFlag::kAbortIfUndefined); + InvokeParseAbslOnly(in_args2); EXPECT_EQ(flags::GetFlagsHelpMode(), flags::HelpMode::kImportant); EXPECT_EQ(absl::GetFlag(FLAGS_int_flag), 3); + + const char* in_args3[] = {"testbin", "--help", "some_positional_arg"}; + + InvokeParseAbslOnly(in_args3); + + EXPECT_EQ(flags::GetFlagsHelpMode(), flags::HelpMode::kImportant); } // -------------------------------------------------------------------- @@ -904,20 +899,10 @@ TEST_F(ParseDeathTest, TestSubstringHelpFlagHandling) { "--help=abcd", }; - auto out_args1 = flags::ParseCommandLineImpl( - 2, const_cast<char**>(in_args1), flags::UsageFlagsAction::kIgnoreUsage, - flags::OnUndefinedFlag::kAbortIfUndefined); + InvokeParseAbslOnly(in_args1); EXPECT_EQ(flags::GetFlagsHelpMode(), flags::HelpMode::kMatch); EXPECT_EQ(flags::GetFlagsHelpMatchSubstr(), "abcd"); - - const char* in_args2[] = {"testbin", "--help", "some_positional_arg"}; - - auto out_args2 = flags::ParseCommandLineImpl( - 3, const_cast<char**>(in_args2), flags::UsageFlagsAction::kIgnoreUsage, - flags::OnUndefinedFlag::kAbortIfUndefined); - - EXPECT_EQ(flags::GetFlagsHelpMode(), flags::HelpMode::kImportant); } // -------------------------------------------------------------------- @@ -942,4 +927,106 @@ TEST_F(ParseTest, WasPresentOnCommandLine) { // -------------------------------------------------------------------- +TEST_F(ParseTest, ParseAbseilFlagsOnlySuccess) { + const char* in_args[] = { + "testbin", + "arg1", + "--bool_flag", + "--int_flag=211", + "arg2", + "--double_flag=1.1", + "--undef_flag1", + "--undef_flag2=123", + "--string_flag", + "asd", + "--", + "--some_flag", + "arg4", + }; + + std::vector<char*> positional_args; + std::vector<absl::UnrecognizedFlag> unrecognized_flags; + + EXPECT_TRUE(absl::ParseAbseilFlagsOnly(13, const_cast<char**>(in_args), + positional_args, unrecognized_flags)); + EXPECT_THAT(positional_args, + ElementsAreArray( + {absl::string_view("testbin"), absl::string_view("arg1"), + absl::string_view("arg2"), absl::string_view("--some_flag"), + absl::string_view("arg4")})); + EXPECT_THAT(unrecognized_flags, + ElementsAreArray( + {absl::UnrecognizedFlag(absl::UnrecognizedFlag::kFromArgv, + "undef_flag1"), + absl::UnrecognizedFlag(absl::UnrecognizedFlag::kFromArgv, + "undef_flag2")})); +} + +// -------------------------------------------------------------------- + +TEST_F(ParseTest, ParseAbseilFlagsOnlyFailure) { + const char* in_args[] = { + "testbin", + "--int_flag=21.1", + }; + + std::vector<char*> positional_args; + std::vector<absl::UnrecognizedFlag> unrecognized_flags; + + EXPECT_FALSE(absl::ParseAbseilFlagsOnly(2, const_cast<char**>(in_args), + positional_args, unrecognized_flags)); +} + +// -------------------------------------------------------------------- + +TEST_F(ParseTest, UndefOkFlagsAreIgnored) { + const char* in_args[] = { + "testbin", "--undef_flag1", + "--undef_flag2=123", "--undefok=undef_flag2", + "--undef_flag3", "value", + }; + + std::vector<char*> positional_args; + std::vector<absl::UnrecognizedFlag> unrecognized_flags; + + EXPECT_TRUE(absl::ParseAbseilFlagsOnly(6, const_cast<char**>(in_args), + positional_args, unrecognized_flags)); + EXPECT_THAT(positional_args, ElementsAreArray({absl::string_view("testbin"), + absl::string_view("value")})); + EXPECT_THAT(unrecognized_flags, + ElementsAreArray( + {absl::UnrecognizedFlag(absl::UnrecognizedFlag::kFromArgv, + "undef_flag1"), + absl::UnrecognizedFlag(absl::UnrecognizedFlag::kFromArgv, + "undef_flag3")})); +} + +// -------------------------------------------------------------------- + +TEST_F(ParseTest, AllUndefOkFlagsAreIgnored) { + const char* in_args[] = { + "testbin", + "--undef_flag1", + "--undef_flag2=123", + "--undefok=undef_flag2,undef_flag1,undef_flag3", + "--undef_flag3", + "value", + "--", + "--undef_flag4", + }; + + std::vector<char*> positional_args; + std::vector<absl::UnrecognizedFlag> unrecognized_flags; + + EXPECT_TRUE(absl::ParseAbseilFlagsOnly(8, const_cast<char**>(in_args), + positional_args, unrecognized_flags)); + EXPECT_THAT(positional_args, + ElementsAreArray({absl::string_view("testbin"), + absl::string_view("value"), + absl::string_view("--undef_flag4")})); + EXPECT_THAT(unrecognized_flags, testing::IsEmpty()); +} + +// -------------------------------------------------------------------- + } // namespace |