From 3b846ea6e7cf53dfd9df5307315c7ee5e7e59232 Mon Sep 17 00:00:00 2001 From: zijunzhao Date: Tue, 11 Apr 2023 20:53:39 +0000 Subject: [PATCH] Implement C23 printf 'w' length modifiers wN: Specifies that a following b, d, i, o, u, x, or X conversion specifier applies to an integer argument with a specific width where N is a positive decimal integer with no leading zeros Bug: b/271903607 Test: adb shell Change-Id: I688f6cefeb2e5c8325b007a59935a46f4116ac29 --- libc/stdio/printf_common.h | 11 +++ libc/stdio/vfprintf.cpp | 28 ++++++++ libc/stdio/vfwprintf.cpp | 28 ++++++++ tests/stdio_test.cpp | 141 +++++++++++++++++++++++++++++++++++++ 4 files changed, 208 insertions(+) diff --git a/libc/stdio/printf_common.h b/libc/stdio/printf_common.h index e7618354e..b0055f0ff 100644 --- a/libc/stdio/printf_common.h +++ b/libc/stdio/printf_common.h @@ -528,6 +528,17 @@ static int __find_arguments(const CHAR_TYPE* fmt0, va_list ap, union arg** argta case 'b': ADDUARG(); break; + case 'w': + n = 0; + ch = *fmt++; + while (is_digit(ch)) { + APPEND_DIGIT(n, ch); + ch = *fmt++; + } + if (n == 64) { + flags |= LLONGINT; + } + goto reswitch; default: /* "%?" prints ?, unless ? is NUL */ if (ch == '\0') goto done; break; diff --git a/libc/stdio/vfprintf.cpp b/libc/stdio/vfprintf.cpp index d83a5bf44..b7c68dd65 100644 --- a/libc/stdio/vfprintf.cpp +++ b/libc/stdio/vfprintf.cpp @@ -521,6 +521,34 @@ int FUNCTION_NAME(FILE* fp, const CHAR_TYPE* fmt0, va_list ap) { _umax = UARG(); base = DEC; goto nosign; + case 'w': + n = 0; + ch = *fmt++; + while (is_digit(ch)) { + APPEND_DIGIT(n, ch); + ch = *fmt++; + } + switch (n) { + case 8: { + flags |= CHARINT; + goto reswitch; + } + case 16: { + flags |= SHORTINT; + goto reswitch; + } + case 32: { + goto reswitch; + } + case 64: { + flags |= LLONGINT; + goto reswitch; + } + default: { + __fortify_fatal("%%w%d is unsupported", n); + break; + } + } case 'X': xdigs = xdigs_upper; goto hex; diff --git a/libc/stdio/vfwprintf.cpp b/libc/stdio/vfwprintf.cpp index 9819a731c..52ae64b6e 100644 --- a/libc/stdio/vfwprintf.cpp +++ b/libc/stdio/vfwprintf.cpp @@ -510,6 +510,34 @@ int FUNCTION_NAME(FILE* fp, const CHAR_TYPE* fmt0, va_list ap) { _umax = UARG(); base = DEC; goto nosign; + case 'w': + n = 0; + ch = *fmt++; + while (is_digit(ch)) { + APPEND_DIGIT(n, ch); + ch = *fmt++; + } + switch (n) { + case 8: { + flags |= CHARINT; + goto reswitch; + } + case 16: { + flags |= SHORTINT; + goto reswitch; + } + case 32: { + goto reswitch; + } + case 64: { + flags |= LLONGINT; + goto reswitch; + } + default: { + __fortify_fatal("%%w%d is unsupported", n); + break; + } + } case 'X': xdigs = xdigs_upper; goto hex; diff --git a/tests/stdio_test.cpp b/tests/stdio_test.cpp index 1275b45e6..0e267c516 100644 --- a/tests/stdio_test.cpp +++ b/tests/stdio_test.cpp @@ -3275,3 +3275,144 @@ TEST(STDIO_TEST, swscanf_b) { EXPECT_EQ(0, i); EXPECT_EQ('b', ch); } + +TEST(STDIO_TEST, snprintf_w_base) { +#if defined(__BIONIC__) +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wformat-invalid-specifier" +#pragma clang diagnostic ignored "-Wconstant-conversion" + char buf[BUFSIZ]; + int8_t a = 0b101; + snprintf(buf, sizeof(buf), "<%w8b>", a); + EXPECT_STREQ("<101>", buf); + int8_t b1 = 0xFF; + snprintf(buf, sizeof(buf), "<%w8d>", b1); + EXPECT_STREQ("<-1>", buf); + int8_t b2 = 0x1FF; + snprintf(buf, sizeof(buf), "<%w8d>", b2); + EXPECT_STREQ("<-1>", buf); + int16_t c = 0xFFFF; + snprintf(buf, sizeof(buf), "<%w16i>", c); + EXPECT_STREQ("<-1>", buf); + int32_t d = 021; + snprintf(buf, sizeof(buf), "<%w32o>", d); + EXPECT_STREQ("<21>", buf); + uint32_t e = -1; + snprintf(buf, sizeof(buf), "<%w32u>", e); + EXPECT_STREQ("<4294967295>", buf); + int64_t f = 0x3b; + snprintf(buf, sizeof(buf), "<%w64x>", f); + EXPECT_STREQ("<3b>", buf); + snprintf(buf, sizeof(buf), "<%w64X>", f); + EXPECT_STREQ("<3B>", buf); +#pragma clang diagnostic pop +#else + GTEST_SKIP() << "no %w in glibc"; +#endif +} + +TEST(STDIO_TEST, snprintf_w_arguments_reordering) { +#if defined(__BIONIC__) +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wformat-invalid-specifier" +#pragma clang diagnostic ignored "-Wformat-extra-args" + char buf[BUFSIZ]; + int32_t a = 0xaaaaaaaa; + int64_t b = 0x11111111'22222222; + int64_t c = 0x33333333'44444444; + int64_t d = 0xaaaaaaaa'aaaaaaaa; + snprintf(buf, sizeof(buf), "<%2$w32b --- %1$w64x>", c, a); + EXPECT_STREQ("<10101010101010101010101010101010 --- 3333333344444444>", buf); + snprintf(buf, sizeof(buf), "<%3$w64b --- %1$w64x --- %2$w64x>", b, c, d); + EXPECT_STREQ( + "<1010101010101010101010101010101010101010101010101010101010101010 --- 1111111122222222 --- " + "3333333344444444>", + buf); +#pragma clang diagnostic pop +#else + GTEST_SKIP() << "no %w in glibc"; +#endif +} + +TEST(STDIO_TEST, snprintf_invalid_w_width) { +#if defined(__BIONIC__) +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wformat-invalid-specifier" + char buf[BUFSIZ]; + int32_t a = 100; + EXPECT_DEATH(snprintf(buf, sizeof(buf), "%w20d", &a), "%w20 is unsupported"); +#pragma clang diagnostic pop +#else + GTEST_SKIP() << "no %w in glibc"; +#endif +} + +TEST(STDIO_TEST, swprintf_w_base) { +#if defined(__BIONIC__) +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wformat-invalid-specifier" +#pragma clang diagnostic ignored "-Wconstant-conversion" + wchar_t buf[BUFSIZ]; + int8_t a = 0b101; + swprintf(buf, sizeof(buf), L"<%w8b>", a); + EXPECT_EQ(std::wstring(L"<101>"), buf); + int8_t b1 = 0xFF; + swprintf(buf, sizeof(buf), L"<%w8d>", b1); + EXPECT_EQ(std::wstring(L"<-1>"), buf); + int8_t b2 = 0x1FF; + swprintf(buf, sizeof(buf), L"<%w8d>", b2); + EXPECT_EQ(std::wstring(L"<-1>"), buf); + int16_t c = 0xFFFF; + swprintf(buf, sizeof(buf), L"<%w16i>", c); + EXPECT_EQ(std::wstring(L"<-1>"), buf); + int32_t d = 021; + swprintf(buf, sizeof(buf), L"<%w32o>", d); + EXPECT_EQ(std::wstring(L"<21>"), buf); + uint32_t e = -1; + swprintf(buf, sizeof(buf), L"<%w32u>", e); + EXPECT_EQ(std::wstring(L"<4294967295>"), buf); + int64_t f = 0x3b; + swprintf(buf, sizeof(buf), L"<%w64x>", f); + EXPECT_EQ(std::wstring(L"<3b>"), buf); + swprintf(buf, sizeof(buf), L"<%w64X>", f); + EXPECT_EQ(std::wstring(L"<3B>"), buf); +#pragma clang diagnostic pop +#else + GTEST_SKIP() << "no %w in glibc"; +#endif +} + +TEST(STDIO_TEST, swprintf_w_arguments_reordering) { +#if defined(__BIONIC__) +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wformat-invalid-specifier" +#pragma clang diagnostic ignored "-Wformat-extra-args" + wchar_t buf[BUFSIZ]; + int32_t a = 0xaaaaaaaa; + int64_t b = 0x11111111'22222222; + int64_t c = 0x33333333'44444444; + int64_t d = 0xaaaaaaaa'aaaaaaaa; + swprintf(buf, sizeof(buf), L"<%2$w32b --- %1$w64x>", c, a); + EXPECT_EQ(std::wstring(L"<10101010101010101010101010101010 --- 3333333344444444>"), buf); + swprintf(buf, sizeof(buf), L"<%3$w64b --- %1$w64x --- %2$w64x>", b, c, d); + EXPECT_EQ(std::wstring(L"<1010101010101010101010101010101010101010101010101010101010101010 --- " + L"1111111122222222 --- 3333333344444444>"), + buf); +#pragma clang diagnostic pop +#else + GTEST_SKIP() << "no %w in glibc"; +#endif +} + +TEST(STDIO_TEST, swprintf_invalid_w_width) { +#if defined(__BIONIC__) +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wformat-invalid-specifier" + wchar_t buf[BUFSIZ]; + int32_t a = 100; + EXPECT_DEATH(swprintf(buf, sizeof(buf), L"%w20d", &a), "%w20 is unsupported"); +#pragma clang diagnostic pop +#else + GTEST_SKIP() << "no %w in glibc"; +#endif +} \ No newline at end of file