Skip to content

Commit

Permalink
fixed keys with \n round-tripping incorrectly
Browse files Browse the repository at this point in the history
  • Loading branch information
marzer committed Oct 10, 2023
1 parent be30d11 commit cc1962e
Show file tree
Hide file tree
Showing 6 changed files with 62 additions and 29 deletions.
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@ template:
- fixed `for_each()` compilation error on GCC <= 7 (#197) (@sagi-ottopia, @damirbarr)
- fixed `FLT_RADIX` check getting broken by Intel MKL headers (#202) (@iago-lito)
- fixed keys containing `\t` incorrectly formatting as bare keys (@jasmine-zhu, @arp242)
- fixed keys containing `\t` and `\n` not round-tripping correctly (@arp242)

#### Additions

Expand Down
5 changes: 4 additions & 1 deletion include/toml++/impl/formatter.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -149,7 +149,10 @@ TOML_IMPL_NAMESPACE_START
void print_unformatted(std::string_view);

TOML_EXPORTED_MEMBER_FUNCTION
void print_string(std::string_view str, bool allow_multi_line = true, bool allow_bare = false);
void print_string(std::string_view str,
bool allow_multi_line = true,
bool allow_bare = false,
bool allow_literal_whitespace = true);

TOML_EXPORTED_MEMBER_FUNCTION
void print(const value<std::string>&);
Expand Down
26 changes: 16 additions & 10 deletions include/toml++/impl/formatter.inl
Original file line number Diff line number Diff line change
Expand Up @@ -113,7 +113,10 @@ TOML_IMPL_NAMESPACE_START
}

TOML_EXTERNAL_LINKAGE
void formatter::print_string(std::string_view str, bool allow_multi_line, bool allow_bare)
void formatter::print_string(std::string_view str,
bool allow_multi_line,
bool allow_bare,
bool allow_literal_whitespace)
{
if (str.empty())
{
Expand Down Expand Up @@ -206,8 +209,10 @@ TOML_IMPL_NAMESPACE_START
bad_unicode();
}

// strings with line breaks and tabs can't be bare
if (!!(traits & (formatted_string_traits::line_breaks | formatted_string_traits::tabs)))
// strings with line breaks, tabs, and single-quotes can't be bare
if (!!(traits
& (formatted_string_traits::line_breaks | formatted_string_traits::tabs
| formatted_string_traits::single_quotes)))
traits |= formatted_string_traits::non_bare;

// if the string meets the requirements of being 'bare' we can emit a bare string
Expand All @@ -218,17 +223,20 @@ TOML_IMPL_NAMESPACE_START
print_unformatted(str);
return;
}
const auto real_tabs_allowed = allow_literal_whitespace && real_tabs_in_strings_allowed();

// determine if this should be a multi-line string (triple-quotes)
const auto multi_line = allow_multi_line //
const auto multi_line = allow_literal_whitespace //
&& allow_multi_line //
&& multi_line_strings_allowed() //
&& !!(traits & formatted_string_traits::line_breaks);

// determine if this should be a literal string (single-quotes with no escaping)
const auto literal = literal_strings_allowed() //
&& !(traits & formatted_string_traits::control_chars) //
&& (!(traits & formatted_string_traits::single_quotes) || multi_line) //
&& (!(traits & formatted_string_traits::tabs) || real_tabs_in_strings_allowed()) //
const auto literal = literal_strings_allowed() //
&& !(traits & formatted_string_traits::control_chars) //
&& (!(traits & formatted_string_traits::single_quotes) || multi_line) //
&& (!(traits & formatted_string_traits::tabs) || real_tabs_allowed) //
&& (!(traits & formatted_string_traits::line_breaks) || multi_line) //
&& (!(traits & formatted_string_traits::non_ascii) || unicode_allowed);

// literal strings (single quotes, no escape codes)
Expand All @@ -244,8 +252,6 @@ TOML_IMPL_NAMESPACE_START
// anything from here down is a non-literal string, so requires iteration and escaping.
print_unformatted(multi_line ? R"(""")"sv : R"(")"sv);

const auto real_tabs_allowed = real_tabs_in_strings_allowed();

// ascii fast path
if (!(traits & formatted_string_traits::non_ascii))
{
Expand Down
2 changes: 1 addition & 1 deletion include/toml++/impl/toml_formatter.inl
Original file line number Diff line number Diff line change
Expand Up @@ -130,7 +130,7 @@ TOML_NAMESPACE_START
TOML_EXTERNAL_LINKAGE
void toml_formatter::print(const key& k)
{
print_string(k.str(), false, true);
print_string(k.str(), false, true, false);
}

TOML_EXTERNAL_LINKAGE
Expand Down
24 changes: 19 additions & 5 deletions tests/user_feedback.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -415,19 +415,33 @@ b = []

SECTION("tomlplusplus/issues/176") // https://github.com/marzer/tomlplusplus/issues/176
{
parsing_should_succeed(FILE_LINE_ARGS,
R"(
parsing_should_succeed(FILE_LINE_ARGS, " a = \"x\\ty\""sv);
parsing_should_succeed(FILE_LINE_ARGS, "\"a\" = \"x\\ty\""sv);
parsing_should_succeed(FILE_LINE_ARGS, "\"a\tb\" = \"x\\ty\""sv);
parsing_should_fail(FILE_LINE_ARGS, "\"a\nb\" = \"x\\ty\""sv); // literal newline in single-line key

static constexpr auto input = R"(
"a" = "x\ty"
"a\tb" = "x\ty"
)",
[](auto&& tbl)
"a\nb" = "x\ty"
)"sv;

static constexpr auto output = "a = 'x\ty'\n"
"\"a\\tb\" = 'x\ty'\n" // tab and newlines in keys should be emitted
"\"a\\nb\" = 'x\ty'" // as escapes, not literals
""sv;

parsing_should_succeed(FILE_LINE_ARGS,
input,
[&](auto&& tbl)
{
CHECK(tbl["a"]);
CHECK(tbl["a\tb"]);
CHECK(tbl["a\nb"]);

std::stringstream ss;
ss << tbl;
CHECK(ss.str() == "a = 'x\ty'\n'a\tb' = 'x\ty'"sv);
CHECK(ss.str() == output);
});
}
}
33 changes: 21 additions & 12 deletions toml.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -9782,7 +9782,10 @@ TOML_IMPL_NAMESPACE_START
void print_unformatted(std::string_view);

