diff --git a/FEXHeaderUtils/FEXHeaderUtils/StringArgumentParser.h b/FEXHeaderUtils/FEXHeaderUtils/StringArgumentParser.h index 2ddfc3f265..661444d5a7 100644 --- a/FEXHeaderUtils/FEXHeaderUtils/StringArgumentParser.h +++ b/FEXHeaderUtils/FEXHeaderUtils/StringArgumentParser.h @@ -8,32 +8,39 @@ namespace FHU { /** - * @brief Parses a string of arguments, returning a vector of string_views. + * @brief Parses a shebang string, returning a vector of string_views. * - * @param ArgumentString The string of arguments to parse + * @param ArgumentString The shebang string to parse * - * @return The array of parsed arguments + * @return The array of parsed elements */ static inline fextl::vector ParseArgumentsFromString(const std::string_view ArgumentString) { fextl::vector Arguments; - auto Begin = ArgumentString.begin(); - auto ArgEnd = Begin; - const auto End = ArgumentString.end(); - while (ArgEnd != End && Begin != End) { - // The end of an argument ends with a space or the end of the interpreter line. - ArgEnd = std::find(Begin, End, ' '); - - if (Begin != ArgEnd) { - const auto View = std::string_view(Begin, ArgEnd - Begin); - if (!View.empty()) { - Arguments.emplace_back(View); - } - } - - Begin = ArgEnd + 1; + const auto SPACE = " \f\n\r\t\v"; + + auto InterpBegin = ArgumentString.find_first_not_of(SPACE); + if (InterpBegin == std::string::npos) { + return Arguments; + } + + auto InterpLen = ArgumentString.substr(InterpBegin).find_first_of(SPACE); + Arguments.emplace_back(ArgumentString.substr(InterpBegin, InterpLen)); + if (InterpLen == std::string::npos) { + return Arguments; } + auto Arg = ArgumentString.substr(InterpBegin + InterpLen); + auto ArgBegin = Arg.find_first_not_of(SPACE); + if (ArgBegin == std::string::npos) { + return Arguments; + } + + Arg = Arg.substr(ArgBegin); + + auto ArgEnd = Arg.find_last_not_of(SPACE); + Arguments.emplace_back(Arg.substr(0, ArgEnd + 1)); + return Arguments; } } // namespace FHU diff --git a/unittests/APITests/ArgumentParser.cpp b/unittests/APITests/ArgumentParser.cpp index c67932c7f5..c1cc16ca5d 100644 --- a/unittests/APITests/ArgumentParser.cpp +++ b/unittests/APITests/ArgumentParser.cpp @@ -5,11 +5,9 @@ TEST_CASE("Basic") { const auto ArgString = "Test a b c"; auto Args = FHU::ParseArgumentsFromString(ArgString); - REQUIRE(Args.size() == 4); + REQUIRE(Args.size() == 2); CHECK(Args.at(0) == "Test"); - CHECK(Args.at(1) == "a"); - CHECK(Args.at(2) == "b"); - CHECK(Args.at(3) == "c"); + CHECK(Args.at(1) == "a b c"); } TEST_CASE("Basic - Empty") { @@ -24,52 +22,70 @@ TEST_CASE("Basic - Empty spaces") { REQUIRE(Args.size() == 0); } +TEST_CASE("Basic - Whitespace") { + const auto ArgString = " \t \f \r \n \v "; + auto Args = FHU::ParseArgumentsFromString(ArgString); + REQUIRE(Args.size() == 0); +} + +TEST_CASE("Basic - Interpreter only") { + const auto ArgString = "Test"; + auto Args = FHU::ParseArgumentsFromString(ArgString); + REQUIRE(Args.size() == 1); + CHECK(Args.at(0) == "Test"); +} + +TEST_CASE("Basic - Interpreter only with spaces") { + const auto ArgString = " Test "; + auto Args = FHU::ParseArgumentsFromString(ArgString); + REQUIRE(Args.size() == 1); + CHECK(Args.at(0) == "Test"); +} + TEST_CASE("Basic - Space at start") { const auto ArgString = " Test a b c"; auto Args = FHU::ParseArgumentsFromString(ArgString); - REQUIRE(Args.size() == 4); + REQUIRE(Args.size() == 2); CHECK(Args.at(0) == "Test"); - CHECK(Args.at(1) == "a"); - CHECK(Args.at(2) == "b"); - CHECK(Args.at(3) == "c"); + CHECK(Args.at(1) == "a b c"); } TEST_CASE("Basic - Bonus spaces between args") { const auto ArgString = "Test a b c"; auto Args = FHU::ParseArgumentsFromString(ArgString); - REQUIRE(Args.size() == 4); + REQUIRE(Args.size() == 2); CHECK(Args.at(0) == "Test"); - CHECK(Args.at(1) == "a"); - CHECK(Args.at(2) == "b"); - CHECK(Args.at(3) == "c"); + CHECK(Args.at(1) == "a b c"); } TEST_CASE("Basic - non printable") { const auto ArgString = "Test a b \x01c"; auto Args = FHU::ParseArgumentsFromString(ArgString); - REQUIRE(Args.size() == 4); + REQUIRE(Args.size() == 2); CHECK(Args.at(0) == "Test"); - CHECK(Args.at(1) == "a"); - CHECK(Args.at(2) == "b"); - CHECK(Args.at(3) == "\x01c"); + CHECK(Args.at(1) == "a b \x01c"); } TEST_CASE("Basic - Emoji") { const auto ArgString = "Test a b 🐸"; auto Args = FHU::ParseArgumentsFromString(ArgString); - REQUIRE(Args.size() == 4); + REQUIRE(Args.size() == 2); CHECK(Args.at(0) == "Test"); - CHECK(Args.at(1) == "a"); - CHECK(Args.at(2) == "b"); - CHECK(Args.at(3) == "🐸"); + CHECK(Args.at(1) == "a b 🐸"); } TEST_CASE("Basic - space at the end") { const auto ArgString = "Test a b 🐸 "; auto Args = FHU::ParseArgumentsFromString(ArgString); - REQUIRE(Args.size() == 4); + REQUIRE(Args.size() == 2); + CHECK(Args.at(0) == "Test"); + CHECK(Args.at(1) == "a b 🐸"); +} + +TEST_CASE("Basic - whitespace between parts") { + const auto ArgString = "\t\f\rTest\t\f\ra b 🐸\t\f\r"; + auto Args = FHU::ParseArgumentsFromString(ArgString); + REQUIRE(Args.size() == 2); CHECK(Args.at(0) == "Test"); - CHECK(Args.at(1) == "a"); - CHECK(Args.at(2) == "b"); - CHECK(Args.at(3) == "🐸"); + CHECK(Args.at(1) == "a b 🐸"); }