diff --git a/src/util/argparser.cpp b/src/util/argparser.cpp index f77adfe..664a7cd 100644 --- a/src/util/argparser.cpp +++ b/src/util/argparser.cpp @@ -204,6 +204,9 @@ bool ArgParser::parse(int argc, const char* argv[]) { bool result = true; + // By default parse all '-' prefixed parameters as options + m_nonOptionMode = false; + // Get program name m_name = argv[0] + std::string_view(argv[0]).find_last_of('/') + 1; @@ -214,6 +217,7 @@ bool ArgParser::parse(int argc, const char* argv[]) for (; m_optionIndex < argc; ++m_optionIndex) { printf("argv[%d]: %s\n", m_optionIndex, argv[m_optionIndex]); + // Get the current and next parameter argument = argv[m_optionIndex]; if (m_optionIndex + 1 < argc && argv[m_optionIndex + 1][0] != '-') { next = argv[m_optionIndex + 1]; @@ -222,15 +226,20 @@ bool ArgParser::parse(int argc, const char* argv[]) next = {}; } + // Stop parsing '-' prefixed parameters as options afer '--' + if (argument.compare("--") == 0) { + m_nonOptionMode = true; + } + // Long Option - if (argument[0] == '-' && argument[1] == '-') { + if (!m_nonOptionMode && argument[0] == '-' && argument[1] == '-') { argument = argument.substr(argument.find_first_not_of('-')); if (!parseLongOption(argument, next)) { result = false; } } // Short Option - else if (argument[0] == '-') { + else if (!m_nonOptionMode && argument[0] == '-') { argument = argument.substr(argument.find_first_not_of('-')); if (!parseShortOption(argument, next)) { result = false; diff --git a/src/util/argparser.h b/src/util/argparser.h index 03480f3..ae5b38e 100644 --- a/src/util/argparser.h +++ b/src/util/argparser.h @@ -60,6 +60,7 @@ private: bool parseLongOption(std::string_view option, std::string_view next); int m_optionIndex { 1 }; + bool m_nonOptionMode { false }; bool m_exitOnFirstError { true }; bool m_errorMessages { true }; // TODO: Implement this, maybe combine with error messages flag, enum class? or bitfield diff --git a/test/testutilargparser.cpp b/test/testutilargparser.cpp index d38bacf..89dee86 100644 --- a/test/testutilargparser.cpp +++ b/test/testutilargparser.cpp @@ -312,3 +312,54 @@ TEST_CASE(MultipleOptions) EXPECT_EQ(boolOpt1, true); EXPECT_EQ(stringOpt1, "a-string-value"); } + +TEST_CASE(NonOptionMode) +{ + // Bool short options, missing + // Expected: The bool options are interpreted as non-option parameters + bool boolOpt1 = false; + bool boolOpt2 = false; + auto result = runParser({ "--", "-b", "-c" }, [&](auto& parser) { + parser.addOption(boolOpt1, 'b', nullptr, nullptr, nullptr); + parser.addOption(boolOpt2, 'c', nullptr, nullptr, nullptr); + }); + EXPECT_EQ(result, true); + EXPECT_EQ(boolOpt1, false); + EXPECT_EQ(boolOpt2, false); + + // Bool short options, one given + // Expected: boolOpt1 is set, one non-option parameter + boolOpt1 = false; + boolOpt2 = false; + result = runParser({ "-b", "--", "-c" }, [&](auto& parser) { + parser.addOption(boolOpt1, 'b', nullptr, nullptr, nullptr); + parser.addOption(boolOpt2, 'c', nullptr, nullptr, nullptr); + }); + EXPECT_EQ(result, true); + EXPECT_EQ(boolOpt1, true); + EXPECT_EQ(boolOpt2, false); + + // Bool long options, missing + // Expected: The bool options are interpreted as non-option parameters + boolOpt1 = false; + boolOpt2 = false; + result = runParser({ "--", "--bool", "--cool" }, [&](auto& parser) { + parser.addOption(boolOpt1, '\0', "bool", nullptr, nullptr); + parser.addOption(boolOpt2, '\0', "cool", nullptr, nullptr); + }); + EXPECT_EQ(result, true); + EXPECT_EQ(boolOpt1, false); + EXPECT_EQ(boolOpt2, false); + + // Bool long options, one given + // Expected: boolOpt1 is set, one non-option parameter + boolOpt1 = false; + boolOpt2 = false; + result = runParser({ "--bool", "--", "--cool" }, [&](auto& parser) { + parser.addOption(boolOpt1, '\0', "bool", nullptr, nullptr); + parser.addOption(boolOpt2, '\0', "cool", nullptr, nullptr); + }); + EXPECT_EQ(result, true); + EXPECT_EQ(boolOpt1, true); + EXPECT_EQ(boolOpt2, false); +}