libc+tests: Add type modifiers and integer conversion specifiers to scanf()
This commit is contained in:
parent
a2c081f219
commit
55d147841f
@ -1,3 +1,4 @@
|
|||||||
|
#include <errno.h>
|
||||||
#include <luna/CType.h>
|
#include <luna/CType.h>
|
||||||
#include <luna/NumberParsing.h>
|
#include <luna/NumberParsing.h>
|
||||||
#include <stdio.h>
|
#include <stdio.h>
|
||||||
@ -7,6 +8,10 @@
|
|||||||
#define FLAG_DISCARD (1 << 0)
|
#define FLAG_DISCARD (1 << 0)
|
||||||
#define FLAG_ALLOC (1 << 1)
|
#define FLAG_ALLOC (1 << 1)
|
||||||
#define FLAG_WIDTH (1 << 2)
|
#define FLAG_WIDTH (1 << 2)
|
||||||
|
#define FLAG_LONG (1 << 3)
|
||||||
|
#define FLAG_LONG_LONG (1 << 4)
|
||||||
|
#define FLAG_SHORT (1 << 5)
|
||||||
|
#define FLAG_CHAR (1 << 6)
|
||||||
|
|
||||||
static int parse_flags(const char** format)
|
static int parse_flags(const char** format)
|
||||||
{
|
{
|
||||||
@ -42,11 +47,73 @@ static size_t parse_width(const char** format, int& flags)
|
|||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void parse_type(const char** format, int& flags)
|
||||||
|
{
|
||||||
|
// FIXME: Support %j (intmax_t/uintmax_t)
|
||||||
|
switch (**format)
|
||||||
|
{
|
||||||
|
case 'h':
|
||||||
|
flags |= FLAG_SHORT;
|
||||||
|
(*format)++;
|
||||||
|
if (**format == 'h')
|
||||||
|
{
|
||||||
|
flags |= FLAG_CHAR;
|
||||||
|
(*format)++;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case 'l':
|
||||||
|
flags |= FLAG_LONG;
|
||||||
|
(*format)++;
|
||||||
|
if (**format == 'l')
|
||||||
|
{
|
||||||
|
flags |= FLAG_LONG_LONG;
|
||||||
|
(*format)++;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case 't':
|
||||||
|
flags |= (sizeof(ptrdiff_t) == sizeof(long)) ? FLAG_LONG : FLAG_LONG_LONG;
|
||||||
|
(*format)++;
|
||||||
|
break;
|
||||||
|
case 'z':
|
||||||
|
flags |= (sizeof(size_t) == sizeof(long)) ? FLAG_LONG : FLAG_LONG_LONG;
|
||||||
|
(*format)++;
|
||||||
|
break;
|
||||||
|
default: break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static void write_parsed_signed_integer(ssize_t value, int flags, va_list ap)
|
||||||
|
{
|
||||||
|
if (flags & FLAG_LONG_LONG) *va_arg(ap, signed long long*) = (signed long long)value;
|
||||||
|
else if (flags & FLAG_LONG)
|
||||||
|
*va_arg(ap, signed long*) = (signed long)value;
|
||||||
|
else if (flags & FLAG_SHORT)
|
||||||
|
*va_arg(ap, signed int*) = (signed short)value;
|
||||||
|
else if (flags & FLAG_CHAR)
|
||||||
|
*va_arg(ap, signed int*) = (signed char)value;
|
||||||
|
else
|
||||||
|
*va_arg(ap, signed int*) = (signed int)value;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void write_parsed_unsigned_integer(size_t value, int flags, va_list ap)
|
||||||
|
{
|
||||||
|
if (flags & FLAG_LONG_LONG) *va_arg(ap, unsigned long long*) = (unsigned long long)value;
|
||||||
|
else if (flags & FLAG_LONG)
|
||||||
|
*va_arg(ap, unsigned long*) = (unsigned long)value;
|
||||||
|
else if (flags & FLAG_SHORT)
|
||||||
|
*va_arg(ap, unsigned int*) = (unsigned short)value;
|
||||||
|
else if (flags & FLAG_CHAR)
|
||||||
|
*va_arg(ap, unsigned int*) = (unsigned char)value;
|
||||||
|
else
|
||||||
|
*va_arg(ap, unsigned int*) = (unsigned int)value;
|
||||||
|
}
|
||||||
|
|
||||||
extern "C"
|
extern "C"
|
||||||
{
|
{
|
||||||
int vsscanf(const char* str, const char* format, va_list ap)
|
int vsscanf(const char* str, const char* format, va_list ap)
|
||||||
{
|
{
|
||||||
int parsed = 0;
|
int parsed = 0;
|
||||||
|
const char* s = str; // Keep a pointer to the beginning of the string for %n
|
||||||
|
|
||||||
if (*str == 0) return EOF;
|
if (*str == 0) return EOF;
|
||||||
|
|
||||||
@ -80,6 +147,7 @@ extern "C"
|
|||||||
|
|
||||||
int flags = parse_flags(&format);
|
int flags = parse_flags(&format);
|
||||||
size_t width = parse_width(&format, flags);
|
size_t width = parse_width(&format, flags);
|
||||||
|
parse_type(&format, flags);
|
||||||
char specifier = *format++;
|
char specifier = *format++;
|
||||||
if (!specifier) return parsed;
|
if (!specifier) return parsed;
|
||||||
|
|
||||||
@ -128,6 +196,53 @@ extern "C"
|
|||||||
parsed++;
|
parsed++;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
case 'd': {
|
||||||
|
str += strspn(str, " \t\f\r\n\v");
|
||||||
|
ssize_t value = scan_signed_integer(&str, 10);
|
||||||
|
if (!(flags & FLAG_DISCARD)) write_parsed_signed_integer(value, flags, ap);
|
||||||
|
parsed++;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case 'i': {
|
||||||
|
str += strspn(str, " \t\f\r\n\v");
|
||||||
|
ssize_t value = scan_signed_integer(&str, 0);
|
||||||
|
if (!(flags & FLAG_DISCARD)) write_parsed_signed_integer(value, flags, ap);
|
||||||
|
parsed++;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case 'o': {
|
||||||
|
str += strspn(str, " \t\f\r\n\v");
|
||||||
|
size_t value = scan_unsigned_integer(&str, 8);
|
||||||
|
if (!(flags & FLAG_DISCARD)) write_parsed_unsigned_integer(value, flags, ap);
|
||||||
|
parsed++;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case 'u': {
|
||||||
|
str += strspn(str, " \t\f\r\n\v");
|
||||||
|
size_t value = scan_unsigned_integer(&str, 10);
|
||||||
|
if (!(flags & FLAG_DISCARD)) write_parsed_unsigned_integer(value, flags, ap);
|
||||||
|
parsed++;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case 'X':
|
||||||
|
case 'x': {
|
||||||
|
str += strspn(str, " \t\f\r\n\v");
|
||||||
|
size_t value = scan_unsigned_integer(&str, 16);
|
||||||
|
if (!(flags & FLAG_DISCARD)) write_parsed_unsigned_integer(value, flags, ap);
|
||||||
|
parsed++;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case 'p': {
|
||||||
|
str += strspn(str, " \t\f\r\n\v");
|
||||||
|
size_t value = scan_unsigned_integer(&str, 16);
|
||||||
|
if (!(flags & FLAG_DISCARD)) *va_arg(ap, void**) = (void*)value;
|
||||||
|
parsed++;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case 'n': {
|
||||||
|
if (!(flags & FLAG_DISCARD)) *va_arg(ap, int*) = (int)(str - s);
|
||||||
|
break;
|
||||||
|
}
|
||||||
default: {
|
default: {
|
||||||
fprintf(stderr, "vsscanf: unknown conversion specifier: %%%c\n", specifier);
|
fprintf(stderr, "vsscanf: unknown conversion specifier: %%%c\n", specifier);
|
||||||
return parsed;
|
return parsed;
|
||||||
|
@ -31,12 +31,61 @@ TestResult test_incomplete_scanf()
|
|||||||
test_success;
|
test_success;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
TestResult test_integer_scanf()
|
||||||
|
{
|
||||||
|
int hour;
|
||||||
|
int min;
|
||||||
|
|
||||||
|
int parsed = sscanf("23:59", "%d:%d", &hour, &min);
|
||||||
|
validate(parsed == 2);
|
||||||
|
|
||||||
|
validate(hour == 23);
|
||||||
|
validate(min == 59);
|
||||||
|
|
||||||
|
test_success;
|
||||||
|
}
|
||||||
|
|
||||||
|
TestResult test_integer_auto_base_scanf()
|
||||||
|
{
|
||||||
|
int a;
|
||||||
|
int b;
|
||||||
|
int c;
|
||||||
|
|
||||||
|
int parsed = sscanf("65, \t0x23, 0755", "%i, %i, %i", &a, &b, &c);
|
||||||
|
validate(parsed == 3);
|
||||||
|
|
||||||
|
validate(a == 65);
|
||||||
|
validate(b == 0x23);
|
||||||
|
validate(c == 0755);
|
||||||
|
|
||||||
|
test_success;
|
||||||
|
}
|
||||||
|
|
||||||
|
TestResult test_scanf_characters_consumed()
|
||||||
|
{
|
||||||
|
int hour;
|
||||||
|
int min;
|
||||||
|
int nr_chars;
|
||||||
|
|
||||||
|
int parsed = sscanf("23:59", "%d:%d%n", &hour, &min, &nr_chars);
|
||||||
|
validate(parsed == 2);
|
||||||
|
|
||||||
|
validate(hour == 23);
|
||||||
|
validate(min == 59);
|
||||||
|
validate(nr_chars == 5);
|
||||||
|
|
||||||
|
test_success;
|
||||||
|
}
|
||||||
|
|
||||||
Result<void> test_main()
|
Result<void> test_main()
|
||||||
{
|
{
|
||||||
test_prelude;
|
test_prelude;
|
||||||
|
|
||||||
run_test(test_basic_scanf);
|
run_test(test_basic_scanf);
|
||||||
run_test(test_incomplete_scanf);
|
run_test(test_incomplete_scanf);
|
||||||
|
run_test(test_integer_scanf);
|
||||||
|
run_test(test_integer_auto_base_scanf);
|
||||||
|
run_test(test_scanf_characters_consumed);
|
||||||
|
|
||||||
return {};
|
return {};
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user