diff --git a/lib/libc/iconv/_strtol.h b/lib/libc/iconv/_strtol.h
--- a/lib/libc/iconv/_strtol.h
+++ b/lib/libc/iconv/_strtol.h
@@ -91,6 +91,13 @@
 		s += 2;
 		base = 16;
 	}
+	if ((base == 0 || base == 2) &&
+	    c == '0' && (*s == 'b' || *s == 'B') &&
+	    (s[1] >= '0' && s[1] <= '1')) {
+		c = s[1];
+		s += 2;
+		base = 2;
+	}
 	if (base == 0)
 		base = (c == '0' ? 8 : 10);
 
diff --git a/lib/libc/iconv/_strtoul.h b/lib/libc/iconv/_strtoul.h
--- a/lib/libc/iconv/_strtoul.h
+++ b/lib/libc/iconv/_strtoul.h
@@ -87,6 +87,13 @@
 		s += 2;
 		base = 16;
 	}
+	if ((base == 0 || base == 2) &&
+	    c == '0' && (*s == 'b' || *s == 'B') &&
+	    (s[1] >= '0' && s[1] <= '1')) {
+		c = s[1];
+		s += 2;
+		base = 2;
+	}
 	if (base == 0)
 		base = (c == '0' ? 8 : 10);
 
diff --git a/lib/libc/locale/wcstoimax.c b/lib/libc/locale/wcstoimax.c
--- a/lib/libc/locale/wcstoimax.c
+++ b/lib/libc/locale/wcstoimax.c
@@ -86,6 +86,13 @@
 		s += 2;
 		base = 16;
 	}
+	if ((base == 0 || base == 2) &&
+	    c == L'0' && (*s == L'b' || *s == L'B') &&
+	    (s[1] >= L'0' && s[1] <= L'1')) {
+		c = s[1];
+		s += 2;
+		base = 2;
+	}
 	if (base == 0)
 		base = c == L'0' ? 8 : 10;
 	acc = any = 0;
diff --git a/lib/libc/locale/wcstol.c b/lib/libc/locale/wcstol.c
--- a/lib/libc/locale/wcstol.c
+++ b/lib/libc/locale/wcstol.c
@@ -80,6 +80,13 @@
 		s += 2;
 		base = 16;
 	}
+	if ((base == 0 || base == 2) &&
+	    c == L'0' && (*s == L'b' || *s == L'B') &&
+	    (s[1] >= L'0' && s[1] <= L'1')) {
+		c = s[1];
+		s += 2;
+		base = 2;
+	}
 	if (base == 0)
 		base = c == L'0' ? 8 : 10;
 	acc = any = 0;
diff --git a/lib/libc/locale/wcstoll.c b/lib/libc/locale/wcstoll.c
--- a/lib/libc/locale/wcstoll.c
+++ b/lib/libc/locale/wcstoll.c
@@ -86,6 +86,13 @@
 		s += 2;
 		base = 16;
 	}
+	if ((base == 0 || base == 2) &&
+	    c == L'0' && (*s == L'b' || *s == L'B') &&
+	    (s[1] >= L'0' && s[1] <= L'1')) {
+		c = s[1];
+		s += 2;
+		base = 2;
+	}
 	if (base == 0)
 		base = c == L'0' ? 8 : 10;
 	acc = any = 0;
diff --git a/lib/libc/locale/wcstoul.c b/lib/libc/locale/wcstoul.c
--- a/lib/libc/locale/wcstoul.c
+++ b/lib/libc/locale/wcstoul.c
@@ -80,6 +80,13 @@
 		s += 2;
 		base = 16;
 	}
+	if ((base == 0 || base == 2) &&
+	    c == L'0' && (*s == L'b' || *s == L'B') &&
+	    (s[1] >= L'0' && s[1] <= L'1')) {
+		c = s[1];
+		s += 2;
+		base = 2;
+	}
 	if (base == 0)
 		base = c == L'0' ? 8 : 10;
 	acc = any = 0;
