diff --git a/include/fmt/format-inl.h b/include/fmt/format-inl.h index 8391aa9b..61256d2c 100644 --- a/include/fmt/format-inl.h +++ b/include/fmt/format-inl.h @@ -1473,17 +1473,13 @@ FMT_FUNC std::string vformat(string_view fmt, format_args args) { return to_string(buffer); } -#ifdef _WIN32 namespace detail { +#ifdef _WIN32 using dword = conditional_t; extern "C" __declspec(dllimport) int __stdcall WriteConsoleW( // void*, const void*, dword, dword*, void*); -} // namespace detail -#endif -namespace detail { -FMT_FUNC void print(std::FILE* f, string_view text) { -#ifdef _WIN32 +FMT_FUNC bool write_console_on_windows(std::FILE* f, string_view text) { auto fd = _fileno(f); if (_isatty(fd)) { detail::utf8_to_utf16 u16(string_view(text.data(), text.size())); @@ -1491,11 +1487,20 @@ FMT_FUNC void print(std::FILE* f, string_view text) { if (detail::WriteConsoleW(reinterpret_cast(_get_osfhandle(fd)), u16.c_str(), static_cast(u16.size()), &written, nullptr)) { - return; + return true; } - // Fallback to fwrite on failure. It can happen if the output has been - // redirected to NUL. } + // We return false if the file descriptor was not TTY, or it was but + // SetConsoleW failed which can happen if the output has been redirected to + // NUL. In both cases when we return false, we should attempt to do regular + // write via fwrite or std::ostream::write. + return false; +} +#endif + +FMT_FUNC void print(std::FILE* f, string_view text) { +#ifdef _WIN32 + if (write_console_on_windows(f, text)) return; #endif detail::fwrite_fully(text.data(), 1, text.size(), f); } diff --git a/include/fmt/format.h b/include/fmt/format.h index 83b2a222..40f15d15 100644 --- a/include/fmt/format.h +++ b/include/fmt/format.h @@ -921,6 +921,7 @@ struct is_contiguous> : std::true_type { }; namespace detail { +bool write_console_on_windows(std::FILE* f, string_view text); FMT_API void print(std::FILE*, string_view); }