diff --git a/include/fmt/format-inl.h b/include/fmt/format-inl.h index 654ccc3d..252154c6 100644 --- a/include/fmt/format-inl.h +++ b/include/fmt/format-inl.h @@ -1517,6 +1517,12 @@ template class apple_file : public file_base { this->file_->_p += size; this->file_->_w -= size; } + + bool needs_flush() const { + if ((this->file_->_flags & 1) == 0) return false; // 1 is __SLBF. + return memchr(this->file_->_p + this->file_->_w, '\n', + to_unsigned(-this->file_->_w)); + } }; // A fallback FILE wrapper. @@ -1529,6 +1535,7 @@ template class fallback_file : public file_base { using file_base::file_base; auto is_buffered() const -> bool { return false; } + auto needs_flush() const -> bool { return false; } auto get_read_buffer() const -> span { return {&next_, has_next_ ? 1u : 0u}; @@ -1581,7 +1588,9 @@ class file_print_buffer : public buffer { } ~file_print_buffer() { file_.advance_write_buffer(size()); + bool flush = file_.needs_flush(); funlockfile(file_); + if (flush) fflush(file_); } }; diff --git a/test/format-test.cc b/test/format-test.cc index 2b8255a5..9c89e898 100644 --- a/test/format-test.cc +++ b/test/format-test.cc @@ -1752,6 +1752,21 @@ TEST(format_test, big_print) { EXPECT_WRITE(stdout, big_print(), std::string(count, 'x')); } +// Windows CRT implements _IOLBF incorrectly (full buffering). +#if FMT_USE_FCNTL && !defined(_WIN32) +TEST(format_test, line_buffering) { + auto pipe = fmt::pipe(); + auto read_end = pipe.read_end.fdopen("r"); + auto write_end = pipe.write_end.fdopen("w"); + setvbuf(write_end.get(), nullptr, _IOLBF, 4096); + write_end.print("42\n"); + int n = 0; + int result = fscanf(read_end.get(), "%d", &n); + (void)result; + EXPECT_EQ(n, 42); +} +#endif + TEST(format_test, variadic) { EXPECT_EQ(fmt::format("{}c{}", "ab", 1), "abc1"); }