| #include <stdio.h> |
| #include <stdlib.h> |
| #include <stdarg.h> |
| #include <ctype.h> |
| #include <wchar.h> |
| #include <wctype.h> |
| #include <limits.h> |
| #include <string.h> |
| |
| #include "stdio_impl.h" |
| #include "shgetc.h" |
| #include "intscan.h" |
| #include "floatscan.h" |
| #include "libc.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; |
| } |
| |
| static int in_set(const wchar_t* set, int c) { |
| int j; |
| const wchar_t* p = set; |
| if (*p == '-') { |
| if (c == '-') |
| return 1; |
| p++; |
| } else if (*p == ']') { |
| if (c == ']') |
| return 1; |
| p++; |
| } |
| for (; *p && *p != ']'; p++) { |
| if (*p == '-' && p[1] && p[1] != ']') |
| for (j = p++ [-1]; j < *p; j++) |
| if (c == j) |
| return 1; |
| if (c == *p) |
| return 1; |
| } |
| return 0; |
| } |
| |
| #undef getwc |
| #define getwc(f) \ |
| ((f)->rpos < (f)->rend && *(f)->rpos < 128 ? *(f)->rpos++ : (getwc)(f)) |
| |
| #undef ungetwc |
| #define ungetwc(c, f) \ |
| ((f)->rend && (c) < 128U ? *--(f)->rpos : ungetwc((c), (f))) |
| |
| int vfwscanf(FILE* restrict f, const wchar_t* restrict fmt, va_list ap) { |
| int width; |
| int size; |
| int alloc; |
| const wchar_t* p; |
| int c, t; |
| char* s; |
| wchar_t* wcs; |
| void* dest = NULL; |
| int invert; |
| int matches = 0; |
| off_t pos = 0, cnt; |
| static const char size_pfx[][3] = {"hh", "h", "", "l", "L", "ll"}; |
| char tmp[3 * sizeof(int) + 10]; |
| const wchar_t* set; |
| size_t i, k; |
| |
| FLOCK(f); |
| |
| fwide(f, 1); |
| |
| for (p = fmt; *p; p++) { |
| alloc = 0; |
| |
| if (iswspace(*p)) { |
| while (iswspace(p[1])) |
| p++; |
| while (iswspace((c = getwc(f)))) |
| pos++; |
| ungetwc(c, f); |
| continue; |
| } |
| if (*p != '%' || p[1] == '%') { |
| p += *p == '%'; |
| c = getwc(f); |
| if (c != *p) { |
| ungetwc(c, f); |
| if (c < 0) |
| goto input_fail; |
| goto match_fail; |
| } |
| pos++; |
| continue; |
| } |
| |
| p++; |
| if (*p == '*') { |
| dest = 0; |
| p++; |
| } else if (iswdigit(*p) && p[1] == '$') { |
| dest = arg_n(ap, *p - '0'); |
| p += 2; |
| } else { |
| dest = va_arg(ap, void*); |
| } |
| |
| for (width = 0; iswdigit(*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; |
| |
| /* Transform S,C -> ls,lc */ |
| if ((t & 0x2f) == 3) { |
| size = SIZE_l; |
| t |= 32; |
| } |
| |
| if (t != 'n') { |
| if (t != '[' && (t | 32) != 'c') |
| while (iswspace((c = getwc(f)))) |
| pos++; |
| else |
| c = getwc(f); |
| if (c < 0) |
| goto input_fail; |
| ungetwc(c, f); |
| } |
| |
| switch (t) { |
| case 'n': |
| store_int(dest, size, pos); |
| /* do not increment match count, etc! */ |
| continue; |
| |
| case 's': |
| case 'c': |
| case '[': |
| if (t == 'c') { |
| if (width < 1) |
| width = 1; |
| invert = 1; |
| set = L""; |
| } else if (t == 's') { |
| invert = 1; |
| set = (const wchar_t[]){ |
| ' ', '\t', '\n', '\r', 11, 12, 0x0085, 0x2000, |
| 0x2001, 0x2002, 0x2003, 0x2004, 0x2005, 0x2006, 0x2008, 0x2009, |
| 0x200a, 0x2028, 0x2029, 0x205f, 0x3000, 0}; |
| } else { |
| if (*++p == '^') |
| p++, invert = 1; |
| else |
| invert = 0; |
| set = p; |
| if (*p == ']') |
| p++; |
| while (*p != ']') { |
| if (!*p) |
| goto fmt_fail; |
| p++; |
| } |
| } |
| |
| s = (size == SIZE_def) ? dest : 0; |
| wcs = (size == SIZE_l) ? dest : 0; |
| |
| int gotmatch = 0; |
| |
| if (width < 1) |
| width = -1; |
| |
| i = 0; |
| if (alloc) { |
| k = t == 'c' ? width + 1U : 31; |
| if (size == SIZE_l) { |
| wcs = malloc(k * sizeof(wchar_t)); |
| if (!wcs) |
| goto alloc_fail; |
| } else { |
| s = malloc(k); |
| if (!s) |
| goto alloc_fail; |
| } |
| } |
| while (width) { |
| if ((c = getwc(f)) < 0) |
| break; |
| if (in_set(set, c) == invert) |
| break; |
| if (wcs) { |
| wcs[i++] = c; |
| if (alloc && i == k) { |
| k += k + 1; |
| wchar_t* tmp = realloc(wcs, k * sizeof(wchar_t)); |
| if (!tmp) |
| goto alloc_fail; |
| wcs = tmp; |
| } |
| } else if (size != SIZE_l) { |
| int l = wctomb(s ? s + i : tmp, c); |
| if (l < 0) |
| goto input_fail; |
| i += l; |
| if (alloc && i > k - 4) { |
| k += k + 1; |
| char* tmp = realloc(s, k); |
| if (!tmp) |
| goto alloc_fail; |
| s = tmp; |
| } |
| } |
| pos++; |
| width -= (width > 0); |
| gotmatch = 1; |
| } |
| if (width) { |
| ungetwc(c, f); |
| if (t == 'c' || !gotmatch) |
| 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 '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 'p': |
| if (width < 1) |
| width = 0; |
| snprintf(tmp, sizeof tmp, "%.*s%.0d%s%c%%lln", 1 + !dest, "%*", width, |
| size_pfx[size + 2], t); |
| cnt = 0; |
| if (fscanf(f, tmp, dest ? dest : &cnt, &cnt) == -1) |
| goto input_fail; |
| else if (!cnt) |
| goto match_fail; |
| pos += cnt; |
| break; |
| default: |
| goto fmt_fail; |
| } |
| |
| 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(vfwscanf, __isoc99_vfwscanf); |