mirror of
https://github.com/Laupetin/OpenAssetTools.git
synced 2026-11-07 01:01:59 +00:00
fix: handle multiline menu script arguments (#1052)
* fix: handle multiline menu script arguments * chore: avoid string concatination via += and extract method --------- Co-authored-by: Jan Laupetin <[email protected]>
This commit is contained in:
1 parent
53e1b06cb3
commit
2346fa1077
2 files changed
+87
-2
No files matched your search
@@ -8,6 +8,38 @@
|
||||
#include <cstdlib>
|
||||
#include <sstream>
|
||||
|
||||
namespace
|
||||
{
|
||||
/**
|
||||
* \brief Escapes new lines in script whenever they are encountered in a script.
|
||||
* e.g. "test<newline>" becomes "test\n". Occurrences outside of strings are not escaped.
|
||||
* \param script The original script input.
|
||||
* \return The script string with newlines in strings escaped.
|
||||
*/
|
||||
std::string EscapeStringNewLines(const char* script)
|
||||
{
|
||||
auto inString = false;
|
||||
auto escaped = false;
|
||||
std::ostringstream scriptString;
|
||||
for (const auto* character = script; *character; ++character)
|
||||
{
|
||||
// Native script arguments can contain literal line breaks.
|
||||
if (inString && *character == '\n')
|
||||
scriptString << "\\n";
|
||||
else if (inString && *character == '\r')
|
||||
scriptString << "\\r";
|
||||
else
|
||||
scriptString << *character;
|
||||
|
||||
if (*character == '"' && !escaped)
|
||||
inString = !inString;
|
||||
escaped = inString && *character == '\\' && !escaped;
|
||||
}
|
||||
|
||||
return std::move(scriptString).str();
|
||||
}
|
||||
} // namespace
|
||||
|
||||
namespace menu
|
||||
{
|
||||
AbstractBaseWriter::AbstractBaseWriter(std::ostream& stream)
|
||||
@@ -66,8 +98,7 @@ namespace menu
|
||||
|
||||
std::vector<std::string> AbstractBaseWriter::CreateScriptTokenList(const char* script)
|
||||
{
|
||||
const std::string scriptString(script);
|
||||
std::istringstream stringStream(scriptString);
|
||||
std::istringstream stringStream(EscapeStringNewLines(script));
|
||||
ParserSingleInputStream inputStream(stringStream, "MenuScript");
|
||||
|
||||
SimpleLexer::Config lexerConfig;
|
||||
|
||||
@@ -0,0 +1,54 @@
|
||||
#include "Menu/AbstractMenuWriter.h"
|
||||
|
||||
#include <catch2/catch_test_macros.hpp>
|
||||
#include <catch2/generators/catch_generators.hpp>
|
||||
#include <sstream>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
namespace
|
||||
{
|
||||
class TestMenuWriter final : public menu::AbstractBaseWriter
|
||||
{
|
||||
public:
|
||||
explicit TestMenuWriter(std::ostream& stream)
|
||||
: AbstractBaseWriter(stream)
|
||||
{
|
||||
}
|
||||
|
||||
using AbstractBaseWriter::CreateScriptTokenList;
|
||||
using AbstractBaseWriter::WriteEscapedString;
|
||||
};
|
||||
|
||||
TEST_CASE("AbstractMenuWriter: Preserves literal line breaks in script arguments", "[menu][writing]")
|
||||
{
|
||||
const std::string lineBreak = GENERATE("\n", "\r", "\r\n");
|
||||
const auto argument = std::format("onlinegame 0;\txstopprivateparty{}", lineBreak);
|
||||
const auto script = std::format(R"("exec" "{}" ; )", argument);
|
||||
const auto tokens = TestMenuWriter::CreateScriptTokenList(script.c_str());
|
||||
|
||||
REQUIRE(tokens.size() == 3);
|
||||
CHECK(tokens[0] == "exec");
|
||||
CHECK(tokens[1] == argument);
|
||||
CHECK(tokens[2] == ";");
|
||||
|
||||
std::ostringstream output;
|
||||
TestMenuWriter writer(output);
|
||||
writer.WriteEscapedString(tokens[1]);
|
||||
const auto escapedArgument = output.str();
|
||||
|
||||
CHECK(escapedArgument.find('\n') == std::string::npos);
|
||||
CHECK(escapedArgument.find('\r') == std::string::npos);
|
||||
const auto reloadedTokens = TestMenuWriter::CreateScriptTokenList(escapedArgument.c_str());
|
||||
REQUIRE(reloadedTokens.size() == 1);
|
||||
CHECK(reloadedTokens[0] == argument);
|
||||
}
|
||||
|
||||
TEST_CASE("AbstractMenuWriter: Preserves script command separators across lines", "[menu][writing]")
|
||||
{
|
||||
const auto tokens = TestMenuWriter::CreateScriptTokenList("\"exec\" \"first\" ;\r\n\"exec\" \"second\" ;");
|
||||
const std::vector<std::string> expected{"exec", "first", ";", "exec", "second", ";"};
|
||||
|
||||
CHECK(tokens == expected);
|
||||
}
|
||||
} // namespace
|
||||
Reference in new issue
Block a user