| /* pformat.c |
| * |
| * $Id: pformat.c,v 1.9 2011/01/07 22:57:00 keithmarshall Exp $ |
| * |
| * Provides a core implementation of the formatting capabilities |
| * common to the entire `printf()' family of functions; it conforms |
| * generally to C99 and SUSv3/POSIX specifications, with extensions |
| * to support Microsoft's non-standard format specifications. |
| * |
| * Written by Keith Marshall <keithmarshall@users.sourceforge.net> |
| * |
| * This is free software. You may redistribute and/or modify it as you |
| * see fit, without restriction of copyright. |
| * |
| * This software is provided "as is", in the hope that it may be useful, |
| * but WITHOUT WARRANTY OF ANY KIND, not even any implied warranty of |
| * MERCHANTABILITY, nor of FITNESS FOR ANY PARTICULAR PURPOSE. At no |
| * time will the author accept any form of liability for any damages, |
| * however caused, resulting from the use of this software. |
| * |
| * The elements of this implementation which deal with the formatting |
| * of floating point numbers, (i.e. the `%e', `%E', `%f', `%F', `%g' |
| * and `%G' format specifiers, but excluding the hexadecimal floating |
| * point `%a' and `%A' specifiers), make use of the `__gdtoa' function |
| * written by David M. Gay, and are modelled on his sample code, which |
| * has been deployed under its accompanying terms of use:-- |
| * |
| ****************************************************************** |
| * Copyright (C) 1997, 1999, 2001 Lucent Technologies |
| * All Rights Reserved |
| * |
| * Permission to use, copy, modify, and distribute this software and |
| * its documentation for any purpose and without fee is hereby |
| * granted, provided that the above copyright notice appear in all |
| * copies and that both that the copyright notice and this |
| * permission notice and warranty disclaimer appear in supporting |
| * documentation, and that the name of Lucent or any of its entities |
| * not be used in advertising or publicity pertaining to |
| * distribution of the software without specific, written prior |
| * permission. |
| * |
| * LUCENT DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, |
| * INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. |
| * IN NO EVENT SHALL LUCENT OR ANY OF ITS ENTITIES BE LIABLE FOR ANY |
| * SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES |
| * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER |
| * IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, |
| * ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF |
| * THIS SOFTWARE. |
| ****************************************************************** |
| * |
| */ |
| |
| #define __LARGE_MBSTATE_T |
| |
| #ifdef HAVE_CONFIG_H |
| #include "config.h" |
| #endif |
| |
| #include <stdio.h> |
| #include <stdarg.h> |
| #include <stddef.h> |
| #include <stdint.h> |
| #include <stdlib.h> |
| #include <string.h> |
| #include <limits.h> |
| #include <locale.h> |
| #include <wchar.h> |
| |
| #ifdef __ENABLE_DFP |
| #ifndef __STDC_WANT_DEC_FP__ |
| #define __STDC_WANT_DEC_FP__ 1 |
| #endif |
| |
| #include "../math/DFP/dfp_internal.h" |
| #endif /* __ENABLE_DFP */ |
| |
| #include <math.h> |
| |
| /* FIXME: The following belongs in values.h, but current MinGW |
| * has nothing useful there! OTOH, values.h is not a standard |
| * header, and its use may be considered obsolete; perhaps it |
| * is better to just keep these definitions here. |
| */ |
| |
| #include <pshpack1.h> |
| /* workaround gcc bug */ |
| #if defined(__GNUC__) && !defined(__clang__) |
| #define ATTRIB_GCC_STRUCT __attribute__((gcc_struct)) |
| #else |
| #define ATTRIB_GCC_STRUCT |
| #endif |
| typedef struct ATTRIB_GCC_STRUCT __tI128 { |
| int64_t digits[2]; |
| } __tI128; |
| |
| typedef struct ATTRIB_GCC_STRUCT __tI128_2 { |
| uint32_t digits32[4]; |
| } __tI128_2; |
| |
| typedef union ATTRIB_GCC_STRUCT __uI128 { |
| __tI128 t128; |
| __tI128_2 t128_2; |
| } __uI128; |
| #include <poppack.h> |
| |
| #ifndef _VALUES_H |
| /* |
| * values.h |
| * |
| */ |
| #define _VALUES_H |
| |
| #include <limits.h> |
| |
| #define _TYPEBITS(type) (sizeof(type) * CHAR_BIT) |
| |
| #if defined(__ENABLE_PRINTF128) || defined(__ENABLE_DFP) |
| #define LLONGBITS _TYPEBITS(__tI128) |
| #else |
| #define LLONGBITS _TYPEBITS(long long) |
| #endif |
| |
| #endif /* !defined _VALUES_H -- end of file */ |
| |
| #include "mingw_pformat.h" |
| |
| /* Bit-map constants, defining the internal format control |
| * states, which propagate through the flags. |
| */ |
| #define PFORMAT_GROUPED 0x00001000 |
| #define PFORMAT_HASHED 0x00000800 |
| #define PFORMAT_LJUSTIFY 0x00000400 |
| #define PFORMAT_ZEROFILL 0x00000200 |
| |
| #define PFORMAT_JUSTIFY (PFORMAT_LJUSTIFY | PFORMAT_ZEROFILL) |
| #define PFORMAT_IGNORE -1 |
| |
| #define PFORMAT_SIGNED 0x000001C0 |
| #define PFORMAT_POSITIVE 0x00000100 |
| #define PFORMAT_NEGATIVE 0x00000080 |
| #define PFORMAT_ADDSPACE 0x00000040 |
| |
| #define PFORMAT_XCASE 0x00000020 |
| |
| #define PFORMAT_LDOUBLE 0x00000004 |
| |
| #ifdef __ENABLE_DFP |
| #define PFORMAT_DECIM32 0x00020000 |
| #define PFORMAT_DECIM64 0x00040000 |
| #define PFORMAT_DECIM128 0x00080000 |
| #endif |
| |
| /* `%o' format digit extraction mask, and shift count... |
| * (These are constant, and do not propagate through the flags). |
| */ |
| #define PFORMAT_OMASK 0x00000007 |
| #define PFORMAT_OSHIFT 0x00000003 |
| |
| /* `%x' and `%X' format digit extraction mask, and shift count... |
| * (These are constant, and do not propagate through the flags). |
| */ |
| #define PFORMAT_XMASK 0x0000000F |
| #define PFORMAT_XSHIFT 0x00000004 |
| |
| /* The radix point character, used in floating point formats, is |
| * localised on the basis of the active LC_NUMERIC locale category. |
| * It is stored locally, as a `wchar_t' entity, which is converted |
| * to a (possibly multibyte) character on output. Initialisation |
| * of the stored `wchar_t' entity, together with a record of its |
| * effective multibyte character length, is required each time |
| * `__pformat()' is entered, (static storage would not be thread |
| * safe), but this initialisation is deferred until it is actually |
| * needed; on entry, the effective character length is first set to |
| * the following value, (and the `wchar_t' entity is zeroed), to |
| * indicate that a call of `localeconv()' is needed, to complete |
| * the initialisation. |
| */ |
| #define PFORMAT_RPINIT -3 |
| |
| /* The floating point format handlers return the following value |
| * for the radix point position index, when the argument value is |
| * infinite, or not a number. |
| */ |
| #define PFORMAT_INFNAN -32768 |
| |
| typedef union |
| { |
| /* A data type agnostic representation, |
| * for printf arguments of any integral data type... |
| */ |
| signed long __pformat_long_t; |
| signed long long __pformat_llong_t; |
| unsigned long __pformat_ulong_t; |
| unsigned long long __pformat_ullong_t; |
| unsigned short __pformat_ushort_t; |
| unsigned char __pformat_uchar_t; |
| signed short __pformat_short_t; |
| signed char __pformat_char_t; |
| void * __pformat_ptr_t; |
| __uI128 __pformat_u128_t; |
| } __pformat_intarg_t; |
| |
| typedef enum |
| { |
| /* Format interpreter state indices... |
| * (used to identify the active phase of format string parsing). |
| */ |
| PFORMAT_INIT = 0, |
| PFORMAT_SET_WIDTH, |
| PFORMAT_GET_PRECISION, |
| PFORMAT_SET_PRECISION, |
| PFORMAT_END |
| } __pformat_state_t; |
| |
| typedef enum |
| { |
| /* Argument length classification indices... |
| * (used for arguments representing integer data types). |
| */ |
| PFORMAT_LENGTH_INT = 0, |
| PFORMAT_LENGTH_SHORT, |
| PFORMAT_LENGTH_LONG, |
| PFORMAT_LENGTH_LLONG, |
| PFORMAT_LENGTH_LLONG128, |
| PFORMAT_LENGTH_CHAR |
| } __pformat_length_t; |
| /* |
| * And a macro to map any arbitrary data type to an appropriate |
| * matching index, selected from those above; the compiler should |
| * collapse this to a simple assignment. |
| */ |
| |
| #ifdef __GNUC__ |
| /* provides for some deadcode elimination via compile time eval */ |
| #define __pformat_arg_length(x) \ |
| __builtin_choose_expr ( \ |
| __builtin_types_compatible_p (typeof (x), __tI128), \ |
| PFORMAT_LENGTH_LLONG128, \ |
| __builtin_choose_expr ( \ |
| __builtin_types_compatible_p (typeof (x), long long), \ |
| PFORMAT_LENGTH_LLONG, \ |
| __builtin_choose_expr ( \ |
| __builtin_types_compatible_p (typeof (x), long), \ |
| PFORMAT_LENGTH_LONG, \ |
| __builtin_choose_expr ( \ |
| __builtin_types_compatible_p (typeof (x), short), \ |
| PFORMAT_LENGTH_SHORT, \ |
| __builtin_choose_expr ( \ |
| __builtin_types_compatible_p (typeof (x), char), \ |
| PFORMAT_LENGTH_CHAR, \ |
| __builtin_choose_expr ( \ |
| __builtin_types_compatible_p (typeof (x), __uI128), \ |
| PFORMAT_LENGTH_LLONG128, \ |
| __builtin_choose_expr ( \ |
| __builtin_types_compatible_p (typeof (x), unsigned long), \ |
| PFORMAT_LENGTH_LONG, \ |
| __builtin_choose_expr ( \ |
| __builtin_types_compatible_p (typeof (x), unsigned long long), \ |
| PFORMAT_LENGTH_LLONG, \ |
| __builtin_choose_expr ( \ |
| __builtin_types_compatible_p (typeof (x), unsigned short), \ |
| PFORMAT_LENGTH_SHORT, \ |
| __builtin_choose_expr ( \ |
| __builtin_types_compatible_p (typeof (x), unsigned char), \ |
| PFORMAT_LENGTH_CHAR, \ |
| PFORMAT_LENGTH_INT)))))))))) |
| |
| #else |
| #define __pformat_arg_length( type ) \ |
| sizeof( type ) == sizeof( __tI128 ) ? PFORMAT_LENGTH_LLONG128 : \ |
| sizeof( type ) == sizeof( long long ) ? PFORMAT_LENGTH_LLONG : \ |
| sizeof( type ) == sizeof( long ) ? PFORMAT_LENGTH_LONG : \ |
| sizeof( type ) == sizeof( short ) ? PFORMAT_LENGTH_SHORT : \ |
| sizeof( type ) == sizeof( char ) ? PFORMAT_LENGTH_CHAR : \ |
| /* should never need this default */ PFORMAT_LENGTH_INT |
| #endif |
| |
| typedef struct |
| { |
| /* Formatting and output control data... |
| * An instance of this control block is created, (on the stack), |
| * for each call to `__pformat()', and is passed by reference to |
| * each of the output handlers, as required. |
| */ |
| void * dest; |
| int flags; |
| int width; |
| int precision; |
| int rplen; |
| wchar_t rpchr; |
| int thousands_chr_len; |
| wchar_t thousands_chr; |
| int count; |
| int quota; |
| int expmin; |
| } __pformat_t; |
| |
| #if defined(__ENABLE_PRINTF128) || defined(__ENABLE_DFP) |
| /* trim leading, leave at least n characters */ |
| static char * __bigint_trim_leading_zeroes(char *in, int n){ |
| char *src = in; |
| int len = strlen(in); |
| while( len > n && *++src == '0') len--; |
| |
| /* we want to null terminator too */ |
| memmove(in, src, strlen(src) + 1); |
| return in; |
| } |
| |
| /* LSB first */ |
| static |
| void __bigint_to_string(const uint32_t *digits, const uint32_t digitlen, char *buff, const uint32_t bufflen){ |
| int64_t digitsize = sizeof(*digits) * 8; |
| int64_t shiftpos = digitlen * digitsize - 1; |
| memset(buff, 0, bufflen); |
| |
| while(shiftpos >= 0) { |
| /* increment */ |
| for(uint32_t i = 0; i < bufflen - 1; i++){ |
| buff[i] += (buff[i] > 4) ? 3 : 0; |
| } |
| |
| /* shift left */ |
| for(uint32_t i = 0; i < bufflen - 1; i++) |
| buff[i] <<= 1; |
| |
| /* shift in */ |
| buff[bufflen - 2] |= digits[shiftpos / digitsize] & (0x1 << (shiftpos % digitsize)) ? 1 : 0; |
| |
| /* overflow check */ |
| for(uint32_t i = bufflen - 1; i > 0; i--){ |
| buff[i - 1] |= (buff[i] > 0xf); |
| buff[i] &= 0x0f; |
| } |
| shiftpos--; |
| } |
| |
| for(uint32_t i = 0; i < bufflen - 1; i++){ |
| buff[i] += '0'; |
| } |
| buff[bufflen - 1] = '\0'; |
| } |
| |
| #if defined(__ENABLE_PRINTF128) |
| /* LSB first, hex version */ |
| static |
| void __bigint_to_stringx(const uint32_t *digits, const uint32_t digitlen, char *buff, const uint32_t bufflen, int upper){ |
| int32_t stride = sizeof(*digits) * 2; |
| uint32_t lastpos = 0; |
| |
| for(uint32_t i = 0; i < digitlen * stride; i++){ |
| int32_t buffpos = bufflen - i - 2; |
| buff[buffpos] = (digits[ i / stride ] & (0xf << 4 * (i % stride))) >> ( 4 * (i % stride)); |
| buff[buffpos] += (buff[buffpos] > 9) ? ((upper) ? 0x7 : 0x27) : 0; |
| buff[buffpos] += '0'; |
| lastpos = buffpos; |
| if(buffpos == 0) break; /* sanity check */ |
| } |
| memset(buff, '0', lastpos); |
| buff[bufflen - 1] = '\0'; |
| } |
| |
| /* LSB first, octet version */ |
| static |
| void __bigint_to_stringo(const uint32_t *digits, const uint32_t digitlen, char *buff, const uint32_t bufflen){ |
| const uint32_t digitsize = sizeof(*digits) * 8; |
| const uint64_t bits = digitsize * digitlen; |
| uint32_t pos = bufflen - 2; |
| uint32_t reg = 0; |
| for(uint32_t i = 0; i <= bits; i++){ |
| reg |= (digits[ i / digitsize] & (0x1 << (i % digitsize))) ? 1 << (i % 3) : 0; |
| if( (i && ( i + 1) % 3 == 0) || (i + 1) == bits){ /* make sure all is committed after last bit */ |
| buff[pos] = '0' + reg; |
| reg = 0; |
| if(!pos) break; /* sanity check */ |
| pos--; |
| } |
| } |
| if(pos < bufflen - 1) |
| memset(buff,'0', pos + 1); |
| buff[bufflen - 1] = '\0'; |
| } |
| #endif /* defined(__ENABLE_PRINTF128) */ |
| #endif /* defined(__ENABLE_PRINTF128) || defined(__ENABLE_DFP) */ |
| |
| static |
| void __pformat_putc( int c, __pformat_t *stream ) |
| { |
| /* Place a single character into the `__pformat()' output queue, |
| * provided any specified output quota has not been exceeded. |
| */ |
| if( (stream->flags & PFORMAT_NOLIMIT) || (stream->quota > stream->count) ) |
| { |
| /* Either there was no quota specified, |
| * or the active quota has not yet been reached. |
| */ |
| if( stream->flags & PFORMAT_TO_FILE ) |
| /* |
| * This is single character output to a FILE stream... |
| */ |
| __fputc(c, (FILE *)(stream->dest)); |
| |
| else |
| /* Whereas, this is to an internal memory buffer... |
| */ |
| ((APICHAR *)(stream->dest))[stream->count] = c; |
| } |
| ++stream->count; |
| } |
| |
| static |
| void __pformat_putchars( const char *s, int count, __pformat_t *stream ) |
| { |
| #ifndef __BUILD_WIDEAPI |
| /* Handler for `%c' and (indirectly) `%s' conversion specifications. |
| * |
| * Transfer characters from the string buffer at `s', character by |
| * character, up to the number of characters specified by `count', or |
| * if `precision' has been explicitly set to a value less than `count', |
| * stopping after the number of characters specified for `precision', |
| * to the `__pformat()' output stream. |
| * |
| * Characters to be emitted are passed through `__pformat_putc()', to |
| * ensure that any specified output quota is honoured. |
| */ |
| if( (stream->precision >= 0) && (count > stream->precision) ) |
| /* |
| * Ensure that the maximum number of characters transferred doesn't |
| * exceed any explicitly set `precision' specification. |
| */ |
| count = stream->precision; |
| |
| /* Establish the width of any field padding required... |
| */ |
| if( stream->width > count ) |
| /* |
| * as the number of spaces equivalent to the number of characters |
| * by which those to be emitted is fewer than the field width... |
| */ |
| stream->width -= count; |
| |
| else |
| /* ignoring any width specification which is insufficient. |
| */ |
| stream->width = PFORMAT_IGNORE; |
| |
| if( (stream->width > 0) && ((stream->flags & PFORMAT_LJUSTIFY) == 0) ) |
| /* |
| * When not doing flush left justification, (i.e. the `-' flag |
| * is not set), any residual unreserved field width must appear |
| * as blank padding, to the left of the output string. |
| */ |
| while( stream->width-- ) |
| __pformat_putc( '\x20', stream ); |
| |
| /* Emit the data... |
| */ |
| while( count-- ) |
| /* |
| * copying the requisite number of characters from the input. |
| */ |
| __pformat_putc( *s++, stream ); |
| |
| /* If we still haven't consumed the entire specified field width, |
| * we must be doing flush left justification; any residual width |
| * must be filled with blanks, to the right of the output value. |
| */ |
| while( stream->width-- > 0 ) |
| __pformat_putc( '\x20', stream ); |
| |
| #else /* __BUILD_WIDEAPI */ |
| |
| int len; |
| |
| if( (stream->precision >= 0) && (count > stream->precision) ) |
| count = stream->precision; |
| |
| if( (stream->flags & PFORMAT_TO_FILE) && (stream->flags & PFORMAT_NOLIMIT) ) |
| { |
| int __cdecl __ms_fwprintf(FILE *, const wchar_t *, ...); |
| |
| if( stream->width > count ) |
| { |
| if( (stream->flags & PFORMAT_LJUSTIFY) == 0 ) |
| len = __ms_fwprintf( (FILE *)(stream->dest), L"%*.*S", stream->width, count, s ); |
| else |
| len = __ms_fwprintf( (FILE *)(stream->dest), L"%-*.*S", stream->width, count, s ); |
| } |
| else |
| { |
| len = __ms_fwprintf( (FILE *)(stream->dest), L"%.*S", count, s ); |
| } |
| if( len > 0 ) |
| stream->count += len; |
| stream->width = PFORMAT_IGNORE; |
| return; |
| } |
| |
| if( stream->width > count ) |
| stream->width -= count; |
| else |
| stream->width = PFORMAT_IGNORE; |
| |
| if( (stream->width > 0) && ((stream->flags & PFORMAT_LJUSTIFY) == 0) ) |
| while( stream->width-- ) |
| __pformat_putc( '\x20', stream ); |
| |
| { |
| /* mbrtowc */ |
| size_t l; |
| wchar_t w[12], *p; |
| while( count > 0 ) |
| { |
| mbstate_t ps; |
| memset(&ps, 0, sizeof(ps) ); |
| --count; |
| p = &w[0]; |
| l = mbrtowc (p, s, strlen (s), &ps); |
| if (!l) |
| break; |
| if ((ssize_t)l < 0) |
| { |
| l = 1; |
| w[0] = (wchar_t) *s; |
| } |
| s += l; |
| __pformat_putc((int)w[0], stream); |
| } |
| } |
| |
| while( stream->width-- > 0 ) |
| __pformat_putc( '\x20', stream ); |
| |
| #endif /* __BUILD_WIDEAPI */ |
| } |
| |
| static |
| void __pformat_puts( const char *s, __pformat_t *stream ) |
| { |
| /* Handler for `%s' conversion specifications. |
| * |
| * Transfer a NUL terminated character string, character by character, |
| * stopping when the end of the string is encountered, or if `precision' |
| * has been explicitly set, when the specified number of characters has |
| * been emitted, if that is less than the length of the input string, |
| * to the `__pformat()' output stream. |
| * |
| * This is implemented as a trivial call to `__pformat_putchars()', |
| * passing the length of the input string as the character count, |
| * (after first verifying that the input pointer is not NULL). |
| */ |
| if( s == NULL ) s = "(null)"; |
| |
| if( stream->precision >= 0 ) |
| __pformat_putchars( s, strnlen( s, stream->precision ), stream ); |
| else |
| __pformat_putchars( s, strlen( s ), stream ); |
| } |
| |
| static |
| void __pformat_wputchars( const wchar_t *s, int count, __pformat_t *stream ) |
| { |
| #ifndef __BUILD_WIDEAPI |
| /* Handler for `%C'(`%lc') and `%S'(`%ls') conversion specifications; |
| * (this is a wide character variant of `__pformat_putchars()'). |
| * |
| * Each multibyte character sequence to be emitted is passed, byte |
| * by byte, through `__pformat_putc()', to ensure that any specified |
| * output quota is honoured. |
| */ |
| char buf[16]; |
| mbstate_t state; |
| int len = wcrtomb(buf, L'\0', &state); |
| |
| if( (stream->precision >= 0) && (count > stream->precision) ) |
| /* |
| * Ensure that the maximum number of characters transferred doesn't |
| * exceed any explicitly set `precision' specification. |
| */ |
| count = stream->precision; |
| |
| /* Establish the width of any field padding required... |
| */ |
| if( stream->width > count ) |
| /* |
| * as the number of spaces equivalent to the number of characters |
| * by which those to be emitted is fewer than the field width... |
| */ |
| stream->width -= count; |
| |
| else |
| /* ignoring any width specification which is insufficient. |
| */ |
| stream->width = PFORMAT_IGNORE; |
| |
| if( (stream->width > 0) && ((stream->flags & PFORMAT_LJUSTIFY) == 0) ) |
| /* |
| * When not doing flush left justification, (i.e. the `-' flag |
| * is not set), any residual unreserved field width must appear |
| * as blank padding, to the left of the output string. |
| */ |
| while( stream->width-- ) |
| __pformat_putc( '\x20', stream ); |
| |
| /* Emit the data, converting each character from the wide |
| * to the multibyte domain as we go... |
| */ |
| while( (count-- > 0) && ((len = wcrtomb( buf, *s++, &state )) > 0) ) |
| { |
| char *p = buf; |
| while( len-- > 0 ) |
| __pformat_putc( *p++, stream ); |
| } |
| |
| /* If we still haven't consumed the entire specified field width, |
| * we must be doing flush left justification; any residual width |
| * must be filled with blanks, to the right of the output value. |
| */ |
| while( stream->width-- > 0 ) |
| __pformat_putc( '\x20', stream ); |
| |
| #else /* __BUILD_WIDEAPI */ |
| |
| int len; |
| |
| if( (stream->precision >= 0) && (count > stream->precision) ) |
| count = stream->precision; |
| |
| if( (stream->flags & PFORMAT_TO_FILE) && (stream->flags & PFORMAT_NOLIMIT) ) |
| { |
| int __cdecl __ms_fwprintf(FILE *, const wchar_t *, ...); |
| |
| if( stream->width > count ) |
| { |
| if( (stream->flags & PFORMAT_LJUSTIFY) == 0 ) |
| len = __ms_fwprintf( (FILE *)(stream->dest), L"%*.*s", stream->width, count, s ); |
| else |
| len = __ms_fwprintf( (FILE *)(stream->dest), L"%-*.*s", stream->width, count, s ); |
| } |
| else |
| { |
| len = __ms_fwprintf( (FILE *)(stream->dest), L"%.*s", count, s ); |
| } |
| if( len > 0 ) |
| stream->count += len; |
| stream->width = PFORMAT_IGNORE; |
| return; |
| } |
| |
| if( stream->width > count ) |
| stream->width -= count; |
| else |
| stream->width = PFORMAT_IGNORE; |
| |
| if( (stream->width > 0) && ((stream->flags & PFORMAT_LJUSTIFY) == 0) ) |
| while( stream->width-- ) |
| __pformat_putc( '\x20', stream ); |
| |
| len = count; |
| while(len-- > 0 && *s != 0) |
| { |
| __pformat_putc(*s++, stream); |
| } |
| |
| while( stream->width-- > 0 ) |
| __pformat_putc( '\x20', stream ); |
| |
| #endif /* __BUILD_WIDEAPI */ |
| } |
| |
| static |
| void __pformat_wcputs( const wchar_t *s, __pformat_t *stream ) |
| { |
| /* Handler for `%S' (`%ls') conversion specifications. |
| * |
| * Transfer a NUL terminated wide character string, character by |
| * character, converting to its equivalent multibyte representation |
| * on output, and stopping when the end of the string is encountered, |
| * or if `precision' has been explicitly set, when the specified number |
| * of characters has been emitted, if that is less than the length of |
| * the input string, to the `__pformat()' output stream. |
| * |
| * This is implemented as a trivial call to `__pformat_wputchars()', |
| * passing the length of the input string as the character count, |
| * (after first verifying that the input pointer is not NULL). |
| */ |
| if( s == NULL ) s = L"(null)"; |
| |
| if( stream->precision >= 0 ) |
| __pformat_wputchars( s, wcsnlen( s, stream->precision ), stream ); |
| else |
| __pformat_wputchars( s, wcslen( s ), stream ); |
| } |
| |
| static |
| int __pformat_int_bufsiz( int bias, int size, __pformat_t *stream ) |
| { |
| /* Helper to establish the size of the internal buffer, which |
| * is required to queue the ASCII decomposition of an integral |
| * data value, prior to transfer to the output stream. |
| */ |
| size = ((size - 1 + LLONGBITS) / size) + bias; |
| size += (stream->precision > 0) ? stream->precision : 0; |
| if ((stream->flags & PFORMAT_GROUPED) != 0 && stream->thousands_chr != 0) |
| size += (size / 3); |
| return (size > stream->width) ? size : stream->width; |
| } |
| |
| static |
| void __pformat_int( __pformat_intarg_t value, __pformat_t *stream ) |
| { |
| /* Handler for `%d', `%i' and `%u' conversion specifications. |
| * |
| * Transfer the ASCII representation of an integer value parameter, |
| * formatted as a decimal number, to the `__pformat()' output queue; |
| * output will be truncated, if any specified quota is exceeded. |
| */ |
| int32_t bufflen = __pformat_int_bufsiz(1, PFORMAT_OSHIFT, stream); |
| #ifdef __ENABLE_PRINTF128 |
| char *tmp_buff = NULL; |
| #endif |
| char *buf = NULL; |
| char *p; |
| int precision; |
| |
| buf = alloca(bufflen); |
| p = buf; |
| if( stream->flags & PFORMAT_NEGATIVE ) |
| #ifdef __ENABLE_PRINTF128 |
| { |
| /* The input value might be negative, (i.e. it is a signed value)... |
| */ |
| if( value.__pformat_u128_t.t128.digits[1] < 0) { |
| /* |
| * It IS negative, but we want to encode it as unsigned, |
| * displayed with a leading minus sign, so convert it... |
| */ |
| /* two's complement */ |
| value.__pformat_u128_t.t128.digits[0] = ~value.__pformat_u128_t.t128.digits[0]; |
| value.__pformat_u128_t.t128.digits[1] = ~value.__pformat_u128_t.t128.digits[1]; |
| value.__pformat_u128_t.t128.digits[0] += 1; |
| value.__pformat_u128_t.t128.digits[1] += (!value.__pformat_u128_t.t128.digits[0]) ? 1 : 0; |
| } else |
| /* It is unequivocally a POSITIVE value, so turn off the |
| * request to prefix it with a minus sign... |
| */ |
| stream->flags &= ~PFORMAT_NEGATIVE; |
| } |
| |
| tmp_buff = alloca(bufflen); |
| /* Encode the input value for display... |
| */ |
| __bigint_to_string(value.__pformat_u128_t.t128_2.digits32, |
| 4, tmp_buff, bufflen); |
| __bigint_trim_leading_zeroes(tmp_buff,1); |
| |
| memset(p,0,bufflen); |
| for(int32_t i = strlen(tmp_buff) - 1; i >= 0; i--){ |
| if ( i && (stream->flags & PFORMAT_GROUPED) != 0 && stream->thousands_chr != 0 |
| && (i % 4) == 3) |
| { |
| *p++ = ','; |
| } |
| *p++ = tmp_buff[i]; |
| if( i > bufflen - 1) break; /* sanity chec */ |
| if( tmp_buff[i] == '\0' ) break; /* end */ |
| } |
| #else |
| { |
| /* The input value might be negative, (i.e. it is a signed value)... |
| */ |
| if( value.__pformat_llong_t < 0LL ) |
| /* |
| * It IS negative, but we want to encode it as unsigned, |
| * displayed with a leading minus sign, so convert it... |
| */ |
| value.__pformat_llong_t = -value.__pformat_llong_t; |
| |
| else |
| /* It is unequivocally a POSITIVE value, so turn off the |
| * request to prefix it with a minus sign... |
| */ |
| stream->flags &= ~PFORMAT_NEGATIVE; |
| } |
| while( value.__pformat_ullong_t ) |
| { |
| /* decomposing it into its constituent decimal digits, |
| * in order from least significant to most significant, using |
| * the local buffer as a LIFO queue in which to store them. |
| */ |
| if (p != buf && (stream->flags & PFORMAT_GROUPED) != 0 && stream->thousands_chr != 0 |
| && ((p - buf) % 4) == 3) |
| { |
| *p++ = ','; |
| } |
| *p++ = '0' + (unsigned char)(value.__pformat_ullong_t % 10LL); |
| value.__pformat_ullong_t /= 10LL; |
| } |
| #endif |
| |
| if( (stream->precision > 0) |
| && ((precision = stream->precision - (p - buf)) > 0) ) |
| /* |
| * We have not yet queued sufficient digits to fill the field width |
| * specified for minimum `precision'; pad with zeros to achieve this. |
| */ |
| while( precision-- > 0 ) |
| *p++ = '0'; |
| |
| if( (p == buf) && (stream->precision != 0) ) |
| /* |
| * Input value was zero; make sure we print at least one digit, |
| * unless the precision is also explicitly zero. |
| */ |
| *p++ = '0'; |
| |
| if( (stream->width > 0) && ((stream->width -= p - buf) > 0) ) |
| { |
| /* We have now queued sufficient characters to display the input value, |
| * at the desired precision, but this will not fill the output field... |
| */ |
| if( stream->flags & PFORMAT_SIGNED ) |
| /* |
| * We will fill one additional space with a sign... |
| */ |
| stream->width--; |
| |
| if( (stream->precision < 0) |
| && ((stream->flags & PFORMAT_JUSTIFY) == PFORMAT_ZEROFILL) ) |
| /* |
| * and the `0' flag is in effect, so we pad the remaining spaces, |
| * to the left of the displayed value, with zeros. |
| */ |
| while( stream->width-- > 0 ) |
| *p++ = '0'; |
| |
| else if( (stream->flags & PFORMAT_LJUSTIFY) == 0 ) |
| /* |
| * the `0' flag is not in effect, and neither is the `-' flag, |
| * so we pad to the left of the displayed value with spaces, so that |
| * the value appears right justified within the output field. |
| */ |
| while( stream->width-- > 0 ) |
| __pformat_putc( '\x20', stream ); |
| } |
| |
| if( stream->flags & PFORMAT_NEGATIVE ) |
| /* |
| * A negative value needs a sign... |
| */ |
| *p++ = '-'; |
| |
| else if( stream->flags & PFORMAT_POSITIVE ) |
| /* |
| * A positive value may have an optionally displayed sign... |
| */ |
| *p++ = '+'; |
| |
| else if( stream->flags & PFORMAT_ADDSPACE ) |
| /* |
| * Space was reserved for displaying a sign, but none was emitted... |
| */ |
| *p++ = '\x20'; |
| |
| while( p > buf ) |
| /* |
| * Emit the accumulated constituent digits, |
| * in order from most significant to least significant... |
| */ |
| __pformat_putc( *--p, stream ); |
| |
| while( stream->width-- > 0 ) |
| /* |
| * The specified output field has not yet been completely filled; |
| * the `-' flag must be in effect, resulting in a displayed value which |
| * appears left justified within the output field; we must pad the field |
| * to the right of the displayed value, by emitting additional spaces, |
| * until we reach the rightmost field boundary. |
| */ |
| __pformat_putc( '\x20', stream ); |
| } |
| |
| static |
| void __pformat_xint( int fmt, __pformat_intarg_t value, __pformat_t *stream ) |
| { |
| /* Handler for `%o', `%p', `%x' and `%X' conversions. |
| * |
| * These can be implemented using a simple `mask and shift' strategy; |
| * set up the mask and shift values appropriate to the conversion format, |
| * and allocate a suitably sized local buffer, in which to queue encoded |
| * digits of the formatted value, in preparation for output. |
| */ |
| int width; |
| int shift = (fmt == 'o') ? PFORMAT_OSHIFT : PFORMAT_XSHIFT; |
| int bufflen = __pformat_int_bufsiz(2, shift, stream); |
| char *buf = NULL; |
| #ifdef __ENABLE_PRINTF128 |
| char *tmp_buf = NULL; |
| #endif |
| char *p; |
| buf = alloca(bufflen); |
| p = buf; |
| #ifdef __ENABLE_PRINTF128 |
| tmp_buf = alloca(bufflen); |
| if(fmt == 'o'){ |
| __bigint_to_stringo(value.__pformat_u128_t.t128_2.digits32,4,tmp_buf,bufflen); |
| } else { |
| __bigint_to_stringx(value.__pformat_u128_t.t128_2.digits32,4,tmp_buf,bufflen, !(fmt & PFORMAT_XCASE)); |
| } |
| __bigint_trim_leading_zeroes(tmp_buf,0); |
| |
| memset(buf,0,bufflen); |
| for(int32_t i = strlen(tmp_buf); i >= 0; i--) |
| *p++ = tmp_buf[i]; |
| #else |
| int mask = (fmt == 'o') ? PFORMAT_OMASK : PFORMAT_XMASK; |
| while( value.__pformat_ullong_t ) |
| { |
| /* Encode the specified non-zero input value as a sequence of digits, |
| * in the appropriate `base' encoding and in reverse digit order, each |
| * encoded in its printable ASCII form, with no leading zeros, using |
| * the local buffer as a LIFO queue in which to store them. |
| */ |
| char *q; |
| if( (*(q = p++) = '0' + (value.__pformat_ullong_t & mask)) > '9' ) |
| *q = (*q + 'A' - '9' - 1) | (fmt & PFORMAT_XCASE); |
| value.__pformat_ullong_t >>= shift; |
| } |
| #endif |
| |
| if( p == buf ) |
| /* |
| * Nothing was queued; input value must be zero, which should never be |
| * emitted in the `alternative' PFORMAT_HASHED style. |
| */ |
| stream->flags &= ~PFORMAT_HASHED; |
| |
| if( ((width = stream->precision) > 0) && ((width -= p - buf) > 0) ) |
| /* |
| * We have not yet queued sufficient digits to fill the field width |
| * specified for minimum `precision'; pad with zeros to achieve this. |
| */ |
| while( width-- > 0 ) |
| *p++ = '0'; |
| |
| else if( (fmt == 'o') && (stream->flags & PFORMAT_HASHED) ) |
| /* |
| * The field width specified for minimum `precision' has already |
| * been filled, but the `alternative' PFORMAT_HASHED style for octal |
| * output requires at least one initial zero; that will not have |
| * been queued, so add it now. |
| */ |
| *p++ = '0'; |
| |
| if( (p == buf) && (stream->precision != 0) ) |
| /* |
| * Still nothing queued for output, but the `precision' has not been |
| * explicitly specified as zero, (which is necessary if no output for |
| * an input value of zero is desired); queue exactly one zero digit. |
| */ |
| *p++ = '0'; |
| |
| if( stream->width > (width = p - buf) ) |
| /* |
| * Specified field width exceeds the minimum required... |
| * Adjust so that we retain only the additional padding width. |
| */ |
| stream->width -= width; |
| |
| else |
| /* Ignore any width specification which is insufficient. |
| */ |
| stream->width = PFORMAT_IGNORE; |
| |
| if( ((width = stream->width) > 0) |
| && (fmt != 'o') && (stream->flags & PFORMAT_HASHED) ) |
| /* |
| * For `%#x' or `%#X' formats, (which have the `#' flag set), |
| * further reduce the padding width to accommodate the radix |
| * indicating prefix. |
| */ |
| width -= 2; |
| |
| if( (width > 0) && (stream->precision < 0) |
| && ((stream->flags & PFORMAT_JUSTIFY) == PFORMAT_ZEROFILL) ) |
| /* |
| * When the `0' flag is set, and not overridden by the `-' flag, |
| * or by a specified precision, add sufficient leading zeros to |
| * consume the remaining field width. |
| */ |
| while( width-- > 0 ) |
| *p++ = '0'; |
| |
| if( (fmt != 'o') && (stream->flags & PFORMAT_HASHED) ) |
| { |
| /* For formats other than octal, the PFORMAT_HASHED output style |
| * requires the addition of a two character radix indicator, as a |
| * prefix to the actual encoded numeric value. |
| */ |
| *p++ = fmt; |
| *p++ = '0'; |
| } |
| |
| if( (width > 0) && ((stream->flags & PFORMAT_LJUSTIFY) == 0) ) |
| /* |
| * When not doing flush left justification, (i.e. the `-' flag |
| * is not set), any residual unreserved field width must appear |
| * as blank padding, to the left of the output value. |
| */ |
| while( width-- > 0 ) |
| __pformat_putc( '\x20', stream ); |
| |
| while( p > buf ) |
| /* |
| * Move the queued output from the local buffer to the ultimate |
| * destination, in LIFO order. |
| */ |
| __pformat_putc( *--p, stream ); |
| |
| /* If we still haven't consumed the entire specified field width, |
| * we must be doing flush left justification; any residual width |
| * must be filled with blanks, to the right of the output value. |
| */ |
| while( width-- > 0 ) |
| __pformat_putc( '\x20', stream ); |
| } |
| |
| typedef union |
| { |
| /* A multifaceted representation of an IEEE extended precision, |
| * (80-bit), floating point number, facilitating access to its |
| * component parts. |
| */ |
| double __pformat_fpreg_double_t; |
| long double __pformat_fpreg_ldouble_t; |
| struct |
| { unsigned long long __pformat_fpreg_mantissa; |
| signed short __pformat_fpreg_exponent; |
| }; |
| unsigned short __pformat_fpreg_bitmap[5]; |
| unsigned long __pformat_fpreg_bits; |
| } __pformat_fpreg_t; |
| |
| #ifdef _WIN32 |
| /* TODO: make this unconditional in final release... |
| * (see note at head of associated `#else' block. |
| */ |
| #include "../gdtoa/gdtoa.h" |
| |
| static |
| char *__pformat_cvt( int mode, __pformat_fpreg_t x, int nd, int *dp, int *sign ) |
| { |
| /* Helper function, derived from David M. Gay's `g_xfmt()', calling |
| * his `__gdtoa()' function in a manner to provide extended precision |
| * replacements for `ecvt()' and `fcvt()'. |
| */ |
| int k; unsigned int e = 0; char *ep; |
| static FPI fpi = { 64, 1-16383-64+1, 32766-16383-64+1, FPI_Round_near, 0, 14 /* Int_max */ }; |
| |
| if( sizeof( double ) == sizeof( long double ) ) |
| { |
| /* The caller has written into x.__pformat_fpreg_ldouble_t, which |
| * actually isn't laid out in the way the rest of the union expects it. |
| */ |
| int exp = (x.__pformat_fpreg_mantissa >> 52) & 0x7ff; |
| unsigned long long mant = x.__pformat_fpreg_mantissa & 0x000fffffffffffffULL; |
| int integer = exp ? 1 : 0; |
| int signbit = x.__pformat_fpreg_mantissa >> 63; |
| |
| k = __fpclassify( x.__pformat_fpreg_double_t ); |
| |
| if (exp == 0x7ff) |
| exp = 0x7fff; |
| else if (exp != 0) |
| exp = exp - 1023 + 16383; |
| x.__pformat_fpreg_mantissa = (mant << 11) | ((unsigned long long)integer << 63); |
| x.__pformat_fpreg_exponent = exp | (signbit << 15); |
| } |
| else |
| k = __fpclassifyl( x.__pformat_fpreg_ldouble_t ); |
| |
| |
| /* Classify the argument into an appropriate `__gdtoa()' category... |
| */ |
| if( k & FP_NAN ) |
| /* |
| * identifying infinities or not-a-number... |
| */ |
| k = (k & FP_NORMAL) ? STRTOG_Infinite : STRTOG_NaN; |
| |
| else if( k & FP_NORMAL ) |
| { |
| /* normal and near-zero `denormals'... |
| */ |
| if( k & FP_ZERO ) |
| { |
| /* with appropriate exponent adjustment for a `denormal'... |
| */ |
| k = STRTOG_Denormal; |
| e = 1 - 0x3FFF - 63; |
| } |
| else |
| { |
| /* or with `normal' exponent adjustment... |
| */ |
| k = STRTOG_Normal; |
| e = (x.__pformat_fpreg_exponent & 0x7FFF) - 0x3FFF - 63; |
| } |
| } |
| |
| else |
| /* or, if none of the above, it's a zero, (positive or negative). |
| */ |
| k = STRTOG_Zero; |
| |
| /* Check for negative values, always treating NaN as unsigned... |
| * (return value is zero for positive/unsigned; non-zero for negative). |
| */ |
| *sign = (k == STRTOG_NaN) ? 0 : x.__pformat_fpreg_exponent & 0x8000; |
| |
| /* Finally, get the raw digit string, and radix point position index. |
| */ |
| return __gdtoa( &fpi, e, &x.__pformat_fpreg_bits, &k, mode, nd, dp, &ep ); |
| } |
| |
| static |
| char *__pformat_ecvt( long double x, int precision, int *dp, int *sign ) |
| { |
| /* A convenience wrapper for the above... |
| * it emulates `ecvt()', but takes a `long double' argument. |
| */ |
| __pformat_fpreg_t z; z.__pformat_fpreg_ldouble_t = x; |
| return __pformat_cvt( 2, z, precision, dp, sign ); |
| } |
| |
| static |
| char *__pformat_fcvt( long double x, int precision, int *dp, int *sign ) |
| { |
| /* A convenience wrapper for the above... |
| * it emulates `fcvt()', but takes a `long double' argument. |
| */ |
| __pformat_fpreg_t z; z.__pformat_fpreg_ldouble_t = x; |
| return __pformat_cvt( 3, z, precision, dp, sign ); |
| } |
| |
| /* The following are required, to clean up the `__gdtoa()' memory pool, |
| * after processing the data returned by the above. |
| */ |
| #define __pformat_ecvt_release( value ) __freedtoa( value ) |
| #define __pformat_fcvt_release( value ) __freedtoa( value ) |
| |
| #else |
| /* |
| * TODO: remove this before final release; it is included here as a |
| * convenience for testing, without requiring a working `__gdtoa()'. |
| */ |
| static |
| char *__pformat_ecvt( long double x, int precision, int *dp, int *sign ) |
| { |
| /* Define in terms of `ecvt()'... |
| */ |
| char *retval = ecvt( (double)(x), precision, dp, sign ); |
| if( isinf( x ) || isnan( x ) ) |
| { |
| /* emulating `__gdtoa()' reporting for infinities and NaN. |
| */ |
| *dp = PFORMAT_INFNAN; |
| if( *retval == '-' ) |
| { |
| /* Need to force the `sign' flag, (particularly for NaN). |
| */ |
| ++retval; *sign = 1; |
| } |
| } |
| return retval; |
| } |
| |
| static |
| char *__pformat_fcvt( long double x, int precision, int *dp, int *sign ) |
| { |
| /* Define in terms of `fcvt()'... |
| */ |
| char *retval = fcvt( (double)(x), precision, dp, sign ); |
| if( isinf( x ) || isnan( x ) ) |
| { |
| /* emulating `__gdtoa()' reporting for infinities and NaN. |
| */ |
| *dp = PFORMAT_INFNAN; |
| if( *retval == '-' ) |
| { |
| /* Need to force the `sign' flag, (particularly for NaN). |
| */ |
| ++retval; *sign = 1; |
| } |
| } |
| return retval; |
| } |
| |
| /* No memory pool clean up needed, for these emulated cases... |
| */ |
| #define __pformat_ecvt_release( value ) /* nothing to be done */ |
| #define __pformat_fcvt_release( value ) /* nothing to be done */ |
| |
| /* TODO: end of conditional to be removed. */ |
| #endif |
| |
| static |
| void __pformat_emit_radix_point( __pformat_t *stream ) |
| { |
| /* Helper to place a localised representation of the radix point |
| * character at the ultimate destination, when formatting fixed or |
| * floating point numbers. |
| */ |
| if( stream->rplen == PFORMAT_RPINIT ) |
| { |
| /* Radix point initialisation not yet completed; |
| * establish a multibyte to `wchar_t' converter... |
| */ |
| int len; wchar_t rpchr; mbstate_t state; |
| |
| /* Initialise the conversion state... |
| */ |
| memset( &state, 0, sizeof( state ) ); |
| |
| /* Fetch and convert the localised radix point representation... |
| */ |
| if( (len = mbrtowc( &rpchr, localeconv()->decimal_point, 16, &state )) > 0 ) |
| /* |
| * and store it, if valid. |
| */ |
| stream->rpchr = rpchr; |
| |
| /* In any case, store the reported effective multibyte length, |
| * (or the error flag), marking initialisation as `done'. |
| */ |
| stream->rplen = len; |
| } |
| |
| if( stream->rpchr != (wchar_t)(0) ) |
| { |
| /* We have a localised radix point mark; |
| * establish a converter to make it a multibyte character... |
| */ |
| #ifdef __BUILD_WIDEAPI |
| __pformat_putc (stream->rpchr, stream); |
| #else |
| int len; char buf[len = stream->rplen]; mbstate_t state; |
| |
| /* Initialise the conversion state... |
| */ |
| memset( &state, 0, sizeof( state ) ); |
| |
| /* Convert the `wchar_t' representation to multibyte... |
| */ |
| if( (len = wcrtomb( buf, stream->rpchr, &state )) > 0 ) |
| { |
| /* and copy to the output destination, when valid... |
| */ |
| char *p = buf; |
| while( len-- > 0 ) |
| __pformat_putc( *p++, stream ); |
| } |
| |
| else |
| /* otherwise fall back to plain ASCII '.'... |
| */ |
| __pformat_putc( '.', stream ); |
| #endif |
| } |
| else |
| /* No localisation: just use ASCII '.'... |
| */ |
| __pformat_putc( '.', stream ); |
| } |
| |
| static |
| void __pformat_emit_numeric_value( int c, __pformat_t *stream ) |
| { |
| /* Convenience helper to transfer numeric data from an internal |
| * formatting buffer to the ultimate destination... |
| */ |
| if( c == '.' ) |
| /* |
| * converting this internal representation of the the radix |
| * point to the appropriately localised representation... |
| */ |
| __pformat_emit_radix_point( stream ); |
| else if (c == ',') |
| { |
| wchar_t wcs; |
| if ((wcs = stream->thousands_chr) != 0) |
| __pformat_wputchars (&wcs, 1, stream); |
| } |
| else |
| /* and passing all other characters through, unmodified. |
| */ |
| __pformat_putc( c, stream ); |
| } |
| |
| static |
| void __pformat_emit_inf_or_nan( int sign, char *value, __pformat_t *stream ) |
| { |
| /* Helper to emit INF or NAN where a floating point value |
| * resolves to one of these special states. |
| */ |
| int i; |
| char buf[4]; |
| char *p = buf; |
| |
| /* We use the string formatting helper to display INF/NAN, |
| * but we don't want truncation if the precision set for the |
| * original floating point output request was insufficient; |
| * ignore it! |
| */ |
| stream->precision = PFORMAT_IGNORE; |
| |
| if( sign ) |
| /* |
| * Negative infinity: emit the sign... |
| */ |
| *p++ = '-'; |
| |
| else if( stream->flags & PFORMAT_POSITIVE ) |
| /* |
| * Not negative infinity, but '+' flag is in effect; |
| * thus, we emit a positive sign... |
| */ |
| *p++ = '+'; |
| |
| else if( stream->flags & PFORMAT_ADDSPACE ) |
| /* |
| * No sign required, but space was reserved for it... |
| */ |
| *p++ = '\x20'; |
| |
| /* Copy the appropriate status indicator, up to a maximum of |
| * three characters, transforming to the case corresponding to |
| * the format specification... |
| */ |
| for( i = 3; i > 0; --i ) |
| *p++ = (*value++ & ~PFORMAT_XCASE) | (stream->flags & PFORMAT_XCASE); |
| |
| /* and emit the result. |
| */ |
| __pformat_putchars( buf, p - buf, stream ); |
| } |
| |
| static |
| void __pformat_emit_float( int sign, char *value, int len, __pformat_t *stream ) |
| { |
| /* Helper to emit a fixed point representation of numeric data, |
| * as encoded by a prior call to `ecvt()' or `fcvt()'; (this does |
| * NOT include the exponent, for floating point format). |
| */ |
| if( len > 0 ) |
| { |
| /* The magnitude of `x' is greater than or equal to 1.0... |
| * reserve space in the output field, for the required number of |
| * decimal digits to be placed before the decimal point... |
| */ |
| if( stream->width >= len) |
| /* |
| * adjusting as appropriate, when width is sufficient... |
| */ |
| stream->width -= len; |
| |
| else |
| /* or simply ignoring the width specification, if not. |
| */ |
| stream->width = PFORMAT_IGNORE; |
| } |
| |
| else if( stream->width > 0 ) |
| /* |
| * The magnitude of `x' is less than 1.0... |
| * reserve space for exactly one zero before the decimal point. |
| */ |
| stream->width--; |
| |
| /* Reserve additional space for the digits which will follow the |
| * decimal point... |
| */ |
| if( (stream->width >= 0) && (stream->width > stream->precision) ) |
| /* |
| * adjusting appropriately, when sufficient width remains... |
| * (note that we must check both of these conditions, because |
| * precision may be more negative than width, as a result of |
| * adjustment to provide extra padding when trailing zeros |
| * are to be discarded from "%g" format conversion with a |
| * specified field width, but if width itself is negative, |
| * then there is explicitly to be no padding anyway). |
| */ |
| stream->width -= stream->precision; |
| |
| else |
| /* or again, ignoring the width specification, if not. |
| */ |
| stream->width = PFORMAT_IGNORE; |
| |
| /* Reserve space in the output field, for display of the decimal point, |
| * unless the precision is explicity zero, with the `#' flag not set. |
| */ |
| if ((stream->width > 0) |
| && ((stream->precision > 0) || (stream->flags & PFORMAT_HASHED))) |
| stream->width--; |
| |
| if (len > 0 && (stream->flags & PFORMAT_GROUPED) != 0 && stream->thousands_chr != 0) |
| { |
| int cths = ((len + 2) / 3) - 1; |
| while (cths > 0 && stream->width > 0) |
| { |
| --cths; stream->width--; |
| } |
| } |
| |
| /* Reserve space in the output field, for display of the sign of the |
| * formatted value, if required; (i.e. if the value is negative, or if |
| * either the `space' or `+' formatting flags are set). |
| */ |
| if( (stream->width > 0) && (sign || (stream->flags & PFORMAT_SIGNED)) ) |
| stream->width--; |
| |
| /* Emit any padding space, as required to correctly right justify |
| * the output within the alloted field width. |
| */ |
| if( (stream->width > 0) && ((stream->flags & PFORMAT_JUSTIFY) == 0) ) |
| while( stream->width-- > 0 ) |
| __pformat_putc( '\x20', stream ); |
| |
| /* Emit the sign indicator, as appropriate... |
| */ |
| if( sign ) |
| /* |
| * mandatory, for negative values... |
| */ |
| __pformat_putc( '-', stream ); |
| |
| else if( stream->flags & PFORMAT_POSITIVE ) |
| /* |
| * optional, for positive values... |
| */ |
| __pformat_putc( '+', stream ); |
| |
| else if( stream->flags & PFORMAT_ADDSPACE ) |
| /* |
| * or just fill reserved space, when the space flag is in effect. |
| */ |
| __pformat_putc( '\x20', stream ); |
| |
| /* If the `0' flag is in effect, and not overridden by the `-' flag, |
| * then zero padding, to fill out the field, goes here... |
| */ |
| if( (stream->width > 0) |
| && ((stream->flags & PFORMAT_JUSTIFY) == PFORMAT_ZEROFILL) ) |
| while( stream->width-- > 0 ) |
| __pformat_putc( '0', stream ); |
| |
| /* Emit the digits of the encoded numeric value... |
| */ |
| if( len > 0 ) |
| { |
| /* |
| * ...beginning with those which precede the radix point, |
| * and appending any necessary significant trailing zeros. |
| */ |
| do { |
| __pformat_putc( *value ? *value++ : '0', stream); |
| --len; |
| if (len != 0 && (stream->flags & PFORMAT_GROUPED) != 0 && stream->thousands_chr != 0 |
| && (len % 3) == 0) |
| __pformat_wputchars (&stream->thousands_chr, 1, stream); |
| } |
| while (len > 0); |
| } |
| else |
| /* The magnitude of the encoded value is less than 1.0, so no |
| * digits precede the radix point; we emit a mandatory initial |
| * zero, followed immediately by the radix point. |
| */ |
| __pformat_putc( '0', stream ); |
| |
| /* Unless the encoded value is integral, AND the radix point |
| * is not expressly demanded by the `#' flag, we must insert |
| * the appropriately localised radix point mark here... |
| */ |
| if( (stream->precision > 0) || (stream->flags & PFORMAT_HASHED) ) |
| __pformat_emit_radix_point( stream ); |
| |
| /* When the radix point offset, `len', is negative, this implies |
| * that additional zeros must appear, following the radix point, |
| * and preceding the first significant digit... |
| */ |
| if( len < 0 ) |
| { |
| /* To accommodate these, we adjust the precision, (reducing it |
| * by adding a negative value), and then we emit as many zeros |
| * as are required. |
| */ |
| stream->precision += len; |
| do __pformat_putc( '0', stream ); |
| while( ++len < 0 ); |
| } |
| |
| /* Now we emit any remaining significant digits, or trailing zeros, |
| * until the required precision has been achieved. |
| */ |
| while( stream->precision-- > 0 ) |
| __pformat_putc( *value ? *value++ : '0', stream ); |
| } |
| |
| static |
| void __pformat_emit_efloat( int sign, char *value, int e, __pformat_t *stream ) |
| { |
| /* Helper to emit a floating point representation of numeric data, |
| * as encoded by a prior call to `ecvt()' or `fcvt()'; (this DOES |
| * include the following exponent). |
| */ |
| int exp_width = 1; |
| __pformat_intarg_t exponent; exponent.__pformat_llong_t = e -= 1; |
| |
| /* Determine how many digit positions are required for the exponent. |
| */ |
| while( (e /= 10) != 0 ) |
| exp_width++; |
| |
| /* Ensure that this is at least as many as the standard requirement. |
| * The C99 standard requires the expenent to contain at least two |
| * digits, unless specified explicitly otherwise. |
| */ |
| if (stream->expmin == -1) |
| stream->expmin = 2; |
| if( exp_width < stream->expmin ) |
| exp_width = stream->expmin; |
| |
| /* Adjust the residual field width allocation, to allow for the |
| * number of exponent digits to be emitted, together with a sign |
| * and exponent separator... |
| */ |
| if( stream->width > (exp_width += 2) ) |
| stream->width -= exp_width; |
| |
| else |
| /* ignoring the field width specification, if insufficient. |
| */ |
| stream->width = PFORMAT_IGNORE; |
| |
| /* Emit the significand, as a fixed point value with one digit |
| * preceding the radix point. |
| */ |
| __pformat_emit_float( sign, value, 1, stream ); |
| |
| /* Reset precision, to ensure the mandatory minimum number of |
| * exponent digits will be emitted, and set the flags to ensure |
| * the sign is displayed. |
| */ |
| stream->precision = stream->expmin; |
| stream->flags |= PFORMAT_SIGNED; |
| |
| /* Emit the exponent separator. |
| */ |
| __pformat_putc( ('E' | (stream->flags & PFORMAT_XCASE)), stream ); |
| |
| /* Readjust the field width setting, such that it again allows |
| * for the digits of the exponent, (which had been discounted when |
| * computing any left side padding requirement), so that they are |
| * correctly included in the computation of any right side padding |
| * requirement, (but here we exclude the exponent separator, which |
| * has been emitted, and so counted already). |
| */ |
| stream->width += exp_width - 1; |
| |
| /* And finally, emit the exponent itself, as a signed integer, |
| * with any padding required to achieve flush left justification, |
| * (which will be added automatically, by `__pformat_int()'). |
| */ |
| __pformat_int( exponent, stream ); |
| } |
| |
| static |
| void __pformat_float( long double x, __pformat_t *stream ) |
| { |
| /* Handler for `%f' and `%F' format specifiers. |
| * |
| * This wraps calls to `__pformat_cvt()', `__pformat_emit_float()' |
| * and `__pformat_emit_inf_or_nan()', as appropriate, to achieve |
| * output in fixed point format. |
| */ |
| int sign, intlen; char *value; |
| |
| /* Establish the precision for the displayed value, defaulting to six |
| * digits following the decimal point, if not explicitly specified. |
| */ |
| if( stream->precision < 0 ) |
| stream->precision = 6; |
| |
| /* Encode the input value as ASCII, for display... |
| */ |
| value = __pformat_fcvt( x, stream->precision, &intlen, &sign ); |
| |
| if( intlen == PFORMAT_INFNAN ) |
| /* |
| * handle cases of `infinity' or `not-a-number'... |
| */ |
| __pformat_emit_inf_or_nan( sign, value, stream ); |
| |
| else |
| { /* or otherwise, emit the formatted result. |
| */ |
| __pformat_emit_float( sign, value, intlen, stream ); |
| |
| /* and, if there is any residual field width as yet unfilled, |
| * then we must be doing flush left justification, so pad out to |
| * the right hand field boundary. |
| */ |
| while( stream->width-- > 0 ) |
| __pformat_putc( '\x20', stream ); |
| } |
| |
| /* Clean up `__pformat_fcvt()' memory allocation for `value'... |
| */ |
| __pformat_fcvt_release( value ); |
| } |
| |
| #ifdef __ENABLE_DFP |
| |
| typedef struct decimal128_decode { |
| int64_t significand[2]; |
| int32_t exponent; |
| int sig_neg; |
| int exp_neg; |
| } decimal128_decode; |
| |
| static uint32_t dec128_decode(decimal128_decode *result, const _Decimal128 deci){ |
| int64_t significand2; |
| int64_t significand1; |
| int32_t exp_part; |
| int8_t sig_sign; |
| ud128 in; |
| in.d = deci; |
| |
| if(in.t0.bits == 0x3){ /*case 11 */ |
| /* should not enter here */ |
| sig_sign = in.t2.sign; |
| exp_part = in.t2.exponent; |
| significand1 = in.t2.mantissaL; |
| significand2 = (in.t2.mantissaH | (0x1ULL << 49)); |
| } else { |
| sig_sign = in.t1.sign; |
| exp_part = in.t1.exponent; |
| significand1 = in.t1.mantissaL; |
| significand2 = in.t1.mantissaH; |
| } |
| exp_part -= 6176; /* exp bias */ |
| |
| result->significand[0] = significand1; |
| result->significand[1] = significand2; /* higher */ |
| result->exponent = exp_part; |
| result->exp_neg = (exp_part < 0 )? 1 : 0; |
| result->sig_neg = sig_sign; |
| |
| return 0; |
| } |
| |
| static |
| void __pformat_efloat_decimal(_Decimal128 x, __pformat_t *stream ){ |
| decimal128_decode in; |
| char str_exp[8]; |
| char str_sig[40]; |
| int floatclass = __fpclassifyd128(x); |
| |
| /* precision control */ |
| int32_t prec = ( (stream->precision < 0) || (stream->precision > 38) ) ? |
| 6 : stream->precision; |
| int32_t max_prec; |
| int32_t exp_strlen; |
| |
| dec128_decode(&in,x); |
| |
| if((floatclass & FP_INFINITE) == FP_INFINITE){ |
| stream->precision = 3; |
| if(stream->flags & PFORMAT_SIGNED) |
| __pformat_putc( in.sig_neg ? '-' : '+', stream ); |
| __pformat_puts( (stream->flags & PFORMAT_XCASE) ? "inf" : "INF", stream); |
| return; |
| } else if(floatclass & FP_NAN){ |
| stream->precision = 3; |
| if(stream->flags & PFORMAT_SIGNED) |
| __pformat_putc( in.sig_neg ? '-' : '+', stream ); |
| __pformat_puts( (stream->flags & PFORMAT_XCASE) ? "nan" : "NAN", stream); |
| return; |
| } |
| |
| /* Stringify significand */ |
| __bigint_to_string( |
| (uint32_t[4]){in.significand[0] & 0x0ffffffff, in.significand[0] >> 32, in.significand[1] & 0x0ffffffff, in.significand[1] >> 32 }, |
| 4, str_sig, sizeof(str_sig)); |
| __bigint_trim_leading_zeroes(str_sig,1); |
| max_prec = strlen(str_sig+1); |
| |
| /* Try to canonize exponent */ |
| in.exponent += max_prec; |
| in.exp_neg = (in.exponent < 0 ) ? 1 : 0; |
| |
| /* stringify exponent */ |
| __bigint_to_string( |
| (uint32_t[1]) { in.exp_neg ? -in.exponent : in.exponent}, |
| 1, str_exp, sizeof(str_exp)); |
| exp_strlen = strlen(__bigint_trim_leading_zeroes(str_exp,3)); |
| |
| /* account for dot, +-e */ |
| for(int32_t spacers = 0; spacers < stream->width - max_prec - exp_strlen - 4; spacers++) |
| __pformat_putc( ' ', stream ); |
| |
| /* optional sign */ |
| if (in.sig_neg || (stream->flags & PFORMAT_SIGNED)) { |
| __pformat_putc( in.sig_neg ? '-' : '+', stream ); |
| } else if( stream->width - max_prec - exp_strlen - 4 > 0 ) { |
| __pformat_putc( ' ', stream ); |
| } |
| stream->width = 0; |
| /* s.sss form */ |
| __pformat_putc(str_sig[0], stream); |
| if(prec) { |
| /* str_sig[prec+1] = '\0';*/ |
| __pformat_emit_radix_point(stream); |
| __pformat_putchars(str_sig+1, prec, stream); |
| |
| /* Pad with 0s */ |
| for(int i = max_prec; i < prec; i++) |
| __pformat_putc('0', stream); |
| } |
| |
| stream->precision = exp_strlen; /* force puts to emit */ |
| |
| __pformat_putc( ('E' | (stream->flags & PFORMAT_XCASE)), stream ); |
| __pformat_putc( in.exp_neg ? '-' : '+', stream ); |
| |
| for(int32_t trailing = 0; trailing < 3 - exp_strlen; trailing++) |
| __pformat_putc('0', stream); |
| __pformat_putchars(str_exp, exp_strlen,stream); |
| } |
| |
| static |
| void __pformat_float_decimal(_Decimal128 x, __pformat_t *stream ){ |
| decimal128_decode in; |
| char str_exp[8]; |
| char str_sig[40]; |
| int floatclass = __fpclassifyd128(x); |
| |
| /* precision control */ |
| int prec = ( (stream->precision < 0) || (stream->precision > 38) ) ? |
| 6 : stream->precision; |
| int max_prec; |
| |
| dec128_decode(&in,x); |
| |
| if((floatclass & FP_INFINITE) == FP_INFINITE){ |
| stream->precision = 3; |
| if(stream->flags & PFORMAT_SIGNED) |
| __pformat_putc( in.sig_neg ? '-' : '+', stream ); |
| __pformat_puts( (stream->flags & PFORMAT_XCASE) ? "inf" : "INF", stream); |
| return; |
| } else if(floatclass & FP_NAN){ |
| stream->precision = 3; |
| if(stream->flags & PFORMAT_SIGNED) |
| __pformat_putc( in.sig_neg ? '-' : '+', stream ); |
| __pformat_puts( (stream->flags & PFORMAT_XCASE) ? "nan" : "NAN", stream); |
| return; |
| } |
| |
| /* Stringify significand */ |
| __bigint_to_string( |
| (uint32_t[4]){in.significand[0] & 0x0ffffffff, in.significand[0] >> 32, in.significand[1] & 0x0ffffffff, in.significand[1] >> 32 }, |
| 4, str_sig, sizeof(str_sig)); |
| __bigint_trim_leading_zeroes(str_sig,0); |
| max_prec = strlen(str_sig); |
| |
| /* stringify exponent */ |
| __bigint_to_string( |
| (uint32_t[1]) { in.exp_neg ? -in.exponent : in.exponent}, |
| 1, str_exp, sizeof(str_exp)); |
| __bigint_trim_leading_zeroes(str_exp,0); |
| |
| int32_t decimal_place = max_prec + in.exponent; |
| int32_t sig_written = 0; |
| |
| /*account for . +- */ |
| for(int32_t spacers = 0; spacers < stream->width - decimal_place - prec - 2; spacers++) |
| __pformat_putc( ' ', stream ); |
| |
| if (in.sig_neg || (stream->flags & PFORMAT_SIGNED)) { |
| __pformat_putc( in.sig_neg ? '-' : '+', stream ); |
| } else if(stream->width - decimal_place - prec - 1 > 0){ |
| __pformat_putc( ' ', stream ); |
| } |
| |
| if(decimal_place <= 0){ /* easy mode */ |
| __pformat_putc( '0', stream ); |
| points: |
| __pformat_emit_radix_point(stream); |
| for(int32_t written = 0; written < prec; written++){ |
| if(decimal_place < 0){ /* leading 0s */ |
| decimal_place++; |
| __pformat_putc( '0', stream ); |
| /* significand */ |
| } else if ( sig_written < max_prec ){ |
| __pformat_putc( str_sig[sig_written], stream ); |
| sig_written++; |
| } else { /* trailing 0s */ |
| __pformat_putc( '0', stream ); |
| } |
| } |
| } else { /* hard mode */ |
| for(; sig_written < decimal_place; sig_written++){ |
| __pformat_putc( str_sig[sig_written], stream ); |
| if(sig_written == max_prec - 1) break; |
| } |
| decimal_place -= sig_written; |
| for(; decimal_place > 0; decimal_place--) |
| __pformat_putc( '0', stream ); |
| goto points; |
| } |
| |
| return; |
| } |
| |
| static |
| void __pformat_gfloat_decimal(_Decimal128 x, __pformat_t *stream ){ |
| int prec = ( (stream->precision < 0)) ? |
| 6 : stream->precision; |
| decimal128_decode in; |
| dec128_decode(&in,x); |
| if(in.exponent > prec) __pformat_efloat_decimal(x,stream); |
| else __pformat_float_decimal(x,stream); |
| } |
| |
| #endif /* __ENABLE_DFP */ |
| |
| static |
| void __pformat_efloat( long double x, __pformat_t *stream ) |
| { |
| /* Handler for `%e' and `%E' format specifiers. |
| * |
| * This wraps calls to `__pformat_cvt()', `__pformat_emit_efloat()' |
| * and `__pformat_emit_inf_or_nan()', as appropriate, to achieve |
| * output in floating point format. |
| */ |
| int sign, intlen; char *value; |
| |
| /* Establish the precision for the displayed value, defaulting to six |
| * digits following the decimal point, if not explicitly specified. |
| */ |
| if( stream->precision < 0 ) |
| stream->precision = 6; |
| |
| /* Encode the input value as ASCII, for display... |
| */ |
| value = __pformat_ecvt( x, stream->precision + 1, &intlen, &sign ); |
| |
| if( intlen == PFORMAT_INFNAN ) |
| /* |
| * handle cases of `infinity' or `not-a-number'... |
| */ |
| __pformat_emit_inf_or_nan( sign, value, stream ); |
| |
| else |
| /* or otherwise, emit the formatted result. |
| */ |
| __pformat_emit_efloat( sign, value, intlen, stream ); |
| |
| /* Clean up `__pformat_ecvt()' memory allocation for `value'... |
| */ |
| __pformat_ecvt_release( value ); |
| } |
| |
| static |
| void __pformat_gfloat( long double x, __pformat_t *stream ) |
| { |
| /* Handler for `%g' and `%G' format specifiers. |
| * |
| * This wraps calls to `__pformat_cvt()', `__pformat_emit_float()', |
| * `__pformat_emit_efloat()' and `__pformat_emit_inf_or_nan()', as |
| * appropriate, to achieve output in the more suitable of either |
| * fixed or floating point format. |
| */ |
| int sign, intlen; char *value; |
| |
| /* Establish the precision for the displayed value, defaulting to |
| * six significant digits, if not explicitly specified... |
| */ |
| if( stream->precision < 0 ) |
| stream->precision = 6; |
| |
| /* or to a minimum of one digit, otherwise... |
| */ |
| else if( stream->precision == 0 ) |
| stream->precision = 1; |
| |
| /* Encode the input value as ASCII, for display. |
| */ |
| value = __pformat_ecvt( x, stream->precision, &intlen, &sign ); |
| |
| if( intlen == PFORMAT_INFNAN ) |
| /* |
| * Handle cases of `infinity' or `not-a-number'. |
| */ |
| __pformat_emit_inf_or_nan( sign, value, stream ); |
| |
| else if( (-4 < intlen) && (intlen <= stream->precision) ) |
| { |
| /* Value lies in the acceptable range for fixed point output, |
| * (i.e. the exponent is no less than minus four, and the number |
| * of significant digits which precede the radix point is fewer |
| * than the least number which would overflow the field width, |
| * specified or implied by the established precision). |
| */ |
| if( (stream->flags & PFORMAT_HASHED) == PFORMAT_HASHED ) |
| /* |
| * The `#' flag is in effect... |
| * Adjust precision to retain the specified number of significant |
| * digits, with the proper number preceding the radix point, and |
| * the balance following it... |
| */ |
| stream->precision -= intlen; |
| |
| else |
| /* The `#' flag is not in effect... |
| * Here we adjust the precision to accommodate all digits which |
| * precede the radix point, but we truncate any balance following |
| * it, to suppress output of non-significant trailing zeros... |
| */ |
| if( ((stream->precision = strlen( value ) - intlen) < 0) |
| /* |
| * This may require a compensating adjustment to the field |
| * width, to accommodate significant trailing zeros, which |
| * precede the radix point... |
| */ |
| && (stream->width > 0) ) |
| stream->width += stream->precision; |
| |
| /* Now, we format the result as any other fixed point value. |
| */ |
| __pformat_emit_float( sign, value, intlen, stream ); |
| |
| /* If there is any residual field width as yet unfilled, then |
| * we must be doing flush left justification, so pad out to the |
| * right hand field boundary. |
| */ |
| while( stream->width-- > 0 ) |
| __pformat_putc( '\x20', stream ); |
| } |
| |
| else |
| { /* Value lies outside the acceptable range for fixed point; |
| * one significant digit will precede the radix point, so we |
| * decrement the precision to retain only the appropriate number |
| * of additional digits following it, when we emit the result |
| * in floating point format. |
| */ |
| if( (stream->flags & PFORMAT_HASHED) == PFORMAT_HASHED ) |
| /* |
| * The `#' flag is in effect... |
| * Adjust precision to emit the specified number of significant |
| * digits, with one preceding the radix point, and the balance |
| * following it, retaining any non-significant trailing zeros |
| * which are required to exactly match the requested precision... |
| */ |
| stream->precision--; |
| |
| else |
| /* The `#' flag is not in effect... |
| * Adjust precision to emit only significant digits, with one |
| * preceding the radix point, and any others following it, but |
| * suppressing non-significant trailing zeros... |
| */ |
| stream->precision = strlen( value ) - 1; |
| |
| /* Now, we format the result as any other floating point value. |
| */ |
| __pformat_emit_efloat( sign, value, intlen, stream ); |
| } |
| |
| /* Clean up `__pformat_ecvt()' memory allocation for `value'. |
| */ |
| __pformat_ecvt_release( value ); |
| } |
| |
| static |
| void __pformat_emit_xfloat( __pformat_fpreg_t value, __pformat_t *stream ) |
| { |
| /* Helper for emitting floating point data, originating as |
| * either `double' or `long double' type, as a hexadecimal |
| * representation of the argument value. |
| */ |
| char buf[18 + 6], *p = buf; |
| __pformat_intarg_t exponent; short exp_width = 2; |
| |
| /* The mantissa field of the argument value representation can |
| * accommodate at most 16 hexadecimal digits, of which one will |
| * be placed before the radix point, leaving at most 15 digits |
| * to satisfy any requested precision; thus... |
| */ |
| if( (stream->precision >= 0) && (stream->precision < 15) ) |
| { |
| /* When the user specifies a precision within this range, |
| * we want to adjust the mantissa, to retain just the number |
| * of digits required, rounding up when the high bit of the |
| * leftmost discarded digit is set; (mask of 0x08 accounts |
| * for exactly one digit discarded, shifting 4 bits per |
| * digit, with up to 14 additional digits, to consume the |
| * full availability of 15 precision digits). |
| * |
| * However, before we perform the rounding operation, we |
| * normalise the mantissa, shifting it to the left by as many |
| * bit positions may be necessary, until its highest order bit |
| * is set, thus preserving the maximum number of bits in the |
| * rounded result as possible. |
| */ |
| while( value.__pformat_fpreg_mantissa < (LLONG_MAX + 1ULL) ) |
| value.__pformat_fpreg_mantissa <<= 1; |
| |
| /* We then shift the mantissa one bit position back to the |
| * right, to guard against possible overflow when the rounding |
| * adjustment is added. |
| */ |
| value.__pformat_fpreg_mantissa >>= 1; |
| |
| /* We now add the rounding adjustment, noting that to keep the |
| * 0x08 mask aligned with the shifted mantissa, we also need to |
| * shift it right by one bit initially, changing its starting |
| * value to 0x04... |
| */ |
| value.__pformat_fpreg_mantissa += 0x04LL << (4 * (14 - stream->precision)); |
| if( (value.__pformat_fpreg_mantissa & (LLONG_MAX + 1ULL)) == 0ULL ) |
| /* |
| * When the rounding adjustment would not have overflowed, |
| * then we shift back to the left again, to fill the vacated |
| * bit we reserved to accommodate the carry. |
| */ |
| value.__pformat_fpreg_mantissa <<= 1; |
| |
| else |
| /* Otherwise the rounding adjustment would have overflowed, |
| * so the carry has already filled the vacated bit; the effect |
| * of this is equivalent to an increment of the exponent. |
| */ |
| value.__pformat_fpreg_exponent++; |
| |
| /* We now complete the rounding to the required precision, by |
| * shifting the unwanted digits out, from the right hand end of |
| * the mantissa. |
| */ |
| value.__pformat_fpreg_mantissa >>= 4 * (15 - stream->precision); |
| } |
| |
| /* Encode the significant digits of the mantissa in hexadecimal |
| * ASCII notation, ready for transfer to the output stream... |
| */ |
| while( value.__pformat_fpreg_mantissa ) |
| { |
| /* taking the rightmost digit in each pass... |
| */ |
| unsigned c = value.__pformat_fpreg_mantissa & 0xF; |
| if( c == value.__pformat_fpreg_mantissa) |
| { |
| /* inserting the radix point, when we reach the last, |
| * (i.e. the most significant digit), unless we found no |
| * less significant digits, with no mandatory radix point |
| * inclusion, and no additional required precision... |
| */ |
| if( (p > buf) |
| || (stream->flags & PFORMAT_HASHED) || (stream->precision > 0) ) |
| { |
| /* |
| * Internally, we represent the radix point as an ASCII '.'; |
| * we will replace it with any locale specific alternative, |
| * at the time of transfer to the ultimate destination. |
| */ |
| *p++ = '.'; |
| } |
| |
| /* If the most significant hexadecimal digit of the encoded |
| * output value is greater than one, then the indicated value |
| * will appear too large, by an additional binary exponent |
| * corresponding to the number of higher order bit positions |
| * which it occupies... |
| */ |
| while( value.__pformat_fpreg_mantissa > 1 ) |
| { |
| /* so reduce the exponent value to compensate... |
| */ |
| value.__pformat_fpreg_exponent--; |
| value.__pformat_fpreg_mantissa >>= 1; |
| } |
| } |
| |
| else if( stream->precision > 0 ) |
| /* |
| * we have not yet fulfilled the desired precision, |
| * and we have not yet found the most significant digit, |
| * so account for the current digit, within the field |
| * width required to meet the specified precision. |
| */ |
| stream->precision--; |
| |
| if( (c > 0) || (p > buf) || (stream->precision >= 0) ) |
| { |
| /* |
| * Ignoring insignificant trailing zeros, (unless required to |
| * satisfy specified precision), store the current encoded digit |
| * into the pending output buffer, in LIFO order, and using the |
| * appropriate case for digits in the `A'..`F' range. |
| */ |
| *p++ = c > 9 ? (c - 10 + 'A') | (stream->flags & PFORMAT_XCASE) : c + '0'; |
| } |
| /* Shift out the current digit, (4-bit logical shift right), |
| * to align the next more significant digit to be extracted, |
| * and encoded in the next pass. |
| */ |
| value.__pformat_fpreg_mantissa >>= 4; |
| } |
| |
| if( p == buf ) |
| { |
| /* Nothing has been queued for output... |
| * We need at least one zero, and possibly a radix point. |
| */ |
| if( (stream->precision > 0) || (stream->flags & PFORMAT_HASHED) ) |
| *p++ = '.'; |
| |
| *p++ = '0'; |
| } |
| |
| if( stream->width > 0 ) |
| { |
| /* Adjust the user specified field width, to account for the |
| * number of digits minimally required, to display the encoded |
| * value, at the requested precision. |
| * |
| * FIXME: this uses the minimum number of digits possible for |
| * representation of the binary exponent, in strict conformance |
| * with C99 and POSIX specifications. Although there appears to |
| * be no Microsoft precedent for doing otherwise, we may wish to |
| * relate this to the `_get_output_format()' result, to maintain |
| * consistency with `%e', `%f' and `%g' styles. |
| */ |
| int min_width = p - buf; |
| int exponent2 = value.__pformat_fpreg_exponent; |
| |
| /* If we have not yet queued sufficient digits to fulfil the |
| * requested precision, then we must adjust the minimum width |
| * specification, to accommodate the additional digits which |
| * are required to do so. |
| */ |
| if( stream->precision > 0 ) |
| min_width += stream->precision; |
| |
| /* Adjust the minimum width requirement, to accomodate the |
| * sign, radix indicator and at least one exponent digit... |
| */ |
| min_width += stream->flags & PFORMAT_SIGNED ? 6 : 5; |
| while( (exponent2 = exponent2 / 10) != 0 ) |
| { |
| /* and increase as required, if additional exponent digits |
| * are needed, also saving the exponent field width adjustment, |
| * for later use when that is emitted. |
| */ |
| min_width++; |
| exp_width++; |
| } |
| |
| if( stream->width > min_width ) |
| { |
| /* When specified field width exceeds the minimum required, |
| * adjust to retain only the excess... |
| */ |
| stream->width -= min_width; |
| |
| /* and then emit any required left side padding spaces. |
| */ |
| if( (stream->flags & PFORMAT_JUSTIFY) == 0 ) |
| while( stream->width-- > 0 ) |
| __pformat_putc( '\x20', stream ); |
| } |
| |
| else |
| /* Specified field width is insufficient; just ignore it! |
| */ |
| stream->width = PFORMAT_IGNORE; |
| } |
| |
| /* Emit the sign of the encoded value, as required... |
| */ |
| if( stream->flags & PFORMAT_NEGATIVE ) |
| /* |
| * this is mandatory, to indicate a negative value... |
| */ |
| __pformat_putc( '-', stream ); |
| |
| else if( stream->flags & PFORMAT_POSITIVE ) |
| /* |
| * but this is optional, for a positive value... |
| */ |
| __pformat_putc( '+', stream ); |
| |
| else if( stream->flags & PFORMAT_ADDSPACE ) |
| /* |
| * with this optional alternative. |
| */ |
| __pformat_putc( '\x20', stream ); |
| |
| /* Prefix a `0x' or `0X' radix indicator to the encoded value, |
| * with case appropriate to the format specification. |
| */ |
| __pformat_putc( '0', stream ); |
| __pformat_putc( 'X' | (stream->flags & PFORMAT_XCASE), stream ); |
| |
| /* If the `0' flag is in effect... |
| * Zero padding, to fill out the field, goes here... |
| */ |
| if( (stream->width > 0) && (stream->flags & PFORMAT_ZEROFILL) ) |
| while( stream->width-- > 0 ) |
| __pformat_putc( '0', stream ); |
| |
| /* Next, we emit the encoded value, without its exponent... |
| */ |
| while( p > buf ) |
| __pformat_emit_numeric_value( *--p, stream ); |
| |
| /* followed by any additional zeros needed to satisfy the |
| * precision specification... |
| */ |
| while( stream->precision-- > 0 ) |
| __pformat_putc( '0', stream ); |
| |
| /* then the exponent prefix, (C99 and POSIX specify `p'), |
| * in the case appropriate to the format specification... |
| */ |
| __pformat_putc( 'P' | (stream->flags & PFORMAT_XCASE), stream ); |
| |
| /* and finally, the decimal representation of the binary exponent, |
| * as a signed value with mandatory sign displayed, in a field width |
| * adjusted to accommodate it, LEFT justified, with any additional |
| * right side padding remaining from the original field width. |
| */ |
| stream->width += exp_width; |
| stream->flags |= PFORMAT_SIGNED; |
| /* sign extend */ |
| exponent.__pformat_u128_t.t128.digits[1] = (value.__pformat_fpreg_exponent < 0) ? -1 : 0; |
| exponent.__pformat_u128_t.t128.digits[0] = value.__pformat_fpreg_exponent; |
| __pformat_int( exponent, stream ); |
| } |
| |
| static |
| void __pformat_xldouble( long double x, __pformat_t *stream ) |
| { |
| /* Handler for `%La' and `%LA' format specifiers, (with argument |
| * value specified as `long double' type). |
| */ |
| unsigned sign_bit = 0; |
| __pformat_fpreg_t z; z.__pformat_fpreg_ldouble_t = x; |
| |
| /* First check for NaN; it is emitted unsigned... |
| */ |
| if( isnan( x ) ) |
| __pformat_emit_inf_or_nan( sign_bit, "NaN", stream ); |
| |
| else |
| { /* Capture the sign bit up-front, so we can show it correctly |
| * even when the argument value is zero or infinite. |
| */ |
| if( (sign_bit = (z.__pformat_fpreg_exponent & 0x8000)) != 0 ) |
| stream->flags |= PFORMAT_NEGATIVE; |
| |
| /* Check for infinity, (positive or negative)... |
| */ |
| if( isinf( x ) ) |
| /* |
| * displaying the appropriately signed indicator, |
| * when appropriate. |
| */ |
| __pformat_emit_inf_or_nan( sign_bit, "Inf", stream ); |
| |
| else |
| { /* The argument value is a representable number... |
| * extract the effective value of the biased exponent... |
| */ |
| z.__pformat_fpreg_exponent &= 0x7FFF; |
| if( z.__pformat_fpreg_exponent == 0 ) |
| { |
| /* A biased exponent value of zero means either a |
| * true zero value, if the mantissa field also has |
| * a zero value, otherwise... |
| */ |
| if( z.__pformat_fpreg_mantissa != 0 ) |
| { |
| /* ...this mantissa represents a subnormal value; |
| * adjust the exponent, while shifting the mantissa |
| * to the left, until its leading bit is 1. |
| */ |
| z.__pformat_fpreg_exponent = 1-0x3FFF; |
| while( (z.__pformat_fpreg_mantissa & (LLONG_MAX + 1ULL)) == 0 ) |
| { |
| z.__pformat_fpreg_mantissa <<= 1; |
| --z.__pformat_fpreg_exponent; |
| } |
| } |
| } |
| else |
| /* This argument represents a non-zero normal number; |
| * eliminate the bias from the exponent... |
| */ |
| z.__pformat_fpreg_exponent -= 0x3FFF; |
| |
| /* Finally, hand the adjusted representation off to the |
| * generalised hexadecimal floating point format handler... |
| */ |
| __pformat_emit_xfloat( z, stream ); |
| } |
| } |
| } |
| |
| int |
| __pformat (int flags, void *dest, int max, const APICHAR *fmt, va_list argv) |
| { |
| int c; |
| int saved_errno = errno; |
| |
| __pformat_t stream = |
| { |
| /* Create and initialise a format control block |
| * for this output request. |
| */ |
| dest, /* output goes to here */ |
| flags &= PFORMAT_TO_FILE | PFORMAT_NOLIMIT, /* only these valid initially */ |
| PFORMAT_IGNORE, /* no field width yet */ |
| PFORMAT_IGNORE, /* nor any precision spec */ |
| PFORMAT_RPINIT, /* radix point uninitialised */ |
| (wchar_t)(0), /* leave it unspecified */ |
| 0, |
| (wchar_t)(0), /* leave it unspecified */ |
| 0, /* zero output char count */ |
| max, /* establish output limit */ |
| -1 /* exponent chars preferred; |
| -1 means to be determined. */ |
| }; |
| |
| #ifdef __BUILD_WIDEAPI |
| const APICHAR *literal_string_start = NULL; |
| #endif |
| |
| format_scan: while( (c = *fmt++) != 0 ) |
| { |
| /* Format string parsing loop... |
| * The entry point is labelled, so that we can return to the start state |
| * from within the inner `conversion specification' interpretation loop, |
| * as soon as a conversion specification has been resolved. |
| */ |
| if( c == '%' ) |
| { |
| /* Initiate parsing of a `conversion specification'... |
| */ |
| __pformat_intarg_t argval; |
| __pformat_state_t state = PFORMAT_INIT; |
| __pformat_length_t length = PFORMAT_LENGTH_INT; |
| |
| /* Save the current format scan position, so that we can backtrack |
| * in the event of encountering an invalid format specification... |
| */ |
| const APICHAR *backtrack = fmt; |
| |
| /* Restart capture for dynamic field width and precision specs... |
| */ |
| int *width_spec = &stream.width; |
| |
| #ifdef __BUILD_WIDEAPI |
| if (literal_string_start) |
| { |
| stream.width = stream.precision = PFORMAT_IGNORE; |
| __pformat_wputchars( literal_string_start, fmt - literal_string_start - 1, &stream ); |
| literal_string_start = NULL; |
| } |
| #endif |
| |
| /* Reset initial state for flags, width and precision specs... |
| */ |
| stream.flags = flags; |
| stream.width = stream.precision = PFORMAT_IGNORE; |
| |
| while( *fmt ) |
| { |
| switch( c = *fmt++ ) |
| { |
| /* Data type specifiers... |
| * All are terminal, so exit the conversion spec parsing loop |
| * with a `goto format_scan', thus resuming at the outer level |
| * in the regular format string parser. |
| */ |
| case '%': |
| /* |
| * Not strictly a data type specifier... |
| * it simply converts as a literal `%' character. |
| * |
| * FIXME: should we require this to IMMEDIATELY follow the |
| * initial `%' of the "conversion spec"? (glibc `printf()' |
| * on GNU/Linux does NOT appear to require this, but POSIX |
| * and SUSv3 do seem to demand it). |
| */ |
| #ifndef __BUILD_WIDEAPI |
| __pformat_putc( c, &stream ); |
| #else |
| stream.width = stream.precision = PFORMAT_IGNORE; |
| __pformat_wputchars( L"%", 1, &stream ); |
| #endif |
| goto format_scan; |
| |
| case 'C': |
| /* |
| * Equivalent to `%lc'; set `length' accordingly, |
| * and simply fall through. |
| */ |
| length = PFORMAT_LENGTH_LONG; |
| |
| case 'c': |
| /* |
| * Single, (or single multibyte), character output... |
| * |
| * We handle these by copying the argument into our local |
| * `argval' buffer, and then we pass the address of that to |
| * either `__pformat_putchars()' or `__pformat_wputchars()', |
| * as appropriate, effectively formatting it as a string of |
| * the appropriate type, with a length of one. |
| * |
| * A side effect of this method of handling character data |
| * is that, if the user sets a precision of zero, then no |
| * character is actually emitted; we don't want that, so we |
| * forcibly override any user specified precision. |
| */ |
| stream.precision = PFORMAT_IGNORE; |
| |
| /* Now we invoke the appropriate format handler... |
| */ |
| if( (length == PFORMAT_LENGTH_LONG) |
| || (length == PFORMAT_LENGTH_LLONG) ) |
| { |
| /* considering any `long' type modifier as a reference to |
| * `wchar_t' data, (which is promoted to an `int' argument)... |
| */ |
| wchar_t iargval = (wchar_t)(va_arg( argv, int )); |
| __pformat_wputchars( &iargval, 1, &stream ); |
| } |
| else |
| { /* while anything else is simply taken as `char', (which |
| * is also promoted to an `int' argument)... |
| */ |
| argval.__pformat_uchar_t = (unsigned char)(va_arg( argv, int )); |
| __pformat_putchars( (char *)(&argval), 1, &stream ); |
| } |
| goto format_scan; |
| |
| case 'S': |
| /* |
| * Equivalent to `%ls'; set `length' accordingly, |
| * and simply fall through. |
| */ |
| length = PFORMAT_LENGTH_LONG; |
| |
| case 's': |
| if( (length == PFORMAT_LENGTH_LONG) |
| || (length == PFORMAT_LENGTH_LLONG)) |
| { |
| /* considering any `long' type modifier as a reference to |
| * a `wchar_t' string... |
| */ |
| __pformat_wcputs( va_arg( argv, wchar_t * ), &stream ); |
| } |
| else |
| /* This is normal string output; |
| * we simply invoke the appropriate handler... |
| */ |
| __pformat_puts( va_arg( argv, char * ), &stream ); |
| goto format_scan; |
| case 'm': /* strerror (errno) */ |
| __pformat_puts (strerror (saved_errno), &stream); |
| goto format_scan; |
| |
| case 'o': |
| case 'u': |
| case 'x': |
| case 'X': |
| /* |
| * Unsigned integer values; octal, decimal or hexadecimal format... |
| */ |
| #if __ENABLE_PRINTF128 |
| argval.__pformat_u128_t.t128.digits[1] = 0LL; /* no sign extend needed */ |
| if( length == PFORMAT_LENGTH_LLONG128 ) |
| argval.__pformat_u128_t.t128 = va_arg( argv, __tI128 ); |
| else |
| #endif |
| if( length == PFORMAT_LENGTH_LLONG ) { |
| /* |
| * with an `unsigned long long' argument, which we |
| * process `as is'... |
| */ |
| argval.__pformat_ullong_t = va_arg( argv, unsigned long long ); |
| |
| } else if( length == PFORMAT_LENGTH_LONG ) { |
| /* |
| * or with an `unsigned long', which we promote to |
| * `unsigned long long'... |
| */ |
| argval.__pformat_ullong_t = va_arg( argv, unsigned long ); |
| |
| } else |
| { /* or for any other size, which will have been promoted |
| * to `unsigned int', we select only the appropriately sized |
| * least significant segment, and again promote to the same |
| * size as `unsigned long long'... |
| */ |
| argval.__pformat_ullong_t = va_arg( argv, unsigned int ); |
| if( length == PFORMAT_LENGTH_SHORT ) |
| /* |
| * from `unsigned short'... |
| */ |
| argval.__pformat_ullong_t = argval.__pformat_ushort_t; |
| |
| else if( length == PFORMAT_LENGTH_CHAR ) |
| /* |
| * or even from `unsigned char'... |
| */ |
| argval.__pformat_ullong_t = argval.__pformat_uchar_t; |
| } |
| |
| /* so we can pass any size of argument to either of two |
| * common format handlers... |
| */ |
| if( c == 'u' ) |
| /* |
| * depending on whether output is to be encoded in |
| * decimal format... |
| */ |
| __pformat_int( argval, &stream ); |
| |
| else |
| /* or in octal or hexadecimal format... |
| */ |
| __pformat_xint( c, argval, &stream ); |
| |
| goto format_scan; |
| |
| case 'd': |
| case 'i': |
| /* |
| * Signed integer values; decimal format... |
| * This is similar to `u', but must process `argval' as signed, |
| * and be prepared to handle negative numbers. |
| */ |
| stream.flags |= PFORMAT_NEGATIVE; |
| #if __ENABLE_PRINTF128 |
| if( length == PFORMAT_LENGTH_LLONG128 ) { |
| argval.__pformat_u128_t.t128 = va_arg( argv, __tI128 ); |
| goto skip_sign; /* skip sign extend */ |
| } else |
| #endif |
| if( length == PFORMAT_LENGTH_LLONG ){ |
| /* |
| * The argument is a `long long' type... |
| */ |
| argval.__pformat_u128_t.t128.digits[0] = va_arg( argv, long long ); |
| } else if( length == PFORMAT_LENGTH_LONG ) { |
| /* |
| * or here, a `long' type... |
| */ |
| argval.__pformat_u128_t.t128.digits[0] = va_arg( argv, long ); |
| } else |
| { /* otherwise, it's an `int' type... |
| */ |
| argval.__pformat_u128_t.t128.digits[0] = va_arg( argv, int ); |
| if( length == PFORMAT_LENGTH_SHORT ) |
| /* |
| * but it was promoted from a `short' type... |
| */ |
| argval.__pformat_u128_t.t128.digits[0] = argval.__pformat_short_t; |
| else if( length == PFORMAT_LENGTH_CHAR ) |
| /* |
| * or even from a `char' type... |
| */ |
| argval.__pformat_u128_t.t128.digits[0] = argval.__pformat_char_t; |
| } |
| |
| /* In any case, all share a common handler... |
| */ |
| argval.__pformat_u128_t.t128.digits[1] = (argval.__pformat_llong_t < 0) ? -1LL : 0LL; |
| #if __ENABLE_PRINTF128 |
| skip_sign: |
| #endif |
| __pformat_int( argval, &stream ); |
| goto format_scan; |
| |
| case 'p': |
| /* |
| * Pointer argument; format as hexadecimal, subject to... |
| */ |
| if( (state == PFORMAT_INIT) && (stream.flags == flags) ) |
| { |
| /* Here, the user didn't specify any particular |
| * formatting attributes. We must choose a default |
| * which will be compatible with Microsoft's (broken) |
| * scanf() implementation, (i.e. matching the default |
| * used by MSVCRT's printf(), which appears to resemble |
| * "%0.8X" for 32-bit pointers); in particular, we MUST |
| * NOT adopt a GNU-like format resembling "%#x", because |
| * Microsoft's scanf() will choke on the "0x" prefix. |
| */ |
| stream.flags |= PFORMAT_ZEROFILL; |
| stream.precision = 2 * sizeof( uintptr_t ); |
| } |
| argval.__pformat_u128_t.t128.digits[0] = va_arg( argv, uintptr_t ); |
| argval.__pformat_u128_t.t128.digits[1] = 0; |
| __pformat_xint( 'x', argval, &stream ); |
| goto format_scan; |
| |
| case 'e': |
| /* |
| * Floating point format, with lower case exponent indicator |
| * and lower case `inf' or `nan' representation when required; |
| * select lower case mode, and simply fall through... |
| */ |
| stream.flags |= PFORMAT_XCASE; |
| |
| case 'E': |
| /* |
| * Floating point format, with upper case exponent indicator |
| * and upper case `INF' or `NAN' representation when required, |
| * (or lower case for all of these, on fall through from above); |
| * select lower case mode, and simply fall through... |
| */ |
| #ifdef __ENABLE_DFP |
| if( stream.flags & PFORMAT_DECIM32 ) |
| /* Is a 32bit decimal float */ |
| __pformat_efloat_decimal((_Decimal128)va_arg( argv, _Decimal32 ), &stream ); |
| else if( stream.flags & PFORMAT_DECIM64 ) |
| /* |
| * Is a 64bit decimal float |
| */ |
| __pformat_efloat_decimal((_Decimal128)va_arg( argv, _Decimal64 ), &stream ); |
| else if( stream.flags & PFORMAT_DECIM128 ) |
| /* |
| * Is a 128bit decimal float |
| */ |
| __pformat_efloat_decimal(va_arg( argv, _Decimal128 ), &stream ); |
| else |
| #endif /* __ENABLE_DFP */ |
| if( stream.flags & PFORMAT_LDOUBLE ) |
| /* |
| * for a `long double' argument... |
| */ |
| __pformat_efloat( va_arg( argv, long double ), &stream ); |
| |
| else |
| /* or just a `double', which we promote to `long double', |
| * so the two may share a common format handler. |
| */ |
| __pformat_efloat( (long double)(va_arg( argv, double )), &stream ); |
| |
| goto format_scan; |
| |
| case 'f': |
| /* |
| * Fixed point format, using lower case for `inf' and |
| * `nan', when appropriate; select lower case mode, and |
| * simply fall through... |
| */ |
| stream.flags |= PFORMAT_XCASE; |
| |
| case 'F': |
| /* |
| * Fixed case format using upper case, or lower case on |
| * fall through from above, for `INF' and `NAN'... |
| */ |
| #ifdef __ENABLE_DFP |
| if( stream.flags & PFORMAT_DECIM32 ) |
| /* Is a 32bit decimal float */ |
| __pformat_float_decimal((_Decimal128)va_arg( argv, _Decimal32 ), &stream ); |
| else if( stream.flags & PFORMAT_DECIM64 ) |
| /* |
| * Is a 64bit decimal float |
| */ |
| __pformat_float_decimal((_Decimal128)va_arg( argv, _Decimal64 ), &stream ); |
| else if( stream.flags & PFORMAT_DECIM128 ) |
| /* |
| * Is a 128bit decimal float |
| */ |
| __pformat_float_decimal(va_arg( argv, _Decimal128 ), &stream ); |
| else |
| #endif /* __ENABLE_DFP */ |
| if( stream.flags & PFORMAT_LDOUBLE ) |
| /* |
| * for a `long double' argument... |
| */ |
| __pformat_float( va_arg( argv, long double ), &stream ); |
| |
| else |
| /* or just a `double', which we promote to `long double', |
| * so the two may share a common format handler. |
| */ |
| __pformat_float( (long double)(va_arg( argv, double )), &stream ); |
| |
| goto format_scan; |
| |
| case 'g': |
| /* |
| * Generalised floating point format, with lower case |
| * exponent indicator when required; select lower case |
| * mode, and simply fall through... |
| */ |
| stream.flags |= PFORMAT_XCASE; |
| |
| case 'G': |
| /* |
| * Generalised floating point format, with upper case, |
| * or on fall through from above, with lower case exponent |
| * indicator when required... |
| */ |
| #ifdef __ENABLE_DFP |
| if( stream.flags & PFORMAT_DECIM32 ) |
| /* Is a 32bit decimal float */ |
| __pformat_gfloat_decimal((_Decimal128)va_arg( argv, _Decimal32 ), &stream ); |
| else if( stream.flags & PFORMAT_DECIM64 ) |
| /* |
| * Is a 64bit decimal float |
| */ |
| __pformat_gfloat_decimal((_Decimal128)va_arg( argv, _Decimal64 ), &stream ); |
| else if( stream.flags & PFORMAT_DECIM128 ) |
| /* |
| * Is a 128bit decimal float |
| */ |
| __pformat_gfloat_decimal(va_arg( argv, _Decimal128 ), &stream ); |
| else |
| #endif /* __ENABLE_DFP */ |
| if( stream.flags & PFORMAT_LDOUBLE ) |
| /* |
| * for a `long double' argument... |
| */ |
| __pformat_gfloat( va_arg( argv, long double ), &stream ); |
| |
| else |
| /* or just a `double', which we promote to `long double', |
| * so the two may share a common format handler. |
| */ |
| __pformat_gfloat( (long double)(va_arg( argv, double )), &stream ); |
| |
| goto format_scan; |
| |
| case 'a': |
| /* |
| * Hexadecimal floating point format, with lower case radix |
| * and exponent indicators; select the lower case mode, and |
| * fall through... |
| */ |
| stream.flags |= PFORMAT_XCASE; |
| |
| case 'A': |
| /* |
| * Hexadecimal floating point format; handles radix and |
| * exponent indicators in either upper or lower case... |
| */ |
| if( stream.flags & PFORMAT_LDOUBLE ) |
| /* |
| * with a `long double' argument... |
| */ |
| __pformat_xldouble( va_arg( argv, long double ), &stream ); |
| |
| else |
| /* or just a `double'. |
| */ |
| __pformat_xldouble( (long double)(va_arg( argv, double )), &stream ); |
| |
| goto format_scan; |
| |
| case 'n': |
| /* |
| * Save current output character count... |
| */ |
| if( length == PFORMAT_LENGTH_CHAR ) |
| /* |
| * to a signed `char' destination... |
| */ |
| *va_arg( argv, char * ) = stream.count; |
| |
| else if( length == PFORMAT_LENGTH_SHORT ) |
| /* |
| * or to a signed `short'... |
| */ |
| *va_arg( argv, short * ) = stream.count; |
| |
| else if( length == PFORMAT_LENGTH_LONG ) |
| /* |
| * or to a signed `long'... |
| */ |
| *va_arg( argv, long * ) = stream.count; |
| |
| else if( length == PFORMAT_LENGTH_LLONG ) |
| /* |
| * or to a signed `long long'... |
| */ |
| *va_arg( argv, long long * ) = stream.count; |
| |
| else |
| /* |
| * or, by default, to a signed `int'. |
| */ |
| *va_arg( argv, int * ) = stream.count; |
| |
| goto format_scan; |
| |
| /* Argument length modifiers... |
| * These are non-terminal; each sets the format parser |
| * into the PFORMAT_END state, and ends with a `break'. |
| */ |
| case 'h': |
| /* |
| * Interpret the argument as explicitly of a `short' |
| * or `char' data type, truncated from the standard |
| * length defined for integer promotion. |
| */ |
| if( *fmt == 'h' ) |
| { |
| /* Modifier is `hh'; data type is `char' sized... |
| * Skip the second `h', and set length accordingly. |
| */ |
| ++fmt; |
| length = PFORMAT_LENGTH_CHAR; |
| } |
| |
| else |
| /* Modifier is `h'; data type is `short' sized... |
| */ |
| length = PFORMAT_LENGTH_SHORT; |
| |
| state = PFORMAT_END; |
| break; |
| |
| case 'j': |
| /* |
| * Interpret the argument as being of the same size as |
| * a `intmax_t' entity... |
| */ |
| length = __pformat_arg_length( intmax_t ); |
| state = PFORMAT_END; |
| break; |
| |
| # ifdef _WIN32 |
| |
| case 'I': |
| /* |
| * The MSVCRT implementation of the printf() family of |
| * functions explicitly uses... |
| */ |
| #ifdef __ENABLE_PRINTF128 |
| if( (fmt[0] == '1') && (fmt[1] == '2') && (fmt[2] == '8')){ |
| length = PFORMAT_LENGTH_LLONG128; |
| fmt += 3; |
| } else |
| #endif |
| if( (fmt[0] == '6') && (fmt[1] == '4') ) |
| { |
| /* I64' instead of `ll', |
| * when referring to `long long' integer types... |
| */ |
| length = PFORMAT_LENGTH_LLONG; |
| fmt += 2; |
| } else |
| if( (fmt[0] == '3') && (fmt[1] == '2') ) |
| { |
| /* and `I32' instead of `l', |
| * when referring to `long' integer types... |
| */ |
| length = PFORMAT_LENGTH_LONG; |
| fmt += 2; |
| } |
| |
| else |
| /* or unqualified `I' instead of `t' or `z', |
| * when referring to `ptrdiff_t' or `size_t' entities; |
| * (we will choose to map it to `ptrdiff_t'). |
| */ |
| length = __pformat_arg_length( ptrdiff_t ); |
| |
| state = PFORMAT_END; |
| break; |
| |
| # endif |
| |
| #ifdef __ENABLE_DFP |
| case 'H': |
| stream.flags |= PFORMAT_DECIM32; |
| state = PFORMAT_END; |
| break; |
| |
| case 'D': |
| /* |
| * Interpret the argument as explicitly of a |
| * `_Decimal64' or `_Decimal128' data type. |
| */ |
| if( *fmt == 'D' ) |
| { |
| /* Modifier is `DD'; data type is `_Decimal128' sized... |
| * Skip the second `D', and set length accordingly. |
| */ |
| ++fmt; |
| stream.flags |= PFORMAT_DECIM128; |
| } |
| |
| else |
| /* Modifier is `D'; data type is `_Decimal64' sized... |
| */ |
| stream.flags |= PFORMAT_DECIM64; |
| |
| state = PFORMAT_END; |
| break; |
| #endif /* __ENABLE_DFP */ |
| case 'l': |
| /* |
| * Interpret the argument as explicitly of a |
| * `long' or `long long' data type. |
| */ |
| if( *fmt == 'l' ) |
| { |
| /* Modifier is `ll'; data type is `long long' sized... |
| * Skip the second `l', and set length accordingly. |
| */ |
| ++fmt; |
| length = PFORMAT_LENGTH_LLONG; |
| } |
| |
| else |
| /* Modifier is `l'; data type is `long' sized... |
| */ |
| length = PFORMAT_LENGTH_LONG; |
| |
| state = PFORMAT_END; |
| break; |
| |
| case 'L': |
| /* |
| * Identify the appropriate argument as a `long double', |
| * when associated with `%a', `%A', `%e', `%E', `%f', `%F', |
| * `%g' or `%G' format specifications. |
| */ |
| stream.flags |= PFORMAT_LDOUBLE; |
| state = PFORMAT_END; |
| break; |
| |
| case 't': |
| /* |
| * Interpret the argument as being of the same size as |
| * a `ptrdiff_t' entity... |
| */ |
| length = __pformat_arg_length( ptrdiff_t ); |
| state = PFORMAT_END; |
| break; |
| |
| case 'z': |
| /* |
| * Interpret the argument as being of the same size as |
| * a `size_t' entity... |
| */ |
| length = __pformat_arg_length( size_t ); |
| state = PFORMAT_END; |
| break; |
| |
| /* Precision indicator... |
| * May appear once only; it must precede any modifier |
| * for argument length, or any data type specifier. |
| */ |
| case '.': |
| if( state < PFORMAT_GET_PRECISION ) |
| { |
| /* We haven't seen a precision specification yet, |
| * so initialise it to zero, (in case no digits follow), |
| * and accept any following digits as the precision. |
| */ |
| stream.precision = 0; |
| width_spec = &stream.precision; |
| state = PFORMAT_GET_PRECISION; |
| } |
| |
| else |
| /* We've already seen a precision specification, |
| * so this is just junk; proceed to end game. |
| */ |
| state = PFORMAT_END; |
| |
| /* Either way, we must not fall through here. |
| */ |
| break; |
| |
| /* Variable field width, or precision specification, |
| * derived from the argument list... |
| */ |
| case '*': |
| /* |
| * When this appears... |
| */ |
| if( width_spec |
| && ((state == PFORMAT_INIT) || (state == PFORMAT_GET_PRECISION)) ) |
| { |
| /* in proper context; assign to field width |
| * or precision, as appropriate. |
| */ |
| if( (*width_spec = va_arg( argv, int )) < 0 ) |
| { |
| /* Assigned value was negative... |
| */ |
| if( state == PFORMAT_INIT ) |
| { |
| /* For field width, this is equivalent to |
| * a positive value with the `-' flag... |
| */ |
| stream.flags |= PFORMAT_LJUSTIFY; |
| stream.width = -stream.width; |
| } |
| |
| else |
| /* while as a precision specification, |
| * it should simply be ignored. |
| */ |
| stream.precision = PFORMAT_IGNORE; |
| } |
| } |
| |
| else |
| /* out of context; give up on width and precision |
| * specifications for this conversion. |
| */ |
| state = PFORMAT_END; |
| |
| /* Mark as processed... |
| * we must not see `*' again, in this context. |
| */ |
| width_spec = NULL; |
| break; |
| |
| /* Formatting flags... |
| * Must appear while in the PFORMAT_INIT state, |
| * and are non-terminal, so again, end with `break'. |
| */ |
| case '#': |
| /* |
| * Select alternate PFORMAT_HASHED output style. |
| */ |
| if( state == PFORMAT_INIT ) |
| stream.flags |= PFORMAT_HASHED; |
| break; |
| |
| case '+': |
| /* |
| * Print a leading sign with numeric output, |
| * for both positive and negative values. |
| */ |
| if( state == PFORMAT_INIT ) |
| stream.flags |= PFORMAT_POSITIVE; |
| break; |
| |
| case '-': |
| /* |
| * Select left justification of displayed output |
| * data, within the output field width, instead of |
| * the default flush right justification. |
| */ |
| if( state == PFORMAT_INIT ) |
| stream.flags |= PFORMAT_LJUSTIFY; |
| break; |
| |
| case '\'': |
| /* |
| * This is an XSI extension to the POSIX standard, |
| * which we do not support, at present. |
| */ |
| if (state == PFORMAT_INIT) |
| { |
| stream.flags |= PFORMAT_GROUPED; /* $$$$ */ |
| int len; wchar_t rpchr; mbstate_t cstate; |
| memset (&cstate, 0, sizeof(state)); |
| if ((len = mbrtowc( &rpchr, localeconv()->thousands_sep, 16, &cstate)) > 0) |
| stream.thousands_chr = rpchr; |
| stream.thousands_chr_len = len; |
| } |
| break; |
| |
| case '\x20': |
| /* |
| * Reserve a single space, within the output field, |
| * for display of the sign of signed data; this will |
| * be occupied by the minus sign, if the data value |
| * is negative, or by a plus sign if the data value |
| * is positive AND the `+' flag is also present, or |
| * by a space otherwise. (Technically, this flag |
| * is redundant, if the `+' flag is present). |
| */ |
| if( state == PFORMAT_INIT ) |
| stream.flags |= PFORMAT_ADDSPACE; |
| break; |
| |
| case '0': |
| /* |
| * May represent a flag, to activate the `pad with zeros' |
| * option, or it may simply be a digit in a width or in a |
| * precision specification... |
| */ |
| if( state == PFORMAT_INIT ) |
| { |
| /* This is the flag usage... |
| */ |
| stream.flags |= PFORMAT_ZEROFILL; |
| break; |
| } |
| |
| default: |
| /* |
| * If we didn't match anything above, then we will check |
| * for digits, which we may accumulate to generate field |
| * width or precision specifications... |
| */ |
| if( (state < PFORMAT_END) && ('9' >= c) && (c >= '0') ) |
| { |
| if( state == PFORMAT_INIT ) |
| /* |
| * Initial digits explicitly relate to field width... |
| */ |
| state = PFORMAT_SET_WIDTH; |
| |
| else if( state == PFORMAT_GET_PRECISION ) |
| /* |
| * while those following a precision indicator |
| * explicitly relate to precision. |
| */ |
| state = PFORMAT_SET_PRECISION; |
| |
| if( width_spec ) |
| { |
| /* We are accepting a width or precision specification... |
| */ |
| if( *width_spec < 0 ) |
| /* |
| * and accumulation hasn't started yet; we simply |
| * initialise the accumulator with the current digit |
| * value, converting from ASCII to decimal. |
| */ |
| *width_spec = c - '0'; |
| |
| else |
| /* Accumulation has already started; we perform a |
| * `leftwise decimal digit shift' on the accumulator, |
| * (i.e. multiply it by ten), then add the decimal |
| * equivalent value of the current digit. |
| */ |
| *width_spec = *width_spec * 10 + c - '0'; |
| } |
| } |
| |
| else |
| { |
| /* We found a digit out of context, or some other character |
| * with no designated meaning; reject this format specification, |
| * backtrack, and emit it as literal text... |
| */ |
| fmt = backtrack; |
|