diff --git a/include/fmt/format.h b/include/fmt/format.h index 690dfb00..7181780f 100644 --- a/include/fmt/format.h +++ b/include/fmt/format.h @@ -1421,11 +1421,11 @@ template class to_utf8 { public: to_utf8() {} - explicit to_utf8(basic_string_view s) { + explicit to_utf8(basic_string_view s, + to_utf8_error_policy policy = to_utf8_error_policy::abort) { static_assert(sizeof(WChar) == 2 || sizeof(WChar) == 4, "Expect utf16 or utf32"); - - if (!convert(s)) + if (!convert(s, policy)) FMT_THROW(std::runtime_error(sizeof(WChar) == 2 ? "invalid utf16" : "invalid utf32")); } @@ -1437,8 +1437,9 @@ template class to_utf8 { // Performs conversion returning a bool instead of throwing exception on // conversion error. This method may still throw in case of memory allocation // error. - bool convert(basic_string_view s) { - if (!convert(buffer_, s)) return false; + bool convert(basic_string_view s, + to_utf8_error_policy policy = to_utf8_error_policy::abort) { + if (!convert(buffer_, s, policy)) return false; buffer_.push_back(0); return true; } @@ -1453,11 +1454,11 @@ template class to_utf8 { if (p == s.end() || (c & 0xfc00) != 0xd800 || (*p & 0xfc00) != 0xdc00) { if (policy == to_utf8_error_policy::abort) return false; buf.append(string_view("�")); + --p; } else { c = (c << 10) + static_cast(*p) - 0x35fdc00; } - } - if (c < 0x80) { + } else if (c < 0x80) { buf.push_back(static_cast(c)); } else if (c < 0x800) { buf.push_back(static_cast(0xc0 | (c >> 6))); diff --git a/include/fmt/std.h b/include/fmt/std.h index 7ac81aea..8135f757 100644 --- a/include/fmt/std.h +++ b/include/fmt/std.h @@ -74,7 +74,7 @@ void write_escaped_path(basic_memory_buffer& quoted, # ifdef _WIN32 template <> auto get_path_string(const std::filesystem::path& p) { - return to_utf8(p.native()); + return to_utf8(p.native(), to_utf8_error_policy::replace); } template <> diff --git a/test/std-test.cc b/test/std-test.cc index 35867f05..fda8e96b 100644 --- a/test/std-test.cc +++ b/test/std-test.cc @@ -15,23 +15,23 @@ #include "fmt/ranges.h" #include "gtest-extra.h" // StartsWith -using testing::StartsWith; - #ifdef __cpp_lib_filesystem TEST(std_test, path) { - EXPECT_EQ(fmt::format("{:8}", std::filesystem::path("foo")), "foo "); - EXPECT_EQ(fmt::format("{}", std::filesystem::path("foo\"bar.txt")), - "foo\"bar.txt"); - EXPECT_EQ(fmt::format("{:?}", std::filesystem::path("foo\"bar.txt")), - "\"foo\\\"bar.txt\""); + using std::filesystem::path; + EXPECT_EQ(fmt::format("{}", path("/usr/bin")), "/usr/bin"); + EXPECT_EQ(fmt::format("{:?}", path("/usr/bin")), "\"/usr/bin\""); + EXPECT_EQ(fmt::format("{:8}", path("foo")), "foo "); + + EXPECT_EQ(fmt::format("{}", path("foo\"bar")), "foo\"bar"); + EXPECT_EQ(fmt::format("{:?}", path("foo\"bar")), "\"foo\\\"bar\""); # ifdef _WIN32 - EXPECT_EQ(fmt::format("{}", std::filesystem::path( + EXPECT_EQ(fmt::format("{}", path( L"\x0428\x0447\x0443\x0447\x044B\x043D\x0448" L"\x0447\x044B\x043D\x0430")), "Шчучыншчына"); - EXPECT_EQ(fmt::format("{:?}", std::filesystem::path(L"\xd800")), - "\"\\ud800\""); + EXPECT_EQ(fmt::format("{}", path(L"\xd800")), "�"); + EXPECT_EQ(fmt::format("{:?}", path(L"\xd800")), "\"\\ud800\""); # endif } @@ -191,6 +191,7 @@ const char* my_exception::what() const noexcept { return msg.c_str(); } } // namespace my_ns1 TEST(std_test, exception) { + using testing::StartsWith; exception_test(); exception_test();