/*************************************************************** MPU-VFPRINTF.C This file contains source code of formatted output functions for LIBMPUIO library. PART OF : MPUIO - library . USAGE : Internal only . NOTE : Format strings and %s arguments are UCS-2. The %a conversion accepts a current-locale multibyte string (__mpu_char8_t *). Copyright (C) 2000 - 2026 by Andrey V.Kosteltsev. All Rights Reserved. ***************************************************************/ #ifdef HAVE_CONFIG_H #include #endif #include #include #include #define FLAG_ALT 0x0001 #define FLAG_ZERO 0x0002 #define FLAG_LEFT 0x0004 #define FLAG_SPACE 0x0008 #define FLAG_PLUS 0x0010 #define LEN_NONE 0 #define LEN_HH 1 #define LEN_H 2 #define LEN_L 3 #define LEN_LL 4 #define LEN_J 5 #define LEN_T 6 #define LEN_CAP_L 7 #define MPU_FMT_DEFAULT_BITS 128 #define MPU_FMT_DEFAULT_PREC 6 static int __mpu_IO_emit_char( mpu_FILE *stream, __mpu_char16_t c, int *count ); static int __mpu_IO_emit_repeat( mpu_FILE *stream, __mpu_char16_t c, int n, int *count ); static int __mpu_IO_emit_string( mpu_FILE *stream, const __mpu_char16_t *s, size_t n, int *count ); /*************************************************************** 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_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_valid_mpu_size() */ static int __mpu_IO_valid_mpu_bits( unsigned bits, int real_or_complex ) { if( !__mpu_IO_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_valid_mpu_bits() */ static int __mpu_IO_mpu_real_buffer_size( int nb, int prec, int exp_digs, size_t extra, size_t *size ) { int max_string; size_t n; max_string = _real_max_string( nb ); if( max_string <= 0 ) { errno = EINVAL; return( mpu_EOF ); } n = (size_t)max_string; if( prec > 0 ) { if( (size_t)prec > SIZE_MAX - n ) { errno = EOVERFLOW; return( mpu_EOF ); } n += (size_t)prec; } if( exp_digs > 0 ) { if( (size_t)exp_digs > SIZE_MAX - n ) { errno = EOVERFLOW; return( mpu_EOF ); } n += (size_t)exp_digs; } if( extra > SIZE_MAX - n ) { errno = EOVERFLOW; return( mpu_EOF ); } n += extra; *size = n; return( 0 ); } /* End of __mpu_IO_mpu_real_buffer_size() */ static int __mpu_IO_emit_ascii_field( mpu_FILE *stream, const char *s, int flags, int width, int *count ) { size_t n; size_t first = 0; int padding; size_t i; n = 0; while( s[n] != '\0' ) ++n; padding = width > (int)n ? width - (int)n : 0; if( !(flags & FLAG_LEFT) ) { if( (flags & FLAG_ZERO) && padding > 0 && (s[0] == '+' || s[0] == '-') ) { if( __mpu_IO_emit_char( stream, (__mpu_char16_t)(unsigned char)s[0], count ) == mpu_EOF ) return( mpu_EOF ); first = 1; if( __mpu_IO_emit_repeat( stream, '0', padding, count ) == mpu_EOF ) return( mpu_EOF ); } else { if( __mpu_IO_emit_repeat( stream, (flags & FLAG_ZERO) ? '0' : ' ', padding, count ) == mpu_EOF ) return( mpu_EOF ); } } for( i = first; i < n; ++i ) { if( __mpu_IO_emit_char( stream, (__mpu_char16_t)(unsigned char)s[i], count ) == mpu_EOF ) return( mpu_EOF ); } if( flags & FLAG_LEFT ) { if( __mpu_IO_emit_repeat( stream, ' ', padding, count ) == mpu_EOF ) return( mpu_EOF ); } return( 0 ); } /* End of __mpu_IO_emit_ascii_field() */ /*************************************************************** Emit one ASCII numeric component with ZEROES inserted after an optional leading sign. N is the component length without the terminating zero. ***************************************************************/ static int __mpu_IO_emit_ascii_zero_component( mpu_FILE *stream, const char *s, size_t n, int zeroes, int *count ) { size_t first = 0; size_t i; if( n > 0 && (s[0] == '+' || s[0] == '-') ) { if( __mpu_IO_emit_char( stream, (__mpu_char16_t)(unsigned char)s[0], count ) == mpu_EOF ) return( mpu_EOF ); first = 1; } if( __mpu_IO_emit_repeat( stream, '0', zeroes, count ) == mpu_EOF ) return( mpu_EOF ); for( i = first; i < n; ++i ) { if( __mpu_IO_emit_char( stream, (__mpu_char16_t)(unsigned char)s[i], count ) == mpu_EOF ) return( mpu_EOF ); } return( 0 ); } /* End of __mpu_IO_emit_ascii_zero_component() */ /*************************************************************** Emit an MPU complex field. Ordinary space padding treats the formatted complex number as one field. Zero padding is split equally between its real and imaginary components; an odd free field character remains ordinary leading space padding. ***************************************************************/ static int __mpu_IO_emit_mpu_complex_field( mpu_FILE *stream, const char *real, const char *imag, int flags, int width, int *count ) { size_t real_len; size_t imag_len; size_t total_len; int padding = 0; int zeroes; int outer_padding; if( !(flags & FLAG_ZERO) || (flags & FLAG_LEFT) ) return( __mpu_IO_emit_ascii_field( stream, real, flags, width, count ) ); real_len = (size_t)(imag - real); imag_len = strlen( imag ); if( real_len > SIZE_MAX - imag_len ) { errno = EOVERFLOW; return( mpu_EOF ); } total_len = real_len + imag_len; /* The cast is safe: this branch is entered only when total_len < width, and width is an int. */ if( width > 0 && (size_t)width > total_len ) padding = width - (int)total_len; zeroes = padding / 2; outer_padding = padding % 2; if( __mpu_IO_emit_repeat( stream, ' ', outer_padding, count ) == mpu_EOF ) return( mpu_EOF ); if( __mpu_IO_emit_ascii_zero_component( stream, real, real_len, zeroes, count ) == mpu_EOF ) return( mpu_EOF ); if( __mpu_IO_emit_ascii_zero_component( stream, imag, imag_len, zeroes, count ) == mpu_EOF ) return( mpu_EOF ); return( 0 ); } /* End of __mpu_IO_emit_mpu_complex_field() */ /*************************************************************** Decode one libc-formatted multibyte field to strict UCS-2 and emit it to STREAM. This keeps ordinary floating output aligned with the active locale without exposing libc multibyte bytes as individual LIBMPUIO characters. ***************************************************************/ static int __mpu_IO_emit_locale_field( mpu_FILE *stream, const char *s, int *count ) { mbstate_t state; const char *p = s; size_t left; memset( &state, 0, sizeof(state) ); left = strlen( s ); 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 || (c >= 0xd800U && c <= 0xdfffU) ) { errno = EILSEQ; stream->_flags |= __MPU_IO_ERR_SEEN; return( mpu_EOF ); } if( used == 0 ) break; if( __mpu_IO_emit_char( stream, (__mpu_char16_t)c, count ) == mpu_EOF ) return( mpu_EOF ); p += used; left -= used; } return( 0 ); } /* End of __mpu_IO_emit_locale_field() */ /*************************************************************** Decode one current-locale multibyte string to strict UCS-2 and emit it with string field-width and precision semantics. WIDTH and PRECISION count UCS-2 characters. ***************************************************************/ static int __mpu_IO_emit_locale_string( mpu_FILE *stream, const __mpu_char8_t *s, int flags, int width, int prec, int *count ) { const char *p; __mpu_char16_t *text; mbstate_t state; size_t bytes; size_t left; size_t n = 0; int padding; int ret = 0; p = (const char *)s; bytes = strlen( p ); if( bytes > (SIZE_MAX / sizeof(__mpu_char16_t)) - 1U ) { errno = EOVERFLOW; return( mpu_EOF ); } text = (__mpu_char16_t *)malloc( (bytes + 1U) * sizeof(__mpu_char16_t) ); if( text == (__mpu_char16_t *)0 ) return( mpu_EOF ); memset( &state, 0, sizeof(state) ); left = bytes; 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 || (c >= 0xd800U && c <= 0xdfffU) ) { free( text ); errno = EILSEQ; stream->_flags |= __MPU_IO_ERR_SEEN; return( mpu_EOF ); } if( used == 0 ) break; text[n++] = (__mpu_char16_t)c; p += used; left -= used; } text[n] = 0; if( prec >= 0 && n > (size_t)prec ) n = (size_t)prec; padding = width > (int)n ? width - (int)n : 0; if( !(flags & FLAG_LEFT) && __mpu_IO_emit_repeat( stream, ' ', padding, count ) == mpu_EOF ) ret = mpu_EOF; if( ret != mpu_EOF && __mpu_IO_emit_string( stream, text, n, count ) == mpu_EOF ) ret = mpu_EOF; if( ret != mpu_EOF && (flags & FLAG_LEFT) && __mpu_IO_emit_repeat( stream, ' ', padding, count ) == mpu_EOF ) ret = mpu_EOF; free( text ); return( ret ); } /* End of __mpu_IO_emit_locale_string() */ static size_t __mpu_IO_mpu_integer_buffer_size( unsigned bits, unsigned base ) { (void)base; /* libmpu iitoa()/iuitoa() copy an internal temporary buffer whose size is n_bits + prefix + NUL, independently of the radix. */ return( (size_t)bits + 4 ); } /* End of __mpu_IO_mpu_integer_buffer_size() */ static int __mpu_IO_format_mpu_integer( mpu_FILE *stream, mpu_int *value, unsigned bits, unsigned base, int upper, int is_signed, int flags, int width, int prec, __mpu_char16_t conversion, int *count ) { char *buf; char *digits; size_t size; size_t n; int negative = 0; int prefix_len = 0; int sign_len = 0; char sign = 0; char prefix[2]; int zeroes; int field; int padding; size_t i; int nb; nb = (int)(bits / 8); size = __mpu_IO_mpu_integer_buffer_size( bits, base ); buf = (char *)malloc( size ); if( buf == (char *)0 ) return( mpu_EOF ); if( is_signed ) iitoa( (__mpu_char8_t *)buf, value, (int)base, upper, nb ); else iuitoa( (__mpu_char8_t *)buf, value, (int)base, upper, nb ); digits = buf; if( *digits == '-' ) { negative = 1; ++digits; } if( base == 2 && digits[0] == '0' && (digits[1] == 'b' || digits[1] == 'B') ) digits += 2; else if( base == 8 && digits[0] == '0' && digits[1] != '\0' ) digits += 1; else if( base == 16 && digits[0] == '0' && (digits[1] == 'x' || digits[1] == 'X') ) digits += 2; n = 0; while( digits[n] != '\0' ) ++n; if( prec == 0 && n == 1 && digits[0] == '0' ) n = 0; if( negative ) sign = '-'; else if( flags & FLAG_PLUS ) sign = '+'; else if( flags & FLAG_SPACE ) sign = ' '; if( sign ) sign_len = 1; if( flags & FLAG_ALT ) { if( base == 16 && n != 0 ) { prefix[0] = '0'; prefix[1] = (char)conversion; prefix_len = 2; } else if( base == 8 && (n == 0 || digits[0] != '0') ) { prefix[0] = '0'; prefix_len = 1; } else if( base == 2 && n != 0 ) { prefix[0] = '0'; prefix[1] = upper ? 'B' : 'b'; prefix_len = 2; } } zeroes = prec >= 0 && (int)n < prec ? prec - (int)n : 0; field = sign_len + prefix_len + zeroes + (int)n; padding = width > field ? width - field : 0; if( !(flags & FLAG_LEFT) && (!(flags & FLAG_ZERO) || prec >= 0) ) if( __mpu_IO_emit_repeat( stream, ' ', padding, count ) == mpu_EOF ) goto error; if( sign ) if( __mpu_IO_emit_char( stream, (__mpu_char16_t)sign, count ) == mpu_EOF ) goto error; for( i = 0; i < (size_t)prefix_len; ++i ) if( __mpu_IO_emit_char( stream, (__mpu_char16_t)prefix[i], count ) == mpu_EOF ) goto error; if( !(flags & FLAG_LEFT) && (flags & FLAG_ZERO) && prec < 0 ) if( __mpu_IO_emit_repeat( stream, '0', padding, count ) == mpu_EOF ) goto error; if( __mpu_IO_emit_repeat( stream, '0', zeroes, count ) == mpu_EOF ) goto error; for( i = 0; i < n; ++i ) if( __mpu_IO_emit_char( stream, (__mpu_char16_t)(unsigned char)digits[i], count ) == mpu_EOF ) goto error; if( flags & FLAG_LEFT ) if( __mpu_IO_emit_repeat( stream, ' ', padding, count ) == mpu_EOF ) goto error; free( buf ); return( 0 ); error: free( buf ); return( mpu_EOF ); } /* End of __mpu_IO_format_mpu_integer() */ static int __mpu_IO_format_mpu_real( mpu_FILE *stream, mpu_real *value, unsigned bits, int exp_delim, int exp_digs, int flags, int width, int prec, int gen_plus, int *count ) { char *buf; size_t size; int nb; if( !__mpu_IO_valid_mpu_bits( bits, 1 ) ) { errno = EINVAL; return( mpu_EOF ); } if( prec < 0 ) prec = MPU_FMT_DEFAULT_PREC; if( exp_digs < 0 ) exp_digs = 0; nb = (int)(bits / 8); if( __mpu_IO_mpu_real_buffer_size( nb, prec, exp_digs, 32, &size ) == mpu_EOF ) return( mpu_EOF ); buf = (char *)malloc( size ); if( buf == (char *)0 ) return( mpu_EOF ); real_to_ascii( (__mpu_char8_t *)buf, value, prec, exp_delim, exp_digs, gen_plus, nb ); if( __mpu_IO_emit_ascii_field( stream, buf, flags, width, count ) == mpu_EOF ) { free( buf ); return( mpu_EOF ); } free( buf ); return( 0 ); } /* End of __mpu_IO_format_mpu_real() */ static void __mpu_IO_trim_g_exponent( char *s, char *ep ) { char *p; p = ep; while( p > s && p[-1] == '0' ) --p; if( p > s && p[-1] == '.' ) --p; memmove( p, ep, strlen( ep ) + 1 ); return; } /* End of __mpu_IO_trim_g_exponent() */ static int __mpu_IO_format_mpu_real_style( mpu_FILE *stream, mpu_real *value, unsigned bits, int conversion, int exp_digs, int flags, int width, int prec, int *count ) { char *buf; char *fixed; char *ep; char *p; char *q; char sign = 0; size_t size; size_t ndigits; int nb; int exp; int e_style; int ndigs; int pos; if( !__mpu_IO_valid_mpu_bits( bits, 1 ) ) { errno = EINVAL; return( mpu_EOF ); } nb = (int)(bits / 8); if( prec < 0 ) prec = MPU_FMT_DEFAULT_PREC; if( exp_digs < 0 ) exp_digs = 0; if( conversion == 'g' || conversion == 'G' ) { if( prec == 0 ) prec = 1; ndigs = prec - 1; } else ndigs = prec; if( __mpu_IO_mpu_real_buffer_size( nb, prec, exp_digs, 32, &size ) == mpu_EOF ) return( mpu_EOF ); buf = (char *)malloc( size ); if( buf == (char *)0 ) return( mpu_EOF ); real_to_ascii( (__mpu_char8_t *)buf, value, ndigs, (conversion == 'E' || conversion == 'F' || conversion == 'G') ? 'E' : 'e', exp_digs, (flags & FLAG_PLUS) != 0, nb ); ep = strchr( buf, (conversion == 'E' || conversion == 'F' || conversion == 'G') ? 'E' : 'e' ); if( ep == (char *)0 ) { if( __mpu_IO_emit_ascii_field( stream, buf, flags, width, count ) == mpu_EOF ) { free( buf ); return( mpu_EOF ); } free( buf ); return( 0 ); } if( conversion == 'e' || conversion == 'E' || conversion == 'f' || conversion == 'F' ) { /* Historical CODE.LIB semantics: f/F are aliases of e/E. */ if( prec == 0 && !(flags & FLAG_ALT) && ep > buf && ep[-1] == '.' ) memmove( ep - 1, ep, strlen( ep ) + 1 ); if( __mpu_IO_emit_ascii_field( stream, buf, flags, width, count ) == mpu_EOF ) { free( buf ); return( mpu_EOF ); } free( buf ); return( 0 ); } exp = (int)strtol( ep + 1, (char **)0, 10 ); e_style = (exp < -4 || exp >= prec); if( e_style ) { if( !(flags & FLAG_ALT) ) __mpu_IO_trim_g_exponent( buf, ep ); if( __mpu_IO_emit_ascii_field( stream, buf, flags, width, count ) == mpu_EOF ) { free( buf ); return( mpu_EOF ); } free( buf ); return( 0 ); } if( size > SIZE_MAX - 32 || (size_t)prec > SIZE_MAX - size - 32 ) { free( buf ); errno = EOVERFLOW; return( mpu_EOF ); } fixed = (char *)malloc( size + (size_t)prec + 32 ); if( fixed == (char *)0 ) { free( buf ); return( mpu_EOF ); } p = buf; q = fixed; if( *p == '-' || *p == '+' ) sign = *p++; if( sign ) *q++ = sign; ndigits = 0; { char *t; for( t = p; t < ep; ++t ) if( *t != '.' ) ++ndigits; } pos = exp + 1; if( pos <= 0 ) { int z; *q++ = '0'; *q++ = '.'; for( z = 0; z < -pos; ++z ) *q++ = '0'; while( p < ep ) { if( *p != '.' ) *q++ = *p; ++p; } } else if( pos >= (int)ndigits ) { int n = 0; while( p < ep ) { if( *p != '.' ) { *q++ = *p; ++n; } ++p; } while( n++ < pos ) *q++ = '0'; if( flags & FLAG_ALT ) *q++ = '.'; } else { int n = 0; while( p < ep ) { if( *p != '.' ) { if( n == pos ) *q++ = '.'; *q++ = *p; ++n; } ++p; } } *q = '\0'; if( !(flags & FLAG_ALT) ) { char *dot = strchr( fixed, '.' ); if( dot ) { q = fixed + strlen( fixed ); while( q > dot + 1 && q[-1] == '0' ) --q; if( q > dot && q[-1] == '.' ) --q; *q = '\0'; } } if( __mpu_IO_emit_ascii_field( stream, fixed, flags, width, count ) == mpu_EOF ) { free( fixed ); free( buf ); return( mpu_EOF ); } free( fixed ); free( buf ); return( 0 ); } /* End of __mpu_IO_format_mpu_real_style() */ static int __mpu_IO_format_mpu_complex( mpu_FILE *stream, mpu_complex *value, unsigned bits, int upper, int exp_digs, int flags, int width, int prec, int *count ) { mpu_real *r; char *buf; char *imag; char *p; size_t part_size; size_t total_size; int nb; if( !__mpu_IO_valid_mpu_bits( bits, 1 ) ) { errno = EINVAL; return( mpu_EOF ); } if( prec < 0 ) prec = MPU_FMT_DEFAULT_PREC; if( exp_digs < 0 ) exp_digs = 0; nb = (int)(bits / 8); r = (mpu_real *)malloc( (size_t)nb ); if( r == (mpu_real *)0 ) return( mpu_EOF ); if( __mpu_IO_mpu_real_buffer_size( nb, prec, exp_digs, 32, &part_size ) == mpu_EOF ) { free( r ); return( mpu_EOF ); } if( part_size > SIZE_MAX / 2 ) { free( r ); errno = EOVERFLOW; return( mpu_EOF ); } total_size = part_size * 2; buf = (char *)malloc( total_size ); if( buf == (char *)0 ) { free( r ); return( mpu_EOF ); } c_real_part( r, value, nb ); real_to_ascii( (__mpu_char8_t *)buf, r, prec, upper ? 'R' : 'r', exp_digs, (flags & FLAG_PLUS) != 0, nb ); p = buf; while( *p ) ++p; imag = p; c_imaginary( r, value, nb ); real_to_ascii( (__mpu_char8_t *)p, r, prec, upper ? 'J' : 'j', exp_digs, 1, nb ); if( __mpu_IO_emit_mpu_complex_field( stream, buf, imag, flags, width, count ) == mpu_EOF ) { free( buf ); free( r ); return( mpu_EOF ); } free( buf ); free( r ); return( 0 ); } /* End of __mpu_IO_format_mpu_complex() */ static int __mpu_IO_emit_char( mpu_FILE *stream, __mpu_char16_t c, int *count ) { if( *count == INT_MAX ) { errno = EOVERFLOW; stream->_flags |= __MPU_IO_ERR_SEEN; return( mpu_EOF ); } if( __mpu_IO_put_char_unlocked( c, stream ) == mpu_EOF ) return( mpu_EOF ); ++*count; return( 0 ); } /* End of __mpu_IO_emit_char() */ static int __mpu_IO_emit_repeat( mpu_FILE *stream, __mpu_char16_t c, int n, int *count ) { if( n > INT_MAX - *count ) { errno = EOVERFLOW; stream->_flags |= __MPU_IO_ERR_SEEN; return( mpu_EOF ); } while( n-- > 0 ) { if( __mpu_IO_emit_char( stream, c, count ) == mpu_EOF ) return( mpu_EOF ); } return( 0 ); } /* End of __mpu_IO_emit_repeat() */ static int __mpu_IO_emit_string( mpu_FILE *stream, const __mpu_char16_t *s, size_t n, int *count ) { size_t i; if( n > (size_t)(INT_MAX - *count) ) { errno = EOVERFLOW; stream->_flags |= __MPU_IO_ERR_SEEN; return( mpu_EOF ); } for( i = 0; i < n; ++i ) { if( __mpu_IO_emit_char( stream, s[i], count ) == mpu_EOF ) return( mpu_EOF ); } return( 0 ); } /* End of __mpu_IO_emit_string() */ static size_t __mpu_IO_strlen( const __mpu_char16_t *s ) { const __mpu_char16_t *p = s; while( *p ) ++p; return( (size_t)(p - s) ); } /* End of __mpu_IO_strlen() */ static size_t __mpu_IO_uint_to_ascii( uintmax_t value, unsigned base, int upper, char *buf ) { static const char lower_digits[] = "0123456789abcdef"; static const char upper_digits[] = "0123456789ABCDEF"; const char *digits; char tmp[sizeof(uintmax_t) * CHAR_BIT + 1]; size_t n = 0; size_t i; digits = upper ? upper_digits : lower_digits; do { tmp[n++] = digits[value % base]; value /= base; } while( value != 0 ); for( i = 0; i < n; ++i ) buf[i] = tmp[n - i - 1]; return( n ); } /* End of __mpu_IO_uint_to_ascii() */ static int __mpu_IO_format_integer( mpu_FILE *stream, uintmax_t value, int negative, unsigned base, int upper, int flags, int width, int prec, __mpu_char16_t conversion, int *count ) { char digits[sizeof(uintmax_t) * CHAR_BIT + 1]; __mpu_char16_t sign = 0; __mpu_char16_t prefix[2]; size_t ndigits; int prefix_len = 0; int zeroes; int field; int padding; size_t i; if( prec == 0 && value == 0 ) ndigits = 0; else ndigits = __mpu_IO_uint_to_ascii( value, base, upper, digits ); if( negative ) sign = '-'; else if( flags & FLAG_PLUS ) sign = '+'; else if( flags & FLAG_SPACE ) sign = ' '; if( flags & FLAG_ALT ) { if( base == 16 && value != 0 ) { prefix[0] = '0'; prefix[1] = conversion; prefix_len = 2; } else if( base == 8 && (ndigits == 0 || digits[0] != '0') ) { prefix[0] = '0'; prefix_len = 1; } else if( base == 2 && value != 0 ) { prefix[0] = '0'; prefix[1] = upper ? 'B' : 'b'; prefix_len = 2; } } zeroes = 0; if( prec >= 0 && (int)ndigits < prec ) zeroes = prec - (int)ndigits; field = (int)ndigits + zeroes + prefix_len + (sign ? 1 : 0); padding = width > field ? width - field : 0; if( !(flags & FLAG_LEFT) && (!(flags & FLAG_ZERO) || prec >= 0) ) { if( __mpu_IO_emit_repeat( stream, ' ', padding, count ) == mpu_EOF ) return( mpu_EOF ); } if( sign ) { if( __mpu_IO_emit_char( stream, sign, count ) == mpu_EOF ) return( mpu_EOF ); } if( prefix_len ) { if( __mpu_IO_emit_string( stream, prefix, (size_t)prefix_len, count ) == mpu_EOF ) return( mpu_EOF ); } if( !(flags & FLAG_LEFT) && (flags & FLAG_ZERO) && prec < 0 ) { if( __mpu_IO_emit_repeat( stream, '0', padding, count ) == mpu_EOF ) return( mpu_EOF ); } if( __mpu_IO_emit_repeat( stream, '0', zeroes, count ) == mpu_EOF ) return( mpu_EOF ); for( i = 0; i < ndigits; ++i ) { if( __mpu_IO_emit_char( stream, (__mpu_char16_t)(unsigned char)digits[i], count ) == mpu_EOF ) return( mpu_EOF ); } if( flags & FLAG_LEFT ) { if( __mpu_IO_emit_repeat( stream, ' ', padding, count ) == mpu_EOF ) return( mpu_EOF ); } return( 0 ); } /* End of __mpu_IO_format_integer() */ /*************************************************************** Format one ordinary decimal floating-point value with libc snprintf() and decode the resulting locale multibyte field to strict UCS-2. ***************************************************************/ static int __mpu_IO_format_float( mpu_FILE *stream, va_list *args, int length, int ch, int flags, int width, int prec, int *count ) { char fmt[96]; char *p = fmt; char *buffer; int n; int ret; *p++ = '%'; if( flags & FLAG_ALT ) *p++ = '#'; if( flags & FLAG_ZERO ) *p++ = '0'; if( flags & FLAG_LEFT ) *p++ = '-'; if( flags & FLAG_SPACE ) *p++ = ' '; if( flags & FLAG_PLUS ) *p++ = '+'; if( width > 0 ) p += snprintf( p, (size_t)(fmt + sizeof(fmt) - p), "%d", width ); if( prec >= 0 ) { *p++ = '.'; p += snprintf( p, (size_t)(fmt + sizeof(fmt) - p), "%d", prec ); } if( length == LEN_CAP_L ) *p++ = 'L'; *p++ = (char)ch; *p = '\0'; if( length == LEN_CAP_L ) { long double v = va_arg( *args, long double ); n = snprintf( (char *)0, 0, fmt, v ); if( n < 0 ) return( mpu_EOF ); buffer = (char *)malloc( (size_t)n + 1 ); if( buffer == (char *)0 ) return( mpu_EOF ); (void)snprintf( buffer, (size_t)n + 1, fmt, v ); } else { double v = va_arg( *args, double ); n = snprintf( (char *)0, 0, fmt, v ); if( n < 0 ) return( mpu_EOF ); buffer = (char *)malloc( (size_t)n + 1 ); if( buffer == (char *)0 ) return( mpu_EOF ); (void)snprintf( buffer, (size_t)n + 1, fmt, v ); } ret = __mpu_IO_emit_locale_field( stream, buffer, count ); free( buffer ); return( ret ); } /* End of __mpu_IO_format_float() */ /*************************************************************** Store the current formatted-output character count for %n. ***************************************************************/ static void __mpu_IO_store_count( va_list *args, int length, int count ) { switch( length ) { case LEN_HH: *va_arg( *args, signed char * ) = (signed char)count; break; case LEN_H: *va_arg( *args, short * ) = (short)count; break; case LEN_L: *va_arg( *args, long * ) = (long)count; break; case LEN_LL: *va_arg( *args, long long * ) = (long long)count; break; case LEN_J: *va_arg( *args, intmax_t * ) = (intmax_t)count; break; case LEN_T: *va_arg( *args, ptrdiff_t * ) = (ptrdiff_t)count; break; default: *va_arg( *args, int * ) = count; break; } return; } /* End of __mpu_IO_store_count() */ static uintmax_t __mpu_IO_get_unsigned( va_list *args, int length ) { switch( length ) { case LEN_HH: return( (unsigned char)va_arg( *args, unsigned int ) ); case LEN_H: return( (unsigned short)va_arg( *args, unsigned int ) ); case LEN_L: return( va_arg( *args, unsigned long ) ); case LEN_LL: return( va_arg( *args, unsigned long long ) ); case LEN_J: return( va_arg( *args, uintmax_t ) ); case LEN_T: return( (uintmax_t)va_arg( *args, ptrdiff_t ) ); default: return( va_arg( *args, unsigned int ) ); } } /* End of __mpu_IO_get_unsigned() */ static intmax_t __mpu_IO_get_signed( va_list *args, int length ) { switch( length ) { case LEN_HH: return( (signed char)va_arg( *args, int ) ); case LEN_H: return( (short)va_arg( *args, int ) ); case LEN_L: return( va_arg( *args, long ) ); case LEN_LL: return( va_arg( *args, long long ) ); case LEN_J: return( va_arg( *args, intmax_t ) ); case LEN_T: return( va_arg( *args, ptrdiff_t ) ); default: return( va_arg( *args, int ) ); } } /* End of __mpu_IO_get_signed() */ /* Format output without acquiring STREAM lock. */ int __mpu_IO_vfprintf_unlocked( mpu_FILE *stream, const __mpu_char16_t *format, va_list args ) { const __mpu_char16_t *fmt; va_list ap; int count = 0; int ret = 0; if( !__mpu_IO_valid( stream ) || format == (const __mpu_char16_t *)0 ) return( mpu_EOF ); va_copy( ap, args ); fmt = format; while( *fmt ) { int flags; int width; int prec; int length; int ch; unsigned mpu_bits; int exp_digs; int mpu_size; if( *fmt != '%' ) { if( __mpu_IO_emit_char( stream, *fmt++, &count ) == mpu_EOF ) { ret = mpu_EOF; goto done; } continue; } ++fmt; if( *fmt == '%' ) { if( __mpu_IO_emit_char( stream, '%', &count ) == mpu_EOF ) { ret = mpu_EOF; goto done; } ++fmt; continue; } flags = 0; for( ;; ) { if( *fmt == '#' ) flags |= FLAG_ALT; else if( *fmt == '0' ) flags |= FLAG_ZERO; else if( *fmt == '-' ) flags |= FLAG_LEFT; else if( *fmt == ' ' ) flags |= FLAG_SPACE; else if( *fmt == '+' ) flags |= FLAG_PLUS; else break; ++fmt; } if( flags & FLAG_LEFT ) flags &= ~FLAG_ZERO; width = 0; if( *fmt == '*' ) { width = va_arg( ap, int ); if( width < 0 ) { if( width == INT_MIN ) { errno = EOVERFLOW; ret = mpu_EOF; goto done; } flags |= FLAG_LEFT; flags &= ~FLAG_ZERO; width = -width; } ++fmt; } else { while( *fmt >= '0' && *fmt <= '9' ) { int digit = *fmt - '0'; if( width > (INT_MAX - digit) / 10 ) { errno = EOVERFLOW; ret = mpu_EOF; goto done; } width = width * 10 + digit; ++fmt; } } prec = -1; if( *fmt == '.' ) { ++fmt; if( *fmt == '*' ) { prec = va_arg( ap, int ); if( prec < 0 ) prec = -1; ++fmt; } else { prec = 0; while( *fmt >= '0' && *fmt <= '9' ) { int digit = *fmt - '0'; if( prec > (INT_MAX - digit) / 10 ) { errno = EOVERFLOW; ret = mpu_EOF; goto done; } prec = prec * 10 + digit; ++fmt; } } } mpu_bits = 0; exp_digs = 0; mpu_size = 0; if( *fmt == '.' ) { ++fmt; if( *fmt == '*' ) { exp_digs = va_arg( ap, int ); if( exp_digs < 0 ) exp_digs = 0; ++fmt; } else { while( *fmt >= '0' && *fmt <= '9' ) { int digit = *fmt - '0'; if( exp_digs > (INT_MAX - digit) / 10 ) { errno = EOVERFLOW; ret = mpu_EOF; goto done; } exp_digs = exp_digs * 10 + digit; ++fmt; } } } if( *fmt == 'z' || *fmt == 'Z' ) { ++fmt; mpu_size = 1; if( *fmt >= '0' && *fmt <= '9' ) { while( *fmt >= '0' && *fmt <= '9' ) { unsigned digit = (unsigned)(*fmt - '0'); if( mpu_bits > (UINT_MAX - digit) / 10U ) { errno = EOVERFLOW; ret = mpu_EOF; goto done; } mpu_bits = mpu_bits * 10U + digit; ++fmt; } } else mpu_bits = MPU_FMT_DEFAULT_BITS; if( !__mpu_IO_valid_mpu_size( mpu_bits ) ) { errno = EINVAL; ret = mpu_EOF; goto done; } } length = LEN_NONE; if( *fmt == 'h' ) { ++fmt; if( *fmt == 'h' ) { length = LEN_HH; ++fmt; } else length = LEN_H; } else if( *fmt == 'l' ) { ++fmt; if( *fmt == 'l' ) { length = LEN_LL; ++fmt; } else length = LEN_L; } else if( *fmt == 'j' && !mpu_size ) { __mpu_char16_t next = fmt[1]; if( next == 'd' || next == 'i' || next == 'u' || next == 'o' || next == 'x' || next == 'X' || next == 'n' ) { length = LEN_J; ++fmt; } } else if( *fmt == 't' ) { length = LEN_T; ++fmt; } else if( *fmt == 'L' ) { length = LEN_CAP_L; ++fmt; } ch = *fmt; if( ch == 0 ) break; ++fmt; switch( ch ) { case 'b': case 'B': if( mpu_size ) { mpu_int *v = va_arg( ap, mpu_int * ); if( __mpu_IO_format_mpu_integer( stream, v, mpu_bits, 2, ch == 'B', 0, flags & ~(FLAG_PLUS|FLAG_SPACE), width, prec, (__mpu_char16_t)ch, &count ) == mpu_EOF ) { ret = mpu_EOF; goto done; } } else { uintmax_t uv = __mpu_IO_get_unsigned( &ap, length ); if( __mpu_IO_format_integer( stream, uv, 0, 2, ch == 'B', flags & ~(FLAG_PLUS|FLAG_SPACE), width, prec, (__mpu_char16_t)ch, &count ) == mpu_EOF ) { ret = mpu_EOF; goto done; } } break; case 'e': case 'E': case 'f': case 'F': case 'g': case 'G': if( mpu_size ) { mpu_real *v; v = va_arg( ap, mpu_real * ); if( __mpu_IO_format_mpu_real_style( stream, v, mpu_bits, ch, exp_digs, flags, width, prec, &count ) == mpu_EOF ) { ret = mpu_EOF; goto done; } break; } if( __mpu_IO_format_float( stream, &ap, length, ch, flags, width, prec, &count ) == mpu_EOF ) { ret = mpu_EOF; goto done; } break; case 'r': case 'R': if( !mpu_size ) mpu_bits = MPU_FMT_DEFAULT_BITS; { mpu_real *v = va_arg( ap, mpu_real * ); if( __mpu_IO_format_mpu_real( stream, v, mpu_bits, ch == 'r' ? 'e' : 'E', exp_digs, flags, width, prec, (flags & FLAG_PLUS) != 0, &count ) == mpu_EOF ) { ret = mpu_EOF; goto done; } break; } case 'j': case 'J': /* j/J are MPU complex conversions unless j was parsed as the standard intmax_t length modifier above. */ if( !mpu_size ) mpu_bits = MPU_FMT_DEFAULT_BITS; { mpu_complex *v = va_arg( ap, mpu_complex * ); if( __mpu_IO_format_mpu_complex( stream, v, mpu_bits, ch == 'J', exp_digs, flags, width, prec, &count ) == mpu_EOF ) { ret = mpu_EOF; goto done; } break; } case 'd': case 'i': { intmax_t sv; uintmax_t uv; int negative; if( mpu_size ) { mpu_int *v = va_arg( ap, mpu_int * ); if( __mpu_IO_format_mpu_integer( stream, v, mpu_bits, 10, 0, 1, flags, width, prec, (__mpu_char16_t)ch, &count ) == mpu_EOF ) { ret = mpu_EOF; goto done; } break; } sv = __mpu_IO_get_signed( &ap, length ); negative = sv < 0; if( negative ) uv = (uintmax_t)(-(sv + 1)) + 1; else uv = (uintmax_t)sv; if( __mpu_IO_format_integer( stream, uv, negative, 10, 0, flags, width, prec, (__mpu_char16_t)ch, &count ) == mpu_EOF ) { ret = mpu_EOF; goto done; } break; } case 'u': case 'o': case 'x': case 'X': { uintmax_t uv; unsigned base; base = ch == 'o' ? 8 : (ch == 'u' ? 10 : 16); if( mpu_size ) { mpu_int *v = va_arg( ap, mpu_int * ); if( __mpu_IO_format_mpu_integer( stream, v, mpu_bits, base, ch == 'X', 0, flags & ~(FLAG_PLUS|FLAG_SPACE), width, prec, (__mpu_char16_t)ch, &count ) == mpu_EOF ) { ret = mpu_EOF; goto done; } break; } uv = __mpu_IO_get_unsigned( &ap, length ); if( __mpu_IO_format_integer( stream, uv, 0, base, ch == 'X', flags & ~(FLAG_PLUS|FLAG_SPACE), width, prec, (__mpu_char16_t)ch, &count ) == mpu_EOF ) { ret = mpu_EOF; goto done; } break; } case 'n': if( mpu_size ) { errno = EINVAL; ret = mpu_EOF; goto done; } __mpu_IO_store_count( &ap, length, count ); break; case 'p': { uintptr_t p; p = (uintptr_t)va_arg( ap, void * ); flags |= FLAG_ALT; if( __mpu_IO_format_integer( stream, (uintmax_t)p, 0, 16, 0, flags, width, prec, 'x', &count ) == mpu_EOF ) { ret = mpu_EOF; goto done; } break; } case 'a': { const __mpu_char8_t *s; static const __mpu_char8_t null_string[] = "(null)"; if( mpu_size || length != LEN_NONE ) { errno = EINVAL; ret = mpu_EOF; goto done; } s = va_arg( ap, const __mpu_char8_t * ); if( s == (const __mpu_char8_t *)0 ) s = null_string; if( __mpu_IO_emit_locale_string( stream, s, flags, width, prec, &count ) == mpu_EOF ) { ret = mpu_EOF; goto done; } break; } case 'c': { __mpu_char16_t wc; int padding; wc = (__mpu_char16_t)va_arg( ap, int ); padding = width > 1 ? width - 1 : 0; if( !(flags & FLAG_LEFT) && __mpu_IO_emit_repeat( stream, ' ', padding, &count ) == mpu_EOF ) { ret = mpu_EOF; goto done; } if( __mpu_IO_emit_char( stream, wc, &count ) == mpu_EOF ) { ret = mpu_EOF; goto done; } if( (flags & FLAG_LEFT) && __mpu_IO_emit_repeat( stream, ' ', padding, &count ) == mpu_EOF ) { ret = mpu_EOF; goto done; } break; } case 's': { const __mpu_char16_t *s; size_t n; int padding; static const __mpu_char16_t null_string[] = { '(', 'n', 'u', 'l', 'l', ')', 0 }; s = va_arg( ap, const __mpu_char16_t * ); if( s == (const __mpu_char16_t *)0 ) s = null_string; n = __mpu_IO_strlen( s ); if( prec >= 0 && n > (size_t)prec ) n = (size_t)prec; padding = width > (int)n ? width - (int)n : 0; if( !(flags & FLAG_LEFT) && __mpu_IO_emit_repeat( stream, ' ', padding, &count ) == mpu_EOF ) { ret = mpu_EOF; goto done; } if( __mpu_IO_emit_string( stream, s, n, &count ) == mpu_EOF ) { ret = mpu_EOF; goto done; } if( (flags & FLAG_LEFT) && __mpu_IO_emit_repeat( stream, ' ', padding, &count ) == mpu_EOF ) { ret = mpu_EOF; goto done; } break; } default: if( __mpu_IO_emit_char( stream, '%', &count ) == mpu_EOF || __mpu_IO_emit_char( stream, (__mpu_char16_t)ch, &count ) == mpu_EOF ) { ret = mpu_EOF; goto done; } break; } } ret = count; done: va_end( ap ); return( ret ); } /* End of __mpu_IO_vfprintf_unlocked() */ /* Format output while holding STREAM lock. */ int __mpu_IO_vfprintf( mpu_FILE *stream, const __mpu_char16_t *format, va_list args ) { int ret; if( !__mpu_IO_valid( stream ) || format == (const __mpu_char16_t *)0 ) return( mpu_EOF ); pthread_mutex_lock( &stream->_lock ); ret = __mpu_IO_vfprintf_unlocked( stream, format, args ); pthread_mutex_unlock( &stream->_lock ); return( ret ); } /* End of __mpu_IO_vfprintf() */ /*************************************************************** Hide internal symbols: ***************************************************************/ __mpu_hidden_decl(__mpu_IO_vfprintf); __mpu_hidden_decl(__mpu_IO_vfprintf_unlocked); /* End of hide internal symbols. ***************************************************************/ /* End of mpu-vfprintf.c */