Skip to content

Commit

Permalink
Add Windows _setmode calls to ensure correct character interpretation
Browse files Browse the repository at this point in the history
  • Loading branch information
jackoalan committed Jul 22, 2019
1 parent 4fb73d1 commit 4c7501c
Show file tree
Hide file tree
Showing 2 changed files with 42 additions and 0 deletions.
31 changes: 31 additions & 0 deletions include/fmt/format-inl.h
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,8 @@
# else
# define NOMINMAX
# include <windows.h>
# include <io.h>
# include <fcntl.h>
# undef NOMINMAX
# endif
#endif
Expand Down Expand Up @@ -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);
Expand Down Expand Up @@ -924,6 +929,26 @@ FMT_FUNC void internal::format_windows_error(internal::buffer<char>& 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<char>& out, int error_code,
Expand Down Expand Up @@ -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<buffer_context<char>>(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);
}

Expand Down
11 changes: 11 additions & 0 deletions include/fmt/format.h
Original file line number Diff line number Diff line change
Expand Up @@ -927,6 +927,17 @@ class utf16_to_utf8 {
FMT_API void format_windows_error(fmt::internal::buffer<char>& 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 <typename T = void> struct null {};
Expand Down

0 comments on commit 4c7501c

Please sign in to comment.