Add %b and %B support to the printf/wprintf family.
Coming to C23 via WG14 N2630, and already in glibc. We're still missing clang support for %b and %B in format string checking, but it's probably easier to fix this first. (Apparently GCC already has support because of glibc.) Test: treehugger Change-Id: Ie8bfe4630d00c50e1d047d6756a7f799205356db
This commit is contained in:
parent
27b3ad2c78
commit
b813a6a6be
5 changed files with 115 additions and 9 deletions
|
@ -51,6 +51,12 @@ list of POSIX functions implemented by glibc but not by bionic.
|
|||
|
||||
Current libc symbols: https://android.googlesource.com/platform/bionic/+/master/libc/libc.map.txt
|
||||
|
||||
New libc functions in U (API level 34):
|
||||
* `close_range` and `copy_file_range` (Linux-specific GNU extensions).
|
||||
|
||||
New libc behavior in U (API level 34):
|
||||
* Support for `%b` and `%B` in the printf and wprintf family.
|
||||
|
||||
New libc functions in T (API level 33):
|
||||
* `backtrace`, `backtrace_symbols`, `backtrace_symbols_fd` (`<execinfo.h>`).
|
||||
* New system call wrappers: `preadv2`, `preadv64v2`, `pwritev2`,
|
||||
|
|
|
@ -524,6 +524,8 @@ static int __find_arguments(const CHAR_TYPE* fmt0, va_list ap, union arg** argta
|
|||
case 'u':
|
||||
case 'X':
|
||||
case 'x':
|
||||
case 'B':
|
||||
case 'b':
|
||||
ADDUARG();
|
||||
break;
|
||||
default: /* "%?" prints ?, unless ? is NUL */
|
||||
|
|
|
@ -81,8 +81,8 @@ int FUNCTION_NAME(FILE* fp, const CHAR_TYPE* fmt0, va_list ap) {
|
|||
char* dtoaresult = nullptr;
|
||||
|
||||
uintmax_t _umax; /* integer arguments %[diouxX] */
|
||||
enum { OCT, DEC, HEX } base; /* base for %[diouxX] conversion */
|
||||
int dprec; /* a copy of prec if %[diouxX], 0 otherwise */
|
||||
enum { BIN, OCT, DEC, HEX } base; /* base for %[bBdiouxX] conversion */
|
||||
int dprec; /* a copy of prec if %[bBdiouxX], 0 otherwise */
|
||||
int realsz; /* field size expanded by dprec */
|
||||
int size; /* size of converted field or string */
|
||||
const char* xdigs; /* digits for %[xX] conversion */
|
||||
|
@ -304,6 +304,12 @@ int FUNCTION_NAME(FILE* fp, const CHAR_TYPE* fmt0, va_list ap) {
|
|||
case 'z':
|
||||
flags |= SIZEINT;
|
||||
goto rflag;
|
||||
case 'B':
|
||||
case 'b':
|
||||
_umax = UARG();
|
||||
base = BIN;
|
||||
if (flags & ALT && _umax != 0) ox[1] = ch;
|
||||
goto nosign;
|
||||
case 'C':
|
||||
flags |= LONGINT;
|
||||
__BIONIC_FALLTHROUGH;
|
||||
|
@ -550,6 +556,13 @@ int FUNCTION_NAME(FILE* fp, const CHAR_TYPE* fmt0, va_list ap) {
|
|||
* a variable; hence this switch.
|
||||
*/
|
||||
switch (base) {
|
||||
case BIN:
|
||||
do {
|
||||
*--cp = to_char(_umax & 1);
|
||||
_umax >>= 1;
|
||||
} while (_umax);
|
||||
break;
|
||||
|
||||
case OCT:
|
||||
do {
|
||||
*--cp = to_char(_umax & 7);
|
||||
|
@ -599,7 +612,7 @@ int FUNCTION_NAME(FILE* fp, const CHAR_TYPE* fmt0, va_list ap) {
|
|||
* first be prefixed by any sign or other prefix; otherwise,
|
||||
* it should be blank padded before the prefix is emitted.
|
||||
* After any left-hand padding and prefixing, emit zeroes
|
||||
* required by a decimal %[diouxX] precision, then print the
|
||||
* required by a decimal %[bBdiouxX] precision, then print the
|
||||
* string proper, then emit zeroes required by any leftover
|
||||
* floating precision; finally, if LADJUST, pad with blanks.
|
||||
*
|
||||
|
|
|
@ -81,8 +81,8 @@ int FUNCTION_NAME(FILE* fp, const CHAR_TYPE* fmt0, va_list ap) {
|
|||
char* dtoaresult = nullptr;
|
||||
|
||||
uintmax_t _umax; /* integer arguments %[diouxX] */
|
||||
enum { OCT, DEC, HEX } base; /* base for %[diouxX] conversion */
|
||||
int dprec; /* a copy of prec if %[diouxX], 0 otherwise */
|
||||
enum { BIN, OCT, DEC, HEX } base; /* base for %[bBdiouxX] conversion */
|
||||
int dprec; /* a copy of prec if %[bBdiouxX], 0 otherwise */
|
||||
int realsz; /* field size expanded by dprec */
|
||||
int size; /* size of converted field or string */
|
||||
const char* xdigs; /* digits for %[xX] conversion */
|
||||
|
@ -293,6 +293,12 @@ int FUNCTION_NAME(FILE* fp, const CHAR_TYPE* fmt0, va_list ap) {
|
|||
case 'z':
|
||||
flags |= SIZEINT;
|
||||
goto rflag;
|
||||
case 'B':
|
||||
case 'b':
|
||||
_umax = UARG();
|
||||
base = BIN;
|
||||
if (flags & ALT && _umax != 0) ox[1] = ch;
|
||||
goto nosign;
|
||||
case 'C':
|
||||
flags |= LONGINT;
|
||||
__BIONIC_FALLTHROUGH;
|
||||
|
@ -539,6 +545,13 @@ int FUNCTION_NAME(FILE* fp, const CHAR_TYPE* fmt0, va_list ap) {
|
|||
* a variable; hence this switch.
|
||||
*/
|
||||
switch (base) {
|
||||
case BIN:
|
||||
do {
|
||||
*--cp = to_char(_umax & 1);
|
||||
_umax >>= 1;
|
||||
} while (_umax);
|
||||
break;
|
||||
|
||||
case OCT:
|
||||
do {
|
||||
*--cp = to_char(_umax & 7);
|
||||
|
@ -588,7 +601,7 @@ int FUNCTION_NAME(FILE* fp, const CHAR_TYPE* fmt0, va_list ap) {
|
|||
* first be prefixed by any sign or other prefix; otherwise,
|
||||
* it should be blank padded before the prefix is emitted.
|
||||
* After any left-hand padding and prefixing, emit zeroes
|
||||
* required by a decimal %[diouxX] precision, then print the
|
||||
* required by a decimal %[bBdiouxX] precision, then print the
|
||||
* string proper, then emit zeroes required by any leftover
|
||||
* floating precision; finally, if LADJUST, pad with blanks.
|
||||
*
|
||||
|
|
|
@ -365,13 +365,13 @@ TEST(STDIO_TEST, snprintf_S) { // Synonym for %ls.
|
|||
|
||||
TEST_F(STDIO_DEATHTEST, snprintf_n) {
|
||||
#if defined(__BIONIC__)
|
||||
#pragma GCC diagnostic push
|
||||
#pragma GCC diagnostic ignored "-Wformat"
|
||||
#pragma clang diagnostic push
|
||||
#pragma clang diagnostic ignored "-Wformat"
|
||||
// http://b/14492135 and http://b/31832608.
|
||||
char buf[32];
|
||||
int i = 1234;
|
||||
EXPECT_DEATH(snprintf(buf, sizeof(buf), "a %n b", &i), "%n not allowed on Android");
|
||||
#pragma GCC diagnostic pop
|
||||
#pragma clang diagnostic pop
|
||||
#else
|
||||
GTEST_SKIP() << "glibc does allow %n";
|
||||
#endif
|
||||
|
@ -2963,3 +2963,75 @@ TEST(STDIO_TEST, fwrite_int_overflow) {
|
|||
std::unique_ptr<FILE, decltype(&fclose)> fp{fopen("/dev/null", "we"), fclose};
|
||||
ASSERT_EQ(too_big_for_an_int, fwrite(&buf[0], 1, too_big_for_an_int, fp.get()));
|
||||
}
|
||||
|
||||
TEST(STDIO_TEST, snprintf_b) {
|
||||
// Our clang doesn't know about %b/%B yet.
|
||||
#pragma clang diagnostic push
|
||||
#pragma clang diagnostic ignored "-Wformat-invalid-specifier"
|
||||
char buf[BUFSIZ];
|
||||
EXPECT_EQ(5, snprintf(buf, sizeof(buf), "<%b>", 5));
|
||||
EXPECT_STREQ("<101>", buf);
|
||||
EXPECT_EQ(10, snprintf(buf, sizeof(buf), "<%08b>", 5));
|
||||
EXPECT_STREQ("<00000101>", buf);
|
||||
EXPECT_EQ(34, snprintf(buf, sizeof(buf), "<%b>", 0xaaaaaaaa));
|
||||
EXPECT_STREQ("<10101010101010101010101010101010>", buf);
|
||||
EXPECT_EQ(36, snprintf(buf, sizeof(buf), "<%#b>", 0xaaaaaaaa));
|
||||
EXPECT_STREQ("<0b10101010101010101010101010101010>", buf);
|
||||
EXPECT_EQ(3, snprintf(buf, sizeof(buf), "<%#b>", 0));
|
||||
EXPECT_STREQ("<0>", buf);
|
||||
#pragma clang diagnostic pop
|
||||
}
|
||||
|
||||
TEST(STDIO_TEST, snprintf_B) {
|
||||
// Our clang doesn't know about %b/%B yet.
|
||||
#pragma clang diagnostic push
|
||||
#pragma clang diagnostic ignored "-Wformat-invalid-specifier"
|
||||
char buf[BUFSIZ];
|
||||
EXPECT_EQ(5, snprintf(buf, sizeof(buf), "<%B>", 5));
|
||||
EXPECT_STREQ("<101>", buf);
|
||||
EXPECT_EQ(10, snprintf(buf, sizeof(buf), "<%08B>", 5));
|
||||
EXPECT_STREQ("<00000101>", buf);
|
||||
EXPECT_EQ(34, snprintf(buf, sizeof(buf), "<%B>", 0xaaaaaaaa));
|
||||
EXPECT_STREQ("<10101010101010101010101010101010>", buf);
|
||||
EXPECT_EQ(36, snprintf(buf, sizeof(buf), "<%#B>", 0xaaaaaaaa));
|
||||
EXPECT_STREQ("<0B10101010101010101010101010101010>", buf);
|
||||
EXPECT_EQ(3, snprintf(buf, sizeof(buf), "<%#B>", 0));
|
||||
EXPECT_STREQ("<0>", buf);
|
||||
#pragma clang diagnostic pop
|
||||
}
|
||||
|
||||
TEST(STDIO_TEST, swprintf_b) {
|
||||
// Our clang doesn't know about %b/%B yet.
|
||||
#pragma clang diagnostic push
|
||||
#pragma clang diagnostic ignored "-Wformat-invalid-specifier"
|
||||
wchar_t buf[BUFSIZ];
|
||||
EXPECT_EQ(5, swprintf(buf, sizeof(buf), L"<%b>", 5));
|
||||
EXPECT_EQ(std::wstring(L"<101>"), buf);
|
||||
EXPECT_EQ(10, swprintf(buf, sizeof(buf), L"<%08b>", 5));
|
||||
EXPECT_EQ(std::wstring(L"<00000101>"), buf);
|
||||
EXPECT_EQ(34, swprintf(buf, sizeof(buf), L"<%b>", 0xaaaaaaaa));
|
||||
EXPECT_EQ(std::wstring(L"<10101010101010101010101010101010>"), buf);
|
||||
EXPECT_EQ(36, swprintf(buf, sizeof(buf), L"<%#b>", 0xaaaaaaaa));
|
||||
EXPECT_EQ(std::wstring(L"<0b10101010101010101010101010101010>"), buf);
|
||||
EXPECT_EQ(3, swprintf(buf, sizeof(buf), L"<%#b>", 0));
|
||||
EXPECT_EQ(std::wstring(L"<0>"), buf);
|
||||
#pragma clang diagnostic pop
|
||||
}
|
||||
|
||||
TEST(STDIO_TEST, swprintf_B) {
|
||||
// Our clang doesn't know about %b/%B yet.
|
||||
#pragma clang diagnostic push
|
||||
#pragma clang diagnostic ignored "-Wformat-invalid-specifier"
|
||||
wchar_t buf[BUFSIZ];
|
||||
EXPECT_EQ(5, swprintf(buf, sizeof(buf), L"<%B>", 5));
|
||||
EXPECT_EQ(std::wstring(L"<101>"), buf);
|
||||
EXPECT_EQ(10, swprintf(buf, sizeof(buf), L"<%08B>", 5));
|
||||
EXPECT_EQ(std::wstring(L"<00000101>"), buf);
|
||||
EXPECT_EQ(34, swprintf(buf, sizeof(buf), L"<%B>", 0xaaaaaaaa));
|
||||
EXPECT_EQ(std::wstring(L"<10101010101010101010101010101010>"), buf);
|
||||
EXPECT_EQ(36, swprintf(buf, sizeof(buf), L"<%#B>", 0xaaaaaaaa));
|
||||
EXPECT_EQ(std::wstring(L"<0B10101010101010101010101010101010>"), buf);
|
||||
EXPECT_EQ(3, swprintf(buf, sizeof(buf), L"<%#B>", 0));
|
||||
EXPECT_EQ(std::wstring(L"<0>"), buf);
|
||||
#pragma clang diagnostic pop
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue