Add %b/%B to async-safe logging.

Missed this when we added the new C23 binary output to the printf()
family.

Also fix %X to use "0X" instead of "0x" and add missing tests.

Test: used by new LD_SHOW_AUXV functionality
Change-Id: I6623b92acec7daf534536697de0f87352a2653ad
diff --git a/libc/async_safe/async_safe_log.cpp b/libc/async_safe/async_safe_log.cpp
index 420560f..2bff616 100644
--- a/libc/async_safe/async_safe_log.cpp
+++ b/libc/async_safe/async_safe_log.cpp
@@ -207,10 +207,12 @@
   // Decode the conversion specifier.
   int is_signed = (conversion == 'd' || conversion == 'i' || conversion == 'o');
   int base = 10;
-  if (conversion == 'x' || conversion == 'X') {
+  if (tolower(conversion) == 'x') {
     base = 16;
   } else if (conversion == 'o') {
     base = 8;
+  } else if (tolower(conversion) == 'b') {
+    base = 2;
   }
   bool caps = (conversion == 'X');
 
@@ -360,7 +362,8 @@
       format_integer(buffer + 2, sizeof(buffer) - 2, value, 'x');
     } else if (c == 'm') {
       strerror_r(errno, buffer, sizeof(buffer));
-    } else if (c == 'd' || c == 'i' || c == 'o' || c == 'u' || c == 'x' || c == 'X') {
+    } else if (tolower(c) == 'b' || c == 'd' || c == 'i' || c == 'o' || c == 'u' ||
+               tolower(c) == 'x') {
       /* integers - first read value from stack */
       uint64_t value;
       int is_signed = (c == 'd' || c == 'i' || c == 'o');
@@ -391,10 +394,10 @@
         value = static_cast<uint64_t>((static_cast<int64_t>(value << shift)) >> shift);
       }
 
-      if (alternate && value != 0 && (c == 'x' || c == 'o')) {
-        if (c == 'x') {
+      if (alternate && value != 0 && (tolower(c) == 'x' || c == 'o' || tolower(c) == 'b')) {
+        if (tolower(c) == 'x' || tolower(c) == 'b') {
           buffer[0] = '0';
-          buffer[1] = 'x';
+          buffer[1] = c;
           format_integer(buffer + 2, sizeof(buffer) - 2, value, c);
         } else {
           buffer[0] = '0';
diff --git a/tests/async_safe_test.cpp b/tests/async_safe_test.cpp
index dc4db07..cc1b598 100644
--- a/tests/async_safe_test.cpp
+++ b/tests/async_safe_test.cpp
@@ -45,6 +45,15 @@
   async_safe_format_buffer(buf, sizeof(buf), "aa%scc", "bb");
   EXPECT_STREQ("aabbcc", buf);
 
+  async_safe_format_buffer(buf, sizeof(buf), "a%bb", 1234);
+  EXPECT_STREQ("a10011010010b", buf);
+
+  async_safe_format_buffer(buf, sizeof(buf), "a%#bb", 1234);
+  EXPECT_STREQ("a0b10011010010b", buf);
+
+  async_safe_format_buffer(buf, sizeof(buf), "a%#Bb", 1234);
+  EXPECT_STREQ("a0B10011010010b", buf);
+
   async_safe_format_buffer(buf, sizeof(buf), "a%cc", 'b');
   EXPECT_STREQ("abc", buf);
 
@@ -76,9 +85,15 @@
   async_safe_format_buffer(buf, sizeof(buf), "a%xz", 0x12ab);
   EXPECT_STREQ("a12abz", buf);
 
+  async_safe_format_buffer(buf, sizeof(buf), "a%#xz", 0x12ab);
+  EXPECT_STREQ("a0x12abz", buf);
+
   async_safe_format_buffer(buf, sizeof(buf), "a%Xz", 0x12ab);
   EXPECT_STREQ("a12ABz", buf);
 
+  async_safe_format_buffer(buf, sizeof(buf), "a%#Xz", 0x12ab);
+  EXPECT_STREQ("a0X12ABz", buf);
+
   async_safe_format_buffer(buf, sizeof(buf), "a%08xz", 0x123456);
   EXPECT_STREQ("a00123456z", buf);