From 4c7501c339382460a23fd1a278d7aec871479e58 Mon Sep 17 00:00:00 2001 From: Jack Andersen Date: Sun, 21 Jul 2019 17:04:10 -1000 Subject: [PATCH] Add Windows _setmode calls to ensure correct character interpretation --- include/fmt/format-inl.h | 31 +++++++++++++++++++++++++++++++ include/fmt/format.h | 11 +++++++++++ 2 files changed, 42 insertions(+) diff --git a/include/fmt/format-inl.h b/include/fmt/format-inl.h index f6fa29abd011..7d22ce114d3b 100644 --- a/include/fmt/format-inl.h +++ b/include/fmt/format-inl.h @@ -32,6 +32,8 @@ # else # define NOMINMAX # include +# include +# include # undef NOMINMAX # endif #endif @@ -186,6 +188,9 @@ FMT_FUNC void report_error(format_func func, int error_code, string_view message) FMT_NOEXCEPT { memory_buffer full_message; func(full_message, error_code, message); +#if FMT_USE_WINDOWS_H + file_mode_setter mode_setter(stderr, _O_TEXT); +#endif // Use Writer::data instead of Writer::c_str to avoid potential memory // allocation. fwrite_fully(full_message.data(), 1, full_message.size(), stderr); @@ -924,6 +929,26 @@ FMT_FUNC void internal::format_windows_error(internal::buffer& out, format_error_code(out, error_code, message); } +FMT_API FMT_FUNC internal::file_mode_setter::file_mode_setter(std::FILE* f, + int mode) + FMT_NOEXCEPT : f_(f), fileno_(_fileno(f)), mode_(mode), + prevmode_(_setmode(fileno_, mode)) { + // Since _getmode does not exist, we must switch back to the previous mode + // in order to flush potentially remaining data in the FILE buffer. + if (prevmode_ != -1 && prevmode_ != mode_) { + _setmode(fileno_, prevmode_); + std::fflush(f_); + _setmode(fileno_, mode_); + } +} + +FMT_API FMT_FUNC internal::file_mode_setter::~file_mode_setter() FMT_NOEXCEPT { + if (prevmode_ != -1 && prevmode_ != mode_) { + std::fflush(f_); + _setmode(fileno_, prevmode_); + } +} + #endif // FMT_USE_WINDOWS_H FMT_FUNC void format_system_error(internal::buffer& out, int error_code, @@ -971,12 +996,18 @@ FMT_FUNC void vprint(std::FILE* f, string_view format_str, format_args args) { memory_buffer buffer; internal::vformat_to(buffer, format_str, basic_format_args>(args)); +#if FMT_USE_WINDOWS_H + internal::file_mode_setter mode_setter(f, _O_TEXT); +#endif internal::fwrite_fully(buffer.data(), 1, buffer.size(), f); } FMT_FUNC void vprint(std::FILE* f, wstring_view format_str, wformat_args args) { wmemory_buffer buffer; internal::vformat_to(buffer, format_str, args); +#if FMT_USE_WINDOWS_H + internal::file_mode_setter mode_setter(f, _O_WTEXT); +#endif internal::fwrite_fully(buffer.data(), sizeof(wchar_t), buffer.size(), f); } diff --git a/include/fmt/format.h b/include/fmt/format.h index ea6678db2ea1..9867e7907a7b 100644 --- a/include/fmt/format.h +++ b/include/fmt/format.h @@ -927,6 +927,17 @@ class utf16_to_utf8 { FMT_API void format_windows_error(fmt::internal::buffer& out, int error_code, fmt::string_view message) FMT_NOEXCEPT; + +// RAII class to temporarily set the mode of a FILE stream for correct +// interpretation of character types other than the stream's default. +class FMT_API file_mode_setter { + private: + std::FILE* f_; + int fileno_, mode_, prevmode_; + public: + explicit file_mode_setter(std::FILE* f, int mode) FMT_NOEXCEPT; + ~file_mode_setter() FMT_NOEXCEPT; +}; #endif template struct null {};