summary refs log tree commit diff
path: root/stdio-common
diff options
context:
space:
mode:
Diffstat (limited to 'stdio-common')
-rw-r--r--stdio-common/Makefile3
-rw-r--r--stdio-common/printf-parsemb.c2
-rw-r--r--stdio-common/tst-printf-binary-main.c130
-rw-r--r--stdio-common/tst-printf-binary.c25
-rw-r--r--stdio-common/tst-printf.c2
-rw-r--r--stdio-common/tst-printf.sh4
-rw-r--r--stdio-common/vfprintf-internal.c48
7 files changed, 193 insertions, 21 deletions
diff --git a/stdio-common/Makefile b/stdio-common/Makefile
index 803f16dae0..bbb3a2ca56 100644
--- a/stdio-common/Makefile
+++ b/stdio-common/Makefile
@@ -70,7 +70,8 @@ tests := tstscanf test_rdwr test-popen tstgetln test-fseek \
 	 tst-vfprintf-width-prec-alloc \
 	 tst-printf-fp-free \
 	 tst-printf-fp-leak \
-	 test-strerr
+	 test-strerr \
+	 tst-printf-binary
 
 
 test-srcs = tst-unbputc tst-printf tst-printfsz-islongdouble
diff --git a/stdio-common/printf-parsemb.c b/stdio-common/printf-parsemb.c
index 6e64a6bb98..8bc5567758 100644
--- a/stdio-common/printf-parsemb.c
+++ b/stdio-common/printf-parsemb.c
@@ -328,6 +328,8 @@ __parse_one_specmb (const UCHAR_T *format, size_t posn,
 	case L'o':
 	case L'X':
 	case L'x':
+	case L'B':
+	case L'b':
 #if LONG_MAX != LONG_LONG_MAX
 	  if (spec->info.is_long_double)
 	    spec->data_arg_type = PA_INT|PA_FLAG_LONG_LONG;
diff --git a/stdio-common/tst-printf-binary-main.c b/stdio-common/tst-printf-binary-main.c
new file mode 100644
index 0000000000..02698f65ba
--- /dev/null
+++ b/stdio-common/tst-printf-binary-main.c
@@ -0,0 +1,130 @@
+/* Test binary printf formats.
+   Copyright (C) 2021 Free Software Foundation, Inc.
+   This file is part of the GNU C Library.
+
+   The GNU C Library is free software; you can redistribute it and/or
+   modify it under the terms of the GNU Lesser General Public
+   License as published by the Free Software Foundation; either
+   version 2.1 of the License, or (at your option) any later version.
+
+   The GNU C Library is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+   Lesser General Public License for more details.
+
+   You should have received a copy of the GNU Lesser General Public
+   License along with the GNU C Library; if not, see
+   <https://www.gnu.org/licenses/>.  */
+
+#include <limits.h>
+#include <stdio.h>
+#include <string.h>
+#include <wchar.h>
+
+#include <libc-diag.h>
+#include <support/check.h>
+
+/* GCC does not know the %b or %B formats before GCC 12.  */
+DIAG_PUSH_NEEDS_COMMENT;
+#if !__GNUC_PREREQ (12, 0)
+DIAG_IGNORE_NEEDS_COMMENT (11, "-Wformat");
+DIAG_IGNORE_NEEDS_COMMENT (11, "-Wformat-extra-args");
+#endif
+
+#define CHECK_PRINTF(EXPECTED, FMT, ...)				\
+  do									\
+    {									\
+      int ret = SNPRINTF (buf, sizeof buf / sizeof buf[0], L_(FMT),	\
+			  __VA_ARGS__);					\
+      TEST_COMPARE_STRING_MACRO (buf, L_(EXPECTED));			\
+      TEST_COMPARE (ret, STRLEN (L_(EXPECTED)));			\
+    }									\
+  while (0)
+
+static int
+do_test (void)
+{
+  CHAR buf[1024];
+  CHECK_PRINTF ("0", "%b", 0u);
+  CHECK_PRINTF ("0", "%B", 0u);
+  CHECK_PRINTF ("0", "%#b", 0u);
+  CHECK_PRINTF ("0", "%#B", 0u);
+  CHECK_PRINTF ("1", "%b", 1u);
+  CHECK_PRINTF ("1", "%B", 1u);
+  CHECK_PRINTF ("10", "%b", 2u);
+  CHECK_PRINTF ("10", "%B", 2u);
+  CHECK_PRINTF ("11", "%b", 3u);
+  CHECK_PRINTF ("11", "%B", 3u);
+  CHECK_PRINTF ("10000111011001010100001100100001", "%b", 0x87654321);
+  CHECK_PRINTF ("10000111011001010100001100100001", "%B", 0x87654321);
+  CHECK_PRINTF ("100001100100001", "%hb", (int) 0x87654321);
+  CHECK_PRINTF ("100001100100001", "%hB", (int) 0x87654321);
+  CHECK_PRINTF ("100001", "%hhb", (int) 0x87654321);
+  CHECK_PRINTF ("100001", "%hhB", (int) 0x87654321);
+  CHECK_PRINTF ("10000111011001010100001100100001", "%lb", 0x87654321ul);
+  CHECK_PRINTF ("10000111011001010100001100100001", "%lB", 0x87654321ul);
+  CHECK_PRINTF ("11111110110111001011101010011001"
+		"10000111011001010100001100100001", "%llb",
+		0xfedcba9987654321ull);
+  CHECK_PRINTF ("11111110110111001011101010011001"
+		"10000111011001010100001100100001", "%llB",
+		0xfedcba9987654321ull);
+#if LONG_WIDTH >= 64
+  CHECK_PRINTF ("11111110110111001011101010011001"
+		"10000111011001010100001100100001", "%lb",
+		0xfedcba9987654321ul);
+  CHECK_PRINTF ("11111110110111001011101010011001"
+		"10000111011001010100001100100001", "%lB",
+		0xfedcba9987654321ul);
+#endif
+  CHECK_PRINTF (" 1010", "%5b", 10u);
+  CHECK_PRINTF (" 1010", "%5B", 10u);
+  CHECK_PRINTF ("01010", "%05b", 10u);
+  CHECK_PRINTF ("01010", "%05B", 10u);
+  CHECK_PRINTF ("1011 ", "%-5b", 11u);
+  CHECK_PRINTF ("1011 ", "%-5B", 11u);
+  CHECK_PRINTF ("0b10011", "%#b", 19u);
+  CHECK_PRINTF ("0B10011", "%#B", 19u);
+  CHECK_PRINTF ("   0b10011", "%#10b", 19u);
+  CHECK_PRINTF ("   0B10011", "%#10B", 19u);
+  CHECK_PRINTF ("0b00010011", "%0#10b", 19u);
+  CHECK_PRINTF ("0B00010011", "%0#10B", 19u);
+  CHECK_PRINTF ("0b00010011", "%#010b", 19u);
+  CHECK_PRINTF ("0B00010011", "%#010B", 19u);
+  CHECK_PRINTF ("0b10011   ", "%#-10b", 19u);
+  CHECK_PRINTF ("0B10011   ", "%#-10B", 19u);
+  CHECK_PRINTF ("00010011", "%.8b", 19u);
+  CHECK_PRINTF ("00010011", "%.8B", 19u);
+  CHECK_PRINTF ("0b00010011", "%#.8b", 19u);
+  CHECK_PRINTF ("0B00010011", "%#.8B", 19u);
+  CHECK_PRINTF ("       00010011", "%15.8b", 19u);
+  CHECK_PRINTF ("       00010011", "%15.8B", 19u);
+  CHECK_PRINTF ("00010011       ", "%-15.8b", 19u);
+  CHECK_PRINTF ("00010011       ", "%-15.8B", 19u);
+  CHECK_PRINTF ("     0b00010011", "%#15.8b", 19u);
+  CHECK_PRINTF ("     0B00010011", "%#15.8B", 19u);
+  CHECK_PRINTF ("0b00010011     ", "%-#15.8b", 19u);
+  CHECK_PRINTF ("0B00010011     ", "%-#15.8B", 19u);
+  /* GCC diagnoses ignored flags.  */
+  DIAG_PUSH_NEEDS_COMMENT;
+  DIAG_IGNORE_NEEDS_COMMENT (12, "-Wformat");
+  /* '0' flag ignored with '-'.  */
+  CHECK_PRINTF ("1011 ", "%0-5b", 11u);
+  CHECK_PRINTF ("1011 ", "%0-5B", 11u);
+  CHECK_PRINTF ("0b10011   ", "%#0-10b", 19u);
+  CHECK_PRINTF ("0B10011   ", "%#0-10B", 19u);
+  /* '0' flag ignored with precision.  */
+  CHECK_PRINTF ("       00010011", "%015.8b", 19u);
+  CHECK_PRINTF ("       00010011", "%015.8B", 19u);
+  CHECK_PRINTF ("     0b00010011", "%0#15.8b", 19u);
+  CHECK_PRINTF ("     0B00010011", "%0#15.8B", 19u);
+  DIAG_POP_NEEDS_COMMENT;
+  /* Test positional argument handling.  */
+  CHECK_PRINTF ("test 1011 test2 100010001000100010001000100010001",
+		"%2$s %1$b %4$s %3$llb", 11u, "test", 0x111111111ull, "test2");
+  return 0;
+}
+
+DIAG_POP_NEEDS_COMMENT;
+
+#include <support/test-driver.c>
diff --git a/stdio-common/tst-printf-binary.c b/stdio-common/tst-printf-binary.c
new file mode 100644
index 0000000000..bb044b571d
--- /dev/null
+++ b/stdio-common/tst-printf-binary.c
@@ -0,0 +1,25 @@
+/* Test binary printf formats.  Narrow string version.
+   Copyright (C) 2021 Free Software Foundation, Inc.
+   This file is part of the GNU C Library.
+
+   The GNU C Library is free software; you can redistribute it and/or
+   modify it under the terms of the GNU Lesser General Public
+   License as published by the Free Software Foundation; either
+   version 2.1 of the License, or (at your option) any later version.
+
+   The GNU C Library is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+   Lesser General Public License for more details.
+
+   You should have received a copy of the GNU Lesser General Public
+   License along with the GNU C Library; if not, see
+   <https://www.gnu.org/licenses/>.  */
+
+#define SNPRINTF snprintf
+#define TEST_COMPARE_STRING_MACRO TEST_COMPARE_STRING
+#define STRLEN strlen
+#define CHAR char
+#define L_(C) C
+
+#include <tst-printf-binary-main.c>
diff --git a/stdio-common/tst-printf.c b/stdio-common/tst-printf.c
index b4241330cb..78007abf15 100644
--- a/stdio-common/tst-printf.c
+++ b/stdio-common/tst-printf.c
@@ -91,7 +91,7 @@ I am ready for my first lesson today.";
   fmtst2chk("%0*.*x");
 
 #ifndef	BSD
-  printf("bad format:\t\"%b\"\n");
+  printf("bad format:\t\"%v\"\n");
   printf("nil pointer (padded):\t\"%10p\"\n", (void *) NULL);
 #endif
 
diff --git a/stdio-common/tst-printf.sh b/stdio-common/tst-printf.sh
index c2aee1f1f3..591ab19bfe 100644
--- a/stdio-common/tst-printf.sh
+++ b/stdio-common/tst-printf.sh
@@ -39,7 +39,7 @@ cat <<'EOF' |
 %0*x:	`0012'
 %*.*x:	`0012'
 %0*.*x:	`0012'
-bad format:	"%b"
+bad format:	"%v"
 nil pointer (padded):	"     (nil)"
 decimal negative:	"-2345"
 octal negative:	"37777773327"
@@ -153,7 +153,7 @@ cat <<'EOF' |
 %0*x:	`0012'
 %*.*x:	`0012'
 %0*.*x:	`0012'
-bad format:	"%b"
+bad format:	"%v"
 nil pointer (padded):	"     (nil)"
 decimal negative:	"-2345"
 octal negative:	"37777773327"
diff --git a/stdio-common/vfprintf-internal.c b/stdio-common/vfprintf-internal.c
index 355ba582e6..e717f50073 100644
--- a/stdio-common/vfprintf-internal.c
+++ b/stdio-common/vfprintf-internal.c
@@ -390,7 +390,7 @@ static const uint8_t jump_table[] =
     /* '4' */  8, /* '5' */  8, /* '6' */  8, /* '7' */  8,
     /* '8' */  8, /* '9' */  8,            0,            0,
 	       0,            0,            0,            0,
-	       0, /* 'A' */ 26,            0, /* 'C' */ 25,
+	       0, /* 'A' */ 26, /* 'B' */ 30, /* 'C' */ 25,
 	       0, /* 'E' */ 19, /* F */   19, /* 'G' */ 19,
 	       0, /* 'I' */ 29,            0,            0,
     /* 'L' */ 12,            0,            0,            0,
@@ -398,7 +398,7 @@ static const uint8_t jump_table[] =
 	       0,            0,            0,            0,
     /* 'X' */ 18,            0, /* 'Z' */ 13,            0,
 	       0,            0,            0,            0,
-	       0, /* 'a' */ 26,            0, /* 'c' */ 20,
+	       0, /* 'a' */ 26, /* 'b' */ 30, /* 'c' */ 20,
     /* 'd' */ 15, /* 'e' */ 19, /* 'f' */ 19, /* 'g' */ 19,
     /* 'h' */ 10, /* 'i' */ 15, /* 'j' */ 28,            0,
     /* 'l' */ 11, /* 'm' */ 24, /* 'n' */ 23, /* 'o' */ 17,
@@ -444,7 +444,7 @@ static const uint8_t jump_table[] =
 
 #define STEP0_3_TABLE							      \
     /* Step 0: at the beginning.  */					      \
-    static JUMP_TABLE_TYPE step0_jumps[30] =				      \
+    static JUMP_TABLE_TYPE step0_jumps[31] =				      \
     {									      \
       REF (form_unknown),						      \
       REF (flag_space),		/* for ' ' */				      \
@@ -476,9 +476,10 @@ static const uint8_t jump_table[] =
       REF (mod_ptrdiff_t),      /* for 't' */				      \
       REF (mod_intmax_t),       /* for 'j' */				      \
       REF (flag_i18n),		/* for 'I' */				      \
+      REF (form_binary),	/* for 'B', 'b' */			      \
     };									      \
     /* Step 1: after processing width.  */				      \
-    static JUMP_TABLE_TYPE step1_jumps[30] =				      \
+    static JUMP_TABLE_TYPE step1_jumps[31] =				      \
     {									      \
       REF (form_unknown),						      \
       REF (form_unknown),	/* for ' ' */				      \
@@ -509,10 +510,11 @@ static const uint8_t jump_table[] =
       REF (form_floathex),	/* for 'A', 'a' */			      \
       REF (mod_ptrdiff_t),      /* for 't' */				      \
       REF (mod_intmax_t),       /* for 'j' */				      \
-      REF (form_unknown)        /* for 'I' */				      \
+      REF (form_unknown),       /* for 'I' */				      \
+      REF (form_binary),	/* for 'B', 'b' */			      \
     };									      \
     /* Step 2: after processing precision.  */				      \
-    static JUMP_TABLE_TYPE step2_jumps[30] =				      \
+    static JUMP_TABLE_TYPE step2_jumps[31] =				      \
     {									      \
       REF (form_unknown),						      \
       REF (form_unknown),	/* for ' ' */				      \
@@ -543,10 +545,11 @@ static const uint8_t jump_table[] =
       REF (form_floathex),	/* for 'A', 'a' */			      \
       REF (mod_ptrdiff_t),      /* for 't' */				      \
       REF (mod_intmax_t),       /* for 'j' */				      \
-      REF (form_unknown)        /* for 'I' */				      \
+      REF (form_unknown),       /* for 'I' */				      \
+      REF (form_binary),	/* for 'B', 'b' */			      \
     };									      \
     /* Step 3a: after processing first 'h' modifier.  */		      \
-    static JUMP_TABLE_TYPE step3a_jumps[30] =				      \
+    static JUMP_TABLE_TYPE step3a_jumps[31] =				      \
     {									      \
       REF (form_unknown),						      \
       REF (form_unknown),	/* for ' ' */				      \
@@ -577,10 +580,11 @@ static const uint8_t jump_table[] =
       REF (form_unknown),	/* for 'A', 'a' */			      \
       REF (form_unknown),       /* for 't' */				      \
       REF (form_unknown),       /* for 'j' */				      \
-      REF (form_unknown)        /* for 'I' */				      \
+      REF (form_unknown),       /* for 'I' */				      \
+      REF (form_binary),	/* for 'B', 'b' */			      \
     };									      \
     /* Step 3b: after processing first 'l' modifier.  */		      \
-    static JUMP_TABLE_TYPE step3b_jumps[30] =				      \
+    static JUMP_TABLE_TYPE step3b_jumps[31] =				      \
     {									      \
       REF (form_unknown),						      \
       REF (form_unknown),	/* for ' ' */				      \
@@ -611,12 +615,13 @@ static const uint8_t jump_table[] =
       REF (form_floathex),	/* for 'A', 'a' */			      \
       REF (form_unknown),       /* for 't' */				      \
       REF (form_unknown),       /* for 'j' */				      \
-      REF (form_unknown)        /* for 'I' */				      \
+      REF (form_unknown),       /* for 'I' */				      \
+      REF (form_binary),	/* for 'B', 'b' */			      \
     }
 
 #define STEP4_TABLE							      \
     /* Step 4: processing format specifier.  */				      \
-    static JUMP_TABLE_TYPE step4_jumps[30] =				      \
+    static JUMP_TABLE_TYPE step4_jumps[31] =				      \
     {									      \
       REF (form_unknown),						      \
       REF (form_unknown),	/* for ' ' */				      \
@@ -647,7 +652,8 @@ static const uint8_t jump_table[] =
       REF (form_floathex),	/* for 'A', 'a' */			      \
       REF (form_unknown),       /* for 't' */				      \
       REF (form_unknown),       /* for 'j' */				      \
-      REF (form_unknown)        /* for 'I' */				      \
+      REF (form_unknown),       /* for 'I' */				      \
+      REF (form_binary),	/* for 'B', 'b' */			      \
     }
 
 /* Before invoking this macro, process_arg_int etc. macros have to be
@@ -706,6 +712,14 @@ static const uint8_t jump_table[] =
     LABEL (form_hexa):							      \
       /* Unsigned hexadecimal integer.  */				      \
       base = 16;							      \
+      goto LABEL (unsigned_number);					      \
+      /* NOTREACHED */							      \
+									      \
+    LABEL (form_binary):						      \
+      /* Unsigned binary integer.  */					      \
+      base = 2;								      \
+      goto LABEL (unsigned_number);					      \
+      /* NOTREACHED */							      \
 									      \
     LABEL (unsigned_number):	  /* Unsigned number of base BASE.  */	      \
 									      \
@@ -803,8 +817,8 @@ static const uint8_t jump_table[] =
 	{								      \
 	  width -= workend - string + prec;				      \
 									      \
-	  if (number.word != 0 && alt && base == 16)			      \
-	    /* Account for 0X hex marker.  */				      \
+	  if (number.word != 0 && alt && (base == 16 || base == 2))	      \
+	    /* Account for 0X, 0x, 0B or 0b hex or binary marker.  */	      \
 	    width -= 2;							      \
 									      \
 	  if (is_negative || showsign || space)				      \
@@ -823,7 +837,7 @@ static const uint8_t jump_table[] =
 	  else if (space)						      \
 	    outchar (L_(' '));						      \
 									      \
-	  if (number.word != 0 && alt && base == 16)			      \
+	  if (number.word != 0 && alt && (base == 16 || base == 2))	      \
 	    {								      \
 	      outchar (L_('0'));					      \
 	      outchar (spec);						      \
@@ -854,7 +868,7 @@ static const uint8_t jump_table[] =
 	      --width;							      \
 	    }								      \
 									      \
-	  if (number.word != 0 && alt && base == 16)			      \
+	  if (number.word != 0 && alt && (base == 16 || base == 2))	      \
 	    {								      \
 	      outchar (L_('0'));					      \
 	      outchar (spec);						      \