X-Git-Url: http://nsz.repo.hu/git/?p=musl;a=blobdiff_plain;f=src%2Fstdio%2Fvfscanf.c;h=62bf47f698857f4c4852783638aa0697c96a7c14;hp=69f45081bf83dda70b42157ac0bc2284a399904e;hb=f18846dd3a048598676e10b2a7b9f931bb3d1d6a;hpb=0b44a0315b47dd8eced9f3b7f31580cf14bbfc01 diff --git a/src/stdio/vfscanf.c b/src/stdio/vfscanf.c index 69f45081..62bf47f6 100644 --- a/src/stdio/vfscanf.c +++ b/src/stdio/vfscanf.c @@ -1,43 +1,311 @@ -#include +#include +#include +#include +#include +#include +#include #include #include -#include +#include +#include +#include #include "stdio_impl.h" -#include "__scanf.h" +#include "shgetc.h" +#include "intscan.h" +#include "floatscan.h" + +#define SIZE_hh -2 +#define SIZE_h -1 +#define SIZE_def 0 +#define SIZE_l 1 +#define SIZE_L 2 +#define SIZE_ll 3 -static void f_read(rctx_t *r) +static void store_int(void *dest, int size, unsigned long long i) { - FILE *f = r->opaque; - if ((r->c = __uflow(f)) >= 0) r->l++; + if (!dest) return; + switch (size) { + case SIZE_hh: + *(char *)dest = i; + break; + case SIZE_h: + *(short *)dest = i; + break; + case SIZE_def: + *(int *)dest = i; + break; + case SIZE_l: + *(long *)dest = i; + break; + case SIZE_ll: + *(long long *)dest = i; + break; + } } -int vfscanf(FILE *f, const char *fmt, va_list ap) +static void *arg_n(va_list ap, unsigned int n) { - size_t l = strlen(fmt), i, result; - rctx_t r = { f_read, (void *)f, 0, isspace }; - wchar_t fmt2[l+1]; + void *p; + unsigned int i; + va_list ap2; + va_copy(ap2, ap); + for (i=n; i>1; i--) va_arg(ap2, void *); + p = va_arg(ap2, void *); + va_end(ap2); + return p; +} - if (l > 0x100000) { - errno = ENOMEM; +static int readwc(int c, wchar_t **wcs, mbstate_t *st) +{ + char ch = c; + wchar_t wc; + switch (mbrtowc(&wc, &ch, 1, st)) { + case -1: return -1; + case -2: + break; + default: + if (*wcs) *(*wcs)++ = wc; } - for (i=0; i<=l; i++) fmt2[i] = (unsigned char)fmt[i]; + return 0; +} + +int vfscanf(FILE *restrict f, const char *restrict fmt, va_list ap) +{ + int width; + int size; + int alloc; + int base; + const unsigned char *p; + int c, t; + char *s; + wchar_t *wcs; + mbstate_t st; + void *dest=NULL; + int invert; + int matches=0; + unsigned long long x; + long double y; + off_t pos = 0; + unsigned char scanset[257]; FLOCK(f); - result = __scanf(&r, fmt2, ap); + for (p=(const unsigned char *)fmt; *p; p++) { - if (r.u && r.c >= 0) { - /* This code takes care of the case where the caller performs - * a nonmatching scanf to leave a character in the unscan - * buffer, followed by an unget, followed by a scanf that - * matches zero characters. In this case the final 'unread' - * character must be returned to the unget buffer rather than - * the unscan buffer. */ - f->rpos--; - } + if (isspace(*p)) { + while (isspace(p[1])) p++; + shlim(f, 0); + while (isspace(shgetc(f))); + shunget(f); + pos += shcnt(f); + continue; + } + if (*p != '%' || p[1] == '%') { + p += *p=='%'; + shlim(f, 0); + c = shgetc(f); + if (c!=*p) { + shunget(f); + if (c<0) goto input_fail; + goto match_fail; + } + pos++; + continue; + } + + p++; + if (*p=='*') { + dest = 0; p++; + } else if (isdigit(*p) && p[1]=='$') { + dest = arg_n(ap, *p-'0'); p+=2; + } else { + dest = va_arg(ap, void *); + } + for (width=0; isdigit(*p); p++) { + width = 10*width + *p - '0'; + } + + if (*p=='m') { + alloc = 1; + p++; + } else { + alloc = 0; + } + + size = SIZE_def; + switch (*p++) { + case 'h': + if (*p == 'h') p++, size = SIZE_hh; + else size = SIZE_h; + break; + case 'l': + if (*p == 'l') p++, size = SIZE_ll; + else size = SIZE_l; + break; + case 'j': + size = SIZE_ll; + break; + case 'z': + case 't': + size = SIZE_l; + break; + case 'L': + size = SIZE_L; + break; + case 'd': case 'i': case 'o': case 'u': case 'x': + case 'a': case 'e': case 'f': case 'g': + case 'A': case 'E': case 'F': case 'G': case 'X': + case 's': case 'c': case '[': + case 'S': case 'C': + case 'p': case 'n': + p--; + break; + default: + goto fmt_fail; + } + + t = *p; + + switch (t) { + case 'C': + if (width < 1) width = 1; + case 'S': + t |= 32; + size = SIZE_l; + break; + case 'c': + if (width < 1) width = 1; + case 'd': case 'i': case 'o': case 'u': case 'x': + case 'a': case 'e': case 'f': case 'g': + case 'A': case 'E': case 'F': case 'G': case 'X': + case '[': case 's': + case 'p': case 'n': + break; + default: + goto fmt_fail; + } + + if (t == 'n') { + store_int(dest, size, pos); + /* do not increment match count, etc! */ + continue; + } + + if (t != '[' && (t|32) != 'c') { + shlim(f, 0); + while (isspace(shgetc(f))); + shunget(f); + pos += shcnt(f); + } + + shlim(f, width); + if (shgetc(f) < 0) goto input_fail; + shunget(f); + + switch (t) { + case 's': + case 'c': + case '[': + if (t == 'c' || t == 's') { + memset(scanset, -1, sizeof scanset); + scanset[0] = 0; + if (t == 's') { + scanset[1+'\t'] = 0; + scanset[1+'\n'] = 0; + scanset[1+'\v'] = 0; + scanset[1+'\f'] = 0; + scanset[1+'\r'] = 0; + scanset[1+' '] = 0; + } + } else { + if (*++p == '^') p++, invert = 1; + else invert = 0; + memset(scanset, invert, sizeof scanset); + scanset[0] = 0; + if (*p == '-') p++, scanset[1+'-'] = 1-invert; + else if (*p == ']') p++, scanset[1+']'] = 1-invert; + for (; *p != ']'; p++) { + if (!*p) goto fmt_fail; + if (*p=='-' && p[1] && p[1] != ']') + for (c=p++[-1]; c<*p; c++) + scanset[1+c] = 1-invert; + scanset[1+*p] = 1-invert; + } + } + wcs = 0; + s = 0; + if (size == SIZE_l) { + wcs = dest; + st = (mbstate_t){0}; + while (scanset[(c=shgetc(f))+1]) { + if (readwc(c, &wcs, &st) < 0) + goto input_fail; + } + if (!mbsinit(&st)) goto input_fail; + } else if ((s = dest)) { + while (scanset[(c=shgetc(f))+1]) + *s++ = c; + } else { + while (scanset[(c=shgetc(f))+1]); + } + shunget(f); + if (!shcnt(f)) goto match_fail; + if (t == 'c' && shcnt(f) != width) goto match_fail; + if (wcs) *wcs = 0; + if (s) *s = 0; + break; + case 'p': + case 'X': + case 'x': + base = 16; + goto int_common; + case 'o': + base = 8; + goto int_common; + case 'd': + case 'u': + base = 10; + goto int_common; + case 'i': + base = 0; + int_common: + x = __intscan(f, base, 0, ULLONG_MAX); + if (!shcnt(f)) goto match_fail; + if (t=='p' && dest) *(void **)dest = (void *)(uintptr_t)x; + else store_int(dest, size, x); + break; + case 'a': case 'A': + case 'e': case 'E': + case 'f': case 'F': + case 'g': case 'G': + y = __floatscan(f, size, 0); + if (!shcnt(f)) goto match_fail; + if (dest) switch (size) { + case SIZE_def: + *(float *)dest = y; + break; + case SIZE_l: + *(double *)dest = y; + break; + case SIZE_L: + *(long double *)dest = y; + break; + } + break; + } + + pos += shcnt(f); + if (dest) matches++; + } + if (0) { +fmt_fail: +input_fail: + if (!matches) matches--; + } +match_fail: FUNLOCK(f); - return result; + return matches; }