From 4e09bca9e34c226b6d7e34b4fa11248405fd988e Mon Sep 17 00:00:00 2001 From: Anton Kling Date: Sun, 22 Oct 2023 19:50:38 +0200 Subject: Move everything into a new repo. --- userland/libc/string/sscanf.c | 193 ++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 193 insertions(+) create mode 100644 userland/libc/string/sscanf.c (limited to 'userland/libc/string/sscanf.c') diff --git a/userland/libc/string/sscanf.c b/userland/libc/string/sscanf.c new file mode 100644 index 0000000..28e1ce1 --- /dev/null +++ b/userland/libc/string/sscanf.c @@ -0,0 +1,193 @@ +#include +#include +#include +#include +#include +#include +#include + +extern int errno; +extern int get_value(char c, long base); + +long ftnum(FILE *stream, int base, int *error) { + char c; + long ret_value = 0; + *error = 0; + // Ignore inital white-space sequence + for (;;) { + if (EOF == (c = fgetc(stream))) { + *error = 1; + return 0; + } + + if (!isspace(c)) { + ungetc(c, stream); + break; + } + } + if (c == '\0') { + *error = 1; + return 0; + } + if (!isdigit(c)) { + *error = 1; + return 0; + } + if (!(2 <= base && 36 >= base)) { + *error = 1; + return 0; + } + for (;;) { + if (EOF == (c = fgetc(stream))) + break; + if (c == '\0') { + ungetc(c, stream); + break; + } + int val = get_value(c, base); + if (-1 == val) { + ungetc(c, stream); + break; + } + if (ret_value * base > LONG_MAX - val) { + ungetc(c, stream); + errno = ERANGE; + *error = 1; + return 0; + } + ret_value *= base; + ret_value += val; + } + return ret_value; +} + +int vfscanf(FILE *stream, const char *format, va_list ap) { + int rc = 0; // Upon successful completion, these functions shall return the + // number of successfully matched and assigned input items + int cont = 0; + int suppress = 0; + for (; *format; format++) { + if (*format != '%' && !cont) { + char c; + if (isspace(*format)) + continue; + if (EOF == (c = fgetc(stream))) { + break; + } + if (*format == c) // TODO: Make sure this is the correct behaviour + continue; + // TODO: Make sure this is the correct behaviour + errno = EINVAL; + assert(0); + break; + } + + if (*format == '%' && !cont) { + cont = 1; + continue; + } + + int is_long = 0; + switch (*format) { + case 'l': + is_long++; + assert(is_long < 3); + cont = 1; + break; + case 'i': // Matches an optionally signed integer, whose format is the same + // as expected for the subject sequence of strtol() with 0 for the + // base argument. + case 'd': { + // Matches an optionally signed decimal integer, whose format is the + // same as expected for the subject sequence of strtol() with the value + // 10 for the base argument. In the absence of a size modifier, the + // application shall ensure that the corresponding argument is a pointer + // to int. + int err = 0; + int result = ftnum(stream, 10, &err); + if (err) { + cont = 0; + break; + } + if (!suppress) { + if (2 == is_long) { + *((long long *)va_arg(ap, long long *)) = result; + } else if (1 == is_long) { + *((long *)va_arg(ap, long *)) = result; + } else { + *((int *)va_arg(ap, int *)) = result; + } + rc++; + } + assert(0 == err); + cont = 0; + break; + } + case 'c': { + char result = fgetc(stream); + if (!suppress) { + *((char *)va_arg(ap, char *)) = result; + rc++; + } + cont = 0; + break; + } + case '*': // Assignment suppression + suppress = 1; + cont = 1; + break; + default: + printf("vfscanf: Got %c but not supported.\n", *format); + assert(0); + break; + } + if (!cont) { + suppress = 0; + } + } + return rc; +} + +struct sscanf_cookie { + const char *s; +}; + +size_t sscanf_read(FILE *f, unsigned char *s, size_t l) { + struct sscanf_cookie *c = f->cookie; + if (!*(c->s)) { + return 0; + } + size_t r = 0; + for (; l && *(c->s); l--, c->s += 1) { + *s = *(c->s); + s++; + r++; + } + if (!(*(c->s))) + f->is_eof = 1; + /* + memcpy(s, c->s, l); + c->s += l;*/ + return r; +} + +int vsscanf(const char *s, const char *restrict format, va_list ap) { + struct sscanf_cookie c = {.s = s}; + FILE f = { + .read = sscanf_read, + .cookie = &c, + .has_buffered_char = 0, + .is_eof = 0, + .has_error = 0, + .offset_in_file = 0, + }; + return vfscanf(&f, format, ap); +} + +int sscanf(const char *s, const char *restrict format, ...) { + va_list ap; + va_start(ap, format); + int rc = vsscanf(s, format, ap); + va_end(ap); + return rc; +} -- cgit v1.2.3