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