/*************************************************************** MPU-VFSCANF.C This file contains common formatted input engine for LIBMPUIO file and UCS-2 string streams. PART OF : MPUIO - library . NOTE : Format strings and string streams are UCS-2. Real files are decoded from UTF-8 by LIBMPUIO. Copyright (C) 2000 - 2026 by Andrey V.Kosteltsev. All Rights Reserved. ***************************************************************/ #ifdef HAVE_CONFIG_H #include #endif #include #include #include #define __MPU_SCAN_TOKEN_MAX 131072 #define __MPU_SCAN_MB_TOKEN_MAX ((size_t)__MPU_SCAN_TOKEN_MAX * MB_LEN_MAX + 8U) #define __MPU_SCAN_LEN_NONE 0 #define __MPU_SCAN_LEN_HH 1 #define __MPU_SCAN_LEN_H 2 #define __MPU_SCAN_LEN_L 3 #define __MPU_SCAN_LEN_LL 4 #define __MPU_SCAN_LEN_J 5 #define __MPU_SCAN_LEN_T 6 #define __MPU_SCAN_LEN_CAP_L 7 /*************************************************************** Return nonzero when C is an ASCII white-space character. ***************************************************************/ static int __mpu_IO_scan_space( __mpu_char16_t c ) { return( c == ' ' || c == '\t' || c == '\n' || c == '\r' || c == '\f' || c == '\v' ); } /* End of __mpu_IO_scan_space() */ /*************************************************************** Get one UCS-2 input character and update the consumed count. ***************************************************************/ static int __mpu_IO_scan_get( mpu_FILE *stream, __mpu_char16_t *c, int *nread ) { if( __mpu_IO_get_char_unlocked( stream, c ) == mpu_EOF ) return( mpu_EOF ); ++*nread; return( 0 ); } /* End of __mpu_IO_scan_get() */ /*************************************************************** Push one UCS-2 character back and correct the consumed count. ***************************************************************/ static int __mpu_IO_scan_unget( mpu_FILE *stream, __mpu_char16_t c, int *nread ) { if( __mpu_IO_unget_char_unlocked( c, stream ) == mpu_EOF ) return( mpu_EOF ); --*nread; return( 0 ); } /* End of __mpu_IO_scan_unget() */ /*************************************************************** Push COUNT characters from BUF back in their original order. Used by numeric parsers to roll back incomplete signs, radix prefixes and exponents without losing input characters. ***************************************************************/ static int __mpu_IO_scan_rollback( mpu_FILE *stream, const __mpu_char16_t *buf, size_t count, int *nread ) { while( count ) { if( __mpu_IO_scan_unget( stream, buf[count - 1], nread ) == mpu_EOF ) return( -1 ); --count; } return( 0 ); } /* End of __mpu_IO_scan_rollback() */ /*************************************************************** Skip input white space. The first non-space character is returned to STREAM so the next conversion can read it. ***************************************************************/ static int __mpu_IO_scan_skip_space( mpu_FILE *stream, int *nread ) { __mpu_char16_t c; while( __mpu_IO_scan_get( stream, &c, nread ) != mpu_EOF ) { if( !__mpu_IO_scan_space( c ) ) return( __mpu_IO_scan_unget( stream, c, nread ) ); } return( mpu_EOF ); } /* End of __mpu_IO_scan_skip_space() */ /*************************************************************** Convert a UCS-2 token containing ASCII syntax to char string. ***************************************************************/ static int __mpu_IO_scan_to_ascii( char *dst, size_t dst_size, const __mpu_char16_t *src, size_t len ) { size_t i; if( len + 1 > dst_size ) { errno = EOVERFLOW; return( -1 ); } for( i = 0; i < len; ++i ) { if( src[i] > 0x7f ) { errno = EILSEQ; return( -1 ); } dst[i] = (char)src[i]; } dst[len] = '\0'; return( 0 ); } /* End of __mpu_IO_scan_to_ascii() */ /*************************************************************** Convert the active locale decimal-point string to strict UCS-2. The returned buffer is allocated with malloc() and must be freed by the caller. Surrogates and non-UCS-2 characters are rejected. ***************************************************************/ static int __mpu_IO_scan_locale_decimal( __mpu_char16_t **point, size_t *length ) { struct lconv *lc; const char *src; const char *p; __mpu_char16_t *dst; mbstate_t state; size_t left; size_t n = 0; if( point == (__mpu_char16_t **)0 || length == (size_t *)0 ) { errno = EINVAL; return( -1 ); } lc = localeconv(); src = lc != (struct lconv *)0 ? lc->decimal_point : (const char *)0; if( src == (const char *)0 || *src == '\0' ) src = "."; left = strlen( src ); if( left > (SIZE_MAX / sizeof(__mpu_char16_t)) - 1U ) { errno = EOVERFLOW; return( -1 ); } dst = (__mpu_char16_t *)malloc( (left + 1U) * sizeof(__mpu_char16_t) ); if( dst == (__mpu_char16_t *)0 ) return( -1 ); memset( &state, 0, sizeof(state) ); p = src; while( left ) { char16_t c; size_t used; used = mbrtoc16( &c, p, left, &state ); if( used == (size_t)-1 || used == (size_t)-2 || used == (size_t)-3 ) { free( dst ); errno = EILSEQ; return( -1 ); } if( used == 0 ) break; if( c >= 0xd800U && c <= 0xdfffU ) { free( dst ); errno = EILSEQ; return( -1 ); } dst[n++] = (__mpu_char16_t)c; p += used; left -= used; } if( n == 0 ) { dst[n++] = (__mpu_char16_t)'.'; } dst[n] = 0; *point = dst; *length = n; return( 0 ); } /* End of __mpu_IO_scan_locale_decimal() */ /*************************************************************** Convert a strict UCS-2 numeric token to the active locale multibyte encoding. This is used before libc strtold() so the tokenizer and libc parser see the same LC_NUMERIC radix string. ***************************************************************/ static int __mpu_IO_scan_to_locale( char *dst, size_t dst_size, const __mpu_char16_t *src, size_t len ) { mbstate_t state; size_t i; size_t out = 0; memset( &state, 0, sizeof(state) ); for( i = 0; i < len; ++i ) { char tmp[MB_LEN_MAX]; size_t used; if( src[i] >= 0xd800U && src[i] <= 0xdfffU ) { errno = EILSEQ; return( -1 ); } used = c16rtomb( tmp, (char16_t)src[i], &state ); if( used == (size_t)-1 ) { errno = EILSEQ; return( -1 ); } if( used > dst_size - out - 1U ) { errno = EOVERFLOW; return( -1 ); } memcpy( dst + out, tmp, used ); out += used; } dst[out] = '\0'; return( 0 ); } /* End of __mpu_IO_scan_to_locale() */ /*************************************************************** Match the active locale decimal-point UCS-2 sequence after FIRST has already been consumed. On mismatch all inspected characters are restored, so the caller leaves the first non-token character untouched. Return 1 for a match, 0 for mismatch and -1 on error. ***************************************************************/ static int __mpu_IO_scan_match_decimal( mpu_FILE *stream, __mpu_char16_t first, const __mpu_char16_t *point, size_t point_len, __mpu_char16_t *buf, size_t *used, size_t limit, int *nread ) { size_t i; if( point_len == 0 || first != point[0] ) return( 0 ); if( point_len > limit - *used ) { if( __mpu_IO_scan_unget( stream, first, nread ) == mpu_EOF ) return( -1 ); return( 0 ); } for( i = 1; i < point_len; ++i ) { __mpu_char16_t c; if( __mpu_IO_scan_get( stream, &c, nread ) == mpu_EOF ) { while( i ) { --i; if( __mpu_IO_scan_unget( stream, point[i], nread ) == mpu_EOF ) return( -1 ); } return( 0 ); } if( c != point[i] ) { if( __mpu_IO_scan_unget( stream, c, nread ) == mpu_EOF ) return( -1 ); while( i ) { --i; if( __mpu_IO_scan_unget( stream, point[i], nread ) == mpu_EOF ) return( -1 ); } return( 0 ); } } memcpy( buf + *used, point, point_len * sizeof(__mpu_char16_t) ); *used += point_len; return( 1 ); } /* End of __mpu_IO_scan_match_decimal() */ /*************************************************************** Return nonzero for a valid MPU integer size modifier. Integer formatted I/O is independent of MPU_REAL_IO_LIMIT and supports the full LibMPU integer range through 65536 bits. ***************************************************************/ static int __mpu_IO_scan_valid_mpu_size( unsigned bits ) { switch( bits ) { case 8: case 16: case 32: case 64: case 128: case 256: case 512: case 1024: case 2048: case 4096: case 8192: case 16384: case 32768: case 65536: return( 1 ); default: return( 0 ); } } /* End of __mpu_IO_scan_valid_mpu_size() */ static int __mpu_IO_scan_valid_mpu_bits( unsigned bits, int real_or_complex ) { if( !__mpu_IO_scan_valid_mpu_size( bits ) ) return( 0 ); if( real_or_complex ) { if( bits < 32 ) return( 0 ); if( bits > (unsigned)MPU_REAL_IO_LIMIT ) return( 0 ); } return( 1 ); } /* End of __mpu_IO_scan_valid_mpu_bits() */ /*************************************************************** Read a non-space token up to WIDTH UCS-2 characters. WIDTH==0 means no explicit field width. The result is not NUL-terminated. ***************************************************************/ static int __mpu_IO_scan_token( mpu_FILE *stream, __mpu_char16_t *buf, size_t capacity, int width, size_t *length, int *nread ) { __mpu_char16_t c; size_t n = 0; size_t limit; limit = width > 0 ? (size_t)width : capacity; if( limit > capacity ) limit = capacity; while( n < limit ) { if( __mpu_IO_scan_get( stream, &c, nread ) == mpu_EOF ) break; if( __mpu_IO_scan_space( c ) ) { if( __mpu_IO_scan_unget( stream, c, nread ) == mpu_EOF ) return( -1 ); break; } buf[n++] = c; } *length = n; return( n ? 0 : -1 ); } /* End of __mpu_IO_scan_token() */ /*************************************************************** Compare one UCS-2 ASCII character case-insensitively. ***************************************************************/ static int __mpu_IO_scan_ascii_equal( __mpu_char16_t c, char a ) { if( c >= 'A' && c <= 'Z' ) c = (__mpu_char16_t)(c - 'A' + 'a'); if( a >= 'A' && a <= 'Z' ) a = (char)(a - 'A' + 'a'); return( c == (__mpu_char16_t)(unsigned char)a ); } /* End of __mpu_IO_scan_ascii_equal() */ /*************************************************************** Return nonzero when C may delimit the exponent/type part of a LIBMPU real component. ***************************************************************/ static int __mpu_IO_scan_mpu_exp_delim( __mpu_char16_t c ) { return( c == 'e' || c == 'E' || c == 'r' || c == 'R' || c == 'j' || c == 'J' ); } /* End of __mpu_IO_scan_mpu_exp_delim() */ /*************************************************************** Convert one already delimited ASCII LIBMPU real token. ascii_to_real() accepts historical shorthand forms which scanf must not classify as numeric tokens. For an ordinary finite number the scanner grammar is stricter: - the mantissa must contain at least one decimal digit; - the mantissa may contain at most one decimal point; - if e/r/j is present, it may be followed by an optional sign, but at least one decimal digit must follow it. Therefore ".", ".e", ".r", ".j", ".e0", ".r0" and ".j1" are rejected. ".1", "1.", "1.0", "1e3", ".1e-3", "1.r0" and ".1j1" are accepted by this lexical check. Special inf/infinity/ind/NaN spellings keep their LIBMPU grammar and are passed unchanged to ascii_to_real(). ***************************************************************/ static int __mpu_IO_scan_mpu_ascii_to_real( mpu_real *dst, const char *token, int nb ) { const char *p; const char *exp; char sign = 0; char delim = 0; int have_mantissa_digit = 0; int mantissa_is_zero = 1; int have_dot = 0; p = token; while( *p == ' ' || *p == '\t' || *p == '\n' || *p == '\r' || *p == '\f' || *p == '\v' ) ++p; if( *p == '+' || *p == '-' ) { sign = *p++; while( *p == ' ' || *p == '\t' || *p == '\n' || *p == '\r' || *p == '\f' || *p == '\v' ) ++p; } /* ascii_to_real() recognizes the short complex special spellings inf_e/inf_r/inf_j. The stream lexer also accepts the standard long spelling infinity. When infinity carries a LIBMPU component suffix, canonicalize only that spelling to inf_x before asking ascii_to_real() to classify it. */ if( __mpu_IO_scan_ascii_equal( (__mpu_char16_t)(unsigned char)p[0], 'i' ) && __mpu_IO_scan_ascii_equal( (__mpu_char16_t)(unsigned char)p[1], 'n' ) && __mpu_IO_scan_ascii_equal( (__mpu_char16_t)(unsigned char)p[2], 'f' ) && __mpu_IO_scan_ascii_equal( (__mpu_char16_t)(unsigned char)p[3], 'i' ) && __mpu_IO_scan_ascii_equal( (__mpu_char16_t)(unsigned char)p[4], 'n' ) && __mpu_IO_scan_ascii_equal( (__mpu_char16_t)(unsigned char)p[5], 'i' ) && __mpu_IO_scan_ascii_equal( (__mpu_char16_t)(unsigned char)p[6], 't' ) && __mpu_IO_scan_ascii_equal( (__mpu_char16_t)(unsigned char)p[7], 'y' ) && p[8] == '_' && __mpu_IO_scan_mpu_exp_delim( (__mpu_char16_t)(unsigned char)p[9] ) && p[10] == '\0' ) { char canonical[8]; char *q = canonical; if( sign ) *q++ = sign; *q++ = 'i'; *q++ = 'n'; *q++ = 'f'; *q++ = '_'; *q++ = p[9]; *q = '\0'; return( ascii_to_real( dst, (__mpu_char8_t *)canonical, nb ) ); } /* Other special values are recognized separately by the stream lexer. Do not impose the ordinary mantissa/exponent grammar on them. */ if( *p == 'i' || *p == 'I' || *p == 'n' || *p == 'N' ) return( ascii_to_real( dst, (__mpu_char8_t *)token, nb ) ); /* Ordinary mantissa. At least one decimal digit is mandatory; the radix point by itself does not make a numeric token. */ while( *p ) { if( *p >= '0' && *p <= '9' ) { have_mantissa_digit = 1; if( *p != '0' ) mantissa_is_zero = 0; ++p; continue; } if( *p == '.' && !have_dot ) { have_dot = 1; ++p; continue; } break; } if( !have_mantissa_digit ) return( _ASCII_TO_REAL_ERROR ); /* LIBMPU uses e/r/j as the exponent/component delimiter. Once a delimiter is present, at least one exponent digit is mandatory, with an optional leading sign. */ if( __mpu_IO_scan_mpu_exp_delim( (__mpu_char16_t)(unsigned char)*p ) ) { delim = *p++; if( *p == '+' || *p == '-' ) ++p; exp = p; while( *p >= '0' && *p <= '9' ) ++p; if( p == exp ) return( _ASCII_TO_REAL_ERROR ); } if( *p != '\0' ) return( _ASCII_TO_REAL_ERROR ); /* ascii_to_real() historically rejects explicit zero-valued finite spellings. At this point the scanf grammar has already been validated, including the requirement for at least one mantissa digit and (when present) at least one e/r/j exponent digit. Preserve such valid zero spellings without re-admitting the forbidden digitless forms ".", ".e", ".r", ".j", etc. */ if( mantissa_is_zero ) { _signull( dst, sign == '-', nb ); if( delim == 'r' || delim == 'R' ) return( _REAL_PART_OF_COMPLEX ); if( delim == 'j' || delim == 'J' ) return( _IMAGINARY_OF_COMPLEX ); return( _LONGHAND_REAL_NUMBER ); } return( ascii_to_real( dst, (__mpu_char8_t *)token, nb ) ); } /* End of __mpu_IO_scan_mpu_ascii_to_real() */ /*************************************************************** Read one character into BUF when field width/capacity permits. No look-ahead character is consumed after the limit. ***************************************************************/ static int __mpu_IO_scan_mpu_store_get( mpu_FILE *stream, __mpu_char16_t *buf, size_t *length, size_t limit, __mpu_char16_t *c, int *nread ) { if( *length >= limit ) return( 0 ); if( __mpu_IO_scan_get( stream, c, nread ) == mpu_EOF ) return( 0 ); buf[(*length)++] = *c; return( 1 ); } /* End of __mpu_IO_scan_mpu_store_get() */ /*************************************************************** Undo a short speculative suffix already appended to BUF. This helper is only used for the fixed "inity" and "_x" look-aheads, so COUNT is bounded by five characters and fits the existing mpu_FILE pushback stack. ***************************************************************/ static int __mpu_IO_scan_mpu_unstore( mpu_FILE *stream, __mpu_char16_t *buf, size_t *length, size_t count, int *nread ) { if( count > *length ) return( -1 ); if( __mpu_IO_scan_rollback( stream, buf + *length - count, count, nread ) != 0 ) return( -1 ); *length -= count; return( 0 ); } /* End of __mpu_IO_scan_mpu_unstore() */ /*************************************************************** Read an optional "_e/_r/_j" suffix after an already recognized three-letter special value. Invalid/incomplete suffix text is left unread; the base inf/ind/NaN token remains accepted. ***************************************************************/ static int __mpu_IO_scan_mpu_special_suffix( mpu_FILE *stream, __mpu_char16_t *buf, size_t *length, size_t limit, int *nread ) { __mpu_char16_t c; if( !__mpu_IO_scan_mpu_store_get( stream, buf, length, limit, &c, nread ) ) return( 0 ); if( c != '_' ) { return( __mpu_IO_scan_mpu_unstore( stream, buf, length, 1U, nread ) ); } if( !__mpu_IO_scan_mpu_store_get( stream, buf, length, limit, &c, nread ) ) { return( __mpu_IO_scan_mpu_unstore( stream, buf, length, 1U, nread ) ); } if( !__mpu_IO_scan_mpu_exp_delim( c ) ) { return( __mpu_IO_scan_mpu_unstore( stream, buf, length, 2U, nread ) ); } return( 0 ); } /* End of __mpu_IO_scan_mpu_special_suffix() */ /*************************************************************** Read the optional "inity" tail after an already recognized "inf". A partial/nonmatching tail is rolled back, leaving exactly "inf" consumed. At most five characters are speculative. ***************************************************************/ static int __mpu_IO_scan_mpu_infinity_tail( mpu_FILE *stream, __mpu_char16_t *buf, size_t *length, size_t limit, int *nread ) { static const char tail[] = "inity"; size_t base = *length; size_t i; __mpu_char16_t c; for( i = 0; i < sizeof(tail) - 1U; ++i ) { if( !__mpu_IO_scan_mpu_store_get( stream, buf, length, limit, &c, nread ) ) { if( *length != base && __mpu_IO_scan_mpu_unstore( stream, buf, length, *length - base, nread ) != 0 ) return( -1 ); return( 0 ); } if( !__mpu_IO_scan_ascii_equal( c, tail[i] ) ) { if( __mpu_IO_scan_mpu_unstore( stream, buf, length, *length - base, nread ) != 0 ) return( -1 ); return( 0 ); } } return( 1 ); } /* End of __mpu_IO_scan_mpu_infinity_tail() */ /*************************************************************** Read one possible LIBMPU real component without scanning past its lexical boundary. The previous implementation first consumed the whole non-space input item and then rolled the unused tail back. That is invalid for mpu_FILE because its internal pushback stack is intentionally small (eight UCS-2 characters); a normal value such as "1.250000R+0-2.500000J+0" therefore overflowed pushback while separating the two complex components. This lexer consumes the LIBMPU lexical form incrementally: [sign [space...]] (special | mantissa [e/r/j exponent]) The stream lexer only delimits a candidate component. Final scanf validation requires at least one decimal digit in the mantissa and, when e/r/j is present, at least one exponent digit. Thus ".", ".e", ".r", ".j", ".e0", ".r0" and ".j1" are not numeric conversions. Only a single ordinary boundary character is ever pushed back; the fixed special-value probes use at most five characters. Numeric meaning and component class are not decided here. ascii_to_real() remains the sole semantic authority. ***************************************************************/ static int __mpu_IO_scan_mpu_component_token( mpu_FILE *stream, __mpu_char16_t *buf, size_t capacity, int width, size_t *length, int *nread ) { __mpu_char16_t c; size_t n = 0; size_t body; size_t limit; int have_dot = 0; limit = width > 0 ? (size_t)width : capacity; if( limit > capacity ) limit = capacity; if( limit == 0 ) return( -1 ); if( !__mpu_IO_scan_mpu_store_get( stream, buf, &n, limit, &c, nread ) ) return( -1 ); if( __mpu_IO_scan_space( c ) ) { if( __mpu_IO_scan_mpu_unstore( stream, buf, &n, 1U, nread ) != 0 ) return( -1 ); return( -1 ); } if( c == '+' || c == '-' ) { for( ;; ) { if( !__mpu_IO_scan_mpu_store_get( stream, buf, &n, limit, &c, nread ) ) break; if( !__mpu_IO_scan_space( c ) ) break; } if( n == 1U || __mpu_IO_scan_space( buf[n - 1U] ) ) { *length = n; return( 0 ); } } body = (buf[0] == '+' || buf[0] == '-') ? 1U : 0U; while( body < n && __mpu_IO_scan_space( buf[body] ) ) ++body; if( body >= n ) { *length = n; return( 0 ); } c = buf[body]; /* Recognize only the conventional high-level spellings of special values. ascii_to_real() itself intentionally accepts "inf" as a prefix; this scanner consumes full "infinity" when present and otherwise leaves the following text unread. */ if( __mpu_IO_scan_ascii_equal( c, 'i' ) ) { __mpu_char16_t c2, c3; if( !__mpu_IO_scan_mpu_store_get( stream, buf, &n, limit, &c2, nread ) || !__mpu_IO_scan_ascii_equal( c2, 'n' ) ) { if( n > body + 1U && __mpu_IO_scan_mpu_unstore( stream, buf, &n, 1U, nread ) != 0 ) return( -1 ); *length = n; return( 0 ); } if( !__mpu_IO_scan_mpu_store_get( stream, buf, &n, limit, &c3, nread ) ) { *length = n; return( 0 ); } if( !__mpu_IO_scan_ascii_equal( c3, 'f' ) && !__mpu_IO_scan_ascii_equal( c3, 'd' ) ) { if( __mpu_IO_scan_mpu_unstore( stream, buf, &n, 1U, nread ) != 0 ) return( -1 ); *length = n; return( 0 ); } if( __mpu_IO_scan_ascii_equal( c3, 'f' ) ) { int full_infinity = __mpu_IO_scan_mpu_infinity_tail( stream, buf, &n, limit, nread ); if( full_infinity < 0 ) return( -1 ); /* Whether the recognized spelling is "inf" or "infinity", allow the same optional LIBMPU component suffix _e/_r/_j. */ } if( __mpu_IO_scan_mpu_special_suffix( stream, buf, &n, limit, nread ) != 0 ) return( -1 ); *length = n; return( 0 ); } if( __mpu_IO_scan_ascii_equal( c, 'n' ) ) { __mpu_char16_t c2, c3; if( !__mpu_IO_scan_mpu_store_get( stream, buf, &n, limit, &c2, nread ) || !__mpu_IO_scan_ascii_equal( c2, 'a' ) ) { if( n > body + 1U && __mpu_IO_scan_mpu_unstore( stream, buf, &n, 1U, nread ) != 0 ) return( -1 ); *length = n; return( 0 ); } if( !__mpu_IO_scan_mpu_store_get( stream, buf, &n, limit, &c3, nread ) || !__mpu_IO_scan_ascii_equal( c3, 'n' ) ) { if( n > body + 2U && __mpu_IO_scan_mpu_unstore( stream, buf, &n, 1U, nread ) != 0 ) return( -1 ); *length = n; return( 0 ); } if( __mpu_IO_scan_mpu_special_suffix( stream, buf, &n, limit, nread ) != 0 ) return( -1 ); *length = n; return( 0 ); } /* Ordinary numeric component. The first body character is already buffered. Digits and at most one decimal point precede the e/r/j delimiter. The exponent consists of optional sign and zero or more digits; zero digits are valid in LIBMPU. */ if( c == '.' ) have_dot = 1; else if( __mpu_IO_scan_mpu_exp_delim( c ) ) { *length = n; return( 0 ); } else if( c < '0' || c > '9' ) { *length = n; return( 0 ); } for( ;; ) { if( !__mpu_IO_scan_mpu_store_get( stream, buf, &n, limit, &c, nread ) ) break; if( c >= '0' && c <= '9' ) continue; if( c == '.' && !have_dot ) { have_dot = 1; continue; } if( __mpu_IO_scan_mpu_exp_delim( c ) ) goto exponent; if( __mpu_IO_scan_mpu_unstore( stream, buf, &n, 1U, nread ) != 0 ) return( -1 ); break; } *length = n; return( 0 ); exponent: if( !__mpu_IO_scan_mpu_store_get( stream, buf, &n, limit, &c, nread ) ) { *length = n; return( 0 ); } if( c == '+' || c == '-' ) { if( !__mpu_IO_scan_mpu_store_get( stream, buf, &n, limit, &c, nread ) ) { *length = n; return( 0 ); } } if( c < '0' || c > '9' ) { if( __mpu_IO_scan_mpu_unstore( stream, buf, &n, 1U, nread ) != 0 ) return( -1 ); *length = n; return( 0 ); } for( ;; ) { if( !__mpu_IO_scan_mpu_store_get( stream, buf, &n, limit, &c, nread ) ) break; if( c < '0' || c > '9' ) { if( __mpu_IO_scan_mpu_unstore( stream, buf, &n, 1U, nread ) != 0 ) return( -1 ); break; } } *length = n; return( 0 ); } /* End of __mpu_IO_scan_mpu_component_token() */ /*************************************************************** Ask LIBMPU to classify one already delimited UCS-2 component. The token syntax is ASCII, while storage remains UCS-2. ***************************************************************/ static int __mpu_IO_scan_mpu_component_kind( const __mpu_char16_t *buf, size_t length, unsigned bits, int *kind ) { mpu_wide_real_t value; char *ascii; int rc; int nb = (int)(bits / 8U); ascii = (char *)malloc( length + 1U ); if( ascii == (char *)0 ) return( -1 ); if( __mpu_IO_scan_to_ascii( ascii, length + 1U, buf, length ) != 0 ) { free( ascii ); return( -1 ); } rc = __mpu_IO_scan_mpu_ascii_to_real( value, ascii, nb ); free( ascii ); if( rc == _ASCII_TO_REAL_ERROR ) return( -1 ); *kind = rc; return( 0 ); } /* End of __mpu_IO_scan_mpu_component_kind() */ /*************************************************************** Read one LIBMPU complex token. A complex value consists of either: - one imaginary component; - one real component; - one real component followed by one imaginary component. White space is allowed between the two components and after a leading sign. The first and second component classes are obtained from ascii_to_real(), not inferred by this scanner. FIRST_LENGTH, SECOND_OFFSET and SECOND_LENGTH describe the exact subranges later passed independently to ascii_to_real(). If no second component exists, SECOND_LENGTH is zero. When a probe for an imaginary component fails, all probed input (including the gap) is rolled back, preserving scanf input semantics. ***************************************************************/ static int __mpu_IO_scan_mpu_complex_token( mpu_FILE *stream, __mpu_char16_t *buf, size_t capacity, int width, unsigned bits, size_t *length, size_t *first_length, size_t *second_offset, size_t *second_length, int *nread ) { __mpu_char16_t c; size_t first_len; size_t gap = 0; size_t second_len = 0; size_t limit; int first_kind; int second_kind; int second_width; limit = width > 0 ? (size_t)width : capacity; if( limit > capacity ) limit = capacity; *first_length = 0; *second_offset = 0; *second_length = 0; if( __mpu_IO_scan_mpu_component_token( stream, buf, limit, width, &first_len, nread ) != 0 ) return( -1 ); if( __mpu_IO_scan_mpu_component_kind( buf, first_len, bits, &first_kind ) != 0 ) { /* Allocation failure is fatal. Rolling a long token back can overflow mpu_FILE's small pushback area and hide ENOMEM behind ENOSPC, so leave the consumed token in place on this path. */ if( errno == ENOMEM ) return( -1 ); if( __mpu_IO_scan_rollback( stream, buf, first_len, nread ) != 0 ) return( -1 ); return( -1 ); } if( first_kind == _IMAGINARY_OF_COMPLEX ) { *length = first_len; *first_length = first_len; return( 0 ); } /* Historical complex syntax requires an explicit r/R real component. A longhand e/E real belongs to a real conversion. */ if( first_kind != _REAL_PART_OF_COMPLEX ) { if( __mpu_IO_scan_rollback( stream, buf, first_len, nread ) != 0 ) return( -1 ); return( -1 ); } if( first_len >= limit ) { *length = first_len; *first_length = first_len; return( 0 ); } while( first_len + gap < limit ) { if( __mpu_IO_scan_get( stream, &c, nread ) == mpu_EOF ) break; if( !__mpu_IO_scan_space( c ) ) { if( __mpu_IO_scan_unget( stream, c, nread ) == mpu_EOF ) return( -1 ); break; } buf[first_len + gap++] = c; } if( first_len + gap >= limit ) { if( gap && __mpu_IO_scan_rollback( stream, buf + first_len, gap, nread ) != 0 ) return( -1 ); *length = first_len; *first_length = first_len; return( 0 ); } second_width = width > 0 ? width - (int)(first_len + gap) : 0; if( __mpu_IO_scan_mpu_component_token( stream, buf + first_len + gap, limit - first_len - gap, second_width, &second_len, nread ) == 0 ) { if( __mpu_IO_scan_mpu_component_kind( buf + first_len + gap, second_len, bits, &second_kind ) == 0 && second_kind == _IMAGINARY_OF_COMPLEX ) { *length = first_len + gap + second_len; *first_length = first_len; *second_offset = first_len + gap; *second_length = second_len; return( 0 ); } /* As above, do not turn a deterministic ENOMEM into a pushback overflow while probing the optional imaginary component. */ if( errno == ENOMEM ) return( -1 ); if( __mpu_IO_scan_rollback( stream, buf + first_len + gap, second_len, nread ) != 0 ) return( -1 ); } if( gap && __mpu_IO_scan_rollback( stream, buf + first_len, gap, nread ) != 0 ) return( -1 ); *length = first_len; *first_length = first_len; return( 0 ); } /* End of __mpu_IO_scan_mpu_complex_token() */ /*************************************************************** Return nonzero when C is accepted by an integer conversion using BASE. BASE must be 2, 8, 10 or 16. ***************************************************************/ static int __mpu_IO_scan_digit_for_base( __mpu_char16_t c, int base ) { if( c >= '0' && c <= '9' ) return( (int)(c - '0') < base ); if( base == 16 ) { if( c >= 'a' && c <= 'f' ) return( 1 ); if( c >= 'A' && c <= 'F' ) return( 1 ); } return( 0 ); } /* End of __mpu_IO_scan_digit_for_base() */ /*************************************************************** Read one integer token without consuming the first character that cannot belong to the requested conversion. For BASE==0 the usual scanf %i prefixes 0, 0x and 0b are recognized. ***************************************************************/ static int __mpu_IO_scan_integer_token( mpu_FILE *stream, __mpu_char16_t *buf, size_t capacity, int width, int base, size_t *length, int *nread ) { __mpu_char16_t c; size_t n = 0; size_t limit; int actual_base = base; int have_digit = 0; int leading_zero = 0; int prefix_ok = 0; limit = width > 0 ? (size_t)width : capacity; if( limit > capacity ) limit = capacity; while( n < limit ) { if( __mpu_IO_scan_get( stream, &c, nread ) == mpu_EOF ) break; if( n == 0 && (c == '+' || c == '-') ) { buf[n++] = c; continue; } if( !have_digit ) { int after_prefix = n > 0 && (buf[n - 1] == 'x' || buf[n - 1] == 'X' || buf[n - 1] == 'b' || buf[n - 1] == 'B'); if( actual_base == 0 ) { if( c < '0' || c > '9' ) { __mpu_IO_scan_unget( stream, c, nread ); break; } if( c == '0' ) { actual_base = 8; leading_zero = 1; prefix_ok = 1; } else actual_base = 10; } else if( !after_prefix && c == '0' && (actual_base == 16 || actual_base == 2) ) { leading_zero = 1; prefix_ok = 1; } if( !__mpu_IO_scan_digit_for_base( c, actual_base ) ) { __mpu_IO_scan_unget( stream, c, nread ); break; } buf[n++] = c; have_digit = 1; continue; } if( prefix_ok && leading_zero && ((c == 'x' || c == 'X') && (base == 0 || base == 16)) ) { actual_base = 16; prefix_ok = 0; buf[n++] = c; have_digit = 0; continue; } if( prefix_ok && leading_zero && ((c == 'b' || c == 'B') && (base == 0 || base == 2)) ) { actual_base = 2; prefix_ok = 0; buf[n++] = c; have_digit = 0; continue; } prefix_ok = 0; if( !__mpu_IO_scan_digit_for_base( c, actual_base ) ) { __mpu_IO_scan_unget( stream, c, nread ); break; } buf[n++] = c; have_digit = 1; } if( !have_digit ) { if( n && (buf[n - 1] == 'x' || buf[n - 1] == 'X' || buf[n - 1] == 'b' || buf[n - 1] == 'B') ) { if( __mpu_IO_scan_rollback( stream, &buf[n - 1], 1, nread ) != 0 ) return( -1 ); --n; have_digit = 1; } else { if( n && __mpu_IO_scan_rollback( stream, buf, n, nread ) != 0 ) return( -1 ); *length = 0; return( -1 ); } } *length = n; return( 0 ); } /* End of __mpu_IO_scan_integer_token() */ /*************************************************************** Read one ordinary floating-point token. The first character not belonging to the token is left in STREAM. ***************************************************************/ static int __mpu_IO_scan_real_token( mpu_FILE *stream, __mpu_char16_t *buf, size_t capacity, int width, size_t *length, int *nread ) { __mpu_char16_t c; __mpu_char16_t *decimal = (__mpu_char16_t *)0; size_t decimal_len = 0; size_t n = 0; size_t limit; size_t exp_pos = 0; int have_digit = 0; int have_dot = 0; int have_exp = 0; int exp_digit = 0; int exp_sign_ok = 0; int special = 0; int nan_payload = 0; int nan_payload_closed = 0; int result = -1; limit = width > 0 ? (size_t)width : capacity; if( limit > capacity ) limit = capacity; if( __mpu_IO_scan_locale_decimal( &decimal, &decimal_len ) != 0 ) return( -1 ); while( n < limit ) { if( __mpu_IO_scan_get( stream, &c, nread ) == mpu_EOF ) break; if( n == 0 && (c == '+' || c == '-') ) { buf[n++] = c; continue; } if( !have_digit && !have_dot && !have_exp && (c == 'i' || c == 'I' || c == 'n' || c == 'N') ) { buf[n++] = c; special = 1; continue; } if( special ) { size_t off = (buf[0] == '+' || buf[0] == '-') ? 1U : 0U; if( nan_payload_closed ) { __mpu_IO_scan_unget( stream, c, nread ); break; } if( nan_payload ) { if( (c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z') || (c >= '0' && c <= '9') || c == '_' ) { buf[n++] = c; continue; } if( c == ')' ) { buf[n++] = c; nan_payload_closed = 1; continue; } __mpu_IO_scan_unget( stream, c, nread ); break; } if( (c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z') ) { buf[n++] = c; continue; } if( c == '(' && n - off == 3 && (buf[off] == 'n' || buf[off] == 'N') && (buf[off + 1] == 'a' || buf[off + 1] == 'A') && (buf[off + 2] == 'n' || buf[off + 2] == 'N') ) { buf[n++] = c; nan_payload = 1; continue; } __mpu_IO_scan_unget( stream, c, nread ); break; } if( c >= '0' && c <= '9' ) { buf[n++] = c; if( have_exp ) exp_digit = 1; else have_digit = 1; exp_sign_ok = 0; continue; } if( !have_dot && !have_exp && c == decimal[0] ) { int match; match = __mpu_IO_scan_match_decimal( stream, c, decimal, decimal_len, buf, &n, limit, nread ); if( match < 0 ) goto done; if( match > 0 ) { have_dot = 1; continue; } break; } if( (c == 'e' || c == 'E') && have_digit && !have_exp ) { exp_pos = n; buf[n++] = c; have_exp = 1; exp_sign_ok = 1; continue; } if( (c == '+' || c == '-') && exp_sign_ok ) { buf[n++] = c; exp_sign_ok = 0; continue; } __mpu_IO_scan_unget( stream, c, nread ); break; } if( special ) { char tmp[64]; size_t i, off = (buf[0] == '+' || buf[0] == '-') ? 1U : 0U; size_t base_len = n - off; if( nan_payload ) { size_t open = off; if( !nan_payload_closed ) goto bad_token; while( open < n && buf[open] != '(' ) ++open; base_len = open - off; } if( base_len >= sizeof(tmp) ) goto bad_token; for( i = 0; i < base_len; ++i ) tmp[i] = (char)tolower( (unsigned char)buf[off + i] ); tmp[base_len] = '\0'; if( strcmp( tmp, "inf" ) != 0 && strcmp( tmp, "infinity" ) != 0 && strcmp( tmp, "nan" ) != 0 ) goto bad_token; *length = n; result = 0; goto done; } if( have_exp && !exp_digit ) { if( __mpu_IO_scan_rollback( stream, &buf[exp_pos], n - exp_pos, nread ) != 0 ) goto done; n = exp_pos; have_exp = 0; } if( !have_digit ) { bad_token: if( n && __mpu_IO_scan_rollback( stream, buf, n, nread ) != 0 ) goto done; *length = 0; goto done; } *length = n; result = 0; done: free( decimal ); return( result ); } /* End of __mpu_IO_scan_real_token() */ /*************************************************************** Test C against the scanf %[ ... ] character list between BEGIN and END. '^' inversion is supplied separately. ***************************************************************/ static int __mpu_IO_scan_scanset_match( __mpu_char16_t c, const __mpu_char16_t *begin, const __mpu_char16_t *end, int invert ) { const __mpu_char16_t *p = begin; int match = 0; while( p < end ) { __mpu_char16_t first = *p++; if( p + 1 < end && *p == '-' ) { __mpu_char16_t last = p[1]; if( first <= c && c <= last ) match = 1; p += 2; } else if( c == first ) match = 1; } return( invert ? !match : match ); } /* End of __mpu_IO_scan_scanset_match() */ /*************************************************************** Convert a complete ordinary signed integer token. Reject any scanner/parser disagreement that would leave trailing bytes. ***************************************************************/ static int __mpu_IO_scan_parse_signed( const char *text, int base, intmax_t *value ) { char *end; intmax_t v; v = strtoimax( text, &end, base ); if( end == text || *end != '\0' ) return( -1 ); *value = v; return( 0 ); } /* End of __mpu_IO_scan_parse_signed() */ /*************************************************************** Convert a complete ordinary unsigned integer token. Binary input accepts the LIBMPUIO 0b/0B prefix before strtoumax(). ***************************************************************/ static int __mpu_IO_scan_parse_unsigned( const char *text, int base, uintmax_t *value ) { const char *p = text; char *end; uintmax_t v; if( base == 2 && p[0] == '0' && (p[1] == 'b' || p[1] == 'B') ) p += 2; v = strtoumax( p, &end, base ); if( end == p || *end != '\0' ) return( -1 ); *value = v; return( 0 ); } /* End of __mpu_IO_scan_parse_unsigned() */ /*************************************************************** Store a signed ordinary integer according to scanf length flag. ***************************************************************/ static void __mpu_IO_scan_store_signed( va_list *args, int length, intmax_t value ) { switch( length ) { case __MPU_SCAN_LEN_HH: *va_arg( *args, signed char * ) = (signed char)value; break; case __MPU_SCAN_LEN_H: *va_arg( *args, short * ) = (short)value; break; case __MPU_SCAN_LEN_L: *va_arg( *args, long * ) = (long)value; break; case __MPU_SCAN_LEN_LL: *va_arg( *args, long long * ) = (long long)value; break; case __MPU_SCAN_LEN_J: *va_arg( *args, intmax_t * ) = value; break; case __MPU_SCAN_LEN_T: *va_arg( *args, ptrdiff_t * ) = (ptrdiff_t)value; break; default: *va_arg( *args, int * ) = (int)value; break; } } /* End of __mpu_IO_scan_store_signed() */ /*************************************************************** Store an unsigned ordinary integer according to scanf length. ***************************************************************/ static void __mpu_IO_scan_store_unsigned( va_list *args, int length, uintmax_t value ) { switch( length ) { case __MPU_SCAN_LEN_HH: *va_arg( *args, unsigned char * ) = (unsigned char)value; break; case __MPU_SCAN_LEN_H: *va_arg( *args, unsigned short * ) = (unsigned short)value; break; case __MPU_SCAN_LEN_L: *va_arg( *args, unsigned long * ) = (unsigned long)value; break; case __MPU_SCAN_LEN_LL: *va_arg( *args, unsigned long long * ) = (unsigned long long)value; break; case __MPU_SCAN_LEN_J: *va_arg( *args, uintmax_t * ) = value; break; case __MPU_SCAN_LEN_T: *va_arg( *args, uintptr_t * ) = (uintptr_t)value; break; default: *va_arg( *args, unsigned int * ) = (unsigned int)value; break; } } /* End of __mpu_IO_scan_store_unsigned() */ /*************************************************************** Convert an MPU integer token using libmpu iatoi()/iatoui(). Non-decimal scanf conversions are normalized with a radix prefix. ***************************************************************/ static int __mpu_IO_scan_mpu_integer( void *dst, unsigned bits, int base, int is_signed, const char *token ) { char *tmp; size_t len; size_t extra = 3; int nb = (int)(bits / 8U); const char *p = token; char *q; len = strlen( token ); tmp = (char *)malloc( len + extra + 1 ); if( tmp == (char *)0 ) return( -1 ); q = tmp; if( *p == '+' || *p == '-' ) *q++ = *p++; if( base == 16 && !(p[0] == '0' && (p[1] == 'x' || p[1] == 'X')) ) { *q++ = '0'; *q++ = 'x'; } else if( base == 2 && !(p[0] == '0' && (p[1] == 'b' || p[1] == 'B')) ) { *q++ = '0'; *q++ = 'b'; } else if( base == 8 && *p != '0' ) { *q++ = '0'; } strcpy( q, p ); if( is_signed ) iatoi( (mpu_int *)dst, (__mpu_char8_t *)tmp, nb ); else iatoui( (mpu_int *)dst, (__mpu_char8_t *)tmp, nb ); free( tmp ); return( 0 ); } /* End of __mpu_IO_scan_mpu_integer() */ /*************************************************************** Convert one MPU real token using libmpu ascii_to_real(). ***************************************************************/ static int __mpu_IO_scan_mpu_real( void *dst, unsigned bits, const char *token ) { int nb = (int)(bits / 8U); int kind; kind = __mpu_IO_scan_mpu_ascii_to_real( (mpu_real *)dst, token, nb ); if( kind != _LONGHAND_REAL_NUMBER ) return( -1 ); return( 0 ); } /* End of __mpu_IO_scan_mpu_real() */ /*************************************************************** Convert one complete LIBMPU complex token using the exact component ranges already established by the stream scanner. Each component is passed independently to ascii_to_real(); no second copy of the LIBMPU numeric grammar is implemented here. Missing real or imaginary components are initialized to exact zero. DST is not modified unless all present components are valid and have the expected LIBMPU class. ***************************************************************/ static int __mpu_IO_scan_mpu_complex( void *dst, unsigned bits, const char *token, size_t first_length, size_t second_offset, size_t second_length ) { mpu_wide_real_t re; mpu_wide_real_t im; mpu_wide_real_t part; char *tmp; size_t max_length; int nb = (int)(bits / 8U); int kind; if( first_length == 0 ) return( -1 ); max_length = first_length; if( second_length > max_length ) max_length = second_length; tmp = (char *)malloc( max_length + 1U ); if( tmp == (char *)0 ) return( -1 ); _m_zero( re, nb ); _m_zero( im, nb ); memcpy( tmp, token, first_length ); tmp[first_length] = '\0'; kind = __mpu_IO_scan_mpu_ascii_to_real( part, tmp, nb ); if( kind == _IMAGINARY_OF_COMPLEX ) { if( second_length != 0 ) { free( tmp ); return( -1 ); } r_cpy( im, part, nb ); } else if( kind == _REAL_PART_OF_COMPLEX ) { r_cpy( re, part, nb ); if( second_length != 0 ) { memcpy( tmp, token + second_offset, second_length ); tmp[second_length] = '\0'; kind = __mpu_IO_scan_mpu_ascii_to_real( part, tmp, nb ); if( kind != _IMAGINARY_OF_COMPLEX ) { free( tmp ); return( -1 ); } r_cpy( im, part, nb ); } } else { free( tmp ); return( -1 ); } c_gen_complex( (mpu_complex *)dst, re, im, nb ); free( tmp ); return( 0 ); } /* End of __mpu_IO_scan_mpu_complex() */ /*************************************************************** Common scanf engine. Implements ordinary integer/string/char/ real conversions and MPU z/Z integer, real and complex input. Returns assignments count or mpu_EOF on input failure before the first assignment. ***************************************************************/ int __mpu_IO_vfscanf_unlocked( mpu_FILE *stream, const __mpu_char16_t *format, va_list args ) { __mpu_char16_t token[__MPU_SCAN_TOKEN_MAX]; char *ascii; va_list ap; int assigned = 0; int nread = 0; int input_failure = 0; if( !__mpu_IO_valid( stream ) || format == (const __mpu_char16_t *)0 ) { errno = EINVAL; return( mpu_EOF ); } ascii = (char *)malloc( __MPU_SCAN_MB_TOKEN_MAX ); if( ascii == (char *)0 ) return( mpu_EOF ); va_copy( ap, args ); while( *format ) { __mpu_char16_t fc = *format++; if( __mpu_IO_scan_space( fc ) ) { while( __mpu_IO_scan_space( *format ) ) ++format; if( __mpu_IO_scan_skip_space( stream, &nread ) == mpu_EOF ) input_failure = 1; continue; } if( fc != '%' ) { __mpu_char16_t c; if( __mpu_IO_scan_get( stream, &c, &nread ) == mpu_EOF ) { input_failure = 1; break; } if( c != fc ) { __mpu_IO_scan_unget( stream, c, &nread ); break; } continue; } if( *format == '%' ) { __mpu_char16_t c; ++format; if( __mpu_IO_scan_get( stream, &c, &nread ) == mpu_EOF ) { input_failure = 1; break; } if( c != '%' ) { __mpu_IO_scan_unget( stream, c, &nread ); break; } continue; } { int suppress = 0; int width = 0; int length = __MPU_SCAN_LEN_NONE; unsigned bits = 0; __mpu_char16_t conv; size_t token_len = 0; size_t complex_first_len = 0; size_t complex_second_off = 0; size_t complex_second_len = 0; int base = 10; int signed_conv = 0; for( ;; ) { if( *format == '*' ) { suppress = 1; ++format; continue; } if( *format >= '0' && *format <= '9' ) { width = 0; while( *format >= '0' && *format <= '9' ) { unsigned digit = (unsigned)(*format++ - '0'); if( width > (INT_MAX - (int)digit) / 10 ) { errno = EOVERFLOW; width = INT_MAX; while( *format >= '0' && *format <= '9' ) ++format; break; } width = width * 10 + (int)digit; } continue; } if( *format == 'z' || *format == 'Z' ) { ++format; bits = 0; while( *format >= '0' && *format <= '9' ) { unsigned digit = (unsigned)(*format++ - '0'); if( bits > (UINT_MAX - digit) / 10U ) { errno = EOVERFLOW; goto format_failure; } bits = bits * 10U + digit; } if( bits == 0 ) bits = 128; if( !__mpu_IO_scan_valid_mpu_size( bits ) ) { errno = EINVAL; goto format_failure; } continue; } if( *format == 'h' ) { ++format; if( *format == 'h' ) { ++format; length = __MPU_SCAN_LEN_HH; } else length = __MPU_SCAN_LEN_H; continue; } if( *format == 'l' ) { ++format; if( *format == 'l' ) { ++format; length = __MPU_SCAN_LEN_LL; } else length = __MPU_SCAN_LEN_L; continue; } if( *format == 'j' ) { if( bits ) break; if( format[1] == 'd' || format[1] == 'i' || format[1] == 'u' || format[1] == 'o' || format[1] == 'x' || format[1] == 'X' || format[1] == 'n' ) { ++format; length = __MPU_SCAN_LEN_J; continue; } break; } if( *format == 't' ) { ++format; length = __MPU_SCAN_LEN_T; continue; } if( *format == 'L' ) { ++format; length = __MPU_SCAN_LEN_CAP_L; continue; } break; } conv = *format++; if( conv == 0 ) break; if( conv != 'c' && conv != '[' && conv != 'n' ) { if( __mpu_IO_scan_skip_space( stream, &nread ) == mpu_EOF ) { input_failure = 1; break; } } if( conv == 'n' ) { if( !suppress ) { if( bits ) { char num[64]; snprintf( num, sizeof(num), "%d", nread ); if( __mpu_IO_scan_mpu_integer( va_arg( ap, void * ), bits, 10, 1, num ) != 0 ) break; } else __mpu_IO_scan_store_signed( &ap, length, (intmax_t)nread ); } continue; } if( conv == 'c' ) { int count = width ? width : 1; __mpu_char16_t *dst = suppress ? (__mpu_char16_t *)0 : va_arg( ap, __mpu_char16_t * ); int i; for( i = 0; i < count; ++i ) { __mpu_char16_t c; if( __mpu_IO_scan_get( stream, &c, &nread ) == mpu_EOF ) { input_failure = 1; break; } if( !suppress ) dst[i] = c; } if( i != count ) break; if( !suppress ) ++assigned; continue; } if( conv == 's' ) { __mpu_char16_t *dst = suppress ? (__mpu_char16_t *)0 : va_arg( ap, __mpu_char16_t * ); size_t i; if( __mpu_IO_scan_token( stream, token, __MPU_SCAN_TOKEN_MAX - 1, width, &token_len, &nread ) != 0 ) { input_failure = 1; break; } if( !suppress ) { for( i = 0; i < token_len; ++i ) dst[i] = token[i]; dst[token_len] = 0; ++assigned; } continue; } if( conv == 'a' ) { __mpu_char8_t *dst; if( bits || length != __MPU_SCAN_LEN_NONE ) { errno = EINVAL; break; } if( __mpu_IO_scan_token( stream, token, __MPU_SCAN_TOKEN_MAX - 1, width, &token_len, &nread ) != 0 ) { input_failure = 1; break; } if( !suppress ) { dst = va_arg( ap, __mpu_char8_t * ); if( __mpu_IO_scan_to_locale( (char *)dst, SIZE_MAX, token, token_len ) != 0 ) break; ++assigned; } continue; } if( conv == '[' ) { const __mpu_char16_t *set_begin; const __mpu_char16_t *set_end; __mpu_char16_t *dst; __mpu_char16_t c; size_t n = 0; size_t limit; int invert = 0; if( *format == '^' ) { invert = 1; ++format; } set_begin = format; if( *format == ']' ) ++format; while( *format && *format != ']' ) ++format; if( *format != ']' ) break; set_end = format; ++format; limit = width > 0 ? (size_t)width : (__MPU_SCAN_TOKEN_MAX - 1); dst = suppress ? (__mpu_char16_t *)0 : va_arg( ap, __mpu_char16_t * ); while( n < limit ) { if( __mpu_IO_scan_get( stream, &c, &nread ) == mpu_EOF ) { if( n == 0 ) input_failure = 1; break; } if( !__mpu_IO_scan_scanset_match( c, set_begin, set_end, invert ) ) { __mpu_IO_scan_unget( stream, c, &nread ); break; } if( !suppress ) dst[n] = c; ++n; } if( n == 0 ) break; if( !suppress ) { dst[n] = 0; ++assigned; } continue; } switch( conv ) { case 'd': case 'D': signed_conv = 1; base = 10; break; case 'i': signed_conv = 1; base = 0; break; case 'u': case 'U': signed_conv = 0; base = 10; break; case 'o': case 'O': signed_conv = 0; base = 8; break; case 'x': case 'X': signed_conv = 0; base = 16; break; case 'b': case 'B': signed_conv = 0; base = 2; break; case 'p': signed_conv = 0; base = 16; break; default: break; } if( conv == 'd' || conv == 'D' || conv == 'i' || conv == 'u' || conv == 'U' || conv == 'o' || conv == 'O' || conv == 'x' || conv == 'X' || conv == 'b' || conv == 'B' || conv == 'p' ) { if( __mpu_IO_scan_integer_token( stream, token, __MPU_SCAN_TOKEN_MAX - 1, width, base, &token_len, &nread ) != 0 ) { if( mpu_feof( stream ) ) input_failure = 1; break; } } else if( !bits && (conv == 'e' || conv == 'E' || conv == 'f' || conv == 'F' || conv == 'g' || conv == 'G') ) { if( __mpu_IO_scan_real_token( stream, token, __MPU_SCAN_TOKEN_MAX - 1, width, &token_len, &nread ) != 0 ) { if( mpu_feof( stream ) ) input_failure = 1; break; } } else if( conv == 'r' || conv == 'R' ) { if( !bits ) bits = 128; if( !__mpu_IO_scan_valid_mpu_bits( bits, 1 ) ) break; if( __mpu_IO_scan_mpu_component_token( stream, token, __MPU_SCAN_TOKEN_MAX - 1, width, &token_len, &nread ) != 0 ) { if( mpu_feof( stream ) ) input_failure = 1; break; } } else if( conv == 'j' || conv == 'J' ) { if( !bits ) bits = 128; if( !__mpu_IO_scan_valid_mpu_bits( bits, 1 ) ) break; if( __mpu_IO_scan_mpu_complex_token( stream, token, __MPU_SCAN_TOKEN_MAX - 1, width, bits, &token_len, &complex_first_len, &complex_second_off, &complex_second_len, &nread ) != 0 ) { if( mpu_feof( stream ) ) input_failure = 1; break; } } else if( bits && (conv == 'e' || conv == 'E' || conv == 'f' || conv == 'F' || conv == 'g' || conv == 'G') ) { if( __mpu_IO_scan_mpu_component_token( stream, token, __MPU_SCAN_TOKEN_MAX - 1, width, &token_len, &nread ) != 0 ) { if( mpu_feof( stream ) ) input_failure = 1; break; } } else { if( __mpu_IO_scan_token( stream, token, __MPU_SCAN_TOKEN_MAX - 1, width, &token_len, &nread ) != 0 ) { if( mpu_feof( stream ) ) input_failure = 1; break; } } if( !bits && (conv == 'e' || conv == 'E' || conv == 'f' || conv == 'F' || conv == 'g' || conv == 'G') ) { if( __mpu_IO_scan_to_locale( ascii, __MPU_SCAN_MB_TOKEN_MAX, token, token_len ) != 0 ) break; } else if( __mpu_IO_scan_to_ascii( ascii, __MPU_SCAN_MB_TOKEN_MAX, token, token_len ) != 0 ) break; if( conv == 'd' || conv == 'D' || conv == 'i' || conv == 'u' || conv == 'U' || conv == 'o' || conv == 'O' || conv == 'x' || conv == 'X' || conv == 'b' || conv == 'B' || conv == 'p' ) { if( !suppress ) { if( conv == 'p' ) { char *end; uintmax_t v; v = strtoumax( ascii, &end, 16 ); if( end == ascii ) break; *va_arg( ap, void ** ) = (void *)(uintptr_t)v; } else if( bits ) { void *dst; dst = va_arg( ap, void * ); if( __mpu_IO_scan_mpu_integer( dst, bits, base, signed_conv, ascii ) != 0 ) break; } else if( signed_conv ) { intmax_t v; if( __mpu_IO_scan_parse_signed( ascii, base, &v ) != 0 ) break; __mpu_IO_scan_store_signed( &ap, length, v ); } else { uintmax_t v; if( __mpu_IO_scan_parse_unsigned( ascii, base, &v ) != 0 ) break; __mpu_IO_scan_store_unsigned( &ap, length, v ); } ++assigned; } continue; } if( (conv == 'r' || conv == 'R') || (bits && (conv == 'e' || conv == 'E' || conv == 'f' || conv == 'F' || conv == 'g' || conv == 'G')) ) { if( !bits ) bits = 128; if( !__mpu_IO_scan_valid_mpu_bits( bits, 1 ) ) break; if( !suppress ) { void *dst = va_arg( ap, void * ); if( __mpu_IO_scan_mpu_real( dst, bits, ascii ) != 0 ) break; ++assigned; } continue; } if( conv == 'j' || conv == 'J' ) { if( !bits ) bits = 128; if( !__mpu_IO_scan_valid_mpu_bits( bits, 1 ) ) break; if( !suppress ) { void *dst = va_arg( ap, void * ); if( __mpu_IO_scan_mpu_complex( dst, bits, ascii, complex_first_len, complex_second_off, complex_second_len ) != 0 ) break; ++assigned; } continue; } if( conv == 'e' || conv == 'E' || conv == 'f' || conv == 'F' || conv == 'g' || conv == 'G' ) { char *end; long double v = strtold( ascii, &end ); if( end == ascii || *end != '\0' ) break; if( !suppress ) { if( length == __MPU_SCAN_LEN_CAP_L ) *va_arg( ap, long double * ) = v; else if( length == __MPU_SCAN_LEN_L ) *va_arg( ap, double * ) = (double)v; else *va_arg( ap, float * ) = (float)v; ++assigned; } continue; } break; } } va_end( ap ); free( ascii ); if( assigned == 0 && input_failure ) return( mpu_EOF ); return( assigned ); format_failure: va_end( ap ); free( ascii ); return( mpu_EOF ); } /* End of __mpu_IO_vfscanf_unlocked() */ /*************************************************************** Common locked scanf entry. Serializes access to STREAM and then calls the unlocked scanner engine. ***************************************************************/ int __mpu_IO_vfscanf( mpu_FILE *stream, const __mpu_char16_t *format, va_list args ) { int ret; if( !__mpu_IO_valid( stream ) || format == (const __mpu_char16_t *)0 ) { errno = EINVAL; return( mpu_EOF ); } pthread_mutex_lock( &stream->_lock ); ret = __mpu_IO_vfscanf_unlocked( stream, format, args ); pthread_mutex_unlock( &stream->_lock ); return( ret ); } /* End of __mpu_IO_vfscanf() */ /*************************************************************** Hide internal symbols: ***************************************************************/ __mpu_hidden_decl(__mpu_IO_vfscanf); __mpu_hidden_decl(__mpu_IO_vfscanf_unlocked); /* End of hide internal symbols. ***************************************************************/ /* End of mpu-vfscanf.c */