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