#include #include #include #include #include #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++; 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; } }