git: b9385720f34b - main - libc: Add unit tests for N2630 and possible collateral damage.

From: Dag-Erling Smørgrav <des_at_FreeBSD.org>
Date: Mon, 28 Aug 2023 15:38:22 UTC
The branch main has been updated by des:

URL: https://cgit.FreeBSD.org/src/commit/?id=b9385720f34b536ef2568a642e8b1fad0450056f

commit b9385720f34b536ef2568a642e8b1fad0450056f
Author:     Dag-Erling Smørgrav <des@FreeBSD.org>
AuthorDate: 2023-08-28 15:34:05 +0000
Commit:     Dag-Erling Smørgrav <des@FreeBSD.org>
CommitDate: 2023-08-28 15:34:05 +0000

    libc: Add unit tests for N2630 and possible collateral damage.
    
    Reviewed by:    imp, emaste
    Differential Revision:  https://reviews.freebsd.org/D41512
---
 contrib/netbsd-tests/lib/libc/stdlib/t_strtol.c |   6 +
 lib/libc/tests/stdio/Makefile                   |   3 +
 lib/libc/tests/stdio/snprintf_test.c            | 138 +++++++++++++
 lib/libc/tests/stdio/sscanf_test.c              | 249 +++++++++++++++++++++++
 lib/libc/tests/stdio/swscanf_test.c             | 250 ++++++++++++++++++++++++
 5 files changed, 646 insertions(+)

diff --git a/contrib/netbsd-tests/lib/libc/stdlib/t_strtol.c b/contrib/netbsd-tests/lib/libc/stdlib/t_strtol.c
index 54e190760656..d1027fcc7bb1 100644
--- a/contrib/netbsd-tests/lib/libc/stdlib/t_strtol.c
+++ b/contrib/netbsd-tests/lib/libc/stdlib/t_strtol.c
@@ -94,6 +94,12 @@ ATF_TC_BODY(strtol_base, tc)
 		{ "01234567",			    342391,  0, NULL	},
 		{ "0123456789",			 123456789, 10, NULL	},
 		{ "0x75bcd15",		         123456789,  0, NULL	},
+#ifdef __FreeBSD__
+		{ "0x",				         0,  0, "x"	},
+		{ "0b111010110111100110100010101", 123456789,  0, NULL	},
+		{ "0b0123",			         1,  0, "23"	},
+		{ "0b",				         0,  0, "b"	},
+#endif
 	};
 
 	long long int lli;
diff --git a/lib/libc/tests/stdio/Makefile b/lib/libc/tests/stdio/Makefile
index 12ae44855da5..f7383e5b3ef4 100644
--- a/lib/libc/tests/stdio/Makefile
+++ b/lib/libc/tests/stdio/Makefile
@@ -16,6 +16,9 @@ ATF_TESTS_C+=		print_positional_test
 ATF_TESTS_C+=		printbasic_test
 ATF_TESTS_C+=		printfloat_test
 ATF_TESTS_C+=		scanfloat_test
+ATF_TESTS_C+=		snprintf_test
+ATF_TESTS_C+=		sscanf_test
+ATF_TESTS_C+=		swscanf_test
 
 SRCS.fopen2_test=	fopen_test.c
 
