[libc-commits] [libc] [libc] Add printf error handling (with fixes) (PR #166382)

Marcell Leleszi via libc-commits libc-commits at lists.llvm.org
Tue Nov 4 07:30:54 PST 2025


https://github.com/mleleszi created https://github.com/llvm/llvm-project/pull/166382

None

>From 25c7d79a96f2ee0db4c49ee4854c3ae986394dda Mon Sep 17 00:00:00 2001
From: Marcell Leleszi <mleleszi at google.com>
Date: Tue, 7 Oct 2025 06:16:51 +0000
Subject: [PATCH 01/27] Change writer to store chars written as size_t

---
 libc/src/stdio/printf_core/printf_main.h         | 3 ++-
 libc/src/stdio/printf_core/write_int_converter.h | 4 ++--
 libc/src/stdio/printf_core/writer.h              | 8 ++++----
 libc/src/stdlib/strfromd.cpp                     | 3 ++-
 libc/src/stdlib/strfromf.cpp                     | 3 ++-
 libc/src/stdlib/strfroml.cpp                     | 3 ++-
 6 files changed, 14 insertions(+), 10 deletions(-)

diff --git a/libc/src/stdio/printf_core/printf_main.h b/libc/src/stdio/printf_core/printf_main.h
index 57f29858d5298..c654571d1dffd 100644
--- a/libc/src/stdio/printf_core/printf_main.h
+++ b/libc/src/stdio/printf_core/printf_main.h
@@ -38,7 +38,8 @@ int printf_main(Writer<write_mode> *writer, const char *__restrict str,
       return result;
   }
 
-  return writer->get_chars_written();
+  // TODO overflow
+  return static_cast<int>(writer->get_chars_written());
 }
 
 } // namespace printf_core
diff --git a/libc/src/stdio/printf_core/write_int_converter.h b/libc/src/stdio/printf_core/write_int_converter.h
index efcff278bd284..04b2bef05bc7b 100644
--- a/libc/src/stdio/printf_core/write_int_converter.h
+++ b/libc/src/stdio/printf_core/write_int_converter.h
@@ -29,11 +29,11 @@ LIBC_INLINE int convert_write_int(Writer<write_mode> *writer,
     return NULLPTR_WRITE_ERROR;
 #endif // LIBC_COPT_PRINTF_NO_NULLPTR_CHECKS
 
-  int written = writer->get_chars_written();
+  size_t written = writer->get_chars_written();
 
   switch (to_conv.length_modifier) {
   case LengthModifier::none:
-    *reinterpret_cast<int *>(to_conv.conv_val_ptr) = written;
+    *reinterpret_cast<int *>(to_conv.conv_val_ptr) = static_cast<int>(written);
     break;
   case LengthModifier::l:
     *reinterpret_cast<long *>(to_conv.conv_val_ptr) = written;
diff --git a/libc/src/stdio/printf_core/writer.h b/libc/src/stdio/printf_core/writer.h
index 1d4734a51b9b8..9de108ece510f 100644
--- a/libc/src/stdio/printf_core/writer.h
+++ b/libc/src/stdio/printf_core/writer.h
@@ -127,7 +127,7 @@ template <WriteMode write_mode> struct WriteBuffer {
 
 template <WriteMode write_mode> class Writer final {
   WriteBuffer<write_mode> &wb;
-  int chars_written = 0;
+  size_t chars_written = 0;
 
   LIBC_INLINE int pad(char new_char, size_t length) {
     // First, fill as much of the buffer as possible with the padding char.
@@ -161,7 +161,7 @@ template <WriteMode write_mode> class Writer final {
   // Takes a string, copies it into the buffer if there is space, else passes it
   // to the overflow mechanism to be handled separately.
   LIBC_INLINE int write(cpp::string_view new_string) {
-    chars_written += static_cast<int>(new_string.size());
+    chars_written += new_string.size();
     if (LIBC_LIKELY(wb.buff_cur + new_string.size() <= wb.buff_len)) {
       inline_memcpy(wb.buff + wb.buff_cur, new_string.data(),
                     new_string.size());
@@ -175,7 +175,7 @@ template <WriteMode write_mode> class Writer final {
   // if there is space, else calls pad which will loop and call the overflow
   // mechanism on a secondary buffer.
   LIBC_INLINE int write(char new_char, size_t length) {
-    chars_written += static_cast<int>(length);
+    chars_written += length;
 
     if (LIBC_LIKELY(wb.buff_cur + length <= wb.buff_len)) {
       inline_memset(wb.buff + wb.buff_cur, static_cast<unsigned char>(new_char),
@@ -199,7 +199,7 @@ template <WriteMode write_mode> class Writer final {
     return wb.overflow_write(char_string_view);
   }
 
-  LIBC_INLINE int get_chars_written() { return chars_written; }
+  LIBC_INLINE size_t get_chars_written() { return chars_written; }
 };
 
 // Class-template auto deduction helpers.
diff --git a/libc/src/stdlib/strfromd.cpp b/libc/src/stdlib/strfromd.cpp
index f51e6d4c7f1df..c0989f1a97dca 100644
--- a/libc/src/stdlib/strfromd.cpp
+++ b/libc/src/stdlib/strfromd.cpp
@@ -36,7 +36,8 @@ LLVM_LIBC_FUNCTION(int, strfromd,
   if (n > 0)
     wb.buff[wb.buff_cur] = '\0';
 
-  return writer.get_chars_written();
+  // TODO overflow
+  return static_cast<int>(writer.get_chars_written());
 }
 
 } // namespace LIBC_NAMESPACE_DECL
diff --git a/libc/src/stdlib/strfromf.cpp b/libc/src/stdlib/strfromf.cpp
index 14dbfdb25bab6..01090c997af1b 100644
--- a/libc/src/stdlib/strfromf.cpp
+++ b/libc/src/stdlib/strfromf.cpp
@@ -36,7 +36,8 @@ LLVM_LIBC_FUNCTION(int, strfromf,
   if (n > 0)
     wb.buff[wb.buff_cur] = '\0';
 
-  return writer.get_chars_written();
+  // TODO overflow
+  return static_cast<int>(writer.get_chars_written());
 }
 
 } // namespace LIBC_NAMESPACE_DECL
diff --git a/libc/src/stdlib/strfroml.cpp b/libc/src/stdlib/strfroml.cpp
index 12f22a8a2fb65..436243d1e1c38 100644
--- a/libc/src/stdlib/strfroml.cpp
+++ b/libc/src/stdlib/strfroml.cpp
@@ -41,7 +41,8 @@ LLVM_LIBC_FUNCTION(int, strfroml,
   if (n > 0)
     wb.buff[wb.buff_cur] = '\0';
 
-  return writer.get_chars_written();
+  // TODO overflow
+  return static_cast<int>(writer.get_chars_written());
 }
 
 } // namespace LIBC_NAMESPACE_DECL

>From 362c00e1c89faafbff7605233bb752447491a7be Mon Sep 17 00:00:00 2001
From: Marcell Leleszi <mleleszi at google.com>
Date: Tue, 7 Oct 2025 06:34:43 +0000
Subject: [PATCH 02/27] Prinf internals: use -errno as return values rather
 than custrom error values

---
 libc/src/stdio/printf_core/core_structs.h        | 12 ++++++------
 libc/src/stdio/printf_core/fixed_converter.h     |  3 ++-
 libc/src/stdio/printf_core/int_converter.h       |  3 ++-
 libc/src/stdio/printf_core/vasprintf_internal.h  |  5 +++--
 libc/src/stdio/printf_core/vfprintf_internal.h   |  3 ++-
 libc/src/stdio/printf_core/write_int_converter.h |  3 ++-
 6 files changed, 17 insertions(+), 12 deletions(-)

diff --git a/libc/src/stdio/printf_core/core_structs.h b/libc/src/stdio/printf_core/core_structs.h
index e27f77b6b594a..83e251a3b1da5 100644
--- a/libc/src/stdio/printf_core/core_structs.h
+++ b/libc/src/stdio/printf_core/core_structs.h
@@ -134,12 +134,12 @@ template <typename T> LIBC_INLINE constexpr TypeDesc type_desc_from_type() {
 constexpr int WRITE_OK = 0;
 // These are the printf return values for when an error has occurred. They are
 // all negative, and should be distinct.
-constexpr int FILE_WRITE_ERROR = -1;
-constexpr int FILE_STATUS_ERROR = -2;
-constexpr int NULLPTR_WRITE_ERROR = -3;
-constexpr int INT_CONVERSION_ERROR = -4;
-constexpr int FIXED_POINT_CONVERSION_ERROR = -5;
-constexpr int ALLOCATION_ERROR = -6;
+// constexpr int FILE_WRITE_ERROR = -1;
+// constexpr int FILE_STATUS_ERROR = -2;
+// constexpr int NULLPTR_WRITE_ERROR = -3;
+// constexpr int INT_CONVERSION_ERROR = -4;
+// constexpr int FIXED_POINT_CONVERSION_ERROR = -5;
+// constexpr int ALLOCATION_ERROR = -6;
 } // namespace printf_core
 } // namespace LIBC_NAMESPACE_DECL
 
diff --git a/libc/src/stdio/printf_core/fixed_converter.h b/libc/src/stdio/printf_core/fixed_converter.h
index e8a3314967562..a7185dcb11d8a 100644
--- a/libc/src/stdio/printf_core/fixed_converter.h
+++ b/libc/src/stdio/printf_core/fixed_converter.h
@@ -20,6 +20,7 @@
 #include "src/stdio/printf_core/converter_utils.h"
 #include "src/stdio/printf_core/core_structs.h"
 #include "src/stdio/printf_core/writer.h"
+#include "hdr/errno_macros.h"
 
 #include <inttypes.h>
 #include <stddef.h>
@@ -59,7 +60,7 @@ LIBC_INLINE constexpr uint32_t const_ten_exp(uint32_t exponent) {
       READ_FX_BITS(unsigned LENGTH_MODIFIER accum);                            \
     } else {                                                                   \
       LIBC_ASSERT(false && "Invalid conversion name passed to convert_fixed"); \
-      return FIXED_POINT_CONVERSION_ERROR;                                     \
+      return -EINVAL;                                     \
     }                                                                          \
   } while (false)
 
diff --git a/libc/src/stdio/printf_core/int_converter.h b/libc/src/stdio/printf_core/int_converter.h
index 11234c32ce997..08e27e1a60447 100644
--- a/libc/src/stdio/printf_core/int_converter.h
+++ b/libc/src/stdio/printf_core/int_converter.h
@@ -17,6 +17,7 @@
 #include "src/stdio/printf_core/converter_utils.h"
 #include "src/stdio/printf_core/core_structs.h"
 #include "src/stdio/printf_core/writer.h"
+#include "hdr/errno_macros.h"
 
 #include <inttypes.h>
 #include <stddef.h>
@@ -91,7 +92,7 @@ LIBC_INLINE int convert_int(Writer<write_mode> *writer,
   cpp::array<char, details::num_buf_size()> buf;
   auto str = details::num_to_strview(num, buf, to_conv.conv_name);
   if (!str)
-    return INT_CONVERSION_ERROR;
+    return -ERANGE;
 
   size_t digits_written = str->size();
 
diff --git a/libc/src/stdio/printf_core/vasprintf_internal.h b/libc/src/stdio/printf_core/vasprintf_internal.h
index 283d8df2810fb..4e868edca1a1e 100644
--- a/libc/src/stdio/printf_core/vasprintf_internal.h
+++ b/libc/src/stdio/printf_core/vasprintf_internal.h
@@ -6,6 +6,7 @@
 //
 //===----------------------------------------------------------------------===//
 
+#include "hdr/errno_macros.h"
 #include "hdr/func/free.h"
 #include "hdr/func/malloc.h"
 #include "hdr/func/realloc.h"
@@ -29,7 +30,7 @@ LIBC_INLINE int resize_overflow_hook(cpp::string_view new_str, void *target) {
   if (new_buff == nullptr) {
     if (wb->buff != wb->init_buff)
       free(wb->buff);
-    return printf_core::ALLOCATION_ERROR;
+    return -ENOMEM;
   }
   if (isBuffOnStack)
     inline_memcpy(new_buff, wb->buff, wb->buff_cur);
@@ -57,7 +58,7 @@ LIBC_INLINE int vasprintf_internal(char **ret, const char *__restrict format,
   if (wb.buff == init_buff_on_stack) {
     *ret = static_cast<char *>(malloc(ret_val + 1));
     if (ret == nullptr)
-      return printf_core::ALLOCATION_ERROR;
+      return -ENOMEM;
     inline_memcpy(*ret, wb.buff, ret_val);
   } else {
     *ret = wb.buff;
diff --git a/libc/src/stdio/printf_core/vfprintf_internal.h b/libc/src/stdio/printf_core/vfprintf_internal.h
index 630de9d9d43dd..552f2a2f5e34d 100644
--- a/libc/src/stdio/printf_core/vfprintf_internal.h
+++ b/libc/src/stdio/printf_core/vfprintf_internal.h
@@ -9,6 +9,7 @@
 #ifndef LLVM_LIBC_SRC_STDIO_PRINTF_CORE_VFPRINTF_INTERNAL_H
 #define LLVM_LIBC_SRC_STDIO_PRINTF_CORE_VFPRINTF_INTERNAL_H
 
+#include "hdr/errno_macros.h"
 #include "src/__support/File/file.h"
 #include "src/__support/arg_list.h"
 #include "src/__support/macros/attributes.h" // For LIBC_INLINE
@@ -63,7 +64,7 @@ LIBC_INLINE int file_write_hook(cpp::string_view new_str, void *fp) {
   size_t written = internal::fwrite_unlocked(new_str.data(), sizeof(char),
                                              new_str.size(), target_file);
   if (written != new_str.size() || internal::ferror_unlocked(target_file))
-    return FILE_WRITE_ERROR;
+    return -EIO;
   return WRITE_OK;
 }
 
diff --git a/libc/src/stdio/printf_core/write_int_converter.h b/libc/src/stdio/printf_core/write_int_converter.h
index 04b2bef05bc7b..15c32d9ac272a 100644
--- a/libc/src/stdio/printf_core/write_int_converter.h
+++ b/libc/src/stdio/printf_core/write_int_converter.h
@@ -12,6 +12,7 @@
 #include "src/__support/macros/config.h"
 #include "src/stdio/printf_core/core_structs.h"
 #include "src/stdio/printf_core/writer.h"
+#include "hdr/errno_macros.h"
 
 #include <inttypes.h>
 #include <stddef.h>
@@ -26,7 +27,7 @@ LIBC_INLINE int convert_write_int(Writer<write_mode> *writer,
 #ifndef LIBC_COPT_PRINTF_NO_NULLPTR_CHECKS
   // This is an additional check added by LLVM-libc.
   if (to_conv.conv_val_ptr == nullptr)
-    return NULLPTR_WRITE_ERROR;
+    return -EINVAL;
 #endif // LIBC_COPT_PRINTF_NO_NULLPTR_CHECKS
 
   size_t written = writer->get_chars_written();

>From f8c75692e43cbb7de691188a8b52f9392b096cf0 Mon Sep 17 00:00:00 2001
From: Marcell Leleszi <mleleszi at google.com>
Date: Tue, 7 Oct 2025 07:07:25 +0000
Subject: [PATCH 03/27] Refactor printf family functions to use PrintfResult

---
 libc/src/stdio/asprintf.cpp                     | 13 +++++++++++--
 libc/src/stdio/generic/fprintf.cpp              | 13 +++++++++++--
 libc/src/stdio/generic/printf.cpp               | 13 +++++++++++--
 libc/src/stdio/generic/vfprintf.cpp             | 13 +++++++++++--
 libc/src/stdio/generic/vprintf.cpp              | 13 +++++++++++--
 libc/src/stdio/printf_core/core_structs.h       | 12 ++++++++++++
 libc/src/stdio/printf_core/printf_main.h        |  9 ++++-----
 libc/src/stdio/printf_core/vasprintf_internal.h | 12 ++++++------
 libc/src/stdio/printf_core/vfprintf_internal.h  |  6 +++---
 libc/src/stdio/snprintf.cpp                     | 14 ++++++++++++--
 libc/src/stdio/sprintf.cpp                      | 14 ++++++++++++--
 libc/src/stdio/vasprintf.cpp                    | 11 ++++++++++-
 libc/src/stdio/vsnprintf.cpp                    | 14 ++++++++++++--
 libc/src/stdio/vsprintf.cpp                     | 13 +++++++++++--
 14 files changed, 137 insertions(+), 33 deletions(-)

diff --git a/libc/src/stdio/asprintf.cpp b/libc/src/stdio/asprintf.cpp
index f8cfb74ce48ea..d392c658b898c 100644
--- a/libc/src/stdio/asprintf.cpp
+++ b/libc/src/stdio/asprintf.cpp
@@ -22,8 +22,17 @@ LLVM_LIBC_FUNCTION(int, asprintf,
                                  // and pointer semantics, as well as handling
                                  // destruction automatically.
   va_end(vlist);
-  int ret = printf_core::vasprintf_internal(buffer, format, args);
-  return ret;
+  auto ret_val = printf_core::vasprintf_internal(buffer, format, args);
+  if (ret_val.has_error()) {
+    libc_errno = ret_val.error;
+    return -1;
+  }
+  if (ret_val.value > cpp::numeric_limits<int>::max()) {
+    libc_errno = EOVERFLOW;
+    return -1;
+  } 
+
+  return static_cast<int>(ret_val.value);  
 }
 
 } // namespace LIBC_NAMESPACE_DECL
diff --git a/libc/src/stdio/generic/fprintf.cpp b/libc/src/stdio/generic/fprintf.cpp
index 087aeadfc52c5..64d6694770e4e 100644
--- a/libc/src/stdio/generic/fprintf.cpp
+++ b/libc/src/stdio/generic/fprintf.cpp
@@ -27,8 +27,17 @@ LLVM_LIBC_FUNCTION(int, fprintf,
                                  // and pointer semantics, as well as handling
                                  // destruction automatically.
   va_end(vlist);
-  int ret_val = printf_core::vfprintf_internal(stream, format, args);
-  return ret_val;
+  auto ret_val = printf_core::vfprintf_internal(stream, format, args);
+  if (ret_val.has_error()) {
+    libc_errno = ret_val.error;
+    return -1;
+  }
+  if (ret_val.value > cpp::numeric_limits<int>::max()) {
+    libc_errno = EOVERFLOW;
+    return -1;
+  } 
+
+  return static_cast<int>(ret_val.value);  
 }
 
 } // namespace LIBC_NAMESPACE_DECL
diff --git a/libc/src/stdio/generic/printf.cpp b/libc/src/stdio/generic/printf.cpp
index bb7c7c86f843f..0020cce2359e8 100644
--- a/libc/src/stdio/generic/printf.cpp
+++ b/libc/src/stdio/generic/printf.cpp
@@ -31,9 +31,18 @@ LLVM_LIBC_FUNCTION(int, printf, (const char *__restrict format, ...)) {
                                  // and pointer semantics, as well as handling
                                  // destruction automatically.
   va_end(vlist);
-  int ret_val = printf_core::vfprintf_internal(
+  auto ret_val = printf_core::vfprintf_internal(
       reinterpret_cast<::FILE *>(PRINTF_STDOUT), format, args);
-  return ret_val;
+  if (ret_val.has_error()) {
+    libc_errno = ret_val.error;
+    return -1;
+  }
+  if (ret_val.value > cpp::numeric_limits<int>::max()) {
+    libc_errno = EOVERFLOW;
+    return -1;
+  } 
+
+  return static_cast<int>(ret_val.value);  
 }
 
 } // namespace LIBC_NAMESPACE_DECL
diff --git a/libc/src/stdio/generic/vfprintf.cpp b/libc/src/stdio/generic/vfprintf.cpp
index 01f4265f118a6..6311318862bd6 100644
--- a/libc/src/stdio/generic/vfprintf.cpp
+++ b/libc/src/stdio/generic/vfprintf.cpp
@@ -24,8 +24,17 @@ LLVM_LIBC_FUNCTION(int, vfprintf,
   internal::ArgList args(vlist); // This holder class allows for easier copying
                                  // and pointer semantics, as well as handling
                                  // destruction automatically.
-  int ret_val = printf_core::vfprintf_internal(stream, format, args);
-  return ret_val;
+  auto ret_val = printf_core::vfprintf_internal(stream, format, args);
+  if (ret_val.has_error()) {
+    libc_errno = ret_val.error;
+    return -1;
+  }
+  if (ret_val.value > cpp::numeric_limits<int>::max()) {
+    libc_errno = EOVERFLOW;
+    return -1;
+  } 
+
+  return static_cast<int>(ret_val.value);  
 }
 
 } // namespace LIBC_NAMESPACE_DECL
diff --git a/libc/src/stdio/generic/vprintf.cpp b/libc/src/stdio/generic/vprintf.cpp
index 08d71515646ed..73b9701ee2eda 100644
--- a/libc/src/stdio/generic/vprintf.cpp
+++ b/libc/src/stdio/generic/vprintf.cpp
@@ -29,9 +29,18 @@ LLVM_LIBC_FUNCTION(int, vprintf,
   internal::ArgList args(vlist); // This holder class allows for easier copying
                                  // and pointer semantics, as well as handling
                                  // destruction automatically.
-  int ret_val = printf_core::vfprintf_internal(
+  auto ret_val = printf_core::vfprintf_internal(
       reinterpret_cast<::FILE *>(PRINTF_STDOUT), format, args);
-  return ret_val;
+  if (ret_val.has_error()) {
+    libc_errno = ret_val.error;
+    return -1;
+  }
+  if (ret_val.value > cpp::numeric_limits<int>::max()) {
+    libc_errno = EOVERFLOW;
+    return -1;
+  } 
+
+  return static_cast<int>(ret_val.value); 
 }
 
 } // namespace LIBC_NAMESPACE_DECL
diff --git a/libc/src/stdio/printf_core/core_structs.h b/libc/src/stdio/printf_core/core_structs.h
index 83e251a3b1da5..e397e4a5b3abe 100644
--- a/libc/src/stdio/printf_core/core_structs.h
+++ b/libc/src/stdio/printf_core/core_structs.h
@@ -22,6 +22,18 @@
 namespace LIBC_NAMESPACE_DECL {
 namespace printf_core {
 
+struct PrintfResult {
+  size_t value;
+  int error;
+
+  constexpr PrintfResult(size_t val) : value(val), error(0) {}
+  constexpr PrintfResult(size_t val, int error) : value(val), error(error) {}
+
+  constexpr bool has_error() { return error != 0; }
+
+  constexpr operator size_t() { return value; }
+};
+
 // These length modifiers match the length modifiers in the format string, which
 // is why they are formatted differently from the rest of the file.
 enum class LengthModifier { hh, h, l, ll, j, z, t, L, w, wf, none };
diff --git a/libc/src/stdio/printf_core/printf_main.h b/libc/src/stdio/printf_core/printf_main.h
index c654571d1dffd..6a9c9a00265d1 100644
--- a/libc/src/stdio/printf_core/printf_main.h
+++ b/libc/src/stdio/printf_core/printf_main.h
@@ -22,7 +22,7 @@ namespace LIBC_NAMESPACE_DECL {
 namespace printf_core {
 
 template <WriteMode write_mode>
-int printf_main(Writer<write_mode> *writer, const char *__restrict str,
+PrintfResult printf_main(Writer<write_mode> *writer, const char *__restrict str,
                 internal::ArgList &args) {
   Parser<internal::ArgList> parser(str, args);
   int result = 0;
@@ -35,11 +35,10 @@ int printf_main(Writer<write_mode> *writer, const char *__restrict str,
       result = writer->write(cur_section.raw_string);
 
     if (result < 0)
-      return result;
+      return {0, -result};
   }
-
-  // TODO overflow
-  return static_cast<int>(writer->get_chars_written());
+  
+  return writer->get_chars_written();
 }
 
 } // namespace printf_core
diff --git a/libc/src/stdio/printf_core/vasprintf_internal.h b/libc/src/stdio/printf_core/vasprintf_internal.h
index 4e868edca1a1e..bfc02fedf5244 100644
--- a/libc/src/stdio/printf_core/vasprintf_internal.h
+++ b/libc/src/stdio/printf_core/vasprintf_internal.h
@@ -43,7 +43,7 @@ LIBC_INLINE int resize_overflow_hook(cpp::string_view new_str, void *target) {
 
 constexpr size_t DEFAULT_BUFFER_SIZE = 200;
 
-LIBC_INLINE int vasprintf_internal(char **ret, const char *__restrict format,
+LIBC_INLINE PrintfResult vasprintf_internal(char **ret, const char *__restrict format,
                                    internal::ArgList args) {
   char init_buff_on_stack[DEFAULT_BUFFER_SIZE];
   printf_core::WriteBuffer<Mode<WriteMode::RESIZE_AND_FILL_BUFF>::value> wb(
@@ -51,19 +51,19 @@ LIBC_INLINE int vasprintf_internal(char **ret, const char *__restrict format,
   printf_core::Writer writer(wb);
 
   auto ret_val = printf_core::printf_main(&writer, format, args);
-  if (ret_val < 0) {
+  if (ret_val.has_error()) {
     *ret = nullptr;
-    return -1;
+    return ret_val;
   }
   if (wb.buff == init_buff_on_stack) {
-    *ret = static_cast<char *>(malloc(ret_val + 1));
+    *ret = static_cast<char *>(malloc(ret_val.value + 1));
     if (ret == nullptr)
       return -ENOMEM;
-    inline_memcpy(*ret, wb.buff, ret_val);
+    inline_memcpy(*ret, wb.buff, ret_val.value);
   } else {
     *ret = wb.buff;
   }
-  (*ret)[ret_val] = '\0';
+  (*ret)[ret_val.value] = '\0'; // TODO OK HERE or overflow
   return ret_val;
 }
 } // namespace printf_core
diff --git a/libc/src/stdio/printf_core/vfprintf_internal.h b/libc/src/stdio/printf_core/vfprintf_internal.h
index 552f2a2f5e34d..71a85077e92b8 100644
--- a/libc/src/stdio/printf_core/vfprintf_internal.h
+++ b/libc/src/stdio/printf_core/vfprintf_internal.h
@@ -68,7 +68,7 @@ LIBC_INLINE int file_write_hook(cpp::string_view new_str, void *fp) {
   return WRITE_OK;
 }
 
-LIBC_INLINE int vfprintf_internal(::FILE *__restrict stream,
+LIBC_INLINE PrintfResult vfprintf_internal(::FILE *__restrict stream,
                                   const char *__restrict format,
                                   internal::ArgList &args) {
   constexpr size_t BUFF_SIZE = 1024;
@@ -77,10 +77,10 @@ LIBC_INLINE int vfprintf_internal(::FILE *__restrict stream,
       buffer, BUFF_SIZE, &file_write_hook, reinterpret_cast<void *>(stream));
   Writer writer(wb);
   internal::flockfile(stream);
-  int retval = printf_main(&writer, format, args);
+  auto retval = printf_main(&writer, format, args);
   int flushval = wb.overflow_write("");
   if (flushval != WRITE_OK)
-    retval = flushval;
+    retval.error = -flushval;
   internal::funlockfile(stream);
   return retval;
 }
diff --git a/libc/src/stdio/snprintf.cpp b/libc/src/stdio/snprintf.cpp
index c8940862f711f..8c22935b849a0 100644
--- a/libc/src/stdio/snprintf.cpp
+++ b/libc/src/stdio/snprintf.cpp
@@ -32,10 +32,20 @@ LLVM_LIBC_FUNCTION(int, snprintf,
       wb(buffer, (buffsz > 0 ? buffsz - 1 : 0));
   printf_core::Writer writer(wb);
 
-  int ret_val = printf_core::printf_main(&writer, format, args);
+  auto ret_val = printf_core::printf_main(&writer, format, args);
+  if (ret_val.has_error()) {
+    libc_errno = ret_val.error;
+    return -1;
+  }
   if (buffsz > 0) // if the buffsz is 0 the buffer may be a null pointer.
     wb.buff[wb.buff_cur] = '\0';
-  return ret_val;
+  
+  if (ret_val.value > cpp::numeric_limits<int>::max()) {
+    libc_errno = EOVERFLOW;
+    return -1;
+  } 
+  
+  return static_cast<int>(ret_val.value);  
 }
 
 } // namespace LIBC_NAMESPACE_DECL
diff --git a/libc/src/stdio/sprintf.cpp b/libc/src/stdio/sprintf.cpp
index 7be97d3591aaf..2557197894bec 100644
--- a/libc/src/stdio/sprintf.cpp
+++ b/libc/src/stdio/sprintf.cpp
@@ -33,9 +33,19 @@ LLVM_LIBC_FUNCTION(int, sprintf,
       wb(buffer, cpp::numeric_limits<size_t>::max());
   printf_core::Writer writer(wb);
 
-  int ret_val = printf_core::printf_main(&writer, format, args);
+  auto ret_val = printf_core::printf_main(&writer, format, args);
+  if (ret_val.has_error()) {
+    libc_errno = ret_val.error;
+    return -1;
+  }
   wb.buff[wb.buff_cur] = '\0';
-  return ret_val;
+
+  if (ret_val.value > cpp::numeric_limits<int>::max()) {
+    libc_errno = EOVERFLOW;
+    return -1;
+  } 
+  
+  return static_cast<int>(ret_val.value);  
 }
 
 } // namespace LIBC_NAMESPACE_DECL
diff --git a/libc/src/stdio/vasprintf.cpp b/libc/src/stdio/vasprintf.cpp
index 4a44d4a0f8842..d8ed3a36c7ecf 100644
--- a/libc/src/stdio/vasprintf.cpp
+++ b/libc/src/stdio/vasprintf.cpp
@@ -18,7 +18,16 @@ LLVM_LIBC_FUNCTION(int, vasprintf,
   internal::ArgList args(vlist); // This holder class allows for easier copying
                                  // and pointer semantics, as well as handling
                                  // destruction automatically.
-  return printf_core::vasprintf_internal(ret, format, args);
+  auto ret_val = printf_core::vasprintf_internal(ret, format, args);
+  if (ret_val.has_error()) {
+    libc_errno = ret_val.error;
+    return -1;
+  }
+  if (ret_val.value > cpp::numeric_limits<int>::max()) {
+    libc_errno = EOVERFLOW;
+    return -1;
+  } 
+  return static_cast<int>(ret_val.value);
 }
 
 } // namespace LIBC_NAMESPACE_DECL
diff --git a/libc/src/stdio/vsnprintf.cpp b/libc/src/stdio/vsnprintf.cpp
index b07a2499a0dd3..f39051cdd4d75 100644
--- a/libc/src/stdio/vsnprintf.cpp
+++ b/libc/src/stdio/vsnprintf.cpp
@@ -29,10 +29,20 @@ LLVM_LIBC_FUNCTION(int, vsnprintf,
       wb(buffer, (buffsz > 0 ? buffsz - 1 : 0));
   printf_core::Writer writer(wb);
 
-  int ret_val = printf_core::printf_main(&writer, format, args);
+  auto ret_val = printf_core::printf_main(&writer, format, args);
+  if (ret_val.has_error()) {
+    libc_errno = ret_val.error;
+    return -1;
+  }
   if (buffsz > 0) // if the buffsz is 0 the buffer may be a null pointer.
     wb.buff[wb.buff_cur] = '\0';
-  return ret_val;
+  
+  if (ret_val.value > cpp::numeric_limits<int>::max()) {
+    libc_errno = EOVERFLOW;
+    return -1;
+  } 
+  
+  return static_cast<int>(ret_val.value);  
 }
 
 } // namespace LIBC_NAMESPACE_DECL
diff --git a/libc/src/stdio/vsprintf.cpp b/libc/src/stdio/vsprintf.cpp
index 26d497be42125..4c4a3eefaa7b4 100644
--- a/libc/src/stdio/vsprintf.cpp
+++ b/libc/src/stdio/vsprintf.cpp
@@ -30,9 +30,18 @@ LLVM_LIBC_FUNCTION(int, vsprintf,
       wb(buffer, cpp::numeric_limits<size_t>::max());
   printf_core::Writer writer(wb);
 
-  int ret_val = printf_core::printf_main(&writer, format, args);
+  auto ret_val = printf_core::printf_main(&writer, format, args);
+  if (ret_val.has_error()) {
+    libc_errno = ret_val.error;
+    return -1;
+  }
   wb.buff[wb.buff_cur] = '\0';
-  return ret_val;
+
+  if (ret_val.value > cpp::numeric_limits<int>::max()) {
+    libc_errno = EOVERFLOW;
+    return -1;
+  } 
+  return static_cast<int>(ret_val.value);  
 }
 
 } // namespace LIBC_NAMESPACE_DECL

>From f8f35538b6dae5d99d58f46e1bc0b7c9055dc916 Mon Sep 17 00:00:00 2001
From: Marcell Leleszi <mleleszi at google.com>
Date: Tue, 7 Oct 2025 07:07:45 +0000
Subject: [PATCH 04/27] Formatting

---
 libc/src/stdio/asprintf.cpp                     | 4 ++--
 libc/src/stdio/generic/fprintf.cpp              | 4 ++--
 libc/src/stdio/generic/printf.cpp               | 4 ++--
 libc/src/stdio/generic/vfprintf.cpp             | 4 ++--
 libc/src/stdio/generic/vprintf.cpp              | 4 ++--
 libc/src/stdio/printf_core/printf_main.h        | 4 ++--
 libc/src/stdio/printf_core/vasprintf_internal.h | 5 +++--
 libc/src/stdio/printf_core/vfprintf_internal.h  | 4 ++--
 libc/src/stdio/snprintf.cpp                     | 8 ++++----
 libc/src/stdio/sprintf.cpp                      | 6 +++---
 libc/src/stdio/vasprintf.cpp                    | 2 +-
 libc/src/stdio/vsnprintf.cpp                    | 8 ++++----
 libc/src/stdio/vsprintf.cpp                     | 4 ++--
 13 files changed, 31 insertions(+), 30 deletions(-)

diff --git a/libc/src/stdio/asprintf.cpp b/libc/src/stdio/asprintf.cpp
index d392c658b898c..13c1ee68ef0f2 100644
--- a/libc/src/stdio/asprintf.cpp
+++ b/libc/src/stdio/asprintf.cpp
@@ -30,9 +30,9 @@ LLVM_LIBC_FUNCTION(int, asprintf,
   if (ret_val.value > cpp::numeric_limits<int>::max()) {
     libc_errno = EOVERFLOW;
     return -1;
-  } 
+  }
 
-  return static_cast<int>(ret_val.value);  
+  return static_cast<int>(ret_val.value);
 }
 
 } // namespace LIBC_NAMESPACE_DECL
diff --git a/libc/src/stdio/generic/fprintf.cpp b/libc/src/stdio/generic/fprintf.cpp
index 64d6694770e4e..632264f944f44 100644
--- a/libc/src/stdio/generic/fprintf.cpp
+++ b/libc/src/stdio/generic/fprintf.cpp
@@ -35,9 +35,9 @@ LLVM_LIBC_FUNCTION(int, fprintf,
   if (ret_val.value > cpp::numeric_limits<int>::max()) {
     libc_errno = EOVERFLOW;
     return -1;
-  } 
+  }
 
-  return static_cast<int>(ret_val.value);  
+  return static_cast<int>(ret_val.value);
 }
 
 } // namespace LIBC_NAMESPACE_DECL
diff --git a/libc/src/stdio/generic/printf.cpp b/libc/src/stdio/generic/printf.cpp
index 0020cce2359e8..482a65cd5a2fa 100644
--- a/libc/src/stdio/generic/printf.cpp
+++ b/libc/src/stdio/generic/printf.cpp
@@ -40,9 +40,9 @@ LLVM_LIBC_FUNCTION(int, printf, (const char *__restrict format, ...)) {
   if (ret_val.value > cpp::numeric_limits<int>::max()) {
     libc_errno = EOVERFLOW;
     return -1;
-  } 
+  }
 
-  return static_cast<int>(ret_val.value);  
+  return static_cast<int>(ret_val.value);
 }
 
 } // namespace LIBC_NAMESPACE_DECL
diff --git a/libc/src/stdio/generic/vfprintf.cpp b/libc/src/stdio/generic/vfprintf.cpp
index 6311318862bd6..6b39011123084 100644
--- a/libc/src/stdio/generic/vfprintf.cpp
+++ b/libc/src/stdio/generic/vfprintf.cpp
@@ -32,9 +32,9 @@ LLVM_LIBC_FUNCTION(int, vfprintf,
   if (ret_val.value > cpp::numeric_limits<int>::max()) {
     libc_errno = EOVERFLOW;
     return -1;
-  } 
+  }
 
-  return static_cast<int>(ret_val.value);  
+  return static_cast<int>(ret_val.value);
 }
 
 } // namespace LIBC_NAMESPACE_DECL
diff --git a/libc/src/stdio/generic/vprintf.cpp b/libc/src/stdio/generic/vprintf.cpp
index 73b9701ee2eda..09ba2ce0d32a9 100644
--- a/libc/src/stdio/generic/vprintf.cpp
+++ b/libc/src/stdio/generic/vprintf.cpp
@@ -38,9 +38,9 @@ LLVM_LIBC_FUNCTION(int, vprintf,
   if (ret_val.value > cpp::numeric_limits<int>::max()) {
     libc_errno = EOVERFLOW;
     return -1;
-  } 
+  }
 
-  return static_cast<int>(ret_val.value); 
+  return static_cast<int>(ret_val.value);
 }
 
 } // namespace LIBC_NAMESPACE_DECL
diff --git a/libc/src/stdio/printf_core/printf_main.h b/libc/src/stdio/printf_core/printf_main.h
index 6a9c9a00265d1..05488ff97f526 100644
--- a/libc/src/stdio/printf_core/printf_main.h
+++ b/libc/src/stdio/printf_core/printf_main.h
@@ -23,7 +23,7 @@ namespace printf_core {
 
 template <WriteMode write_mode>
 PrintfResult printf_main(Writer<write_mode> *writer, const char *__restrict str,
-                internal::ArgList &args) {
+                         internal::ArgList &args) {
   Parser<internal::ArgList> parser(str, args);
   int result = 0;
   for (FormatSection cur_section = parser.get_next_section();
@@ -37,7 +37,7 @@ PrintfResult printf_main(Writer<write_mode> *writer, const char *__restrict str,
     if (result < 0)
       return {0, -result};
   }
-  
+
   return writer->get_chars_written();
 }
 
diff --git a/libc/src/stdio/printf_core/vasprintf_internal.h b/libc/src/stdio/printf_core/vasprintf_internal.h
index bfc02fedf5244..b96e1afa0b467 100644
--- a/libc/src/stdio/printf_core/vasprintf_internal.h
+++ b/libc/src/stdio/printf_core/vasprintf_internal.h
@@ -43,8 +43,9 @@ LIBC_INLINE int resize_overflow_hook(cpp::string_view new_str, void *target) {
 
 constexpr size_t DEFAULT_BUFFER_SIZE = 200;
 
-LIBC_INLINE PrintfResult vasprintf_internal(char **ret, const char *__restrict format,
-                                   internal::ArgList args) {
+LIBC_INLINE PrintfResult vasprintf_internal(char **ret,
+                                            const char *__restrict format,
+                                            internal::ArgList args) {
   char init_buff_on_stack[DEFAULT_BUFFER_SIZE];
   printf_core::WriteBuffer<Mode<WriteMode::RESIZE_AND_FILL_BUFF>::value> wb(
       init_buff_on_stack, DEFAULT_BUFFER_SIZE, resize_overflow_hook);
diff --git a/libc/src/stdio/printf_core/vfprintf_internal.h b/libc/src/stdio/printf_core/vfprintf_internal.h
index 71a85077e92b8..53a9762606295 100644
--- a/libc/src/stdio/printf_core/vfprintf_internal.h
+++ b/libc/src/stdio/printf_core/vfprintf_internal.h
@@ -69,8 +69,8 @@ LIBC_INLINE int file_write_hook(cpp::string_view new_str, void *fp) {
 }
 
 LIBC_INLINE PrintfResult vfprintf_internal(::FILE *__restrict stream,
-                                  const char *__restrict format,
-                                  internal::ArgList &args) {
+                                           const char *__restrict format,
+                                           internal::ArgList &args) {
   constexpr size_t BUFF_SIZE = 1024;
   char buffer[BUFF_SIZE];
   printf_core::WriteBuffer<Mode<WriteMode::FLUSH_TO_STREAM>::value> wb(
diff --git a/libc/src/stdio/snprintf.cpp b/libc/src/stdio/snprintf.cpp
index 8c22935b849a0..6691da1debb5a 100644
--- a/libc/src/stdio/snprintf.cpp
+++ b/libc/src/stdio/snprintf.cpp
@@ -39,13 +39,13 @@ LLVM_LIBC_FUNCTION(int, snprintf,
   }
   if (buffsz > 0) // if the buffsz is 0 the buffer may be a null pointer.
     wb.buff[wb.buff_cur] = '\0';
-  
+
   if (ret_val.value > cpp::numeric_limits<int>::max()) {
     libc_errno = EOVERFLOW;
     return -1;
-  } 
-  
-  return static_cast<int>(ret_val.value);  
+  }
+
+  return static_cast<int>(ret_val.value);
 }
 
 } // namespace LIBC_NAMESPACE_DECL
diff --git a/libc/src/stdio/sprintf.cpp b/libc/src/stdio/sprintf.cpp
index 2557197894bec..10db9feb80121 100644
--- a/libc/src/stdio/sprintf.cpp
+++ b/libc/src/stdio/sprintf.cpp
@@ -43,9 +43,9 @@ LLVM_LIBC_FUNCTION(int, sprintf,
   if (ret_val.value > cpp::numeric_limits<int>::max()) {
     libc_errno = EOVERFLOW;
     return -1;
-  } 
-  
-  return static_cast<int>(ret_val.value);  
+  }
+
+  return static_cast<int>(ret_val.value);
 }
 
 } // namespace LIBC_NAMESPACE_DECL
diff --git a/libc/src/stdio/vasprintf.cpp b/libc/src/stdio/vasprintf.cpp
index d8ed3a36c7ecf..11174727e5d64 100644
--- a/libc/src/stdio/vasprintf.cpp
+++ b/libc/src/stdio/vasprintf.cpp
@@ -26,7 +26,7 @@ LLVM_LIBC_FUNCTION(int, vasprintf,
   if (ret_val.value > cpp::numeric_limits<int>::max()) {
     libc_errno = EOVERFLOW;
     return -1;
-  } 
+  }
   return static_cast<int>(ret_val.value);
 }
 
diff --git a/libc/src/stdio/vsnprintf.cpp b/libc/src/stdio/vsnprintf.cpp
index f39051cdd4d75..25e6165897674 100644
--- a/libc/src/stdio/vsnprintf.cpp
+++ b/libc/src/stdio/vsnprintf.cpp
@@ -36,13 +36,13 @@ LLVM_LIBC_FUNCTION(int, vsnprintf,
   }
   if (buffsz > 0) // if the buffsz is 0 the buffer may be a null pointer.
     wb.buff[wb.buff_cur] = '\0';
-  
+
   if (ret_val.value > cpp::numeric_limits<int>::max()) {
     libc_errno = EOVERFLOW;
     return -1;
-  } 
-  
-  return static_cast<int>(ret_val.value);  
+  }
+
+  return static_cast<int>(ret_val.value);
 }
 
 } // namespace LIBC_NAMESPACE_DECL
diff --git a/libc/src/stdio/vsprintf.cpp b/libc/src/stdio/vsprintf.cpp
index 4c4a3eefaa7b4..6dda2db250d50 100644
--- a/libc/src/stdio/vsprintf.cpp
+++ b/libc/src/stdio/vsprintf.cpp
@@ -40,8 +40,8 @@ LLVM_LIBC_FUNCTION(int, vsprintf,
   if (ret_val.value > cpp::numeric_limits<int>::max()) {
     libc_errno = EOVERFLOW;
     return -1;
-  } 
-  return static_cast<int>(ret_val.value);  
+  }
+  return static_cast<int>(ret_val.value);
 }
 
 } // namespace LIBC_NAMESPACE_DECL

>From 4761e9a68d279739b4e932331ec3307bdbd4c81f Mon Sep 17 00:00:00 2001
From: Marcell Leleszi <mleleszi at google.com>
Date: Tue, 7 Oct 2025 15:39:17 +0000
Subject: [PATCH 05/27] Fix tests

---
 libc/src/stdlib/strfromd.cpp                  |  2 +-
 .../src/stdio/printf_core/converter_test.cpp  | 30 ++++++++---------
 .../src/stdio/printf_core/writer_test.cpp     | 32 +++++++++----------
 3 files changed, 32 insertions(+), 32 deletions(-)

diff --git a/libc/src/stdlib/strfromd.cpp b/libc/src/stdlib/strfromd.cpp
index c0989f1a97dca..b35e476086691 100644
--- a/libc/src/stdlib/strfromd.cpp
+++ b/libc/src/stdlib/strfromd.cpp
@@ -28,7 +28,7 @@ LLVM_LIBC_FUNCTION(int, strfromd,
   if (section.has_conv)
     result = internal::strfromfloat_convert<double>(&writer, section);
   else
-    result = writer.write(section.raw_string);
+    result = writer.write(section.raw_string); // TODO everywhere where writer is used, set errno and ret -1
 
   if (result < 0)
     return result;
diff --git a/libc/test/src/stdio/printf_core/converter_test.cpp b/libc/test/src/stdio/printf_core/converter_test.cpp
index bf088937e4104..2dae2a22c864c 100644
--- a/libc/test/src/stdio/printf_core/converter_test.cpp
+++ b/libc/test/src/stdio/printf_core/converter_test.cpp
@@ -38,7 +38,7 @@ TEST_F(LlvmLibcPrintfConverterTest, SimpleRawConversion) {
   wb.buff[wb.buff_cur] = '\0';
 
   ASSERT_STREQ(str, "abc");
-  ASSERT_EQ(writer.get_chars_written(), 3);
+  ASSERT_EQ(writer.get_chars_written(), size_t{3});
 }
 
 TEST_F(LlvmLibcPrintfConverterTest, PercentConversion) {
@@ -52,7 +52,7 @@ TEST_F(LlvmLibcPrintfConverterTest, PercentConversion) {
   wb.buff[wb.buff_cur] = '\0';
 
   ASSERT_STREQ(str, "%");
-  ASSERT_EQ(writer.get_chars_written(), 1);
+  ASSERT_EQ(writer.get_chars_written(), size_t{1});
 }
 
 TEST_F(LlvmLibcPrintfConverterTest, CharConversionSimple) {
@@ -70,7 +70,7 @@ TEST_F(LlvmLibcPrintfConverterTest, CharConversionSimple) {
   wb.buff[wb.buff_cur] = '\0';
 
   ASSERT_STREQ(str, "D");
-  ASSERT_EQ(writer.get_chars_written(), 1);
+  ASSERT_EQ(writer.get_chars_written(), size_t{1});
 }
 
 TEST_F(LlvmLibcPrintfConverterTest, CharConversionRightJustified) {
@@ -85,7 +85,7 @@ TEST_F(LlvmLibcPrintfConverterTest, CharConversionRightJustified) {
   wb.buff[wb.buff_cur] = '\0';
 
   ASSERT_STREQ(str, "   E");
-  ASSERT_EQ(writer.get_chars_written(), 4);
+  ASSERT_EQ(writer.get_chars_written(), size_t{4});
 }
 
 TEST_F(LlvmLibcPrintfConverterTest, CharConversionLeftJustified) {
@@ -102,7 +102,7 @@ TEST_F(LlvmLibcPrintfConverterTest, CharConversionLeftJustified) {
   wb.buff[wb.buff_cur] = '\0';
 
   ASSERT_STREQ(str, "F   ");
-  ASSERT_EQ(writer.get_chars_written(), 4);
+  ASSERT_EQ(writer.get_chars_written(), size_t{4});
 }
 
 TEST_F(LlvmLibcPrintfConverterTest, StringConversionSimple) {
@@ -118,7 +118,7 @@ TEST_F(LlvmLibcPrintfConverterTest, StringConversionSimple) {
   wb.buff[wb.buff_cur] = '\0';
 
   ASSERT_STREQ(str, "DEF");
-  ASSERT_EQ(writer.get_chars_written(), 3);
+  ASSERT_EQ(writer.get_chars_written(), size_t{3});
 }
 
 TEST_F(LlvmLibcPrintfConverterTest, StringConversionPrecisionHigh) {
@@ -133,7 +133,7 @@ TEST_F(LlvmLibcPrintfConverterTest, StringConversionPrecisionHigh) {
   wb.buff[wb.buff_cur] = '\0';
 
   ASSERT_STREQ(str, "456");
-  ASSERT_EQ(writer.get_chars_written(), 3);
+  ASSERT_EQ(writer.get_chars_written(), size_t{3});
 }
 
 TEST_F(LlvmLibcPrintfConverterTest, StringConversionPrecisionLow) {
@@ -148,7 +148,7 @@ TEST_F(LlvmLibcPrintfConverterTest, StringConversionPrecisionLow) {
   wb.buff[wb.buff_cur] = '\0';
 
   ASSERT_STREQ(str, "xy");
-  ASSERT_EQ(writer.get_chars_written(), 2);
+  ASSERT_EQ(writer.get_chars_written(), size_t{2});
 }
 
 TEST_F(LlvmLibcPrintfConverterTest, StringConversionRightJustified) {
@@ -163,7 +163,7 @@ TEST_F(LlvmLibcPrintfConverterTest, StringConversionRightJustified) {
   wb.buff[wb.buff_cur] = '\0';
 
   ASSERT_STREQ(str, " 789");
-  ASSERT_EQ(writer.get_chars_written(), 4);
+  ASSERT_EQ(writer.get_chars_written(), size_t{4});
 }
 
 TEST_F(LlvmLibcPrintfConverterTest, StringConversionLeftJustified) {
@@ -180,7 +180,7 @@ TEST_F(LlvmLibcPrintfConverterTest, StringConversionLeftJustified) {
   wb.buff[wb.buff_cur] = '\0';
 
   ASSERT_STREQ(str, "ghi ");
-  ASSERT_EQ(writer.get_chars_written(), 4);
+  ASSERT_EQ(writer.get_chars_written(), size_t{4});
 }
 
 TEST_F(LlvmLibcPrintfConverterTest, IntConversionSimple) {
@@ -194,7 +194,7 @@ TEST_F(LlvmLibcPrintfConverterTest, IntConversionSimple) {
   wb.buff[wb.buff_cur] = '\0';
 
   ASSERT_STREQ(str, "12345");
-  ASSERT_EQ(writer.get_chars_written(), 5);
+  ASSERT_EQ(writer.get_chars_written(), size_t{5});
 }
 
 TEST_F(LlvmLibcPrintfConverterTest, HexConversion) {
@@ -211,7 +211,7 @@ TEST_F(LlvmLibcPrintfConverterTest, HexConversion) {
 
   wb.buff[wb.buff_cur] = '\0';
   ASSERT_STREQ(str, "0x00000000123456ab");
-  ASSERT_EQ(writer.get_chars_written(), 18);
+  ASSERT_EQ(writer.get_chars_written(), size_t{18});
 }
 
 TEST_F(LlvmLibcPrintfConverterTest, BinaryConversion) {
@@ -225,7 +225,7 @@ TEST_F(LlvmLibcPrintfConverterTest, BinaryConversion) {
   wb.buff[wb.buff_cur] = '\0';
 
   ASSERT_STREQ(str, "101010");
-  ASSERT_EQ(writer.get_chars_written(), 6);
+  ASSERT_EQ(writer.get_chars_written(), size_t{6});
 }
 
 TEST_F(LlvmLibcPrintfConverterTest, PointerConversion) {
@@ -239,7 +239,7 @@ TEST_F(LlvmLibcPrintfConverterTest, PointerConversion) {
 
   wb.buff[wb.buff_cur] = '\0';
   ASSERT_STREQ(str, "0x123456ab");
-  ASSERT_EQ(writer.get_chars_written(), 10);
+  ASSERT_EQ(writer.get_chars_written(), size_t{10});
 }
 
 TEST_F(LlvmLibcPrintfConverterTest, OctConversion) {
@@ -253,5 +253,5 @@ TEST_F(LlvmLibcPrintfConverterTest, OctConversion) {
 
   wb.buff[wb.buff_cur] = '\0';
   ASSERT_STREQ(str, "1234");
-  ASSERT_EQ(writer.get_chars_written(), 4);
+  ASSERT_EQ(writer.get_chars_written(), size_t{4});
 }
diff --git a/libc/test/src/stdio/printf_core/writer_test.cpp b/libc/test/src/stdio/printf_core/writer_test.cpp
index d036341be7981..d263cf55aa474 100644
--- a/libc/test/src/stdio/printf_core/writer_test.cpp
+++ b/libc/test/src/stdio/printf_core/writer_test.cpp
@@ -39,7 +39,7 @@ TEST(LlvmLibcPrintfWriterTest, Write) {
   wb.buff[wb.buff_cur] = '\0';
 
   ASSERT_STREQ("abc", str);
-  ASSERT_EQ(writer.get_chars_written(), 3);
+  ASSERT_EQ(writer.get_chars_written(), size_t{3});
 }
 
 TEST(LlvmLibcPrintfWriterTest, WriteMultipleTimes) {
@@ -53,7 +53,7 @@ TEST(LlvmLibcPrintfWriterTest, WriteMultipleTimes) {
   wb.buff[wb.buff_cur] = '\0';
 
   ASSERT_STREQ("abcDEF123", str);
-  ASSERT_EQ(writer.get_chars_written(), 9);
+  ASSERT_EQ(writer.get_chars_written(), size_t{9});
 }
 
 TEST(LlvmLibcPrintfWriterTest, WriteChars) {
@@ -66,7 +66,7 @@ TEST(LlvmLibcPrintfWriterTest, WriteChars) {
   wb.buff[wb.buff_cur] = '\0';
 
   ASSERT_STREQ("aaa", str);
-  ASSERT_EQ(writer.get_chars_written(), 3);
+  ASSERT_EQ(writer.get_chars_written(), size_t{3});
 }
 
 TEST(LlvmLibcPrintfWriterTest, WriteCharsMultipleTimes) {
@@ -80,7 +80,7 @@ TEST(LlvmLibcPrintfWriterTest, WriteCharsMultipleTimes) {
   wb.buff[wb.buff_cur] = '\0';
 
   ASSERT_STREQ("aaaDDD111", str);
-  ASSERT_EQ(writer.get_chars_written(), 9);
+  ASSERT_EQ(writer.get_chars_written(), size_t{9});
 }
 
 TEST(LlvmLibcPrintfWriterTest, WriteManyChars) {
@@ -102,7 +102,7 @@ TEST(LlvmLibcPrintfWriterTest, WriteManyChars) {
                "ZZZZZZZZZZ"
                "ZZZZZZZZZ",
                str);
-  ASSERT_EQ(writer.get_chars_written(), 99);
+  ASSERT_EQ(writer.get_chars_written(), size_t{99});
 }
 
 TEST(LlvmLibcPrintfWriterTest, MixedWrites) {
@@ -117,7 +117,7 @@ TEST(LlvmLibcPrintfWriterTest, MixedWrites) {
   wb.buff[wb.buff_cur] = '\0';
 
   ASSERT_STREQ("aaaDEF111456", str);
-  ASSERT_EQ(writer.get_chars_written(), 12);
+  ASSERT_EQ(writer.get_chars_written(), size_t{12});
 }
 
 TEST(LlvmLibcPrintfWriterTest, WriteWithMaxLength) {
@@ -129,7 +129,7 @@ TEST(LlvmLibcPrintfWriterTest, WriteWithMaxLength) {
   wb.buff[wb.buff_cur] = '\0';
 
   ASSERT_STREQ("abcDEF1234", str);
-  ASSERT_EQ(writer.get_chars_written(), 12);
+  ASSERT_EQ(writer.get_chars_written(), size_t{12});
 }
 
 TEST(LlvmLibcPrintfWriterTest, WriteCharsWithMaxLength) {
@@ -141,7 +141,7 @@ TEST(LlvmLibcPrintfWriterTest, WriteCharsWithMaxLength) {
   wb.buff[wb.buff_cur] = '\0';
 
   ASSERT_STREQ("1111111111", str);
-  ASSERT_EQ(writer.get_chars_written(), 15);
+  ASSERT_EQ(writer.get_chars_written(), size_t{15});
 }
 
 TEST(LlvmLibcPrintfWriterTest, MixedWriteWithMaxLength) {
@@ -157,7 +157,7 @@ TEST(LlvmLibcPrintfWriterTest, MixedWriteWithMaxLength) {
   wb.buff[wb.buff_cur] = '\0';
 
   ASSERT_STREQ("aaaDEF1114", str);
-  ASSERT_EQ(writer.get_chars_written(), 12);
+  ASSERT_EQ(writer.get_chars_written(), size_t{12});
 }
 
 TEST(LlvmLibcPrintfWriterTest, StringWithMaxLengthOne) {
@@ -175,7 +175,7 @@ TEST(LlvmLibcPrintfWriterTest, StringWithMaxLengthOne) {
   wb.buff[wb.buff_cur] = '\0';
 
   ASSERT_STREQ("", str);
-  ASSERT_EQ(writer.get_chars_written(), 12);
+  ASSERT_EQ(writer.get_chars_written(), size_t{12});
 }
 
 TEST(LlvmLibcPrintfWriterTest, NullStringWithZeroMaxLength) {
@@ -187,7 +187,7 @@ TEST(LlvmLibcPrintfWriterTest, NullStringWithZeroMaxLength) {
   writer.write('1', 3);
   writer.write({"456", 3});
 
-  ASSERT_EQ(writer.get_chars_written(), 12);
+  ASSERT_EQ(writer.get_chars_written(), size_t{12});
 }
 
 struct OutBuff {
@@ -226,7 +226,7 @@ TEST(LlvmLibcPrintfWriterTest, WriteWithMaxLengthWithCallback) {
   str[out_buff.cur_pos] = '\0';
 
   ASSERT_STREQ("abcDEF123456", str);
-  ASSERT_EQ(writer.get_chars_written(), 12);
+  ASSERT_EQ(writer.get_chars_written(), size_t{12});
 }
 
 TEST(LlvmLibcPrintfWriterTest, WriteCharsWithMaxLengthWithCallback) {
@@ -246,7 +246,7 @@ TEST(LlvmLibcPrintfWriterTest, WriteCharsWithMaxLengthWithCallback) {
   str[out_buff.cur_pos] = '\0';
 
   ASSERT_STREQ("111111111111111", str);
-  ASSERT_EQ(writer.get_chars_written(), 15);
+  ASSERT_EQ(writer.get_chars_written(), size_t{15});
 }
 
 TEST(LlvmLibcPrintfWriterTest, MixedWriteWithMaxLengthWithCallback) {
@@ -269,7 +269,7 @@ TEST(LlvmLibcPrintfWriterTest, MixedWriteWithMaxLengthWithCallback) {
   str[out_buff.cur_pos] = '\0';
 
   ASSERT_STREQ("aaaDEF111456", str);
-  ASSERT_EQ(writer.get_chars_written(), 12);
+  ASSERT_EQ(writer.get_chars_written(), size_t{12});
 }
 
 TEST(LlvmLibcPrintfWriterTest, ZeroLengthBufferWithCallback) {
@@ -292,7 +292,7 @@ TEST(LlvmLibcPrintfWriterTest, ZeroLengthBufferWithCallback) {
   str[out_buff.cur_pos] = '\0';
 
   ASSERT_STREQ("aaaDEF111456", str);
-  ASSERT_EQ(writer.get_chars_written(), 12);
+  ASSERT_EQ(writer.get_chars_written(), size_t{12});
 }
 
 TEST(LlvmLibcPrintfWriterTest, NullStringWithZeroMaxLengthWithCallback) {
@@ -312,7 +312,7 @@ TEST(LlvmLibcPrintfWriterTest, NullStringWithZeroMaxLengthWithCallback) {
   wb.overflow_write("");
   str[out_buff.cur_pos] = '\0';
 
-  ASSERT_EQ(writer.get_chars_written(), 12);
+  ASSERT_EQ(writer.get_chars_written(), size_t{12});
   ASSERT_STREQ("aaaDEF111456", str);
 }
 

>From 1dd65af1dae8b5a55305b1881f3dbc30163c0724 Mon Sep 17 00:00:00 2001
From: Marcell Leleszi <mleleszi at google.com>
Date: Tue, 7 Oct 2025 16:28:29 +0000
Subject: [PATCH 06/27] Add IO error test cases for file variants

---
 libc/src/stdio/printf_core/vasprintf_internal.h | 4 ++--
 libc/test/src/stdio/CMakeLists.txt              | 2 ++
 libc/test/src/stdio/fprintf_test.cpp            | 5 +++++
 libc/test/src/stdio/vfprintf_test.cpp           | 5 +++++
 4 files changed, 14 insertions(+), 2 deletions(-)

diff --git a/libc/src/stdio/printf_core/vasprintf_internal.h b/libc/src/stdio/printf_core/vasprintf_internal.h
index b96e1afa0b467..f84f60bec2fc6 100644
--- a/libc/src/stdio/printf_core/vasprintf_internal.h
+++ b/libc/src/stdio/printf_core/vasprintf_internal.h
@@ -59,12 +59,12 @@ LIBC_INLINE PrintfResult vasprintf_internal(char **ret,
   if (wb.buff == init_buff_on_stack) {
     *ret = static_cast<char *>(malloc(ret_val.value + 1));
     if (ret == nullptr)
-      return -ENOMEM;
+      return {0, ENOMEM};
     inline_memcpy(*ret, wb.buff, ret_val.value);
   } else {
     *ret = wb.buff;
   }
-  (*ret)[ret_val.value] = '\0'; // TODO OK HERE or overflow
+  (*ret)[ret_val.value] = '\0';
   return ret_val;
 }
 } // namespace printf_core
diff --git a/libc/test/src/stdio/CMakeLists.txt b/libc/test/src/stdio/CMakeLists.txt
index eec108bc12ca5..d71f1dff11943 100644
--- a/libc/test/src/stdio/CMakeLists.txt
+++ b/libc/test/src/stdio/CMakeLists.txt
@@ -186,6 +186,8 @@ add_libc_test(
     fprintf_test.cpp
   DEPENDS
     libc.src.stdio.fprintf
+    libc.test.UnitTest.ErrnoCheckingTest
+    libc.test.UnitTest.ErrnoSetterMatcher
     ${fprintf_test_deps}
   COMPILE_OPTIONS
     ${use_system_file}
diff --git a/libc/test/src/stdio/fprintf_test.cpp b/libc/test/src/stdio/fprintf_test.cpp
index 6799323cc6ad9..3a764e102dc71 100644
--- a/libc/test/src/stdio/fprintf_test.cpp
+++ b/libc/test/src/stdio/fprintf_test.cpp
@@ -16,6 +16,8 @@
 #include "src/stdio/fprintf.h"
 
 #include "test/UnitTest/Test.h"
+#include "test/UnitTest/ErrnoCheckingTest.h"
+#include "test/UnitTest/ErrnoSetterMatcher.h"
 
 namespace printf_test {
 #ifndef LIBC_COPT_STDIO_USE_SYSTEM_FILE
@@ -31,6 +33,8 @@ using ::fread;
 #endif // LIBC_COPT_STDIO_USE_SYSTEM_FILE
 } // namespace printf_test
 
+using LlvmLibcFPrintfTest = LIBC_NAMESPACE::testing::ErrnoCheckingTest;
+
 TEST(LlvmLibcFPrintfTest, WriteToFile) {
   const char *FILENAME = APPEND_LIBC_TEST("fprintf_output.test");
   auto FILE_PATH = libc_make_test_file_path(FILENAME);
@@ -78,6 +82,7 @@ TEST(LlvmLibcFPrintfTest, WriteToFile) {
   written =
       LIBC_NAMESPACE::fprintf(file, "Writing to a read only file should fail.");
   EXPECT_LT(written, 0);
+  ASSERT_ERRNO_EQ(EIO);
 
   ASSERT_EQ(printf_test::fclose(file), 0);
 }
diff --git a/libc/test/src/stdio/vfprintf_test.cpp b/libc/test/src/stdio/vfprintf_test.cpp
index f50565a0f68ca..43240ce13cc96 100644
--- a/libc/test/src/stdio/vfprintf_test.cpp
+++ b/libc/test/src/stdio/vfprintf_test.cpp
@@ -20,6 +20,8 @@
 #include "src/stdio/vfprintf.h"
 
 #include "test/UnitTest/Test.h"
+#include "test/UnitTest/ErrnoCheckingTest.h"
+#include "test/UnitTest/ErrnoSetterMatcher.h"
 
 namespace printf_test {
 #ifndef LIBC_COPT_STDIO_USE_SYSTEM_FILE
@@ -44,6 +46,8 @@ int call_vfprintf(::FILE *__restrict stream, const char *__restrict format,
   return ret;
 }
 
+using LlvmLibcVFPrintfTest = LIBC_NAMESPACE::testing::ErrnoCheckingTest;
+
 TEST(LlvmLibcVFPrintfTest, WriteToFile) {
   const char *FILENAME = APPEND_LIBC_TEST("vfprintf_output.test");
   auto FILE_PATH = libc_make_test_file_path(FILENAME);
@@ -90,6 +94,7 @@ TEST(LlvmLibcVFPrintfTest, WriteToFile) {
 
   written = call_vfprintf(file, "Writing to a read only file should fail.");
   EXPECT_LT(written, 0);
+  ASSERT_ERRNO_EQ(EIO);
 
   ASSERT_EQ(printf_test::fclose(file), 0);
 }

>From ef55545a80b33bbd2947514f4a081b993b389c0e Mon Sep 17 00:00:00 2001
From: Marcell Leleszi <mleleszi at google.com>
Date: Tue, 7 Oct 2025 17:17:50 +0000
Subject: [PATCH 07/27] Add overflow test for fprintf

---
 libc/test/src/stdio/CMakeLists.txt   |  1 +
 libc/test/src/stdio/fprintf_test.cpp | 28 +++++++++++++++++++++++++++-
 2 files changed, 28 insertions(+), 1 deletion(-)

diff --git a/libc/test/src/stdio/CMakeLists.txt b/libc/test/src/stdio/CMakeLists.txt
index d71f1dff11943..b513007cdf7c6 100644
--- a/libc/test/src/stdio/CMakeLists.txt
+++ b/libc/test/src/stdio/CMakeLists.txt
@@ -169,6 +169,7 @@ if(LLVM_LIBC_FULL_BUILD)
        libc.src.stdio.ferror
        libc.src.stdio.fopen
        libc.src.stdio.fread
+       libc.src.stdio.fopencookie
   )
   # This is to be used for tests which write to libc's platform streams
   # under full build but write to system-lib's streams otherwise.
diff --git a/libc/test/src/stdio/fprintf_test.cpp b/libc/test/src/stdio/fprintf_test.cpp
index 3a764e102dc71..6d52611ae98b1 100644
--- a/libc/test/src/stdio/fprintf_test.cpp
+++ b/libc/test/src/stdio/fprintf_test.cpp
@@ -10,25 +10,29 @@
 #include "src/stdio/fclose.h"
 #include "src/stdio/ferror.h"
 #include "src/stdio/fopen.h"
+#include "src/stdio/fopencookie.h"
 #include "src/stdio/fread.h"
 #endif // LIBC_COPT_STDIO_USE_SYSTEM_FILE
 
 #include "src/stdio/fprintf.h"
 
-#include "test/UnitTest/Test.h"
+#include "src/__support/CPP/limits.h"
 #include "test/UnitTest/ErrnoCheckingTest.h"
 #include "test/UnitTest/ErrnoSetterMatcher.h"
+#include "test/UnitTest/Test.h"
 
 namespace printf_test {
 #ifndef LIBC_COPT_STDIO_USE_SYSTEM_FILE
 using LIBC_NAMESPACE::fclose;
 using LIBC_NAMESPACE::ferror;
 using LIBC_NAMESPACE::fopen;
+using LIBC_NAMESPACE::fopencookie;
 using LIBC_NAMESPACE::fread;
 #else  // defined(LIBC_COPT_STDIO_USE_SYSTEM_FILE)
 using ::fclose;
 using ::ferror;
 using ::fopen;
+using ::fopencookie;
 using ::fread;
 #endif // LIBC_COPT_STDIO_USE_SYSTEM_FILE
 } // namespace printf_test
@@ -86,3 +90,25 @@ TEST(LlvmLibcFPrintfTest, WriteToFile) {
 
   ASSERT_EQ(printf_test::fclose(file), 0);
 }
+
+struct NoopStream {};
+
+static ssize_t noop_write(void *, const char *, size_t size) { return size; }
+
+TEST(LlvmLibcFPrintfTest, CharsWrittenOverflow) {
+  NoopStream stream;
+  cookie_io_functions_t funcs = {nullptr, &noop_write, nullptr, nullptr};
+  ::FILE *file = printf_test::fopencookie(&stream, "w", funcs);
+  ASSERT_NE(file, nullptr);
+
+  // Trigger an overflow in the return value of fprintf by writing more than
+  // INT_MAX bytes. We do this by printing a string with precision INT_MAX, and
+  // then one more character.
+  int max_int = LIBC_NAMESPACE::cpp::numeric_limits<int>::max();
+  int result = LIBC_NAMESPACE::fprintf(file, "%*sA", max_int, "");
+
+  EXPECT_LT(result, 0);
+  ASSERT_ERRNO_EQ(EOVERFLOW);
+
+  EXPECT_EQ(printf_test::fclose(file), 0);
+}
\ No newline at end of file

>From 63c710b793e4bcfe1b6401aeb5c0843e45ae9150 Mon Sep 17 00:00:00 2001
From: Marcell Leleszi <mleleszi at google.com>
Date: Tue, 7 Oct 2025 18:19:56 +0000
Subject: [PATCH 08/27] Cleanup

---
 libc/src/stdio/baremetal/printf.cpp         |  2 +-
 libc/src/stdio/baremetal/vprintf.cpp        |  4 ++--
 libc/src/stdio/printf_core/core_structs.h   |  2 +-
 libc/src/stdio/printf_core/printf_main.h    |  4 ++--
 libc/src/time/strftime_core/strftime_main.h |  2 +-
 libc/test/src/stdio/fprintf_test.cpp        | 11 ++++++-----
 6 files changed, 13 insertions(+), 12 deletions(-)

diff --git a/libc/src/stdio/baremetal/printf.cpp b/libc/src/stdio/baremetal/printf.cpp
index 7253c6549a4e4..6f4ebb35d4004 100644
--- a/libc/src/stdio/baremetal/printf.cpp
+++ b/libc/src/stdio/baremetal/printf.cpp
@@ -47,7 +47,7 @@ LLVM_LIBC_FUNCTION(int, printf, (const char *__restrict format, ...)) {
   int flushval = wb.overflow_write("");
   if (flushval != printf_core::WRITE_OK)
     retval = flushval;
-
+    
   return retval;
 }
 
diff --git a/libc/src/stdio/baremetal/vprintf.cpp b/libc/src/stdio/baremetal/vprintf.cpp
index ab02533f14911..4401ddbe8690e 100644
--- a/libc/src/stdio/baremetal/vprintf.cpp
+++ b/libc/src/stdio/baremetal/vprintf.cpp
@@ -22,7 +22,7 @@ namespace LIBC_NAMESPACE_DECL {
 namespace {
 
 LIBC_INLINE int stdout_write_hook(cpp::string_view new_str, void *) {
-  write_to_stdout(new_str);
+  write_to_stdout(new_str); 
   return printf_core::WRITE_OK;
 }
 
@@ -40,7 +40,7 @@ LLVM_LIBC_FUNCTION(int, vprintf,
       buffer, BUFF_SIZE, &stdout_write_hook, nullptr);
   printf_core::Writer<printf_core::WriteMode::FLUSH_TO_STREAM> writer(wb);
 
-  int retval = printf_core::printf_main(&writer, format, args);
+  int retval = printf_core::printf_main(&writer, format, args); // TODO baremetal stuff
 
   int flushval = wb.overflow_write("");
   if (flushval != printf_core::WRITE_OK)
diff --git a/libc/src/stdio/printf_core/core_structs.h b/libc/src/stdio/printf_core/core_structs.h
index e397e4a5b3abe..22b674da789a5 100644
--- a/libc/src/stdio/printf_core/core_structs.h
+++ b/libc/src/stdio/printf_core/core_structs.h
@@ -31,7 +31,7 @@ struct PrintfResult {
 
   constexpr bool has_error() { return error != 0; }
 
-  constexpr operator size_t() { return value; }
+  // constexpr operator size_t() { return value; }
 };
 
 // These length modifiers match the length modifiers in the format string, which
diff --git a/libc/src/stdio/printf_core/printf_main.h b/libc/src/stdio/printf_core/printf_main.h
index 05488ff97f526..7bb5b3f3d1cf5 100644
--- a/libc/src/stdio/printf_core/printf_main.h
+++ b/libc/src/stdio/printf_core/printf_main.h
@@ -30,9 +30,9 @@ PrintfResult printf_main(Writer<write_mode> *writer, const char *__restrict str,
        !cur_section.raw_string.empty();
        cur_section = parser.get_next_section()) {
     if (cur_section.has_conv)
-      result = convert(writer, cur_section);
+      result = convert(writer, cur_section); // look at usages
     else
-      result = writer->write(cur_section.raw_string);
+      result = writer->write(cur_section.raw_string); // look at usages
 
     if (result < 0)
       return {0, -result};
diff --git a/libc/src/time/strftime_core/strftime_main.h b/libc/src/time/strftime_core/strftime_main.h
index c7e590627094a..a97e00dfd9b64 100644
--- a/libc/src/time/strftime_core/strftime_main.h
+++ b/libc/src/time/strftime_core/strftime_main.h
@@ -36,7 +36,7 @@ int strftime_main(printf_core::Writer<write_mode> *writer,
       return result;
   }
 
-  return writer->get_chars_written();
+  return static_cast<int>(writer->get_chars_written());
 }
 
 } // namespace strftime_core
diff --git a/libc/test/src/stdio/fprintf_test.cpp b/libc/test/src/stdio/fprintf_test.cpp
index 6d52611ae98b1..53d056da1f36d 100644
--- a/libc/test/src/stdio/fprintf_test.cpp
+++ b/libc/test/src/stdio/fprintf_test.cpp
@@ -91,13 +91,14 @@ TEST(LlvmLibcFPrintfTest, WriteToFile) {
   ASSERT_EQ(printf_test::fclose(file), 0);
 }
 
-struct NoopStream {};
-
-static ssize_t noop_write(void *, const char *, size_t size) { return size; }
-
 TEST(LlvmLibcFPrintfTest, CharsWrittenOverflow) {
+  struct NoopStream {};
+  auto noop_write = [](void *cookie, const char *buf, size_t size) -> ssize_t {
+    return size;
+  };
+
   NoopStream stream;
-  cookie_io_functions_t funcs = {nullptr, &noop_write, nullptr, nullptr};
+  cookie_io_functions_t funcs = {nullptr, +noop_write, nullptr, nullptr};
   ::FILE *file = printf_test::fopencookie(&stream, "w", funcs);
   ASSERT_NE(file, nullptr);
 

>From e035a15cb26c61de8ee052a8e8758856a4ebb56d Mon Sep 17 00:00:00 2001
From: Marcell Leleszi <mleleszi at google.com>
Date: Wed, 8 Oct 2025 16:03:09 +0000
Subject: [PATCH 09/27] Add overflow test for vfprintf

---
 libc/test/src/stdio/vfprintf_test.cpp | 26 ++++++++++++++++++++++++++
 1 file changed, 26 insertions(+)

diff --git a/libc/test/src/stdio/vfprintf_test.cpp b/libc/test/src/stdio/vfprintf_test.cpp
index 43240ce13cc96..6664d500f2816 100644
--- a/libc/test/src/stdio/vfprintf_test.cpp
+++ b/libc/test/src/stdio/vfprintf_test.cpp
@@ -15,6 +15,7 @@
 #include "src/stdio/ferror.h"
 #include "src/stdio/fopen.h"
 #include "src/stdio/fread.h"
+#include "src/stdio/fopencookie.h"
 #endif // LIBC_COPT_STDIO_USE_SYSTEM_FILE
 
 #include "src/stdio/vfprintf.h"
@@ -29,11 +30,13 @@ using LIBC_NAMESPACE::fclose;
 using LIBC_NAMESPACE::ferror;
 using LIBC_NAMESPACE::fopen;
 using LIBC_NAMESPACE::fread;
+using LIBC_NAMESPACE::fopencookie;
 #else  // defined(LIBC_COPT_STDIO_USE_SYSTEM_FILE)
 using ::fclose;
 using ::ferror;
 using ::fopen;
 using ::fread;
+using ::fopencookie;
 #endif // LIBC_COPT_STDIO_USE_SYSTEM_FILE
 } // namespace printf_test
 
@@ -98,3 +101,26 @@ TEST(LlvmLibcVFPrintfTest, WriteToFile) {
 
   ASSERT_EQ(printf_test::fclose(file), 0);
 }
+
+TEST(LlvmLibcVFPrintfTest, CharsWrittenOverflow) {
+  struct NoopStream {};
+  auto noop_write = [](void *cookie, const char *buf, size_t size) -> ssize_t {
+    return size;
+  };
+
+  NoopStream stream;
+  cookie_io_functions_t funcs = {nullptr, +noop_write, nullptr, nullptr};
+  ::FILE *file = printf_test::fopencookie(&stream, "w", funcs);
+  ASSERT_NE(file, nullptr);
+
+  // Trigger an overflow in the return value of vfprintf by writing more than
+  // INT_MAX bytes. We do this by printing a string with precision INT_MAX, and
+  // then one more character.
+  int max_int = LIBC_NAMESPACE::cpp::numeric_limits<int>::max();
+  int result = call_vfprintf(file, "%*sA", max_int, "");
+
+  EXPECT_LT(result, 0);
+  ASSERT_ERRNO_EQ(EOVERFLOW);
+
+  EXPECT_EQ(printf_test::fclose(file), 0);
+}

>From 56d69681324d3d49f1c156ed8341b3a68e447b1e Mon Sep 17 00:00:00 2001
From: Marcell Leleszi <mleleszi at google.com>
Date: Wed, 8 Oct 2025 16:25:14 +0000
Subject: [PATCH 10/27] Baremetal stuff

---
 libc/src/stdio/baremetal/printf.cpp           | 21 ++++++++++++++-----
 libc/src/stdio/baremetal/vprintf.cpp          | 20 ++++++++++++++----
 .../src/stdio/printf_core/vfprintf_internal.h |  4 ++++
 3 files changed, 36 insertions(+), 9 deletions(-)

diff --git a/libc/src/stdio/baremetal/printf.cpp b/libc/src/stdio/baremetal/printf.cpp
index 6f4ebb35d4004..58ff12ba125ea 100644
--- a/libc/src/stdio/baremetal/printf.cpp
+++ b/libc/src/stdio/baremetal/printf.cpp
@@ -42,13 +42,24 @@ LLVM_LIBC_FUNCTION(int, printf, (const char *__restrict format, ...)) {
       buffer, BUFF_SIZE, &stdout_write_hook, nullptr);
   printf_core::Writer<printf_core::WriteMode::FLUSH_TO_STREAM> writer(wb);
 
-  int retval = printf_core::printf_main(&writer, format, args);
+  auto retval = printf_core::printf_main(&writer, format, args);
+  if (retval.has_error()) {
+    libc_errno = retval.error;
+    return -1;
+  }
 
   int flushval = wb.overflow_write("");
-  if (flushval != printf_core::WRITE_OK)
-    retval = flushval;
-    
-  return retval;
+  if (flushval != printf_core::WRITE_OK) {
+    libc_errno = -flushval;
+    return -1;
+  }
+
+  if (retval.value >= cpp::numeric_limits<int>::max()) {
+    libc_errno = EOVERFLOW;
+    return -1;
+  }
+
+  return static_cast<int>(retval.value);  
 }
 
 } // namespace LIBC_NAMESPACE_DECL
diff --git a/libc/src/stdio/baremetal/vprintf.cpp b/libc/src/stdio/baremetal/vprintf.cpp
index 4401ddbe8690e..7dcf01280685f 100644
--- a/libc/src/stdio/baremetal/vprintf.cpp
+++ b/libc/src/stdio/baremetal/vprintf.cpp
@@ -40,13 +40,25 @@ LLVM_LIBC_FUNCTION(int, vprintf,
       buffer, BUFF_SIZE, &stdout_write_hook, nullptr);
   printf_core::Writer<printf_core::WriteMode::FLUSH_TO_STREAM> writer(wb);
 
-  int retval = printf_core::printf_main(&writer, format, args); // TODO baremetal stuff
+  auto retval = printf_core::printf_main(&writer, format, args);
+  if (retval.has_error()) {
+    libc_errno = retval.error;
+    return -1;
+  }
 
   int flushval = wb.overflow_write("");
-  if (flushval != printf_core::WRITE_OK)
-    retval = flushval;
+  if (flushval != printf_core::WRITE_OK) {
+    libc_errno = -flushval;
+    return -1;
+  
+  }
 
-  return retval;
+  if (retval.value >= cpp::numeric_limits<int>::max()) {
+    libc_errno = EOVERFLOW;
+    return -1;
+  }
+
+  return static_cast<int>(retval.value);
 }
 
 } // namespace LIBC_NAMESPACE_DECL
diff --git a/libc/src/stdio/printf_core/vfprintf_internal.h b/libc/src/stdio/printf_core/vfprintf_internal.h
index 53a9762606295..c50cacf2709a7 100644
--- a/libc/src/stdio/printf_core/vfprintf_internal.h
+++ b/libc/src/stdio/printf_core/vfprintf_internal.h
@@ -78,6 +78,10 @@ LIBC_INLINE PrintfResult vfprintf_internal(::FILE *__restrict stream,
   Writer writer(wb);
   internal::flockfile(stream);
   auto retval = printf_main(&writer, format, args);
+  if (retval.has_error()) {
+    internal::funlockfile(stream);
+    return retval;
+  }
   int flushval = wb.overflow_write("");
   if (flushval != WRITE_OK)
     retval.error = -flushval;

>From 995bb08795001429bdd18f784ca005684805a076 Mon Sep 17 00:00:00 2001
From: Marcell Leleszi <mleleszi at google.com>
Date: Wed, 8 Oct 2025 16:42:05 +0000
Subject: [PATCH 11/27] Add test for nullptr write error

---
 libc/src/stdio/printf_core/core_structs.h |  8 --------
 libc/test/src/stdio/fprintf_test.cpp      | 18 +++++++++++++++++-
 2 files changed, 17 insertions(+), 9 deletions(-)

diff --git a/libc/src/stdio/printf_core/core_structs.h b/libc/src/stdio/printf_core/core_structs.h
index 22b674da789a5..ab6c9228fd76f 100644
--- a/libc/src/stdio/printf_core/core_structs.h
+++ b/libc/src/stdio/printf_core/core_structs.h
@@ -144,14 +144,6 @@ template <typename T> LIBC_INLINE constexpr TypeDesc type_desc_from_type() {
 
 // This is the value to be returned by conversions when no error has occurred.
 constexpr int WRITE_OK = 0;
-// These are the printf return values for when an error has occurred. They are
-// all negative, and should be distinct.
-// constexpr int FILE_WRITE_ERROR = -1;
-// constexpr int FILE_STATUS_ERROR = -2;
-// constexpr int NULLPTR_WRITE_ERROR = -3;
-// constexpr int INT_CONVERSION_ERROR = -4;
-// constexpr int FIXED_POINT_CONVERSION_ERROR = -5;
-// constexpr int ALLOCATION_ERROR = -6;
 } // namespace printf_core
 } // namespace LIBC_NAMESPACE_DECL
 
diff --git a/libc/test/src/stdio/fprintf_test.cpp b/libc/test/src/stdio/fprintf_test.cpp
index 53d056da1f36d..f80077fd0885d 100644
--- a/libc/test/src/stdio/fprintf_test.cpp
+++ b/libc/test/src/stdio/fprintf_test.cpp
@@ -112,4 +112,20 @@ TEST(LlvmLibcFPrintfTest, CharsWrittenOverflow) {
   ASSERT_ERRNO_EQ(EOVERFLOW);
 
   EXPECT_EQ(printf_test::fclose(file), 0);
-}
\ No newline at end of file
+}
+
+#ifndef LIBC_COPT_PRINTF_NO_NULLPTR_CHECKS
+TEST(LlvmLibcFPrintfTest, NullPtrCheck) {
+  const char *FILENAME = APPEND_LIBC_TEST("fprintf_nullptr.test");
+  auto FILE_PATH = libc_make_test_file_path(FILENAME);
+
+  ::FILE *file = printf_test::fopen(FILE_PATH, "w");
+  ASSERT_FALSE(file == nullptr);
+
+  int ret = LIBC_NAMESPACE::fprintf(file, "hello %n", (int *)nullptr);
+  EXPECT_LT(ret, 0);
+  ASSERT_ERRNO_EQ(EINVAL);
+
+  ASSERT_EQ(printf_test::fclose(file), 0);
+}
+#endif // LIBC_COPT_PRINTF_NO_NULLPTR_CHECKS

>From eedbad9782d5f9368334be8064e3a1d34847686b Mon Sep 17 00:00:00 2001
From: Marcell Leleszi <mleleszi at google.com>
Date: Wed, 8 Oct 2025 18:44:12 +0000
Subject: [PATCH 12/27] Add overflow handling to strfrom functions

---
 libc/src/stdlib/strfromd.cpp                |  5 ++++-
 libc/src/stdlib/strfromf.cpp                |  5 ++++-
 libc/src/stdlib/strfroml.cpp                |  5 ++++-
 libc/src/time/strftime_core/strftime_main.h |  1 +
 libc/test/src/stdlib/StrfromTest.h          | 20 ++++++++++++++++++--
 5 files changed, 31 insertions(+), 5 deletions(-)

diff --git a/libc/src/stdlib/strfromd.cpp b/libc/src/stdlib/strfromd.cpp
index b35e476086691..2ef8d7e7bdd64 100644
--- a/libc/src/stdlib/strfromd.cpp
+++ b/libc/src/stdlib/strfromd.cpp
@@ -36,7 +36,10 @@ LLVM_LIBC_FUNCTION(int, strfromd,
   if (n > 0)
     wb.buff[wb.buff_cur] = '\0';
 
-  // TODO overflow
+  if (writer.get_chars_written() > cpp::numeric_limits<int>::max()) {
+    libc_errno = EOVERFLOW;
+    return -1;
+  }
   return static_cast<int>(writer.get_chars_written());
 }
 
diff --git a/libc/src/stdlib/strfromf.cpp b/libc/src/stdlib/strfromf.cpp
index 01090c997af1b..5c3e95f4146f2 100644
--- a/libc/src/stdlib/strfromf.cpp
+++ b/libc/src/stdlib/strfromf.cpp
@@ -36,7 +36,10 @@ LLVM_LIBC_FUNCTION(int, strfromf,
   if (n > 0)
     wb.buff[wb.buff_cur] = '\0';
 
-  // TODO overflow
+  if (writer.get_chars_written() > cpp::numeric_limits<int>::max()) {
+    libc_errno = EOVERFLOW;
+    return -1;
+  }
   return static_cast<int>(writer.get_chars_written());
 }
 
diff --git a/libc/src/stdlib/strfroml.cpp b/libc/src/stdlib/strfroml.cpp
index 436243d1e1c38..1605e8ab7c18f 100644
--- a/libc/src/stdlib/strfroml.cpp
+++ b/libc/src/stdlib/strfroml.cpp
@@ -41,7 +41,10 @@ LLVM_LIBC_FUNCTION(int, strfroml,
   if (n > 0)
     wb.buff[wb.buff_cur] = '\0';
 
-  // TODO overflow
+  if (writer.get_chars_written() > cpp::numeric_limits<int>::max()) {
+    libc_errno = EOVERFLOW;
+    return -1;
+  }
   return static_cast<int>(writer.get_chars_written());
 }
 
diff --git a/libc/src/time/strftime_core/strftime_main.h b/libc/src/time/strftime_core/strftime_main.h
index a97e00dfd9b64..865433b8b54c9 100644
--- a/libc/src/time/strftime_core/strftime_main.h
+++ b/libc/src/time/strftime_core/strftime_main.h
@@ -36,6 +36,7 @@ int strftime_main(printf_core::Writer<write_mode> *writer,
       return result;
   }
 
+  // TODO could work similar to printf, retval is size_t in libc outer func
   return static_cast<int>(writer->get_chars_written());
 }
 
diff --git a/libc/test/src/stdlib/StrfromTest.h b/libc/test/src/stdlib/StrfromTest.h
index e82c94499aa11..f24178b2366c9 100644
--- a/libc/test/src/stdlib/StrfromTest.h
+++ b/libc/test/src/stdlib/StrfromTest.h
@@ -6,16 +6,19 @@
 //
 //===----------------------------------------------------------------------===//
 
+#include "src/__support/CPP/limits.h"
 #include "src/__support/CPP/type_traits.h"
 #include "src/__support/FPUtil/FPBits.h"
 #include "test/UnitTest/Test.h"
+#include "test/UnitTest/ErrnoCheckingTest.h"
+#include "test/UnitTest/ErrnoSetterMatcher.h"
 
 #define ASSERT_STREQ_LEN(actual_written, actual_str, expected_str)             \
   EXPECT_EQ(actual_written, static_cast<int>(sizeof(expected_str) - 1));       \
   EXPECT_STREQ(actual_str, expected_str);
 
 template <typename InputT>
-class StrfromTest : public LIBC_NAMESPACE::testing::Test {
+class StrfromTest : public LIBC_NAMESPACE::testing::ErrnoCheckingTest {
 
   static constexpr bool is_single_prec =
       LIBC_NAMESPACE::cpp::is_same<InputT, float>::value;
@@ -481,6 +484,16 @@ class StrfromTest : public LIBC_NAMESPACE::testing::Test {
     written = func(buff, 10, "%A", -ld_nan);
     ASSERT_STREQ_LEN(written, buff, "-NAN");
   }
+
+  void charsWrittenOverflow(FunctionT func) {
+    char buff[100];
+    // Trigger an overflow in the return value of strfrom by writing more than
+    // INT_MAX bytes.
+    int result = func(buff, sizeof(buff), "%.2147483647f", 1.0f);
+
+    EXPECT_LT(result, 0);
+    ASSERT_ERRNO_EQ(EOVERFLOW);
+  }
 };
 
 #define STRFROM_TEST(InputType, name, func)                                    \
@@ -501,4 +514,7 @@ class StrfromTest : public LIBC_NAMESPACE::testing::Test {
   TEST_F(LlvmLibc##name##Test, InsufficientBufferSize) {                       \
     insufficentBufsize(func);                                                  \
   }                                                                            \
-  TEST_F(LlvmLibc##name##Test, InfAndNanValues) { infNanValues(func); }
+  TEST_F(LlvmLibc##name##Test, InfAndNanValues) { infNanValues(func); }        \
+  TEST_F(LlvmLibc##name##Test, CharsWrittenOverflow) {                         \
+    charsWrittenOverflow(func);                                                \
+  }

>From 242b75ced96c1263654e4261509aadc6f6c340be Mon Sep 17 00:00:00 2001
From: Marcell Leleszi <mleleszi at google.com>
Date: Fri, 10 Oct 2025 16:00:28 +0000
Subject: [PATCH 13/27] Cleanup

---
 libc/src/stdio/baremetal/printf.cpp       | 2 +-
 libc/src/stdio/baremetal/vprintf.cpp      | 4 ++--
 libc/src/stdio/printf_core/core_structs.h | 2 +-
 libc/src/stdio/printf_core/printf_main.h  | 5 ++---
 4 files changed, 6 insertions(+), 7 deletions(-)

diff --git a/libc/src/stdio/baremetal/printf.cpp b/libc/src/stdio/baremetal/printf.cpp
index 58ff12ba125ea..e100f946ae9d7 100644
--- a/libc/src/stdio/baremetal/printf.cpp
+++ b/libc/src/stdio/baremetal/printf.cpp
@@ -54,7 +54,7 @@ LLVM_LIBC_FUNCTION(int, printf, (const char *__restrict format, ...)) {
     return -1;
   }
 
-  if (retval.value >= cpp::numeric_limits<int>::max()) {
+  if (retval.value > cpp::numeric_limits<int>::max()) {
     libc_errno = EOVERFLOW;
     return -1;
   }
diff --git a/libc/src/stdio/baremetal/vprintf.cpp b/libc/src/stdio/baremetal/vprintf.cpp
index 7dcf01280685f..992618d062b0d 100644
--- a/libc/src/stdio/baremetal/vprintf.cpp
+++ b/libc/src/stdio/baremetal/vprintf.cpp
@@ -22,7 +22,7 @@ namespace LIBC_NAMESPACE_DECL {
 namespace {
 
 LIBC_INLINE int stdout_write_hook(cpp::string_view new_str, void *) {
-  write_to_stdout(new_str); 
+  write_to_stdout(new_str);
   return printf_core::WRITE_OK;
 }
 
@@ -53,7 +53,7 @@ LLVM_LIBC_FUNCTION(int, vprintf,
   
   }
 
-  if (retval.value >= cpp::numeric_limits<int>::max()) {
+  if (retval.value > cpp::numeric_limits<int>::max()) {
     libc_errno = EOVERFLOW;
     return -1;
   }
diff --git a/libc/src/stdio/printf_core/core_structs.h b/libc/src/stdio/printf_core/core_structs.h
index ab6c9228fd76f..3cb76ed32776f 100644
--- a/libc/src/stdio/printf_core/core_structs.h
+++ b/libc/src/stdio/printf_core/core_structs.h
@@ -31,7 +31,7 @@ struct PrintfResult {
 
   constexpr bool has_error() { return error != 0; }
 
-  // constexpr operator size_t() { return value; }
+  constexpr operator size_t() { return value; }
 };
 
 // These length modifiers match the length modifiers in the format string, which
diff --git a/libc/src/stdio/printf_core/printf_main.h b/libc/src/stdio/printf_core/printf_main.h
index 7bb5b3f3d1cf5..f187b43a9cd8f 100644
--- a/libc/src/stdio/printf_core/printf_main.h
+++ b/libc/src/stdio/printf_core/printf_main.h
@@ -30,10 +30,9 @@ PrintfResult printf_main(Writer<write_mode> *writer, const char *__restrict str,
        !cur_section.raw_string.empty();
        cur_section = parser.get_next_section()) {
     if (cur_section.has_conv)
-      result = convert(writer, cur_section); // look at usages
+      result = convert(writer, cur_section);
     else
-      result = writer->write(cur_section.raw_string); // look at usages
-
+      result = writer->write(cur_section.raw_string);
     if (result < 0)
       return {0, -result};
   }

>From 492b9f1c02d5c00392d2ec0b718806288c6cd169 Mon Sep 17 00:00:00 2001
From: Marcell Leleszi <mleleszi at google.com>
Date: Fri, 10 Oct 2025 16:16:46 +0000
Subject: [PATCH 14/27] Run clang-format

---
 libc/src/stdio/baremetal/printf.cpp              | 2 +-
 libc/src/stdio/baremetal/vprintf.cpp             | 1 -
 libc/src/stdio/printf_core/fixed_converter.h     | 4 ++--
 libc/src/stdio/printf_core/int_converter.h       | 2 +-
 libc/src/stdio/printf_core/write_int_converter.h | 2 +-
 libc/src/stdlib/strfromd.cpp                     | 3 ++-
 libc/test/src/stdio/vfprintf_test.cpp            | 8 ++++----
 libc/test/src/stdlib/StrfromTest.h               | 2 +-
 8 files changed, 12 insertions(+), 12 deletions(-)

diff --git a/libc/src/stdio/baremetal/printf.cpp b/libc/src/stdio/baremetal/printf.cpp
index e100f946ae9d7..a2e308ed54a57 100644
--- a/libc/src/stdio/baremetal/printf.cpp
+++ b/libc/src/stdio/baremetal/printf.cpp
@@ -59,7 +59,7 @@ LLVM_LIBC_FUNCTION(int, printf, (const char *__restrict format, ...)) {
     return -1;
   }
 
-  return static_cast<int>(retval.value);  
+  return static_cast<int>(retval.value);
 }
 
 } // namespace LIBC_NAMESPACE_DECL
diff --git a/libc/src/stdio/baremetal/vprintf.cpp b/libc/src/stdio/baremetal/vprintf.cpp
index 992618d062b0d..003d7d70707fb 100644
--- a/libc/src/stdio/baremetal/vprintf.cpp
+++ b/libc/src/stdio/baremetal/vprintf.cpp
@@ -50,7 +50,6 @@ LLVM_LIBC_FUNCTION(int, vprintf,
   if (flushval != printf_core::WRITE_OK) {
     libc_errno = -flushval;
     return -1;
-  
   }
 
   if (retval.value > cpp::numeric_limits<int>::max()) {
diff --git a/libc/src/stdio/printf_core/fixed_converter.h b/libc/src/stdio/printf_core/fixed_converter.h
index a7185dcb11d8a..77384b1891174 100644
--- a/libc/src/stdio/printf_core/fixed_converter.h
+++ b/libc/src/stdio/printf_core/fixed_converter.h
@@ -9,6 +9,7 @@
 #ifndef LLVM_LIBC_SRC_STDIO_PRINTF_CORE_FIXED_CONVERTER_H
 #define LLVM_LIBC_SRC_STDIO_PRINTF_CORE_FIXED_CONVERTER_H
 
+#include "hdr/errno_macros.h"
 #include "include/llvm-libc-macros/stdfix-macros.h"
 #include "src/__support/CPP/string_view.h"
 #include "src/__support/ctype_utils.h"
@@ -20,7 +21,6 @@
 #include "src/stdio/printf_core/converter_utils.h"
 #include "src/stdio/printf_core/core_structs.h"
 #include "src/stdio/printf_core/writer.h"
-#include "hdr/errno_macros.h"
 
 #include <inttypes.h>
 #include <stddef.h>
@@ -60,7 +60,7 @@ LIBC_INLINE constexpr uint32_t const_ten_exp(uint32_t exponent) {
       READ_FX_BITS(unsigned LENGTH_MODIFIER accum);                            \
     } else {                                                                   \
       LIBC_ASSERT(false && "Invalid conversion name passed to convert_fixed"); \
-      return -EINVAL;                                     \
+      return -EINVAL;                                                          \
     }                                                                          \
   } while (false)
 
diff --git a/libc/src/stdio/printf_core/int_converter.h b/libc/src/stdio/printf_core/int_converter.h
index 08e27e1a60447..554436c9091a8 100644
--- a/libc/src/stdio/printf_core/int_converter.h
+++ b/libc/src/stdio/printf_core/int_converter.h
@@ -9,6 +9,7 @@
 #ifndef LLVM_LIBC_SRC_STDIO_PRINTF_CORE_INT_CONVERTER_H
 #define LLVM_LIBC_SRC_STDIO_PRINTF_CORE_INT_CONVERTER_H
 
+#include "hdr/errno_macros.h"
 #include "src/__support/CPP/span.h"
 #include "src/__support/CPP/string_view.h"
 #include "src/__support/ctype_utils.h"
@@ -17,7 +18,6 @@
 #include "src/stdio/printf_core/converter_utils.h"
 #include "src/stdio/printf_core/core_structs.h"
 #include "src/stdio/printf_core/writer.h"
-#include "hdr/errno_macros.h"
 
 #include <inttypes.h>
 #include <stddef.h>
diff --git a/libc/src/stdio/printf_core/write_int_converter.h b/libc/src/stdio/printf_core/write_int_converter.h
index 15c32d9ac272a..b424278c66185 100644
--- a/libc/src/stdio/printf_core/write_int_converter.h
+++ b/libc/src/stdio/printf_core/write_int_converter.h
@@ -9,10 +9,10 @@
 #ifndef LLVM_LIBC_SRC_STDIO_PRINTF_CORE_WRITE_INT_CONVERTER_H
 #define LLVM_LIBC_SRC_STDIO_PRINTF_CORE_WRITE_INT_CONVERTER_H
 
+#include "hdr/errno_macros.h"
 #include "src/__support/macros/config.h"
 #include "src/stdio/printf_core/core_structs.h"
 #include "src/stdio/printf_core/writer.h"
-#include "hdr/errno_macros.h"
 
 #include <inttypes.h>
 #include <stddef.h>
diff --git a/libc/src/stdlib/strfromd.cpp b/libc/src/stdlib/strfromd.cpp
index 2ef8d7e7bdd64..a0e8bb3ff35f4 100644
--- a/libc/src/stdlib/strfromd.cpp
+++ b/libc/src/stdlib/strfromd.cpp
@@ -28,7 +28,8 @@ LLVM_LIBC_FUNCTION(int, strfromd,
   if (section.has_conv)
     result = internal::strfromfloat_convert<double>(&writer, section);
   else
-    result = writer.write(section.raw_string); // TODO everywhere where writer is used, set errno and ret -1
+    result = writer.write(section.raw_string); // TODO everywhere where writer
+                                               // is used, set errno and ret -1
 
   if (result < 0)
     return result;
diff --git a/libc/test/src/stdio/vfprintf_test.cpp b/libc/test/src/stdio/vfprintf_test.cpp
index 6664d500f2816..804adbc26ddda 100644
--- a/libc/test/src/stdio/vfprintf_test.cpp
+++ b/libc/test/src/stdio/vfprintf_test.cpp
@@ -14,29 +14,29 @@
 #include "src/stdio/fclose.h"
 #include "src/stdio/ferror.h"
 #include "src/stdio/fopen.h"
-#include "src/stdio/fread.h"
 #include "src/stdio/fopencookie.h"
+#include "src/stdio/fread.h"
 #endif // LIBC_COPT_STDIO_USE_SYSTEM_FILE
 
 #include "src/stdio/vfprintf.h"
 
-#include "test/UnitTest/Test.h"
 #include "test/UnitTest/ErrnoCheckingTest.h"
 #include "test/UnitTest/ErrnoSetterMatcher.h"
+#include "test/UnitTest/Test.h"
 
 namespace printf_test {
 #ifndef LIBC_COPT_STDIO_USE_SYSTEM_FILE
 using LIBC_NAMESPACE::fclose;
 using LIBC_NAMESPACE::ferror;
 using LIBC_NAMESPACE::fopen;
-using LIBC_NAMESPACE::fread;
 using LIBC_NAMESPACE::fopencookie;
+using LIBC_NAMESPACE::fread;
 #else  // defined(LIBC_COPT_STDIO_USE_SYSTEM_FILE)
 using ::fclose;
 using ::ferror;
 using ::fopen;
-using ::fread;
 using ::fopencookie;
+using ::fread;
 #endif // LIBC_COPT_STDIO_USE_SYSTEM_FILE
 } // namespace printf_test
 
diff --git a/libc/test/src/stdlib/StrfromTest.h b/libc/test/src/stdlib/StrfromTest.h
index f24178b2366c9..1442f172e19fe 100644
--- a/libc/test/src/stdlib/StrfromTest.h
+++ b/libc/test/src/stdlib/StrfromTest.h
@@ -9,9 +9,9 @@
 #include "src/__support/CPP/limits.h"
 #include "src/__support/CPP/type_traits.h"
 #include "src/__support/FPUtil/FPBits.h"
-#include "test/UnitTest/Test.h"
 #include "test/UnitTest/ErrnoCheckingTest.h"
 #include "test/UnitTest/ErrnoSetterMatcher.h"
+#include "test/UnitTest/Test.h"
 
 #define ASSERT_STREQ_LEN(actual_written, actual_str, expected_str)             \
   EXPECT_EQ(actual_written, static_cast<int>(sizeof(expected_str) - 1));       \

>From 71554f995b01088c595e75ff419b12ed93b75806 Mon Sep 17 00:00:00 2001
From: Marcell Leleszi <mleleszi at google.com>
Date: Fri, 10 Oct 2025 16:25:49 +0000
Subject: [PATCH 15/27] Fix unused param error

---
 libc/src/stdlib/strfromd.cpp                | 3 +--
 libc/src/time/strftime_core/strftime_main.h | 2 +-
 libc/test/src/stdio/fprintf_test.cpp        | 2 +-
 libc/test/src/stdio/vfprintf_test.cpp       | 2 +-
 4 files changed, 4 insertions(+), 5 deletions(-)

diff --git a/libc/src/stdlib/strfromd.cpp b/libc/src/stdlib/strfromd.cpp
index a0e8bb3ff35f4..c89f611831579 100644
--- a/libc/src/stdlib/strfromd.cpp
+++ b/libc/src/stdlib/strfromd.cpp
@@ -28,8 +28,7 @@ LLVM_LIBC_FUNCTION(int, strfromd,
   if (section.has_conv)
     result = internal::strfromfloat_convert<double>(&writer, section);
   else
-    result = writer.write(section.raw_string); // TODO everywhere where writer
-                                               // is used, set errno and ret -1
+    result = writer.write(section.raw_string);
 
   if (result < 0)
     return result;
diff --git a/libc/src/time/strftime_core/strftime_main.h b/libc/src/time/strftime_core/strftime_main.h
index 865433b8b54c9..8e369c2710ddb 100644
--- a/libc/src/time/strftime_core/strftime_main.h
+++ b/libc/src/time/strftime_core/strftime_main.h
@@ -36,7 +36,7 @@ int strftime_main(printf_core::Writer<write_mode> *writer,
       return result;
   }
 
-  // TODO could work similar to printf, retval is size_t in libc outer func
+  // TODO: return result struct like printf, so that size_t can be returned
   return static_cast<int>(writer->get_chars_written());
 }
 
diff --git a/libc/test/src/stdio/fprintf_test.cpp b/libc/test/src/stdio/fprintf_test.cpp
index f80077fd0885d..9a26c44e7f12c 100644
--- a/libc/test/src/stdio/fprintf_test.cpp
+++ b/libc/test/src/stdio/fprintf_test.cpp
@@ -93,7 +93,7 @@ TEST(LlvmLibcFPrintfTest, WriteToFile) {
 
 TEST(LlvmLibcFPrintfTest, CharsWrittenOverflow) {
   struct NoopStream {};
-  auto noop_write = [](void *cookie, const char *buf, size_t size) -> ssize_t {
+  auto noop_write = [](void *, const char *, size_t size) -> ssize_t {
     return size;
   };
 
diff --git a/libc/test/src/stdio/vfprintf_test.cpp b/libc/test/src/stdio/vfprintf_test.cpp
index 804adbc26ddda..952c55b0b0a85 100644
--- a/libc/test/src/stdio/vfprintf_test.cpp
+++ b/libc/test/src/stdio/vfprintf_test.cpp
@@ -104,7 +104,7 @@ TEST(LlvmLibcVFPrintfTest, WriteToFile) {
 
 TEST(LlvmLibcVFPrintfTest, CharsWrittenOverflow) {
   struct NoopStream {};
-  auto noop_write = [](void *cookie, const char *buf, size_t size) -> ssize_t {
+  auto noop_write = [](void *, const char *, size_t size) -> ssize_t {
     return size;
   };
 

>From b03c5458b42673b508eb107dae64f67934aedc6d Mon Sep 17 00:00:00 2001
From: Marcell Leleszi <mleleszi at google.com>
Date: Sun, 12 Oct 2025 13:16:26 +0000
Subject: [PATCH 16/27] Refactor File to store error_code instead of error flag

---
 libc/src/__support/File/file.cpp | 56 +++++++++++++++++---------------
 libc/src/__support/File/file.h   | 12 +++----
 2 files changed, 35 insertions(+), 33 deletions(-)

diff --git a/libc/src/__support/File/file.cpp b/libc/src/__support/File/file.cpp
index 4217e73828388..4df34cbd6c719 100644
--- a/libc/src/__support/File/file.cpp
+++ b/libc/src/__support/File/file.cpp
@@ -20,7 +20,7 @@ namespace LIBC_NAMESPACE_DECL {
 
 FileIOResult File::write_unlocked(const void *data, size_t len) {
   if (!write_allowed()) {
-    err = true;
+    error_code = EBADF;
     return {0, EBADF};
   }
 
@@ -45,16 +45,16 @@ FileIOResult File::write_unlocked_nbf(const uint8_t *data, size_t len) {
     FileIOResult write_result = platform_write(this, buf, write_size);
     pos = 0; // Buffer is now empty so reset pos to the beginning.
     // If less bytes were written than expected, then an error occurred.
-    if (write_result < write_size) {
-      err = true;
+    if (write_result.has_error() || write_result < write_size) {
+      error_code = write_result.has_error() ? write_result.error : EIO;
       // No bytes from data were written, so return 0.
-      return {0, write_result.error};
+      return {0, error_code};
     }
   }
 
   FileIOResult write_result = platform_write(this, data, len);
-  if (write_result < len)
-    err = true;
+  if (write_result.has_error() || write_result < len)
+    error_code = write_result.has_error() ? write_result.error : EIO;
   return write_result;
 }
 
@@ -106,9 +106,9 @@ FileIOResult File::write_unlocked_fbf(const uint8_t *data, size_t len) {
   // If less bytes were written than expected, then an error occurred. Return
   // the number of bytes that have been written from |data|.
   if (buf_result.has_error() || bytes_written < write_size) {
-    err = true;
+    error_code = buf_result.has_error() ? buf_result.error : EIO;
     return {bytes_written <= init_pos ? 0 : bytes_written - init_pos,
-            buf_result.error};
+            error_code};
   }
 
   // The second piece is handled basically the same as the first, although we
@@ -128,8 +128,8 @@ FileIOResult File::write_unlocked_fbf(const uint8_t *data, size_t len) {
     // If less bytes were written than expected, then an error occurred. Return
     // the number of bytes that have been written from |data|.
     if (result.has_error() || bytes_written < remainder.size()) {
-      err = true;
-      return {primary.size() + bytes_written, result.error};
+      error_code = result.has_error() ? result.error : EIO;
+      return {primary.size() + bytes_written, error_code};
     }
   }
 
@@ -166,18 +166,20 @@ FileIOResult File::write_unlocked_lbf(const uint8_t *data, size_t len) {
 
   size_t written = 0;
 
-  written = write_unlocked_nbf(primary.data(), primary.size());
-  if (written < primary.size()) {
-    err = true;
-    return written;
+  auto write_result = write_unlocked_nbf(primary.data(), primary.size());
+  written += write_result;
+  if (write_result.has_error() || written < primary.size()) {
+    error_code = write_result.has_error() ? write_result.error : EIO;
+    return {written, error_code};
   }
 
   flush_unlocked();
 
-  written += write_unlocked_fbf(remainder.data(), remainder.size());
-  if (written < len) {
-    err = true;
-    return written;
+  write_result = write_unlocked_fbf(remainder.data(), remainder.size());
+  written += write_result;;
+  if (write_result.has_error() || written < len) {
+    error_code = write_result.has_error() ? write_result.error : EIO;
+    return {written, error_code};
   }
 
   return len;
@@ -185,7 +187,7 @@ FileIOResult File::write_unlocked_lbf(const uint8_t *data, size_t len) {
 
 FileIOResult File::read_unlocked(void *data, size_t len) {
   if (!read_allowed()) {
-    err = true;
+    error_code = EBADF;
     return {0, EBADF};
   }
 
@@ -244,7 +246,7 @@ FileIOResult File::read_unlocked_fbf(uint8_t *data, size_t len) {
       if (!result.has_error())
         eof = true;
       else
-        err = true;
+        error_code = result.error;
       return {available_data + fetched_size, result.error};
     }
     return len;
@@ -262,7 +264,7 @@ FileIOResult File::read_unlocked_fbf(uint8_t *data, size_t len) {
     if (!result.has_error())
       eof = true;
     else
-      err = true;
+      error_code = result.error;
   }
   return {transfer_size + available_data, result.error};
 }
@@ -282,7 +284,7 @@ FileIOResult File::read_unlocked_nbf(uint8_t *data, size_t len) {
     if (!result.has_error())
       eof = true;
     else
-      err = true;
+      error_code = result.error;
   }
   return {result + available_data, result.error};
 }
@@ -321,7 +323,7 @@ int File::ungetc_unlocked(int c) {
   }
 
   eof = false; // There is atleast one character that can be read now.
-  err = false; // This operation was a success.
+  error_code = 0; // This operation was a success.
   return c;
 }
 
@@ -331,8 +333,8 @@ ErrorOr<int> File::seek(off_t offset, int whence) {
 
     FileIOResult buf_result = platform_write(this, buf, pos);
     if (buf_result.has_error() || buf_result.value < pos) {
-      err = true;
-      return Error(buf_result.error);
+      error_code = buf_result.has_error() ? buf_result.error : EIO;
+      return Error(error_code);
     }
   } else if (prev_op == FileOp::READ && whence == SEEK_CUR) {
     // More data could have been read out from the platform file than was
@@ -369,8 +371,8 @@ int File::flush_unlocked() {
   if (prev_op == FileOp::WRITE && pos > 0) {
     FileIOResult buf_result = platform_write(this, buf, pos);
     if (buf_result.has_error() || buf_result.value < pos) {
-      err = true;
-      return buf_result.error;
+      error_code = buf_result.has_error() ? buf_result.error : EIO;
+      return error_code;
     }
     pos = 0;
   }
diff --git a/libc/src/__support/File/file.h b/libc/src/__support/File/file.h
index 3652e448c7f5a..ad3250f1f84bd 100644
--- a/libc/src/__support/File/file.h
+++ b/libc/src/__support/File/file.h
@@ -118,7 +118,7 @@ class File {
   size_t read_limit;
 
   bool eof;
-  bool err;
+  int error_code;
 
   // This is a convenience RAII class to lock and unlock file objects.
   class FileLock {
@@ -161,7 +161,7 @@ class File {
                                   /*robust=*/false, /*pshared=*/false),
         ungetc_buf(0), buf(buffer), bufsize(buffer_size), bufmode(buffer_mode),
         own_buf(owned), mode(modeflags), pos(0), prev_op(FileOp::NONE),
-        read_limit(0), eof(false), err(false) {
+        read_limit(0), eof(false), error_code(0) {
     adjust_buf();
   }
 
@@ -214,8 +214,8 @@ class File {
       if (prev_op == FileOp::WRITE && pos > 0) {
         auto buf_result = platform_write(this, buf, pos);
         if (buf_result.has_error() || buf_result.value < pos) {
-          err = true;
-          return buf_result.error;
+          error_code = buf_result.has_error() ? buf_result.error : EIO;
+          return error_code;
         }
       }
     }
@@ -250,14 +250,14 @@ class File {
   void lock() { mutex.lock(); }
   void unlock() { mutex.unlock(); }
 
-  bool error_unlocked() const { return err; }
+  bool error_unlocked() const { return error_code != 0; }
 
   bool error() {
     FileLock l(this);
     return error_unlocked();
   }
 
-  void clearerr_unlocked() { err = false; }
+  void clearerr_unlocked() { error_code = 0; }
 
   void clearerr() {
     FileLock l(this);

>From 6d05774e8bfb2444ec9fdb48ddf235b930bbcdc7 Mon Sep 17 00:00:00 2001
From: Marcell Leleszi <mleleszi at google.com>
Date: Sun, 12 Oct 2025 16:13:35 +0000
Subject: [PATCH 17/27] Map internall err code to errno

---
 libc/src/__support/File/file.cpp              | 46 +++++++++----------
 libc/src/__support/File/file.h                | 19 +++++---
 libc/src/stdio/CMakeLists.txt                 |  6 +++
 libc/src/stdio/asprintf.cpp                   |  2 +-
 libc/src/stdio/baremetal/printf.cpp           |  4 +-
 libc/src/stdio/baremetal/vprintf.cpp          |  4 +-
 libc/src/stdio/generic/CMakeLists.txt         |  1 +
 libc/src/stdio/generic/fprintf.cpp            |  2 +-
 libc/src/stdio/generic/printf.cpp             |  6 ++-
 libc/src/stdio/generic/vfprintf.cpp           |  3 +-
 libc/src/stdio/generic/vprintf.cpp            |  3 +-
 libc/src/stdio/printf_core/CMakeLists.txt     |  2 +
 libc/src/stdio/printf_core/core_structs.h     | 45 ++++++++++++++++++
 libc/src/stdio/printf_core/fixed_converter.h  |  2 +-
 libc/src/stdio/printf_core/int_converter.h    |  2 +-
 .../stdio/printf_core/vasprintf_internal.h    |  4 +-
 .../src/stdio/printf_core/vfprintf_internal.h |  2 +-
 .../stdio/printf_core/write_int_converter.h   |  2 +-
 libc/src/stdio/snprintf.cpp                   |  3 +-
 libc/src/stdio/sprintf.cpp                    |  3 +-
 libc/src/stdio/vasprintf.cpp                  |  3 +-
 libc/src/stdio/vsnprintf.cpp                  |  3 +-
 libc/src/stdio/vsprintf.cpp                   |  3 +-
 23 files changed, 121 insertions(+), 49 deletions(-)

diff --git a/libc/src/__support/File/file.cpp b/libc/src/__support/File/file.cpp
index 4df34cbd6c719..b4f0115e09bd2 100644
--- a/libc/src/__support/File/file.cpp
+++ b/libc/src/__support/File/file.cpp
@@ -20,7 +20,7 @@ namespace LIBC_NAMESPACE_DECL {
 
 FileIOResult File::write_unlocked(const void *data, size_t len) {
   if (!write_allowed()) {
-    error_code = EBADF;
+    err_code = EBADF;
     return {0, EBADF};
   }
 
@@ -46,15 +46,15 @@ FileIOResult File::write_unlocked_nbf(const uint8_t *data, size_t len) {
     pos = 0; // Buffer is now empty so reset pos to the beginning.
     // If less bytes were written than expected, then an error occurred.
     if (write_result.has_error() || write_result < write_size) {
-      error_code = write_result.has_error() ? write_result.error : EIO;
+      err_code = write_result.has_error() ? write_result.error : EIO;
       // No bytes from data were written, so return 0.
-      return {0, error_code};
+      return {0, err_code};
     }
   }
 
   FileIOResult write_result = platform_write(this, data, len);
   if (write_result.has_error() || write_result < len)
-    error_code = write_result.has_error() ? write_result.error : EIO;
+    err_code = write_result.has_error() ? write_result.error : EIO;
   return write_result;
 }
 
@@ -106,9 +106,8 @@ FileIOResult File::write_unlocked_fbf(const uint8_t *data, size_t len) {
   // If less bytes were written than expected, then an error occurred. Return
   // the number of bytes that have been written from |data|.
   if (buf_result.has_error() || bytes_written < write_size) {
-    error_code = buf_result.has_error() ? buf_result.error : EIO;
-    return {bytes_written <= init_pos ? 0 : bytes_written - init_pos,
-            error_code};
+    err_code = buf_result.has_error() ? buf_result.error : EIO;
+    return {bytes_written <= init_pos ? 0 : bytes_written - init_pos, err_code};
   }
 
   // The second piece is handled basically the same as the first, although we
@@ -128,8 +127,8 @@ FileIOResult File::write_unlocked_fbf(const uint8_t *data, size_t len) {
     // If less bytes were written than expected, then an error occurred. Return
     // the number of bytes that have been written from |data|.
     if (result.has_error() || bytes_written < remainder.size()) {
-      error_code = result.has_error() ? result.error : EIO;
-      return {primary.size() + bytes_written, error_code};
+      err_code = result.has_error() ? result.error : EIO;
+      return {primary.size() + bytes_written, err_code};
     }
   }
 
@@ -169,17 +168,18 @@ FileIOResult File::write_unlocked_lbf(const uint8_t *data, size_t len) {
   auto write_result = write_unlocked_nbf(primary.data(), primary.size());
   written += write_result;
   if (write_result.has_error() || written < primary.size()) {
-    error_code = write_result.has_error() ? write_result.error : EIO;
-    return {written, error_code};
+    err_code = write_result.has_error() ? write_result.error : EIO;
+    return {written, err_code};
   }
 
   flush_unlocked();
 
   write_result = write_unlocked_fbf(remainder.data(), remainder.size());
-  written += write_result;;
+  written += write_result;
+  ;
   if (write_result.has_error() || written < len) {
-    error_code = write_result.has_error() ? write_result.error : EIO;
-    return {written, error_code};
+    err_code = write_result.has_error() ? write_result.error : EIO;
+    return {written, err_code};
   }
 
   return len;
@@ -187,7 +187,7 @@ FileIOResult File::write_unlocked_lbf(const uint8_t *data, size_t len) {
 
 FileIOResult File::read_unlocked(void *data, size_t len) {
   if (!read_allowed()) {
-    error_code = EBADF;
+    err_code = EBADF;
     return {0, EBADF};
   }
 
@@ -246,7 +246,7 @@ FileIOResult File::read_unlocked_fbf(uint8_t *data, size_t len) {
       if (!result.has_error())
         eof = true;
       else
-        error_code = result.error;
+        err_code = result.error;
       return {available_data + fetched_size, result.error};
     }
     return len;
@@ -264,7 +264,7 @@ FileIOResult File::read_unlocked_fbf(uint8_t *data, size_t len) {
     if (!result.has_error())
       eof = true;
     else
-      error_code = result.error;
+      err_code = result.error;
   }
   return {transfer_size + available_data, result.error};
 }
@@ -284,7 +284,7 @@ FileIOResult File::read_unlocked_nbf(uint8_t *data, size_t len) {
     if (!result.has_error())
       eof = true;
     else
-      error_code = result.error;
+      err_code = result.error;
   }
   return {result + available_data, result.error};
 }
@@ -323,7 +323,7 @@ int File::ungetc_unlocked(int c) {
   }
 
   eof = false; // There is atleast one character that can be read now.
-  error_code = 0; // This operation was a success.
+  err_code = 0; // This operation was a success.
   return c;
 }
 
@@ -333,8 +333,8 @@ ErrorOr<int> File::seek(off_t offset, int whence) {
 
     FileIOResult buf_result = platform_write(this, buf, pos);
     if (buf_result.has_error() || buf_result.value < pos) {
-      error_code = buf_result.has_error() ? buf_result.error : EIO;
-      return Error(error_code);
+      err_code = buf_result.has_error() ? buf_result.error : EIO;
+      return Error(err_code);
     }
   } else if (prev_op == FileOp::READ && whence == SEEK_CUR) {
     // More data could have been read out from the platform file than was
@@ -371,8 +371,8 @@ int File::flush_unlocked() {
   if (prev_op == FileOp::WRITE && pos > 0) {
     FileIOResult buf_result = platform_write(this, buf, pos);
     if (buf_result.has_error() || buf_result.value < pos) {
-      error_code = buf_result.has_error() ? buf_result.error : EIO;
-      return error_code;
+      err_code = buf_result.has_error() ? buf_result.error : EIO;
+      return err_code;
     }
     pos = 0;
   }
diff --git a/libc/src/__support/File/file.h b/libc/src/__support/File/file.h
index ad3250f1f84bd..2f7bfaf1a779a 100644
--- a/libc/src/__support/File/file.h
+++ b/libc/src/__support/File/file.h
@@ -118,7 +118,7 @@ class File {
   size_t read_limit;
 
   bool eof;
-  int error_code;
+  int err_code;
 
   // This is a convenience RAII class to lock and unlock file objects.
   class FileLock {
@@ -161,7 +161,7 @@ class File {
                                   /*robust=*/false, /*pshared=*/false),
         ungetc_buf(0), buf(buffer), bufsize(buffer_size), bufmode(buffer_mode),
         own_buf(owned), mode(modeflags), pos(0), prev_op(FileOp::NONE),
-        read_limit(0), eof(false), error_code(0) {
+        read_limit(0), eof(false), err_code(0) {
     adjust_buf();
   }
 
@@ -214,8 +214,8 @@ class File {
       if (prev_op == FileOp::WRITE && pos > 0) {
         auto buf_result = platform_write(this, buf, pos);
         if (buf_result.has_error() || buf_result.value < pos) {
-          error_code = buf_result.has_error() ? buf_result.error : EIO;
-          return error_code;
+          err_code = buf_result.has_error() ? buf_result.error : EIO;
+          return err_code;
         }
       }
     }
@@ -250,14 +250,21 @@ class File {
   void lock() { mutex.lock(); }
   void unlock() { mutex.unlock(); }
 
-  bool error_unlocked() const { return error_code != 0; }
+  bool error_unlocked() const { return err_code != 0; }
 
   bool error() {
     FileLock l(this);
     return error_unlocked();
   }
 
-  void clearerr_unlocked() { error_code = 0; }
+  int error_code_unlocked() const { return err_code; }
+
+  int error_code() {
+    FileLock l(this);
+    return error_code_unlocked();
+  }
+
+  void clearerr_unlocked() { err_code = 0; }
 
   void clearerr() {
     FileLock l(this);
diff --git a/libc/src/stdio/CMakeLists.txt b/libc/src/stdio/CMakeLists.txt
index b0a6ef1e291b5..d7af6c47efbc8 100644
--- a/libc/src/stdio/CMakeLists.txt
+++ b/libc/src/stdio/CMakeLists.txt
@@ -125,6 +125,7 @@ add_entrypoint_object(
   DEPENDS
     libc.src.stdio.printf_core.printf_main
     libc.src.stdio.printf_core.writer
+    libc.src.stdio.printf_core.core_structs
 )
 
 add_entrypoint_object(
@@ -136,6 +137,7 @@ add_entrypoint_object(
   DEPENDS
     libc.src.stdio.printf_core.printf_main
     libc.src.stdio.printf_core.writer
+    libc.src.stdio.printf_core.core_structs
 )
 
 add_entrypoint_object(
@@ -146,6 +148,7 @@ add_entrypoint_object(
     asprintf.h
   DEPENDS
     libc.src.stdio.printf_core.vasprintf_internal
+    libc.src.stdio.printf_core.core_structs
 )
 
 add_entrypoint_object(
@@ -157,6 +160,7 @@ add_entrypoint_object(
   DEPENDS
     libc.src.stdio.printf_core.printf_main
     libc.src.stdio.printf_core.writer
+    libc.src.stdio.printf_core.core_structs
 )
 
 add_entrypoint_object(
@@ -168,6 +172,7 @@ add_entrypoint_object(
   DEPENDS
     libc.src.stdio.printf_core.printf_main
     libc.src.stdio.printf_core.writer
+    libc.src.stdio.printf_core.core_structs
 )
 
 add_entrypoint_object(
@@ -178,6 +183,7 @@ add_entrypoint_object(
     vasprintf.h
   DEPENDS
     libc.src.stdio.printf_core.vasprintf_internal
+    libc.src.stdio.printf_core.core_structs
 )
 
 add_subdirectory(printf_core)
diff --git a/libc/src/stdio/asprintf.cpp b/libc/src/stdio/asprintf.cpp
index 13c1ee68ef0f2..014cf7efade6b 100644
--- a/libc/src/stdio/asprintf.cpp
+++ b/libc/src/stdio/asprintf.cpp
@@ -24,7 +24,7 @@ LLVM_LIBC_FUNCTION(int, asprintf,
   va_end(vlist);
   auto ret_val = printf_core::vasprintf_internal(buffer, format, args);
   if (ret_val.has_error()) {
-    libc_errno = ret_val.error;
+    libc_errno = printf_core::internal_error_to_errno(ret_val.error);
     return -1;
   }
   if (ret_val.value > cpp::numeric_limits<int>::max()) {
diff --git a/libc/src/stdio/baremetal/printf.cpp b/libc/src/stdio/baremetal/printf.cpp
index a2e308ed54a57..5ac9433152ee9 100644
--- a/libc/src/stdio/baremetal/printf.cpp
+++ b/libc/src/stdio/baremetal/printf.cpp
@@ -44,13 +44,13 @@ LLVM_LIBC_FUNCTION(int, printf, (const char *__restrict format, ...)) {
 
   auto retval = printf_core::printf_main(&writer, format, args);
   if (retval.has_error()) {
-    libc_errno = retval.error;
+    libc_errno = retval.error; // TODO map
     return -1;
   }
 
   int flushval = wb.overflow_write("");
   if (flushval != printf_core::WRITE_OK) {
-    libc_errno = -flushval;
+    libc_errno = -flushval; // TODO map
     return -1;
   }
 
diff --git a/libc/src/stdio/baremetal/vprintf.cpp b/libc/src/stdio/baremetal/vprintf.cpp
index 003d7d70707fb..03b6f698db6c4 100644
--- a/libc/src/stdio/baremetal/vprintf.cpp
+++ b/libc/src/stdio/baremetal/vprintf.cpp
@@ -42,13 +42,13 @@ LLVM_LIBC_FUNCTION(int, vprintf,
 
   auto retval = printf_core::printf_main(&writer, format, args);
   if (retval.has_error()) {
-    libc_errno = retval.error;
+    libc_errno = retval.error; // TODO map
     return -1;
   }
 
   int flushval = wb.overflow_write("");
   if (flushval != printf_core::WRITE_OK) {
-    libc_errno = -flushval;
+    libc_errno = -flushval; // TODO map
     return -1;
   }
 
diff --git a/libc/src/stdio/generic/CMakeLists.txt b/libc/src/stdio/generic/CMakeLists.txt
index 6361822b61999..77ff3b76a4d1d 100644
--- a/libc/src/stdio/generic/CMakeLists.txt
+++ b/libc/src/stdio/generic/CMakeLists.txt
@@ -394,6 +394,7 @@ list(APPEND fprintf_deps
       libc.hdr.types.FILE
       libc.src.__support.arg_list
       libc.src.stdio.printf_core.vfprintf_internal
+      libc.src.stdio.printf_core.core_structs
 )
 
 if(LLVM_LIBC_FULL_BUILD)
diff --git a/libc/src/stdio/generic/fprintf.cpp b/libc/src/stdio/generic/fprintf.cpp
index 632264f944f44..e2c57809ae8cc 100644
--- a/libc/src/stdio/generic/fprintf.cpp
+++ b/libc/src/stdio/generic/fprintf.cpp
@@ -29,7 +29,7 @@ LLVM_LIBC_FUNCTION(int, fprintf,
   va_end(vlist);
   auto ret_val = printf_core::vfprintf_internal(stream, format, args);
   if (ret_val.has_error()) {
-    libc_errno = ret_val.error;
+    libc_errno = printf_core::internal_error_to_errno(ret_val.error, stream);
     return -1;
   }
   if (ret_val.value > cpp::numeric_limits<int>::max()) {
diff --git a/libc/src/stdio/generic/printf.cpp b/libc/src/stdio/generic/printf.cpp
index 482a65cd5a2fa..6b99433e9440a 100644
--- a/libc/src/stdio/generic/printf.cpp
+++ b/libc/src/stdio/generic/printf.cpp
@@ -11,6 +11,7 @@
 #include "src/__support/File/file.h"
 #include "src/__support/arg_list.h"
 #include "src/__support/macros/config.h"
+#include "src/stdio/printf_core/core_structs.h"
 #include "src/stdio/printf_core/vfprintf_internal.h"
 
 #include "hdr/types/FILE.h"
@@ -34,7 +35,10 @@ LLVM_LIBC_FUNCTION(int, printf, (const char *__restrict format, ...)) {
   auto ret_val = printf_core::vfprintf_internal(
       reinterpret_cast<::FILE *>(PRINTF_STDOUT), format, args);
   if (ret_val.has_error()) {
-    libc_errno = ret_val.error;
+    libc_errno = printf_core::internal_error_to_errno(
+        ret_val.error, reinterpret_cast<::FILE *>(PRINTF_STDOUT));
+    return -1;
+    ;
     return -1;
   }
   if (ret_val.value > cpp::numeric_limits<int>::max()) {
diff --git a/libc/src/stdio/generic/vfprintf.cpp b/libc/src/stdio/generic/vfprintf.cpp
index 6b39011123084..8430c99e6d84b 100644
--- a/libc/src/stdio/generic/vfprintf.cpp
+++ b/libc/src/stdio/generic/vfprintf.cpp
@@ -11,6 +11,7 @@
 #include "src/__support/File/file.h"
 #include "src/__support/arg_list.h"
 #include "src/__support/macros/config.h"
+#include "src/stdio/printf_core/core_structs.h"
 #include "src/stdio/printf_core/vfprintf_internal.h"
 
 #include "hdr/types/FILE.h"
@@ -26,7 +27,7 @@ LLVM_LIBC_FUNCTION(int, vfprintf,
                                  // destruction automatically.
   auto ret_val = printf_core::vfprintf_internal(stream, format, args);
   if (ret_val.has_error()) {
-    libc_errno = ret_val.error;
+    libc_errno = printf_core::internal_error_to_errno(ret_val.error, stream);
     return -1;
   }
   if (ret_val.value > cpp::numeric_limits<int>::max()) {
diff --git a/libc/src/stdio/generic/vprintf.cpp b/libc/src/stdio/generic/vprintf.cpp
index 09ba2ce0d32a9..c0bde9dce9c4d 100644
--- a/libc/src/stdio/generic/vprintf.cpp
+++ b/libc/src/stdio/generic/vprintf.cpp
@@ -32,7 +32,8 @@ LLVM_LIBC_FUNCTION(int, vprintf,
   auto ret_val = printf_core::vfprintf_internal(
       reinterpret_cast<::FILE *>(PRINTF_STDOUT), format, args);
   if (ret_val.has_error()) {
-    libc_errno = ret_val.error;
+    libc_errno = printf_core::internal_error_to_errno(
+        ret_val.error, reinterpret_cast<::FILE *>(PRINTF_STDOUT));
     return -1;
   }
   if (ret_val.value > cpp::numeric_limits<int>::max()) {
diff --git a/libc/src/stdio/printf_core/CMakeLists.txt b/libc/src/stdio/printf_core/CMakeLists.txt
index ee66145e60156..42173e25bc5e2 100644
--- a/libc/src/stdio/printf_core/CMakeLists.txt
+++ b/libc/src/stdio/printf_core/CMakeLists.txt
@@ -47,6 +47,8 @@ add_header_library(
     libc.include.inttypes
     libc.src.__support.CPP.string_view
     libc.src.__support.FPUtil.fp_bits
+    libc.hdr.types.FILE
+    libc.src.__support.File.file
 )
 
 add_header_library(
diff --git a/libc/src/stdio/printf_core/core_structs.h b/libc/src/stdio/printf_core/core_structs.h
index 3cb76ed32776f..b56eef78e02b5 100644
--- a/libc/src/stdio/printf_core/core_structs.h
+++ b/libc/src/stdio/printf_core/core_structs.h
@@ -11,9 +11,11 @@
 
 #include "src/__support/macros/config.h"
 
+#include "hdr/types/FILE.h"
 #include "src/__support/CPP/string_view.h"
 #include "src/__support/CPP/type_traits.h"
 #include "src/__support/FPUtil/FPBits.h"
+#include "src/__support/File/file.h"
 #include "src/stdio/printf_core/printf_config.h"
 
 #include <inttypes.h>
@@ -144,6 +146,49 @@ template <typename T> LIBC_INLINE constexpr TypeDesc type_desc_from_type() {
 
 // This is the value to be returned by conversions when no error has occurred.
 constexpr int WRITE_OK = 0;
+// These are the printf return values for when an error has occurred. They are
+// all negative, and should be distinct.
+constexpr int FILE_WRITE_ERROR = -1;
+constexpr int FILE_STATUS_ERROR = -2;
+constexpr int NULLPTR_WRITE_ERROR = -3;
+constexpr int INT_CONVERSION_ERROR = -4;
+constexpr int FIXED_POINT_CONVERSION_ERROR = -5;
+constexpr int ALLOCATION_ERROR = -6;
+
+LIBC_INLINE static int internal_error_to_errno(int internal_errno,
+                                               FILE *f = nullptr) {
+#if !defined(LIBC_COPT_STDIO_USE_SYSTEM_FILE)
+  LIBC_NAMESPACE::File *file = reinterpret_cast<LIBC_NAMESPACE::File *>(f);
+#else
+  LIBC_NAMESPACE::File *file = nullptr;
+  (void)f;
+#endif
+
+  switch (-internal_errno) {
+  case WRITE_OK:
+    return 0;
+  case FILE_WRITE_ERROR:
+    if (file == nullptr)
+      return EIO;
+    return file->error_unlocked() ? file->error_code_unlocked() : EIO;
+  case FILE_STATUS_ERROR:
+    return EIO;
+  case NULLPTR_WRITE_ERROR:
+    return EINVAL;
+  case INT_CONVERSION_ERROR:
+    return ERANGE;
+  case FIXED_POINT_CONVERSION_ERROR:
+    return EINVAL;
+  case ALLOCATION_ERROR:
+    return ENOMEM;
+  default:
+    LIBC_ASSERT(
+        false &&
+        "Invalid internal printf error code passed to internal_error_to_errno");
+    return EINVAL;
+  }
+}
+
 } // namespace printf_core
 } // namespace LIBC_NAMESPACE_DECL
 
diff --git a/libc/src/stdio/printf_core/fixed_converter.h b/libc/src/stdio/printf_core/fixed_converter.h
index 77384b1891174..069447f27d918 100644
--- a/libc/src/stdio/printf_core/fixed_converter.h
+++ b/libc/src/stdio/printf_core/fixed_converter.h
@@ -60,7 +60,7 @@ LIBC_INLINE constexpr uint32_t const_ten_exp(uint32_t exponent) {
       READ_FX_BITS(unsigned LENGTH_MODIFIER accum);                            \
     } else {                                                                   \
       LIBC_ASSERT(false && "Invalid conversion name passed to convert_fixed"); \
-      return -EINVAL;                                                          \
+      return FIXED_POINT_CONVERSION_ERROR;                                     \
     }                                                                          \
   } while (false)
 
diff --git a/libc/src/stdio/printf_core/int_converter.h b/libc/src/stdio/printf_core/int_converter.h
index 554436c9091a8..61cb402732ab7 100644
--- a/libc/src/stdio/printf_core/int_converter.h
+++ b/libc/src/stdio/printf_core/int_converter.h
@@ -92,7 +92,7 @@ LIBC_INLINE int convert_int(Writer<write_mode> *writer,
   cpp::array<char, details::num_buf_size()> buf;
   auto str = details::num_to_strview(num, buf, to_conv.conv_name);
   if (!str)
-    return -ERANGE;
+    return INT_CONVERSION_ERROR;
 
   size_t digits_written = str->size();
 
diff --git a/libc/src/stdio/printf_core/vasprintf_internal.h b/libc/src/stdio/printf_core/vasprintf_internal.h
index f84f60bec2fc6..b8dfb59164d33 100644
--- a/libc/src/stdio/printf_core/vasprintf_internal.h
+++ b/libc/src/stdio/printf_core/vasprintf_internal.h
@@ -30,7 +30,7 @@ LIBC_INLINE int resize_overflow_hook(cpp::string_view new_str, void *target) {
   if (new_buff == nullptr) {
     if (wb->buff != wb->init_buff)
       free(wb->buff);
-    return -ENOMEM;
+    return ALLOCATION_ERROR;
   }
   if (isBuffOnStack)
     inline_memcpy(new_buff, wb->buff, wb->buff_cur);
@@ -59,7 +59,7 @@ LIBC_INLINE PrintfResult vasprintf_internal(char **ret,
   if (wb.buff == init_buff_on_stack) {
     *ret = static_cast<char *>(malloc(ret_val.value + 1));
     if (ret == nullptr)
-      return {0, ENOMEM};
+      return {0, ALLOCATION_ERROR};
     inline_memcpy(*ret, wb.buff, ret_val.value);
   } else {
     *ret = wb.buff;
diff --git a/libc/src/stdio/printf_core/vfprintf_internal.h b/libc/src/stdio/printf_core/vfprintf_internal.h
index c50cacf2709a7..a1c2c43bd5873 100644
--- a/libc/src/stdio/printf_core/vfprintf_internal.h
+++ b/libc/src/stdio/printf_core/vfprintf_internal.h
@@ -64,7 +64,7 @@ LIBC_INLINE int file_write_hook(cpp::string_view new_str, void *fp) {
   size_t written = internal::fwrite_unlocked(new_str.data(), sizeof(char),
                                              new_str.size(), target_file);
   if (written != new_str.size() || internal::ferror_unlocked(target_file))
-    return -EIO;
+    return FILE_WRITE_ERROR;
   return WRITE_OK;
 }
 
diff --git a/libc/src/stdio/printf_core/write_int_converter.h b/libc/src/stdio/printf_core/write_int_converter.h
index b424278c66185..f63a9a538690d 100644
--- a/libc/src/stdio/printf_core/write_int_converter.h
+++ b/libc/src/stdio/printf_core/write_int_converter.h
@@ -27,7 +27,7 @@ LIBC_INLINE int convert_write_int(Writer<write_mode> *writer,
 #ifndef LIBC_COPT_PRINTF_NO_NULLPTR_CHECKS
   // This is an additional check added by LLVM-libc.
   if (to_conv.conv_val_ptr == nullptr)
-    return -EINVAL;
+    return NULLPTR_WRITE_ERROR;
 #endif // LIBC_COPT_PRINTF_NO_NULLPTR_CHECKS
 
   size_t written = writer->get_chars_written();
diff --git a/libc/src/stdio/snprintf.cpp b/libc/src/stdio/snprintf.cpp
index 6691da1debb5a..8e42f8133c997 100644
--- a/libc/src/stdio/snprintf.cpp
+++ b/libc/src/stdio/snprintf.cpp
@@ -10,6 +10,7 @@
 
 #include "src/__support/arg_list.h"
 #include "src/__support/macros/config.h"
+#include "src/stdio/printf_core/core_structs.h"
 #include "src/stdio/printf_core/printf_main.h"
 #include "src/stdio/printf_core/writer.h"
 
@@ -34,7 +35,7 @@ LLVM_LIBC_FUNCTION(int, snprintf,
 
   auto ret_val = printf_core::printf_main(&writer, format, args);
   if (ret_val.has_error()) {
-    libc_errno = ret_val.error;
+    libc_errno = printf_core::internal_error_to_errno(ret_val.error);
     return -1;
   }
   if (buffsz > 0) // if the buffsz is 0 the buffer may be a null pointer.
diff --git a/libc/src/stdio/sprintf.cpp b/libc/src/stdio/sprintf.cpp
index 10db9feb80121..a0935b318bd53 100644
--- a/libc/src/stdio/sprintf.cpp
+++ b/libc/src/stdio/sprintf.cpp
@@ -11,6 +11,7 @@
 #include "src/__support/CPP/limits.h"
 #include "src/__support/arg_list.h"
 #include "src/__support/macros/config.h"
+#include "src/stdio/printf_core/core_structs.h"
 #include "src/stdio/printf_core/printf_main.h"
 #include "src/stdio/printf_core/writer.h"
 
@@ -35,7 +36,7 @@ LLVM_LIBC_FUNCTION(int, sprintf,
 
   auto ret_val = printf_core::printf_main(&writer, format, args);
   if (ret_val.has_error()) {
-    libc_errno = ret_val.error;
+    libc_errno = printf_core::internal_error_to_errno(ret_val.error);
     return -1;
   }
   wb.buff[wb.buff_cur] = '\0';
diff --git a/libc/src/stdio/vasprintf.cpp b/libc/src/stdio/vasprintf.cpp
index 11174727e5d64..fea49363d9079 100644
--- a/libc/src/stdio/vasprintf.cpp
+++ b/libc/src/stdio/vasprintf.cpp
@@ -8,6 +8,7 @@
 
 #include "src/stdio/vasprintf.h"
 #include "src/__support/arg_list.h"
+#include "src/stdio/printf_core/core_structs.h"
 #include "src/stdio/printf_core/vasprintf_internal.h"
 
 namespace LIBC_NAMESPACE_DECL {
@@ -20,7 +21,7 @@ LLVM_LIBC_FUNCTION(int, vasprintf,
                                  // destruction automatically.
   auto ret_val = printf_core::vasprintf_internal(ret, format, args);
   if (ret_val.has_error()) {
-    libc_errno = ret_val.error;
+    libc_errno = printf_core::internal_error_to_errno(ret_val.error);
     return -1;
   }
   if (ret_val.value > cpp::numeric_limits<int>::max()) {
diff --git a/libc/src/stdio/vsnprintf.cpp b/libc/src/stdio/vsnprintf.cpp
index 25e6165897674..466f24d3eb433 100644
--- a/libc/src/stdio/vsnprintf.cpp
+++ b/libc/src/stdio/vsnprintf.cpp
@@ -10,6 +10,7 @@
 
 #include "src/__support/arg_list.h"
 #include "src/__support/macros/config.h"
+#include "src/stdio/printf_core/core_structs.h"
 #include "src/stdio/printf_core/printf_main.h"
 #include "src/stdio/printf_core/writer.h"
 
@@ -31,7 +32,7 @@ LLVM_LIBC_FUNCTION(int, vsnprintf,
 
   auto ret_val = printf_core::printf_main(&writer, format, args);
   if (ret_val.has_error()) {
-    libc_errno = ret_val.error;
+    libc_errno = printf_core::internal_error_to_errno(ret_val.error);
     return -1;
   }
   if (buffsz > 0) // if the buffsz is 0 the buffer may be a null pointer.
diff --git a/libc/src/stdio/vsprintf.cpp b/libc/src/stdio/vsprintf.cpp
index 6dda2db250d50..5a2fda6ef1a8c 100644
--- a/libc/src/stdio/vsprintf.cpp
+++ b/libc/src/stdio/vsprintf.cpp
@@ -11,6 +11,7 @@
 #include "src/__support/CPP/limits.h"
 #include "src/__support/arg_list.h"
 #include "src/__support/macros/config.h"
+#include "src/stdio/printf_core/core_structs.h"
 #include "src/stdio/printf_core/printf_main.h"
 #include "src/stdio/printf_core/writer.h"
 
@@ -32,7 +33,7 @@ LLVM_LIBC_FUNCTION(int, vsprintf,
 
   auto ret_val = printf_core::printf_main(&writer, format, args);
   if (ret_val.has_error()) {
-    libc_errno = ret_val.error;
+    libc_errno = printf_core::internal_error_to_errno(ret_val.error);
     return -1;
   }
   wb.buff[wb.buff_cur] = '\0';

>From d1949753dc9459f3b1c25987d670ad8e21383082 Mon Sep 17 00:00:00 2001
From: Marcell Leleszi <mleleszi at google.com>
Date: Fri, 17 Oct 2025 06:08:42 +0000
Subject: [PATCH 18/27] Return system IO errors return values

---
 libc/src/__support/File/file.cpp              | 58 +++++++++----------
 libc/src/__support/File/file.h                | 19 ++----
 libc/src/stdio/generic/fprintf.cpp            |  2 +-
 libc/src/stdio/generic/printf.cpp             |  3 +-
 libc/src/stdio/generic/vfprintf.cpp           |  2 +-
 libc/src/stdio/generic/vprintf.cpp            |  3 +-
 libc/src/stdio/printf_core/core_structs.h     | 33 +++++------
 .../src/stdio/printf_core/vfprintf_internal.h | 28 ++++++---
 8 files changed, 73 insertions(+), 75 deletions(-)

diff --git a/libc/src/__support/File/file.cpp b/libc/src/__support/File/file.cpp
index b4f0115e09bd2..4217e73828388 100644
--- a/libc/src/__support/File/file.cpp
+++ b/libc/src/__support/File/file.cpp
@@ -20,7 +20,7 @@ namespace LIBC_NAMESPACE_DECL {
 
 FileIOResult File::write_unlocked(const void *data, size_t len) {
   if (!write_allowed()) {
-    err_code = EBADF;
+    err = true;
     return {0, EBADF};
   }
 
@@ -45,16 +45,16 @@ FileIOResult File::write_unlocked_nbf(const uint8_t *data, size_t len) {
     FileIOResult write_result = platform_write(this, buf, write_size);
     pos = 0; // Buffer is now empty so reset pos to the beginning.
     // If less bytes were written than expected, then an error occurred.
-    if (write_result.has_error() || write_result < write_size) {
-      err_code = write_result.has_error() ? write_result.error : EIO;
+    if (write_result < write_size) {
+      err = true;
       // No bytes from data were written, so return 0.
-      return {0, err_code};
+      return {0, write_result.error};
     }
   }
 
   FileIOResult write_result = platform_write(this, data, len);
-  if (write_result.has_error() || write_result < len)
-    err_code = write_result.has_error() ? write_result.error : EIO;
+  if (write_result < len)
+    err = true;
   return write_result;
 }
 
@@ -106,8 +106,9 @@ FileIOResult File::write_unlocked_fbf(const uint8_t *data, size_t len) {
   // If less bytes were written than expected, then an error occurred. Return
   // the number of bytes that have been written from |data|.
   if (buf_result.has_error() || bytes_written < write_size) {
-    err_code = buf_result.has_error() ? buf_result.error : EIO;
-    return {bytes_written <= init_pos ? 0 : bytes_written - init_pos, err_code};
+    err = true;
+    return {bytes_written <= init_pos ? 0 : bytes_written - init_pos,
+            buf_result.error};
   }
 
   // The second piece is handled basically the same as the first, although we
@@ -127,8 +128,8 @@ FileIOResult File::write_unlocked_fbf(const uint8_t *data, size_t len) {
     // If less bytes were written than expected, then an error occurred. Return
     // the number of bytes that have been written from |data|.
     if (result.has_error() || bytes_written < remainder.size()) {
-      err_code = result.has_error() ? result.error : EIO;
-      return {primary.size() + bytes_written, err_code};
+      err = true;
+      return {primary.size() + bytes_written, result.error};
     }
   }
 
@@ -165,21 +166,18 @@ FileIOResult File::write_unlocked_lbf(const uint8_t *data, size_t len) {
 
   size_t written = 0;
 
-  auto write_result = write_unlocked_nbf(primary.data(), primary.size());
-  written += write_result;
-  if (write_result.has_error() || written < primary.size()) {
-    err_code = write_result.has_error() ? write_result.error : EIO;
-    return {written, err_code};
+  written = write_unlocked_nbf(primary.data(), primary.size());
+  if (written < primary.size()) {
+    err = true;
+    return written;
   }
 
   flush_unlocked();
 
-  write_result = write_unlocked_fbf(remainder.data(), remainder.size());
-  written += write_result;
-  ;
-  if (write_result.has_error() || written < len) {
-    err_code = write_result.has_error() ? write_result.error : EIO;
-    return {written, err_code};
+  written += write_unlocked_fbf(remainder.data(), remainder.size());
+  if (written < len) {
+    err = true;
+    return written;
   }
 
   return len;
@@ -187,7 +185,7 @@ FileIOResult File::write_unlocked_lbf(const uint8_t *data, size_t len) {
 
 FileIOResult File::read_unlocked(void *data, size_t len) {
   if (!read_allowed()) {
-    err_code = EBADF;
+    err = true;
     return {0, EBADF};
   }
 
@@ -246,7 +244,7 @@ FileIOResult File::read_unlocked_fbf(uint8_t *data, size_t len) {
       if (!result.has_error())
         eof = true;
       else
-        err_code = result.error;
+        err = true;
       return {available_data + fetched_size, result.error};
     }
     return len;
@@ -264,7 +262,7 @@ FileIOResult File::read_unlocked_fbf(uint8_t *data, size_t len) {
     if (!result.has_error())
       eof = true;
     else
-      err_code = result.error;
+      err = true;
   }
   return {transfer_size + available_data, result.error};
 }
@@ -284,7 +282,7 @@ FileIOResult File::read_unlocked_nbf(uint8_t *data, size_t len) {
     if (!result.has_error())
       eof = true;
     else
-      err_code = result.error;
+      err = true;
   }
   return {result + available_data, result.error};
 }
@@ -323,7 +321,7 @@ int File::ungetc_unlocked(int c) {
   }
 
   eof = false; // There is atleast one character that can be read now.
-  err_code = 0; // This operation was a success.
+  err = false; // This operation was a success.
   return c;
 }
 
@@ -333,8 +331,8 @@ ErrorOr<int> File::seek(off_t offset, int whence) {
 
     FileIOResult buf_result = platform_write(this, buf, pos);
     if (buf_result.has_error() || buf_result.value < pos) {
-      err_code = buf_result.has_error() ? buf_result.error : EIO;
-      return Error(err_code);
+      err = true;
+      return Error(buf_result.error);
     }
   } else if (prev_op == FileOp::READ && whence == SEEK_CUR) {
     // More data could have been read out from the platform file than was
@@ -371,8 +369,8 @@ int File::flush_unlocked() {
   if (prev_op == FileOp::WRITE && pos > 0) {
     FileIOResult buf_result = platform_write(this, buf, pos);
     if (buf_result.has_error() || buf_result.value < pos) {
-      err_code = buf_result.has_error() ? buf_result.error : EIO;
-      return err_code;
+      err = true;
+      return buf_result.error;
     }
     pos = 0;
   }
diff --git a/libc/src/__support/File/file.h b/libc/src/__support/File/file.h
index 2f7bfaf1a779a..3652e448c7f5a 100644
--- a/libc/src/__support/File/file.h
+++ b/libc/src/__support/File/file.h
@@ -118,7 +118,7 @@ class File {
   size_t read_limit;
 
   bool eof;
-  int err_code;
+  bool err;
 
   // This is a convenience RAII class to lock and unlock file objects.
   class FileLock {
@@ -161,7 +161,7 @@ class File {
                                   /*robust=*/false, /*pshared=*/false),
         ungetc_buf(0), buf(buffer), bufsize(buffer_size), bufmode(buffer_mode),
         own_buf(owned), mode(modeflags), pos(0), prev_op(FileOp::NONE),
-        read_limit(0), eof(false), err_code(0) {
+        read_limit(0), eof(false), err(false) {
     adjust_buf();
   }
 
@@ -214,8 +214,8 @@ class File {
       if (prev_op == FileOp::WRITE && pos > 0) {
         auto buf_result = platform_write(this, buf, pos);
         if (buf_result.has_error() || buf_result.value < pos) {
-          err_code = buf_result.has_error() ? buf_result.error : EIO;
-          return err_code;
+          err = true;
+          return buf_result.error;
         }
       }
     }
@@ -250,21 +250,14 @@ class File {
   void lock() { mutex.lock(); }
   void unlock() { mutex.unlock(); }
 
-  bool error_unlocked() const { return err_code != 0; }
+  bool error_unlocked() const { return err; }
 
   bool error() {
     FileLock l(this);
     return error_unlocked();
   }
 
-  int error_code_unlocked() const { return err_code; }
-
-  int error_code() {
-    FileLock l(this);
-    return error_code_unlocked();
-  }
-
-  void clearerr_unlocked() { err_code = 0; }
+  void clearerr_unlocked() { err = false; }
 
   void clearerr() {
     FileLock l(this);
diff --git a/libc/src/stdio/generic/fprintf.cpp b/libc/src/stdio/generic/fprintf.cpp
index e2c57809ae8cc..a7eb1a4e445be 100644
--- a/libc/src/stdio/generic/fprintf.cpp
+++ b/libc/src/stdio/generic/fprintf.cpp
@@ -29,7 +29,7 @@ LLVM_LIBC_FUNCTION(int, fprintf,
   va_end(vlist);
   auto ret_val = printf_core::vfprintf_internal(stream, format, args);
   if (ret_val.has_error()) {
-    libc_errno = printf_core::internal_error_to_errno(ret_val.error, stream);
+    libc_errno = printf_core::internal_error_to_errno(ret_val.error);
     return -1;
   }
   if (ret_val.value > cpp::numeric_limits<int>::max()) {
diff --git a/libc/src/stdio/generic/printf.cpp b/libc/src/stdio/generic/printf.cpp
index 6b99433e9440a..9d65f435db480 100644
--- a/libc/src/stdio/generic/printf.cpp
+++ b/libc/src/stdio/generic/printf.cpp
@@ -35,8 +35,7 @@ LLVM_LIBC_FUNCTION(int, printf, (const char *__restrict format, ...)) {
   auto ret_val = printf_core::vfprintf_internal(
       reinterpret_cast<::FILE *>(PRINTF_STDOUT), format, args);
   if (ret_val.has_error()) {
-    libc_errno = printf_core::internal_error_to_errno(
-        ret_val.error, reinterpret_cast<::FILE *>(PRINTF_STDOUT));
+    libc_errno = printf_core::internal_error_to_errno(ret_val.error);
     return -1;
     ;
     return -1;
diff --git a/libc/src/stdio/generic/vfprintf.cpp b/libc/src/stdio/generic/vfprintf.cpp
index 8430c99e6d84b..0b7763e8637db 100644
--- a/libc/src/stdio/generic/vfprintf.cpp
+++ b/libc/src/stdio/generic/vfprintf.cpp
@@ -27,7 +27,7 @@ LLVM_LIBC_FUNCTION(int, vfprintf,
                                  // destruction automatically.
   auto ret_val = printf_core::vfprintf_internal(stream, format, args);
   if (ret_val.has_error()) {
-    libc_errno = printf_core::internal_error_to_errno(ret_val.error, stream);
+    libc_errno = printf_core::internal_error_to_errno(ret_val.error);
     return -1;
   }
   if (ret_val.value > cpp::numeric_limits<int>::max()) {
diff --git a/libc/src/stdio/generic/vprintf.cpp b/libc/src/stdio/generic/vprintf.cpp
index c0bde9dce9c4d..3cf66e3fad3e2 100644
--- a/libc/src/stdio/generic/vprintf.cpp
+++ b/libc/src/stdio/generic/vprintf.cpp
@@ -32,8 +32,7 @@ LLVM_LIBC_FUNCTION(int, vprintf,
   auto ret_val = printf_core::vfprintf_internal(
       reinterpret_cast<::FILE *>(PRINTF_STDOUT), format, args);
   if (ret_val.has_error()) {
-    libc_errno = printf_core::internal_error_to_errno(
-        ret_val.error, reinterpret_cast<::FILE *>(PRINTF_STDOUT));
+    libc_errno = printf_core::internal_error_to_errno(ret_val.error);
     return -1;
   }
   if (ret_val.value > cpp::numeric_limits<int>::max()) {
diff --git a/libc/src/stdio/printf_core/core_structs.h b/libc/src/stdio/printf_core/core_structs.h
index b56eef78e02b5..4348e65592160 100644
--- a/libc/src/stdio/printf_core/core_structs.h
+++ b/libc/src/stdio/printf_core/core_structs.h
@@ -148,29 +148,24 @@ template <typename T> LIBC_INLINE constexpr TypeDesc type_desc_from_type() {
 constexpr int WRITE_OK = 0;
 // These are the printf return values for when an error has occurred. They are
 // all negative, and should be distinct.
-constexpr int FILE_WRITE_ERROR = -1;
-constexpr int FILE_STATUS_ERROR = -2;
-constexpr int NULLPTR_WRITE_ERROR = -3;
-constexpr int INT_CONVERSION_ERROR = -4;
-constexpr int FIXED_POINT_CONVERSION_ERROR = -5;
-constexpr int ALLOCATION_ERROR = -6;
-
-LIBC_INLINE static int internal_error_to_errno(int internal_errno,
-                                               FILE *f = nullptr) {
-#if !defined(LIBC_COPT_STDIO_USE_SYSTEM_FILE)
-  LIBC_NAMESPACE::File *file = reinterpret_cast<LIBC_NAMESPACE::File *>(f);
-#else
-  LIBC_NAMESPACE::File *file = nullptr;
-  (void)f;
-#endif
+constexpr int FILE_WRITE_ERROR = -1001;
+constexpr int FILE_STATUS_ERROR = -1002;
+constexpr int NULLPTR_WRITE_ERROR = -1003;
+constexpr int INT_CONVERSION_ERROR = -1004;
+constexpr int FIXED_POINT_CONVERSION_ERROR = -1005;
+constexpr int ALLOCATION_ERROR = -1006;
+constexpr int SHORT_WRITE_ERROR = -1007;
+
+LIBC_INLINE static int internal_error_to_errno(int internal_errno) {
+  if (internal_errno < 1001) {
+    return internal_errno;
+  }
 
   switch (-internal_errno) {
   case WRITE_OK:
     return 0;
   case FILE_WRITE_ERROR:
-    if (file == nullptr)
-      return EIO;
-    return file->error_unlocked() ? file->error_code_unlocked() : EIO;
+    return EIO;
   case FILE_STATUS_ERROR:
     return EIO;
   case NULLPTR_WRITE_ERROR:
@@ -181,6 +176,8 @@ LIBC_INLINE static int internal_error_to_errno(int internal_errno,
     return EINVAL;
   case ALLOCATION_ERROR:
     return ENOMEM;
+  case SHORT_WRITE_ERROR:
+    return EIO;
   default:
     LIBC_ASSERT(
         false &&
diff --git a/libc/src/stdio/printf_core/vfprintf_internal.h b/libc/src/stdio/printf_core/vfprintf_internal.h
index a1c2c43bd5873..2428508d0178c 100644
--- a/libc/src/stdio/printf_core/vfprintf_internal.h
+++ b/libc/src/stdio/printf_core/vfprintf_internal.h
@@ -36,8 +36,8 @@ LIBC_INLINE void funlockfile(FILE *f) {
   reinterpret_cast<LIBC_NAMESPACE::File *>(f)->unlock();
 }
 
-LIBC_INLINE size_t fwrite_unlocked(const void *ptr, size_t size, size_t nmemb,
-                                   FILE *f) {
+LIBC_INLINE FileIOResult fwrite_unlocked(const void *ptr, size_t size,
+                                         size_t nmemb, FILE *f) {
   return reinterpret_cast<LIBC_NAMESPACE::File *>(f)->write_unlocked(
       ptr, size * nmemb);
 }
@@ -48,9 +48,9 @@ LIBC_INLINE void flockfile(::FILE *f) { ::flockfile(f); }
 
 LIBC_INLINE void funlockfile(::FILE *f) { ::funlockfile(f); }
 
-LIBC_INLINE size_t fwrite_unlocked(const void *ptr, size_t size, size_t nmemb,
-                                   ::FILE *f) {
-  return ::fwrite_unlocked(ptr, size, nmemb, f);
+LIBC_INLINE FileIOResult fwrite_unlocked(const void *ptr, size_t size,
+                                         size_t nmemb, ::FILE *f) {
+  return {::fwrite_unlocked(ptr, size, nmemb, f), 0}; // todo err
 }
 #endif // LIBC_COPT_STDIO_USE_SYSTEM_FILE
 } // namespace internal
@@ -61,10 +61,22 @@ LIBC_INLINE int file_write_hook(cpp::string_view new_str, void *fp) {
   ::FILE *target_file = reinterpret_cast<::FILE *>(fp);
   // Write new_str to the target file. The logic preventing a zero-length write
   // is in the writer, so we don't check here.
-  size_t written = internal::fwrite_unlocked(new_str.data(), sizeof(char),
-                                             new_str.size(), target_file);
-  if (written != new_str.size() || internal::ferror_unlocked(target_file))
+  auto write_result = internal::fwrite_unlocked(new_str.data(), sizeof(char),
+                                                new_str.size(), target_file);
+  // Propagate actual system error in FileIOResult.
+  if (write_result.has_error())
+    return -write_result.error;
+
+  // On short write no system error is returned, so return custom error.
+  if (write_result.value != new_str.size())
+    return SHORT_WRITE_ERROR;
+
+  // Return custom error in case error wasn't set on FileIOResult for some
+  // reason (like using LIBC_COPT_STDIO_USE_SYSTEM_FILE)
+  if (internal::ferror_unlocked(target_file)) {
     return FILE_WRITE_ERROR;
+  }
+
   return WRITE_OK;
 }
 

>From 69e93624cfe0707649a6c15cdaa3c883bad73417 Mon Sep 17 00:00:00 2001
From: Marcell Leleszi <mleleszi at google.com>
Date: Fri, 17 Oct 2025 06:29:21 +0000
Subject: [PATCH 19/27] Use ErroOr for print retval instead of custom struct

---
 libc/src/stdio/asprintf.cpp                     |  8 ++++----
 libc/src/stdio/baremetal/printf.cpp             |  8 ++++----
 libc/src/stdio/baremetal/vprintf.cpp            |  8 ++++----
 libc/src/stdio/generic/fprintf.cpp              |  8 ++++----
 libc/src/stdio/generic/printf.cpp               | 10 ++++------
 libc/src/stdio/generic/vfprintf.cpp             |  8 ++++----
 libc/src/stdio/generic/vprintf.cpp              |  8 ++++----
 libc/src/stdio/printf_core/CMakeLists.txt       |  3 +++
 libc/src/stdio/printf_core/core_structs.h       | 12 ------------
 libc/src/stdio/printf_core/printf_main.h        |  8 +++++---
 libc/src/stdio/printf_core/vasprintf_internal.h | 17 +++++++++--------
 libc/src/stdio/printf_core/vfprintf_internal.h  | 11 ++++++-----
 libc/src/stdio/snprintf.cpp                     |  8 ++++----
 libc/src/stdio/sprintf.cpp                      |  8 ++++----
 libc/src/stdio/vasprintf.cpp                    |  8 ++++----
 libc/src/stdio/vsnprintf.cpp                    |  8 ++++----
 libc/src/stdio/vsprintf.cpp                     |  8 ++++----
 17 files changed, 71 insertions(+), 78 deletions(-)

diff --git a/libc/src/stdio/asprintf.cpp b/libc/src/stdio/asprintf.cpp
index 014cf7efade6b..3187ea40490a4 100644
--- a/libc/src/stdio/asprintf.cpp
+++ b/libc/src/stdio/asprintf.cpp
@@ -23,16 +23,16 @@ LLVM_LIBC_FUNCTION(int, asprintf,
                                  // destruction automatically.
   va_end(vlist);
   auto ret_val = printf_core::vasprintf_internal(buffer, format, args);
-  if (ret_val.has_error()) {
-    libc_errno = printf_core::internal_error_to_errno(ret_val.error);
+  if (!ret_val.has_value()) {
+    libc_errno = printf_core::internal_error_to_errno(ret_val.error());
     return -1;
   }
-  if (ret_val.value > cpp::numeric_limits<int>::max()) {
+  if (ret_val.value() > cpp::numeric_limits<int>::max()) {
     libc_errno = EOVERFLOW;
     return -1;
   }
 
-  return static_cast<int>(ret_val.value);
+  return static_cast<int>(ret_val.value());
 }
 
 } // namespace LIBC_NAMESPACE_DECL
diff --git a/libc/src/stdio/baremetal/printf.cpp b/libc/src/stdio/baremetal/printf.cpp
index 5ac9433152ee9..53a7c95219664 100644
--- a/libc/src/stdio/baremetal/printf.cpp
+++ b/libc/src/stdio/baremetal/printf.cpp
@@ -43,8 +43,8 @@ LLVM_LIBC_FUNCTION(int, printf, (const char *__restrict format, ...)) {
   printf_core::Writer<printf_core::WriteMode::FLUSH_TO_STREAM> writer(wb);
 
   auto retval = printf_core::printf_main(&writer, format, args);
-  if (retval.has_error()) {
-    libc_errno = retval.error; // TODO map
+  if (!retval.has_value()) {
+    libc_errno = retval.error(); // TODO map
     return -1;
   }
 
@@ -54,12 +54,12 @@ LLVM_LIBC_FUNCTION(int, printf, (const char *__restrict format, ...)) {
     return -1;
   }
 
-  if (retval.value > cpp::numeric_limits<int>::max()) {
+  if (retval.value() > cpp::numeric_limits<int>::max()) {
     libc_errno = EOVERFLOW;
     return -1;
   }
 
-  return static_cast<int>(retval.value);
+  return static_cast<int>(retval.value());
 }
 
 } // namespace LIBC_NAMESPACE_DECL
diff --git a/libc/src/stdio/baremetal/vprintf.cpp b/libc/src/stdio/baremetal/vprintf.cpp
index 03b6f698db6c4..2a058885a0d9b 100644
--- a/libc/src/stdio/baremetal/vprintf.cpp
+++ b/libc/src/stdio/baremetal/vprintf.cpp
@@ -41,8 +41,8 @@ LLVM_LIBC_FUNCTION(int, vprintf,
   printf_core::Writer<printf_core::WriteMode::FLUSH_TO_STREAM> writer(wb);
 
   auto retval = printf_core::printf_main(&writer, format, args);
-  if (retval.has_error()) {
-    libc_errno = retval.error; // TODO map
+  if (!retval.has_value()) {
+    libc_errno = retval.error(); // TODO map
     return -1;
   }
 
@@ -52,12 +52,12 @@ LLVM_LIBC_FUNCTION(int, vprintf,
     return -1;
   }
 
-  if (retval.value > cpp::numeric_limits<int>::max()) {
+  if (retval.value() > cpp::numeric_limits<int>::max()) {
     libc_errno = EOVERFLOW;
     return -1;
   }
 
-  return static_cast<int>(retval.value);
+  return static_cast<int>(retval.value());
 }
 
 } // namespace LIBC_NAMESPACE_DECL
diff --git a/libc/src/stdio/generic/fprintf.cpp b/libc/src/stdio/generic/fprintf.cpp
index a7eb1a4e445be..8251a098a9705 100644
--- a/libc/src/stdio/generic/fprintf.cpp
+++ b/libc/src/stdio/generic/fprintf.cpp
@@ -28,16 +28,16 @@ LLVM_LIBC_FUNCTION(int, fprintf,
                                  // destruction automatically.
   va_end(vlist);
   auto ret_val = printf_core::vfprintf_internal(stream, format, args);
-  if (ret_val.has_error()) {
-    libc_errno = printf_core::internal_error_to_errno(ret_val.error);
+  if (!ret_val.has_value()) {
+    libc_errno = printf_core::internal_error_to_errno(ret_val.error());
     return -1;
   }
-  if (ret_val.value > cpp::numeric_limits<int>::max()) {
+  if (ret_val.value() > cpp::numeric_limits<int>::max()) {
     libc_errno = EOVERFLOW;
     return -1;
   }
 
-  return static_cast<int>(ret_val.value);
+  return static_cast<int>(ret_val.value());
 }
 
 } // namespace LIBC_NAMESPACE_DECL
diff --git a/libc/src/stdio/generic/printf.cpp b/libc/src/stdio/generic/printf.cpp
index 9d65f435db480..2ff1b35c02c07 100644
--- a/libc/src/stdio/generic/printf.cpp
+++ b/libc/src/stdio/generic/printf.cpp
@@ -34,18 +34,16 @@ LLVM_LIBC_FUNCTION(int, printf, (const char *__restrict format, ...)) {
   va_end(vlist);
   auto ret_val = printf_core::vfprintf_internal(
       reinterpret_cast<::FILE *>(PRINTF_STDOUT), format, args);
-  if (ret_val.has_error()) {
-    libc_errno = printf_core::internal_error_to_errno(ret_val.error);
-    return -1;
-    ;
+  if (!ret_val.has_value()) {
+    libc_errno = printf_core::internal_error_to_errno(ret_val.error());
     return -1;
   }
-  if (ret_val.value > cpp::numeric_limits<int>::max()) {
+  if (ret_val.value() > cpp::numeric_limits<int>::max()) {
     libc_errno = EOVERFLOW;
     return -1;
   }
 
-  return static_cast<int>(ret_val.value);
+  return static_cast<int>(ret_val.value());
 }
 
 } // namespace LIBC_NAMESPACE_DECL
diff --git a/libc/src/stdio/generic/vfprintf.cpp b/libc/src/stdio/generic/vfprintf.cpp
index 0b7763e8637db..280f8092a5520 100644
--- a/libc/src/stdio/generic/vfprintf.cpp
+++ b/libc/src/stdio/generic/vfprintf.cpp
@@ -26,16 +26,16 @@ LLVM_LIBC_FUNCTION(int, vfprintf,
                                  // and pointer semantics, as well as handling
                                  // destruction automatically.
   auto ret_val = printf_core::vfprintf_internal(stream, format, args);
-  if (ret_val.has_error()) {
-    libc_errno = printf_core::internal_error_to_errno(ret_val.error);
+  if (!ret_val.has_value()) {
+    libc_errno = printf_core::internal_error_to_errno(ret_val.error());
     return -1;
   }
-  if (ret_val.value > cpp::numeric_limits<int>::max()) {
+  if (ret_val.value() > cpp::numeric_limits<int>::max()) {
     libc_errno = EOVERFLOW;
     return -1;
   }
 
-  return static_cast<int>(ret_val.value);
+  return static_cast<int>(ret_val.value());
 }
 
 } // namespace LIBC_NAMESPACE_DECL
diff --git a/libc/src/stdio/generic/vprintf.cpp b/libc/src/stdio/generic/vprintf.cpp
index 3cf66e3fad3e2..ba9b283bf9655 100644
--- a/libc/src/stdio/generic/vprintf.cpp
+++ b/libc/src/stdio/generic/vprintf.cpp
@@ -31,16 +31,16 @@ LLVM_LIBC_FUNCTION(int, vprintf,
                                  // destruction automatically.
   auto ret_val = printf_core::vfprintf_internal(
       reinterpret_cast<::FILE *>(PRINTF_STDOUT), format, args);
-  if (ret_val.has_error()) {
-    libc_errno = printf_core::internal_error_to_errno(ret_val.error);
+  if (!ret_val.has_value()) {
+    libc_errno = printf_core::internal_error_to_errno(ret_val.error());
     return -1;
   }
-  if (ret_val.value > cpp::numeric_limits<int>::max()) {
+  if (ret_val.value() > cpp::numeric_limits<int>::max()) {
     libc_errno = EOVERFLOW;
     return -1;
   }
 
-  return static_cast<int>(ret_val.value);
+  return static_cast<int>(ret_val.value());
 }
 
 } // namespace LIBC_NAMESPACE_DECL
diff --git a/libc/src/stdio/printf_core/CMakeLists.txt b/libc/src/stdio/printf_core/CMakeLists.txt
index 42173e25bc5e2..95c32f4ac16b7 100644
--- a/libc/src/stdio/printf_core/CMakeLists.txt
+++ b/libc/src/stdio/printf_core/CMakeLists.txt
@@ -127,6 +127,7 @@ add_header_library(
     .writer
     .core_structs
     libc.src.__support.arg_list
+    libc.src.__support.error_or
 )
 
 add_header_library(
@@ -138,6 +139,7 @@ add_header_library(
     libc.hdr.func.free
     libc.hdr.func.realloc
     libc.src.__support.arg_list
+    libc.src.__support.error_or
     libc.src.stdio.printf_core.printf_main
     libc.src.stdio.printf_core.writer
 )
@@ -154,6 +156,7 @@ add_header_library(
     vfprintf_internal.h
   DEPENDS
     libc.src.__support.File.file
+    libc.src.__support.error_or
     libc.src.__support.arg_list
     libc.src.stdio.printf_core.printf_main
     libc.src.stdio.printf_core.writer
diff --git a/libc/src/stdio/printf_core/core_structs.h b/libc/src/stdio/printf_core/core_structs.h
index 4348e65592160..726b767778848 100644
--- a/libc/src/stdio/printf_core/core_structs.h
+++ b/libc/src/stdio/printf_core/core_structs.h
@@ -24,18 +24,6 @@
 namespace LIBC_NAMESPACE_DECL {
 namespace printf_core {
 
-struct PrintfResult {
-  size_t value;
-  int error;
-
-  constexpr PrintfResult(size_t val) : value(val), error(0) {}
-  constexpr PrintfResult(size_t val, int error) : value(val), error(error) {}
-
-  constexpr bool has_error() { return error != 0; }
-
-  constexpr operator size_t() { return value; }
-};
-
 // These length modifiers match the length modifiers in the format string, which
 // is why they are formatted differently from the rest of the file.
 enum class LengthModifier { hh, h, l, ll, j, z, t, L, w, wf, none };
diff --git a/libc/src/stdio/printf_core/printf_main.h b/libc/src/stdio/printf_core/printf_main.h
index f187b43a9cd8f..1c7a7237c097d 100644
--- a/libc/src/stdio/printf_core/printf_main.h
+++ b/libc/src/stdio/printf_core/printf_main.h
@@ -10,6 +10,7 @@
 #define LLVM_LIBC_SRC_STDIO_PRINTF_CORE_PRINTF_MAIN_H
 
 #include "src/__support/arg_list.h"
+#include "src/__support/error_or.h"
 #include "src/__support/macros/config.h"
 #include "src/stdio/printf_core/converter.h"
 #include "src/stdio/printf_core/core_structs.h"
@@ -22,8 +23,9 @@ namespace LIBC_NAMESPACE_DECL {
 namespace printf_core {
 
 template <WriteMode write_mode>
-PrintfResult printf_main(Writer<write_mode> *writer, const char *__restrict str,
-                         internal::ArgList &args) {
+ErrorOr<size_t> printf_main(Writer<write_mode> *writer,
+                            const char *__restrict str,
+                            internal::ArgList &args) {
   Parser<internal::ArgList> parser(str, args);
   int result = 0;
   for (FormatSection cur_section = parser.get_next_section();
@@ -34,7 +36,7 @@ PrintfResult printf_main(Writer<write_mode> *writer, const char *__restrict str,
     else
       result = writer->write(cur_section.raw_string);
     if (result < 0)
-      return {0, -result};
+      return Error(-result);
   }
 
   return writer->get_chars_written();
diff --git a/libc/src/stdio/printf_core/vasprintf_internal.h b/libc/src/stdio/printf_core/vasprintf_internal.h
index b8dfb59164d33..50ebf0ea4555d 100644
--- a/libc/src/stdio/printf_core/vasprintf_internal.h
+++ b/libc/src/stdio/printf_core/vasprintf_internal.h
@@ -11,6 +11,7 @@
 #include "hdr/func/malloc.h"
 #include "hdr/func/realloc.h"
 #include "src/__support/arg_list.h"
+#include "src/__support/error_or.h"
 #include "src/stdio/printf_core/core_structs.h"
 #include "src/stdio/printf_core/printf_main.h"
 #include "src/stdio/printf_core/writer.h"
@@ -43,28 +44,28 @@ LIBC_INLINE int resize_overflow_hook(cpp::string_view new_str, void *target) {
 
 constexpr size_t DEFAULT_BUFFER_SIZE = 200;
 
-LIBC_INLINE PrintfResult vasprintf_internal(char **ret,
-                                            const char *__restrict format,
-                                            internal::ArgList args) {
+LIBC_INLINE ErrorOr<size_t> vasprintf_internal(char **ret,
+                                               const char *__restrict format,
+                                               internal::ArgList args) {
   char init_buff_on_stack[DEFAULT_BUFFER_SIZE];
   printf_core::WriteBuffer<Mode<WriteMode::RESIZE_AND_FILL_BUFF>::value> wb(
       init_buff_on_stack, DEFAULT_BUFFER_SIZE, resize_overflow_hook);
   printf_core::Writer writer(wb);
 
   auto ret_val = printf_core::printf_main(&writer, format, args);
-  if (ret_val.has_error()) {
+  if (!ret_val.has_value()) {
     *ret = nullptr;
     return ret_val;
   }
   if (wb.buff == init_buff_on_stack) {
-    *ret = static_cast<char *>(malloc(ret_val.value + 1));
+    *ret = static_cast<char *>(malloc(ret_val.value() + 1));
     if (ret == nullptr)
-      return {0, ALLOCATION_ERROR};
-    inline_memcpy(*ret, wb.buff, ret_val.value);
+      return Error(ALLOCATION_ERROR);
+    inline_memcpy(*ret, wb.buff, ret_val.value());
   } else {
     *ret = wb.buff;
   }
-  (*ret)[ret_val.value] = '\0';
+  (*ret)[ret_val.value()] = '\0';
   return ret_val;
 }
 } // namespace printf_core
diff --git a/libc/src/stdio/printf_core/vfprintf_internal.h b/libc/src/stdio/printf_core/vfprintf_internal.h
index 2428508d0178c..3fabe04d921cc 100644
--- a/libc/src/stdio/printf_core/vfprintf_internal.h
+++ b/libc/src/stdio/printf_core/vfprintf_internal.h
@@ -12,6 +12,7 @@
 #include "hdr/errno_macros.h"
 #include "src/__support/File/file.h"
 #include "src/__support/arg_list.h"
+#include "src/__support/error_or.h"
 #include "src/__support/macros/attributes.h" // For LIBC_INLINE
 #include "src/__support/macros/config.h"
 #include "src/stdio/printf_core/core_structs.h"
@@ -80,9 +81,9 @@ LIBC_INLINE int file_write_hook(cpp::string_view new_str, void *fp) {
   return WRITE_OK;
 }
 
-LIBC_INLINE PrintfResult vfprintf_internal(::FILE *__restrict stream,
-                                           const char *__restrict format,
-                                           internal::ArgList &args) {
+LIBC_INLINE ErrorOr<size_t> vfprintf_internal(::FILE *__restrict stream,
+                                              const char *__restrict format,
+                                              internal::ArgList &args) {
   constexpr size_t BUFF_SIZE = 1024;
   char buffer[BUFF_SIZE];
   printf_core::WriteBuffer<Mode<WriteMode::FLUSH_TO_STREAM>::value> wb(
@@ -90,13 +91,13 @@ LIBC_INLINE PrintfResult vfprintf_internal(::FILE *__restrict stream,
   Writer writer(wb);
   internal::flockfile(stream);
   auto retval = printf_main(&writer, format, args);
-  if (retval.has_error()) {
+  if (!retval.has_value()) {
     internal::funlockfile(stream);
     return retval;
   }
   int flushval = wb.overflow_write("");
   if (flushval != WRITE_OK)
-    retval.error = -flushval;
+    retval = Error(-flushval);
   internal::funlockfile(stream);
   return retval;
 }
diff --git a/libc/src/stdio/snprintf.cpp b/libc/src/stdio/snprintf.cpp
index 8e42f8133c997..d63f5c8f0957e 100644
--- a/libc/src/stdio/snprintf.cpp
+++ b/libc/src/stdio/snprintf.cpp
@@ -34,19 +34,19 @@ LLVM_LIBC_FUNCTION(int, snprintf,
   printf_core::Writer writer(wb);
 
   auto ret_val = printf_core::printf_main(&writer, format, args);
-  if (ret_val.has_error()) {
-    libc_errno = printf_core::internal_error_to_errno(ret_val.error);
+  if (!ret_val.has_value()) {
+    libc_errno = printf_core::internal_error_to_errno(ret_val.error());
     return -1;
   }
   if (buffsz > 0) // if the buffsz is 0 the buffer may be a null pointer.
     wb.buff[wb.buff_cur] = '\0';
 
-  if (ret_val.value > cpp::numeric_limits<int>::max()) {
+  if (ret_val.value() > cpp::numeric_limits<int>::max()) {
     libc_errno = EOVERFLOW;
     return -1;
   }
 
-  return static_cast<int>(ret_val.value);
+  return static_cast<int>(ret_val.value());
 }
 
 } // namespace LIBC_NAMESPACE_DECL
diff --git a/libc/src/stdio/sprintf.cpp b/libc/src/stdio/sprintf.cpp
index a0935b318bd53..b9be409500086 100644
--- a/libc/src/stdio/sprintf.cpp
+++ b/libc/src/stdio/sprintf.cpp
@@ -35,18 +35,18 @@ LLVM_LIBC_FUNCTION(int, sprintf,
   printf_core::Writer writer(wb);
 
   auto ret_val = printf_core::printf_main(&writer, format, args);
-  if (ret_val.has_error()) {
-    libc_errno = printf_core::internal_error_to_errno(ret_val.error);
+  if (!ret_val.has_value()) {
+    libc_errno = printf_core::internal_error_to_errno(ret_val.error());
     return -1;
   }
   wb.buff[wb.buff_cur] = '\0';
 
-  if (ret_val.value > cpp::numeric_limits<int>::max()) {
+  if (ret_val.value() > cpp::numeric_limits<int>::max()) {
     libc_errno = EOVERFLOW;
     return -1;
   }
 
-  return static_cast<int>(ret_val.value);
+  return static_cast<int>(ret_val.value());
 }
 
 } // namespace LIBC_NAMESPACE_DECL
diff --git a/libc/src/stdio/vasprintf.cpp b/libc/src/stdio/vasprintf.cpp
index fea49363d9079..4e3e601e12b94 100644
--- a/libc/src/stdio/vasprintf.cpp
+++ b/libc/src/stdio/vasprintf.cpp
@@ -20,15 +20,15 @@ LLVM_LIBC_FUNCTION(int, vasprintf,
                                  // and pointer semantics, as well as handling
                                  // destruction automatically.
   auto ret_val = printf_core::vasprintf_internal(ret, format, args);
-  if (ret_val.has_error()) {
-    libc_errno = printf_core::internal_error_to_errno(ret_val.error);
+  if (!ret_val.has_value()) {
+    libc_errno = printf_core::internal_error_to_errno(ret_val.error());
     return -1;
   }
-  if (ret_val.value > cpp::numeric_limits<int>::max()) {
+  if (ret_val.value() > cpp::numeric_limits<int>::max()) {
     libc_errno = EOVERFLOW;
     return -1;
   }
-  return static_cast<int>(ret_val.value);
+  return static_cast<int>(ret_val.value());
 }
 
 } // namespace LIBC_NAMESPACE_DECL
diff --git a/libc/src/stdio/vsnprintf.cpp b/libc/src/stdio/vsnprintf.cpp
index 466f24d3eb433..d822a1090f647 100644
--- a/libc/src/stdio/vsnprintf.cpp
+++ b/libc/src/stdio/vsnprintf.cpp
@@ -31,19 +31,19 @@ LLVM_LIBC_FUNCTION(int, vsnprintf,
   printf_core::Writer writer(wb);
 
   auto ret_val = printf_core::printf_main(&writer, format, args);
-  if (ret_val.has_error()) {
-    libc_errno = printf_core::internal_error_to_errno(ret_val.error);
+  if (!ret_val.has_value()) {
+    libc_errno = printf_core::internal_error_to_errno(ret_val.error());
     return -1;
   }
   if (buffsz > 0) // if the buffsz is 0 the buffer may be a null pointer.
     wb.buff[wb.buff_cur] = '\0';
 
-  if (ret_val.value > cpp::numeric_limits<int>::max()) {
+  if (ret_val.value() > cpp::numeric_limits<int>::max()) {
     libc_errno = EOVERFLOW;
     return -1;
   }
 
-  return static_cast<int>(ret_val.value);
+  return static_cast<int>(ret_val.value());
 }
 
 } // namespace LIBC_NAMESPACE_DECL
diff --git a/libc/src/stdio/vsprintf.cpp b/libc/src/stdio/vsprintf.cpp
index 5a2fda6ef1a8c..dd03c71f114a8 100644
--- a/libc/src/stdio/vsprintf.cpp
+++ b/libc/src/stdio/vsprintf.cpp
@@ -32,17 +32,17 @@ LLVM_LIBC_FUNCTION(int, vsprintf,
   printf_core::Writer writer(wb);
 
   auto ret_val = printf_core::printf_main(&writer, format, args);
-  if (ret_val.has_error()) {
-    libc_errno = printf_core::internal_error_to_errno(ret_val.error);
+  if (!ret_val.has_value()) {
+    libc_errno = printf_core::internal_error_to_errno(ret_val.error());
     return -1;
   }
   wb.buff[wb.buff_cur] = '\0';
 
-  if (ret_val.value > cpp::numeric_limits<int>::max()) {
+  if (ret_val.value() > cpp::numeric_limits<int>::max()) {
     libc_errno = EOVERFLOW;
     return -1;
   }
-  return static_cast<int>(ret_val.value);
+  return static_cast<int>(ret_val.value());
 }
 
 } // namespace LIBC_NAMESPACE_DECL

>From f8cb7022e9175678da0966758b65c30733d2997b Mon Sep 17 00:00:00 2001
From: Marcell Leleszi <mleleszi at google.com>
Date: Fri, 17 Oct 2025 14:28:57 +0000
Subject: [PATCH 20/27] Fix tests

---
 libc/src/stdio/printf_core/core_structs.h      | 18 +++++++++---------
 libc/src/stdio/printf_core/vfprintf_internal.h | 14 +++++---------
 libc/test/src/stdio/CMakeLists.txt             |  6 +++++-
 libc/test/src/stdio/fprintf_test.cpp           | 16 +++++++++++-----
 libc/test/src/stdio/vfprintf_test.cpp          | 16 +++++++++++-----
 5 files changed, 41 insertions(+), 29 deletions(-)

diff --git a/libc/src/stdio/printf_core/core_structs.h b/libc/src/stdio/printf_core/core_structs.h
index 726b767778848..e088bddd4db3f 100644
--- a/libc/src/stdio/printf_core/core_structs.h
+++ b/libc/src/stdio/printf_core/core_structs.h
@@ -134,22 +134,24 @@ template <typename T> LIBC_INLINE constexpr TypeDesc type_desc_from_type() {
 
 // This is the value to be returned by conversions when no error has occurred.
 constexpr int WRITE_OK = 0;
-// These are the printf return values for when an error has occurred. They are
-// all negative, and should be distinct.
+// These are the error return values used by the printf engine when an
+// error has occurred. They are all large negative, distinct values starting
+// from -1000 to not overlap with system errors.
 constexpr int FILE_WRITE_ERROR = -1001;
 constexpr int FILE_STATUS_ERROR = -1002;
 constexpr int NULLPTR_WRITE_ERROR = -1003;
 constexpr int INT_CONVERSION_ERROR = -1004;
 constexpr int FIXED_POINT_CONVERSION_ERROR = -1005;
 constexpr int ALLOCATION_ERROR = -1006;
-constexpr int SHORT_WRITE_ERROR = -1007;
 
-LIBC_INLINE static int internal_error_to_errno(int internal_errno) {
-  if (internal_errno < 1001) {
-    return internal_errno;
+LIBC_INLINE static int internal_error_to_errno(int internal_error) {
+  // System error occured, return error as is.
+  if (internal_error < 1001) {
+    return internal_error;
   }
 
-  switch (-internal_errno) {
+  // Map internal error to errno.
+  switch (-internal_error) {
   case WRITE_OK:
     return 0;
   case FILE_WRITE_ERROR:
@@ -164,8 +166,6 @@ LIBC_INLINE static int internal_error_to_errno(int internal_errno) {
     return EINVAL;
   case ALLOCATION_ERROR:
     return ENOMEM;
-  case SHORT_WRITE_ERROR:
-    return EIO;
   default:
     LIBC_ASSERT(
         false &&
diff --git a/libc/src/stdio/printf_core/vfprintf_internal.h b/libc/src/stdio/printf_core/vfprintf_internal.h
index 3fabe04d921cc..6e29c6a6a3695 100644
--- a/libc/src/stdio/printf_core/vfprintf_internal.h
+++ b/libc/src/stdio/printf_core/vfprintf_internal.h
@@ -51,7 +51,7 @@ LIBC_INLINE void funlockfile(::FILE *f) { ::funlockfile(f); }
 
 LIBC_INLINE FileIOResult fwrite_unlocked(const void *ptr, size_t size,
                                          size_t nmemb, ::FILE *f) {
-  return {::fwrite_unlocked(ptr, size, nmemb, f), 0}; // todo err
+  return {::fwrite_unlocked(ptr, size, nmemb, f), errno};
 }
 #endif // LIBC_COPT_STDIO_USE_SYSTEM_FILE
 } // namespace internal
@@ -68,15 +68,11 @@ LIBC_INLINE int file_write_hook(cpp::string_view new_str, void *fp) {
   if (write_result.has_error())
     return -write_result.error;
 
-  // On short write no system error is returned, so return custom error.
-  if (write_result.value != new_str.size())
-    return SHORT_WRITE_ERROR;
-
-  // Return custom error in case error wasn't set on FileIOResult for some
-  // reason (like using LIBC_COPT_STDIO_USE_SYSTEM_FILE)
-  if (internal::ferror_unlocked(target_file)) {
+  // In case short write occured or error was not set on FileIOResult for some
+  // reason.
+  if (write_result.value != new_str.size() ||
+      internal::ferror_unlocked(target_file))
     return FILE_WRITE_ERROR;
-  }
 
   return WRITE_OK;
 }
diff --git a/libc/test/src/stdio/CMakeLists.txt b/libc/test/src/stdio/CMakeLists.txt
index b513007cdf7c6..abf67f4e97afc 100644
--- a/libc/test/src/stdio/CMakeLists.txt
+++ b/libc/test/src/stdio/CMakeLists.txt
@@ -169,8 +169,12 @@ if(LLVM_LIBC_FULL_BUILD)
        libc.src.stdio.ferror
        libc.src.stdio.fopen
        libc.src.stdio.fread
-       libc.src.stdio.fopencookie
   )
+  if(${LIBC_TARGET_OS} STREQUAL "linux")
+    list(APPEND fprintf_test_deps
+         libc.src.stdio.fopencookie
+    )
+  endif()
   # This is to be used for tests which write to libc's platform streams
   # under full build but write to system-lib's streams otherwise.
   set(hermetic_test_only HERMETIC_TEST_ONLY)
diff --git a/libc/test/src/stdio/fprintf_test.cpp b/libc/test/src/stdio/fprintf_test.cpp
index 9a26c44e7f12c..186e090fe9181 100644
--- a/libc/test/src/stdio/fprintf_test.cpp
+++ b/libc/test/src/stdio/fprintf_test.cpp
@@ -10,7 +10,6 @@
 #include "src/stdio/fclose.h"
 #include "src/stdio/ferror.h"
 #include "src/stdio/fopen.h"
-#include "src/stdio/fopencookie.h"
 #include "src/stdio/fread.h"
 #endif // LIBC_COPT_STDIO_USE_SYSTEM_FILE
 
@@ -21,18 +20,21 @@
 #include "test/UnitTest/ErrnoSetterMatcher.h"
 #include "test/UnitTest/Test.h"
 
+#if defined(LIBC_TARGET_OS_IS_LINUX) &&                                        \
+    !defined(LIBC_COPT_STDIO_USE_SYSTEM_FILE)
+#include "src/stdio/fopencookie.h"
+#endif
+
 namespace printf_test {
 #ifndef LIBC_COPT_STDIO_USE_SYSTEM_FILE
 using LIBC_NAMESPACE::fclose;
 using LIBC_NAMESPACE::ferror;
 using LIBC_NAMESPACE::fopen;
-using LIBC_NAMESPACE::fopencookie;
 using LIBC_NAMESPACE::fread;
 #else  // defined(LIBC_COPT_STDIO_USE_SYSTEM_FILE)
 using ::fclose;
 using ::ferror;
 using ::fopen;
-using ::fopencookie;
 using ::fread;
 #endif // LIBC_COPT_STDIO_USE_SYSTEM_FILE
 } // namespace printf_test
@@ -86,11 +88,13 @@ TEST(LlvmLibcFPrintfTest, WriteToFile) {
   written =
       LIBC_NAMESPACE::fprintf(file, "Writing to a read only file should fail.");
   EXPECT_LT(written, 0);
-  ASSERT_ERRNO_EQ(EIO);
+  ASSERT_ERRNO_EQ(EBADF);
 
   ASSERT_EQ(printf_test::fclose(file), 0);
 }
 
+#if !defined(LIBC_COPT_STDIO_USE_SYSTEM_FILE) &&                               \
+    defined(LIBC_TARGET_OS_IS_LINUX)
 TEST(LlvmLibcFPrintfTest, CharsWrittenOverflow) {
   struct NoopStream {};
   auto noop_write = [](void *, const char *, size_t size) -> ssize_t {
@@ -99,7 +103,7 @@ TEST(LlvmLibcFPrintfTest, CharsWrittenOverflow) {
 
   NoopStream stream;
   cookie_io_functions_t funcs = {nullptr, +noop_write, nullptr, nullptr};
-  ::FILE *file = printf_test::fopencookie(&stream, "w", funcs);
+  ::FILE *file = LIBC_NAMESPACE::fopencookie(&stream, "w", funcs);
   ASSERT_NE(file, nullptr);
 
   // Trigger an overflow in the return value of fprintf by writing more than
@@ -113,6 +117,8 @@ TEST(LlvmLibcFPrintfTest, CharsWrittenOverflow) {
 
   EXPECT_EQ(printf_test::fclose(file), 0);
 }
+#endif // #if !defined(LIBC_COPT_STDIO_USE_SYSTEM_FILE) &&
+       // defined(LIBC_TARGET_OS_IS_LINUX)
 
 #ifndef LIBC_COPT_PRINTF_NO_NULLPTR_CHECKS
 TEST(LlvmLibcFPrintfTest, NullPtrCheck) {
diff --git a/libc/test/src/stdio/vfprintf_test.cpp b/libc/test/src/stdio/vfprintf_test.cpp
index 952c55b0b0a85..8340bf90b30a8 100644
--- a/libc/test/src/stdio/vfprintf_test.cpp
+++ b/libc/test/src/stdio/vfprintf_test.cpp
@@ -14,7 +14,6 @@
 #include "src/stdio/fclose.h"
 #include "src/stdio/ferror.h"
 #include "src/stdio/fopen.h"
-#include "src/stdio/fopencookie.h"
 #include "src/stdio/fread.h"
 #endif // LIBC_COPT_STDIO_USE_SYSTEM_FILE
 
@@ -24,18 +23,21 @@
 #include "test/UnitTest/ErrnoSetterMatcher.h"
 #include "test/UnitTest/Test.h"
 
+#if defined(LIBC_TARGET_OS_IS_LINUX) &&                                        \
+    !defined(LIBC_COPT_STDIO_USE_SYSTEM_FILE)
+#include "src/stdio/fopencookie.h"
+#endif
+
 namespace printf_test {
 #ifndef LIBC_COPT_STDIO_USE_SYSTEM_FILE
 using LIBC_NAMESPACE::fclose;
 using LIBC_NAMESPACE::ferror;
 using LIBC_NAMESPACE::fopen;
-using LIBC_NAMESPACE::fopencookie;
 using LIBC_NAMESPACE::fread;
 #else  // defined(LIBC_COPT_STDIO_USE_SYSTEM_FILE)
 using ::fclose;
 using ::ferror;
 using ::fopen;
-using ::fopencookie;
 using ::fread;
 #endif // LIBC_COPT_STDIO_USE_SYSTEM_FILE
 } // namespace printf_test
@@ -97,11 +99,13 @@ TEST(LlvmLibcVFPrintfTest, WriteToFile) {
 
   written = call_vfprintf(file, "Writing to a read only file should fail.");
   EXPECT_LT(written, 0);
-  ASSERT_ERRNO_EQ(EIO);
+  ASSERT_ERRNO_EQ(EBADF);
 
   ASSERT_EQ(printf_test::fclose(file), 0);
 }
 
+#if !defined(LIBC_COPT_STDIO_USE_SYSTEM_FILE) &&                               \
+    defined(LIBC_TARGET_OS_IS_LINUX)
 TEST(LlvmLibcVFPrintfTest, CharsWrittenOverflow) {
   struct NoopStream {};
   auto noop_write = [](void *, const char *, size_t size) -> ssize_t {
@@ -110,7 +114,7 @@ TEST(LlvmLibcVFPrintfTest, CharsWrittenOverflow) {
 
   NoopStream stream;
   cookie_io_functions_t funcs = {nullptr, +noop_write, nullptr, nullptr};
-  ::FILE *file = printf_test::fopencookie(&stream, "w", funcs);
+  ::FILE *file = LIBC_NAMESPACE::fopencookie(&stream, "w", funcs);
   ASSERT_NE(file, nullptr);
 
   // Trigger an overflow in the return value of vfprintf by writing more than
@@ -124,3 +128,5 @@ TEST(LlvmLibcVFPrintfTest, CharsWrittenOverflow) {
 
   EXPECT_EQ(printf_test::fclose(file), 0);
 }
+#endif // !defined(LIBC_COPT_STDIO_USE_SYSTEM_FILE) &&
+       // defined(LIBC_TARGET_OS_IS_LINUX)

>From 0e8b25c861a63a9e6a359c49c404ec54757494da Mon Sep 17 00:00:00 2001
From: Marcell Leleszi <mleleszi at google.com>
Date: Fri, 17 Oct 2025 17:28:58 +0000
Subject: [PATCH 21/27] Fix baremetal impl

---
 .../llvm-libc-macros/generic-error-number-macros.h     |  1 +
 libc/src/stdio/CMakeLists.txt                          | 10 ++++++++++
 libc/src/stdio/asprintf.cpp                            |  2 ++
 libc/src/stdio/baremetal/CMakeLists.txt                |  2 ++
 libc/src/stdio/baremetal/printf.cpp                    |  5 +++--
 libc/src/stdio/baremetal/vprintf.cpp                   |  6 ++++--
 libc/src/stdio/printf_core/CMakeLists.txt              |  1 +
 libc/src/stdio/printf_core/core_structs.h              |  1 +
 libc/src/stdio/snprintf.cpp                            |  2 ++
 libc/src/stdio/sprintf.cpp                             |  2 ++
 libc/src/stdio/vasprintf.cpp                           |  2 ++
 libc/src/stdio/vsnprintf.cpp                           |  2 ++
 libc/src/stdio/vsprintf.cpp                            |  2 ++
 libc/src/time/strftime_core/strftime_main.h            |  2 +-
 14 files changed, 35 insertions(+), 5 deletions(-)

diff --git a/libc/include/llvm-libc-macros/generic-error-number-macros.h b/libc/include/llvm-libc-macros/generic-error-number-macros.h
index 199a86217b347..5dd6eb2f97051 100644
--- a/libc/include/llvm-libc-macros/generic-error-number-macros.h
+++ b/libc/include/llvm-libc-macros/generic-error-number-macros.h
@@ -43,6 +43,7 @@
 #define EPIPE 32
 #define EDOM 33
 #define ERANGE 34
+#define EOVERFLOW 75
 #define EILSEQ 84
 
 #endif // LLVM_LIBC_MACROS_GENERIC_ERROR_NUMBER_MACROS_H
diff --git a/libc/src/stdio/CMakeLists.txt b/libc/src/stdio/CMakeLists.txt
index d7af6c47efbc8..72fda8d343815 100644
--- a/libc/src/stdio/CMakeLists.txt
+++ b/libc/src/stdio/CMakeLists.txt
@@ -126,6 +126,8 @@ add_entrypoint_object(
     libc.src.stdio.printf_core.printf_main
     libc.src.stdio.printf_core.writer
     libc.src.stdio.printf_core.core_structs
+    libc.src.__support.libc_errno
+    libc.hdr.errno_macros
 )
 
 add_entrypoint_object(
@@ -138,6 +140,8 @@ add_entrypoint_object(
     libc.src.stdio.printf_core.printf_main
     libc.src.stdio.printf_core.writer
     libc.src.stdio.printf_core.core_structs
+    libc.src.__support.libc_errno
+    libc.hdr.errno_macros
 )
 
 add_entrypoint_object(
@@ -149,6 +153,8 @@ add_entrypoint_object(
   DEPENDS
     libc.src.stdio.printf_core.vasprintf_internal
     libc.src.stdio.printf_core.core_structs
+    libc.src.__support.libc_errno
+    libc.hdr.errno_macros
 )
 
 add_entrypoint_object(
@@ -173,6 +179,8 @@ add_entrypoint_object(
     libc.src.stdio.printf_core.printf_main
     libc.src.stdio.printf_core.writer
     libc.src.stdio.printf_core.core_structs
+    libc.src.__support.libc_errno
+    libc.hdr.errno_macros
 )
 
 add_entrypoint_object(
@@ -184,6 +192,8 @@ add_entrypoint_object(
   DEPENDS
     libc.src.stdio.printf_core.vasprintf_internal
     libc.src.stdio.printf_core.core_structs
+    libc.src.__support.libc_errno
+    libc.hdr.errno_macros
 )
 
 add_subdirectory(printf_core)
diff --git a/libc/src/stdio/asprintf.cpp b/libc/src/stdio/asprintf.cpp
index 3187ea40490a4..bb4077fa89542 100644
--- a/libc/src/stdio/asprintf.cpp
+++ b/libc/src/stdio/asprintf.cpp
@@ -7,7 +7,9 @@
 //===----------------------------------------------------------------------===//
 
 #include "src/stdio/asprintf.h"
+#include "hdr/errno_macros.h"
 #include "src/__support/arg_list.h"
+#include "src/__support/libc_errno.h"
 #include "src/__support/macros/config.h"
 #include "src/stdio/printf_core/vasprintf_internal.h"
 
diff --git a/libc/src/stdio/baremetal/CMakeLists.txt b/libc/src/stdio/baremetal/CMakeLists.txt
index 548938f885c94..e4de151b109ed 100644
--- a/libc/src/stdio/baremetal/CMakeLists.txt
+++ b/libc/src/stdio/baremetal/CMakeLists.txt
@@ -31,6 +31,7 @@ add_entrypoint_object(
     libc.src.stdio.printf_core.writer
     libc.src.__support.arg_list
     libc.src.__support.OSUtil.osutil
+    libc.src.__support.libc_errno
 )
 
 add_entrypoint_object(
@@ -89,6 +90,7 @@ add_entrypoint_object(
     libc.src.stdio.printf_core.writer
     libc.src.__support.arg_list
     libc.src.__support.OSUtil.osutil
+    libc.src.__support.libc_errno
 )
 
 add_entrypoint_object(
diff --git a/libc/src/stdio/baremetal/printf.cpp b/libc/src/stdio/baremetal/printf.cpp
index 53a7c95219664..7ce2bb5e256cb 100644
--- a/libc/src/stdio/baremetal/printf.cpp
+++ b/libc/src/stdio/baremetal/printf.cpp
@@ -9,6 +9,7 @@
 #include "src/stdio/printf.h"
 #include "src/__support/OSUtil/io.h"
 #include "src/__support/arg_list.h"
+#include "src/__support/libc_errno.h"
 #include "src/__support/macros/config.h"
 #include "src/stdio/printf_core/core_structs.h"
 #include "src/stdio/printf_core/printf_main.h"
@@ -44,13 +45,13 @@ LLVM_LIBC_FUNCTION(int, printf, (const char *__restrict format, ...)) {
 
   auto retval = printf_core::printf_main(&writer, format, args);
   if (!retval.has_value()) {
-    libc_errno = retval.error(); // TODO map
+    libc_errno = printf_core::internal_error_to_errno(retval.error());
     return -1;
   }
 
   int flushval = wb.overflow_write("");
   if (flushval != printf_core::WRITE_OK) {
-    libc_errno = -flushval; // TODO map
+    libc_errno = printf_core::internal_error_to_errno(-flushval);
     return -1;
   }
 
diff --git a/libc/src/stdio/baremetal/vprintf.cpp b/libc/src/stdio/baremetal/vprintf.cpp
index 2a058885a0d9b..e60260c49138d 100644
--- a/libc/src/stdio/baremetal/vprintf.cpp
+++ b/libc/src/stdio/baremetal/vprintf.cpp
@@ -7,8 +7,10 @@
 //===----------------------------------------------------------------------===//
 
 #include "src/stdio/vprintf.h"
+#include "hdr/errno_macros.h"
 #include "src/__support/OSUtil/io.h"
 #include "src/__support/arg_list.h"
+#include "src/__support/libc_errno.h"
 #include "src/__support/macros/config.h"
 #include "src/stdio/printf_core/core_structs.h"
 #include "src/stdio/printf_core/printf_main.h"
@@ -42,13 +44,13 @@ LLVM_LIBC_FUNCTION(int, vprintf,
 
   auto retval = printf_core::printf_main(&writer, format, args);
   if (!retval.has_value()) {
-    libc_errno = retval.error(); // TODO map
+    libc_errno = printf_core::internal_error_to_errno(retval.error());
     return -1;
   }
 
   int flushval = wb.overflow_write("");
   if (flushval != printf_core::WRITE_OK) {
-    libc_errno = -flushval; // TODO map
+    libc_errno = printf_core::internal_error_to_errno(-flushval);
     return -1;
   }
 
diff --git a/libc/src/stdio/printf_core/CMakeLists.txt b/libc/src/stdio/printf_core/CMakeLists.txt
index 95c32f4ac16b7..24435c3a03d63 100644
--- a/libc/src/stdio/printf_core/CMakeLists.txt
+++ b/libc/src/stdio/printf_core/CMakeLists.txt
@@ -48,6 +48,7 @@ add_header_library(
     libc.src.__support.CPP.string_view
     libc.src.__support.FPUtil.fp_bits
     libc.hdr.types.FILE
+    libc.hdr.errno_macros
     libc.src.__support.File.file
 )
 
diff --git a/libc/src/stdio/printf_core/core_structs.h b/libc/src/stdio/printf_core/core_structs.h
index e088bddd4db3f..37c11e28ce7ea 100644
--- a/libc/src/stdio/printf_core/core_structs.h
+++ b/libc/src/stdio/printf_core/core_structs.h
@@ -11,6 +11,7 @@
 
 #include "src/__support/macros/config.h"
 
+#include "hdr/errno_macros.h"
 #include "hdr/types/FILE.h"
 #include "src/__support/CPP/string_view.h"
 #include "src/__support/CPP/type_traits.h"
diff --git a/libc/src/stdio/snprintf.cpp b/libc/src/stdio/snprintf.cpp
index d63f5c8f0957e..10b46ddfd6249 100644
--- a/libc/src/stdio/snprintf.cpp
+++ b/libc/src/stdio/snprintf.cpp
@@ -8,7 +8,9 @@
 
 #include "src/stdio/snprintf.h"
 
+#include "hdr/errno_macros.h"
 #include "src/__support/arg_list.h"
+#include "src/__support/libc_errno.h"
 #include "src/__support/macros/config.h"
 #include "src/stdio/printf_core/core_structs.h"
 #include "src/stdio/printf_core/printf_main.h"
diff --git a/libc/src/stdio/sprintf.cpp b/libc/src/stdio/sprintf.cpp
index b9be409500086..53e951bea6aaa 100644
--- a/libc/src/stdio/sprintf.cpp
+++ b/libc/src/stdio/sprintf.cpp
@@ -8,8 +8,10 @@
 
 #include "src/stdio/sprintf.h"
 
+#include "hdr/errno_macros.h"
 #include "src/__support/CPP/limits.h"
 #include "src/__support/arg_list.h"
+#include "src/__support/libc_errno.h"
 #include "src/__support/macros/config.h"
 #include "src/stdio/printf_core/core_structs.h"
 #include "src/stdio/printf_core/printf_main.h"
diff --git a/libc/src/stdio/vasprintf.cpp b/libc/src/stdio/vasprintf.cpp
index 4e3e601e12b94..1060c39d416c1 100644
--- a/libc/src/stdio/vasprintf.cpp
+++ b/libc/src/stdio/vasprintf.cpp
@@ -7,7 +7,9 @@
 //===----------------------------------------------------------------------===//
 
 #include "src/stdio/vasprintf.h"
+#include "hdr/errno_macros.h"
 #include "src/__support/arg_list.h"
+#include "src/__support/libc_errno.h"
 #include "src/stdio/printf_core/core_structs.h"
 #include "src/stdio/printf_core/vasprintf_internal.h"
 
diff --git a/libc/src/stdio/vsnprintf.cpp b/libc/src/stdio/vsnprintf.cpp
index d822a1090f647..000ec5d5cfa5b 100644
--- a/libc/src/stdio/vsnprintf.cpp
+++ b/libc/src/stdio/vsnprintf.cpp
@@ -8,7 +8,9 @@
 
 #include "src/stdio/vsnprintf.h"
 
+#include "hdr/errno_macros.h"
 #include "src/__support/arg_list.h"
+#include "src/__support/libc_errno.h"
 #include "src/__support/macros/config.h"
 #include "src/stdio/printf_core/core_structs.h"
 #include "src/stdio/printf_core/printf_main.h"
diff --git a/libc/src/stdio/vsprintf.cpp b/libc/src/stdio/vsprintf.cpp
index dd03c71f114a8..5f8c79418e83d 100644
--- a/libc/src/stdio/vsprintf.cpp
+++ b/libc/src/stdio/vsprintf.cpp
@@ -8,8 +8,10 @@
 
 #include "src/stdio/vsprintf.h"
 
+#include "hdr/errno_macros.h"
 #include "src/__support/CPP/limits.h"
 #include "src/__support/arg_list.h"
+#include "src/__support/libc_errno.h"
 #include "src/__support/macros/config.h"
 #include "src/stdio/printf_core/core_structs.h"
 #include "src/stdio/printf_core/printf_main.h"
diff --git a/libc/src/time/strftime_core/strftime_main.h b/libc/src/time/strftime_core/strftime_main.h
index 8e369c2710ddb..2b136d83234cd 100644
--- a/libc/src/time/strftime_core/strftime_main.h
+++ b/libc/src/time/strftime_core/strftime_main.h
@@ -36,7 +36,7 @@ int strftime_main(printf_core::Writer<write_mode> *writer,
       return result;
   }
 
-  // TODO: return result struct like printf, so that size_t can be returned
+  // TODO: Use ErrorOr<size_t>
   return static_cast<int>(writer->get_chars_written());
 }
 

>From 756cb37f38a698ca66e17a51546108a8081b054a Mon Sep 17 00:00:00 2001
From: Marcell Leleszi <mleleszi at google.com>
Date: Sat, 18 Oct 2025 06:26:12 +0000
Subject: [PATCH 22/27] Clean up tests, dependencies

---
 libc/src/stdio/printf_core/CMakeLists.txt     |  2 --
 libc/src/stdio/printf_core/core_structs.h     |  2 --
 libc/src/stdio/printf_core/fixed_converter.h  |  1 -
 .../src/stdio/printf_core/vfprintf_internal.h |  1 -
 libc/test/src/stdio/CMakeLists.txt            |  5 ---
 libc/test/src/stdio/fprintf_test.cpp          | 32 -------------------
 libc/test/src/stdio/snprintf_test.cpp         | 15 +++++++++
 libc/test/src/stdio/vfprintf_test.cpp         | 32 -------------------
 8 files changed, 15 insertions(+), 75 deletions(-)

diff --git a/libc/src/stdio/printf_core/CMakeLists.txt b/libc/src/stdio/printf_core/CMakeLists.txt
index 24435c3a03d63..dbb36ac96bc4c 100644
--- a/libc/src/stdio/printf_core/CMakeLists.txt
+++ b/libc/src/stdio/printf_core/CMakeLists.txt
@@ -47,9 +47,7 @@ add_header_library(
     libc.include.inttypes
     libc.src.__support.CPP.string_view
     libc.src.__support.FPUtil.fp_bits
-    libc.hdr.types.FILE
     libc.hdr.errno_macros
-    libc.src.__support.File.file
 )
 
 add_header_library(
diff --git a/libc/src/stdio/printf_core/core_structs.h b/libc/src/stdio/printf_core/core_structs.h
index 37c11e28ce7ea..7b205ee890c98 100644
--- a/libc/src/stdio/printf_core/core_structs.h
+++ b/libc/src/stdio/printf_core/core_structs.h
@@ -12,11 +12,9 @@
 #include "src/__support/macros/config.h"
 
 #include "hdr/errno_macros.h"
-#include "hdr/types/FILE.h"
 #include "src/__support/CPP/string_view.h"
 #include "src/__support/CPP/type_traits.h"
 #include "src/__support/FPUtil/FPBits.h"
-#include "src/__support/File/file.h"
 #include "src/stdio/printf_core/printf_config.h"
 
 #include <inttypes.h>
diff --git a/libc/src/stdio/printf_core/fixed_converter.h b/libc/src/stdio/printf_core/fixed_converter.h
index 069447f27d918..e8a3314967562 100644
--- a/libc/src/stdio/printf_core/fixed_converter.h
+++ b/libc/src/stdio/printf_core/fixed_converter.h
@@ -9,7 +9,6 @@
 #ifndef LLVM_LIBC_SRC_STDIO_PRINTF_CORE_FIXED_CONVERTER_H
 #define LLVM_LIBC_SRC_STDIO_PRINTF_CORE_FIXED_CONVERTER_H
 
-#include "hdr/errno_macros.h"
 #include "include/llvm-libc-macros/stdfix-macros.h"
 #include "src/__support/CPP/string_view.h"
 #include "src/__support/ctype_utils.h"
diff --git a/libc/src/stdio/printf_core/vfprintf_internal.h b/libc/src/stdio/printf_core/vfprintf_internal.h
index 6e29c6a6a3695..4e60248810f27 100644
--- a/libc/src/stdio/printf_core/vfprintf_internal.h
+++ b/libc/src/stdio/printf_core/vfprintf_internal.h
@@ -9,7 +9,6 @@
 #ifndef LLVM_LIBC_SRC_STDIO_PRINTF_CORE_VFPRINTF_INTERNAL_H
 #define LLVM_LIBC_SRC_STDIO_PRINTF_CORE_VFPRINTF_INTERNAL_H
 
-#include "hdr/errno_macros.h"
 #include "src/__support/File/file.h"
 #include "src/__support/arg_list.h"
 #include "src/__support/error_or.h"
diff --git a/libc/test/src/stdio/CMakeLists.txt b/libc/test/src/stdio/CMakeLists.txt
index abf67f4e97afc..d71f1dff11943 100644
--- a/libc/test/src/stdio/CMakeLists.txt
+++ b/libc/test/src/stdio/CMakeLists.txt
@@ -170,11 +170,6 @@ if(LLVM_LIBC_FULL_BUILD)
        libc.src.stdio.fopen
        libc.src.stdio.fread
   )
-  if(${LIBC_TARGET_OS} STREQUAL "linux")
-    list(APPEND fprintf_test_deps
-         libc.src.stdio.fopencookie
-    )
-  endif()
   # This is to be used for tests which write to libc's platform streams
   # under full build but write to system-lib's streams otherwise.
   set(hermetic_test_only HERMETIC_TEST_ONLY)
diff --git a/libc/test/src/stdio/fprintf_test.cpp b/libc/test/src/stdio/fprintf_test.cpp
index 186e090fe9181..1d081f00a31a4 100644
--- a/libc/test/src/stdio/fprintf_test.cpp
+++ b/libc/test/src/stdio/fprintf_test.cpp
@@ -20,11 +20,6 @@
 #include "test/UnitTest/ErrnoSetterMatcher.h"
 #include "test/UnitTest/Test.h"
 
-#if defined(LIBC_TARGET_OS_IS_LINUX) &&                                        \
-    !defined(LIBC_COPT_STDIO_USE_SYSTEM_FILE)
-#include "src/stdio/fopencookie.h"
-#endif
-
 namespace printf_test {
 #ifndef LIBC_COPT_STDIO_USE_SYSTEM_FILE
 using LIBC_NAMESPACE::fclose;
@@ -93,33 +88,6 @@ TEST(LlvmLibcFPrintfTest, WriteToFile) {
   ASSERT_EQ(printf_test::fclose(file), 0);
 }
 
-#if !defined(LIBC_COPT_STDIO_USE_SYSTEM_FILE) &&                               \
-    defined(LIBC_TARGET_OS_IS_LINUX)
-TEST(LlvmLibcFPrintfTest, CharsWrittenOverflow) {
-  struct NoopStream {};
-  auto noop_write = [](void *, const char *, size_t size) -> ssize_t {
-    return size;
-  };
-
-  NoopStream stream;
-  cookie_io_functions_t funcs = {nullptr, +noop_write, nullptr, nullptr};
-  ::FILE *file = LIBC_NAMESPACE::fopencookie(&stream, "w", funcs);
-  ASSERT_NE(file, nullptr);
-
-  // Trigger an overflow in the return value of fprintf by writing more than
-  // INT_MAX bytes. We do this by printing a string with precision INT_MAX, and
-  // then one more character.
-  int max_int = LIBC_NAMESPACE::cpp::numeric_limits<int>::max();
-  int result = LIBC_NAMESPACE::fprintf(file, "%*sA", max_int, "");
-
-  EXPECT_LT(result, 0);
-  ASSERT_ERRNO_EQ(EOVERFLOW);
-
-  EXPECT_EQ(printf_test::fclose(file), 0);
-}
-#endif // #if !defined(LIBC_COPT_STDIO_USE_SYSTEM_FILE) &&
-       // defined(LIBC_TARGET_OS_IS_LINUX)
-
 #ifndef LIBC_COPT_PRINTF_NO_NULLPTR_CHECKS
 TEST(LlvmLibcFPrintfTest, NullPtrCheck) {
   const char *FILENAME = APPEND_LIBC_TEST("fprintf_nullptr.test");
diff --git a/libc/test/src/stdio/snprintf_test.cpp b/libc/test/src/stdio/snprintf_test.cpp
index baaa664cdc9ee..1062f952d7429 100644
--- a/libc/test/src/stdio/snprintf_test.cpp
+++ b/libc/test/src/stdio/snprintf_test.cpp
@@ -8,8 +8,12 @@
 
 #include "src/stdio/snprintf.h"
 
+#include "test/UnitTest/ErrnoCheckingTest.h"
+#include "test/UnitTest/ErrnoSetterMatcher.h"
 #include "test/UnitTest/Test.h"
 
+using LlvmLibcSNPrintfTest = LIBC_NAMESPACE::testing::ErrnoCheckingTest;
+
 // The sprintf test cases cover testing the shared printf functionality, so
 // these tests will focus on snprintf exclusive features.
 
@@ -59,3 +63,14 @@ TEST(LlvmLibcSNPrintfTest, NoCutOff) {
   EXPECT_EQ(written, 10);
   ASSERT_STREQ(buff, "1234567890");
 }
+
+TEST(LlvmLibcSNPrintfTest, CharsWrittenOverflow) {
+  char buff[0];
+
+  // Trigger an overflow in the return value of snprintf by writing more than
+  // INT_MAX bytes.
+  int int_max = LIBC_NAMESPACE::cpp::numeric_limits<int>::max();
+  int written = LIBC_NAMESPACE::snprintf(buff, 0, "%*stest", int_max, "");
+  EXPECT_LT(written, 0);
+  ASSERT_ERRNO_EQ(EOVERFLOW);
+}
diff --git a/libc/test/src/stdio/vfprintf_test.cpp b/libc/test/src/stdio/vfprintf_test.cpp
index 8340bf90b30a8..9b5f09db8fd41 100644
--- a/libc/test/src/stdio/vfprintf_test.cpp
+++ b/libc/test/src/stdio/vfprintf_test.cpp
@@ -23,11 +23,6 @@
 #include "test/UnitTest/ErrnoSetterMatcher.h"
 #include "test/UnitTest/Test.h"
 
-#if defined(LIBC_TARGET_OS_IS_LINUX) &&                                        \
-    !defined(LIBC_COPT_STDIO_USE_SYSTEM_FILE)
-#include "src/stdio/fopencookie.h"
-#endif
-
 namespace printf_test {
 #ifndef LIBC_COPT_STDIO_USE_SYSTEM_FILE
 using LIBC_NAMESPACE::fclose;
@@ -103,30 +98,3 @@ TEST(LlvmLibcVFPrintfTest, WriteToFile) {
 
   ASSERT_EQ(printf_test::fclose(file), 0);
 }
-
-#if !defined(LIBC_COPT_STDIO_USE_SYSTEM_FILE) &&                               \
-    defined(LIBC_TARGET_OS_IS_LINUX)
-TEST(LlvmLibcVFPrintfTest, CharsWrittenOverflow) {
-  struct NoopStream {};
-  auto noop_write = [](void *, const char *, size_t size) -> ssize_t {
-    return size;
-  };
-
-  NoopStream stream;
-  cookie_io_functions_t funcs = {nullptr, +noop_write, nullptr, nullptr};
-  ::FILE *file = LIBC_NAMESPACE::fopencookie(&stream, "w", funcs);
-  ASSERT_NE(file, nullptr);
-
-  // Trigger an overflow in the return value of vfprintf by writing more than
-  // INT_MAX bytes. We do this by printing a string with precision INT_MAX, and
-  // then one more character.
-  int max_int = LIBC_NAMESPACE::cpp::numeric_limits<int>::max();
-  int result = call_vfprintf(file, "%*sA", max_int, "");
-
-  EXPECT_LT(result, 0);
-  ASSERT_ERRNO_EQ(EOVERFLOW);
-
-  EXPECT_EQ(printf_test::fclose(file), 0);
-}
-#endif // !defined(LIBC_COPT_STDIO_USE_SYSTEM_FILE) &&
-       // defined(LIBC_TARGET_OS_IS_LINUX)

>From 463e929121cc07557cc83b9a38dac1a2bee82b5d Mon Sep 17 00:00:00 2001
From: Marcell Leleszi <mleleszi at google.com>
Date: Thu, 23 Oct 2025 08:45:46 +0000
Subject: [PATCH 23/27] Add platfrom specific error converter implementations

---
 .../generic-error-number-macros.h             |  1 -
 libc/src/stdio/CMakeLists.txt                 |  6 +++
 libc/src/stdio/asprintf.cpp                   |  4 +-
 libc/src/stdio/baremetal/CMakeLists.txt       |  2 +
 libc/src/stdio/baremetal/printf.cpp           |  4 +-
 libc/src/stdio/baremetal/vprintf.cpp          |  4 +-
 libc/src/stdio/generic/CMakeLists.txt         |  1 +
 libc/src/stdio/generic/fprintf.cpp            |  4 +-
 libc/src/stdio/generic/printf.cpp             |  4 +-
 libc/src/stdio/generic/vfprintf.cpp           |  4 +-
 libc/src/stdio/generic/vprintf.cpp            |  4 +-
 libc/src/stdio/printf_core/CMakeLists.txt     | 20 +++++++
 .../printf_core/baremetal/CMakeLists.txt      |  8 +++
 .../printf_core/baremetal/error_converter.h   | 49 +++++++++++++++++
 libc/src/stdio/printf_core/core_structs.h     | 31 +----------
 libc/src/stdio/printf_core/error_converter.h  | 22 ++++++++
 .../stdio/printf_core/linux/CMakeLists.txt    |  8 +++
 .../stdio/printf_core/linux/error_converter.h | 54 +++++++++++++++++++
 libc/src/stdio/snprintf.cpp                   |  4 +-
 libc/src/stdio/sprintf.cpp                    |  4 +-
 libc/src/stdio/vasprintf.cpp                  |  4 +-
 libc/src/stdio/vsnprintf.cpp                  |  4 +-
 libc/src/stdio/vsprintf.cpp                   |  4 +-
 libc/src/stdlib/CMakeLists.txt                |  3 ++
 libc/src/stdlib/strfromd.cpp                  |  4 +-
 libc/src/stdlib/strfromf.cpp                  |  4 +-
 libc/src/stdlib/strfroml.cpp                  |  4 +-
 27 files changed, 219 insertions(+), 46 deletions(-)
 create mode 100644 libc/src/stdio/printf_core/baremetal/CMakeLists.txt
 create mode 100644 libc/src/stdio/printf_core/baremetal/error_converter.h
 create mode 100644 libc/src/stdio/printf_core/error_converter.h
 create mode 100644 libc/src/stdio/printf_core/linux/CMakeLists.txt
 create mode 100644 libc/src/stdio/printf_core/linux/error_converter.h

diff --git a/libc/include/llvm-libc-macros/generic-error-number-macros.h b/libc/include/llvm-libc-macros/generic-error-number-macros.h
index 5dd6eb2f97051..199a86217b347 100644
--- a/libc/include/llvm-libc-macros/generic-error-number-macros.h
+++ b/libc/include/llvm-libc-macros/generic-error-number-macros.h
@@ -43,7 +43,6 @@
 #define EPIPE 32
 #define EDOM 33
 #define ERANGE 34
-#define EOVERFLOW 75
 #define EILSEQ 84
 
 #endif // LLVM_LIBC_MACROS_GENERIC_ERROR_NUMBER_MACROS_H
diff --git a/libc/src/stdio/CMakeLists.txt b/libc/src/stdio/CMakeLists.txt
index 72fda8d343815..67da5bec258a0 100644
--- a/libc/src/stdio/CMakeLists.txt
+++ b/libc/src/stdio/CMakeLists.txt
@@ -126,6 +126,7 @@ add_entrypoint_object(
     libc.src.stdio.printf_core.printf_main
     libc.src.stdio.printf_core.writer
     libc.src.stdio.printf_core.core_structs
+    libc.src.stdio.printf_core.error_converter
     libc.src.__support.libc_errno
     libc.hdr.errno_macros
 )
@@ -140,6 +141,7 @@ add_entrypoint_object(
     libc.src.stdio.printf_core.printf_main
     libc.src.stdio.printf_core.writer
     libc.src.stdio.printf_core.core_structs
+    libc.src.stdio.printf_core.error_converter
     libc.src.__support.libc_errno
     libc.hdr.errno_macros
 )
@@ -153,6 +155,7 @@ add_entrypoint_object(
   DEPENDS
     libc.src.stdio.printf_core.vasprintf_internal
     libc.src.stdio.printf_core.core_structs
+    libc.src.stdio.printf_core.error_converter
     libc.src.__support.libc_errno
     libc.hdr.errno_macros
 )
@@ -167,6 +170,7 @@ add_entrypoint_object(
     libc.src.stdio.printf_core.printf_main
     libc.src.stdio.printf_core.writer
     libc.src.stdio.printf_core.core_structs
+    libc.src.stdio.printf_core.error_converter
 )
 
 add_entrypoint_object(
@@ -179,6 +183,7 @@ add_entrypoint_object(
     libc.src.stdio.printf_core.printf_main
     libc.src.stdio.printf_core.writer
     libc.src.stdio.printf_core.core_structs
+    libc.src.stdio.printf_core.error_converter
     libc.src.__support.libc_errno
     libc.hdr.errno_macros
 )
@@ -192,6 +197,7 @@ add_entrypoint_object(
   DEPENDS
     libc.src.stdio.printf_core.vasprintf_internal
     libc.src.stdio.printf_core.core_structs
+    libc.src.stdio.printf_core.error_converter
     libc.src.__support.libc_errno
     libc.hdr.errno_macros
 )
diff --git a/libc/src/stdio/asprintf.cpp b/libc/src/stdio/asprintf.cpp
index bb4077fa89542..a4852b75e4124 100644
--- a/libc/src/stdio/asprintf.cpp
+++ b/libc/src/stdio/asprintf.cpp
@@ -11,6 +11,7 @@
 #include "src/__support/arg_list.h"
 #include "src/__support/libc_errno.h"
 #include "src/__support/macros/config.h"
+#include "src/stdio/printf_core/error_converter.h"
 #include "src/stdio/printf_core/vasprintf_internal.h"
 
 namespace LIBC_NAMESPACE_DECL {
@@ -30,7 +31,8 @@ LLVM_LIBC_FUNCTION(int, asprintf,
     return -1;
   }
   if (ret_val.value() > cpp::numeric_limits<int>::max()) {
-    libc_errno = EOVERFLOW;
+    libc_errno =
+        printf_core::internal_error_to_errno(-printf_core::OVERFLOW_ERROR);
     return -1;
   }
 
diff --git a/libc/src/stdio/baremetal/CMakeLists.txt b/libc/src/stdio/baremetal/CMakeLists.txt
index e4de151b109ed..af17635b9156a 100644
--- a/libc/src/stdio/baremetal/CMakeLists.txt
+++ b/libc/src/stdio/baremetal/CMakeLists.txt
@@ -29,6 +29,7 @@ add_entrypoint_object(
   DEPENDS
     libc.src.stdio.printf_core.printf_main
     libc.src.stdio.printf_core.writer
+    libc.src.stdio.printf_core.error_converter
     libc.src.__support.arg_list
     libc.src.__support.OSUtil.osutil
     libc.src.__support.libc_errno
@@ -88,6 +89,7 @@ add_entrypoint_object(
   DEPENDS
     libc.src.stdio.printf_core.printf_main
     libc.src.stdio.printf_core.writer
+    libc.src.stdio.printf_core.error_converter
     libc.src.__support.arg_list
     libc.src.__support.OSUtil.osutil
     libc.src.__support.libc_errno
diff --git a/libc/src/stdio/baremetal/printf.cpp b/libc/src/stdio/baremetal/printf.cpp
index 7ce2bb5e256cb..74748a85ce614 100644
--- a/libc/src/stdio/baremetal/printf.cpp
+++ b/libc/src/stdio/baremetal/printf.cpp
@@ -12,6 +12,7 @@
 #include "src/__support/libc_errno.h"
 #include "src/__support/macros/config.h"
 #include "src/stdio/printf_core/core_structs.h"
+#include "src/stdio/printf_core/error_converter.h"
 #include "src/stdio/printf_core/printf_main.h"
 #include "src/stdio/printf_core/writer.h"
 
@@ -56,7 +57,8 @@ LLVM_LIBC_FUNCTION(int, printf, (const char *__restrict format, ...)) {
   }
 
   if (retval.value() > cpp::numeric_limits<int>::max()) {
-    libc_errno = EOVERFLOW;
+    libc_errno =
+        printf_core::internal_error_to_errno(-printf_core::OVERFLOW_ERROR);
     return -1;
   }
 
diff --git a/libc/src/stdio/baremetal/vprintf.cpp b/libc/src/stdio/baremetal/vprintf.cpp
index e60260c49138d..19254d956a916 100644
--- a/libc/src/stdio/baremetal/vprintf.cpp
+++ b/libc/src/stdio/baremetal/vprintf.cpp
@@ -13,6 +13,7 @@
 #include "src/__support/libc_errno.h"
 #include "src/__support/macros/config.h"
 #include "src/stdio/printf_core/core_structs.h"
+#include "src/stdio/printf_core/error_converter.h"
 #include "src/stdio/printf_core/printf_main.h"
 #include "src/stdio/printf_core/writer.h"
 
@@ -55,7 +56,8 @@ LLVM_LIBC_FUNCTION(int, vprintf,
   }
 
   if (retval.value() > cpp::numeric_limits<int>::max()) {
-    libc_errno = EOVERFLOW;
+    libc_errno =
+        printf_core::internal_error_to_errno(-printf_core::OVERFLOW_ERROR);
     return -1;
   }
 
diff --git a/libc/src/stdio/generic/CMakeLists.txt b/libc/src/stdio/generic/CMakeLists.txt
index 77ff3b76a4d1d..34ab478777e5e 100644
--- a/libc/src/stdio/generic/CMakeLists.txt
+++ b/libc/src/stdio/generic/CMakeLists.txt
@@ -395,6 +395,7 @@ list(APPEND fprintf_deps
       libc.src.__support.arg_list
       libc.src.stdio.printf_core.vfprintf_internal
       libc.src.stdio.printf_core.core_structs
+      libc.src.stdio.printf_core.error_converter
 )
 
 if(LLVM_LIBC_FULL_BUILD)
diff --git a/libc/src/stdio/generic/fprintf.cpp b/libc/src/stdio/generic/fprintf.cpp
index 8251a098a9705..9b83986fb496f 100644
--- a/libc/src/stdio/generic/fprintf.cpp
+++ b/libc/src/stdio/generic/fprintf.cpp
@@ -11,6 +11,7 @@
 #include "src/__support/File/file.h"
 #include "src/__support/arg_list.h"
 #include "src/__support/macros/config.h"
+#include "src/stdio/printf_core/error_converter.h"
 #include "src/stdio/printf_core/vfprintf_internal.h"
 
 #include "hdr/types/FILE.h"
@@ -33,7 +34,8 @@ LLVM_LIBC_FUNCTION(int, fprintf,
     return -1;
   }
   if (ret_val.value() > cpp::numeric_limits<int>::max()) {
-    libc_errno = EOVERFLOW;
+    libc_errno =
+        printf_core::internal_error_to_errno(-printf_core::OVERFLOW_ERROR);
     return -1;
   }
 
diff --git a/libc/src/stdio/generic/printf.cpp b/libc/src/stdio/generic/printf.cpp
index 2ff1b35c02c07..2a40f59011a40 100644
--- a/libc/src/stdio/generic/printf.cpp
+++ b/libc/src/stdio/generic/printf.cpp
@@ -12,6 +12,7 @@
 #include "src/__support/arg_list.h"
 #include "src/__support/macros/config.h"
 #include "src/stdio/printf_core/core_structs.h"
+#include "src/stdio/printf_core/error_converter.h"
 #include "src/stdio/printf_core/vfprintf_internal.h"
 
 #include "hdr/types/FILE.h"
@@ -39,7 +40,8 @@ LLVM_LIBC_FUNCTION(int, printf, (const char *__restrict format, ...)) {
     return -1;
   }
   if (ret_val.value() > cpp::numeric_limits<int>::max()) {
-    libc_errno = EOVERFLOW;
+    libc_errno =
+        printf_core::internal_error_to_errno(-printf_core::OVERFLOW_ERROR);
     return -1;
   }
 
diff --git a/libc/src/stdio/generic/vfprintf.cpp b/libc/src/stdio/generic/vfprintf.cpp
index 280f8092a5520..a00a66d993962 100644
--- a/libc/src/stdio/generic/vfprintf.cpp
+++ b/libc/src/stdio/generic/vfprintf.cpp
@@ -12,6 +12,7 @@
 #include "src/__support/arg_list.h"
 #include "src/__support/macros/config.h"
 #include "src/stdio/printf_core/core_structs.h"
+#include "src/stdio/printf_core/error_converter.h"
 #include "src/stdio/printf_core/vfprintf_internal.h"
 
 #include "hdr/types/FILE.h"
@@ -31,7 +32,8 @@ LLVM_LIBC_FUNCTION(int, vfprintf,
     return -1;
   }
   if (ret_val.value() > cpp::numeric_limits<int>::max()) {
-    libc_errno = EOVERFLOW;
+    libc_errno =
+        printf_core::internal_error_to_errno(-printf_core::OVERFLOW_ERROR);
     return -1;
   }
 
diff --git a/libc/src/stdio/generic/vprintf.cpp b/libc/src/stdio/generic/vprintf.cpp
index ba9b283bf9655..155865f8dbf66 100644
--- a/libc/src/stdio/generic/vprintf.cpp
+++ b/libc/src/stdio/generic/vprintf.cpp
@@ -11,6 +11,7 @@
 #include "src/__support/File/file.h"
 #include "src/__support/arg_list.h"
 #include "src/__support/macros/config.h"
+#include "src/stdio/printf_core/error_converter.h"
 #include "src/stdio/printf_core/vfprintf_internal.h"
 
 #include "hdr/types/FILE.h"
@@ -36,7 +37,8 @@ LLVM_LIBC_FUNCTION(int, vprintf,
     return -1;
   }
   if (ret_val.value() > cpp::numeric_limits<int>::max()) {
-    libc_errno = EOVERFLOW;
+    libc_errno =
+        printf_core::internal_error_to_errno(-printf_core::OVERFLOW_ERROR);
     return -1;
   }
 
diff --git a/libc/src/stdio/printf_core/CMakeLists.txt b/libc/src/stdio/printf_core/CMakeLists.txt
index dbb36ac96bc4c..baca2ffc82180 100644
--- a/libc/src/stdio/printf_core/CMakeLists.txt
+++ b/libc/src/stdio/printf_core/CMakeLists.txt
@@ -32,6 +32,16 @@ if(printf_config_copts)
   list(PREPEND printf_config_copts "COMPILE_OPTIONS")
 endif()
 
+if(NOT EXISTS ${CMAKE_CURRENT_SOURCE_DIR}/${LIBC_TARGET_OS})
+  return()
+endif()
+add_subdirectory(${LIBC_TARGET_OS})
+
+set(target_error_converter libc.src.stdio.printf_core.${LIBC_TARGET_OS}.${LIBC_TARGET_OS}_error_converter)
+if(NOT TARGET ${target_error_converter})
+  return()
+endif()
+
 add_header_library(
   printf_config
   HDRS
@@ -143,6 +153,15 @@ add_header_library(
     libc.src.stdio.printf_core.writer
 )
 
+add_header_library(
+  error_converter
+  HDRS
+    error_converter.h
+  DEPENDS
+    ${target_error_converter}
+    libc.src.__support.macros.properties.architectures
+)
+
 if(NOT (TARGET libc.src.__support.File.file) AND LLVM_LIBC_FULL_BUILD)
   # Not all platforms have a file implementation. If file is unvailable, and a
   # full build is requested, then we must skip all file based printf sections.
@@ -161,3 +180,4 @@ add_header_library(
     libc.src.stdio.printf_core.writer
   ${use_system_file}
 )
+
diff --git a/libc/src/stdio/printf_core/baremetal/CMakeLists.txt b/libc/src/stdio/printf_core/baremetal/CMakeLists.txt
new file mode 100644
index 0000000000000..2a436862e6afa
--- /dev/null
+++ b/libc/src/stdio/printf_core/baremetal/CMakeLists.txt
@@ -0,0 +1,8 @@
+add_header_library(
+  baremetal_error_converter
+  HDRS
+    error_converter.h
+  DEPENDS
+    libc.src.stdio.printf_core.core_structs
+    libc.hdr.errno_macros
+)
diff --git a/libc/src/stdio/printf_core/baremetal/error_converter.h b/libc/src/stdio/printf_core/baremetal/error_converter.h
new file mode 100644
index 0000000000000..5aabcfc8dc5c5
--- /dev/null
+++ b/libc/src/stdio/printf_core/baremetal/error_converter.h
@@ -0,0 +1,49 @@
+//===-- Baremetal implementation of error converter -------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef LLVM_LIBC_SRC_STDIO_PRINTF_CORE_BAREMETAL_ERROR_CONVERTER_H
+#define LLVM_LIBC_SRC_STDIO_PRINTF_CORE_BAREMETAL_ERROR_CONVERTER_H
+
+#include "hdr/errno_macros.h"
+#include "src/stdio/printf_core/core_structs.h"
+#include "src/stdio/printf_core/error_converter.h"
+
+namespace LIBC_NAMESPACE_DECL {
+namespace printf_core {
+
+LIBC_INLINE static int internal_error_to_errno(int internal_error) {
+  // System error occured, return error as is.
+  if (internal_error < 1001 && internal_error > 0) {
+    return internal_error;
+  }
+
+  // Map internal error to the available C standard errnos.
+  switch (-internal_error) {
+  case WRITE_OK:
+    return 0;
+  case FILE_WRITE_ERROR:
+  case FILE_STATUS_ERROR:
+  case NULLPTR_WRITE_ERROR:
+  case ALLOCATION_ERROR:
+    return EDOM;
+  case INT_CONVERSION_ERROR:
+  case FIXED_POINT_CONVERSION_ERROR:
+  case OVERFLOW_ERROR:
+    return ERANGE;
+  default:
+    LIBC_ASSERT(
+        false &&
+        "Invalid internal printf error code passed to internal_error_to_errno");
+    return EDOM;
+  }
+}
+
+} // namespace printf_core
+} // namespace LIBC_NAMESPACE_DECL
+
+#endif // LLVM_LIBC_SRC_STDIO_PRINTF_CORE_BAREMETAL_ERROR_CONVERTER_H
diff --git a/libc/src/stdio/printf_core/core_structs.h b/libc/src/stdio/printf_core/core_structs.h
index 7b205ee890c98..2f8188d1c4f8e 100644
--- a/libc/src/stdio/printf_core/core_structs.h
+++ b/libc/src/stdio/printf_core/core_structs.h
@@ -142,36 +142,7 @@ constexpr int NULLPTR_WRITE_ERROR = -1003;
 constexpr int INT_CONVERSION_ERROR = -1004;
 constexpr int FIXED_POINT_CONVERSION_ERROR = -1005;
 constexpr int ALLOCATION_ERROR = -1006;
-
-LIBC_INLINE static int internal_error_to_errno(int internal_error) {
-  // System error occured, return error as is.
-  if (internal_error < 1001) {
-    return internal_error;
-  }
-
-  // Map internal error to errno.
-  switch (-internal_error) {
-  case WRITE_OK:
-    return 0;
-  case FILE_WRITE_ERROR:
-    return EIO;
-  case FILE_STATUS_ERROR:
-    return EIO;
-  case NULLPTR_WRITE_ERROR:
-    return EINVAL;
-  case INT_CONVERSION_ERROR:
-    return ERANGE;
-  case FIXED_POINT_CONVERSION_ERROR:
-    return EINVAL;
-  case ALLOCATION_ERROR:
-    return ENOMEM;
-  default:
-    LIBC_ASSERT(
-        false &&
-        "Invalid internal printf error code passed to internal_error_to_errno");
-    return EINVAL;
-  }
-}
+constexpr int OVERFLOW_ERROR = -1007;
 
 } // namespace printf_core
 } // namespace LIBC_NAMESPACE_DECL
diff --git a/libc/src/stdio/printf_core/error_converter.h b/libc/src/stdio/printf_core/error_converter.h
new file mode 100644
index 0000000000000..6c4716267b27a
--- /dev/null
+++ b/libc/src/stdio/printf_core/error_converter.h
@@ -0,0 +1,22 @@
+//===-- Error converter for printf ------------------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef LLVM_LIBC_SRC_STDIO_PRINTF_CORE_ERROR_CONVERTER_H
+#define LLVM_LIBC_SRC_STDIO_PRINTF_CORE_ERROR_CONVERTER_H
+
+#include "src/__support/macros/properties/architectures.h"
+
+// Maps internal errors to the available errnos on the platform.
+#if defined(__linux__)
+#include "linux/error_converter.h"
+#elif defined(__ELF__)
+// TODO: Ideally we would have LIBC_TARGET_OS_IS_BAREMETAL.
+#include "baremetal/error_converter.h"
+#endif
+
+#endif // LLVM_LIBC_SRC_STDIO_PRINTF_CORE_ERROR_CONVERTER_H
diff --git a/libc/src/stdio/printf_core/linux/CMakeLists.txt b/libc/src/stdio/printf_core/linux/CMakeLists.txt
new file mode 100644
index 0000000000000..4b193866a1914
--- /dev/null
+++ b/libc/src/stdio/printf_core/linux/CMakeLists.txt
@@ -0,0 +1,8 @@
+add_header_library(
+  linux_error_converter
+  HDRS
+    error_converter.h
+  DEPENDS
+    libc.src.stdio.printf_core.core_structs
+    libc.hdr.errno_macros
+)
diff --git a/libc/src/stdio/printf_core/linux/error_converter.h b/libc/src/stdio/printf_core/linux/error_converter.h
new file mode 100644
index 0000000000000..9de24858bb3cf
--- /dev/null
+++ b/libc/src/stdio/printf_core/linux/error_converter.h
@@ -0,0 +1,54 @@
+//===-- Linux implementation of error converter -----------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef LLVM_LIBC_SRC_STDIO_PRINTF_CORE_LINUX_ERROR_CONVERTER_H
+#define LLVM_LIBC_SRC_STDIO_PRINTF_CORE_LINUX_ERROR_CONVERTER_H
+
+#include "hdr/errno_macros.h"
+#include "src/stdio/printf_core/core_structs.h"
+#include "src/stdio/printf_core/error_converter.h"
+
+namespace LIBC_NAMESPACE_DECL {
+namespace printf_core {
+
+LIBC_INLINE static int internal_error_to_errno(int internal_error) {
+  // System error occured, return error as is.
+  if (internal_error < 1001 && internal_error > 0) {
+    return internal_error;
+  }
+
+  // Map internal error to POSIX errnos.
+  switch (-internal_error) {
+  case WRITE_OK:
+    return 0;
+  case FILE_WRITE_ERROR:
+    return EIO;
+  case FILE_STATUS_ERROR:
+    return EIO;
+  case NULLPTR_WRITE_ERROR:
+    return EINVAL;
+  case INT_CONVERSION_ERROR:
+    return ERANGE;
+  case FIXED_POINT_CONVERSION_ERROR:
+    return EINVAL;
+  case ALLOCATION_ERROR:
+    return ENOMEM;
+  case OVERFLOW_ERROR:
+    return EOVERFLOW;
+  default:
+    LIBC_ASSERT(
+        false &&
+        "Invalid internal printf error code passed to internal_error_to_errno");
+    return EINVAL;
+  }
+}
+
+} // namespace printf_core
+} // namespace LIBC_NAMESPACE_DECL
+
+#endif // LLVM_LIBC_SRC_STDIO_PRINTF_CORE_LINUX_ERROR_CONVERTER_H
diff --git a/libc/src/stdio/snprintf.cpp b/libc/src/stdio/snprintf.cpp
index 10b46ddfd6249..b703ac2894fb2 100644
--- a/libc/src/stdio/snprintf.cpp
+++ b/libc/src/stdio/snprintf.cpp
@@ -13,6 +13,7 @@
 #include "src/__support/libc_errno.h"
 #include "src/__support/macros/config.h"
 #include "src/stdio/printf_core/core_structs.h"
+#include "src/stdio/printf_core/error_converter.h"
 #include "src/stdio/printf_core/printf_main.h"
 #include "src/stdio/printf_core/writer.h"
 
@@ -44,7 +45,8 @@ LLVM_LIBC_FUNCTION(int, snprintf,
     wb.buff[wb.buff_cur] = '\0';
 
   if (ret_val.value() > cpp::numeric_limits<int>::max()) {
-    libc_errno = EOVERFLOW;
+    libc_errno =
+        printf_core::internal_error_to_errno(-printf_core::OVERFLOW_ERROR);
     return -1;
   }
 
diff --git a/libc/src/stdio/sprintf.cpp b/libc/src/stdio/sprintf.cpp
index 53e951bea6aaa..343f75c595e2e 100644
--- a/libc/src/stdio/sprintf.cpp
+++ b/libc/src/stdio/sprintf.cpp
@@ -14,6 +14,7 @@
 #include "src/__support/libc_errno.h"
 #include "src/__support/macros/config.h"
 #include "src/stdio/printf_core/core_structs.h"
+#include "src/stdio/printf_core/error_converter.h"
 #include "src/stdio/printf_core/printf_main.h"
 #include "src/stdio/printf_core/writer.h"
 
@@ -44,7 +45,8 @@ LLVM_LIBC_FUNCTION(int, sprintf,
   wb.buff[wb.buff_cur] = '\0';
 
   if (ret_val.value() > cpp::numeric_limits<int>::max()) {
-    libc_errno = EOVERFLOW;
+    libc_errno =
+        printf_core::internal_error_to_errno(-printf_core::OVERFLOW_ERROR);
     return -1;
   }
 
diff --git a/libc/src/stdio/vasprintf.cpp b/libc/src/stdio/vasprintf.cpp
index 1060c39d416c1..fddc9e318cae6 100644
--- a/libc/src/stdio/vasprintf.cpp
+++ b/libc/src/stdio/vasprintf.cpp
@@ -11,6 +11,7 @@
 #include "src/__support/arg_list.h"
 #include "src/__support/libc_errno.h"
 #include "src/stdio/printf_core/core_structs.h"
+#include "src/stdio/printf_core/error_converter.h"
 #include "src/stdio/printf_core/vasprintf_internal.h"
 
 namespace LIBC_NAMESPACE_DECL {
@@ -27,7 +28,8 @@ LLVM_LIBC_FUNCTION(int, vasprintf,
     return -1;
   }
   if (ret_val.value() > cpp::numeric_limits<int>::max()) {
-    libc_errno = EOVERFLOW;
+    libc_errno =
+        printf_core::internal_error_to_errno(-printf_core::OVERFLOW_ERROR);
     return -1;
   }
   return static_cast<int>(ret_val.value());
diff --git a/libc/src/stdio/vsnprintf.cpp b/libc/src/stdio/vsnprintf.cpp
index 000ec5d5cfa5b..aceae31c5ef3d 100644
--- a/libc/src/stdio/vsnprintf.cpp
+++ b/libc/src/stdio/vsnprintf.cpp
@@ -13,6 +13,7 @@
 #include "src/__support/libc_errno.h"
 #include "src/__support/macros/config.h"
 #include "src/stdio/printf_core/core_structs.h"
+#include "src/stdio/printf_core/error_converter.h"
 #include "src/stdio/printf_core/printf_main.h"
 #include "src/stdio/printf_core/writer.h"
 
@@ -41,7 +42,8 @@ LLVM_LIBC_FUNCTION(int, vsnprintf,
     wb.buff[wb.buff_cur] = '\0';
 
   if (ret_val.value() > cpp::numeric_limits<int>::max()) {
-    libc_errno = EOVERFLOW;
+    libc_errno =
+        printf_core::internal_error_to_errno(-printf_core::OVERFLOW_ERROR);
     return -1;
   }
 
diff --git a/libc/src/stdio/vsprintf.cpp b/libc/src/stdio/vsprintf.cpp
index 5f8c79418e83d..8fef3fdbc9834 100644
--- a/libc/src/stdio/vsprintf.cpp
+++ b/libc/src/stdio/vsprintf.cpp
@@ -14,6 +14,7 @@
 #include "src/__support/libc_errno.h"
 #include "src/__support/macros/config.h"
 #include "src/stdio/printf_core/core_structs.h"
+#include "src/stdio/printf_core/error_converter.h"
 #include "src/stdio/printf_core/printf_main.h"
 #include "src/stdio/printf_core/writer.h"
 
@@ -41,7 +42,8 @@ LLVM_LIBC_FUNCTION(int, vsprintf,
   wb.buff[wb.buff_cur] = '\0';
 
   if (ret_val.value() > cpp::numeric_limits<int>::max()) {
-    libc_errno = EOVERFLOW;
+    libc_errno =
+        printf_core::internal_error_to_errno(-printf_core::OVERFLOW_ERROR);
     return -1;
   }
   return static_cast<int>(ret_val.value());
diff --git a/libc/src/stdlib/CMakeLists.txt b/libc/src/stdlib/CMakeLists.txt
index c464f82dcbda7..379fb39424f55 100644
--- a/libc/src/stdlib/CMakeLists.txt
+++ b/libc/src/stdlib/CMakeLists.txt
@@ -73,6 +73,7 @@ add_entrypoint_object(
     strfromf.h
   DEPENDS
     .str_from_util
+    libc.src.stdio.printf_core.error_converter
 )
 
 add_entrypoint_object(
@@ -83,6 +84,7 @@ add_entrypoint_object(
     strfromd.h
   DEPENDS
     .str_from_util
+    libc.src.stdio.printf_core.error_converter
 )
 
 add_entrypoint_object(
@@ -93,6 +95,7 @@ add_entrypoint_object(
     strfroml.h
   DEPENDS
     .str_from_util
+    libc.src.stdio.printf_core.error_converter
 )
 
 add_header_library(
diff --git a/libc/src/stdlib/strfromd.cpp b/libc/src/stdlib/strfromd.cpp
index c89f611831579..c402daa957e34 100644
--- a/libc/src/stdlib/strfromd.cpp
+++ b/libc/src/stdlib/strfromd.cpp
@@ -8,6 +8,7 @@
 
 #include "src/stdlib/strfromd.h"
 #include "src/__support/macros/config.h"
+#include "src/stdio/printf_core/error_converter.h"
 #include "src/stdlib/str_from_util.h"
 
 namespace LIBC_NAMESPACE_DECL {
@@ -37,7 +38,8 @@ LLVM_LIBC_FUNCTION(int, strfromd,
     wb.buff[wb.buff_cur] = '\0';
 
   if (writer.get_chars_written() > cpp::numeric_limits<int>::max()) {
-    libc_errno = EOVERFLOW;
+    libc_errno =
+        printf_core::internal_error_to_errno(-printf_core::OVERFLOW_ERROR);
     return -1;
   }
   return static_cast<int>(writer.get_chars_written());
diff --git a/libc/src/stdlib/strfromf.cpp b/libc/src/stdlib/strfromf.cpp
index 5c3e95f4146f2..53fb913d2a6b3 100644
--- a/libc/src/stdlib/strfromf.cpp
+++ b/libc/src/stdlib/strfromf.cpp
@@ -8,6 +8,7 @@
 
 #include "src/stdlib/strfromf.h"
 #include "src/__support/macros/config.h"
+#include "src/stdio/printf_core/error_converter.h"
 #include "src/stdlib/str_from_util.h"
 
 namespace LIBC_NAMESPACE_DECL {
@@ -37,7 +38,8 @@ LLVM_LIBC_FUNCTION(int, strfromf,
     wb.buff[wb.buff_cur] = '\0';
 
   if (writer.get_chars_written() > cpp::numeric_limits<int>::max()) {
-    libc_errno = EOVERFLOW;
+    libc_errno =
+        printf_core::internal_error_to_errno(-printf_core::OVERFLOW_ERROR);
     return -1;
   }
   return static_cast<int>(writer.get_chars_written());
diff --git a/libc/src/stdlib/strfroml.cpp b/libc/src/stdlib/strfroml.cpp
index 1605e8ab7c18f..aab0052931e7a 100644
--- a/libc/src/stdlib/strfroml.cpp
+++ b/libc/src/stdlib/strfroml.cpp
@@ -8,6 +8,7 @@
 
 #include "src/stdlib/strfroml.h"
 #include "src/__support/macros/config.h"
+#include "src/stdio/printf_core/error_converter.h"
 #include "src/stdlib/str_from_util.h"
 
 namespace LIBC_NAMESPACE_DECL {
@@ -42,7 +43,8 @@ LLVM_LIBC_FUNCTION(int, strfroml,
     wb.buff[wb.buff_cur] = '\0';
 
   if (writer.get_chars_written() > cpp::numeric_limits<int>::max()) {
-    libc_errno = EOVERFLOW;
+    libc_errno =
+        printf_core::internal_error_to_errno(-printf_core::OVERFLOW_ERROR);
     return -1;
   }
   return static_cast<int>(writer.get_chars_written());

>From 5f3b0af532865366d45ec751a6911439051db806 Mon Sep 17 00:00:00 2001
From: Marcell Leleszi <mleleszi at google.com>
Date: Thu, 23 Oct 2025 09:18:26 +0000
Subject: [PATCH 24/27] Clean up dependencies

---
 libc/src/stdio/CMakeLists.txt                    | 5 -----
 libc/src/stdio/asprintf.cpp                      | 2 +-
 libc/src/stdio/baremetal/CMakeLists.txt          | 2 ++
 libc/src/stdio/baremetal/vprintf.cpp             | 1 -
 libc/src/stdio/generic/fprintf.cpp               | 1 +
 libc/src/stdio/generic/vprintf.cpp               | 1 +
 libc/src/stdio/printf_core/core_structs.h        | 1 -
 libc/src/stdio/printf_core/int_converter.h       | 1 -
 libc/src/stdio/printf_core/vasprintf_internal.h  | 1 -
 libc/src/stdio/printf_core/write_int_converter.h | 1 -
 libc/src/stdio/snprintf.cpp                      | 1 -
 libc/src/stdio/sprintf.cpp                       | 1 -
 libc/src/stdio/vasprintf.cpp                     | 1 -
 libc/src/stdio/vsnprintf.cpp                     | 1 -
 libc/src/stdio/vsprintf.cpp                      | 1 -
 libc/src/stdlib/strfromd.cpp                     | 1 +
 libc/src/stdlib/strfromf.cpp                     | 1 +
 libc/src/stdlib/strfroml.cpp                     | 1 +
 libc/test/src/stdlib/StrfromTest.h               | 1 -
 19 files changed, 8 insertions(+), 17 deletions(-)

diff --git a/libc/src/stdio/CMakeLists.txt b/libc/src/stdio/CMakeLists.txt
index 67da5bec258a0..7f8c95ef7b41e 100644
--- a/libc/src/stdio/CMakeLists.txt
+++ b/libc/src/stdio/CMakeLists.txt
@@ -128,7 +128,6 @@ add_entrypoint_object(
     libc.src.stdio.printf_core.core_structs
     libc.src.stdio.printf_core.error_converter
     libc.src.__support.libc_errno
-    libc.hdr.errno_macros
 )
 
 add_entrypoint_object(
@@ -143,7 +142,6 @@ add_entrypoint_object(
     libc.src.stdio.printf_core.core_structs
     libc.src.stdio.printf_core.error_converter
     libc.src.__support.libc_errno
-    libc.hdr.errno_macros
 )
 
 add_entrypoint_object(
@@ -157,7 +155,6 @@ add_entrypoint_object(
     libc.src.stdio.printf_core.core_structs
     libc.src.stdio.printf_core.error_converter
     libc.src.__support.libc_errno
-    libc.hdr.errno_macros
 )
 
 add_entrypoint_object(
@@ -185,7 +182,6 @@ add_entrypoint_object(
     libc.src.stdio.printf_core.core_structs
     libc.src.stdio.printf_core.error_converter
     libc.src.__support.libc_errno
-    libc.hdr.errno_macros
 )
 
 add_entrypoint_object(
@@ -199,7 +195,6 @@ add_entrypoint_object(
     libc.src.stdio.printf_core.core_structs
     libc.src.stdio.printf_core.error_converter
     libc.src.__support.libc_errno
-    libc.hdr.errno_macros
 )
 
 add_subdirectory(printf_core)
diff --git a/libc/src/stdio/asprintf.cpp b/libc/src/stdio/asprintf.cpp
index a4852b75e4124..49183c0614abc 100644
--- a/libc/src/stdio/asprintf.cpp
+++ b/libc/src/stdio/asprintf.cpp
@@ -7,10 +7,10 @@
 //===----------------------------------------------------------------------===//
 
 #include "src/stdio/asprintf.h"
-#include "hdr/errno_macros.h"
 #include "src/__support/arg_list.h"
 #include "src/__support/libc_errno.h"
 #include "src/__support/macros/config.h"
+#include "src/stdio/printf_core/core_structs.h"
 #include "src/stdio/printf_core/error_converter.h"
 #include "src/stdio/printf_core/vasprintf_internal.h"
 
diff --git a/libc/src/stdio/baremetal/CMakeLists.txt b/libc/src/stdio/baremetal/CMakeLists.txt
index af17635b9156a..3f97254356001 100644
--- a/libc/src/stdio/baremetal/CMakeLists.txt
+++ b/libc/src/stdio/baremetal/CMakeLists.txt
@@ -30,6 +30,7 @@ add_entrypoint_object(
     libc.src.stdio.printf_core.printf_main
     libc.src.stdio.printf_core.writer
     libc.src.stdio.printf_core.error_converter
+    libc.src.stdio.printf_core.core_structs
     libc.src.__support.arg_list
     libc.src.__support.OSUtil.osutil
     libc.src.__support.libc_errno
@@ -90,6 +91,7 @@ add_entrypoint_object(
     libc.src.stdio.printf_core.printf_main
     libc.src.stdio.printf_core.writer
     libc.src.stdio.printf_core.error_converter
+    libc.src.stdio.printf_core.core_structs
     libc.src.__support.arg_list
     libc.src.__support.OSUtil.osutil
     libc.src.__support.libc_errno
diff --git a/libc/src/stdio/baremetal/vprintf.cpp b/libc/src/stdio/baremetal/vprintf.cpp
index 19254d956a916..8170a3b16f22e 100644
--- a/libc/src/stdio/baremetal/vprintf.cpp
+++ b/libc/src/stdio/baremetal/vprintf.cpp
@@ -7,7 +7,6 @@
 //===----------------------------------------------------------------------===//
 
 #include "src/stdio/vprintf.h"
-#include "hdr/errno_macros.h"
 #include "src/__support/OSUtil/io.h"
 #include "src/__support/arg_list.h"
 #include "src/__support/libc_errno.h"
diff --git a/libc/src/stdio/generic/fprintf.cpp b/libc/src/stdio/generic/fprintf.cpp
index 9b83986fb496f..6126a00665a9f 100644
--- a/libc/src/stdio/generic/fprintf.cpp
+++ b/libc/src/stdio/generic/fprintf.cpp
@@ -11,6 +11,7 @@
 #include "src/__support/File/file.h"
 #include "src/__support/arg_list.h"
 #include "src/__support/macros/config.h"
+#include "src/stdio/printf_core/core_structs.h"
 #include "src/stdio/printf_core/error_converter.h"
 #include "src/stdio/printf_core/vfprintf_internal.h"
 
diff --git a/libc/src/stdio/generic/vprintf.cpp b/libc/src/stdio/generic/vprintf.cpp
index 155865f8dbf66..829cf232834e2 100644
--- a/libc/src/stdio/generic/vprintf.cpp
+++ b/libc/src/stdio/generic/vprintf.cpp
@@ -11,6 +11,7 @@
 #include "src/__support/File/file.h"
 #include "src/__support/arg_list.h"
 #include "src/__support/macros/config.h"
+#include "src/stdio/printf_core/core_structs.h"
 #include "src/stdio/printf_core/error_converter.h"
 #include "src/stdio/printf_core/vfprintf_internal.h"
 
diff --git a/libc/src/stdio/printf_core/core_structs.h b/libc/src/stdio/printf_core/core_structs.h
index 2f8188d1c4f8e..0d41f2244d8da 100644
--- a/libc/src/stdio/printf_core/core_structs.h
+++ b/libc/src/stdio/printf_core/core_structs.h
@@ -11,7 +11,6 @@
 
 #include "src/__support/macros/config.h"
 
-#include "hdr/errno_macros.h"
 #include "src/__support/CPP/string_view.h"
 #include "src/__support/CPP/type_traits.h"
 #include "src/__support/FPUtil/FPBits.h"
diff --git a/libc/src/stdio/printf_core/int_converter.h b/libc/src/stdio/printf_core/int_converter.h
index 61cb402732ab7..11234c32ce997 100644
--- a/libc/src/stdio/printf_core/int_converter.h
+++ b/libc/src/stdio/printf_core/int_converter.h
@@ -9,7 +9,6 @@
 #ifndef LLVM_LIBC_SRC_STDIO_PRINTF_CORE_INT_CONVERTER_H
 #define LLVM_LIBC_SRC_STDIO_PRINTF_CORE_INT_CONVERTER_H
 
-#include "hdr/errno_macros.h"
 #include "src/__support/CPP/span.h"
 #include "src/__support/CPP/string_view.h"
 #include "src/__support/ctype_utils.h"
diff --git a/libc/src/stdio/printf_core/vasprintf_internal.h b/libc/src/stdio/printf_core/vasprintf_internal.h
index 50ebf0ea4555d..41df17b67f35b 100644
--- a/libc/src/stdio/printf_core/vasprintf_internal.h
+++ b/libc/src/stdio/printf_core/vasprintf_internal.h
@@ -6,7 +6,6 @@
 //
 //===----------------------------------------------------------------------===//
 
-#include "hdr/errno_macros.h"
 #include "hdr/func/free.h"
 #include "hdr/func/malloc.h"
 #include "hdr/func/realloc.h"
diff --git a/libc/src/stdio/printf_core/write_int_converter.h b/libc/src/stdio/printf_core/write_int_converter.h
index f63a9a538690d..04b2bef05bc7b 100644
--- a/libc/src/stdio/printf_core/write_int_converter.h
+++ b/libc/src/stdio/printf_core/write_int_converter.h
@@ -9,7 +9,6 @@
 #ifndef LLVM_LIBC_SRC_STDIO_PRINTF_CORE_WRITE_INT_CONVERTER_H
 #define LLVM_LIBC_SRC_STDIO_PRINTF_CORE_WRITE_INT_CONVERTER_H
 
-#include "hdr/errno_macros.h"
 #include "src/__support/macros/config.h"
 #include "src/stdio/printf_core/core_structs.h"
 #include "src/stdio/printf_core/writer.h"
diff --git a/libc/src/stdio/snprintf.cpp b/libc/src/stdio/snprintf.cpp
index b703ac2894fb2..25da3a9dbd2c5 100644
--- a/libc/src/stdio/snprintf.cpp
+++ b/libc/src/stdio/snprintf.cpp
@@ -8,7 +8,6 @@
 
 #include "src/stdio/snprintf.h"
 
-#include "hdr/errno_macros.h"
 #include "src/__support/arg_list.h"
 #include "src/__support/libc_errno.h"
 #include "src/__support/macros/config.h"
diff --git a/libc/src/stdio/sprintf.cpp b/libc/src/stdio/sprintf.cpp
index 343f75c595e2e..09f222927afd6 100644
--- a/libc/src/stdio/sprintf.cpp
+++ b/libc/src/stdio/sprintf.cpp
@@ -8,7 +8,6 @@
 
 #include "src/stdio/sprintf.h"
 
-#include "hdr/errno_macros.h"
 #include "src/__support/CPP/limits.h"
 #include "src/__support/arg_list.h"
 #include "src/__support/libc_errno.h"
diff --git a/libc/src/stdio/vasprintf.cpp b/libc/src/stdio/vasprintf.cpp
index fddc9e318cae6..d1ac9f4342824 100644
--- a/libc/src/stdio/vasprintf.cpp
+++ b/libc/src/stdio/vasprintf.cpp
@@ -7,7 +7,6 @@
 //===----------------------------------------------------------------------===//
 
 #include "src/stdio/vasprintf.h"
-#include "hdr/errno_macros.h"
 #include "src/__support/arg_list.h"
 #include "src/__support/libc_errno.h"
 #include "src/stdio/printf_core/core_structs.h"
diff --git a/libc/src/stdio/vsnprintf.cpp b/libc/src/stdio/vsnprintf.cpp
index aceae31c5ef3d..01ece7d04df7e 100644
--- a/libc/src/stdio/vsnprintf.cpp
+++ b/libc/src/stdio/vsnprintf.cpp
@@ -8,7 +8,6 @@
 
 #include "src/stdio/vsnprintf.h"
 
-#include "hdr/errno_macros.h"
 #include "src/__support/arg_list.h"
 #include "src/__support/libc_errno.h"
 #include "src/__support/macros/config.h"
diff --git a/libc/src/stdio/vsprintf.cpp b/libc/src/stdio/vsprintf.cpp
index 8fef3fdbc9834..3b1a4a37bc405 100644
--- a/libc/src/stdio/vsprintf.cpp
+++ b/libc/src/stdio/vsprintf.cpp
@@ -8,7 +8,6 @@
 
 #include "src/stdio/vsprintf.h"
 
-#include "hdr/errno_macros.h"
 #include "src/__support/CPP/limits.h"
 #include "src/__support/arg_list.h"
 #include "src/__support/libc_errno.h"
diff --git a/libc/src/stdlib/strfromd.cpp b/libc/src/stdlib/strfromd.cpp
index c402daa957e34..dce1b5e5f253f 100644
--- a/libc/src/stdlib/strfromd.cpp
+++ b/libc/src/stdlib/strfromd.cpp
@@ -8,6 +8,7 @@
 
 #include "src/stdlib/strfromd.h"
 #include "src/__support/macros/config.h"
+#include "src/stdio/printf_core/core_structs.h"
 #include "src/stdio/printf_core/error_converter.h"
 #include "src/stdlib/str_from_util.h"
 
diff --git a/libc/src/stdlib/strfromf.cpp b/libc/src/stdlib/strfromf.cpp
index 53fb913d2a6b3..fe811ee27242f 100644
--- a/libc/src/stdlib/strfromf.cpp
+++ b/libc/src/stdlib/strfromf.cpp
@@ -8,6 +8,7 @@
 
 #include "src/stdlib/strfromf.h"
 #include "src/__support/macros/config.h"
+#include "src/stdio/printf_core/core_structs.h"
 #include "src/stdio/printf_core/error_converter.h"
 #include "src/stdlib/str_from_util.h"
 
diff --git a/libc/src/stdlib/strfroml.cpp b/libc/src/stdlib/strfroml.cpp
index aab0052931e7a..ef27312d026c2 100644
--- a/libc/src/stdlib/strfroml.cpp
+++ b/libc/src/stdlib/strfroml.cpp
@@ -8,6 +8,7 @@
 
 #include "src/stdlib/strfroml.h"
 #include "src/__support/macros/config.h"
+#include "src/stdio/printf_core/core_structs.h"
 #include "src/stdio/printf_core/error_converter.h"
 #include "src/stdlib/str_from_util.h"
 
diff --git a/libc/test/src/stdlib/StrfromTest.h b/libc/test/src/stdlib/StrfromTest.h
index 1442f172e19fe..fdeed0e3c06f5 100644
--- a/libc/test/src/stdlib/StrfromTest.h
+++ b/libc/test/src/stdlib/StrfromTest.h
@@ -6,7 +6,6 @@
 //
 //===----------------------------------------------------------------------===//
 
-#include "src/__support/CPP/limits.h"
 #include "src/__support/CPP/type_traits.h"
 #include "src/__support/FPUtil/FPBits.h"
 #include "test/UnitTest/ErrnoCheckingTest.h"

>From 428c7e9a5464a0e0a31dc585a89d46da9d0b8e21 Mon Sep 17 00:00:00 2001
From: Marcell Leleszi <mleleszi at google.com>
Date: Thu, 23 Oct 2025 10:42:52 +0000
Subject: [PATCH 25/27] Add darwin and generic impl of error converter

---
 libc/src/stdio/printf_core/CMakeLists.txt     |  9 ++--
 .../{baremetal => darwin}/CMakeLists.txt      |  2 +-
 .../printf_core/darwin/error_converter.h      | 54 +++++++++++++++++++
 libc/src/stdio/printf_core/error_converter.h  |  7 +--
 .../stdio/printf_core/generic/CMakeLists.txt  |  8 +++
 .../{baremetal => generic}/error_converter.h  |  8 +--
 6 files changed, 76 insertions(+), 12 deletions(-)
 rename libc/src/stdio/printf_core/{baremetal => darwin}/CMakeLists.txt (82%)
 create mode 100644 libc/src/stdio/printf_core/darwin/error_converter.h
 create mode 100644 libc/src/stdio/printf_core/generic/CMakeLists.txt
 rename libc/src/stdio/printf_core/{baremetal => generic}/error_converter.h (82%)

diff --git a/libc/src/stdio/printf_core/CMakeLists.txt b/libc/src/stdio/printf_core/CMakeLists.txt
index baca2ffc82180..016543bbfb535 100644
--- a/libc/src/stdio/printf_core/CMakeLists.txt
+++ b/libc/src/stdio/printf_core/CMakeLists.txt
@@ -32,14 +32,15 @@ if(printf_config_copts)
   list(PREPEND printf_config_copts "COMPILE_OPTIONS")
 endif()
 
-if(NOT EXISTS ${CMAKE_CURRENT_SOURCE_DIR}/${LIBC_TARGET_OS})
-  return()
+if(EXISTS ${CMAKE_CURRENT_SOURCE_DIR}/${LIBC_TARGET_OS})
+  add_subdirectory(${LIBC_TARGET_OS})
+else()
+  add_subdirectory(generic)
 endif()
-add_subdirectory(${LIBC_TARGET_OS})
 
 set(target_error_converter libc.src.stdio.printf_core.${LIBC_TARGET_OS}.${LIBC_TARGET_OS}_error_converter)
 if(NOT TARGET ${target_error_converter})
-  return()
+    set(target_error_converter libc.src.stdio.printf_core.generic.generic_error_converter)
 endif()
 
 add_header_library(
diff --git a/libc/src/stdio/printf_core/baremetal/CMakeLists.txt b/libc/src/stdio/printf_core/darwin/CMakeLists.txt
similarity index 82%
rename from libc/src/stdio/printf_core/baremetal/CMakeLists.txt
rename to libc/src/stdio/printf_core/darwin/CMakeLists.txt
index 2a436862e6afa..89bc59097764c 100644
--- a/libc/src/stdio/printf_core/baremetal/CMakeLists.txt
+++ b/libc/src/stdio/printf_core/darwin/CMakeLists.txt
@@ -1,5 +1,5 @@
 add_header_library(
-  baremetal_error_converter
+  darwin_error_converter
   HDRS
     error_converter.h
   DEPENDS
diff --git a/libc/src/stdio/printf_core/darwin/error_converter.h b/libc/src/stdio/printf_core/darwin/error_converter.h
new file mode 100644
index 0000000000000..897c76ffe7099
--- /dev/null
+++ b/libc/src/stdio/printf_core/darwin/error_converter.h
@@ -0,0 +1,54 @@
+//===-- Darwin implementation of error converter ----------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef LLVM_LIBC_SRC_STDIO_PRINTF_CORE_DARWIN_ERROR_CONVERTER_H
+#define LLVM_LIBC_SRC_STDIO_PRINTF_CORE_DARWIN_ERROR_CONVERTER_H
+
+#include "hdr/errno_macros.h"
+#include "src/stdio/printf_core/core_structs.h"
+#include "src/stdio/printf_core/error_converter.h"
+
+namespace LIBC_NAMESPACE_DECL {
+namespace printf_core {
+
+LIBC_INLINE static int internal_error_to_errno(int internal_error) {
+  // System error occured, return error as is.
+  if (internal_error < 1001 && internal_error > 0) {
+    return internal_error;
+  }
+
+  // Map internal error to POSIX errnos.
+  switch (-internal_error) {
+  case WRITE_OK:
+    return 0;
+  case FILE_WRITE_ERROR:
+    return EIO;
+  case FILE_STATUS_ERROR:
+    return EIO;
+  case NULLPTR_WRITE_ERROR:
+    return EINVAL;
+  case INT_CONVERSION_ERROR:
+    return ERANGE;
+  case FIXED_POINT_CONVERSION_ERROR:
+    return EINVAL;
+  case ALLOCATION_ERROR:
+    return ENOMEM;
+  case OVERFLOW_ERROR:
+    return EOVERFLOW;
+  default:
+    LIBC_ASSERT(
+        false &&
+        "Invalid internal printf error code passed to internal_error_to_errno");
+    return EINVAL;
+  }
+}
+
+} // namespace printf_core
+} // namespace LIBC_NAMESPACE_DECL
+
+#endif // LLVM_LIBC_SRC_STDIO_PRINTF_CORE_DARWIN_ERROR_CONVERTER_H
diff --git a/libc/src/stdio/printf_core/error_converter.h b/libc/src/stdio/printf_core/error_converter.h
index 6c4716267b27a..f2fe303203fc7 100644
--- a/libc/src/stdio/printf_core/error_converter.h
+++ b/libc/src/stdio/printf_core/error_converter.h
@@ -14,9 +14,10 @@
 // Maps internal errors to the available errnos on the platform.
 #if defined(__linux__)
 #include "linux/error_converter.h"
-#elif defined(__ELF__)
-// TODO: Ideally we would have LIBC_TARGET_OS_IS_BAREMETAL.
-#include "baremetal/error_converter.h"
+#elif defined(__APPLE__)
+#include "darwin/error_converter.h"
+#else
+#include "generic/error_converter.h"
 #endif
 
 #endif // LLVM_LIBC_SRC_STDIO_PRINTF_CORE_ERROR_CONVERTER_H
diff --git a/libc/src/stdio/printf_core/generic/CMakeLists.txt b/libc/src/stdio/printf_core/generic/CMakeLists.txt
new file mode 100644
index 0000000000000..f7fc250734645
--- /dev/null
+++ b/libc/src/stdio/printf_core/generic/CMakeLists.txt
@@ -0,0 +1,8 @@
+add_header_library(
+  generic_error_converter
+  HDRS
+    error_converter.h
+  DEPENDS
+    libc.src.stdio.printf_core.core_structs
+    libc.hdr.errno_macros
+)
diff --git a/libc/src/stdio/printf_core/baremetal/error_converter.h b/libc/src/stdio/printf_core/generic/error_converter.h
similarity index 82%
rename from libc/src/stdio/printf_core/baremetal/error_converter.h
rename to libc/src/stdio/printf_core/generic/error_converter.h
index 5aabcfc8dc5c5..2f59a105415c7 100644
--- a/libc/src/stdio/printf_core/baremetal/error_converter.h
+++ b/libc/src/stdio/printf_core/generic/error_converter.h
@@ -1,4 +1,4 @@
-//===-- Baremetal implementation of error converter -------------*- C++ -*-===//
+//===-- Generic implementation of error converter ---------------*- C++ -*-===//
 //
 // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
 // See https://llvm.org/LICENSE.txt for license information.
@@ -6,8 +6,8 @@
 //
 //===----------------------------------------------------------------------===//
 
-#ifndef LLVM_LIBC_SRC_STDIO_PRINTF_CORE_BAREMETAL_ERROR_CONVERTER_H
-#define LLVM_LIBC_SRC_STDIO_PRINTF_CORE_BAREMETAL_ERROR_CONVERTER_H
+#ifndef LLVM_LIBC_SRC_STDIO_PRINTF_CORE_GENERIC_ERROR_CONVERTER_H
+#define LLVM_LIBC_SRC_STDIO_PRINTF_CORE_GENERIC_ERROR_CONVERTER_H
 
 #include "hdr/errno_macros.h"
 #include "src/stdio/printf_core/core_structs.h"
@@ -46,4 +46,4 @@ LIBC_INLINE static int internal_error_to_errno(int internal_error) {
 } // namespace printf_core
 } // namespace LIBC_NAMESPACE_DECL
 
-#endif // LLVM_LIBC_SRC_STDIO_PRINTF_CORE_BAREMETAL_ERROR_CONVERTER_H
+#endif // LLVM_LIBC_SRC_STDIO_PRINTF_CORE_GENERIC_ERROR_CONVERTER_H

>From 21df09553671a206a104f052de196733e3932b62 Mon Sep 17 00:00:00 2001
From: Marcell Leleszi <mleleszi at google.com>
Date: Fri, 31 Oct 2025 18:45:48 +0000
Subject: [PATCH 26/27] Cleanup

---
 libc/src/stdio/CMakeLists.txt                 | 19 ++++---
 libc/src/stdio/asprintf.cpp                   |  3 +-
 libc/src/stdio/baremetal/CMakeLists.txt       |  6 ++-
 libc/src/stdio/baremetal/printf.cpp           |  3 +-
 libc/src/stdio/baremetal/vprintf.cpp          |  3 +-
 libc/src/stdio/generic/CMakeLists.txt         |  4 +-
 libc/src/stdio/generic/fprintf.cpp            |  3 +-
 libc/src/stdio/generic/printf.cpp             |  3 +-
 libc/src/stdio/generic/vfprintf.cpp           |  3 +-
 libc/src/stdio/generic/vprintf.cpp            |  3 +-
 libc/src/stdio/printf_core/CMakeLists.txt     | 10 ++--
 .../stdio/printf_core/darwin/CMakeLists.txt   |  8 ---
 .../printf_core/darwin/error_converter.h      | 54 -------------------
 .../{error_converter.h => error_mapper.h}     | 14 +++--
 .../stdio/printf_core/generic/CMakeLists.txt  |  4 +-
 .../{error_converter.h => error_mapper.h}     | 10 ++--
 .../stdio/printf_core/linux/CMakeLists.txt    |  4 +-
 .../{error_converter.h => error_mapper.h}     | 10 ++--
 .../src/stdio/printf_core/vfprintf_internal.h |  2 +
 libc/src/stdio/snprintf.cpp                   |  3 +-
 libc/src/stdio/sprintf.cpp                    |  2 +-
 libc/src/stdio/vasprintf.cpp                  |  3 +-
 libc/src/stdio/vsnprintf.cpp                  |  3 +-
 libc/src/stdio/vsprintf.cpp                   |  2 +-
 libc/src/stdlib/CMakeLists.txt                |  9 ++--
 libc/src/stdlib/strfromd.cpp                  |  3 +-
 libc/src/stdlib/strfromf.cpp                  |  3 +-
 libc/src/stdlib/strfroml.cpp                  |  3 +-
 libc/test/src/stdio/fprintf_test.cpp          |  3 +-
 29 files changed, 83 insertions(+), 117 deletions(-)
 delete mode 100644 libc/src/stdio/printf_core/darwin/CMakeLists.txt
 delete mode 100644 libc/src/stdio/printf_core/darwin/error_converter.h
 rename libc/src/stdio/printf_core/{error_converter.h => error_mapper.h} (55%)
 rename libc/src/stdio/printf_core/generic/{error_converter.h => error_mapper.h} (79%)
 rename libc/src/stdio/printf_core/linux/{error_converter.h => error_mapper.h} (80%)

diff --git a/libc/src/stdio/CMakeLists.txt b/libc/src/stdio/CMakeLists.txt
index 7f8c95ef7b41e..c75c8b11be2b5 100644
--- a/libc/src/stdio/CMakeLists.txt
+++ b/libc/src/stdio/CMakeLists.txt
@@ -126,8 +126,9 @@ add_entrypoint_object(
     libc.src.stdio.printf_core.printf_main
     libc.src.stdio.printf_core.writer
     libc.src.stdio.printf_core.core_structs
-    libc.src.stdio.printf_core.error_converter
+    libc.src.stdio.printf_core.error_mapper
     libc.src.__support.libc_errno
+    libc.src.__support.CPP.limits
 )
 
 add_entrypoint_object(
@@ -140,8 +141,9 @@ add_entrypoint_object(
     libc.src.stdio.printf_core.printf_main
     libc.src.stdio.printf_core.writer
     libc.src.stdio.printf_core.core_structs
-    libc.src.stdio.printf_core.error_converter
+    libc.src.stdio.printf_core.error_mapper
     libc.src.__support.libc_errno
+    libc.src.__support.CPP.limits
 )
 
 add_entrypoint_object(
@@ -153,8 +155,9 @@ add_entrypoint_object(
   DEPENDS
     libc.src.stdio.printf_core.vasprintf_internal
     libc.src.stdio.printf_core.core_structs
-    libc.src.stdio.printf_core.error_converter
+    libc.src.stdio.printf_core.error_mapper
     libc.src.__support.libc_errno
+    libc.src.__support.CPP.limits
 )
 
 add_entrypoint_object(
@@ -167,7 +170,9 @@ add_entrypoint_object(
     libc.src.stdio.printf_core.printf_main
     libc.src.stdio.printf_core.writer
     libc.src.stdio.printf_core.core_structs
-    libc.src.stdio.printf_core.error_converter
+    libc.src.stdio.printf_core.error_mapper
+    libc.src.__support.libc_errno
+    libc.src.__support.CPP.limits
 )
 
 add_entrypoint_object(
@@ -180,8 +185,9 @@ add_entrypoint_object(
     libc.src.stdio.printf_core.printf_main
     libc.src.stdio.printf_core.writer
     libc.src.stdio.printf_core.core_structs
-    libc.src.stdio.printf_core.error_converter
+    libc.src.stdio.printf_core.error_mapper
     libc.src.__support.libc_errno
+    libc.src.__support.CPP.limits
 )
 
 add_entrypoint_object(
@@ -193,8 +199,9 @@ add_entrypoint_object(
   DEPENDS
     libc.src.stdio.printf_core.vasprintf_internal
     libc.src.stdio.printf_core.core_structs
-    libc.src.stdio.printf_core.error_converter
+    libc.src.stdio.printf_core.error_mapper
     libc.src.__support.libc_errno
+    libc.src.__support.CPP.limits
 )
 
 add_subdirectory(printf_core)
diff --git a/libc/src/stdio/asprintf.cpp b/libc/src/stdio/asprintf.cpp
index 49183c0614abc..083f40c1f19fa 100644
--- a/libc/src/stdio/asprintf.cpp
+++ b/libc/src/stdio/asprintf.cpp
@@ -7,11 +7,12 @@
 //===----------------------------------------------------------------------===//
 
 #include "src/stdio/asprintf.h"
+#include "src/__support/CPP/limits.h"
 #include "src/__support/arg_list.h"
 #include "src/__support/libc_errno.h"
 #include "src/__support/macros/config.h"
 #include "src/stdio/printf_core/core_structs.h"
-#include "src/stdio/printf_core/error_converter.h"
+#include "src/stdio/printf_core/error_mapper.h"
 #include "src/stdio/printf_core/vasprintf_internal.h"
 
 namespace LIBC_NAMESPACE_DECL {
diff --git a/libc/src/stdio/baremetal/CMakeLists.txt b/libc/src/stdio/baremetal/CMakeLists.txt
index 3f97254356001..bfeff0e2b5880 100644
--- a/libc/src/stdio/baremetal/CMakeLists.txt
+++ b/libc/src/stdio/baremetal/CMakeLists.txt
@@ -29,11 +29,12 @@ add_entrypoint_object(
   DEPENDS
     libc.src.stdio.printf_core.printf_main
     libc.src.stdio.printf_core.writer
-    libc.src.stdio.printf_core.error_converter
+    libc.src.stdio.printf_core.error_mapper
     libc.src.stdio.printf_core.core_structs
     libc.src.__support.arg_list
     libc.src.__support.OSUtil.osutil
     libc.src.__support.libc_errno
+    libc.src.__support.CPP.limits
 )
 
 add_entrypoint_object(
@@ -90,11 +91,12 @@ add_entrypoint_object(
   DEPENDS
     libc.src.stdio.printf_core.printf_main
     libc.src.stdio.printf_core.writer
-    libc.src.stdio.printf_core.error_converter
+    libc.src.stdio.printf_core.error_mapper
     libc.src.stdio.printf_core.core_structs
     libc.src.__support.arg_list
     libc.src.__support.OSUtil.osutil
     libc.src.__support.libc_errno
+    libc.src.__support.CPP.limits
 )
 
 add_entrypoint_object(
diff --git a/libc/src/stdio/baremetal/printf.cpp b/libc/src/stdio/baremetal/printf.cpp
index 74748a85ce614..0c6c9ad338c9f 100644
--- a/libc/src/stdio/baremetal/printf.cpp
+++ b/libc/src/stdio/baremetal/printf.cpp
@@ -7,12 +7,13 @@
 //===----------------------------------------------------------------------===//
 
 #include "src/stdio/printf.h"
+#include "src/__support/CPP/limits.h"
 #include "src/__support/OSUtil/io.h"
 #include "src/__support/arg_list.h"
 #include "src/__support/libc_errno.h"
 #include "src/__support/macros/config.h"
 #include "src/stdio/printf_core/core_structs.h"
-#include "src/stdio/printf_core/error_converter.h"
+#include "src/stdio/printf_core/error_mapper.h"
 #include "src/stdio/printf_core/printf_main.h"
 #include "src/stdio/printf_core/writer.h"
 
diff --git a/libc/src/stdio/baremetal/vprintf.cpp b/libc/src/stdio/baremetal/vprintf.cpp
index 8170a3b16f22e..d2f586c70ad1c 100644
--- a/libc/src/stdio/baremetal/vprintf.cpp
+++ b/libc/src/stdio/baremetal/vprintf.cpp
@@ -7,12 +7,13 @@
 //===----------------------------------------------------------------------===//
 
 #include "src/stdio/vprintf.h"
+#include "src/__support/CPP/limits.h"
 #include "src/__support/OSUtil/io.h"
 #include "src/__support/arg_list.h"
 #include "src/__support/libc_errno.h"
 #include "src/__support/macros/config.h"
 #include "src/stdio/printf_core/core_structs.h"
-#include "src/stdio/printf_core/error_converter.h"
+#include "src/stdio/printf_core/error_mapper.h"
 #include "src/stdio/printf_core/printf_main.h"
 #include "src/stdio/printf_core/writer.h"
 
diff --git a/libc/src/stdio/generic/CMakeLists.txt b/libc/src/stdio/generic/CMakeLists.txt
index 34ab478777e5e..71055edea3d9e 100644
--- a/libc/src/stdio/generic/CMakeLists.txt
+++ b/libc/src/stdio/generic/CMakeLists.txt
@@ -393,9 +393,11 @@ add_generic_entrypoint_object(
 list(APPEND fprintf_deps
       libc.hdr.types.FILE
       libc.src.__support.arg_list
+      libc.src.__support.CPP.limits
+      libc.src.__support.libc_errno
       libc.src.stdio.printf_core.vfprintf_internal
       libc.src.stdio.printf_core.core_structs
-      libc.src.stdio.printf_core.error_converter
+      libc.src.stdio.printf_core.error_mapper
 )
 
 if(LLVM_LIBC_FULL_BUILD)
diff --git a/libc/src/stdio/generic/fprintf.cpp b/libc/src/stdio/generic/fprintf.cpp
index 6126a00665a9f..b07f2528fe11d 100644
--- a/libc/src/stdio/generic/fprintf.cpp
+++ b/libc/src/stdio/generic/fprintf.cpp
@@ -8,11 +8,12 @@
 
 #include "src/stdio/fprintf.h"
 
+#include "src/__support/CPP/limits.h"
 #include "src/__support/File/file.h"
 #include "src/__support/arg_list.h"
 #include "src/__support/macros/config.h"
 #include "src/stdio/printf_core/core_structs.h"
-#include "src/stdio/printf_core/error_converter.h"
+#include "src/stdio/printf_core/error_mapper.h"
 #include "src/stdio/printf_core/vfprintf_internal.h"
 
 #include "hdr/types/FILE.h"
diff --git a/libc/src/stdio/generic/printf.cpp b/libc/src/stdio/generic/printf.cpp
index 2a40f59011a40..d6d4adcefb3b1 100644
--- a/libc/src/stdio/generic/printf.cpp
+++ b/libc/src/stdio/generic/printf.cpp
@@ -8,11 +8,12 @@
 
 #include "src/stdio/printf.h"
 
+#include "src/__support/CPP/limits.h"
 #include "src/__support/File/file.h"
 #include "src/__support/arg_list.h"
 #include "src/__support/macros/config.h"
 #include "src/stdio/printf_core/core_structs.h"
-#include "src/stdio/printf_core/error_converter.h"
+#include "src/stdio/printf_core/error_mapper.h"
 #include "src/stdio/printf_core/vfprintf_internal.h"
 
 #include "hdr/types/FILE.h"
diff --git a/libc/src/stdio/generic/vfprintf.cpp b/libc/src/stdio/generic/vfprintf.cpp
index a00a66d993962..c00352d1dd666 100644
--- a/libc/src/stdio/generic/vfprintf.cpp
+++ b/libc/src/stdio/generic/vfprintf.cpp
@@ -8,11 +8,12 @@
 
 #include "src/stdio/vfprintf.h"
 
+#include "src/__support/CPP/limits.h"
 #include "src/__support/File/file.h"
 #include "src/__support/arg_list.h"
 #include "src/__support/macros/config.h"
 #include "src/stdio/printf_core/core_structs.h"
-#include "src/stdio/printf_core/error_converter.h"
+#include "src/stdio/printf_core/error_mapper.h"
 #include "src/stdio/printf_core/vfprintf_internal.h"
 
 #include "hdr/types/FILE.h"
diff --git a/libc/src/stdio/generic/vprintf.cpp b/libc/src/stdio/generic/vprintf.cpp
index 829cf232834e2..1c0837fd5d441 100644
--- a/libc/src/stdio/generic/vprintf.cpp
+++ b/libc/src/stdio/generic/vprintf.cpp
@@ -8,11 +8,12 @@
 
 #include "src/stdio/vprintf.h"
 
+#include "src/__support/CPP/limits.h"
 #include "src/__support/File/file.h"
 #include "src/__support/arg_list.h"
 #include "src/__support/macros/config.h"
 #include "src/stdio/printf_core/core_structs.h"
-#include "src/stdio/printf_core/error_converter.h"
+#include "src/stdio/printf_core/error_mapper.h"
 #include "src/stdio/printf_core/vfprintf_internal.h"
 
 #include "hdr/types/FILE.h"
diff --git a/libc/src/stdio/printf_core/CMakeLists.txt b/libc/src/stdio/printf_core/CMakeLists.txt
index 016543bbfb535..2d1daea71406e 100644
--- a/libc/src/stdio/printf_core/CMakeLists.txt
+++ b/libc/src/stdio/printf_core/CMakeLists.txt
@@ -38,9 +38,9 @@ else()
   add_subdirectory(generic)
 endif()
 
-set(target_error_converter libc.src.stdio.printf_core.${LIBC_TARGET_OS}.${LIBC_TARGET_OS}_error_converter)
+set(target_error_mapper libc.src.stdio.printf_core.${LIBC_TARGET_OS}.error_mapper)
 if(NOT TARGET ${target_error_converter})
-    set(target_error_converter libc.src.stdio.printf_core.generic.generic_error_converter)
+    set(target_error_mapper libc.src.stdio.printf_core.generic.error_mapper)
 endif()
 
 add_header_library(
@@ -155,11 +155,11 @@ add_header_library(
 )
 
 add_header_library(
-  error_converter
+  error_mapper
   HDRS
-    error_converter.h
+    error_mapper.h
   DEPENDS
-    ${target_error_converter}
+    ${target_error_mapper}
     libc.src.__support.macros.properties.architectures
 )
 
diff --git a/libc/src/stdio/printf_core/darwin/CMakeLists.txt b/libc/src/stdio/printf_core/darwin/CMakeLists.txt
deleted file mode 100644
index 89bc59097764c..0000000000000
--- a/libc/src/stdio/printf_core/darwin/CMakeLists.txt
+++ /dev/null
@@ -1,8 +0,0 @@
-add_header_library(
-  darwin_error_converter
-  HDRS
-    error_converter.h
-  DEPENDS
-    libc.src.stdio.printf_core.core_structs
-    libc.hdr.errno_macros
-)
diff --git a/libc/src/stdio/printf_core/darwin/error_converter.h b/libc/src/stdio/printf_core/darwin/error_converter.h
deleted file mode 100644
index 897c76ffe7099..0000000000000
--- a/libc/src/stdio/printf_core/darwin/error_converter.h
+++ /dev/null
@@ -1,54 +0,0 @@
-//===-- Darwin implementation of error converter ----------------*- C++ -*-===//
-//
-// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
-// See https://llvm.org/LICENSE.txt for license information.
-// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
-//
-//===----------------------------------------------------------------------===//
-
-#ifndef LLVM_LIBC_SRC_STDIO_PRINTF_CORE_DARWIN_ERROR_CONVERTER_H
-#define LLVM_LIBC_SRC_STDIO_PRINTF_CORE_DARWIN_ERROR_CONVERTER_H
-
-#include "hdr/errno_macros.h"
-#include "src/stdio/printf_core/core_structs.h"
-#include "src/stdio/printf_core/error_converter.h"
-
-namespace LIBC_NAMESPACE_DECL {
-namespace printf_core {
-
-LIBC_INLINE static int internal_error_to_errno(int internal_error) {
-  // System error occured, return error as is.
-  if (internal_error < 1001 && internal_error > 0) {
-    return internal_error;
-  }
-
-  // Map internal error to POSIX errnos.
-  switch (-internal_error) {
-  case WRITE_OK:
-    return 0;
-  case FILE_WRITE_ERROR:
-    return EIO;
-  case FILE_STATUS_ERROR:
-    return EIO;
-  case NULLPTR_WRITE_ERROR:
-    return EINVAL;
-  case INT_CONVERSION_ERROR:
-    return ERANGE;
-  case FIXED_POINT_CONVERSION_ERROR:
-    return EINVAL;
-  case ALLOCATION_ERROR:
-    return ENOMEM;
-  case OVERFLOW_ERROR:
-    return EOVERFLOW;
-  default:
-    LIBC_ASSERT(
-        false &&
-        "Invalid internal printf error code passed to internal_error_to_errno");
-    return EINVAL;
-  }
-}
-
-} // namespace printf_core
-} // namespace LIBC_NAMESPACE_DECL
-
-#endif // LLVM_LIBC_SRC_STDIO_PRINTF_CORE_DARWIN_ERROR_CONVERTER_H
diff --git a/libc/src/stdio/printf_core/error_converter.h b/libc/src/stdio/printf_core/error_mapper.h
similarity index 55%
rename from libc/src/stdio/printf_core/error_converter.h
rename to libc/src/stdio/printf_core/error_mapper.h
index f2fe303203fc7..23030930133a1 100644
--- a/libc/src/stdio/printf_core/error_converter.h
+++ b/libc/src/stdio/printf_core/error_mapper.h
@@ -1,4 +1,4 @@
-//===-- Error converter for printf ------------------------------*- C++ -*-===//
+//===-- Error mapper for printf ---------------------------------*- C++ -*-===//
 //
 // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
 // See https://llvm.org/LICENSE.txt for license information.
@@ -6,18 +6,16 @@
 //
 //===----------------------------------------------------------------------===//
 
-#ifndef LLVM_LIBC_SRC_STDIO_PRINTF_CORE_ERROR_CONVERTER_H
-#define LLVM_LIBC_SRC_STDIO_PRINTF_CORE_ERROR_CONVERTER_H
+#ifndef LLVM_LIBC_SRC_STDIO_PRINTF_CORE_ERROR_MAPPER_H
+#define LLVM_LIBC_SRC_STDIO_PRINTF_CORE_ERROR_MAPPER_H
 
 #include "src/__support/macros/properties/architectures.h"
 
 // Maps internal errors to the available errnos on the platform.
 #if defined(__linux__)
-#include "linux/error_converter.h"
-#elif defined(__APPLE__)
-#include "darwin/error_converter.h"
+#include "linux/error_mapper.h"
 #else
-#include "generic/error_converter.h"
+#include "generic/error_mapper.h"
 #endif
 
-#endif // LLVM_LIBC_SRC_STDIO_PRINTF_CORE_ERROR_CONVERTER_H
+#endif // LLVM_LIBC_SRC_STDIO_PRINTF_CORE_ERROR_MAPPER_H
diff --git a/libc/src/stdio/printf_core/generic/CMakeLists.txt b/libc/src/stdio/printf_core/generic/CMakeLists.txt
index f7fc250734645..2f0143d992e31 100644
--- a/libc/src/stdio/printf_core/generic/CMakeLists.txt
+++ b/libc/src/stdio/printf_core/generic/CMakeLists.txt
@@ -1,7 +1,7 @@
 add_header_library(
-  generic_error_converter
+  error_mapper
   HDRS
-    error_converter.h
+    error_mapper.h
   DEPENDS
     libc.src.stdio.printf_core.core_structs
     libc.hdr.errno_macros
diff --git a/libc/src/stdio/printf_core/generic/error_converter.h b/libc/src/stdio/printf_core/generic/error_mapper.h
similarity index 79%
rename from libc/src/stdio/printf_core/generic/error_converter.h
rename to libc/src/stdio/printf_core/generic/error_mapper.h
index 2f59a105415c7..d8cdd2cc2dbaa 100644
--- a/libc/src/stdio/printf_core/generic/error_converter.h
+++ b/libc/src/stdio/printf_core/generic/error_mapper.h
@@ -1,4 +1,4 @@
-//===-- Generic implementation of error converter ---------------*- C++ -*-===//
+//===-- Generic implementation of error mapper ------------------*- C++ -*-===//
 //
 // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
 // See https://llvm.org/LICENSE.txt for license information.
@@ -6,12 +6,12 @@
 //
 //===----------------------------------------------------------------------===//
 
-#ifndef LLVM_LIBC_SRC_STDIO_PRINTF_CORE_GENERIC_ERROR_CONVERTER_H
-#define LLVM_LIBC_SRC_STDIO_PRINTF_CORE_GENERIC_ERROR_CONVERTER_H
+#ifndef LLVM_LIBC_SRC_STDIO_PRINTF_CORE_GENERIC_ERROR_MAPPER_H
+#define LLVM_LIBC_SRC_STDIO_PRINTF_CORE_GENERIC_ERROR_MAPPER_H
 
 #include "hdr/errno_macros.h"
 #include "src/stdio/printf_core/core_structs.h"
-#include "src/stdio/printf_core/error_converter.h"
+#include "src/stdio/printf_core/error_mapper.h"
 
 namespace LIBC_NAMESPACE_DECL {
 namespace printf_core {
@@ -46,4 +46,4 @@ LIBC_INLINE static int internal_error_to_errno(int internal_error) {
 } // namespace printf_core
 } // namespace LIBC_NAMESPACE_DECL
 
-#endif // LLVM_LIBC_SRC_STDIO_PRINTF_CORE_GENERIC_ERROR_CONVERTER_H
+#endif // LLVM_LIBC_SRC_STDIO_PRINTF_CORE_GENERIC_ERROR_MAPPER_H
diff --git a/libc/src/stdio/printf_core/linux/CMakeLists.txt b/libc/src/stdio/printf_core/linux/CMakeLists.txt
index 4b193866a1914..2f0143d992e31 100644
--- a/libc/src/stdio/printf_core/linux/CMakeLists.txt
+++ b/libc/src/stdio/printf_core/linux/CMakeLists.txt
@@ -1,7 +1,7 @@
 add_header_library(
-  linux_error_converter
+  error_mapper
   HDRS
-    error_converter.h
+    error_mapper.h
   DEPENDS
     libc.src.stdio.printf_core.core_structs
     libc.hdr.errno_macros
diff --git a/libc/src/stdio/printf_core/linux/error_converter.h b/libc/src/stdio/printf_core/linux/error_mapper.h
similarity index 80%
rename from libc/src/stdio/printf_core/linux/error_converter.h
rename to libc/src/stdio/printf_core/linux/error_mapper.h
index 9de24858bb3cf..3c2fe663072d0 100644
--- a/libc/src/stdio/printf_core/linux/error_converter.h
+++ b/libc/src/stdio/printf_core/linux/error_mapper.h
@@ -1,4 +1,4 @@
-//===-- Linux implementation of error converter -----------------*- C++ -*-===//
+//===-- Linux implementation of error mapper --------------------*- C++ -*-===//
 //
 // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
 // See https://llvm.org/LICENSE.txt for license information.
@@ -6,12 +6,12 @@
 //
 //===----------------------------------------------------------------------===//
 
-#ifndef LLVM_LIBC_SRC_STDIO_PRINTF_CORE_LINUX_ERROR_CONVERTER_H
-#define LLVM_LIBC_SRC_STDIO_PRINTF_CORE_LINUX_ERROR_CONVERTER_H
+#ifndef LLVM_LIBC_SRC_STDIO_PRINTF_CORE_LINUX_ERROR_MAPPER_H
+#define LLVM_LIBC_SRC_STDIO_PRINTF_CORE_LINUX_ERROR_MAPPER_H
 
 #include "hdr/errno_macros.h"
 #include "src/stdio/printf_core/core_structs.h"
-#include "src/stdio/printf_core/error_converter.h"
+#include "src/stdio/printf_core/error_mapper.h"
 
 namespace LIBC_NAMESPACE_DECL {
 namespace printf_core {
@@ -51,4 +51,4 @@ LIBC_INLINE static int internal_error_to_errno(int internal_error) {
 } // namespace printf_core
 } // namespace LIBC_NAMESPACE_DECL
 
-#endif // LLVM_LIBC_SRC_STDIO_PRINTF_CORE_LINUX_ERROR_CONVERTER_H
+#endif // LLVM_LIBC_SRC_STDIO_PRINTF_CORE_LINUX_ERROR_MAPPER_H
diff --git a/libc/src/stdio/printf_core/vfprintf_internal.h b/libc/src/stdio/printf_core/vfprintf_internal.h
index 4e60248810f27..564441d3bf51a 100644
--- a/libc/src/stdio/printf_core/vfprintf_internal.h
+++ b/libc/src/stdio/printf_core/vfprintf_internal.h
@@ -50,6 +50,8 @@ LIBC_INLINE void funlockfile(::FILE *f) { ::funlockfile(f); }
 
 LIBC_INLINE FileIOResult fwrite_unlocked(const void *ptr, size_t size,
                                          size_t nmemb, ::FILE *f) {
+  // Need to use system errno in this case, as system write will set this errno
+  // which we need to propagate back into our code.
   return {::fwrite_unlocked(ptr, size, nmemb, f), errno};
 }
 #endif // LIBC_COPT_STDIO_USE_SYSTEM_FILE
diff --git a/libc/src/stdio/snprintf.cpp b/libc/src/stdio/snprintf.cpp
index 25da3a9dbd2c5..206c50d1b41a4 100644
--- a/libc/src/stdio/snprintf.cpp
+++ b/libc/src/stdio/snprintf.cpp
@@ -8,11 +8,12 @@
 
 #include "src/stdio/snprintf.h"
 
+#include "src/__support/CPP/limits.h"
 #include "src/__support/arg_list.h"
 #include "src/__support/libc_errno.h"
 #include "src/__support/macros/config.h"
 #include "src/stdio/printf_core/core_structs.h"
-#include "src/stdio/printf_core/error_converter.h"
+#include "src/stdio/printf_core/error_mapper.h"
 #include "src/stdio/printf_core/printf_main.h"
 #include "src/stdio/printf_core/writer.h"
 
diff --git a/libc/src/stdio/sprintf.cpp b/libc/src/stdio/sprintf.cpp
index 09f222927afd6..9e9ecdfdf38cc 100644
--- a/libc/src/stdio/sprintf.cpp
+++ b/libc/src/stdio/sprintf.cpp
@@ -13,7 +13,7 @@
 #include "src/__support/libc_errno.h"
 #include "src/__support/macros/config.h"
 #include "src/stdio/printf_core/core_structs.h"
-#include "src/stdio/printf_core/error_converter.h"
+#include "src/stdio/printf_core/error_mapper.h"
 #include "src/stdio/printf_core/printf_main.h"
 #include "src/stdio/printf_core/writer.h"
 
diff --git a/libc/src/stdio/vasprintf.cpp b/libc/src/stdio/vasprintf.cpp
index d1ac9f4342824..4bc6a5992d5c8 100644
--- a/libc/src/stdio/vasprintf.cpp
+++ b/libc/src/stdio/vasprintf.cpp
@@ -7,10 +7,11 @@
 //===----------------------------------------------------------------------===//
 
 #include "src/stdio/vasprintf.h"
+#include "src/__support/CPP/limits.h"
 #include "src/__support/arg_list.h"
 #include "src/__support/libc_errno.h"
 #include "src/stdio/printf_core/core_structs.h"
-#include "src/stdio/printf_core/error_converter.h"
+#include "src/stdio/printf_core/error_mapper.h"
 #include "src/stdio/printf_core/vasprintf_internal.h"
 
 namespace LIBC_NAMESPACE_DECL {
diff --git a/libc/src/stdio/vsnprintf.cpp b/libc/src/stdio/vsnprintf.cpp
index 01ece7d04df7e..ba3568fbe78b3 100644
--- a/libc/src/stdio/vsnprintf.cpp
+++ b/libc/src/stdio/vsnprintf.cpp
@@ -8,11 +8,12 @@
 
 #include "src/stdio/vsnprintf.h"
 
+#include "src/__support/CPP/limits.h"
 #include "src/__support/arg_list.h"
 #include "src/__support/libc_errno.h"
 #include "src/__support/macros/config.h"
 #include "src/stdio/printf_core/core_structs.h"
-#include "src/stdio/printf_core/error_converter.h"
+#include "src/stdio/printf_core/error_mapper.h"
 #include "src/stdio/printf_core/printf_main.h"
 #include "src/stdio/printf_core/writer.h"
 
diff --git a/libc/src/stdio/vsprintf.cpp b/libc/src/stdio/vsprintf.cpp
index 3b1a4a37bc405..65c223a442d4b 100644
--- a/libc/src/stdio/vsprintf.cpp
+++ b/libc/src/stdio/vsprintf.cpp
@@ -13,7 +13,7 @@
 #include "src/__support/libc_errno.h"
 #include "src/__support/macros/config.h"
 #include "src/stdio/printf_core/core_structs.h"
-#include "src/stdio/printf_core/error_converter.h"
+#include "src/stdio/printf_core/error_mapper.h"
 #include "src/stdio/printf_core/printf_main.h"
 #include "src/stdio/printf_core/writer.h"
 
diff --git a/libc/src/stdlib/CMakeLists.txt b/libc/src/stdlib/CMakeLists.txt
index 379fb39424f55..1ccdcc8bec148 100644
--- a/libc/src/stdlib/CMakeLists.txt
+++ b/libc/src/stdlib/CMakeLists.txt
@@ -73,7 +73,8 @@ add_entrypoint_object(
     strfromf.h
   DEPENDS
     .str_from_util
-    libc.src.stdio.printf_core.error_converter
+    libc.src.__support.CPP.limits
+    libc.src.stdio.printf_core.error_mapper
 )
 
 add_entrypoint_object(
@@ -84,7 +85,8 @@ add_entrypoint_object(
     strfromd.h
   DEPENDS
     .str_from_util
-    libc.src.stdio.printf_core.error_converter
+    libc.src.__support.CPP.limits
+    libc.src.stdio.printf_core.error_mapper
 )
 
 add_entrypoint_object(
@@ -95,7 +97,8 @@ add_entrypoint_object(
     strfroml.h
   DEPENDS
     .str_from_util
-    libc.src.stdio.printf_core.error_converter
+    libc.src.__support.CPP.limits
+    libc.src.stdio.printf_core.error_mapper
 )
 
 add_header_library(
diff --git a/libc/src/stdlib/strfromd.cpp b/libc/src/stdlib/strfromd.cpp
index dce1b5e5f253f..f970e22010201 100644
--- a/libc/src/stdlib/strfromd.cpp
+++ b/libc/src/stdlib/strfromd.cpp
@@ -7,9 +7,10 @@
 //===----------------------------------------------------------------------===//
 
 #include "src/stdlib/strfromd.h"
+#include "src/__support/CPP/limits.h"
 #include "src/__support/macros/config.h"
 #include "src/stdio/printf_core/core_structs.h"
-#include "src/stdio/printf_core/error_converter.h"
+#include "src/stdio/printf_core/error_mapper.h"
 #include "src/stdlib/str_from_util.h"
 
 namespace LIBC_NAMESPACE_DECL {
diff --git a/libc/src/stdlib/strfromf.cpp b/libc/src/stdlib/strfromf.cpp
index fe811ee27242f..55ede003134b5 100644
--- a/libc/src/stdlib/strfromf.cpp
+++ b/libc/src/stdlib/strfromf.cpp
@@ -7,9 +7,10 @@
 //===----------------------------------------------------------------------===//
 
 #include "src/stdlib/strfromf.h"
+#include "src/__support/CPP/limits.h"
 #include "src/__support/macros/config.h"
 #include "src/stdio/printf_core/core_structs.h"
-#include "src/stdio/printf_core/error_converter.h"
+#include "src/stdio/printf_core/error_mapper.h"
 #include "src/stdlib/str_from_util.h"
 
 namespace LIBC_NAMESPACE_DECL {
diff --git a/libc/src/stdlib/strfroml.cpp b/libc/src/stdlib/strfroml.cpp
index ef27312d026c2..37d18738902bc 100644
--- a/libc/src/stdlib/strfroml.cpp
+++ b/libc/src/stdlib/strfroml.cpp
@@ -7,9 +7,10 @@
 //===----------------------------------------------------------------------===//
 
 #include "src/stdlib/strfroml.h"
+#include "src/__support/CPP/limits.h"
 #include "src/__support/macros/config.h"
 #include "src/stdio/printf_core/core_structs.h"
-#include "src/stdio/printf_core/error_converter.h"
+#include "src/stdio/printf_core/error_mapper.h"
 #include "src/stdlib/str_from_util.h"
 
 namespace LIBC_NAMESPACE_DECL {
diff --git a/libc/test/src/stdio/fprintf_test.cpp b/libc/test/src/stdio/fprintf_test.cpp
index 1d081f00a31a4..7d36bd30854b8 100644
--- a/libc/test/src/stdio/fprintf_test.cpp
+++ b/libc/test/src/stdio/fprintf_test.cpp
@@ -96,7 +96,8 @@ TEST(LlvmLibcFPrintfTest, NullPtrCheck) {
   ::FILE *file = printf_test::fopen(FILE_PATH, "w");
   ASSERT_FALSE(file == nullptr);
 
-  int ret = LIBC_NAMESPACE::fprintf(file, "hello %n", (int *)nullptr);
+  int ret =
+      LIBC_NAMESPACE::fprintf(file, "hello %s", static_cast<int *>(nullptr));
   EXPECT_LT(ret, 0);
   ASSERT_ERRNO_EQ(EINVAL);
 

>From 506b66b9ed10cbbf75a334e7ab47c5650eebc1ff Mon Sep 17 00:00:00 2001
From: Marcell Leleszi <mleleszi at google.com>
Date: Tue, 4 Nov 2025 15:26:44 +0000
Subject: [PATCH 27/27] Fix build issues

---
 libc/src/stdio/asprintf.cpp           | 2 +-
 libc/src/stdio/baremetal/printf.cpp   | 2 +-
 libc/src/stdio/baremetal/vprintf.cpp  | 2 +-
 libc/src/stdio/generic/fprintf.cpp    | 2 +-
 libc/src/stdio/generic/printf.cpp     | 2 +-
 libc/src/stdio/generic/vfprintf.cpp   | 2 +-
 libc/src/stdio/generic/vprintf.cpp    | 2 +-
 libc/src/stdio/snprintf.cpp           | 2 +-
 libc/src/stdio/sprintf.cpp            | 2 +-
 libc/src/stdio/vasprintf.cpp          | 2 +-
 libc/src/stdio/vsnprintf.cpp          | 2 +-
 libc/src/stdio/vsprintf.cpp           | 2 +-
 libc/src/stdlib/strfromd.cpp          | 3 ++-
 libc/src/stdlib/strfroml.cpp          | 3 ++-
 libc/test/src/stdio/snprintf_test.cpp | 2 +-
 15 files changed, 17 insertions(+), 15 deletions(-)

diff --git a/libc/src/stdio/asprintf.cpp b/libc/src/stdio/asprintf.cpp
index 083f40c1f19fa..0991dfca6a059 100644
--- a/libc/src/stdio/asprintf.cpp
+++ b/libc/src/stdio/asprintf.cpp
@@ -31,7 +31,7 @@ LLVM_LIBC_FUNCTION(int, asprintf,
     libc_errno = printf_core::internal_error_to_errno(ret_val.error());
     return -1;
   }
-  if (ret_val.value() > cpp::numeric_limits<int>::max()) {
+  if (ret_val.value() > static_cast<size_t>(cpp::numeric_limits<int>::max())) {
     libc_errno =
         printf_core::internal_error_to_errno(-printf_core::OVERFLOW_ERROR);
     return -1;
diff --git a/libc/src/stdio/baremetal/printf.cpp b/libc/src/stdio/baremetal/printf.cpp
index 0c6c9ad338c9f..5a9b19ff20471 100644
--- a/libc/src/stdio/baremetal/printf.cpp
+++ b/libc/src/stdio/baremetal/printf.cpp
@@ -57,7 +57,7 @@ LLVM_LIBC_FUNCTION(int, printf, (const char *__restrict format, ...)) {
     return -1;
   }
 
-  if (retval.value() > cpp::numeric_limits<int>::max()) {
+  if (retval.value() > static_cast<size_t>(cpp::numeric_limits<int>::max())) {
     libc_errno =
         printf_core::internal_error_to_errno(-printf_core::OVERFLOW_ERROR);
     return -1;
diff --git a/libc/src/stdio/baremetal/vprintf.cpp b/libc/src/stdio/baremetal/vprintf.cpp
index d2f586c70ad1c..c172b368d15f3 100644
--- a/libc/src/stdio/baremetal/vprintf.cpp
+++ b/libc/src/stdio/baremetal/vprintf.cpp
@@ -55,7 +55,7 @@ LLVM_LIBC_FUNCTION(int, vprintf,
     return -1;
   }
 
-  if (retval.value() > cpp::numeric_limits<int>::max()) {
+  if (retval.value() > static_cast<size_t>(cpp::numeric_limits<int>::max())) {
     libc_errno =
         printf_core::internal_error_to_errno(-printf_core::OVERFLOW_ERROR);
     return -1;
diff --git a/libc/src/stdio/generic/fprintf.cpp b/libc/src/stdio/generic/fprintf.cpp
index b07f2528fe11d..b2033901557a0 100644
--- a/libc/src/stdio/generic/fprintf.cpp
+++ b/libc/src/stdio/generic/fprintf.cpp
@@ -35,7 +35,7 @@ LLVM_LIBC_FUNCTION(int, fprintf,
     libc_errno = printf_core::internal_error_to_errno(ret_val.error());
     return -1;
   }
-  if (ret_val.value() > cpp::numeric_limits<int>::max()) {
+  if (ret_val.value() > static_cast<size_t>(cpp::numeric_limits<int>::max())) {
     libc_errno =
         printf_core::internal_error_to_errno(-printf_core::OVERFLOW_ERROR);
     return -1;
diff --git a/libc/src/stdio/generic/printf.cpp b/libc/src/stdio/generic/printf.cpp
index d6d4adcefb3b1..8d159d5c70870 100644
--- a/libc/src/stdio/generic/printf.cpp
+++ b/libc/src/stdio/generic/printf.cpp
@@ -40,7 +40,7 @@ LLVM_LIBC_FUNCTION(int, printf, (const char *__restrict format, ...)) {
     libc_errno = printf_core::internal_error_to_errno(ret_val.error());
     return -1;
   }
-  if (ret_val.value() > cpp::numeric_limits<int>::max()) {
+  if (ret_val.value() > static_cast<size_t>(cpp::numeric_limits<int>::max())) {
     libc_errno =
         printf_core::internal_error_to_errno(-printf_core::OVERFLOW_ERROR);
     return -1;
diff --git a/libc/src/stdio/generic/vfprintf.cpp b/libc/src/stdio/generic/vfprintf.cpp
index c00352d1dd666..a26f082ed9347 100644
--- a/libc/src/stdio/generic/vfprintf.cpp
+++ b/libc/src/stdio/generic/vfprintf.cpp
@@ -32,7 +32,7 @@ LLVM_LIBC_FUNCTION(int, vfprintf,
     libc_errno = printf_core::internal_error_to_errno(ret_val.error());
     return -1;
   }
-  if (ret_val.value() > cpp::numeric_limits<int>::max()) {
+  if (ret_val.value() > static_cast<size_t>(cpp::numeric_limits<int>::max())) {
     libc_errno =
         printf_core::internal_error_to_errno(-printf_core::OVERFLOW_ERROR);
     return -1;
diff --git a/libc/src/stdio/generic/vprintf.cpp b/libc/src/stdio/generic/vprintf.cpp
index 1c0837fd5d441..ae2160219f2bb 100644
--- a/libc/src/stdio/generic/vprintf.cpp
+++ b/libc/src/stdio/generic/vprintf.cpp
@@ -38,7 +38,7 @@ LLVM_LIBC_FUNCTION(int, vprintf,
     libc_errno = printf_core::internal_error_to_errno(ret_val.error());
     return -1;
   }
-  if (ret_val.value() > cpp::numeric_limits<int>::max()) {
+  if (ret_val.value() > static_cast<size_t>(cpp::numeric_limits<int>::max())) {
     libc_errno =
         printf_core::internal_error_to_errno(-printf_core::OVERFLOW_ERROR);
     return -1;
diff --git a/libc/src/stdio/snprintf.cpp b/libc/src/stdio/snprintf.cpp
index 206c50d1b41a4..d95195f6f485f 100644
--- a/libc/src/stdio/snprintf.cpp
+++ b/libc/src/stdio/snprintf.cpp
@@ -44,7 +44,7 @@ LLVM_LIBC_FUNCTION(int, snprintf,
   if (buffsz > 0) // if the buffsz is 0 the buffer may be a null pointer.
     wb.buff[wb.buff_cur] = '\0';
 
-  if (ret_val.value() > cpp::numeric_limits<int>::max()) {
+  if (ret_val.value() > static_cast<size_t>(cpp::numeric_limits<int>::max())) {
     libc_errno =
         printf_core::internal_error_to_errno(-printf_core::OVERFLOW_ERROR);
     return -1;
diff --git a/libc/src/stdio/sprintf.cpp b/libc/src/stdio/sprintf.cpp
index 9e9ecdfdf38cc..2a9b6ea7c5e50 100644
--- a/libc/src/stdio/sprintf.cpp
+++ b/libc/src/stdio/sprintf.cpp
@@ -43,7 +43,7 @@ LLVM_LIBC_FUNCTION(int, sprintf,
   }
   wb.buff[wb.buff_cur] = '\0';
 
-  if (ret_val.value() > cpp::numeric_limits<int>::max()) {
+  if (ret_val.value() > static_cast<size_t>(cpp::numeric_limits<int>::max())) {
     libc_errno =
         printf_core::internal_error_to_errno(-printf_core::OVERFLOW_ERROR);
     return -1;
diff --git a/libc/src/stdio/vasprintf.cpp b/libc/src/stdio/vasprintf.cpp
index 4bc6a5992d5c8..bd77cd8864312 100644
--- a/libc/src/stdio/vasprintf.cpp
+++ b/libc/src/stdio/vasprintf.cpp
@@ -27,7 +27,7 @@ LLVM_LIBC_FUNCTION(int, vasprintf,
     libc_errno = printf_core::internal_error_to_errno(ret_val.error());
     return -1;
   }
-  if (ret_val.value() > cpp::numeric_limits<int>::max()) {
+  if (ret_val.value() > static_cast<size_t>(cpp::numeric_limits<int>::max())) {
     libc_errno =
         printf_core::internal_error_to_errno(-printf_core::OVERFLOW_ERROR);
     return -1;
diff --git a/libc/src/stdio/vsnprintf.cpp b/libc/src/stdio/vsnprintf.cpp
index ba3568fbe78b3..5d936360c0857 100644
--- a/libc/src/stdio/vsnprintf.cpp
+++ b/libc/src/stdio/vsnprintf.cpp
@@ -41,7 +41,7 @@ LLVM_LIBC_FUNCTION(int, vsnprintf,
   if (buffsz > 0) // if the buffsz is 0 the buffer may be a null pointer.
     wb.buff[wb.buff_cur] = '\0';
 
-  if (ret_val.value() > cpp::numeric_limits<int>::max()) {
+  if (ret_val.value() > static_cast<size_t>(cpp::numeric_limits<int>::max())) {
     libc_errno =
         printf_core::internal_error_to_errno(-printf_core::OVERFLOW_ERROR);
     return -1;
diff --git a/libc/src/stdio/vsprintf.cpp b/libc/src/stdio/vsprintf.cpp
index 65c223a442d4b..f9cf8118534f6 100644
--- a/libc/src/stdio/vsprintf.cpp
+++ b/libc/src/stdio/vsprintf.cpp
@@ -40,7 +40,7 @@ LLVM_LIBC_FUNCTION(int, vsprintf,
   }
   wb.buff[wb.buff_cur] = '\0';
 
-  if (ret_val.value() > cpp::numeric_limits<int>::max()) {
+  if (ret_val.value() > static_cast<size_t>(cpp::numeric_limits<int>::max())) {
     libc_errno =
         printf_core::internal_error_to_errno(-printf_core::OVERFLOW_ERROR);
     return -1;
diff --git a/libc/src/stdlib/strfromd.cpp b/libc/src/stdlib/strfromd.cpp
index f970e22010201..71e257f08645b 100644
--- a/libc/src/stdlib/strfromd.cpp
+++ b/libc/src/stdlib/strfromd.cpp
@@ -39,7 +39,8 @@ LLVM_LIBC_FUNCTION(int, strfromd,
   if (n > 0)
     wb.buff[wb.buff_cur] = '\0';
 
-  if (writer.get_chars_written() > cpp::numeric_limits<int>::max()) {
+  if (writer.get_chars_written() >
+      static_cast<size_t>(cpp::numeric_limits<int>::max())) {
     libc_errno =
         printf_core::internal_error_to_errno(-printf_core::OVERFLOW_ERROR);
     return -1;
diff --git a/libc/src/stdlib/strfroml.cpp b/libc/src/stdlib/strfroml.cpp
index 37d18738902bc..31668a0323c93 100644
--- a/libc/src/stdlib/strfroml.cpp
+++ b/libc/src/stdlib/strfroml.cpp
@@ -44,7 +44,8 @@ LLVM_LIBC_FUNCTION(int, strfroml,
   if (n > 0)
     wb.buff[wb.buff_cur] = '\0';
 
-  if (writer.get_chars_written() > cpp::numeric_limits<int>::max()) {
+  if (writer.get_chars_written() >
+      static_cast<size_t>(cpp::numeric_limits<int>::max())) {
     libc_errno =
         printf_core::internal_error_to_errno(-printf_core::OVERFLOW_ERROR);
     return -1;
diff --git a/libc/test/src/stdio/snprintf_test.cpp b/libc/test/src/stdio/snprintf_test.cpp
index 1062f952d7429..95507e0885dbf 100644
--- a/libc/test/src/stdio/snprintf_test.cpp
+++ b/libc/test/src/stdio/snprintf_test.cpp
@@ -72,5 +72,5 @@ TEST(LlvmLibcSNPrintfTest, CharsWrittenOverflow) {
   int int_max = LIBC_NAMESPACE::cpp::numeric_limits<int>::max();
   int written = LIBC_NAMESPACE::snprintf(buff, 0, "%*stest", int_max, "");
   EXPECT_LT(written, 0);
-  ASSERT_ERRNO_EQ(EOVERFLOW);
+  ASSERT_ERRNO_FAILURE();
 }



More information about the libc-commits mailing list