diff --git a/CHANGELOG.md b/CHANGELOG.md index 896466d4..f3d466f4 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,8 +1,9 @@ ## Version 1.7.0: Parse breakup (in progress) The parsing procedure now maps much more sensibly to complex, nested subcommand structures. Each phase of the parsing happens on all subcommands before moving on with the next phase of the parse. This allows several features, like required environment variables, to work properly even through subcommand boundaries. -Passing the same subcommand multiple times is better supported. A few new features were added as well. +Passing the same subcommand multiple times is better supported. A few new features were added as well, including Windows style option support, parsing strings directly, and ignoring underscores in names. +* Support Windows style options with `->allow_windows_style_options`. [#187] * Added `parse(string)` to split up and parse a command-line style string directly. [#186] * Added `ignore_underscore` and related functions, to ignore underscores when matching names. [#185] * Subcommands now track how many times they were parsed in a parsing process. `count()` with no arguments will return the number of times a subcommand was encountered. [#179] @@ -15,6 +16,7 @@ Passing the same subcommand multiple times is better supported. A few new featur [#183]: https://github.com/CLIUtils/CLI11/pull/183 [#185]: https://github.com/CLIUtils/CLI11/pull/185 [#186]: https://github.com/CLIUtils/CLI11/pull/186 +[#187]: https://github.com/CLIUtils/CLI11/pull/187 ## Version 1.6.2: Help-all diff --git a/include/CLI/App.hpp b/include/CLI/App.hpp index 5f420edc..c081fbb4 100644 --- a/include/CLI/App.hpp +++ b/include/CLI/App.hpp @@ -1193,21 +1193,9 @@ class App { detail::trim(commandline); // the next section of code is to deal with quoted arguments after an '=' or ':' for windows like operations if(!commandline.empty()) { - auto escape_detect = [](std::string &str, size_t offset) { - auto next = str[offset + 1]; - if((next == '\"') || (next == '\'') || (next == '`')) { - auto astart = str.find_last_of("-/ \"\'`", offset - 1); - if(astart != std::string::npos) { - if(str[astart] == (str[offset] == '=') ? '-' : '/') - str[offset] = ' '; // interpret this as a space so the split_up works properly - } - } - return (offset + 1); - }; - - commandline = detail::find_and_modify(commandline, "=", escape_detect); + commandline = detail::find_and_modify(commandline, "=", detail::escape_detect); if(allow_windows_style_options_) - commandline = detail::find_and_modify(commandline, ":", escape_detect); + commandline = detail::find_and_modify(commandline, ":", detail::escape_detect); } auto args = detail::split_up(std::move(commandline)); diff --git a/include/CLI/StringTools.hpp b/include/CLI/StringTools.hpp index f60cc3df..777c8532 100644 --- a/include/CLI/StringTools.hpp +++ b/include/CLI/StringTools.hpp @@ -234,5 +234,17 @@ inline std::string fix_newlines(std::string leader, std::string input) { return input; } +inline size_t escape_detect(std::string &str, size_t offset) { + auto next = str[offset + 1]; + if((next == '\"') || (next == '\'') || (next == '`')) { + auto astart = str.find_last_of("-/ \"\'`", offset - 1); + if(astart != std::string::npos) { + if(str[astart] == (str[offset] == '=') ? '-' : '/') + str[offset] = ' '; // interpret this as a space so the split_up works properly + } + } + return offset + 1; +}; + } // namespace detail } // namespace CLI diff --git a/tests/HelpersTest.cpp b/tests/HelpersTest.cpp index 174272ce..6413e848 100644 --- a/tests/HelpersTest.cpp +++ b/tests/HelpersTest.cpp @@ -47,9 +47,8 @@ TEST(StringTools, Modify) { } TEST(StringTools, Modify2) { - int cnt = 0; std::string newString = - CLI::detail::find_and_modify("this is a string test", "is", [&cnt](std::string &str, size_t index) { + CLI::detail::find_and_modify("this is a string test", "is", [](std::string &str, size_t index) { if((index > 1) && (str[index - 1] != ' ')) { str[index] = 'a'; str[index + 1] = 't'; @@ -60,9 +59,8 @@ TEST(StringTools, Modify2) { } TEST(StringTools, Modify3) { - int cnt = 0; // this picks up 3 sets of 3 after the 'b' then collapses the new first set - std::string newString = CLI::detail::find_and_modify("baaaaaaaaaa", "aaa", [&cnt](std::string &str, size_t index) { + std::string newString = CLI::detail::find_and_modify("baaaaaaaaaa", "aaa", [](std::string &str, size_t index) { str.erase(index, 3); str.insert(str.begin(), 'a'); return 0; diff --git a/tests/StringParseTest.cpp b/tests/StringParseTest.cpp index b37ad162..a457403c 100644 --- a/tests/StringParseTest.cpp +++ b/tests/StringParseTest.cpp @@ -26,6 +26,30 @@ TEST_F(TApp, ExistingExeCheck) { EXPECT_EQ(str3, "\"quoted string\""); } +TEST_F(TApp, ExistingExeCheckWithSpace) { + + TempFile tmpexe{"Space File.out"}; + + std::string str, str2, str3; + app.add_option("-s,--string", str); + app.add_option("-t,--tstr", str2); + app.add_option("-m,--mstr", str3); + + { + std::ofstream out{tmpexe}; + out << "useless string doesn't matter" << std::endl; + } + + app.parse(std::string("./") + std::string(tmpexe) + + " --string=\"this is my quoted string\" -t 'qstring 2' -m=`\"quoted string\"`", + true); + EXPECT_EQ(str, "this is my quoted string"); + EXPECT_EQ(str2, "qstring 2"); + EXPECT_EQ(str3, "\"quoted string\""); + + EXPECT_EQ(app.get_name(), std::string("./") + std::string(tmpexe)); +} + TEST_F(TApp, ExistingExeCheckWithLotsOfSpace) { TempFile tmpexe{"this is a weird file.exe"}; diff --git a/tests/SubcommandTest.cpp b/tests/SubcommandTest.cpp index a45c6406..cf2fc8e8 100644 --- a/tests/SubcommandTest.cpp +++ b/tests/SubcommandTest.cpp @@ -798,7 +798,7 @@ TEST_F(ManySubcommands, MaxCommands) { // The extra subcommand counts as an extra args = {"sub1", "sub2", "sub3"}; EXPECT_NO_THROW(run()); - EXPECT_EQ(sub2->remaining().size(), 1); + EXPECT_EQ(sub2->remaining().size(), (size_t)1); // Currently, setting sub2 to throw causes an extras error // In the future, would passing on up to app's extras be better?