From f6fac063591b0f959437528aa95af4ccec38c7e0 Mon Sep 17 00:00:00 2001 From: Dimitrij Mijoski Date: Wed, 25 Oct 2023 19:48:56 +0200 Subject: [PATCH] Fix flushing C++ iostreams before calling write_console() 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 | 27 ++++++++++++++++++++------- 3 files changed, 31 insertions(+), 14 deletions(-) diff --git a/include/fmt/format-inl.h b/include/fmt/format-inl.h index 4dc182f083c16..e1d1eb5657bb7 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 /*fd*/, string_view /*text*/) { 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 6646f0409f304..8030e9a5122af 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 a5eddcb0874f8..0c69e46c3d833 100644 --- a/include/fmt/ostream.h +++ b/include/fmt/ostream.h @@ -10,10 +10,13 @@ #include // std::filebuf -#if defined(_WIN32) && defined(__GLIBCXX__) +#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); + f = get_file(*buf); + else + return false; #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(); + f = sfbuf->file(); else if (auto* fbuf = dynamic_cast<__gnu_cxx::stdio_filebuf*>(rdbuf)) - c_file = fbuf->file(); + 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; }