diff --git a/lib/libc/locale/wcstoull.c b/lib/libc/locale/wcstoull.c
--- a/lib/libc/locale/wcstoull.c
+++ b/lib/libc/locale/wcstoull.c
@@ -86,6 +86,13 @@
 		s += 2;
 		base = 16;
 	}
+	if ((base == 0 || base == 2) &&
+	    c == L'0' && (*s == L'b' || *s == L'B') &&
+	    (s[1] >= L'0' && s[1] <= L'1')) {
+		c = s[1];
+		s += 2;
+		base = 2;
+	}
 	if (base == 0)
 		base = c == L'0' ? 8 : 10;
 	acc = any = 0;
diff --git a/lib/libc/locale/wcstoumax.c b/lib/libc/locale/wcstoumax.c
--- a/lib/libc/locale/wcstoumax.c
+++ b/lib/libc/locale/wcstoumax.c
@@ -86,6 +86,13 @@
 		s += 2;
 		base = 16;
 	}
+	if ((base == 0 || base == 2) &&
+	    c == L'0' && (*s == L'b' || *s == L'B') &&
+	    (s[1] >= L'0' && s[1] <= L'1')) {
+		c = s[1];
+		s += 2;
+		base = 2;
+	}
 	if (base == 0)
 		base = c == L'0' ? 8 : 10;
 	acc = any = 0;
diff --git a/lib/libc/stdio/printfcommon.h b/lib/libc/stdio/printfcommon.h
--- a/lib/libc/stdio/printfcommon.h
+++ b/lib/libc/stdio/printfcommon.h
@@ -194,6 +194,13 @@
 		} while (sval != 0);
 		break;
 
+	case 2:
+		do {
+			*--cp = to_char(val & 1);
+			val >>= 1;
+		} while (val);
+		break;
+
 	case 8:
 		do {
 			*--cp = to_char(val & 7);
@@ -244,6 +251,13 @@
 		} while (sval != 0);
 		break;
 
+	case 2:
+		do {
+			*--cp = to_char(val & 1);
+			val >>= 1;
+		} while (val);
+		break;
+
 	case 8:
 		do {
 			*--cp = to_char(val & 7);
diff --git a/lib/libc/stdio/vfprintf.c b/lib/libc/stdio/vfprintf.c
--- a/lib/libc/stdio/vfprintf.c
+++ b/lib/libc/stdio/vfprintf.c
@@ -613,6 +613,19 @@
 		case 'z':
 			flags |= SIZET;
 			goto rflag;
+		case 'B':
+		case 'b':
+			if (flags & INTMAX_SIZE)
+				ujval = UJARG();
+			else
+				ulval = UARG();
+			base = 2;
+			/* leading 0b/B only if non-zero */
+			if (flags & ALT &&
+			    (flags & INTMAX_SIZE ? ujval != 0 : ulval != 0))
+				ox[1] = ch;
+			goto nosign;
+			break;
 		case 'C':
 			flags |= LONGINT;
 			/*FALLTHROUGH*/
diff --git a/lib/libc/stdio/vfscanf.c b/lib/libc/stdio/vfscanf.c
--- a/lib/libc/stdio/vfscanf.c
+++ b/lib/libc/stdio/vfscanf.c
@@ -6,6 +6,8 @@
  *
  * Copyright (c) 2011 The FreeBSD Foundation
  *
+ * Copyright (c) 2023 Dag-Erling Smørgrav
+ *
  * Portions of this software were developed by David Chisnall
  * under sponsorship from the FreeBSD Foundation.
  *
@@ -80,16 +82,6 @@
 #define	SHORTSHORT	0x4000	/* hh: char */
 #define	UNSIGNED	0x8000	/* %[oupxX] conversions */
 
-/*
- * The following are used in integral conversions only:
- * SIGNOK, NDIGITS, PFXOK, and NZDIGITS
- */
-#define	SIGNOK		0x40	/* +/- is (still) legal */
-#define	NDIGITS		0x80	/* no digits detected */
-#define	PFXOK		0x100	/* 0x prefix is (still) legal */
-#define	NZDIGITS	0x200	/* no zero digits detected */
-#define	HAVESIGN	0x10000	/* sign detected */
-
 /*
  * Conversion types.
  */
@@ -307,129 +299,160 @@
 	return (n);
 }
 
