From 030cfaf4b32667e9731ae2f1e7c3f6aba37a3d4d Mon Sep 17 00:00:00 2001 From: Vojtech Bubnik Date: Thu, 6 Apr 2023 10:40:07 +0200 Subject: [PATCH] Fix of SPE-1630 crash when referencing an invalid variable name using the old placeholder parser syntax. Also implemented unescaping of \r\n\"\\ in string syntax. --- src/libslic3r/PlaceholderParser.cpp | 54 +++++++++++++++++++-- tests/libslic3r/test_placeholder_parser.cpp | 2 + 2 files changed, 52 insertions(+), 4 deletions(-) diff --git a/src/libslic3r/PlaceholderParser.cpp b/src/libslic3r/PlaceholderParser.cpp index 11ffd4a54..44b9d1ee9 100644 --- a/src/libslic3r/PlaceholderParser.cpp +++ b/src/libslic3r/PlaceholderParser.cpp @@ -1,6 +1,7 @@ #include "PlaceholderParser.hpp" #include "Exception.hpp" #include "Flow.hpp" +#include "Utils.hpp" #include #include #include @@ -204,6 +205,7 @@ namespace client explicit expr(double d, const Iterator &it_begin, const Iterator &it_end) : m_type(TYPE_DOUBLE), it_range(it_begin, it_end) { m_data.d = d; } explicit expr(const char *s) : m_type(TYPE_STRING) { m_data.s = new std::string(s); } explicit expr(const std::string &s) : m_type(TYPE_STRING) { m_data.s = new std::string(s); } + explicit expr(std::string &&s) : m_type(TYPE_STRING) { m_data.s = new std::string(std::move(s)); } explicit expr(const std::string &s, const Iterator &it_begin, const Iterator &it_end) : m_type(TYPE_STRING), it_range(it_begin, it_end) { m_data.s = new std::string(s); } explicit expr(expr &&rhs, const Iterator &it_begin, const Iterator &it_end) : m_type(rhs.type()), it_range{ it_begin, it_end } @@ -904,9 +906,12 @@ namespace client const ConfigOption *opt = ctx->resolve_symbol(opt_key_str); if (opt == nullptr) { // Check whether the opt_key ends with '_'. - if (opt_key_str.back() == '_') + if (opt_key_str.back() == '_') { opt_key_str.resize(opt_key_str.size() - 1); - opt = ctx->resolve_symbol(opt_key_str); + opt = ctx->resolve_symbol(opt_key_str); + } + if (opt == nullptr) + ctx->throw_exception("Variable does not exist", opt_key); } if (! opt->is_vector()) ctx->throw_exception("Trying to index a scalar variable", opt_key); @@ -1790,8 +1795,49 @@ namespace client if (ctx->skipping()) { out.reset(); out.it_range = it_range; - } else - out = expr(std::string(it_range.begin() + 1, it_range.end() - 1), it_range.begin(), it_range.end()); + } else { + // Unescape the string, UTF-8 safe. + std::string s; + auto begin = std::next(it_range.begin()); + auto end = std::prev(it_range.end()); + assert(begin <= end); + { + // 1) Get the size of the string after unescaping. + size_t len = 0; + for (auto it = begin; it != end;) { + if (*it == '\\') { + if (++ it == end || + (*it != 'r' && *it != 'n' && *it != '"' && *it != '\\')) + ctx->throw_exception("Invalid escape sequence", {std::prev(it), std::next(it) }); + ++ len; + ++ it; + } else { + size_t n = get_utf8_sequence_length(&*it, end - it); + len += n; + it += n; + } + } + // and reserve the string. + s.reserve(len); + } + // 2) Copy & unescape the string. + for (auto it = begin; it != end;) { + if (*it == '\\') { + char c = *(++ it); + if (c == 'r') + c = '\r'; + else if (c == 'n') + c = '\n'; + s += c; + ++ it; + } else { + size_t n = get_utf8_sequence_length(&*it, end - it); + s.append(&*it, n); + it += n; + } + } + out = expr(std::move(s), it_range.begin(), it_range.end()); + } } static void expr_(expr &value, Iterator &end_pos, expr &out) { auto begin_pos = out.it_range.begin(); out = expr(std::move(value), begin_pos, end_pos); } diff --git a/tests/libslic3r/test_placeholder_parser.cpp b/tests/libslic3r/test_placeholder_parser.cpp index a6da48bf3..e6fe5bfe2 100644 --- a/tests/libslic3r/test_placeholder_parser.cpp +++ b/tests/libslic3r/test_placeholder_parser.cpp @@ -44,6 +44,8 @@ SCENARIO("Placeholder parser scripting", "[PlaceholderParser]") { SECTION("multiple expressions with semicolons 2") { REQUIRE(parser.process("{temperature[foo];;temperature[foo];}") == "357357"); } SECTION("multiple expressions with semicolons 3") { REQUIRE(parser.process("{temperature[foo];;;temperature[foo];;}") == "357357"); } + SECTION("parsing string with escaped characters") { REQUIRE(parser.process("{\"hu\\nha\\\\\\\"ha\\\"\"}") == "hu\nha\\\"ha\""); } + // Test the math expressions. SECTION("math: 2*3") { REQUIRE(parser.process("{2*3}") == "6"); } SECTION("math: 2*3/6") { REQUIRE(parser.process("{2*3/6}") == "1"); }