Skip to content

Commit 552b263

Browse files
author
shubh@DOE
committed
[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.
1 parent 8378a6f commit 552b263

File tree

8 files changed

+158
-6
lines changed

8 files changed

+158
-6
lines changed

libc/docs/dev/printf_behavior.rst

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -71,6 +71,13 @@ conversions (%r, %k); any fixed point number conversion will be treated as
7171
invalid. This reduces code size. This has no effect if the current compiler does
7272
not support fixed point numbers.
7373

74+
LIBC_COPT_PRINTF_DISABLE_WIDE
75+
--------------------------------
76+
When set, this flag disables support for wide characters (%lc and %ls). Any
77+
conversions will be ignored. This reduces code size. This will be set by default
78+
on windows platforms as current printf implementation does not support UTF-16 wide
79+
characters.
80+
7481
LIBC_COPT_PRINTF_NO_NULLPTR_CHECKS
7582
----------------------------------
7683
When set, this flag disables the nullptr checks in %n and %s.

libc/src/stdio/printf_core/CMakeLists.txt

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,14 @@ if(NOT TARGET ${target_error_mapper})
4343
set(target_error_mapper libc.src.stdio.printf_core.generic.error_mapper)
4444
endif()
4545

46+
set(wchar_deps libc.src.__support.wchar.wcrtomb)
47+
set(wchar_compile_flags "")
48+
49+
if(${LIBC_TARGET_OS} STREQUAL "windows" OR WIN32)
50+
set(wchar_deps "")
51+
list(APPEND wchar_compile_flags "-DLIBC_COPT_PRINTF_DISABLE_WIDE")
52+
endif()
53+
4654
add_header_library(
4755
printf_config
4856
HDRS
@@ -67,6 +75,7 @@ add_header_library(
6775
parser.h
6876
DEPENDS
6977
.core_structs
78+
libc.hdr.types.wint_t
7079
libc.src.__support.arg_list
7180
libc.src.__support.ctype_utils
7281
libc.src.__support.str_to_integer
@@ -106,11 +115,16 @@ add_header_library(
106115
float_dec_converter.h
107116
fixed_converter.h #TODO: Check if this should be disabled when fixed unavail
108117
strerror_converter.h
118+
COMPILE_OPTIONS
119+
${wchar_compile_flags}
109120
DEPENDS
110121
.core_structs
111122
.printf_config
112123
.writer
113124
libc.include.inttypes
125+
libc.hdr.types.wchar_t
126+
libc.hdr.types.wint_t
127+
libc.hdr.wchar_macros
114128
libc.src.__support.big_int
115129
libc.src.__support.common
116130
libc.src.__support.CPP.limits
@@ -123,8 +137,10 @@ add_header_library(
123137
libc.src.__support.integer_to_string
124138
libc.src.__support.libc_assert
125139
libc.src.__support.uint128
140+
libc.src.__support.wchar.mbstate
126141
libc.src.__support.StringUtil.error_to_string
127142
libc.src.string.memory_utils.inline_memcpy
143+
${wchar_deps}
128144
)
129145

130146
add_header_library(

libc/src/stdio/printf_core/char_converter.h

Lines changed: 32 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
//===-- String Converter for printf -----------------------------*- C++ -*-===//
1+
//===-- Character Converter for printf --------------------------*- C++ -*-===//
22
//
33
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
44
// See https://llvm.org/LICENSE.txt for license information.
@@ -9,6 +9,15 @@
99
#ifndef LLVM_LIBC_SRC_STDIO_PRINTF_CORE_CHAR_CONVERTER_H
1010
#define LLVM_LIBC_SRC_STDIO_PRINTF_CORE_CHAR_CONVERTER_H
1111

12+
#include "hdr/types/wchar_t.h"
13+
#include "hdr/types/wint_t.h"
14+
#include "hdr/wchar_macros.h"
15+
#include "src/__support/wchar/mbstate.h"
16+
17+
#ifndef LIBC_COPT_PRINTF_DISABLE_WIDE
18+
#include "src/__support/wchar/wcrtomb.h"
19+
#endif // LIBC_COPT_PRINTF_DISABLE_WIDE
20+
1221
#include "src/__support/macros/config.h"
1322
#include "src/stdio/printf_core/converter_utils.h"
1423
#include "src/stdio/printf_core/core_structs.h"
@@ -33,7 +42,28 @@ LIBC_INLINE int convert_char(Writer<write_mode> *writer,
3342
RET_IF_RESULT_NEGATIVE(writer->write(' ', padding_spaces));
3443
}
3544

36-
RET_IF_RESULT_NEGATIVE(writer->write(c));
45+
if (to_conv.length_modifier == LengthModifier::l) {
46+
#ifndef LIBC_COPT_PRINTF_DISABLE_WIDE
47+
wint_t wi = static_cast<wint_t>(to_conv.conv_val_raw);
48+
49+
if (wi == WEOF) {
50+
return -1;
51+
}
52+
53+
char mb_str[MB_LEN_MAX];
54+
internal::mbstate mbstate;
55+
wchar_t wc = static_cast<wchar_t>(wi);
56+
57+
auto ret = internal::wcrtomb(mb_str, wc, &mbstate);
58+
if (!ret.has_value()) {
59+
return -1;
60+
}
61+
62+
RET_IF_RESULT_NEGATIVE(writer->write({mb_str, ret.value()}));
63+
#endif // LIBC_COPT_PRINTF_DISABLE_WIDE
64+
} else {
65+
RET_IF_RESULT_NEGATIVE(writer->write(c));
66+
}
3767

3868
// If the padding is on the right side, write the spaces last.
3969
if (padding_spaces > 0 &&

libc/src/stdio/printf_core/parser.h

Lines changed: 9 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@
99
#ifndef LLVM_LIBC_SRC_STDIO_PRINTF_CORE_PARSER_H
1010
#define LLVM_LIBC_SRC_STDIO_PRINTF_CORE_PARSER_H
1111

12+
#include "hdr/types/wint_t.h"
1213
#include "include/llvm-libc-macros/stdfix-macros.h"
1314
#include "src/__support/CPP/algorithm.h" // max
1415
#include "src/__support/CPP/limits.h"
@@ -73,9 +74,9 @@ template <typename ArgProvider> class Parser {
7374
ArgProvider args_cur;
7475

7576
#ifndef LIBC_COPT_PRINTF_DISABLE_INDEX_MODE
76-
// args_start stores the start of the va_args, which is allows getting the
77-
// value of arguments that have already been passed. args_index is tracked so
78-
// that we know which argument args_cur is on.
77+
// args_start stores the start of the va_args, which helps in getting the
78+
// number of arguments that have already been passed. args_index is tracked
79+
// so that we know which argument args_cur is on.
7980
ArgProvider args_start;
8081
size_t args_index = 1;
8182

@@ -173,7 +174,11 @@ template <typename ArgProvider> class Parser {
173174
section.has_conv = true;
174175
break;
175176
case ('c'):
176-
WRITE_ARG_VAL_SIMPLEST(section.conv_val_raw, int, conv_index);
177+
if (section.length_modifier == LengthModifier::l) {
178+
WRITE_ARG_VAL_SIMPLEST(section.conv_val_raw, wint_t, conv_index);
179+
} else {
180+
WRITE_ARG_VAL_SIMPLEST(section.conv_val_raw, int, conv_index);
181+
}
177182
break;
178183
case ('d'):
179184
case ('i'):

libc/test/src/stdio/CMakeLists.txt

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -137,6 +137,11 @@ if(LIBC_CONF_PRINTF_DISABLE_STRERROR)
137137
list(APPEND sprintf_test_copts "-DLIBC_COPT_PRINTF_DISABLE_STRERROR")
138138
endif()
139139

140+
set(snprintf_test_flags "")
141+
if(${LIBC_TARGET_OS} STREQUAL "windows" OR WIN32)
142+
list(APPEND snprintf_test_flags "-DLIBC_COPT_PRINTF_DISABLE_WIDE")
143+
endif()
144+
140145
add_fp_unittest(
141146
sprintf_test
142147
UNIT_TEST_ONLY
@@ -158,7 +163,11 @@ add_libc_test(
158163
libc_stdio_unittests
159164
SRCS
160165
snprintf_test.cpp
166+
COMPILE_OPTIONS
167+
${snprintf_test_flags}
161168
DEPENDS
169+
libc.hdr.types.wchar_t
170+
libc.hdr.wchar_macros
162171
libc.src.stdio.snprintf
163172
)
164173

libc/test/src/stdio/printf_core/CMakeLists.txt

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,8 @@
1+
set(printf_test_flags "")
2+
if(${LIBC_TARGET_OS} STREQUAL "windows" OR WIN32)
3+
list(APPEND printf_test_flags "-DLIBC_COPT_PRINTF_DISABLE_WIDE")
4+
endif()
5+
16
add_libc_unittest(
27
parser_test
38
SUITE
@@ -6,7 +11,10 @@ add_libc_unittest(
611
parser_test.cpp
712
LINK_LIBRARIES
813
LibcPrintfHelpers
14+
COMPILE_OPTIONS
15+
${printf_test_flags}
916
DEPENDS
17+
libc.hdr.types.wchar_t
1018
libc.src.stdio.printf_core.parser
1119
libc.src.stdio.printf_core.core_structs
1220
libc.src.__support.CPP.string_view
@@ -19,6 +27,8 @@ add_libc_unittest(
1927
libc_stdio_unittests
2028
SRCS
2129
writer_test.cpp
30+
COMPILE_OPTIONS
31+
${printf_test_flags}
2232
DEPENDS
2333
libc.src.stdio.printf_core.writer
2434
libc.src.string.memory_utils.inline_memcpy
@@ -31,6 +41,8 @@ add_libc_unittest(
3141
libc_stdio_unittests
3242
SRCS
3343
converter_test.cpp
44+
COMPILE_OPTIONS
45+
${printf_test_flags}
3446
DEPENDS
3547
libc.src.stdio.printf_core.converter
3648
libc.src.stdio.printf_core.writer

libc/test/src/stdio/printf_core/parser_test.cpp

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66
//
77
//===----------------------------------------------------------------------===//
88

9+
#include "hdr/types/wchar_t.h"
910
#include "src/__support/CPP/bit.h"
1011
#include "src/__support/CPP/string_view.h"
1112
#include "src/__support/arg_list.h"
@@ -370,6 +371,24 @@ TEST(LlvmLibcPrintfParserTest,
370371
ASSERT_PFORMAT_EQ(expected, format_arr[0]);
371372
}
372373

374+
TEST(LlvmLibcPrintfParserTest, EvalOneArgWithWideCharacter) {
375+
LIBC_NAMESPACE::printf_core::FormatSection format_arr[2];
376+
const char *str = "%lc";
377+
wchar_t arg1 = L'';
378+
evaluate(format_arr, str, arg1);
379+
380+
LIBC_NAMESPACE::printf_core::FormatSection expected;
381+
expected.has_conv = true;
382+
383+
expected.raw_string = {str, 3};
384+
expected.length_modifier = LIBC_NAMESPACE::printf_core::LengthModifier::l;
385+
expected.conv_val_raw =
386+
static_cast<LIBC_NAMESPACE::fputil::FPBits<double>::StorageType>(arg1);
387+
expected.conv_name = 'c';
388+
389+
ASSERT_PFORMAT_EQ(expected, format_arr[0]);
390+
}
391+
373392
#ifndef LIBC_COPT_PRINTF_DISABLE_INDEX_MODE
374393

375394
TEST(LlvmLibcPrintfParserTest, IndexModeOneArg) {

libc/test/src/stdio/snprintf_test.cpp

Lines changed: 54 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,8 @@
66
//
77
//===----------------------------------------------------------------------===//
88

9+
#include "hdr/types/wchar_t.h"
10+
#include "hdr/wchar_macros.h"
911
#include "src/stdio/snprintf.h"
1012

1113
#include "test/UnitTest/ErrnoCheckingTest.h"
@@ -74,3 +76,55 @@ TEST(LlvmLibcSNPrintfTest, CharsWrittenOverflow) {
7476
EXPECT_LT(written, 0);
7577
ASSERT_ERRNO_FAILURE();
7678
}
79+
80+
#ifndef LIBC_COPT_PRINTF_DISABLE_WIDE
81+
TEST(LlvmLibcSNPrintfTest, WideCharConversion) {
82+
char buff[16];
83+
int written;
84+
85+
written = LIBC_NAMESPACE::snprintf(buff, sizeof(buff), "%lc",
86+
static_cast<wchar_t>(L''));
87+
EXPECT_EQ(written, 3);
88+
ASSERT_STREQ(buff, "");
89+
}
90+
91+
TEST(LlvmLibcSNPrintfTest, WideCharConversionLeftJustified) {
92+
char buff[16];
93+
int written;
94+
95+
written = LIBC_NAMESPACE::snprintf(buff, sizeof(buff), "%-4lc",
96+
static_cast<wchar_t>(L''));
97+
EXPECT_EQ(written, 6);
98+
ASSERT_STREQ(buff, "");
99+
}
100+
101+
TEST(LlvmLibcSNPrintfTest, WideCharConversionRightJustified) {
102+
char buff[16];
103+
int written;
104+
105+
written = LIBC_NAMESPACE::snprintf(buff, sizeof(buff), "%4lc",
106+
static_cast<wchar_t>(L''));
107+
EXPECT_EQ(written, 6);
108+
ASSERT_STREQ(buff, "");
109+
}
110+
111+
TEST(LlvmLibcSNPrintfTest, WideCharWEOFConversion) {
112+
char buff[16];
113+
int written;
114+
115+
written = LIBC_NAMESPACE::snprintf(buff, sizeof(buff), "%lc",
116+
static_cast<wchar_t>(WEOF));
117+
EXPECT_EQ(written, -1);
118+
ASSERT_ERRNO_FAILURE();
119+
}
120+
121+
TEST(LlvmLibcSNPrintfTest, WideCharInvalidConversion) {
122+
char buff[16];
123+
int written;
124+
125+
written = LIBC_NAMESPACE::snprintf(buff, sizeof(buff), "%lc",
126+
static_cast<wchar_t>(0x12ffff));
127+
EXPECT_EQ(written, -1);
128+
ASSERT_ERRNO_FAILURE();
129+
}
130+
#endif // LIBC_COPT_PRINTF_DISABLE_WIDE

0 commit comments

Comments
 (0)