|  | #include <stdlib.h> | 
|  | #include <stdarg.h> | 
|  | #include <ctype.h> | 
|  | #include <wchar.h> | 
|  | #include <wctype.h> | 
|  | #include <limits.h> | 
|  | #include <string.h> | 
|  | #include <stdint.h> | 
|  |  | 
|  | #include "stdio_impl.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 store_int(void *dest, int size, unsigned long long i) | 
|  | { | 
|  | 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; | 
|  | } | 
|  | } | 
|  |  | 
|  | static void *arg_n(va_list ap, unsigned int n) | 
|  | { | 
|  | 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; | 
|  | } | 
|  |  | 
|  | 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]; | 
|  | size_t i, k; | 
|  | wchar_t wc; | 
|  |  | 
|  | FLOCK(f); | 
|  |  | 
|  | for (p=(const unsigned char *)fmt; *p; p++) { | 
|  |  | 
|  | alloc = 0; | 
|  |  | 
|  | 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') { | 
|  | wcs = 0; | 
|  | s = 0; | 
|  | alloc = !!dest; | 
|  | 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; | 
|  |  | 
|  | /* C or S */ | 
|  | if ((t&0x2f) == 3) { | 
|  | t |= 32; | 
|  | size = SIZE_l; | 
|  | } | 
|  |  | 
|  | switch (t) { | 
|  | case 'c': | 
|  | if (width < 1) width = 1; | 
|  | case '[': | 
|  | break; | 
|  | case 'n': | 
|  | store_int(dest, size, pos); | 
|  | /* do not increment match count, etc! */ | 
|  | continue; | 
|  | default: | 
|  | 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; | 
|  | i = 0; | 
|  | k = t=='c' ? width+1U : 31; | 
|  | if (size == SIZE_l) { | 
|  | if (alloc) { | 
|  | wcs = malloc(k*sizeof(wchar_t)); | 
|  | if (!wcs) goto alloc_fail; | 
|  | } else { | 
|  | wcs = dest; | 
|  | } | 
|  | st = (mbstate_t){0}; | 
|  | while (scanset[(c=shgetc(f))+1]) { | 
|  | switch (mbrtowc(&wc, &(char){c}, 1, &st)) { | 
|  | case -1: | 
|  | goto input_fail; | 
|  | case -2: | 
|  | continue; | 
|  | } | 
|  | if (wcs) wcs[i++] = wc; | 
|  | if (alloc && i==k) { | 
|  | k+=k+1; | 
|  | wchar_t *tmp = realloc(wcs, k*sizeof(wchar_t)); | 
|  | if (!tmp) goto alloc_fail; | 
|  | wcs = tmp; | 
|  | } | 
|  | } | 
|  | if (!mbsinit(&st)) goto input_fail; | 
|  | } else if (alloc) { | 
|  | s = malloc(k); | 
|  | if (!s) goto alloc_fail; | 
|  | while (scanset[(c=shgetc(f))+1]) { | 
|  | s[i++] = c; | 
|  | if (i==k) { | 
|  | k+=k+1; | 
|  | char *tmp = realloc(s, k); | 
|  | if (!tmp) goto alloc_fail; | 
|  | s = tmp; | 
|  | } | 
|  | } | 
|  | } else if ((s = dest)) { | 
|  | while (scanset[(c=shgetc(f))+1]) | 
|  | s[i++] = 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 (alloc) { | 
|  | if (size == SIZE_l) *(wchar_t **)dest = wcs; | 
|  | else *(char **)dest = s; | 
|  | } | 
|  | if (t != 'c') { | 
|  | if (wcs) wcs[i] = 0; | 
|  | if (s) s[i] = 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: | 
|  | alloc_fail: | 
|  | input_fail: | 
|  | if (!matches) matches--; | 
|  | match_fail: | 
|  | if (alloc) { | 
|  | free(s); | 
|  | free(wcs); | 
|  | } | 
|  | } | 
|  | FUNLOCK(f); | 
|  | return matches; | 
|  | } | 
|  |  | 
|  | weak_alias(vfscanf,__isoc99_vfscanf); |