Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

tools/nolibc: add support for [v]sscanf()

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.

Acked-by: Willy Tarreau <w@1wt.eu>
Link: https://lore.kernel.org/r/20250209-nolibc-scanf-v2-1-c29dea32f1cd@weissschuh.net
Signed-off-by: Thomas Weißschuh <linux@weissschuh.net>

+166
+98
tools/include/nolibc/stdio.h
··· 350 350 } 351 351 352 352 static __attribute__((unused)) 353 + int vsscanf(const char *str, const char *format, va_list args) 354 + { 355 + uintmax_t uval; 356 + intmax_t ival; 357 + int base; 358 + char *endptr; 359 + int matches; 360 + int lpref; 361 + 362 + matches = 0; 363 + 364 + while (1) { 365 + if (*format == '%') { 366 + /* start of pattern */ 367 + lpref = 0; 368 + format++; 369 + 370 + if (*format == 'l') { 371 + /* same as in printf() */ 372 + lpref = 1; 373 + format++; 374 + if (*format == 'l') { 375 + lpref = 2; 376 + format++; 377 + } 378 + } 379 + 380 + if (*format == '%') { 381 + /* literal % */ 382 + if ('%' != *str) 383 + goto done; 384 + str++; 385 + format++; 386 + continue; 387 + } else if (*format == 'd') { 388 + ival = strtoll(str, &endptr, 10); 389 + if (lpref == 0) 390 + *va_arg(args, int *) = ival; 391 + else if (lpref == 1) 392 + *va_arg(args, long *) = ival; 393 + else if (lpref == 2) 394 + *va_arg(args, long long *) = ival; 395 + } else if (*format == 'u' || *format == 'x' || *format == 'X') { 396 + base = *format == 'u' ? 10 : 16; 397 + uval = strtoull(str, &endptr, base); 398 + if (lpref == 0) 399 + *va_arg(args, unsigned int *) = uval; 400 + else if (lpref == 1) 401 + *va_arg(args, unsigned long *) = uval; 402 + else if (lpref == 2) 403 + *va_arg(args, unsigned long long *) = uval; 404 + } else if (*format == 'p') { 405 + *va_arg(args, void **) = (void *)strtoul(str, &endptr, 16); 406 + } else { 407 + SET_ERRNO(EILSEQ); 408 + goto done; 409 + } 410 + 411 + format++; 412 + str = endptr; 413 + matches++; 414 + 415 + } else if (*format == '\0') { 416 + goto done; 417 + } else if (isspace(*format)) { 418 + /* skip spaces in format and str */ 419 + while (isspace(*format)) 420 + format++; 421 + while (isspace(*str)) 422 + str++; 423 + } else if (*format == *str) { 424 + /* literal match */ 425 + format++; 426 + str++; 427 + } else { 428 + if (!matches) 429 + matches = EOF; 430 + goto done; 431 + } 432 + } 433 + 434 + done: 435 + return matches; 436 + } 437 + 438 + static __attribute__((unused, format(scanf, 2, 3))) 439 + int sscanf(const char *str, const char *format, ...) 440 + { 441 + va_list args; 442 + int ret; 443 + 444 + va_start(args, format); 445 + ret = vsscanf(str, format, args); 446 + va_end(args); 447 + return ret; 448 + } 449 + 450 + static __attribute__((unused)) 353 451 void perror(const char *msg) 354 452 { 355 453 fprintf(stderr, "%s%serrno=%d\n", (msg && *msg) ? msg : "", (msg && *msg) ? ": " : "", errno);
+68
tools/testing/selftests/nolibc/nolibc-test.c
··· 1325 1325 return ret; 1326 1326 } 1327 1327 1328 + static int test_scanf(void) 1329 + { 1330 + unsigned long long ull; 1331 + unsigned long ul; 1332 + unsigned int u; 1333 + long long ll; 1334 + long l; 1335 + void *p; 1336 + int i; 1337 + 1338 + /* return __LINE__ to point to the specific failure */ 1339 + 1340 + /* test EOF */ 1341 + if (sscanf("", "foo") != EOF) 1342 + return __LINE__; 1343 + 1344 + /* test simple literal without placeholder */ 1345 + if (sscanf("foo", "foo") != 0) 1346 + return __LINE__; 1347 + 1348 + /* test single placeholder */ 1349 + if (sscanf("123", "%d", &i) != 1) 1350 + return __LINE__; 1351 + 1352 + if (i != 123) 1353 + return __LINE__; 1354 + 1355 + /* test multiple place holders and separators */ 1356 + if (sscanf("a123b456c0x90", "a%db%uc%p", &i, &u, &p) != 3) 1357 + return __LINE__; 1358 + 1359 + if (i != 123) 1360 + return __LINE__; 1361 + 1362 + if (u != 456) 1363 + return __LINE__; 1364 + 1365 + if (p != (void *)0x90) 1366 + return __LINE__; 1367 + 1368 + /* test space handling */ 1369 + if (sscanf("a b1", "a b%d", &i) != 1) 1370 + return __LINE__; 1371 + 1372 + if (i != 1) 1373 + return __LINE__; 1374 + 1375 + /* test literal percent */ 1376 + if (sscanf("a%1", "a%%%d", &i) != 1) 1377 + return __LINE__; 1378 + 1379 + if (i != 1) 1380 + return __LINE__; 1381 + 1382 + /* test stdint.h types */ 1383 + if (sscanf("1|2|3|4|5|6", 1384 + "%d|%ld|%lld|%u|%lu|%llu", 1385 + &i, &l, &ll, &u, &ul, &ull) != 6) 1386 + return __LINE__; 1387 + 1388 + if (i != 1 || l != 2 || ll != 3 || 1389 + u != 4 || ul != 5 || ull != 6) 1390 + return __LINE__; 1391 + 1392 + return 0; 1393 + } 1394 + 1328 1395 static int run_vfprintf(int min, int max) 1329 1396 { 1330 1397 int test; ··· 1413 1346 CASE_TEST(char); EXPECT_VFPRINTF(1, "c", "%c", 'c'); break; 1414 1347 CASE_TEST(hex); EXPECT_VFPRINTF(1, "f", "%x", 0xf); break; 1415 1348 CASE_TEST(pointer); EXPECT_VFPRINTF(3, "0x1", "%p", (void *) 0x1); break; 1349 + CASE_TEST(scanf); EXPECT_ZR(1, test_scanf()); break; 1416 1350 case __LINE__: 1417 1351 return ret; /* must be last */ 1418 1352 /* note: do not set any defaults so as to permit holes above */