From 2a2c6e676f4c2100b2b0f6b3390543ba94ca4b3d Mon Sep 17 00:00:00 2001 From: Dimitrij Mijoski Date: Wed, 25 Oct 2023 22:13:31 +0200 Subject: [PATCH] Fix flushing C++ iostreams before calling write_console() (#3689) This change correctly implements https://wg21.link/P2539/ for both C streams and C++ iostreams. Fixes #3688. --- include/fmt/format-inl.h | 16 ++++++++++------ include/fmt/format.h | 2 +- include/fmt/ostream.h | 39 ++++++++++++++++++++++++++------------- 3 files changed, 37 insertions(+), 20 deletions(-) diff --git a/include/fmt/format-inl.h b/include/fmt/format-inl.h index 4dc182f0..5f8c83a2 100644 --- a/include/fmt/format-inl.h +++ b/include/fmt/format-inl.h @@ -1425,16 +1425,13 @@ FMT_FUNC std::string vformat(string_view fmt, format_args args) { namespace detail { #if !defined(_WIN32) || defined(FMT_WINDOWS_NO_WCHAR) -FMT_FUNC bool write_console(std::FILE*, string_view) { return false; } +FMT_FUNC bool write_console(int, string_view) { return false; } #else using dword = conditional_t; extern "C" __declspec(dllimport) int __stdcall WriteConsoleW( // void*, const void*, dword, dword*, void*); -FMT_FUNC bool write_console(std::FILE* f, string_view text) { - int fd = _fileno(f); - if (!_isatty(fd)) return false; - std::fflush(f); +FMT_FUNC bool write_console(int fd, string_view text) { auto u16 = utf8_to_utf16(text); return WriteConsoleW(reinterpret_cast(_get_osfhandle(fd)), u16.c_str(), static_cast(u16.size()), nullptr, nullptr) != 0; @@ -1451,7 +1448,14 @@ FMT_FUNC void vprint_mojibake(std::FILE* f, string_view fmt, format_args args) { #endif FMT_FUNC void print(std::FILE* f, string_view text) { - if (!write_console(f, text)) fwrite_fully(text.data(), text.size(), f); +#ifdef _WIN32 + int fd = _fileno(f); + if (_isatty(fd)) { + std::fflush(f); + if (write_console(fd, text)) return; + } +#endif + fwrite_fully(text.data(), text.size(), f); } } // namespace detail diff --git a/include/fmt/format.h b/include/fmt/format.h index 6646f040..8030e9a5 100644 --- a/include/fmt/format.h +++ b/include/fmt/format.h @@ -1038,7 +1038,7 @@ struct is_contiguous> : std::true_type { FMT_END_EXPORT namespace detail { -FMT_API bool write_console(std::FILE* f, string_view text); +FMT_API bool write_console(int fd, string_view text); FMT_API void print(std::FILE*, string_view); } // namespace detail diff --git a/include/fmt/ostream.h b/include/fmt/ostream.h index a5eddcb0..782ace5c 100644 --- a/include/fmt/ostream.h +++ b/include/fmt/ostream.h @@ -10,9 +10,12 @@ #include // std::filebuf -#if defined(_WIN32) && defined(__GLIBCXX__) -# include -# include +#ifdef _WIN32 +# ifdef __GLIBCXX__ +# include +# include +# endif +# include #endif #include "format.h" @@ -38,21 +41,31 @@ auto get_file(std::filebuf&) -> FILE*; #endif inline bool write_ostream_unicode(std::ostream& os, fmt::string_view data) { + FILE* f = nullptr; #if FMT_MSC_VERSION if (auto* buf = dynamic_cast(os.rdbuf())) - if (FILE* f = get_file(*buf)) return write_console(f, data); -#elif defined(_WIN32) && defined(__GLIBCXX__) - auto* rdbuf = os.rdbuf(); - FILE* c_file; - if (auto* sfbuf = dynamic_cast<__gnu_cxx::stdio_sync_filebuf*>(rdbuf)) - c_file = sfbuf->file(); - else if (auto* fbuf = dynamic_cast<__gnu_cxx::stdio_filebuf*>(rdbuf)) - c_file = fbuf->file(); + f = get_file(*buf); + else + return false; +#elif defined(_WIN32) && defined(__GLIBCXX__) + auto* rdbuf = os.rdbuf(); + if (auto* sfbuf = dynamic_cast<__gnu_cxx::stdio_sync_filebuf*>(rdbuf)) + f = sfbuf->file(); + else if (auto* fbuf = dynamic_cast<__gnu_cxx::stdio_filebuf*>(rdbuf)) + f = fbuf->file(); else return false; - if (c_file) return write_console(c_file, data); #else - ignore_unused(os, data); + ignore_unused(os, data, f); +#endif +#ifdef _WIN32 + if (f) { + int fd = _fileno(f); + if (_isatty(fd)) { + os.flush(); + return write_console(fd, data); + } + } #endif return false; }