diff --git a/src/json.cc b/src/json.cc index 1a18a9e4f..1666b23c8 100644 --- a/src/json.cc +++ b/src/json.cc @@ -2042,32 +2042,58 @@ Parses a string after opening quotes (\p ") where read. */ std::string json::parser::parseString() { - // remember the position where the first character of the string was - const auto startPos = pos_; // true if and only if the amount of backslashes before the current // character is even bool evenAmountOfBackslashes = true; + // the result of the parse process + std::string result; + // iterate with pos_ over the whole string - for (; pos_ < buffer_.size(); pos_++) - { + for (; pos_ < buffer_.size(); pos_++) { char currentChar = buffer_[pos_]; - // currentChar is a quote, so we might have found the end of the string - if (currentChar == '"') - { - // but only if the amount of backslashes before that quote is even - if (evenAmountOfBackslashes) - { + // uneven amount of backslashes means the user wants to escape something + if (!evenAmountOfBackslashes) { + + // slash, backslash and quote are copied as is + if ( currentChar == '/' + || currentChar == '\\' + || currentChar == '"') { + result += currentChar; + } else { + // All other characters are replaced by their respective special character + if (currentChar == 't') { + result += '\t'; + } else if (currentChar == 'b') { + result += '\b'; + } else if (currentChar == 'f') { + result += '\f'; + } else if (currentChar == 'n') { + result += '\n'; + } else if (currentChar == 'r') { + result += '\r'; + } else { + error("expected one of \\,/,b,f,n,r,t behind backslash."); + } + // TODO implement \uXXXX + } + } else { + if (currentChar == '"') { + // currentChar is a quote, so we found the end of the string + - const auto stringLength = pos_ - startPos; // set pos_ behind the trailing quote pos_++; // find next char to parse next(); - // return string inside the quotes - return buffer_.substr(startPos, stringLength); + // bring the result of the parsing process back to the caller + return result; + } else if (currentChar != '\\') { + // all non-backslash characters are added to the end of the result string. + // the only backslashes we want in the result are the ones that are escaped (which happens above). + result += currentChar; } } diff --git a/test/json_unit.cc b/test/json_unit.cc index 7856bdd18..b2fcd65e3 100644 --- a/test/json_unit.cc +++ b/test/json_unit.cc @@ -1618,10 +1618,44 @@ TEST_CASE("Parser") CHECK(json::parse("\"\"") == json("")); CHECK(json::parse("\"foo\"") == json("foo")); - // escape characters + // escaping quotes CHECK_THROWS_AS(json::parse("\"\\\""), std::invalid_argument); CHECK_NOTHROW(json::parse("\"\\\"\"")); - CHECK_NOTHROW(json::parse("\"\\\\\"")); + + // escaping backslashes + CHECK(json::parse("\"a\\\\z\"") == json("a\\z")); + CHECK(json::parse("\"\\\\\"") == json("\\")); + CHECK(json::parse("\"\\\\a\\\\\"") == json("\\a\\")); + CHECK(json::parse("\"\\\\\\\\\"") == json("\\\\")); + + // escaping slash + CHECK(json::parse("\"a\\/z\"") == json("a/z")); + CHECK(json::parse("\"\\/\"") == json("/")); + + // escaping tabs + CHECK(json::parse("\"a\\tz\"") == json("a\tz")); + CHECK(json::parse("\"\\t\"") == json("\t")); + + // escaping formfeed + CHECK(json::parse("\"a\\fz\"") == json("a\fz")); + CHECK(json::parse("\"\\f\"") == json("\f")); + + // escaping carriage return + CHECK(json::parse("\"a\\rz\"") == json("a\rz")); + CHECK(json::parse("\"\\r\"") == json("\r")); + + // escaping backspace + CHECK(json::parse("\"a\\bz\"") == json("a\bz")); + CHECK(json::parse("\"\\b\"") == json("\b")); + + // escaping newline + CHECK(json::parse("\"a\\nz\"") == json("a\nz")); + CHECK(json::parse("\"\\n\"") == json("\n")); + + // escaping senseless stuff + CHECK_THROWS_AS(json::parse("\"\\z\""), std::invalid_argument); + CHECK_THROWS_AS(json::parse("\"\\ \""), std::invalid_argument); + CHECK_THROWS_AS(json::parse("\"\\9\""), std::invalid_argument); // quotes must be closed CHECK_THROWS_AS(json::parse("\""), std::invalid_argument);