TOML_EXPORTED_MEMBER_FUNCTION
void print_string(std::string_view str, bool allow_multi_line = true, bool allow_bare = false);
void print_string(std::string_view str,
bool allow_multi_line = true,
bool allow_bare = false,
bool allow_literal_whitespace = true);

TOML_EXPORTED_MEMBER_FUNCTION
void print(const value<std::string>&);
Expand Down Expand Up @@ -16468,7 +16471,10 @@ TOML_IMPL_NAMESPACE_START
}

TOML_EXTERNAL_LINKAGE
void formatter::print_string(std::string_view str, bool allow_multi_line, bool allow_bare)
void formatter::print_string(std::string_view str,
bool allow_multi_line,
bool allow_bare,
bool allow_literal_whitespace)
{
if (str.empty())
{
Expand Down Expand Up @@ -16561,8 +16567,10 @@ TOML_IMPL_NAMESPACE_START
bad_unicode();
}

// strings with line breaks and tabs can't be bare
if (!!(traits & (formatted_string_traits::line_breaks | formatted_string_traits::tabs)))
// strings with line breaks, tabs, and single-quotes can't be bare
if (!!(traits
& (formatted_string_traits::line_breaks | formatted_string_traits::tabs
| formatted_string_traits::single_quotes)))
traits |= formatted_string_traits::non_bare;

// if the string meets the requirements of being 'bare' we can emit a bare string
Expand All @@ -16573,17 +16581,20 @@ TOML_IMPL_NAMESPACE_START
print_unformatted(str);
return;
}
const auto real_tabs_allowed = allow_literal_whitespace && real_tabs_in_strings_allowed();

// determine if this should be a multi-line string (triple-quotes)
const auto multi_line = allow_multi_line //
const auto multi_line = allow_literal_whitespace //
&& allow_multi_line //
&& multi_line_strings_allowed() //
&& !!(traits & formatted_string_traits::line_breaks);

// determine if this should be a literal string (single-quotes with no escaping)
const auto literal = literal_strings_allowed() //
&& !(traits & formatted_string_traits::control_chars) //
&& (!(traits & formatted_string_traits::single_quotes) || multi_line) //
&& (!(traits & formatted_string_traits::tabs) || real_tabs_in_strings_allowed()) //
const auto literal = literal_strings_allowed() //
&& !(traits & formatted_string_traits::control_chars) //
&& (!(traits & formatted_string_traits::single_quotes) || multi_line) //
&& (!(traits & formatted_string_traits::tabs) || real_tabs_allowed) //
&& (!(traits & formatted_string_traits::line_breaks) || multi_line) //
&& (!(traits & formatted_string_traits::non_ascii) || unicode_allowed);

// literal strings (single quotes, no escape codes)
Expand All @@ -16599,8 +16610,6 @@ TOML_IMPL_NAMESPACE_START
// anything from here down is a non-literal string, so requires iteration and escaping.
print_unformatted(multi_line ? R"(""")"sv : R"(")"sv);

const auto real_tabs_allowed = real_tabs_in_strings_allowed();

// ascii fast path
if (!(traits & formatted_string_traits::non_ascii))
{
Expand Down Expand Up @@ -17005,7 +17014,7 @@ TOML_NAMESPACE_START
TOML_EXTERNAL_LINKAGE
void toml_formatter::print(const key& k)
{
print_string(k.str(), false, true);
print_string(k.str(), false, true, false);
}

TOML_EXTERNAL_LINKAGE
Expand Down

0 comments on commit cc1962e

Please sign in to comment.