public inbox for libc-alpha@sourceware.org
 help / color / mirror / Atom feed
* [PATCH] printf: Add %B convespecifier for binary
@ 2020-04-27 13:35 Alejandro Colomar
  2020-04-27 14:46 ` Florian Weimer
  0 siblings, 1 reply; 9+ messages in thread
From: Alejandro Colomar @ 2020-04-27 13:35 UTC (permalink / raw)
  To: libc-alpha, jrnieder

Hi all,

This patch adds a new feature to the ``printf`` family of functions:

``%B`` conversion specifier for printing unsigned numbers in binary.

Behaviour is exactly as with ``%X``, only changing the base (16 -> 2).

As Jonathan Nieder pointed out in a previous email, ``%b`` is already in
use by ``printf(1)``, so I didn't use it for binary.

I added some tests, and everything looks good.

		Alex.


 From b2bd279d76f2e729953a43fe8d619ceb927257ba Mon Sep 17 00:00:00 2001
From: Alejandro Colomar <colomar.6.4.3@gmail.com>
Date: Mon, 27 Apr 2020 15:10:25 +0200
Subject: [PATCH] printf: Add %B conversion specifier for binary

---
  stdio-common/_itoa.c             |  5 ++++
  stdio-common/_itowa.c            |  4 +++
  stdio-common/_itowa.h            |  1 +
  stdio-common/tst-printf.c        | 24 ++++++++++++++--
  stdio-common/vfprintf-internal.c | 48 ++++++++++++++++++++------------
  sysdeps/generic/_itoa.h          |  1 +
  6 files changed, 62 insertions(+), 21 deletions(-)

diff --git a/stdio-common/_itoa.c b/stdio-common/_itoa.c
index 5ab6bc982e..c0057eba00 100644
--- a/stdio-common/_itoa.c
+++ b/stdio-common/_itoa.c
@@ -179,6 +179,7 @@ _itoa_word (_ITOA_WORD_TYPE value, char *buflim,
        SPECIAL (10);
        SPECIAL (16);
        SPECIAL (8);
+      SPECIAL (2);
      default:
        do
  	*--buflim = digits[value % base];
@@ -245,6 +246,10 @@ _itoa (unsigned long long int value, char *buflim, 
unsigned int base,
  	  while (work_hi != 0);						      \
  	}								      \
        while (0)
+    case 2:
+      RUN_2N (1);
+      break;
+
      case 8:
        RUN_2N (3);
        break;
diff --git a/stdio-common/_itowa.c b/stdio-common/_itowa.c
index 2b68a85dbe..7543656c3b 100644
--- a/stdio-common/_itowa.c
+++ b/stdio-common/_itowa.c
@@ -141,6 +141,10 @@ _itowa (unsigned long long int value, wchar_t 
*buflim, unsigned int base,
  	  while (work_hi != 0);						      \
  	}								      \
        while (0)
+    case 2:
+      RUN_2N (1);
+      break;
+
      case 8:
        RUN_2N (3);
        break;
diff --git a/stdio-common/_itowa.h b/stdio-common/_itowa.h
index 91c5fd4f11..b525c761fd 100644
--- a/stdio-common/_itowa.h
+++ b/stdio-common/_itowa.h
@@ -53,6 +53,7 @@ _itowa_word (_ITOA_WORD_TYPE value, wchar_t *buflim,
        SPECIAL (10);
        SPECIAL (16);
        SPECIAL (8);
+      SPECIAL (2);
      default:
        do
  	*--bp = digits[value % base];
diff --git a/stdio-common/tst-printf.c b/stdio-common/tst-printf.c
index 52caf04e85..edcf6237cc 100644
--- a/stdio-common/tst-printf.c
+++ b/stdio-common/tst-printf.c
@@ -96,9 +96,19 @@ I am ready for my first lesson today.";
  #endif

    printf("decimal negative:\t\"%d\"\n", -2345);
+  /* FIXME: GCC 9 ignores `%B' yet */
+  DIAG_PUSH_NEEDS_COMMENT;
+  DIAG_IGNORE_NEEDS_COMMENT(9, "-Wformat-extra-args");
+  printf("binary negative:\t\"%B\"\n", -2345);
+  DIAG_POP_NEEDS_COMMENT;
    printf("octal negative:\t\"%o\"\n", -2345);
    printf("hex negative:\t\"%x\"\n", -2345);
    printf("long decimal number:\t\"%ld\"\n", -123456L);
+  /* FIXME: GCC 9 ignores `%B' yet */
+  DIAG_PUSH_NEEDS_COMMENT;
+  DIAG_IGNORE_NEEDS_COMMENT(9, "-Wformat-extra-args");
+  printf("long binary negative:\t\"%lB\"\n", -2345L);
+  DIAG_POP_NEEDS_COMMENT;
    printf("long octal negative:\t\"%lo\"\n", -2345L);
    printf("long unsigned decimal number:\t\"%lu\"\n", -123456L);
    printf("zero-padded LDN:\t\"%010ld\"\n", -123456L);
@@ -311,6 +321,14 @@ rfg2 (void)
    sprintf (buf, "%09.*u", prec, 33);
    if (strcmp (buf, "  0000033") != 0)
      printf ("got: '%s', expected: '%s'\n", buf, "  0000033");
