about summary refs log tree commit diff
path: root/absl/strings
diff options
context:
space:
mode:
Diffstat (limited to 'absl/strings')
-rw-r--r--absl/strings/internal/str_format/output.cc27
-rw-r--r--absl/strings/str_format_test.cc5
2 files changed, 28 insertions, 4 deletions
diff --git a/absl/strings/internal/str_format/output.cc b/absl/strings/internal/str_format/output.cc
index 5c3795b737ca..d7fef69b4cf9 100644
--- a/absl/strings/internal/str_format/output.cc
+++ b/absl/strings/internal/str_format/output.cc
@@ -20,6 +20,16 @@
 namespace absl {
 namespace str_format_internal {
 
+namespace {
+struct ClearErrnoGuard {
+  ClearErrnoGuard() : old_value(errno) { errno = 0; }
+  ~ClearErrnoGuard() {
+    if (!errno) errno = old_value;
+  }
+  int old_value;
+};
+}  // namespace
+
 void BufferRawSink::Write(string_view v) {
   size_t to_write = std::min(v.size(), size_);
   std::memcpy(buffer_, v.data(), to_write);
@@ -30,14 +40,27 @@ void BufferRawSink::Write(string_view v) {
 
 void FILERawSink::Write(string_view v) {
   while (!v.empty() && !error_) {
+    // Reset errno to zero in case the libc implementation doesn't set errno
+    // when a failure occurs.
+    ClearErrnoGuard guard;
+
     if (size_t result = std::fwrite(v.data(), 1, v.size(), output_)) {
       // Some progress was made.
       count_ += result;
       v.remove_prefix(result);
     } else {
-      // Some error occurred.
-      if (errno != EINTR) {
+      if (errno == EINTR) {
+        continue;
+      } else if (errno) {
         error_ = errno;
+      } else if (std::ferror(output_)) {
+        // Non-POSIX compliant libc implementations may not set errno, so we
+        // have check the streams error indicator.
+        error_ = EBADF;
+      } else {
+        // We're likely on a non-POSIX system that encountered EINTR but had no
+        // way of reporting it.
+        continue;
       }
     }
   }
diff --git a/absl/strings/str_format_test.cc b/absl/strings/str_format_test.cc
index ea9a3a176612..87ed234fe825 100644
--- a/absl/strings/str_format_test.cc
+++ b/absl/strings/str_format_test.cc
@@ -242,6 +242,7 @@ class TempFile {
   std::string ReadFile() {
     std::fseek(file_, 0, SEEK_END);
     int size = std::ftell(file_);
+    EXPECT_GT(size, 0);
     std::rewind(file_);
     std::string str(2 * size, ' ');
     int read_bytes = std::fread(&str[0], 1, str.size(), file_);
@@ -270,7 +271,7 @@ TEST_F(FormatEntryPointTest, FPrintFError) {
   EXPECT_EQ(errno, EBADF);
 }
 
-#if __GNUC__
+#if __GLIBC__
 TEST_F(FormatEntryPointTest, FprintfTooLarge) {
   std::FILE* f = std::fopen("/dev/null", "w");
   int width = 2000000000;
@@ -297,7 +298,7 @@ TEST_F(FormatEntryPointTest, PrintF) {
   EXPECT_EQ(result, 30);
   EXPECT_EQ(tmp.ReadFile(), "STRING: ABC NUMBER: -000000019");
 }
-#endif  // __GNUC__
+#endif  // __GLIBC__
 
 TEST_F(FormatEntryPointTest, SNPrintF) {
   char buffer[16];