[libc-commits] [libc] [libc] Support %lc in printf (PR #169983)

Shubh Pachchigar via libc-commits libc-commits at lists.llvm.org
Fri Dec 26 10:20:04 PST 2025


https://github.com/shubhe25p updated https://github.com/llvm/llvm-project/pull/169983

>From c7886e01dead9a1f359321106eb0d247e3a3f96e Mon Sep 17 00:00:00 2001
From: "shubh at DOE" <shubhp at mbm3a24.local>
Date: Sat, 20 Dec 2025 02:34:05 -0800
Subject: [PATCH 1/7] [libc] Support %lc in printf

Add support for %lc in printf by calling internal
wcrtomb function and relevant end-to-end snprintf
test. Additionally, printf parser can also
recognize length modifier. Added a flag to disable
wchar support on windows platform.
---
 libc/config/config.json                       |  4 ++
 libc/config/windows/config.json               |  8 ++++
 libc/docs/configure.rst                       |  1 +
 libc/docs/dev/printf_behavior.rst             |  7 ++++
 libc/src/stdio/printf_core/CMakeLists.txt     | 15 +++++++
 libc/src/stdio/printf_core/char_converter.h   | 32 ++++++++++++++-
 libc/src/stdio/printf_core/parser.h           | 24 +++++++++---
 libc/test/src/stdio/CMakeLists.txt            | 11 ++++++
 .../test/src/stdio/printf_core/CMakeLists.txt |  8 ++++
 libc/test/src/stdio/sprintf_test.cpp          | 39 +++++++++++++++++++
 10 files changed, 142 insertions(+), 7 deletions(-)
 create mode 100644 libc/config/windows/config.json

diff --git a/libc/config/config.json b/libc/config/config.json
index f981c433b2c7c..296d2e539c23d 100644
--- a/libc/config/config.json
+++ b/libc/config/config.json
@@ -52,6 +52,10 @@
     "LIBC_CONF_PRINTF_RUNTIME_DISPATCH": {
       "value": true,
       "doc": "Use dynamic dispatch for the output mechanism to reduce code size."
+    },
+    "LIBC_CONF_PRINTF_DISABLE_WIDE": {
+      "value": false,
+      "doc": "Disable handling wide characters for printf and friends."
     }
   },
   "scanf": {
diff --git a/libc/config/windows/config.json b/libc/config/windows/config.json
new file mode 100644
index 0000000000000..75bb5e9debdde
--- /dev/null
+++ b/libc/config/windows/config.json
@@ -0,0 +1,8 @@
+{
+    "printf": {
+      "LIBC_CONF_PRINTF_DISABLE_WIDE": {
+        "value": "true",
+        "doc": "Disable handling wide characters for printf and friends."
+      }
+    }
+}
\ No newline at end of file
diff --git a/libc/docs/configure.rst b/libc/docs/configure.rst
index 81888bb07153c..8f5074ec23475 100644
--- a/libc/docs/configure.rst
+++ b/libc/docs/configure.rst
@@ -49,6 +49,7 @@ to learn about the defaults for your platform and target.
     - ``LIBC_CONF_PRINTF_FLOAT_TO_STR_USE_FLOAT320``: Use an alternative printf float implementation based on 320-bit floats
     - ``LIBC_CONF_PRINTF_FLOAT_TO_STR_USE_MEGA_LONG_DOUBLE_TABLE``: Use large table for better printf long double performance.
     - ``LIBC_CONF_PRINTF_RUNTIME_DISPATCH``: Use dynamic dispatch for the output mechanism to reduce code size.
+    - ``LIBC_CONF_PRINTF_DISABLE_WIDE``: Disable handling of %lc and %ls on unsupported platforms.
 * **"pthread" options**
     - ``LIBC_CONF_RAW_MUTEX_DEFAULT_SPIN_COUNT``: Default number of spins before blocking if a mutex is in contention (default to 100).
     - ``LIBC_CONF_RWLOCK_DEFAULT_SPIN_COUNT``: Default number of spins before blocking if a rwlock is in contention (default to 100).
diff --git a/libc/docs/dev/printf_behavior.rst b/libc/docs/dev/printf_behavior.rst
index 01ab128a1f238..ba0578aee3fd8 100644
--- a/libc/docs/dev/printf_behavior.rst
+++ b/libc/docs/dev/printf_behavior.rst
@@ -71,6 +71,13 @@ conversions (%r, %k); any fixed point number conversion will be treated as
 invalid. This reduces code size. This has no effect if the current compiler does
 not support fixed point numbers.
 
+LIBC_COPT_PRINTF_DISABLE_WIDE
+--------------------------------
+When set, this flag disables support for wide characters (%lc and %ls). Any
+conversions will be ignored. This reduces code size. This will be set by default
+on windows platforms as current printf implementation does not support UTF-16 wide
+characters.
+
 LIBC_COPT_PRINTF_NO_NULLPTR_CHECKS
 ----------------------------------
 When set, this flag disables the nullptr checks in %n and %s.
diff --git a/libc/src/stdio/printf_core/CMakeLists.txt b/libc/src/stdio/printf_core/CMakeLists.txt
index 624129b2b36e7..06d74937def17 100644
--- a/libc/src/stdio/printf_core/CMakeLists.txt
+++ b/libc/src/stdio/printf_core/CMakeLists.txt
@@ -43,6 +43,19 @@ if(NOT TARGET ${target_error_mapper})
     set(target_error_mapper libc.src.stdio.printf_core.generic.error_mapper)
 endif()
 
+if(LIBC_CONF_PRINTF_DISABLE_WIDE)
+  set(wchar_deps "")
+  list(APPEND printf_config_copts "-DLIBC_COPT_PRINTF_DISABLE_WIDE")
+else()
+  set(wchar_deps
+    libc.hdr.types.wchar_t
+    libc.hdr.types.wint_t
+    libc.hdr.wchar_macros
+    libc.src.__support.wchar.wcrtomb
+    libc.src.__support.wchar.mbstate
+  )
+endif()
+
 add_header_library(
   printf_config
   HDRS
@@ -76,6 +89,7 @@ add_header_library(
     libc.src.__support.CPP.string_view
     libc.src.__support.CPP.type_traits
     libc.src.__support.common
+    ${wchar_deps}
 )
 
 add_header_library(
@@ -125,6 +139,7 @@ add_header_library(
     libc.src.__support.uint128
     libc.src.__support.StringUtil.error_to_string
     libc.src.string.memory_utils.inline_memcpy
+    ${wchar_deps}
 )
 
 add_header_library(
diff --git a/libc/src/stdio/printf_core/char_converter.h b/libc/src/stdio/printf_core/char_converter.h
index fd2eb2553887a..53417f3e050d3 100644
--- a/libc/src/stdio/printf_core/char_converter.h
+++ b/libc/src/stdio/printf_core/char_converter.h
@@ -1,4 +1,4 @@
-//===-- String Converter for printf -----------------------------*- C++ -*-===//
+//===-- Character Converter for printf --------------------------*- C++ -*-===//
 //
 // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
 // See https://llvm.org/LICENSE.txt for license information.
@@ -9,6 +9,14 @@
 #ifndef LLVM_LIBC_SRC_STDIO_PRINTF_CORE_CHAR_CONVERTER_H
 #define LLVM_LIBC_SRC_STDIO_PRINTF_CORE_CHAR_CONVERTER_H
 
+#ifndef LIBC_COPT_PRINTF_DISABLE_WIDE
+#include "hdr/types/wchar_t.h"
+#include "hdr/types/wint_t.h"
+#include "hdr/wchar_macros.h"
+#include "src/__support/wchar/mbstate.h"
+#include "src/__support/wchar/wcrtomb.h"
+#endif // LIBC_COPT_PRINTF_DISABLE_WIDE
+
 #include "src/__support/macros/config.h"
 #include "src/stdio/printf_core/converter_utils.h"
 #include "src/stdio/printf_core/core_structs.h"
@@ -33,7 +41,27 @@ LIBC_INLINE int convert_char(Writer<write_mode> *writer,
     RET_IF_RESULT_NEGATIVE(writer->write(' ', padding_spaces));
   }
 
-  RET_IF_RESULT_NEGATIVE(writer->write(c));
+#ifndef LIBC_COPT_PRINTF_DISABLE_WIDE
+  if (to_conv.length_modifier == LengthModifier::l) {
+    wint_t wi = static_cast<wint_t>(to_conv.conv_val_raw);
+
+    if (wi == WEOF) {
+      return -1;
+    }
+
+    char mb_str[MB_LEN_MAX];
+    internal::mbstate mbstate;
+    wchar_t wc = static_cast<wchar_t>(wi);
+
+    auto ret = internal::wcrtomb(mb_str, wc, &mbstate);
+    if (!ret.has_value()) {
+      return -1;
+    }
+
+    RET_IF_RESULT_NEGATIVE(writer->write({mb_str, ret.value()}));
+  } else
+#endif // LIBC_COPT_PRINTF_DISABLE_WIDE
+    RET_IF_RESULT_NEGATIVE(writer->write(c));
 
   // If the padding is on the right side, write the spaces last.
   if (padding_spaces > 0 &&
diff --git a/libc/src/stdio/printf_core/parser.h b/libc/src/stdio/printf_core/parser.h
index cef9b1ae58fa0..e563308647fae 100644
--- a/libc/src/stdio/printf_core/parser.h
+++ b/libc/src/stdio/printf_core/parser.h
@@ -9,6 +9,10 @@
 #ifndef LLVM_LIBC_SRC_STDIO_PRINTF_CORE_PARSER_H
 #define LLVM_LIBC_SRC_STDIO_PRINTF_CORE_PARSER_H
 
+#ifndef LIBC_COPT_PRINTF_DISABLE_WIDE
+#include "hdr/types/wint_t.h"
+#endif // LIBC_COPT_PRINTF_DISABLE_WIDE
+
 #include "include/llvm-libc-macros/stdfix-macros.h"
 #include "src/__support/CPP/algorithm.h" // max
 #include "src/__support/CPP/limits.h"
@@ -73,9 +77,9 @@ template <typename ArgProvider> class Parser {
   ArgProvider args_cur;
 
 #ifndef LIBC_COPT_PRINTF_DISABLE_INDEX_MODE
-  // args_start stores the start of the va_args, which is allows getting the
-  // value of arguments that have already been passed. args_index is tracked so
-  // that we know which argument args_cur is on.
+  // args_start stores the start of the va_args, which helps in getting the
+  // number of arguments that have already been passed. args_index is tracked
+  // so that we know which argument args_cur is on.
   ArgProvider args_start;
   size_t args_index = 1;
 
@@ -173,7 +177,12 @@ template <typename ArgProvider> class Parser {
         section.has_conv = true;
         break;
       case ('c'):
-        WRITE_ARG_VAL_SIMPLEST(section.conv_val_raw, int, conv_index);
+#ifndef LIBC_COPT_PRINTF_DISABLE_WIDE
+        if (section.length_modifier == LengthModifier::l) {
+          WRITE_ARG_VAL_SIMPLEST(section.conv_val_raw, wint_t, conv_index);
+        } else
+#endif // LIBC_COPT_PRINTF_DISABLE_WIDE
+          WRITE_ARG_VAL_SIMPLEST(section.conv_val_raw, int, conv_index);
         break;
       case ('d'):
       case ('i'):
@@ -574,7 +583,12 @@ template <typename ArgProvider> class Parser {
           conv_size = type_desc_from_type<void>();
           break;
         case ('c'):
-          conv_size = type_desc_from_type<int>();
+#ifndef LIBC_COPT_PRINTF_DISABLE_WIDE
+          if (lm == LengthModifier::l) {
+            conv_size = type_desc_from_type<wint_t>();
+          } else
+#endif // LIBC_COPT_PRINTF_DISABLE_WIDE
+            conv_size = type_desc_from_type<int>();
           break;
         case ('d'):
         case ('i'):
diff --git a/libc/test/src/stdio/CMakeLists.txt b/libc/test/src/stdio/CMakeLists.txt
index a39428fb8d16c..3bfeefd0750d8 100644
--- a/libc/test/src/stdio/CMakeLists.txt
+++ b/libc/test/src/stdio/CMakeLists.txt
@@ -137,6 +137,16 @@ if(LIBC_CONF_PRINTF_DISABLE_STRERROR)
   list(APPEND sprintf_test_copts "-DLIBC_COPT_PRINTF_DISABLE_STRERROR")
 endif()
 
+if(LIBC_CONF_PRINTF_DISABLE_WIDE)
+  list(APPEND sprintf_test_copts "-DLIBC_COPT_PRINTF_DISABLE_WIDE")
+  set(wchar_deps "")
+else()
+  set(wchar_deps
+    libc.hdr.types.wchar_t
+    libc.hdr.wchar_macros
+  )
+endif()
+
 add_fp_unittest(
   sprintf_test
   UNIT_TEST_ONLY
@@ -148,6 +158,7 @@ add_fp_unittest(
     libc.src.stdio.sprintf
     libc.src.__support.FPUtil.fp_bits
     libc.include.inttypes
+    ${wchar_deps}
   COMPILE_OPTIONS
     ${sprintf_test_copts}
 )
diff --git a/libc/test/src/stdio/printf_core/CMakeLists.txt b/libc/test/src/stdio/printf_core/CMakeLists.txt
index ff7ebbc4f5fd0..f508aff784b14 100644
--- a/libc/test/src/stdio/printf_core/CMakeLists.txt
+++ b/libc/test/src/stdio/printf_core/CMakeLists.txt
@@ -1,3 +1,8 @@
+if(LIBC_CONF_PRINTF_DISABLE_WIDE)
+  list(PREPEND printf_core_copts "COMPILE_OPTIONS")
+  list(APPEND printf_core_copts "-DLIBC_COPT_PRINTF_DISABLE_WIDE")
+endif()
+
 add_libc_unittest(
   parser_test
   SUITE
@@ -11,6 +16,7 @@ add_libc_unittest(
     libc.src.stdio.printf_core.core_structs
     libc.src.__support.CPP.string_view
     libc.src.__support.arg_list
+    ${printf_core_copts}
 )
 
 add_libc_unittest(
@@ -23,6 +29,7 @@ add_libc_unittest(
     libc.src.stdio.printf_core.writer
     libc.src.string.memory_utils.inline_memcpy
     libc.src.__support.CPP.string_view
+    ${printf_core_copts}
 )
 
 add_libc_unittest(
@@ -35,4 +42,5 @@ add_libc_unittest(
     libc.src.stdio.printf_core.converter
     libc.src.stdio.printf_core.writer
     libc.src.stdio.printf_core.core_structs
+    ${printf_core_copts}
 )
diff --git a/libc/test/src/stdio/sprintf_test.cpp b/libc/test/src/stdio/sprintf_test.cpp
index 689a38a49f13c..f4969a51a336e 100644
--- a/libc/test/src/stdio/sprintf_test.cpp
+++ b/libc/test/src/stdio/sprintf_test.cpp
@@ -9,8 +9,14 @@
 #include "src/__support/macros/config.h"
 #include "src/stdio/sprintf.h"
 
+#ifndef LIBC_COPT_PRINTF_DISABLE_WIDE
+#include "hdr/types/wchar_t.h"
+#include "hdr/wchar_macros.h"
+#endif // LIBC_COPT_PRINTF_DISABLE_WIDE
+
 #include "src/__support/FPUtil/FPBits.h"
 #include "src/__support/libc_errno.h"
+#include "test/UnitTest/ErrnoCheckingTest.h"
 #include "test/UnitTest/RoundingModeUtils.h"
 #include "test/UnitTest/Test.h"
 #include <inttypes.h>
@@ -3487,3 +3493,36 @@ TEST(LlvmLibcSPrintfTest, IndexModeParsing) {
                    "why would u do this, this is such   a pain. %");
 }
 #endif // LIBC_COPT_PRINTF_DISABLE_INDEX_MODE
+
+#ifndef LIBC_COPT_PRINTF_DISABLE_WIDE
+TEST(LlvmLibcSprintfTest, WideCharConversion) {
+  char buff[16];
+  int written;
+
+  // Euro sign is a 3-byte UTF-8 character.
+  written = LIBC_NAMESPACE::sprintf(buff, "%lc", static_cast<wchar_t>(L'€'));
+  EXPECT_EQ(written, 3);
+  ASSERT_STREQ(buff, "€");
+
+  // Euro sign left justified.
+  written = LIBC_NAMESPACE::sprintf(buff, "%-4lc", static_cast<wchar_t>(L'€'));
+  EXPECT_EQ(written, 6);
+  ASSERT_STREQ(buff, "€   ");
+
+  // Euro sign right justified.
+  written = LIBC_NAMESPACE::sprintf(buff, "%4lc", static_cast<wchar_t>(L'€'));
+  EXPECT_EQ(written, 6);
+  ASSERT_STREQ(buff, "   €");
+
+  // WEOF test.
+  written = LIBC_NAMESPACE::sprintf(buff, "%lc", static_cast<wchar_t>(WEOF));
+  EXPECT_EQ(written, -1);
+  ASSERT_ERRNO_FAILURE();
+
+  // Invalid wide character test
+  written =
+      LIBC_NAMESPACE::sprintf(buff, "%lc", static_cast<wchar_t>(0x12ffff));
+  EXPECT_EQ(written, -1);
+  ASSERT_ERRNO_FAILURE();
+}
+#endif // LIBC_COPT_PRINTF_DISABLE_WIDE

>From ced347cf46a56ce934b87ef7cddc363c7207c1fc Mon Sep 17 00:00:00 2001
From: "shubh at DOE" <shubhp at mbm3a24.local>
Date: Wed, 24 Dec 2025 01:27:01 -0800
Subject: [PATCH 2/7] changed a lot of things with rebase

---
 .../modules/LLVMLibCCompileOptionRules.cmake  |  3 ++
 libc/src/stdio/printf_core/CMakeLists.txt     |  3 +-
 libc/src/stdio/printf_core/char_converter.h   | 51 +++++++++++--------
 libc/src/stdio/printf_core/core_structs.h     |  2 +
 .../stdio/printf_core/linux/error_mapper.h    |  3 ++
 .../test/src/stdio/printf_core/CMakeLists.txt |  8 +--
 libc/test/src/stdio/sprintf_test.cpp          | 38 +++++++++++---
 7 files changed, 71 insertions(+), 37 deletions(-)

diff --git a/libc/cmake/modules/LLVMLibCCompileOptionRules.cmake b/libc/cmake/modules/LLVMLibCCompileOptionRules.cmake
index 57e4381d5f1af..ecc93b6934b4b 100644
--- a/libc/cmake/modules/LLVMLibCCompileOptionRules.cmake
+++ b/libc/cmake/modules/LLVMLibCCompileOptionRules.cmake
@@ -129,6 +129,9 @@ function(_get_compile_options_from_config output_var)
     else()
       list(APPEND config_options "-DLIBC_MATH_USE_SYSTEM_FENV")
     endif()
+
+  if (LIBC_CONF_PRINTF_DISABLE_WIDE)
+    list(APPEND config_options "-DLIBC_COPT_PRINTF_DISABLE_WIDE")
   endif()
 
   set(${output_var} ${config_options} PARENT_SCOPE)
diff --git a/libc/src/stdio/printf_core/CMakeLists.txt b/libc/src/stdio/printf_core/CMakeLists.txt
index 06d74937def17..73c2ada1e32f8 100644
--- a/libc/src/stdio/printf_core/CMakeLists.txt
+++ b/libc/src/stdio/printf_core/CMakeLists.txt
@@ -45,7 +45,6 @@ endif()
 
 if(LIBC_CONF_PRINTF_DISABLE_WIDE)
   set(wchar_deps "")
-  list(APPEND printf_config_copts "-DLIBC_COPT_PRINTF_DISABLE_WIDE")
 else()
   set(wchar_deps
     libc.hdr.types.wchar_t
@@ -80,6 +79,7 @@ add_header_library(
     parser.h
   DEPENDS
     .core_structs
+    libc.hdr.types.wint_t
     libc.src.__support.arg_list
     libc.src.__support.ctype_utils
     libc.src.__support.str_to_integer
@@ -89,7 +89,6 @@ add_header_library(
     libc.src.__support.CPP.string_view
     libc.src.__support.CPP.type_traits
     libc.src.__support.common
-    ${wchar_deps}
 )
 
 add_header_library(
diff --git a/libc/src/stdio/printf_core/char_converter.h b/libc/src/stdio/printf_core/char_converter.h
index 53417f3e050d3..9f2d83f0451ad 100644
--- a/libc/src/stdio/printf_core/char_converter.h
+++ b/libc/src/stdio/printf_core/char_converter.h
@@ -17,6 +17,7 @@
 #include "src/__support/wchar/wcrtomb.h"
 #endif // LIBC_COPT_PRINTF_DISABLE_WIDE
 
+#include "src/__support/CPP/string_view.h"
 #include "src/__support/macros/config.h"
 #include "src/stdio/printf_core/converter_utils.h"
 #include "src/stdio/printf_core/core_structs.h"
@@ -28,40 +29,48 @@ namespace printf_core {
 template <WriteMode write_mode>
 LIBC_INLINE int convert_char(Writer<write_mode> *writer,
                              const FormatSection &to_conv) {
-  char c = static_cast<char>(to_conv.conv_val_raw);
 
-  constexpr int STRING_LEN = 1;
+  char buffer[MB_LEN_MAX];
+  cpp::string_view to_write;
 
-  size_t padding_spaces =
-      to_conv.min_width > STRING_LEN ? to_conv.min_width - STRING_LEN : 0;
-
-  // If the padding is on the left side, write the spaces first.
-  if (padding_spaces > 0 &&
-      (to_conv.flags & FormatFlags::LEFT_JUSTIFIED) == 0) {
-    RET_IF_RESULT_NEGATIVE(writer->write(' ', padding_spaces));
-  }
-
-#ifndef LIBC_COPT_PRINTF_DISABLE_WIDE
   if (to_conv.length_modifier == LengthModifier::l) {
+#ifndef LIBC_COPT_PRINTF_DISABLE_WIDE
     wint_t wi = static_cast<wint_t>(to_conv.conv_val_raw);
 
     if (wi == WEOF) {
-      return -1;
+      return ILLEGAL_WIDE_CHAR;
     }
 
-    char mb_str[MB_LEN_MAX];
-    internal::mbstate mbstate;
+    mbstate_t mbstate;
     wchar_t wc = static_cast<wchar_t>(wi);
+    auto ret = wcrtomb(buffer, wc, &mbstate);
 
-    auto ret = internal::wcrtomb(mb_str, wc, &mbstate);
-    if (!ret.has_value()) {
-      return -1;
+    if (ret.has_error()) {
+      return MB_CONVERSION_ERROR;
     }
 
-    RET_IF_RESULT_NEGATIVE(writer->write({mb_str, ret.value()}));
-  } else
+    to_write = {buffer, ret.value()};
+#else
+    // If wide characters are disabled, treat the 'l' modifier as a no-op.
+    buffer[0] = static_cast<char>(to_conv.conv_val_raw);
+    to_write = {buffer, 1};
+
 #endif // LIBC_COPT_PRINTF_DISABLE_WIDE
-    RET_IF_RESULT_NEGATIVE(writer->write(c));
+  } else {
+    buffer[0] = static_cast<char>(to_conv.conv_val_raw);
+    to_write = {buffer, 1};
+  }
+
+  size_t padding_spaces =
+      to_conv.min_width > to_write.size() ? to_conv.min_width - to_write.size() : 0;
+  
+  // If the padding is on the left side, write the spaces first.
+  if (padding_spaces > 0 &&
+      (to_conv.flags & FormatFlags::LEFT_JUSTIFIED) == 0) {
+    RET_IF_RESULT_NEGATIVE(writer->write(' ', padding_spaces));
+  }
+
+  RET_IF_RESULT_NEGATIVE(writer->write(to_write));
 
   // If the padding is on the right side, write the spaces last.
   if (padding_spaces > 0 &&
diff --git a/libc/src/stdio/printf_core/core_structs.h b/libc/src/stdio/printf_core/core_structs.h
index 0d41f2244d8da..d93fa962db90e 100644
--- a/libc/src/stdio/printf_core/core_structs.h
+++ b/libc/src/stdio/printf_core/core_structs.h
@@ -142,6 +142,8 @@ constexpr int INT_CONVERSION_ERROR = -1004;
 constexpr int FIXED_POINT_CONVERSION_ERROR = -1005;
 constexpr int ALLOCATION_ERROR = -1006;
 constexpr int OVERFLOW_ERROR = -1007;
+constexpr int ILLEGAL_WIDE_CHAR = -1008;
+constexpr int MB_CONVERSION_ERROR = -1009;
 
 } // namespace printf_core
 } // namespace LIBC_NAMESPACE_DECL
diff --git a/libc/src/stdio/printf_core/linux/error_mapper.h b/libc/src/stdio/printf_core/linux/error_mapper.h
index 3c2fe663072d0..3449f12593469 100644
--- a/libc/src/stdio/printf_core/linux/error_mapper.h
+++ b/libc/src/stdio/printf_core/linux/error_mapper.h
@@ -40,6 +40,9 @@ LIBC_INLINE static int internal_error_to_errno(int internal_error) {
     return ENOMEM;
   case OVERFLOW_ERROR:
     return EOVERFLOW;
+  case ILLEGAL_WIDE_CHAR:
+  case MB_CONVERSION_ERROR:
+    return EILSEQ;
   default:
     LIBC_ASSERT(
         false &&
diff --git a/libc/test/src/stdio/printf_core/CMakeLists.txt b/libc/test/src/stdio/printf_core/CMakeLists.txt
index f508aff784b14..2e66d2aa070d4 100644
--- a/libc/test/src/stdio/printf_core/CMakeLists.txt
+++ b/libc/test/src/stdio/printf_core/CMakeLists.txt
@@ -1,7 +1,4 @@
-if(LIBC_CONF_PRINTF_DISABLE_WIDE)
-  list(PREPEND printf_core_copts "COMPILE_OPTIONS")
-  list(APPEND printf_core_copts "-DLIBC_COPT_PRINTF_DISABLE_WIDE")
-endif()
+
 
 add_libc_unittest(
   parser_test
@@ -16,7 +13,6 @@ add_libc_unittest(
     libc.src.stdio.printf_core.core_structs
     libc.src.__support.CPP.string_view
     libc.src.__support.arg_list
-    ${printf_core_copts}
 )
 
 add_libc_unittest(
@@ -29,7 +25,6 @@ add_libc_unittest(
     libc.src.stdio.printf_core.writer
     libc.src.string.memory_utils.inline_memcpy
     libc.src.__support.CPP.string_view
-    ${printf_core_copts}
 )
 
 add_libc_unittest(
@@ -42,5 +37,4 @@ add_libc_unittest(
     libc.src.stdio.printf_core.converter
     libc.src.stdio.printf_core.writer
     libc.src.stdio.printf_core.core_structs
-    ${printf_core_copts}
 )
diff --git a/libc/test/src/stdio/sprintf_test.cpp b/libc/test/src/stdio/sprintf_test.cpp
index f4969a51a336e..926ec7bed63d1 100644
--- a/libc/test/src/stdio/sprintf_test.cpp
+++ b/libc/test/src/stdio/sprintf_test.cpp
@@ -16,7 +16,6 @@
 
 #include "src/__support/FPUtil/FPBits.h"
 #include "src/__support/libc_errno.h"
-#include "test/UnitTest/ErrnoCheckingTest.h"
 #include "test/UnitTest/RoundingModeUtils.h"
 #include "test/UnitTest/Test.h"
 #include <inttypes.h>
@@ -3506,23 +3505,48 @@ TEST(LlvmLibcSprintfTest, WideCharConversion) {
 
   // Euro sign left justified.
   written = LIBC_NAMESPACE::sprintf(buff, "%-4lc", static_cast<wchar_t>(L'€'));
-  EXPECT_EQ(written, 6);
-  ASSERT_STREQ(buff, "€   ");
+  EXPECT_EQ(written, 4);
+  ASSERT_STREQ(buff, " €");
 
   // Euro sign right justified.
   written = LIBC_NAMESPACE::sprintf(buff, "%4lc", static_cast<wchar_t>(L'€'));
-  EXPECT_EQ(written, 6);
-  ASSERT_STREQ(buff, "   €");
+  EXPECT_EQ(written, 4);
+  ASSERT_STREQ(buff, "€ ");
+
+  // Grinning face emoji is a 4-byte UTF-8 character.
+  written = LIBC_NAMESPACE::sprintf(buff, "%lc", static_cast<wchar_t>(L'😀'));
+  EXPECT_EQ(written, 4);
+  ASSERT_STREQ(buff, "😀");
+
+  // Grinning face emoji left justified.
+  written = LIBC_NAMESPACE::sprintf(buff, "%-4lc", static_cast<wchar_t>(L'😀'));
+  EXPECT_EQ(written, 4);
+  ASSERT_STREQ(buff, "😀");
+
+  // Grinning face emoji right justified.
+  written = LIBC_NAMESPACE::sprintf(buff, "%4lc", static_cast<wchar_t>(L'😀'));
+  EXPECT_EQ(written, 4);
+  ASSERT_STREQ(buff, "😀");
+
+  // Grinning face emoji with smaller width, left justified.
+  written = LIBC_NAMESPACE::sprintf(buff, "%-3lc", static_cast<wchar_t>(L'😀'));
+  EXPECT_EQ(written, 4);
+  ASSERT_STREQ(buff, "😀");
+
+  // Grinning face emoji with smaller width, right justified.
+  written = LIBC_NAMESPACE::sprintf(buff, "%3lc", static_cast<wchar_t>(L'😀'));
+  EXPECT_EQ(written, 4);
+  ASSERT_STREQ(buff, "😀");
 
   // WEOF test.
   written = LIBC_NAMESPACE::sprintf(buff, "%lc", static_cast<wchar_t>(WEOF));
   EXPECT_EQ(written, -1);
-  ASSERT_ERRNO_FAILURE();
+  ASSERT_EQ(libc_errno, EILSEQ);
 
   // Invalid wide character test
   written =
       LIBC_NAMESPACE::sprintf(buff, "%lc", static_cast<wchar_t>(0x12ffff));
   EXPECT_EQ(written, -1);
-  ASSERT_ERRNO_FAILURE();
+  ASSERT_EQ(libc_errno, EILSEQ);
 }
 #endif // LIBC_COPT_PRINTF_DISABLE_WIDE

>From bdd486b20f042e01f418c9cc42760b114d22025d Mon Sep 17 00:00:00 2001
From: "shubh at DOE" <shubhp at mbm3a24.local>
Date: Thu, 25 Dec 2025 12:55:46 -0800
Subject: [PATCH 3/7] squash merge test branch

---
 libc/src/stdio/printf_core/char_converter.h |  7 +++---
 libc/src/stdio/printf_core/parser.h         | 16 +++++++++-----
 libc/test/src/stdio/sprintf_test.cpp        | 24 ++++++++++-----------
 3 files changed, 27 insertions(+), 20 deletions(-)

diff --git a/libc/src/stdio/printf_core/char_converter.h b/libc/src/stdio/printf_core/char_converter.h
index 9f2d83f0451ad..2b92fc4000430 100644
--- a/libc/src/stdio/printf_core/char_converter.h
+++ b/libc/src/stdio/printf_core/char_converter.h
@@ -41,11 +41,11 @@ LIBC_INLINE int convert_char(Writer<write_mode> *writer,
       return ILLEGAL_WIDE_CHAR;
     }
 
-    mbstate_t mbstate;
+    internal::mbstate mbstate;
     wchar_t wc = static_cast<wchar_t>(wi);
     auto ret = wcrtomb(buffer, wc, &mbstate);
 
-    if (ret.has_error()) {
+    if (!ret.has_value()) {
       return MB_CONVERSION_ERROR;
     }
 
@@ -61,8 +61,9 @@ LIBC_INLINE int convert_char(Writer<write_mode> *writer,
     to_write = {buffer, 1};
   }
 
+  int wlen = static_cast<int>(to_write.size());
   size_t padding_spaces =
-      to_conv.min_width > to_write.size() ? to_conv.min_width - to_write.size() : 0;
+      to_conv.min_width > wlen ? to_conv.min_width - wlen : 0;
   
   // If the padding is on the left side, write the spaces first.
   if (padding_spaces > 0 &&
diff --git a/libc/src/stdio/printf_core/parser.h b/libc/src/stdio/printf_core/parser.h
index e563308647fae..d079af17db4df 100644
--- a/libc/src/stdio/printf_core/parser.h
+++ b/libc/src/stdio/printf_core/parser.h
@@ -177,12 +177,15 @@ template <typename ArgProvider> class Parser {
         section.has_conv = true;
         break;
       case ('c'):
-#ifndef LIBC_COPT_PRINTF_DISABLE_WIDE
         if (section.length_modifier == LengthModifier::l) {
+#ifndef LIBC_COPT_PRINTF_DISABLE_WIDE
           WRITE_ARG_VAL_SIMPLEST(section.conv_val_raw, wint_t, conv_index);
-        } else
-#endif // LIBC_COPT_PRINTF_DISABLE_WIDE
+#else
           WRITE_ARG_VAL_SIMPLEST(section.conv_val_raw, int, conv_index);
+#endif // LIBC_COPT_PRINTF_DISABLE_WIDE     
+        } else {
+          WRITE_ARG_VAL_SIMPLEST(section.conv_val_raw, int, conv_index);
+        }
         break;
       case ('d'):
       case ('i'):
@@ -583,12 +586,15 @@ template <typename ArgProvider> class Parser {
           conv_size = type_desc_from_type<void>();
           break;
         case ('c'):
-#ifndef LIBC_COPT_PRINTF_DISABLE_WIDE
           if (lm == LengthModifier::l) {
+#ifndef LIBC_COPT_PRINTF_DISABLE_WIDE
             conv_size = type_desc_from_type<wint_t>();
-          } else
+#else
+            conv_size = type_desc_from_type<int>();
 #endif // LIBC_COPT_PRINTF_DISABLE_WIDE
+          } else {
             conv_size = type_desc_from_type<int>();
+          }
           break;
         case ('d'):
         case ('i'):
diff --git a/libc/test/src/stdio/sprintf_test.cpp b/libc/test/src/stdio/sprintf_test.cpp
index 926ec7bed63d1..75990a04774ac 100644
--- a/libc/test/src/stdio/sprintf_test.cpp
+++ b/libc/test/src/stdio/sprintf_test.cpp
@@ -3501,52 +3501,52 @@ TEST(LlvmLibcSprintfTest, WideCharConversion) {
   // Euro sign is a 3-byte UTF-8 character.
   written = LIBC_NAMESPACE::sprintf(buff, "%lc", static_cast<wchar_t>(L'€'));
   EXPECT_EQ(written, 3);
-  ASSERT_STREQ(buff, "€");
+  ASSERT_STREQ_LEN(written, buff, "€");
 
-  // Euro sign left justified.
+  // Euro sign right justified.
   written = LIBC_NAMESPACE::sprintf(buff, "%-4lc", static_cast<wchar_t>(L'€'));
   EXPECT_EQ(written, 4);
-  ASSERT_STREQ(buff, " €");
+  ASSERT_STREQ_LEN(written, buff, "€ ");
 
-  // Euro sign right justified.
+  // Euro sign left justified.
   written = LIBC_NAMESPACE::sprintf(buff, "%4lc", static_cast<wchar_t>(L'€'));
   EXPECT_EQ(written, 4);
-  ASSERT_STREQ(buff, "€ ");
+  ASSERT_STREQ_LEN(written, buff, " €");
 
   // Grinning face emoji is a 4-byte UTF-8 character.
   written = LIBC_NAMESPACE::sprintf(buff, "%lc", static_cast<wchar_t>(L'😀'));
   EXPECT_EQ(written, 4);
-  ASSERT_STREQ(buff, "😀");
+  ASSERT_STREQ_LEN(written, buff, "😀");
 
   // Grinning face emoji left justified.
   written = LIBC_NAMESPACE::sprintf(buff, "%-4lc", static_cast<wchar_t>(L'😀'));
   EXPECT_EQ(written, 4);
-  ASSERT_STREQ(buff, "😀");
+  ASSERT_STREQ_LEN(written, buff, "😀");
 
   // Grinning face emoji right justified.
   written = LIBC_NAMESPACE::sprintf(buff, "%4lc", static_cast<wchar_t>(L'😀'));
   EXPECT_EQ(written, 4);
-  ASSERT_STREQ(buff, "😀");
+  ASSERT_STREQ_LEN(written, buff, "😀");
 
   // Grinning face emoji with smaller width, left justified.
   written = LIBC_NAMESPACE::sprintf(buff, "%-3lc", static_cast<wchar_t>(L'😀'));
   EXPECT_EQ(written, 4);
-  ASSERT_STREQ(buff, "😀");
+  ASSERT_STREQ_LEN(written, buff, "😀");
 
   // Grinning face emoji with smaller width, right justified.
   written = LIBC_NAMESPACE::sprintf(buff, "%3lc", static_cast<wchar_t>(L'😀'));
   EXPECT_EQ(written, 4);
-  ASSERT_STREQ(buff, "😀");
+  ASSERT_STREQ_LEN(written, buff, "😀");
 
   // WEOF test.
   written = LIBC_NAMESPACE::sprintf(buff, "%lc", static_cast<wchar_t>(WEOF));
   EXPECT_EQ(written, -1);
-  ASSERT_EQ(libc_errno, EILSEQ);
+  EXPECT_EQ(static_cast<int>(libc_errno), EILSEQ);
 
   // Invalid wide character test
   written =
       LIBC_NAMESPACE::sprintf(buff, "%lc", static_cast<wchar_t>(0x12ffff));
   EXPECT_EQ(written, -1);
-  ASSERT_EQ(libc_errno, EILSEQ);
+  EXPECT_EQ(static_cast<int>(libc_errno), EILSEQ);
 }
 #endif // LIBC_COPT_PRINTF_DISABLE_WIDE

>From 0c29dbf8f7527bfa33350b88da145359573fa6af Mon Sep 17 00:00:00 2001
From: "shubh at DOE" <shubhp at mbm3a24.local>
Date: Thu, 25 Dec 2025 13:09:33 -0800
Subject: [PATCH 4/7] remove spaces

---
 libc/src/stdio/printf_core/char_converter.h | 2 +-
 libc/src/stdio/printf_core/parser.h         | 2 +-
 2 files changed, 2 insertions(+), 2 deletions(-)

diff --git a/libc/src/stdio/printf_core/char_converter.h b/libc/src/stdio/printf_core/char_converter.h
index 2b92fc4000430..44759f10eb715 100644
--- a/libc/src/stdio/printf_core/char_converter.h
+++ b/libc/src/stdio/printf_core/char_converter.h
@@ -64,7 +64,7 @@ LIBC_INLINE int convert_char(Writer<write_mode> *writer,
   int wlen = static_cast<int>(to_write.size());
   size_t padding_spaces =
       to_conv.min_width > wlen ? to_conv.min_width - wlen : 0;
-  
+
   // If the padding is on the left side, write the spaces first.
   if (padding_spaces > 0 &&
       (to_conv.flags & FormatFlags::LEFT_JUSTIFIED) == 0) {
diff --git a/libc/src/stdio/printf_core/parser.h b/libc/src/stdio/printf_core/parser.h
index d079af17db4df..281062dd193a2 100644
--- a/libc/src/stdio/printf_core/parser.h
+++ b/libc/src/stdio/printf_core/parser.h
@@ -182,7 +182,7 @@ template <typename ArgProvider> class Parser {
           WRITE_ARG_VAL_SIMPLEST(section.conv_val_raw, wint_t, conv_index);
 #else
           WRITE_ARG_VAL_SIMPLEST(section.conv_val_raw, int, conv_index);
-#endif // LIBC_COPT_PRINTF_DISABLE_WIDE     
+#endif // LIBC_COPT_PRINTF_DISABLE_WIDE
         } else {
           WRITE_ARG_VAL_SIMPLEST(section.conv_val_raw, int, conv_index);
         }

>From 1cf6f2ecf5fb13249d7a702462e2181a15291dfe Mon Sep 17 00:00:00 2001
From: "shubh at DOE" <shubhp at mbm3a24.local>
Date: Thu, 25 Dec 2025 13:17:30 -0800
Subject: [PATCH 5/7] fix build

---
 libc/cmake/modules/LLVMLibCCompileOptionRules.cmake | 1 +
 1 file changed, 1 insertion(+)

diff --git a/libc/cmake/modules/LLVMLibCCompileOptionRules.cmake b/libc/cmake/modules/LLVMLibCCompileOptionRules.cmake
index ecc93b6934b4b..abf958e56af21 100644
--- a/libc/cmake/modules/LLVMLibCCompileOptionRules.cmake
+++ b/libc/cmake/modules/LLVMLibCCompileOptionRules.cmake
@@ -129,6 +129,7 @@ function(_get_compile_options_from_config output_var)
     else()
       list(APPEND config_options "-DLIBC_MATH_USE_SYSTEM_FENV")
     endif()
+  endif()
 
   if (LIBC_CONF_PRINTF_DISABLE_WIDE)
     list(APPEND config_options "-DLIBC_COPT_PRINTF_DISABLE_WIDE")

>From d4593aaa2030be5a49205ee28ff7707fa76031b8 Mon Sep 17 00:00:00 2001
From: "shubh at DOE" <shubhp at mbm3a24.local>
Date: Fri, 26 Dec 2025 10:12:36 -0800
Subject: [PATCH 6/7] resolve comments

---
 libc/src/stdio/printf_core/CMakeLists.txt     | 11 +++++-
 libc/src/stdio/printf_core/char_converter.h   |  3 +-
 libc/test/src/stdio/CMakeLists.txt            |  3 +-
 .../test/src/stdio/printf_core/CMakeLists.txt |  2 -
 libc/test/src/stdio/sprintf_test.cpp          | 39 ++++++++++---------
 5 files changed, 34 insertions(+), 24 deletions(-)

diff --git a/libc/src/stdio/printf_core/CMakeLists.txt b/libc/src/stdio/printf_core/CMakeLists.txt
index 73c2ada1e32f8..c0c2e575b5e00 100644
--- a/libc/src/stdio/printf_core/CMakeLists.txt
+++ b/libc/src/stdio/printf_core/CMakeLists.txt
@@ -55,6 +55,14 @@ else()
   )
 endif()
 
+if(LIBC_CONF_PRINTF_DISABLE_WIDE)
+  set(parser_wchar_deps "")
+else()
+  set(parser_wchar_deps
+    libc.hdr.types.wint_t
+  )
+endif()
+
 add_header_library(
   printf_config
   HDRS
@@ -79,7 +87,6 @@ add_header_library(
     parser.h
   DEPENDS
     .core_structs
-    libc.hdr.types.wint_t
     libc.src.__support.arg_list
     libc.src.__support.ctype_utils
     libc.src.__support.str_to_integer
@@ -89,6 +96,7 @@ add_header_library(
     libc.src.__support.CPP.string_view
     libc.src.__support.CPP.type_traits
     libc.src.__support.common
+    ${parser_wchar_deps}
 )
 
 add_header_library(
@@ -124,6 +132,7 @@ add_header_library(
     .printf_config
     .writer
     libc.include.inttypes
+    libc.hdr.limits_macros
     libc.src.__support.big_int
     libc.src.__support.common
     libc.src.__support.CPP.limits
diff --git a/libc/src/stdio/printf_core/char_converter.h b/libc/src/stdio/printf_core/char_converter.h
index 44759f10eb715..252af8e862455 100644
--- a/libc/src/stdio/printf_core/char_converter.h
+++ b/libc/src/stdio/printf_core/char_converter.h
@@ -17,6 +17,7 @@
 #include "src/__support/wchar/wcrtomb.h"
 #endif // LIBC_COPT_PRINTF_DISABLE_WIDE
 
+#include "hdr/limits_macros.h"
 #include "src/__support/CPP/string_view.h"
 #include "src/__support/macros/config.h"
 #include "src/stdio/printf_core/converter_utils.h"
@@ -43,7 +44,7 @@ LIBC_INLINE int convert_char(Writer<write_mode> *writer,
 
     internal::mbstate mbstate;
     wchar_t wc = static_cast<wchar_t>(wi);
-    auto ret = wcrtomb(buffer, wc, &mbstate);
+    auto ret = internal::wcrtomb(buffer, wc, &mbstate);
 
     if (!ret.has_value()) {
       return MB_CONVERSION_ERROR;
diff --git a/libc/test/src/stdio/CMakeLists.txt b/libc/test/src/stdio/CMakeLists.txt
index 3bfeefd0750d8..fde2023caaac1 100644
--- a/libc/test/src/stdio/CMakeLists.txt
+++ b/libc/test/src/stdio/CMakeLists.txt
@@ -138,11 +138,10 @@ if(LIBC_CONF_PRINTF_DISABLE_STRERROR)
 endif()
 
 if(LIBC_CONF_PRINTF_DISABLE_WIDE)
-  list(APPEND sprintf_test_copts "-DLIBC_COPT_PRINTF_DISABLE_WIDE")
   set(wchar_deps "")
 else()
   set(wchar_deps
-    libc.hdr.types.wchar_t
+    libc.hdr.types.wint_t
     libc.hdr.wchar_macros
   )
 endif()
diff --git a/libc/test/src/stdio/printf_core/CMakeLists.txt b/libc/test/src/stdio/printf_core/CMakeLists.txt
index 2e66d2aa070d4..ff7ebbc4f5fd0 100644
--- a/libc/test/src/stdio/printf_core/CMakeLists.txt
+++ b/libc/test/src/stdio/printf_core/CMakeLists.txt
@@ -1,5 +1,3 @@
-
-
 add_libc_unittest(
   parser_test
   SUITE
diff --git a/libc/test/src/stdio/sprintf_test.cpp b/libc/test/src/stdio/sprintf_test.cpp
index 75990a04774ac..707185c0dd8bc 100644
--- a/libc/test/src/stdio/sprintf_test.cpp
+++ b/libc/test/src/stdio/sprintf_test.cpp
@@ -10,7 +10,7 @@
 #include "src/stdio/sprintf.h"
 
 #ifndef LIBC_COPT_PRINTF_DISABLE_WIDE
-#include "hdr/types/wchar_t.h"
+#include "hdr/types/wint_t.h"
 #include "hdr/wchar_macros.h"
 #endif // LIBC_COPT_PRINTF_DISABLE_WIDE
 
@@ -3499,54 +3499,57 @@ TEST(LlvmLibcSprintfTest, WideCharConversion) {
   int written;
 
   // Euro sign is a 3-byte UTF-8 character.
-  written = LIBC_NAMESPACE::sprintf(buff, "%lc", static_cast<wchar_t>(L'€'));
+  written = LIBC_NAMESPACE::sprintf(buff, "%lc", L'€');
   EXPECT_EQ(written, 3);
   ASSERT_STREQ_LEN(written, buff, "€");
 
-  // Euro sign right justified.
-  written = LIBC_NAMESPACE::sprintf(buff, "%-4lc", static_cast<wchar_t>(L'€'));
+  // Euro sign left justified.
+  written = LIBC_NAMESPACE::sprintf(buff, "%-4lc", L'€');
   EXPECT_EQ(written, 4);
   ASSERT_STREQ_LEN(written, buff, "€ ");
 
-  // Euro sign left justified.
-  written = LIBC_NAMESPACE::sprintf(buff, "%4lc", static_cast<wchar_t>(L'€'));
+  // Euro sign right justified.
+  written = LIBC_NAMESPACE::sprintf(buff, "%4lc", L'€');
+  EXPECT_EQ(written, 4);
+  ASSERT_STREQ_LEN(written, buff, " €");
+
+  // Euro sign right justified.
+  written = LIBC_NAMESPACE::sprintf(buff, "%+4lc", L'€');
   EXPECT_EQ(written, 4);
   ASSERT_STREQ_LEN(written, buff, " €");
 
   // Grinning face emoji is a 4-byte UTF-8 character.
-  written = LIBC_NAMESPACE::sprintf(buff, "%lc", static_cast<wchar_t>(L'😀'));
+  written = LIBC_NAMESPACE::sprintf(buff, "%lc", L'😀');
   EXPECT_EQ(written, 4);
   ASSERT_STREQ_LEN(written, buff, "😀");
 
   // Grinning face emoji left justified.
-  written = LIBC_NAMESPACE::sprintf(buff, "%-4lc", static_cast<wchar_t>(L'😀'));
+  written = LIBC_NAMESPACE::sprintf(buff, "%-4lc", L'😀');
   EXPECT_EQ(written, 4);
   ASSERT_STREQ_LEN(written, buff, "😀");
 
   // Grinning face emoji right justified.
-  written = LIBC_NAMESPACE::sprintf(buff, "%4lc", static_cast<wchar_t>(L'😀'));
+  written = LIBC_NAMESPACE::sprintf(buff, "%4lc", L'😀');
   EXPECT_EQ(written, 4);
   ASSERT_STREQ_LEN(written, buff, "😀");
 
   // Grinning face emoji with smaller width, left justified.
-  written = LIBC_NAMESPACE::sprintf(buff, "%-3lc", static_cast<wchar_t>(L'😀'));
+  written = LIBC_NAMESPACE::sprintf(buff, "%-3lc", L'😀');
   EXPECT_EQ(written, 4);
   ASSERT_STREQ_LEN(written, buff, "😀");
 
   // Grinning face emoji with smaller width, right justified.
-  written = LIBC_NAMESPACE::sprintf(buff, "%3lc", static_cast<wchar_t>(L'😀'));
+  written = LIBC_NAMESPACE::sprintf(buff, "%3lc", L'😀');
   EXPECT_EQ(written, 4);
   ASSERT_STREQ_LEN(written, buff, "😀");
 
   // WEOF test.
-  written = LIBC_NAMESPACE::sprintf(buff, "%lc", static_cast<wchar_t>(WEOF));
-  EXPECT_EQ(written, -1);
-  EXPECT_EQ(static_cast<int>(libc_errno), EILSEQ);
+  EXPECT_EQ(LIBC_NAMESPACE::sprintf(buff, "%lc", WEOF), -1);
+  ASSERT_ERRNO_EQ(EILSEQ);
 
   // Invalid wide character test
-  written =
-      LIBC_NAMESPACE::sprintf(buff, "%lc", static_cast<wchar_t>(0x12ffff));
-  EXPECT_EQ(written, -1);
-  EXPECT_EQ(static_cast<int>(libc_errno), EILSEQ);
+  EXPECT_EQ(
+      LIBC_NAMESPACE::sprintf(buff, "%lc", static_cast<wint_t>(0x12ffff)), -1);
+  ASSERT_ERRNO_EQ(EILSEQ);
 }
 #endif // LIBC_COPT_PRINTF_DISABLE_WIDE

>From f91e0e8767e1ca3998d8183265e29717d98106f3 Mon Sep 17 00:00:00 2001
From: "shubh at DOE" <shubhp at mbm3a24.local>
Date: Fri, 26 Dec 2025 10:19:42 -0800
Subject: [PATCH 7/7] fix formatter and test errors

---
 libc/test/src/stdio/sprintf_test.cpp | 9 +++++----
 1 file changed, 5 insertions(+), 4 deletions(-)

diff --git a/libc/test/src/stdio/sprintf_test.cpp b/libc/test/src/stdio/sprintf_test.cpp
index 707185c0dd8bc..5fbd54d702023 100644
--- a/libc/test/src/stdio/sprintf_test.cpp
+++ b/libc/test/src/stdio/sprintf_test.cpp
@@ -15,7 +15,7 @@
 #endif // LIBC_COPT_PRINTF_DISABLE_WIDE
 
 #include "src/__support/FPUtil/FPBits.h"
-#include "src/__support/libc_errno.h"
+#include "test/UnitTest/ErrnoCheckingTest.h"
 #include "test/UnitTest/RoundingModeUtils.h"
 #include "test/UnitTest/Test.h"
 #include <inttypes.h>
@@ -3544,12 +3544,13 @@ TEST(LlvmLibcSprintfTest, WideCharConversion) {
   ASSERT_STREQ_LEN(written, buff, "😀");
 
   // WEOF test.
-  EXPECT_EQ(LIBC_NAMESPACE::sprintf(buff, "%lc", WEOF), -1);
+  EXPECT_EQ(LIBC_NAMESPACE::sprintf(buff, "%lc", WEOF),
+            -1);
   ASSERT_ERRNO_EQ(EILSEQ);
 
   // Invalid wide character test
-  EXPECT_EQ(
-      LIBC_NAMESPACE::sprintf(buff, "%lc", static_cast<wint_t>(0x12ffff)), -1);
+  EXPECT_EQ(LIBC_NAMESPACE::sprintf(buff, "%lc", static_cast<wint_t>(0x12ffff)),
+            -1);
   ASSERT_ERRNO_EQ(EILSEQ);
 }
 #endif // LIBC_COPT_PRINTF_DISABLE_WIDE



More information about the libc-commits mailing list