mirror of https://github.com/jarro2783/cxxopts.git
fix: Fix short options parsing with equal sign (#488)
* feat: Add test for short options parsing * fix: Update option parsing to allow = with short options * fix: Fix custom option parsing in case of CXXOPTS_NO_REGEX
This commit is contained in:
parent
b39f57f6bc
commit
a0f13a1528
|
|
@ -758,8 +758,16 @@ inline ArguDesc ParseArgument(const char *arg, bool &matched)
|
|||
{
|
||||
pdata += 1;
|
||||
if(isalnum(*pdata, std::locale::classic())) {
|
||||
// If we have '=' right after first alnum, its a match.
|
||||
if(*(pdata+1) == '=') {
|
||||
argu_desc.arg_name.push_back(*pdata);
|
||||
argu_desc.set_value = true;
|
||||
argu_desc.value = std::string(pdata+2);
|
||||
}
|
||||
else{
|
||||
argu_desc.arg_name = std::string(pdata);
|
||||
}
|
||||
argu_desc.grouping = true;
|
||||
argu_desc.arg_name = std::string(pdata);
|
||||
matched = true;
|
||||
}
|
||||
}
|
||||
|
|
@ -780,7 +788,20 @@ const char* const falsy_pattern =
|
|||
"(f|F)(alse)?|0";
|
||||
CXXOPTS_LINKONCE
|
||||
const char* const option_pattern =
|
||||
"--([[:alnum:]][-_[:alnum:]\\.]+)(=(.*))?|-([[:alnum:]].*)";
|
||||
"--([[:alnum:]][-_[:alnum:]\\.]+)(=(.*))?|-([[:alnum:]])((=(.*))|(.*))";
|
||||
// <-------Long Option--------------------> <-----Short Option------->
|
||||
// Groups :
|
||||
// <---------1------------------><--2--> <--4--------><-----5------>
|
||||
// <-3> <--6--> <-8>
|
||||
// <-7>
|
||||
const int LONG_NAME_IDX=1;
|
||||
const int LONG_MATCH_IDX=2;
|
||||
const int LONG_MATCH_VALUE_IDX=3;
|
||||
const int SHORT_NAME_IDX=4;
|
||||
const int SHORT_MATCH_IDX=6;
|
||||
const int SHORT_MATCH_VALUE_IDX=7;
|
||||
const int SHORT_GROUPING_IDX=8;
|
||||
|
||||
CXXOPTS_LINKONCE
|
||||
const char* const option_specifier_pattern =
|
||||
"([[:alnum:]][-_[:alnum:]\\.]*)(,[ ]*[[:alnum:]][-_[:alnum:]]*)*";
|
||||
|
|
@ -862,13 +883,21 @@ inline ArguDesc ParseArgument(const char *arg, bool &matched)
|
|||
|
||||
ArguDesc argu_desc;
|
||||
if (matched) {
|
||||
argu_desc.arg_name = result[1].str();
|
||||
argu_desc.set_value = result[2].length() > 0;
|
||||
argu_desc.value = result[3].str();
|
||||
if (result[4].length() > 0)
|
||||
if(result[LONG_NAME_IDX].length() > 0) {
|
||||
argu_desc.arg_name = result[LONG_NAME_IDX].str();
|
||||
argu_desc.set_value = result[LONG_MATCH_IDX].length() > 0;
|
||||
argu_desc.value = result[LONG_MATCH_VALUE_IDX].str();
|
||||
}
|
||||
else if (result[SHORT_NAME_IDX].length() > 0)
|
||||
{
|
||||
argu_desc.grouping = true;
|
||||
argu_desc.arg_name = result[4].str();
|
||||
argu_desc.arg_name = result[SHORT_NAME_IDX].str();
|
||||
if(result[SHORT_MATCH_IDX].length() > 0){
|
||||
argu_desc.set_value = true;
|
||||
argu_desc.value = result[SHORT_MATCH_VALUE_IDX].str();
|
||||
} else {
|
||||
argu_desc.arg_name += result[SHORT_GROUPING_IDX].str();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -2579,7 +2608,12 @@ OptionParser::parse(int argc, const char* const* argv)
|
|||
if (i + 1 == s.size())
|
||||
{
|
||||
//it must be the last argument
|
||||
checked_parse_arg(argc, argv, current, value, name);
|
||||
if (argu_desc.set_value) {
|
||||
parse_option(value, name, argu_desc.value);
|
||||
}
|
||||
else{
|
||||
checked_parse_arg(argc, argv, current, value, name);
|
||||
}
|
||||
}
|
||||
else if (value->value().has_implicit())
|
||||
{
|
||||
|
|
|
|||
124
test/options.cpp
124
test/options.cpp
|
|
@ -153,6 +153,130 @@ TEST_CASE("Short options", "[options]")
|
|||
cxxopts::exceptions::invalid_option_format);
|
||||
}
|
||||
|
||||
TEST_CASE("Providing options values via equal sign", "[options]")
|
||||
{
|
||||
cxxopts::Options options("test_equal_sign", " - Providing options values via equal sign");
|
||||
|
||||
options.add_options()
|
||||
("o,option", "a option", cxxopts::value<std::string>())
|
||||
("b", "bool option as string",
|
||||
cxxopts::value<std::string>()->implicit_value("true")->default_value("false"))
|
||||
("c", "c option", cxxopts::value<std::string>()->implicit_value("implicit"));
|
||||
|
||||
struct testcases
|
||||
{
|
||||
std::string name;
|
||||
Argv argv;
|
||||
std::vector<std::pair<std::string, std::string>> expValues;
|
||||
bool parseException;
|
||||
} tests[] = {
|
||||
{
|
||||
"Short value with =",
|
||||
Argv{"test_equal_sign", "-o=hi", "-b=true"},
|
||||
{{"o", "hi"}, {"b", "true"}},
|
||||
false
|
||||
},
|
||||
{
|
||||
"Short non alphanumeric value with =",
|
||||
Argv{"test_equal_sign", "-o==*hi_$&"},
|
||||
{{"o", "=*hi_$&"}, {"b", "false"}},
|
||||
false
|
||||
},
|
||||
{
|
||||
"Short value with = in the value, not as seperator",
|
||||
Argv{"test_equal_sign", "-oDEBUG=1"},
|
||||
{{"o", "DEBUG=1"}},
|
||||
false
|
||||
},
|
||||
{
|
||||
"Invalid short name with =",
|
||||
Argv{"test_equal_sign", "-?=hi"},
|
||||
{},
|
||||
true
|
||||
},
|
||||
{
|
||||
"Short value without =",
|
||||
Argv{"test_equal_sign", "-ohi"},
|
||||
{{"o", "hi"}},
|
||||
false
|
||||
},
|
||||
{
|
||||
"Short empty value",
|
||||
Argv{"test_equal_sign", "-o="},
|
||||
{{"o", ""}},
|
||||
false
|
||||
},
|
||||
{
|
||||
"Short value grouped",
|
||||
Argv{"test_equal_sign", "-bo", "hi"},
|
||||
{{"o", "hi"}, {"b", "true"}},
|
||||
false
|
||||
},
|
||||
{
|
||||
"Multiple short values",
|
||||
Argv{"test_equal_sign", "-o=hi", "-c=bye"},
|
||||
{{"o", "hi"}, {"c", "bye"}},
|
||||
false
|
||||
},
|
||||
{
|
||||
"Multiple short values",
|
||||
Argv{"test_equal_sign", "-o=hi", "-bc"},
|
||||
{{"o", "hi"}, {"c", "implicit"}},
|
||||
false
|
||||
},
|
||||
{
|
||||
"Grouped short values with implicit value",
|
||||
Argv{"test_equal_sign", "-cbo", "hi"},
|
||||
{{"o", "hi"}, {"c", "implicit"}, {"b", "true"}},
|
||||
false
|
||||
},
|
||||
{
|
||||
"Grouped short values with implicit value with =",
|
||||
Argv{"test_equal_sign", "-cbo=hi"},
|
||||
{{"o", "=hi"}, {"c", "implicit"}, {"b", "true"}},
|
||||
false
|
||||
},
|
||||
{
|
||||
"Explicit value for implicit valued options (Parse Expection)",
|
||||
Argv{"test_equal_sign", "-cX"},
|
||||
{},
|
||||
true
|
||||
},
|
||||
{
|
||||
"Long value with equal",
|
||||
Argv{"test_equal_sign", "--option=hi"},
|
||||
{{"o", "hi"}},
|
||||
false
|
||||
},
|
||||
{
|
||||
"Long value without equal",
|
||||
Argv{"test_equal_sign", "--option", "hi"},
|
||||
{{"o", "hi"}},
|
||||
false
|
||||
},
|
||||
{
|
||||
"Long empty value",
|
||||
Argv{"test_equal_sign", "--option="},
|
||||
{{"o", ""}},
|
||||
false
|
||||
},
|
||||
};
|
||||
|
||||
for(const auto& tc : tests) {
|
||||
SECTION(tc.name){
|
||||
if(tc.parseException) {
|
||||
CHECK_THROWS(options.parse(tc.argv.argc(), tc.argv.argv()));
|
||||
continue;
|
||||
}
|
||||
auto result = options.parse(tc.argv.argc(), tc.argv.argv());
|
||||
for (const auto& p : tc.expValues) {
|
||||
// TODO: Allow the type to be defined in the testcase
|
||||
CHECK(result[p.first].as<std::string>() == p.second);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
TEST_CASE("No positional", "[positional]")
|
||||
{
|
||||
cxxopts::Options options("test_no_positional",
|
||||
|
|
|
|||
Loading…
Reference in New Issue