+enum parseint_state {
+	begin,
+	havesign,
+	havezero,
+	haveprefix,
+	any,
+};
+
+static __inline int
+parseint_fsm(int c, enum parseint_state *state, int *base)
+{
+	switch (c) {
+	case '+':
+	case '-':
+		if (*state == begin) {
+			*state = havesign;
+			return 1;
+		}
+		break;
+	case '0':
+		if (*state == begin || *state == havesign) {
+			*state = havezero;
+		} else {
+			*state = any;
+		}
+		return 1;
+	case '1':
+	case '2':
+	case '3':
+	case '4':
+	case '5':
+	case '6':
+	case '7':
+		if (*state == havezero && *base == 0) {
+			*base = 8;
+		}
+		/* FALL THROUGH */
+	case '8':
+	case '9':
+		if (*state == begin ||
+		    *state == havesign) {
+			if (*base == 0) {
+				*base = 10;
+			}
+		}
+		if (*state == begin ||
+		    *state == havesign ||
+		    *state == havezero ||
+		    *state == haveprefix ||
+		    *state == any) {
+			if (*base > c - '0') {
+				*state = any;
+				return 1;
+			}
+		}
+		break;
+	case 'b':
+		if (*state == havezero) {
+			if (*base == 0 || *base == 2) {
+				*state = haveprefix;
+				*base = 2;
+				return 1;
+			}
+		}
+		/* FALL THROUGH */
+	case 'a':
+	case 'c':
+	case 'd':
+	case 'e':
+	case 'f':
+		if (*state == begin ||
+		    *state == havesign ||
+		    *state == havezero ||
+		    *state == haveprefix ||
+		    *state == any) {
+			if (*base > c - 'a' + 10) {
+				*state = any;
+				return 1;
+			}
+		}
+		break;
+	case 'B':
+		if (*state == havezero) {
+			if (*base == 0 || *base == 2) {
+				*state = haveprefix;
+				*base = 2;
+				return 1;
+			}
+		}
+		/* FALL THROUGH */
+	case 'A':
+	case 'C':
+	case 'D':
+	case 'E':
+	case 'F':
+		if (*state == begin ||
+		    *state == havesign ||
+		    *state == havezero ||
+		    *state == haveprefix ||
+		    *state == any) {
+			if (*base > c - 'A' + 10) {
+				*state = any;
+				return 1;
+			}
+		}
+		break;
+	case 'x':
+	case 'X':
+		if (*state == havezero) {
+			if (*base == 0 || *base == 16) {
+				*state = haveprefix;
+				*base = 16;
+				return 1;
+			}
+		}
+		break;
+	}
+	return 0;
+}
+
 /*
- * Read an integer, storing it in buf.  The only relevant bit in the
- * flags argument is PFXOK.
+ * Read an integer, storing it in buf.
  *
  * Return 0 on a match failure, and the number of characters read
  * otherwise.
  */
 static __inline int
