/* scanf implementation Copyright (C) 2021 Sampo Hippeläinen (hisahi) Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ #include #include #include /* =============================== * * defines & checks * * =============================== */ /* C99/C++11 */ #ifndef SCANF_C99 #if (defined(__STDC_VERSION__) && __STDC_VERSION__ >= 199901L) \ || (defined(__cplusplus) && __cplusplus >= 201103L) #define SCANF_C99 1 #else #define SCANF_C99 0 #endif #endif /* long long? */ #ifndef LLONG_MAX #undef SCANF_DISABLE_SUPPORT_LONG_LONG #define SCANF_DISABLE_SUPPORT_LONG_LONG 1 #endif /* stdint.h? */ #ifndef SCANF_STDINT #if SCANF_C99 #define SCANF_STDINT 1 #elif HAVE_STDINT_H #define SCANF_STDINT 1 #endif #endif #if SCANF_STDINT #ifndef __STDC_LIMIT_MACROS #define __STDC_LIMIT_MACROS 1 #endif #include #if SCANF_DISABLE_SUPPORT_LONG_LONG && defined(UINTMAX_MAX) \ && UINTMAX_MAX == ULLONG_MAX /* replace (u)intmax_t with (unsigned) long if long long is disabled */ #undef intmax_t #define intmax_t long #undef uintmax_t #define uintmax_t unsigned long #undef INTMAX_MIN #define INTMAX_MIN LONG_MIN #undef INTMAX_MAX #define INTMAX_MAX LONG_MAX #undef UINTMAX_MAX #define UINTMAX_MAX ULONG_MAX #endif #else /* (u)intmax_t, (U)INTMAX_(MIN_MAX) */ #if SCANF_C99 && !SCANF_DISABLE_SUPPORT_LONG_LONG #ifndef intmax_t #define intmax_t long long int #endif #ifndef uintmax_t #define uintmax_t unsigned long long int #endif #ifndef INTMAX_MIN #define INTMAX_MIN LLONG_MIN #endif #ifndef INTMAX_MAX #define INTMAX_MAX LLONG_MAX #endif #ifndef UINTMAX_MAX #define UINTMAX_MAX ULLONG_MAX #endif #else #ifndef intmax_t #define intmax_t long int #endif #ifndef uintmax_t #define uintmax_t unsigned long int #endif #ifndef INTMAX_MIN #define INTMAX_MIN LONG_MIN #endif #ifndef INTMAX_MAX #define INTMAX_MAX LONG_MAX #endif #ifndef UINTMAX_MAX #define UINTMAX_MAX ULONG_MAX #endif #endif #endif /* maximum precision floating point type */ #ifndef floatmax_t #define floatmax_t long double #endif #ifndef INLINE #if SCANF_C99 #define INLINE inline #else #define INLINE #endif #endif /* boolean type */ #ifndef BOOL #if defined(__cplusplus) #define BOOL bool #elif SCANF_C99 #define BOOL _Bool #else #define BOOL char #endif #endif /* freestanding? */ #ifndef SCANF_FREESTANDING #if defined(__STDC_HOSTED__) && __STDC_HOSTED__ == 0 #define SCANF_FREESTANDING 1 #else #define SCANF_FREESTANDING 0 #endif #endif #ifndef SCANF_NOMATH #define SCANF_NOMATH SCANF_FREESTANDING #endif #ifndef SCANF_INTERNAL_CTYPE #define SCANF_INTERNAL_CTYPE SCANF_FREESTANDING #endif #ifndef SCANF_BINARY #define SCANF_BINARY 1 #endif /* this is technically UB, as the character encodings used by the preprocessor and the actual compiled code might be different. in practice this is hardly ever an issue, and if it is, you can just define SCANF_ASCII manually */ #ifndef SCANF_ASCII #if 'B' - 'A' == 1 && 'K' - 'A' == 10 && 'Z' - 'A' == 25 && 'a' - 'A' == 32 \ && 'n' - 'N' == 32 && 'v' - 'V' == 32 && 'z' - 'Z' == 32 \ && '3' - '0' == 3 && '9' - '0' == 9 #define SCANF_ASCII 1 #else #define SCANF_ASCII 0 #endif #endif #ifndef SCANF_FAST_SCANSET #if CHAR_BIT == 8 #define SCANF_FAST_SCANSET 1 #else #define SCANF_FAST_SCANSET 0 #endif #endif /* fast scanset only supported with multibyte/narrow characters */ #undef SCANF_CAN_FAST_SCANSET #if SCANF_WIDE #define SCANF_CAN_FAST_SCANSET 0 #else #define SCANF_CAN_FAST_SCANSET SCANF_FAST_SCANSET #endif #ifndef SCANF_NOPOW #define SCANF_NOPOW 1 #endif #ifndef SCANF_LOGN_POW #define SCANF_LOGN_POW 1 #endif /* wide setup */ #ifndef SCANF_WIDE #define SCANF_WIDE 0 #endif #if SCANF_WIDE < 0 || SCANF_WIDE > 3 #error invalid value for SCANF_WIDE (valid: 0, 1, 2, 3) #endif #if SCANF_WIDE == 3 #define SCANF_WIDE_CONVERT 1 #endif #define SCANF_NODEFINE 1 #if SCANF_WIDE /* also includes wide.h */ #include "wscanf.h" #endif /* include more stuff */ #if !SCANF_INTERNAL_CTYPE #if SCANF_WIDE #include #define isdigitw_ iswdigit #define isspacew_ iswspace #define isalnumw_ iswalnum #define isalphaw_ iswalpha #define tolowerw_ towlower #else #include #endif #endif /* type & literal defines */ #undef C_ #undef S_ #undef F_ #undef CHAR #undef UCHAR #undef CINT #if SCANF_WIDE /* character */ #ifdef MAKE_WCHAR #define C_(x) MAKE_WCHAR(x) #else #define C_(x) L##x #endif /* string */ #ifdef MAKE_WSTRING #define S_(x) MAKE_WSTRING(x) #else #define S_(x) L##x #endif /* function */ #define F_(x) x##w_ #define CHAR WCHAR #define UCHAR WCHAR #define CINT WINT #else /* character */ #define C_(x) x /* string */ #define S_(x) x /* function */ #define F_(x) x #define CHAR char #define UCHAR unsigned char #define CINT int #endif #if !SCANF_DISABLE_SUPPORT_FLOAT #include #endif #if !SCANF_NOMATH #include #endif #ifndef SCANF_INFINITE #if SCANF_C99 && !SCANF_NOMATH && defined(NAN) && defined(INFINITY) #define SCANF_INFINITE 1 #else #define SCANF_INFINITE 0 #endif #endif #if SCANF_ASCII #define INLINE_IF_ASCII INLINE #else #define INLINE_IF_ASCII #endif #ifndef EOF #define EOF -1 #endif /* WEOF from wide.h */ /* try to map size_t to unsigned long long, unsigned long, or int */ #if defined(SIZE_MAX) #if defined(ULLONG_MAX) && SIZE_MAX == ULLONG_MAX #if SCANF_DISABLE_SUPPORT_LONG_LONG #define SIZET_DISABLE 1 #pragma message("size_t == long long (disabled), so it will be disabled too") #else #define SIZET_ALIAS ll #endif #elif defined(ULONG_MAX) && SIZE_MAX == ULONG_MAX #define SIZET_ALIAS l #elif defined(UINT_MAX) && SIZE_MAX == UINT_MAX #define SIZET_ALIAS /* intentionally empty -> maps to int */ #endif #endif /* this is intentionally after the previous check */ #ifndef SIZE_MAX #define SIZE_MAX (size_t)(-1) #endif /* try to map ptrdiff_t to long long, long, or int */ #if defined(PTRDIFF_MAX) #if defined(LLONG_MAX) && PTRDIFF_MAX == LLONG_MAX && PTRDIFF_MIN == LLONG_MIN #if SCANF_DISABLE_SUPPORT_LONG_LONG #define PTRDIFFT_DISABLE 1 #pragma message("ptrdiff_t == long long (disabled), so it will be disabled too") #else #define PTRDIFFT_ALIAS ll #endif #elif defined(LONG_MAX) && PTRDIFF_MAX == LONG_MAX && PTRDIFF_MIN == LONG_MIN #define PTRDIFFT_ALIAS l #elif defined(INT_MAX) && PTRDIFF_MAX == INT_MAX && PTRDIFF_MIN == INT_MIN #define PTRDIFFT_ALIAS /* intentionally empty -> maps to int */ #endif #endif /* try to map intmax_t to unsigned long long or unsigned long */ #if defined(INTMAX_MAX) && defined(UINTMAX_MAX) #if !SCANF_DISABLE_SUPPORT_LONG_LONG \ && (defined(LLONG_MAX) && INTMAX_MAX == LLONG_MAX \ && INTMAX_MIN == LLONG_MIN) \ && (defined(ULLONG_MAX) && UINTMAX_MAX == ULLONG_MAX) #define INTMAXT_ALIAS ll #elif (defined(LONG_MAX) && INTMAX_MAX == LONG_MAX && INTMAX_MIN == LONG_MIN) \ && (defined(ULONG_MAX) && UINTMAX_MAX == ULONG_MAX) #define INTMAXT_ALIAS l #endif #endif /* check if short == int */ #if defined(INT_MIN) && defined(SHRT_MIN) && INT_MIN == SHRT_MIN \ && defined(INT_MAX) && defined(SHRT_MAX) && INT_MAX == SHRT_MAX \ && defined(UINT_MAX) && defined(USHRT_MAX) && UINT_MAX == USHRT_MAX #define SHORT_IS_INT 1 #endif /* check if long == int */ #if defined(INT_MIN) && defined(LONG_MIN) && INT_MIN == LONG_MIN \ && defined(INT_MAX) && defined(LONG_MAX) && INT_MAX == LONG_MAX \ && defined(UINT_MAX) && defined(ULONG_MAX) && UINT_MAX == ULONG_MAX #define LONG_IS_INT 1 #endif /* check if long long == long */ #if defined(LONG_MIN) && defined(LLONG_MIN) && LONG_MIN == LLONG_MIN \ && defined(LONG_MAX) && defined(LLONG_MAX) && LONG_MAX == LLONG_MAX \ && defined(ULONG_MAX) && defined(ULLONG_MAX) \ && ULONG_MAX == ULLONG_MAX #define LLONG_IS_LONG 1 #endif #if !SCANF_DISABLE_SUPPORT_FLOAT /* check if double == float */ #if defined(FLT_MANT_DIG) && defined(DBL_MANT_DIG) \ && FLT_MANT_DIG == DBL_MANT_DIG && defined(FLT_MIN_EXP) \ && defined(DBL_MIN_EXP) && FLT_MIN_EXP == DBL_MIN_EXP \ && defined(FLT_MAX_EXP) && defined(DBL_MAX_EXP) \ && FLT_MAX_EXP == DBL_MAX_EXP #define DOUBLE_IS_FLOAT 1 #endif /* check if long double == double */ #if defined(DBL_MANT_DIG) && defined(LDBL_MANT_DIG) \ && DBL_MANT_DIG == LDBL_MANT_DIG && defined(DBL_MIN_EXP) \ && defined(LDBL_MIN_EXP) && DBL_MIN_EXP == LDBL_MIN_EXP \ && defined(DBL_MAX_EXP) && defined(LDBL_MAX_EXP) \ && DBL_MAX_EXP == LDBL_MAX_EXP #define LDOUBLE_IS_DOUBLE 1 #endif #endif #ifdef UINTPTR_MAX #define INT_TO_PTR(x) ((void *)(uintptr_t)(uintmax_t)(x)) #else #define INT_TO_PTR(x) ((void *)(uintmax_t)(x)) #endif #if SCANF_CLAMP /* try to figure out values for PTRDIFF_MAX and PTRDIFF_MIN */ #ifndef PTRDIFF_MAX #define PTRDIFF_MAX ptrdiff_max_ #define PTRDIFF_MAX_COMPUTE 1 #endif #ifndef PTRDIFF_MIN #define PTRDIFF_MIN ptrdiff_min_ #define PTRDIFF_MIN_COMPUTE 1 #endif #endif /* =============================== * * digit conversion * * =============================== */ static INLINE_IF_ASCII int F_(ctodn_)(CINT c) { #if SCANF_ASCII return c - C_('0'); #else switch (c) { case C_('0'): return 0; case C_('1'): return 1; case C_('2'): return 2; case C_('3'): return 3; case C_('4'): return 4; case C_('5'): return 5; case C_('6'): return 6; case C_('7'): return 7; case C_('8'): return 8; case C_('9'): return 9; default: return -1; } #endif } static INLINE_IF_ASCII int F_(ctoon_)(CINT c) { #if SCANF_ASCII return c - C_('0'); #else switch (c) { case C_('0'): return 0; case C_('1'): return 1; case C_('2'): return 2; case C_('3'): return 3; case C_('4'): return 4; case C_('5'): return 5; case C_('6'): return 6; case C_('7'): return 7; default: return -1; } #endif } static int F_(ctoxn_)(CINT c) { #if SCANF_ASCII if (c >= C_('a')) return c - C_('a') + 10; else if (c >= C_('A')) return c - C_('A') + 10; return c - C_('0'); #else switch (c) { case C_('0'): return 0; case C_('1'): return 1; case C_('2'): return 2; case C_('3'): return 3; case C_('4'): return 4; case C_('5'): return 5; case C_('6'): return 6; case C_('7'): return 7; case C_('8'): return 8; case C_('9'): return 9; case C_('A'): case C_('a'): return 10; case C_('B'): case C_('b'): return 11; case C_('C'): case C_('c'): return 12; case C_('D'): case C_('d'): return 13; case C_('E'): case C_('e'): return 14; case C_('F'): case C_('f'): return 15; default: return -1; } #endif } #if SCANF_BINARY static INLINE_IF_ASCII int F_(ctobn_)(CINT c) { #if SCANF_ASCII return c - C_('0'); #else switch (c) { case C_('0'): return 0; case C_('1'): return 1; default: return -1; } #endif } #endif static int F_(ctorn_)(CINT c, int b) { switch (b) { case 8: return F_(ctoon_)(c); case 16: return F_(ctoxn_)(c); #if SCANF_BINARY case 2: return F_(ctobn_)(c); #endif default: /* 10 */ return F_(ctodn_)(c); } } /* =============================== * * character checks * * =============================== */ #if SCANF_INTERNAL_CTYPE static int F_(isspace)(CINT c) { switch (c) { case C_(' '): case C_('\t'): case C_('\n'): case C_('\v'): case C_('\f'): case C_('\r'): return 1; } return 0; } static INLINE int F_(isdigit)(CINT c) { #if SCANF_ASCII return C_('0') <= c && c <= C_('9'); #else return F_(ctodn_)(c) >= 0; #endif } #if SCANF_INFINITE #if SCANF_ASCII static int F_(isalpha)(CINT c) { return (C_('A') <= c && c <= C_('Z')) || (C_('a') <= c && c <= C_('z')); } static INLINE CINT F_(tolower)(CINT c) { return F_(isalpha)(c) ? c | 0x20 : c; } #else static int F_(isalpha)(CINT c) { switch (c) { case _C('A'): case _C('N'): case _C('a'): case _C('n'): case _C('B'): case _C('O'): case _C('b'): case _C('o'): case _C('C'): case _C('P'): case _C('c'): case _C('p'): case _C('D'): case _C('Q'): case _C('d'): case _C('q'): case _C('E'): case _C('R'): case _C('e'): case _C('r'): case _C('F'): case _C('S'): case _C('f'): case _C('s'): case _C('G'): case _C('T'): case _C('g'): case _C('t'): case _C('H'): case _C('U'): case _C('h'): case _C('u'): case _C('I'): case _C('V'): case _C('i'): case _C('v'): case _C('J'): case _C('W'): case _C('j'): case _C('w'): case _C('K'): case _C('X'): case _C('k'): case _C('x'): case _C('L'): case _C('Y'): case _C('l'): case _C('y'): case _C('M'): case _C('Z'): case _C('m'): case _C('z'): return 1; } return 0; } static CINT F_(tolower)(CINT c) { switch (c) { case _C('A'): return _C('a'); case _C('N'): return _C('n'); case _C('B'): return _C('b'); case _C('O'): return _C('o'); case _C('C'): return _C('c'); case _C('P'): return _C('p'); case _C('D'): return _C('d'); case _C('Q'): return _C('q'); case _C('E'): return _C('e'); case _C('R'): return _C('r'); case _C('F'): return _C('f'); case _C('S'): return _C('s'); case _C('G'): return _C('g'); case _C('T'): return _C('t'); case _C('H'): return _C('h'); case _C('U'): return _C('u'); case _C('I'): return _C('i'); case _C('V'): return _C('v'); case _C('J'): return _C('j'); case _C('W'): return _C('w'); case _C('K'): return _C('k'); case _C('X'): return _C('x'); case _C('L'): return _C('l'); case _C('Y'): return _C('y'); case _C('M'): return _C('m'); case _C('Z'): return _C('z'); } return c; } #endif /* SCANF_ASCII */ static INLINE int F_(isalnum)(CINT c) { return F_(isdigit)(c) || F_(isalpha)(c); } #endif /* SCANF_INFINITE */ #endif /* SCANF_INTERNAL_CTYPE */ /* case-insensitive character comparison. compares character c to upper (uppercase) and lower (lowercase). only defined if upper and lower are letters and it applies that tolower(upper) == lower */ #ifndef SCANF_REPEAT #if SCANF_ASCII #define ICASEEQ(c, upper, lower) ((((CINT)(c)) & ~0x20U) == (CINT)(C_(upper))) #else #define ICASEEQ(c, upper, lower) (((c) == C_(upper)) || ((c) == C_(lower))) #endif #endif static INLINE int F_(isdigo_)(CINT c) { #if SCANF_ASCII return C_('0') <= c && c <= C_('7'); #else return F_(ctoon_)(c) >= 0; #endif } static INLINE int F_(isdigx_)(CINT c) { #if SCANF_ASCII return F_(isdigit)(c) || (C_('A') <= c && c <= C_('F')) || (C_('a') <= c && c <= C_('f')); #else return F_(ctoxn_)(c) >= 0; #endif } #if SCANF_BINARY static INLINE int F_(isdigb_)(CINT c) { return c == C_('0') || c == C_('1'); } #endif static INLINE int F_(isdigr_)(CINT c, int b) { switch (b) { case 8: return F_(isdigo_)(c); case 16: return F_(isdigx_)(c); #if SCANF_BINARY case 2: return F_(isdigb_)(c); #endif default: /* 10 */ return F_(isdigit)(c); } } /* =============================== * * integer math * * =============================== */ #if SCANF_CLAMP && !defined(SCANF_REPEAT) static INLINE intmax_t clamps_(intmax_t m0, intmax_t v, intmax_t m1) { return v < m0 ? m0 : v > m1 ? m1 : v; } static INLINE uintmax_t clampu_(uintmax_t m0, uintmax_t v, uintmax_t m1) { return v < m0 ? m0 : v > m1 ? m1 : v; } #endif /* SCANF_CLAMP && !defined(SCANF_REPEAT) */ /* =============================== * * floating point math * * =============================== */ #ifndef SCANF_REPEAT #if !SCANF_DISABLE_SUPPORT_FLOAT #if !SCANF_NOPOW static INLINE floatmax_t powi_(floatmax_t x, intmax_t y) { return pow(x, y); } #elif SCANF_LOGN_POW static floatmax_t powi_(floatmax_t x, intmax_t y) { floatmax_t r = (floatmax_t)1; for (; y > 0; y >>= 1) { if (y & 1) r *= x; x *= x; } return r; } #else static floatmax_t powi_(floatmax_t x, intmax_t y) { floatmax_t r = (floatmax_t)1; for (; y > 0; --y) r *= x; return r; } #endif #endif /* !SCANF_DISABLE_SUPPORT_FLOAT */ #endif /* SCANF_REPEAT */ /* =============================== * * scanset functions * * =============================== */ #if !SCANF_DISABLE_SUPPORT_SCANSET && !SCANF_CAN_FAST_SCANSET static BOOL F_(inscan_)(const UCHAR *begin, const UCHAR *end, UCHAR c) { BOOL hyphen = 0; UCHAR prev = 0, f; const UCHAR *p = begin; while (p < end) { f = *p++; if (hyphen) { if (prev <= c && c <= f) return 1; hyphen = 0; prev = f; } else if (f == C_('-') && prev) { hyphen = 1; } else if (f == c) return 1; else prev = f; } return hyphen && c == C_('-'); } #endif /* =============================== * * conversion functions * * =============================== */ #ifndef SCANF_REPEAT /* still characters to read? (not EOF and width not exceeded) */ #define KEEP_READING() (nowread < maxlen && !GOT_EOF()) /* read next char and increment counter */ #define NEXT_CHAR(counter) (next = getch(p), ++counter) #endif #undef IS_EOF #undef GCEOF /* EOF check */ #if SCANF_WIDE #define IS_EOF(c) ((c) == WEOF) #define GCEOF WEOF #else #define IS_EOF(c) ((c) < 0) #define GCEOF EOF #endif #undef GOT_EOF #define GOT_EOF() (IS_EOF(next)) /* convert stream to integer getch: stream read function p: pointer to pass to above nextc: pointer to next character in buffer readin: pointer to number of characters read maxlen: value that *readin should be at most base: 10 for decimal, 16 for hexadecimal, etc. unsign: whether the result should be unsigned negative: whether there was a - sign zero: if no digits, allow zero (i.e. read zero before iaton_) dest: intmax_t* or uintmax_t*, where result is stored return value: 1 if conversion OK, 0 if not (if 0, dest guaranteed to not be modified) */ static INLINE BOOL F_(iaton_)( CINT (*getch)(void *p), void *p, CINT *nextc, size_t *readin, size_t maxlen, int base, BOOL unsign, BOOL negative, BOOL zero, void *dest) { CINT next = *nextc; size_t nowread = *readin; uintmax_t r = 0, pr = 0; /* read digits? overflow? */ BOOL digit = 0, ovf = 0; #if !SCANF_MINIMIZE /* skip initial zeros */ while (KEEP_READING() && next == C_('0')) { digit = 1; NEXT_CHAR(nowread); } #endif /* read digits and convert to integer */ while (KEEP_READING() && F_(isdigr_)(next, base)) { if (!ovf) { digit = 1; r = r * base + F_(ctorn_)(next, base); if (pr > r) ovf = 1; else pr = r; } NEXT_CHAR(nowread); } /* if no digits read? */ if (digit) { /* overflow detection, negation, etc. */ if (unsign) { if (ovf) r = (intmax_t)UINTMAX_MAX; else if (negative) #if SCANF_CLAMP r = 0; #else r = (uintmax_t)-r; #endif *(uintmax_t *)dest = r; } else { intmax_t sr; if (ovf || (negative ? (intmax_t)r < 0 : r > (uintmax_t)INTMAX_MAX)) sr = negative ? INTMAX_MIN : INTMAX_MAX; else { sr = negative ? -(intmax_t)r : (intmax_t)r; } *(intmax_t *)dest = sr; } } else if (zero) { if (unsign) *(uintmax_t *)dest = 0; else *(intmax_t *)dest = 0; digit = 1; } *nextc = next; *readin = nowread; return digit; } #if !SCANF_DISABLE_SUPPORT_FLOAT /* convert stream to floating point getch: stream read function p: pointer to pass to above nextc: pointer to next character in buffer readin: pointer to number of characters read maxlen: value that *readin should be at most hex: whether in hex mode negative: whether there was a - sign zero: if no digits, allow zero (i.e. read zero before iatof_) dest: floatmax_t*, where result is stored return value: 1 if conversion OK, 0 if not (if 0, dest guaranteed to not be modified) */ static INLINE BOOL F_(iatof_)( CINT (*getch)(void *p), void *p, CINT *nextc, size_t *readin, size_t maxlen, BOOL hex, BOOL negative, BOOL zero, floatmax_t *dest) { CINT next = *nextc; size_t nowread = *readin; floatmax_t r = 0, pr = 0; /* saw dot? saw digit? was there an overflow? */ BOOL dot = 0, digit = 0, ovf = 0; /* exponent, offset (with decimals, etc.) */ intmax_t exp = 0, off = 0; /* how much to subtract from off with every digit */ int sub = 0; /* base */ int base = hex ? 16 : 10; /* exponent character */ CHAR expuc = hex ? 'P' : 'E', explc = hex ? 'p' : 'e'; #if !SCANF_MINIMIZE while (KEEP_READING() && next == C_('0')) { digit = 1; NEXT_CHAR(nowread); } #endif /* read digits and convert */ while (KEEP_READING()) { if (F_(isdigr_)(next, base)) { if (!ovf) { digit = 1; r = r * base + F_(ctorn_)(next, base); if (r > 0 && pr == r) { ovf = 1; } else { pr = r; off += sub; } } } else if (next == C_('.')) { if (dot) break; dot = 1, sub = hex ? 4 : 1; } else break; NEXT_CHAR(nowread); } if (zero && !digit) digit = 1; /* r == 0 should already apply */ if (digit) { /* exponent? */ if (KEEP_READING() && (next == explc || next == expuc)) { BOOL eneg = 0; NEXT_CHAR(nowread); if (KEEP_READING()) { switch (next) { case C_('-'): eneg = 1; /* fall-through */ case C_('+'): NEXT_CHAR(nowread); } } if (!F_(iaton_)( getch, p, &next, &nowread, maxlen, 10, 0, eneg, 0, &exp)) digit = 0; } } if (digit) { if (dot) { intmax_t oexp = exp; exp -= off; if (exp > oexp) exp = INTMAX_MIN; /* underflow protection */ } if (r != 0) { if (exp > 0) { #ifdef INFINITY #if FLT_RADIX == 2 if (exp > (hex ? LDBL_MAX_EXP : LDBL_MAX_10_EXP)) #else /* FLT_RADIX > 2 means LDBL_MAX_EXP is defined as e in b^e for some b > 2; we'd need to compute LDBL_MAX_EXP * log(b) / log(2) at compile time, which is hardly an option. so instead we'll do the next best thing */ if (exp > (hex ? (LDBL_MAX_EXP * FLT_RADIX + 1) : LDBL_MAX_10_EXP)) #endif r = INFINITY; else #endif r *= powi_(hex ? 2 : 10, exp); } else if (exp < 0) { #if FLT_RADIX == 2 if (exp < (hex ? LDBL_MIN_EXP : LDBL_MIN_10_EXP)) #else if (exp < (hex ? (LDBL_MIN_EXP * FLT_RADIX - 1) : LDBL_MIN_10_EXP)) #endif r = 0; else r /= powi_(hex ? 2 : 10, -exp); } } if (negative) r = -r; *dest = r; } *nextc = next; *readin = nowread; return digit; } #endif /* !SCANF_DISABLE_SUPPORT_FLOAT */ #ifndef SCANF_REPEAT enum iscans_type { A_CHAR, A_STRING, A_SCANSET }; #endif /* SCANF_REPEAT */ #if !SCANF_DISABLE_SUPPORT_SCANSET struct F_(scanset_) { #if SCANF_CAN_FAST_SCANSET const BOOL *mask; #else const UCHAR *set_begin; const UCHAR *set_end; #endif BOOL invert; }; #define STRUCT_SCANSET struct F_(scanset_) static INLINE BOOL F_(insset_)(const struct F_(scanset_) * set, UCHAR c) { #if SCANF_CAN_FAST_SCANSET return set->mask[c] != set->invert; #else return F_(inscan_)(set->set_begin, set->set_end, c) != set->invert; #endif } #else /* !SCANF_DISABLE_SUPPORT_SCANSET */ #define STRUCT_SCANSET void #endif /* !SCANF_DISABLE_SUPPORT_SCANSET */ /* read char(s)/string from stream without char conversion getch: stream read function p: pointer to pass to above nextc: pointer to next character in buffer readin: pointer to number of characters read maxlen: value that *readin should be at most ctype: one of the values of iscans_type set: a struct scanset_, only used with A_SCANSET nostore: whether nostore was specified outp: output CHAR pointer (not dereferenced if nostore=1) return value: 1 if conversion OK, 0 if not */ static INLINE BOOL F_(iscans_)( CINT (*getch)(void *p), void *p, CINT *nextc, size_t *readin, size_t maxlen, enum iscans_type ctype, #if !SCANF_DISABLE_SUPPORT_SCANSET const STRUCT_SCANSET *set, #endif BOOL nostore, CHAR *outp) { CINT next = *nextc; size_t nowread = *readin; while (KEEP_READING()) { if (ctype == A_STRING && F_(isspace)(next)) break; #if !SCANF_DISABLE_SUPPORT_SCANSET if (ctype == A_SCANSET && !F_(insset_)(set, (UCHAR)next)) break; #endif /* !SCANF_DISABLE_SUPPORT_SCANSET */ if (!nostore) *outp++ = (CHAR)(UCHAR)next; NEXT_CHAR(nowread); } *nextc = next; *readin = nowread; switch (ctype) { case A_CHAR: if (nowread < maxlen) return 0; break; case A_STRING: #if !SCANF_DISABLE_SUPPORT_SCANSET case A_SCANSET: #endif /* !SCANF_DISABLE_SUPPORT_SCANSET */ if (!nowread) return 0; if (!nostore) *outp = C_('\0'); #if SCANF_DISABLE_SUPPORT_SCANSET default:; /* inhibit compiler warning for missing case for enum */ #endif } return 1; } #if SCANF_WIDE_CONVERT #undef CVTCHAR #if SCANF_WIDE #define CVTCHAR char #else #define CVTCHAR WCHAR #endif /* read char(s)/string from stream with conversion getch: stream read function p: pointer to pass to above nextc: pointer to next character in buffer readin: pointer to number of characters read maxlen: value that *readin should be at most ctype: one of the values of iscans_type set: a struct scanset_, only used with A_SCANSET nostore: whether nostore was specified outp: output pointer of the other char type (not dereferenced if nostore=1) return value: 1 if conversion OK, 0 if not */ static INLINE BOOL F_(iscvts_)( CINT (*getch)(void *p), void *p, CINT *nextc, size_t *readin, size_t maxlen, enum iscans_type ctype, #if !SCANF_DISABLE_SUPPORT_SCANSET const STRUCT_SCANSET *set, #endif BOOL nostore, CVTCHAR *outp) { CINT next = *nextc; size_t nowread = *readin, mbr; scanf_mbstate_t mbstate; #if SCANF_WIDE char tmp[MB_LEN_MAX]; if (nostore) outp = tmp; #else char nc; WCHAR tmp; if (nostore) outp = &tmp; #endif mbsetup_(&mbstate); while (KEEP_READING()) { if (ctype == A_STRING && F_(isspace)(next)) break; #if !SCANF_DISABLE_SUPPORT_SCANSET if (ctype == A_SCANSET && !F_(insset_)(set, (UCHAR)next)) break; #endif /* !SCANF_DISABLE_SUPPORT_SCANSET */ #if SCANF_WIDE /* wide => narrow */ mbr = wcrtomb_(outp, next, &mbstate); if (mbr == (size_t)(-1)) { *nextc = next; *readin = nowread; return 0; } else if (mbr > 0 && !nostore) outp += mbr; #else /* narrow => wide */ nc = (char)next; mbr = mbrtowc_(outp, &nc, 1, &mbstate); if (mbr == (size_t)(-1)) { *nextc = next; *readin = nowread; return 0; } else if (mbr != (size_t)(-2) && !nostore) ++outp; #endif NEXT_CHAR(nowread); } *nextc = next; *readin = nowread; switch (ctype) { case A_CHAR: if (nowread < maxlen) return 0; break; case A_STRING: #if !SCANF_DISABLE_SUPPORT_SCANSET case A_SCANSET: #endif if (!nowread) return 0; if (!nostore) *outp = 0; #if SCANF_DISABLE_SUPPORT_SCANSET default:; /* inhibit compiler warning for missing case for enum */ #endif } return 1; } #endif /* =============================== * * extension support * * =============================== */ /* there should almost never be a reason to change this */ #ifndef SCANF_EXT_CHAR #define SCANF_EXT_CHAR '!' #endif #if SCANF_EXTENSIONS #if SCANF_WIDE int scnwext_( WINT (*getwch)(void *data), void *data, const WCHAR **format, WINT *buffer, int length, int nostore, void *destination); #else int scnext_( int (*getch)(void *data), void *data, const char **format, int *buffer, int length, int nostore, void *destination); #endif struct F_(scanf_ext_tmp) { CINT (*getch)(void *data); void *data; size_t len; }; CINT F_(scanf_ext_getch_)(void *data) { struct F_(scanf_ext_tmp) *st = (struct F_(scanf_ext_tmp) *)data; if (!st->len) return GCEOF; else { --st->len; return st->getch(st->data); } } #endif /* SCANF_EXTENSIONS */ /* =============================== * * main scanf function * * =============================== */ #ifndef SCANF_REPEAT /* signal input failure and exit loop */ #define INPUT_FAILURE() \ do { \ goto read_failure; \ } while (0) /* signal match OK */ #define MATCH_SUCCESS() (noconv = 0) /* signal match failure and exit loop */ #define MATCH_FAILURE() \ do { \ if (!GOT_EOF()) \ MATCH_SUCCESS(); \ INPUT_FAILURE(); \ } while (0) /* store value to dst with cast */ #define STORE_DST(value, T) (*(T *)(dst) = (T)(value)) /* store value to dst with cast and possible signed clamp */ #if SCANF_CLAMP #define STORE_DSTI(v, T, minv, maxv) STORE_DST(clamps_(minv, v, maxv), T) #else #define STORE_DSTI(v, T, minv, maxv) STORE_DST(v, T) #endif /* store value to dst with cast and possible unsigned clamp */ #if SCANF_CLAMP #define STORE_DSTU(v, T, minv, maxv) STORE_DST(clampu_(minv, v, maxv), T) #else #define STORE_DSTU(v, T, minv, maxv) STORE_DST(v, T) #endif #endif #ifndef SCANF_REPEAT /* enum for possible data types */ enum dlength { LN_, LN_hh, LN_h, LN_l, LN_ll, LN_L, LN_j, LN_z, LN_t }; #define vLNa_(x) LN_##x #define vLN_(x) vLNa_(x) #if PTRDIFF_MAX_COMPUTE static const int signed_padding_div_ = (int)(( sizeof(ptrdiff_t) > sizeof(uintmax_t) ? 1 : INT_MAX < UINTMAX_MAX ? ((uintmax_t)1 << (CHAR_BIT * sizeof(int))) / ((uintmax_t)INT_MAX + 1) : SHRT_MAX < UINTMAX_MAX ? ((uintmax_t)1 << (CHAR_BIT * sizeof(short))) / ((uintmax_t)SHRT_MAX + 1) : 2)); static const ptrdiff_t ptrdiff_max_ = (ptrdiff_t)(sizeof(ptrdiff_t) > sizeof(intmax_t) ? 0 : sizeof(ptrdiff_t) == sizeof(intmax_t) ? INTMAX_MAX : (((uintmax_t)1 << (CHAR_BIT * sizeof(ptrdiff_t))) / signed_padding_div_ + UINTMAX_MAX)); #endif #if PTRDIFF_MIN_COMPUTE static const ptrdiff_t ptrdiff_min_ = (ptrdiff_t)(sizeof(ptrdiff_t) > sizeof(intmax_t) ? 0 : sizeof(ptrdiff_t) == sizeof(intmax_t) ? INTMAX_MIN : (~(intmax_t)-1 > ~(intmax_t)-2) ? -PTRDIFF_MAX : -PTRDIFF_MAX + ~(intmax_t)0); #endif #endif /* SCANF_REPEAT */ static int F_(iscanf_)( CINT (*getch)(void *p), void (*ungetch)(CINT c, void *p), void *p, const CHAR *ff, va_list va) { /* fields = number of fields successfully read; this is the return value */ int fields = 0; /* next = the "next" character to be processed */ CINT next; /* total characters read, returned by %n */ size_t read_chars = 0; /* there were attempts to convert? there were no conversions? */ BOOL tryconv = 0, noconv = 1; const UCHAR *f = (const UCHAR *)ff; UCHAR c; /* empty format string always returns 0 */ if (!*f) return 0; /* read and cache first character */ next = getch(p); /* ++read_chars; intentionally left out, otherwise %n is off by 1 */ while ((c = *f++)) { if (F_(isspace)(c)) { /* skip 0-N whitespace */ while (!GOT_EOF() && F_(isspace)(next)) NEXT_CHAR(read_chars); } else if (c != C_('%')) { if (GOT_EOF()) break; /* must match literal character */ if (next != c) { INPUT_FAILURE(); break; } NEXT_CHAR(read_chars); } else { /* % */ /* nostore is %*, prevents a value from being stored */ BOOL nostore; /* nowread = characters read for this format specifier maxlen = maximum number of characters to be read "field width" */ size_t nowread = 0, maxlen = 0; /* length specifier (l, ll, h, hh...) */ enum dlength dlen = LN_; /* where the value will be stored */ void *dst; /* nostore */ if (*f == C_('*')) { nostore = 1; dst = NULL; ++f; } else { nostore = 0; dst = va_arg(va, void *); /* A pointer to any incomplete or object type may be converted to a pointer to void and back again; the result shall compare equal to the original pointer. */ } /* width specifier => maxlen */ if (F_(isdigit)(*f)) { size_t pr = 0; #if !SCANF_MINIMIZE /* skip initial zeros */ while (*f == C_('0')) ++f; #endif while (F_(isdigit)(*f)) { maxlen = maxlen * 10 + F_(ctodn_)(*f); if (maxlen < pr) { maxlen = SIZE_MAX; while (F_(isdigit)(*f)) ++f; break; } else pr = maxlen; ++f; } } #if SCANF_EXTENSIONS if (*f == C_(SCANF_EXT_CHAR)) { const CHAR *sf = (const CHAR *)(f + 1); BOOL hadlen = maxlen != 0; struct F_(scanf_ext_tmp) tmp; int ok; if (!hadlen) maxlen = SIZE_MAX; tmp.getch = getch; tmp.data = p; tmp.len = maxlen; #if SCANF_WIDE ok = scnwext_( &F_(scanf_ext_getch_), &tmp, &sf, &next, hadlen, nostore, dst); #else ok = scnext_( &F_(scanf_ext_getch_), &tmp, &sf, &next, hadlen, nostore, dst); #endif f = (const UCHAR *)sf; if (ok < 0) INPUT_FAILURE(); else if (ok > 0) MATCH_FAILURE(); else if (!nostore) ++fields; read_chars += maxlen - tmp.len; continue; } #endif /* length specifier */ switch (*f++) { case C_('h'): if (*f == C_('h')) dlen = LN_hh, ++f; else dlen = LN_h; break; case C_('l'): #if !SCANF_DISABLE_SUPPORT_LONG_LONG if (*f == C_('l')) dlen = LN_ll, ++f; else #endif dlen = LN_l; break; case C_('j'): #ifdef INTMAXT_ALIAS dlen = vLN_(INTMAXT_ALIAS); #else dlen = LN_j; #endif break; case C_('t'): #if PTRDIFFT_DISABLE MATCH_FAILURE(); #else #ifdef PTRDIFFT_ALIAS dlen = vLN_(PTRDIFFT_ALIAS); #else if (sizeof(ptrdiff_t) > sizeof(intmax_t)) MATCH_FAILURE(); dlen = LN_t; #endif break; #endif /* PTRDIFFT_DISABLE */ case C_('z'): #if SIZET_DISABLE MATCH_FAILURE(); #else #ifdef SIZET_ALIAS dlen = vLN_(SIZET_ALIAS); #else if (sizeof(size_t) > sizeof(uintmax_t)) MATCH_FAILURE(); dlen = LN_z; #endif break; #endif /* SIZET_DISABLE */ case C_('L'): dlen = LN_L; break; default: --f; } c = *f; switch (c) { default: /* skip whitespace. include in %n, but not * elsewhere */ while (!GOT_EOF() && F_(isspace)(next)) NEXT_CHAR(read_chars); /* fall-through */ /* do not skip whitespace for... */ case C_('['): case C_('c'): tryconv = 1; if (GOT_EOF()) INPUT_FAILURE(); /* fall-through */ /* do not even check EOF for... */ case C_('n'): break; } /* format */ switch (c) { case C_('%'): /* literal % */ if (next != C_('%')) MATCH_FAILURE(); NEXT_CHAR(nowread); break; { /* =========== READ INT =========== */ /* variables for reading ints */ /* decimal, hexadecimal, octal, binary */ int base; /* is negative? unsigned? %p? */ BOOL negative, unsign, isptr; /* result: i for signed integers, u for unsigned integers, p for pointers */ union { intmax_t i; uintmax_t u; void *p; } r; case C_('n'): /* number of characters read */ if (nostore) break; r.i = (intmax_t)read_chars; unsign = 0, isptr = 0; goto storenum; case C_('p'): /* pointer */ isptr = 1; #if !SCANF_DISABLE_SUPPORT_NIL if (next == C_('(')) { /* handle (nil) */ int k; const CHAR *rest = S_("nil)"); if (!maxlen) maxlen = SIZE_MAX; NEXT_CHAR(nowread); for (k = 0; k < 4; ++k) { if (!KEEP_READING() || next != rest[k]) MATCH_FAILURE(); NEXT_CHAR(nowread); } MATCH_SUCCESS(); if (!nostore) { ++fields; r.p = NULL; goto storeptr; } break; } #endif /* !SCANF_DISABLE_SUPPORT_NIL */ base = 10, unsign = 1, negative = 0; goto readptr; case C_('o'): /* unsigned octal integer */ base = 8, unsign = 1; goto readnum; case C_('x'): /* unsigned hexadecimal integer */ case C_('X'): base = 16, unsign = 1; goto readnum; #if SCANF_BINARY case C_('b'): /* non-standard: unsigned binary integer */ base = 2, unsign = 1; goto readnum; #endif case C_('u'): /* unsigned decimal integer */ case C_('d'): /* signed decimal integer */ case C_('i'): /* signed decimal/hex/binary integer */ base = 10, unsign = c == C_('u'); /* fall-through */ readnum: isptr = 0, negative = 0; /* sign, read even for %u */ /* maxlen > 0, so this is always fine */ switch (next) { case C_('-'): negative = 1; /* fall-through */ case C_('+'): NEXT_CHAR(nowread); } /* fall-through */ readptr: { BOOL zero = 0; if (!maxlen) maxlen = SIZE_MAX; /* detect base from string for %i and * %p, skip 0x for %x */ if (c == C_('i') || c == C_('p') || ICASEEQ(c, 'X', 'x') #if SCANF_BINARY || ICASEEQ(c, 'B', 'b') #endif ) { if (KEEP_READING() && next == C_('0')) { zero = 1; NEXT_CHAR(nowread); if (KEEP_READING() && ICASEEQ( next, 'X', 'x')) { if (base == 10) base = 16; else if ( base != 16) { /* read * 0b * for * %x, * etc. */ unsign ? (r.u = 0) : (r.i = 0); goto readnumok; } NEXT_CHAR( nowread); /* zero = 1. "0x" should be read as 0, because 0 is a valid strtol input, but we cannot unread x at this point, so it'll stay read */ #if SCANF_BINARY } else if ( KEEP_READING() && ICASEEQ( next, 'B', 'b')) { if (base == 10) base = 2; else if ( base != 2) { /* read * 0x * for * %b, * etc. */ unsign ? (r.u = 0) : (r.i = 0); goto readnumok; } NEXT_CHAR( nowread); #endif } else if (c == C_('i')) base = 8; } } /* convert */ if (!F_(iaton_)( getch, p, &next, &nowread, maxlen, base, unsign, negative, zero, unsign ? (void *)&r.u : (void *)&r.i)) MATCH_FAILURE(); readnumok: MATCH_SUCCESS(); if (nostore) break; ++fields; } storenum: /* store number, either as ptr, unsigned * or signed */ if (isptr) { r.p = unsign ? INT_TO_PTR(r.u) : INT_TO_PTR(r.i); storeptr: STORE_DST(r.p, void *); } else { switch (dlen) { case LN_hh: if (unsign) STORE_DSTU( r.u, unsigned char, 0, UCHAR_MAX); else STORE_DSTI( r.i, signed char, SCHAR_MIN, SCHAR_MAX); break; #if !SHORT_IS_INT case LN_h: /* if SHORT_IS_INT, match fails => default: */ if (unsign) STORE_DSTU( r.u, unsigned short, 0, USHRT_MAX); else STORE_DSTI( r.i, short, SHRT_MIN, SHRT_MAX); break; #endif #ifndef INTMAXT_ALIAS case LN_j: if (unsign) STORE_DSTU( r.u, uintmax_t, 0, UINTMAX_MAX); else STORE_DSTI( r.i, intmax_t, INTMAX_MIN, INTMAX_MAX); break; #endif /* INTMAXT_ALIAS */ #ifndef SIZET_ALIAS case LN_z: if (!unsign) { #if SCANF_CLAMP if (r.i < 0) r.u = 0; else #endif r.u = (uintmax_t) r.i; } STORE_DSTU( r.u, size_t, 0, SIZE_MAX); break; #endif /* SIZET_ALIAS */ #ifndef PTRDIFFT_ALIAS case LN_t: if (unsign) { #if SCANF_CLAMP if (r.u > (uintmax_t) INTMAX_MAX) r.i = INTMAX_MAX; else #endif r.i = (intmax_t)r .u; } #if PTRDIFF_MAX_COMPUTE /* min/max are wrong, * don't even try to * clamp */ if (PTRDIFF_MIN >= PTRDIFF_MAX) STORE_DST( r.i, ptrdiff_t); else #endif STORE_DSTI( r.i, ptrdiff_t, PTRDIFF_MIN, PTRDIFF_MAX); break; #endif /* PTRDIFFT_ALIAS */ #if !SCANF_DISABLE_SUPPORT_LONG_LONG case LN_ll: #if !LLONG_IS_LONG if (unsign) STORE_DSTU( r.u, unsigned long long, 0, ULLONG_MAX); else STORE_DSTI( r.i, long long, LLONG_MIN, LLONG_MAX); break; #endif #endif /* SCANF_DISABLE_SUPPORT_LONG_LONG */ case LN_l: #if !LONG_IS_INT if (unsign) STORE_DSTU( r.u, unsigned long, 0, ULONG_MAX); else STORE_DSTI( r.i, long, LONG_MIN, LONG_MAX); break; #endif default: if (unsign) STORE_DSTU( r.u, unsigned, 0, UINT_MAX); else STORE_DSTI( r.i, int, INT_MIN, INT_MAX); } } break; } /* =========== READ INT =========== */ case C_('e'): case C_('E'): /* scientific format float */ case C_('f'): case C_('F'): /* decimal format float */ case C_('g'): case C_('G'): /* decimal/scientific format float */ case C_('a'): case C_('A'): /* hex format float */ /* all treated equal by scanf, but not by printf */ #if SCANF_DISABLE_SUPPORT_FLOAT /* no support here */ MATCH_FAILURE(); #else { /* =========== READ FLOAT =========== */ floatmax_t r; /* negative? allow zero? hex mode? */ BOOL negative = 0, zero = 0, hex = 0; if (!maxlen) maxlen = SIZE_MAX; switch (next) { case C_('-'): negative = 1; /* fall-through */ case C_('+'): NEXT_CHAR(nowread); } #if SCANF_INFINITE if (KEEP_READING() && ICASEEQ(next, 'N', 'n')) { NEXT_CHAR(nowread); if (!KEEP_READING() || !ICASEEQ(next, 'A', 'a')) MATCH_FAILURE(); NEXT_CHAR(nowread); if (!KEEP_READING() || !ICASEEQ(next, 'N', 'n')) MATCH_FAILURE(); NEXT_CHAR(nowread); if (KEEP_READING() && next == C_('(')) { while (KEEP_READING()) { NEXT_CHAR(nowread); if (next == C_(')')) { NEXT_CHAR( nowread); break; } else if ( next != C_('_') && !F_(isalnum)( next)) MATCH_FAILURE(); } } r = negative ? -NAN : NAN; goto storefp; } else if ( KEEP_READING() && ICASEEQ(next, 'I', 'i')) { NEXT_CHAR(nowread); if (!KEEP_READING() || !ICASEEQ(next, 'N', 'n')) MATCH_FAILURE(); NEXT_CHAR(nowread); if (!KEEP_READING() || !ICASEEQ(next, 'F', 'f')) MATCH_FAILURE(); NEXT_CHAR(nowread); /* try reading the rest */ if (KEEP_READING()) { int k; const CHAR *rest2 = S_("INITY"); for (k = 0; k < 5; ++k) { if (!KEEP_READING() || (next != rest2[k] && next != F_(tolower)( rest2[k]))) break; NEXT_CHAR(nowread); } } r = negative ? -INFINITY : INFINITY; goto storefp; } #endif /* SCANF_INFINITE */ /* 0x for hex floats */ if (KEEP_READING() && next == C_('0')) { zero = 1; NEXT_CHAR(nowread); if (KEEP_READING() && ICASEEQ(next, 'X', 'x')) { hex = 1; NEXT_CHAR(nowread); } } /* convert */ if (!F_(iatof_)( getch, p, &next, &nowread, maxlen, hex, negative, zero, &r)) MATCH_FAILURE(); #if SCANF_INFINITE storefp: #endif MATCH_SUCCESS(); if (nostore) break; ++fields; switch (dlen) { case LN_L: #if !LDOUBLE_IS_DOUBLE STORE_DST(r, long double); break; #endif case LN_l: #if !DOUBLE_IS_FLOAT STORE_DST(r, double); break; #endif default: STORE_DST(r, float); } break; } /* =========== READ FLOAT =========== */ #endif /* SCANF_DISABLE_SUPPORT_FLOAT */ case C_('c'): { /* =========== READ CHAR =========== */ CHAR *outp; #if SCANF_WIDE_CONVERT BOOL wide = dlen == LN_l; #elif SCANF_WIDE /* SCANF_WIDE_CONVERT */ if (dlen != LN_l) /* read narrow but not supported */ MATCH_FAILURE(); #else /* SCANF_WIDE_CONVERT */ if (dlen == LN_l) /* read wide but not supported */ MATCH_FAILURE(); #endif /* SCANF_WIDE_CONVERT */ outp = (CHAR *)dst; if (!maxlen) maxlen = 1; #if SCANF_WIDE_CONVERT #if SCANF_WIDE if (!wide) { /* convert wide => narrow */ #else /* SCANF_WIDE */ if (wide) { /* convert narrow => wide */ #endif /* SCANF_WIDE */ if (!F_(iscvts_)( getch, p, &next, &nowread, maxlen, A_CHAR, #if !SCANF_DISABLE_SUPPORT_SCANSET NULL, #endif nostore, (CVTCHAR *)dst)) MATCH_FAILURE(); } else #endif /* SCANF_WIDE_CONVERT */ if (!F_(iscans_)( getch, p, &next, &nowread, maxlen, A_CHAR, #if !SCANF_DISABLE_SUPPORT_SCANSET NULL, #endif nostore, outp)) MATCH_FAILURE(); if (!nostore) ++fields; MATCH_SUCCESS(); break; } /* =========== READ CHAR =========== */ case C_('s'): { /* =========== READ STR =========== */ CHAR *outp; #if SCANF_WIDE_CONVERT BOOL wide = dlen == LN_l; #elif SCANF_WIDE /* SCANF_WIDE_CONVERT */ if (dlen != LN_l) /* read narrow but not supported */ MATCH_FAILURE(); #else /* SCANF_WIDE_CONVERT */ if (dlen == LN_l) /* read wide but not supported */ MATCH_FAILURE(); #endif /* SCANF_WIDE_CONVERT */ outp = (CHAR *)dst; if (!maxlen) { #if SCANF_SECURE if (!nostore) MATCH_FAILURE(); #endif maxlen = SIZE_MAX; } #if SCANF_WIDE_CONVERT #if SCANF_WIDE if (!wide) { /* convert wide => narrow */ #else /* SCANF_WIDE */ if (wide) { /* convert narrow => wide */ #endif /* SCANF_WIDE */ if (!F_(iscvts_)( getch, p, &next, &nowread, maxlen, A_STRING, #if !SCANF_DISABLE_SUPPORT_SCANSET NULL, #endif nostore, (CVTCHAR *)dst)) MATCH_FAILURE(); } else #endif /* SCANF_WIDE_CONVERT */ if (!F_(iscans_)( getch, p, &next, &nowread, maxlen, A_STRING, #if !SCANF_DISABLE_SUPPORT_SCANSET NULL, #endif nostore, outp)) MATCH_FAILURE(); if (!nostore) ++fields; MATCH_SUCCESS(); break; } /* =========== READ STR =========== */ case C_('['): #if SCANF_DISABLE_SUPPORT_SCANSET MATCH_FAILURE(); #else { /* =========== READ SCANSET =========== */ CHAR *outp; BOOL invert = 0; #if SCANF_CAN_FAST_SCANSET BOOL hyphen = 0; BOOL mention[UCHAR_MAX + 1] = {0}; UCHAR prev = 0, c; #else const UCHAR *set; #endif struct F_(scanset_) scanset; #if SCANF_WIDE_CONVERT BOOL wide = dlen == LN_l; #elif SCANF_WIDE /* SCANF_WIDE_CONVERT */ if (dlen != LN_l) /* read narrow but not supported */ MATCH_FAILURE(); #else /* SCANF_WIDE_CONVERT */ if (dlen == LN_l) /* read wide but not supported */ MATCH_FAILURE(); #endif /* SCANF_WIDE_CONVERT */ outp = (CHAR *)dst; if (!maxlen) { #if SCANF_SECURE if (!nostore) MATCH_FAILURE(); #endif maxlen = SIZE_MAX; } ++f; if (*f == C_('^')) invert = 1, ++f; if (*f == C_(']')) ++f; #if SCANF_CAN_FAST_SCANSET /* populate mention, 0 if character not listed, * 1 if it is */ while ((c = *f) && c != C_(']')) { if (hyphen) { int k; for (k = prev; k <= c; ++k) mention[k] = 1; hyphen = 0; prev = c; } else if (c == C_('-') && prev) hyphen = 1; else mention[c] = 1, prev = c; ++f; } if (hyphen) mention[C_('-')] = 1; scanset.mask = mention; #else /* SCANF_CAN_FAST_SCANSET */ set = f; while (*f && *f != C_(']')) ++f; scanset.set_begin = set, scanset.set_end = f; #endif /* SCANF_CAN_FAST_SCANSET */ scanset.invert = invert; #if SCANF_WIDE_CONVERT #if SCANF_WIDE if (!wide) { /* convert wide => narrow */ #else /* SCANF_WIDE */ if (wide) { /* convert narrow => wide */ #endif /* SCANF_WIDE */ if (!F_(iscvts_)( getch, p, &next, &nowread, maxlen, A_SCANSET, &scanset, nostore, (CVTCHAR *)dst)) MATCH_FAILURE(); } else #endif /* SCANF_WIDE_CONVERT */ { if (!F_(iscans_)( getch, p, &next, &nowread, maxlen, A_SCANSET, &scanset, nostore, outp)) MATCH_FAILURE(); } if (!nostore) ++fields; MATCH_SUCCESS(); break; } /* =========== READ SCANSET =========== */ #endif /* SCANF_DISABLE_SUPPORT_SCANSET */ default: /* unrecognized specification */ MATCH_FAILURE(); } ++f; /* next fmt char */ read_chars += nowread; } } read_failure: /* if we have a leftover character, put it back into the stream */ if (!GOT_EOF() && ungetch) ungetch(next, p); return tryconv && noconv ? EOF : fields; } /* =============================== * * wrapper functions * * =============================== */ static CINT F_(sscanw_)(void *arg) { const UCHAR **p = (const UCHAR **)arg; const UCHAR c = *(*p)++; return c ? c : GCEOF; } #if SCANF_WIDE #if SCANF_SSCANF_ONLY int vwscanf_(const WCHAR *format, va_list arg) { return *format ? EOF : 0; } int wscanf_(const WCHAR *format, ...) { return *format ? EOF : 0; } #else /* SCANF_SSCANF_ONLY */ static WINT getwchw_(void *arg) { (void)arg; return getwch_(); } static void ungetwchw_(WINT c, void *arg) { (void)arg; ungetwch_(c); } int vwscanf_(const WCHAR *format, va_list arg) { return F_(iscanf_)(&getwchw_, &ungetwchw_, NULL, format, arg); } int wscanf_(const WCHAR *format, ...) { int r; va_list va; va_start(va, format); r = vwscanf_(format, va); va_end(va); return r; } #endif /* SCANF_SSCANF_ONLY */ int vspwscanf_(const WCHAR **sp, const WCHAR *format, va_list arg) { int i = F_(iscanf_)(&F_(sscanw_), NULL, sp, format, arg); /* ungetch = NULL, because see below */ --*sp; /* back up by one character, even if it was EOF we want the pointer at the null terminator */ return i; } int spwscanf_(const WCHAR **sp, const WCHAR *format, ...) { int r; va_list va; va_start(va, format); r = vspwscanf_(sp, format, va); va_end(va); return r; } int vswscanf_(const WCHAR *s, const WCHAR *format, va_list arg) { return F_(iscanf_)(&F_(sscanw_), NULL, &s, format, arg); } int swscanf_(const WCHAR *s, const WCHAR *format, ...) { int r; va_list va; va_start(va, format); r = vswscanf_(s, format, va); va_end(va); return r; } int vfctwscanf_( WINT (*getwch)(void *data), void (*ungetwch)(WINT c, void *data), void *data, const WCHAR *format, va_list arg) { return F_(iscanf_)(getwch, ungetwch, data, format, arg); } int fctwscanf_( WINT (*getwch)(void *data), void (*ungetwch)(WINT c, void *data), void *data, const WCHAR *format, ...) { int r; va_list va; va_start(va, format); r = vfctwscanf_(getwch, ungetwch, data, format, va); va_end(va); return r; } #else /* SCANF_WIDE */ #if SCANF_SSCANF_ONLY int vscanf_(const char *format, va_list arg) { return *format ? EOF : 0; } int scanf_(const char *format, ...) { return *format ? EOF : 0; } #else /* SCANF_SSCANF_ONLY */ static int getchw_(void *arg) { return 0; } static void ungetchw_(int c, void *arg) { (void)arg; } #if 0 /* TODO move to libc-io */ int vscanf(const char *format, va_list arg) { return iscanf_(&getchw_, &ungetchw_, NULL, format, arg); } int scanf(const char *format, ...) { int r; va_list va; va_start(va, format); r = vscanf(format, va); va_end(va); return r; } #endif #endif /* SCANF_SSCANF_ONLY */ int vspscanf(const char **sp, const char *format, va_list arg) { int i = iscanf_(&F_(sscanw_), NULL, sp, format, arg); --*sp; /* back up by one character, even if it was EOF we want the pointer at the null terminator */ return i; } int spscanf(const char **sp, const char *format, ...) { int r; va_list va; va_start(va, format); r = vspscanf(sp, format, va); va_end(va); return r; } int vsscanf(const char *s, const char *format, va_list arg) { return iscanf_(&F_(sscanw_), NULL, &s, format, arg); } int sscanf(const char *s, const char *format, ...) { int r; va_list va; va_start(va, format); r = vsscanf(s, format, va); va_end(va); return r; } int vfctscanf( int (*getch)(void *data), void (*ungetch)(int c, void *data), void *data, const char *format, va_list arg) { return iscanf_(getch, ungetch, data, format, arg); } int __libc_fctscanf( int (*getch)(void *data), void (*ungetch)(int c, void *data), void *data, const char *format, ...) { int r; va_list va; va_start(va, format); r = vfctscanf(getch, ungetch, data, format, va); va_end(va); return r; } #endif /* SCANF_WIDE */ #if SCANF_WIDE >= 2 /* reinclude with SCANF_WIDE=0 to get narrow/multibyte functions */ #undef SCANF_WIDE #define SCANF_WIDE 0 #define SCANF_REPEAT #include "scanf.c" #endif