diff --git a/lib/libc/tests/stdio/snprintf_test.c b/lib/libc/tests/stdio/snprintf_test.c
new file mode 100644
index 000000000000..0c335bc1a512
--- /dev/null
+++ b/lib/libc/tests/stdio/snprintf_test.c
@@ -0,0 +1,138 @@
+/*-
+ * Copyright (c) 2023 Dag-Erling Smørgrav
+ *
+ * SPDX-License-Identifier: BSD-2-Clause
+ */
+
+#include <assert.h>
+#include <limits.h>
+#include <locale.h>
+#include <stdint.h>
+#include <stdio.h>
+
+#include <atf-c.h>
+
+#define SNPRINTF_TEST(output, format, ...) \
+	do {								\
+		char buf[256];						\
+		assert(output == NULL || strlen(output) < sizeof(buf));	\
+		int ret = snprintf(buf, sizeof(buf), format, __VA_ARGS__); \
+		if (output == NULL) {					\
+			ATF_CHECK_EQ(-1, ret);				\
+		} else {						\
+			ATF_CHECK_EQ(strlen(output), ret);		\
+			if (ret > 0) {					\
+				ATF_CHECK_STREQ(output, buf);		\
+			}						\
+		}							\
+	} while (0)
+
+ATF_TC_WITHOUT_HEAD(snprintf_b);
+ATF_TC_BODY(snprintf_b, tc)
+{
+	SNPRINTF_TEST("0", "%b", 0);
+	SNPRINTF_TEST("           0", "%12b", 0);
+	SNPRINTF_TEST("000000000000", "%012b", 0);
+	SNPRINTF_TEST("1", "%b", 1);
+	SNPRINTF_TEST("           1", "%12b", 1);
+	SNPRINTF_TEST("000000000001", "%012b", 1);
+	SNPRINTF_TEST("1111111111111111111111111111111", "%b", INT_MAX);
+	SNPRINTF_TEST("0", "%#b", 0);
+	SNPRINTF_TEST("           0", "%#12b", 0);
+	SNPRINTF_TEST("000000000000", "%#012b", 0);
+	SNPRINTF_TEST("0b1", "%#b", 1);
+	SNPRINTF_TEST("         0b1", "%#12b", 1);
+	SNPRINTF_TEST("0b0000000001", "%#012b", 1);
+	SNPRINTF_TEST("0b1111111111111111111111111111111", "%#b", INT_MAX);
+}
+
+ATF_TC_WITHOUT_HEAD(snprintf_B);
+ATF_TC_BODY(snprintf_B, tc)
+{
+	SNPRINTF_TEST("0", "%B", 0);
+	SNPRINTF_TEST("           0", "%12B", 0);
+	SNPRINTF_TEST("000000000000", "%012B", 0);
+	SNPRINTF_TEST("1", "%B", 1);
+	SNPRINTF_TEST("           1", "%12B", 1);
+	SNPRINTF_TEST("000000000001", "%012B", 1);
+	SNPRINTF_TEST("1111111111111111111111111111111", "%B", INT_MAX);
+	SNPRINTF_TEST("0", "%#B", 0);
+	SNPRINTF_TEST("           0", "%#12B", 0);
+	SNPRINTF_TEST("000000000000", "%#012B", 0);
+	SNPRINTF_TEST("0B1", "%#B", 1);
+	SNPRINTF_TEST("         0B1", "%#12B", 1);
+	SNPRINTF_TEST("0B0000000001", "%#012B", 1);
+	SNPRINTF_TEST("0B1111111111111111111111111111111", "%#B", INT_MAX);
+}
+
+ATF_TC_WITHOUT_HEAD(snprintf_d);
+ATF_TC_BODY(snprintf_d, tc)
+{
+	SNPRINTF_TEST("0", "%d", 0);
+	SNPRINTF_TEST("           0", "%12d", 0);
+	SNPRINTF_TEST("000000000000", "%012d", 0);
+	SNPRINTF_TEST("1", "%d", 1);
+	SNPRINTF_TEST("           1", "%12d", 1);
+	SNPRINTF_TEST("000000000001", "%012d", 1);
+	SNPRINTF_TEST("2147483647", "%d", INT_MAX);
+	SNPRINTF_TEST("  2147483647", "%12d", INT_MAX);
+	SNPRINTF_TEST("002147483647", "%012d", INT_MAX);
+	SNPRINTF_TEST("2,147,483,647", "%'d", INT_MAX);
+}
+
+ATF_TC_WITHOUT_HEAD(snprintf_x);
+ATF_TC_BODY(snprintf_x, tc)
+{
+	SNPRINTF_TEST("0", "%x", 0);
+	SNPRINTF_TEST("           0", "%12x", 0);
+	SNPRINTF_TEST("000000000000", "%012x", 0);
+	SNPRINTF_TEST("1", "%x", 1);
+	SNPRINTF_TEST("           1", "%12x", 1);
+	SNPRINTF_TEST("000000000001", "%012x", 1);
+	SNPRINTF_TEST("7fffffff", "%x", INT_MAX);
+	SNPRINTF_TEST("    7fffffff", "%12x", INT_MAX);
+	SNPRINTF_TEST("00007fffffff", "%012x", INT_MAX);
+	SNPRINTF_TEST("0", "%#x", 0);
+	SNPRINTF_TEST("           0", "%#12x", 0);
+	SNPRINTF_TEST("000000000000", "%#012x", 0);
+	SNPRINTF_TEST("0x1", "%#x", 1);
+	SNPRINTF_TEST("         0x1", "%#12x", 1);
+	SNPRINTF_TEST("0x0000000001", "%#012x", 1);
+	SNPRINTF_TEST("0x7fffffff", "%#x", INT_MAX);
+	SNPRINTF_TEST("  0x7fffffff", "%#12x", INT_MAX);
+	SNPRINTF_TEST("0x007fffffff", "%#012x", INT_MAX);
+}
+
+ATF_TC_WITHOUT_HEAD(snprintf_X);
+ATF_TC_BODY(snprintf_X, tc)
+{
+	SNPRINTF_TEST("0", "%X", 0);
+	SNPRINTF_TEST("           0", "%12X", 0);
+	SNPRINTF_TEST("000000000000", "%012X", 0);
+	SNPRINTF_TEST("1", "%X", 1);
+	SNPRINTF_TEST("           1", "%12X", 1);
+	SNPRINTF_TEST("000000000001", "%012X", 1);
+	SNPRINTF_TEST("7FFFFFFF", "%X", INT_MAX);
+	SNPRINTF_TEST("    7FFFFFFF", "%12X", INT_MAX);
+	SNPRINTF_TEST("00007FFFFFFF", "%012X", INT_MAX);
+	SNPRINTF_TEST("0", "%#X", 0);
+	SNPRINTF_TEST("           0", "%#12X", 0);
+	SNPRINTF_TEST("000000000000", "%#012X", 0);
+	SNPRINTF_TEST("0X1", "%#X", 1);
+	SNPRINTF_TEST("         0X1", "%#12X", 1);
+	SNPRINTF_TEST("0X0000000001", "%#012X", 1);
+	SNPRINTF_TEST("0X7FFFFFFF", "%#X", INT_MAX);
+	SNPRINTF_TEST("  0X7FFFFFFF", "%#12X", INT_MAX);
+	SNPRINTF_TEST("0X007FFFFFFF", "%#012X", INT_MAX);
+}
+
+ATF_TP_ADD_TCS(tp)
+{
+	setlocale(LC_NUMERIC, "en_US.UTF-8");
+	ATF_TP_ADD_TC(tp, snprintf_b);
+	ATF_TP_ADD_TC(tp, snprintf_B);
+	ATF_TP_ADD_TC(tp, snprintf_d);
+	ATF_TP_ADD_TC(tp, snprintf_x);
+	ATF_TP_ADD_TC(tp, snprintf_X);
+	return (atf_no_error());
+}
diff --git a/lib/libc/tests/stdio/sscanf_test.c b/lib/libc/tests/stdio/sscanf_test.c
new file mode 100644
index 000000000000..24226f3b3a8d
--- /dev/null
+++ b/lib/libc/tests/stdio/sscanf_test.c
@@ -0,0 +1,249 @@
+/*-
+ * Copyright (c) 2023 Dag-Erling Smørgrav
+ *
+ * SPDX-License-Identifier: BSD-2-Clause
+ */
+
+#include <locale.h>
+#include <stdint.h>
+#include <stdio.h>
+
+#include <atf-c.h>
+
+static const struct sscanf_test_case {
+	char input[8];
+	struct {
+		int ret, val, len;
+	} b, o, d, x, i;
+} sscanf_test_cases[] = {
+//	input		binary		octal		decimal		hexadecimal	automatic
+	// all digits
+	{ "0",		{ 1,   0, 1 },	{ 1,   0, 1 },	{ 1,   0, 1 },	{ 1,   0, 1 },	{ 1,   0, 1 }, },
+	{ "1",		{ 1,   1, 1 },	{ 1,   1, 1 },	{ 1,   1, 1 },	{ 1,   1, 1 },	{ 1,   1, 1 }, },
+	{ "2",		{ 0,   0, 0 },	{ 1,   2, 1 },	{ 1,   2, 1 },	{ 1,   2, 1 },	{ 1,   2, 1 }, },
+	{ "3",		{ 0,   0, 0 },	{ 1,   3, 1 },	{ 1,   3, 1 },	{ 1,   3, 1 },	{ 1,   3, 1 }, },
+	{ "4",		{ 0,   0, 0 },	{ 1,   4, 1 },	{ 1,   4, 1 },	{ 1,   4, 1 },	{ 1,   4, 1 }, },
+	{ "5",		{ 0,   0, 0 },	{ 1,   5, 1 },	{ 1,   5, 1 },	{ 1,   5, 1 },	{ 1,   5, 1 }, },
+	{ "6",		{ 0,   0, 0 },	{ 1,   6, 1 },	{ 1,   6, 1 },	{ 1,   6, 1 },	{ 1,   6, 1 }, },
+	{ "7",		{ 0,   0, 0 },	{ 1,   7, 1 },	{ 1,   7, 1 },	{ 1,   7, 1 },	{ 1,   7, 1 }, },
+	{ "8",		{ 0,   0, 0 },	{ 0,   0, 0 },	{ 1,   8, 1 },	{ 1,   8, 1 },	{ 1,   8, 1 }, },
+	{ "9",		{ 0,   0, 0 },	{ 0,   0, 0 },	{ 1,   9, 1 },	{ 1,   9, 1 },	{ 1,   9, 1 }, },
+	{ "A",		{ 0,   0, 0 },	{ 0,   0, 0 },	{ 0,   0, 0 },	{ 1,  10, 1 },	{ 0,   0, 0 }, },
+	{ "B",		{ 0,   0, 0 },	{ 0,   0, 0 },	{ 0,   0, 0 },	{ 1,  11, 1 },	{ 0,   0, 0 }, },
+	{ "C",		{ 0,   0, 0 },	{ 0,   0, 0 },	{ 0,   0, 0 },	{ 1,  12, 1 },	{ 0,   0, 0 }, },
+	{ "D",		{ 0,   0, 0 },	{ 0,   0, 0 },	{ 0,   0, 0 },	{ 1,  13, 1 },	{ 0,   0, 0 }, },
+	{ "E",		{ 0,   0, 0 },	{ 0,   0, 0 },	{ 0,   0, 0 },	{ 1,  14, 1 },	{ 0,   0, 0 }, },
+	{ "F",		{ 0,   0, 0 },	{ 0,   0, 0 },	{ 0,   0, 0 },	{ 1,  15, 1 },	{ 0,   0, 0 }, },
+	{ "X",		{ 0,   0, 0 },	{ 0,   0, 0 },	{ 0,   0, 0 },	{ 0,   0, 0 },	{ 0,   0, 0 }, },
+	{ "a",		{ 0,   0, 0 },	{ 0,   0, 0 },	{ 0,   0, 0 },	{ 1,  10, 1 },	{ 0,   0, 0 }, },
+	{ "b",		{ 0,   0, 0 },	{ 0,   0, 0 },	{ 0,   0, 0 },	{ 1,  11, 1 },	{ 0,   0, 0 }, },
+	{ "c",		{ 0,   0, 0 },	{ 0,   0, 0 },	{ 0,   0, 0 },	{ 1,  12, 1 },	{ 0,   0, 0 }, },
+	{ "d",		{ 0,   0, 0 },	{ 0,   0, 0 },	{ 0,   0, 0 },	{ 1,  13, 1 },	{ 0,   0, 0 }, },
+	{ "e",		{ 0,   0, 0 },	{ 0,   0, 0 },	{ 0,   0, 0 },	{ 1,  14, 1 },	{ 0,   0, 0 }, },
+	{ "f",		{ 0,   0, 0 },	{ 0,   0, 0 },	{ 0,   0, 0 },	{ 1,  15, 1 },	{ 0,   0, 0 }, },
+	{ "x",		{ 0,   0, 0 },	{ 0,   0, 0 },	{ 0,   0, 0 },	{ 0,   0, 0 },	{ 0,   0, 0 }, },
+	// all digits with leading zero
+	{ "00",		{ 1,   0, 2 },	{ 1,   0, 2 },	{ 1,   0, 2 },	{ 1,   0, 2 },	{ 1,   0, 2 }, },
+	{ "01",		{ 1,   1, 2 },	{ 1,   1, 2 },	{ 1,   1, 2 },	{ 1,   1, 2 },	{ 1,   1, 2 }, },
+	{ "02",		{ 1,   0, 1 },	{ 1,   2, 2 },	{ 1,   2, 2 },	{ 1,   2, 2 },	{ 1,   2, 2 }, },
+	{ "03",		{ 1,   0, 1 },	{ 1,   3, 2 },	{ 1,   3, 2 },	{ 1,   3, 2 },	{ 1,   3, 2 }, },
+	{ "04",		{ 1,   0, 1 },	{ 1,   4, 2 },	{ 1,   4, 2 },	{ 1,   4, 2 },	{ 1,   4, 2 }, },
+	{ "05",		{ 1,   0, 1 },	{ 1,   5, 2 },	{ 1,   5, 2 },	{ 1,   5, 2 },	{ 1,   5, 2 }, },
+	{ "06",		{ 1,   0, 1 },	{ 1,   6, 2 },	{ 1,   6, 2 },	{ 1,   6, 2 },	{ 1,   6, 2 }, },
+	{ "07",		{ 1,   0, 1 },	{ 1,   7, 2 },	{ 1,   7, 2 },	{ 1,   7, 2 },	{ 1,   7, 2 }, },
+	{ "08",		{ 1,   0, 1 },	{ 1,   0, 1 },	{ 1,   8, 2 },	{ 1,   8, 2 },	{ 1,   0, 1 }, },
+	{ "09",		{ 1,   0, 1 },	{ 1,   0, 1 },	{ 1,   9, 2 },	{ 1,   9, 2 },	{ 1,   0, 1 }, },
+	{ "0A",		{ 1,   0, 1 },	{ 1,   0, 1 },	{ 1,   0, 1 },	{ 1,  10, 2 },	{ 1,   0, 1 }, },
+	{ "0B",		{ 1,   0, 1 },	{ 1,   0, 1 },	{ 1,   0, 1 },	{ 1,  11, 2 },	{ 1,   0, 1 }, },
+	{ "0C",		{ 1,   0, 1 },	{ 1,   0, 1 },	{ 1,   0, 1 },	{ 1,  12, 2 },	{ 1,   0, 1 }, },
+	{ "0D",		{ 1,   0, 1 },	{ 1,   0, 1 },	{ 1,   0, 1 },	{ 1,  13, 2 },	{ 1,   0, 1 }, },
+	{ "0E",		{ 1,   0, 1 },	{ 1,   0, 1 },	{ 1,   0, 1 },	{ 1,  14, 2 },	{ 1,   0, 1 }, },
+	{ "0F",		{ 1,   0, 1 },	{ 1,   0, 1 },	{ 1,   0, 1 },	{ 1,  15, 2 },	{ 1,   0, 1 }, },
+	{ "0X",		{ 1,   0, 1 },	{ 1,   0, 1 },	{ 1,   0, 1 },	{ 1,   0, 1 },	{ 1,   0, 1 }, },
+	{ "0a",		{ 1,   0, 1 },	{ 1,   0, 1 },	{ 1,   0, 1 },	{ 1,  10, 2 },	{ 1,   0, 1 }, },
+	{ "0b",		{ 1,   0, 1 },	{ 1,   0, 1 },	{ 1,   0, 1 },	{ 1,  11, 2 },	{ 1,   0, 1 }, },
+	{ "0c",		{ 1,   0, 1 },	{ 1,   0, 1 },	{ 1,   0, 1 },	{ 1,  12, 2 },	{ 1,   0, 1 }, },
+	{ "0d",		{ 1,   0, 1 },	{ 1,   0, 1 },	{ 1,   0, 1 },	{ 1,  13, 2 },	{ 1,   0, 1 }, },
+	{ "0e",		{ 1,   0, 1 },	{ 1,   0, 1 },	{ 1,   0, 1 },	{ 1,  14, 2 },	{ 1,   0, 1 }, },
+	{ "0f",		{ 1,   0, 1 },	{ 1,   0, 1 },	{ 1,   0, 1 },	{ 1,  15, 2 },	{ 1,   0, 1 }, },
+	{ "0x",		{ 1,   0, 1 },	{ 1,   0, 1 },	{ 1,   0, 1 },	{ 1,   0, 1 },	{ 1,   0, 1 }, },
+	// all digits with leading one
+	{ "10",		{ 1,   2, 2 },	{ 1,   8, 2 },	{ 1,  10, 2 },	{ 1,  16, 2 },	{ 1,  10, 2 }, },
+	{ "11",		{ 1,   3, 2 },	{ 1,   9, 2 },	{ 1,  11, 2 },	{ 1,  17, 2 },	{ 1,  11, 2 }, },
+	{ "12",		{ 1,   1, 1 },	{ 1,  10, 2 },	{ 1,  12, 2 },	{ 1,  18, 2 },	{ 1,  12, 2 }, },
+	{ "13",		{ 1,   1, 1 },	{ 1,  11, 2 },	{ 1,  13, 2 },	{ 1,  19, 2 },	{ 1,  13, 2 }, },
+	{ "14",		{ 1,   1, 1 },	{ 1,  12, 2 },	{ 1,  14, 2 },	{ 1,  20, 2 },	{ 1,  14, 2 }, },
+	{ "15",		{ 1,   1, 1 },	{ 1,  13, 2 },	{ 1,  15, 2 },	{ 1,  21, 2 },	{ 1,  15, 2 }, },
+	{ "16",		{ 1,   1, 1 },	{ 1,  14, 2 },	{ 1,  16, 2 },	{ 1,  22, 2 },	{ 1,  16, 2 }, },
+	{ "17",		{ 1,   1, 1 },	{ 1,  15, 2 },	{ 1,  17, 2 },	{ 1,  23, 2 },	{ 1,  17, 2 }, },
+	{ "18",		{ 1,   1, 1 },	{ 1,   1, 1 },	{ 1,  18, 2 },	{ 1,  24, 2 },	{ 1,  18, 2 }, },
+	{ "19",		{ 1,   1, 1 },	{ 1,   1, 1 },	{ 1,  19, 2 },	{ 1,  25, 2 },	{ 1,  19, 2 }, },
+	{ "1A",		{ 1,   1, 1 },	{ 1,   1, 1 },	{ 1,   1, 1 },	{ 1,  26, 2 },	{ 1,   1, 1 }, },
+	{ "1B",		{ 1,   1, 1 },	{ 1,   1, 1 },	{ 1,   1, 1 },	{ 1,  27, 2 },	{ 1,   1, 1 }, },
+	{ "1C",		{ 1,   1, 1 },	{ 1,   1, 1 },	{ 1,   1, 1 },	{ 1,  28, 2 },	{ 1,   1, 1 }, },
+	{ "1D",		{ 1,   1, 1 },	{ 1,   1, 1 },	{ 1,   1, 1 },	{ 1,  29, 2 },	{ 1,   1, 1 }, },
+	{ "1E",		{ 1,   1, 1 },	{ 1,   1, 1 },	{ 1,   1, 1 },	{ 1,  30, 2 },	{ 1,   1, 1 }, },
+	{ "1F",		{ 1,   1, 1 },	{ 1,   1, 1 },	{ 1,   1, 1 },	{ 1,  31, 2 },	{ 1,   1, 1 }, },
+	{ "1X",		{ 1,   1, 1 },	{ 1,   1, 1 },	{ 1,   1, 1 },	{ 1,   1, 1 },	{ 1,   1, 1 }, },
+	{ "1a",		{ 1,   1, 1 },	{ 1,   1, 1 },	{ 1,   1, 1 },	{ 1,  26, 2 },	{ 1,   1, 1 }, },
+	{ "1b",		{ 1,   1, 1 },	{ 1,   1, 1 },	{ 1,   1, 1 },	{ 1,  27, 2 },	{ 1,   1, 1 }, },
+	{ "1c",		{ 1,   1, 1 },	{ 1,   1, 1 },	{ 1,   1, 1 },	{ 1,  28, 2 },	{ 1,   1, 1 }, },
+	{ "1d",		{ 1,   1, 1 },	{ 1,   1, 1 },	{ 1,   1, 1 },	{ 1,  29, 2 },	{ 1,   1, 1 }, },
+	{ "1e",		{ 1,   1, 1 },	{ 1,   1, 1 },	{ 1,   1, 1 },	{ 1,  30, 2 },	{ 1,   1, 1 }, },
+	{ "1f",		{ 1,   1, 1 },	{ 1,   1, 1 },	{ 1,   1, 1 },	{ 1,  31, 2 },	{ 1,   1, 1 }, },
+	{ "1x",		{ 1,   1, 1 },	{ 1,   1, 1 },	{ 1,   1, 1 },	{ 1,   1, 1 },	{ 1,   1, 1 }, },
+	// all digits with leading binary prefix
+	{ "0b0",	{ 1,   0, 3 },	{ 1,   0, 1 },	{ 1,   0, 1 },	{ 1, 176, 3 },	{ 1,   0, 3 }, },
+	{ "0b1",	{ 1,   1, 3 },	{ 1,   0, 1 },	{ 1,   0, 1 },	{ 1, 177, 3 },	{ 1,   1, 3 }, },
+	{ "0b2",	{ 1,   0, 1 },	{ 1,   0, 1 },	{ 1,   0, 1 },	{ 1, 178, 3 },	{ 1,   0, 1 }, },
+	{ "0b3",	{ 1,   0, 1 },	{ 1,   0, 1 },	{ 1,   0, 1 },	{ 1, 179, 3 },	{ 1,   0, 1 }, },
+	{ "0b4",	{ 1,   0, 1 },	{ 1,   0, 1 },	{ 1,   0, 1 },	{ 1, 180, 3 },	{ 1,   0, 1 }, },
+	{ "0b5",	{ 1,   0, 1 },	{ 1,   0, 1 },	{ 1,   0, 1 },	{ 1, 181, 3 },	{ 1,   0, 1 }, },
+	{ "0b6",	{ 1,   0, 1 },	{ 1,   0, 1 },	{ 1,   0, 1 },	{ 1, 182, 3 },	{ 1,   0, 1 }, },
+	{ "0b7",	{ 1,   0, 1 },	{ 1,   0, 1 },	{ 1,   0, 1 },	{ 1, 183, 3 },	{ 1,   0, 1 }, },
+	{ "0b8",	{ 1,   0, 1 },	{ 1,   0, 1 },	{ 1,   0, 1 },	{ 1, 184, 3 },	{ 1,   0, 1 }, },
+	{ "0b9",	{ 1,   0, 1 },	{ 1,   0, 1 },	{ 1,   0, 1 },	{ 1, 185, 3 },	{ 1,   0, 1 }, },
+	{ "0bA",	{ 1,   0, 1 },	{ 1,   0, 1 },	{ 1,   0, 1 },	{ 1, 186, 3 },	{ 1,   0, 1 }, },
+	{ "0bB",	{ 1,   0, 1 },	{ 1,   0, 1 },	{ 1,   0, 1 },	{ 1, 187, 3 },	{ 1,   0, 1 }, },
+	{ "0bC",	{ 1,   0, 1 },	{ 1,   0, 1 },	{ 1,   0, 1 },	{ 1, 188, 3 },	{ 1,   0, 1 }, },
+	{ "0bD",	{ 1,   0, 1 },	{ 1,   0, 1 },	{ 1,   0, 1 },	{ 1, 189, 3 },	{ 1,   0, 1 }, },
+	{ "0bE",	{ 1,   0, 1 },	{ 1,   0, 1 },	{ 1,   0, 1 },	{ 1, 190, 3 },	{ 1,   0, 1 }, },
+	{ "0bF",	{ 1,   0, 1 },	{ 1,   0, 1 },	{ 1,   0, 1 },	{ 1, 191, 3 },	{ 1,   0, 1 }, },
+	{ "0bX",	{ 1,   0, 1 },	{ 1,   0, 1 },	{ 1,   0, 1 },	{ 1,  11, 2 },	{ 1,   0, 1 }, },
+	{ "0ba",	{ 1,   0, 1 },	{ 1,   0, 1 },	{ 1,   0, 1 },	{ 1, 186, 3 },	{ 1,   0, 1 }, },
+	{ "0bb",	{ 1,   0, 1 },	{ 1,   0, 1 },	{ 1,   0, 1 },	{ 1, 187, 3 },	{ 1,   0, 1 }, },
+	{ "0bc",	{ 1,   0, 1 },	{ 1,   0, 1 },	{ 1,   0, 1 },	{ 1, 188, 3 },	{ 1,   0, 1 }, },
+	{ "0bd",	{ 1,   0, 1 },	{ 1,   0, 1 },	{ 1,   0, 1 },	{ 1, 189, 3 },	{ 1,   0, 1 }, },
+	{ "0be",	{ 1,   0, 1 },	{ 1,   0, 1 },	{ 1,   0, 1 },	{ 1, 190, 3 },	{ 1,   0, 1 }, },
+	{ "0bf",	{ 1,   0, 1 },	{ 1,   0, 1 },	{ 1,   0, 1 },	{ 1, 191, 3 },	{ 1,   0, 1 }, },
+	{ "0bx",	{ 1,   0, 1 },	{ 1,   0, 1 },	{ 1,   0, 1 },	{ 1,  11, 2 },	{ 1,   0, 1 }, },
+	// all digits with leading hexadecimal prefix
+	{ "0x0",	{ 1,   0, 1 },	{ 1,   0, 1 },	{ 1,   0, 1 },	{ 1,   0, 3 },	{ 1,   0, 3 }, },
+	{ "0x1",	{ 1,   0, 1 },	{ 1,   0, 1 },	{ 1,   0, 1 },	{ 1,   1, 3 },	{ 1,   1, 3 }, },
+	{ "0x2",	{ 1,   0, 1 },	{ 1,   0, 1 },	{ 1,   0, 1 },	{ 1,   2, 3 },	{ 1,   2, 3 }, },
+	{ "0x3",	{ 1,   0, 1 },	{ 1,   0, 1 },	{ 1,   0, 1 },	{ 1,   3, 3 },	{ 1,   3, 3 }, },
+	{ "0x4",	{ 1,   0, 1 },	{ 1,   0, 1 },	{ 1,   0, 1 },	{ 1,   4, 3 },	{ 1,   4, 3 }, },
+	{ "0x5",	{ 1,   0, 1 },	{ 1,   0, 1 },	{ 1,   0, 1 },	{ 1,   5, 3 },	{ 1,   5, 3 }, },
+	{ "0x6",	{ 1,   0, 1 },	{ 1,   0, 1 },	{ 1,   0, 1 },	{ 1,   6, 3 },	{ 1,   6, 3 }, },
+	{ "0x7",	{ 1,   0, 1 },	{ 1,   0, 1 },	{ 1,   0, 1 },	{ 1,   7, 3 },	{ 1,   7, 3 }, },
+	{ "0x8",	{ 1,   0, 1 },	{ 1,   0, 1 },	{ 1,   0, 1 },	{ 1,   8, 3 },	{ 1,   8, 3 }, },
+	{ "0x9",	{ 1,   0, 1 },	{ 1,   0, 1 },	{ 1,   0, 1 },	{ 1,   9, 3 },	{ 1,   9, 3 }, },
+	{ "0xA",	{ 1,   0, 1 },	{ 1,   0, 1 },	{ 1,   0, 1 },	{ 1,  10, 3 },	{ 1,  10, 3 }, },
+	{ "0xB",	{ 1,   0, 1 },	{ 1,   0, 1 },	{ 1,   0, 1 },	{ 1,  11, 3 },	{ 1,  11, 3 }, },
+	{ "0xC",	{ 1,   0, 1 },	{ 1,   0, 1 },	{ 1,   0, 1 },	{ 1,  12, 3 },	{ 1,  12, 3 }, },
+	{ "0xD",	{ 1,   0, 1 },	{ 1,   0, 1 },	{ 1,   0, 1 },	{ 1,  13, 3 },	{ 1,  13, 3 }, },
+	{ "0xE",	{ 1,   0, 1 },	{ 1,   0, 1 },	{ 1,   0, 1 },	{ 1,  14, 3 },	{ 1,  14, 3 }, },
+	{ "0xF",	{ 1,   0, 1 },	{ 1,   0, 1 },	{ 1,   0, 1 },	{ 1,  15, 3 },	{ 1,  15, 3 }, },
+	{ "0xX",	{ 1,   0, 1 },	{ 1,   0, 1 },	{ 1,   0, 1 },	{ 1,   0, 1 },	{ 1,   0, 1 }, },
+	{ "0xa",	{ 1,   0, 1 },	{ 1,   0, 1 },	{ 1,   0, 1 },	{ 1,  10, 3 },	{ 1,  10, 3 }, },
+	{ "0xb",	{ 1,   0, 1 },	{ 1,   0, 1 },	{ 1,   0, 1 },	{ 1,  11, 3 },	{ 1,  11, 3 }, },
+	{ "0xc",	{ 1,   0, 1 },	{ 1,   0, 1 },	{ 1,   0, 1 },	{ 1,  12, 3 },	{ 1,  12, 3 }, },
+	{ "0xd",	{ 1,   0, 1 },	{ 1,   0, 1 },	{ 1,   0, 1 },	{ 1,  13, 3 },	{ 1,  13, 3 }, },
+	{ "0xe",	{ 1,   0, 1 },	{ 1,   0, 1 },	{ 1,   0, 1 },	{ 1,  14, 3 },	{ 1,  14, 3 }, },
+	{ "0xf",	{ 1,   0, 1 },	{ 1,   0, 1 },	{ 1,   0, 1 },	{ 1,  15, 3 },	{ 1,  15, 3 }, },
+	{ "0xX",	{ 1,   0, 1 },	{ 1,   0, 1 },	{ 1,   0, 1 },	{ 1,   0, 1 },	{ 1,   0, 1 }, },
+	// terminator
+	{ "" }
+};
+
+#define SSCANF_TEST(string, format, expret, expval, explen)		\
+	do {								\
+		int ret = 0, val = 0, len = 0;				\
+		ret = sscanf(string, format "%n", &val, &len);		\
+		ATF_CHECK_EQ(expret, ret);				\
+		if (expret && ret) {					\
+			ATF_CHECK_EQ(expval, val);			\
+			ATF_CHECK_EQ(explen, len);			\
+		}							\
+	} while (0)
+
+ATF_TC_WITHOUT_HEAD(sscanf_b);
+ATF_TC_BODY(sscanf_b, tc)
+{
+	const struct sscanf_test_case *stc;
+	char input[16];
+
+	for (stc = sscanf_test_cases; *stc->input; stc++) {
+		strcpy(input + 1, stc->input);
+		SSCANF_TEST(input + 1, "%b", stc->b.ret, stc->b.val, stc->b.len);
+		input[0] = '+';
+		SSCANF_TEST(input, "%b", stc->b.ret, stc->b.val, stc->b.len ? stc->b.len + 1 : 0);
+		input[0] = '-';
+		SSCANF_TEST(input, "%b", stc->b.ret, -stc->b.val, stc->b.len ? stc->b.len + 1 : 0);
+	}
+}
+
+ATF_TC_WITHOUT_HEAD(sscanf_o);
+ATF_TC_BODY(sscanf_o, tc)
+{
+	const struct sscanf_test_case *stc;
+	char input[16];
+
+	for (stc = sscanf_test_cases; *stc->input; stc++) {
+		strcpy(input + 1, stc->input);
+		SSCANF_TEST(input + 1, "%o", stc->o.ret, stc->o.val, stc->o.len);
+		input[0] = '+';
+		SSCANF_TEST(input, "%o", stc->o.ret, stc->o.val, stc->o.len ? stc->o.len + 1 : 0);
+		input[0] = '-';
+		SSCANF_TEST(input, "%o", stc->o.ret, -stc->o.val, stc->o.len ? stc->o.len + 1 : 0);
+	}
+}
+
+ATF_TC_WITHOUT_HEAD(sscanf_d);
+ATF_TC_BODY(sscanf_d, tc)
+{
+	const struct sscanf_test_case *stc;
+	char input[16];
+
+	for (stc = sscanf_test_cases; *stc->input; stc++) {
+		strcpy(input + 1, stc->input);
+		SSCANF_TEST(input + 1, "%d", stc->d.ret, stc->d.val, stc->d.len);
+		input[0] = '+';
+		SSCANF_TEST(input, "%d", stc->d.ret, stc->d.val, stc->d.len ? stc->d.len + 1 : 0);
+		input[0] = '-';
+		SSCANF_TEST(input, "%d", stc->d.ret, -stc->d.val, stc->d.len ? stc->d.len + 1 : 0);
+	}
+}
+
+ATF_TC_WITHOUT_HEAD(sscanf_x);
+ATF_TC_BODY(sscanf_x, tc)
+{
+	const struct sscanf_test_case *stc;
+	char input[16];
+
+	for (stc = sscanf_test_cases; *stc->input; stc++) {
+		strcpy(input + 1, stc->input);
+		SSCANF_TEST(input + 1, "%x", stc->x.ret, stc->x.val, stc->x.len);
+		input[0] = '+';
+		SSCANF_TEST(input, "%x", stc->x.ret, stc->x.val, stc->x.len ? stc->x.len + 1 : 0);
+		input[0] = '-';
+		SSCANF_TEST(input, "%x", stc->x.ret, -stc->x.val, stc->x.len ? stc->x.len + 1 : 0);
+	}
+}
+
+ATF_TC_WITHOUT_HEAD(sscanf_i);
+ATF_TC_BODY(sscanf_i, tc)
+{
+	const struct sscanf_test_case *stc;
+	char input[16];
+
+	for (stc = sscanf_test_cases; *stc->input; stc++) {
+		strcpy(input + 1, stc->input);
+		SSCANF_TEST(input + 1, "%i", stc->i.ret, stc->i.val, stc->i.len);
+		input[0] = '+';
+		SSCANF_TEST(input, "%i", stc->i.ret, stc->i.val, stc->i.len ? stc->i.len + 1 : 0);
+		input[0] = '-';
+		SSCANF_TEST(input, "%i", stc->i.ret, -stc->i.val, stc->i.len ? stc->i.len + 1 : 0);
+	}
+}
+
+ATF_TP_ADD_TCS(tp)
+{
+	setlocale(LC_NUMERIC, "en_US.UTF-8");
+	ATF_TP_ADD_TC(tp, sscanf_b);
+	ATF_TP_ADD_TC(tp, sscanf_o);
+	ATF_TP_ADD_TC(tp, sscanf_d);
+	ATF_TP_ADD_TC(tp, sscanf_x);
+	ATF_TP_ADD_TC(tp, sscanf_i);
+	return (atf_no_error());
+}
diff --git a/lib/libc/tests/stdio/swscanf_test.c b/lib/libc/tests/stdio/swscanf_test.c
new file mode 100644
index 000000000000..391a54e0ed5e
--- /dev/null
+++ b/lib/libc/tests/stdio/swscanf_test.c
@@ -0,0 +1,250 @@
+/*-
+ * Copyright (c) 2023 Dag-Erling Smørgrav
+ *
+ * SPDX-License-Identifier: BSD-2-Clause
+ */
+
+#include <locale.h>
+#include <stdint.h>
+#include <stdio.h>
+#include <wchar.h>
+
+#include <atf-c.h>
+
+static const struct swscanf_test_case {
+	wchar_t input[8];
+	struct {
+		int ret, val, len;
+	} b, o, d, x, i;
+} swscanf_test_cases[] = {
+//	input		binary		octal		decimal		hexadecimal	automatic
+	// all digits
+	{ L"0",		{ 1,   0, 1 },	{ 1,   0, 1 },	{ 1,   0, 1 },	{ 1,   0, 1 },	{ 1,   0, 1 }, },
+	{ L"1",		{ 1,   1, 1 },	{ 1,   1, 1 },	{ 1,   1, 1 },	{ 1,   1, 1 },	{ 1,   1, 1 }, },
+	{ L"2",		{ 0,   0, 0 },	{ 1,   2, 1 },	{ 1,   2, 1 },	{ 1,   2, 1 },	{ 1,   2, 1 }, },
+	{ L"3",		{ 0,   0, 0 },	{ 1,   3, 1 },	{ 1,   3, 1 },	{ 1,   3, 1 },	{ 1,   3, 1 }, },
+	{ L"4",		{ 0,   0, 0 },	{ 1,   4, 1 },	{ 1,   4, 1 },	{ 1,   4, 1 },	{ 1,   4, 1 }, },
+	{ L"5",		{ 0,   0, 0 },	{ 1,   5, 1 },	{ 1,   5, 1 },	{ 1,   5, 1 },	{ 1,   5, 1 }, },
+	{ L"6",		{ 0,   0, 0 },	{ 1,   6, 1 },	{ 1,   6, 1 },	{ 1,   6, 1 },	{ 1,   6, 1 }, },
+	{ L"7",		{ 0,   0, 0 },	{ 1,   7, 1 },	{ 1,   7, 1 },	{ 1,   7, 1 },	{ 1,   7, 1 }, },
+	{ L"8",		{ 0,   0, 0 },	{ 0,   0, 0 },	{ 1,   8, 1 },	{ 1,   8, 1 },	{ 1,   8, 1 }, },
+	{ L"9",		{ 0,   0, 0 },	{ 0,   0, 0 },	{ 1,   9, 1 },	{ 1,   9, 1 },	{ 1,   9, 1 }, },
+	{ L"A",		{ 0,   0, 0 },	{ 0,   0, 0 },	{ 0,   0, 0 },	{ 1,  10, 1 },	{ 0,   0, 0 }, },
+	{ L"B",		{ 0,   0, 0 },	{ 0,   0, 0 },	{ 0,   0, 0 },	{ 1,  11, 1 },	{ 0,   0, 0 }, },
+	{ L"C",		{ 0,   0, 0 },	{ 0,   0, 0 },	{ 0,   0, 0 },	{ 1,  12, 1 },	{ 0,   0, 0 }, },
+	{ L"D",		{ 0,   0, 0 },	{ 0,   0, 0 },	{ 0,   0, 0 },	{ 1,  13, 1 },	{ 0,   0, 0 }, },
+	{ L"E",		{ 0,   0, 0 },	{ 0,   0, 0 },	{ 0,   0, 0 },	{ 1,  14, 1 },	{ 0,   0, 0 }, },
+	{ L"F",		{ 0,   0, 0 },	{ 0,   0, 0 },	{ 0,   0, 0 },	{ 1,  15, 1 },	{ 0,   0, 0 }, },
+	{ L"X",		{ 0,   0, 0 },	{ 0,   0, 0 },	{ 0,   0, 0 },	{ 0,   0, 0 },	{ 0,   0, 0 }, },
+	{ L"a",		{ 0,   0, 0 },	{ 0,   0, 0 },	{ 0,   0, 0 },	{ 1,  10, 1 },	{ 0,   0, 0 }, },
+	{ L"b",		{ 0,   0, 0 },	{ 0,   0, 0 },	{ 0,   0, 0 },	{ 1,  11, 1 },	{ 0,   0, 0 }, },
+	{ L"c",		{ 0,   0, 0 },	{ 0,   0, 0 },	{ 0,   0, 0 },	{ 1,  12, 1 },	{ 0,   0, 0 }, },
+	{ L"d",		{ 0,   0, 0 },	{ 0,   0, 0 },	{ 0,   0, 0 },	{ 1,  13, 1 },	{ 0,   0, 0 }, },
+	{ L"e",		{ 0,   0, 0 },	{ 0,   0, 0 },	{ 0,   0, 0 },	{ 1,  14, 1 },	{ 0,   0, 0 }, },
+	{ L"f",		{ 0,   0, 0 },	{ 0,   0, 0 },	{ 0,   0, 0 },	{ 1,  15, 1 },	{ 0,   0, 0 }, },
+	{ L"x",		{ 0,   0, 0 },	{ 0,   0, 0 },	{ 0,   0, 0 },	{ 0,   0, 0 },	{ 0,   0, 0 }, },
+	// all digits with leading zero
+	{ L"00",	{ 1,   0, 2 },	{ 1,   0, 2 },	{ 1,   0, 2 },	{ 1,   0, 2 },	{ 1,   0, 2 }, },
+	{ L"01",	{ 1,   1, 2 },	{ 1,   1, 2 },	{ 1,   1, 2 },	{ 1,   1, 2 },	{ 1,   1, 2 }, },
+	{ L"02",	{ 1,   0, 1 },	{ 1,   2, 2 },	{ 1,   2, 2 },	{ 1,   2, 2 },	{ 1,   2, 2 }, },
+	{ L"03",	{ 1,   0, 1 },	{ 1,   3, 2 },	{ 1,   3, 2 },	{ 1,   3, 2 },	{ 1,   3, 2 }, },
+	{ L"04",	{ 1,   0, 1 },	{ 1,   4, 2 },	{ 1,   4, 2 },	{ 1,   4, 2 },	{ 1,   4, 2 }, },
+	{ L"05",	{ 1,   0, 1 },	{ 1,   5, 2 },	{ 1,   5, 2 },	{ 1,   5, 2 },	{ 1,   5, 2 }, },
+	{ L"06",	{ 1,   0, 1 },	{ 1,   6, 2 },	{ 1,   6, 2 },	{ 1,   6, 2 },	{ 1,   6, 2 }, },
+	{ L"07",	{ 1,   0, 1 },	{ 1,   7, 2 },	{ 1,   7, 2 },	{ 1,   7, 2 },	{ 1,   7, 2 }, },
+	{ L"08",	{ 1,   0, 1 },	{ 1,   0, 1 },	{ 1,   8, 2 },	{ 1,   8, 2 },	{ 1,   0, 1 }, },
+	{ L"09",	{ 1,   0, 1 },	{ 1,   0, 1 },	{ 1,   9, 2 },	{ 1,   9, 2 },	{ 1,   0, 1 }, },
+	{ L"0A",	{ 1,   0, 1 },	{ 1,   0, 1 },	{ 1,   0, 1 },	{ 1,  10, 2 },	{ 1,   0, 1 }, },
+	{ L"0B",	{ 1,   0, 1 },	{ 1,   0, 1 },	{ 1,   0, 1 },	{ 1,  11, 2 },	{ 1,   0, 1 }, },
+	{ L"0C",	{ 1,   0, 1 },	{ 1,   0, 1 },	{ 1,   0, 1 },	{ 1,  12, 2 },	{ 1,   0, 1 }, },
+	{ L"0D",	{ 1,   0, 1 },	{ 1,   0, 1 },	{ 1,   0, 1 },	{ 1,  13, 2 },	{ 1,   0, 1 }, },
+	{ L"0E",	{ 1,   0, 1 },	{ 1,   0, 1 },	{ 1,   0, 1 },	{ 1,  14, 2 },	{ 1,   0, 1 }, },
+	{ L"0F",	{ 1,   0, 1 },	{ 1,   0, 1 },	{ 1,   0, 1 },	{ 1,  15, 2 },	{ 1,   0, 1 }, },
+	{ L"0X",	{ 1,   0, 1 },	{ 1,   0, 1 },	{ 1,   0, 1 },	{ 1,   0, 1 },	{ 1,   0, 1 }, },
+	{ L"0a",	{ 1,   0, 1 },	{ 1,   0, 1 },	{ 1,   0, 1 },	{ 1,  10, 2 },	{ 1,   0, 1 }, },
+	{ L"0b",	{ 1,   0, 1 },	{ 1,   0, 1 },	{ 1,   0, 1 },	{ 1,  11, 2 },	{ 1,   0, 1 }, },
+	{ L"0c",	{ 1,   0, 1 },	{ 1,   0, 1 },	{ 1,   0, 1 },	{ 1,  12, 2 },	{ 1,   0, 1 }, },
+	{ L"0d",	{ 1,   0, 1 },	{ 1,   0, 1 },	{ 1,   0, 1 },	{ 1,  13, 2 },	{ 1,   0, 1 }, },
+	{ L"0e",	{ 1,   0, 1 },	{ 1,   0, 1 },	{ 1,   0, 1 },	{ 1,  14, 2 },	{ 1,   0, 1 }, },
+	{ L"0f",	{ 1,   0, 1 },	{ 1,   0, 1 },	{ 1,   0, 1 },	{ 1,  15, 2 },	{ 1,   0, 1 }, },
+	{ L"0x",	{ 1,   0, 1 },	{ 1,   0, 1 },	{ 1,   0, 1 },	{ 1,   0, 1 },	{ 1,   0, 1 }, },
+	// all digits with leading one
+	{ L"10",	{ 1,   2, 2 },	{ 1,   8, 2 },	{ 1,  10, 2 },	{ 1,  16, 2 },	{ 1,  10, 2 }, },
+	{ L"11",	{ 1,   3, 2 },	{ 1,   9, 2 },	{ 1,  11, 2 },	{ 1,  17, 2 },	{ 1,  11, 2 }, },
+	{ L"12",	{ 1,   1, 1 },	{ 1,  10, 2 },	{ 1,  12, 2 },	{ 1,  18, 2 },	{ 1,  12, 2 }, },
+	{ L"13",	{ 1,   1, 1 },	{ 1,  11, 2 },	{ 1,  13, 2 },	{ 1,  19, 2 },	{ 1,  13, 2 }, },
+	{ L"14",	{ 1,   1, 1 },	{ 1,  12, 2 },	{ 1,  14, 2 },	{ 1,  20, 2 },	{ 1,  14, 2 }, },
+	{ L"15",	{ 1,   1, 1 },	{ 1,  13, 2 },	{ 1,  15, 2 },	{ 1,  21, 2 },	{ 1,  15, 2 }, },
+	{ L"16",	{ 1,   1, 1 },	{ 1,  14, 2 },	{ 1,  16, 2 },	{ 1,  22, 2 },	{ 1,  16, 2 }, },
+	{ L"17",	{ 1,   1, 1 },	{ 1,  15, 2 },	{ 1,  17, 2 },	{ 1,  23, 2 },	{ 1,  17, 2 }, },
+	{ L"18",	{ 1,   1, 1 },	{ 1,   1, 1 },	{ 1,  18, 2 },	{ 1,  24, 2 },	{ 1,  18, 2 }, },
+	{ L"19",	{ 1,   1, 1 },	{ 1,   1, 1 },	{ 1,  19, 2 },	{ 1,  25, 2 },	{ 1,  19, 2 }, },
+	{ L"1A",	{ 1,   1, 1 },	{ 1,   1, 1 },	{ 1,   1, 1 },	{ 1,  26, 2 },	{ 1,   1, 1 }, },
+	{ L"1B",	{ 1,   1, 1 },	{ 1,   1, 1 },	{ 1,   1, 1 },	{ 1,  27, 2 },	{ 1,   1, 1 }, },
+	{ L"1C",	{ 1,   1, 1 },	{ 1,   1, 1 },	{ 1,   1, 1 },	{ 1,  28, 2 },	{ 1,   1, 1 }, },
+	{ L"1D",	{ 1,   1, 1 },	{ 1,   1, 1 },	{ 1,   1, 1 },	{ 1,  29, 2 },	{ 1,   1, 1 }, },
+	{ L"1E",	{ 1,   1, 1 },	{ 1,   1, 1 },	{ 1,   1, 1 },	{ 1,  30, 2 },	{ 1,   1, 1 }, },
+	{ L"1F",	{ 1,   1, 1 },	{ 1,   1, 1 },	{ 1,   1, 1 },	{ 1,  31, 2 },	{ 1,   1, 1 }, },
+	{ L"1X",	{ 1,   1, 1 },	{ 1,   1, 1 },	{ 1,   1, 1 },	{ 1,   1, 1 },	{ 1,   1, 1 }, },
+	{ L"1a",	{ 1,   1, 1 },	{ 1,   1, 1 },	{ 1,   1, 1 },	{ 1,  26, 2 },	{ 1,   1, 1 }, },
+	{ L"1b",	{ 1,   1, 1 },	{ 1,   1, 1 },	{ 1,   1, 1 },	{ 1,  27, 2 },	{ 1,   1, 1 }, },
+	{ L"1c",	{ 1,   1, 1 },	{ 1,   1, 1 },	{ 1,   1, 1 },	{ 1,  28, 2 },	{ 1,   1, 1 }, },
+	{ L"1d",	{ 1,   1, 1 },	{ 1,   1, 1 },	{ 1,   1, 1 },	{ 1,  29, 2 },	{ 1,   1, 1 }, },
+	{ L"1e",	{ 1,   1, 1 },	{ 1,   1, 1 },	{ 1,   1, 1 },	{ 1,  30, 2 },	{ 1,   1, 1 }, },
+	{ L"1f",	{ 1,   1, 1 },	{ 1,   1, 1 },	{ 1,   1, 1 },	{ 1,  31, 2 },	{ 1,   1, 1 }, },
+	{ L"1x",	{ 1,   1, 1 },	{ 1,   1, 1 },	{ 1,   1, 1 },	{ 1,   1, 1 },	{ 1,   1, 1 }, },
+	// all digits with leading binary prefix
+	{ L"0b0",	{ 1,   0, 3 },	{ 1,   0, 1 },	{ 1,   0, 1 },	{ 1, 176, 3 },	{ 1,   0, 3 }, },
+	{ L"0b1",	{ 1,   1, 3 },	{ 1,   0, 1 },	{ 1,   0, 1 },	{ 1, 177, 3 },	{ 1,   1, 3 }, },
+	{ L"0b2",	{ 1,   0, 1 },	{ 1,   0, 1 },	{ 1,   0, 1 },	{ 1, 178, 3 },	{ 1,   0, 1 }, },
+	{ L"0b3",	{ 1,   0, 1 },	{ 1,   0, 1 },	{ 1,   0, 1 },	{ 1, 179, 3 },	{ 1,   0, 1 }, },
+	{ L"0b4",	{ 1,   0, 1 },	{ 1,   0, 1 },	{ 1,   0, 1 },	{ 1, 180, 3 },	{ 1,   0, 1 }, },
+	{ L"0b5",	{ 1,   0, 1 },	{ 1,   0, 1 },	{ 1,   0, 1 },	{ 1, 181, 3 },	{ 1,   0, 1 }, },
+	{ L"0b6",	{ 1,   0, 1 },	{ 1,   0, 1 },	{ 1,   0, 1 },	{ 1, 182, 3 },	{ 1,   0, 1 }, },
+	{ L"0b7",	{ 1,   0, 1 },	{ 1,   0, 1 },	{ 1,   0, 1 },	{ 1, 183, 3 },	{ 1,   0, 1 }, },
+	{ L"0b8",	{ 1,   0, 1 },	{ 1,   0, 1 },	{ 1,   0, 1 },	{ 1, 184, 3 },	{ 1,   0, 1 }, },
+	{ L"0b9",	{ 1,   0, 1 },	{ 1,   0, 1 },	{ 1,   0, 1 },	{ 1, 185, 3 },	{ 1,   0, 1 }, },
+	{ L"0bA",	{ 1,   0, 1 },	{ 1,   0, 1 },	{ 1,   0, 1 },	{ 1, 186, 3 },	{ 1,   0, 1 }, },
+	{ L"0bB",	{ 1,   0, 1 },	{ 1,   0, 1 },	{ 1,   0, 1 },	{ 1, 187, 3 },	{ 1,   0, 1 }, },
+	{ L"0bC",	{ 1,   0, 1 },	{ 1,   0, 1 },	{ 1,   0, 1 },	{ 1, 188, 3 },	{ 1,   0, 1 }, },
+	{ L"0bD",	{ 1,   0, 1 },	{ 1,   0, 1 },	{ 1,   0, 1 },	{ 1, 189, 3 },	{ 1,   0, 1 }, },
+	{ L"0bE",	{ 1,   0, 1 },	{ 1,   0, 1 },	{ 1,   0, 1 },	{ 1, 190, 3 },	{ 1,   0, 1 }, },
+	{ L"0bF",	{ 1,   0, 1 },	{ 1,   0, 1 },	{ 1,   0, 1 },	{ 1, 191, 3 },	{ 1,   0, 1 }, },
+	{ L"0bX",	{ 1,   0, 1 },	{ 1,   0, 1 },	{ 1,   0, 1 },	{ 1,  11, 2 },	{ 1,   0, 1 }, },
+	{ L"0ba",	{ 1,   0, 1 },	{ 1,   0, 1 },	{ 1,   0, 1 },	{ 1, 186, 3 },	{ 1,   0, 1 }, },
+	{ L"0bb",	{ 1,   0, 1 },	{ 1,   0, 1 },	{ 1,   0, 1 },	{ 1, 187, 3 },	{ 1,   0, 1 }, },
+	{ L"0bc",	{ 1,   0, 1 },	{ 1,   0, 1 },	{ 1,   0, 1 },	{ 1, 188, 3 },	{ 1,   0, 1 }, },
+	{ L"0bd",	{ 1,   0, 1 },	{ 1,   0, 1 },	{ 1,   0, 1 },	{ 1, 189, 3 },	{ 1,   0, 1 }, },
+	{ L"0be",	{ 1,   0, 1 },	{ 1,   0, 1 },	{ 1,   0, 1 },	{ 1, 190, 3 },	{ 1,   0, 1 }, },
+	{ L"0bf",	{ 1,   0, 1 },	{ 1,   0, 1 },	{ 1,   0, 1 },	{ 1, 191, 3 },	{ 1,   0, 1 }, },
+	{ L"0bx",	{ 1,   0, 1 },	{ 1,   0, 1 },	{ 1,   0, 1 },	{ 1,  11, 2 },	{ 1,   0, 1 }, },
+	// all digits with leading hexadecimal prefix
+	{ L"0x0",	{ 1,   0, 1 },	{ 1,   0, 1 },	{ 1,   0, 1 },	{ 1,   0, 3 },	{ 1,   0, 3 }, },
+	{ L"0x1",	{ 1,   0, 1 },	{ 1,   0, 1 },	{ 1,   0, 1 },	{ 1,   1, 3 },	{ 1,   1, 3 }, },
+	{ L"0x2",	{ 1,   0, 1 },	{ 1,   0, 1 },	{ 1,   0, 1 },	{ 1,   2, 3 },	{ 1,   2, 3 }, },
+	{ L"0x3",	{ 1,   0, 1 },	{ 1,   0, 1 },	{ 1,   0, 1 },	{ 1,   3, 3 },	{ 1,   3, 3 }, },
+	{ L"0x4",	{ 1,   0, 1 },	{ 1,   0, 1 },	{ 1,   0, 1 },	{ 1,   4, 3 },	{ 1,   4, 3 }, },
+	{ L"0x5",	{ 1,   0, 1 },	{ 1,   0, 1 },	{ 1,   0, 1 },	{ 1,   5, 3 },	{ 1,   5, 3 }, },
+	{ L"0x6",	{ 1,   0, 1 },	{ 1,   0, 1 },	{ 1,   0, 1 },	{ 1,   6, 3 },	{ 1,   6, 3 }, },
+	{ L"0x7",	{ 1,   0, 1 },	{ 1,   0, 1 },	{ 1,   0, 1 },	{ 1,   7, 3 },	{ 1,   7, 3 }, },
+	{ L"0x8",	{ 1,   0, 1 },	{ 1,   0, 1 },	{ 1,   0, 1 },	{ 1,   8, 3 },	{ 1,   8, 3 }, },
+	{ L"0x9",	{ 1,   0, 1 },	{ 1,   0, 1 },	{ 1,   0, 1 },	{ 1,   9, 3 },	{ 1,   9, 3 }, },
+	{ L"0xA",	{ 1,   0, 1 },	{ 1,   0, 1 },	{ 1,   0, 1 },	{ 1,  10, 3 },	{ 1,  10, 3 }, },
+	{ L"0xB",	{ 1,   0, 1 },	{ 1,   0, 1 },	{ 1,   0, 1 },	{ 1,  11, 3 },	{ 1,  11, 3 }, },
+	{ L"0xC",	{ 1,   0, 1 },	{ 1,   0, 1 },	{ 1,   0, 1 },	{ 1,  12, 3 },	{ 1,  12, 3 }, },
+	{ L"0xD",	{ 1,   0, 1 },	{ 1,   0, 1 },	{ 1,   0, 1 },	{ 1,  13, 3 },	{ 1,  13, 3 }, },
+	{ L"0xE",	{ 1,   0, 1 },	{ 1,   0, 1 },	{ 1,   0, 1 },	{ 1,  14, 3 },	{ 1,  14, 3 }, },
+	{ L"0xF",	{ 1,   0, 1 },	{ 1,   0, 1 },	{ 1,   0, 1 },	{ 1,  15, 3 },	{ 1,  15, 3 }, },
+	{ L"0xX",	{ 1,   0, 1 },	{ 1,   0, 1 },	{ 1,   0, 1 },	{ 1,   0, 1 },	{ 1,   0, 1 }, },
+	{ L"0xa",	{ 1,   0, 1 },	{ 1,   0, 1 },	{ 1,   0, 1 },	{ 1,  10, 3 },	{ 1,  10, 3 }, },
+	{ L"0xb",	{ 1,   0, 1 },	{ 1,   0, 1 },	{ 1,   0, 1 },	{ 1,  11, 3 },	{ 1,  11, 3 }, },
+	{ L"0xc",	{ 1,   0, 1 },	{ 1,   0, 1 },	{ 1,   0, 1 },	{ 1,  12, 3 },	{ 1,  12, 3 }, },
+	{ L"0xd",	{ 1,   0, 1 },	{ 1,   0, 1 },	{ 1,   0, 1 },	{ 1,  13, 3 },	{ 1,  13, 3 }, },
+	{ L"0xe",	{ 1,   0, 1 },	{ 1,   0, 1 },	{ 1,   0, 1 },	{ 1,  14, 3 },	{ 1,  14, 3 }, },
+	{ L"0xf",	{ 1,   0, 1 },	{ 1,   0, 1 },	{ 1,   0, 1 },	{ 1,  15, 3 },	{ 1,  15, 3 }, },
+	{ L"0xX",	{ 1,   0, 1 },	{ 1,   0, 1 },	{ 1,   0, 1 },	{ 1,   0, 1 },	{ 1,   0, 1 }, },
+	// terminator
+	{ L"" }
+};
+
+#define SWSCANF_TEST(string, format, expret, expval, explen)		\
+	do {								\
+		int ret = 0, val = 0, len = 0;				\
+		ret = swscanf(string, format "%n", &val, &len);		\
+		ATF_CHECK_EQ(expret, ret);				\
+		if (expret && ret) {					\
+			ATF_CHECK_EQ(expval, val);			\
+			ATF_CHECK_EQ(explen, len);			\
+		}							\
+	} while (0)
+
+ATF_TC_WITHOUT_HEAD(swscanf_b);
+ATF_TC_BODY(swscanf_b, tc)
+{
+	const struct swscanf_test_case *stc;
+	wchar_t input[16];
+
+	for (stc = swscanf_test_cases; *stc->input; stc++) {
+		wcscpy(input + 1, stc->input);
+		SWSCANF_TEST(input + 1, L"%b", stc->b.ret, stc->b.val, stc->b.len);
+		input[0] = L'+';
+		SWSCANF_TEST(input, L"%b", stc->b.ret, stc->b.val, stc->b.len ? stc->b.len + 1 : 0);
+		input[0] = L'-';
+		SWSCANF_TEST(input, L"%b", stc->b.ret, -stc->b.val, stc->b.len ? stc->b.len + 1 : 0);
+	}
+}
+
+ATF_TC_WITHOUT_HEAD(swscanf_o);
+ATF_TC_BODY(swscanf_o, tc)
+{
+	const struct swscanf_test_case *stc;
+	wchar_t input[16];
+
+	for (stc = swscanf_test_cases; *stc->input; stc++) {
+		wcscpy(input + 1, stc->input);
+		SWSCANF_TEST(input + 1, L"%o", stc->o.ret, stc->o.val, stc->o.len);
+		input[0] = L'+';
+		SWSCANF_TEST(input, L"%o", stc->o.ret, stc->o.val, stc->o.len ? stc->o.len + 1 : 0);
+		input[0] = L'-';
+		SWSCANF_TEST(input, L"%o", stc->o.ret, -stc->o.val, stc->o.len ? stc->o.len + 1 : 0);
+	}
+}
+
+ATF_TC_WITHOUT_HEAD(swscanf_d);
+ATF_TC_BODY(swscanf_d, tc)
+{
+	const struct swscanf_test_case *stc;
+	wchar_t input[16];
+
+	for (stc = swscanf_test_cases; *stc->input; stc++) {
+		wcscpy(input + 1, stc->input);
+		SWSCANF_TEST(input + 1, L"%d", stc->d.ret, stc->d.val, stc->d.len);
+		input[0] = L'+';
+		SWSCANF_TEST(input, L"%d", stc->d.ret, stc->d.val, stc->d.len ? stc->d.len + 1 : 0);
+		input[0] = L'-';
+		SWSCANF_TEST(input, L"%d", stc->d.ret, -stc->d.val, stc->d.len ? stc->d.len + 1 : 0);
+	}
+}
+
+ATF_TC_WITHOUT_HEAD(swscanf_x);
+ATF_TC_BODY(swscanf_x, tc)
+{
+	const struct swscanf_test_case *stc;
+	wchar_t input[16];
+
+	for (stc = swscanf_test_cases; *stc->input; stc++) {
+		wcscpy(input + 1, stc->input);
+		SWSCANF_TEST(input + 1, L"%x", stc->x.ret, stc->x.val, stc->x.len);
+		input[0] = L'+';
+		SWSCANF_TEST(input, L"%x", stc->x.ret, stc->x.val, stc->x.len ? stc->x.len + 1 : 0);
+		input[0] = L'-';
+		SWSCANF_TEST(input, L"%x", stc->x.ret, -stc->x.val, stc->x.len ? stc->x.len + 1 : 0);
+	}
+}
+
+ATF_TC_WITHOUT_HEAD(swscanf_i);
+ATF_TC_BODY(swscanf_i, tc)
+{
+	const struct swscanf_test_case *stc;
+	wchar_t input[16];
+
+	for (stc = swscanf_test_cases; *stc->input; stc++) {
+		wcscpy(input + 1, stc->input);
+		SWSCANF_TEST(input + 1, L"%i", stc->i.ret, stc->i.val, stc->i.len);
+		input[0] = L'+';
+		SWSCANF_TEST(input, L"%i", stc->i.ret, stc->i.val, stc->i.len ? stc->i.len + 1 : 0);
+		input[0] = L'-';
+		SWSCANF_TEST(input, L"%i", stc->i.ret, -stc->i.val, stc->i.len ? stc->i.len + 1 : 0);
+	}
+}
+
+ATF_TP_ADD_TCS(tp)
+{
+	setlocale(LC_NUMERIC, "en_US.UTF-8");
+	ATF_TP_ADD_TC(tp, swscanf_b);
+	ATF_TP_ADD_TC(tp, swscanf_o);
+	ATF_TP_ADD_TC(tp, swscanf_d);
+	ATF_TP_ADD_TC(tp, swscanf_x);
+	ATF_TP_ADD_TC(tp, swscanf_i);
+	return (atf_no_error());
+}