diff --git a/lib/libc/core/stdio/scanf.c b/lib/libc/core/stdio/scanf.c new file mode 100644 index 0000000..87aced9 --- /dev/null +++ b/lib/libc/core/stdio/scanf.c @@ -0,0 +1,2625 @@ +/* + +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