The implementation is limited and only supports numeric arguments.
Signed-off-by: Thomas Weißschuh linux@weissschuh.net --- Changes in v2: - Return __LINE__ from different testcases to directly point to the failed testcase - Add some comments - Expand commit message - Link to v1: https://lore.kernel.org/r/20240731-nolibc-scanf-v1-0-f71bcc4abb9e@weissschuh...
--- Thomas Weißschuh (2): tools/nolibc: add support for [v]sscanf() Revert "selftests: kselftest: Fix build failure with NOLIBC"
tools/include/nolibc/stdio.h | 98 ++++++++++++++++++++++++++++ tools/testing/selftests/kselftest.h | 5 -- tools/testing/selftests/nolibc/nolibc-test.c | 68 +++++++++++++++++++ 3 files changed, 166 insertions(+), 5 deletions(-) --- base-commit: 665fa8dea90d9fbc0e7137c7e1315d6f7e15757e change-id: 20240414-nolibc-scanf-f1db6930d0c6
Best regards,
These functions are used often, also in selftests. sscanf() itself is also used by kselftest.h itself.
The implementation is limited and only supports numeric arguments.
Signed-off-by: Thomas Weißschuh linux@weissschuh.net --- tools/include/nolibc/stdio.h | 98 ++++++++++++++++++++++++++++ tools/testing/selftests/nolibc/nolibc-test.c | 68 +++++++++++++++++++ 2 files changed, 166 insertions(+)
diff --git a/tools/include/nolibc/stdio.h b/tools/include/nolibc/stdio.h index 3892034198dd566d21a5cc0a9f67cf097d428393..a403351dbf6098ac8292b9589ed8a054b4c5669f 100644 --- a/tools/include/nolibc/stdio.h +++ b/tools/include/nolibc/stdio.h @@ -349,6 +349,104 @@ int printf(const char *fmt, ...) return ret; }
+static __attribute__((unused)) +int vsscanf(const char *str, const char *format, va_list args) +{ + uintmax_t uval; + intmax_t ival; + int base; + char *endptr; + int matches; + int lpref; + + matches = 0; + + while (1) { + if (*format == '%') { + /* start of pattern */ + lpref = 0; + format++; + + if (*format == 'l') { + /* same as in printf() */ + lpref = 1; + format++; + if (*format == 'l') { + lpref = 2; + format++; + } + } + + if (*format == '%') { + /* literal % */ + if ('%' != *str) + goto done; + str++; + format++; + continue; + } else if (*format == 'd') { + ival = strtoll(str, &endptr, 10); + if (lpref == 0) + *va_arg(args, int *) = ival; + else if (lpref == 1) + *va_arg(args, long *) = ival; + else if (lpref == 2) + *va_arg(args, long long *) = ival; + } else if (*format == 'u' || *format == 'x' || *format == 'X') { + base = *format == 'u' ? 10 : 16; + uval = strtoull(str, &endptr, base); + if (lpref == 0) + *va_arg(args, unsigned int *) = uval; + else if (lpref == 1) + *va_arg(args, unsigned long *) = uval; + else if (lpref == 2) + *va_arg(args, unsigned long long *) = uval; + } else if (*format == 'p') { + *va_arg(args, void **) = (void *)strtoul(str, &endptr, 16); + } else { + SET_ERRNO(EILSEQ); + goto done; + } + + format++; + str = endptr; + matches++; + + } else if (*format == '\0') { + goto done; + } else if (isspace(*format)) { + /* skip spaces in format and str */ + while (isspace(*format)) + format++; + while (isspace(*str)) + str++; + } else if (*format == *str) { + /* literal match */ + format++; + str++; + } else { + if (!matches) + matches = EOF; + goto done; + } + } + +done: + return matches; +} + +static __attribute__((unused, format(scanf, 2, 3))) +int sscanf(const char *str, const char *format, ...) +{ + va_list args; + int ret; + + va_start(args, format); + ret = vsscanf(str, format, args); + va_end(args); + return ret; +} + static __attribute__((unused)) void perror(const char *msg) { diff --git a/tools/testing/selftests/nolibc/nolibc-test.c b/tools/testing/selftests/nolibc/nolibc-test.c index 798fbdcd3ff8c36b514feb3fa1c7b8d7701cccd7..81e942c7d1684a7dd59af0b05c8330378532ecc1 100644 --- a/tools/testing/selftests/nolibc/nolibc-test.c +++ b/tools/testing/selftests/nolibc/nolibc-test.c @@ -1325,6 +1325,73 @@ static int expect_vfprintf(int llen, int c, const char *expected, const char *fm return ret; }
+static int test_scanf(void) +{ + unsigned long long ull; + unsigned long ul; + unsigned int u; + long long ll; + long l; + void *p; + int i; + + /* return __LINE__ to point to the specific failure */ + + /* test EOF */ + if (sscanf("", "foo") != EOF) + return __LINE__; + + /* test simple literal without placeholder */ + if (sscanf("foo", "foo") != 0) + return __LINE__; + + /* test single placeholder */ + if (sscanf("123", "%d", &i) != 1) + return __LINE__; + + if (i != 123) + return __LINE__; + + /* test multiple place holders and separators */ + if (sscanf("a123b456c0x90", "a%db%uc%p", &i, &u, &p) != 3) + return __LINE__; + + if (i != 123) + return __LINE__; + + if (u != 456) + return __LINE__; + + if (p != (void *)0x90) + return __LINE__; + + /* test space handling */ + if (sscanf("a b1", "a b%d", &i) != 1) + return __LINE__; + + if (i != 1) + return __LINE__; + + /* test literal percent */ + if (sscanf("a%1", "a%%%d", &i) != 1) + return __LINE__; + + if (i != 1) + return __LINE__; + + /* test stdint.h types */ + if (sscanf("1|2|3|4|5|6", + "%d|%ld|%lld|%u|%lu|%llu", + &i, &l, &ll, &u, &ul, &ull) != 6) + return __LINE__; + + if (i != 1 || l != 2 || ll != 3 || + u != 4 || ul != 5 || ull != 6) + return __LINE__; + + return 0; +} + static int run_vfprintf(int min, int max) { int test; @@ -1346,6 +1413,7 @@ static int run_vfprintf(int min, int max) CASE_TEST(char); EXPECT_VFPRINTF(1, "c", "%c", 'c'); break; CASE_TEST(hex); EXPECT_VFPRINTF(1, "f", "%x", 0xf); break; CASE_TEST(pointer); EXPECT_VFPRINTF(3, "0x1", "%p", (void *) 0x1); break; + CASE_TEST(scanf); EXPECT_ZR(1, test_scanf()); break; case __LINE__: return ret; /* must be last */ /* note: do not set any defaults so as to permit holes above */
This reverts commit 16767502aa990cca2cb7d1372b31d328c4c85b40.
Nolibc gained support for uname(2) and sscanf(3) which are the dependencies of ksft_min_kernel_version().
So re-enable support for ksft_min_kernel_version() under nolibc.
Signed-off-by: Thomas Weißschuh linux@weissschuh.net --- tools/testing/selftests/kselftest.h | 5 ----- 1 file changed, 5 deletions(-)
diff --git a/tools/testing/selftests/kselftest.h b/tools/testing/selftests/kselftest.h index cdf91b0ca40fbdc4fb825b86d4dc547b5afa673c..c3b6d2604b1e486af5a224a11386f75fe0a83495 100644 --- a/tools/testing/selftests/kselftest.h +++ b/tools/testing/selftests/kselftest.h @@ -444,10 +444,6 @@ static inline __noreturn __printf(1, 2) void ksft_exit_skip(const char *msg, ... static inline int ksft_min_kernel_version(unsigned int min_major, unsigned int min_minor) { -#ifdef NOLIBC - ksft_print_msg("NOLIBC: Can't check kernel version: Function not implemented\n"); - return 0; -#else unsigned int major, minor; struct utsname info;
@@ -455,7 +451,6 @@ static inline int ksft_min_kernel_version(unsigned int min_major, ksft_exit_fail_msg("Can't parse kernel version\n");
return major > min_major || (major == min_major && minor >= min_minor); -#endif }
#endif /* __KSELFTEST_H */
On Sun, Feb 09, 2025 at 09:27:11PM +0100, Thomas Weißschuh wrote:
The implementation is limited and only supports numeric arguments.
Signed-off-by: Thomas Weißschuh linux@weissschuh.net
Changes in v2:
- Return __LINE__ from different testcases to directly point to the failed testcase
- Add some comments
- Expand commit message
- Link to v1: https://lore.kernel.org/r/20240731-nolibc-scanf-v1-0-f71bcc4abb9e@weissschuh...
Looks good. For the whole series:
Acked-by: Willy Tarreau w@1wt.eu
Thanks! Willy
linux-kselftest-mirror@lists.linaro.org