[libc-commits] [libc] [libc] Support ls in printf (PR #178841)
Shubh Pachchigar via libc-commits
libc-commits at lists.llvm.org
Sat Jan 31 02:04:05 PST 2026
https://github.com/shubhe25p updated https://github.com/llvm/llvm-project/pull/178841
>From ea54d3d7405669ed505c29459ffed035c3bad719 Mon Sep 17 00:00:00 2001
From: shubhe25p <shubhp at mbm3a24.local>
Date: Sat, 24 Jan 2026 23:21:38 -0800
Subject: [PATCH 01/14] support v1
---
libc/src/stdio/printf_core/CMakeLists.txt | 1 +
libc/src/stdio/printf_core/parser.h | 25 +++++-
libc/src/stdio/printf_core/string_converter.h | 81 +++++++++++++++++--
libc/test/src/stdio/sprintf_test.cpp | 52 +++++++++++-
4 files changed, 149 insertions(+), 10 deletions(-)
diff --git a/libc/src/stdio/printf_core/CMakeLists.txt b/libc/src/stdio/printf_core/CMakeLists.txt
index ae93cc754299b..54e5051a838bc 100644
--- a/libc/src/stdio/printf_core/CMakeLists.txt
+++ b/libc/src/stdio/printf_core/CMakeLists.txt
@@ -56,6 +56,7 @@ else()
)
set(parser_wchar_deps
libc.hdr.types.wint_t
+ libc.hdr.types.wchar_t
)
endif()
diff --git a/libc/src/stdio/printf_core/parser.h b/libc/src/stdio/printf_core/parser.h
index a3b62991bcec9..2aa02ff7cb474 100644
--- a/libc/src/stdio/printf_core/parser.h
+++ b/libc/src/stdio/printf_core/parser.h
@@ -29,6 +29,7 @@
#endif // LIBC_COPT_PRINTF_DISABLE_STRERROR
#ifndef LIBC_COPT_PRINTF_DISABLE_WIDE
#include "hdr/types/wint_t.h"
+#include "hdr/types/wchar_t.h"
#endif // LIBC_COPT_PRINTF_DISABLE_WIDE
namespace LIBC_NAMESPACE_DECL {
@@ -290,7 +291,17 @@ template <typename ArgProvider> class Parser {
WRITE_ARG_VAL_SIMPLEST(section.conv_val_ptr, void *, conv_index);
break;
case ('s'):
- WRITE_ARG_VAL_SIMPLEST(section.conv_val_ptr, char *, conv_index);
+ if (section.length_modifier == LengthModifier::l) {
+#ifdef LIBC_COPT_PRINTF_DISABLE_WIDE
+ using WideCharArgType = char;
+#else
+ using WideCharArgType = wchar_t;
+#endif // LIBC_COPT_PRINTF_DISABLE_WIDE
+ WRITE_ARG_VAL_SIMPLEST(section.conv_val_raw, WideCharArgType *,
+ conv_index);
+ } else {
+ WRITE_ARG_VAL_SIMPLEST(section.conv_val_raw, char *, conv_index);
+ }
break;
default:
// if the conversion is undefined, change this to a raw section.
@@ -679,7 +690,17 @@ template <typename ArgProvider> class Parser {
#endif // LIBC_COPT_PRINTF_DISABLE_WRITE_INT
case ('p'):
case ('s'):
- conv_size = type_desc_from_type<void *>();
+ if (section.length_modifier == LengthModifier::l) {
+#ifdef LIBC_COPT_PRINTF_DISABLE_WIDE
+ using WideCharArgType = void;
+#else
+ using WideCharArgType = wchar_t;
+#endif // LIBC_COPT_PRINTF_DISABLE_WIDE
+ WRITE_ARG_VAL_SIMPLEST(section.conv_val_raw, WideCharArgType *,
+ conv_index);
+ } else {
+ WRITE_ARG_VAL_SIMPLEST(section.conv_val_raw, void *, conv_index);
+ }
break;
default:
conv_size = type_desc_from_type<int>();
diff --git a/libc/src/stdio/printf_core/string_converter.h b/libc/src/stdio/printf_core/string_converter.h
index 74c9f598210f7..52ef3eeb3da18 100644
--- a/libc/src/stdio/printf_core/string_converter.h
+++ b/libc/src/stdio/printf_core/string_converter.h
@@ -9,6 +9,14 @@
#ifndef LLVM_LIBC_SRC_STDIO_PRINTF_CORE_STRING_CONVERTER_H
#define LLVM_LIBC_SRC_STDIO_PRINTF_CORE_STRING_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/CPP/string_view.h"
#include "src/__support/macros/config.h"
#include "src/stdio/printf_core/converter_utils.h"
@@ -24,6 +32,8 @@ template <WriteMode write_mode>
LIBC_INLINE int convert_string(Writer<write_mode> *writer,
const FormatSection &to_conv) {
size_t string_len = 0;
+ char buffer[MB_LEN_MAX];
+ internal::mbstate mbstate;
const char *str_ptr = reinterpret_cast<const char *>(to_conv.conv_val_ptr);
#ifndef LIBC_COPT_PRINTF_NO_NULLPTR_CHECKS
@@ -32,13 +42,47 @@ LIBC_INLINE int convert_string(Writer<write_mode> *writer,
}
#endif // LIBC_COPT_PRINTF_NO_NULLPTR_CHECKS
- for (const char *cur_str = (str_ptr); cur_str[string_len]; ++string_len) {
- ;
- }
+ if (to_conv.length_modifier == LengthModifier::l) {
+#ifndef LIBC_COPT_PRINTF_DISABLE_WIDE
+ const wchar_t *wstr_ptr = reinterpret_cast<const wchar_t *>(to_conv.conv_val_ptr);
+#ifndef LIBC_COPT_PRINTF_NO_NULLPTR_CHECKS
+ if (wstr_ptr == nullptr) {
+ wstr_ptr = L"(null)";
+ }
+#endif // LIBC_COPT_PRINTF_NO_NULLPTR_CHECKS
+ size_t written = 0;
+ for (const wchar_t *cur_str = (wstr_ptr); cur_str[string_len]; ++string_len) {
+ wchar_t wc = cur_str[string_len];
+
+ auto ret = internal::wcrtomb(buffer, wc, &mbstate);
+ if (!ret.has_value()) {
+ return MB_CONVERSION_ERROR;
+ }
+ written += ret.value();
- if (to_conv.precision >= 0 &&
- static_cast<size_t>(to_conv.precision) < string_len)
- string_len = to_conv.precision;
+ if (to_conv.precision >= 0 && static_cast<size_t>(to_conv.precision) < written) {
+ written -= ret.value();
+ break;
+ }
+ }
+ string_len = written;
+#else
+ for (const char *cur_str = (str_ptr); cur_str[string_len]; ++string_len) {
+ if (to_conv.precision >= 0 && static_cast<size_t>(to_conv.precision) < string_len) {
+ string_len = to_conv.precision;
+ break;
+ }
+ }
+#endif // LIBC_COPT_PRINTF_DISABLE_WIDE
+ } else {
+
+ for (const char *cur_str = (str_ptr); cur_str[string_len]; ++string_len) {
+ if (to_conv.precision >= 0 && static_cast<size_t>(to_conv.precision) < string_len) {
+ string_len = to_conv.precision;
+ break;
+ }
+ }
+ }
size_t padding_spaces = to_conv.min_width > static_cast<int>(string_len)
? to_conv.min_width - string_len
@@ -50,7 +94,30 @@ LIBC_INLINE int convert_string(Writer<write_mode> *writer,
RET_IF_RESULT_NEGATIVE(writer->write(' ', padding_spaces));
}
- RET_IF_RESULT_NEGATIVE(writer->write({(str_ptr), string_len}));
+ if (to_conv.length_modifier == LengthModifier::l) {
+#ifndef LIBC_COPT_PRINTF_DISABLE_WIDE
+ const wchar_t *wstr_ptr = reinterpret_cast<const wchar_t *>(to_conv.conv_val_ptr);
+#ifndef LIBC_COPT_PRINTF_NO_NULLPTR_CHECKS
+ if (wstr_ptr == nullptr) {
+ wstr_ptr = L"(null)";
+ }
+#endif // LIBC_COPT_PRINTF_NO_NULLPTR_CHECKS
+ size_t written = 0;
+
+ for (size_t i = 0; written < string_len; ++i) {
+ // We don't need to check errors/precision here; Pass 1 guaranteed safety.
+ auto ret = internal::wcrtomb(buffer, wstr_ptr[i], &mbstate);
+ size_t mb_len = ret.value();
+
+ RET_IF_RESULT_NEGATIVE(writer->write({buffer, mb_len}));
+ written += mb_len;
+ }
+#else
+ RET_IF_RESULT_NEGATIVE(writer->write({(str_ptr), string_len}));
+#endif // LIBC_COPT_PRINTF_DISABLE_WIDE
+ } else {
+ RET_IF_RESULT_NEGATIVE(writer->write({(str_ptr), string_len}));
+ }
// If the padding is on the right side, write the spaces last.
if (padding_spaces > 0 &&
diff --git a/libc/test/src/stdio/sprintf_test.cpp b/libc/test/src/stdio/sprintf_test.cpp
index 78186abb9966e..3aa512e06081f 100644
--- a/libc/test/src/stdio/sprintf_test.cpp
+++ b/libc/test/src/stdio/sprintf_test.cpp
@@ -3582,4 +3582,54 @@ TEST(LlvmLibcSprintfTest, WideCharConversion) {
-1);
ASSERT_ERRNO_EQ(EILSEQ);
}
-#endif // LIBC_COPT_PRINTF_DISABLE_WIDE
+
+TEST(LlvmLibcSprintfTest, WideCharStringConversion) {
+ char buff[64];
+ int written;
+
+ // Basic test.
+ written = LIBC_NAMESPACE::sprintf(buff, "%ls", L"Hello, World!");
+ EXPECT_EQ(written, 13);
+ ASSERT_STREQ_LEN(written, buff, "Hello, World!");
+
+ // Left justified.
+ written = LIBC_NAMESPACE::sprintf(buff, "%-20ls", L"Hello, World!");
+ EXPECT_EQ(written, 20);
+ ASSERT_STREQ_LEN(written, buff, "Hello, World! ");
+
+ // Right justified.
+ written = LIBC_NAMESPACE::sprintf(buff, "%20ls", L"Hello, World!");
+ EXPECT_EQ(written, 20);
+ ASSERT_STREQ_LEN(written, buff, " Hello, World!");
+
+ // Precision test.
+ written = LIBC_NAMESPACE::sprintf(buff, "%.5ls", L"Hello, World!");
+ EXPECT_EQ(written, 5);
+ ASSERT_STREQ_LEN(written, buff, "Hello");
+
+ // Precision with multibyte characters.
+ written = LIBC_NAMESPACE::sprintf(buff, "%.4ls", L"¢€😀");
+ EXPECT_EQ(written, 4); // "¢€" is 4 bytes in UTF-8.
+ ASSERT_STREQ_LEN(written, buff, "¢€");
+
+ // Precision with padding and multibyte characters.
+ written = LIBC_NAMESPACE::sprintf(buff, "%10.4ls", L"¢€😀");
+ EXPECT_EQ(written, 10);
+ ASSERT_STREQ_LEN(written, buff, " ¢€");
+
+ // Precision with left justification and multibyte characters.
+ written = LIBC_NAMESPACE::sprintf(buff, "%-10.4ls", L"¢€😀");
+ EXPECT_EQ(written, 10);
+ ASSERT_STREQ_LEN(written, buff, "¢€ ");
+
+ // Short precision that cuts a multibyte character.
+ written = LIBC_NAMESPACE::sprintf(buff, "%.1ls", L"¢€😀");
+ EXPECT_EQ(written, 0);
+ ASSERT_STREQ_LEN(written, buff, "");
+
+ // NULL string test.
+ written = LIBC_NAMESPACE::sprintf(buff, "%ls", nullptr);
+ EXPECT_EQ(written, 6);
+ ASSERT_STREQ_LEN(written, buff, "(null)");
+}
+#endif // LIBC_COPT_PRINTF_DISABLE_WIDE
\ No newline at end of file
>From 6d5f1b7f3a1547c90aeaacdf2e37a74025168e62 Mon Sep 17 00:00:00 2001
From: shubhe25p <shubhp at mbm3a24.local>
Date: Sat, 24 Jan 2026 23:48:42 -0800
Subject: [PATCH 02/14] printf issues p1
---
libc/src/stdio/printf_core/parser.h | 5 ++---
1 file changed, 2 insertions(+), 3 deletions(-)
diff --git a/libc/src/stdio/printf_core/parser.h b/libc/src/stdio/printf_core/parser.h
index 2aa02ff7cb474..6e66d765bf94b 100644
--- a/libc/src/stdio/printf_core/parser.h
+++ b/libc/src/stdio/printf_core/parser.h
@@ -297,8 +297,7 @@ template <typename ArgProvider> class Parser {
#else
using WideCharArgType = wchar_t;
#endif // LIBC_COPT_PRINTF_DISABLE_WIDE
- WRITE_ARG_VAL_SIMPLEST(section.conv_val_raw, WideCharArgType *,
- conv_index);
+ WRITE_ARG_VAL_SIMPLEST(section.conv_val_raw, WideCharArgType *, conv_index);
} else {
WRITE_ARG_VAL_SIMPLEST(section.conv_val_raw, char *, conv_index);
}
@@ -690,7 +689,7 @@ template <typename ArgProvider> class Parser {
#endif // LIBC_COPT_PRINTF_DISABLE_WRITE_INT
case ('p'):
case ('s'):
- if (section.length_modifier == LengthModifier::l) {
+ if (lm == LengthModifier::l) {
#ifdef LIBC_COPT_PRINTF_DISABLE_WIDE
using WideCharArgType = void;
#else
>From f4b8b8b09992aca526fa3bd6bef6172137dcc94f Mon Sep 17 00:00:00 2001
From: shubhe25p <shubhp at mbm3a24.local>
Date: Sat, 24 Jan 2026 23:53:23 -0800
Subject: [PATCH 03/14] printf issues p2
---
libc/src/stdio/printf_core/parser.h | 5 ++---
1 file changed, 2 insertions(+), 3 deletions(-)
diff --git a/libc/src/stdio/printf_core/parser.h b/libc/src/stdio/printf_core/parser.h
index 6e66d765bf94b..761356cdc6a45 100644
--- a/libc/src/stdio/printf_core/parser.h
+++ b/libc/src/stdio/printf_core/parser.h
@@ -695,10 +695,9 @@ template <typename ArgProvider> class Parser {
#else
using WideCharArgType = wchar_t;
#endif // LIBC_COPT_PRINTF_DISABLE_WIDE
- WRITE_ARG_VAL_SIMPLEST(section.conv_val_raw, WideCharArgType *,
- conv_index);
+ conv_size = type_desc_from_type<WideCharArgType *>();
} else {
- WRITE_ARG_VAL_SIMPLEST(section.conv_val_raw, void *, conv_index);
+ conv_size = type_desc_from_type<char *>();
}
break;
default:
>From b2cfa129a1b9d0c878191d4edd50c633d1b49e28 Mon Sep 17 00:00:00 2001
From: shubhe25p <shubhp at mbm3a24.local>
Date: Sun, 25 Jan 2026 00:12:50 -0800
Subject: [PATCH 04/14] printf issues p3
---
libc/src/stdio/printf_core/parser.h | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/libc/src/stdio/printf_core/parser.h b/libc/src/stdio/printf_core/parser.h
index 761356cdc6a45..750b1fbb77d11 100644
--- a/libc/src/stdio/printf_core/parser.h
+++ b/libc/src/stdio/printf_core/parser.h
@@ -295,7 +295,7 @@ template <typename ArgProvider> class Parser {
#ifdef LIBC_COPT_PRINTF_DISABLE_WIDE
using WideCharArgType = char;
#else
- using WideCharArgType = wchar_t;
+ using WideCharArgType = void;
#endif // LIBC_COPT_PRINTF_DISABLE_WIDE
WRITE_ARG_VAL_SIMPLEST(section.conv_val_raw, WideCharArgType *, conv_index);
} else {
>From 8cdda9398d4f5dcaaf28748bab620fc987c570fe Mon Sep 17 00:00:00 2001
From: shubhe25p <shubhp at mbm3a24.local>
Date: Sun, 25 Jan 2026 00:33:59 -0800
Subject: [PATCH 05/14] printf issues p4
---
libc/src/stdio/printf_core/parser.h | 6 +++---
1 file changed, 3 insertions(+), 3 deletions(-)
diff --git a/libc/src/stdio/printf_core/parser.h b/libc/src/stdio/printf_core/parser.h
index 750b1fbb77d11..40f96772c3a0b 100644
--- a/libc/src/stdio/printf_core/parser.h
+++ b/libc/src/stdio/printf_core/parser.h
@@ -295,11 +295,11 @@ template <typename ArgProvider> class Parser {
#ifdef LIBC_COPT_PRINTF_DISABLE_WIDE
using WideCharArgType = char;
#else
- using WideCharArgType = void;
+ using WideCharArgType = wchar_t;
#endif // LIBC_COPT_PRINTF_DISABLE_WIDE
- WRITE_ARG_VAL_SIMPLEST(section.conv_val_raw, WideCharArgType *, conv_index);
+ WRITE_ARG_VAL_SIMPLEST(section.conv_val_ptr, WideCharArgType *, conv_index);
} else {
- WRITE_ARG_VAL_SIMPLEST(section.conv_val_raw, char *, conv_index);
+ WRITE_ARG_VAL_SIMPLEST(section.conv_val_ptr, char *, conv_index);
}
break;
default:
>From 6c5467d5a79ae39d6935d8d14dd79ee55293f0ad Mon Sep 17 00:00:00 2001
From: shubhe25p <shubhp at mbm3a24.local>
Date: Mon, 26 Jan 2026 23:43:42 -0800
Subject: [PATCH 06/14] fix tests v1
---
libc/src/stdio/printf_core/string_converter.h | 1 +
libc/test/src/stdio/sprintf_test.cpp | 26 ++++++++-----------
2 files changed, 12 insertions(+), 15 deletions(-)
diff --git a/libc/src/stdio/printf_core/string_converter.h b/libc/src/stdio/printf_core/string_converter.h
index 52ef3eeb3da18..443de61e0e4ef 100644
--- a/libc/src/stdio/printf_core/string_converter.h
+++ b/libc/src/stdio/printf_core/string_converter.h
@@ -97,6 +97,7 @@ LIBC_INLINE int convert_string(Writer<write_mode> *writer,
if (to_conv.length_modifier == LengthModifier::l) {
#ifndef LIBC_COPT_PRINTF_DISABLE_WIDE
const wchar_t *wstr_ptr = reinterpret_cast<const wchar_t *>(to_conv.conv_val_ptr);
+
#ifndef LIBC_COPT_PRINTF_NO_NULLPTR_CHECKS
if (wstr_ptr == nullptr) {
wstr_ptr = L"(null)";
diff --git a/libc/test/src/stdio/sprintf_test.cpp b/libc/test/src/stdio/sprintf_test.cpp
index 3aa512e06081f..5d2d35623ea72 100644
--- a/libc/test/src/stdio/sprintf_test.cpp
+++ b/libc/test/src/stdio/sprintf_test.cpp
@@ -3593,14 +3593,9 @@ TEST(LlvmLibcSprintfTest, WideCharStringConversion) {
ASSERT_STREQ_LEN(written, buff, "Hello, World!");
// Left justified.
- written = LIBC_NAMESPACE::sprintf(buff, "%-20ls", L"Hello, World!");
- EXPECT_EQ(written, 20);
- ASSERT_STREQ_LEN(written, buff, "Hello, World! ");
-
- // Right justified.
- written = LIBC_NAMESPACE::sprintf(buff, "%20ls", L"Hello, World!");
- EXPECT_EQ(written, 20);
- ASSERT_STREQ_LEN(written, buff, " Hello, World!");
+ written = LIBC_NAMESPACE::sprintf(buff, "%-10ls", L"Hello, World!");
+ EXPECT_EQ(written, 13);
+ ASSERT_STREQ_LEN(written, buff, "Hello, World!");
// Precision test.
written = LIBC_NAMESPACE::sprintf(buff, "%.5ls", L"Hello, World!");
@@ -3608,19 +3603,20 @@ TEST(LlvmLibcSprintfTest, WideCharStringConversion) {
ASSERT_STREQ_LEN(written, buff, "Hello");
// Precision with multibyte characters.
+ // ¢ is 2 bytes, € is 3 bytes, 😀 is 4 bytes
written = LIBC_NAMESPACE::sprintf(buff, "%.4ls", L"¢€😀");
- EXPECT_EQ(written, 4); // "¢€" is 4 bytes in UTF-8.
- ASSERT_STREQ_LEN(written, buff, "¢€");
+ EXPECT_EQ(written, 2); // Only ¢ fits in 4 bytes.
+ ASSERT_STREQ_LEN(written, buff, "¢");
// Precision with padding and multibyte characters.
- written = LIBC_NAMESPACE::sprintf(buff, "%10.4ls", L"¢€😀");
- EXPECT_EQ(written, 10);
- ASSERT_STREQ_LEN(written, buff, " ¢€");
+ written = LIBC_NAMESPACE::sprintf(buff, "%5.4ls", L"¢€😀");
+ EXPECT_EQ(written, 5);
+ ASSERT_STREQ_LEN(written, buff, " ¢");
// Precision with left justification and multibyte characters.
- written = LIBC_NAMESPACE::sprintf(buff, "%-10.4ls", L"¢€😀");
+ written = LIBC_NAMESPACE::sprintf(buff, "%-5.4ls", L"¢€😀");
EXPECT_EQ(written, 10);
- ASSERT_STREQ_LEN(written, buff, "¢€ ");
+ ASSERT_STREQ_LEN(written, buff, "¢ ");
// Short precision that cuts a multibyte character.
written = LIBC_NAMESPACE::sprintf(buff, "%.1ls", L"¢€😀");
>From 9f0bd7592aa421be7ff469c1fdfd30c24b0c4384 Mon Sep 17 00:00:00 2001
From: shubhe25p <shubhp at mbm3a24.local>
Date: Mon, 26 Jan 2026 23:45:12 -0800
Subject: [PATCH 07/14] fix tests v2
---
libc/test/src/stdio/sprintf_test.cpp | 9 +++++++++
1 file changed, 9 insertions(+)
diff --git a/libc/test/src/stdio/sprintf_test.cpp b/libc/test/src/stdio/sprintf_test.cpp
index 5d2d35623ea72..2f132b05fca5a 100644
--- a/libc/test/src/stdio/sprintf_test.cpp
+++ b/libc/test/src/stdio/sprintf_test.cpp
@@ -3627,5 +3627,14 @@ TEST(LlvmLibcSprintfTest, WideCharStringConversion) {
written = LIBC_NAMESPACE::sprintf(buff, "%ls", nullptr);
EXPECT_EQ(written, 6);
ASSERT_STREQ_LEN(written, buff, "(null)");
+
+ // WEOF test.
+ EXPECT_EQ(LIBC_NAMESPACE::sprintf(buff, "%ls", WEOF), -1);
+ ASSERT_ERRNO_EQ(EILSEQ);
+
+ // Invalid wide character test
+ EXPECT_EQ(LIBC_NAMESPACE::sprintf(buff, "%ls", static_cast<wint_t>(0x12ffff)),
+ -1);
+ ASSERT_ERRNO_EQ(EILSEQ);
}
#endif // LIBC_COPT_PRINTF_DISABLE_WIDE
\ No newline at end of file
>From a7755d6a8cdceb7d73fd7bbc9944e4efd3fb1571 Mon Sep 17 00:00:00 2001
From: shubhe25p <shubhp at mbm3a24.local>
Date: Tue, 27 Jan 2026 00:05:06 -0800
Subject: [PATCH 08/14] fix tests v3
---
libc/test/src/stdio/sprintf_test.cpp | 7 ++++++-
1 file changed, 6 insertions(+), 1 deletion(-)
diff --git a/libc/test/src/stdio/sprintf_test.cpp b/libc/test/src/stdio/sprintf_test.cpp
index 2f132b05fca5a..c3a68a523b89a 100644
--- a/libc/test/src/stdio/sprintf_test.cpp
+++ b/libc/test/src/stdio/sprintf_test.cpp
@@ -3615,7 +3615,7 @@ TEST(LlvmLibcSprintfTest, WideCharStringConversion) {
// Precision with left justification and multibyte characters.
written = LIBC_NAMESPACE::sprintf(buff, "%-5.4ls", L"¢€😀");
- EXPECT_EQ(written, 10);
+ EXPECT_EQ(written, 5);
ASSERT_STREQ_LEN(written, buff, "¢ ");
// Short precision that cuts a multibyte character.
@@ -3623,6 +3623,11 @@ TEST(LlvmLibcSprintfTest, WideCharStringConversion) {
EXPECT_EQ(written, 0);
ASSERT_STREQ_LEN(written, buff, "");
+ // Longer precision and shorter width.
+ written = LIBC_NAMESPACE::sprintf(buff, "%3.5ls", L"¢€😀");
+ EXPECT_EQ(written, 5);
+ ASSERT_STREQ_LEN(written, buff, "¢€");
+
// NULL string test.
written = LIBC_NAMESPACE::sprintf(buff, "%ls", nullptr);
EXPECT_EQ(written, 6);
>From cf598c6d17818ebdf637c7cef9526b34a3793f40 Mon Sep 17 00:00:00 2001
From: shubhe25p <shubhp at mbm3a24.local>
Date: Wed, 28 Jan 2026 22:54:51 -0800
Subject: [PATCH 09/14] some fixes
---
libc/test/src/stdio/sprintf_test.cpp | 16 ++++++++--------
1 file changed, 8 insertions(+), 8 deletions(-)
diff --git a/libc/test/src/stdio/sprintf_test.cpp b/libc/test/src/stdio/sprintf_test.cpp
index c3a68a523b89a..9ac826a5110f1 100644
--- a/libc/test/src/stdio/sprintf_test.cpp
+++ b/libc/test/src/stdio/sprintf_test.cpp
@@ -3633,13 +3633,13 @@ TEST(LlvmLibcSprintfTest, WideCharStringConversion) {
EXPECT_EQ(written, 6);
ASSERT_STREQ_LEN(written, buff, "(null)");
- // WEOF test.
- EXPECT_EQ(LIBC_NAMESPACE::sprintf(buff, "%ls", WEOF), -1);
- ASSERT_ERRNO_EQ(EILSEQ);
-
- // Invalid wide character test
- EXPECT_EQ(LIBC_NAMESPACE::sprintf(buff, "%ls", static_cast<wint_t>(0x12ffff)),
- -1);
- ASSERT_ERRNO_EQ(EILSEQ);
+ // // WEOF test.
+ // EXPECT_EQ(LIBC_NAMESPACE::sprintf(buff, "%ls", WEOF), -1);
+ // ASSERT_ERRNO_EQ(EILSEQ);
+
+ // // Invalid wide character test
+ // EXPECT_EQ(LIBC_NAMESPACE::sprintf(buff, "%ls", static_cast<wint_t>(0x12ffff)),
+ // -1);
+ // ASSERT_ERRNO_EQ(EILSEQ);
}
#endif // LIBC_COPT_PRINTF_DISABLE_WIDE
\ No newline at end of file
>From ac9645cdc27b43c61b1aa193f0a4c82d58655bcd Mon Sep 17 00:00:00 2001
From: shubhe25p <shubhp at mbm3a24.local>
Date: Wed, 28 Jan 2026 23:24:37 -0800
Subject: [PATCH 10/14] fix precision
---
libc/src/stdio/printf_core/string_converter.h | 4 ++--
libc/test/src/stdio/sprintf_test.cpp | 18 +++++++++---------
2 files changed, 11 insertions(+), 11 deletions(-)
diff --git a/libc/src/stdio/printf_core/string_converter.h b/libc/src/stdio/printf_core/string_converter.h
index 443de61e0e4ef..c479a43f68957 100644
--- a/libc/src/stdio/printf_core/string_converter.h
+++ b/libc/src/stdio/printf_core/string_converter.h
@@ -68,7 +68,7 @@ LIBC_INLINE int convert_string(Writer<write_mode> *writer,
string_len = written;
#else
for (const char *cur_str = (str_ptr); cur_str[string_len]; ++string_len) {
- if (to_conv.precision >= 0 && static_cast<size_t>(to_conv.precision) < string_len) {
+ if (to_conv.precision >= 0 && static_cast<size_t>(to_conv.precision) <= string_len) {
string_len = to_conv.precision;
break;
}
@@ -77,7 +77,7 @@ LIBC_INLINE int convert_string(Writer<write_mode> *writer,
} else {
for (const char *cur_str = (str_ptr); cur_str[string_len]; ++string_len) {
- if (to_conv.precision >= 0 && static_cast<size_t>(to_conv.precision) < string_len) {
+ if (to_conv.precision >= 0 && static_cast<size_t>(to_conv.precision) <= string_len) {
string_len = to_conv.precision;
break;
}
diff --git a/libc/test/src/stdio/sprintf_test.cpp b/libc/test/src/stdio/sprintf_test.cpp
index 9ac826a5110f1..5b5baed071037 100644
--- a/libc/test/src/stdio/sprintf_test.cpp
+++ b/libc/test/src/stdio/sprintf_test.cpp
@@ -3633,13 +3633,13 @@ TEST(LlvmLibcSprintfTest, WideCharStringConversion) {
EXPECT_EQ(written, 6);
ASSERT_STREQ_LEN(written, buff, "(null)");
- // // WEOF test.
- // EXPECT_EQ(LIBC_NAMESPACE::sprintf(buff, "%ls", WEOF), -1);
- // ASSERT_ERRNO_EQ(EILSEQ);
-
- // // Invalid wide character test
- // EXPECT_EQ(LIBC_NAMESPACE::sprintf(buff, "%ls", static_cast<wint_t>(0x12ffff)),
- // -1);
- // ASSERT_ERRNO_EQ(EILSEQ);
+ // WEOF test.
+ EXPECT_EQ(LIBC_NAMESPACE::sprintf(buff, "%ls", WEOF), -1);
+ ASSERT_ERRNO_EQ(EILSEQ);
+
+ // Invalid wide character test
+ EXPECT_EQ(LIBC_NAMESPACE::sprintf(buff, "%ls", static_cast<wint_t>(0x12ffff)),
+ -1);
+ ASSERT_ERRNO_EQ(EILSEQ);
}
-#endif // LIBC_COPT_PRINTF_DISABLE_WIDE
\ No newline at end of file
+#endif // LIBC_COPT_PRINTF_DISABLE_WIDE
>From 6a7f670581b95841ef8dd39d15f6ca2f3ce14550 Mon Sep 17 00:00:00 2001
From: shubhe25p <shubhp at mbm3a24.local>
Date: Wed, 28 Jan 2026 23:47:55 -0800
Subject: [PATCH 11/14] fix precision v2
---
libc/src/stdio/printf_core/string_converter.h | 12 ++++++------
1 file changed, 6 insertions(+), 6 deletions(-)
diff --git a/libc/src/stdio/printf_core/string_converter.h b/libc/src/stdio/printf_core/string_converter.h
index c479a43f68957..4da0c7b2f28e5 100644
--- a/libc/src/stdio/printf_core/string_converter.h
+++ b/libc/src/stdio/printf_core/string_converter.h
@@ -68,20 +68,20 @@ LIBC_INLINE int convert_string(Writer<write_mode> *writer,
string_len = written;
#else
for (const char *cur_str = (str_ptr); cur_str[string_len]; ++string_len) {
- if (to_conv.precision >= 0 && static_cast<size_t>(to_conv.precision) <= string_len) {
+ ;
+ }
+ if (to_conv.precision >= 0 && static_cast<size_t>(to_conv.precision) < string_len) {
string_len = to_conv.precision;
- break;
}
- }
#endif // LIBC_COPT_PRINTF_DISABLE_WIDE
} else {
for (const char *cur_str = (str_ptr); cur_str[string_len]; ++string_len) {
- if (to_conv.precision >= 0 && static_cast<size_t>(to_conv.precision) <= string_len) {
+ ;
+ }
+ if (to_conv.precision >= 0 && static_cast<size_t>(to_conv.precision) < string_len) {
string_len = to_conv.precision;
- break;
}
- }
}
size_t padding_spaces = to_conv.min_width > static_cast<int>(string_len)
>From a4ac9ea89e83b2678bd2be180828affc04f41696 Mon Sep 17 00:00:00 2001
From: shubhe25p <shubhp at mbm3a24.local>
Date: Fri, 30 Jan 2026 00:57:04 -0800
Subject: [PATCH 12/14] fix build and test
---
libc/src/stdio/printf_core/parser.h | 22 ++-----------------
libc/src/stdio/printf_core/string_converter.h | 13 ++++++++---
2 files changed, 12 insertions(+), 23 deletions(-)
diff --git a/libc/src/stdio/printf_core/parser.h b/libc/src/stdio/printf_core/parser.h
index 40f96772c3a0b..92a21e7da4862 100644
--- a/libc/src/stdio/printf_core/parser.h
+++ b/libc/src/stdio/printf_core/parser.h
@@ -291,16 +291,7 @@ template <typename ArgProvider> class Parser {
WRITE_ARG_VAL_SIMPLEST(section.conv_val_ptr, void *, conv_index);
break;
case ('s'):
- if (section.length_modifier == LengthModifier::l) {
-#ifdef LIBC_COPT_PRINTF_DISABLE_WIDE
- using WideCharArgType = char;
-#else
- using WideCharArgType = wchar_t;
-#endif // LIBC_COPT_PRINTF_DISABLE_WIDE
- WRITE_ARG_VAL_SIMPLEST(section.conv_val_ptr, WideCharArgType *, conv_index);
- } else {
- WRITE_ARG_VAL_SIMPLEST(section.conv_val_ptr, char *, conv_index);
- }
+ WRITE_ARG_VAL_SIMPLEST(section.conv_val_ptr, void *, conv_index);
break;
default:
// if the conversion is undefined, change this to a raw section.
@@ -689,16 +680,7 @@ template <typename ArgProvider> class Parser {
#endif // LIBC_COPT_PRINTF_DISABLE_WRITE_INT
case ('p'):
case ('s'):
- if (lm == LengthModifier::l) {
-#ifdef LIBC_COPT_PRINTF_DISABLE_WIDE
- using WideCharArgType = void;
-#else
- using WideCharArgType = wchar_t;
-#endif // LIBC_COPT_PRINTF_DISABLE_WIDE
- conv_size = type_desc_from_type<WideCharArgType *>();
- } else {
- conv_size = type_desc_from_type<char *>();
- }
+ conv_size = type_desc_from_type<void *>();
break;
default:
conv_size = type_desc_from_type<int>();
diff --git a/libc/src/stdio/printf_core/string_converter.h b/libc/src/stdio/printf_core/string_converter.h
index 4da0c7b2f28e5..66f2637a4dd79 100644
--- a/libc/src/stdio/printf_core/string_converter.h
+++ b/libc/src/stdio/printf_core/string_converter.h
@@ -32,8 +32,6 @@ template <WriteMode write_mode>
LIBC_INLINE int convert_string(Writer<write_mode> *writer,
const FormatSection &to_conv) {
size_t string_len = 0;
- char buffer[MB_LEN_MAX];
- internal::mbstate mbstate;
const char *str_ptr = reinterpret_cast<const char *>(to_conv.conv_val_ptr);
#ifndef LIBC_COPT_PRINTF_NO_NULLPTR_CHECKS
@@ -45,11 +43,15 @@ LIBC_INLINE int convert_string(Writer<write_mode> *writer,
if (to_conv.length_modifier == LengthModifier::l) {
#ifndef LIBC_COPT_PRINTF_DISABLE_WIDE
const wchar_t *wstr_ptr = reinterpret_cast<const wchar_t *>(to_conv.conv_val_ptr);
+
#ifndef LIBC_COPT_PRINTF_NO_NULLPTR_CHECKS
if (wstr_ptr == nullptr) {
wstr_ptr = L"(null)";
}
#endif // LIBC_COPT_PRINTF_NO_NULLPTR_CHECKS
+
+ char buffer[MB_LEN_MAX];
+ internal::mbstate mbstate;
size_t written = 0;
for (const wchar_t *cur_str = (wstr_ptr); cur_str[string_len]; ++string_len) {
wchar_t wc = cur_str[string_len];
@@ -64,15 +66,18 @@ LIBC_INLINE int convert_string(Writer<write_mode> *writer,
written -= ret.value();
break;
}
+
}
string_len = written;
#else
for (const char *cur_str = (str_ptr); cur_str[string_len]; ++string_len) {
;
}
+
if (to_conv.precision >= 0 && static_cast<size_t>(to_conv.precision) < string_len) {
string_len = to_conv.precision;
}
+
#endif // LIBC_COPT_PRINTF_DISABLE_WIDE
} else {
@@ -103,8 +108,10 @@ LIBC_INLINE int convert_string(Writer<write_mode> *writer,
wstr_ptr = L"(null)";
}
#endif // LIBC_COPT_PRINTF_NO_NULLPTR_CHECKS
- size_t written = 0;
+ size_t written = 0;
+ char buffer[MB_LEN_MAX];
+ internal::mbstate mbstate;
for (size_t i = 0; written < string_len; ++i) {
// We don't need to check errors/precision here; Pass 1 guaranteed safety.
auto ret = internal::wcrtomb(buffer, wstr_ptr[i], &mbstate);
>From 37260a810335f746640f28e4ba8c0ef1791ee4b9 Mon Sep 17 00:00:00 2001
From: shubhe25p <shubhp at mbm3a24.local>
Date: Fri, 30 Jan 2026 01:03:21 -0800
Subject: [PATCH 13/14] fix seg fault
---
libc/test/src/stdio/sprintf_test.cpp | 8 ++++----
1 file changed, 4 insertions(+), 4 deletions(-)
diff --git a/libc/test/src/stdio/sprintf_test.cpp b/libc/test/src/stdio/sprintf_test.cpp
index 5b5baed071037..08bb666f85c41 100644
--- a/libc/test/src/stdio/sprintf_test.cpp
+++ b/libc/test/src/stdio/sprintf_test.cpp
@@ -3628,10 +3628,10 @@ TEST(LlvmLibcSprintfTest, WideCharStringConversion) {
EXPECT_EQ(written, 5);
ASSERT_STREQ_LEN(written, buff, "¢€");
- // NULL string test.
- written = LIBC_NAMESPACE::sprintf(buff, "%ls", nullptr);
- EXPECT_EQ(written, 6);
- ASSERT_STREQ_LEN(written, buff, "(null)");
+ // // NULL string test.
+ // written = LIBC_NAMESPACE::sprintf(buff, "%ls", nullptr);
+ // EXPECT_EQ(written, 6);
+ // ASSERT_STREQ_LEN(written, buff, "(null)");
// WEOF test.
EXPECT_EQ(LIBC_NAMESPACE::sprintf(buff, "%ls", WEOF), -1);
>From da6c050d3e69054ec7930a6a528280ef952b1a04 Mon Sep 17 00:00:00 2001
From: shubhe25p <shubhp at mbm3a24.local>
Date: Sat, 31 Jan 2026 02:03:44 -0800
Subject: [PATCH 14/14] fix seg fault
---
libc/test/src/stdio/sprintf_test.cpp | 17 ++++-------------
1 file changed, 4 insertions(+), 13 deletions(-)
diff --git a/libc/test/src/stdio/sprintf_test.cpp b/libc/test/src/stdio/sprintf_test.cpp
index 08bb666f85c41..04ee46609d004 100644
--- a/libc/test/src/stdio/sprintf_test.cpp
+++ b/libc/test/src/stdio/sprintf_test.cpp
@@ -3628,18 +3628,9 @@ TEST(LlvmLibcSprintfTest, WideCharStringConversion) {
EXPECT_EQ(written, 5);
ASSERT_STREQ_LEN(written, buff, "¢€");
- // // NULL string test.
- // written = LIBC_NAMESPACE::sprintf(buff, "%ls", nullptr);
- // EXPECT_EQ(written, 6);
- // ASSERT_STREQ_LEN(written, buff, "(null)");
-
- // WEOF test.
- EXPECT_EQ(LIBC_NAMESPACE::sprintf(buff, "%ls", WEOF), -1);
- ASSERT_ERRNO_EQ(EILSEQ);
-
- // Invalid wide character test
- EXPECT_EQ(LIBC_NAMESPACE::sprintf(buff, "%ls", static_cast<wint_t>(0x12ffff)),
- -1);
- ASSERT_ERRNO_EQ(EILSEQ);
+ // NULL string test.
+ written = LIBC_NAMESPACE::sprintf(buff, "%ls", nullptr);
+ EXPECT_EQ(written, 6);
+ ASSERT_STREQ_LEN(written, buff, "(null)");
}
#endif // LIBC_COPT_PRINTF_DISABLE_WIDE
More information about the libc-commits
mailing list