Skip to content

Commit 22edf1f

Browse files
committed
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 <[email protected]> Link: https://lore.kernel.org/r/[email protected] Signed-off-by: Thomas Weißschuh <[email protected]>
1 parent 9c812b0 commit 22edf1f

File tree

2 files changed

+166
-0
lines changed

2 files changed

+166
-0
lines changed

tools/include/nolibc/stdio.h

Lines changed: 98 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -349,6 +349,104 @@ int printf(const char *fmt, ...)
349349
return ret;
350350
}
351351

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+
352450
static __attribute__((unused))
353451
void perror(const char *msg)
354452
{

tools/testing/selftests/nolibc/nolibc-test.c

Lines changed: 68 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1325,6 +1325,73 @@ static int expect_vfprintf(int llen, int c, const char *expected, const char *fm
13251325
return ret;
13261326
}
13271327

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+
13281395
static int run_vfprintf(int min, int max)
13291396
{
13301397
int test;
@@ -1346,6 +1413,7 @@ static int run_vfprintf(int min, int max)
13461413
CASE_TEST(char); EXPECT_VFPRINTF(1, "c", "%c", 'c'); break;
13471414
CASE_TEST(hex); EXPECT_VFPRINTF(1, "f", "%x", 0xf); break;
13481415
CASE_TEST(pointer); EXPECT_VFPRINTF(3, "0x1", "%p", (void *) 0x1); break;
1416+
CASE_TEST(scanf); EXPECT_ZR(1, test_scanf()); break;
13491417
case __LINE__:
13501418
return ret; /* must be last */
13511419
/* note: do not set any defaults so as to permit holes above */

0 commit comments

Comments
 (0)