From 792b1c84c0f2810ada230eff3a857ce7ce0ce6a4 Mon Sep 17 00:00:00 2001 From: Gabriel Ravier Date: Sat, 25 Mar 2023 19:39:25 +0100 Subject: [PATCH] Fix padding+minus flag on numbers for printf-family functions (#787) The C standard states, for conversions using the d, i, b, B, o, u, x or X conversion specifiers: > The precision specifies the minimum number of digits to appear; if > the value being converted can be represented in fewer digits, it is > expanded with leading zeros. - C standard, 7.23.6.1. The fprintf function However, cosmopolitan currently suppresses the addition of leading zeros when the minus flag is set. This is not reflected by anything within the C standard, meaning that behavior is incorrect. This patch fixes this. --- libc/fmt/ntoa.c | 24 +++++++++++------------- test/libc/fmt/fmt_test.c | 19 +++++++++++++++++++ 2 files changed, 30 insertions(+), 13 deletions(-) diff --git a/libc/fmt/ntoa.c b/libc/fmt/ntoa.c index 8bf081768..8b1878df0 100644 --- a/libc/fmt/ntoa.c +++ b/libc/fmt/ntoa.c @@ -34,17 +34,15 @@ static int __fmt_ntoa_format(int out(const char *, void *, size_t), void *arg, unsigned char flags) { unsigned i; /* pad leading zeros */ - if (!(flags & FLAGS_LEFT)) { - if (width && (flags & FLAGS_ZEROPAD) && - (negative || (flags & (FLAGS_PLUS | FLAGS_SPACE)))) { - width--; - } - while ((len < prec) && (len < BUFFER_SIZE)) { - buf[len++] = '0'; - } - while ((flags & FLAGS_ZEROPAD) && (len < width) && (len < BUFFER_SIZE)) { - buf[len++] = '0'; - } + if (width && (flags & FLAGS_ZEROPAD) && + (negative || (flags & (FLAGS_PLUS | FLAGS_SPACE)))) { + width--; + } + while ((len < prec) && (len < BUFFER_SIZE)) { + buf[len++] = '0'; + } + while ((flags & FLAGS_ZEROPAD) && (len < width) && (len < BUFFER_SIZE)) { + buf[len++] = '0'; } /* handle hash */ if (flags & FLAGS_HASH) { @@ -134,8 +132,8 @@ int __fmt_ntoa(int out(const char *, void *, size_t), void *arg, va_list va, bool neg; uint128_t value, sign; - /* ignore '0' flag when prec is given */ - if (flags & FLAGS_PRECISION) { + /* ignore '0' flag when prec or minus flag is given */ + if (flags & (FLAGS_PRECISION | FLAGS_LEFT)) { flags &= ~FLAGS_ZEROPAD; } diff --git a/test/libc/fmt/fmt_test.c b/test/libc/fmt/fmt_test.c index caef6f655..bccd22e9a 100644 --- a/test/libc/fmt/fmt_test.c +++ b/test/libc/fmt/fmt_test.c @@ -62,6 +62,25 @@ TEST(fmt, d) { EXPECT_STREQ(" 16", _gc(xasprintf("% d", 16))); EXPECT_STREQ(" 2147483647", _gc(xasprintf("% d", INT_MAX))); EXPECT_STREQ("-2147483648", _gc(xasprintf("% d", INT_MIN))); + EXPECT_STREQ("042 ", _gc(xasprintf("%-4.3d", 42))); + EXPECT_STREQ("-00054", _gc(xasprintf("%-1.5lld", -54ll))); + EXPECT_STREQ("00109", _gc(xasprintf("%-.5lld", 109ll))); + EXPECT_STREQ("-00116", _gc(xasprintf("%-.5lld", -116ll))); + EXPECT_STREQ("00108 ", _gc(xasprintf("%-8.5lld", 108ll))); + EXPECT_STREQ("-00054 ", _gc(xasprintf("%-8.5lld", -54ll))); +} + +TEST(fmt, u) { + EXPECT_STREQ("042 ", _gc(xasprintf("%-4.3u", 42))); +} + +TEST(fmt, x) { + EXPECT_STREQ("0x01 ", _gc(xasprintf("%#-07.2x", 1))); + EXPECT_STREQ("0x00136d ", _gc(xasprintf("%#-010.6x", 4973))); +} + +TEST(fmt, b) { + EXPECT_STREQ("000010100 ", _gc(xasprintf("%-14.9b", 20))); } TEST(fmt, s) {