diff --git a/options/ansi/generic/stdio-stubs.cpp b/options/ansi/generic/stdio-stubs.cpp index 2c21ba0a04..cf00db831a 100644 --- a/options/ansi/generic/stdio-stubs.cpp +++ b/options/ansi/generic/stdio-stubs.cpp @@ -54,7 +54,7 @@ struct PrintfAgent { case 'p': case 's': frg::do_printf_chars(*_formatter, t, opts, szmod, _vsp); break; - case 'd': case 'i': case 'o': case 'x': case 'X': case 'u': + case 'd': case 'i': case 'o': case 'x': case 'X': case 'b': case 'B': case 'u': frg::do_printf_ints(*_formatter, t, opts, szmod, _vsp); break; case 'f': case 'F': case 'g': case 'G': case 'e': case 'E': @@ -596,6 +596,34 @@ static int do_scanf(H &handler, const char *fmt, __builtin_va_list args) { store_int(dest, type, res); break; } + case 'b': { + unsigned long long res = 0; + char c = handler.look_ahead(); + int count = 0; + EOF_CHECK(c == '\0'); + if (c == '0') { + handler.consume(); + c = handler.look_ahead(); + if (c == 'b' || c == 'B') { + handler.consume(); + c = handler.look_ahead(); + } + } + while (true) { + if (c == '0' || c == '1') { + handler.consume(); + res = res * 2 + (c - '0'); + } else { + break; + } + count++; + c = handler.look_ahead(); + } + NOMATCH_CHECK(count == 0); + if (dest) + store_int(dest, type, res); + break; + } case 's': { char c = handler.look_ahead(); EOF_CHECK(c == '\0'); diff --git a/options/internal/include/mlibc/strtol.hpp b/options/internal/include/mlibc/strtol.hpp index 3b8fca9469..2dab276715 100644 --- a/options/internal/include/mlibc/strtol.hpp +++ b/options/internal/include/mlibc/strtol.hpp @@ -83,6 +83,7 @@ Return stringToInteger(const Char *__restrict nptr, Char **__restrict endptr, in bool hasOctalPrefix = s[0] == widen('0'); bool hasHexPrefix = hasOctalPrefix && (s[1] == widen('x') || s[1] == widen('X')); + bool hasBinPrefix = hasOctalPrefix && (s[1] == widen('b') || s[1] == widen('B')); // There's two tricky cases we need to keep in mind here: // 1. We should interpret "0x5" as hex 5 rather than octal 0. @@ -91,6 +92,9 @@ Return stringToInteger(const Char *__restrict nptr, Char **__restrict endptr, in if ((base == 0 || base == 16) && hasHexPrefix && char_detail::isHexDigit(s[2])) { s += 2; base = 16; + } else if ((base == 0 || base == 2) && hasBinPrefix) { + s += 2; + base = 2; } else if ((base == 0 || base == 8) && hasOctalPrefix) { base = 8; } else if (base == 0) { diff --git a/tests/ansi/sprintf.c b/tests/ansi/sprintf.c index 8ef8f09b6e..e239bda962 100644 --- a/tests/ansi/sprintf.c +++ b/tests/ansi/sprintf.c @@ -110,6 +110,7 @@ int main() { assert(!strcmp(buf, "0XC")); sprintf(buf, "%#o", 12); assert(!strcmp(buf, "014")); + sprintf(buf, "%#x", 0); assert(!strcmp(buf, "0")); sprintf(buf, "%#X", 0); @@ -117,6 +118,19 @@ int main() { sprintf(buf, "%#o", 0); assert(!strcmp(buf, "0")); + // Disable -Wformat here because the compiler might not know about the b specifier. +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Wformat" + sprintf(buf, "%#b", 12); + assert(!strcmp(buf, "0b1100")); + sprintf(buf, "%#B", 12); + assert(!strcmp(buf, "0B1100")); + sprintf(buf, "%#b", 0); + assert(!strcmp(buf, "0")); + sprintf(buf, "%#B", 0); + assert(!strcmp(buf, "0")); +#pragma GCC diagnostic pop + // Test 'd' with different size mods to see // if they work sprintf(buf, "%d", 12); @@ -177,6 +191,25 @@ int main() { sprintf(buf, "%hho", (unsigned char) 12); assert(!strcmp(buf, "14")); + // Disable -Wformat here because the compiler might not know about the b specifier. +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Wformat" + // Test 'b' with different size mods to see + // if they work + sprintf(buf, "%b", 12); + assert(!strcmp(buf, "1100")); + sprintf(buf, "%lb", 12L); + assert(!strcmp(buf, "1100")); + sprintf(buf, "%llb", 12LL); + assert(!strcmp(buf, "1100")); + sprintf(buf, "%zb", (size_t)12); + assert(!strcmp(buf, "1100")); + sprintf(buf, "%hb", (unsigned short) 12); + assert(!strcmp(buf, "1100")); + sprintf(buf, "%hhb", (unsigned char) 12); + assert(!strcmp(buf, "1100")); +#pragma GCC diagnostic pop + // Test %n$ syntax. sprintf(buf, "%1$d", 12); assert(!strcmp(buf, "12")); diff --git a/tests/ansi/sscanf.c b/tests/ansi/sscanf.c index 802c39d6a3..da9e8c0197 100644 --- a/tests/ansi/sscanf.c +++ b/tests/ansi/sscanf.c @@ -24,6 +24,10 @@ struct format_test_cases { {"%u", "0420", 420, T_UINT, 1}, {"%o", "0420", 0420, T_UINT, 1}, {"%x", "0xCB7", 0xCB7, T_UINT, 1}, +#ifndef USE_HOST_LIBC + {"%b", "0b1011", 0b1011, T_UINT, 1}, + {"%b", "0B1011", 0b1011, T_UINT, 1}, +#endif {"%%", "%", 0, T_NONE, 0}, {"%c", " I am not a fan of this solution.", ' ', T_CHAR, 1}, {" %c", " CBT (capybara therapy)", 'C', T_CHAR, 1}, diff --git a/tests/ansi/strtol.c b/tests/ansi/strtol.c index 823cb4a09f..255f1a036a 100644 --- a/tests/ansi/strtol.c +++ b/tests/ansi/strtol.c @@ -48,6 +48,12 @@ int main () { DO_TEST("-1101110100110100100000", -3624224, -1, strtol, 2); DO_TEST("0x6fffff", 0x6fffff, -1, strtol, 0); DO_TEST("0666", 0666, -1, strtol, 0); +#ifndef USE_HOST_LIBC + DO_TEST("1100", 0b1100, -1, strtol, 2); + DO_TEST("0b1100", 0b1100, -1, strtol, 0); + DO_TEST("0B1100", 0b1100, -1, strtol, 0); + DO_TEST("0b1zzz", 1, 3, strtol, 0); +#endif DO_TEST("0xzzz", 0, 1, strtol, 0); DO_TEST("0yzzz", 0, 1, strtol, 0); DO_TEST("00xzz", 0, 2, strtol, 0);