[libc-commits] [libc] [libc] Add printf error handling (PR #162876)
Marcell Leleszi via libc-commits
libc-commits at lists.llvm.org
Thu Oct 16 23:09:12 PDT 2025
https://github.com/mleleszi updated https://github.com/llvm/llvm-project/pull/162876
>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/18] 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/18] 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/18] 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/18] 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/18] 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/18] 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/18] 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/18] 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/18] 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/18] 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/18] 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/18] 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/18] 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/18] 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/18] 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/18] 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/18] 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/18] 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;
}
More information about the libc-commits
mailing list