diff --git a/tests/test_parse_file.cpp b/tests/test_parse_file.cpp index f6e4512..c2d3610 100644 --- a/tests/test_parse_file.cpp +++ b/tests/test_parse_file.cpp @@ -905,6 +905,22 @@ BOOST_AUTO_TEST_CASE(test_files_end_with_empty_lines) } } +BOOST_AUTO_TEST_CASE(test_file_ends_without_lf) +{ + { + const std::string table( + "key = \"value\"\n" + "[table]\n" + "key = \"value\"" + ); + std::istringstream iss(table); + const auto data = toml::parse(iss, + "test_files_end_without_lf.toml"); + + BOOST_TEST(toml::find(data, "key") == "value"); + BOOST_TEST(toml::find(toml::find(data, "table"), "key") == "value"); + } +} BOOST_AUTO_TEST_CASE(test_parse_function_compiles) { diff --git a/toml/parser.hpp b/toml/parser.hpp index 7636f14..a060114 100644 --- a/toml/parser.hpp +++ b/toml/parser.hpp @@ -2209,12 +2209,24 @@ parse(std::istream& is, const std::string& fname = "unknown file") std::vector letters(static_cast(fsize)); is.read(letters.data(), fsize); + // remove null character if exists while(!letters.empty() && letters.back() == '\0') { letters.pop_back(); } assert(letters.empty() || letters.back() != '\0'); + // append LF. + // Although TOML does not require LF at the EOF, to make parsing logic + // simpler, we "normalize" the content by adding LF if it does not exist. + // It also checks if the last char is CR, to avoid changing the meaning. + // This is not the *best* way to deal with the last character, but is a + // simple and quick fix. + if(!letters.empty() && letters.back() != '\n' && letters.back() != '\r') + { + letters.push_back('\n'); + } + detail::location loc(std::move(fname), std::move(letters)); // skip BOM if exists.