+  prec = 7;
+  /* FIXME: GCC 9 ignores `%B' yet */
+  DIAG_PUSH_NEEDS_COMMENT;
+  DIAG_IGNORE_NEEDS_COMMENT(9, "-Wformat-extra-args");
+  sprintf (buf, "%08.*B", prec, 33);
+  DIAG_POP_NEEDS_COMMENT;
+  if (strcmp (buf, " 0100001") != 0)
+    printf ("got: '%s', expected: '%s'\n", buf, " 0100001");
    prec = 3;
    sprintf (buf, "%04.*x", prec, 33);
    if (strcmp (buf, " 021") != 0)
@@ -333,12 +351,12 @@ rfg3 (void)
    int h = 1234;

    sprintf (buf,
-	   "%1$*5$d %2$*6$hi %3$*7$lo %4$*8$f %9$*12$e %10$*13$g %11$*14$s",
+	   "%1$*5$d %2$*6$hi %2$*7$hB %3$*7$lo %4$*8$f %9$*12$e %10$*13$g 
%11$*14$s",
  	   i, h, l, d, 8, 5, 14, 14, d, g, s, 14, 3, 14);
    if (strcmp (buf,
-	      "   12345  1234    11145401322     321.765432   3.217654e+02   5 
    test-string") != 0)
+	      "   12345  1234    10011010010    11145401322     321.765432 
3.217654e+02   5    test-string") != 0)
      printf ("got: '%s', expected: '%s'\n", buf,
-	    "   12345  1234    11145401322     321.765432   3.217654e+02   5 
  test-string");
+	    "   12345  1234    10011010010    11145401322     321.765432 
3.217654e+02   5    test-string");
  }

  #define TEST_FUNCTION do_test ()
diff --git a/stdio-common/vfprintf-internal.c 
b/stdio-common/vfprintf-internal.c
index 3be92d4b6e..e31edbc75c 100644
--- a/stdio-common/vfprintf-internal.c
+++ b/stdio-common/vfprintf-internal.c
@@ -270,7 +270,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,
@@ -324,7 +324,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 ' ' */				      \
@@ -356,9 +356,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_bin)		/* for '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 ' ' */				      \
@@ -389,10 +390,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_bin)		/* for '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 ' ' */				      \
@@ -423,10 +425,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_bin)		/* for '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 ' ' */				      \
@@ -457,10 +460,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_bin)		/* for '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 ' ' */				      \
@@ -491,12 +495,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_bin)		/* for '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 ' ' */				      \
@@ -527,7 +532,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_bin)		/* for 'B' */				      \
      }


@@ -597,6 +603,12 @@ static const uint8_t jump_table[] =
        goto LABEL (unsigned_number);					      \
        /* NOTREACHED */							      \
  									      \
+    LABEL (form_bin):							      \
+      /* Unsigned binary integer.  */					      \
+      base = 2;								      \
+      goto LABEL (unsigned_number);					      \
+      /* NOTREACHED */							      \
+									      \
      LABEL (form_octal):							      \
        /* Unsigned octal integer.  */					      \
        base = 8;								      \
@@ -644,7 +656,7 @@ static const uint8_t jump_table[] =
  	    {								      \
  	      /* Put the number in WORK.  */				      \
  	      string = _itoa (number.longlong, workend, base,		      \
-			      spec == L_('X'));				      \
+			      spec == L_('X') || spec == L_('B'));	      \
  	      if (group && grouping)					      \
  		string = group_number (work_buffer, string, workend,	      \
  				       grouping, thousands_sep);	      \
@@ -701,7 +713,7 @@ static const uint8_t jump_table[] =
  	    {								      \
  	      /* Put the number in WORK.  */				      \
  	      string = _itoa_word (number.word, workend, base,		      \
-				   spec == L_('X'));			      \
+				   spec == L_('X') || spec == L_('B'));	      \
  	      if (group && grouping)					      \
  		string = group_number (work_buffer, string, workend,	      \
  				       grouping, thousands_sep);	      \
@@ -720,8 +732,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 hex marker or 0B bin marker.  */		      \
  	    width -= 2;							      \
  									      \
  	  if (is_negative || showsign || space)				      \
@@ -740,7 +752,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);						      \
@@ -771,7 +783,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);						      \
diff --git a/sysdeps/generic/_itoa.h b/sysdeps/generic/_itoa.h
index 790f88a6d5..5d2a0720f6 100644
--- a/sysdeps/generic/_itoa.h
+++ b/sysdeps/generic/_itoa.h
@@ -76,6 +76,7 @@ _itoa_word (_ITOA_WORD_TYPE value, char *buflim,
        SPECIAL (10);
        SPECIAL (16);
        SPECIAL (8);
+      SPECIAL (2);
      default:
        do
  	*--buflim = digits[value % base];
-- 
2.26.2

^ permalink raw reply	[flat|nested] 9+ messages in thread

end of thread, other threads:[~2021-10-20 17:56 UTC | newest]

Thread overview: 9+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2020-04-27 13:35 [PATCH] printf: Add %B convespecifier for binary Alejandro Colomar
2020-04-27 14:46 ` Florian Weimer
2020-04-27 15:27   ` Andreas Schwab
2020-04-27 15:40     ` Florian Weimer
2020-04-27 20:42       ` Jonathan Nieder
2020-04-27 20:42   ` Joseph Myers
2021-10-20 16:57   ` Alejandro Colomar
2021-10-20 17:04     ` Florian Weimer
2021-10-20 17:56       ` Joseph Myers

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for read-only IMAP folder(s) and NNTP newsgroup(s).