Luna/libc/src/scanf.cpp

142 lines
3.9 KiB
C++
Raw Normal View History

#include <luna/CType.h>
#include <luna/NumberParsing.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#define FLAG_DISCARD (1 << 0)
#define FLAG_ALLOC (1 << 1)
#define FLAG_WIDTH (1 << 2)
static int parse_flags(const char** format)
{
int result = 0;
while (true)
{
switch (**format)
{
case '*':
result |= FLAG_DISCARD;
(*format)++;
break;
case 'm':
result |= FLAG_ALLOC;
(*format)++;
break;
default: return result;
}
}
}
static size_t parse_width(const char** format, int& flags)
{
size_t result = 0;
if (_isdigit(**format))
{
result = scan_unsigned_integer(format);
flags |= FLAG_WIDTH;
}
return result;
}
extern "C"
{
int vsscanf(const char* str, const char* format, va_list ap)
{
int parsed = 0;
if (*str == 0) return EOF;
while (*format)
{
if (*format != '%')
{
normal:
if (!_isspace(*format))
{
if (*str != *format) return parsed;
str++;
format++;
if (*str == 0) return parsed;
continue;
}
format += strspn(format, " \t\f\r\n\v");
str += strspn(str, " \t\f\r\n\v");
if (*str == 0) return parsed;
continue;
}
else
{
format++;
2023-06-18 21:51:44 +00:00
if (*format == '%')
{
str += strspn(str, " \t\f\r\n\v");
goto normal;
}
int flags = parse_flags(&format);
size_t width = parse_width(&format, flags);
char specifier = *format++;
if (!specifier) return parsed;
switch (specifier)
{
case 's': {
str += strspn(str, " \t\f\r\n\v");
size_t chars = strcspn(str, " \t\f\r\n\v");
if (!chars) return parsed;
if ((flags & FLAG_WIDTH) && chars > width) chars = width;
if (!(flags & FLAG_DISCARD))
{
char* ptr;
if (flags & FLAG_ALLOC)
{
ptr = (char*)malloc(chars + 1);
if (!ptr) return parsed;
*va_arg(ap, char**) = ptr;
}
else
ptr = va_arg(ap, char*);
memcpy(ptr, str, chars);
ptr[chars] = 0;
}
str += chars;
parsed++;
break;
}
case 'c': {
if (strlen(str) < width) return parsed;
if (!(flags & FLAG_WIDTH)) width = 1;
if (!(flags & FLAG_DISCARD))
{
char* ptr;
if (flags & FLAG_ALLOC)
{
ptr = (char*)malloc(width);
if (!ptr) return parsed;
*va_arg(ap, char**) = ptr;
}
else
ptr = va_arg(ap, char*);
memcpy(ptr, str, width);
}
str += width;
parsed++;
break;
}
default: {
fprintf(stderr, "vsscanf: unknown conversion specifier: %%%c\n", specifier);
return parsed;
}
}
}
}
return parsed;
}
}