Files
rosetta/lib/libc/core/stdio/scanf.c
T

2626 lines
54 KiB
C

/*
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 <limits.h>
#include <stdarg.h>
#include <stddef.h>
/* =============================== *
* 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 <stdint.h>
#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 <wctype.h>
#define isdigitw_ iswdigit
#define isspacew_ iswspace
#define isalnumw_ iswalnum
#define isalphaw_ iswalpha
#define tolowerw_ towlower
#else
#include <ctype.h>
#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 <float.h>
#endif
#if !SCANF_NOMATH
#include <math.h>
#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