-parseint(FILE *fp, char * __restrict buf, int width, int base, int flags)
+parseint(FILE *fp, char * __restrict buf, int width, int base)
 {
-	/* `basefix' is used to avoid `if' tests */
-	static const short basefix[17] =
-		{ 10, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16 };
+	enum parseint_state state = begin;
 	char *p;
 	int c;
 
-	flags |= SIGNOK | NDIGITS | NZDIGITS;
 	for (p = buf; width; width--) {
-		c = *fp->_p;
-		/*
-		 * Switch on the character; `goto ok' if we accept it
-		 * as a part of number.
-		 */
-		switch (c) {
-
-		/*
-		 * The digit 0 is always legal, but is special.  For
-		 * %i conversions, if no digits (zero or nonzero) have
-		 * been scanned (only signs), we will have base==0.
-		 * In that case, we should set it to 8 and enable 0x
-		 * prefixing.  Also, if we have not scanned zero
-		 * digits before this, do not turn off prefixing
-		 * (someone else will turn it off if we have scanned
-		 * any nonzero digits).
-		 */
-		case '0':
-			if (base == 0) {
-				base = 8;
-				flags |= PFXOK;
-			}
-			if (flags & NZDIGITS)
-				flags &= ~(SIGNOK|NZDIGITS|NDIGITS);
-			else
-				flags &= ~(SIGNOK|PFXOK|NDIGITS);
-			goto ok;
-
-		/* 1 through 7 always legal */
-		case '1': case '2': case '3':
-		case '4': case '5': case '6': case '7':
-			base = basefix[base];
-			flags &= ~(SIGNOK | PFXOK | NDIGITS);
-			goto ok;
-
-		/* digits 8 and 9 ok iff decimal or hex */
-		case '8': case '9':
-			base = basefix[base];
-			if (base <= 8)
-				break;	/* not legal here */
-			flags &= ~(SIGNOK | PFXOK | NDIGITS);
-			goto ok;
-
-		/* letters ok iff hex */
-		case 'A': case 'B': case 'C':
-		case 'D': case 'E': case 'F':
-		case 'a': case 'b': case 'c':
-		case 'd': case 'e': case 'f':
-			/* no need to fix base here */
-			if (base <= 10)
-				break;	/* not legal here */
-			flags &= ~(SIGNOK | PFXOK | NDIGITS);
-			goto ok;
-
-		/* sign ok only as first character */
-		case '+': case '-':
-			if (flags & SIGNOK) {
-				flags &= ~SIGNOK;
-				flags |= HAVESIGN;
-				goto ok;
-			}
+		c = __sgetc(fp);
+		if (c == EOF)
 			break;
-
-		/*
-		 * x ok iff flag still set & 2nd char (or 3rd char if
-		 * we have a sign).
-		 */
-		case 'x': case 'X':
-			if (flags & PFXOK && p ==
-			    buf + 1 + !!(flags & HAVESIGN)) {
-				base = 16;	/* if %i */
-				flags &= ~PFXOK;
-				goto ok;
-			}
+		if (!parseint_fsm(c, &state, &base))
 			break;
-		}
-
-		/*
-		 * If we got here, c is not a legal character for a
-		 * number.  Stop accumulating digits.
-		 */
-		break;
-	ok:
-		/*
-		 * c is legal: store it and look at the next.
-		 */
 		*p++ = c;
-		if (--fp->_r > 0)
-			fp->_p++;
-		else if (__srefill(fp))
-			break;		/* EOF */
 	}
 	/*
-	 * If we had only a sign, it is no good; push back the sign.
-	 * If the number ends in `x', it was [sign] '0' 'x', so push
-	 * back the x and treat it as [sign] '0'.
+	 * If we only had a sign, push it back.  If we only had a 0b or 0x
+	 * prefix (possibly preceded by a sign), we view it as "0" and
+	 * push back the letter.  In all other cases, if we stopped
+	 * because we read a non-number character, push it back.
 	 */
-	if (flags & NDIGITS) {
-		if (p > buf)
-			(void) __ungetc(*(u_char *)--p, fp);
-		return (0);
-	}
-	c = ((u_char *)p)[-1];
-	if (c == 'x' || c == 'X') {
-		--p;
+	if (state == havesign) {
+		p--;
+		(void) __ungetc(*(u_char *)p, fp);
+	} else if (state == haveprefix) {
+		p--;
+		(void) __ungetc(c, fp);
+	} else if (c != EOF) {
 		(void) __ungetc(c, fp);
 	}
 	return (p - buf);
@@ -554,6 +577,13 @@
 		/*
 		 * Conversions.
 		 */
+		case 'B':
+		case 'b':
+			c = CT_INT;
+			flags |= UNSIGNED;
+			base = 2;
+			break;
+
 		case 'd':
 			c = CT_INT;
 			base = 10;
@@ -578,7 +608,6 @@
 
 		case 'X':
 		case 'x':
-			flags |= PFXOK;	/* enable 0x prefixing */
 			c = CT_INT;
 			flags |= UNSIGNED;
 			base = 16;
@@ -613,7 +642,7 @@
 			break;
 
 		case 'p':	/* pointer format is like hex */
-			flags |= POINTER | PFXOK;
+			flags |= POINTER;
 			c = CT_INT;		/* assumes sizeof(uintmax_t) */
 			flags |= UNSIGNED;	/*      >= sizeof(uintptr_t) */
 			base = 16;
@@ -738,7 +767,7 @@
 				width = sizeof(buf) - 2;
 			width++;
 #endif
-			nr = parseint(fp, buf, width, base, flags);
+			nr = parseint(fp, buf, width, base);
 			if (nr == 0)
 				goto match_failure;
 			if ((flags & SUPPRESS) == 0) {
diff --git a/lib/libc/stdio/vfwprintf.c b/lib/libc/stdio/vfwprintf.c
--- a/lib/libc/stdio/vfwprintf.c
+++ b/lib/libc/stdio/vfwprintf.c
@@ -684,6 +684,19 @@
 		case 'z':
 			flags |= SIZET;
 			goto rflag;
+		case 'B':
+		case 'b':
+			if (flags & INTMAX_SIZE)
+				ujval = UJARG();
+			else
+				ulval = UARG();
+			base = 2;
+			/* leading 0b/B only if non-zero */
+			if (flags & ALT &&
+			    (flags & INTMAX_SIZE ? ujval != 0 : ulval != 0))
+				ox[1] = ch;
+			goto nosign;
+			break;
 		case 'C':
 			flags |= LONGINT;
 			/*FALLTHROUGH*/
diff --git a/lib/libc/stdio/vfwscanf.c b/lib/libc/stdio/vfwscanf.c
--- a/lib/libc/stdio/vfwscanf.c
+++ b/lib/libc/stdio/vfwscanf.c
@@ -9,6 +9,8 @@
  *
  * Copyright (c) 2011 The FreeBSD Foundation
  *
+ * Copyright (c) 2023 Dag-Erling Smørgrav
+ *
  * Portions of this software were developed by David Chisnall
  * under sponsorship from the FreeBSD Foundation.
  *
@@ -78,16 +80,6 @@
 #define	SHORTSHORT	0x4000	/* hh: char */
 #define	UNSIGNED	0x8000	/* %[oupxX] conversions */
 
-/*
- * The following are used in integral conversions only:
- * SIGNOK, NDIGITS, PFXOK, and NZDIGITS
- */
-#define	SIGNOK		0x40	/* +/- is (still) legal */
-#define	NDIGITS		0x80	/* no digits detected */
-#define	PFXOK		0x100	/* 0x prefix is (still) legal */
-#define	NZDIGITS	0x200	/* no zero digits detected */
-#define	HAVESIGN	0x10000	/* sign detected */
-
 /*
  * Conversion types.
  */
@@ -289,128 +281,161 @@
 	return (nread);
 }
 
+enum parseint_state {
+	begin,
+	havesign,
+	havezero,
+	haveprefix,
+	any,
+};
+
+static __inline int
+parseint_fsm(wchar_t c, enum parseint_state *state, int *base)
+{
+	switch (c) {
+	case '+':
+	case '-':
+		if (*state == begin) {
+			*state = havesign;
+			return 1;
+		}
+		break;
+	case '0':
+		if (*state == begin || *state == havesign) {
+			*state = havezero;
+		} else {
+			*state = any;
+		}
+		return 1;
+	case '1':
+	case '2':
+	case '3':
+	case '4':
+	case '5':
+	case '6':
+	case '7':
+		if (*state == havezero && *base == 0) {
+			*base = 8;
+		}
+		/* FALL THROUGH */
+	case '8':
+	case '9':
+		if (*state == begin ||
+		    *state == havesign) {
+			if (*base == 0) {
+				*base = 10;
+			}
+		}
+		if (*state == begin ||
+		    *state == havesign ||
+		    *state == havezero ||
+		    *state == haveprefix ||
+		    *state == any) {
+			if (*base > c - '0') {
+				*state = any;
+				return 1;
+			}
+		}
+		break;
+	case 'b':
+		if (*state == havezero) {
+			if (*base == 0 || *base == 2) {
+				*state = haveprefix;
+				*base = 2;
+				return 1;
+			}
+		}
+		/* FALL THROUGH */
+	case 'a':
+	case 'c':
+	case 'd':
+	case 'e':
+	case 'f':
+		if (*state == begin ||
+		    *state == havesign ||
+		    *state == havezero ||
+		    *state == haveprefix ||
+		    *state == any) {
+			if (*base > c - 'a' + 10) {
+				*state = any;
+				return 1;
+			}
+		}
+		break;
+	case 'B':
+		if (*state == havezero) {
+			if (*base == 0 || *base == 2) {
+				*state = haveprefix;
+				*base = 2;
+				return 1;
+			}
+		}
+		/* FALL THROUGH */
+	case 'A':
+	case 'C':
+	case 'D':
+	case 'E':
+	case 'F':
+		if (*state == begin ||
+		    *state == havesign ||
+		    *state == havezero ||
+		    *state == haveprefix ||
+		    *state == any) {
+			if (*base > c - 'A' + 10) {
+				*state = any;
+				return 1;
+			}
+		}
+		break;
+	case 'x':
+	case 'X':
+		if (*state == havezero) {
+			if (*base == 0 || *base == 16) {
+				*state = haveprefix;
+				*base = 16;
+				return 1;
+			}
+		}
+		break;
+	}
+	return 0;
+}
+
 /*
- * Read an integer, storing it in buf.  The only relevant bit in the
- * flags argument is PFXOK.
+ * Read an integer, storing it in buf.
  *
  * Return 0 on a match failure, and the number of characters read
  * otherwise.
  */
 static __inline int
-parseint(FILE *fp, wchar_t *buf, int width, int base, int flags,
+parseint(FILE *fp, wchar_t * __restrict buf, int width, int base,
     locale_t locale)
 {
-	/* `basefix' is used to avoid `if' tests */
-	static const short basefix[17] =
-		{ 10, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16 };
+	enum parseint_state state = begin;
 	wchar_t *wcp;
 	int c;
 
-	flags |= SIGNOK | NDIGITS | NZDIGITS;
 	for (wcp = buf; width; width--) {
 		c = __fgetwc(fp, locale);
-		/*
-		 * Switch on the character; `goto ok' if we accept it
-		 * as a part of number.
-		 */
-		switch (c) {
-
-		/*
-		 * The digit 0 is always legal, but is special.  For
-		 * %i conversions, if no digits (zero or nonzero) have
-		 * been scanned (only signs), we will have base==0.
-		 * In that case, we should set it to 8 and enable 0x
-		 * prefixing.  Also, if we have not scanned zero
-		 * digits before this, do not turn off prefixing
-		 * (someone else will turn it off if we have scanned
-		 * any nonzero digits).
-		 */
-		case '0':
-			if (base == 0) {
-				base = 8;
-				flags |= PFXOK;
-			}
-			if (flags & NZDIGITS)
-				flags &= ~(SIGNOK|NZDIGITS|NDIGITS);
-			else
-				flags &= ~(SIGNOK|PFXOK|NDIGITS);
-			goto ok;
-
-		/* 1 through 7 always legal */
-		case '1': case '2': case '3':
-		case '4': case '5': case '6': case '7':
-			base = basefix[base];
-			flags &= ~(SIGNOK | PFXOK | NDIGITS);
-			goto ok;
-
-		/* digits 8 and 9 ok iff decimal or hex */
-		case '8': case '9':
-			base = basefix[base];
-			if (base <= 8)
-				break;	/* not legal here */
-			flags &= ~(SIGNOK | PFXOK | NDIGITS);
-			goto ok;
-
-		/* letters ok iff hex */
-		case 'A': case 'B': case 'C':
-		case 'D': case 'E': case 'F':
-		case 'a': case 'b': case 'c':
-		case 'd': case 'e': case 'f':
-			/* no need to fix base here */
-			if (base <= 10)
-				break;	/* not legal here */
-			flags &= ~(SIGNOK | PFXOK | NDIGITS);
-			goto ok;
-
-		/* sign ok only as first character */
-		case '+': case '-':
-			if (flags & SIGNOK) {
-				flags &= ~SIGNOK;
-				flags |= HAVESIGN;
-				goto ok;
-			}
+		if (c == WEOF)
 			break;
-
-		/*
-		 * x ok iff flag still set & 2nd char (or 3rd char if
-		 * we have a sign).
-		 */
-		case 'x': case 'X':
-			if (flags & PFXOK && wcp ==
-			    buf + 1 + !!(flags & HAVESIGN)) {
-				base = 16;	/* if %i */
-				flags &= ~PFXOK;
-				goto ok;
-			}
+		if (!parseint_fsm(c, &state, &base))
 			break;
-		}
-
-		/*
-		 * If we got here, c is not a legal character for a
-		 * number.  Stop accumulating digits.
-		 */
-		if (c != WEOF)
-			__ungetwc(c, fp, locale);
-		break;
-	ok:
-		/*
-		 * c is legal: store it and look at the next.
-		 */
 		*wcp++ = (wchar_t)c;
 	}
 	/*
-	 * If we had only a sign, it is no good; push back the sign.
-	 * If the number ends in `x', it was [sign] '0' 'x', so push
-	 * back the x and treat it as [sign] '0'.
+	 * If we only had a sign, push it back.  If we only had a 0b or 0x
+	 * prefix (possibly preceded by a sign), we view it as "0" and
+	 * push back the letter.  In all other cases, if we stopped
+	 * because we read a non-number character, push it back.
 	 */
-	if (flags & NDIGITS) {
-		if (wcp > buf)
-			__ungetwc(*--wcp, fp, locale);
-		return (0);
-	}
-	c = wcp[-1];
-	if (c == 'x' || c == 'X') {
-		--wcp;
+	if (state == havesign) {
+		wcp--;
+		__ungetwc(*wcp, fp, locale);
+	} else if (state == haveprefix) {
+		wcp--;
+		__ungetwc(c, fp, locale);
+	} else if (c != WEOF) {
 		__ungetwc(c, fp, locale);
 	}
 	return (wcp - buf);
@@ -536,6 +561,13 @@
 		/*
 		 * Conversions.
 		 */
+		case 'B':
+		case 'b':
+			c = CT_INT;
+			flags |= UNSIGNED;
+			base = 2;
+			break;
+
 		case 'd':
 			c = CT_INT;
 			base = 10;
@@ -560,7 +592,6 @@
 
 		case 'X':
 		case 'x':
-			flags |= PFXOK;	/* enable 0x prefixing */
 			c = CT_INT;
 			flags |= UNSIGNED;
 			base = 16;
@@ -606,7 +637,7 @@
 			break;
 
 		case 'p':	/* pointer format is like hex */
-			flags |= POINTER | PFXOK;
+			flags |= POINTER;
 			c = CT_INT;		/* assumes sizeof(uintmax_t) */
 			flags |= UNSIGNED;	/*      >= sizeof(uintptr_t) */
 			base = 16;
@@ -716,7 +747,7 @@
 			    sizeof(*buf) - 1)
 				width = sizeof(buf) / sizeof(*buf) - 1;
 
-			nr = parseint(fp, buf, width, base, flags, locale);
+			nr = parseint(fp, buf, width, base, locale);
 			if (nr == 0)
 				goto match_failure;
 			if ((flags & SUPPRESS) == 0) {
diff --git a/lib/libc/stdlib/strtoimax.c b/lib/libc/stdlib/strtoimax.c
--- a/lib/libc/stdlib/strtoimax.c
+++ b/lib/libc/stdlib/strtoimax.c
@@ -87,6 +87,13 @@
 		s += 2;
 		base = 16;
 	}
+	if ((base == 0 || base == 2) &&
+	    c == '0' && (*s == 'b' || *s == 'B') &&
+	    (s[1] >= '0' && s[1] <= '1')) {
+		c = s[1];
+		s += 2;
+		base = 2;
+	}
 	if (base == 0)
 		base = c == '0' ? 8 : 10;
 	acc = any = 0;
diff --git a/lib/libc/stdlib/strtol.c b/lib/libc/stdlib/strtol.c
--- a/lib/libc/stdlib/strtol.c
+++ b/lib/libc/stdlib/strtol.c
@@ -87,6 +87,13 @@
 		s += 2;
 		base = 16;
 	}
+	if ((base == 0 || base == 2) &&
+	    c == '0' && (*s == 'b' || *s == 'B') &&
+	    (s[1] >= '0' && s[1] <= '1')) {
+		c = s[1];
+		s += 2;
+		base = 2;
+	}
 	if (base == 0)
 		base = c == '0' ? 8 : 10;
 	acc = any = 0;
diff --git a/lib/libc/stdlib/strtoll.c b/lib/libc/stdlib/strtoll.c
--- a/lib/libc/stdlib/strtoll.c
+++ b/lib/libc/stdlib/strtoll.c
@@ -63,8 +63,9 @@
 
 	/*
 	 * Skip white space and pick up leading +/- sign if any.
-	 * If base is 0, allow 0x for hex and 0 for octal, else
-	 * assume decimal; if base is already 16, allow 0x.
+	 * If base is 0, allow 0b for binary, 0x for hex, and 0 for
+	 * octal, else assume decimal; if base is already 2, allow
+	 * 0b; if base is already 16, allow 0x.
 	 */
 	s = nptr;
 	do {
@@ -87,6 +88,13 @@
 		s += 2;
 		base = 16;
 	}
+	if ((base == 0 || base == 2) &&
+	    c == '0' && (*s == 'b' || *s == 'B') &&
+	    (s[1] >= '0' && s[1] <= '1')) {
+		c = s[1];
+		s += 2;
+		base = 2;
+	}
 	if (base == 0)
 		base = c == '0' ? 8 : 10;
 	acc = any = 0;
diff --git a/lib/libc/stdlib/strtoul.c b/lib/libc/stdlib/strtoul.c
--- a/lib/libc/stdlib/strtoul.c
+++ b/lib/libc/stdlib/strtoul.c
@@ -84,6 +84,13 @@
 		s += 2;
 		base = 16;
 	}
+	if ((base == 0 || base == 2) &&
+	    c == '0' && (*s == 'b' || *s == 'B') &&
+	    (s[1] >= '0' && s[1] <= '1')) {
+		c = s[1];
+		s += 2;
+		base = 2;
+	}
 	if (base == 0)
 		base = c == '0' ? 8 : 10;
 	acc = any = 0;
diff --git a/lib/libc/stdlib/strtoull.c b/lib/libc/stdlib/strtoull.c
--- a/lib/libc/stdlib/strtoull.c
+++ b/lib/libc/stdlib/strtoull.c
@@ -85,6 +85,13 @@
 		s += 2;
 		base = 16;
 	}
+	if ((base == 0 || base == 2) &&
+	    c == '0' && (*s == 'b' || *s == 'B') &&
+	    (s[1] >= '0' && s[1] <= '1')) {
+		c = s[1];
+		s += 2;
+		base = 2;
+	}
 	if (base == 0)
 		base = c == '0' ? 8 : 10;
 	acc = any = 0;
diff --git a/lib/libc/stdlib/strtoumax.c b/lib/libc/stdlib/strtoumax.c
--- a/lib/libc/stdlib/strtoumax.c
+++ b/lib/libc/stdlib/strtoumax.c
@@ -85,6 +85,13 @@
 		s += 2;
 		base = 16;
 	}
+	if ((base == 0 || base == 2) &&
+	    c == '0' && (*s == 'b' || *s == 'B') &&
+	    (s[1] >= '0' && s[1] <= '1')) {
+		c = s[1];
+		s += 2;
+		base = 2;
+	}
 	if (base == 0)
 		base = c == '0' ? 8 : 10;
 	acc = any = 0;